mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-19 08:37:54 +01:00

In https://webrtc-review.googlesource.com/c/src/+/1560 we moved WebRTC from src/webrtc to src/ (in order to preserve an healthy git history). This CL takes care of fixing header guards, #include paths, etc... NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true TBR=tommi@webrtc.org Bug: chromium:611808 Change-Id: Iea91618212bee0af16aa3f05071eab8f93706578 Reviewed-on: https://webrtc-review.googlesource.com/1561 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Henrik Kjellander <kjellander@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#19846}
1069 lines
29 KiB
C++
1069 lines
29 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "modules/media_file/media_file_impl.h"
|
|
#include "rtc_base/format_macros.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "system_wrappers/include/file_wrapper.h"
|
|
|
|
namespace webrtc {
|
|
MediaFile* MediaFile::CreateMediaFile(const int32_t id)
|
|
{
|
|
return new MediaFileImpl(id);
|
|
}
|
|
|
|
void MediaFile::DestroyMediaFile(MediaFile* module)
|
|
{
|
|
delete static_cast<MediaFileImpl*>(module);
|
|
}
|
|
|
|
MediaFileImpl::MediaFileImpl(const int32_t id)
|
|
: _id(id),
|
|
_ptrFileUtilityObj(NULL),
|
|
codec_info_(),
|
|
_ptrInStream(NULL),
|
|
_ptrOutStream(NULL),
|
|
_fileFormat((FileFormats)-1),
|
|
_recordDurationMs(0),
|
|
_playoutPositionMs(0),
|
|
_notificationMs(0),
|
|
_playingActive(false),
|
|
_recordingActive(false),
|
|
_isStereo(false),
|
|
_openFile(false),
|
|
_fileName(),
|
|
_ptrCallback(NULL)
|
|
{
|
|
LOG(LS_INFO) << "MediaFileImpl()";
|
|
|
|
codec_info_.plname[0] = '\0';
|
|
_fileName[0] = '\0';
|
|
}
|
|
|
|
|
|
MediaFileImpl::~MediaFileImpl()
|
|
{
|
|
LOG(LS_INFO) << "~MediaFileImpl()";
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
|
|
if(_playingActive)
|
|
{
|
|
StopPlaying();
|
|
}
|
|
|
|
if(_recordingActive)
|
|
{
|
|
StopRecording();
|
|
}
|
|
|
|
delete _ptrFileUtilityObj;
|
|
|
|
if(_openFile)
|
|
{
|
|
delete _ptrInStream;
|
|
_ptrInStream = NULL;
|
|
delete _ptrOutStream;
|
|
_ptrOutStream = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int64_t MediaFileImpl::TimeUntilNextProcess()
|
|
{
|
|
LOG(LS_WARNING)
|
|
<< "TimeUntilNextProcess: This method is not used by MediaFile class.";
|
|
return -1;
|
|
}
|
|
|
|
void MediaFileImpl::Process()
|
|
{
|
|
LOG(LS_WARNING) << "Process: This method is not used by MediaFile class.";
|
|
}
|
|
|
|
int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer,
|
|
size_t& dataLengthInBytes)
|
|
{
|
|
LOG(LS_INFO) << "MediaFileImpl::PlayoutData(buffer= "
|
|
<< static_cast<void*>(buffer)
|
|
<< ", bufLen= " << dataLengthInBytes << ")";
|
|
|
|
const size_t bufferLengthInBytes = dataLengthInBytes;
|
|
dataLengthInBytes = 0;
|
|
|
|
if(buffer == NULL || bufferLengthInBytes == 0)
|
|
{
|
|
LOG(LS_ERROR) << "Buffer pointer or length is NULL!";
|
|
return -1;
|
|
}
|
|
|
|
int32_t bytesRead = 0;
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
|
|
if(!_playingActive)
|
|
{
|
|
LOG(LS_WARNING) << "Not currently playing!";
|
|
return -1;
|
|
}
|
|
|
|
if(!_ptrFileUtilityObj)
|
|
{
|
|
LOG(LS_ERROR) << "Playing, but no FileUtility object!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
switch(_fileFormat)
|
|
{
|
|
case kFileFormatPcm48kHzFile:
|
|
case kFileFormatPcm32kHzFile:
|
|
case kFileFormatPcm16kHzFile:
|
|
case kFileFormatPcm8kHzFile:
|
|
bytesRead = _ptrFileUtilityObj->ReadPCMData(
|
|
*_ptrInStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
break;
|
|
case kFileFormatCompressedFile:
|
|
bytesRead = _ptrFileUtilityObj->ReadCompressedData(
|
|
*_ptrInStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
break;
|
|
case kFileFormatWavFile:
|
|
bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono(
|
|
*_ptrInStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
break;
|
|
case kFileFormatPreencodedFile:
|
|
bytesRead = _ptrFileUtilityObj->ReadPreEncodedData(
|
|
*_ptrInStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
if(bytesRead > 0)
|
|
{
|
|
dataLengthInBytes = static_cast<size_t>(bytesRead);
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
LOG(LS_ERROR) << "Invalid file format: " << _fileFormat;
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bytesRead > 0)
|
|
{
|
|
dataLengthInBytes = static_cast<size_t>(bytesRead);
|
|
}
|
|
}
|
|
HandlePlayCallbacks(bytesRead);
|
|
return 0;
|
|
}
|
|
|
|
void MediaFileImpl::HandlePlayCallbacks(int32_t bytesRead)
|
|
{
|
|
bool playEnded = false;
|
|
uint32_t callbackNotifyMs = 0;
|
|
|
|
if(bytesRead > 0)
|
|
{
|
|
// Check if it's time for PlayNotification(..).
|
|
_playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
|
|
if(_notificationMs)
|
|
{
|
|
if(_playoutPositionMs >= _notificationMs)
|
|
{
|
|
_notificationMs = 0;
|
|
callbackNotifyMs = _playoutPositionMs;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If no bytes were read assume end of file.
|
|
StopPlaying();
|
|
playEnded = true;
|
|
}
|
|
|
|
// Only _callbackCrit may and should be taken when making callbacks.
|
|
rtc::CritScope lock(&_callbackCrit);
|
|
if(_ptrCallback)
|
|
{
|
|
if(callbackNotifyMs)
|
|
{
|
|
_ptrCallback->PlayNotification(_id, callbackNotifyMs);
|
|
}
|
|
if(playEnded)
|
|
{
|
|
_ptrCallback->PlayFileEnded(_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t MediaFileImpl::PlayoutStereoData(
|
|
int8_t* bufferLeft,
|
|
int8_t* bufferRight,
|
|
size_t& dataLengthInBytes)
|
|
{
|
|
LOG(LS_INFO)
|
|
<< "MediaFileImpl::PlayoutStereoData(Left = "
|
|
<< static_cast<void*>(bufferLeft) << ", Right = "
|
|
<< static_cast<void*>(bufferRight) << ", Len= " << dataLengthInBytes
|
|
<< ")";
|
|
|
|
const size_t bufferLengthInBytes = dataLengthInBytes;
|
|
dataLengthInBytes = 0;
|
|
|
|
if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0)
|
|
{
|
|
LOG(LS_ERROR) << "A buffer pointer or the length is NULL!";
|
|
return -1;
|
|
}
|
|
|
|
bool playEnded = false;
|
|
uint32_t callbackNotifyMs = 0;
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
|
|
if(!_playingActive || !_isStereo)
|
|
{
|
|
LOG(LS_WARNING) << "Not currently playing stereo!";
|
|
return -1;
|
|
}
|
|
|
|
if(!_ptrFileUtilityObj)
|
|
{
|
|
LOG(LS_ERROR)
|
|
<< "Playing stereo, but the FileUtility objects is NULL!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
// Stereo playout only supported for WAV files.
|
|
int32_t bytesRead = 0;
|
|
switch(_fileFormat)
|
|
{
|
|
case kFileFormatWavFile:
|
|
bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo(
|
|
*_ptrInStream,
|
|
bufferLeft,
|
|
bufferRight,
|
|
bufferLengthInBytes);
|
|
break;
|
|
default:
|
|
LOG(LS_ERROR)
|
|
<< "Trying to read non-WAV as stereo audio (not supported)";
|
|
break;
|
|
}
|
|
|
|
if(bytesRead > 0)
|
|
{
|
|
dataLengthInBytes = static_cast<size_t>(bytesRead);
|
|
|
|
// Check if it's time for PlayNotification(..).
|
|
_playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
|
|
if(_notificationMs)
|
|
{
|
|
if(_playoutPositionMs >= _notificationMs)
|
|
{
|
|
_notificationMs = 0;
|
|
callbackNotifyMs = _playoutPositionMs;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If no bytes were read assume end of file.
|
|
StopPlaying();
|
|
playEnded = true;
|
|
}
|
|
}
|
|
|
|
rtc::CritScope lock(&_callbackCrit);
|
|
if(_ptrCallback)
|
|
{
|
|
if(callbackNotifyMs)
|
|
{
|
|
_ptrCallback->PlayNotification(_id, callbackNotifyMs);
|
|
}
|
|
if(playEnded)
|
|
{
|
|
_ptrCallback->PlayFileEnded(_id);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StartPlayingAudioFile(
|
|
const char* fileName,
|
|
const uint32_t notificationTimeMs,
|
|
const bool loop,
|
|
const FileFormats format,
|
|
const CodecInst* codecInst,
|
|
const uint32_t startPointMs,
|
|
const uint32_t stopPointMs)
|
|
{
|
|
if(!ValidFileName(fileName))
|
|
{
|
|
return -1;
|
|
}
|
|
if(!ValidFileFormat(format,codecInst))
|
|
{
|
|
return -1;
|
|
}
|
|
if(!ValidFilePositions(startPointMs,stopPointMs))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Check that the file will play longer than notificationTimeMs ms.
|
|
if((startPointMs && stopPointMs && !loop) &&
|
|
(notificationTimeMs > (stopPointMs - startPointMs)))
|
|
{
|
|
LOG(LS_ERROR) << "specified notification time is longer than amount of"
|
|
<< " ms that will be played";
|
|
return -1;
|
|
}
|
|
|
|
FileWrapper* inputStream = FileWrapper::Create();
|
|
if(inputStream == NULL)
|
|
{
|
|
LOG(LS_INFO) << "Failed to allocate input stream for file " << fileName;
|
|
return -1;
|
|
}
|
|
|
|
if (!inputStream->OpenFile(fileName, true)) {
|
|
delete inputStream;
|
|
LOG(LS_ERROR) << "Could not open input file " << fileName;
|
|
return -1;
|
|
}
|
|
|
|
if(StartPlayingStream(*inputStream, loop, notificationTimeMs,
|
|
format, codecInst, startPointMs, stopPointMs) == -1)
|
|
{
|
|
inputStream->CloseFile();
|
|
delete inputStream;
|
|
return -1;
|
|
}
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
_openFile = true;
|
|
strncpy(_fileName, fileName, sizeof(_fileName));
|
|
_fileName[sizeof(_fileName) - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StartPlayingAudioStream(
|
|
InStream& stream,
|
|
const uint32_t notificationTimeMs,
|
|
const FileFormats format,
|
|
const CodecInst* codecInst,
|
|
const uint32_t startPointMs,
|
|
const uint32_t stopPointMs)
|
|
{
|
|
return StartPlayingStream(stream, false, notificationTimeMs, format,
|
|
codecInst, startPointMs, stopPointMs);
|
|
}
|
|
|
|
int32_t MediaFileImpl::StartPlayingStream(
|
|
InStream& stream,
|
|
bool loop,
|
|
const uint32_t notificationTimeMs,
|
|
const FileFormats format,
|
|
const CodecInst* codecInst,
|
|
const uint32_t startPointMs,
|
|
const uint32_t stopPointMs)
|
|
{
|
|
if(!ValidFileFormat(format,codecInst))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if(!ValidFilePositions(startPointMs,stopPointMs))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
if(_playingActive || _recordingActive)
|
|
{
|
|
LOG(LS_ERROR)
|
|
<< "StartPlaying called, but already playing or recording file "
|
|
<< ((_fileName[0] == '\0') ? "(name not set)" : _fileName);
|
|
return -1;
|
|
}
|
|
|
|
if(_ptrFileUtilityObj != NULL)
|
|
{
|
|
LOG(LS_ERROR)
|
|
<< "StartPlaying called, but FileUtilityObj already exists!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
_ptrFileUtilityObj = new ModuleFileUtility();
|
|
if(_ptrFileUtilityObj == NULL)
|
|
{
|
|
LOG(LS_INFO) << "Failed to create FileUtilityObj!";
|
|
return -1;
|
|
}
|
|
|
|
switch(format)
|
|
{
|
|
case kFileFormatWavFile:
|
|
{
|
|
if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs,
|
|
stopPointMs) == -1)
|
|
{
|
|
LOG(LS_ERROR) << "Not a valid WAV file!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
_fileFormat = kFileFormatWavFile;
|
|
break;
|
|
}
|
|
case kFileFormatCompressedFile:
|
|
{
|
|
if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs,
|
|
stopPointMs) == -1)
|
|
{
|
|
LOG(LS_ERROR) << "Not a valid Compressed file!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
_fileFormat = kFileFormatCompressedFile;
|
|
break;
|
|
}
|
|
case kFileFormatPcm8kHzFile:
|
|
case kFileFormatPcm16kHzFile:
|
|
case kFileFormatPcm32kHzFile:
|
|
case kFileFormatPcm48kHzFile:
|
|
{
|
|
// ValidFileFormat() called in the beginneing of this function
|
|
// prevents codecInst from being NULL here.
|
|
assert(codecInst != NULL);
|
|
if(!ValidFrequency(codecInst->plfreq) ||
|
|
_ptrFileUtilityObj->InitPCMReading(stream, startPointMs,
|
|
stopPointMs,
|
|
codecInst->plfreq) == -1)
|
|
{
|
|
LOG(LS_ERROR) << "Not a valid raw 8 or 16 KHz PCM file!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
_fileFormat = format;
|
|
break;
|
|
}
|
|
case kFileFormatPreencodedFile:
|
|
{
|
|
// ValidFileFormat() called in the beginneing of this function
|
|
// prevents codecInst from being NULL here.
|
|
assert(codecInst != NULL);
|
|
if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) ==
|
|
-1)
|
|
{
|
|
LOG(LS_ERROR) << "Not a valid PreEncoded file!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
_fileFormat = kFileFormatPreencodedFile;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOG(LS_ERROR) << "Invalid file format: " << format;
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
if(_ptrFileUtilityObj->codec_info(codec_info_) == -1)
|
|
{
|
|
LOG(LS_ERROR) << "Failed to retrieve codec info!";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
|
|
_isStereo = (codec_info_.channels == 2);
|
|
if(_isStereo && (_fileFormat != kFileFormatWavFile))
|
|
{
|
|
LOG(LS_WARNING) << "Stereo is only allowed for WAV files";
|
|
StopPlaying();
|
|
return -1;
|
|
}
|
|
_playingActive = true;
|
|
_playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs();
|
|
_ptrInStream = &stream;
|
|
_notificationMs = notificationTimeMs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StopPlaying()
|
|
{
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
_isStereo = false;
|
|
if(_ptrFileUtilityObj)
|
|
{
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
}
|
|
if(_ptrInStream)
|
|
{
|
|
// If MediaFileImpl opened the InStream it must be reclaimed here.
|
|
if(_openFile)
|
|
{
|
|
delete _ptrInStream;
|
|
_openFile = false;
|
|
}
|
|
_ptrInStream = NULL;
|
|
}
|
|
|
|
codec_info_.pltype = 0;
|
|
codec_info_.plname[0] = '\0';
|
|
|
|
if(!_playingActive)
|
|
{
|
|
LOG(LS_WARNING) << "playing is not active!";
|
|
return -1;
|
|
}
|
|
|
|
_playingActive = false;
|
|
return 0;
|
|
}
|
|
|
|
bool MediaFileImpl::IsPlaying()
|
|
{
|
|
LOG(LS_VERBOSE) << "MediaFileImpl::IsPlaying()";
|
|
rtc::CritScope lock(&_crit);
|
|
return _playingActive;
|
|
}
|
|
|
|
int32_t MediaFileImpl::IncomingAudioData(
|
|
const int8_t* buffer,
|
|
const size_t bufferLengthInBytes)
|
|
{
|
|
LOG(LS_INFO) << "MediaFile::IncomingData(buffer= "
|
|
<< static_cast<const void*>(buffer) << ", bufLen= "
|
|
<< bufferLengthInBytes << ")";
|
|
|
|
if(buffer == NULL || bufferLengthInBytes == 0)
|
|
{
|
|
LOG(LS_ERROR) << "Buffer pointer or length is NULL!";
|
|
return -1;
|
|
}
|
|
|
|
bool recordingEnded = false;
|
|
uint32_t callbackNotifyMs = 0;
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
|
|
if(!_recordingActive)
|
|
{
|
|
LOG(LS_WARNING) << "Not currently recording!";
|
|
return -1;
|
|
}
|
|
if(_ptrOutStream == NULL)
|
|
{
|
|
LOG(LS_ERROR) << "Recording is active, but output stream is NULL!";
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
|
|
int32_t bytesWritten = 0;
|
|
uint32_t samplesWritten = codec_info_.pacsize;
|
|
if(_ptrFileUtilityObj)
|
|
{
|
|
switch(_fileFormat)
|
|
{
|
|
case kFileFormatPcm8kHzFile:
|
|
case kFileFormatPcm16kHzFile:
|
|
case kFileFormatPcm32kHzFile:
|
|
case kFileFormatPcm48kHzFile:
|
|
bytesWritten = _ptrFileUtilityObj->WritePCMData(
|
|
*_ptrOutStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
|
|
// Sample size is 2 bytes.
|
|
if(bytesWritten > 0)
|
|
{
|
|
samplesWritten = bytesWritten/sizeof(int16_t);
|
|
}
|
|
break;
|
|
case kFileFormatCompressedFile:
|
|
bytesWritten = _ptrFileUtilityObj->WriteCompressedData(
|
|
*_ptrOutStream, buffer, bufferLengthInBytes);
|
|
break;
|
|
case kFileFormatWavFile:
|
|
bytesWritten = _ptrFileUtilityObj->WriteWavData(
|
|
*_ptrOutStream,
|
|
buffer,
|
|
bufferLengthInBytes);
|
|
if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname,
|
|
"L16", 4) == 0)
|
|
{
|
|
// Sample size is 2 bytes.
|
|
samplesWritten = bytesWritten/sizeof(int16_t);
|
|
}
|
|
break;
|
|
case kFileFormatPreencodedFile:
|
|
bytesWritten = _ptrFileUtilityObj->WritePreEncodedData(
|
|
*_ptrOutStream, buffer, bufferLengthInBytes);
|
|
break;
|
|
default:
|
|
LOG(LS_ERROR) << "Invalid file format: " << _fileFormat;
|
|
assert(false);
|
|
break;
|
|
}
|
|
} else {
|
|
// TODO (hellner): quick look at the code makes me think that this
|
|
// code is never executed. Remove?
|
|
if(_ptrOutStream)
|
|
{
|
|
if(_ptrOutStream->Write(buffer, bufferLengthInBytes))
|
|
{
|
|
bytesWritten = static_cast<int32_t>(bufferLengthInBytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
_recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000);
|
|
|
|
// Check if it's time for RecordNotification(..).
|
|
if(_notificationMs)
|
|
{
|
|
if(_recordDurationMs >= _notificationMs)
|
|
{
|
|
_notificationMs = 0;
|
|
callbackNotifyMs = _recordDurationMs;
|
|
}
|
|
}
|
|
if(bytesWritten < (int32_t)bufferLengthInBytes)
|
|
{
|
|
LOG(LS_WARNING) << "Failed to write all requested bytes!";
|
|
StopRecording();
|
|
recordingEnded = true;
|
|
}
|
|
}
|
|
|
|
// Only _callbackCrit may and should be taken when making callbacks.
|
|
rtc::CritScope lock(&_callbackCrit);
|
|
if(_ptrCallback)
|
|
{
|
|
if(callbackNotifyMs)
|
|
{
|
|
_ptrCallback->RecordNotification(_id, callbackNotifyMs);
|
|
}
|
|
if(recordingEnded)
|
|
{
|
|
_ptrCallback->RecordFileEnded(_id);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StartRecordingAudioFile(
|
|
const char* fileName,
|
|
const FileFormats format,
|
|
const CodecInst& codecInst,
|
|
const uint32_t notificationTimeMs,
|
|
const uint32_t maxSizeBytes)
|
|
{
|
|
if(!ValidFileName(fileName))
|
|
{
|
|
return -1;
|
|
}
|
|
if(!ValidFileFormat(format,&codecInst))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
FileWrapper* outputStream = FileWrapper::Create();
|
|
if(outputStream == NULL)
|
|
{
|
|
LOG(LS_INFO) << "Failed to allocate memory for output stream";
|
|
return -1;
|
|
}
|
|
|
|
if (!outputStream->OpenFile(fileName, false)) {
|
|
delete outputStream;
|
|
LOG(LS_ERROR) << "Could not open output file '" << fileName
|
|
<< "' for writing!";
|
|
return -1;
|
|
}
|
|
|
|
if(maxSizeBytes)
|
|
{
|
|
outputStream->SetMaxFileSize(maxSizeBytes);
|
|
}
|
|
|
|
if(StartRecordingAudioStream(*outputStream, format, codecInst,
|
|
notificationTimeMs) == -1)
|
|
{
|
|
outputStream->CloseFile();
|
|
delete outputStream;
|
|
return -1;
|
|
}
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
_openFile = true;
|
|
strncpy(_fileName, fileName, sizeof(_fileName));
|
|
_fileName[sizeof(_fileName) - 1] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StartRecordingAudioStream(
|
|
OutStream& stream,
|
|
const FileFormats format,
|
|
const CodecInst& codecInst,
|
|
const uint32_t notificationTimeMs)
|
|
{
|
|
// Check codec info
|
|
if(!ValidFileFormat(format,&codecInst))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
if(_recordingActive || _playingActive)
|
|
{
|
|
LOG(LS_ERROR)
|
|
<< "StartRecording called, but already recording or playing file "
|
|
<< _fileName << "!";
|
|
return -1;
|
|
}
|
|
|
|
if(_ptrFileUtilityObj != NULL)
|
|
{
|
|
LOG(LS_ERROR)
|
|
<< "StartRecording called, but fileUtilityObj already exists!";
|
|
StopRecording();
|
|
return -1;
|
|
}
|
|
|
|
_ptrFileUtilityObj = new ModuleFileUtility();
|
|
if(_ptrFileUtilityObj == NULL)
|
|
{
|
|
LOG(LS_INFO) << "Cannot allocate fileUtilityObj!";
|
|
return -1;
|
|
}
|
|
|
|
CodecInst tmpAudioCodec;
|
|
memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst));
|
|
switch(format)
|
|
{
|
|
case kFileFormatWavFile:
|
|
{
|
|
if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1)
|
|
{
|
|
LOG(LS_ERROR) << "Failed to initialize WAV file!";
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
return -1;
|
|
}
|
|
_fileFormat = kFileFormatWavFile;
|
|
break;
|
|
}
|
|
case kFileFormatCompressedFile:
|
|
{
|
|
// Write compression codec name at beginning of file
|
|
if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) ==
|
|
-1)
|
|
{
|
|
LOG(LS_ERROR) << "Failed to initialize Compressed file!";
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
return -1;
|
|
}
|
|
_fileFormat = kFileFormatCompressedFile;
|
|
break;
|
|
}
|
|
case kFileFormatPcm8kHzFile:
|
|
case kFileFormatPcm16kHzFile:
|
|
case kFileFormatPcm32kHzFile:
|
|
case kFileFormatPcm48kHzFile:
|
|
{
|
|
if(!ValidFrequency(codecInst.plfreq) ||
|
|
_ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) ==
|
|
-1)
|
|
{
|
|
LOG(LS_ERROR) << "Failed to initialize PCM file!";
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
return -1;
|
|
}
|
|
_fileFormat = format;
|
|
break;
|
|
}
|
|
case kFileFormatPreencodedFile:
|
|
{
|
|
if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) ==
|
|
-1)
|
|
{
|
|
LOG(LS_ERROR) << "Failed to initialize Pre-Encoded file!";
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
return -1;
|
|
}
|
|
|
|
_fileFormat = kFileFormatPreencodedFile;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOG(LS_ERROR) << "Invalid file format " << format << " specified!";
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
_isStereo = (tmpAudioCodec.channels == 2);
|
|
if(_isStereo)
|
|
{
|
|
if(_fileFormat != kFileFormatWavFile)
|
|
{
|
|
LOG(LS_WARNING) << "Stereo is only allowed for WAV files";
|
|
StopRecording();
|
|
return -1;
|
|
}
|
|
if((STR_NCASE_CMP(tmpAudioCodec.plname, "L16", 4) != 0) &&
|
|
(STR_NCASE_CMP(tmpAudioCodec.plname, "PCMU", 5) != 0) &&
|
|
(STR_NCASE_CMP(tmpAudioCodec.plname, "PCMA", 5) != 0))
|
|
{
|
|
LOG(LS_WARNING)
|
|
<< "Stereo is only allowed for codec PCMU, PCMA and L16 ";
|
|
StopRecording();
|
|
return -1;
|
|
}
|
|
}
|
|
memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst));
|
|
_recordingActive = true;
|
|
_ptrOutStream = &stream;
|
|
_notificationMs = notificationTimeMs;
|
|
_recordDurationMs = 0;
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::StopRecording()
|
|
{
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
if(!_recordingActive)
|
|
{
|
|
LOG(LS_WARNING) << "recording is not active!";
|
|
return -1;
|
|
}
|
|
|
|
_isStereo = false;
|
|
|
|
if(_ptrFileUtilityObj != NULL)
|
|
{
|
|
// Both AVI and WAV header has to be updated before closing the stream
|
|
// because they contain size information.
|
|
if((_fileFormat == kFileFormatWavFile) &&
|
|
(_ptrOutStream != NULL))
|
|
{
|
|
_ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream);
|
|
}
|
|
delete _ptrFileUtilityObj;
|
|
_ptrFileUtilityObj = NULL;
|
|
}
|
|
|
|
if(_ptrOutStream != NULL)
|
|
{
|
|
// If MediaFileImpl opened the OutStream it must be reclaimed here.
|
|
if(_openFile)
|
|
{
|
|
delete _ptrOutStream;
|
|
_openFile = false;
|
|
}
|
|
_ptrOutStream = NULL;
|
|
}
|
|
|
|
_recordingActive = false;
|
|
codec_info_.pltype = 0;
|
|
codec_info_.plname[0] = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool MediaFileImpl::IsRecording()
|
|
{
|
|
LOG(LS_VERBOSE) << "MediaFileImpl::IsRecording()";
|
|
rtc::CritScope lock(&_crit);
|
|
return _recordingActive;
|
|
}
|
|
|
|
int32_t MediaFileImpl::RecordDurationMs(uint32_t& durationMs)
|
|
{
|
|
|
|
rtc::CritScope lock(&_crit);
|
|
if(!_recordingActive)
|
|
{
|
|
durationMs = 0;
|
|
return -1;
|
|
}
|
|
durationMs = _recordDurationMs;
|
|
return 0;
|
|
}
|
|
|
|
bool MediaFileImpl::IsStereo()
|
|
{
|
|
LOG(LS_VERBOSE) << "MediaFileImpl::IsStereo()";
|
|
rtc::CritScope lock(&_crit);
|
|
return _isStereo;
|
|
}
|
|
|
|
int32_t MediaFileImpl::SetModuleFileCallback(FileCallback* callback)
|
|
{
|
|
|
|
rtc::CritScope lock(&_callbackCrit);
|
|
|
|
_ptrCallback = callback;
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::FileDurationMs(const char* fileName,
|
|
uint32_t& durationMs,
|
|
const FileFormats format,
|
|
const uint32_t freqInHz)
|
|
{
|
|
|
|
if(!ValidFileName(fileName))
|
|
{
|
|
return -1;
|
|
}
|
|
if(!ValidFrequency(freqInHz))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ModuleFileUtility* utilityObj = new ModuleFileUtility();
|
|
if(utilityObj == NULL)
|
|
{
|
|
LOG(LS_ERROR) << "failed to allocate utility object!";
|
|
return -1;
|
|
}
|
|
|
|
const int32_t duration = utilityObj->FileDurationMs(fileName, format,
|
|
freqInHz);
|
|
delete utilityObj;
|
|
if(duration == -1)
|
|
{
|
|
durationMs = 0;
|
|
return -1;
|
|
}
|
|
|
|
durationMs = duration;
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::PlayoutPositionMs(uint32_t& positionMs) const
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
if(!_playingActive)
|
|
{
|
|
positionMs = 0;
|
|
return -1;
|
|
}
|
|
positionMs = _playoutPositionMs;
|
|
return 0;
|
|
}
|
|
|
|
int32_t MediaFileImpl::codec_info(CodecInst& codecInst) const
|
|
{
|
|
rtc::CritScope lock(&_crit);
|
|
if(!_playingActive && !_recordingActive)
|
|
{
|
|
LOG(LS_ERROR) << "Neither playout nor recording has been initialized!";
|
|
return -1;
|
|
}
|
|
if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0')
|
|
{
|
|
LOG(LS_ERROR) << "The CodecInst for "
|
|
<< (_playingActive ? "Playback" : "Recording")
|
|
<< " is unknown!";
|
|
return -1;
|
|
}
|
|
memcpy(&codecInst,&codec_info_,sizeof(CodecInst));
|
|
return 0;
|
|
}
|
|
|
|
bool MediaFileImpl::ValidFileFormat(const FileFormats format,
|
|
const CodecInst* codecInst)
|
|
{
|
|
if(codecInst == NULL)
|
|
{
|
|
if(format == kFileFormatPreencodedFile ||
|
|
format == kFileFormatPcm8kHzFile ||
|
|
format == kFileFormatPcm16kHzFile ||
|
|
format == kFileFormatPcm32kHzFile ||
|
|
format == kFileFormatPcm48kHzFile)
|
|
{
|
|
LOG(LS_ERROR) << "Codec info required for file format specified!";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MediaFileImpl::ValidFileName(const char* fileName)
|
|
{
|
|
if((fileName == NULL) ||(fileName[0] == '\0'))
|
|
{
|
|
LOG(LS_ERROR) << "FileName not specified!";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool MediaFileImpl::ValidFilePositions(const uint32_t startPointMs,
|
|
const uint32_t stopPointMs)
|
|
{
|
|
if(startPointMs == 0 && stopPointMs == 0) // Default values
|
|
{
|
|
return true;
|
|
}
|
|
if(stopPointMs &&(startPointMs >= stopPointMs))
|
|
{
|
|
LOG(LS_ERROR) << "startPointMs must be less than stopPointMs!";
|
|
return false;
|
|
}
|
|
if(stopPointMs &&((stopPointMs - startPointMs) < 20))
|
|
{
|
|
LOG(LS_ERROR) << "minimum play duration for files is 20 ms!";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MediaFileImpl::ValidFrequency(const uint32_t frequency)
|
|
{
|
|
if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000) ||
|
|
(frequency == 48000))
|
|
{
|
|
return true;
|
|
}
|
|
LOG(LS_ERROR) << "Frequency should be 8000, 16000, 32000, or 48000 (Hz)";
|
|
return false;
|
|
}
|
|
} // namespace webrtc
|