Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(447)

Side by Side Diff: content/renderer/media_recorder/audio_track_recorder.cc

Issue 2854363002: Replace AudioFifo with a deque of AudioBus in ATR::AudioEncoder (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698