| 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( |
| 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 // Note: The max buffer is fairly large, but will rarely be used. |
| 300 // a fixed size combined with bad audio at overflow. The alternative is | 286 // Cast needs the buffer to hold at least one second of audio. |
| 301 // that we start to build up latency and that can be more difficult to | 287 // The clock accuracy is set to 20ms because clock accuracy is |
| 302 // detect. Tests have shown that the FIFO never contains more than 2 or 3 | 288 // ~15ms on windows. |
| 303 // audio frames but I have selected a max size of ten buffers just | 289 media::AudioShifter* const new_shifter = new media::AudioShifter( |
| 304 // in case since these tests were performed on a 16 core, 64GB Win 7 | 290 base::TimeDelta::FromSeconds(2), |
| 305 // machine. We could also add some sort of error notifier in this area if | 291 base::TimeDelta::FromMilliseconds(20), |
| 306 // the FIFO overflows. | 292 base::TimeDelta::FromSeconds(20), |
| 307 media::AudioFifo* const new_fifo = new media::AudioFifo( | 293 source_params_.sample_rate(), |
| 308 params.channels(), 10 * params.frames_per_buffer()); | 294 params.channels()); |
| 309 | 295 |
| 310 base::AutoLock auto_lock(thread_lock_); | 296 base::AutoLock auto_lock(thread_lock_); |
| 311 loopback_fifo_.reset(new_fifo); | 297 audio_shifter_.reset(new_shifter); |
| 312 } | 298 } |
| 313 | 299 |
| 314 if (!sink_.get()) | 300 if (!sink_.get()) |
| 315 return; // WebRtcLocalAudioRenderer has not yet been started. | 301 return; // WebRtcLocalAudioRenderer has not yet been started. |
| 316 | 302 |
| 317 // Stop |sink_| and re-create a new one to be initialized with different audio | 303 // Stop |sink_| and re-create a new one to be initialized with different audio |
| 318 // parameters. Then, invoke MaybeStartSink() to restart everything again. | 304 // parameters. Then, invoke MaybeStartSink() to restart everything again. |
| 319 if (sink_started_) { | 305 if (sink_started_) { |
| 320 sink_->Stop(); | 306 sink_->Stop(); |
| 321 sink_started_ = false; | 307 sink_started_ = false; |
| 322 } | 308 } |
| 323 | 309 |
| 324 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_, | 310 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_, |
| 325 source_render_frame_id_); | 311 source_render_frame_id_); |
| 326 MaybeStartSink(); | 312 MaybeStartSink(); |
| 327 } | 313 } |
| 328 | 314 |
| 329 } // namespace content | 315 } // namespace content |
| OLD | NEW |