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. |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
120 base::ThreadChecker encoder_thread_checker_; | 119 base::ThreadChecker encoder_thread_checker_; |
121 | 120 |
122 // Track Audio (ingress) and Opus encoder input parameters, respectively. They | 121 // Track Audio (ingress) and Opus encoder input parameters, respectively. They |
123 // only differ in their sample_rate() and frames_per_buffer(): output is | 122 // only differ in their sample_rate() and frames_per_buffer(): output is |
124 // 48ksamples/s and 2880, respectively. | 123 // 48ksamples/s and 2880, respectively. |
125 media::AudioParameters input_params_; | 124 media::AudioParameters input_params_; |
126 media::AudioParameters output_params_; | 125 media::AudioParameters output_params_; |
127 | 126 |
128 // Sampling rate adapter between an OpusEncoder supported and the provided. | 127 // Sampling rate adapter between an OpusEncoder supported and the provided. |
129 std::unique_ptr<media::AudioConverter> converter_; | 128 std::unique_ptr<media::AudioConverter> converter_; |
130 std::unique_ptr<media::AudioFifo> fifo_; | 129 std::deque<std::unique_ptr<media::AudioBus>> audio_bus_queue_; |
131 | 130 |
132 // Buffer for passing AudioBus data to OpusEncoder. | 131 // Buffer for passing AudioBus data to OpusEncoder. |
133 std::unique_ptr<float[]> buffer_; | 132 std::unique_ptr<float[]> buffer_; |
134 | 133 |
135 // While |paused_|, AudioBuses are not encoded. | 134 // While |paused_|, AudioBuses are not encoded. |
136 bool paused_; | 135 bool paused_; |
137 | 136 |
| 137 int frames_queued_; |
| 138 |
138 OpusEncoder* opus_encoder_; | 139 OpusEncoder* opus_encoder_; |
139 | 140 |
140 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); | 141 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); |
141 }; | 142 }; |
142 | 143 |
143 AudioTrackRecorder::AudioEncoder::AudioEncoder( | 144 AudioTrackRecorder::AudioEncoder::AudioEncoder( |
144 const OnEncodedAudioCB& on_encoded_audio_cb, | 145 const OnEncodedAudioCB& on_encoded_audio_cb, |
145 int32_t bits_per_second) | 146 int32_t bits_per_second) |
146 : on_encoded_audio_cb_(on_encoded_audio_cb), | 147 : on_encoded_audio_cb_(on_encoded_audio_cb), |
147 bits_per_second_(bits_per_second), | 148 bits_per_second_(bits_per_second), |
148 paused_(false), | 149 paused_(false), |
| 150 frames_queued_(0), |
149 opus_encoder_(nullptr) { | 151 opus_encoder_(nullptr) { |
150 // AudioEncoder is constructed on the thread that ATR lives on, but should | 152 // AudioEncoder is constructed on the thread that ATR lives on, but should |
151 // operate only on the encoder thread after that. Reset | 153 // operate only on the encoder thread after that. Reset |
152 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() | 154 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() |
153 // will be from the encoder thread. | 155 // will be from the encoder thread. |
154 encoder_thread_checker_.DetachFromThread(); | 156 encoder_thread_checker_.DetachFromThread(); |
155 } | 157 } |
156 | 158 |
157 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { | 159 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { |
158 // We don't DCHECK that we're on the encoder thread here, as it should have | 160 // We don't DCHECK that we're on the encoder thread here, as it should have |
159 // already been deleted at this point. | 161 // already been deleted at this point. |
160 DestroyExistingOpusEncoder(); | 162 DestroyExistingOpusEncoder(); |
161 } | 163 } |
162 | 164 |
163 void AudioTrackRecorder::AudioEncoder::OnSetFormat( | 165 void AudioTrackRecorder::AudioEncoder::OnSetFormat( |
164 const media::AudioParameters& input_params) { | 166 const media::AudioParameters& input_params) { |
165 DVLOG(1) << __func__; | 167 DVLOG(1) << __func__; |
166 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 168 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
167 if (input_params_.Equals(input_params)) | 169 if (input_params_.Equals(input_params)) |
168 return; | 170 return; |
169 | 171 |
170 DestroyExistingOpusEncoder(); | 172 DestroyExistingOpusEncoder(); |
171 | 173 |
172 if (!input_params.IsValid()) { | 174 if (!input_params.IsValid()) { |
173 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); | 175 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); |
174 return; | 176 return; |
175 } | 177 } |
| 178 |
176 input_params_ = input_params; | 179 input_params_ = input_params; |
177 input_params_.set_frames_per_buffer(input_params_.sample_rate() * | 180 input_params_.set_frames_per_buffer(input_params_.sample_rate() * |
178 kOpusPreferredBufferDurationMs / | 181 kOpusPreferredBufferDurationMs / |
179 base::Time::kMillisecondsPerSecond); | 182 base::Time::kMillisecondsPerSecond); |
180 | 183 |
181 // third_party/libopus supports up to 2 channels (see implementation of | 184 // third_party/libopus supports up to 2 channels (see implementation of |
182 // opus_encoder_create()): force |output_params_| to at most those. | 185 // opus_encoder_create()): force |output_params_| to at most those. |
183 output_params_ = media::AudioParameters( | 186 output_params_ = media::AudioParameters( |
184 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 187 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
185 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), | 188 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), |
186 kOpusPreferredSamplingRate, | 189 kOpusPreferredSamplingRate, |
187 input_params_.bits_per_sample(), | 190 input_params_.bits_per_sample(), |
188 kOpusPreferredFramesPerBuffer); | 191 kOpusPreferredFramesPerBuffer); |
189 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() | 192 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() |
190 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); | 193 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); |
191 | 194 |
192 converter_.reset(new media::AudioConverter(input_params_, output_params_, | 195 converter_.reset(new media::AudioConverter(input_params_, output_params_, |
193 false /* disable_fifo */)); | 196 false /* disable_fifo */)); |
194 converter_->AddInput(this); | 197 converter_->AddInput(this); |
195 converter_->PrimeWithSilence(); | 198 converter_->PrimeWithSilence(); |
196 | 199 |
197 fifo_.reset(new media::AudioFifo( | 200 frames_queued_ = 0; |
198 input_params_.channels(), | 201 audio_bus_queue_.clear(); |
199 kMaxNumberOfFifoBuffers * input_params_.frames_per_buffer())); | |
200 | 202 |
201 buffer_.reset(new float[output_params_.channels() * | 203 buffer_.reset(new float[output_params_.channels() * |
202 output_params_.frames_per_buffer()]); | 204 output_params_.frames_per_buffer()]); |
203 | 205 |
204 // Initialize OpusEncoder. | 206 // Initialize OpusEncoder. |
205 int opus_result; | 207 int opus_result; |
206 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), | 208 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), |
207 output_params_.channels(), | 209 output_params_.channels(), |
208 OPUS_APPLICATION_AUDIO, | 210 OPUS_APPLICATION_AUDIO, |
209 &opus_result); | 211 &opus_result); |
(...skipping 20 matching lines...) Expand all Loading... |
230 std::unique_ptr<media::AudioBus> input_bus, | 232 std::unique_ptr<media::AudioBus> input_bus, |
231 const base::TimeTicks& capture_time) { | 233 const base::TimeTicks& capture_time) { |
232 DVLOG(3) << __func__ << ", #frames " << input_bus->frames(); | 234 DVLOG(3) << __func__ << ", #frames " << input_bus->frames(); |
233 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 235 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
234 DCHECK_EQ(input_bus->channels(), input_params_.channels()); | 236 DCHECK_EQ(input_bus->channels(), input_params_.channels()); |
235 DCHECK(!capture_time.is_null()); | 237 DCHECK(!capture_time.is_null()); |
236 DCHECK(converter_); | 238 DCHECK(converter_); |
237 | 239 |
238 if (!is_initialized() || paused_) | 240 if (!is_initialized() || paused_) |
239 return; | 241 return; |
240 // TODO(mcasas): Consider using a std::deque<std::unique_ptr<AudioBus>> | 242 |
241 // instead of | 243 frames_queued_ += input_bus->frames(); |
242 // an AudioFifo, to avoid copying data needlessly since we know the sizes of | 244 audio_bus_queue_.push_back(std::move(input_bus)); |
243 // both input and output and they are multiples. | 245 |
244 fifo_->Push(input_bus.get()); | 246 const int max_frame_limit = |
| 247 kMaxNumberOfFifoBuffers * input_params_.frames_per_buffer(); |
| 248 while (frames_queued_ > max_frame_limit) { |
| 249 frames_queued_ -= audio_bus_queue_.front()->frames(); |
| 250 audio_bus_queue_.pop_front(); |
| 251 } |
245 | 252 |
246 // Wait to have enough |input_bus|s to guarantee a satisfactory conversion. | 253 // Wait to have enough |input_bus|s to guarantee a satisfactory conversion. |
247 while (fifo_->frames() >= input_params_.frames_per_buffer()) { | 254 while (frames_queued_ >= input_params_.frames_per_buffer()) { |
248 std::unique_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( | 255 std::unique_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( |
249 output_params_.channels(), kOpusPreferredFramesPerBuffer); | 256 output_params_.channels(), kOpusPreferredFramesPerBuffer); |
250 converter_->Convert(audio_bus.get()); | 257 converter_->Convert(audio_bus.get()); |
251 audio_bus->ToInterleaved<media::Float32SampleTypeTraits>( | 258 audio_bus->ToInterleaved<media::Float32SampleTypeTraits>( |
252 audio_bus->frames(), buffer_.get()); | 259 audio_bus->frames(), buffer_.get()); |
253 | 260 |
254 std::unique_ptr<std::string> encoded_data(new std::string()); | 261 std::unique_ptr<std::string> encoded_data(new std::string()); |
255 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, | 262 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, |
256 encoded_data.get())) { | 263 encoded_data.get())) { |
257 const base::TimeTicks capture_time_of_first_sample = | 264 const base::TimeTicks capture_time_of_first_sample = |
258 capture_time - | 265 capture_time - |
259 base::TimeDelta::FromMicroseconds(fifo_->frames() * | 266 base::TimeDelta::FromMicroseconds(frames_queued_ * |
260 base::Time::kMicrosecondsPerSecond / | 267 base::Time::kMicrosecondsPerSecond / |
261 input_params_.sample_rate()); | 268 input_params_.sample_rate()); |
262 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), | 269 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), |
263 capture_time_of_first_sample); | 270 capture_time_of_first_sample); |
264 } | 271 } |
265 } | 272 } |
266 } | 273 } |
267 | 274 |
268 double AudioTrackRecorder::AudioEncoder::ProvideInput( | 275 double AudioTrackRecorder::AudioEncoder::ProvideInput( |
269 media::AudioBus* audio_bus, | 276 media::AudioBus* audio_bus, |
270 uint32_t frames_delayed) { | 277 uint32_t frames_delayed) { |
271 fifo_->Consume(audio_bus, 0, audio_bus->frames()); | 278 if (audio_bus_queue_.empty()) { |
| 279 audio_bus->Zero(); |
| 280 return 0.0; |
| 281 } |
| 282 |
| 283 frames_queued_ -= audio_bus->frames(); |
| 284 audio_bus_queue_.front()->CopyTo(audio_bus); |
| 285 audio_bus_queue_.pop_front(); |
272 return 1.0; // Return volume greater than zero to indicate we have more data. | 286 return 1.0; // Return volume greater than zero to indicate we have more data. |
273 } | 287 } |
274 | 288 |
275 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { | 289 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
276 // We don't DCHECK that we're on the encoder thread here, as this could be | 290 // 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). | 291 // called from the dtor (main thread) or from OnSetFormat() (encoder thread). |
278 if (opus_encoder_) { | 292 if (opus_encoder_) { |
279 opus_encoder_destroy(opus_encoder_); | 293 opus_encoder_destroy(opus_encoder_); |
280 opus_encoder_ = nullptr; | 294 opus_encoder_ = nullptr; |
281 } | 295 } |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 } | 351 } |
338 | 352 |
339 void AudioTrackRecorder::Resume() { | 353 void AudioTrackRecorder::Resume() { |
340 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 354 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
341 DCHECK(encoder_); | 355 DCHECK(encoder_); |
342 encoder_thread_.task_runner()->PostTask( | 356 encoder_thread_.task_runner()->PostTask( |
343 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); | 357 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); |
344 } | 358 } |
345 | 359 |
346 } // namespace content | 360 } // namespace content |
OLD | NEW |