• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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_source_provider.h"
6 
7 #include "base/logging.h"
8 #include "content/renderer/render_thread_impl.h"
9 #include "media/audio/audio_parameters.h"
10 #include "media/base/audio_fifo.h"
11 #include "media/base/audio_hardware_config.h"
12 #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"
13 
14 using blink::WebVector;
15 
16 namespace content {
17 
18 static const size_t kMaxNumberOfBuffers = 10;
19 
20 // Size of the buffer that WebAudio processes each time, it is the same value
21 // as AudioNode::ProcessingSizeInFrames in WebKit.
22 // static
23 const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize = 128;
24 
WebRtcLocalAudioSourceProvider(const blink::WebMediaStreamTrack & track)25 WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider(
26     const blink::WebMediaStreamTrack& track)
27     : is_enabled_(false),
28       track_(track),
29       track_stopped_(false) {
30   // Get the native audio output hardware sample-rate for the sink.
31   // We need to check if RenderThreadImpl is valid here since the unittests
32   // do not have one and they will inject their own |sink_params_| for testing.
33   if (RenderThreadImpl::current()) {
34     media::AudioHardwareConfig* hardware_config =
35         RenderThreadImpl::current()->GetAudioHardwareConfig();
36     int sample_rate = hardware_config->GetOutputSampleRate();
37     sink_params_.Reset(
38         media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
39         media::CHANNEL_LAYOUT_STEREO, 2, sample_rate, 16,
40         kWebAudioRenderBufferSize);
41   }
42 
43   // Connect the source provider to the track as a sink.
44   MediaStreamAudioSink::AddToAudioTrack(this, track_);
45 }
46 
~WebRtcLocalAudioSourceProvider()47 WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() {
48   if (audio_converter_.get())
49     audio_converter_->RemoveInput(this);
50 
51   // If the track is still active, it is necessary to notify the track before
52   // the source provider goes away.
53   if (!track_stopped_)
54     MediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
55 }
56 
OnSetFormat(const media::AudioParameters & params)57 void WebRtcLocalAudioSourceProvider::OnSetFormat(
58     const media::AudioParameters& params) {
59   // We need detach the thread here because it will be a new capture thread
60   // calling OnSetFormat() and OnData() if the source is restarted.
61   capture_thread_checker_.DetachFromThread();
62   DCHECK(capture_thread_checker_.CalledOnValidThread());
63   DCHECK(params.IsValid());
64   DCHECK(sink_params_.IsValid());
65 
66   base::AutoLock auto_lock(lock_);
67   source_params_ = params;
68   // Create the audio converter with |disable_fifo| as false so that the
69   // converter will request source_params.frames_per_buffer() each time.
70   // This will not increase the complexity as there is only one client to
71   // the converter.
72   audio_converter_.reset(
73       new media::AudioConverter(params, sink_params_, false));
74   audio_converter_->AddInput(this);
75   fifo_.reset(new media::AudioFifo(
76       params.channels(),
77       kMaxNumberOfBuffers * params.frames_per_buffer()));
78   input_bus_ = media::AudioBus::Create(params.channels(),
79                                        params.frames_per_buffer());
80 }
81 
OnReadyStateChanged(blink::WebMediaStreamSource::ReadyState state)82 void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
83       blink::WebMediaStreamSource::ReadyState state) {
84   if (state ==  blink::WebMediaStreamSource::ReadyStateEnded)
85     track_stopped_ = true;
86 }
87 
OnData(const int16 * audio_data,int sample_rate,int number_of_channels,int number_of_frames)88 void WebRtcLocalAudioSourceProvider::OnData(
89     const int16* audio_data,
90     int sample_rate,
91     int number_of_channels,
92     int number_of_frames) {
93   DCHECK(capture_thread_checker_.CalledOnValidThread());
94   base::AutoLock auto_lock(lock_);
95   if (!is_enabled_)
96     return;
97 
98   DCHECK(fifo_.get());
99 
100   // TODO(xians): A better way to handle the interleaved and deinterleaved
101   // format switching, see issue/317710.
102   DCHECK(input_bus_->frames() == number_of_frames);
103   DCHECK(input_bus_->channels() == number_of_channels);
104   input_bus_->FromInterleaved(audio_data, number_of_frames, 2);
105 
106   if (fifo_->frames() + number_of_frames <= fifo_->max_frames()) {
107     fifo_->Push(input_bus_.get());
108   } else {
109     // This can happen if the data in FIFO is too slowly consumed or
110     // WebAudio stops consuming data.
111     DVLOG(3) << "Local source provicer FIFO is full" << fifo_->frames();
112   }
113 }
114 
setClient(blink::WebAudioSourceProviderClient * client)115 void WebRtcLocalAudioSourceProvider::setClient(
116     blink::WebAudioSourceProviderClient* client) {
117   NOTREACHED();
118 }
119 
provideInput(const WebVector<float * > & audio_data,size_t number_of_frames)120 void WebRtcLocalAudioSourceProvider::provideInput(
121     const WebVector<float*>& audio_data, size_t number_of_frames) {
122   DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize);
123   if (!output_wrapper_ ||
124       static_cast<size_t>(output_wrapper_->channels()) != audio_data.size()) {
125     output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
126   }
127 
128   output_wrapper_->set_frames(number_of_frames);
129   for (size_t i = 0; i < audio_data.size(); ++i)
130     output_wrapper_->SetChannelData(i, audio_data[i]);
131 
132   base::AutoLock auto_lock(lock_);
133   if (!audio_converter_)
134     return;
135 
136   is_enabled_ = true;
137   audio_converter_->Convert(output_wrapper_.get());
138 }
139 
ProvideInput(media::AudioBus * audio_bus,base::TimeDelta buffer_delay)140 double WebRtcLocalAudioSourceProvider::ProvideInput(
141     media::AudioBus* audio_bus, base::TimeDelta buffer_delay) {
142   if (fifo_->frames() >= audio_bus->frames()) {
143     fifo_->Consume(audio_bus, 0, audio_bus->frames());
144   } else {
145     audio_bus->Zero();
146     DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_->frames()
147              << " samples but " << audio_bus->frames()
148              << " samples are needed";
149   }
150 
151   return 1.0;
152 }
153 
SetSinkParamsForTesting(const media::AudioParameters & sink_params)154 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
155     const media::AudioParameters& sink_params) {
156   sink_params_ = sink_params;
157 }
158 
159 }  // namespace content
160