OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |