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 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 : state_(UNINITIALIZED), | 172 : state_(UNINITIALIZED), |
173 source_render_view_id_(source_render_view_id), | 173 source_render_view_id_(source_render_view_id), |
174 source_render_frame_id_(source_render_frame_id), | 174 source_render_frame_id_(source_render_frame_id), |
175 session_id_(session_id), | 175 session_id_(session_id), |
176 source_(NULL), | 176 source_(NULL), |
177 play_ref_count_(0), | 177 play_ref_count_(0), |
178 start_ref_count_(0), | 178 start_ref_count_(0), |
179 audio_delay_milliseconds_(0), | 179 audio_delay_milliseconds_(0), |
180 fifo_delay_milliseconds_(0), | 180 fifo_delay_milliseconds_(0), |
181 sample_rate_(sample_rate), | 181 sample_rate_(sample_rate), |
| 182 number_of_channels_(0), |
182 frames_per_buffer_(frames_per_buffer) { | 183 frames_per_buffer_(frames_per_buffer) { |
183 WebRtcLogMessage(base::StringPrintf( | 184 WebRtcLogMessage(base::StringPrintf( |
184 "WAR::WAR. source_render_view_id=%d" | 185 "WAR::WAR. source_render_view_id=%d" |
185 ", session_id=%d, sample_rate=%d, frames_per_buffer=%d", | 186 ", session_id=%d, sample_rate=%d, frames_per_buffer=%d", |
186 source_render_view_id, | 187 source_render_view_id, |
187 session_id, | 188 session_id, |
188 sample_rate, | 189 sample_rate, |
189 frames_per_buffer)); | 190 frames_per_buffer)); |
190 } | 191 } |
191 | 192 |
192 WebRtcAudioRenderer::~WebRtcAudioRenderer() { | 193 WebRtcAudioRenderer::~WebRtcAudioRenderer() { |
193 DCHECK(thread_checker_.CalledOnValidThread()); | 194 DCHECK(thread_checker_.CalledOnValidThread()); |
194 DCHECK_EQ(state_, UNINITIALIZED); | 195 DCHECK_EQ(state_, UNINITIALIZED); |
195 buffer_.reset(); | |
196 } | 196 } |
197 | 197 |
198 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { | 198 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { |
199 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; | 199 DVLOG(1) << "WebRtcAudioRenderer::Initialize()"; |
200 DCHECK(thread_checker_.CalledOnValidThread()); | 200 DCHECK(thread_checker_.CalledOnValidThread()); |
201 base::AutoLock auto_lock(lock_); | 201 base::AutoLock auto_lock(lock_); |
202 DCHECK_EQ(state_, UNINITIALIZED); | 202 DCHECK_EQ(state_, UNINITIALIZED); |
203 DCHECK(source); | 203 DCHECK(source); |
204 DCHECK(!sink_.get()); | 204 DCHECK(!sink_.get()); |
205 DCHECK(!source_); | 205 DCHECK(!source_); |
206 | 206 |
207 // Use stereo output on all platforms. | 207 // Use stereo output on all platforms. |
208 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; | 208 media::ChannelLayout channel_layout = media::CHANNEL_LAYOUT_STEREO; |
209 | 209 |
210 // TODO(tommi,henrika): Maybe we should just change |sample_rate_| to be | 210 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate_; |
211 // immutable and change its value instead of using a temporary? | |
212 int sample_rate = sample_rate_; | |
213 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate; | |
214 | 211 |
215 // WebRTC does not yet support higher rates than 96000 on the client side | 212 // WebRTC does not yet support higher rates than 96000 on the client side |
216 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, | 213 // and 48000 is the preferred sample rate. Therefore, if 192000 is detected, |
217 // we change the rate to 48000 instead. The consequence is that the native | 214 // we change the rate to 48000 instead. The consequence is that the native |
218 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz | 215 // layer will be opened up at 192kHz but WebRTC will provide data at 48kHz |
219 // which will then be resampled by the audio converted on the browser side | 216 // which will then be resampled by the audio converted on the browser side |
220 // to match the native audio layer. | 217 // to match the native audio layer. |
221 if (sample_rate == 192000) { | 218 if (sample_rate_ == 192000) { |
222 DVLOG(1) << "Resampling from 48000 to 192000 is required"; | 219 DVLOG(1) << "Resampling from 48000 to 192000 is required"; |
223 sample_rate = 48000; | 220 sample_rate_ = 48000; |
224 } | 221 } |
225 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); | 222 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate_); |
226 if (asr != media::kUnexpectedAudioSampleRate) { | 223 if (asr != media::kUnexpectedAudioSampleRate) { |
227 UMA_HISTOGRAM_ENUMERATION( | 224 UMA_HISTOGRAM_ENUMERATION( |
228 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); | 225 "WebRTC.AudioOutputSampleRate", asr, media::kUnexpectedAudioSampleRate); |
229 } else { | 226 } else { |
230 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", sample_rate); | 227 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputSampleRateUnexpected", |
| 228 sample_rate_); |
231 } | 229 } |
232 | 230 |
233 // Verify that the reported output hardware sample rate is supported | 231 // Verify that the reported output hardware sample rate is supported |
234 // on the current platform. | 232 // on the current platform. |
235 if (std::find(&kValidOutputRates[0], | 233 if (std::find(&kValidOutputRates[0], |
236 &kValidOutputRates[0] + arraysize(kValidOutputRates), | 234 &kValidOutputRates[0] + arraysize(kValidOutputRates), |
237 sample_rate) == | 235 sample_rate_) == |
238 &kValidOutputRates[arraysize(kValidOutputRates)]) { | 236 &kValidOutputRates[arraysize(kValidOutputRates)]) { |
239 DLOG(ERROR) << sample_rate << " is not a supported output rate."; | 237 DLOG(ERROR) << sample_rate_ << " is not a supported output rate."; |
240 return false; | 238 return false; |
241 } | 239 } |
242 | 240 |
243 // Set up audio parameters for the source, i.e., the WebRTC client. | 241 // Set up audio parameters for the source, i.e., the WebRTC client. |
244 | 242 |
245 // The WebRTC client only supports multiples of 10ms as buffer size where | 243 // The WebRTC client only supports multiples of 10ms as buffer size where |
246 // 10ms is preferred for lowest possible delay. | 244 // 10ms is preferred for lowest possible delay. |
247 media::AudioParameters source_params; | 245 media::AudioParameters source_params; |
248 int buffer_size = (sample_rate / 100); | 246 const int frames_per_10ms = (sample_rate_ / 100); |
249 DVLOG(1) << "Using WebRTC output buffer size: " << buffer_size; | 247 DVLOG(1) << "Using WebRTC output buffer size: " << frames_per_10ms; |
250 | 248 |
251 int channels = ChannelLayoutToChannelCount(channel_layout); | 249 number_of_channels_ = ChannelLayoutToChannelCount(channel_layout); |
252 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 250 source_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
253 channel_layout, channels, 0, | 251 channel_layout, number_of_channels_, 0, |
254 sample_rate, 16, buffer_size); | 252 sample_rate_, 16, frames_per_10ms); |
255 | 253 |
256 // Set up audio parameters for the sink, i.e., the native audio output stream. | 254 // Set up audio parameters for the sink, i.e., the native audio output stream. |
257 // We strive to open up using native parameters to achieve best possible | 255 // We strive to open up using native parameters to achieve best possible |
258 // performance and to ensure that no FIFO is needed on the browser side to | 256 // performance and to ensure that no FIFO is needed on the browser side to |
259 // match the client request. Any mismatch between the source and the sink is | 257 // match the client request. Any mismatch between the source and the sink is |
260 // taken care of in this class instead using a pull FIFO. | 258 // taken care of in this class instead using a pull FIFO. |
261 | 259 |
262 media::AudioParameters sink_params; | 260 media::AudioParameters sink_params; |
263 | 261 |
264 // Use native output siz as default. | 262 // Use native output size as default. |
265 buffer_size = frames_per_buffer_; | |
266 #if defined(OS_ANDROID) | 263 #if defined(OS_ANDROID) |
267 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency | 264 // TODO(henrika): Keep tuning this scheme and espcicially for low-latency |
268 // cases. Might not be possible to come up with the perfect solution using | 265 // cases. Might not be possible to come up with the perfect solution using |
269 // the render side only. | 266 // the render side only. |
270 const int frames_per_10ms = (sample_rate / 100); | 267 if (frames_per_buffer_ < 2 * frames_per_10ms) { |
271 if (buffer_size < 2 * frames_per_10ms) { | |
272 // Examples of low-latency frame sizes and the resulting |buffer_size|: | 268 // Examples of low-latency frame sizes and the resulting |buffer_size|: |
273 // Nexus 7 : 240 audio frames => 2*480 = 960 | 269 // Nexus 7 : 240 audio frames => 2*480 = 960 |
274 // Nexus 10 : 256 => 2*441 = 882 | 270 // Nexus 10 : 256 => 2*441 = 882 |
275 // Galaxy Nexus: 144 => 2*441 = 882 | 271 // Galaxy Nexus: 144 => 2*441 = 882 |
276 buffer_size = 2 * frames_per_10ms; | 272 frames_per_buffer_ = 2 * frames_per_10ms; |
277 DVLOG(1) << "Low-latency output detected on Android"; | 273 DVLOG(1) << "Low-latency output detected on Android"; |
278 } | 274 } |
279 #endif | 275 #endif |
280 DVLOG(1) << "Using sink output buffer size: " << buffer_size; | 276 DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer_; |
281 | 277 |
282 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, | 278 sink_params.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
283 channel_layout, channels, 0, sample_rate, 16, buffer_size); | 279 channel_layout, number_of_channels_, 0, sample_rate_, 16, |
| 280 frames_per_buffer_); |
284 | 281 |
285 // Create a FIFO if re-buffering is required to match the source input with | 282 // Create a FIFO if re-buffering is required to match the source input with |
286 // the sink request. The source acts as provider here and the sink as | 283 // the sink request. The source acts as provider here and the sink as |
287 // consumer. | 284 // consumer. |
288 fifo_delay_milliseconds_ = 0; | 285 fifo_delay_milliseconds_ = 0; |
289 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { | 286 if (source_params.frames_per_buffer() != sink_params.frames_per_buffer()) { |
290 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() | 287 DVLOG(1) << "Rebuffering from " << source_params.frames_per_buffer() |
291 << " to " << sink_params.frames_per_buffer(); | 288 << " to " << sink_params.frames_per_buffer(); |
292 audio_fifo_.reset(new media::AudioPullFifo( | 289 audio_fifo_.reset(new media::AudioPullFifo( |
293 source_params.channels(), | 290 source_params.channels(), |
294 source_params.frames_per_buffer(), | 291 source_params.frames_per_buffer(), |
295 base::Bind( | 292 base::Bind( |
296 &WebRtcAudioRenderer::SourceCallback, | 293 &WebRtcAudioRenderer::SourceCallback, |
297 base::Unretained(this)))); | 294 base::Unretained(this)))); |
298 | 295 |
299 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { | 296 if (sink_params.frames_per_buffer() > source_params.frames_per_buffer()) { |
300 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / | 297 int frame_duration_milliseconds = base::Time::kMillisecondsPerSecond / |
301 static_cast<double>(source_params.sample_rate()); | 298 static_cast<double>(source_params.sample_rate()); |
302 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - | 299 fifo_delay_milliseconds_ = (sink_params.frames_per_buffer() - |
303 source_params.frames_per_buffer()) * frame_duration_milliseconds; | 300 source_params.frames_per_buffer()) * frame_duration_milliseconds; |
304 } | 301 } |
305 } | 302 } |
306 | 303 |
307 // Allocate local audio buffers based on the parameters above. | |
308 // It is assumed that each audio sample contains 16 bits and each | |
309 // audio frame contains one or two audio samples depending on the | |
310 // number of channels. | |
311 buffer_.reset( | |
312 new int16[source_params.frames_per_buffer() * source_params.channels()]); | |
313 | |
314 source_ = source; | 304 source_ = source; |
315 source->SetRenderFormat(source_params); | |
316 | 305 |
317 // Configure the audio rendering client and start rendering. | 306 // Configure the audio rendering client and start rendering. |
318 sink_ = AudioDeviceFactory::NewOutputDevice( | 307 sink_ = AudioDeviceFactory::NewOutputDevice( |
319 source_render_view_id_, source_render_frame_id_); | 308 source_render_view_id_, source_render_frame_id_); |
320 | 309 |
321 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association | 310 // TODO(tommi): Rename InitializeUnifiedStream to rather reflect association |
322 // with a session. | 311 // with a session. |
323 DCHECK_GE(session_id_, 0); | 312 DCHECK_GE(session_id_, 0); |
324 sink_->InitializeUnifiedStream(sink_params, this, session_id_); | 313 sink_->InitializeUnifiedStream(sink_params, this, session_id_); |
325 | 314 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" | 450 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback(" |
462 << fifo_frame_delay << ", " | 451 << fifo_frame_delay << ", " |
463 << audio_bus->frames() << ")"; | 452 << audio_bus->frames() << ")"; |
464 | 453 |
465 int output_delay_milliseconds = audio_delay_milliseconds_; | 454 int output_delay_milliseconds = audio_delay_milliseconds_; |
466 output_delay_milliseconds += fifo_delay_milliseconds_; | 455 output_delay_milliseconds += fifo_delay_milliseconds_; |
467 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; | 456 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds; |
468 | 457 |
469 // We need to keep render data for the |source_| regardless of |state_|, | 458 // We need to keep render data for the |source_| regardless of |state_|, |
470 // otherwise the data will be buffered up inside |source_|. | 459 // otherwise the data will be buffered up inside |source_|. |
471 source_->RenderData(reinterpret_cast<uint8*>(buffer_.get()), | 460 source_->RenderData(audio_bus, sample_rate_, output_delay_milliseconds); |
472 audio_bus->channels(), audio_bus->frames(), | |
473 output_delay_milliseconds); | |
474 | 461 |
475 // Avoid filling up the audio bus if we are not playing; instead | 462 // Avoid filling up the audio bus if we are not playing; instead |
476 // return here and ensure that the returned value in Render() is 0. | 463 // return here and ensure that the returned value in Render() is 0. |
477 if (state_ != PLAYING) { | 464 if (state_ != PLAYING) |
478 audio_bus->Zero(); | 465 audio_bus->Zero(); |
479 return; | |
480 } | |
481 | |
482 // De-interleave each channel and convert to 32-bit floating-point | |
483 // with nominal range -1.0 -> +1.0 to match the callback format. | |
484 audio_bus->FromInterleaved(buffer_.get(), | |
485 audio_bus->frames(), | |
486 sizeof(buffer_[0])); | |
487 } | 466 } |
488 | 467 |
489 } // namespace content | 468 } // namespace content |
OLD | NEW |