| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/renderer/media/audio_track_recorder.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/stl_util.h" |
| 9 #include "media/audio/audio_parameters.h" |
| 10 #include "media/base/audio_bus.h" |
| 11 #include "media/base/bind_to_current_loop.h" |
| 12 #include "third_party/opus/src/include/opus.h" |
| 13 |
| 14 namespace content { |
| 15 |
| 16 namespace { |
| 17 |
| 18 enum { |
| 19 // This is the recommended value, according to documentation in |
| 20 // third_party/opus/src/include/opus.h, so that the Opus encoder does not |
| 21 // degrade the audio due to memory constraints. |
| 22 OPUS_MAX_PAYLOAD_SIZE = 4000, |
| 23 |
| 24 // Support for max sampling rate of 48KHz, 2 channels, 60 ms duration. |
| 25 MAX_SAMPLES_PER_BUFFER = 48 * 2 * 60, |
| 26 }; |
| 27 |
| 28 // Note that this code follows the Chrome media convention of defining a "frame" |
| 29 // as "one multi-channel sample" as opposed to another common definition |
| 30 // meaning "a chunk of samples". Here this second definition of "frame" is |
| 31 // called a "buffer"; so what might be called "frame duration" is instead |
| 32 // "buffer duration", and so on. |
| 33 |
| 34 // Returns the Opus buffer duration in milliseconds, or zero if none will work |
| 35 // for the given |sample_rate|. |
| 36 static int GetOpusBufferDuration(int sample_rate) { |
| 37 // Valid buffer durations in millseconds. Note there are other valid |
| 38 // durations for Opus, see https://tools.ietf.org/html/rfc6716#section-2.1.4 |
| 39 // Descending order as longer durations can increase compression performance. |
| 40 const std::vector<int> opus_valid_buffer_durations_ms = |
| 41 {60, 40, 20, 10}; |
| 42 |
| 43 // Search for a duration such that |sample_rate| % |buffers_per_second| == 0, |
| 44 // where |buffers_per_second| = 1000ms / |possible_duration|. |
| 45 for (auto possible_duration : opus_valid_buffer_durations_ms) { |
| 46 if (sample_rate * possible_duration % 1000 == 0) { |
| 47 return possible_duration; |
| 48 } |
| 49 } |
| 50 |
| 51 // Otherwise, couldn't find a good duration. |
| 52 return 0; |
| 53 } |
| 54 |
| 55 } // anonymous namespace |
| 56 |
| 57 // Nested class encapsulating opus-related encoding details. |
| 58 // AudioEncoder is created and destroyed on ATR's main thread (usually the |
| 59 // main render thread) but otherwise should operate entirely on |
| 60 // |encoder_thread_|, which is owned by AudioTrackRecorder. Be sure to delete |
| 61 // |encoder_thread_| before deleting the AudioEncoder using it. |
| 62 class AudioTrackRecorder::AudioEncoder |
| 63 : public base::RefCountedThreadSafe<AudioEncoder> { |
| 64 public: |
| 65 explicit AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb) |
| 66 : on_encoded_audio_cb_(on_encoded_audio_cb), opus_encoder_(nullptr) { |
| 67 // AudioEncoder is constructed on the thread that ATR lives on, but should |
| 68 // operate only on the encoder thread after that. Reset |
| 69 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread() |
| 70 // will be from the encoder thread. |
| 71 encoder_thread_checker_.DetachFromThread(); |
| 72 } |
| 73 |
| 74 void OnSetFormat(const media::AudioParameters& params); |
| 75 |
| 76 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, |
| 77 const base::TimeTicks& capture_time); |
| 78 |
| 79 private: |
| 80 friend class base::RefCountedThreadSafe<AudioEncoder>; |
| 81 |
| 82 ~AudioEncoder(); |
| 83 |
| 84 bool is_initialized() const { return !!opus_encoder_; } |
| 85 |
| 86 void DestroyExistingOpusEncoder(); |
| 87 |
| 88 void TransferSamplesIntoBuffer(const media::AudioBus* audio_bus, |
| 89 int source_offset, |
| 90 int buffer_fill_offset, |
| 91 int num_samples); |
| 92 bool EncodeFromFilledBuffer(std::string* out); |
| 93 |
| 94 const OnEncodedAudioCB on_encoded_audio_cb_; |
| 95 |
| 96 base::ThreadChecker encoder_thread_checker_; |
| 97 |
| 98 // In the case where a call to EncodeAudio() cannot completely fill the |
| 99 // buffer, this points to the position at which to populate data in a later |
| 100 // call. |
| 101 int buffer_fill_end_; |
| 102 |
| 103 int frames_per_buffer_; |
| 104 |
| 105 // The duration of one set of frames of encoded audio samples. |
| 106 base::TimeDelta buffer_duration_; |
| 107 |
| 108 // The local system time associated with the start of the next set of frames |
| 109 // of encoded audio. This value is passed on to a receiver as a reference |
| 110 // clock timestamp for the purposes of synchronizing audio and video. |
| 111 base::TimeTicks buffer_capture_time_; |
| 112 |
| 113 media::AudioParameters audio_params_; |
| 114 |
| 115 // Buffer for passing AudioBus data to OpusEncoder. |
| 116 scoped_ptr<float[]> buffer_; |
| 117 |
| 118 OpusEncoder* opus_encoder_; |
| 119 |
| 120 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); |
| 121 }; |
| 122 |
| 123 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { |
| 124 // We don't DCHECK that we're on the encoder thread here, as it should have |
| 125 // already been deleted at this point. |
| 126 DestroyExistingOpusEncoder(); |
| 127 } |
| 128 |
| 129 void AudioTrackRecorder::AudioEncoder::OnSetFormat( |
| 130 const media::AudioParameters& params) { |
| 131 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 132 if (audio_params_.Equals(params)) |
| 133 return; |
| 134 |
| 135 DestroyExistingOpusEncoder(); |
| 136 |
| 137 if (!params.IsValid()) { |
| 138 DLOG(ERROR) << "Invalid audio params: " << params.AsHumanReadableString(); |
| 139 return; |
| 140 } |
| 141 |
| 142 buffer_duration_ = base::TimeDelta::FromMilliseconds( |
| 143 GetOpusBufferDuration(params.sample_rate())); |
| 144 if (buffer_duration_ == base::TimeDelta()) { |
| 145 DLOG(ERROR) << "Could not find a valid |buffer_duration| for the given " |
| 146 << "sample rate: " << params.sample_rate(); |
| 147 return; |
| 148 } |
| 149 |
| 150 frames_per_buffer_ = |
| 151 params.sample_rate() * buffer_duration_.InMilliseconds() / 1000; |
| 152 if (frames_per_buffer_ * params.channels() > MAX_SAMPLES_PER_BUFFER) { |
| 153 DLOG(ERROR) << "Invalid |frames_per_buffer_|: " << frames_per_buffer_; |
| 154 return; |
| 155 } |
| 156 |
| 157 // Initialize AudioBus buffer for OpusEncoder. |
| 158 buffer_fill_end_ = 0; |
| 159 buffer_.reset(new float[params.channels() * frames_per_buffer_]); |
| 160 |
| 161 // Initialize OpusEncoder. |
| 162 int opus_result; |
| 163 opus_encoder_ = opus_encoder_create(params.sample_rate(), params.channels(), |
| 164 OPUS_APPLICATION_AUDIO, &opus_result); |
| 165 if (opus_result < 0) { |
| 166 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result); |
| 167 return; |
| 168 } |
| 169 |
| 170 // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a |
| 171 // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms |
| 172 // buffer duration. The opus library authors may, of course, adjust this in |
| 173 // later versions. |
| 174 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(OPUS_AUTO)) != OPUS_OK) { |
| 175 DLOG(ERROR) << "Failed to set opus bitrate."; |
| 176 return; |
| 177 } |
| 178 |
| 179 audio_params_ = params; |
| 180 } |
| 181 |
| 182 void AudioTrackRecorder::AudioEncoder::EncodeAudio( |
| 183 scoped_ptr<media::AudioBus> audio_bus, |
| 184 const base::TimeTicks& capture_time) { |
| 185 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 186 DCHECK_EQ(audio_bus->channels(), audio_params_.channels()); |
| 187 |
| 188 if (!is_initialized()) |
| 189 return; |
| 190 |
| 191 base::TimeDelta buffer_fill_duration = |
| 192 buffer_fill_end_ * buffer_duration_ / frames_per_buffer_; |
| 193 buffer_capture_time_ = capture_time - buffer_fill_duration; |
| 194 |
| 195 // Encode all audio in |audio_bus| into zero or more packets. |
| 196 int src_pos = 0; |
| 197 while (src_pos < audio_bus->frames()) { |
| 198 const int num_samples_to_xfer = std::min( |
| 199 frames_per_buffer_ - buffer_fill_end_, audio_bus->frames() - src_pos); |
| 200 TransferSamplesIntoBuffer(audio_bus.get(), src_pos, buffer_fill_end_, |
| 201 num_samples_to_xfer); |
| 202 src_pos += num_samples_to_xfer; |
| 203 buffer_fill_end_ += num_samples_to_xfer; |
| 204 |
| 205 if (buffer_fill_end_ < frames_per_buffer_) |
| 206 break; |
| 207 |
| 208 scoped_ptr<std::string> encoded_data(new std::string()); |
| 209 if (EncodeFromFilledBuffer(encoded_data.get())) { |
| 210 on_encoded_audio_cb_.Run(audio_params_, encoded_data.Pass(), |
| 211 buffer_capture_time_); |
| 212 } |
| 213 |
| 214 // Reset the capture timestamp and internal buffer for next set of frames. |
| 215 buffer_capture_time_ += buffer_duration_; |
| 216 buffer_fill_end_ = 0; |
| 217 |
| 218 } |
| 219 } |
| 220 |
| 221 void AudioTrackRecorder::AudioEncoder::DestroyExistingOpusEncoder() { |
| 222 // We don't DCHECK that we're on the encoder thread here, as this could be |
| 223 // called from the dtor (main thread) or from OnSetForamt() (render thread); |
| 224 if (opus_encoder_) { |
| 225 opus_encoder_destroy(opus_encoder_); |
| 226 opus_encoder_ = nullptr; |
| 227 } |
| 228 } |
| 229 |
| 230 void AudioTrackRecorder::AudioEncoder::TransferSamplesIntoBuffer( |
| 231 const media::AudioBus* audio_bus, |
| 232 int source_offset, |
| 233 int buffer_fill_offset, |
| 234 int num_samples) { |
| 235 // TODO(ajose): Consider replacing with AudioBus::ToInterleaved(). |
| 236 // http://crbug.com/547918 |
| 237 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 238 DCHECK(is_initialized()); |
| 239 // Opus requires channel-interleaved samples in a single array. |
| 240 for (int ch = 0; ch < audio_bus->channels(); ++ch) { |
| 241 const float* src = audio_bus->channel(ch) + source_offset; |
| 242 const float* const src_end = src + num_samples; |
| 243 float* dest = |
| 244 buffer_.get() + buffer_fill_offset * audio_params_.channels() + ch; |
| 245 for (; src < src_end; ++src, dest += audio_params_.channels()) |
| 246 *dest = *src; |
| 247 } |
| 248 } |
| 249 |
| 250 bool AudioTrackRecorder::AudioEncoder::EncodeFromFilledBuffer( |
| 251 std::string* out) { |
| 252 DCHECK(encoder_thread_checker_.CalledOnValidThread()); |
| 253 DCHECK(is_initialized()); |
| 254 |
| 255 out->resize(OPUS_MAX_PAYLOAD_SIZE); |
| 256 const opus_int32 result = opus_encode_float( |
| 257 opus_encoder_, buffer_.get(), frames_per_buffer_, |
| 258 reinterpret_cast<uint8*>(string_as_array(out)), OPUS_MAX_PAYLOAD_SIZE); |
| 259 if (result > 1) { |
| 260 // TODO(ajose): Investigate improving this. http://crbug.com/547918 |
| 261 out->resize(result); |
| 262 return true; |
| 263 } |
| 264 // If |result| in {0,1}, do nothing; the documentation says that a return |
| 265 // value of zero or one means the packet does not need to be transmitted. |
| 266 // Otherwise, we have an error. |
| 267 DLOG_IF(ERROR, result < 0) << __FUNCTION__ |
| 268 << " failed: " << opus_strerror(result); |
| 269 return false; |
| 270 } |
| 271 |
| 272 AudioTrackRecorder::AudioTrackRecorder( |
| 273 const blink::WebMediaStreamTrack& track, |
| 274 const OnEncodedAudioCB& on_encoded_audio_cb) |
| 275 : track_(track), |
| 276 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb))), |
| 277 encoder_thread_("AudioEncoderThread") { |
| 278 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 279 DCHECK(!track_.isNull()); |
| 280 DCHECK(track_.extraData()); |
| 281 |
| 282 // Start the |encoder_thread_|. From this point on, |encoder_| should work |
| 283 // only on |encoder_thread_|, as enforced by DCHECKs. |
| 284 DCHECK(!encoder_thread_.IsRunning()); |
| 285 encoder_thread_.Start(); |
| 286 |
| 287 // Connect the source provider to the track as a sink. |
| 288 MediaStreamAudioSink::AddToAudioTrack(this, track_); |
| 289 } |
| 290 |
| 291 AudioTrackRecorder::~AudioTrackRecorder() { |
| 292 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 293 MediaStreamAudioSink::RemoveFromAudioTrack(this, track_); |
| 294 } |
| 295 |
| 296 void AudioTrackRecorder::OnSetFormat(const media::AudioParameters& params) { |
| 297 DCHECK(encoder_thread_.IsRunning()); |
| 298 // If the source is restarted, might have changed to another capture thread. |
| 299 capture_thread_checker_.DetachFromThread(); |
| 300 DCHECK(capture_thread_checker_.CalledOnValidThread()); |
| 301 |
| 302 encoder_thread_.task_runner()->PostTask( |
| 303 FROM_HERE, base::Bind(&AudioEncoder::OnSetFormat, encoder_, params)); |
| 304 } |
| 305 |
| 306 void AudioTrackRecorder::OnData(const media::AudioBus& audio_bus, |
| 307 base::TimeTicks capture_time) { |
| 308 DCHECK(encoder_thread_.IsRunning()); |
| 309 DCHECK(capture_thread_checker_.CalledOnValidThread()); |
| 310 DCHECK(!capture_time.is_null()); |
| 311 |
| 312 scoped_ptr<media::AudioBus> audio_data = |
| 313 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()); |
| 314 audio_bus.CopyTo(audio_data.get()); |
| 315 |
| 316 encoder_thread_.task_runner()->PostTask( |
| 317 FROM_HERE, base::Bind(&AudioEncoder::EncodeAudio, encoder_, |
| 318 base::Passed(&audio_data), capture_time)); |
| 319 } |
| 320 |
| 321 int AudioTrackRecorder::BufferDurationForTesting(int sample_rate) { |
| 322 return GetOpusBufferDuration(sample_rate); |
| 323 }; |
| 324 |
| 325 } // namespace content |
| OLD | NEW |