| 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 bb7a54efce7dcd5d35c9ba0bbe09f6d09fe7c3dc..b9efc8963beb9216b5f31f43fd2b46ea18002626 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"
|
| @@ -12,39 +13,26 @@
|
| #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 "media/base/bind_to_current_loop.h"
|
| #include "media/base/decoder_buffer.h"
|
|
|
| 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;
|
| @@ -55,108 +43,135 @@ 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);
|
| -
|
| - scoped_ptr<MediaPipelineBackend> pipeline_backend =
|
| - audio_manager->CreateMediaPipelineBackend();
|
| - if (pipeline_backend && InitClockDevice(pipeline_backend->GetClock()) &&
|
| - InitAudioDevice(audio_params_, pipeline_backend->GetAudio())) {
|
| - backend_ = pipeline_backend.Pass();
|
| - }
|
| - *success = backend_ != nullptr;
|
| + DCHECK(audio_manager);
|
| + DCHECK(success);
|
| + DCHECK(completion_event);
|
| +
|
| + backend_ = audio_manager->CreateMediaPipelineBackend();
|
| + 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();
|
| }
|
|
|
| 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();
|
| }
|
|
|
| void Stop() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| + DCHECK(backend_);
|
|
|
| - MediaClockDevice* clock_device = backend_->GetClock();
|
| - clock_device->SetRate(0.0f);
|
| + backend_->Pause();
|
| + first_start_ = false;
|
| }
|
|
|
| - 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());
|
| + 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));
|
|
|
| - if (status != MediaComponentDevice::kFramePending)
|
| - OnPushFrameStatus(completion_cb, status);
|
| + MediaPipelineBackend::BufferStatus status =
|
| + decoder_->PushBuffer(nullptr, backend_buffer_.get());
|
| + 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_;
|
| + 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);
|
| };
|
| @@ -240,7 +255,7 @@ void CastAudioOutputStream::Start(AudioSourceCallback* source_callback) {
|
| next_push_time_ = base::TimeTicks::Now();
|
| if (!backend_busy_) {
|
| audio_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&CastAudioOutputStream::PushFrame,
|
| + base::Bind(&CastAudioOutputStream::PushBuffer,
|
| weak_factory_.GetWeakPtr()));
|
| }
|
|
|
| @@ -279,7 +294,7 @@ void CastAudioOutputStream::OnClosed() {
|
| audio_manager_->ReleaseOutputStream(this);
|
| }
|
|
|
| -void CastAudioOutputStream::PushFrame() {
|
| +void CastAudioOutputStream::PushBuffer() {
|
| DCHECK(audio_task_runner_->BelongsToCurrentThread());
|
| DCHECK(!backend_busy_);
|
|
|
| @@ -296,16 +311,18 @@ 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));
|
| backend_busy_ = true;
|
| }
|
|
|
| -void CastAudioOutputStream::OnPushFrameComplete(bool success) {
|
| +void CastAudioOutputStream::OnPushBufferComplete(bool success) {
|
| DCHECK(audio_task_runner_->BelongsToCurrentThread());
|
|
|
| backend_busy_ = false;
|
| @@ -317,8 +334,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;
|
| @@ -327,7 +344,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);
|
| }
|
|
|
|
|