- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / media / webrtc_local_audio_track.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_track.h"
6
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"
13
14 namespace content {
15
16 static const size_t kMaxNumberOfBuffersInFifo = 2;
17 static const char kAudioTrackKind[] = "audio";
18
19 namespace {
20
21 using webrtc::MediaConstraintsInterface;
22
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) {
28   if (!constraints)
29     return false;
30
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,
39   };
40
41   for (size_t i = 0; i < arraysize(kAudioProcessingProperties); ++i) {
42     bool value = false;
43     if (webrtc::FindConstraint(constraints, kAudioProcessingProperties[i],
44                                &value, NULL) &&
45         value) {
46       return true;
47     }
48   }
49
50   return false;
51 }
52
53 }  // namespace.
54
55 // This is a temporary audio buffer with parameters used to send data to
56 // callbacks.
57 class WebRtcLocalAudioTrack::ConfiguredBuffer :
58     public base::RefCounted<WebRtcLocalAudioTrack::ConfiguredBuffer> {
59  public:
60   ConfiguredBuffer() : sink_buffer_size_(0) {}
61
62   void Initialize(const media::AudioParameters& params) {
63     DCHECK(params.IsValid());
64     params_ = params;
65
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;
69     audio_wrapper_ =
70         media::AudioBus::Create(params.channels(), sink_buffer_size_);
71     buffer_.reset(new int16[sink_buffer_size_ * params.channels()]);
72
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));
79   }
80
81   void Push(media::AudioBus* audio_source) {
82     DCHECK(fifo_->frames() + audio_source->frames() <= fifo_->max_frames());
83     fifo_->Push(audio_source);
84   }
85
86   bool Consume() {
87     if (fifo_->frames() < audio_wrapper_->frames())
88       return false;
89
90     fifo_->Consume(audio_wrapper_.get(), 0, audio_wrapper_->frames());
91     audio_wrapper_->ToInterleaved(audio_wrapper_->frames(),
92                                   params_.bits_per_sample() / 8,
93                                   buffer());
94     return true;
95   }
96
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_; }
100
101  private:
102   ~ConfiguredBuffer() {}
103   friend class base::RefCounted<WebRtcLocalAudioTrack::ConfiguredBuffer>;
104
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_;
110 };
111
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);
121   return track;
122 }
123
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),
131       capturer_(capturer),
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()";
137 }
138
139 WebRtcLocalAudioTrack::~WebRtcLocalAudioTrack() {
140   DCHECK(thread_checker_.CalledOnValidThread());
141   DVLOG(1) << "WebRtcLocalAudioTrack::~WebRtcLocalAudioTrack()";
142   // Users might not call Stop() on the track.
143   Stop();
144 }
145
146 void WebRtcLocalAudioTrack::Capture(media::AudioBus* audio_source,
147                                     int audio_delay_milliseconds,
148                                     int volume,
149                                     bool key_pressed) {
150   scoped_refptr<WebRtcAudioCapturer> capturer;
151   std::vector<int> voe_channels;
152   int sample_rate = 0;
153   int number_of_channels = 0;
154   int number_of_frames = 0;
155   SinkList sinks;
156   bool is_webaudio_source = false;
157   scoped_refptr<ConfiguredBuffer> current_buffer;
158   {
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();
166     sinks = sinks_;
167     is_webaudio_source = (webaudio_source_.get() != NULL);
168   }
169
170   // Push the data to the fifo.
171   current_buffer->Push(audio_source);
172
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);
181
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(),
192                                           sample_rate,
193                                           number_of_channels,
194                                           number_of_frames,
195                                           audio_delay_milliseconds,
196                                           current_volume,
197                                           need_audio_processing,
198                                           key_pressed);
199       if (new_volume != 0 && capturer.get()) {
200         // Feed the new volume to WebRtc while changing the volume on the
201         // browser.
202         capturer->SetVolume(new_volume);
203         current_volume = new_volume;
204       }
205     }
206   }
207 }
208
209 void WebRtcLocalAudioTrack::SetCaptureFormat(
210     const media::AudioParameters& params) {
211   if (!params.IsValid())
212     return;
213
214   scoped_refptr<ConfiguredBuffer> new_buffer(new ConfiguredBuffer());
215   new_buffer->Initialize(params);
216
217   SinkList sinks;
218   {
219     base::AutoLock auto_lock(lock_);
220     buffer_ = new_buffer;
221     sinks = sinks_;
222   }
223
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);
228   }
229 }
230
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.
239     return;
240   }
241
242   voe_channels_.push_back(channel_id);
243 }
244
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);
253 }
254
255 // webrtc::AudioTrackInterface implementation.
256 webrtc::AudioSourceInterface* WebRtcLocalAudioTrack::GetSource() const {
257   return track_source_;
258 }
259
260 cricket::AudioRenderer* WebRtcLocalAudioTrack::GetRenderer() {
261   return this;
262 }
263
264 std::string WebRtcLocalAudioTrack::kind() const {
265   return kAudioTrackKind;
266 }
267
268 void WebRtcLocalAudioTrack::AddSink(WebRtcAudioCapturerSink* sink) {
269   DCHECK(thread_checker_.CalledOnValidThread());
270   DVLOG(1) << "WebRtcLocalAudioTrack::AddSink()";
271   base::AutoLock auto_lock(lock_);
272   if (buffer_.get())
273     sink->SetCaptureFormat(buffer_->params());
274
275   // Verify that |sink| is not already added to the list.
276   DCHECK(std::find_if(
277       sinks_.begin(), sinks_.end(),
278       WebRtcAudioCapturerSinkOwner::WrapsSink(sink)) == sinks_.end());
279
280   // Create (and add to the list) a new WebRtcAudioCapturerSinkOwner which owns
281   // the |sink| and delagates all calls to the WebRtcAudioCapturerSink
282   // interface.
283   sinks_.push_back(new WebRtcAudioCapturerSinkOwner(sink));
284 }
285
286 void WebRtcLocalAudioTrack::RemoveSink(
287     WebRtcAudioCapturerSink* sink) {
288   DCHECK(thread_checker_.CalledOnValidThread());
289   DVLOG(1) << "WebRtcLocalAudioTrack::RemoveSink()";
290
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.
300     (*it)->Reset();
301     sinks_.erase(it);
302   }
303 }
304
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());
316     }
317     webaudio_source_->Start(this, source_provider);
318     return;
319   }
320
321   if (capturer_.get())
322     capturer_->AddTrack(this);
323 }
324
325 void WebRtcLocalAudioTrack::Stop() {
326   DCHECK(thread_checker_.CalledOnValidThread());
327   DVLOG(1) << "WebRtcLocalAudioTrack::Stop()";
328   if (!capturer_.get() && !webaudio_source_.get())
329     return;
330
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();
337   } else {
338     capturer_->RemoveTrack(this);
339   }
340
341   // Protect the pointers using the lock when accessing |sinks_| and
342   // setting the |capturer_| to NULL.
343   SinkList sinks;
344   {
345     base::AutoLock auto_lock(lock_);
346     sinks = sinks_;
347     webaudio_source_ = NULL;
348     capturer_ = NULL;
349   }
350
351   for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it)
352     (*it)->Reset();
353 }
354
355 }  // namespace content