diff options
Diffstat (limited to 'chromium/content/renderer/media/webrtc_audio_device_impl.cc')
-rw-r--r-- | chromium/content/renderer/media/webrtc_audio_device_impl.cc | 226 |
1 files changed, 153 insertions, 73 deletions
diff --git a/chromium/content/renderer/media/webrtc_audio_device_impl.cc b/chromium/content/renderer/media/webrtc_audio_device_impl.cc index 1daf048fe86..1339a6f4cb8 100644 --- a/chromium/content/renderer/media/webrtc_audio_device_impl.cc +++ b/chromium/content/renderer/media/webrtc_audio_device_impl.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram.h" #include "base/strings/string_util.h" #include "base/win/windows_version.h" +#include "content/renderer/media/media_stream_audio_processor.h" #include "content/renderer/media/webrtc_audio_capturer.h" #include "content/renderer/media/webrtc_audio_renderer.h" #include "content/renderer/render_thread_impl.h" @@ -27,7 +28,9 @@ WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl() initialized_(false), playing_(false), recording_(false), - microphone_volume_(0) { + microphone_volume_(0), + is_audio_track_processing_enabled_( + MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled()) { DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; } @@ -73,15 +76,25 @@ int WebRtcAudioDeviceImpl::OnData(const int16* audio_data, DVLOG(2) << "total delay: " << input_delay_ms_ + output_delay_ms_; } - // Write audio samples in blocks of 10 milliseconds to the registered + // Write audio frames in blocks of 10 milliseconds to the registered // webrtc::AudioTransport sink. Keep writing until our internal byte // buffer is empty. const int16* audio_buffer = audio_data; - const int samples_per_10_msec = (sample_rate / 100); - CHECK_EQ(number_of_frames % samples_per_10_msec, 0); - int accumulated_audio_samples = 0; + const int frames_per_10_ms = (sample_rate / 100); + CHECK_EQ(number_of_frames % frames_per_10_ms, 0); + int accumulated_audio_frames = 0; uint32_t new_volume = 0; - while (accumulated_audio_samples < number_of_frames) { + + // The lock here is to protect a race in the resampler inside webrtc when + // there are more than one input stream calling OnData(), which can happen + // when the users setup two getUserMedia, one for the microphone, another + // for WebAudio. Currently we don't have a better way to fix it except for + // adding a lock here to sequence the call. + // TODO(xians): Remove this workaround after we move the + // webrtc::AudioProcessing module to Chrome. See http://crbug/264611 for + // details. + base::AutoLock auto_lock(capture_callback_lock_); + while (accumulated_audio_frames < number_of_frames) { // Deliver 10ms of recorded 16-bit linear PCM audio. int new_mic_level = audio_transport_callback_->OnDataAvailable( &channels[0], @@ -89,14 +102,14 @@ int WebRtcAudioDeviceImpl::OnData(const int16* audio_data, audio_buffer, sample_rate, number_of_channels, - samples_per_10_msec, + frames_per_10_ms, total_delay_ms, current_volume, key_pressed, need_audio_processing); - accumulated_audio_samples += samples_per_10_msec; - audio_buffer += samples_per_10_msec * number_of_channels; + accumulated_audio_frames += frames_per_10_ms; + audio_buffer += frames_per_10_ms * number_of_channels; // The latest non-zero new microphone level will be returned. if (new_mic_level) @@ -111,11 +124,12 @@ void WebRtcAudioDeviceImpl::OnSetFormat( DVLOG(1) << "WebRtcAudioDeviceImpl::OnSetFormat()"; } -void WebRtcAudioDeviceImpl::RenderData(uint8* audio_data, - int number_of_channels, - int number_of_frames, - int audio_delay_milliseconds) { - DCHECK_LE(number_of_frames, output_buffer_size()); +void WebRtcAudioDeviceImpl::RenderData(media::AudioBus* audio_bus, + int sample_rate, + int audio_delay_milliseconds, + base::TimeDelta* current_time) { + render_buffer_.resize(audio_bus->frames() * audio_bus->channels()); + { base::AutoLock auto_lock(lock_); DCHECK(audio_transport_callback_); @@ -123,43 +137,79 @@ void WebRtcAudioDeviceImpl::RenderData(uint8* audio_data, output_delay_ms_ = audio_delay_milliseconds; } - const int channels = number_of_channels; - DCHECK_LE(channels, output_channels()); - - int samples_per_sec = output_sample_rate(); - int samples_per_10_msec = (samples_per_sec / 100); - int bytes_per_sample = output_audio_parameters_.bits_per_sample() / 8; - const int bytes_per_10_msec = - channels * samples_per_10_msec * bytes_per_sample; + int frames_per_10_ms = (sample_rate / 100); + int bytes_per_sample = sizeof(render_buffer_[0]); + const int bytes_per_10_ms = + audio_bus->channels() * frames_per_10_ms * bytes_per_sample; + DCHECK_EQ(audio_bus->frames() % frames_per_10_ms, 0); - uint32_t num_audio_samples = 0; - int accumulated_audio_samples = 0; - - // Get audio samples in blocks of 10 milliseconds from the registered + // Get audio frames in blocks of 10 milliseconds from the registered // webrtc::AudioTransport source. Keep reading until our internal buffer // is full. - while (accumulated_audio_samples < number_of_frames) { + uint32_t num_audio_frames = 0; + int accumulated_audio_frames = 0; + int16* audio_data = &render_buffer_[0]; + while (accumulated_audio_frames < audio_bus->frames()) { // Get 10ms and append output to temporary byte buffer. - audio_transport_callback_->NeedMorePlayData(samples_per_10_msec, - bytes_per_sample, - channels, - samples_per_sec, + int64_t elapsed_time_ms = -1; + int64_t ntp_time_ms = -1; + if (is_audio_track_processing_enabled_) { + // When audio processing is enabled in the audio track, we use + // PullRenderData() instead of NeedMorePlayData() to avoid passing the + // render data to the APM in WebRTC as reference signal for echo + // cancellation. + static const int kBitsPerByte = 8; + audio_transport_callback_->PullRenderData(bytes_per_sample * kBitsPerByte, + sample_rate, + audio_bus->channels(), + frames_per_10_ms, audio_data, - num_audio_samples); - accumulated_audio_samples += num_audio_samples; - audio_data += bytes_per_10_msec; + &elapsed_time_ms, + &ntp_time_ms); + accumulated_audio_frames += frames_per_10_ms; + } else { + // TODO(xians): Remove the following code after the APM in WebRTC is + // deprecated. + audio_transport_callback_->NeedMorePlayData(frames_per_10_ms, + bytes_per_sample, + audio_bus->channels(), + sample_rate, + audio_data, + num_audio_frames, + &elapsed_time_ms, + &ntp_time_ms); + accumulated_audio_frames += num_audio_frames; + } + if (elapsed_time_ms >= 0) { + *current_time = base::TimeDelta::FromMilliseconds(elapsed_time_ms); + } + audio_data += bytes_per_10_ms; } -} -void WebRtcAudioDeviceImpl::SetRenderFormat(const AudioParameters& params) { - DCHECK(thread_checker_.CalledOnValidThread()); - output_audio_parameters_ = params; + // De-interleave each channel and convert to 32-bit floating-point + // with nominal range -1.0 -> +1.0 to match the callback format. + audio_bus->FromInterleaved(&render_buffer_[0], + audio_bus->frames(), + bytes_per_sample); + + // Pass the render data to the playout sinks. + base::AutoLock auto_lock(lock_); + for (PlayoutDataSinkList::const_iterator it = playout_sinks_.begin(); + it != playout_sinks_.end(); ++it) { + (*it)->OnPlayoutData(audio_bus, sample_rate, audio_delay_milliseconds); + } } void WebRtcAudioDeviceImpl::RemoveAudioRenderer(WebRtcAudioRenderer* renderer) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(renderer, renderer_); base::AutoLock auto_lock(lock_); + // Notify the playout sink of the change. + for (PlayoutDataSinkList::const_iterator it = playout_sinks_.begin(); + it != playout_sinks_.end(); ++it) { + (*it)->OnPlayoutDataSourceChanged(); + } + renderer_ = NULL; playing_ = false; } @@ -199,7 +249,16 @@ int32_t WebRtcAudioDeviceImpl::Terminate() { DCHECK(!renderer_.get() || !renderer_->IsStarted()) << "The shared audio renderer shouldn't be running"; - capturers_.clear(); + // Stop all the capturers to ensure no further OnData() and + // RemoveAudioCapturer() callback. + // Cache the capturers in a local list since WebRtcAudioCapturer::Stop() + // will trigger RemoveAudioCapturer() callback. + CapturerList capturers; + capturers.swap(capturers_); + for (CapturerList::const_iterator iter = capturers.begin(); + iter != capturers.end(); ++iter) { + (*iter)->Stop(); + } initialized_ = false; return 0; @@ -245,7 +304,6 @@ int32_t WebRtcAudioDeviceImpl::StartPlayout() { } playing_ = true; - start_render_time_ = base::Time::Now(); return 0; } @@ -256,13 +314,6 @@ int32_t WebRtcAudioDeviceImpl::StopPlayout() { return 0; } - // Add histogram data to be uploaded as part of an UMA logging event. - // This histogram keeps track of total playout times. - if (!start_render_time_.is_null()) { - base::TimeDelta render_time = base::Time::Now() - start_render_time_; - UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioRenderTime", render_time); - } - playing_ = false; return 0; } @@ -287,8 +338,6 @@ int32_t WebRtcAudioDeviceImpl::StartRecording() { recording_ = true; } - start_capture_time_ = base::Time::Now(); - return 0; } @@ -302,13 +351,6 @@ int32_t WebRtcAudioDeviceImpl::StopRecording() { recording_ = false; } - // Add histogram data to be uploaded as part of an UMA logging event. - // This histogram keeps track of total recording times. - if (!start_capture_time_.is_null()) { - base::TimeDelta capture_time = base::Time::Now() - start_capture_time_; - UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioCaptureTime", capture_time); - } - return 0; } @@ -359,7 +401,7 @@ int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume) const { int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const { DCHECK(initialized_); - *available = (output_channels() == 2); + *available = renderer_ && renderer_->channels() == 2; return 0; } @@ -372,7 +414,7 @@ int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable( if (!capturer.get()) return -1; - *available = (capturer->audio_parameters().channels() == 2); + *available = (capturer->source_audio_parameters().channels() == 2); return 0; } @@ -389,20 +431,20 @@ int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const { } int32_t WebRtcAudioDeviceImpl::RecordingSampleRate( - uint32_t* samples_per_sec) const { + uint32_t* sample_rate) const { // We use the default capturer as the recording sample rate. scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer()); if (!capturer.get()) return -1; - *samples_per_sec = static_cast<uint32_t>( - capturer->audio_parameters().sample_rate()); + *sample_rate = static_cast<uint32_t>( + capturer->source_audio_parameters().sample_rate()); return 0; } int32_t WebRtcAudioDeviceImpl::PlayoutSampleRate( - uint32_t* samples_per_sec) const { - *samples_per_sec = static_cast<uint32_t>(output_sample_rate()); + uint32_t* sample_rate) const { + *sample_rate = renderer_ ? renderer_->sample_rate() : 0; return 0; } @@ -426,24 +468,62 @@ void WebRtcAudioDeviceImpl::AddAudioCapturer( DVLOG(1) << "WebRtcAudioDeviceImpl::AddAudioCapturer()"; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(capturer.get()); + DCHECK(!capturer->device_id().empty()); + { + base::AutoLock auto_lock(lock_); + DCHECK(std::find(capturers_.begin(), capturers_.end(), capturer) == + capturers_.end()); + capturers_.push_back(capturer); + } +} - // We only support one microphone today, which means the list can contain - // only one capturer with a valid device id. - DCHECK(capturer->device_id().empty() || !GetDefaultCapturer()); +void WebRtcAudioDeviceImpl::RemoveAudioCapturer( + const scoped_refptr<WebRtcAudioCapturer>& capturer) { + DVLOG(1) << "WebRtcAudioDeviceImpl::AddAudioCapturer()"; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(capturer.get()); base::AutoLock auto_lock(lock_); - capturers_.push_back(capturer); + capturers_.remove(capturer); } scoped_refptr<WebRtcAudioCapturer> WebRtcAudioDeviceImpl::GetDefaultCapturer() const { base::AutoLock auto_lock(lock_); - for (CapturerList::const_iterator iter = capturers_.begin(); - iter != capturers_.end(); ++iter) { - if (!(*iter)->device_id().empty()) - return *iter; - } + // Use the last |capturer| which is from the latest getUserMedia call as + // the default capture device. + return capturers_.empty() ? NULL : capturers_.back(); +} + +void WebRtcAudioDeviceImpl::AddPlayoutSink( + WebRtcPlayoutDataSource::Sink* sink) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sink); + base::AutoLock auto_lock(lock_); + DCHECK(std::find(playout_sinks_.begin(), playout_sinks_.end(), sink) == + playout_sinks_.end()); + playout_sinks_.push_back(sink); +} + +void WebRtcAudioDeviceImpl::RemovePlayoutSink( + WebRtcPlayoutDataSource::Sink* sink) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(sink); + base::AutoLock auto_lock(lock_); + playout_sinks_.remove(sink); +} + +bool WebRtcAudioDeviceImpl::GetAuthorizedDeviceInfoForAudioRenderer( + int* session_id, + int* output_sample_rate, + int* output_frames_per_buffer) { + DCHECK(thread_checker_.CalledOnValidThread()); + // If there is no capturer or there are more than one open capture devices, + // return false. + if (capturers_.empty() || capturers_.size() > 1) + return false; - return NULL; + return GetDefaultCapturer()->GetPairedOutputParameters( + session_id, output_sample_rate, output_frames_per_buffer); } } // namespace content |