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

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

Issue 1579693006: MediaRecorder: support sampling rate adaption in AudioTrackRecorder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: miu@s comments, rebased (bits per second stuff) Created 4 years, 11 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
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/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
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
OLDNEW
« no previous file with comments | « content/renderer/media/audio_track_recorder.h ('k') | content/renderer/media/audio_track_recorder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698