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 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 sample_rate_(sample_rate), |
| 204 number_of_channels_(0), |
204 frames_per_buffer_(frames_per_buffer) { | 205 frames_per_buffer_(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. | 229 // Use stereo output on all platforms. |
230 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; | 230 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; |
231 | 231 |
232 // TODO(tommi,henrika): Maybe we should just change |sample_rate_| to be | 232 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate_; |
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 | 233 |
237 // WebRTC does not yet support higher rates than 96000 on the client side | 234 // 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, | 235 // 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 | 236 // 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 | 237 // 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 | 238 // which will then be resampled by the audio converted on the browser side |
242 // to match the native audio layer. | 239 // to match the native audio layer. |
243 if (sample_rate == 192000) { | 240 if (sample_rate_ == 192000) { |
244 DVLOG(1) << "Resampling from 48000 to 192000 is required"; | 241 DVLOG(1) << "Resampling from 48000 to 192000 is required"; |
245 sample_rate = 48000; | 242 sample_rate_ = 48000; |
246 } | 243 } |
247 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); | 244 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate_); |
248 if (asr != media::kUnexpectedAudioSampleRate) { | 245 if (asr != media::kUnexpectedAudioSampleRate) { |
249 UMA_HISTOGRAM_ENUMERATION( | 246 UMA_HISTOGRAM_ENUMERATION( |
250 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); | 247 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); |
251 } else { | 248 } else { |
252 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", sample_rate); | 249 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", |
| 250 sample_rate_); |
253 } | 251 } |
254 | 252 |
255 // Verify that the reported output hardware sample rate is supported | 253 // Verify that the reported output hardware sample rate is supported |
256 // on the current platform. | 254 // on the current platform. |
257 if (std::find(&kValidOutputRates[0], | 255 if (std::find(&kValidOutputRates[0], |
258 &kValidOutputRates[0] + arraysize(kValidOutputRates), | 256 &kValidOutputRates[0] + arraysize(kValidOutputRates), |
259 sample_rate) == | 257 sample_rate_) == |
260 &kValidOutputRates[arraysize(kValidOutputRates)]) { | 258 &kValidOutputRates[arraysize(kValidOutputRates)]) { |
261 DLOG(ERROR) << sample_rate << " is not a supported output rate."; | 259 DLOG(ERROR) << sample_rate_ << " is not a supported output rate."; |
262 return false; | 260 return false; |
263 } | 261 } |
264 | 262 |
265 // Set up audio parameters for the source, i.e., the WebRTC client. | 263 // Set up audio parameters for the source, i.e., the WebRTC client. |
266 | 264 |
267 // The WebRTC client only supports multiples of 10ms as buffer size where | 265 // The WebRTC client only supports multiples of 10ms as buffer size where |
268 // 10ms is preferred for lowest possible delay. | 266 // 10ms is preferred for lowest possible delay. |
269 media::AudioParameters source_params; | 267 media::AudioParameters source_params; |
270 int buffer_size = (sample_rate / 100); | 268 const int frames_per_10ms = (sample_rate_ / 100); |
271 DVLOG(1) << "Using WebRTC output buffer size: " << buffer_size; | 269 DVLOG(1) << "Using WebRTC output buffer size: " << frames_per_10ms; |
272 | 270 |
273 int channels = ChannelLayoutToChannelCount(channel_layout); | 271 number_of_channels_ = ChannelLayoutToChannelCount(channel_layout); |
274 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 272 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
275 channel_layout, channels, 0, | 273 channel_layout, number_of_channels_, 0, |
276 sample_rate, 16, buffer_size); | 274 sample_rate_, 16, frames_per_10ms); |
277 | 275 |
278 // Set up audio parameters for the sink, i.e., the native audio output stream. | 276 // Set up 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 | 277 // 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 | 278 // 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 | 279 // 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. | 280 // taken care of in this class instead using a pull FIFO. |
283 | 281 |
284 media::AudioParameters sink_params; | 282 media::AudioParameters sink_params; |
285 | 283 |
286 // Use native output siz as default. | 284 // Use native output size as default. |
287 buffer_size = frames_per_buffer_; | |
288 #if defined(OS_ANDROID) | 285 #if defined(OS_ANDROID) |
289 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency | 286 // 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 | 287 // cases. Might not be possible to come up with the perfect solution using |
291 // the render side only. | 288 // the render side only. |
292 const int frames_per_10ms = (sample_rate / 100); | 289 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|: | 290 // Examples of low-latency frame sizes and the resulting |buffer_size|: |
295 // Nexus 7 : 240 audio frames => 2*480 = 960 | 291 // Nexus 7 : 240 audio frames => 2*480 = 960 |
296 // Nexus 10 : 256 => 2*441 = 882 | 292 // Nexus 10 : 256 => 2*441 = 882 |
297 // Galaxy Nexus: 144 => 2*441 = 882 | 293 // Galaxy Nexus: 144 => 2*441 = 882 |
298 buffer_size = 2 * frames_per_10ms; | 294 frames_per_buffer_ = 2 * frames_per_10ms; |
299 DVLOG(1) << "Low-latency output detected on Android"; | 295 DVLOG(1) << "Low-latency output detected on Android"; |
300 } | 296 } |
301 #endif | 297 #endif |
302 DVLOG(1) << "Using sink output buffer size: " << buffer_size; | 298 DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer_; |
303 | 299 |
304 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 300 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
305 channel_layout, channels, 0, sample_rate, 16, buffer_size); | 301 channel_layout, number_of_channels_, 0, sample_rate_, 16, |
| 302 frames_per_buffer_); |
306 | 303 |
307 // Create a FIFO if re-buffering is required to match the source input with | 304 // 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 | 305 // the sink request. The source acts as provider here and the sink as |
309 // consumer. | 306 // consumer. |
310 fifo_delay_milliseconds_ = 0; | 307 fifo_delay_milliseconds_ = 0; |
311 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { | 308 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { |
312 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() | 309 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() |
313 << " to " << sink_params.frames_per_buffer(); | 310 << " to " << sink_params.frames_per_buffer(); |
314 audio_fifo_.reset(new media::AudioPullFifo( | 311 audio_fifo_.reset(new media::AudioPullFifo( |
315 source_params.channels(), | 312 source_params.channels(), |
316 source_params.frames_per_buffer(), | 313 source_params.frames_per_buffer(), |
317 base::Bind( | 314 base::Bind( |
318 &WebRtcAudioRenderer::SourceCallback, | 315 &WebRtcAudioRenderer::SourceCallback, |
319 base::Unretained(this)))); | 316 base::Unretained(this)))); |
320 | 317 |
321 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { | 318 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { |
322 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / | 319 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / |
323 static_cast<double>(source_params.sample_rate()); | 320 static_cast<double>(source_params.sample_rate()); |
324 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - | 321 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - |
325 source_params.frames_per_buffer()) * frame_duration_milliseconds; | 322 source_params.frames_per_buffer()) * frame_duration_milliseconds; |
326 } | 323 } |
327 } | 324 } |
328 | 325 |
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; | 326 source_ = source; |
337 source->SetRenderFormat(source_params); | |
338 | 327 |
339 // Configure the audio rendering client and start rendering. | 328 // Configure the audio rendering client and start rendering. |
340 sink_ = AudioDeviceFactory::NewOutputDevice( | 329 sink_ = AudioDeviceFactory::NewOutputDevice( |
341 source_render_view_id_, source_render_frame_id_); | 330 source_render_view_id_, source_render_frame_id_); |
342 | 331 |
343 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association | 332 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association |
344 // with a session. | 333 // with a session. |
345 DCHECK_GE(session_id_, 0); | 334 DCHECK_GE(session_id_, 0); |
346 sink_->InitializeUnifiedStream(sink_params, this, session_id_); | 335 sink_->InitializeUnifiedStream(sink_params, this, session_id_); |
347 | 336 |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" | 497 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" |
509 << fifo_frame_delay << ", " | 498 << fifo_frame_delay << ", " |
510 << audio_bus->frames() << ")"; | 499 << audio_bus->frames() << ")"; |
511 | 500 |
512 int output_delay_milliseconds = audio_delay_milliseconds_; | 501 int output_delay_milliseconds = audio_delay_milliseconds_; |
513 output_delay_milliseconds += fifo_delay_milliseconds_; | 502 output_delay_milliseconds += fifo_delay_milliseconds_; |
514 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; | 503 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; |
515 | 504 |
516 // We need to keep render data for the |source_| regardless of |state_|, | 505 // We need to keep render data for the |source_| regardless of |state_|, |
517 // otherwise the data will be buffered up inside |source_|. | 506 // otherwise the data will be buffered up inside |source_|. |
518 source_->RenderData(reinterpret_cast<uint8*>(buffer_.get()), | 507 source_->RenderData(audio_bus, sample_rate_, output_delay_milliseconds); |
519 audio_bus->channels(), audio_bus->frames(), | |
520 output_delay_milliseconds); | |
521 | 508 |
522 // Avoid filling up the audio bus if we are not playing; instead | 509 // 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. | 510 // return here and ensure that the returned value in Render() is 0. |
524 if (state_ != PLAYING) { | 511 if (state_ != PLAYING) |
525 audio_bus->Zero(); | 512 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 } | 513 } |
535 | 514 |
536 void WebRtcAudioRenderer::UpdateSourceVolume( | 515 void WebRtcAudioRenderer::UpdateSourceVolume( |
537 webrtc::AudioSourceInterface* source) { | 516 webrtc::AudioSourceInterface* source) { |
538 DCHECK(thread_checker_.CalledOnValidThread()); | 517 DCHECK(thread_checker_.CalledOnValidThread()); |
539 | 518 |
540 // Note: If there are no playing audio renderers, then the volume will be | 519 // Note: If there are no playing audio renderers, then the volume will be |
541 // set to 0.0. | 520 // set to 0.0. |
542 float volume = 0.0f; | 521 float volume = 0.0f; |
543 | 522 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
611 if (RemovePlayingState(source, state)) | 590 if (RemovePlayingState(source, state)) |
612 EnterPauseState(); | 591 EnterPauseState(); |
613 } else if (AddPlayingState(source, state)) { | 592 } else if (AddPlayingState(source, state)) { |
614 EnterPlayState(); | 593 EnterPlayState(); |
615 } | 594 } |
616 UpdateSourceVolume(source); | 595 UpdateSourceVolume(source); |
617 } | 596 } |
618 } | 597 } |
619 | 598 |
620 } // namespace content | 599 } // namespace content |
OLD | NEW |