OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media_recorder/audio_track_recorder.h" | 5 #include "content/renderer/media_recorder/audio_track_recorder.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/macros.h" | 11 #include "base/macros.h" |
12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
13 #include "content/renderer/media/media_stream_audio_track.h" | 13 #include "content/renderer/media/media_stream_audio_track.h" |
14 #include "media/base/audio_bus.h" | 14 #include "media/base/audio_bus.h" |
15 #include "media/base/audio_converter.h" | 15 #include "media/base/audio_converter.h" |
16 #include "media/base/audio_fifo.h" | |
17 #include "media/base/audio_parameters.h" | 16 #include "media/base/audio_parameters.h" |
18 #include "media/base/audio_sample_types.h" | 17 #include "media/base/audio_sample_types.h" |
19 #include "media/base/bind_to_current_loop.h" | 18 #include "media/base/bind_to_current_loop.h" |
20 #include "third_party/opus/src/include/opus.h" | 19 #include "third_party/opus/src/include/opus.h" |
21 | 20 |
22 // Note that this code follows the Chrome media convention of defining a "frame" | 21 // Note that this code follows the Chrome media convention of defining a "frame" |
23 // as "one multi-channel sample" as opposed to another common definition meaning | 22 // as "one multi-channel sample" as opposed to another common definition meaning |
24 // "a chunk of samples". Here this second definition of "frame" is called a | 23 // "a chunk of samples". Here this second definition of "frame" is called a |
25 // "buffer"; so what might be called "frame duration" is instead "buffer | 24 // "buffer"; so what might be called "frame duration" is instead "buffer |
26 // duration", and so on. | 25 // duration", and so on. |
27 | 26 |
28 namespace content { | 27 namespace content { |
29 | 28 |
30 namespace { | 29 namespace { |
31 | 30 |
32 enum : int { | 31 enum : int { |
33 // Recommended value for opus_encode_float(), according to documentation in | 32 // Recommended value for opus_encode_float(), according to documentation in |
34 // third_party/opus/src/include/opus.h, so that the Opus encoder does not | 33 // third_party/opus/src/include/opus.h, so that the Opus encoder does not |
35 // degrade the audio due to memory constraints, and is independent of the | 34 // degrade the audio due to memory constraints, and is independent of the |
36 // duration of the encoded buffer. | 35 // duration of the encoded buffer. |
37 kOpusMaxDataBytes = 4000, | 36 kOpusMaxDataBytes = 4000, |
38 | 37 |
39 // Opus preferred sampling rate for encoding. This is also the one WebM likes | 38 // Opus preferred sampling rate for encoding. This is also the one WebM likes |
40 // to have: https://wiki.xiph.org/MatroskaOpus. | 39 // to have: https://wiki.xiph.org/MatroskaOpus. |
41 kOpusPreferredSamplingRate = 48000, | 40 kOpusPreferredSamplingRate = 48000, |
42 | 41 |
43 // For quality reasons we try to encode 60ms, the maximum Opus buffer. | 42 // For quality reasons we try to encode 60ms, the maximum Opus buffer. |
44 kOpusPreferredBufferDurationMs = 60, | 43 kOpusPreferredBufferDurationMs = 60, |
45 | |
46 // Maximum amount of buffers that can be held in the AudioEncoders' AudioFifo. | |
47 // Recording is not real time, hence a certain buffering is allowed. | |
48 kMaxNumberOfFifoBuffers = 2, | |
miu
2017/05/09 19:53:19
Keep this. (See comment below.)
Chandan
2017/05/10 09:35:07
Done.
| |
49 }; | 44 }; |
50 | 45 |
51 // The amount of Frames in a 60 ms buffer @ 48000 samples/second. | 46 // The amount of Frames in a 60 ms buffer @ 48000 samples/second. |
52 const int kOpusPreferredFramesPerBuffer = kOpusPreferredSamplingRate * | 47 const int kOpusPreferredFramesPerBuffer = kOpusPreferredSamplingRate * |
53 kOpusPreferredBufferDurationMs / | 48 kOpusPreferredBufferDurationMs / |
54 base::Time::kMillisecondsPerSecond; | 49 base::Time::kMillisecondsPerSecond; |
55 | 50 |
56 // Tries to encode |data_in|'s |num_samples| into |data_out|. | 51 // Tries to encode |data_in|'s |num_samples| into |data_out|. |
57 bool DoEncode(OpusEncoder* opus_encoder, | 52 bool DoEncode(OpusEncoder* opus_encoder, |
58 float* data_in, | 53 float* data_in, |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
120 base::ThreadChecker encoder_thread_checker_; | 115 base::ThreadChecker encoder_thread_checker_; |
121 | 116 |
122 // Track Audio (ingress) and Opus encoder input parameters, respectively. They | 117 // Track Audio (ingress) and Opus encoder input parameters, respectively. They |
123 // only differ in their sample_rate() and frames_per_buffer(): output is | 118 // only differ in their sample_rate() and frames_per_buffer(): output is |
124 // 48ksamples/s and 2880, respectively. | 119 // 48ksamples/s and 2880, respectively. |
125 media::AudioParameters input_params_; | 120 media::AudioParameters input_params_; |
126 media::AudioParameters output_params_; | 121 media::AudioParameters output_params_; |
127 | 122 |
128 // Sampling rate adapter between an OpusEncoder supported and the provided. | 123 // Sampling rate adapter between an OpusEncoder supported and the provided. |
129 std::unique_ptr<media::AudioConverter> converter_; | 124 std::unique_ptr<media::AudioConverter> converter_; |
130 std::unique_ptr<media::AudioFifo> fifo_; | 125 std::deque<std::unique_ptr<media::AudioBus>> audio_bus_queue_; |
131 | 126 |
132 // Buffer for passing AudioBus data to OpusEncoder. | 127 // Buffer for passing AudioBus data to OpusEncoder. |
133 std::unique_ptr<float[]> buffer_; | 128 std::unique_ptr<float[]> buffer_; |
134 | 129 |
135 // While |paused_|, AudioBuses are not encoded. | 130 // While |paused_|, AudioBuses are not encoded. |
136 bool paused_; | 131 bool paused_; |
137 | 132 |
133 int frames_in_; | |
miu
2017/05/09 19:53:19
Since these values always increase, they are at ri
Chandan
2017/05/10 09:35:07
Acknowledged.
| |
134 int frames_out_; | |
135 | |
138 OpusEncoder* opus_encoder_; | 136 OpusEncoder* opus_encoder_; |
139 | 137 |
140 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); | 138 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); |
141 }; | 139 }; |
142 | 140 |
143 AudioTrackRecorder::AudioEncoder::AudioEncoder( | 141 AudioTrackRecorder::AudioEncoder::AudioEncoder( |
144 const OnEncodedAudioCB& on_encoded_audio_cb, | 142 const OnEncodedAudioCB& on_encoded_audio_cb, |
145 int32_t bits_per_second) | 143 int32_t bits_per_second) |
146 : on_encoded_audio_cb_(on_encoded_audio_cb), | 144 : on_encoded_audio_cb_(on_encoded_audio_cb), |
147 bits_per_second_(bits_per_second), | 145 bits_per_second_(bits_per_second), |
148 paused_(false), | 146 paused_(false), |
147 frames_in_(0), | |
148 frames_out_(0), | |
149 opus_encoder_(nullptr) { | 149 opus_encoder_(nullptr) { |
150 // AudioEncoder is constructed on the thread that ATR lives on, but should | 150 // AudioEncoder is constructed on the thread that ATR lives on, but should |
151 // operate only on the encoder thread after that. Reset | 151 // operate only on the encoder thread after that. Reset |
152 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() | 152 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() |
153 // will be from the encoder thread. | 153 // will be from the encoder thread. |
154 encoder_thread_checker_.DetachFromThread(); | 154 encoder_thread_checker_.DetachFromThread(); |
155 } | 155 } |
156 | 156 |
157 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { | 157 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { |
158 // We don't DCHECK that we're on the encoder thread here, as it should have | 158 // We don't DCHECK that we're on the encoder thread here, as it should have |
159 // already been deleted at this point. | 159 // already been deleted at this point. |
160 DestroyExistingOpusEncoder(); | 160 DestroyExistingOpusEncoder(); |
161 } | 161 } |
162 | 162 |
163 void AudioTrackRecorder::AudioEncoder::OnSetFormat( | 163 void AudioTrackRecorder::AudioEncoder::OnSetFormat( |
164 const media::AudioParameters& input_params) { | 164 const media::AudioParameters& input_params) { |
165 DVLOG(1) << __func__; | 165 DVLOG(1) << __func__; |
166 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 166 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
167 if (input_params_.Equals(input_params)) | 167 if (input_params_.Equals(input_params)) |
168 return; | 168 return; |
169 | 169 |
170 DestroyExistingOpusEncoder(); | 170 DestroyExistingOpusEncoder(); |
171 | 171 |
172 if (!input_params.IsValid()) { | 172 if (!input_params.IsValid()) { |
173 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); | 173 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); |
174 return; | 174 return; |
175 } | 175 } |
176 | |
176 input_params_ = input_params; | 177 input_params_ = input_params; |
177 input_params_.set_frames_per_buffer(input_params_.sample_rate() * | 178 input_params_.set_frames_per_buffer(input_params_.sample_rate() * |
178 kOpusPreferredBufferDurationMs / | 179 kOpusPreferredBufferDurationMs / |
179 base::Time::kMillisecondsPerSecond); | 180 base::Time::kMillisecondsPerSecond); |
180 | 181 |
181 // third_party/libopus supports up to 2 channels (see implementation of | 182 // third_party/libopus supports up to 2 channels (see implementation of |
182 // opus_encoder_create()): force |output_params_| to at most those. | 183 // opus_encoder_create()): force |output_params_| to at most those. |
183 output_params_ = media::AudioParameters( | 184 output_params_ = media::AudioParameters( |
184 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 185 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
185 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), | 186 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), |
186 kOpusPreferredSamplingRate, | 187 kOpusPreferredSamplingRate, |
187 input_params_.bits_per_sample(), | 188 input_params_.bits_per_sample(), |
188 kOpusPreferredFramesPerBuffer); | 189 kOpusPreferredFramesPerBuffer); |
189 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() | 190 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() |
190 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); | 191 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); |
191 | 192 |
192 converter_.reset(new media::AudioConverter(input_params_, output_params_, | 193 converter_.reset(new media::AudioConverter(input_params_, output_params_, |
193 false /* disable_fifo */)); | 194 false /* disable_fifo */)); |
194 converter_->AddInput(this); | 195 converter_->AddInput(this); |
195 converter_->PrimeWithSilence(); | 196 converter_->PrimeWithSilence(); |
196 | 197 |
197 fifo_.reset(new media::AudioFifo( | 198 frames_in_ = 0; |
198 input_params_.channels(), | 199 frames_out_ = 0; |
199 kMaxNumberOfFifoBuffers * input_params_.frames_per_buffer())); | 200 audio_bus_queue_.clear(); |
200 | 201 |
201 buffer_.reset(new float[output_params_.channels() * | 202 buffer_.reset(new float[output_params_.channels() * |
202 output_params_.frames_per_buffer()]); | 203 output_params_.frames_per_buffer()]); |
203 | 204 |
204 // Initialize OpusEncoder. | 205 // Initialize OpusEncoder. |
205 int opus_result; | 206 int opus_result; |
206 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), | 207 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), |
207 output_params_.channels(), | 208 output_params_.channels(), |
208 OPUS_APPLICATION_AUDIO, | 209 OPUS_APPLICATION_AUDIO, |
209 &opus_result); | 210 &opus_result); |
(...skipping 20 matching lines...) Expand all Loading... | |
230 std::unique_ptr<media::AudioBus> input_bus, | 231 std::unique_ptr<media::AudioBus> input_bus, |
231 const base::TimeTicks& capture_time) { | 232 const base::TimeTicks& capture_time) { |
232 DVLOG(3) << __func__ << ", #frames " << input_bus->frames(); | 233 DVLOG(3) << __func__ << ", #frames " << input_bus->frames(); |
233 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 234 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
234 DCHECK_EQ(input_bus->channels(), input_params_.channels()); | 235 DCHECK_EQ(input_bus->channels(), input_params_.channels()); |
235 DCHECK(!capture_time.is_null()); | 236 DCHECK(!capture_time.is_null()); |
236 DCHECK(converter_); | 237 DCHECK(converter_); |
237 | 238 |
238 if (!is_initialized() || paused_) | 239 if (!is_initialized() || paused_) |
239 return; | 240 return; |
240 // TODO(mcasas): Consider using a std::deque<std::unique_ptr<AudioBus>> | 241 |
241 // instead of | 242 frames_in_ += input_bus->frames(); |
242 // an AudioFifo, to avoid copying data needlessly since we know the sizes of | 243 audio_bus_queue_.push_back(std::move(input_bus)); |
miu
2017/05/09 19:53:19
The old code had a limit on the maximum amount of
Chandan
2017/05/10 09:35:07
Done.
| |
243 // both input and output and they are multiples. | |
244 fifo_->Push(input_bus.get()); | |
245 | 244 |
246 // Wait to have enough |input_bus|s to guarantee a satisfactory conversion. | 245 // Wait to have enough |input_bus|s to guarantee a satisfactory conversion. |
247 while (fifo_->frames() >= input_params_.frames_per_buffer()) { | 246 while ((frames_in_ - frames_out_) >= input_params_.frames_per_buffer()) { |
248 std::unique_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( | 247 std::unique_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( |
249 output_params_.channels(), kOpusPreferredFramesPerBuffer); | 248 output_params_.channels(), kOpusPreferredFramesPerBuffer); |
250 converter_->Convert(audio_bus.get()); | 249 converter_->Convert(audio_bus.get()); |
251 audio_bus->ToInterleaved<media::Float32SampleTypeTraits>( | 250 audio_bus->ToInterleaved<media::Float32SampleTypeTraits>( |
252 audio_bus->frames(), buffer_.get()); | 251 audio_bus->frames(), buffer_.get()); |
253 | 252 |
254 std::unique_ptr<std::string> encoded_data(new std::string()); | 253 std::unique_ptr<std::string> encoded_data(new std::string()); |
255 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, | 254 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, |
256 encoded_data.get())) { | 255 encoded_data.get())) { |
257 const base::TimeTicks capture_time_of_first_sample = | 256 const base::TimeTicks capture_time_of_first_sample = |
258 capture_time - | 257 capture_time - |
259 base::TimeDelta::FromMicroseconds(fifo_->frames() * | 258 base::TimeDelta::FromMicroseconds((frames_in_ - frames_out_) * |
260 base::Time::kMicrosecondsPerSecond / | 259 base::Time::kMicrosecondsPerSecond / |
261 input_params_.sample_rate()); | 260 input_params_.sample_rate()); |
262 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), | 261 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), |
263 capture_time_of_first_sample); | 262 capture_time_of_first_sample); |
264 } | 263 } |
265 } | 264 } |
266 } | 265 } |
267 | 266 |
268 double AudioTrackRecorder::AudioEncoder::ProvideInput( | 267 double AudioTrackRecorder::AudioEncoder::ProvideInput( |
269 media::AudioBus* audio_bus, | 268 media::AudioBus* audio_bus, |
270 uint32_t frames_delayed) { | 269 uint32_t frames_delayed) { |
271 fifo_->Consume(audio_bus, 0, audio_bus->frames()); | 270 if (!audio_bus_queue_.empty()) { |
271 frames_out_ += audio_bus->frames(); | |
272 audio_bus_queue_.front()->CopyTo(audio_bus); | |
273 audio_bus_queue_.pop_front(); | |
274 } | |
272 return 1.0; // Return volume greater than zero to indicate we have more data. | 275 return 1.0; // Return volume greater than zero to indicate we have more data. |
miu
2017/05/09 19:53:19
It seems that if |audio_bus_queue_| is empty, we s
Chandan
2017/05/10 09:35:07
Done.
| |
273 } | 276 } |
274 | 277 |
275 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { | 278 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
276 // We don't DCHECK that we're on the encoder thread here, as this could be | 279 // We don't DCHECK that we're on the encoder thread here, as this could be |
277 // called from the dtor (main thread) or from OnSetFormat() (encoder thread). | 280 // called from the dtor (main thread) or from OnSetFormat() (encoder thread). |
278 if (opus_encoder_) { | 281 if (opus_encoder_) { |
279 opus_encoder_destroy(opus_encoder_); | 282 opus_encoder_destroy(opus_encoder_); |
280 opus_encoder_ = nullptr; | 283 opus_encoder_ = nullptr; |
281 } | 284 } |
282 } | 285 } |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
337 } | 340 } |
338 | 341 |
339 void AudioTrackRecorder::Resume() { | 342 void AudioTrackRecorder::Resume() { |
340 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 343 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
341 DCHECK(encoder_); | 344 DCHECK(encoder_); |
342 encoder_thread_.task_runner()->PostTask( | 345 encoder_thread_.task_runner()->PostTask( |
343 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); | 346 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); |
344 } | 347 } |
345 | 348 |
346 } // namespace content | 349 } // namespace content |
OLD | NEW |