Roll src/third_party/WebKit d10c917:a1123a1 (svn 198729:198730)
[chromium-blink-merge.git] / content / renderer / media / webrtc_local_audio_renderer.cc
blob22382d12e853f9cf2efc9e4fae13ae5ce914c651
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
3 // found in the LICENSE file.
5 #include "content/renderer/media/webrtc_local_audio_renderer.h"
7 #include "base/location.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/synchronization/lock.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/renderer/media/audio_device_factory.h"
14 #include "content/renderer/media/media_stream_dispatcher.h"
15 #include "content/renderer/media/webrtc_audio_capturer.h"
16 #include "content/renderer/media/webrtc_audio_renderer.h"
17 #include "content/renderer/render_frame_impl.h"
18 #include "media/audio/audio_output_device.h"
19 #include "media/base/audio_bus.h"
20 #include "media/base/audio_shifter.h"
22 namespace content {
24 namespace {
26 enum LocalRendererSinkStates {
27 kSinkStarted = 0,
28 kSinkNeverStarted,
29 kSinkStatesMax // Must always be last!
32 } // namespace
34 // media::AudioRendererSink::RenderCallback implementation
35 int WebRtcLocalAudioRenderer::Render(
36 media::AudioBus* audio_bus, int audio_delay_milliseconds) {
37 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
38 base::AutoLock auto_lock(thread_lock_);
40 if (!playing_ || !volume_ || !audio_shifter_) {
41 audio_bus->Zero();
42 return 0;
45 audio_shifter_->Pull(
46 audio_bus,
47 base::TimeTicks::Now() -
48 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds));
50 return audio_bus->frames();
53 void WebRtcLocalAudioRenderer::OnRenderError() {
54 NOTIMPLEMENTED();
57 // content::MediaStreamAudioSink implementation
58 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus,
59 base::TimeTicks estimated_capture_time) {
60 DCHECK(capture_thread_checker_.CalledOnValidThread());
61 DCHECK(!estimated_capture_time.is_null());
63 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
65 base::AutoLock auto_lock(thread_lock_);
66 if (!playing_ || !volume_ || !audio_shifter_)
67 return;
69 scoped_ptr<media::AudioBus> audio_data(
70 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()));
71 audio_bus.CopyTo(audio_data.get());
72 audio_shifter_->Push(audio_data.Pass(), estimated_capture_time);
73 const base::TimeTicks now = base::TimeTicks::Now();
74 total_render_time_ += now - last_render_time_;
75 last_render_time_ = now;
78 void WebRtcLocalAudioRenderer::OnSetFormat(
79 const media::AudioParameters& params) {
80 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
81 // If the source is restarted, we might have changed to another capture
82 // thread.
83 capture_thread_checker_.DetachFromThread();
84 DCHECK(capture_thread_checker_.CalledOnValidThread());
86 // Post a task on the main render thread to reconfigure the |sink_| with the
87 // new format.
88 task_runner_->PostTask(
89 FROM_HERE,
90 base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this, params));
93 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
94 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
95 const blink::WebMediaStreamTrack& audio_track,
96 int source_render_frame_id,
97 int session_id,
98 int frames_per_buffer)
99 : audio_track_(audio_track),
100 source_render_frame_id_(source_render_frame_id),
101 session_id_(session_id),
102 task_runner_(base::ThreadTaskRunnerHandle::Get()),
103 playing_(false),
104 frames_per_buffer_(frames_per_buffer),
105 volume_(0.0),
106 sink_started_(false) {
107 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
110 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
111 DCHECK(task_runner_->BelongsToCurrentThread());
112 DCHECK(!sink_.get());
113 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
116 void WebRtcLocalAudioRenderer::Start() {
117 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
118 DCHECK(task_runner_->BelongsToCurrentThread());
120 // We get audio data from |audio_track_|...
121 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
122 // ...and |sink_| will get audio data from us.
123 DCHECK(!sink_.get());
124 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_frame_id_);
126 base::AutoLock auto_lock(thread_lock_);
127 last_render_time_ = base::TimeTicks::Now();
128 playing_ = false;
131 void WebRtcLocalAudioRenderer::Stop() {
132 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
133 DCHECK(task_runner_->BelongsToCurrentThread());
136 base::AutoLock auto_lock(thread_lock_);
137 playing_ = false;
138 audio_shifter_.reset();
141 // Stop the output audio stream, i.e, stop asking for data to render.
142 // It is safer to call Stop() on the |sink_| to clean up the resources even
143 // when the |sink_| is never started.
144 if (sink_.get()) {
145 sink_->Stop();
146 sink_ = NULL;
149 if (!sink_started_) {
150 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
151 kSinkNeverStarted, kSinkStatesMax);
153 sink_started_ = false;
155 // Ensure that the capturer stops feeding us with captured audio.
156 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
159 void WebRtcLocalAudioRenderer::Play() {
160 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
161 DCHECK(task_runner_->BelongsToCurrentThread());
163 if (!sink_.get())
164 return;
167 base::AutoLock auto_lock(thread_lock_);
168 // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
169 // now reads data from the local FIFO.
170 playing_ = true;
171 last_render_time_ = base::TimeTicks::Now();
174 // Note: If volume_ is currently muted, the |sink_| will not be started yet.
175 MaybeStartSink();
178 void WebRtcLocalAudioRenderer::Pause() {
179 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
180 DCHECK(task_runner_->BelongsToCurrentThread());
182 if (!sink_.get())
183 return;
185 base::AutoLock auto_lock(thread_lock_);
186 // Temporarily suspends rendering audio.
187 // WebRtcLocalAudioRenderer::Render() will return early during this state
188 // and only zeros will be provided to the active sink.
189 playing_ = false;
192 void WebRtcLocalAudioRenderer::SetVolume(float volume) {
193 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
194 DCHECK(task_runner_->BelongsToCurrentThread());
197 base::AutoLock auto_lock(thread_lock_);
198 // Cache the volume.
199 volume_ = volume;
202 // Lazily start the |sink_| when the local renderer is unmuted during
203 // playing.
204 MaybeStartSink();
206 if (sink_.get())
207 sink_->SetVolume(volume);
210 void WebRtcLocalAudioRenderer::SwitchOutputDevice(
211 const std::string& device_id,
212 const GURL& security_origin,
213 const media::SwitchOutputDeviceCB& callback) {
214 DVLOG(1) << __FUNCTION__
215 << "(" << device_id << ", " << security_origin << ")";
216 DCHECK(task_runner_->BelongsToCurrentThread());
218 if (sink_.get())
219 sink_->SwitchOutputDevice(device_id, security_origin, callback);
220 else
221 callback.Run(media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED);
224 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
225 DCHECK(task_runner_->BelongsToCurrentThread());
226 base::AutoLock auto_lock(thread_lock_);
227 if (!sink_.get())
228 return base::TimeDelta();
229 return total_render_time();
232 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
233 return true;
236 void WebRtcLocalAudioRenderer::MaybeStartSink() {
237 DCHECK(task_runner_->BelongsToCurrentThread());
238 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
240 if (!sink_.get() || !source_params_.IsValid())
241 return;
244 // Clear up the old data in the FIFO.
245 base::AutoLock auto_lock(thread_lock_);
246 audio_shifter_->Flush();
249 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_)
250 return;
252 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
253 sink_->InitializeWithSessionId(sink_params_, this, session_id_);
254 sink_->Start();
255 sink_started_ = true;
256 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
257 kSinkStarted, kSinkStatesMax);
260 void WebRtcLocalAudioRenderer::ReconfigureSink(
261 const media::AudioParameters& params) {
262 DCHECK(task_runner_->BelongsToCurrentThread());
264 DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
266 int implicit_ducking_effect = 0;
267 RenderFrameImpl* const frame =
268 RenderFrameImpl::FromRoutingID(source_render_frame_id_);
269 MediaStreamDispatcher* const dispatcher = frame ?
270 frame->GetMediaStreamDispatcher() : NULL;
271 if (dispatcher && dispatcher->IsAudioDuckingActive()) {
272 DVLOG(1) << "Forcing DUCKING to be ON for output";
273 implicit_ducking_effect = media::AudioParameters::DUCKING;
274 } else {
275 DVLOG(1) << "DUCKING not forced ON for output";
278 if (source_params_.Equals(params))
279 return;
281 // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
282 // the new format.
284 source_params_ = params;
286 sink_params_ = media::AudioParameters(source_params_.format(),
287 source_params_.channel_layout(), source_params_.sample_rate(),
288 source_params_.bits_per_sample(),
289 WebRtcAudioRenderer::GetOptimalBufferSize(source_params_.sample_rate(),
290 frames_per_buffer_),
291 // If DUCKING is enabled on the source, it needs to be enabled on the
292 // sink as well.
293 source_params_.effects() | implicit_ducking_effect);
296 // Note: The max buffer is fairly large, but will rarely be used.
297 // Cast needs the buffer to hold at least one second of audio.
298 // The clock accuracy is set to 20ms because clock accuracy is
299 // ~15ms on windows.
300 media::AudioShifter* const new_shifter = new media::AudioShifter(
301 base::TimeDelta::FromSeconds(2),
302 base::TimeDelta::FromMilliseconds(20),
303 base::TimeDelta::FromSeconds(20),
304 source_params_.sample_rate(),
305 params.channels());
307 base::AutoLock auto_lock(thread_lock_);
308 audio_shifter_.reset(new_shifter);
311 if (!sink_.get())
312 return; // WebRtcLocalAudioRenderer has not yet been started.
314 // Stop |sink_| and re-create a new one to be initialized with different audio
315 // parameters. Then, invoke MaybeStartSink() to restart everything again.
316 if (sink_started_) {
317 sink_->Stop();
318 sink_started_ = false;
321 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_frame_id_);
322 MaybeStartSink();
325 } // namespace content