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 enum : int { |
29 // This is the recommended value, according to documentation in | 31 // Recommended value for opus_encode_float(), according to documentation in |
30 // third_party/opus/src/include/opus.h, so that the Opus encoder does not | 32 // third_party/opus/src/include/opus.h, so that the Opus encoder does not |
31 // degrade the audio due to memory constraints. | 33 // degrade the audio due to memory constraints, and is independent of the |
32 OPUS_MAX_PAYLOAD_SIZE = 4000, | 34 // duration of the encoded buffer. |
35 kOpusMaxDataBytes = 4000, | |
33 | 36 |
34 // Support for max sampling rate of 48KHz, 2 channels, 60 ms duration. | 37 // Opus preferred sampling rate for encoding. This is also the one WebM likes |
35 MAX_SAMPLES_PER_BUFFER = 48 * 2 * 60, | 38 // to have: https://wiki.xiph.org/MatroskaOpus. |
39 kOpusPreferredSamplingRate = 48000, | |
40 | |
41 // Media Stream Audio Tracks always send 10ms worth of Audio. | |
42 kMediaStreamTrackBufferDurationMs = 10, | |
43 // For quality reasons we try to encode 60ms, the maximum Opus buffer. | |
44 kOpusPreferredBufferDurationMs = 60, | |
36 }; | 45 }; |
37 | 46 |
47 // Conversion between buffers following a N:1 length ratio is much easier, as is | |
48 // the case here. This parameter represents that ratio: need N input buffers for | |
49 // 1 output buffer. | |
50 static const int kRatioInputToOutputBuffers = | |
51 kOpusPreferredBufferDurationMs / kMediaStreamTrackBufferDurationMs; | |
52 | |
53 // The amount of Frames in a 60 ms buffer @ 48000 samples/second. | |
54 static const int kOpusPreferredFramesPerBuffer = | |
miu
2016/01/29 02:37:15
nit: No need to use the 'static' keyword inside th
mcasas
2016/01/29 20:37:34
Done.
| |
55 kOpusPreferredSamplingRate * kOpusPreferredBufferDurationMs / | |
56 base::Time::kMillisecondsPerSecond; | |
57 | |
58 // Maximum amount of buffers that can be held in the AudioFifo of AudioEncoder. | |
59 // Recording is not real time, hence a certain buffering is allowed. | |
60 static const size_t kMaxNumberOfFifoBuffers = 2 * kRatioInputToOutputBuffers; | |
61 | |
62 // Tries to encode |data_in|'s |num_samples| into |data_out|. | |
63 bool DoEncode(OpusEncoder* opus_encoder, | |
64 float* data_in, | |
65 int num_samples, | |
66 std::string* data_out) { | |
67 DCHECK_EQ(kOpusPreferredFramesPerBuffer, num_samples); | |
68 | |
69 data_out->resize(kOpusMaxDataBytes); | |
70 const opus_int32 result = opus_encode_float( | |
71 opus_encoder, data_in, num_samples, | |
72 reinterpret_cast<uint8_t*>(string_as_array(data_out)), kOpusMaxDataBytes); | |
73 | |
74 if (result > 1) { | |
75 // TODO(ajose): Investigate improving this. http://crbug.com/547918 | |
76 data_out->resize(result); | |
77 return true; | |
78 } | |
79 // If |result| in {0,1}, do nothing; the documentation says that a return | |
80 // value of zero or one means the packet does not need to be transmitted. | |
81 // Otherwise, we have an error. | |
82 DLOG_IF(ERROR, result < 0) << " encode failed: " << opus_strerror(result); | |
83 return false; | |
84 } | |
85 | |
86 // Interleaves |audio_bus| channels() of floats into a single output linear | |
87 // |buffer|. | |
88 // TODO(mcasas) https://crbug.com/580391 use AudioBus::ToInterleavedFloat(). | |
89 void ToInterleaved(media::AudioBus* audio_bus, float* buffer) { | |
90 for (int ch = 0; ch < audio_bus->channels(); ++ch) { | |
91 const float* src = audio_bus->channel(ch); | |
92 const float* const src_end = src + audio_bus->frames(); | |
93 float* dest = buffer + ch; | |
94 for (; src < src_end; ++src, dest += audio_bus->channels()) | |
95 *dest = *src; | |
96 } | |
97 } | |
98 | |
38 } // anonymous namespace | 99 } // anonymous namespace |
39 | 100 |
40 // Nested class encapsulating opus-related encoding details. | 101 // Nested class encapsulating opus-related encoding details. It contains an |
41 // AudioEncoder is created and destroyed on ATR's main thread (usually the | 102 // AudioConverter to adapt incoming data to the format Opus likes to have. |
42 // main render thread) but otherwise should operate entirely on | 103 // 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 | 104 // render thread) but otherwise should operate entirely on |encoder_thread_|, |
44 // |encoder_thread_| before deleting the AudioEncoder using it. | 105 // which is owned by AudioTrackRecorder. Be sure to delete |encoder_thread_| |
106 // before deleting the AudioEncoder using it. | |
45 class AudioTrackRecorder::AudioEncoder | 107 class AudioTrackRecorder::AudioEncoder |
46 : public base::RefCountedThreadSafe<AudioEncoder> { | 108 : public base::RefCountedThreadSafe<AudioEncoder>, |
109 public media::AudioConverter::InputCallback { | |
47 public: | 110 public: |
48 AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb, | 111 AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb, |
49 int32_t bits_per_second); | 112 int32_t bits_per_second); |
50 | 113 |
51 void OnSetFormat(const media::AudioParameters& params); | 114 void OnSetFormat(const media::AudioParameters& params); |
52 | 115 |
53 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, | 116 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, |
54 const base::TimeTicks& capture_time); | 117 const base::TimeTicks& capture_time); |
55 | 118 |
56 private: | 119 private: |
57 friend class base::RefCountedThreadSafe<AudioEncoder>; | 120 friend class base::RefCountedThreadSafe<AudioEncoder>; |
58 | 121 |
59 ~AudioEncoder(); | 122 ~AudioEncoder() override; |
60 | 123 |
61 bool is_initialized() const { return !!opus_encoder_; } | 124 bool is_initialized() const { return !!opus_encoder_; } |
62 | 125 |
126 // media::AudioConverted::InputCallback implementation. | |
127 double ProvideInput(media::AudioBus* audio_bus, | |
128 base::TimeDelta buffer_delay) override; | |
129 | |
63 void DestroyExistingOpusEncoder(); | 130 void DestroyExistingOpusEncoder(); |
64 | 131 |
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_; | 132 const OnEncodedAudioCB on_encoded_audio_cb_; |
72 | 133 |
73 // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used. | 134 // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used. |
74 const int32_t bits_per_second_; | 135 const int32_t bits_per_second_; |
75 | 136 |
76 base::ThreadChecker encoder_thread_checker_; | 137 base::ThreadChecker encoder_thread_checker_; |
77 | 138 |
78 // In the case where a call to EncodeAudio() cannot completely fill the | 139 // 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 | 140 // only differ in their sample_rate() and frames_per_buffer(): output is |
80 // call. | 141 // 48ksamples/s and 2880, respectively. |
81 int buffer_fill_end_; | 142 media::AudioParameters input_params_; |
143 media::AudioParameters output_params_; | |
82 | 144 |
83 int frames_per_buffer_; | 145 // Sampling rate adapter between an OpusEncoder supported and the provided. |
84 | 146 scoped_ptr<media::AudioConverter> converter_; |
85 // The duration of one set of frames of encoded audio samples. | 147 scoped_ptr<media::AudioFifo> fifo_; |
86 base::TimeDelta buffer_duration_; | |
87 | |
88 media::AudioParameters audio_params_; | |
89 | 148 |
90 // Buffer for passing AudioBus data to OpusEncoder. | 149 // Buffer for passing AudioBus data to OpusEncoder. |
91 scoped_ptr<float[]> buffer_; | 150 scoped_ptr<float[]> buffer_; |
92 | 151 |
93 OpusEncoder* opus_encoder_; | 152 OpusEncoder* opus_encoder_; |
94 | 153 |
95 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); | 154 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); |
96 }; | 155 }; |
97 | 156 |
98 AudioTrackRecorder::AudioEncoder::AudioEncoder( | 157 AudioTrackRecorder::AudioEncoder::AudioEncoder( |
99 const OnEncodedAudioCB& on_encoded_audio_cb, | 158 const OnEncodedAudioCB& on_encoded_audio_cb, |
100 int32_t bits_per_second) | 159 int32_t bits_per_second) |
101 : on_encoded_audio_cb_(on_encoded_audio_cb), | 160 : on_encoded_audio_cb_(on_encoded_audio_cb), |
102 bits_per_second_(bits_per_second), | 161 bits_per_second_(bits_per_second), |
103 opus_encoder_(nullptr) { | 162 opus_encoder_(nullptr) { |
104 // AudioEncoder is constructed on the thread that ATR lives on, but should | 163 // AudioEncoder is constructed on the thread that ATR lives on, but should |
105 // operate only on the encoder thread after that. Reset | 164 // operate only on the encoder thread after that. Reset |
106 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() | 165 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() |
107 // will be from the encoder thread. | 166 // will be from the encoder thread. |
108 encoder_thread_checker_.DetachFromThread(); | 167 encoder_thread_checker_.DetachFromThread(); |
109 } | 168 } |
110 | 169 |
111 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { | 170 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { |
112 // We don't DCHECK that we're on the encoder thread here, as it should have | 171 // We don't DCHECK that we're on the encoder thread here, as it should have |
113 // already been deleted at this point. | 172 // already been deleted at this point. |
114 DestroyExistingOpusEncoder(); | 173 DestroyExistingOpusEncoder(); |
115 } | 174 } |
116 | 175 |
117 void AudioTrackRecorder::AudioEncoder::OnSetFormat( | 176 void AudioTrackRecorder::AudioEncoder::OnSetFormat( |
118 const media::AudioParameters& params) { | 177 const media::AudioParameters& input_params) { |
178 DVLOG(1) << __FUNCTION__; | |
119 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 179 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
120 if (audio_params_.Equals(params)) | 180 if (input_params_.Equals(input_params)) |
121 return; | 181 return; |
122 | 182 |
123 DestroyExistingOpusEncoder(); | 183 DestroyExistingOpusEncoder(); |
124 | 184 |
125 if (!params.IsValid() || params.channels() > 2) { | 185 if (!input_params.IsValid()) { |
126 DLOG(ERROR) << "Invalid audio params: " << params.AsHumanReadableString(); | 186 DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString(); |
187 return; | |
188 } | |
189 input_params_ = input_params; | |
190 input_params_.set_frames_per_buffer(input_params_.sample_rate() * | |
191 kOpusPreferredBufferDurationMs / | |
192 base::Time::kMillisecondsPerSecond); | |
193 | |
194 // third_party/libopus supports up to 2 channels (see implementation of | |
195 // opus_encoder_create()): force |output_params_| to at most those. | |
196 output_params_ = media::AudioParameters( | |
197 media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | |
198 media::GuessChannelLayout(std::min(input_params_.channels(), 2)), | |
199 kOpusPreferredSamplingRate, | |
200 input_params_.bits_per_sample(), | |
201 kOpusPreferredFramesPerBuffer); | |
202 DVLOG(1) << "|input_params_|:" << input_params_.AsHumanReadableString() | |
203 << " -->|output_params_|:" << output_params_.AsHumanReadableString(); | |
204 | |
205 converter_.reset(new media::AudioConverter(input_params_, output_params_, | |
206 false /* disable_fifo */)); | |
207 converter_->AddInput(this); | |
208 converter_->PrimeWithSilence(); | |
209 | |
210 fifo_.reset(new media::AudioFifo( | |
211 input_params_.channels(), | |
212 kMaxNumberOfFifoBuffers * input_params_.frames_per_buffer())); | |
miu
2016/01/29 02:37:15
I think the second argument to the ctor here is si
mcasas
2016/01/29 20:37:34
I'm leaving the constant kMaxNumberOfFifoBuffers f
| |
213 | |
214 buffer_.reset(new float[output_params_.channels() * | |
215 output_params_.frames_per_buffer()]); | |
216 | |
217 // Initialize OpusEncoder. | |
218 int opus_result; | |
219 opus_encoder_ = opus_encoder_create(output_params_.sample_rate(), | |
220 output_params_.channels(), | |
221 OPUS_APPLICATION_AUDIO, | |
222 &opus_result); | |
223 if (opus_result < 0) { | |
224 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result) | |
225 << ", sample rate: " << output_params_.sample_rate() | |
226 << ", channels: " << output_params_.channels(); | |
127 return; | 227 return; |
128 } | 228 } |
129 | 229 |
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 | 230 // 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 | 231 // 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 | 232 // buffer duration. The opus library authors may, of course, adjust this in |
168 // later versions. | 233 // later versions. |
169 const opus_int32 bitrate = | 234 const opus_int32 bitrate = |
170 (bits_per_second_ > 0) ? bits_per_second_ : OPUS_AUTO; | 235 (bits_per_second_ > 0) ? bits_per_second_ : OPUS_AUTO; |
171 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) { | 236 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) { |
172 DLOG(ERROR) << "Failed to set opus bitrate: " << bitrate; | 237 DLOG(ERROR) << "Failed to set opus bitrate: " << bitrate; |
173 return; | 238 return; |
174 } | 239 } |
175 | |
176 audio_params_ = params; | |
177 } | 240 } |
178 | 241 |
179 void AudioTrackRecorder::AudioEncoder::EncodeAudio( | 242 void AudioTrackRecorder::AudioEncoder::EncodeAudio( |
180 scoped_ptr<media::AudioBus> audio_bus, | 243 scoped_ptr<media::AudioBus> input_bus, |
181 const base::TimeTicks& capture_time) { | 244 const base::TimeTicks& capture_time) { |
245 DVLOG(1) << __FUNCTION__ << ", #frames " << input_bus->frames(); | |
182 DCHECK(encoder_thread_checker_.CalledOnValidThread()); | 246 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
183 DCHECK_EQ(audio_bus->channels(), audio_params_.channels()); | 247 DCHECK_EQ(input_bus->channels(), input_params_.channels()); |
248 DCHECK_EQ(input_bus->frames(), input_params_.sample_rate() * | |
miu
2016/01/29 02:37:15
You don't need this DCHECK(). AudioFifo::Push() w
mcasas
2016/01/29 20:37:34
Done.
| |
249 kMediaStreamTrackBufferDurationMs / | |
250 base::Time::kMillisecondsPerSecond); | |
251 DCHECK(!capture_time.is_null()); | |
252 DCHECK(converter_); | |
184 | 253 |
185 if (!is_initialized()) | 254 if (!is_initialized()) |
186 return; | 255 return; |
256 // TODO(mcasas): Consider using a std::deque<scoped_ptr<AudioBus>> instead of | |
257 // an AudioFifo, to avoid copying data needlessly since we know the sizes of | |
258 // both input and output and they are multiples. | |
259 fifo_->Push(input_bus.get()); | |
187 | 260 |
188 base::TimeDelta buffer_fill_duration = | 261 // Wait to have enough |input_bus|s queued up to guarantee a satisfactory |
189 buffer_fill_end_ * buffer_duration_ / frames_per_buffer_; | 262 // conversion. Luckily here there is an integerkRatioInputToOutputBuffers:1 |
miu
2016/01/29 02:37:15
Please delete the second sentence of this comment
mcasas
2016/01/29 20:37:34
Done.
| |
190 base::TimeTicks buffer_capture_time = capture_time - buffer_fill_duration; | 263 // ratio, possible since all buffers are multiples of 10ms. |
191 | 264 while (fifo_->frames() >= input_params_.frames_per_buffer()) { |
192 // Encode all audio in |audio_bus| into zero or more packets. | 265 scoped_ptr<media::AudioBus> audio_bus = media::AudioBus::Create( |
193 int src_pos = 0; | 266 output_params_.channels(), kOpusPreferredFramesPerBuffer); |
194 while (src_pos < audio_bus->frames()) { | 267 converter_->Convert(audio_bus.get()); |
195 const int num_samples_to_xfer = std::min( | 268 ToInterleaved(audio_bus.release(), buffer_.get()); |
196 frames_per_buffer_ - buffer_fill_end_, audio_bus->frames() - src_pos); | |
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 | 269 |
205 scoped_ptr<std::string> encoded_data(new std::string()); | 270 scoped_ptr<std::string> encoded_data(new std::string()); |
206 if (EncodeFromFilledBuffer(encoded_data.get())) { | 271 if (DoEncode(opus_encoder_, buffer_.get(), kOpusPreferredFramesPerBuffer, |
207 on_encoded_audio_cb_.Run(audio_params_, std::move(encoded_data), | 272 encoded_data.get())) { |
208 buffer_capture_time); | 273 const base::TimeTicks capture_time_of_first_sample = |
274 capture_time - | |
275 base::TimeDelta::FromMicroseconds(fifo_->frames() * | |
276 base::Time::kMicrosecondsPerSecond / | |
277 input_params_.sample_rate()); | |
278 on_encoded_audio_cb_.Run(output_params_, std::move(encoded_data), | |
279 capture_time_of_first_sample); | |
209 } | 280 } |
281 } | |
282 } | |
210 | 283 |
211 // Reset the capture timestamp and internal buffer for next set of frames. | 284 double AudioTrackRecorder::AudioEncoder::ProvideInput( |
212 buffer_capture_time += buffer_duration_; | 285 media::AudioBus* audio_bus, |
213 buffer_fill_end_ = 0; | 286 base::TimeDelta buffer_delay) { |
214 } | 287 if (fifo_->frames() >= audio_bus->frames()) |
miu
2016/01/29 02:37:15
AudioFifo will CHECK that there are sufficient fra
mcasas
2016/01/29 20:37:34
Done.
| |
288 fifo_->Consume(audio_bus, 0, audio_bus->frames()); | |
289 else | |
290 audio_bus->Zero(); | |
291 // Return volume greater than zero to indicate we have more data. | |
292 return 1.0; | |
215 } | 293 } |
216 | 294 |
217 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { | 295 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
218 // We don't DCHECK that we're on the encoder thread here, as this could be | 296 // 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); | 297 // called from the dtor (main thread) or from OnSetForamt() (render thread); |
220 if (opus_encoder_) { | 298 if (opus_encoder_) { |
221 opus_encoder_destroy(opus_encoder_); | 299 opus_encoder_destroy(opus_encoder_); |
222 opus_encoder_ = nullptr; | 300 opus_encoder_ = nullptr; |
223 } | 301 } |
224 } | 302 } |
225 | 303 |
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( | 304 AudioTrackRecorder::AudioTrackRecorder( |
269 const blink::WebMediaStreamTrack& track, | 305 const blink::WebMediaStreamTrack& track, |
270 const OnEncodedAudioCB& on_encoded_audio_cb, | 306 const OnEncodedAudioCB& on_encoded_audio_cb, |
271 int32_t bits_per_second) | 307 int32_t bits_per_second) |
272 : track_(track), | 308 : track_(track), |
273 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb), | 309 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb), |
274 bits_per_second)), | 310 bits_per_second)), |
275 encoder_thread_("AudioEncoderThread") { | 311 encoder_thread_("AudioEncoderThread") { |
276 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 312 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
277 DCHECK(!track_.isNull()); | 313 DCHECK(!track_.isNull()); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
309 | 345 |
310 scoped_ptr<media::AudioBus> audio_data = | 346 scoped_ptr<media::AudioBus> audio_data = |
311 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()); | 347 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()); |
312 audio_bus.CopyTo(audio_data.get()); | 348 audio_bus.CopyTo(audio_data.get()); |
313 | 349 |
314 encoder_thread_.task_runner()->PostTask( | 350 encoder_thread_.task_runner()->PostTask( |
315 FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, encoder_, | 351 FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, encoder_, |
316 base::Passed(&audio_data), capture_time)); | 352 base::Passed(&audio_data), capture_time)); |
317 } | 353 } |
318 | 354 |
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 | 355 } // namespace content |
OLD | NEW |