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_track.h"
7 #include "content/renderer/media/webaudio_capturer_source.h"
8 #include "content/renderer/media/webrtc_audio_capturer.h"
9 #include "content/renderer/media/webrtc_audio_capturer_sink_owner.h"
10 #include "content/renderer/media/webrtc_local_audio_source_provider.h"
11 #include "media/base/audio_fifo.h"
12 #include "third_party/libjingle/source/talk/media/base/audiorenderer.h"
16 static const size_t kMaxNumberOfBuffersInFifo = 2;
17 static const char kAudioTrackKind[] = "audio";
21 using webrtc::MediaConstraintsInterface;
23 // This helper function checks if any audio constraints are set that require
24 // audio processing to be applied. Right now this is a big, single switch for
25 // all of the properties, but in the future they'll be handled one by one.
26 bool NeedsAudioProcessing(
27 const webrtc::MediaConstraintsInterface* constraints) {
31 static const char* kAudioProcessingProperties[] = {
32 MediaConstraintsInterface::kEchoCancellation,
33 MediaConstraintsInterface::kExperimentalEchoCancellation,
34 MediaConstraintsInterface::kAutoGainControl,
35 MediaConstraintsInterface::kExperimentalAutoGainControl,
36 MediaConstraintsInterface::kNoiseSuppression,
37 MediaConstraintsInterface::kHighpassFilter,
38 MediaConstraintsInterface::kTypingNoiseDetection,
41 for (size_t i = 0; i < arraysize(kAudioProcessingProperties); ++i) {
43 if (webrtc::FindConstraint(constraints, kAudioProcessingProperties[i],
55 // This is a temporary audio buffer with parameters used to send data to
57 class WebRtcLocalAudioTrack::ConfiguredBuffer :
58 public base::RefCounted<WebRtcLocalAudioTrack::ConfiguredBuffer> {
60 ConfiguredBuffer() : sink_buffer_size_(0) {}
62 void Initialize(const media::AudioParameters& params) {
63 DCHECK(params.IsValid());
66 // Use 10ms as the sink buffer size since that is the native packet size
67 // WebRtc is running on.
68 sink_buffer_size_ = params.sample_rate() / 100;
70 media::AudioBus::Create(params.channels(), sink_buffer_size_);
71 buffer_.reset(new int16[sink_buffer_size_ * params.channels()]);
73 // The size of the FIFO should be at least twice of the source buffer size
74 // or twice of the sink buffer size.
75 int buffer_size = std::max(
76 kMaxNumberOfBuffersInFifo * params.frames_per_buffer(),
77 kMaxNumberOfBuffersInFifo * sink_buffer_size_);
78 fifo_.reset(new media::AudioFifo(params.channels(), buffer_size));
81 void Push(media::AudioBus* audio_source) {
82 DCHECK(fifo_->frames() + audio_source->frames() <= fifo_->max_frames());
83 fifo_->Push(audio_source);
87 if (fifo_->frames() < audio_wrapper_->frames())
90 fifo_->Consume(audio_wrapper_.get(), 0, audio_wrapper_->frames());
91 audio_wrapper_->ToInterleaved(audio_wrapper_->frames(),
92 params_.bits_per_sample() / 8,
97 int16* buffer() const { return buffer_.get(); }
98 const media::AudioParameters& params() const { return params_; }
99 int sink_buffer_size() const { return sink_buffer_size_; }
102 ~ConfiguredBuffer() {}
103 friend class base::RefCounted<WebRtcLocalAudioTrack::ConfiguredBuffer>;
105 media::AudioParameters params_;
106 scoped_ptr<media::AudioBus> audio_wrapper_;
107 scoped_ptr<media::AudioFifo> fifo_;
108 scoped_ptr<int16[]> buffer_;
109 int sink_buffer_size_;
112 scoped_refptr<WebRtcLocalAudioTrack> WebRtcLocalAudioTrack::Create(
113 const std::string& id,
114 const scoped_refptr<WebRtcAudioCapturer>& capturer,
115 WebAudioCapturerSource* webaudio_source,
116 webrtc::AudioSourceInterface* track_source,
117 const webrtc::MediaConstraintsInterface* constraints) {
118 talk_base::RefCountedObject<WebRtcLocalAudioTrack>* track =
119 new talk_base::RefCountedObject<WebRtcLocalAudioTrack>(
120 id, capturer, webaudio_source, track_source, constraints);
124 WebRtcLocalAudioTrack::WebRtcLocalAudioTrack(
125 const std::string& label,
126 const scoped_refptr<WebRtcAudioCapturer>& capturer,
127 WebAudioCapturerSource* webaudio_source,
128 webrtc::AudioSourceInterface* track_source,
129 const webrtc::MediaConstraintsInterface* constraints)
130 : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(label),
132 webaudio_source_(webaudio_source),
133 track_source_(track_source),
134 need_audio_processing_(NeedsAudioProcessing(constraints)) {
135 DCHECK(capturer.get() || webaudio_source);
136 DVLOG(1) << "WebRtcLocalAudioTrack::WebRtcLocalAudioTrack()";
139 WebRtcLocalAudioTrack::~WebRtcLocalAudioTrack() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141 DVLOG(1) << "WebRtcLocalAudioTrack::~WebRtcLocalAudioTrack()";
142 // Users might not call Stop() on the track.
146 void WebRtcLocalAudioTrack::Capture(media::AudioBus* audio_source,
147 int audio_delay_milliseconds,
150 scoped_refptr<WebRtcAudioCapturer> capturer;
151 std::vector<int> voe_channels;
153 int number_of_channels = 0;
154 int number_of_frames = 0;
156 bool is_webaudio_source = false;
157 scoped_refptr<ConfiguredBuffer> current_buffer;
159 base::AutoLock auto_lock(lock_);
160 capturer = capturer_;
161 voe_channels = voe_channels_;
162 current_buffer = buffer_;
163 sample_rate = current_buffer->params().sample_rate();
164 number_of_channels = current_buffer->params().channels();
165 number_of_frames = current_buffer->sink_buffer_size();
167 is_webaudio_source = (webaudio_source_.get() != NULL);
170 // Push the data to the fifo.
171 current_buffer->Push(audio_source);
173 // When the source is WebAudio, turn off the audio processing if the delay
174 // value is 0 even though the constraint is set to true. In such case, it
175 // indicates the data is not from microphone.
176 // TODO(xians): remove the flag when supporting one APM per audio track.
177 // See crbug/264611 for details.
178 bool need_audio_processing = need_audio_processing_;
179 if (is_webaudio_source && need_audio_processing)
180 need_audio_processing = (audio_delay_milliseconds != 0);
182 int current_volume = volume;
183 while (current_buffer->Consume()) {
184 // Feed the data to the sinks.
185 // TODO (jiayl): we should not pass the real audio data down if the track is
186 // disabled. This is currently done so to feed input to WebRTC typing
187 // detection and should be changed when audio processing is moved from
188 // WebRTC to the track.
189 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) {
190 int new_volume = (*it)->CaptureData(voe_channels,
191 current_buffer->buffer(),
195 audio_delay_milliseconds,
197 need_audio_processing,
199 if (new_volume != 0 && capturer.get()) {
200 // Feed the new volume to WebRtc while changing the volume on the
202 capturer->SetVolume(new_volume);
203 current_volume = new_volume;
209 void WebRtcLocalAudioTrack::SetCaptureFormat(
210 const media::AudioParameters& params) {
211 if (!params.IsValid())
214 scoped_refptr<ConfiguredBuffer> new_buffer(new ConfiguredBuffer());
215 new_buffer->Initialize(params);
219 base::AutoLock auto_lock(lock_);
220 buffer_ = new_buffer;
224 // Update all the existing sinks with the new format.
225 for (SinkList::const_iterator it = sinks.begin();
226 it != sinks.end(); ++it) {
227 (*it)->SetCaptureFormat(params);
231 void WebRtcLocalAudioTrack::AddChannel(int channel_id) {
232 DVLOG(1) << "WebRtcLocalAudioTrack::AddChannel(channel_id="
233 << channel_id << ")";
234 base::AutoLock auto_lock(lock_);
235 if (std::find(voe_channels_.begin(), voe_channels_.end(), channel_id) !=
236 voe_channels_.end()) {
237 // We need to handle the case when the same channel is connected to the
238 // track more than once.
242 voe_channels_.push_back(channel_id);
245 void WebRtcLocalAudioTrack::RemoveChannel(int channel_id) {
246 DVLOG(1) << "WebRtcLocalAudioTrack::RemoveChannel(channel_id="
247 << channel_id << ")";
248 base::AutoLock auto_lock(lock_);
249 std::vector<int>::iterator iter =
250 std::find(voe_channels_.begin(), voe_channels_.end(), channel_id);
251 DCHECK(iter != voe_channels_.end());
252 voe_channels_.erase(iter);
255 // webrtc::AudioTrackInterface implementation.
256 webrtc::AudioSourceInterface* WebRtcLocalAudioTrack::GetSource() const {
257 return track_source_;
260 cricket::AudioRenderer* WebRtcLocalAudioTrack::GetRenderer() {
264 std::string WebRtcLocalAudioTrack::kind() const {
265 return kAudioTrackKind;
268 void WebRtcLocalAudioTrack::AddSink(WebRtcAudioCapturerSink* sink) {
269 DCHECK(thread_checker_.CalledOnValidThread());
270 DVLOG(1) << "WebRtcLocalAudioTrack::AddSink()";
271 base::AutoLock auto_lock(lock_);
273 sink->SetCaptureFormat(buffer_->params());
275 // Verify that |sink| is not already added to the list.
277 sinks_.begin(), sinks_.end(),
278 WebRtcAudioCapturerSinkOwner::WrapsSink(sink)) == sinks_.end());
280 // Create (and add to the list) a new WebRtcAudioCapturerSinkOwner which owns
281 // the |sink| and delagates all calls to the WebRtcAudioCapturerSink
283 sinks_.push_back(new WebRtcAudioCapturerSinkOwner(sink));
286 void WebRtcLocalAudioTrack::RemoveSink(
287 WebRtcAudioCapturerSink* sink) {
288 DCHECK(thread_checker_.CalledOnValidThread());
289 DVLOG(1) << "WebRtcLocalAudioTrack::RemoveSink()";
291 base::AutoLock auto_lock(lock_);
292 // Get iterator to the first element for which WrapsSink(sink) returns true.
293 SinkList::iterator it = std::find_if(
294 sinks_.begin(), sinks_.end(),
295 WebRtcAudioCapturerSinkOwner::WrapsSink(sink));
296 if (it != sinks_.end()) {
297 // Clear the delegate to ensure that no more capture callbacks will
298 // be sent to this sink. Also avoids a possible crash which can happen
299 // if this method is called while capturing is active.
305 void WebRtcLocalAudioTrack::Start() {
306 DCHECK(thread_checker_.CalledOnValidThread());
307 DVLOG(1) << "WebRtcLocalAudioTrack::Start()";
308 if (webaudio_source_.get()) {
309 // If the track is hooking up with WebAudio, do NOT add the track to the
310 // capturer as its sink otherwise two streams in different clock will be
311 // pushed through the same track.
312 WebRtcLocalAudioSourceProvider* source_provider = NULL;
313 if (capturer_.get()) {
314 source_provider = static_cast<WebRtcLocalAudioSourceProvider*>(
315 capturer_->audio_source_provider());
317 webaudio_source_->Start(this, source_provider);
322 capturer_->AddTrack(this);
325 void WebRtcLocalAudioTrack::Stop() {
326 DCHECK(thread_checker_.CalledOnValidThread());
327 DVLOG(1) << "WebRtcLocalAudioTrack::Stop()";
328 if (!capturer_.get() && !webaudio_source_.get())
331 if (webaudio_source_.get()) {
332 // Called Stop() on the |webaudio_source_| explicitly so that
333 // |webaudio_source_| won't push more data to the track anymore.
334 // Also note that the track is not registered as a sink to the |capturer_|
335 // in such case and no need to call RemoveTrack().
336 webaudio_source_->Stop();
338 capturer_->RemoveTrack(this);
341 // Protect the pointers using the lock when accessing |sinks_| and
342 // setting the |capturer_| to NULL.
345 base::AutoLock auto_lock(lock_);
347 webaudio_source_ = NULL;
351 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it)
355 } // namespace content