// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/media/webrtc_local_audio_source_provider.h" #include "base/logging.h" #include "content/renderer/render_thread_impl.h" #include "media/audio/audio_parameters.h" #include "media/base/audio_fifo.h" #include "media/base/audio_hardware_config.h" #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h" using blink::WebVector; namespace content { static const size_t kMaxNumberOfBuffers = 10; // Size of the buffer that WebAudio processes each time, it is the same value // as AudioNode::ProcessingSizeInFrames in WebKit. // static const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize = 128; WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider( const blink::WebMediaStreamTrack& track) : is_enabled_(false), track_(track), track_stopped_(false) { // Get the native audio output hardware sample-rate for the sink. // We need to check if RenderThreadImpl is valid here since the unittests // do not have one and they will inject their own |sink_params_| for testing. if (RenderThreadImpl::current()) { media::AudioHardwareConfig* hardware_config = RenderThreadImpl::current()->GetAudioHardwareConfig(); int sample_rate = hardware_config->GetOutputSampleRate(); sink_params_.Reset( media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::CHANNEL_LAYOUT_STEREO, 2, 0, sample_rate, 16, kWebAudioRenderBufferSize); } // Connect the source provider to the track as a sink. MediaStreamAudioSink::AddToAudioTrack(this, track_); } WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() { if (audio_converter_.get()) audio_converter_->RemoveInput(this); // If the track is still active, it is necessary to notify the track before // the source provider goes away. if (!track_stopped_) MediaStreamAudioSink::RemoveFromAudioTrack(this, track_); } void WebRtcLocalAudioSourceProvider::OnSetFormat( const media::AudioParameters& params) { // We need detach the thread here because it will be a new capture thread // calling OnSetFormat() and OnData() if the source is restarted. capture_thread_checker_.DetachFromThread(); DCHECK(capture_thread_checker_.CalledOnValidThread()); DCHECK(params.IsValid()); DCHECK(sink_params_.IsValid()); base::AutoLock auto_lock(lock_); source_params_ = params; // Create the audio converter with |disable_fifo| as false so that the // converter will request source_params.frames_per_buffer() each time. // This will not increase the complexity as there is only one client to // the converter. audio_converter_.reset( new media::AudioConverter(params, sink_params_, false)); audio_converter_->AddInput(this); fifo_.reset(new media::AudioFifo( params.channels(), kMaxNumberOfBuffers * params.frames_per_buffer())); input_bus_ = media::AudioBus::Create(params.channels(), params.frames_per_buffer()); } void WebRtcLocalAudioSourceProvider::OnReadyStateChanged( blink::WebMediaStreamSource::ReadyState state) { if (state == blink::WebMediaStreamSource::ReadyStateEnded) track_stopped_ = true; } void WebRtcLocalAudioSourceProvider::OnData( const int16* audio_data, int sample_rate, int number_of_channels, int number_of_frames) { DCHECK(capture_thread_checker_.CalledOnValidThread()); base::AutoLock auto_lock(lock_); if (!is_enabled_) return; DCHECK(fifo_.get()); // TODO(xians): A better way to handle the interleaved and deinterleaved // format switching, see issue/317710. DCHECK(input_bus_->frames() == number_of_frames); DCHECK(input_bus_->channels() == number_of_channels); input_bus_->FromInterleaved(audio_data, number_of_frames, 2); if (fifo_->frames() + number_of_frames <= fifo_->max_frames()) { fifo_->Push(input_bus_.get()); } else { // This can happen if the data in FIFO is too slowly consumed or // WebAudio stops consuming data. DVLOG(3) << "Local source provicer FIFO is full" << fifo_->frames(); } } void WebRtcLocalAudioSourceProvider::setClient( blink::WebAudioSourceProviderClient* client) { NOTREACHED(); } void WebRtcLocalAudioSourceProvider::provideInput( const WebVector& audio_data, size_t number_of_frames) { DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize); if (!output_wrapper_ || static_cast(output_wrapper_->channels()) != audio_data.size()) { output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size()); } output_wrapper_->set_frames(number_of_frames); for (size_t i = 0; i < audio_data.size(); ++i) output_wrapper_->SetChannelData(i, audio_data[i]); base::AutoLock auto_lock(lock_); if (!audio_converter_) return; is_enabled_ = true; audio_converter_->Convert(output_wrapper_.get()); } double WebRtcLocalAudioSourceProvider::ProvideInput( media::AudioBus* audio_bus, base::TimeDelta buffer_delay) { if (fifo_->frames() >= audio_bus->frames()) { fifo_->Consume(audio_bus, 0, audio_bus->frames()); } else { audio_bus->Zero(); DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_->frames() << " samples but " << audio_bus->frames() << " samples are needed"; } return 1.0; } void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting( const media::AudioParameters& sink_params) { sink_params_ = sink_params; } } // namespace content