| 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/track_audio_renderer.h" | 5 #include "content/renderer/media/track_audio_renderer.h" |
| 6 | 6 |
| 7 #include "base/location.h" | 7 #include "base/location.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/synchronization/lock.h" | 10 #include "base/synchronization/lock.h" |
| 11 #include "base/thread_task_runner_handle.h" | 11 #include "base/thread_task_runner_handle.h" |
| 12 #include "base/trace_event/trace_event.h" | 12 #include "base/trace_event/trace_event.h" |
| 13 #include "content/renderer/media/audio_device_factory.h" | 13 #include "content/renderer/media/audio_device_factory.h" |
| 14 #include "content/renderer/media/media_stream_audio_track.h" | 14 #include "content/renderer/media/media_stream_audio_track.h" |
| 15 #include "media/audio/audio_output_device.h" | |
| 16 #include "media/base/audio_bus.h" | 15 #include "media/base/audio_bus.h" |
| 17 #include "media/base/audio_shifter.h" | 16 #include "media/base/audio_shifter.h" |
| 18 | 17 |
| 19 namespace content { | 18 namespace content { |
| 20 | 19 |
| 21 namespace { | 20 namespace { |
| 22 | 21 |
| 23 enum LocalRendererSinkStates { | 22 enum LocalRendererSinkStates { |
| 24 kSinkStarted = 0, | 23 kSinkStarted = 0, |
| 25 kSinkNeverStarted, | 24 kSinkNeverStarted, |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 | 143 |
| 145 void TrackAudioRenderer::Start() { | 144 void TrackAudioRenderer::Start() { |
| 146 DVLOG(1) << "TrackAudioRenderer::Start()"; | 145 DVLOG(1) << "TrackAudioRenderer::Start()"; |
| 147 DCHECK(task_runner_->BelongsToCurrentThread()); | 146 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 148 DCHECK_EQ(playing_, false); | 147 DCHECK_EQ(playing_, false); |
| 149 | 148 |
| 150 // We get audio data from |audio_track_|... | 149 // We get audio data from |audio_track_|... |
| 151 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); | 150 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); |
| 152 // ...and |sink_| will get audio data from us. | 151 // ...and |sink_| will get audio data from us. |
| 153 DCHECK(!sink_.get()); | 152 DCHECK(!sink_.get()); |
| 154 sink_ = | 153 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
| 155 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_, | 154 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 156 output_device_id_, security_origin_); | 155 session_id_, output_device_id_, security_origin_); |
| 157 | 156 |
| 158 base::AutoLock auto_lock(thread_lock_); | 157 base::AutoLock auto_lock(thread_lock_); |
| 159 prior_elapsed_render_time_ = base::TimeDelta(); | 158 prior_elapsed_render_time_ = base::TimeDelta(); |
| 160 num_samples_rendered_ = 0; | 159 num_samples_rendered_ = 0; |
| 161 } | 160 } |
| 162 | 161 |
| 163 void TrackAudioRenderer::Stop() { | 162 void TrackAudioRenderer::Stop() { |
| 164 DVLOG(1) << "TrackAudioRenderer::Stop()"; | 163 DVLOG(1) << "TrackAudioRenderer::Stop()"; |
| 165 DCHECK(task_runner_->BelongsToCurrentThread()); | 164 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 166 | 165 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 const url::Origin& security_origin, | 245 const url::Origin& security_origin, |
| 247 const media::SwitchOutputDeviceCB& callback) { | 246 const media::SwitchOutputDeviceCB& callback) { |
| 248 DVLOG(1) << "TrackAudioRenderer::SwitchOutputDevice()"; | 247 DVLOG(1) << "TrackAudioRenderer::SwitchOutputDevice()"; |
| 249 DCHECK(task_runner_->BelongsToCurrentThread()); | 248 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 250 | 249 |
| 251 { | 250 { |
| 252 base::AutoLock auto_lock(thread_lock_); | 251 base::AutoLock auto_lock(thread_lock_); |
| 253 HaltAudioFlowWhileLockHeld(); | 252 HaltAudioFlowWhileLockHeld(); |
| 254 } | 253 } |
| 255 | 254 |
| 256 scoped_refptr<media::AudioOutputDevice> new_sink = | 255 scoped_refptr<media::AudioRendererSink> new_sink = |
| 257 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_, | 256 AudioDeviceFactory::NewAudioRendererSink( |
| 258 device_id, security_origin); | 257 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 259 if (new_sink->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) { | 258 session_id_, device_id, security_origin); |
| 260 callback.Run(new_sink->GetDeviceStatus()); | 259 media::OutputDeviceStatus new_sink_status = |
| 260 new_sink->GetOutputDevice()->GetDeviceStatus(); |
| 261 if (new_sink_status != media::OUTPUT_DEVICE_STATUS_OK) { |
| 262 callback.Run(new_sink_status); |
| 261 return; | 263 return; |
| 262 } | 264 } |
| 263 | 265 |
| 264 output_device_id_ = device_id; | 266 output_device_id_ = device_id; |
| 265 security_origin_ = security_origin; | 267 security_origin_ = security_origin; |
| 266 bool was_sink_started = sink_started_; | 268 bool was_sink_started = sink_started_; |
| 267 | 269 |
| 268 if (sink_.get()) | 270 if (sink_.get()) |
| 269 sink_->Stop(); | 271 sink_->Stop(); |
| 270 | 272 |
| 271 sink_started_ = false; | 273 sink_started_ = false; |
| 272 sink_ = new_sink; | 274 sink_ = new_sink; |
| 273 if (was_sink_started) | 275 if (was_sink_started) |
| 274 MaybeStartSink(); | 276 MaybeStartSink(); |
| 275 | 277 |
| 276 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); | 278 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); |
| 277 } | 279 } |
| 278 | 280 |
| 279 media::AudioParameters TrackAudioRenderer::GetOutputParameters() { | 281 media::AudioParameters TrackAudioRenderer::GetOutputParameters() { |
| 280 DCHECK(task_runner_->BelongsToCurrentThread()); | 282 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 281 if (!sink_ || !source_params_.IsValid()) | 283 if (!sink_ || !source_params_.IsValid()) |
| 282 return media::AudioParameters(); | 284 return media::AudioParameters(); |
| 283 | 285 |
| 284 // Output parameters consist of the same channel layout and sample rate as the | 286 // Output parameters consist of the same channel layout and sample rate as the |
| 285 // source, but having the buffer duration preferred by the hardware. | 287 // source, but having the buffer duration preferred by the hardware. |
| 286 const media::AudioParameters& preferred_params = sink_->GetOutputParameters(); | 288 const media::AudioParameters& preferred_params = |
| 289 sink_->GetOutputDevice()->GetOutputParameters(); |
| 287 return media::AudioParameters( | 290 return media::AudioParameters( |
| 288 preferred_params.format(), source_params_.channel_layout(), | 291 preferred_params.format(), source_params_.channel_layout(), |
| 289 source_params_.sample_rate(), source_params_.bits_per_sample(), | 292 source_params_.sample_rate(), source_params_.bits_per_sample(), |
| 290 preferred_params.frames_per_buffer() * source_params_.sample_rate() / | 293 preferred_params.frames_per_buffer() * source_params_.sample_rate() / |
| 291 preferred_params.sample_rate()); | 294 preferred_params.sample_rate()); |
| 292 } | 295 } |
| 293 | 296 |
| 294 media::OutputDeviceStatus TrackAudioRenderer::GetDeviceStatus() { | 297 media::OutputDeviceStatus TrackAudioRenderer::GetDeviceStatus() { |
| 295 DCHECK(task_runner_->BelongsToCurrentThread()); | 298 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 296 if (!sink_.get()) | 299 if (!sink_.get()) |
| 297 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; | 300 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; |
| 298 | 301 |
| 299 return sink_->GetDeviceStatus(); | 302 return sink_->GetOutputDevice()->GetDeviceStatus(); |
| 300 } | 303 } |
| 301 | 304 |
| 302 void TrackAudioRenderer::MaybeStartSink() { | 305 void TrackAudioRenderer::MaybeStartSink() { |
| 303 DCHECK(task_runner_->BelongsToCurrentThread()); | 306 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 304 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; | 307 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; |
| 305 | 308 |
| 306 if (!sink_.get() || !source_params_.IsValid() || !playing_) | 309 if (!sink_.get() || !source_params_.IsValid() || !playing_) |
| 307 return; | 310 return; |
| 308 | 311 |
| 309 // Re-create the AudioShifter to drop old audio data and reset to a starting | 312 // Re-create the AudioShifter to drop old audio data and reset to a starting |
| 310 // state. MaybeStartSink() is always called in a situation where either the | 313 // state. MaybeStartSink() is always called in a situation where either the |
| 311 // source or sink has changed somehow and so all of AudioShifter's internal | 314 // source or sink has changed somehow and so all of AudioShifter's internal |
| 312 // time-sync state is invalid. | 315 // time-sync state is invalid. |
| 313 CreateAudioShifter(); | 316 CreateAudioShifter(); |
| 314 | 317 |
| 315 if (sink_started_ || | 318 if (sink_started_ || |
| 316 sink_->GetDeviceStatus() != media::OUTPUT_DEVICE_STATUS_OK) { | 319 sink_->GetOutputDevice()->GetDeviceStatus() != |
| 320 media::OUTPUT_DEVICE_STATUS_OK) { |
| 317 return; | 321 return; |
| 318 } | 322 } |
| 319 | 323 |
| 320 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " | 324 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " |
| 321 "source_params_={") | 325 "source_params_={") |
| 322 << source_params_.AsHumanReadableString() << "}, sink parameters={" | 326 << source_params_.AsHumanReadableString() << "}, sink parameters={" |
| 323 << GetOutputParameters().AsHumanReadableString() << '}'; | 327 << GetOutputParameters().AsHumanReadableString() << '}'; |
| 324 sink_->Initialize(GetOutputParameters(), this); | 328 sink_->Initialize(GetOutputParameters(), this); |
| 325 sink_->Start(); | 329 sink_->Start(); |
| 326 sink_->SetVolume(volume_); | 330 sink_->SetVolume(volume_); |
| 331 sink_->Play(); // Not all the sinks play on start. |
| 327 sink_started_ = true; | 332 sink_started_ = true; |
| 328 if (IsLocalRenderer()) { | 333 if (IsLocalRenderer()) { |
| 329 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, | 334 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, |
| 330 kSinkStatesMax); | 335 kSinkStatesMax); |
| 331 } | 336 } |
| 332 } | 337 } |
| 333 | 338 |
| 334 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { | 339 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { |
| 335 DCHECK(task_runner_->BelongsToCurrentThread()); | 340 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 336 | 341 |
| 337 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; | 342 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; |
| 338 | 343 |
| 339 if (source_params_.Equals(params)) | 344 if (source_params_.Equals(params)) |
| 340 return; | 345 return; |
| 341 source_params_ = params; | 346 source_params_ = params; |
| 342 | 347 |
| 343 if (!sink_.get()) | 348 if (!sink_.get()) |
| 344 return; // TrackAudioRenderer has not yet been started. | 349 return; // TrackAudioRenderer has not yet been started. |
| 345 | 350 |
| 346 // Stop |sink_| and re-create a new one to be initialized with different audio | 351 // Stop |sink_| and re-create a new one to be initialized with different audio |
| 347 // parameters. Then, invoke MaybeStartSink() to restart everything again. | 352 // parameters. Then, invoke MaybeStartSink() to restart everything again. |
| 348 sink_->Stop(); | 353 sink_->Stop(); |
| 349 sink_started_ = false; | 354 sink_started_ = false; |
| 350 sink_ = | 355 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
| 351 AudioDeviceFactory::NewOutputDevice(playout_render_frame_id_, session_id_, | 356 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 352 output_device_id_, security_origin_); | 357 session_id_, output_device_id_, security_origin_); |
| 353 MaybeStartSink(); | 358 MaybeStartSink(); |
| 354 } | 359 } |
| 355 | 360 |
| 356 void TrackAudioRenderer::CreateAudioShifter() { | 361 void TrackAudioRenderer::CreateAudioShifter() { |
| 357 DCHECK(task_runner_->BelongsToCurrentThread()); | 362 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 358 | 363 |
| 359 // Note 1: The max buffer is fairly large to cover the case where | 364 // Note 1: The max buffer is fairly large to cover the case where |
| 360 // remotely-sourced audio is delivered well ahead of its scheduled playout | 365 // remotely-sourced audio is delivered well ahead of its scheduled playout |
| 361 // time (e.g., content streaming with a very large end-to-end | 366 // time (e.g., content streaming with a very large end-to-end |
| 362 // latency). However, there is no penalty for making it large in the | 367 // latency). However, there is no penalty for making it large in the |
| (...skipping 20 matching lines...) Expand all Loading... |
| 383 if (source_params_.IsValid()) { | 388 if (source_params_.IsValid()) { |
| 384 prior_elapsed_render_time_ = | 389 prior_elapsed_render_time_ = |
| 385 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, | 390 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, |
| 386 num_samples_rendered_, | 391 num_samples_rendered_, |
| 387 source_params_.sample_rate()); | 392 source_params_.sample_rate()); |
| 388 num_samples_rendered_ = 0; | 393 num_samples_rendered_ = 0; |
| 389 } | 394 } |
| 390 } | 395 } |
| 391 | 396 |
| 392 } // namespace content | 397 } // namespace content |
| OLD | NEW |