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" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/synchronization/lock.h" 11 #include "content/renderer/media/audio_device_factory.h" 12 #include "content/renderer/media/webrtc_audio_capturer.h" 13 #include "content/renderer/render_thread_impl.h" 14 #include "media/audio/audio_output_device.h" 15 #include "media/base/audio_bus.h" 16 #include "media/base/audio_fifo.h" 17 #include "media/base/audio_hardware_config.h" 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 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 54 int WebRtcLocalAudioRenderer::CaptureData(const std::vector<int>& channels, 55 const int16* audio_data, 56 int sample_rate, 57 int number_of_channels, 58 int number_of_frames, 59 int audio_delay_milliseconds, 60 int current_volume, 61 bool need_audio_processing) { 62 TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData"); 63 base::AutoLock auto_lock(thread_lock_); 64 65 if (!playing_) 66 return 0; 67 68 // Push captured audio to FIFO so it can be read by a local sink. 69 if (loopback_fifo_) { 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()); 78 79 base::Time now = base::Time::Now(); 80 total_render_time_ += now - last_render_time_; 81 last_render_time_ = now; 82 } else { 83 DVLOG(1) << "FIFO is full"; 84 } 85 } 86 87 return 0; 88 } 89 90 void WebRtcLocalAudioRenderer::SetCaptureFormat( 91 const media::AudioParameters& params) { 92 audio_params_ = params; 93 } 94 95 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation. 96 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer( 97 WebRtcLocalAudioTrack* audio_track, 98 int source_render_view_id) 99 : audio_track_(audio_track), 100 source_render_view_id_(source_render_view_id), 101 playing_(false) { 102 DCHECK(audio_track); 103 DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()"; 104 } 105 106 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() { 107 DCHECK(thread_checker_.CalledOnValidThread()); 108 DCHECK(!sink_.get()); 109 DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()"; 110 } 111 112 void WebRtcLocalAudioRenderer::Start() { 113 DVLOG(1) << "WebRtcLocalAudioRenderer::Start()"; 114 DCHECK(thread_checker_.CalledOnValidThread()); 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); 119 120 base::AutoLock auto_lock(thread_lock_); 121 DCHECK(!sink_.get()); 122 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( 133 audio_params_.channels(), 10 * audio_params_.frames_per_buffer())); 134 135 #if defined(OS_ANDROID) 136 media::AudioHardwareConfig* hardware_config = 137 RenderThreadImpl::current()->GetAudioHardwareConfig(); 138 #endif 139 140 media::AudioParameters sink_params(audio_params_.format(), 141 audio_params_.channel_layout(), 142 audio_params_.sample_rate(), 143 audio_params_.bits_per_sample(), 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 ); 154 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_); 155 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); 160 161 // Start the capturer and local rendering. Note that, the capturer is owned 162 // by the WebRTC ADM and might already bee running. 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 173 if (!sink_.get()) 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. 193 audio_track_->RemoveSink(this); 194 audio_track_ = NULL; 195 } 196 197 void WebRtcLocalAudioRenderer::Play() { 198 DVLOG(1) << "WebRtcLocalAudioRenderer::Play()"; 199 DCHECK(thread_checker_.CalledOnValidThread()); 200 base::AutoLock auto_lock(thread_lock_); 201 202 if (!sink_.get()) 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 219 if (!sink_.get()) 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_); 232 if (sink_.get()) 233 sink_->SetVolume(volume); 234 } 235 236 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const { 237 DCHECK(thread_checker_.CalledOnValidThread()); 238 base::AutoLock auto_lock(thread_lock_); 239 if (!sink_.get()) 240 return base::TimeDelta(); 241 return total_render_time(); 242 } 243 244 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const { 245 return true; 246 } 247 248 } // namespace content 249