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_audio_renderer.h"
6
7#include "base/logging.h"
8#include "base/metrics/histogram.h"
9#include "base/strings/string_util.h"
10#include "content/renderer/media/audio_device_factory.h"
11#include "content/renderer/media/webrtc_audio_device_impl.h"
12#include "content/renderer/render_thread_impl.h"
13#include "media/audio/audio_output_device.h"
14#include "media/audio/audio_parameters.h"
15#include "media/audio/sample_rates.h"
16#include "media/base/audio_hardware_config.h"
17
18#if defined(OS_WIN)
19#include "base/win/windows_version.h"
20#include "media/audio/win/core_audio_util_win.h"
21#endif
22
23namespace content {
24
25namespace {
26
27// Supported hardware sample rates for output sides.
28#if defined(OS_WIN) || defined(OS_MACOSX)
29// AudioHardwareConfig::GetOutputSampleRate() asks the audio layer for its
30// current sample rate (set by the user) on Windows and Mac OS X.  The listed
31// rates below adds restrictions and Initialize() will fail if the user selects
32// any rate outside these ranges.
33const int kValidOutputRates[] = {96000, 48000, 44100, 32000, 16000};
34#elif defined(OS_LINUX) || defined(OS_OPENBSD)
35const int kValidOutputRates[] = {48000, 44100};
36#elif defined(OS_ANDROID)
37// TODO(leozwang): We want to use native sampling rate on Android to achieve
38// low latency, currently 16000 is used to work around audio problem on some
39// Android devices.
40const int kValidOutputRates[] = {48000, 44100, 16000};
41const int kDefaultOutputBufferSize = 2048;
42#else
43const int kValidOutputRates[] = {44100};
44#endif
45
46// TODO(xians): Merge the following code to WebRtcAudioCapturer, or remove.
47enum AudioFramesPerBuffer {
48  k160,
49  k320,
50  k440,
51  k480,
52  k640,
53  k880,
54  k960,
55  k1440,
56  k1920,
57  kUnexpectedAudioBufferSize  // Must always be last!
58};
59
60// Helper method to convert integral values to their respective enum values
61// above, or kUnexpectedAudioBufferSize if no match exists.
62// We map 441 to k440 to avoid changes in the XML part for histograms.
63// It is still possible to map the histogram result to the actual buffer size.
64// See http://crbug.com/243450 for details.
65AudioFramesPerBuffer AsAudioFramesPerBuffer(int frames_per_buffer) {
66  switch (frames_per_buffer) {
67    case 160: return k160;
68    case 320: return k320;
69    case 441: return k440;
70    case 480: return k480;
71    case 640: return k640;
72    case 880: return k880;
73    case 960: return k960;
74    case 1440: return k1440;
75    case 1920: return k1920;
76  }
77  return kUnexpectedAudioBufferSize;
78}
79
80void AddHistogramFramesPerBuffer(int param) {
81  AudioFramesPerBuffer afpb = AsAudioFramesPerBuffer(param);
82  if (afpb != kUnexpectedAudioBufferSize) {
83    UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
84                              afpb, kUnexpectedAudioBufferSize);
85  } else {
86    // Report unexpected sample rates using a unique histogram name.
87    UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected", param);
88  }
89}
90
91}  // namespace
92
93WebRtcAudioRenderer::WebRtcAudioRenderer(int source_render_view_id)
94    : state_(UNINITIALIZED),
95      source_render_view_id_(source_render_view_id),
96      source_(NULL),
97      play_ref_count_(0),
98      audio_delay_milliseconds_(0),
99      fifo_delay_milliseconds_(0) {
100}
101
102WebRtcAudioRenderer::~WebRtcAudioRenderer() {
103  DCHECK(thread_checker_.CalledOnValidThread());
104  DCHECK_EQ(state_, UNINITIALIZED);
105  buffer_.reset();
106}
107
108bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) {
109  DVLOG(1) << "WebRtcAudioRenderer::Initialize()";
110  DCHECK(thread_checker_.CalledOnValidThread());
111  base::AutoLock auto_lock(lock_);
112  DCHECK_EQ(state_, UNINITIALIZED);
113  DCHECK(source);
114  DCHECK(!sink_.get());
115  DCHECK(!source_);
116
117  // Use stereo output on all platforms exept Android.
118  media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO;
119#if defined(OS_ANDROID)
120  DVLOG(1) << "Using mono audio output for Android";
121  channel_layout = media::CHANNEL_LAYOUT_MONO;
122#endif
123  // Ask the renderer for the default audio output hardware sample-rate.
124  media::AudioHardwareConfig* hardware_config =
125      RenderThreadImpl::current()->GetAudioHardwareConfig();
126  int sample_rate = hardware_config->GetOutputSampleRate();
127  DVLOG(1) << "Audio output hardware sample rate: " << sample_rate;
128
129  // WebRTC does not yet support higher rates than 96000 on the client side
130  // and 48000 is the preferred sample rate. Therefore, if 192000 is detected,
131  // we change the rate to 48000 instead. The consequence is that the native
132  // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz
133  // which will then be resampled by the audio converted on the browser side
134  // to match the native audio layer.
135  if (sample_rate == 192000) {
136    DVLOG(1) << "Resampling from 48000 to 192000 is required";
137    sample_rate = 48000;
138  }
139  media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate);
140  if (asr != media::kUnexpectedAudioSampleRate) {
141    UMA_HISTOGRAM_ENUMERATION(
142        "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate);
143  } else {
144    UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", sample_rate);
145  }
146
147  // Verify that the reported output hardware sample rate is supported
148  // on the current platform.
149  if (std::find(&kValidOutputRates[0],
150                &kValidOutputRates[0] + arraysize(kValidOutputRates),
151                sample_rate) ==
152                    &kValidOutputRates[arraysize(kValidOutputRates)]) {
153    DLOG(ERROR) << sample_rate << " is not a supported output rate.";
154    return false;
155  }
156
157  // Set up audio parameters for the source, i.e., the WebRTC client.
158
159  // The WebRTC client only supports multiples of 10ms as buffer size where
160  // 10ms is preferred for lowest possible delay.
161  media::AudioParameters source_params;
162  int buffer_size = (sample_rate / 100);
163  DVLOG(1) << "Using WebRTC output buffer size: " << buffer_size;
164
165  int channels = ChannelLayoutToChannelCount(channel_layout);
166  source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
167                      channel_layout, channels, 0,
168                      sample_rate, 16, buffer_size);
169
170  // Set up audio parameters for the sink, i.e., the native audio output stream.
171  // We strive to open up using native parameters to achieve best possible
172  // performance and to ensure that no FIFO is needed on the browser side to
173  // match the client request. Any mismatch between the source and the sink is
174  // taken care of in this class instead using a pull FIFO.
175
176  media::AudioParameters sink_params;
177
178#if defined(OS_ANDROID)
179  buffer_size = kDefaultOutputBufferSize;
180#else
181  buffer_size = hardware_config->GetOutputBufferSize();
182#endif
183
184  sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
185                    channel_layout, channels, 0, sample_rate, 16, buffer_size);
186
187  // Create a FIFO if re-buffering is required to match the source input with
188  // the sink request. The source acts as provider here and the sink as
189  // consumer.
190  fifo_delay_milliseconds_ = 0;
191  if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) {
192    DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer()
193             << " to " << sink_params.frames_per_buffer();
194    audio_fifo_.reset(new media::AudioPullFifo(
195        source_params.channels(),
196        source_params.frames_per_buffer(),
197        base::Bind(
198            &WebRtcAudioRenderer::SourceCallback,
199            base::Unretained(this))));
200
201    if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) {
202      int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond /
203          static_cast<double>(source_params.sample_rate());
204      fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() -
205        source_params.frames_per_buffer()) * frame_duration_milliseconds;
206    }
207  }
208
209
210  // Allocate local audio buffers based on the parameters above.
211  // It is assumed that each audio sample contains 16 bits and each
212  // audio frame contains one or two audio samples depending on the
213  // number of channels.
214  buffer_.reset(
215      new int16[source_params.frames_per_buffer() * source_params.channels()]);
216
217  source_ = source;
218  source->SetRenderFormat(source_params);
219
220  // Configure the audio rendering client and start rendering.
221  sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_);
222  sink_->Initialize(sink_params, this);
223  sink_->Start();
224
225  // User must call Play() before any audio can be heard.
226  state_ = PAUSED;
227
228  UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout",
229                            source_params.channel_layout(),
230                            media::CHANNEL_LAYOUT_MAX);
231  UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
232                            source_params.frames_per_buffer(),
233                            kUnexpectedAudioBufferSize);
234  AddHistogramFramesPerBuffer(source_params.frames_per_buffer());
235
236  return true;
237}
238
239void WebRtcAudioRenderer::Start() {
240  // TODO(xians): refactor to make usage of Start/Stop more symmetric.
241  NOTIMPLEMENTED();
242}
243
244void WebRtcAudioRenderer::Play() {
245  DVLOG(1) << "WebRtcAudioRenderer::Play()";
246  DCHECK(thread_checker_.CalledOnValidThread());
247  base::AutoLock auto_lock(lock_);
248  if (state_ == UNINITIALIZED)
249    return;
250
251  DCHECK(play_ref_count_ == 0 || state_ == PLAYING);
252  ++play_ref_count_;
253  state_ = PLAYING;
254
255  if (audio_fifo_) {
256    audio_delay_milliseconds_ = 0;
257    audio_fifo_->Clear();
258  }
259}
260
261void WebRtcAudioRenderer::Pause() {
262  DVLOG(1) << "WebRtcAudioRenderer::Pause()";
263  DCHECK(thread_checker_.CalledOnValidThread());
264  base::AutoLock auto_lock(lock_);
265  if (state_ == UNINITIALIZED)
266    return;
267
268  DCHECK_EQ(state_, PLAYING);
269  DCHECK_GT(play_ref_count_, 0);
270  if (!--play_ref_count_)
271    state_ = PAUSED;
272}
273
274void WebRtcAudioRenderer::Stop() {
275  DVLOG(1) << "WebRtcAudioRenderer::Stop()";
276  DCHECK(thread_checker_.CalledOnValidThread());
277  base::AutoLock auto_lock(lock_);
278  if (state_ == UNINITIALIZED)
279    return;
280
281  source_->RemoveAudioRenderer(this);
282  source_ = NULL;
283  sink_->Stop();
284  state_ = UNINITIALIZED;
285}
286
287void WebRtcAudioRenderer::SetVolume(float volume) {
288  DCHECK(thread_checker_.CalledOnValidThread());
289  base::AutoLock auto_lock(lock_);
290  if (state_ == UNINITIALIZED)
291    return;
292
293  sink_->SetVolume(volume);
294}
295
296base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const {
297  return base::TimeDelta();
298}
299
300bool WebRtcAudioRenderer::IsLocalRenderer() const {
301  return false;
302}
303
304int WebRtcAudioRenderer::Render(media::AudioBus* audio_bus,
305                                int audio_delay_milliseconds) {
306  base::AutoLock auto_lock(lock_);
307  if (!source_)
308    return 0;
309
310  DVLOG(2) << "WebRtcAudioRenderer::Render()";
311  DVLOG(2) << "audio_delay_milliseconds: " << audio_delay_milliseconds;
312
313  audio_delay_milliseconds_ = audio_delay_milliseconds;
314
315  if (audio_fifo_)
316    audio_fifo_->Consume(audio_bus, audio_bus->frames());
317  else
318    SourceCallback(0, audio_bus);
319
320  return (state_ == PLAYING) ? audio_bus->frames() : 0;
321}
322
323void WebRtcAudioRenderer::OnRenderError() {
324  NOTIMPLEMENTED();
325  LOG(ERROR) << "OnRenderError()";
326}
327
328// Called by AudioPullFifo when more data is necessary.
329void WebRtcAudioRenderer::SourceCallback(
330    int fifo_frame_delay, media::AudioBus* audio_bus) {
331  DVLOG(2) << "WebRtcAudioRenderer::SourceCallback("
332           << fifo_frame_delay << ", "
333           << audio_bus->frames() << ")";
334
335  int output_delay_milliseconds = audio_delay_milliseconds_;
336  output_delay_milliseconds += fifo_delay_milliseconds_;
337  DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds;
338
339  // We need to keep render data for the |source_| regardless of |state_|,
340  // otherwise the data will be buffered up inside |source_|.
341  source_->RenderData(reinterpret_cast<uint8*>(buffer_.get()),
342                      audio_bus->channels(), audio_bus->frames(),
343                      output_delay_milliseconds);
344
345  // Avoid filling up the audio bus if we are not playing; instead
346  // return here and ensure that the returned value in Render() is 0.
347  if (state_ != PLAYING) {
348    audio_bus->Zero();
349    return;
350  }
351
352  // De-interleave each channel and convert to 32-bit floating-point
353  // with nominal range -1.0 -> +1.0 to match the callback format.
354  audio_bus->FromInterleaved(buffer_.get(),
355                             audio_bus->frames(),
356                             sizeof(buffer_[0]));
357}
358
359}  // namespace content
360