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/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "base/message_loop_proxy.h"
10 #include "base/synchronization/lock.h"
11 #include "content/renderer/media/audio_device_factory.h"
12 #include "content/renderer/media/renderer_audio_output_device.h"
13 #include "content/renderer/media/webrtc_audio_capturer.h"
14 #include "media/base/audio_bus.h"
18 // media::AudioRendererSink::RenderCallback implementation
19 int WebRtcLocalAudioRenderer::Render(
20 media::AudioBus
* audio_bus
, int audio_delay_milliseconds
) {
21 base::AutoLock
auto_lock(thread_lock_
);
28 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
30 base::Time now
= base::Time::Now();
31 total_render_time_
+= now
- last_render_time_
;
32 last_render_time_
= now
;
34 DCHECK(loopback_fifo_
.get() != NULL
);
36 // Provide data by reading from the FIFO if the FIFO contains enough
37 // to fulfill the request.
38 if (loopback_fifo_
->frames() >= audio_bus
->frames()) {
39 loopback_fifo_
->Consume(audio_bus
, 0, audio_bus
->frames());
42 // This warning is perfectly safe if it happens for the first audio
43 // frames. It should not happen in a steady-state mode.
44 DVLOG(2) << "loopback FIFO is empty";
47 return audio_bus
->frames();
50 void WebRtcLocalAudioRenderer::OnRenderError() {
54 // content::WebRtcAudioCapturerSink implementation
55 void WebRtcLocalAudioRenderer::CaptureData(const int16
* audio_data
,
56 int number_of_channels
,
58 int audio_delay_milliseconds
,
60 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
61 base::AutoLock
auto_lock(thread_lock_
);
63 if (!playing_
|| !track_is_enabled_
) {
67 // Push captured audio to FIFO so it can be read by a local sink.
68 if (loopback_fifo_
&& track_is_enabled_
) {
69 if (loopback_fifo_
->frames() + number_of_frames
<=
70 loopback_fifo_
->max_frames()) {
71 scoped_ptr
<media::AudioBus
> audio_source
= media::AudioBus::Create(
72 number_of_channels
, number_of_frames
);
73 audio_source
->FromInterleaved(audio_data
,
74 audio_source
->frames(),
75 sizeof(audio_data
[0]));
76 loopback_fifo_
->Push(audio_source
.get());
78 DVLOG(1) << "FIFO is full";
83 void WebRtcLocalAudioRenderer::SetCaptureFormat(
84 const media::AudioParameters
& params
) {
85 NOTIMPLEMENTED() << "WebRtcLocalAudioRenderer::SetCaptureFormat()";
88 // webrtc::ObserverInterface implementation
89 void WebRtcLocalAudioRenderer::OnChanged() {
90 DVLOG(1) << "WebRtcLocalAudioRenderer::OnChanged()";
91 DCHECK(thread_checker_
.CalledOnValidThread());
92 base::AutoLock
auto_lock(thread_lock_
);
93 track_is_enabled_
= audio_track_
->enabled();
96 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
97 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
98 const scoped_refptr
<WebRtcAudioCapturer
>& source
,
99 webrtc::AudioTrackInterface
* audio_track
,
100 int source_render_view_id
)
102 audio_track_(audio_track
),
103 source_render_view_id_(source_render_view_id
),
105 track_is_enabled_(true) {
106 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
109 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
110 DCHECK(thread_checker_
.CalledOnValidThread());
112 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
115 void WebRtcLocalAudioRenderer::Start() {
116 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
117 DCHECK(thread_checker_
.CalledOnValidThread());
121 audio_track_
->RegisterObserver(this);
122 track_is_enabled_
= audio_track_
->enabled();
125 base::AutoLock
auto_lock(thread_lock_
);
128 // Add this class as sink to the capturer to ensure that we receive
129 // WebRtcAudioCapturerSink::CaptureData() callbacks for captured audio.
130 source_
->AddCapturerSink(this);
132 // Use the capturing source audio parameters when opening the output audio
133 // device. Any mismatch will be compensated for by the audio output back-end.
134 // Note that the buffer size is modified to make the full-duplex scheme less
135 // resource intensive. By doubling the buffer size (compared to the capture
136 // side), the callback frequency of browser side callbacks will be lower and
137 // tests have shown that it resolves issues with audio glitches for some
138 // cases where resampling is needed on the output side.
139 // TODO(henrika): verify this scheme on as many different devices and
140 // combinations of sample rates as possible
141 media::AudioParameters source_params
= source_
->audio_parameters();
143 // TODO(henrika): we could add a more dynamic solution here but I prefer
144 // a fixed size combined with bad audio at overflow. The alternative is
145 // that we start to build up latency and that can be more difficult to
146 // detect. Tests have shown that the FIFO never contains more than 2 or 3
147 // audio frames but I have selected a max size of ten buffers just
148 // in case since these tests were performed on a 16 core, 64GB Win 7
149 // machine. We could also add some sort of error notifier in this area if
150 // the FIFO overflows.
151 DCHECK(!loopback_fifo_
);
152 loopback_fifo_
.reset(new media::AudioFifo(
153 source_params
.channels(),
154 10 * source_params
.frames_per_buffer()));
156 media::AudioParameters
sink_params(source_params
.format(),
157 source_params
.channel_layout(),
158 source_params
.sample_rate(),
159 source_params
.bits_per_sample(),
160 2 * source_params
.frames_per_buffer());
161 sink_
= AudioDeviceFactory::NewOutputDevice();
162 // TODO(henrika): we could utilize the unified audio here instead and do
163 // sink_->InitializeIO(sink_params, 2, callback_.get());
164 // It would then be possible to avoid using the WebRtcAudioCapturer.
165 sink_
->Initialize(sink_params
, this);
166 sink_
->SetSourceRenderView(source_render_view_id_
);
168 // Start the capturer and local rendering. Note that, the capturer is owned
169 // by the WebRTC ADM and might already bee running.
173 last_render_time_
= base::Time::Now();
177 void WebRtcLocalAudioRenderer::Stop() {
178 DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
179 DCHECK(thread_checker_
.CalledOnValidThread());
185 base::AutoLock
auto_lock(thread_lock_
);
188 if (loopback_fifo_
.get() != NULL
) {
189 loopback_fifo_
->Clear();
190 loopback_fifo_
.reset();
194 // Stop the output audio stream, i.e, stop asking for data to render.
198 // Ensure that the capturer stops feeding us with captured audio.
199 // Note that, we do not stop the capturer here since it may still be used by
201 source_
->RemoveCapturerSink(this);
205 audio_track_
->UnregisterObserver(this);
210 void WebRtcLocalAudioRenderer::Play() {
211 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
212 DCHECK(thread_checker_
.CalledOnValidThread());
213 base::AutoLock
auto_lock(thread_lock_
);
218 // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
219 // now reads data from the local FIFO.
221 last_render_time_
= base::Time::Now();
224 loopback_fifo_
->Clear();
227 void WebRtcLocalAudioRenderer::Pause() {
228 DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
229 DCHECK(thread_checker_
.CalledOnValidThread());
230 base::AutoLock
auto_lock(thread_lock_
);
235 // Temporarily suspends rendering audio.
236 // WebRtcLocalAudioRenderer::Render() will return early during this state
237 // and only zeros will be provided to the active sink.
241 void WebRtcLocalAudioRenderer::SetVolume(float volume
) {
242 DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume
<< ")";
243 DCHECK(thread_checker_
.CalledOnValidThread());
244 base::AutoLock
auto_lock(thread_lock_
);
246 sink_
->SetVolume(volume
);
249 base::TimeDelta
WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
250 DCHECK(thread_checker_
.CalledOnValidThread());
251 base::AutoLock
auto_lock(thread_lock_
);
253 return base::TimeDelta();
254 return total_render_time();
257 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
261 } // namespace content