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 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 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 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 82 void WebRtcLocalAudioSourceProvider::OnReadyStateChanged( 83 blink::WebMediaStreamSource::ReadyState state) { 84 if (state == blink::WebMediaStreamSource::ReadyStateEnded) 85 track_stopped_ = true; 86 } 87 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 115 void WebRtcLocalAudioSourceProvider::setClient( 116 blink::WebAudioSourceProviderClient* client) { 117 NOTREACHED(); 118 } 119 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 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 154 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting( 155 const media::AudioParameters& sink_params) { 156 sink_params_ = sink_params; 157 } 158 159 } // namespace content 160