| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/media/webrtc_audio_renderer.h" | 5 #include "content/renderer/media/webrtc_audio_renderer.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 : state_(UNINITIALIZED), | 193 : state_(UNINITIALIZED), |
| 194 source_render_view_id_(source_render_view_id), | 194 source_render_view_id_(source_render_view_id), |
| 195 source_render_frame_id_(source_render_frame_id), | 195 source_render_frame_id_(source_render_frame_id), |
| 196 session_id_(session_id), | 196 session_id_(session_id), |
| 197 media_stream_(media_stream), | 197 media_stream_(media_stream), |
| 198 source_(NULL), | 198 source_(NULL), |
| 199 play_ref_count_(0), | 199 play_ref_count_(0), |
| 200 start_ref_count_(0), | 200 start_ref_count_(0), |
| 201 audio_delay_milliseconds_(0), | 201 audio_delay_milliseconds_(0), |
| 202 fifo_delay_milliseconds_(0), | 202 fifo_delay_milliseconds_(0), |
| 203 sample_rate_(sample_rate), | 203 sink_params_(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 204 frames_per_buffer_(frames_per_buffer) { | 204 media::CHANNEL_LAYOUT_STEREO, sample_rate, 16, |
| 205 frames_per_buffer) { |
| 205 WebRtcLogMessage(base::StringPrintf( | 206 WebRtcLogMessage(base::StringPrintf( |
| 206 "WAR::WAR. source_render_view_id=%d" | 207 "WAR::WAR. source_render_view_id=%d" |
| 207 ", session_id=%d, sample_rate=%d, frames_per_buffer=%d", | 208 ", session_id=%d, sample_rate=%d, frames_per_buffer=%d", |
| 208 source_render_view_id, | 209 source_render_view_id, |
| 209 session_id, | 210 session_id, |
| 210 sample_rate, | 211 sample_rate, |
| 211 frames_per_buffer)); | 212 frames_per_buffer)); |
| 212 } | 213 } |
| 213 | 214 |
| 214 WebRtcAudioRenderer::~WebRtcAudioRenderer() { | 215 WebRtcAudioRenderer::~WebRtcAudioRenderer() { |
| 215 DCHECK(thread_checker_.CalledOnValidThread()); | 216 DCHECK(thread_checker_.CalledOnValidThread()); |
| 216 DCHECK_EQ(state_, UNINITIALIZED); | 217 DCHECK_EQ(state_, UNINITIALIZED); |
| 217 buffer_.reset(); | |
| 218 } | 218 } |
| 219 | 219 |
| 220 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { | 220 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { |
| 221 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; | 221 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; |
| 222 DCHECK(thread_checker_.CalledOnValidThread()); | 222 DCHECK(thread_checker_.CalledOnValidThread()); |
| 223 base::AutoLock auto_lock(lock_); | 223 base::AutoLock auto_lock(lock_); |
| 224 DCHECK_EQ(state_, UNINITIALIZED); | 224 DCHECK_EQ(state_, UNINITIALIZED); |
| 225 DCHECK(source); | 225 DCHECK(source); |
| 226 DCHECK(!sink_.get()); | 226 DCHECK(!sink_.get()); |
| 227 DCHECK(!source_); | 227 DCHECK(!source_); |
| 228 | 228 |
| 229 // Use stereo output on all platforms. | |
| 230 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; | |
| 231 | |
| 232 // TODO(tommi,henrika): Maybe we should just change |sample_rate_| to be | |
| 233 // immutable and change its value instead of using a temporary? | |
| 234 int sample_rate = sample_rate_; | |
| 235 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate; | |
| 236 | |
| 237 // WebRTC does not yet support higher rates than 96000 on the client side | 229 // WebRTC does not yet support higher rates than 96000 on the client side |
| 238 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, | 230 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, |
| 239 // we change the rate to 48000 instead. The consequence is that the native | 231 // we change the rate to 48000 instead. The consequence is that the native |
| 240 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz | 232 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz |
| 241 // which will then be resampled by the audio converted on the browser side | 233 // which will then be resampled by the audio converted on the browser side |
| 242 // to match the native audio layer. | 234 // to match the native audio layer. |
| 235 int sample_rate = sink_params_.sample_rate(); |
| 236 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate; |
| 243 if (sample_rate == 192000) { | 237 if (sample_rate == 192000) { |
| 244 DVLOG(1) << "Resampling from 48000 to 192000 is required"; | 238 DVLOG(1) << "Resampling from 48000 to 192000 is required"; |
| 245 sample_rate = 48000; | 239 sample_rate = 48000; |
| 246 } | 240 } |
| 247 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); | 241 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); |
| 248 if (asr != media::kUnexpectedAudioSampleRate) { | 242 if (asr != media::kUnexpectedAudioSampleRate) { |
| 249 UMA_HISTOGRAM_ENUMERATION( | 243 UMA_HISTOGRAM_ENUMERATION( |
| 250 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); | 244 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); |
| 251 } else { | 245 } else { |
| 252 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", sample_rate); | 246 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", |
| 247 sample_rate); |
| 253 } | 248 } |
| 254 | 249 |
| 255 // Verify that the reported output hardware sample rate is supported | 250 // Verify that the reported output hardware sample rate is supported |
| 256 // on the current platform. | 251 // on the current platform. |
| 257 if (std::find(&kValidOutputRates[0], | 252 if (std::find(&kValidOutputRates[0], |
| 258 &kValidOutputRates[0] + arraysize(kValidOutputRates), | 253 &kValidOutputRates[0] + arraysize(kValidOutputRates), |
| 259 sample_rate) == | 254 sample_rate) == |
| 260 &kValidOutputRates[arraysize(kValidOutputRates)]) { | 255 &kValidOutputRates[arraysize(kValidOutputRates)]) { |
| 261 DLOG(ERROR) << sample_rate << " is not a supported output rate."; | 256 DLOG(ERROR) << sample_rate << " is not a supported output rate."; |
| 262 return false; | 257 return false; |
| 263 } | 258 } |
| 264 | 259 |
| 265 // Set up audio parameters for the source, i.e., the WebRTC client. | 260 // Set up audio parameters for the source, i.e., the WebRTC client. |
| 266 | 261 |
| 267 // The WebRTC client only supports multiples of 10ms as buffer size where | 262 // The WebRTC client only supports multiples of 10ms as buffer size where |
| 268 // 10ms is preferred for lowest possible delay. | 263 // 10ms is preferred for lowest possible delay. |
| 269 media::AudioParameters source_params; | 264 media::AudioParameters source_params; |
| 270 int buffer_size = (sample_rate / 100); | 265 const int frames_per_10ms = (sample_rate / 100); |
| 271 DVLOG(1) << "Using WebRTC output buffer size: " << buffer_size; | 266 DVLOG(1) << "Using WebRTC output buffer size: " << frames_per_10ms; |
| 272 | 267 |
| 273 int channels = ChannelLayoutToChannelCount(channel_layout); | |
| 274 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 268 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| 275 channel_layout, channels, 0, | 269 sink_params_.channel_layout(), sink_params_.channels(), 0, |
| 276 sample_rate, 16, buffer_size); | 270 sample_rate, 16, frames_per_10ms); |
| 277 | 271 |
| 278 // Set up audio parameters for the sink, i.e., the native audio output stream. | 272 // Update audio parameters for the sink, i.e., the native audio output stream. |
| 279 // We strive to open up using native parameters to achieve best possible | 273 // We strive to open up using native parameters to achieve best possible |
| 280 // performance and to ensure that no FIFO is needed on the browser side to | 274 // performance and to ensure that no FIFO is needed on the browser side to |
| 281 // match the client request. Any mismatch between the source and the sink is | 275 // match the client request. Any mismatch between the source and the sink is |
| 282 // taken care of in this class instead using a pull FIFO. | 276 // taken care of in this class instead using a pull FIFO. |
| 283 | 277 |
| 284 media::AudioParameters sink_params; | 278 // Use native output size as default. |
| 285 | 279 int frames_per_buffer = sink_params_.frames_per_buffer(); |
| 286 // Use native output siz as default. | |
| 287 buffer_size = frames_per_buffer_; | |
| 288 #if defined(OS_ANDROID) | 280 #if defined(OS_ANDROID) |
| 289 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency | 281 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency |
| 290 // cases. Might not be possible to come up with the perfect solution using | 282 // cases. Might not be possible to come up with the perfect solution using |
| 291 // the render side only. | 283 // the render side only. |
| 292 const int frames_per_10ms = (sample_rate / 100); | 284 if (frames_per_buffer < 2 * frames_per_10ms) { |
| 293 if (buffer_size < 2 * frames_per_10ms) { | |
| 294 // Examples of low-latency frame sizes and the resulting |buffer_size|: | 285 // Examples of low-latency frame sizes and the resulting |buffer_size|: |
| 295 // Nexus 7 : 240 audio frames => 2*480 = 960 | 286 // Nexus 7 : 240 audio frames => 2*480 = 960 |
| 296 // Nexus 10 : 256 => 2*441 = 882 | 287 // Nexus 10 : 256 => 2*441 = 882 |
| 297 // Galaxy Nexus: 144 => 2*441 = 882 | 288 // Galaxy Nexus: 144 => 2*441 = 882 |
| 298 buffer_size = 2 * frames_per_10ms; | 289 frames_per_buffer = 2 * frames_per_10ms; |
| 299 DVLOG(1) << "Low-latency output detected on Android"; | 290 DVLOG(1) << "Low-latency output detected on Android"; |
| 300 } | 291 } |
| 301 #endif | 292 #endif |
| 302 DVLOG(1) << "Using sink output buffer size: " << buffer_size; | 293 DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer; |
| 303 | 294 |
| 304 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 295 sink_params_.Reset(sink_params_.format(), sink_params_.channel_layout(), |
| 305 channel_layout, channels, 0, sample_rate, 16, buffer_size); | 296 sink_params_.channels(), 0, sample_rate, 16, |
| 297 frames_per_buffer); |
| 306 | 298 |
| 307 // Create a FIFO if re-buffering is required to match the source input with | 299 // Create a FIFO if re-buffering is required to match the source input with |
| 308 // the sink request. The source acts as provider here and the sink as | 300 // the sink request. The source acts as provider here and the sink as |
| 309 // consumer. | 301 // consumer. |
| 310 fifo_delay_milliseconds_ = 0; | 302 fifo_delay_milliseconds_ = 0; |
| 311 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { | 303 if (source_params.frames_per_buffer() != sink_params_.frames_per_buffer()) { |
| 312 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() | 304 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() |
| 313 << " to " << sink_params.frames_per_buffer(); | 305 << " to " << sink_params_.frames_per_buffer(); |
| 314 audio_fifo_.reset(new media::AudioPullFifo( | 306 audio_fifo_.reset(new media::AudioPullFifo( |
| 315 source_params.channels(), | 307 source_params.channels(), |
| 316 source_params.frames_per_buffer(), | 308 source_params.frames_per_buffer(), |
| 317 base::Bind( | 309 base::Bind( |
| 318 &WebRtcAudioRenderer::SourceCallback, | 310 &WebRtcAudioRenderer::SourceCallback, |
| 319 base::Unretained(this)))); | 311 base::Unretained(this)))); |
| 320 | 312 |
| 321 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { | 313 if (sink_params_.frames_per_buffer() > source_params.frames_per_buffer()) { |
| 322 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / | 314 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / |
| 323 static_cast<double>(source_params.sample_rate()); | 315 static_cast<double>(source_params.sample_rate()); |
| 324 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - | 316 fifo_delay_milliseconds_ = (sink_params_.frames_per_buffer() - |
| 325 source_params.frames_per_buffer()) * frame_duration_milliseconds; | 317 source_params.frames_per_buffer()) * frame_duration_milliseconds; |
| 326 } | 318 } |
| 327 } | 319 } |
| 328 | 320 |
| 329 // Allocate local audio buffers based on the parameters above. | |
| 330 // It is assumed that each audio sample contains 16 bits and each | |
| 331 // audio frame contains one or two audio samples depending on the | |
| 332 // number of channels. | |
| 333 buffer_.reset( | |
| 334 new int16[source_params.frames_per_buffer() * source_params.channels()]); | |
| 335 | |
| 336 source_ = source; | 321 source_ = source; |
| 337 source->SetRenderFormat(source_params); | |
| 338 | 322 |
| 339 // Configure the audio rendering client and start rendering. | 323 // Configure the audio rendering client and start rendering. |
| 340 sink_ = AudioDeviceFactory::NewOutputDevice( | 324 sink_ = AudioDeviceFactory::NewOutputDevice( |
| 341 source_render_view_id_, source_render_frame_id_); | 325 source_render_view_id_, source_render_frame_id_); |
| 342 | 326 |
| 343 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association | 327 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association |
| 344 // with a session. | 328 // with a session. |
| 345 DCHECK_GE(session_id_, 0); | 329 DCHECK_GE(session_id_, 0); |
| 346 sink_->InitializeUnifiedStream(sink_params, this, session_id_); | 330 sink_->InitializeUnifiedStream(sink_params_, this, session_id_); |
| 347 | 331 |
| 348 sink_->Start(); | 332 sink_->Start(); |
| 349 | 333 |
| 350 // User must call Play() before any audio can be heard. | 334 // User must call Play() before any audio can be heard. |
| 351 state_ = PAUSED; | 335 state_ = PAUSED; |
| 352 | 336 |
| 353 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout", | 337 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout", |
| 354 source_params.channel_layout(), | 338 source_params.channel_layout(), |
| 355 media::CHANNEL_LAYOUT_MAX); | 339 media::CHANNEL_LAYOUT_MAX); |
| 356 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", | 340 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" | 492 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" |
| 509 << fifo_frame_delay << ", " | 493 << fifo_frame_delay << ", " |
| 510 << audio_bus->frames() << ")"; | 494 << audio_bus->frames() << ")"; |
| 511 | 495 |
| 512 int output_delay_milliseconds = audio_delay_milliseconds_; | 496 int output_delay_milliseconds = audio_delay_milliseconds_; |
| 513 output_delay_milliseconds += fifo_delay_milliseconds_; | 497 output_delay_milliseconds += fifo_delay_milliseconds_; |
| 514 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; | 498 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; |
| 515 | 499 |
| 516 // We need to keep render data for the |source_| regardless of |state_|, | 500 // We need to keep render data for the |source_| regardless of |state_|, |
| 517 // otherwise the data will be buffered up inside |source_|. | 501 // otherwise the data will be buffered up inside |source_|. |
| 518 source_->RenderData(reinterpret_cast<uint8*>(buffer_.get()), | 502 source_->RenderData(audio_bus, sink_params_.sample_rate(), |
| 519 audio_bus->channels(), audio_bus->frames(), | |
| 520 output_delay_milliseconds); | 503 output_delay_milliseconds); |
| 521 | 504 |
| 522 // Avoid filling up the audio bus if we are not playing; instead | 505 // Avoid filling up the audio bus if we are not playing; instead |
| 523 // return here and ensure that the returned value in Render() is 0. | 506 // return here and ensure that the returned value in Render() is 0. |
| 524 if (state_ != PLAYING) { | 507 if (state_ != PLAYING) |
| 525 audio_bus->Zero(); | 508 audio_bus->Zero(); |
| 526 return; | |
| 527 } | |
| 528 | |
| 529 // De-interleave each channel and convert to 32-bit floating-point | |
| 530 // with nominal range -1.0 -> +1.0 to match the callback format. | |
| 531 audio_bus->FromInterleaved(buffer_.get(), | |
| 532 audio_bus->frames(), | |
| 533 sizeof(buffer_[0])); | |
| 534 } | 509 } |
| 535 | 510 |
| 536 void WebRtcAudioRenderer::UpdateSourceVolume( | 511 void WebRtcAudioRenderer::UpdateSourceVolume( |
| 537 webrtc::AudioSourceInterface* source) { | 512 webrtc::AudioSourceInterface* source) { |
| 538 DCHECK(thread_checker_.CalledOnValidThread()); | 513 DCHECK(thread_checker_.CalledOnValidThread()); |
| 539 | 514 |
| 540 // Note: If there are no playing audio renderers, then the volume will be | 515 // Note: If there are no playing audio renderers, then the volume will be |
| 541 // set to 0.0. | 516 // set to 0.0. |
| 542 float volume = 0.0f; | 517 float volume = 0.0f; |
| 543 | 518 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 if (RemovePlayingState(source, state)) | 586 if (RemovePlayingState(source, state)) |
| 612 EnterPauseState(); | 587 EnterPauseState(); |
| 613 } else if (AddPlayingState(source, state)) { | 588 } else if (AddPlayingState(source, state)) { |
| 614 EnterPlayState(); | 589 EnterPlayState(); |
| 615 } | 590 } |
| 616 UpdateSourceVolume(source); | 591 UpdateSourceVolume(source); |
| 617 } | 592 } |
| 618 } | 593 } |
| 619 | 594 |
| 620 } // namespace content | 595 } // namespace content |
| OLD | NEW |