| 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 audio_bus->Zero(); |
| 279 if (audio_bus_queue_.empty()) |
| 280 return 0.0; |
| 281 |
| 282 frames_queued_ -= audio_bus->frames(); |
| 283 audio_bus_queue_.front()->CopyTo(audio_bus); |
| 284 audio_bus_queue_.pop_front(); |
| 272 return 1.0; // Return volume greater than zero to indicate we have more data. | 285 return 1.0; // Return volume greater than zero to indicate we have more data. |
| 273 } | 286 } |
| 274 | 287 |
| 275 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { | 288 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
| 276 // We don't DCHECK that we're on the encoder thread here, as this could be | 289 // 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). | 290 // called from the dtor (main thread) or from OnSetFormat() (encoder thread). |
| 278 if (opus_encoder_) { | 291 if (opus_encoder_) { |
| 279 opus_encoder_destroy(opus_encoder_); | 292 opus_encoder_destroy(opus_encoder_); |
| 280 opus_encoder_ = nullptr; | 293 opus_encoder_ = nullptr; |
| 281 } | 294 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 } | 350 } |
| 338 | 351 |
| 339 void AudioTrackRecorder::Resume() { | 352 void AudioTrackRecorder::Resume() { |
| 340 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 353 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 341 DCHECK(encoder_); | 354 DCHECK(encoder_); |
| 342 encoder_thread_.task_runner()->PostTask( | 355 encoder_thread_.task_runner()->PostTask( |
| 343 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); | 356 FROM_HERE, base::Bind(&AudioEncoder::set_paused, encoder_, false)); |
| 344 } | 357 } |
| 345 | 358 |
| 346 } // namespace content | 359 } // namespace content |
| OLD | NEW |