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/synchronization/lock.h"
11#include "content/renderer/media/audio_device_factory.h"
12#include "content/renderer/media/webrtc_audio_capturer.h"
13#include "content/renderer/render_thread_impl.h"
14#include "media/audio/audio_output_device.h"
15#include "media/base/audio_bus.h"
16#include "media/base/audio_fifo.h"
17#include "media/base/audio_hardware_config.h"
18
19namespace content {
20
21// media::AudioRendererSink::RenderCallback implementation
22int WebRtcLocalAudioRenderer::Render(
23    media::AudioBus* audio_bus, int audio_delay_milliseconds) {
24  base::AutoLock auto_lock(thread_lock_);
25
26  if (!playing_) {
27    audio_bus->Zero();
28    return 0;
29  }
30
31  TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
32
33  DCHECK(loopback_fifo_.get() != NULL);
34
35  // Provide data by reading from the FIFO if the FIFO contains enough
36  // to fulfill the request.
37  if (loopback_fifo_->frames() >= audio_bus->frames()) {
38    loopback_fifo_->Consume(audio_bus, 0, audio_bus->frames());
39  } else {
40    audio_bus->Zero();
41    // This warning is perfectly safe if it happens for the first audio
42    // frames. It should not happen in a steady-state mode.
43    DVLOG(2) << "loopback FIFO is empty";
44  }
45
46  return audio_bus->frames();
47}
48
49void WebRtcLocalAudioRenderer::OnRenderError() {
50  NOTIMPLEMENTED();
51}
52
53// content::WebRtcAudioCapturerSink implementation
54int WebRtcLocalAudioRenderer::CaptureData(const std::vector<int>& channels,
55                                           const int16* audio_data,
56                                           int sample_rate,
57                                           int number_of_channels,
58                                           int number_of_frames,
59                                           int audio_delay_milliseconds,
60                                           int current_volume,
61                                           bool need_audio_processing) {
62  TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
63  base::AutoLock auto_lock(thread_lock_);
64
65  if (!playing_)
66    return 0;
67
68  // Push captured audio to FIFO so it can be read by a local sink.
69  if (loopback_fifo_) {
70    if (loopback_fifo_->frames() + number_of_frames <=
71        loopback_fifo_->max_frames()) {
72      scoped_ptr<media::AudioBus> audio_source = media::AudioBus::Create(
73          number_of_channels, number_of_frames);
74      audio_source->FromInterleaved(audio_data,
75                                    audio_source->frames(),
76                                    sizeof(audio_data[0]));
77      loopback_fifo_->Push(audio_source.get());
78
79      base::Time now = base::Time::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  return 0;
88}
89
90void WebRtcLocalAudioRenderer::SetCaptureFormat(
91    const media::AudioParameters& params) {
92  audio_params_ = params;
93}
94
95// WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
96WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
97    WebRtcLocalAudioTrack* audio_track,
98    int source_render_view_id)
99    : audio_track_(audio_track),
100      source_render_view_id_(source_render_view_id),
101      playing_(false) {
102  DCHECK(audio_track);
103  DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
104}
105
106WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
107  DCHECK(thread_checker_.CalledOnValidThread());
108  DCHECK(!sink_.get());
109  DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
110}
111
112void WebRtcLocalAudioRenderer::Start() {
113  DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
114  DCHECK(thread_checker_.CalledOnValidThread());
115  // Add this class as sink to the audio track to ensure that we receive
116  // WebRtcAudioCapturerSink::CaptureData() callbacks for captured audio.
117  // |audio_params_| will be updated right after the AddCapturerAudioTrack().
118  audio_track_->AddSink(this);
119
120  base::AutoLock auto_lock(thread_lock_);
121  DCHECK(!sink_.get());
122
123  // TODO(henrika): we could add a more dynamic solution here but I prefer
124  // a fixed size combined with bad audio at overflow. The alternative is
125  // that we start to build up latency and that can be more difficult to
126  // detect. Tests have shown that the FIFO never contains more than 2 or 3
127  // audio frames but I have selected a max size of ten buffers just
128  // in case since these tests were performed on a 16 core, 64GB Win 7
129  // machine. We could also add some sort of error notifier in this area if
130  // the FIFO overflows.
131  DCHECK(!loopback_fifo_);
132  loopback_fifo_.reset(new media::AudioFifo(
133      audio_params_.channels(), 10 * audio_params_.frames_per_buffer()));
134
135#if defined(OS_ANDROID)
136  media::AudioHardwareConfig* hardware_config =
137      RenderThreadImpl::current()->GetAudioHardwareConfig();
138#endif
139
140  media::AudioParameters sink_params(audio_params_.format(),
141                                     audio_params_.channel_layout(),
142                                     audio_params_.sample_rate(),
143                                     audio_params_.bits_per_sample(),
144#if defined(OS_ANDROID)
145  // On Android, input and output are using same sampling rate. In order to
146  // achieve low latency mode, we need use buffer size suggested by
147  // AudioManager for the sink paramters which will be used to decide
148  // buffer size for shared memory buffer.
149                                     hardware_config->GetOutputBufferSize()
150#else
151                                     2 * audio_params_.frames_per_buffer()
152#endif
153                                    );
154  sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_);
155
156  // TODO(henrika): we could utilize the unified audio here instead and do
157  // sink_->InitializeIO(sink_params, 2, callback_.get());
158  // It would then be possible to avoid using the WebRtcAudioCapturer.
159  sink_->Initialize(sink_params, this);
160
161  // Start the capturer and local rendering. Note that, the capturer is owned
162  // by the WebRTC ADM and might already bee running.
163  sink_->Start();
164
165  last_render_time_ = base::Time::Now();
166  playing_ = false;
167}
168
169void WebRtcLocalAudioRenderer::Stop() {
170  DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
171  DCHECK(thread_checker_.CalledOnValidThread());
172
173  if (!sink_.get())
174    return;
175
176  {
177    base::AutoLock auto_lock(thread_lock_);
178    playing_ = false;
179
180    if (loopback_fifo_.get() != NULL) {
181      loopback_fifo_->Clear();
182      loopback_fifo_.reset();
183    }
184  }
185
186  // Stop the output audio stream, i.e, stop asking for data to render.
187  sink_->Stop();
188  sink_ = NULL;
189
190  // Ensure that the capturer stops feeding us with captured audio.
191  // Note that, we do not stop the capturer here since it may still be used by
192  // the WebRTC ADM.
193  audio_track_->RemoveSink(this);
194  audio_track_ = NULL;
195}
196
197void WebRtcLocalAudioRenderer::Play() {
198  DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
199  DCHECK(thread_checker_.CalledOnValidThread());
200  base::AutoLock auto_lock(thread_lock_);
201
202  if (!sink_.get())
203    return;
204
205  // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
206  // now reads data from the local FIFO.
207  playing_ = true;
208  last_render_time_ = base::Time::Now();
209
210  if (loopback_fifo_)
211    loopback_fifo_->Clear();
212}
213
214void WebRtcLocalAudioRenderer::Pause() {
215  DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
216  DCHECK(thread_checker_.CalledOnValidThread());
217  base::AutoLock auto_lock(thread_lock_);
218
219  if (!sink_.get())
220    return;
221
222  // Temporarily suspends rendering audio.
223  // WebRtcLocalAudioRenderer::Render() will return early during this state
224  // and only zeros will be provided to the active sink.
225  playing_ = false;
226}
227
228void WebRtcLocalAudioRenderer::SetVolume(float volume) {
229  DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
230  DCHECK(thread_checker_.CalledOnValidThread());
231  base::AutoLock auto_lock(thread_lock_);
232  if (sink_.get())
233    sink_->SetVolume(volume);
234}
235
236base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
237  DCHECK(thread_checker_.CalledOnValidThread());
238  base::AutoLock auto_lock(thread_lock_);
239  if (!sink_.get())
240    return base::TimeDelta();
241  return total_render_time();
242}
243
244bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
245  return true;
246}
247
248}  // namespace content
249