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"
26 enum LocalRendererSinkStates
{
29 kSinkStatesMax
// Must always be last!
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_
) {
47 base::TimeTicks::Now() -
48 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds
));
50 return audio_bus
->frames();
53 void WebRtcLocalAudioRenderer::OnRenderError() {
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_
)
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
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
88 task_runner_
->PostTask(
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
,
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()),
104 frames_per_buffer_(frames_per_buffer
),
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(
125 source_render_frame_id_
, session_id_
, std::string(), url::Origin());
127 base::AutoLock
auto_lock(thread_lock_
);
128 last_render_time_
= base::TimeTicks::Now();
132 void WebRtcLocalAudioRenderer::Stop() {
133 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
134 DCHECK(task_runner_
->BelongsToCurrentThread());
137 base::AutoLock
auto_lock(thread_lock_
);
139 audio_shifter_
.reset();
142 // Stop the output audio stream, i.e, stop asking for data to render.
143 // It is safer to call Stop() on the |sink_| to clean up the resources even
144 // when the |sink_| is never started.
150 if (!sink_started_
) {
151 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
152 kSinkNeverStarted
, kSinkStatesMax
);
154 sink_started_
= false;
156 // Ensure that the capturer stops feeding us with captured audio.
157 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_
);
160 void WebRtcLocalAudioRenderer::Play() {
161 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
162 DCHECK(task_runner_
->BelongsToCurrentThread());
168 base::AutoLock
auto_lock(thread_lock_
);
169 // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
170 // now reads data from the local FIFO.
172 last_render_time_
= base::TimeTicks::Now();
175 // Note: If volume_ is currently muted, the |sink_| will not be started yet.
179 void WebRtcLocalAudioRenderer::Pause() {
180 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
181 DCHECK(task_runner_
->BelongsToCurrentThread());
186 base::AutoLock
auto_lock(thread_lock_
);
187 // Temporarily suspends rendering audio.
188 // WebRtcLocalAudioRenderer::Render() will return early during this state
189 // and only zeros will be provided to the active sink.
193 void WebRtcLocalAudioRenderer::SetVolume(float volume
) {
194 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume
<< ")";
195 DCHECK(task_runner_
->BelongsToCurrentThread());
198 base::AutoLock
auto_lock(thread_lock_
);
203 // Lazily start the |sink_| when the local renderer is unmuted during
208 sink_
->SetVolume(volume
);
211 media::OutputDevice
* WebRtcLocalAudioRenderer::GetOutputDevice() {
212 DVLOG(1) << __FUNCTION__
;
213 DCHECK(task_runner_
->BelongsToCurrentThread());
217 base::TimeDelta
WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
218 DCHECK(task_runner_
->BelongsToCurrentThread());
219 base::AutoLock
auto_lock(thread_lock_
);
221 return base::TimeDelta();
222 return total_render_time();
225 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
229 void WebRtcLocalAudioRenderer::MaybeStartSink() {
230 DCHECK(task_runner_
->BelongsToCurrentThread());
231 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
233 if (!sink_
.get() || !source_params_
.IsValid())
237 // Clear up the old data in the FIFO.
238 base::AutoLock
auto_lock(thread_lock_
);
239 audio_shifter_
->Flush();
242 if (!sink_params_
.IsValid() || !playing_
|| !volume_
|| sink_started_
)
245 DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
246 sink_
->Initialize(sink_params_
, this);
248 sink_started_
= true;
249 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
250 kSinkStarted
, kSinkStatesMax
);
253 void WebRtcLocalAudioRenderer::ReconfigureSink(
254 const media::AudioParameters
& params
) {
255 DCHECK(task_runner_
->BelongsToCurrentThread());
257 DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
259 if (source_params_
.Equals(params
))
262 // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
265 source_params_
= params
;
266 sink_params_
= source_params_
;
267 sink_params_
.set_frames_per_buffer(WebRtcAudioRenderer::GetOptimalBufferSize(
268 source_params_
.sample_rate(), frames_per_buffer_
));
270 // Note: The max buffer is fairly large, but will rarely be used.
271 // Cast needs the buffer to hold at least one second of audio.
272 // The clock accuracy is set to 20ms because clock accuracy is
274 media::AudioShifter
* const new_shifter
= new media::AudioShifter(
275 base::TimeDelta::FromSeconds(2),
276 base::TimeDelta::FromMilliseconds(20),
277 base::TimeDelta::FromSeconds(20),
278 source_params_
.sample_rate(),
281 base::AutoLock
auto_lock(thread_lock_
);
282 audio_shifter_
.reset(new_shifter
);
286 return; // WebRtcLocalAudioRenderer has not yet been started.
288 // Stop |sink_| and re-create a new one to be initialized with different audio
289 // parameters. Then, invoke MaybeStartSink() to restart everything again.
291 sink_started_
= false;
293 sink_
= AudioDeviceFactory::NewOutputDevice(
294 source_render_frame_id_
, session_id_
, std::string(), url::Origin());
298 } // namespace content