[ServiceWorker] Implement WebServiceWorkerContextClient::openWindow().
[chromium-blink-merge.git] / content / renderer / media / webrtc_local_audio_renderer.cc
blobd3a88d53e6d72f76cb78896c816da4644a0e700f
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/logging.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/metrics/histogram.h"
10 #include "base/synchronization/lock.h"
11 #include "base/trace_event/trace_event.h"
12 #include "content/renderer/media/audio_device_factory.h"
13 #include "content/renderer/media/media_stream_dispatcher.h"
14 #include "content/renderer/media/webrtc_audio_capturer.h"
15 #include "content/renderer/media/webrtc_audio_renderer.h"
16 #include "content/renderer/render_frame_impl.h"
17 #include "media/audio/audio_output_device.h"
18 #include "media/base/audio_bus.h"
19 #include "media/base/audio_shifter.h"
21 namespace content {
23 namespace {
25 enum LocalRendererSinkStates {
26 kSinkStarted = 0,
27 kSinkNeverStarted,
28 kSinkStatesMax // Must always be last!
31 } // namespace
33 // media::AudioRendererSink::RenderCallback implementation
34 int WebRtcLocalAudioRenderer::Render(
35 media::AudioBus* audio_bus, int audio_delay_milliseconds) {
36 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
37 base::AutoLock auto_lock(thread_lock_);
39 if (!playing_ || !volume_ || !audio_shifter_) {
40 audio_bus->Zero();
41 return 0;
44 audio_shifter_->Pull(
45 audio_bus,
46 base::TimeTicks::Now() -
47 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds));
49 return audio_bus->frames();
52 void WebRtcLocalAudioRenderer::OnRenderError() {
53 NOTIMPLEMENTED();
56 // content::MediaStreamAudioSink implementation
57 void WebRtcLocalAudioRenderer::OnData(const media::AudioBus& audio_bus,
58 base::TimeTicks estimated_capture_time) {
59 DCHECK(capture_thread_checker_.CalledOnValidThread());
60 DCHECK(!estimated_capture_time.is_null());
62 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
64 base::AutoLock auto_lock(thread_lock_);
65 if (!playing_ || !volume_ || !audio_shifter_)
66 return;
68 scoped_ptr<media::AudioBus> audio_data(
69 media::AudioBus::Create(audio_bus.channels(), audio_bus.frames()));
70 audio_bus.CopyTo(audio_data.get());
71 audio_shifter_->Push(audio_data.Pass(), estimated_capture_time);
72 const base::TimeTicks now = base::TimeTicks::Now();
73 total_render_time_ += now - last_render_time_;
74 last_render_time_ = now;
77 void WebRtcLocalAudioRenderer::OnSetFormat(
78 const media::AudioParameters& params) {
79 DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
80 // If the source is restarted, we might have changed to another capture
81 // thread.
82 capture_thread_checker_.DetachFromThread();
83 DCHECK(capture_thread_checker_.CalledOnValidThread());
85 // Post a task on the main render thread to reconfigure the |sink_| with the
86 // new format.
87 message_loop_->PostTask(
88 FROM_HERE,
89 base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this,
90 params));
93 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
94 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
95 const blink::WebMediaStreamTrack& audio_track,
96 int source_render_view_id,
97 int source_render_frame_id,
98 int session_id,
99 int frames_per_buffer)
100 : audio_track_(audio_track),
101 source_render_view_id_(source_render_view_id),
102 source_render_frame_id_(source_render_frame_id),
103 session_id_(session_id),
104 message_loop_(base::MessageLoopProxy::current()),
105 playing_(false),
106 frames_per_buffer_(frames_per_buffer),
107 volume_(0.0),
108 sink_started_(false) {
109 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
112 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
113 DCHECK(message_loop_->BelongsToCurrentThread());
114 DCHECK(!sink_.get());
115 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
118 void WebRtcLocalAudioRenderer::Start() {
119 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
120 DCHECK(message_loop_->BelongsToCurrentThread());
122 // We get audio data from |audio_track_|...
123 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
124 // ...and |sink_| will get audio data from us.
125 DCHECK(!sink_.get());
126 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
127 source_render_frame_id_);
129 base::AutoLock auto_lock(thread_lock_);
130 last_render_time_ = base::TimeTicks::Now();
131 playing_ = false;
134 void WebRtcLocalAudioRenderer::Stop() {
135 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
136 DCHECK(message_loop_->BelongsToCurrentThread());
139 base::AutoLock auto_lock(thread_lock_);
140 playing_ = false;
141 audio_shifter_.reset();
144 // Stop the output audio stream, i.e, stop asking for data to render.
145 // It is safer to call Stop() on the |sink_| to clean up the resources even
146 // when the |sink_| is never started.
147 if (sink_.get()) {
148 sink_->Stop();
149 sink_ = NULL;
152 if (!sink_started_) {
153 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
154 kSinkNeverStarted, kSinkStatesMax);
156 sink_started_ = false;
158 // Ensure that the capturer stops feeding us with captured audio.
159 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
162 void WebRtcLocalAudioRenderer::Play() {
163 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
164 DCHECK(message_loop_->BelongsToCurrentThread());
166 if (!sink_.get())
167 return;
170 base::AutoLock auto_lock(thread_lock_);
171 // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
172 // now reads data from the local FIFO.
173 playing_ = true;
174 last_render_time_ = base::TimeTicks::Now();
177 // Note: If volume_ is currently muted, the |sink_| will not be started yet.
178 MaybeStartSink();
181 void WebRtcLocalAudioRenderer::Pause() {
182 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
183 DCHECK(message_loop_->BelongsToCurrentThread());
185 if (!sink_.get())
186 return;
188 base::AutoLock auto_lock(thread_lock_);
189 // Temporarily suspends rendering audio.
190 // WebRtcLocalAudioRenderer::Render() will return early during this state
191 // and only zeros will be provided to the active sink.
192 playing_ = false;
195 void WebRtcLocalAudioRenderer::SetVolume(float volume) {
196 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
197 DCHECK(message_loop_->BelongsToCurrentThread());
200 base::AutoLock auto_lock(thread_lock_);
201 // Cache the volume.
202 volume_ = volume;
205 // Lazily start the |sink_| when the local renderer is unmuted during
206 // playing.
207 MaybeStartSink();
209 if (sink_.get())
210 sink_->SetVolume(volume);
213 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
214 DCHECK(message_loop_->BelongsToCurrentThread());
215 base::AutoLock auto_lock(thread_lock_);
216 if (!sink_.get())
217 return base::TimeDelta();
218 return total_render_time();
221 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
222 return true;
225 void WebRtcLocalAudioRenderer::MaybeStartSink() {
226 DCHECK(message_loop_->BelongsToCurrentThread());
227 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
229 if (!sink_.get() || !source_params_.IsValid())
230 return;
233 // Clear up the old data in the FIFO.
234 base::AutoLock auto_lock(thread_lock_);
235 audio_shifter_->Flush();
238 if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_)
239 return;
241 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
242 sink_->InitializeWithSessionId(sink_params_, this, session_id_);
243 sink_->Start();
244 sink_started_ = true;
245 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
246 kSinkStarted, kSinkStatesMax);
249 void WebRtcLocalAudioRenderer::ReconfigureSink(
250 const media::AudioParameters& params) {
251 DCHECK(message_loop_->BelongsToCurrentThread());
253 DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
255 int implicit_ducking_effect = 0;
256 RenderFrameImpl* const frame =
257 RenderFrameImpl::FromRoutingID(source_render_frame_id_);
258 MediaStreamDispatcher* const dispatcher = frame ?
259 frame->GetMediaStreamDispatcher() : NULL;
260 if (dispatcher && dispatcher->IsAudioDuckingActive()) {
261 DVLOG(1) << "Forcing DUCKING to be ON for output";
262 implicit_ducking_effect = media::AudioParameters::DUCKING;
263 } else {
264 DVLOG(1) << "DUCKING not forced ON for output";
267 if (source_params_.Equals(params))
268 return;
270 // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
271 // the new format.
273 source_params_ = params;
275 sink_params_ = media::AudioParameters(source_params_.format(),
276 source_params_.channel_layout(), source_params_.sample_rate(),
277 source_params_.bits_per_sample(),
278 WebRtcAudioRenderer::GetOptimalBufferSize(source_params_.sample_rate(),
279 frames_per_buffer_),
280 // If DUCKING is enabled on the source, it needs to be enabled on the
281 // sink as well.
282 source_params_.effects() | implicit_ducking_effect);
285 // Note: The max buffer is fairly large, but will rarely be used.
286 // Cast needs the buffer to hold at least one second of audio.
287 // The clock accuracy is set to 20ms because clock accuracy is
288 // ~15ms on windows.
289 media::AudioShifter* const new_shifter = new media::AudioShifter(
290 base::TimeDelta::FromSeconds(2),
291 base::TimeDelta::FromMilliseconds(20),
292 base::TimeDelta::FromSeconds(20),
293 source_params_.sample_rate(),
294 params.channels());
296 base::AutoLock auto_lock(thread_lock_);
297 audio_shifter_.reset(new_shifter);
300 if (!sink_.get())
301 return; // WebRtcLocalAudioRenderer has not yet been started.
303 // Stop |sink_| and re-create a new one to be initialized with different audio
304 // parameters. Then, invoke MaybeStartSink() to restart everything again.
305 if (sink_started_) {
306 sink_->Stop();
307 sink_started_ = false;
310 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
311 source_render_frame_id_);
312 MaybeStartSink();
315 } // namespace content