Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / webrtc_local_audio_renderer.cc
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.
4
5 #include "content/renderer/media/webrtc_local_audio_renderer.h"
6
7 #include "base/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/metrics/histogram.h"
11 #include "base/synchronization/lock.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_block_fifo.h"
19 #include "media/base/audio_bus.h"
20
21 namespace content {
22
23 namespace {
24
25 enum LocalRendererSinkStates {
26   kSinkStarted = 0,
27   kSinkNeverStarted,
28   kSinkStatesMax  // Must always be last!
29 };
30
31 }  // namespace
32
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_);
38
39   if (!playing_ || !volume_ || !loopback_fifo_) {
40     audio_bus->Zero();
41     return 0;
42   }
43
44   // Provide data by reading from the FIFO if the FIFO contains enough
45   // to fulfill the request.
46   if (loopback_fifo_->available_blocks()) {
47     const media::AudioBus* audio_data = loopback_fifo_->Consume();
48     DCHECK_EQ(audio_data->frames(), audio_bus->frames());
49     audio_data->CopyTo(audio_bus);
50   } else {
51     audio_bus->Zero();
52     // This warning is perfectly safe if it happens for the first audio
53     // frames. It should not happen in a steady-state mode.
54     DVLOG(2) << "loopback FIFO is empty";
55   }
56
57   return audio_bus->frames();
58 }
59
60 void WebRtcLocalAudioRenderer::OnRenderError() {
61   NOTIMPLEMENTED();
62 }
63
64 // content::MediaStreamAudioSink implementation
65 void WebRtcLocalAudioRenderer::OnData(const int16* audio_data,
66                                       int sample_rate,
67                                       int number_of_channels,
68                                       int number_of_frames) {
69   DCHECK(capture_thread_checker_.CalledOnValidThread());
70   TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
71   base::AutoLock auto_lock(thread_lock_);
72   if (!playing_ || !volume_ || !loopback_fifo_)
73     return;
74
75   // Push captured audio to FIFO so it can be read by a local sink.
76   if (loopback_fifo_->GetUnfilledFrames() >= number_of_frames) {
77     loopback_fifo_->Push(audio_data, number_of_frames, sizeof(audio_data[0]));
78
79     const base::TimeTicks now = base::TimeTicks::Now();
80     total_render_time_ += now - last_render_time_;
81     last_render_time_ = now;
82   } else {
83     DVLOG(1) << "FIFO is full";
84   }
85 }
86
87 void WebRtcLocalAudioRenderer::OnSetFormat(
88     const media::AudioParameters& params) {
89   DVLOG(1) << "WebRtcLocalAudioRenderer::OnSetFormat()";
90   // If the source is restarted, we might have changed to another capture
91   // thread.
92   capture_thread_checker_.DetachFromThread();
93   DCHECK(capture_thread_checker_.CalledOnValidThread());
94
95   // Post a task on the main render thread to reconfigure the |sink_| with the
96   // new format.
97   message_loop_->PostTask(
98       FROM_HERE,
99       base::Bind(&WebRtcLocalAudioRenderer::ReconfigureSink, this,
100                  params));
101 }
102
103 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
104 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
105     const blink::WebMediaStreamTrack& audio_track,
106     int source_render_view_id,
107     int source_render_frame_id,
108     int session_id,
109     int frames_per_buffer)
110     : audio_track_(audio_track),
111       source_render_view_id_(source_render_view_id),
112       source_render_frame_id_(source_render_frame_id),
113       session_id_(session_id),
114       message_loop_(base::MessageLoopProxy::current()),
115       playing_(false),
116       frames_per_buffer_(frames_per_buffer),
117       volume_(0.0),
118       sink_started_(false) {
119   DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
120 }
121
122 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
123   DCHECK(message_loop_->BelongsToCurrentThread());
124   DCHECK(!sink_.get());
125   DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
126 }
127
128 void WebRtcLocalAudioRenderer::Start() {
129   DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
130   DCHECK(message_loop_->BelongsToCurrentThread());
131
132   // We get audio data from |audio_track_|...
133   MediaStreamAudioSink::AddToAudioTrack(this, audio_track_);
134   // ...and |sink_| will get audio data from us.
135   DCHECK(!sink_.get());
136   sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
137                                               source_render_frame_id_);
138
139   base::AutoLock auto_lock(thread_lock_);
140   last_render_time_ = base::TimeTicks::Now();
141   playing_ = false;
142 }
143
144 void WebRtcLocalAudioRenderer::Stop() {
145   DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
146   DCHECK(message_loop_->BelongsToCurrentThread());
147
148   {
149     base::AutoLock auto_lock(thread_lock_);
150     playing_ = false;
151     loopback_fifo_.reset();
152   }
153
154   // Stop the output audio stream, i.e, stop asking for data to render.
155   // It is safer to call Stop() on the |sink_| to clean up the resources even
156   // when the |sink_| is never started.
157   if (sink_.get()) {
158     sink_->Stop();
159     sink_ = NULL;
160   }
161
162   if (!sink_started_) {
163     UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
164                               kSinkNeverStarted, kSinkStatesMax);
165   }
166   sink_started_ = false;
167
168   // Ensure that the capturer stops feeding us with captured audio.
169   MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_);
170 }
171
172 void WebRtcLocalAudioRenderer::Play() {
173   DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
174   DCHECK(message_loop_->BelongsToCurrentThread());
175
176   if (!sink_.get())
177     return;
178
179   {
180     base::AutoLock auto_lock(thread_lock_);
181     // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
182     // now reads data from the local FIFO.
183     playing_ = true;
184     last_render_time_ = base::TimeTicks::Now();
185   }
186
187   // Note: If volume_ is currently muted, the |sink_| will not be started yet.
188   MaybeStartSink();
189 }
190
191 void WebRtcLocalAudioRenderer::Pause() {
192   DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
193   DCHECK(message_loop_->BelongsToCurrentThread());
194
195   if (!sink_.get())
196     return;
197
198   base::AutoLock auto_lock(thread_lock_);
199   // Temporarily suspends rendering audio.
200   // WebRtcLocalAudioRenderer::Render() will return early during this state
201   // and only zeros will be provided to the active sink.
202   playing_ = false;
203 }
204
205 void WebRtcLocalAudioRenderer::SetVolume(float volume) {
206   DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
207   DCHECK(message_loop_->BelongsToCurrentThread());
208
209   {
210     base::AutoLock auto_lock(thread_lock_);
211     // Cache the volume.
212     volume_ = volume;
213   }
214
215   // Lazily start the |sink_| when the local renderer is unmuted during
216   // playing.
217   MaybeStartSink();
218
219   if (sink_.get())
220     sink_->SetVolume(volume);
221 }
222
223 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
224   DCHECK(message_loop_->BelongsToCurrentThread());
225   base::AutoLock auto_lock(thread_lock_);
226   if (!sink_.get())
227     return base::TimeDelta();
228   return total_render_time();
229 }
230
231 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
232   return true;
233 }
234
235 void WebRtcLocalAudioRenderer::MaybeStartSink() {
236   DCHECK(message_loop_->BelongsToCurrentThread());
237   DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink()";
238
239   if (!sink_.get() || !source_params_.IsValid())
240     return;
241
242   {
243     // Clear up the old data in the FIFO.
244     base::AutoLock auto_lock(thread_lock_);
245     loopback_fifo_->Clear();
246   }
247
248   if (!sink_params_.IsValid() || !playing_ || !volume_ || sink_started_)
249     return;
250
251   DVLOG(1) << "WebRtcLocalAudioRenderer::MaybeStartSink() -- Starting sink_.";
252   sink_->InitializeWithSessionId(sink_params_, this, session_id_);
253   sink_->Start();
254   sink_started_ = true;
255   UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates",
256                             kSinkStarted, kSinkStatesMax);
257 }
258
259 void WebRtcLocalAudioRenderer::ReconfigureSink(
260     const media::AudioParameters& params) {
261   DCHECK(message_loop_->BelongsToCurrentThread());
262
263   DVLOG(1) << "WebRtcLocalAudioRenderer::ReconfigureSink()";
264
265   int implicit_ducking_effect = 0;
266   RenderFrameImpl* const frame =
267       RenderFrameImpl::FromRoutingID(source_render_frame_id_);
268   MediaStreamDispatcher* const dispatcher = frame ?
269       frame->GetMediaStreamDispatcher() : NULL;
270   if (dispatcher && dispatcher->IsAudioDuckingActive()) {
271     DVLOG(1) << "Forcing DUCKING to be ON for output";
272     implicit_ducking_effect = media::AudioParameters::DUCKING;
273   } else {
274     DVLOG(1) << "DUCKING not forced ON for output";
275   }
276
277   if (source_params_.Equals(params))
278     return;
279
280   // Reset the |source_params_|, |sink_params_| and |loopback_fifo_| to match
281   // the new format.
282
283   source_params_ = params;
284
285   sink_params_ = media::AudioParameters(source_params_.format(),
286       source_params_.channel_layout(), source_params_.sample_rate(),
287       source_params_.bits_per_sample(),
288       WebRtcAudioRenderer::GetOptimalBufferSize(source_params_.sample_rate(),
289                                                 frames_per_buffer_),
290       // If DUCKING is enabled on the source, it needs to be enabled on the
291       // sink as well.
292       source_params_.effects() | implicit_ducking_effect);
293
294   {
295     // TODO(henrika): we could add a more dynamic solution here but I prefer
296     // a fixed size combined with bad audio at overflow. The alternative is
297     // that we start to build up latency and that can be more difficult to
298     // detect. Tests have shown that the FIFO never contains more than 2 or 3
299     // audio frames but I have selected a max size of ten buffers just
300     // in case since these tests were performed on a 16 core, 64GB Win 7
301     // machine. We could also add some sort of error notifier in this area if
302     // the FIFO overflows.
303     const int blocks_of_buffers =
304         10 * params.frames_per_buffer() / sink_params_.frames_per_buffer() + 1;
305     media::AudioBlockFifo* new_fifo = new media::AudioBlockFifo(
306         params.channels(), sink_params_.frames_per_buffer(), blocks_of_buffers);
307
308     base::AutoLock auto_lock(thread_lock_);
309     loopback_fifo_.reset(new_fifo);
310   }
311
312   if (!sink_.get())
313     return;  // WebRtcLocalAudioRenderer has not yet been started.
314
315   // Stop |sink_| and re-create a new one to be initialized with different audio
316   // parameters.  Then, invoke MaybeStartSink() to restart everything again.
317   if (sink_started_) {
318     sink_->Stop();
319     sink_started_ = false;
320   }
321
322   sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_,
323                                               source_render_frame_id_);
324   MaybeStartSink();
325 }
326
327 }  // namespace content