OLD | NEW |
(Empty) | |
| 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/web/WebAudioSourceProviderClient.h" |
| 13 |
| 14 using WebKit::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 : audio_delay_ms_(0), |
| 27 volume_(1), |
| 28 key_pressed_(false), |
| 29 is_enabled_(false) { |
| 30 } |
| 31 |
| 32 WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() { |
| 33 if (audio_converter_.get()) |
| 34 audio_converter_->RemoveInput(this); |
| 35 } |
| 36 |
| 37 void WebRtcLocalAudioSourceProvider::Initialize( |
| 38 const media::AudioParameters& source_params) { |
| 39 DCHECK(thread_checker_.CalledOnValidThread()); |
| 40 |
| 41 // Use the native audio output hardware sample-rate for the sink. |
| 42 if (RenderThreadImpl::current()) { |
| 43 media::AudioHardwareConfig* hardware_config = |
| 44 RenderThreadImpl::current()->GetAudioHardwareConfig(); |
| 45 int sample_rate = hardware_config->GetOutputSampleRate(); |
| 46 sink_params_.Reset( |
| 47 source_params.format(), media::CHANNEL_LAYOUT_STEREO, 2, 0, |
| 48 sample_rate, source_params.bits_per_sample(), |
| 49 kWebAudioRenderBufferSize); |
| 50 } else { |
| 51 // This happens on unittests which does not have a valid RenderThreadImpl, |
| 52 // the unittests should have injected their own |sink_params_| for testing. |
| 53 DCHECK(sink_params_.IsValid()); |
| 54 } |
| 55 |
| 56 base::AutoLock auto_lock(lock_); |
| 57 source_params_ = source_params; |
| 58 // Create the audio converter with |disable_fifo| as false so that the |
| 59 // converter will request source_params.frames_per_buffer() each time. |
| 60 // This will not increase the complexity as there is only one client to |
| 61 // the converter. |
| 62 audio_converter_.reset( |
| 63 new media::AudioConverter(source_params, sink_params_, false)); |
| 64 audio_converter_->AddInput(this); |
| 65 fifo_.reset(new media::AudioFifo( |
| 66 source_params.channels(), |
| 67 kMaxNumberOfBuffers * source_params.frames_per_buffer())); |
| 68 } |
| 69 |
| 70 void WebRtcLocalAudioSourceProvider::DeliverData( |
| 71 media::AudioBus* audio_source, |
| 72 int audio_delay_milliseconds, |
| 73 int volume, |
| 74 bool key_pressed) { |
| 75 base::AutoLock auto_lock(lock_); |
| 76 if (!is_enabled_) |
| 77 return; |
| 78 |
| 79 DCHECK(fifo_.get()); |
| 80 |
| 81 if (fifo_->frames() + audio_source->frames() <= fifo_->max_frames()) { |
| 82 fifo_->Push(audio_source); |
| 83 } else { |
| 84 // This can happen if the data in FIFO is too slowed to be consumed or |
| 85 // WebAudio stops consuming data. |
| 86 DLOG(WARNING) << "Local source provicer FIFO is full" << fifo_->frames(); |
| 87 } |
| 88 |
| 89 // Cache the values for GetAudioProcessingParams(). |
| 90 last_fill_ = base::TimeTicks::Now(); |
| 91 audio_delay_ms_ = audio_delay_milliseconds; |
| 92 volume_ = volume; |
| 93 key_pressed_ = key_pressed; |
| 94 } |
| 95 |
| 96 void WebRtcLocalAudioSourceProvider::GetAudioProcessingParams( |
| 97 int* delay_ms, int* volume, bool* key_pressed) { |
| 98 int elapsed_ms = 0; |
| 99 if (!last_fill_.is_null()) { |
| 100 elapsed_ms = static_cast<int>( |
| 101 (base::TimeTicks::Now() - last_fill_).InMilliseconds()); |
| 102 } |
| 103 *delay_ms = audio_delay_ms_ + elapsed_ms + static_cast<int>( |
| 104 1000 * fifo_->frames() / source_params_.sample_rate() + 0.5); |
| 105 *volume = volume_; |
| 106 *key_pressed = key_pressed_; |
| 107 } |
| 108 |
| 109 void WebRtcLocalAudioSourceProvider::setClient( |
| 110 WebKit::WebAudioSourceProviderClient* client) { |
| 111 NOTREACHED(); |
| 112 } |
| 113 |
| 114 void WebRtcLocalAudioSourceProvider::provideInput( |
| 115 const WebVector<float*>& audio_data, size_t number_of_frames) { |
| 116 DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize); |
| 117 if (!bus_wrapper_ || |
| 118 static_cast<size_t>(bus_wrapper_->channels()) != audio_data.size()) { |
| 119 bus_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size()); |
| 120 } |
| 121 |
| 122 bus_wrapper_->set_frames(number_of_frames); |
| 123 for (size_t i = 0; i < audio_data.size(); ++i) |
| 124 bus_wrapper_->SetChannelData(i, audio_data[i]); |
| 125 |
| 126 base::AutoLock auto_lock(lock_); |
| 127 DCHECK(audio_converter_.get()); |
| 128 DCHECK(fifo_.get()); |
| 129 is_enabled_ = true; |
| 130 audio_converter_->Convert(bus_wrapper_.get()); |
| 131 } |
| 132 |
| 133 double WebRtcLocalAudioSourceProvider::ProvideInput( |
| 134 media::AudioBus* audio_bus, base::TimeDelta buffer_delay) { |
| 135 if (fifo_->frames() >= audio_bus->frames()) { |
| 136 fifo_->Consume(audio_bus, 0, audio_bus->frames()); |
| 137 } else { |
| 138 audio_bus->Zero(); |
| 139 if (!last_fill_.is_null()) { |
| 140 DLOG(WARNING) << "Underrun, FIFO has data " << fifo_->frames() |
| 141 << " samples but " << audio_bus->frames() |
| 142 << " samples are needed"; |
| 143 } |
| 144 } |
| 145 |
| 146 return 1.0; |
| 147 } |
| 148 |
| 149 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting( |
| 150 const media::AudioParameters& sink_params) { |
| 151 DCHECK(thread_checker_.CalledOnValidThread()); |
| 152 sink_params_ = sink_params; |
| 153 } |
| 154 |
| 155 } // namespace content |
OLD | NEW |