Chromium Code Reviews| Index: chromecast/media/audio/cast_audio_output_stream.cc |
| diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc |
| index 5f0645ec82c93764a5e641e77e10b2c9ba16e011..880143691e20057daad78502ab4e6c41a9128a28 100644 |
| --- a/chromecast/media/audio/cast_audio_output_stream.cc |
| +++ b/chromecast/media/audio/cast_audio_output_stream.cc |
| @@ -5,6 +5,7 @@ |
| #include "chromecast/media/audio/cast_audio_output_stream.h" |
| #include "base/bind.h" |
| +#include "base/callback_helpers.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread_checker.h" |
| #include "chromecast/base/metrics/cast_metrics_helper.h" |
| @@ -13,11 +14,8 @@ |
| #include "chromecast/media/base/media_message_loop.h" |
| #include "chromecast/media/cma/base/cast_decoder_buffer_impl.h" |
| #include "chromecast/media/cma/base/decoder_buffer_adapter.h" |
| -#include "chromecast/media/cma/pipeline/frame_status_cb_impl.h" |
| -#include "chromecast/public/media/audio_pipeline_device.h" |
| #include "chromecast/public/media/decoder_config.h" |
| #include "chromecast/public/media/decrypt_context.h" |
| -#include "chromecast/public/media/media_clock_device.h" |
| #include "chromecast/public/media/media_pipeline_backend.h" |
| #include "chromecast/public/media/media_pipeline_device_params.h" |
| #include "media/base/bind_to_current_loop.h" |
| @@ -25,28 +23,18 @@ |
| namespace chromecast { |
| namespace media { |
| - |
| namespace { |
| -bool InitClockDevice(MediaClockDevice* clock_device) { |
| - DCHECK(clock_device); |
| - DCHECK_EQ(clock_device->GetState(), MediaClockDevice::kStateUninitialized); |
| - |
| - if (!clock_device->SetState(media::MediaClockDevice::kStateIdle)) |
| - return false; |
| - |
| - if (!clock_device->ResetTimeline(0)) |
| - return false; |
| - |
| - if (!clock_device->SetRate(1.0)) |
| - return false; |
| - return true; |
| -} |
| +MediaPipelineBackend::AudioDecoder* InitializeBackend( |
| + const ::media::AudioParameters& audio_params, |
| + MediaPipelineBackend* backend, |
| + MediaPipelineBackend::Delegate* delegate) { |
| + DCHECK(backend); |
| + DCHECK(delegate); |
| -bool InitAudioDevice(const ::media::AudioParameters& audio_params, |
| - AudioPipelineDevice* audio_device) { |
| - DCHECK(audio_device); |
| - DCHECK_EQ(audio_device->GetState(), AudioPipelineDevice::kStateUninitialized); |
| + MediaPipelineBackend::AudioDecoder* decoder = backend->CreateAudioDecoder(); |
| + if (!decoder) |
| + return nullptr; |
| AudioConfig audio_config; |
| audio_config.codec = kCodecPCM; |
| @@ -57,114 +45,140 @@ bool InitAudioDevice(const ::media::AudioParameters& audio_params, |
| audio_config.extra_data = nullptr; |
| audio_config.extra_data_size = 0; |
| audio_config.is_encrypted = false; |
| - if (!audio_device->SetConfig(audio_config)) |
| - return false; |
| - if (!audio_device->SetState(AudioPipelineDevice::kStateIdle)) |
| - return false; |
| + if (!decoder->SetConfig(audio_config)) |
| + return nullptr; |
| - return true; |
| + if (!backend->Initialize(delegate)) |
| + return nullptr; |
| + |
| + return decoder; |
| } |
| + |
| } // namespace |
| // Backend represents a MediaPipelineBackend adapter that runs on cast |
| // media thread (media::MediaMessageLoop::GetTaskRunner). |
| // It can be created and destroyed on any thread, but all other member functions |
| // must be called on a single thread. |
| -class CastAudioOutputStream::Backend { |
| +class CastAudioOutputStream::Backend : public MediaPipelineBackend::Delegate { |
| public: |
| - typedef base::Callback<void(bool)> PushFrameCompletionCallback; |
| + typedef base::Callback<void(bool)> PushBufferCompletionCallback; |
| Backend(const ::media::AudioParameters& audio_params) |
| - : audio_params_(audio_params) { |
| + : audio_params_(audio_params), |
| + decoder_(nullptr), |
| + first_start_(true), |
| + error_(false) { |
| thread_checker_.DetachFromThread(); |
| } |
| - ~Backend() {} |
| + ~Backend() override {} |
| void Open(CastAudioManager* audio_manager, |
| bool* success, |
| base::WaitableEvent* completion_event) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(backend_ == nullptr); |
| + DCHECK(audio_manager); |
| + DCHECK(success); |
| + DCHECK(completion_event); |
| backend_task_runner_.reset(new TaskRunnerImpl()); |
| MediaPipelineDeviceParams device_params( |
| MediaPipelineDeviceParams::kModeIgnorePts, backend_task_runner_.get()); |
| - |
| - scoped_ptr<MediaPipelineBackend> pipeline_backend = |
| - audio_manager->CreateMediaPipelineBackend(device_params); |
| - if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) && |
| - InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) { |
| - backend_ = pipeline_backend.Pass(); |
| - } |
| - *success = backend_ != nullptr; |
| + backend_ = audio_manager->CreateMediaPipelineBackend(device_params); |
| + if (backend_) |
| + decoder_ = InitializeBackend(audio_params_, backend_.get(), this); |
| + *success = decoder_ != nullptr; |
| completion_event->Signal(); |
| } |
| void Close() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - if (backend_) { |
| - backend_->GetClock()->SetState(MediaClockDevice::kStateIdle); |
| - backend_->GetAudio()->SetState(AudioPipelineDevice::kStateIdle); |
| - } |
| + if (backend_) |
| + backend_->Stop(); |
| backend_.reset(); |
| backend_task_runner_.reset(); |
| } |
| void Start() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(backend_); |
| - MediaClockDevice* clock_device = backend_->GetClock(); |
| - clock_device->SetState(MediaClockDevice::kStateRunning); |
| - clock_device->SetRate(1.0f); |
| - |
| - AudioPipelineDevice* audio_device = backend_->GetAudio(); |
| - audio_device->SetState(AudioPipelineDevice::kStateRunning); |
| + if (first_start_) |
| + backend_->Start(0); |
| + else |
| + backend_->Resume(); |
|
alokp
2015/10/14 23:16:52
Is it legal to call Start on a backend in error st
kmackay
2015/10/15 00:35:53
I don't see how the backend could be in an error s
|
| } |
| void Stop() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(backend_); |
| - MediaClockDevice* clock_device = backend_->GetClock(); |
| - clock_device->SetRate(0.0f); |
| + backend_->Pause(); |
| + first_start_ = false; |
|
alokp
2015/10/14 23:16:52
move this to Start?
kmackay
2015/10/15 00:35:53
Done.
|
| } |
| - void PushFrame(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
| - const PushFrameCompletionCallback& completion_cb) { |
| + void PushBuffer(scoped_refptr<media::DecoderBufferBase> decoder_buffer, |
| + const PushBufferCompletionCallback& completion_cb) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(decoder_); |
| + DCHECK(completion_cb_.is_null()); |
|
alokp
2015/10/14 23:16:52
Shouldn't this be !is_null?
kmackay
2015/10/15 00:35:53
No, this is checking the member variable to make s
|
| + if (error_) { |
| + completion_cb.Run(false); |
| + return; |
| + } |
| - AudioPipelineDevice* audio_device = backend_->GetAudio(); |
| - MediaComponentDevice::FrameStatus status = |
| - audio_device->PushFrame(nullptr, // decrypt_context |
| - new CastDecoderBufferImpl(decoder_buffer), |
| - new media::FrameStatusCBImpl(base::Bind( |
| - &Backend::OnPushFrameStatus, |
| - base::Unretained(this), completion_cb))); |
| + if (backend_buffer_) |
| + backend_buffer_->set_buffer(decoder_buffer); |
| + else |
| + backend_buffer_.reset(new CastDecoderBufferImpl(decoder_buffer)); |
|
halliwell
2015/10/14 15:02:37
This would be slightly cleaner if we added a no-ar
kmackay
2015/10/14 21:27:26
Done.
|
| - if (status != MediaComponentDevice::kFramePending) |
| - OnPushFrameStatus(completion_cb, status); |
| + MediaPipelineBackend::BufferStatus status = |
| + decoder_->PushBuffer(nullptr, backend_buffer_.get()); |
|
alokp
2015/10/14 23:16:52
either document what nullptr is or use a variable
kmackay
2015/10/15 00:35:53
Done.
|
| + completion_cb_ = completion_cb; |
| + if (status != MediaPipelineBackend::kBufferPending) |
| + OnPushBufferComplete(decoder_, status); |
| } |
| void SetVolume(double volume) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - |
| - AudioPipelineDevice* audio_device = backend_->GetAudio(); |
| - audio_device->SetStreamVolumeMultiplier(volume); |
| + DCHECK(decoder_); |
| + decoder_->SetVolume(volume); |
| } |
| - private: |
| - void OnPushFrameStatus(const PushFrameCompletionCallback& completion_cb, |
| - MediaComponentDevice::FrameStatus status) { |
| + // MediaPipelineBackend::Delegate implementation |
| + void OnVideoResolutionChanged(MediaPipelineBackend::VideoDecoder* decoder, |
| + const Size& size) override {} |
| + |
| + void OnPushBufferComplete( |
| + MediaPipelineBackend::Decoder* decoder, |
| + MediaPipelineBackend::BufferStatus status) override { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| - DCHECK_NE(status, MediaComponentDevice::kFramePending); |
| + DCHECK_NE(status, MediaPipelineBackend::kBufferPending); |
| + |
| + base::ResetAndReturn(&completion_cb_) |
| + .Run(status == MediaPipelineBackend::kBufferSuccess); |
| + } |
| + |
| + void OnEndOfStream(MediaPipelineBackend::Decoder* decoder) override {} |
| - completion_cb.Run(status == MediaComponentDevice::kFrameSuccess); |
| + void OnDecoderError(MediaPipelineBackend::Decoder* decoder) override { |
| + error_ = true; |
| + if (!completion_cb_.is_null()) |
| + OnPushBufferComplete(decoder_, MediaPipelineBackend::kBufferFailed); |
| } |
| + private: |
| const ::media::AudioParameters audio_params_; |
| scoped_ptr<MediaPipelineBackend> backend_; |
| scoped_ptr<TaskRunnerImpl> backend_task_runner_; |
| + MediaPipelineBackend::AudioDecoder* decoder_; |
| + PushBufferCompletionCallback completion_cb_; |
| + bool first_start_; |
| + bool error_; |
| + scoped_ptr<CastDecoderBufferImpl> backend_buffer_; |
| base::ThreadChecker thread_checker_; |
| DISALLOW_COPY_AND_ASSIGN(Backend); |
| }; |
| @@ -248,7 +262,7 @@ void CastAudioOutputStream::Start(AudioSourceCallback* source_callback) { |
| next_push_time_ = base::TimeTicks::Now(); |
| if (!push_in_progress_) { |
| audio_task_runner_->PostTask(FROM_HERE, |
| - base::Bind(&CastAudioOutputStream::PushFrame, |
| + base::Bind(&CastAudioOutputStream::PushBuffer, |
| weak_factory_.GetWeakPtr())); |
| push_in_progress_ = true; |
| } |
| @@ -288,7 +302,7 @@ void CastAudioOutputStream::OnClosed() { |
| audio_manager_->ReleaseOutputStream(this); |
| } |
| -void CastAudioOutputStream::PushFrame() { |
| +void CastAudioOutputStream::PushBuffer() { |
| DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| DCHECK(push_in_progress_); |
| @@ -307,15 +321,17 @@ void CastAudioOutputStream::PushFrame() { |
| audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, |
| decoder_buffer_->writable_data()); |
| - auto completion_cb = ::media::BindToCurrentLoop(base::Bind( |
| - &CastAudioOutputStream::OnPushFrameComplete, weak_factory_.GetWeakPtr())); |
| - backend_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&Backend::PushFrame, base::Unretained(backend_.get()), |
| - decoder_buffer_, completion_cb)); |
| + auto completion_cb = ::media::BindToCurrentLoop( |
| + base::Bind(&CastAudioOutputStream::OnPushBufferComplete, |
| + weak_factory_.GetWeakPtr())); |
| + backend_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&Backend::PushBuffer, |
| + base::Unretained(backend_.get()), |
| + decoder_buffer_, |
| + completion_cb)); |
| } |
| -void CastAudioOutputStream::OnPushFrameComplete(bool success) { |
| +void CastAudioOutputStream::OnPushBufferComplete(bool success) { |
| DCHECK(audio_task_runner_->BelongsToCurrentThread()); |
| DCHECK(push_in_progress_); |
| @@ -329,8 +345,8 @@ void CastAudioOutputStream::OnPushFrameComplete(bool success) { |
| return; |
| } |
| - // Schedule next push frame. |
| - // Need to account for time spent in pulling and pushing frame as well |
| + // Schedule next push buffer. |
| + // Need to account for time spent in pulling and pushing buffer as well |
| // as the imprecision of PostDelayedTask(). |
| const base::TimeTicks now = base::TimeTicks::Now(); |
| base::TimeDelta delay = next_push_time_ + buffer_duration_ - now; |
| @@ -339,7 +355,8 @@ void CastAudioOutputStream::OnPushFrameComplete(bool success) { |
| audio_task_runner_->PostDelayedTask( |
| FROM_HERE, |
| - base::Bind(&CastAudioOutputStream::PushFrame, weak_factory_.GetWeakPtr()), |
| + base::Bind(&CastAudioOutputStream::PushBuffer, |
| + weak_factory_.GetWeakPtr()), |
| delay); |
| push_in_progress_ = true; |
| } |