Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 1 | // Copyright (c) 2012 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_renderer.h" |
| 6 | |
| 7 | #include "base/debug/trace_event.h" |
| 8 | #include "base/logging.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 9 | #include "base/message_loop/message_loop_proxy.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 10 | #include "base/synchronization/lock.h" |
| 11 | #include "content/renderer/media/audio_device_factory.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 12 | #include "content/renderer/media/webrtc_audio_capturer.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 13 | #include "content/renderer/render_thread_impl.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 14 | #include "media/audio/audio_output_device.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 15 | #include "media/base/audio_bus.h" |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 16 | #include "media/base/audio_fifo.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 17 | #include "media/base/audio_hardware_config.h" |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 18 | |
| 19 | namespace content { |
| 20 | |
| 21 | // media::AudioRendererSink::RenderCallback implementation |
| 22 | int WebRtcLocalAudioRenderer::Render( |
| 23 | media::AudioBus* audio_bus, int audio_delay_milliseconds) { |
| 24 | base::AutoLock auto_lock(thread_lock_); |
| 25 | |
| 26 | if (!playing_) { |
| 27 | audio_bus->Zero(); |
| 28 | return 0; |
| 29 | } |
| 30 | |
| 31 | TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render"); |
| 32 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 33 | DCHECK(loopback_fifo_.get() != NULL); |
| 34 | |
| 35 | // Provide data by reading from the FIFO if the FIFO contains enough |
| 36 | // to fulfill the request. |
| 37 | if (loopback_fifo_->frames() >= audio_bus->frames()) { |
| 38 | loopback_fifo_->Consume(audio_bus, 0, audio_bus->frames()); |
| 39 | } else { |
| 40 | audio_bus->Zero(); |
| 41 | // This warning is perfectly safe if it happens for the first audio |
| 42 | // frames. It should not happen in a steady-state mode. |
| 43 | DVLOG(2) << "loopback FIFO is empty"; |
| 44 | } |
| 45 | |
| 46 | return audio_bus->frames(); |
| 47 | } |
| 48 | |
| 49 | void WebRtcLocalAudioRenderer::OnRenderError() { |
| 50 | NOTIMPLEMENTED(); |
| 51 | } |
| 52 | |
| 53 | // content::WebRtcAudioCapturerSink implementation |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 54 | int WebRtcLocalAudioRenderer::CaptureData(const std::vector<int>& channels, |
| 55 | const int16* audio_data, |
| 56 | int sample_rate, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 57 | int number_of_channels, |
| 58 | int number_of_frames, |
| 59 | int audio_delay_milliseconds, |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 60 | int current_volume, |
| 61 | bool need_audio_processing) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 62 | TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData"); |
| 63 | base::AutoLock auto_lock(thread_lock_); |
| 64 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 65 | if (!playing_) |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 66 | return 0; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 67 | |
| 68 | // Push captured audio to FIFO so it can be read by a local sink. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 69 | if (loopback_fifo_) { |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 70 | if (loopback_fifo_->frames() + number_of_frames <= |
| 71 | loopback_fifo_->max_frames()) { |
| 72 | scoped_ptr<media::AudioBus> audio_source = media::AudioBus::Create( |
| 73 | number_of_channels, number_of_frames); |
| 74 | audio_source->FromInterleaved(audio_data, |
| 75 | audio_source->frames(), |
| 76 | sizeof(audio_data[0])); |
| 77 | loopback_fifo_->Push(audio_source.get()); |
Torne (Richard Coles) | 90dce4d | 2013-05-29 14:40:03 +0100 | [diff] [blame] | 78 | |
| 79 | base::Time now = base::Time::Now(); |
| 80 | total_render_time_ += now - last_render_time_; |
| 81 | last_render_time_ = now; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 82 | } else { |
| 83 | DVLOG(1) << "FIFO is full"; |
| 84 | } |
| 85 | } |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 86 | |
| 87 | return 0; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | void WebRtcLocalAudioRenderer::SetCaptureFormat( |
| 91 | const media::AudioParameters& params) { |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 92 | audio_params_ = params; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation. |
| 96 | WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer( |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 97 | WebRtcLocalAudioTrack* audio_track, |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 98 | int source_render_view_id) |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 99 | : audio_track_(audio_track), |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 100 | source_render_view_id_(source_render_view_id), |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 101 | playing_(false) { |
| 102 | DCHECK(audio_track); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 103 | DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()"; |
| 104 | } |
| 105 | |
| 106 | WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() { |
| 107 | DCHECK(thread_checker_.CalledOnValidThread()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 108 | DCHECK(!sink_.get()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 109 | DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()"; |
| 110 | } |
| 111 | |
| 112 | void WebRtcLocalAudioRenderer::Start() { |
| 113 | DVLOG(1) << "WebRtcLocalAudioRenderer::Start()"; |
| 114 | DCHECK(thread_checker_.CalledOnValidThread()); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 115 | // Add this class as sink to the audio track to ensure that we receive |
| 116 | // WebRtcAudioCapturerSink::CaptureData() callbacks for captured audio. |
| 117 | // |audio_params_| will be updated right after the AddCapturerAudioTrack(). |
| 118 | audio_track_->AddSink(this); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 119 | |
| 120 | base::AutoLock auto_lock(thread_lock_); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 121 | DCHECK(!sink_.get()); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 122 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 123 | // TODO(henrika): we could add a more dynamic solution here but I prefer |
| 124 | // a fixed size combined with bad audio at overflow. The alternative is |
| 125 | // that we start to build up latency and that can be more difficult to |
| 126 | // detect. Tests have shown that the FIFO never contains more than 2 or 3 |
| 127 | // audio frames but I have selected a max size of ten buffers just |
| 128 | // in case since these tests were performed on a 16 core, 64GB Win 7 |
| 129 | // machine. We could also add some sort of error notifier in this area if |
| 130 | // the FIFO overflows. |
| 131 | DCHECK(!loopback_fifo_); |
| 132 | loopback_fifo_.reset(new media::AudioFifo( |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 133 | audio_params_.channels(), 10 * audio_params_.frames_per_buffer())); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 134 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 135 | #if defined(OS_ANDROID) |
| 136 | media::AudioHardwareConfig* hardware_config = |
| 137 | RenderThreadImpl::current()->GetAudioHardwareConfig(); |
| 138 | #endif |
| 139 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 140 | media::AudioParameters sink_params(audio_params_.format(), |
| 141 | audio_params_.channel_layout(), |
| 142 | audio_params_.sample_rate(), |
| 143 | audio_params_.bits_per_sample(), |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 144 | #if defined(OS_ANDROID) |
| 145 | // On Android, input and output are using same sampling rate. In order to |
| 146 | // achieve low latency mode, we need use buffer size suggested by |
| 147 | // AudioManager for the sink paramters which will be used to decide |
| 148 | // buffer size for shared memory buffer. |
| 149 | hardware_config->GetOutputBufferSize() |
| 150 | #else |
| 151 | 2 * audio_params_.frames_per_buffer() |
| 152 | #endif |
| 153 | ); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 154 | sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_); |
| 155 | |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 156 | // TODO(henrika): we could utilize the unified audio here instead and do |
| 157 | // sink_->InitializeIO(sink_params, 2, callback_.get()); |
| 158 | // It would then be possible to avoid using the WebRtcAudioCapturer. |
| 159 | sink_->Initialize(sink_params, this); |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 160 | |
| 161 | // Start the capturer and local rendering. Note that, the capturer is owned |
| 162 | // by the WebRTC ADM and might already bee running. |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 163 | sink_->Start(); |
| 164 | |
| 165 | last_render_time_ = base::Time::Now(); |
| 166 | playing_ = false; |
| 167 | } |
| 168 | |
| 169 | void WebRtcLocalAudioRenderer::Stop() { |
| 170 | DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()"; |
| 171 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 172 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 173 | if (!sink_.get()) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 174 | return; |
| 175 | |
| 176 | { |
| 177 | base::AutoLock auto_lock(thread_lock_); |
| 178 | playing_ = false; |
| 179 | |
| 180 | if (loopback_fifo_.get() != NULL) { |
| 181 | loopback_fifo_->Clear(); |
| 182 | loopback_fifo_.reset(); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // Stop the output audio stream, i.e, stop asking for data to render. |
| 187 | sink_->Stop(); |
| 188 | sink_ = NULL; |
| 189 | |
| 190 | // Ensure that the capturer stops feeding us with captured audio. |
| 191 | // Note that, we do not stop the capturer here since it may still be used by |
| 192 | // the WebRTC ADM. |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 193 | audio_track_->RemoveSink(this); |
| 194 | audio_track_ = NULL; |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | void WebRtcLocalAudioRenderer::Play() { |
| 198 | DVLOG(1) << "WebRtcLocalAudioRenderer::Play()"; |
| 199 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 200 | base::AutoLock auto_lock(thread_lock_); |
| 201 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 202 | if (!sink_.get()) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 203 | return; |
| 204 | |
| 205 | // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render() |
| 206 | // now reads data from the local FIFO. |
| 207 | playing_ = true; |
| 208 | last_render_time_ = base::Time::Now(); |
| 209 | |
| 210 | if (loopback_fifo_) |
| 211 | loopback_fifo_->Clear(); |
| 212 | } |
| 213 | |
| 214 | void WebRtcLocalAudioRenderer::Pause() { |
| 215 | DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()"; |
| 216 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 217 | base::AutoLock auto_lock(thread_lock_); |
| 218 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 219 | if (!sink_.get()) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 220 | return; |
| 221 | |
| 222 | // Temporarily suspends rendering audio. |
| 223 | // WebRtcLocalAudioRenderer::Render() will return early during this state |
| 224 | // and only zeros will be provided to the active sink. |
| 225 | playing_ = false; |
| 226 | } |
| 227 | |
| 228 | void WebRtcLocalAudioRenderer::SetVolume(float volume) { |
| 229 | DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")"; |
| 230 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 231 | base::AutoLock auto_lock(thread_lock_); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 232 | if (sink_.get()) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 233 | sink_->SetVolume(volume); |
| 234 | } |
| 235 | |
| 236 | base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const { |
| 237 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 238 | base::AutoLock auto_lock(thread_lock_); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 239 | if (!sink_.get()) |
Torne (Richard Coles) | 2a99a7e | 2013-03-28 15:31:22 +0000 | [diff] [blame] | 240 | return base::TimeDelta(); |
| 241 | return total_render_time(); |
| 242 | } |
| 243 | |
| 244 | bool WebRtcLocalAudioRenderer::IsLocalRenderer() const { |
| 245 | return true; |
| 246 | } |
| 247 | |
| 248 | } // namespace content |