| /* |
| * 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/common_video/libyuv/include/webrtc_libyuv.h" |
| #include "webrtc/engine_configurations.h" |
| #include "webrtc/modules/media_file/media_file.h" |
| #include "webrtc/modules/utility/source/file_recorder_impl.h" |
| #include "webrtc/system_wrappers/include/logging.h" |
| |
| namespace webrtc { |
| FileRecorder* FileRecorder::CreateFileRecorder(uint32_t instanceID, |
| FileFormats fileFormat) |
| { |
| return new FileRecorderImpl(instanceID, fileFormat); |
| } |
| |
| void FileRecorder::DestroyFileRecorder(FileRecorder* recorder) |
| { |
| delete recorder; |
| } |
| |
| FileRecorderImpl::FileRecorderImpl(uint32_t instanceID, |
| FileFormats fileFormat) |
| : _instanceID(instanceID), |
| _fileFormat(fileFormat), |
| _moduleFile(MediaFile::CreateMediaFile(_instanceID)), |
| codec_info_(), |
| _audioBuffer(), |
| _audioEncoder(instanceID), |
| _audioResampler() |
| { |
| } |
| |
| FileRecorderImpl::~FileRecorderImpl() |
| { |
| MediaFile::DestroyMediaFile(_moduleFile); |
| } |
| |
| FileFormats FileRecorderImpl::RecordingFileFormat() const |
| { |
| return _fileFormat; |
| } |
| |
| int32_t FileRecorderImpl::RegisterModuleFileCallback( |
| FileCallback* callback) |
| { |
| if(_moduleFile == NULL) |
| { |
| return -1; |
| } |
| return _moduleFile->SetModuleFileCallback(callback); |
| } |
| |
| int32_t FileRecorderImpl::StartRecordingAudioFile( |
| const char* fileName, |
| const CodecInst& codecInst, |
| uint32_t notificationTimeMs) |
| { |
| if(_moduleFile == NULL) |
| { |
| return -1; |
| } |
| codec_info_ = codecInst; |
| int32_t retVal = 0; |
| retVal =_moduleFile->StartRecordingAudioFile(fileName, _fileFormat, |
| codecInst, |
| notificationTimeMs); |
| |
| if( retVal == 0) |
| { |
| retVal = SetUpAudioEncoder(); |
| } |
| if( retVal != 0) |
| { |
| LOG(LS_WARNING) << "Failed to initialize file " << fileName |
| << " for recording."; |
| |
| if(IsRecording()) |
| { |
| StopRecording(); |
| } |
| } |
| return retVal; |
| } |
| |
| int32_t FileRecorderImpl::StartRecordingAudioFile( |
| OutStream& destStream, |
| const CodecInst& codecInst, |
| uint32_t notificationTimeMs) |
| { |
| codec_info_ = codecInst; |
| int32_t retVal = _moduleFile->StartRecordingAudioStream( |
| destStream, |
| _fileFormat, |
| codecInst, |
| notificationTimeMs); |
| |
| if( retVal == 0) |
| { |
| retVal = SetUpAudioEncoder(); |
| } |
| if( retVal != 0) |
| { |
| LOG(LS_WARNING) << "Failed to initialize outStream for recording."; |
| |
| if(IsRecording()) |
| { |
| StopRecording(); |
| } |
| } |
| return retVal; |
| } |
| |
| int32_t FileRecorderImpl::StopRecording() |
| { |
| memset(&codec_info_, 0, sizeof(CodecInst)); |
| return _moduleFile->StopRecording(); |
| } |
| |
| bool FileRecorderImpl::IsRecording() const |
| { |
| return _moduleFile->IsRecording(); |
| } |
| |
| int32_t FileRecorderImpl::RecordAudioToFile( |
| const AudioFrame& incomingAudioFrame, |
| const TickTime* playoutTS) |
| { |
| if (codec_info_.plfreq == 0) |
| { |
| LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not " |
| << "turned on."; |
| return -1; |
| } |
| AudioFrame tempAudioFrame; |
| tempAudioFrame.samples_per_channel_ = 0; |
| if( incomingAudioFrame.num_channels_ == 2 && |
| !_moduleFile->IsStereo()) |
| { |
| // Recording mono but incoming audio is (interleaved) stereo. |
| tempAudioFrame.num_channels_ = 1; |
| tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; |
| tempAudioFrame.samples_per_channel_ = |
| incomingAudioFrame.samples_per_channel_; |
| for (size_t i = 0; |
| i < (incomingAudioFrame.samples_per_channel_); i++) |
| { |
| // Sample value is the average of left and right buffer rounded to |
| // closest integer value. Note samples can be either 1 or 2 byte. |
| tempAudioFrame.data_[i] = |
| ((incomingAudioFrame.data_[2 * i] + |
| incomingAudioFrame.data_[(2 * i) + 1] + 1) >> 1); |
| } |
| } |
| else if( incomingAudioFrame.num_channels_ == 1 && |
| _moduleFile->IsStereo()) |
| { |
| // Recording stereo but incoming audio is mono. |
| tempAudioFrame.num_channels_ = 2; |
| tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; |
| tempAudioFrame.samples_per_channel_ = |
| incomingAudioFrame.samples_per_channel_; |
| for (size_t i = 0; |
| i < (incomingAudioFrame.samples_per_channel_); i++) |
| { |
| // Duplicate sample to both channels |
| tempAudioFrame.data_[2*i] = |
| incomingAudioFrame.data_[i]; |
| tempAudioFrame.data_[2*i+1] = |
| incomingAudioFrame.data_[i]; |
| } |
| } |
| |
| const AudioFrame* ptrAudioFrame = &incomingAudioFrame; |
| if(tempAudioFrame.samples_per_channel_ != 0) |
| { |
| // If ptrAudioFrame is not empty it contains the audio to be recorded. |
| ptrAudioFrame = &tempAudioFrame; |
| } |
| |
| // Encode the audio data before writing to file. Don't encode if the codec |
| // is PCM. |
| // NOTE: stereo recording is only supported for WAV files. |
| // TODO (hellner): WAV expect PCM in little endian byte order. Not |
| // "encoding" with PCM coder should be a problem for big endian systems. |
| size_t encodedLenInBytes = 0; |
| if (_fileFormat == kFileFormatPreencodedFile || |
| STR_CASE_CMP(codec_info_.plname, "L16") != 0) |
| { |
| if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer, |
| encodedLenInBytes) == -1) |
| { |
| LOG(LS_WARNING) << "RecordAudioToFile() codec " |
| << codec_info_.plname |
| << " not supported or failed to encode stream."; |
| return -1; |
| } |
| } else { |
| size_t outLen = 0; |
| _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_, |
| codec_info_.plfreq, |
| ptrAudioFrame->num_channels_); |
| _audioResampler.Push(ptrAudioFrame->data_, |
| ptrAudioFrame->samples_per_channel_ * |
| ptrAudioFrame->num_channels_, |
| (int16_t*)_audioBuffer, |
| MAX_AUDIO_BUFFER_IN_BYTES, outLen); |
| encodedLenInBytes = outLen * sizeof(int16_t); |
| } |
| |
| // Codec may not be operating at a frame rate of 10 ms. Whenever enough |
| // 10 ms chunks of data has been pushed to the encoder an encoded frame |
| // will be available. Wait until then. |
| if (encodedLenInBytes) |
| { |
| if (WriteEncodedAudioData(_audioBuffer, encodedLenInBytes) == -1) |
| { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int32_t FileRecorderImpl::SetUpAudioEncoder() |
| { |
| if (_fileFormat == kFileFormatPreencodedFile || |
| STR_CASE_CMP(codec_info_.plname, "L16") != 0) |
| { |
| if(_audioEncoder.SetEncodeCodec(codec_info_) == -1) |
| { |
| LOG(LS_ERROR) << "SetUpAudioEncoder() codec " |
| << codec_info_.plname << " not supported."; |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int32_t FileRecorderImpl::codec_info(CodecInst& codecInst) const |
| { |
| if(codec_info_.plfreq == 0) |
| { |
| return -1; |
| } |
| codecInst = codec_info_; |
| return 0; |
| } |
| |
| int32_t FileRecorderImpl::WriteEncodedAudioData(const int8_t* audioBuffer, |
| size_t bufferLength) |
| { |
| return _moduleFile->IncomingAudioData(audioBuffer, bufferLength); |
| } |
| } // namespace webrtc |