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 "base/strings/stringprintf.h" 11 #include "content/renderer/media/audio_device_factory.h" 12 #include "content/renderer/media/media_stream_dispatcher.h" 13 #include "content/renderer/media/webrtc_audio_device_impl.h" 14 #include "content/renderer/media/webrtc_logging.h" 15 #include "content/renderer/render_frame_impl.h" 16 #include "media/audio/audio_output_device.h" 17 #include "media/audio/audio_parameters.h" 18 #include "media/audio/sample_rates.h" 19 #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" 20 #include "third_party/libjingle/source/talk/media/base/audiorenderer.h" 21 22 23 #if defined(OS_WIN) 24 #include "base/win/windows_version.h" 25 #include "media/audio/win/core_audio_util_win.h" 26 #endif 27 28 namespace content { 29 30 namespace { 31 32 // We add a UMA histogram measuring the execution time of the Render() method 33 // every |kNumCallbacksBetweenRenderTimeHistograms| callback. Assuming 10ms 34 // between each callback leads to one UMA update each 100ms. 35 const int kNumCallbacksBetweenRenderTimeHistograms = 10; 36 37 // This is a simple wrapper class that's handed out to users of a shared 38 // WebRtcAudioRenderer instance. This class maintains the per-user 'playing' 39 // and 'started' states to avoid problems related to incorrect usage which 40 // might violate the implementation assumptions inside WebRtcAudioRenderer 41 // (see the play reference count). 42 class SharedAudioRenderer : public MediaStreamAudioRenderer { 43 public: 44 // Callback definition for a callback that is called when when Play(), Pause() 45 // or SetVolume are called (whenever the internal |playing_state_| changes). 46 typedef base::Callback< 47 void(const scoped_refptr<webrtc::MediaStreamInterface>&, 48 WebRtcAudioRenderer::PlayingState*)> OnPlayStateChanged; 49 50 SharedAudioRenderer( 51 const scoped_refptr<MediaStreamAudioRenderer>& delegate, 52 const scoped_refptr<webrtc::MediaStreamInterface>& media_stream, 53 const OnPlayStateChanged& on_play_state_changed) 54 : delegate_(delegate), media_stream_(media_stream), started_(false), 55 on_play_state_changed_(on_play_state_changed) { 56 DCHECK(!on_play_state_changed_.is_null()); 57 DCHECK(media_stream_.get()); 58 } 59 60 protected: 61 virtual ~SharedAudioRenderer() { 62 DCHECK(thread_checker_.CalledOnValidThread()); 63 DVLOG(1) << __FUNCTION__; 64 Stop(); 65 } 66 67 virtual void Start() OVERRIDE { 68 DCHECK(thread_checker_.CalledOnValidThread()); 69 if (started_) 70 return; 71 started_ = true; 72 delegate_->Start(); 73 } 74 75 virtual void Play() OVERRIDE { 76 DCHECK(thread_checker_.CalledOnValidThread()); 77 DCHECK(started_); 78 if (playing_state_.playing()) 79 return; 80 playing_state_.set_playing(true); 81 on_play_state_changed_.Run(media_stream_, &playing_state_); 82 } 83 84 virtual void Pause() OVERRIDE { 85 DCHECK(thread_checker_.CalledOnValidThread()); 86 DCHECK(started_); 87 if (!playing_state_.playing()) 88 return; 89 playing_state_.set_playing(false); 90 on_play_state_changed_.Run(media_stream_, &playing_state_); 91 } 92 93 virtual void Stop() OVERRIDE { 94 DCHECK(thread_checker_.CalledOnValidThread()); 95 if (!started_) 96 return; 97 Pause(); 98 started_ = false; 99 delegate_->Stop(); 100 } 101 102 virtual void SetVolume(float volume) OVERRIDE { 103 DCHECK(thread_checker_.CalledOnValidThread()); 104 DCHECK(volume >= 0.0f && volume <= 1.0f); 105 playing_state_.set_volume(volume); 106 on_play_state_changed_.Run(media_stream_, &playing_state_); 107 } 108 109 virtual base::TimeDelta GetCurrentRenderTime() const OVERRIDE { 110 DCHECK(thread_checker_.CalledOnValidThread()); 111 return delegate_->GetCurrentRenderTime(); 112 } 113 114 virtual bool IsLocalRenderer() const OVERRIDE { 115 DCHECK(thread_checker_.CalledOnValidThread()); 116 return delegate_->IsLocalRenderer(); 117 } 118 119 private: 120 base::ThreadChecker thread_checker_; 121 const scoped_refptr<MediaStreamAudioRenderer> delegate_; 122 const scoped_refptr<webrtc::MediaStreamInterface> media_stream_; 123 bool started_; 124 WebRtcAudioRenderer::PlayingState playing_state_; 125 OnPlayStateChanged on_play_state_changed_; 126 }; 127 128 // Returns either AudioParameters::NO_EFFECTS or AudioParameters::DUCKING 129 // depending on whether or not an input element is currently open with 130 // ducking enabled. 131 int GetCurrentDuckingFlag(int render_frame_id) { 132 RenderFrameImpl* const frame = 133 RenderFrameImpl::FromRoutingID(render_frame_id); 134 MediaStreamDispatcher* const dispatcher = frame ? 135 frame->GetMediaStreamDispatcher() : NULL; 136 if (dispatcher && dispatcher->IsAudioDuckingActive()) { 137 return media::AudioParameters::DUCKING; 138 } 139 140 return media::AudioParameters::NO_EFFECTS; 141 } 142 143 // Helper method to get platform specific optimal buffer size. 144 int GetOptimalBufferSize(int sample_rate, int hardware_buffer_size) { 145 // Use native hardware buffer size as default. On Windows, we strive to open 146 // up using this native hardware buffer size to achieve best 147 // possible performance and to ensure that no FIFO is needed on the browser 148 // side to match the client request. That is why there is no #if case for 149 // Windows below. 150 int frames_per_buffer = hardware_buffer_size; 151 152 #if defined(OS_LINUX) || defined(OS_MACOSX) 153 // On Linux and MacOS, the low level IO implementations on the browser side 154 // supports all buffer size the clients want. We use the native peer 155 // connection buffer size (10ms) to achieve best possible performance. 156 frames_per_buffer = sample_rate / 100; 157 #elif defined(OS_ANDROID) 158 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency 159 // cases. Might not be possible to come up with the perfect solution using 160 // the render side only. 161 int frames_per_10ms = sample_rate / 100; 162 if (frames_per_buffer < 2 * frames_per_10ms) { 163 // Examples of low-latency frame sizes and the resulting |buffer_size|: 164 // Nexus 7 : 240 audio frames => 2*480 = 960 165 // Nexus 10 : 256 => 2*441 = 882 166 // Galaxy Nexus: 144 => 2*441 = 882 167 frames_per_buffer = 2 * frames_per_10ms; 168 DVLOG(1) << "Low-latency output detected on Android"; 169 } 170 #endif 171 172 DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer; 173 return frames_per_buffer; 174 } 175 176 } // namespace 177 178 WebRtcAudioRenderer::WebRtcAudioRenderer( 179 const scoped_refptr<webrtc::MediaStreamInterface>& media_stream, 180 int source_render_view_id, 181 int source_render_frame_id, 182 int session_id, 183 int sample_rate, 184 int frames_per_buffer) 185 : state_(UNINITIALIZED), 186 source_render_view_id_(source_render_view_id), 187 source_render_frame_id_(source_render_frame_id), 188 session_id_(session_id), 189 media_stream_(media_stream), 190 source_(NULL), 191 play_ref_count_(0), 192 start_ref_count_(0), 193 audio_delay_milliseconds_(0), 194 fifo_delay_milliseconds_(0), 195 sink_params_(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 196 media::CHANNEL_LAYOUT_STEREO, sample_rate, 16, 197 frames_per_buffer, 198 GetCurrentDuckingFlag(source_render_frame_id)), 199 render_callback_count_(0) { 200 WebRtcLogMessage(base::StringPrintf( 201 "WAR::WAR. source_render_view_id=%d" 202 ", session_id=%d, sample_rate=%d, frames_per_buffer=%d, effects=%i", 203 source_render_view_id, 204 session_id, 205 sample_rate, 206 frames_per_buffer, 207 sink_params_.effects())); 208 } 209 210 WebRtcAudioRenderer::~WebRtcAudioRenderer() { 211 DCHECK(thread_checker_.CalledOnValidThread()); 212 DCHECK_EQ(state_, UNINITIALIZED); 213 } 214 215 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { 216 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; 217 DCHECK(thread_checker_.CalledOnValidThread()); 218 base::AutoLock auto_lock(lock_); 219 DCHECK_EQ(state_, UNINITIALIZED); 220 DCHECK(source); 221 DCHECK(!sink_.get()); 222 DCHECK(!source_); 223 224 // WebRTC does not yet support higher rates than 96000 on the client side 225 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, 226 // we change the rate to 48000 instead. The consequence is that the native 227 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz 228 // which will then be resampled by the audio converted on the browser side 229 // to match the native audio layer. 230 int sample_rate = sink_params_.sample_rate(); 231 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate; 232 if (sample_rate == 192000) { 233 DVLOG(1) << "Resampling from 48000 to 192000 is required"; 234 sample_rate = 48000; 235 } 236 media::AudioSampleRate asr; 237 if (media::ToAudioSampleRate(sample_rate, &asr)) { 238 UMA_HISTOGRAM_ENUMERATION( 239 "WebRTC.AudioOutputSampleRate", asr, media::kAudioSampleRateMax + 1); 240 } else { 241 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", 242 sample_rate); 243 } 244 245 // Set up audio parameters for the source, i.e., the WebRTC client. 246 247 // The WebRTC client only supports multiples of 10ms as buffer size where 248 // 10ms is preferred for lowest possible delay. 249 media::AudioParameters source_params; 250 const int frames_per_10ms = (sample_rate / 100); 251 DVLOG(1) << "Using WebRTC output buffer size: " << frames_per_10ms; 252 253 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, 254 sink_params_.channel_layout(), sink_params_.channels(), 255 sample_rate, 16, frames_per_10ms); 256 257 const int frames_per_buffer = 258 GetOptimalBufferSize(sample_rate, sink_params_.frames_per_buffer()); 259 260 sink_params_.Reset(sink_params_.format(), sink_params_.channel_layout(), 261 sink_params_.channels(), sample_rate, 16, 262 frames_per_buffer); 263 264 // Create a FIFO if re-buffering is required to match the source input with 265 // the sink request. The source acts as provider here and the sink as 266 // consumer. 267 fifo_delay_milliseconds_ = 0; 268 if (source_params.frames_per_buffer() != sink_params_.frames_per_buffer()) { 269 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() 270 << " to " << sink_params_.frames_per_buffer(); 271 audio_fifo_.reset(new media::AudioPullFifo( 272 source_params.channels(), 273 source_params.frames_per_buffer(), 274 base::Bind( 275 &WebRtcAudioRenderer::SourceCallback, 276 base::Unretained(this)))); 277 278 if (sink_params_.frames_per_buffer() > source_params.frames_per_buffer()) { 279 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / 280 static_cast<double>(source_params.sample_rate()); 281 fifo_delay_milliseconds_ = (sink_params_.frames_per_buffer() - 282 source_params.frames_per_buffer()) * frame_duration_milliseconds; 283 } 284 } 285 286 source_ = source; 287 288 // Configure the audio rendering client and start rendering. 289 sink_ = AudioDeviceFactory::NewOutputDevice( 290 source_render_view_id_, source_render_frame_id_); 291 292 DCHECK_GE(session_id_, 0); 293 sink_->InitializeWithSessionId(sink_params_, this, session_id_); 294 295 sink_->Start(); 296 297 // User must call Play() before any audio can be heard. 298 state_ = PAUSED; 299 300 return true; 301 } 302 303 scoped_refptr<MediaStreamAudioRenderer> 304 WebRtcAudioRenderer::CreateSharedAudioRendererProxy( 305 const scoped_refptr<webrtc::MediaStreamInterface>& media_stream) { 306 content::SharedAudioRenderer::OnPlayStateChanged on_play_state_changed = 307 base::Bind(&WebRtcAudioRenderer::OnPlayStateChanged, this); 308 return new SharedAudioRenderer(this, media_stream, on_play_state_changed); 309 } 310 311 bool WebRtcAudioRenderer::IsStarted() const { 312 DCHECK(thread_checker_.CalledOnValidThread()); 313 return start_ref_count_ != 0; 314 } 315 316 void WebRtcAudioRenderer::Start() { 317 DVLOG(1) << "WebRtcAudioRenderer::Start()"; 318 DCHECK(thread_checker_.CalledOnValidThread()); 319 ++start_ref_count_; 320 } 321 322 void WebRtcAudioRenderer::Play() { 323 DVLOG(1) << "WebRtcAudioRenderer::Play()"; 324 DCHECK(thread_checker_.CalledOnValidThread()); 325 326 if (playing_state_.playing()) 327 return; 328 329 playing_state_.set_playing(true); 330 render_callback_count_ = 0; 331 332 OnPlayStateChanged(media_stream_, &playing_state_); 333 } 334 335 void WebRtcAudioRenderer::EnterPlayState() { 336 DVLOG(1) << "WebRtcAudioRenderer::EnterPlayState()"; 337 DCHECK(thread_checker_.CalledOnValidThread()); 338 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; 339 base::AutoLock auto_lock(lock_); 340 if (state_ == UNINITIALIZED) 341 return; 342 343 DCHECK(play_ref_count_ == 0 || state_ == PLAYING); 344 ++play_ref_count_; 345 346 if (state_ != PLAYING) { 347 state_ = PLAYING; 348 349 if (audio_fifo_) { 350 audio_delay_milliseconds_ = 0; 351 audio_fifo_->Clear(); 352 } 353 } 354 } 355 356 void WebRtcAudioRenderer::Pause() { 357 DVLOG(1) << "WebRtcAudioRenderer::Pause()"; 358 DCHECK(thread_checker_.CalledOnValidThread()); 359 if (!playing_state_.playing()) 360 return; 361 362 playing_state_.set_playing(false); 363 364 OnPlayStateChanged(media_stream_, &playing_state_); 365 } 366 367 void WebRtcAudioRenderer::EnterPauseState() { 368 DVLOG(1) << "WebRtcAudioRenderer::EnterPauseState()"; 369 DCHECK(thread_checker_.CalledOnValidThread()); 370 DCHECK_GT(start_ref_count_, 0) << "Did you forget to call Start()?"; 371 base::AutoLock auto_lock(lock_); 372 if (state_ == UNINITIALIZED) 373 return; 374 375 DCHECK_EQ(state_, PLAYING); 376 DCHECK_GT(play_ref_count_, 0); 377 if (!--play_ref_count_) 378 state_ = PAUSED; 379 } 380 381 void WebRtcAudioRenderer::Stop() { 382 DVLOG(1) << "WebRtcAudioRenderer::Stop()"; 383 DCHECK(thread_checker_.CalledOnValidThread()); 384 { 385 base::AutoLock auto_lock(lock_); 386 if (state_ == UNINITIALIZED) 387 return; 388 389 if (--start_ref_count_) 390 return; 391 392 DVLOG(1) << "Calling RemoveAudioRenderer and Stop()."; 393 394 source_->RemoveAudioRenderer(this); 395 source_ = NULL; 396 state_ = UNINITIALIZED; 397 } 398 399 // Make sure to stop the sink while _not_ holding the lock since the Render() 400 // callback may currently be executing and try to grab the lock while we're 401 // stopping the thread on which it runs. 402 sink_->Stop(); 403 } 404 405 void WebRtcAudioRenderer::SetVolume(float volume) { 406 DCHECK(thread_checker_.CalledOnValidThread()); 407 DCHECK(volume >= 0.0f && volume <= 1.0f); 408 409 playing_state_.set_volume(volume); 410 OnPlayStateChanged(media_stream_, &playing_state_); 411 } 412 413 base::TimeDelta WebRtcAudioRenderer::GetCurrentRenderTime() const { 414 DCHECK(thread_checker_.CalledOnValidThread()); 415 base::AutoLock auto_lock(lock_); 416 return current_time_; 417 } 418 419 bool WebRtcAudioRenderer::IsLocalRenderer() const { 420 return false; 421 } 422 423 int WebRtcAudioRenderer::Render(media::AudioBus* audio_bus, 424 int audio_delay_milliseconds) { 425 base::AutoLock auto_lock(lock_); 426 if (!source_) 427 return 0; 428 429 DVLOG(2) << "WebRtcAudioRenderer::Render()"; 430 DVLOG(2) << "audio_delay_milliseconds: " << audio_delay_milliseconds; 431 432 audio_delay_milliseconds_ = audio_delay_milliseconds; 433 434 if (audio_fifo_) 435 audio_fifo_->Consume(audio_bus, audio_bus->frames()); 436 else 437 SourceCallback(0, audio_bus); 438 439 return (state_ == PLAYING) ? audio_bus->frames() : 0; 440 } 441 442 void WebRtcAudioRenderer::OnRenderError() { 443 NOTIMPLEMENTED(); 444 LOG(ERROR) << "OnRenderError()"; 445 } 446 447 // Called by AudioPullFifo when more data is necessary. 448 void WebRtcAudioRenderer::SourceCallback( 449 int fifo_frame_delay, media::AudioBus* audio_bus) { 450 base::TimeTicks start_time = base::TimeTicks::Now() ; 451 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" 452 << fifo_frame_delay << ", " 453 << audio_bus->frames() << ")"; 454 455 int output_delay_milliseconds = audio_delay_milliseconds_; 456 output_delay_milliseconds += fifo_delay_milliseconds_; 457 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; 458 459 // We need to keep render data for the |source_| regardless of |state_|, 460 // otherwise the data will be buffered up inside |source_|. 461 source_->RenderData(audio_bus, sink_params_.sample_rate(), 462 output_delay_milliseconds, 463 ¤t_time_); 464 465 // Avoid filling up the audio bus if we are not playing; instead 466 // return here and ensure that the returned value in Render() is 0. 467 if (state_ != PLAYING) 468 audio_bus->Zero(); 469 470 if (++render_callback_count_ == kNumCallbacksBetweenRenderTimeHistograms) { 471 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; 472 render_callback_count_ = 0; 473 UMA_HISTOGRAM_TIMES("WebRTC.AudioRenderTimes", elapsed); 474 } 475 } 476 477 void WebRtcAudioRenderer::UpdateSourceVolume( 478 webrtc::AudioSourceInterface* source) { 479 DCHECK(thread_checker_.CalledOnValidThread()); 480 481 // Note: If there are no playing audio renderers, then the volume will be 482 // set to 0.0. 483 float volume = 0.0f; 484 485 SourcePlayingStates::iterator entry = source_playing_states_.find(source); 486 if (entry != source_playing_states_.end()) { 487 PlayingStates& states = entry->second; 488 for (PlayingStates::const_iterator it = states.begin(); 489 it != states.end(); ++it) { 490 if ((*it)->playing()) 491 volume += (*it)->volume(); 492 } 493 } 494 495 // The valid range for volume scaling of a remote webrtc source is 496 // 0.0-10.0 where 1.0 is no attenuation/boost. 497 DCHECK(volume >= 0.0f); 498 if (volume > 10.0f) 499 volume = 10.0f; 500 501 DVLOG(1) << "Setting remote source volume: " << volume; 502 source->SetVolume(volume); 503 } 504 505 bool WebRtcAudioRenderer::AddPlayingState( 506 webrtc::AudioSourceInterface* source, 507 PlayingState* state) { 508 DCHECK(thread_checker_.CalledOnValidThread()); 509 DCHECK(state->playing()); 510 // Look up or add the |source| to the map. 511 PlayingStates& array = source_playing_states_[source]; 512 if (std::find(array.begin(), array.end(), state) != array.end()) 513 return false; 514 515 array.push_back(state); 516 517 return true; 518 } 519 520 bool WebRtcAudioRenderer::RemovePlayingState( 521 webrtc::AudioSourceInterface* source, 522 PlayingState* state) { 523 DCHECK(thread_checker_.CalledOnValidThread()); 524 DCHECK(!state->playing()); 525 SourcePlayingStates::iterator found = source_playing_states_.find(source); 526 if (found == source_playing_states_.end()) 527 return false; 528 529 PlayingStates& array = found->second; 530 PlayingStates::iterator state_it = 531 std::find(array.begin(), array.end(), state); 532 if (state_it == array.end()) 533 return false; 534 535 array.erase(state_it); 536 537 if (array.empty()) 538 source_playing_states_.erase(found); 539 540 return true; 541 } 542 543 void WebRtcAudioRenderer::OnPlayStateChanged( 544 const scoped_refptr<webrtc::MediaStreamInterface>& media_stream, 545 PlayingState* state) { 546 webrtc::AudioTrackVector tracks(media_stream->GetAudioTracks()); 547 for (webrtc::AudioTrackVector::iterator it = tracks.begin(); 548 it != tracks.end(); ++it) { 549 webrtc::AudioSourceInterface* source = (*it)->GetSource(); 550 DCHECK(source); 551 if (!state->playing()) { 552 if (RemovePlayingState(source, state)) 553 EnterPauseState(); 554 } else if (AddPlayingState(source, state)) { 555 EnterPlayState(); 556 } 557 UpdateSourceVolume(source); 558 } 559 } 560 561 } // namespace content 562