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

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

Issue 856843002: Use audio shifter instead of a fifo for local mediastream playback. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 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
« no previous file with comments | « content/renderer/media/webrtc_local_audio_renderer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/webrtc_local_audio_renderer.h" 5 #include "content/renderer/media/webrtc_local_audio_renderer.h"
6 6
7 #include "base/debug/trace_event.h" 7 #include "base/debug/trace_event.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/synchronization/lock.h" 11 #include "base/synchronization/lock.h"
12 #include "content/renderer/media/audio_device_factory.h" 12 #include "content/renderer/media/audio_device_factory.h"
13 #include "content/renderer/media/media_stream_dispatcher.h" 13 #include "content/renderer/media/media_stream_dispatcher.h"
14 #include "content/renderer/media/webrtc_audio_capturer.h" 14 #include "content/renderer/media/webrtc_audio_capturer.h"
15 #include "content/renderer/media/webrtc_audio_renderer.h" 15 #include "content/renderer/media/webrtc_audio_renderer.h"
16 #include "content/renderer/render_frame_impl.h" 16 #include "content/renderer/render_frame_impl.h"
17 #include "media/audio/audio_output_device.h" 17 #include "media/audio/audio_output_device.h"
18 #include "media/base/audio_bus.h" 18 #include "media/base/audio_bus.h"
19 #include "media/base/audio_fifo.h" 19 #include "media/base/audio_shifter.h"
20 20
21 namespace content { 21 namespace content {
22 22
23 namespace { 23 namespace {
24 24
25 enum LocalRendererSinkStates { 25 enum LocalRendererSinkStates {
26 kSinkStarted = 0, 26 kSinkStarted = 0,
27 kSinkNeverStarted, 27 kSinkNeverStarted,
28 kSinkStatesMax // Must always be last! 28 kSinkStatesMax // Must always be last!
29 }; 29 };
30 30
31 } // namespace 31 } // namespace
32 32
33 // media::AudioRendererSink::RenderCallback implementation 33 // media::AudioRendererSink::RenderCallback implementation
34 int WebRtcLocalAudioRenderer::Render( 34 int WebRtcLocalAudioRenderer::Render(
35 media::AudioBus* audio_bus, int audio_delay_milliseconds) { 35 media::AudioBus* audio_bus, int audio_delay_milliseconds) {
36 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render"); 36 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
37 base::AutoLock auto_lock(thread_lock_); 37 base::AutoLock auto_lock(thread_lock_);
38 38
39 if (!playing_ || !volume_ || !loopback_fifo_) { 39 if (!playing_ || !volume_ || !audio_shifter_) {
40 audio_bus->Zero(); 40 audio_bus->Zero();
41 return 0; 41 return 0;
42 } 42 }
43 43
44 // Provide data by reading from the FIFO if the FIFO contains enough 44 audio_shifter_->Pull(
henrika (OOO until Aug 14) 2015/01/21 08:54:38 What effect does the provided time value have here
hubbe 2015/01/23 00:54:56 The audio shifter tries to provide audio for the g
45 // to fulfill the request. 45 audio_bus,
46 if (loopback_fifo_->frames() >= audio_bus->frames()) { 46 base::TimeTicks::Now() -
47 loopback_fifo_->Consume(audio_bus, 0, audio_bus->frames()); 47 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds));
48 } else {
49 audio_bus->Zero();
50 // This warning is perfectly safe if it happens for the first audio
51 // frames. It should not happen in a steady-state mode.
52 DVLOG(2) << "loopback FIFO is not full enough yet";
53 }
54 48
55 return audio_bus->frames(); 49 return audio_bus->frames();
56 } 50 }
57 51
58 void WebRtcLocalAudioRenderer::OnRenderError() { 52 void WebRtcLocalAudioRenderer::OnRenderError() {
59 NOTIMPLEMENTED(); 53 NOTIMPLEMENTED();
60 } 54 }
61 55
62 // content::MediaStreamAudioSink implementation 56 // content::MediaStreamAudioSink implementation
63 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus, 57 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus,
64 base::TimeTicks estimated_capture_time) { 58 base::TimeTicks estimated_capture_time) {
65 DCHECK(capture_thread_checker_.CalledOnValidThread()); 59 DCHECK(capture_thread_checker_.CalledOnValidThread());
66 DCHECK(!estimated_capture_time.is_null()); 60 DCHECK(!estimated_capture_time.is_null());
67 61
68 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData"); 62 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
69 63
70 base::AutoLock auto_lock(thread_lock_); 64 base::AutoLock auto_lock(thread_lock_);
71 if (!playing_ || !volume_ || !loopback_fifo_) 65 if (!playing_ || !volume_ || !audio_shifter_)
72 return; 66 return;
73 67
74 // Push captured audio to FIFO so it can be read by a local sink. 68 scoped_ptr<media::AudioBus> audio_data(
75 if ((loopback_fifo_->frames() + audio_bus.frames()) <= 69 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()));
76 loopback_fifo_->max_frames()) { 70 audio_bus.CopyTo(audio_data.get());
77 // TODO(miu): Make sure the Render() method accounts for time shifting 71 audio_shifter_->Push(audio_data.Pass(), estimated_capture_time);
78 // appropriately, per the comments for the usage of the 72 const base::TimeTicks now = base::TimeTicks::Now();
79 // |estimated_capture_time| field found in media_stream_audio_sink.h. 73 total_render_time_ += now - last_render_time_;
80 // http://crbug.com/437064 74 last_render_time_ = now;
81 loopback_fifo_->Push(&audio_bus);
82
83 const base::TimeTicks now = base::TimeTicks::Now();
84 total_render_time_ += now - last_render_time_;
85 last_render_time_ = now;
86 } else {
87 DVLOG(1) << "FIFO is full";
88 }
89 } 75 }
90 76
91 void WebRtcLocalAudioRenderer::OnSetFormat( 77 void WebRtcLocalAudioRenderer::OnSetFormat(
92 const media::AudioParameters& params) { 78 const media::AudioParameters& params) {
93 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()"; 79 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
94 // If the source is restarted, we might have changed to another capture 80 // If the source is restarted, we might have changed to another capture
95 // thread. 81 // thread.
96 capture_thread_checker_.DetachFromThread(); 82 capture_thread_checker_.DetachFromThread();
97 DCHECK(capture_thread_checker_.CalledOnValidThread()); 83 DCHECK(capture_thread_checker_.CalledOnValidThread());
98 84
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 playing_ = false; 131 playing_ = false;
146 } 132 }
147 133
148 void WebRtcLocalAudioRenderer::Stop() { 134 void WebRtcLocalAudioRenderer::Stop() {
149 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()"; 135 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
150 DCHECK(message_loop_->BelongsToCurrentThread()); 136 DCHECK(message_loop_->BelongsToCurrentThread());
151 137
152 { 138 {
153 base::AutoLock auto_lock(thread_lock_); 139 base::AutoLock auto_lock(thread_lock_);
154 playing_ = false; 140 playing_ = false;
155 loopback_fifo_.reset(); 141 audio_shifter_.reset();
156 } 142 }
157 143
158 // Stop the output audio stream, i.e, stop asking for data to render. 144 // Stop the output audio stream, i.e, stop asking for data to render.
159 // It is safer to call Stop() on the |sink_| to clean up the resources even 145 // It is safer to call Stop() on the |sink_| to clean up the resources even
160 // when the |sink_| is never started. 146 // when the |sink_| is never started.
161 if (sink_.get()) { 147 if (sink_.get()) {
162 sink_->Stop(); 148 sink_->Stop();
163 sink_ = NULL; 149 sink_ = NULL;
164 } 150 }
165 151
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 void WebRtcLocalAudioRenderer::MaybeStartSink() { 225 void WebRtcLocalAudioRenderer::MaybeStartSink() {
240 DCHECK(message_loop_->BelongsToCurrentThread()); 226 DCHECK(message_loop_->BelongsToCurrentThread());
241 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()"; 227 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
242 228
243 if (!sink_.get() || !source_params_.IsValid()) 229 if (!sink_.get() || !source_params_.IsValid())
244 return; 230 return;
245 231
246 { 232 {
247 // Clear up the old data in the FIFO. 233 // Clear up the old data in the FIFO.
248 base::AutoLock auto_lock(thread_lock_); 234 base::AutoLock auto_lock(thread_lock_);
249 loopback_fifo_->Clear(); 235 audio_shifter_->Flush();
250 } 236 }
251 237
252 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_) 238 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_)
253 return; 239 return;
254 240
255 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_."; 241 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
256 sink_->InitializeWithSessionId(sink_params_, this, session_id_); 242 sink_->InitializeWithSessionId(sink_params_, this, session_id_);
257 sink_->Start(); 243 sink_->Start();
258 sink_started_ = true; 244 sink_started_ = true;
259 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", 245 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
(...skipping 29 matching lines...) Expand all
289 sink_params_ = media::AudioParameters(source_params_.format(), 275 sink_params_ = media::AudioParameters(source_params_.format(),
290 source_params_.channel_layout(), source_params_.sample_rate(), 276 source_params_.channel_layout(), source_params_.sample_rate(),
291 source_params_.bits_per_sample(), 277 source_params_.bits_per_sample(),
292 WebRtcAudioRenderer::GetOptimalBufferSize(source_params_.sample_rate(), 278 WebRtcAudioRenderer::GetOptimalBufferSize(source_params_.sample_rate(),
293 frames_per_buffer_), 279 frames_per_buffer_),
294 // If DUCKING is enabled on the source, it needs to be enabled on the 280 // If DUCKING is enabled on the source, it needs to be enabled on the
295 // sink as well. 281 // sink as well.
296 source_params_.effects() | implicit_ducking_effect); 282 source_params_.effects() | implicit_ducking_effect);
297 283
298 { 284 {
299 // TODO(henrika): we could add a more dynamic solution here but I prefer 285 media::AudioShifter* const new_shifter = new media::AudioShifter(
henrika (OOO until Aug 14) 2015/01/21 08:54:38 It is not clear to me why you use 2 and 20 here. C
hubbe 2015/01/23 00:54:56 2 was selected because we need at least 1 second f
300 // a fixed size combined with bad audio at overflow. The alternative is 286 base::TimeDelta::FromSeconds(2),
301 // that we start to build up latency and that can be more difficult to 287 base::TimeDelta::FromMilliseconds(20),
302 // detect. Tests have shown that the FIFO never contains more than 2 or 3 288 base::TimeDelta::FromSeconds(20),
303 // audio frames but I have selected a max size of ten buffers just 289 source_params_.sample_rate(),
304 // in case since these tests were performed on a 16 core, 64GB Win 7 290 params.channels());
305 // machine. We could also add some sort of error notifier in this area if
306 // the FIFO overflows.
307 media::AudioFifo* const new_fifo = new media::AudioFifo(
308 params.channels(), 10 * params.frames_per_buffer());
309 291
310 base::AutoLock auto_lock(thread_lock_); 292 base::AutoLock auto_lock(thread_lock_);
311 loopback_fifo_.reset(new_fifo); 293 audio_shifter_.reset(new_shifter);
312 } 294 }
313 295
314 if (!sink_.get()) 296 if (!sink_.get())
315 return; // WebRtcLocalAudioRenderer has not yet been started. 297 return; // WebRtcLocalAudioRenderer has not yet been started.
316 298
317 // Stop |sink_| and re-create a new one to be initialized with different audio 299 // Stop |sink_| and re-create a new one to be initialized with different audio
318 // parameters. Then, invoke MaybeStartSink() to restart everything again. 300 // parameters. Then, invoke MaybeStartSink() to restart everything again.
319 if (sink_started_) { 301 if (sink_started_) {
320 sink_->Stop(); 302 sink_->Stop();
321 sink_started_ = false; 303 sink_started_ = false;
322 } 304 }
323 305
324 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_, 306 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
325 source_render_frame_id_); 307 source_render_frame_id_);
326 MaybeStartSink(); 308 MaybeStartSink();
327 } 309 }
328 310
329 } // namespace content 311 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/webrtc_local_audio_renderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698