Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "chromecast/media/audio/cast_audio_output_stream.h" | 5 #include "chromecast/media/audio/cast_audio_output_stream.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | |
| 8 #include "base/synchronization/waitable_event.h" | 9 #include "base/synchronization/waitable_event.h" |
| 9 #include "base/threading/thread_checker.h" | 10 #include "base/threading/thread_checker.h" |
| 10 #include "chromecast/base/metrics/cast_metrics_helper.h" | 11 #include "chromecast/base/metrics/cast_metrics_helper.h" |
| 11 #include "chromecast/media/audio/cast_audio_manager.h" | 12 #include "chromecast/media/audio/cast_audio_manager.h" |
| 12 #include "chromecast/media/base/media_message_loop.h" | 13 #include "chromecast/media/base/media_message_loop.h" |
| 13 #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h" | 14 #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h" |
| 14 #include "chromecast/media/cma/base/decoder_buffer_adapter.h" | 15 #include "chromecast/media/cma/base/decoder_buffer_adapter.h" |
| 15 #include "chromecast/media/cma/pipeline/frame_status_cb_impl.h" | |
| 16 #include "chromecast/public/media/audio_pipeline_device.h" | |
| 17 #include "chromecast/public/media/decoder_config.h" | 16 #include "chromecast/public/media/decoder_config.h" |
| 18 #include "chromecast/public/media/decrypt_context.h" | 17 #include "chromecast/public/media/decrypt_context.h" |
| 19 #include "chromecast/public/media/media_clock_device.h" | |
| 20 #include "chromecast/public/media/media_pipeline_backend.h" | 18 #include "chromecast/public/media/media_pipeline_backend.h" |
| 21 #include "media/base/bind_to_current_loop.h" | 19 #include "media/base/bind_to_current_loop.h" |
| 22 #include "media/base/decoder_buffer.h" | 20 #include "media/base/decoder_buffer.h" |
| 23 | 21 |
| 24 namespace chromecast { | 22 namespace chromecast { |
| 25 namespace media { | 23 namespace media { |
| 24 namespace { | |
| 26 | 25 |
| 27 namespace { | 26 MediaPipelineBackend::AudioDecoder* InitializeBackend( |
| 28 bool InitClockDevice(MediaClockDevice* clock_device) { | 27 const ::media::AudioParameters& audio_params, |
| 29 DCHECK(clock_device); | 28 MediaPipelineBackend* backend, |
| 30 DCHECK_EQ(clock_device->GetState(), MediaClockDevice::kStateUninitialized); | 29 MediaPipelineBackend::Delegate* delegate) { |
| 30 DCHECK(backend); | |
| 31 DCHECK(delegate); | |
| 31 | 32 |
| 32 if (!clock_device->SetState(media::MediaClockDevice::kStateIdle)) | 33 MediaPipelineBackend::AudioDecoder* decoder = backend->CreateAudioDecoder(); |
| 33 return false; | 34 if (!decoder) |
| 34 | 35 return nullptr; |
| 35 if (!clock_device->ResetTimeline(0)) | |
| 36 return false; | |
| 37 | |
| 38 if (!clock_device->SetRate(1.0)) | |
| 39 return false; | |
| 40 | |
| 41 return true; | |
| 42 } | |
| 43 | |
| 44 bool InitAudioDevice(const ::media::AudioParameters& audio_params, | |
| 45 AudioPipelineDevice* audio_device) { | |
| 46 DCHECK(audio_device); | |
| 47 DCHECK_EQ(audio_device->GetState(), AudioPipelineDevice::kStateUninitialized); | |
| 48 | 36 |
| 49 AudioConfig audio_config; | 37 AudioConfig audio_config; |
| 50 audio_config.codec = kCodecPCM; | 38 audio_config.codec = kCodecPCM; |
| 51 audio_config.sample_format = kSampleFormatS16; | 39 audio_config.sample_format = kSampleFormatS16; |
| 52 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8; | 40 audio_config.bytes_per_channel = audio_params.bits_per_sample() / 8; |
| 53 audio_config.channel_number = audio_params.channels(); | 41 audio_config.channel_number = audio_params.channels(); |
| 54 audio_config.samples_per_second = audio_params.sample_rate(); | 42 audio_config.samples_per_second = audio_params.sample_rate(); |
| 55 audio_config.extra_data = nullptr; | 43 audio_config.extra_data = nullptr; |
| 56 audio_config.extra_data_size = 0; | 44 audio_config.extra_data_size = 0; |
| 57 audio_config.is_encrypted = false; | 45 audio_config.is_encrypted = false; |
| 58 if (!audio_device->SetConfig(audio_config)) | |
| 59 return false; | |
| 60 | 46 |
| 61 if (!audio_device->SetState(AudioPipelineDevice::kStateIdle)) | 47 if (!decoder->SetConfig(audio_config)) |
| 62 return false; | 48 return nullptr; |
| 63 | 49 |
| 64 return true; | 50 if (!backend->Initialize(delegate)) |
| 51 return nullptr; | |
| 52 | |
| 53 return decoder; | |
| 65 } | 54 } |
| 55 | |
| 66 } // namespace | 56 } // namespace |
| 67 | 57 |
| 68 // Backend represents a MediaPipelineBackend adapter that runs on cast | 58 // Backend represents a MediaPipelineBackend adapter that runs on cast |
| 69 // media thread (media::MediaMessageLoop::GetTaskRunner). | 59 // media thread (media::MediaMessageLoop::GetTaskRunner). |
| 70 // It can be created and destroyed on any thread, but all other member functions | 60 // It can be created and destroyed on any thread, but all other member functions |
| 71 // must be called on a single thread. | 61 // must be called on a single thread. |
| 72 class CastAudioOutputStream::Backend { | 62 class CastAudioOutputStream::Backend : public MediaPipelineBackend::Delegate { |
| 73 public: | 63 public: |
| 74 typedef base::Callback<void(bool)> PushFrameCompletionCallback; | 64 typedef base::Callback<void(bool)> PushBufferCompletionCallback; |
| 75 | 65 |
| 76 Backend(const ::media::AudioParameters& audio_params) | 66 Backend(const ::media::AudioParameters& audio_params) |
| 77 : audio_params_(audio_params) { | 67 : audio_params_(audio_params), |
| 68 decoder_(nullptr), | |
| 69 first_start_(true), | |
| 70 error_(false) { | |
| 78 thread_checker_.DetachFromThread(); | 71 thread_checker_.DetachFromThread(); |
| 79 } | 72 } |
| 80 ~Backend() {} | 73 ~Backend() override {} |
| 81 | 74 |
| 82 void Open(CastAudioManager* audio_manager, | 75 void Open(CastAudioManager* audio_manager, |
| 83 bool* success, | 76 bool* success, |
|
slan
2015/10/06 19:33:59
nit: DCHECK all raw input
kmackay
2015/10/06 21:44:55
Done.
| |
| 84 base::WaitableEvent* completion_event) { | 77 base::WaitableEvent* completion_event) { |
| 85 DCHECK(thread_checker_.CalledOnValidThread()); | 78 DCHECK(thread_checker_.CalledOnValidThread()); |
| 86 DCHECK(backend_ == nullptr); | 79 DCHECK(backend_ == nullptr); |
| 87 | 80 |
| 88 scoped_ptr<MediaPipelineBackend> pipeline_backend = | 81 backend_ = audio_manager->CreateMediaPipelineBackend(); |
|
slan
2015/10/06 19:34:00
Can't an AudioOuput stream be created and opened a
kmackay
2015/10/06 21:44:55
A whole new backend is created for each stream.
slan
2015/10/06 22:15:05
Duh, thank you.
| |
| 89 audio_manager->CreateMediaPipelineBackend(); | 82 if (backend_) |
| 90 if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && | 83 decoder_ = InitializeBackend(audio_params_, backend_.get(), this); |
| 91 InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) { | 84 *success = decoder_ != nullptr; |
| 92 backend_ = pipeline_backend.Pass(); | |
| 93 } | |
| 94 *success = backend_ != nullptr; | |
| 95 completion_event->Signal(); | 85 completion_event->Signal(); |
| 96 } | 86 } |
| 97 | 87 |
| 98 void Close() { | 88 void Close() { |
| 99 DCHECK(thread_checker_.CalledOnValidThread()); | 89 DCHECK(thread_checker_.CalledOnValidThread()); |
| 100 | 90 |
| 101 if (backend_) { | 91 if (backend_) |
| 102 backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); | 92 backend_->Stop(); |
| 103 backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle); | |
| 104 } | |
| 105 backend_.reset(); | 93 backend_.reset(); |
| 106 } | 94 } |
| 107 | 95 |
| 108 void Start() { | 96 void Start() { |
| 109 DCHECK(thread_checker_.CalledOnValidThread()); | 97 DCHECK(thread_checker_.CalledOnValidThread()); |
| 110 | 98 |
| 111 MediaClockDevice* clock_device = backend_->GetClock(); | 99 if (first_start_) |
| 112 clock_device->SetState(MediaClockDevice::kStateRunning); | 100 backend_->Start(0); |
|
slan
2015/10/06 19:34:00
DCHECK backend_ before use to be defensive. Here a
kmackay
2015/10/06 21:44:55
Done.
| |
| 113 clock_device->SetRate(1.0f); | 101 else |
| 114 | 102 backend_->Resume(); |
| 115 AudioPipelineDevice* audio_device = backend_->GetAudio(); | |
| 116 audio_device->SetState(AudioPipelineDevice::kStateRunning); | |
| 117 } | 103 } |
| 118 | 104 |
| 119 void Stop() { | 105 void Stop() { |
| 120 DCHECK(thread_checker_.CalledOnValidThread()); | 106 DCHECK(thread_checker_.CalledOnValidThread()); |
| 121 | 107 |
| 122 MediaClockDevice* clock_device = backend_->GetClock(); | 108 backend_->Pause(); |
|
slan
2015/10/06 19:34:00
DCHECK backend_ before use
kmackay
2015/10/06 21:44:55
Done.
| |
| 123 clock_device->SetRate(0.0f); | 109 first_start_ = false; |
| 124 } | 110 } |
| 125 | 111 |
| 126 void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, | 112 void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
| 127 const PushFrameCompletionCallback& completion_cb) { | 113 const PushBufferCompletionCallback& completion_cb) { |
| 128 DCHECK(thread_checker_.CalledOnValidThread()); | 114 DCHECK(thread_checker_.CalledOnValidThread()); |
| 115 DCHECK(decoder_); | |
| 116 DCHECK(completion_cb_.is_null()); | |
| 117 if (error_) { | |
| 118 completion_cb.Run(false); | |
| 119 return; | |
| 120 } | |
| 129 | 121 |
| 130 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 122 if (backend_buffer_) |
| 131 MediaComponentDevice::FrameStatus status = | 123 backend_buffer_->set_buffer(decoder_buffer); |
| 132 audio_device->PushFrame(nullptr, // decrypt_context | 124 else |
| 133 new CastDecoderBufferImpl(decoder_buffer), | 125 backend_buffer_.reset(new CastDecoderBufferImpl(decoder_buffer)); |
| 134 new media::FrameStatusCBImpl(base::Bind( | |
| 135 &Backend::OnPushFrameStatus, | |
| 136 base::Unretained(this), completion_cb))); | |
| 137 | 126 |
| 138 if (status != MediaComponentDevice::kFramePending) | 127 MediaPipelineBackend::BufferStatus status = |
| 139 OnPushFrameStatus(completion_cb, status); | 128 decoder_->PushBuffer(nullptr, backend_buffer_.get()); |
| 129 completion_cb_ = completion_cb; | |
| 130 if (status != MediaPipelineBackend::kBufferPending) | |
| 131 OnPushBufferComplete(decoder_, status); | |
| 140 } | 132 } |
| 141 | 133 |
| 142 void SetVolume(double volume) { | 134 void SetVolume(double volume) { |
| 143 DCHECK(thread_checker_.CalledOnValidThread()); | 135 DCHECK(thread_checker_.CalledOnValidThread()); |
| 136 decoder_->SetVolume(volume); | |
|
slan
2015/10/06 19:34:00
DCHECK(decoder_) before use
kmackay
2015/10/06 21:44:55
Done.
| |
| 137 } | |
| 144 | 138 |
| 145 AudioPipelineDevice* audio_device = backend_->GetAudio(); | 139 // MediaPipelineBackend::Delegate implementation |
| 146 audio_device->SetStreamVolumeMultiplier(volume); | 140 void OnVideoResolutionChanged(MediaPipelineBackend::Decoder* decoder, |
| 141 const Size& size) override {} | |
| 142 | |
| 143 void OnPushBufferComplete( | |
| 144 MediaPipelineBackend::Decoder* decoder, | |
| 145 MediaPipelineBackend::BufferStatus status) override { | |
| 146 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 147 DCHECK_NE(status, MediaPipelineBackend::kBufferPending); | |
| 148 | |
| 149 base::ResetAndReturn(&completion_cb_) | |
| 150 .Run(status == MediaPipelineBackend::kBufferSuccess); | |
| 151 } | |
| 152 | |
| 153 void OnEndOfStream(MediaPipelineBackend::Decoder* decoder) override {} | |
| 154 | |
| 155 void OnDecoderError(MediaPipelineBackend::Decoder* decoder) override { | |
| 156 error_ = true; | |
| 157 if (!completion_cb_.is_null()) | |
| 158 OnPushBufferComplete(decoder_, MediaPipelineBackend::kBufferFailed); | |
| 147 } | 159 } |
| 148 | 160 |
| 149 private: | 161 private: |
| 150 void OnPushFrameStatus(const PushFrameCompletionCallback& completion_cb, | |
| 151 MediaComponentDevice::FrameStatus status) { | |
| 152 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 153 DCHECK_NE(status, MediaComponentDevice::kFramePending); | |
| 154 | |
| 155 completion_cb.Run(status == MediaComponentDevice::kFrameSuccess); | |
| 156 } | |
| 157 | |
| 158 const ::media::AudioParameters audio_params_; | 162 const ::media::AudioParameters audio_params_; |
| 159 scoped_ptr<MediaPipelineBackend> backend_; | 163 scoped_ptr<MediaPipelineBackend> backend_; |
| 164 MediaPipelineBackend::AudioDecoder* decoder_; | |
| 165 PushBufferCompletionCallback completion_cb_; | |
| 166 bool first_start_; | |
| 167 bool error_; | |
| 168 scoped_ptr<CastDecoderBufferImpl> backend_buffer_; | |
| 160 base::ThreadChecker thread_checker_; | 169 base::ThreadChecker thread_checker_; |
| 161 DISALLOW_COPY_AND_ASSIGN(Backend); | 170 DISALLOW_COPY_AND_ASSIGN(Backend); |
| 162 }; | 171 }; |
| 163 | 172 |
| 164 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). | 173 // CastAudioOutputStream runs on audio thread (AudioManager::GetTaskRunner). |
| 165 CastAudioOutputStream::CastAudioOutputStream( | 174 CastAudioOutputStream::CastAudioOutputStream( |
| 166 const ::media::AudioParameters& audio_params, | 175 const ::media::AudioParameters& audio_params, |
| 167 CastAudioManager* audio_manager) | 176 CastAudioManager* audio_manager) |
| 168 : audio_params_(audio_params), | 177 : audio_params_(audio_params), |
| 169 audio_manager_(audio_manager), | 178 audio_manager_(audio_manager), |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 233 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 242 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| 234 DCHECK(source_callback); | 243 DCHECK(source_callback); |
| 235 | 244 |
| 236 source_callback_ = source_callback; | 245 source_callback_ = source_callback; |
| 237 backend_task_runner_->PostTask( | 246 backend_task_runner_->PostTask( |
| 238 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get()))); | 247 FROM_HERE, base::Bind(&Backend::Start, base::Unretained(backend_.get()))); |
| 239 | 248 |
| 240 next_push_time_ = base::TimeTicks::Now(); | 249 next_push_time_ = base::TimeTicks::Now(); |
| 241 if (!backend_busy_) { | 250 if (!backend_busy_) { |
| 242 audio_task_runner_->PostTask(FROM_HERE, | 251 audio_task_runner_->PostTask(FROM_HERE, |
| 243 base::Bind(&CastAudioOutputStream::PushFrame, | 252 base::Bind(&CastAudioOutputStream::PushBuffer, |
| 244 weak_factory_.GetWeakPtr())); | 253 weak_factory_.GetWeakPtr())); |
| 245 } | 254 } |
| 246 | 255 |
| 247 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); | 256 metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio(); |
| 248 } | 257 } |
| 249 | 258 |
| 250 void CastAudioOutputStream::Stop() { | 259 void CastAudioOutputStream::Stop() { |
| 251 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 260 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| 252 | 261 |
| 253 source_callback_ = nullptr; | 262 source_callback_ = nullptr; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 272 | 281 |
| 273 void CastAudioOutputStream::OnClosed() { | 282 void CastAudioOutputStream::OnClosed() { |
| 274 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 283 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| 275 | 284 |
| 276 VLOG(1) << __FUNCTION__ << " : " << this; | 285 VLOG(1) << __FUNCTION__ << " : " << this; |
| 277 // Signal to the manager that we're closed and can be removed. | 286 // Signal to the manager that we're closed and can be removed. |
| 278 // This should be the last call in the function as it deletes "this". | 287 // This should be the last call in the function as it deletes "this". |
| 279 audio_manager_->ReleaseOutputStream(this); | 288 audio_manager_->ReleaseOutputStream(this); |
| 280 } | 289 } |
| 281 | 290 |
| 282 void CastAudioOutputStream::PushFrame() { | 291 void CastAudioOutputStream::PushBuffer() { |
| 283 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 292 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| 284 DCHECK(!backend_busy_); | 293 DCHECK(!backend_busy_); |
| 285 | 294 |
| 286 if (!source_callback_) | 295 if (!source_callback_) |
| 287 return; | 296 return; |
| 288 | 297 |
| 289 uint32_t bytes_delay = 0; | 298 uint32_t bytes_delay = 0; |
| 290 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay); | 299 int frame_count = source_callback_->OnMoreData(audio_bus_.get(), bytes_delay); |
| 291 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay; | 300 VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay; |
| 292 | 301 |
| 293 DCHECK_EQ(frame_count, audio_bus_->frames()); | 302 DCHECK_EQ(frame_count, audio_bus_->frames()); |
| 294 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), | 303 DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()), |
| 295 frame_count * audio_params_.GetBytesPerFrame()); | 304 frame_count * audio_params_.GetBytesPerFrame()); |
| 296 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, | 305 audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, |
| 297 decoder_buffer_->writable_data()); | 306 decoder_buffer_->writable_data()); |
| 298 | 307 |
| 299 auto completion_cb = ::media::BindToCurrentLoop(base::Bind( | 308 auto completion_cb = ::media::BindToCurrentLoop( |
| 300 &CastAudioOutputStream::OnPushFrameComplete, weak_factory_.GetWeakPtr())); | 309 base::Bind(&CastAudioOutputStream::OnPushBufferComplete, |
| 301 backend_task_runner_->PostTask( | 310 weak_factory_.GetWeakPtr())); |
| 302 FROM_HERE, | 311 backend_task_runner_->PostTask(FROM_HERE, |
| 303 base::Bind(&Backend::PushFrame, base::Unretained(backend_.get()), | 312 base::Bind(&Backend::PushBuffer, |
| 304 decoder_buffer_, completion_cb)); | 313 base::Unretained(backend_.get()), |
| 314 decoder_buffer_, | |
| 315 completion_cb)); | |
| 305 backend_busy_ = true; | 316 backend_busy_ = true; |
| 306 } | 317 } |
| 307 | 318 |
| 308 void CastAudioOutputStream::OnPushFrameComplete(bool success) { | 319 void CastAudioOutputStream::OnPushBufferComplete(bool success) { |
| 309 DCHECK(audio_task_runner_->BelongsToCurrentThread()); | 320 DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| 310 | 321 |
| 311 backend_busy_ = false; | 322 backend_busy_ = false; |
| 312 if (!source_callback_) | 323 if (!source_callback_) |
| 313 return; | 324 return; |
| 314 | 325 |
| 315 if (!success) { | 326 if (!success) { |
| 316 source_callback_->OnError(this); | 327 source_callback_->OnError(this); |
| 317 return; | 328 return; |
| 318 } | 329 } |
| 319 | 330 |
| 320 // Schedule next push frame. | 331 // Schedule next push buffer. |
| 321 // Need to account for time spent in pulling and pushing frame as well | 332 // Need to account for time spent in pulling and pushing buffer as well |
| 322 // as the imprecision of PostDelayedTask(). | 333 // as the imprecision of PostDelayedTask(). |
| 323 const base::TimeTicks now = base::TimeTicks::Now(); | 334 const base::TimeTicks now = base::TimeTicks::Now(); |
| 324 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; | 335 base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; |
| 325 delay = std::max(delay, base::TimeDelta()); | 336 delay = std::max(delay, base::TimeDelta()); |
| 326 next_push_time_ = now + delay; | 337 next_push_time_ = now + delay; |
| 327 | 338 |
| 328 audio_task_runner_->PostDelayedTask( | 339 audio_task_runner_->PostDelayedTask( |
| 329 FROM_HERE, | 340 FROM_HERE, |
| 330 base::Bind(&CastAudioOutputStream::PushFrame, weak_factory_.GetWeakPtr()), | 341 base::Bind(&CastAudioOutputStream::PushBuffer, |
| 342 weak_factory_.GetWeakPtr()), | |
| 331 delay); | 343 delay); |
| 332 } | 344 } |
| 333 | 345 |
| 334 } // namespace media | 346 } // namespace media |
| 335 } // namespace chromecast | 347 } // namespace chromecast |
| OLD | NEW |