1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 12 #include "webrtc/engine_configurations.h" 13 #include "webrtc/modules/media_file/media_file.h" 14 #include "webrtc/modules/utility/source/file_recorder_impl.h" 15 #include "webrtc/system_wrappers/include/logging.h" 16 17 namespace webrtc { 18 FileRecorder* FileRecorder::CreateFileRecorder(uint32_t instanceID, 19 FileFormats fileFormat) 20 { 21 return new FileRecorderImpl(instanceID, fileFormat); 22 } 23 24 void FileRecorder::DestroyFileRecorder(FileRecorder* recorder) 25 { 26 delete recorder; 27 } 28 29 FileRecorderImpl::FileRecorderImpl(uint32_t instanceID, 30 FileFormats fileFormat) 31 : _instanceID(instanceID), 32 _fileFormat(fileFormat), 33 _moduleFile(MediaFile::CreateMediaFile(_instanceID)), 34 codec_info_(), 35 _audioBuffer(), 36 _audioEncoder(instanceID), 37 _audioResampler() 38 { 39 } 40 41 FileRecorderImpl::~FileRecorderImpl() 42 { 43 MediaFile::DestroyMediaFile(_moduleFile); 44 } 45 46 FileFormats FileRecorderImpl::RecordingFileFormat() const 47 { 48 return _fileFormat; 49 } 50 51 int32_t FileRecorderImpl::RegisterModuleFileCallback( 52 FileCallback* callback) 53 { 54 if(_moduleFile == NULL) 55 { 56 return -1; 57 } 58 return _moduleFile->SetModuleFileCallback(callback); 59 } 60 61 int32_t FileRecorderImpl::StartRecordingAudioFile( 62 const char* fileName, 63 const CodecInst& codecInst, 64 uint32_t notificationTimeMs) 65 { 66 if(_moduleFile == NULL) 67 { 68 return -1; 69 } 70 codec_info_ = codecInst; 71 int32_t retVal = 0; 72 retVal =_moduleFile->StartRecordingAudioFile(fileName, _fileFormat, 73 codecInst, 74 notificationTimeMs); 75 76 if( retVal == 0) 77 { 78 retVal = SetUpAudioEncoder(); 79 } 80 if( retVal != 0) 81 { 82 LOG(LS_WARNING) << "Failed to initialize file " << fileName 83 << " for recording."; 84 85 if(IsRecording()) 86 { 87 StopRecording(); 88 } 89 } 90 return retVal; 91 } 92 93 int32_t FileRecorderImpl::StartRecordingAudioFile( 94 OutStream& destStream, 95 const CodecInst& codecInst, 96 uint32_t notificationTimeMs) 97 { 98 codec_info_ = codecInst; 99 int32_t retVal = _moduleFile->StartRecordingAudioStream( 100 destStream, 101 _fileFormat, 102 codecInst, 103 notificationTimeMs); 104 105 if( retVal == 0) 106 { 107 retVal = SetUpAudioEncoder(); 108 } 109 if( retVal != 0) 110 { 111 LOG(LS_WARNING) << "Failed to initialize outStream for recording."; 112 113 if(IsRecording()) 114 { 115 StopRecording(); 116 } 117 } 118 return retVal; 119 } 120 121 int32_t FileRecorderImpl::StopRecording() 122 { 123 memset(&codec_info_, 0, sizeof(CodecInst)); 124 return _moduleFile->StopRecording(); 125 } 126 127 bool FileRecorderImpl::IsRecording() const 128 { 129 return _moduleFile->IsRecording(); 130 } 131 132 int32_t FileRecorderImpl::RecordAudioToFile( 133 const AudioFrame& incomingAudioFrame, 134 const TickTime* playoutTS) 135 { 136 if (codec_info_.plfreq == 0) 137 { 138 LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not " 139 << "turned on."; 140 return -1; 141 } 142 AudioFrame tempAudioFrame; 143 tempAudioFrame.samples_per_channel_ = 0; 144 if( incomingAudioFrame.num_channels_ == 2 && 145 !_moduleFile->IsStereo()) 146 { 147 // Recording mono but incoming audio is (interleaved) stereo. 148 tempAudioFrame.num_channels_ = 1; 149 tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; 150 tempAudioFrame.samples_per_channel_ = 151 incomingAudioFrame.samples_per_channel_; 152 for (size_t i = 0; 153 i < (incomingAudioFrame.samples_per_channel_); i++) 154 { 155 // Sample value is the average of left and right buffer rounded to 156 // closest integer value. Note samples can be either 1 or 2 byte. 157 tempAudioFrame.data_[i] = 158 ((incomingAudioFrame.data_[2 * i] + 159 incomingAudioFrame.data_[(2 * i) + 1] + 1) >> 1); 160 } 161 } 162 else if( incomingAudioFrame.num_channels_ == 1 && 163 _moduleFile->IsStereo()) 164 { 165 // Recording stereo but incoming audio is mono. 166 tempAudioFrame.num_channels_ = 2; 167 tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_; 168 tempAudioFrame.samples_per_channel_ = 169 incomingAudioFrame.samples_per_channel_; 170 for (size_t i = 0; 171 i < (incomingAudioFrame.samples_per_channel_); i++) 172 { 173 // Duplicate sample to both channels 174 tempAudioFrame.data_[2*i] = 175 incomingAudioFrame.data_[i]; 176 tempAudioFrame.data_[2*i+1] = 177 incomingAudioFrame.data_[i]; 178 } 179 } 180 181 const AudioFrame* ptrAudioFrame = &incomingAudioFrame; 182 if(tempAudioFrame.samples_per_channel_ != 0) 183 { 184 // If ptrAudioFrame is not empty it contains the audio to be recorded. 185 ptrAudioFrame = &tempAudioFrame; 186 } 187 188 // Encode the audio data before writing to file. Don't encode if the codec 189 // is PCM. 190 // NOTE: stereo recording is only supported for WAV files. 191 // TODO (hellner): WAV expect PCM in little endian byte order. Not 192 // "encoding" with PCM coder should be a problem for big endian systems. 193 size_t encodedLenInBytes = 0; 194 if (_fileFormat == kFileFormatPreencodedFile || 195 STR_CASE_CMP(codec_info_.plname, "L16") != 0) 196 { 197 if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer, 198 encodedLenInBytes) == -1) 199 { 200 LOG(LS_WARNING) << "RecordAudioToFile() codec " 201 << codec_info_.plname 202 << " not supported or failed to encode stream."; 203 return -1; 204 } 205 } else { 206 size_t outLen = 0; 207 _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_, 208 codec_info_.plfreq, 209 ptrAudioFrame->num_channels_); 210 _audioResampler.Push(ptrAudioFrame->data_, 211 ptrAudioFrame->samples_per_channel_ * 212 ptrAudioFrame->num_channels_, 213 (int16_t*)_audioBuffer, 214 MAX_AUDIO_BUFFER_IN_BYTES, outLen); 215 encodedLenInBytes = outLen * sizeof(int16_t); 216 } 217 218 // Codec may not be operating at a frame rate of 10 ms. Whenever enough 219 // 10 ms chunks of data has been pushed to the encoder an encoded frame 220 // will be available. Wait until then. 221 if (encodedLenInBytes) 222 { 223 if (WriteEncodedAudioData(_audioBuffer, encodedLenInBytes) == -1) 224 { 225 return -1; 226 } 227 } 228 return 0; 229 } 230 231 int32_t FileRecorderImpl::SetUpAudioEncoder() 232 { 233 if (_fileFormat == kFileFormatPreencodedFile || 234 STR_CASE_CMP(codec_info_.plname, "L16") != 0) 235 { 236 if(_audioEncoder.SetEncodeCodec(codec_info_) == -1) 237 { 238 LOG(LS_ERROR) << "SetUpAudioEncoder() codec " 239 << codec_info_.plname << " not supported."; 240 return -1; 241 } 242 } 243 return 0; 244 } 245 246 int32_t FileRecorderImpl::codec_info(CodecInst& codecInst) const 247 { 248 if(codec_info_.plfreq == 0) 249 { 250 return -1; 251 } 252 codecInst = codec_info_; 253 return 0; 254 } 255 256 int32_t FileRecorderImpl::WriteEncodedAudioData(const int8_t* audioBuffer, 257 size_t bufferLength) 258 { 259 return _moduleFile->IncomingAudioData(audioBuffer, bufferLength); 260 } 261 } // namespace webrtc 262