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 {
CreateFileRecorder(uint32_t instanceID,FileFormats fileFormat)18 FileRecorder* FileRecorder::CreateFileRecorder(uint32_t instanceID,
19                                                FileFormats fileFormat)
20 {
21     return new FileRecorderImpl(instanceID, fileFormat);
22 }
23 
DestroyFileRecorder(FileRecorder * recorder)24 void FileRecorder::DestroyFileRecorder(FileRecorder* recorder)
25 {
26     delete recorder;
27 }
28 
FileRecorderImpl(uint32_t instanceID,FileFormats fileFormat)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 
~FileRecorderImpl()41 FileRecorderImpl::~FileRecorderImpl()
42 {
43     MediaFile::DestroyMediaFile(_moduleFile);
44 }
45 
RecordingFileFormat() const46 FileFormats FileRecorderImpl::RecordingFileFormat() const
47 {
48     return _fileFormat;
49 }
50 
RegisterModuleFileCallback(FileCallback * callback)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 
StartRecordingAudioFile(const char * fileName,const CodecInst & codecInst,uint32_t notificationTimeMs)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 
StartRecordingAudioFile(OutStream & destStream,const CodecInst & codecInst,uint32_t notificationTimeMs)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 
StopRecording()121 int32_t FileRecorderImpl::StopRecording()
122 {
123     memset(&codec_info_, 0, sizeof(CodecInst));
124     return _moduleFile->StopRecording();
125 }
126 
IsRecording() const127 bool FileRecorderImpl::IsRecording() const
128 {
129     return _moduleFile->IsRecording();
130 }
131 
RecordAudioToFile(const AudioFrame & incomingAudioFrame,const TickTime * playoutTS)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 
SetUpAudioEncoder()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 
codec_info(CodecInst & codecInst) const246 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 
WriteEncodedAudioData(const int8_t * audioBuffer,size_t bufferLength)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