| 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" |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 output_device_id_(device_id), | 130 output_device_id_(device_id), |
| 131 security_origin_(security_origin), | 131 security_origin_(security_origin), |
| 132 volume_(0.0), | 132 volume_(0.0), |
| 133 sink_started_(false) { | 133 sink_started_(false) { |
| 134 DCHECK(MediaStreamAudioTrack::From(audio_track_)); | 134 DCHECK(MediaStreamAudioTrack::From(audio_track_)); |
| 135 DVLOG(1) << "TrackAudioRenderer::TrackAudioRenderer()"; | 135 DVLOG(1) << "TrackAudioRenderer::TrackAudioRenderer()"; |
| 136 } | 136 } |
| 137 | 137 |
| 138 TrackAudioRenderer::~TrackAudioRenderer() { | 138 TrackAudioRenderer::~TrackAudioRenderer() { |
| 139 DCHECK(task_runner_->BelongsToCurrentThread()); | 139 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 140 DCHECK(!sink_.get()); | 140 DCHECK(!sink_); |
| 141 DVLOG(1) << "TrackAudioRenderer::~TrackAudioRenderer()"; | 141 DVLOG(1) << "TrackAudioRenderer::~TrackAudioRenderer()"; |
| 142 } | 142 } |
| 143 | 143 |
| 144 void TrackAudioRenderer::Start() { | 144 void TrackAudioRenderer::Start() { |
| 145 DVLOG(1) << "TrackAudioRenderer::Start()"; | 145 DVLOG(1) << "TrackAudioRenderer::Start()"; |
| 146 DCHECK(task_runner_->BelongsToCurrentThread()); | 146 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 147 DCHECK_EQ(playing_, false); | 147 DCHECK_EQ(playing_, false); |
| 148 | 148 |
| 149 // We get audio data from |audio_track_|... | 149 // We get audio data from |audio_track_|... |
| 150 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); | 150 MediaStreamAudioSink::AddToAudioTrack(this, audio_track_); |
| 151 // ...and |sink_| will get audio data from us. | 151 // ...and |sink_| will get audio data from us. |
| 152 DCHECK(!sink_.get()); | 152 DCHECK(!sink_); |
| 153 sink_ = AudioDeviceFactory::NewAudioRendererSink( | 153 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
| 154 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 154 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 155 session_id_, output_device_id_, security_origin_); | 155 session_id_, output_device_id_, security_origin_); |
| 156 | 156 |
| 157 base::AutoLock auto_lock(thread_lock_); | 157 base::AutoLock auto_lock(thread_lock_); |
| 158 prior_elapsed_render_time_ = base::TimeDelta(); | 158 prior_elapsed_render_time_ = base::TimeDelta(); |
| 159 num_samples_rendered_ = 0; | 159 num_samples_rendered_ = 0; |
| 160 } | 160 } |
| 161 | 161 |
| 162 void TrackAudioRenderer::Stop() { | 162 void TrackAudioRenderer::Stop() { |
| 163 DVLOG(1) << "TrackAudioRenderer::Stop()"; | 163 DVLOG(1) << "TrackAudioRenderer::Stop()"; |
| 164 DCHECK(task_runner_->BelongsToCurrentThread()); | 164 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 165 | 165 |
| 166 Pause(); | 166 Pause(); |
| 167 | 167 |
| 168 // Stop the output audio stream, i.e, stop asking for data to render. | 168 // Stop the output audio stream, i.e, stop asking for data to render. |
| 169 // It is safer to call Stop() on the |sink_| to clean up the resources even | 169 // It is safer to call Stop() on the |sink_| to clean up the resources even |
| 170 // when the |sink_| is never started. | 170 // when the |sink_| is never started. |
| 171 if (sink_.get()) { | 171 if (sink_) { |
| 172 sink_->Stop(); | 172 sink_->Stop(); |
| 173 sink_ = NULL; | 173 sink_ = NULL; |
| 174 } | 174 } |
| 175 | 175 |
| 176 if (!sink_started_ && IsLocalRenderer()) { | 176 if (!sink_started_ && IsLocalRenderer()) { |
| 177 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", | 177 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", |
| 178 kSinkNeverStarted, kSinkStatesMax); | 178 kSinkNeverStarted, kSinkStatesMax); |
| 179 } | 179 } |
| 180 sink_started_ = false; | 180 sink_started_ = false; |
| 181 | 181 |
| 182 // Ensure that the capturer stops feeding us with captured audio. | 182 // Ensure that the capturer stops feeding us with captured audio. |
| 183 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_); | 183 MediaStreamAudioSink::RemoveFromAudioTrack(this, audio_track_); |
| 184 } | 184 } |
| 185 | 185 |
| 186 void TrackAudioRenderer::Play() { | 186 void TrackAudioRenderer::Play() { |
| 187 DVLOG(1) << "TrackAudioRenderer::Play()"; | 187 DVLOG(1) << "TrackAudioRenderer::Play()"; |
| 188 DCHECK(task_runner_->BelongsToCurrentThread()); | 188 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 189 | 189 |
| 190 if (!sink_.get()) | 190 if (!sink_) |
| 191 return; | 191 return; |
| 192 | 192 |
| 193 playing_ = true; | 193 playing_ = true; |
| 194 | 194 |
| 195 MaybeStartSink(); | 195 MaybeStartSink(); |
| 196 } | 196 } |
| 197 | 197 |
| 198 void TrackAudioRenderer::Pause() { | 198 void TrackAudioRenderer::Pause() { |
| 199 DVLOG(1) << "TrackAudioRenderer::Pause()"; | 199 DVLOG(1) << "TrackAudioRenderer::Pause()"; |
| 200 DCHECK(task_runner_->BelongsToCurrentThread()); | 200 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 201 | 201 |
| 202 if (!sink_.get()) | 202 if (!sink_) |
| 203 return; | 203 return; |
| 204 | 204 |
| 205 playing_ = false; | 205 playing_ = false; |
| 206 | 206 |
| 207 base::AutoLock auto_lock(thread_lock_); | 207 base::AutoLock auto_lock(thread_lock_); |
| 208 HaltAudioFlowWhileLockHeld(); | 208 HaltAudioFlowWhileLockHeld(); |
| 209 } | 209 } |
| 210 | 210 |
| 211 void TrackAudioRenderer::SetVolume(float volume) { | 211 void TrackAudioRenderer::SetVolume(float volume) { |
| 212 DVLOG(1) << "TrackAudioRenderer::SetVolume(" << volume << ")"; | 212 DVLOG(1) << "TrackAudioRenderer::SetVolume(" << volume << ")"; |
| 213 DCHECK(task_runner_->BelongsToCurrentThread()); | 213 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 214 | 214 |
| 215 // Cache the volume. Whenever |sink_| is re-created, call SetVolume() with | 215 // Cache the volume. Whenever |sink_| is re-created, call SetVolume() with |
| 216 // this cached volume. | 216 // this cached volume. |
| 217 volume_ = volume; | 217 volume_ = volume; |
| 218 if (sink_.get()) | 218 if (sink_) |
| 219 sink_->SetVolume(volume); | 219 sink_->SetVolume(volume); |
| 220 } | 220 } |
| 221 | 221 |
| 222 media::OutputDevice* TrackAudioRenderer::GetOutputDevice() { | 222 media::OutputDeviceInfo TrackAudioRenderer::GetOutputDeviceInfo() { |
| 223 DCHECK(task_runner_->BelongsToCurrentThread()); | 223 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 224 return this; | 224 return sink_ ? sink_->GetOutputDeviceInfo() : media::OutputDeviceInfo(); |
| 225 } | 225 } |
| 226 | 226 |
| 227 base::TimeDelta TrackAudioRenderer::GetCurrentRenderTime() const { | 227 base::TimeDelta TrackAudioRenderer::GetCurrentRenderTime() const { |
| 228 DCHECK(task_runner_->BelongsToCurrentThread()); | 228 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 229 base::AutoLock auto_lock(thread_lock_); | 229 base::AutoLock auto_lock(thread_lock_); |
| 230 if (source_params_.IsValid()) { | 230 if (source_params_.IsValid()) { |
| 231 return ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, | 231 return ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, |
| 232 num_samples_rendered_, | 232 num_samples_rendered_, |
| 233 source_params_.sample_rate()); | 233 source_params_.sample_rate()); |
| 234 } | 234 } |
| 235 return prior_elapsed_render_time_; | 235 return prior_elapsed_render_time_; |
| 236 } | 236 } |
| 237 | 237 |
| 238 bool TrackAudioRenderer::IsLocalRenderer() const { | 238 bool TrackAudioRenderer::IsLocalRenderer() const { |
| 239 DCHECK(task_runner_->BelongsToCurrentThread()); | 239 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 240 return MediaStreamAudioTrack::From(audio_track_)->is_local_track(); | 240 return MediaStreamAudioTrack::From(audio_track_)->is_local_track(); |
| 241 } | 241 } |
| 242 | 242 |
| 243 void TrackAudioRenderer::SwitchOutputDevice( | 243 void TrackAudioRenderer::SwitchOutputDevice( |
| 244 const std::string& device_id, | 244 const std::string& device_id, |
| 245 const url::Origin& security_origin, | 245 const url::Origin& security_origin, |
| 246 const media::SwitchOutputDeviceCB& callback) { | 246 const media::OutputDeviceStatusCB& callback) { |
| 247 DVLOG(1) << "TrackAudioRenderer::SwitchOutputDevice()"; | 247 DVLOG(1) << "TrackAudioRenderer::SwitchOutputDevice()"; |
| 248 DCHECK(task_runner_->BelongsToCurrentThread()); | 248 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 249 | 249 |
| 250 { | 250 { |
| 251 base::AutoLock auto_lock(thread_lock_); | 251 base::AutoLock auto_lock(thread_lock_); |
| 252 HaltAudioFlowWhileLockHeld(); | 252 HaltAudioFlowWhileLockHeld(); |
| 253 } | 253 } |
| 254 | 254 |
| 255 scoped_refptr<media::AudioRendererSink> new_sink = | 255 scoped_refptr<media::AudioRendererSink> new_sink = |
| 256 AudioDeviceFactory::NewAudioRendererSink( | 256 AudioDeviceFactory::NewAudioRendererSink( |
| 257 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 257 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 258 session_id_, device_id, security_origin); | 258 session_id_, device_id, security_origin); |
| 259 |
| 259 media::OutputDeviceStatus new_sink_status = | 260 media::OutputDeviceStatus new_sink_status = |
| 260 new_sink->GetOutputDevice()->GetDeviceStatus(); | 261 new_sink->GetOutputDeviceInfo().device_status(); |
| 261 if (new_sink_status != media::OUTPUT_DEVICE_STATUS_OK) { | 262 if (new_sink_status != media::OUTPUT_DEVICE_STATUS_OK) { |
| 262 callback.Run(new_sink_status); | 263 callback.Run(new_sink_status); |
| 263 return; | 264 return; |
| 264 } | 265 } |
| 265 | 266 |
| 266 output_device_id_ = device_id; | 267 output_device_id_ = device_id; |
| 267 security_origin_ = security_origin; | 268 security_origin_ = security_origin; |
| 268 bool was_sink_started = sink_started_; | 269 bool was_sink_started = sink_started_; |
| 269 | 270 |
| 270 if (sink_.get()) | 271 if (sink_) |
| 271 sink_->Stop(); | 272 sink_->Stop(); |
| 272 | 273 |
| 273 sink_started_ = false; | 274 sink_started_ = false; |
| 274 sink_ = new_sink; | 275 sink_ = new_sink; |
| 275 if (was_sink_started) | 276 if (was_sink_started) |
| 276 MaybeStartSink(); | 277 MaybeStartSink(); |
| 277 | 278 |
| 278 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); | 279 callback.Run(media::OUTPUT_DEVICE_STATUS_OK); |
| 279 } | 280 } |
| 280 | 281 |
| 281 media::AudioParameters TrackAudioRenderer::GetOutputParameters() { | |
| 282 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 283 if (!sink_ || !source_params_.IsValid()) | |
| 284 return media::AudioParameters(); | |
| 285 | |
| 286 // Output parameters consist of the same channel layout and sample rate as the | |
| 287 // source, but having the buffer duration preferred by the hardware. | |
| 288 const media::AudioParameters& preferred_params = | |
| 289 sink_->GetOutputDevice()->GetOutputParameters(); | |
| 290 return media::AudioParameters( | |
| 291 preferred_params.format(), source_params_.channel_layout(), | |
| 292 source_params_.sample_rate(), source_params_.bits_per_sample(), | |
| 293 preferred_params.frames_per_buffer() * source_params_.sample_rate() / | |
| 294 preferred_params.sample_rate()); | |
| 295 } | |
| 296 | |
| 297 media::OutputDeviceStatus TrackAudioRenderer::GetDeviceStatus() { | |
| 298 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 299 if (!sink_.get()) | |
| 300 return media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL; | |
| 301 | |
| 302 return sink_->GetOutputDevice()->GetDeviceStatus(); | |
| 303 } | |
| 304 | |
| 305 void TrackAudioRenderer::MaybeStartSink() { | 282 void TrackAudioRenderer::MaybeStartSink() { |
| 306 DCHECK(task_runner_->BelongsToCurrentThread()); | 283 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 307 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; | 284 DVLOG(1) << "TrackAudioRenderer::MaybeStartSink()"; |
| 308 | 285 |
| 309 if (!sink_.get() || !source_params_.IsValid() || !playing_) | 286 if (!sink_ || !source_params_.IsValid() || !playing_) |
| 310 return; | 287 return; |
| 311 | 288 |
| 312 // Re-create the AudioShifter to drop old audio data and reset to a starting | 289 // Re-create the AudioShifter to drop old audio data and reset to a starting |
| 313 // state. MaybeStartSink() is always called in a situation where either the | 290 // state. MaybeStartSink() is always called in a situation where either the |
| 314 // source or sink has changed somehow and so all of AudioShifter's internal | 291 // source or sink has changed somehow and so all of AudioShifter's internal |
| 315 // time-sync state is invalid. | 292 // time-sync state is invalid. |
| 316 CreateAudioShifter(); | 293 CreateAudioShifter(); |
| 317 | 294 |
| 318 if (sink_started_ || | 295 if (sink_started_) |
| 319 sink_->GetOutputDevice()->GetDeviceStatus() != | |
| 320 media::OUTPUT_DEVICE_STATUS_OK) { | |
| 321 return; | 296 return; |
| 322 } | |
| 323 | 297 |
| 298 const media::OutputDeviceInfo& device_info = sink_->GetOutputDeviceInfo(); |
| 299 if (device_info.device_status() != media::OUTPUT_DEVICE_STATUS_OK) |
| 300 return; |
| 301 |
| 302 // Output parameters consist of the same channel layout and sample rate as the |
| 303 // source, but having the buffer duration preferred by the hardware. |
| 304 const media::AudioParameters& hardware_params = device_info.output_params(); |
| 305 media::AudioParameters sink_params( |
| 306 hardware_params.format(), source_params_.channel_layout(), |
| 307 source_params_.sample_rate(), source_params_.bits_per_sample(), |
| 308 hardware_params.frames_per_buffer() * source_params_.sample_rate() / |
| 309 hardware_params.sample_rate()); |
| 324 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " | 310 DVLOG(1) << ("TrackAudioRenderer::MaybeStartSink() -- Starting sink. " |
| 325 "source_params_={") | 311 "source_params_={") |
| 326 << source_params_.AsHumanReadableString() << "}, sink parameters={" | 312 << source_params_.AsHumanReadableString() << "}, sink parameters={" |
| 327 << GetOutputParameters().AsHumanReadableString() << '}'; | 313 << sink_params.AsHumanReadableString() << '}'; |
| 328 sink_->Initialize(GetOutputParameters(), this); | 314 sink_->Initialize(sink_params, this); |
| 329 sink_->Start(); | 315 sink_->Start(); |
| 330 sink_->SetVolume(volume_); | 316 sink_->SetVolume(volume_); |
| 331 sink_->Play(); // Not all the sinks play on start. | 317 sink_->Play(); // Not all the sinks play on start. |
| 332 sink_started_ = true; | 318 sink_started_ = true; |
| 333 if (IsLocalRenderer()) { | 319 if (IsLocalRenderer()) { |
| 334 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, | 320 UMA_HISTOGRAM_ENUMERATION("Media.LocalRendererSinkStates", kSinkStarted, |
| 335 kSinkStatesMax); | 321 kSinkStatesMax); |
| 336 } | 322 } |
| 337 } | 323 } |
| 338 | 324 |
| 339 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { | 325 void TrackAudioRenderer::ReconfigureSink(const media::AudioParameters& params) { |
| 340 DCHECK(task_runner_->BelongsToCurrentThread()); | 326 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 341 | 327 |
| 342 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; | 328 DVLOG(1) << "TrackAudioRenderer::ReconfigureSink()"; |
| 343 | 329 |
| 344 if (source_params_.Equals(params)) | 330 if (source_params_.Equals(params)) |
| 345 return; | 331 return; |
| 346 source_params_ = params; | 332 source_params_ = params; |
| 347 | 333 |
| 348 if (!sink_.get()) | 334 if (!sink_) |
| 349 return; // TrackAudioRenderer has not yet been started. | 335 return; // TrackAudioRenderer has not yet been started. |
| 350 | 336 |
| 351 // Stop |sink_| and re-create a new one to be initialized with different audio | 337 // Stop |sink_| and re-create a new one to be initialized with different audio |
| 352 // parameters. Then, invoke MaybeStartSink() to restart everything again. | 338 // parameters. Then, invoke MaybeStartSink() to restart everything again. |
| 353 sink_->Stop(); | 339 sink_->Stop(); |
| 354 sink_started_ = false; | 340 sink_started_ = false; |
| 355 sink_ = AudioDeviceFactory::NewAudioRendererSink( | 341 sink_ = AudioDeviceFactory::NewAudioRendererSink( |
| 356 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, | 342 AudioDeviceFactory::kSourceNonRtcAudioTrack, playout_render_frame_id_, |
| 357 session_id_, output_device_id_, security_origin_); | 343 session_id_, output_device_id_, security_origin_); |
| 358 MaybeStartSink(); | 344 MaybeStartSink(); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 388 if (source_params_.IsValid()) { | 374 if (source_params_.IsValid()) { |
| 389 prior_elapsed_render_time_ = | 375 prior_elapsed_render_time_ = |
| 390 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, | 376 ComputeTotalElapsedRenderTime(prior_elapsed_render_time_, |
| 391 num_samples_rendered_, | 377 num_samples_rendered_, |
| 392 source_params_.sample_rate()); | 378 source_params_.sample_rate()); |
| 393 num_samples_rendered_ = 0; | 379 num_samples_rendered_ = 0; |
| 394 } | 380 } |
| 395 } | 381 } |
| 396 | 382 |
| 397 } // namespace content | 383 } // namespace content |
| OLD | NEW |