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