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

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

Issue 1610473002: MediaRecorder: wire the bitRate settings in Blink and content (2nd go) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments addressed. Added TODO for unsigned->signed change 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"
(...skipping 27 matching lines...) Expand all
38 } // anonymous namespace 38 } // anonymous namespace
39 39
40 // Nested class encapsulating opus-related encoding details. 40 // Nested class encapsulating opus-related encoding details.
41 // AudioEncoder is created and destroyed on ATR's main thread (usually the 41 // AudioEncoder is created and destroyed on ATR's main thread (usually the
42 // main render thread) but otherwise should operate entirely on 42 // main render thread) but otherwise should operate entirely on
43 // |encoder_thread_|, which is owned by AudioTrackRecorder. Be sure to delete 43 // |encoder_thread_|, which is owned by AudioTrackRecorder. Be sure to delete
44 // |encoder_thread_| before deleting the AudioEncoder using it. 44 // |encoder_thread_| before deleting the AudioEncoder using it.
45 class AudioTrackRecorder::AudioEncoder 45 class AudioTrackRecorder::AudioEncoder
46 : public base::RefCountedThreadSafe<AudioEncoder> { 46 : public base::RefCountedThreadSafe<AudioEncoder> {
47 public: 47 public:
48 explicit AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb) 48 AudioEncoder(const OnEncodedAudioCB& on_encoded_audio_cb,
49 : on_encoded_audio_cb_(on_encoded_audio_cb), opus_encoder_(nullptr) { 49 int32_t bits_per_second);
50 // AudioEncoder is constructed on the thread that ATR lives on, but should
51 // operate only on the encoder thread after that. Reset
52 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread()
53 // will be from the encoder thread.
54 encoder_thread_checker_.DetachFromThread();
55 }
56 50
57 void OnSetFormat(const media::AudioParameters& params); 51 void OnSetFormat(const media::AudioParameters& params);
58 52
59 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus, 53 void EncodeAudio(scoped_ptr<media::AudioBus> audio_bus,
60 const base::TimeTicks& capture_time); 54 const base::TimeTicks& capture_time);
61 55
62 private: 56 private:
63 friend class base::RefCountedThreadSafe<AudioEncoder>; 57 friend class base::RefCountedThreadSafe<AudioEncoder>;
64 58
65 ~AudioEncoder(); 59 ~AudioEncoder();
66 60
67 bool is_initialized() const { return !!opus_encoder_; } 61 bool is_initialized() const { return !!opus_encoder_; }
68 62
69 void DestroyExistingOpusEncoder(); 63 void DestroyExistingOpusEncoder();
70 64
71 void TransferSamplesIntoBuffer(const media::AudioBus* audio_bus, 65 void TransferSamplesIntoBuffer(const media::AudioBus* audio_bus,
72 int source_offset, 66 int source_offset,
73 int buffer_fill_offset, 67 int buffer_fill_offset,
74 int num_samples); 68 int num_samples);
75 bool EncodeFromFilledBuffer(std::string* out); 69 bool EncodeFromFilledBuffer(std::string* out);
76 70
77 const OnEncodedAudioCB on_encoded_audio_cb_; 71 const OnEncodedAudioCB on_encoded_audio_cb_;
78 72
73 // Target bitrate for Opus. If 0, Opus provide automatic bitrate is used.
74 const int32_t bits_per_second_;
75
79 base::ThreadChecker encoder_thread_checker_; 76 base::ThreadChecker encoder_thread_checker_;
80 77
81 // In the case where a call to EncodeAudio() cannot completely fill the 78 // In the case where a call to EncodeAudio() cannot completely fill the
82 // buffer, this points to the position at which to populate data in a later 79 // buffer, this points to the position at which to populate data in a later
83 // call. 80 // call.
84 int buffer_fill_end_; 81 int buffer_fill_end_;
85 82
86 int frames_per_buffer_; 83 int frames_per_buffer_;
87 84
88 // The duration of one set of frames of encoded audio samples. 85 // The duration of one set of frames of encoded audio samples.
89 base::TimeDelta buffer_duration_; 86 base::TimeDelta buffer_duration_;
90 87
91 media::AudioParameters audio_params_; 88 media::AudioParameters audio_params_;
92 89
93 // Buffer for passing AudioBus data to OpusEncoder. 90 // Buffer for passing AudioBus data to OpusEncoder.
94 scoped_ptr<float[]> buffer_; 91 scoped_ptr<float[]> buffer_;
95 92
96 OpusEncoder* opus_encoder_; 93 OpusEncoder* opus_encoder_;
97 94
98 DISALLOW_COPY_AND_ASSIGN(AudioEncoder); 95 DISALLOW_COPY_AND_ASSIGN(AudioEncoder);
99 }; 96 };
100 97
98 AudioTrackRecorder::AudioEncoder::AudioEncoder(
99 const OnEncodedAudioCB& on_encoded_audio_cb,
100 int32_t bits_per_second)
101 : on_encoded_audio_cb_(on_encoded_audio_cb),
102 bits_per_second_(bits_per_second),
103 opus_encoder_(nullptr) {
104 // AudioEncoder is constructed on the thread that ATR lives on, but should
105 // operate only on the encoder thread after that. Reset
106 // |encoder_thread_checker_| here, as the next call to CalledOnValidThread()
107 // will be from the encoder thread.
108 encoder_thread_checker_.DetachFromThread();
109 }
110
101 AudioTrackRecorder::AudioEncoder::~AudioEncoder() { 111 AudioTrackRecorder::AudioEncoder::~AudioEncoder() {
102 // We don't DCHECK that we're on the encoder thread here, as it should have 112 // We don't DCHECK that we're on the encoder thread here, as it should have
103 // already been deleted at this point. 113 // already been deleted at this point.
104 DestroyExistingOpusEncoder(); 114 DestroyExistingOpusEncoder();
105 } 115 }
106 116
107 void AudioTrackRecorder::AudioEncoder::OnSetFormat( 117 void AudioTrackRecorder::AudioEncoder::OnSetFormat(
108 const media::AudioParameters& params) { 118 const media::AudioParameters& params) {
109 DCHECK(encoder_thread_checker_.CalledOnValidThread()); 119 DCHECK(encoder_thread_checker_.CalledOnValidThread());
110 if (audio_params_.Equals(params)) 120 if (audio_params_.Equals(params))
111 return; 121 return;
112 122
113 DestroyExistingOpusEncoder(); 123 DestroyExistingOpusEncoder();
114 124
115 if (!params.IsValid()) { 125 if (!params.IsValid() || params.channels() > 2) {
116 DLOG(ERROR) << "Invalid audio params: " << params.AsHumanReadableString(); 126 DLOG(ERROR) << "Invalid audio params: " << params.AsHumanReadableString();
117 return; 127 return;
118 } 128 }
119 129
120 buffer_duration_ = base::TimeDelta::FromMilliseconds( 130 buffer_duration_ = base::TimeDelta::FromMilliseconds(
121 AudioTrackRecorder::GetOpusBufferDuration(params.sample_rate())); 131 AudioTrackRecorder::GetOpusBufferDuration(params.sample_rate()));
122 if (buffer_duration_ == base::TimeDelta()) { 132 if (buffer_duration_ == base::TimeDelta()) {
123 DLOG(ERROR) << "Could not find a valid |buffer_duration| for the given " 133 DLOG(ERROR) << "Could not find a valid |buffer_duration| for the given "
124 << "sample rate: " << params.sample_rate(); 134 << "sample rate: " << params.sample_rate();
125 return; 135 return;
126 } 136 }
127 137
128 frames_per_buffer_ = 138 frames_per_buffer_ =
129 params.sample_rate() * buffer_duration_.InMilliseconds() / 1000; 139 params.sample_rate() * buffer_duration_.InMilliseconds() / 1000;
130 if (frames_per_buffer_ * params.channels() > MAX_SAMPLES_PER_BUFFER) { 140 if (frames_per_buffer_ * params.channels() > MAX_SAMPLES_PER_BUFFER) {
131 DLOG(ERROR) << "Invalid |frames_per_buffer_|: " << frames_per_buffer_; 141 DLOG(ERROR) << "Invalid |frames_per_buffer_|: " << frames_per_buffer_;
132 return; 142 return;
133 } 143 }
134 144
135 // Initialize AudioBus buffer for OpusEncoder. 145 // Initialize AudioBus buffer for OpusEncoder.
136 buffer_fill_end_ = 0; 146 buffer_fill_end_ = 0;
137 buffer_.reset(new float[params.channels() * frames_per_buffer_]); 147 buffer_.reset(new float[params.channels() * frames_per_buffer_]);
138 148
139 // Initialize OpusEncoder. 149 // Initialize OpusEncoder.
150 DCHECK((params.sample_rate() != 48000) || (params.sample_rate() != 24000) ||
brucedawson 2016/01/26 18:28:47 This DCHECK is accidentally tautological. There ar
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();
140 int opus_result; 155 int opus_result;
141 opus_encoder_ = opus_encoder_create(params.sample_rate(), params.channels(), 156 opus_encoder_ = opus_encoder_create(params.sample_rate(), params.channels(),
142 OPUS_APPLICATION_AUDIO, &opus_result); 157 OPUS_APPLICATION_AUDIO, &opus_result);
143 if (opus_result < 0) { 158 if (opus_result < 0) {
144 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result) 159 DLOG(ERROR) << "Couldn't init opus encoder: " << opus_strerror(opus_result)
145 << ", sample rate: " << params.sample_rate() 160 << ", sample rate: " << params.sample_rate()
146 << ", channels: " << params.channels(); 161 << ", channels: " << params.channels();
147 return; 162 return;
148 } 163 }
149 164
150 // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a 165 // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a
151 // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms 166 // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms
152 // buffer duration. The opus library authors may, of course, adjust this in 167 // buffer duration. The opus library authors may, of course, adjust this in
153 // later versions. 168 // later versions.
154 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(OPUS_AUTO)) != OPUS_OK) { 169 const opus_int32 bitrate =
155 DLOG(ERROR) << "Failed to set opus bitrate."; 170 (bits_per_second_ > 0) ? bits_per_second_ : OPUS_AUTO;
171 if (opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) {
172 DLOG(ERROR) << "Failed to set opus bitrate: " << bitrate;
156 return; 173 return;
157 } 174 }
158 175
159 audio_params_ = params; 176 audio_params_ = params;
160 } 177 }
161 178
162 void AudioTrackRecorder::AudioEncoder::EncodeAudio( 179 void AudioTrackRecorder::AudioEncoder::EncodeAudio(
163 scoped_ptr<media::AudioBus> audio_bus, 180 scoped_ptr<media::AudioBus> audio_bus,
164 const base::TimeTicks& capture_time) { 181 const base::TimeTicks& capture_time) {
165 DCHECK(encoder_thread_checker_.CalledOnValidThread()); 182 DCHECK(encoder_thread_checker_.CalledOnValidThread());
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 // If |result| in {0,1}, do nothing; the documentation says that a return 260 // If |result| in {0,1}, do nothing; the documentation says that a return
244 // value of zero or one means the packet does not need to be transmitted. 261 // value of zero or one means the packet does not need to be transmitted.
245 // Otherwise, we have an error. 262 // Otherwise, we have an error.
246 DLOG_IF(ERROR, result < 0) << __FUNCTION__ 263 DLOG_IF(ERROR, result < 0) << __FUNCTION__
247 << " failed: " << opus_strerror(result); 264 << " failed: " << opus_strerror(result);
248 return false; 265 return false;
249 } 266 }
250 267
251 AudioTrackRecorder::AudioTrackRecorder( 268 AudioTrackRecorder::AudioTrackRecorder(
252 const blink::WebMediaStreamTrack& track, 269 const blink::WebMediaStreamTrack& track,
253 const OnEncodedAudioCB& on_encoded_audio_cb) 270 const OnEncodedAudioCB& on_encoded_audio_cb,
271 int32_t bits_per_second)
254 : track_(track), 272 : track_(track),
255 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb))), 273 encoder_(new AudioEncoder(media::BindToCurrentLoop(on_encoded_audio_cb),
274 bits_per_second)),
256 encoder_thread_("AudioEncoderThread") { 275 encoder_thread_("AudioEncoderThread") {
257 DCHECK(main_render_thread_checker_.CalledOnValidThread()); 276 DCHECK(main_render_thread_checker_.CalledOnValidThread());
258 DCHECK(!track_.isNull()); 277 DCHECK(!track_.isNull());
259 DCHECK(track_.extraData()); 278 DCHECK(track_.extraData());
260 279
261 // Start the |encoder_thread_|. From this point on, |encoder_| should work 280 // Start the |encoder_thread_|. From this point on, |encoder_| should work
262 // only on |encoder_thread_|, as enforced by DCHECKs. 281 // only on |encoder_thread_|, as enforced by DCHECKs.
263 DCHECK(!encoder_thread_.IsRunning()); 282 DCHECK(!encoder_thread_.IsRunning());
264 encoder_thread_.Start(); 283 encoder_thread_.Start();
265 284
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 if (sample_rate * possible_duration % 1000 == 0) { 328 if (sample_rate * possible_duration % 1000 == 0) {
310 return possible_duration; 329 return possible_duration;
311 } 330 }
312 } 331 }
313 332
314 // Otherwise, couldn't find a good duration. 333 // Otherwise, couldn't find a good duration.
315 return 0; 334 return 0;
316 } 335 }
317 336
318 } // namespace content 337 } // 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