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/webaudio_capturer_source.h"
6
7#include "base/logging.h"
8#include "base/time/time.h"
9#include "content/renderer/media/webrtc_audio_capturer.h"
10#include "content/renderer/media/webrtc_local_audio_track.h"
11
12using media::AudioBus;
13using media::AudioFifo;
14using media::AudioParameters;
15using media::ChannelLayout;
16using media::CHANNEL_LAYOUT_MONO;
17using media::CHANNEL_LAYOUT_STEREO;
18
19static const int kMaxNumberOfBuffersInFifo = 5;
20
21namespace content {
22
23WebAudioCapturerSource::WebAudioCapturerSource()
24    : track_(NULL),
25      capturer_(NULL),
26      audio_format_changed_(false) {
27}
28
29WebAudioCapturerSource::~WebAudioCapturerSource() {
30}
31
32void WebAudioCapturerSource::setFormat(
33    size_t number_of_channels, float sample_rate) {
34  DCHECK(thread_checker_.CalledOnValidThread());
35  DVLOG(1) << "WebAudioCapturerSource::setFormat(sample_rate="
36           << sample_rate << ")";
37  if (number_of_channels > 2) {
38    // TODO(xians): Handle more than just the mono and stereo cases.
39    LOG(WARNING) << "WebAudioCapturerSource::setFormat() : unhandled format.";
40    return;
41  }
42
43  ChannelLayout channel_layout =
44      number_of_channels == 1 ? CHANNEL_LAYOUT_MONO : CHANNEL_LAYOUT_STEREO;
45
46  base::AutoLock auto_lock(lock_);
47  // Set the format used by this WebAudioCapturerSource. We are using 10ms data
48  // as buffer size since that is the native buffer size of WebRtc packet
49  // running on.
50  params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
51                channel_layout, number_of_channels, sample_rate, 16,
52                sample_rate / 100);
53  audio_format_changed_ = true;
54
55  wrapper_bus_ = AudioBus::CreateWrapper(params_.channels());
56  capture_bus_ = AudioBus::Create(params_);
57  audio_data_.reset(
58      new int16[params_.frames_per_buffer() * params_.channels()]);
59  fifo_.reset(new AudioFifo(
60      params_.channels(),
61      kMaxNumberOfBuffersInFifo * params_.frames_per_buffer()));
62}
63
64void WebAudioCapturerSource::Start(
65    WebRtcLocalAudioTrack* track, WebRtcAudioCapturer* capturer) {
66  DCHECK(thread_checker_.CalledOnValidThread());
67  DCHECK(track);
68  base::AutoLock auto_lock(lock_);
69  track_ = track;
70  capturer_ = capturer;
71}
72
73void WebAudioCapturerSource::Stop() {
74  DCHECK(thread_checker_.CalledOnValidThread());
75  base::AutoLock auto_lock(lock_);
76  track_ = NULL;
77  capturer_ = NULL;
78}
79
80void WebAudioCapturerSource::consumeAudio(
81    const blink::WebVector<const float*>& audio_data,
82    size_t number_of_frames) {
83  base::AutoLock auto_lock(lock_);
84  if (!track_)
85    return;
86
87  // Update the downstream client if the audio format has been changed.
88  if (audio_format_changed_) {
89    track_->OnSetFormat(params_);
90    audio_format_changed_ = false;
91  }
92
93  wrapper_bus_->set_frames(number_of_frames);
94
95  // Make sure WebKit is honoring what it told us up front
96  // about the channels.
97  DCHECK_EQ(params_.channels(), static_cast<int>(audio_data.size()));
98
99  for (size_t i = 0; i < audio_data.size(); ++i)
100    wrapper_bus_->SetChannelData(i, const_cast<float*>(audio_data[i]));
101
102  // Handle mismatch between WebAudio buffer-size and WebRTC.
103  int available = fifo_->max_frames() - fifo_->frames();
104  if (available < static_cast<int>(number_of_frames)) {
105    NOTREACHED() << "WebAudioCapturerSource::Consume() : FIFO overrun.";
106    return;
107  }
108
109  fifo_->Push(wrapper_bus_.get());
110  int capture_frames = params_.frames_per_buffer();
111  base::TimeDelta delay;
112  int volume = 0;
113  bool key_pressed = false;
114  if (capturer_) {
115    capturer_->GetAudioProcessingParams(&delay, &volume, &key_pressed);
116  }
117
118  // Turn off audio processing if the delay value is 0, since in such case,
119  // it indicates the data is not from microphone.
120  // TODO(xians): remove the flag when supporting one APM per audio track.
121  // See crbug/264611 for details.
122  bool need_audio_processing = (delay.InMilliseconds() != 0);
123  while (fifo_->frames() >= capture_frames) {
124    fifo_->Consume(capture_bus_.get(), 0, capture_frames);
125    // TODO(xians): Avoid this interleave/deinterleave operation.
126    capture_bus_->ToInterleaved(capture_bus_->frames(),
127                                params_.bits_per_sample() / 8,
128                                audio_data_.get());
129    track_->Capture(audio_data_.get(), delay, volume, key_pressed,
130                    need_audio_processing, false);
131  }
132}
133
134}  // namespace content
135