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_audio_renderer.h" 6 7#include "base/logging.h" 8#include "base/metrics/histogram.h" 9#include "base/strings/string_util.h" 10#include "content/renderer/media/audio_device_factory.h" 11#include "content/renderer/media/webrtc_audio_device_impl.h" 12#include "content/renderer/render_thread_impl.h" 13#include "media/audio/audio_output_device.h" 14#include "media/audio/audio_parameters.h" 15#include "media/audio/sample_rates.h" 16#include "media/base/audio_hardware_config.h" 17 18#if defined(OS_WIN) 19#include "base/win/windows_version.h" 20#include "media/audio/win/core_audio_util_win.h" 21#endif 22 23namespace content { 24 25namespace { 26 27// Supported hardware sample rates for output sides. 28#if defined(OS_WIN) || defined(OS_MACOSX) 29// AudioHardwareConfig::GetOutputSampleRate() asks the audio layer for its 30// current sample rate (set by the user) on Windows and Mac OS X. The listed 31// rates below adds restrictions and Initialize() will fail if the user selects 32// any rate outside these ranges. 33const int kValidOutputRates[] = {96000, 48000, 44100, 32000, 16000}; 34#elif defined(OS_LINUX) || defined(OS_OPENBSD) 35const int kValidOutputRates[] = {48000, 44100}; 36#elif defined(OS_ANDROID) 37// TODO(leozwang): We want to use native sampling rate on Android to achieve 38// low latency, currently 16000 is used to work around audio problem on some 39// Android devices. 40const int kValidOutputRates[] = {48000, 44100, 16000}; 41const int kDefaultOutputBufferSize = 2048; 42#else 43const int kValidOutputRates[] = {44100}; 44#endif 45 46// TODO(xians): Merge the following code to WebRtcAudioCapturer, or remove. 47enum AudioFramesPerBuffer { 48 k160, 49 k320, 50 k440, 51 k480, 52 k640, 53 k880, 54 k960, 55 k1440, 56 k1920, 57 kUnexpectedAudioBufferSize // Must always be last! 58}; 59 60// Helper method to convert integral values to their respective enum values 61// above, or kUnexpectedAudioBufferSize if no match exists. 62// We map 441 to k440 to avoid changes in the XML part for histograms. 63// It is still possible to map the histogram result to the actual buffer size. 64// See http://crbug.com/243450 for details. 65AudioFramesPerBuffer AsAudioFramesPerBuffer(int frames_per_buffer) { 66 switch (frames_per_buffer) { 67 case 160: return k160; 68 case 320: return k320; 69 case 441: return k440; 70 case 480: return k480; 71 case 640: return k640; 72 case 880: return k880; 73 case 960: return k960; 74 case 1440: return k1440; 75 case 1920: return k1920; 76 } 77 return kUnexpectedAudioBufferSize; 78} 79 80void AddHistogramFramesPerBuffer(int param) { 81 AudioFramesPerBuffer afpb = AsAudioFramesPerBuffer(param); 82 if (afpb != kUnexpectedAudioBufferSize) { 83 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", 84 afpb, kUnexpectedAudioBufferSize); 85 } else { 86 // Report unexpected sample rates using a unique histogram name. 87 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected", param); 88 } 89} 90 91} // namespace 92 93WebRtcAudioRenderer::WebRtcAudioRenderer(int source_render_view_id) 94 : state_(UNINITIALIZED), 95 source_render_view_id_(source_render_view_id), 96 source_(NULL), 97 play_ref_count_(0), 98 audio_delay_milliseconds_(0), 99 fifo_delay_milliseconds_(0) { 100} 101 102WebRtcAudioRenderer::~WebRtcAudioRenderer() { 103 DCHECK(thread_checker_.CalledOnValidThread()); 104 DCHECK_EQ(state_, UNINITIALIZED); 105 buffer_.reset(); 106} 107 108bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { 109 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; 110 DCHECK(thread_checker_.CalledOnValidThread()); 111 base::AutoLock auto_lock(lock_); 112 DCHECK_EQ(state_, UNINITIALIZED); 113 DCHECK(source); 114 DCHECK(!sink_.get()); 115 DCHECK(!source_); 116 117 // Use stereo output on all platforms exept Android. 118 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; 119#if defined(OS_ANDROID) 120 DVLOG(1) << "Using mono audio output for Android"; 121 channel_layout = media::CHANNEL_LAYOUT_MONO; 122#endif 123 // Ask the renderer for the default audio output hardware sample-rate. 124 media::AudioHardwareConfig* hardware_config = 125 RenderThreadImpl::current()->GetAudioHardwareConfig(); 126 int sample_rate = hardware_config->GetOutputSampleRate(); 127 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate; 128 129 // WebRTC does not yet support higher rates than 96000 on the client side 130 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, 131 // we change the rate to 48000 instead. The consequence is that the native 132 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz 133 // which will then be resampled by the audio converted on the browser side 134 // to match the native audio layer. 135 if (sample_rate == 192000) { 136 DVLOG(1) << "Resampling from 48000 to 192000 is required"; 137 sample_rate = 48000; 138 } 139 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); 140 if (asr != media::kUnexpectedAudioSampleRate) { 141 UMA_HISTOGRAM_ENUMERATION( 142 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); 143 } else { 144 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", sample_rate); 145 } 146 147 // Verify that the reported output hardware sample rate is supported 148 // on the current platform. 149 if (std::find(&kValidOutputRates[0], 150 &kValidOutputRates[0] + arraysize(kValidOutputRates), 151 sample_rate) == 152 &kValidOutputRates[arraysize(kValidOutputRates)]) { 153 DLOG(ERROR) << sample_rate << " is not a supported output rate."; 154 return false; 155 } 156 157 // Set up audio parameters for the source, i.e., the WebRTC client. 158 159 // The WebRTC client only supports multiples of 10ms as buffer size where 160 // 10ms is preferred for lowest possible delay. 161 media::AudioParameters source_params; 162 int buffer_size = (sample_rate / 100); 163 DVLOG(1) << "Using WebRTC output buffer size: " << buffer_size; 164 165 int channels = ChannelLayoutToChannelCount(channel_layout); 166 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 167 channel_layout, channels, 0, 168 sample_rate, 16, buffer_size); 169 170 // Set up audio parameters for the sink, i.e., the native audio output stream. 171 // We strive to open up using native parameters to achieve best possible 172 // performance and to ensure that no FIFO is needed on the browser side to 173 // match the client request. Any mismatch between the source and the sink is 174 // taken care of in this class instead using a pull FIFO. 175 176 media::AudioParameters sink_params; 177 178#if defined(OS_ANDROID) 179 buffer_size = kDefaultOutputBufferSize; 180#else 181 buffer_size = hardware_config->GetOutputBufferSize(); 182#endif 183 184 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 185 channel_layout, channels, 0, sample_rate, 16, buffer_size); 186 187 // Create a FIFO if re-buffering is required to match the source input with 188 // the sink request. The source acts as provider here and the sink as 189 // consumer. 190 fifo_delay_milliseconds_ = 0; 191 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { 192 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() 193 << " to " << sink_params.frames_per_buffer(); 194 audio_fifo_.reset(new media::AudioPullFifo( 195 source_params.channels(), 196 source_params.frames_per_buffer(), 197 base::Bind( 198 &WebRtcAudioRenderer::SourceCallback, 199 base::Unretained(this)))); 200 201 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { 202 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / 203 static_cast<double>(source_params.sample_rate()); 204 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - 205 source_params.frames_per_buffer()) * frame_duration_milliseconds; 206 } 207 } 208 209 210 // Allocate local audio buffers based on the parameters above. 211 // It is assumed that each audio sample contains 16 bits and each 212 // audio frame contains one or two audio samples depending on the 213 // number of channels. 214 buffer_.reset( 215 new int16[source_params.frames_per_buffer() * source_params.channels()]); 216 217 source_ = source; 218 source->SetRenderFormat(source_params); 219 220 // Configure the audio rendering client and start rendering. 221 sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_); 222 sink_->Initialize(sink_params, this); 223 sink_->Start(); 224 225 // User must call Play() before any audio can be heard. 226 state_ = PAUSED; 227 228 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout", 229 source_params.channel_layout(), 230 media::CHANNEL_LAYOUT_MAX); 231 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", 232 source_params.frames_per_buffer(), 233 kUnexpectedAudioBufferSize); 234 AddHistogramFramesPerBuffer(source_params.frames_per_buffer()); 235 236 return true; 237} 238 239void WebRtcAudioRenderer::Start() { 240 // TODO(xians): refactor to make usage of Start/Stop more symmetric. 241 NOTIMPLEMENTED(); 242} 243 244void WebRtcAudioRenderer::Play() { 245 DVLOG(1) << "WebRtcAudioRenderer::Play()"; 246 DCHECK(thread_checker_.CalledOnValidThread()); 247 base::AutoLock auto_lock(lock_); 248 if (state_ == UNINITIALIZED) 249 return; 250 251 DCHECK(play_ref_count_ == 0 || state_ == PLAYING); 252 ++play_ref_count_; 253 state_ = PLAYING; 254 255 if (audio_fifo_) { 256 audio_delay_milliseconds_ = 0; 257 audio_fifo_->Clear(); 258 } 259} 260 261void WebRtcAudioRenderer::Pause() { 262 DVLOG(1) << "WebRtcAudioRenderer::Pause()"; 263 DCHECK(thread_checker_.CalledOnValidThread()); 264 base::AutoLock auto_lock(lock_); 265 if (state_ == UNINITIALIZED) 266 return; 267 268 DCHECK_EQ(state_, PLAYING); 269 DCHECK_GT(play_ref_count_, 0); 270 if (!--play_ref_count_) 271 state_ = PAUSED; 272} 273 274void WebRtcAudioRenderer::Stop() { 275 DVLOG(1) << "WebRtcAudioRenderer::Stop()"; 276 DCHECK(thread_checker_.CalledOnValidThread()); 277 base::AutoLock auto_lock(lock_); 278 if (state_ == UNINITIALIZED) 279 return; 280 281 source_->RemoveAudioRenderer(this); 282 source_ = NULL; 283 sink_->Stop(); 284 state_ = UNINITIALIZED; 285} 286 287void WebRtcAudioRenderer::SetVolume(float volume) { 288 DCHECK(thread_checker_.CalledOnValidThread()); 289 base::AutoLock auto_lock(lock_); 290 if (state_ == UNINITIALIZED) 291 return; 292 293 sink_->SetVolume(volume); 294} 295 296base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const { 297 return base::TimeDelta(); 298} 299 300bool WebRtcAudioRenderer::IsLocalRenderer() const { 301 return false; 302} 303 304int WebRtcAudioRenderer::Render(media::AudioBus* audio_bus, 305 int audio_delay_milliseconds) { 306 base::AutoLock auto_lock(lock_); 307 if (!source_) 308 return 0; 309 310 DVLOG(2) << "WebRtcAudioRenderer::Render()"; 311 DVLOG(2) << "audio_delay_milliseconds: " << audio_delay_milliseconds; 312 313 audio_delay_milliseconds_ = audio_delay_milliseconds; 314 315 if (audio_fifo_) 316 audio_fifo_->Consume(audio_bus, audio_bus->frames()); 317 else 318 SourceCallback(0, audio_bus); 319 320 return (state_ == PLAYING) ? audio_bus->frames() : 0; 321} 322 323void WebRtcAudioRenderer::OnRenderError() { 324 NOTIMPLEMENTED(); 325 LOG(ERROR) << "OnRenderError()"; 326} 327 328// Called by AudioPullFifo when more data is necessary. 329void WebRtcAudioRenderer::SourceCallback( 330 int fifo_frame_delay, media::AudioBus* audio_bus) { 331 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" 332 << fifo_frame_delay << ", " 333 << audio_bus->frames() << ")"; 334 335 int output_delay_milliseconds = audio_delay_milliseconds_; 336 output_delay_milliseconds += fifo_delay_milliseconds_; 337 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; 338 339 // We need to keep render data for the |source_| regardless of |state_|, 340 // otherwise the data will be buffered up inside |source_|. 341 source_->RenderData(reinterpret_cast<uint8*>(buffer_.get()), 342 audio_bus->channels(), audio_bus->frames(), 343 output_delay_milliseconds); 344 345 // Avoid filling up the audio bus if we are not playing; instead 346 // return here and ensure that the returned value in Render() is 0. 347 if (state_ != PLAYING) { 348 audio_bus->Zero(); 349 return; 350 } 351 352 // De-interleave each channel and convert to 32-bit floating-point 353 // with nominal range -1.0 -> +1.0 to match the callback format. 354 audio_bus->FromInterleaved(buffer_.get(), 355 audio_bus->frames(), 356 sizeof(buffer_[0])); 357} 358 359} // namespace content 360