/* * Copyright (c) 2011 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 "file_player_impl.h" #include "trace.h" #ifdef WEBRTC_MODULE_UTILITY_VIDEO #include "cpu_wrapper.h" #include "frame_scaler.h" #include "tick_util.h" #include "video_coder.h" #endif // OS independent case insensitive string comparison. #ifdef WIN32 #define STR_CASE_CMP(x,y) ::_stricmp(x,y) #else #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) #endif namespace webrtc { FilePlayer* FilePlayer::CreateFilePlayer(WebRtc_UWord32 instanceID, FileFormats fileFormat) { switch(fileFormat) { case kFileFormatWavFile: case kFileFormatCompressedFile: case kFileFormatPreencodedFile: case kFileFormatPcm16kHzFile: case kFileFormatPcm8kHzFile: case kFileFormatPcm32kHzFile: // audio formats return new FilePlayerImpl(instanceID, fileFormat); #ifdef WEBRTC_MODULE_UTILITY_VIDEO case kFileFormatAviFile: return new VideoFilePlayerImpl(instanceID, fileFormat); #endif default: return NULL; } } void FilePlayer::DestroyFilePlayer(FilePlayer* player) { delete player; } FilePlayerImpl::FilePlayerImpl(const WebRtc_UWord32 instanceID, const FileFormats fileFormat) : _instanceID(instanceID), _fileFormat(fileFormat), _fileModule(*MediaFile::CreateMediaFile(instanceID)), _decodedLengthInMS(0), _audioDecoder(instanceID), _codec(), _numberOf10MsPerFrame(0), _numberOf10MsInDecoder(0), _scaling(1.0) { _codec.plfreq = 0; } FilePlayerImpl::~FilePlayerImpl() { MediaFile::DestroyMediaFile(&_fileModule); } WebRtc_Word32 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; } } WebRtc_Word32 FilePlayerImpl::AudioCodec(CodecInst& audioCodec) const { audioCodec = _codec; return 0; } WebRtc_Word32 FilePlayerImpl::Get10msAudioFromFile( WebRtc_Word16* outBuffer, WebRtc_UWord32& lengthInSamples, WebRtc_UWord32 frequencyInHz) { if(_codec.plfreq == 0) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::Get10msAudioFromFile() playing not started!\ codecFreq = %d, wantedFreq = %d", _codec.plfreq, frequencyInHz); return -1; } AudioFrame unresampledAudioFrame; if(STR_CASE_CMP(_codec.plname, "L16") == 0) { unresampledAudioFrame._frequencyInHz = _codec.plfreq; // L16 is un-encoded data. Just pull 10 ms. WebRtc_UWord32 lengthInBytes = sizeof(unresampledAudioFrame._payloadData); if (_fileModule.PlayoutAudioData( (WebRtc_Word8*)unresampledAudioFrame._payloadData, lengthInBytes) == -1) { // End of file reached. return -1; } if(lengthInBytes == 0) { lengthInSamples = 0; return 0; } // One sample is two bytes. unresampledAudioFrame._payloadDataLengthInSamples = (WebRtc_UWord16)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. WebRtc_Word16 encodedBuffer[MAX_AUDIO_BUFFER_IN_SAMPLES]; WebRtc_UWord32 encodedLengthInBytes = 0; if(++_numberOf10MsInDecoder >= _numberOf10MsPerFrame) { _numberOf10MsInDecoder = 0; WebRtc_UWord32 bytesFromFile = sizeof(encodedBuffer); if (_fileModule.PlayoutAudioData((WebRtc_Word8*)encodedBuffer, bytesFromFile) == -1) { // End of file reached. return -1; } encodedLengthInBytes = bytesFromFile; } if(_audioDecoder.Decode(unresampledAudioFrame,frequencyInHz, (WebRtc_Word8*)encodedBuffer, encodedLengthInBytes) == -1) { return -1; } } int outLen = 0; if(_resampler.ResetIfNeeded(unresampledAudioFrame._frequencyInHz, frequencyInHz, kResamplerSynchronous)) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::Get10msAudioFromFile() unexpected codec"); // New sampling frequency. Update state. outLen = frequencyInHz / 100; memset(outBuffer, 0, outLen * sizeof(WebRtc_Word16)); return 0; } _resampler.Push(unresampledAudioFrame._payloadData, unresampledAudioFrame._payloadDataLengthInSamples, outBuffer, MAX_AUDIO_BUFFER_IN_SAMPLES, outLen); lengthInSamples = outLen; if(_scaling != 1.0) { for (int i = 0;i < outLen; i++) { outBuffer[i] = (WebRtc_Word16)(outBuffer[i] * _scaling); } } _decodedLengthInMS += 10; return 0; } WebRtc_Word32 FilePlayerImpl::RegisterModuleFileCallback(FileCallback* callback) { return _fileModule.SetModuleFileCallback(callback); } WebRtc_Word32 FilePlayerImpl::SetAudioScaling(float scaleFactor) { if((scaleFactor >= 0)&&(scaleFactor <= 2.0)) { _scaling = scaleFactor; return 0; } WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::SetAudioScaling() not allowed scale factor"); return -1; } WebRtc_Word32 FilePlayerImpl::StartPlayingFile(const WebRtc_Word8* fileName, bool loop, WebRtc_UWord32 startPosition, float volumeScaling, WebRtc_UWord32 notification, WebRtc_UWord32 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 { WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() sample frequency\ specifed not supported for PCM format."); return -1; } if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, _fileFormat, &codecInstL16, startPosition, stopPosition) == -1) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to initialize file\ %s playout.", fileName); return -1; } SetAudioScaling(volumeScaling); }else if(_fileFormat == kFileFormatPreencodedFile) { if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, _fileFormat, codecInst) == -1) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingPreEncodedFile() failed to\ initialize pre-encoded file %s playout.", fileName); return -1; } } else { CodecInst* no_inst = NULL; if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, _fileFormat, no_inst, startPosition, stopPosition) == -1) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to initialize file\ %s playout.", fileName); return -1; } SetAudioScaling(volumeScaling); } if (SetUpAudioDecoder() == -1) { StopPlayingFile(); return -1; } return 0; } WebRtc_Word32 FilePlayerImpl::StartPlayingFile(InStream& sourceStream, WebRtc_UWord32 startPosition, float volumeScaling, WebRtc_UWord32 notification, WebRtc_UWord32 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 { WEBRTC_TRACE( kTraceError, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() sample frequency specifed\ not supported for PCM format."); return -1; } if (_fileModule.StartPlayingAudioStream(sourceStream, notification, _fileFormat, &codecInstL16, startPosition, stopPosition) == -1) { WEBRTC_TRACE( kTraceError, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ playout."); return -1; } }else if(_fileFormat == kFileFormatPreencodedFile) { if (_fileModule.StartPlayingAudioStream(sourceStream, notification, _fileFormat, codecInst) == -1) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ playout."); return -1; } } else { CodecInst* no_inst = NULL; if (_fileModule.StartPlayingAudioStream(sourceStream, notification, _fileFormat, no_inst, startPosition, stopPosition) == -1) { WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to initialize\ stream playout."); return -1; } } SetAudioScaling(volumeScaling); if (SetUpAudioDecoder() == -1) { StopPlayingFile(); return -1; } return 0; } WebRtc_Word32 FilePlayerImpl::StopPlayingFile() { memset(&_codec, 0, sizeof(CodecInst)); _numberOf10MsPerFrame = 0; _numberOf10MsInDecoder = 0; return _fileModule.StopPlaying(); } bool FilePlayerImpl::IsPlayingFile() const { return _fileModule.IsPlaying(); } WebRtc_Word32 FilePlayerImpl::GetPlayoutPosition(WebRtc_UWord32& durationMs) { return _fileModule.PlayoutPositionMs(durationMs); } WebRtc_Word32 FilePlayerImpl::SetUpAudioDecoder() { if ((_fileModule.codec_info(_codec) == -1)) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() failed to retrieve Codec info\ of file data."); return -1; } if( STR_CASE_CMP(_codec.plname, "L16") != 0 && _audioDecoder.SetDecodeCodec(_codec,AMRFileStorage) == -1) { WEBRTC_TRACE( kTraceWarning, kTraceVoice, _instanceID, "FilePlayerImpl::StartPlayingFile() codec %s not supported", _codec.plname); return -1; } _numberOf10MsPerFrame = _codec.pacsize / (_codec.plfreq / 100); _numberOf10MsInDecoder = 0; return 0; } #ifdef WEBRTC_MODULE_UTILITY_VIDEO VideoFilePlayerImpl::VideoFilePlayerImpl(WebRtc_UWord32 instanceID, FileFormats fileFormat) : FilePlayerImpl(instanceID,fileFormat), _videoDecoder(*new VideoCoder(instanceID)), _decodedVideoFrames(0), _encodedData(*new EncodedVideoData()), _frameScaler(*new FrameScaler()), _critSec(*CriticalSectionWrapper::CreateCriticalSection()), _accumulatedRenderTimeMs(0), _numberOfFramesRead(0), _videoOnly(false) { memset(&video_codec_info_, 0, sizeof(video_codec_info_)); } VideoFilePlayerImpl::~VideoFilePlayerImpl() { delete &_critSec; delete &_frameScaler; delete &_videoDecoder; delete &_encodedData; } WebRtc_Word32 VideoFilePlayerImpl::StartPlayingVideoFile( const WebRtc_Word8* fileName, bool loop, bool videoOnly) { CriticalSectionScoped lock( _critSec); if(_fileModule.StartPlayingVideoFile(fileName, loop, videoOnly, _fileFormat) != 0) { return -1; } _decodedVideoFrames = 0; _accumulatedRenderTimeMs = 0; _frameLengthMS = 0; _numberOfFramesRead = 0; _videoOnly = videoOnly; // Set up video_codec_info_ according to file, if(SetUpVideoDecoder() != 0) { StopPlayingFile(); return -1; } if(!videoOnly) { // Set up _codec according to file, if(SetUpAudioDecoder() != 0) { StopPlayingFile(); return -1; } } return 0; } WebRtc_Word32 VideoFilePlayerImpl::StopPlayingFile() { CriticalSectionScoped lock( _critSec); _decodedVideoFrames = 0; _videoDecoder.Reset(); return FilePlayerImpl::StopPlayingFile(); } WebRtc_Word32 VideoFilePlayerImpl::GetVideoFromFile(VideoFrame& videoFrame, WebRtc_UWord32 outWidth, WebRtc_UWord32 outHeight) { CriticalSectionScoped lock( _critSec); WebRtc_Word32 retVal = GetVideoFromFile(videoFrame); if(retVal != 0) { return retVal; } if( videoFrame.Length() > 0) { retVal = _frameScaler.ResizeFrameIfNeeded(videoFrame, outWidth, outHeight); } return retVal; } WebRtc_Word32 VideoFilePlayerImpl::GetVideoFromFile(VideoFrame& videoFrame) { CriticalSectionScoped lock( _critSec); // No new video data read from file. if(_encodedData.payloadSize == 0) { videoFrame.SetLength(0); return -1; } WebRtc_Word32 retVal = 0; if(strncmp(video_codec_info_.plName, "I420", 5) == 0) { videoFrame.CopyFrame(_encodedData.payloadSize,_encodedData.payloadData); videoFrame.SetLength(_encodedData.payloadSize); videoFrame.SetWidth(video_codec_info_.width); videoFrame.SetHeight(video_codec_info_.height); }else { // Set the timestamp manually since there is no timestamp in the file. // Update timestam according to 90 kHz stream. _encodedData.timeStamp += (90000 / video_codec_info_.maxFramerate); retVal = _videoDecoder.Decode(videoFrame, _encodedData); } WebRtc_Word64 renderTimeMs = TickTime::MillisecondTimestamp(); videoFrame.SetRenderTime(renderTimeMs); // Indicate that the current frame in the encoded buffer is old/has // already been read. _encodedData.payloadSize = 0; if( retVal == 0) { _decodedVideoFrames++; } return retVal; } WebRtc_Word32 VideoFilePlayerImpl::video_codec_info( VideoCodec& videoCodec) const { if(video_codec_info_.plName[0] == 0) { return -1; } memcpy(&videoCodec, &video_codec_info_, sizeof(VideoCodec)); return 0; } WebRtc_Word32 VideoFilePlayerImpl::TimeUntilNextVideoFrame() { if(_fileFormat != kFileFormatAviFile) { return -1; } if(!_fileModule.IsPlaying()) { return -1; } if(_encodedData.payloadSize <= 0) { // Read next frame from file. CriticalSectionScoped lock( _critSec); if(_fileFormat == kFileFormatAviFile) { // Get next video frame WebRtc_UWord32 encodedBufferLengthInBytes = _encodedData.bufferSize; if(_fileModule.PlayoutAVIVideoData( reinterpret_cast< WebRtc_Word8*>(_encodedData.payloadData), encodedBufferLengthInBytes) != 0) { WEBRTC_TRACE( kTraceWarning, kTraceVideo, _instanceID, "FilePlayerImpl::TimeUntilNextVideoFrame() error reading\ video data"); return -1; } _encodedData.payloadSize = encodedBufferLengthInBytes; _encodedData.codec = video_codec_info_.codecType; _numberOfFramesRead++; if(_accumulatedRenderTimeMs == 0) { _startTime = TickTime::Now(); // This if-statement should only trigger once. _accumulatedRenderTimeMs = 1; } else { // A full seconds worth of frames have been read. if(_numberOfFramesRead % video_codec_info_.maxFramerate == 0) { // Frame rate is in frames per seconds. Frame length is // calculated as an integer division which means it may // be rounded down. Compensate for this every second. WebRtc_UWord32 rest = 1000%_frameLengthMS; _accumulatedRenderTimeMs += rest; } _accumulatedRenderTimeMs += _frameLengthMS; } } } WebRtc_Word64 timeToNextFrame; if(_videoOnly) { timeToNextFrame = _accumulatedRenderTimeMs - (TickTime::Now() - _startTime).Milliseconds(); } else { // Synchronize with the audio stream instead of system clock. timeToNextFrame = _accumulatedRenderTimeMs - _decodedLengthInMS; } if(timeToNextFrame < 0) { return 0; } else if(timeToNextFrame > 0x0fffffff) { // Wraparound or audio stream has gone to far ahead of the video stream. return -1; } return static_cast(timeToNextFrame); } WebRtc_Word32 VideoFilePlayerImpl::SetUpVideoDecoder() { if (_fileModule.VideoCodecInst(video_codec_info_) != 0) { WEBRTC_TRACE( kTraceWarning, kTraceVideo, _instanceID, "FilePlayerImpl::SetVideoDecoder() failed to retrieve Codec info of\ file data."); return -1; } WebRtc_Word32 useNumberOfCores = 1; if(_videoDecoder.SetDecodeCodec(video_codec_info_, useNumberOfCores) != 0) { WEBRTC_TRACE( kTraceWarning, kTraceVideo, _instanceID, "FilePlayerImpl::SetUpVideoDecoder() codec %s not supported", video_codec_info_.plName); return -1; } if(strncmp(video_codec_info_.plName, "MP4V-ES", 8) == 0) { if(_videoDecoder.SetCodecConfigParameters( video_codec_info_.plType, video_codec_info_.codecSpecific.MPEG4.configParameters, video_codec_info_.codecSpecific.MPEG4.configParametersSize) != 0) { return -1; } } _frameLengthMS = 1000/video_codec_info_.maxFramerate; // Size of unencoded data (I420) should be the largest possible frame size // in a file. const WebRtc_UWord32 KReadBufferSize = 3 * video_codec_info_.width * video_codec_info_.height / 2; _encodedData.VerifyAndAllocate(KReadBufferSize); _encodedData.encodedHeight = video_codec_info_.height; _encodedData.encodedWidth = video_codec_info_.width; _encodedData.payloadType = video_codec_info_.plType; _encodedData.timeStamp = 0; return 0; } #endif // WEBRTC_MODULE_UTILITY_VIDEO } // namespace webrtc