|  | /* | 
|  | *  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 "webrtc/modules/utility/source/file_player_impl.h" | 
|  | #include "webrtc/system_wrappers/include/logging.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | FilePlayer* FilePlayer::CreateFilePlayer(uint32_t instanceID, | 
|  | FileFormats fileFormat) | 
|  | { | 
|  | switch(fileFormat) | 
|  | { | 
|  | case kFileFormatWavFile: | 
|  | case kFileFormatCompressedFile: | 
|  | case kFileFormatPreencodedFile: | 
|  | case kFileFormatPcm16kHzFile: | 
|  | case kFileFormatPcm8kHzFile: | 
|  | case kFileFormatPcm32kHzFile: | 
|  | // audio formats | 
|  | return new FilePlayerImpl(instanceID, fileFormat); | 
|  | default: | 
|  | assert(false); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | void FilePlayer::DestroyFilePlayer(FilePlayer* player) | 
|  | { | 
|  | delete player; | 
|  | } | 
|  |  | 
|  | FilePlayerImpl::FilePlayerImpl(const uint32_t instanceID, | 
|  | const FileFormats fileFormat) | 
|  | : _instanceID(instanceID), | 
|  | _fileFormat(fileFormat), | 
|  | _fileModule(*MediaFile::CreateMediaFile(instanceID)), | 
|  | _decodedLengthInMS(0), | 
|  | _audioDecoder(instanceID), | 
|  | _codec(), | 
|  | _numberOf10MsPerFrame(0), | 
|  | _numberOf10MsInDecoder(0), | 
|  | _resampler(), | 
|  | _scaling(1.0) | 
|  | { | 
|  | _codec.plfreq = 0; | 
|  | } | 
|  |  | 
|  | FilePlayerImpl::~FilePlayerImpl() | 
|  | { | 
|  | MediaFile::DestroyMediaFile(&_fileModule); | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::Frequency() const | 
|  | { | 
|  | if(_codec.plfreq == 0) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  | // Make sure that sample rate is 8,16 or 32 kHz. E.g. WAVE files may have | 
|  | // other sampling rates. | 
|  | if(_codec.plfreq == 11000) | 
|  | { | 
|  | return 16000; | 
|  | } | 
|  | else if(_codec.plfreq == 22000) | 
|  | { | 
|  | return 32000; | 
|  | } | 
|  | else if(_codec.plfreq == 44000) | 
|  | { | 
|  | return 32000; | 
|  | } | 
|  | else if(_codec.plfreq == 48000) | 
|  | { | 
|  | return 32000; | 
|  | } | 
|  | else | 
|  | { | 
|  | return _codec.plfreq; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::AudioCodec(CodecInst& audioCodec) const | 
|  | { | 
|  | audioCodec = _codec; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::Get10msAudioFromFile( | 
|  | int16_t* outBuffer, | 
|  | size_t& lengthInSamples, | 
|  | int frequencyInHz) | 
|  | { | 
|  | if(_codec.plfreq == 0) | 
|  | { | 
|  | LOG(LS_WARNING) << "Get10msAudioFromFile() playing not started!" | 
|  | << " codec freq = " << _codec.plfreq | 
|  | << ", wanted freq = " << frequencyInHz; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | AudioFrame unresampledAudioFrame; | 
|  | if(STR_CASE_CMP(_codec.plname, "L16") == 0) | 
|  | { | 
|  | unresampledAudioFrame.sample_rate_hz_ = _codec.plfreq; | 
|  |  | 
|  | // L16 is un-encoded data. Just pull 10 ms. | 
|  | size_t lengthInBytes = | 
|  | sizeof(unresampledAudioFrame.data_); | 
|  | if (_fileModule.PlayoutAudioData( | 
|  | (int8_t*)unresampledAudioFrame.data_, | 
|  | lengthInBytes) == -1) | 
|  | { | 
|  | // End of file reached. | 
|  | return -1; | 
|  | } | 
|  | if(lengthInBytes == 0) | 
|  | { | 
|  | lengthInSamples = 0; | 
|  | return 0; | 
|  | } | 
|  | // One sample is two bytes. | 
|  | unresampledAudioFrame.samples_per_channel_ = lengthInBytes >> 1; | 
|  |  | 
|  | } else { | 
|  | // Decode will generate 10 ms of audio data. PlayoutAudioData(..) | 
|  | // expects a full frame. If the frame size is larger than 10 ms, | 
|  | // PlayoutAudioData(..) data should be called proportionally less often. | 
|  | int16_t encodedBuffer[MAX_AUDIO_BUFFER_IN_SAMPLES]; | 
|  | size_t encodedLengthInBytes = 0; | 
|  | if(++_numberOf10MsInDecoder >= _numberOf10MsPerFrame) | 
|  | { | 
|  | _numberOf10MsInDecoder = 0; | 
|  | size_t bytesFromFile = sizeof(encodedBuffer); | 
|  | if (_fileModule.PlayoutAudioData((int8_t*)encodedBuffer, | 
|  | bytesFromFile) == -1) | 
|  | { | 
|  | // End of file reached. | 
|  | return -1; | 
|  | } | 
|  | encodedLengthInBytes = bytesFromFile; | 
|  | } | 
|  | if(_audioDecoder.Decode(unresampledAudioFrame,frequencyInHz, | 
|  | (int8_t*)encodedBuffer, | 
|  | encodedLengthInBytes) == -1) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t outLen = 0; | 
|  | if(_resampler.ResetIfNeeded(unresampledAudioFrame.sample_rate_hz_, | 
|  | frequencyInHz, 1)) | 
|  | { | 
|  | LOG(LS_WARNING) << "Get10msAudioFromFile() unexpected codec."; | 
|  |  | 
|  | // New sampling frequency. Update state. | 
|  | outLen = static_cast<size_t>(frequencyInHz / 100); | 
|  | memset(outBuffer, 0, outLen * sizeof(int16_t)); | 
|  | return 0; | 
|  | } | 
|  | _resampler.Push(unresampledAudioFrame.data_, | 
|  | unresampledAudioFrame.samples_per_channel_, | 
|  | outBuffer, | 
|  | MAX_AUDIO_BUFFER_IN_SAMPLES, | 
|  | outLen); | 
|  |  | 
|  | lengthInSamples = outLen; | 
|  |  | 
|  | if(_scaling != 1.0) | 
|  | { | 
|  | for (size_t i = 0;i < outLen; i++) | 
|  | { | 
|  | outBuffer[i] = (int16_t)(outBuffer[i] * _scaling); | 
|  | } | 
|  | } | 
|  | _decodedLengthInMS += 10; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::RegisterModuleFileCallback(FileCallback* callback) | 
|  | { | 
|  | return _fileModule.SetModuleFileCallback(callback); | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::SetAudioScaling(float scaleFactor) | 
|  | { | 
|  | if((scaleFactor >= 0)&&(scaleFactor <= 2.0)) | 
|  | { | 
|  | _scaling = scaleFactor; | 
|  | return 0; | 
|  | } | 
|  | LOG(LS_WARNING) << "SetAudioScaling() non-allowed scale factor."; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::StartPlayingFile(const char* fileName, | 
|  | bool loop, | 
|  | uint32_t startPosition, | 
|  | float volumeScaling, | 
|  | uint32_t notification, | 
|  | uint32_t stopPosition, | 
|  | const CodecInst* codecInst) | 
|  | { | 
|  | if (_fileFormat == kFileFormatPcm16kHzFile || | 
|  | _fileFormat == kFileFormatPcm8kHzFile|| | 
|  | _fileFormat == kFileFormatPcm32kHzFile ) | 
|  | { | 
|  | CodecInst codecInstL16; | 
|  | strncpy(codecInstL16.plname,"L16",32); | 
|  | codecInstL16.pltype   = 93; | 
|  | codecInstL16.channels = 1; | 
|  |  | 
|  | if (_fileFormat == kFileFormatPcm8kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 128000; | 
|  | codecInstL16.plfreq   = 8000; | 
|  | codecInstL16.pacsize  = 80; | 
|  |  | 
|  | } else if(_fileFormat == kFileFormatPcm16kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 256000; | 
|  | codecInstL16.plfreq   = 16000; | 
|  | codecInstL16.pacsize  = 160; | 
|  |  | 
|  | }else if(_fileFormat == kFileFormatPcm32kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 512000; | 
|  | codecInstL16.plfreq   = 32000; | 
|  | codecInstL16.pacsize  = 160; | 
|  | } else | 
|  | { | 
|  | LOG(LS_ERROR) << "StartPlayingFile() sample frequency not " | 
|  | << "supported for PCM format."; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, | 
|  | _fileFormat, &codecInstL16, | 
|  | startPosition, | 
|  | stopPosition) == -1) | 
|  | { | 
|  | LOG(LS_WARNING) << "StartPlayingFile() failed to initialize " | 
|  | << "pcm file " << fileName; | 
|  | return -1; | 
|  | } | 
|  | SetAudioScaling(volumeScaling); | 
|  | }else if(_fileFormat == kFileFormatPreencodedFile) | 
|  | { | 
|  | if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, | 
|  | _fileFormat, codecInst) == -1) | 
|  | { | 
|  | LOG(LS_WARNING) << "StartPlayingFile() failed to initialize " | 
|  | << "pre-encoded file " << fileName; | 
|  | return -1; | 
|  | } | 
|  | } else | 
|  | { | 
|  | CodecInst* no_inst = NULL; | 
|  | if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, | 
|  | _fileFormat, no_inst, | 
|  | startPosition, | 
|  | stopPosition) == -1) | 
|  | { | 
|  | LOG(LS_WARNING) << "StartPlayingFile() failed to initialize file " | 
|  | << fileName; | 
|  | return -1; | 
|  | } | 
|  | SetAudioScaling(volumeScaling); | 
|  | } | 
|  | if (SetUpAudioDecoder() == -1) | 
|  | { | 
|  | StopPlayingFile(); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::StartPlayingFile(InStream& sourceStream, | 
|  | uint32_t startPosition, | 
|  | float volumeScaling, | 
|  | uint32_t notification, | 
|  | uint32_t stopPosition, | 
|  | const CodecInst* codecInst) | 
|  | { | 
|  | if (_fileFormat == kFileFormatPcm16kHzFile || | 
|  | _fileFormat == kFileFormatPcm32kHzFile || | 
|  | _fileFormat == kFileFormatPcm8kHzFile) | 
|  | { | 
|  | CodecInst codecInstL16; | 
|  | strncpy(codecInstL16.plname,"L16",32); | 
|  | codecInstL16.pltype   = 93; | 
|  | codecInstL16.channels = 1; | 
|  |  | 
|  | if (_fileFormat == kFileFormatPcm8kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 128000; | 
|  | codecInstL16.plfreq   = 8000; | 
|  | codecInstL16.pacsize  = 80; | 
|  |  | 
|  | }else if (_fileFormat == kFileFormatPcm16kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 256000; | 
|  | codecInstL16.plfreq   = 16000; | 
|  | codecInstL16.pacsize  = 160; | 
|  |  | 
|  | }else if (_fileFormat == kFileFormatPcm32kHzFile) | 
|  | { | 
|  | codecInstL16.rate     = 512000; | 
|  | codecInstL16.plfreq   = 32000; | 
|  | codecInstL16.pacsize  = 160; | 
|  | }else | 
|  | { | 
|  | LOG(LS_ERROR) << "StartPlayingFile() sample frequency not " | 
|  | << "supported for PCM format."; | 
|  | return -1; | 
|  | } | 
|  | if (_fileModule.StartPlayingAudioStream(sourceStream, notification, | 
|  | _fileFormat, &codecInstL16, | 
|  | startPosition, | 
|  | stopPosition) == -1) | 
|  | { | 
|  | LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " | 
|  | << "playout."; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | }else if(_fileFormat == kFileFormatPreencodedFile) | 
|  | { | 
|  | if (_fileModule.StartPlayingAudioStream(sourceStream, notification, | 
|  | _fileFormat, codecInst) == -1) | 
|  | { | 
|  | LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " | 
|  | << "playout."; | 
|  | return -1; | 
|  | } | 
|  | } else { | 
|  | CodecInst* no_inst = NULL; | 
|  | if (_fileModule.StartPlayingAudioStream(sourceStream, notification, | 
|  | _fileFormat, no_inst, | 
|  | startPosition, | 
|  | stopPosition) == -1) | 
|  | { | 
|  | LOG(LS_ERROR) << "StartPlayingFile() failed to initialize stream " | 
|  | << "playout."; | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | SetAudioScaling(volumeScaling); | 
|  |  | 
|  | if (SetUpAudioDecoder() == -1) | 
|  | { | 
|  | StopPlayingFile(); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::StopPlayingFile() | 
|  | { | 
|  | memset(&_codec, 0, sizeof(CodecInst)); | 
|  | _numberOf10MsPerFrame  = 0; | 
|  | _numberOf10MsInDecoder = 0; | 
|  | return _fileModule.StopPlaying(); | 
|  | } | 
|  |  | 
|  | bool FilePlayerImpl::IsPlayingFile() const | 
|  | { | 
|  | return _fileModule.IsPlaying(); | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::GetPlayoutPosition(uint32_t& durationMs) | 
|  | { | 
|  | return _fileModule.PlayoutPositionMs(durationMs); | 
|  | } | 
|  |  | 
|  | int32_t FilePlayerImpl::SetUpAudioDecoder() | 
|  | { | 
|  | if ((_fileModule.codec_info(_codec) == -1)) | 
|  | { | 
|  | LOG(LS_WARNING) << "Failed to retrieve codec info of file data."; | 
|  | return -1; | 
|  | } | 
|  | if( STR_CASE_CMP(_codec.plname, "L16") != 0 && | 
|  | _audioDecoder.SetDecodeCodec(_codec) == -1) | 
|  | { | 
|  | LOG(LS_WARNING) << "SetUpAudioDecoder() codec " << _codec.plname | 
|  | << " not supported."; | 
|  | return -1; | 
|  | } | 
|  | _numberOf10MsPerFrame = _codec.pacsize / (_codec.plfreq / 100); | 
|  | _numberOf10MsInDecoder = 0; | 
|  | return 0; | 
|  | } | 
|  | }  // namespace webrtc |