/* * 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 #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(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) { RTC_LOG(LS_INFO) << "MediaFileImpl()"; codec_info_.plname[0] = '\0'; _fileName[0] = '\0'; } MediaFileImpl::~MediaFileImpl() { RTC_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() { RTC_LOG(LS_WARNING) << "TimeUntilNextProcess: This method is not used by MediaFile class."; return -1; } void MediaFileImpl::Process() { RTC_LOG(LS_WARNING) << "Process: This method is not used by MediaFile class."; } int32_t MediaFileImpl::PlayoutAudioData(int8_t* buffer, size_t& dataLengthInBytes) { RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutData(buffer= " << static_cast(buffer) << ", bufLen= " << dataLengthInBytes << ")"; const size_t bufferLengthInBytes = dataLengthInBytes; dataLengthInBytes = 0; if (buffer == NULL || bufferLengthInBytes == 0) { RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!"; return -1; } int32_t bytesRead = 0; { rtc::CritScope lock(&_crit); if (!_playingActive) { RTC_LOG(LS_WARNING) << "Not currently playing!"; return -1; } if (!_ptrFileUtilityObj) { RTC_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(bytesRead); return 0; } break; default: { RTC_LOG(LS_ERROR) << "Invalid file format: " << _fileFormat; assert(false); break; } } if (bytesRead > 0) { dataLengthInBytes = static_cast(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) { RTC_LOG(LS_INFO) << "MediaFileImpl::PlayoutStereoData(Left = " << static_cast(bufferLeft) << ", Right = " << static_cast(bufferRight) << ", Len= " << dataLengthInBytes << ")"; const size_t bufferLengthInBytes = dataLengthInBytes; dataLengthInBytes = 0; if (bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0) { RTC_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) { RTC_LOG(LS_WARNING) << "Not currently playing stereo!"; return -1; } if (!_ptrFileUtilityObj) { RTC_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: RTC_LOG(LS_ERROR) << "Trying to read non-WAV as stereo audio (not supported)"; break; } if (bytesRead > 0) { dataLengthInBytes = static_cast(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))) { RTC_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) { RTC_LOG(LS_INFO) << "Failed to allocate input stream for file " << fileName; return -1; } if (!inputStream->OpenFile(fileName, true)) { delete inputStream; RTC_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) { RTC_LOG(LS_ERROR) << "StartPlaying called, but already playing or recording file " << ((_fileName[0] == '\0') ? "(name not set)" : _fileName); return -1; } if (_ptrFileUtilityObj != NULL) { RTC_LOG(LS_ERROR) << "StartPlaying called, but FileUtilityObj already exists!"; StopPlaying(); return -1; } _ptrFileUtilityObj = new ModuleFileUtility(); if (_ptrFileUtilityObj == NULL) { RTC_LOG(LS_INFO) << "Failed to create FileUtilityObj!"; return -1; } switch (format) { case kFileFormatWavFile: { if (_ptrFileUtilityObj->InitWavReading(stream, startPointMs, stopPointMs) == -1) { RTC_LOG(LS_ERROR) << "Not a valid WAV file!"; StopPlaying(); return -1; } _fileFormat = kFileFormatWavFile; break; } case kFileFormatCompressedFile: { if (_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs, stopPointMs) == -1) { RTC_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) { RTC_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) { RTC_LOG(LS_ERROR) << "Not a valid PreEncoded file!"; StopPlaying(); return -1; } _fileFormat = kFileFormatPreencodedFile; break; } default: { RTC_LOG(LS_ERROR) << "Invalid file format: " << format; assert(false); break; } } if (_ptrFileUtilityObj->codec_info(codec_info_) == -1) { RTC_LOG(LS_ERROR) << "Failed to retrieve codec info!"; StopPlaying(); return -1; } _isStereo = (codec_info_.channels == 2); if (_isStereo && (_fileFormat != kFileFormatWavFile)) { RTC_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) { RTC_LOG(LS_WARNING) << "playing is not active!"; return -1; } _playingActive = false; return 0; } bool MediaFileImpl::IsPlaying() { RTC_LOG(LS_VERBOSE) << "MediaFileImpl::IsPlaying()"; rtc::CritScope lock(&_crit); return _playingActive; } int32_t MediaFileImpl::IncomingAudioData(const int8_t* buffer, const size_t bufferLengthInBytes) { RTC_LOG(LS_INFO) << "MediaFile::IncomingData(buffer= " << static_cast(buffer) << ", bufLen= " << bufferLengthInBytes << ")"; if (buffer == NULL || bufferLengthInBytes == 0) { RTC_LOG(LS_ERROR) << "Buffer pointer or length is NULL!"; return -1; } bool recordingEnded = false; uint32_t callbackNotifyMs = 0; { rtc::CritScope lock(&_crit); if (!_recordingActive) { RTC_LOG(LS_WARNING) << "Not currently recording!"; return -1; } if (_ptrOutStream == NULL) { RTC_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: RTC_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(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) { RTC_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) { RTC_LOG(LS_INFO) << "Failed to allocate memory for output stream"; return -1; } if (!outputStream->OpenFile(fileName, false)) { delete outputStream; RTC_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) { RTC_LOG(LS_ERROR) << "StartRecording called, but already recording or playing file " << _fileName << "!"; return -1; } if (_ptrFileUtilityObj != NULL) { RTC_LOG(LS_ERROR) << "StartRecording called, but fileUtilityObj already exists!"; StopRecording(); return -1; } _ptrFileUtilityObj = new ModuleFileUtility(); if (_ptrFileUtilityObj == NULL) { RTC_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) { RTC_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) { RTC_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) { RTC_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) { RTC_LOG(LS_ERROR) << "Failed to initialize Pre-Encoded file!"; delete _ptrFileUtilityObj; _ptrFileUtilityObj = NULL; return -1; } _fileFormat = kFileFormatPreencodedFile; break; } default: { RTC_LOG(LS_ERROR) << "Invalid file format " << format << " specified!"; delete _ptrFileUtilityObj; _ptrFileUtilityObj = NULL; return -1; } } _isStereo = (tmpAudioCodec.channels == 2); if (_isStereo) { if (_fileFormat != kFileFormatWavFile) { RTC_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)) { RTC_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) { RTC_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() { RTC_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() { RTC_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) { RTC_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) { RTC_LOG(LS_ERROR) << "Neither playout nor recording has been initialized!"; return -1; } if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0') { RTC_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) { RTC_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')) { RTC_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)) { RTC_LOG(LS_ERROR) << "startPointMs must be less than stopPointMs!"; return false; } if (stopPointMs && ((stopPointMs - startPointMs) < 20)) { RTC_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; } RTC_LOG(LS_ERROR) << "Frequency should be 8000, 16000, 32000, or 48000 (Hz)"; return false; } } // namespace webrtc