Line data Source code
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/voice_engine/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/system_wrappers/include/event_wrapper.h"
22 : #include "webrtc/system_wrappers/include/logging.h"
23 : #include "webrtc/typedefs.h"
24 : #include "webrtc/voice_engine/coder.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 0 : FileRecorderImpl::FileRecorderImpl(uint32_t instanceID, FileFormats fileFormat)
72 : : _instanceID(instanceID),
73 : _fileFormat(fileFormat),
74 0 : _moduleFile(MediaFile::CreateMediaFile(_instanceID)),
75 : codec_info_(),
76 : _audioBuffer(),
77 : _audioEncoder(instanceID),
78 0 : _audioResampler() {}
79 :
80 0 : FileRecorderImpl::~FileRecorderImpl() {
81 0 : MediaFile::DestroyMediaFile(_moduleFile);
82 0 : }
83 :
84 0 : FileFormats FileRecorderImpl::RecordingFileFormat() const {
85 0 : return _fileFormat;
86 : }
87 :
88 0 : int32_t FileRecorderImpl::RegisterModuleFileCallback(FileCallback* callback) {
89 0 : if (_moduleFile == NULL) {
90 0 : return -1;
91 : }
92 0 : return _moduleFile->SetModuleFileCallback(callback);
93 : }
94 :
95 0 : int32_t FileRecorderImpl::StartRecordingAudioFile(const char* fileName,
96 : const CodecInst& codecInst,
97 : uint32_t notificationTimeMs) {
98 0 : if (_moduleFile == NULL) {
99 0 : return -1;
100 : }
101 0 : codec_info_ = codecInst;
102 0 : int32_t retVal = 0;
103 0 : retVal = _moduleFile->StartRecordingAudioFile(fileName, _fileFormat,
104 0 : codecInst, notificationTimeMs);
105 :
106 0 : if (retVal == 0) {
107 0 : retVal = SetUpAudioEncoder();
108 : }
109 0 : if (retVal != 0) {
110 0 : LOG(LS_WARNING) << "Failed to initialize file " << fileName
111 0 : << " for recording.";
112 :
113 0 : if (IsRecording()) {
114 0 : StopRecording();
115 : }
116 : }
117 0 : return retVal;
118 : }
119 :
120 0 : int32_t FileRecorderImpl::StartRecordingAudioFile(OutStream* destStream,
121 : const CodecInst& codecInst,
122 : uint32_t notificationTimeMs) {
123 0 : codec_info_ = codecInst;
124 0 : int32_t retVal = _moduleFile->StartRecordingAudioStream(
125 0 : *destStream, _fileFormat, codecInst, notificationTimeMs);
126 :
127 0 : if (retVal == 0) {
128 0 : retVal = SetUpAudioEncoder();
129 : }
130 0 : if (retVal != 0) {
131 0 : LOG(LS_WARNING) << "Failed to initialize outStream for recording.";
132 :
133 0 : if (IsRecording()) {
134 0 : StopRecording();
135 : }
136 : }
137 0 : return retVal;
138 : }
139 :
140 0 : int32_t FileRecorderImpl::StopRecording() {
141 0 : memset(&codec_info_, 0, sizeof(CodecInst));
142 0 : return _moduleFile->StopRecording();
143 : }
144 :
145 0 : bool FileRecorderImpl::IsRecording() const {
146 0 : return _moduleFile->IsRecording();
147 : }
148 :
149 0 : int32_t FileRecorderImpl::RecordAudioToFile(
150 : const AudioFrame& incomingAudioFrame) {
151 0 : if (codec_info_.plfreq == 0) {
152 0 : LOG(LS_WARNING) << "RecordAudioToFile() recording audio is not "
153 0 : << "turned on.";
154 0 : return -1;
155 : }
156 0 : AudioFrame tempAudioFrame;
157 0 : tempAudioFrame.samples_per_channel_ = 0;
158 0 : if (incomingAudioFrame.num_channels_ == 2 && !_moduleFile->IsStereo()) {
159 : // Recording mono but incoming audio is (interleaved) stereo.
160 0 : tempAudioFrame.num_channels_ = 1;
161 0 : tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_;
162 0 : tempAudioFrame.samples_per_channel_ =
163 0 : incomingAudioFrame.samples_per_channel_;
164 0 : 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 0 : tempAudioFrame.data_[i] = ((incomingAudioFrame.data_[2 * i] +
168 0 : incomingAudioFrame.data_[(2 * i) + 1] + 1) >>
169 0 : 1);
170 : }
171 0 : } else if (incomingAudioFrame.num_channels_ == 1 && _moduleFile->IsStereo()) {
172 : // Recording stereo but incoming audio is mono.
173 0 : tempAudioFrame.num_channels_ = 2;
174 0 : tempAudioFrame.sample_rate_hz_ = incomingAudioFrame.sample_rate_hz_;
175 0 : tempAudioFrame.samples_per_channel_ =
176 0 : incomingAudioFrame.samples_per_channel_;
177 0 : for (size_t i = 0; i < (incomingAudioFrame.samples_per_channel_); i++) {
178 : // Duplicate sample to both channels
179 0 : tempAudioFrame.data_[2 * i] = incomingAudioFrame.data_[i];
180 0 : tempAudioFrame.data_[2 * i + 1] = incomingAudioFrame.data_[i];
181 : }
182 : }
183 :
184 0 : const AudioFrame* ptrAudioFrame = &incomingAudioFrame;
185 0 : if (tempAudioFrame.samples_per_channel_ != 0) {
186 : // If ptrAudioFrame is not empty it contains the audio to be recorded.
187 0 : 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 0 : size_t encodedLenInBytes = 0;
196 0 : if (_fileFormat == kFileFormatPreencodedFile ||
197 0 : STR_CASE_CMP(codec_info_.plname, "L16") != 0) {
198 0 : if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer,
199 : &encodedLenInBytes) == -1) {
200 0 : LOG(LS_WARNING) << "RecordAudioToFile() codec " << codec_info_.plname
201 0 : << " not supported or failed to encode stream.";
202 0 : return -1;
203 : }
204 : } else {
205 0 : size_t outLen = 0;
206 0 : _audioResampler.ResetIfNeeded(ptrAudioFrame->sample_rate_hz_,
207 : codec_info_.plfreq,
208 0 : ptrAudioFrame->num_channels_);
209 0 : _audioResampler.Push(
210 : ptrAudioFrame->data_,
211 0 : ptrAudioFrame->samples_per_channel_ * ptrAudioFrame->num_channels_,
212 : reinterpret_cast<int16_t*>(_audioBuffer), MAX_AUDIO_BUFFER_IN_BYTES,
213 0 : outLen);
214 0 : 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 0 : if (encodedLenInBytes) {
221 0 : if (WriteEncodedAudioData(_audioBuffer, encodedLenInBytes) == -1) {
222 0 : return -1;
223 : }
224 : }
225 0 : return 0;
226 : }
227 :
228 0 : int32_t FileRecorderImpl::SetUpAudioEncoder() {
229 0 : if (_fileFormat == kFileFormatPreencodedFile ||
230 0 : STR_CASE_CMP(codec_info_.plname, "L16") != 0) {
231 0 : if (_audioEncoder.SetEncodeCodec(codec_info_) == -1) {
232 0 : LOG(LS_ERROR) << "SetUpAudioEncoder() codec " << codec_info_.plname
233 0 : << " not supported.";
234 0 : return -1;
235 : }
236 : }
237 0 : return 0;
238 : }
239 :
240 0 : int32_t FileRecorderImpl::codec_info(CodecInst* codecInst) const {
241 0 : if (codec_info_.plfreq == 0) {
242 0 : return -1;
243 : }
244 0 : *codecInst = codec_info_;
245 0 : return 0;
246 : }
247 :
248 0 : int32_t FileRecorderImpl::WriteEncodedAudioData(const int8_t* audioBuffer,
249 : size_t bufferLength) {
250 0 : return _moduleFile->IncomingAudioData(audioBuffer, bufferLength);
251 : }
252 :
253 : } // namespace
254 :
255 0 : std::unique_ptr<FileRecorder> FileRecorder::CreateFileRecorder(
256 : uint32_t instanceID,
257 : FileFormats fileFormat) {
258 : return std::unique_ptr<FileRecorder>(
259 0 : new FileRecorderImpl(instanceID, fileFormat));
260 : }
261 :
262 : } // namespace webrtc
|