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