Chromium Code Reviews| 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/audio_track_recorder.h" | 5 #include "content/renderer/media/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 "media/audio/audio_parameters.h" | 13 #include "media/audio/audio_parameters.h" |
| 14 #include "media/base/audio_bus.h" | 14 #include "media/base/audio_bus.h" |
| 15 #include "media/base/audio_converter.h" | |
| 16 #include "media/base/audio_fifo.h" | |
| 15 #include "media/base/bind_to_current_loop.h" | 17 #include "media/base/bind_to_current_loop.h" |
| 16 #include "third_party/opus/src/include/opus.h" | 18 #include "third_party/opus/src/include/opus.h" |
| 17 | 19 |
| 18 // Note that this code follows the Chrome media convention of defining a "frame" | 20 // Note that this code follows the Chrome media convention of defining a "frame" |
| 19 // as "one multi-channel sample" as opposed to another common definition | 21 // as "one multi-channel sample" as opposed to another common definition meaning |
| 20 // meaning "a chunk of samples". Here this second definition of "frame" is | 22 // "a chunk of samples". Here this second definition of "frame" is called a |
| 21 // called a "buffer"; so what might be called "frame duration" is instead | 23 // "buffer"; so what might be called "frame duration" is instead "buffer |
| 22 // "buffer duration", and so on. | 24 // duration", and so on. |
| 23 | 25 |
| 24 namespace content { | 26 namespace content { |
| 25 | 27 |
| 26 namespace { | 28 namespace { |
| 27 | 29 |
| 28 enum { | 30 // Recommended value for opus_encode_float(), according to documentation in |
| 29 // This is the recommended value, according to documentation in | 31 // third_party/opus/src/include/opus.h, so that the Opus encoder does not |
| 30 // third_party/opus/src/include/opus.h, so that the Opus encoder does not | 32 // degrade the audio due to memory constraints, and is independent of the |
| 31 // degrade the audio due to memory constraints. | 33 // duration of the encoded buffer. |
| 32 OPUS_MAX_PAYLOAD_SIZE = 4000, | 34 static const int kOpusMaxDataBytes = 4000; |
|
miu
2016/01/27 01:52:58
Consider making these all enum constants, per rece
mcasas
2016/01/28 01:28:19
Done for those that are not calculations.
| |
| 33 | 35 |
| 34 // Support for max sampling rate of 48KHz, 2 channels, 60 ms duration. | 36 // Opus preferred sampling rate for encoding. This is also the one WebM likes to |
| 35 MAX_SAMPLES_PER_BUFFER = 48 * 2 * 60, | 37 // have: https://wiki.xiph.org/MatroskaOpus. |
| 36 }; | 38 static const int kOpusPreferredSamplingRate = 48000; |
| 39 | |
| 40 // Media Stream Audio Tracks always send 10ms worth of Audio. | |
| 41 static const int kMediaStreamTrackBufferDurationMs = 10; | |
| 42 // For quality reasons we try to encode 60ms, the maximum Opus buffer. | |
| 43 static const int kOpusPreferredBufferDurationMs = 60; | |
| 44 // Conversion between buffers following a N:1 length ratio is much easier, as is | |
| 45 // the case here. This parameter represents that ratio: need N input buffers for | |
| 46 // 1 output buffer. | |
| 47 static const int kRatioInputToOutputBuffers = | |
| 48 kOpusPreferredBufferDurationMs / kMediaStreamTrackBufferDurationMs; | |
| 49 | |
| 50 // The amount of Frames in a 60 ms buffer @ 48000 samples/second. | |
| 51 static const int kOpusPreferredFramesPerBuffer = | |
| 52 (kOpusPreferredSamplingRate / 1000) * kOpusPreferredBufferDurationMs; | |
|
miu
2016/01/27 01:52:58
nit (to avoid rounding error if kOpusPreferredSamp
mcasas
2016/01/28 01:28:19
Done.
| |
| 53 | |
| 54 // Maximum amount of buffers that can be held in the AudioFifo of AudioEncoder. | |
| 55 // Recording is not real time, hence a certain buffering is allowed. | |
| 56 static const size_t kMaxNumberOfFifoBuffers = 2 * kRatioInputToOutputBuffers; | |
| 57 | |
| 58 // Tries to encode |data_in|'s |num_samples| into |data_out|. | |
| 59 bool DoEncode(OpusEncoder* opus_encoder, | |
| 60 float* data_in, | |
| 61 int num_samples, | |
| 62 std::string* data_out) { | |
| 63 DCHECK_EQ(kOpusPreferredFramesPerBuffer, num_samples); | |
| 64 | |
| 65 data_out->resize(kOpusMaxDataBytes); | |
| 66 const opus_int32 result = opus_encode_float( | |
| 67 opus_encoder, data_in, num_samples, | |
| 68 reinterpret_cast<uint8_t*>(string_as_array(data_out)), kOpusMaxDataBytes); | |
| 69 | |
| 70 if (result > 1) { | |
| 71 // TODO(ajose): Investigate improving this. http://crbug.com/547918 | |
| 72 data_out->resize(result); | |
| 73 return true; | |
| 74 } | |
| 75 // If |result| in {0,1}, do nothing; the documentation says that a return | |
| 76 // value of zero or one means the packet does not need to be transmitted. | |
| 77 // Otherwise, we have an error. | |
| 78 DLOG_IF(ERROR, result < 0) << " encode failed: " << opus_strerror(result); | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 // Interleaves |audio_bus| channels() of floats into a single output linear | |
| 83 // |buffer|. | |
| 84 // TODO(mcasas) https://crbug.com/580391 use AudioBus::ToInterleavedFloat(). | |
| 85 void ToInterleaved(media::AudioBus* audio_bus, float* buffer) { | |
| 86 for (int ch = 0; ch < audio_bus->channels(); ++ch) { | |
| 87 const float* src = audio_bus->channel(ch); | |
| 88 const float* const src_end = src + audio_bus->frames(); | |
| 89 float* dest = buffer + ch; | |
| 90 for (; src < src_end; ++src, dest += audio_bus->channels()) | |
| 91 *dest = *src; | |
| 92 } | |
| 93 } | |
| 37 | 94 |
| 38 } // anonymous namespace | 95 } // anonymous namespace |
| 39 | 96 |
| 40 // Nested class encapsulating opus-related encoding details. | 97 // Nested class encapsulating opus-related encoding details. It contains an |
| 41 // AudioEncoder is created and destroyed on ATR's main thread (usually the | 98 // AudioConverter to adapt incoming data to the format Opus likes to have. |
| 42 // main render thread) but otherwise should operate entirely on | 99 // AudioEncoder is created and destroyed on ATR's main thread (usually the main |
| 43 // |encoder_thread_|, which is owned by AudioTrackRecorder. Be sure to delete | 100 // render thread) but otherwise should operate entirely on |encoder_thread_|, |
| 44 // |encoder_thread_| before deleting the AudioEncoder using it. | 101 // which is owned by AudioTrackRecorder. Be sure to delete |encoder_thread_| |
| 102 // before deleting the AudioEncoder using it. | |
| 45 class AudioTrackRecorder::AudioEncoder | 103 class AudioTrackRecorder::AudioEncoder |
| 46 : public base::RefCountedThreadSafe<AudioEncoder> { | 104 : public base::RefCountedThreadSafe<AudioEncoder>, |
| 105 public media::AudioConverter::InputCallback { | |
| 47 public: | 106 public: |
| 48 AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb, | 107 AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb, |
| 49 int32_t bits_per_second); | 108 int32_t bits_per_second); |
| 50 | 109 |
| 51 void OnSetFormat(const media::AudioParameters& params); | 110 void OnSetFormat(const media::AudioParameters& params); |
| 52 | 111 |
| 53 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, | 112 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, |
| 54 const base::TimeTicks& capture_time); | 113 const base::TimeTicks& capture_time); |
| 55 | 114 |
| 56 private: | 115 private: |
| 57 friend class base::RefCountedThreadSafe<AudioEncoder>; | 116 friend class base::RefCountedThreadSafe<AudioEncoder>; |
| 58 | 117 |
| 59 ~AudioEncoder(); | 118 ~AudioEncoder() override; |
| 60 | 119 |
| 61 bool is_initialized() const { return !!opus_encoder_; } | 120 bool is_initialized() const { return !!opus_encoder_; } |
| 62 | 121 |
| 122 // media::AudioConverted::InputCallback implementation. | |
| 123 double ProvideInput(media::AudioBus* audio_bus, | |
| 124 base::TimeDelta buffer_delay) override; | |
| 125 | |
| 63 void DestroyExistingOpusEncoder(); | 126 void DestroyExistingOpusEncoder(); |
| 64 | 127 |
| 65 void TransferSamplesIntoBuffer(const media::AudioBus* audio_bus, | |
| 66 int source_offset, | |
| 67 int buffer_fill_offset, | |
| 68 int num_samples); | |
| 69 bool EncodeFromFilledBuffer(std::string* out); | |
| 70 | |
| 71 const OnEncodedAudioCB on_encoded_audio_cb_; | 128 const OnEncodedAudioCB on_encoded_audio_cb_; |
| 72 | 129 |
| 73 // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used. | 130 // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used. |
| 74 const int32_t bits_per_second_; | 131 const int32_t bits_per_second_; |
| 75 | 132 |
| 76 base::ThreadChecker encoder_thread_checker_; | 133 base::ThreadChecker encoder_thread_checker_; |
| 77 | 134 |
| 78 // In the case where a call to EncodeAudio() cannot completely fill the | 135 // Track Audio (ingress) and Opus encoder input parameters, respectively. They |
| 79 // buffer, this points to the position at which to populate data in a later | 136 // only differ in their sample_rate() and frames_per_buffer(): output is |
| 80 // call. | 137 // 48ksamples/s and 480, respectively. |
|
miu
2016/01/27 01:52:58
Output is 2880 frames per buffer.
mcasas
2016/01/28 01:28:19
Done.
| |
| 81 int buffer_fill_end_; | 138 media::AudioParameters input_params_; |
| 139 media::AudioParameters output_params_; | |
| 82 | 140 |
| 83 int frames_per_buffer_; | 141 // Sampling rate adapter between an OpusEncoder supported and the provided. |
| 84 | 142 scoped_ptr<media::AudioConverter> converter_; |
| 85 // The duration of one set of frames of encoded audio samples. | 143 scoped_ptr<media::AudioFifo> fifo_; |
| 86 base::TimeDelta buffer_duration_; | 144 base::TimeTicks capture_time_of_first_buffer_; |
| 87 | |
| 88 media::AudioParameters audio_params_; | |
| 89 | 145 |
| 90 // Buffer for passing AudioBus data to OpusEncoder. | 146 // Buffer for passing AudioBus data to OpusEncoder. |
| 91 scoped_ptr<float[]> buffer_; | 147 scoped_ptr<float[]> buffer_; |
| 92 | 148 |
| 93 OpusEncoder* opus_encoder_; | 149 OpusEncoder* opus_encoder_; |
| 94 | 150 |
| 95 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); | 151 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); |
| 96 }; | 152 }; |
| 97 | 153 |
| 98 AudioTrackRecorder::AudioEncoder::AudioEncoder( | 154 AudioTrackRecorder::AudioEncoder::AudioEncoder( |
| 99 const OnEncodedAudioCB& on_encoded_audio_cb, | 155 const OnEncodedAudioCB& on_encoded_audio_cb, |
| 100 int32_t bits_per_second) | 156 int32_t bits_per_second) |
| 101 : on_encoded_audio_cb_(on_encoded_audio_cb), | 157 : on_encoded_audio_cb_(on_encoded_audio_cb), |
| 102 bits_per_second_(bits_per_second), | 158 bits_per_second_(bits_per_second), |
| 103 opus_encoder_(nullptr) { | 159 opus_encoder_(nullptr) { |
| 104 // AudioEncoder is constructed on the thread that ATR lives on, but should | 160 // AudioEncoder is constructed on the thread that ATR lives on, but should |
| 105 // operate only on the encoder thread after that. Reset | 161 // operate only on the encoder thread after that. Reset |
| 106 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() | 162 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() |
| 107 // will be from the encoder thread. | 163 // will be from the encoder thread. |
| 108 encoder_thread_checker_.DetachFromThread(); | 164 encoder_thread_checker_.DetachFromThread(); |
| 109 } | 165 } |
| 110 | 166 |
| 111 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { | 167 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { |
| 112 // We don't DCHECK that we're on the encoder thread here, as it should have | 168 // We don't DCHECK that we're on the encoder thread here, as it should have |
| 113 // already been deleted at this point. | 169 // already been deleted at this point. |
| 114 DestroyExistingOpusEncoder(); | 170 DestroyExistingOpusEncoder(); |
| 115 } | 171 } |
| 116 | 172 |
| 117 void AudioTrackRecorder::AudioEncoder::OnSetFormat( | 173 void AudioTrackRecorder::AudioEncoder::OnSetFormat( |
| 118 const media::AudioParameters& params) { | 174 const media::AudioParameters& input_params) { |
| 175 DVLOG(1) << __FUNCTION__; | |
| 119 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 176 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 120 if (audio_params_.Equals(params)) | 177 if (input_params_.Equals(input_params)) |
|
miu
2016/01/27 01:52:58
This will always be false, since you call input_pa
| |
| 121 return; | 178 return; |
| 122 | 179 |
| 123 DestroyExistingOpusEncoder(); | 180 DestroyExistingOpusEncoder(); |
| 124 | 181 |
| 125 if (!params.IsValid() || params.channels() > 2) { | 182 if (!input_params.IsValid()) { |
| 126 DLOG(ERROR) << "Invalid audio params: " << params.AsHumanReadableString(); | 183 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); |
| 184 return; | |
| 185 } | |
| 186 input_params_ = input_params; | |
| 187 input_params_.set_frames_per_buffer(input_params_.sample_rate() * | |
|
miu
2016/01/27 01:52:58
These are the AudioParameters for the |converter_|
mcasas
2016/01/28 01:28:19
Done.
I still need kMediaStreamTrackBufferDuratio
| |
| 188 kMediaStreamTrackBufferDurationMs / 1000); | |
| 189 | |
| 190 // Opus supports up to 2 channel`s, force |output_params_| to at most those. | |
| 191 output_params_ = media::AudioParameters( | |
| 192 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
| 193 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), | |
|
miu
2016/01/27 01:52:59
Opus supports from 1 to 255 channels. Why restric
mcasas
2016/01/28 01:28:19
Maybe Opus does, but our third_party doesn't :(
| |
| 194 kOpusPreferredSamplingRate, | |
| 195 input_params_.bits_per_sample(), | |
| 196 kOpusPreferredFramesPerBuffer); | |
| 197 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() | |
| 198 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); | |
| 199 | |
| 200 converter_.reset(new media::AudioConverter(input_params_, output_params_, | |
| 201 false /* disable_fifo */)); | |
| 202 converter_->AddInput(this); | |
| 203 converter_->PrimeWithSilence(); | |
| 204 | |
| 205 fifo_.reset(new media::AudioFifo( | |
| 206 input_params_.channels(), | |
| 207 kMaxNumberOfFifoBuffers * input_params_.frames_per_buffer())); | |
| 208 | |
| 209 buffer_.reset(new float[output_params_.channels() * | |
| 210 output_params_.frames_per_buffer()]); | |
| 211 | |
| 212 // Initialize OpusEncoder. | |
| 213 int opus_result; | |
| 214 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), | |
| 215 output_params_.channels(), | |
| 216 OPUS_APPLICATION_AUDIO, | |
| 217 &opus_result); | |
| 218 if (opus_result < 0) { | |
| 219 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result) | |
| 220 << ", sample rate: " << output_params_.sample_rate() | |
| 221 << ", channels: " << output_params_.channels(); | |
| 127 return; | 222 return; |
| 128 } | 223 } |
| 129 | 224 |
| 130 buffer_duration_ = base::TimeDelta::FromMilliseconds( | |
| 131 AudioTrackRecorder::GetOpusBufferDuration(params.sample_rate())); | |
| 132 if (buffer_duration_ == base::TimeDelta()) { | |
| 133 DLOG(ERROR) << "Could not find a valid |buffer_duration| for the given " | |
| 134 << "sample rate: " << params.sample_rate(); | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 frames_per_buffer_ = | |
| 139 params.sample_rate() * buffer_duration_.InMilliseconds() / 1000; | |
| 140 if (frames_per_buffer_ * params.channels() > MAX_SAMPLES_PER_BUFFER) { | |
| 141 DLOG(ERROR) << "Invalid |frames_per_buffer_|: " << frames_per_buffer_; | |
| 142 return; | |
| 143 } | |
| 144 | |
| 145 // Initialize AudioBus buffer for OpusEncoder. | |
| 146 buffer_fill_end_ = 0; | |
| 147 buffer_.reset(new float[params.channels() * frames_per_buffer_]); | |
| 148 | |
| 149 // Initialize OpusEncoder. | |
| 150 DCHECK((params.sample_rate() != 48000) || (params.sample_rate() != 24000) || | |
| 151 (params.sample_rate() != 16000) || (params.sample_rate() != 12000) || | |
| 152 (params.sample_rate() != 8000)) | |
| 153 << "Opus supports only sample rates of {48, 24, 16, 12, 8}000, requested " | |
| 154 << params.sample_rate(); | |
| 155 int opus_result; | |
| 156 opus_encoder_ = opus_encoder_create(params.sample_rate(), params.channels(), | |
| 157 OPUS_APPLICATION_AUDIO, &opus_result); | |
| 158 if (opus_result < 0) { | |
| 159 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result) | |
| 160 << ", sample rate: " << params.sample_rate() | |
| 161 << ", channels: " << params.channels(); | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a | 225 // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a |
| 166 // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms | 226 // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms |
| 167 // buffer duration. The opus library authors may, of course, adjust this in | 227 // buffer duration. The opus library authors may, of course, adjust this in |
| 168 // later versions. | 228 // later versions. |
| 169 const opus_int32 bitrate = | 229 const opus_int32 bitrate = |
| 170 (bits_per_second_ > 0) ? bits_per_second_ : OPUS_AUTO; | 230 (bits_per_second_ > 0) ? bits_per_second_ : OPUS_AUTO; |
| 171 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) { | 231 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) { |
| 172 DLOG(ERROR) << "Failed to set opus bitrate: " << bitrate; | 232 DLOG(ERROR) << "Failed to set opus bitrate: " << bitrate; |
| 173 return; | 233 return; |
| 174 } | 234 } |
| 175 | |
| 176 audio_params_ = params; | |
| 177 } | 235 } |
| 178 | 236 |
| 179 void AudioTrackRecorder::AudioEncoder::EncodeAudio( | 237 void AudioTrackRecorder::AudioEncoder::EncodeAudio( |
| 180 scoped_ptr<media::AudioBus> audio_bus, | 238 scoped_ptr<media::AudioBus> input_bus, |
| 181 const base::TimeTicks& capture_time) { | 239 const base::TimeTicks& capture_time) { |
| 240 DVLOG(1) << __FUNCTION__ << ", #frames " << input_bus->frames(); | |
| 182 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 241 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 183 DCHECK_EQ(audio_bus->channels(), audio_params_.channels()); | 242 DCHECK_EQ(input_bus->channels(), input_params_.channels()); |
| 243 DCHECK_EQ(input_bus->frames(), input_params_.frames_per_buffer()); | |
| 244 DCHECK(!capture_time.is_null()); | |
| 245 DCHECK(converter_); | |
| 184 | 246 |
| 185 if (!is_initialized()) | 247 if (!is_initialized()) |
| 186 return; | 248 return; |
| 249 // If |fifo_| is empty, cache the capture time. | |
| 250 if (fifo_->frames() == 0) | |
| 251 capture_time_of_first_buffer_ = capture_time; | |
| 252 fifo_->Push(input_bus.release()); | |
|
miu
2016/01/27 01:52:59
Memory leak: Don't release the scoped_ptr here.
Y
mcasas
2016/01/28 01:28:19
Done
| |
| 187 | 253 |
| 188 base::TimeDelta buffer_fill_duration = | 254 // Wait to have enough |input_bus|s queued up to guarantee a satisfactory |
| 189 buffer_fill_end_ * buffer_duration_ / frames_per_buffer_; | 255 // conversion. Luckily here there is an integerkRatioInputToOutputBuffers:1 |
| 190 base::TimeTicks buffer_capture_time = capture_time - buffer_fill_duration; | 256 // ratio, possible since all buffers are multiples of 10ms. |
| 191 | 257 while (fifo_->frames() >= |
| 192 // Encode all audio in |audio_bus| into zero or more packets. | 258 kRatioInputToOutputBuffers * input_params_.frames_per_buffer()) { |
| 193 int src_pos = 0; | 259 scoped_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( |
| 194 while (src_pos < audio_bus->frames()) { | 260 output_params_.channels(), kOpusPreferredFramesPerBuffer); |
| 195 const int num_samples_to_xfer = std::min( | 261 converter_->Convert(audio_bus.get()); |
| 196 frames_per_buffer_ - buffer_fill_end_, audio_bus->frames() - src_pos); | 262 ToInterleaved(audio_bus.release(), buffer_.get()); |
| 197 TransferSamplesIntoBuffer(audio_bus.get(), src_pos, buffer_fill_end_, | |
| 198 num_samples_to_xfer); | |
| 199 src_pos += num_samples_to_xfer; | |
| 200 buffer_fill_end_ += num_samples_to_xfer; | |
| 201 | |
| 202 if (buffer_fill_end_ < frames_per_buffer_) | |
| 203 break; | |
| 204 | 263 |
| 205 scoped_ptr<std::string> encoded_data(new std::string()); | 264 scoped_ptr<std::string> encoded_data(new std::string()); |
| 206 if (EncodeFromFilledBuffer(encoded_data.get())) { | 265 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, |
| 207 on_encoded_audio_cb_.Run(audio_params_, std::move(encoded_data), | 266 encoded_data.get())) { |
| 208 buffer_capture_time); | 267 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), |
| 268 capture_time_of_first_buffer_); | |
|
miu
2016/01/27 01:52:58
After this, you need to increment capture_time_of_
mcasas
2016/01/28 01:28:19
Done.
| |
| 209 } | 269 } |
| 270 } | |
| 271 } | |
| 210 | 272 |
| 211 // Reset the capture timestamp and internal buffer for next set of frames. | 273 double AudioTrackRecorder::AudioEncoder::ProvideInput( |
| 212 buffer_capture_time += buffer_duration_; | 274 media::AudioBus* audio_bus, |
| 213 buffer_fill_end_ = 0; | 275 base::TimeDelta buffer_delay) { |
| 214 } | 276 if (fifo_->frames() >= audio_bus->frames()) |
| 277 fifo_->Consume(audio_bus, 0, audio_bus->frames()); | |
| 278 else | |
| 279 audio_bus->Zero(); | |
| 280 // Return volume greater than zero to indicate we have more data. | |
| 281 return 1.0; | |
| 215 } | 282 } |
| 216 | 283 |
| 217 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { | 284 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
| 218 // We don't DCHECK that we're on the encoder thread here, as this could be | 285 // We don't DCHECK that we're on the encoder thread here, as this could be |
| 219 // called from the dtor (main thread) or from OnSetForamt() (render thread); | 286 // called from the dtor (main thread) or from OnSetForamt() (render thread); |
| 220 if (opus_encoder_) { | 287 if (opus_encoder_) { |
| 221 opus_encoder_destroy(opus_encoder_); | 288 opus_encoder_destroy(opus_encoder_); |
| 222 opus_encoder_ = nullptr; | 289 opus_encoder_ = nullptr; |
| 223 } | 290 } |
| 224 } | 291 } |
| 225 | 292 |
| 226 void AudioTrackRecorder::AudioEncoder::TransferSamplesIntoBuffer( | |
| 227 const media::AudioBus* audio_bus, | |
| 228 int source_offset, | |
| 229 int buffer_fill_offset, | |
| 230 int num_samples) { | |
| 231 // TODO(ajose): Consider replacing with AudioBus::ToInterleaved(). | |
| 232 // http://crbug.com/547918 | |
| 233 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | |
| 234 DCHECK(is_initialized()); | |
| 235 // Opus requires channel-interleaved samples in a single array. | |
| 236 for (int ch = 0; ch < audio_bus->channels(); ++ch) { | |
| 237 const float* src = audio_bus->channel(ch) + source_offset; | |
| 238 const float* const src_end = src + num_samples; | |
| 239 float* dest = | |
| 240 buffer_.get() + buffer_fill_offset * audio_params_.channels() + ch; | |
| 241 for (; src < src_end; ++src, dest += audio_params_.channels()) | |
| 242 *dest = *src; | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 bool AudioTrackRecorder::AudioEncoder::EncodeFromFilledBuffer( | |
| 247 std::string* out) { | |
| 248 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | |
| 249 DCHECK(is_initialized()); | |
| 250 | |
| 251 out->resize(OPUS_MAX_PAYLOAD_SIZE); | |
| 252 const opus_int32 result = opus_encode_float( | |
| 253 opus_encoder_, buffer_.get(), frames_per_buffer_, | |
| 254 reinterpret_cast<uint8_t*>(string_as_array(out)), OPUS_MAX_PAYLOAD_SIZE); | |
| 255 if (result > 1) { | |
| 256 // TODO(ajose): Investigate improving this. http://crbug.com/547918 | |
| 257 out->resize(result); | |
| 258 return true; | |
| 259 } | |
| 260 // If |result| in {0,1}, do nothing; the documentation says that a return | |
| 261 // value of zero or one means the packet does not need to be transmitted. | |
| 262 // Otherwise, we have an error. | |
| 263 DLOG_IF(ERROR, result < 0) << __FUNCTION__ | |
| 264 << " failed: " << opus_strerror(result); | |
| 265 return false; | |
| 266 } | |
| 267 | |
| 268 AudioTrackRecorder::AudioTrackRecorder( | 293 AudioTrackRecorder::AudioTrackRecorder( |
| 269 const blink::WebMediaStreamTrack& track, | 294 const blink::WebMediaStreamTrack& track, |
| 270 const OnEncodedAudioCB& on_encoded_audio_cb, | 295 const OnEncodedAudioCB& on_encoded_audio_cb, |
| 271 int32_t bits_per_second) | 296 int32_t bits_per_second) |
| 272 : track_(track), | 297 : track_(track), |
| 273 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb), | 298 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb), |
| 274 bits_per_second)), | 299 bits_per_second)), |
| 275 encoder_thread_("AudioEncoderThread") { | 300 encoder_thread_("AudioEncoderThread") { |
| 276 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 301 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 277 DCHECK(!track_.isNull()); | 302 DCHECK(!track_.isNull()); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 309 | 334 |
| 310 scoped_ptr<media::AudioBus> audio_data = | 335 scoped_ptr<media::AudioBus> audio_data = |
| 311 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()); | 336 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()); |
| 312 audio_bus.CopyTo(audio_data.get()); | 337 audio_bus.CopyTo(audio_data.get()); |
| 313 | 338 |
| 314 encoder_thread_.task_runner()->PostTask( | 339 encoder_thread_.task_runner()->PostTask( |
| 315 FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, encoder_, | 340 FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, encoder_, |
| 316 base::Passed(&audio_data), capture_time)); | 341 base::Passed(&audio_data), capture_time)); |
| 317 } | 342 } |
| 318 | 343 |
| 319 int AudioTrackRecorder::GetOpusBufferDuration(int sample_rate) { | |
| 320 // Valid buffer durations in millseconds. Note there are other valid | |
| 321 // durations for Opus, see https://tools.ietf.org/html/rfc6716#section-2.1.4 | |
| 322 // Descending order as longer durations can increase compression performance. | |
| 323 const std::vector<int> opus_valid_buffer_durations_ms = {60, 40, 20, 10}; | |
| 324 | |
| 325 // Search for a duration such that |sample_rate| % |buffers_per_second| == 0, | |
| 326 // where |buffers_per_second| = 1000ms / |possible_duration|. | |
| 327 for (auto possible_duration : opus_valid_buffer_durations_ms) { | |
| 328 if (sample_rate * possible_duration % 1000 == 0) { | |
| 329 return possible_duration; | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 // Otherwise, couldn't find a good duration. | |
| 334 return 0; | |
| 335 } | |
| 336 | |
| 337 } // namespace content | 344 } // namespace content |
| OLD | NEW |