Index: chromecast/media/cma/backend/media_pipeline_device_fake.cc |
diff --git a/chromecast/media/cma/backend/media_pipeline_device_fake.cc b/chromecast/media/cma/backend/media_pipeline_device_fake.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c075191e62f9a125b4a756fbc16be7ab35984e98 |
--- /dev/null |
+++ b/chromecast/media/cma/backend/media_pipeline_device_fake.cc |
@@ -0,0 +1,573 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chromecast/media/cma/backend/media_pipeline_device_fake.h" |
+ |
+#include <list> |
+ |
+#include "base/bind.h" |
+#include "base/callback_helpers.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/message_loop/message_loop_proxy.h" |
+#include "chromecast/media/cma/backend/audio_pipeline_device.h" |
+#include "chromecast/media/cma/backend/media_clock_device.h" |
+#include "chromecast/media/cma/backend/media_component_device.h" |
+#include "chromecast/media/cma/backend/video_pipeline_device.h" |
+#include "chromecast/media/cma/base/decoder_buffer_base.h" |
+#include "media/base/audio_decoder_config.h" |
+#include "media/base/buffers.h" |
+#include "media/base/video_decoder_config.h" |
+ |
+namespace chromecast { |
+namespace media { |
+ |
+class MediaClockDeviceFake : public MediaClockDevice { |
+ public: |
+ MediaClockDeviceFake(); |
+ virtual ~MediaClockDeviceFake(); |
+ |
+ // MediaClockDevice implementation. |
+ virtual State GetState() const override; |
+ virtual bool SetState(State new_state) override; |
+ virtual bool ResetTimeline(base::TimeDelta time) override; |
+ virtual bool SetRate(float rate) override; |
+ virtual base::TimeDelta GetTime() override; |
+ |
+ private: |
+ State state_; |
+ |
+ // Media time sampled at STC time |stc_|. |
+ base::TimeDelta media_time_; |
+ base::TimeTicks stc_; |
+ |
+ float rate_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MediaClockDeviceFake); |
+}; |
+ |
+MediaClockDeviceFake::MediaClockDeviceFake() |
+ : state_(kStateUninitialized), |
+ media_time_(::media::kNoTimestamp()) { |
+ DetachFromThread(); |
+} |
+ |
+MediaClockDeviceFake::~MediaClockDeviceFake() { |
+} |
+ |
+MediaClockDevice::State MediaClockDeviceFake::GetState() const { |
+ DCHECK(CalledOnValidThread()); |
+ return state_; |
+} |
+ |
+bool MediaClockDeviceFake::SetState(State new_state) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!MediaClockDevice::IsValidStateTransition(state_, new_state)) |
+ return false; |
+ |
+ if (new_state == state_) |
+ return true; |
+ |
+ state_ = new_state; |
+ |
+ if (state_ == kStateRunning) { |
+ stc_ = base::TimeTicks::Now(); |
+ DCHECK(media_time_ != ::media::kNoTimestamp()); |
+ return true; |
+ } |
+ |
+ if (state_ == kStateIdle) { |
+ media_time_ = ::media::kNoTimestamp(); |
+ return true; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool MediaClockDeviceFake::ResetTimeline(base::TimeDelta time) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK_EQ(state_, kStateIdle); |
+ media_time_ = time; |
+ return true; |
+} |
+ |
+bool MediaClockDeviceFake::SetRate(float rate) { |
+ DCHECK(CalledOnValidThread()); |
+ if (state_ == kStateRunning) { |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ media_time_ = media_time_ + (now - stc_) * rate_; |
+ stc_ = now; |
+ } |
+ |
+ rate_ = rate; |
+ return true; |
+} |
+ |
+base::TimeDelta MediaClockDeviceFake::GetTime() { |
+ DCHECK(CalledOnValidThread()); |
+ if (state_ != kStateRunning) |
+ return media_time_; |
+ |
+ if (media_time_ == ::media::kNoTimestamp()) |
+ return ::media::kNoTimestamp(); |
+ |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ base::TimeDelta interpolated_media_time = |
+ media_time_ + (now - stc_) * rate_; |
+ return interpolated_media_time; |
+} |
+ |
+ |
+namespace { |
+ |
+// Maximum number of frames that can be buffered. |
+const size_t kMaxFrameCount = 20; |
+ |
+} // namespace |
+ |
+class MediaComponentDeviceFake : public MediaComponentDevice { |
+ public: |
+ explicit MediaComponentDeviceFake(MediaClockDeviceFake* media_clock_device); |
+ virtual ~MediaComponentDeviceFake(); |
+ |
+ // MediaComponentDevice implementation. |
+ virtual void SetClient(const Client& client) override; |
+ virtual State GetState() const override; |
+ virtual bool SetState(State new_state) override; |
+ virtual bool SetStartPts(base::TimeDelta time) override; |
+ virtual FrameStatus PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) override; |
+ virtual base::TimeDelta GetRenderingTime() const override; |
+ virtual base::TimeDelta GetRenderingDelay() const override; |
+ virtual bool GetStatistics(Statistics* stats) const override; |
+ |
+ private: |
+ struct FakeDecoderBuffer { |
+ FakeDecoderBuffer(); |
+ ~FakeDecoderBuffer(); |
+ |
+ // Buffer size. |
+ size_t size; |
+ |
+ // Presentation timestamp. |
+ base::TimeDelta pts; |
+ }; |
+ |
+ void RenderTask(); |
+ |
+ MediaClockDeviceFake* const media_clock_device_; |
+ Client client_; |
+ |
+ State state_; |
+ |
+ // Indicate whether the end of stream has been received. |
+ bool is_eos_; |
+ |
+ // Media time of the last rendered audio sample. |
+ base::TimeDelta rendering_time_; |
+ |
+ // Frame decoded/rendered since the pipeline left the idle state. |
+ uint64 decoded_frame_count_; |
+ uint64 decoded_byte_count_; |
+ |
+ // List of frames not rendered yet. |
+ std::list<FakeDecoderBuffer> frames_; |
+ |
+ // Indicate whether there is a scheduled rendering task. |
+ bool scheduled_rendering_task_; |
+ |
+ // Pending frame. |
+ scoped_refptr<DecoderBufferBase> pending_buffer_; |
+ FrameStatusCB frame_pushed_cb_; |
+ |
+ base::WeakPtr<MediaComponentDeviceFake> weak_this_; |
+ base::WeakPtrFactory<MediaComponentDeviceFake> weak_factory_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MediaComponentDeviceFake); |
+}; |
+ |
+MediaComponentDeviceFake::FakeDecoderBuffer::FakeDecoderBuffer() |
+ : size(0) { |
+} |
+ |
+MediaComponentDeviceFake::FakeDecoderBuffer::~FakeDecoderBuffer() { |
+} |
+ |
+MediaComponentDeviceFake::MediaComponentDeviceFake( |
+ MediaClockDeviceFake* media_clock_device) |
+ : media_clock_device_(media_clock_device), |
+ state_(kStateUninitialized), |
+ rendering_time_(::media::kNoTimestamp()), |
+ decoded_frame_count_(0), |
+ decoded_byte_count_(0), |
+ scheduled_rendering_task_(false), |
+ weak_factory_(this) { |
+ weak_this_ = weak_factory_.GetWeakPtr(); |
+ DetachFromThread(); |
+} |
+ |
+MediaComponentDeviceFake::~MediaComponentDeviceFake() { |
+} |
+ |
+void MediaComponentDeviceFake::SetClient(const Client& client) { |
+ DCHECK(CalledOnValidThread()); |
+ client_ = client; |
+} |
+ |
+MediaComponentDevice::State MediaComponentDeviceFake::GetState() const { |
+ DCHECK(CalledOnValidThread()); |
+ return state_; |
+} |
+ |
+bool MediaComponentDeviceFake::SetState(State new_state) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!MediaComponentDevice::IsValidStateTransition(state_, new_state)) |
+ return false; |
+ state_ = new_state; |
+ |
+ if (state_ == kStateIdle) { |
+ // Back to the idle state: reset a bunch of parameters. |
+ is_eos_ = false; |
+ rendering_time_ = ::media::kNoTimestamp(); |
+ decoded_frame_count_ = 0; |
+ decoded_byte_count_ = 0; |
+ frames_.clear(); |
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>(); |
+ frame_pushed_cb_.Reset(); |
+ return true; |
+ } |
+ |
+ if (state_ == kStateRunning) { |
+ if (!scheduled_rendering_task_) { |
+ scheduled_rendering_task_ = true; |
+ base::MessageLoopProxy::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_)); |
+ } |
+ return true; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool MediaComponentDeviceFake::SetStartPts(base::TimeDelta time) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK_EQ(state_, kStateIdle); |
+ rendering_time_ = time; |
+ return true; |
+} |
+ |
+MediaComponentDevice::FrameStatus MediaComponentDeviceFake::PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(state_ == kStatePaused || state_ == kStateRunning); |
+ DCHECK(!is_eos_); |
+ DCHECK(!pending_buffer_.get()); |
+ DCHECK(buffer.get()); |
+ |
+ if (buffer->end_of_stream()) { |
+ is_eos_ = true; |
+ return kFrameSuccess; |
+ } |
+ |
+ if (frames_.size() > kMaxFrameCount) { |
+ pending_buffer_ = buffer; |
+ frame_pushed_cb_ = completion_cb; |
+ return kFramePending; |
+ } |
+ |
+ FakeDecoderBuffer fake_buffer; |
+ fake_buffer.size = buffer->data_size(); |
+ fake_buffer.pts = buffer->timestamp(); |
+ frames_.push_back(fake_buffer); |
+ return kFrameSuccess; |
+} |
+ |
+base::TimeDelta MediaComponentDeviceFake::GetRenderingTime() const { |
+ return rendering_time_; |
+} |
+ |
+base::TimeDelta MediaComponentDeviceFake::GetRenderingDelay() const { |
+ NOTIMPLEMENTED(); |
+ return ::media::kNoTimestamp(); |
+} |
+ |
+void MediaComponentDeviceFake::RenderTask() { |
+ scheduled_rendering_task_ = false; |
+ |
+ if (state_ != kStateRunning) |
+ return; |
+ |
+ base::TimeDelta media_time = media_clock_device_->GetTime(); |
+ if (media_time == ::media::kNoTimestamp()) { |
+ scheduled_rendering_task_ = true; |
+ base::MessageLoopProxy::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_), |
+ base::TimeDelta::FromMilliseconds(50)); |
+ return; |
+ } |
+ |
+ while (!frames_.empty() && frames_.front().pts <= media_time) { |
+ rendering_time_ = frames_.front().pts; |
+ decoded_frame_count_++; |
+ decoded_byte_count_ += frames_.front().size; |
+ frames_.pop_front(); |
+ if (pending_buffer_.get()) { |
+ FakeDecoderBuffer fake_buffer; |
+ fake_buffer.size = pending_buffer_->data_size(); |
+ fake_buffer.pts = pending_buffer_->timestamp(); |
+ frames_.push_back(fake_buffer); |
+ pending_buffer_ = scoped_refptr<DecoderBufferBase>(); |
+ base::ResetAndReturn(&frame_pushed_cb_).Run(kFrameSuccess); |
+ } |
+ } |
+ |
+ if (frames_.empty() && is_eos_) { |
+ if (!client_.eos_cb.is_null()) |
+ client_.eos_cb.Run(); |
+ return; |
+ } |
+ |
+ scheduled_rendering_task_ = true; |
+ base::MessageLoopProxy::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&MediaComponentDeviceFake::RenderTask, weak_this_), |
+ base::TimeDelta::FromMilliseconds(50)); |
+} |
+ |
+bool MediaComponentDeviceFake::GetStatistics(Statistics* stats) const { |
+ if (state_ != kStateRunning) |
+ return false; |
+ |
+ // Note: what is returned here is not the number of samples but the number of |
+ // frames. The value is different for audio. |
+ stats->decoded_bytes = decoded_byte_count_; |
+ stats->decoded_samples = decoded_frame_count_; |
+ stats->dropped_samples = 0; |
+ return true; |
+} |
+ |
+ |
+class AudioPipelineDeviceFake : public AudioPipelineDevice { |
+ public: |
+ explicit AudioPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); |
+ virtual ~AudioPipelineDeviceFake(); |
+ |
+ // AudioPipelineDevice implementation. |
+ virtual void SetClient(const Client& client) override; |
+ virtual State GetState() const override; |
+ virtual bool SetState(State new_state) override; |
+ virtual bool SetStartPts(base::TimeDelta time) override; |
+ virtual FrameStatus PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) override; |
+ virtual base::TimeDelta GetRenderingTime() const override; |
+ virtual base::TimeDelta GetRenderingDelay() const override; |
+ virtual bool SetConfig(const ::media::AudioDecoderConfig& config) override; |
+ virtual void SetStreamVolumeMultiplier(float multiplier) override; |
+ virtual bool GetStatistics(Statistics* stats) const override; |
+ |
+ private: |
+ scoped_ptr<MediaComponentDeviceFake> fake_pipeline_; |
+ |
+ ::media::AudioDecoderConfig config_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AudioPipelineDeviceFake); |
+}; |
+ |
+AudioPipelineDeviceFake::AudioPipelineDeviceFake( |
+ MediaClockDeviceFake* media_clock_device) |
+ : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) { |
+ DetachFromThread(); |
+} |
+ |
+AudioPipelineDeviceFake::~AudioPipelineDeviceFake() { |
+} |
+ |
+void AudioPipelineDeviceFake::SetClient(const Client& client) { |
+ fake_pipeline_->SetClient(client); |
+} |
+ |
+MediaComponentDevice::State AudioPipelineDeviceFake::GetState() const { |
+ return fake_pipeline_->GetState(); |
+} |
+ |
+bool AudioPipelineDeviceFake::SetState(State new_state) { |
+ bool success = fake_pipeline_->SetState(new_state); |
+ if (!success) |
+ return false; |
+ |
+ if (new_state == kStateIdle) { |
+ DCHECK(config_.IsValidConfig()); |
+ } |
+ if (new_state == kStateUninitialized) { |
+ config_ = ::media::AudioDecoderConfig(); |
+ } |
+ return true; |
+} |
+ |
+bool AudioPipelineDeviceFake::SetStartPts(base::TimeDelta time) { |
+ return fake_pipeline_->SetStartPts(time); |
+} |
+ |
+MediaComponentDevice::FrameStatus AudioPipelineDeviceFake::PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) { |
+ return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb); |
+} |
+ |
+base::TimeDelta AudioPipelineDeviceFake::GetRenderingTime() const { |
+ return fake_pipeline_->GetRenderingTime(); |
+} |
+ |
+base::TimeDelta AudioPipelineDeviceFake::GetRenderingDelay() const { |
+ return fake_pipeline_->GetRenderingDelay(); |
+} |
+ |
+bool AudioPipelineDeviceFake::SetConfig( |
+ const ::media::AudioDecoderConfig& config) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!config.IsValidConfig()) |
+ return false; |
+ config_ = config; |
+ return true; |
+} |
+ |
+void AudioPipelineDeviceFake::SetStreamVolumeMultiplier(float multiplier) { |
+ DCHECK(CalledOnValidThread()); |
+} |
+ |
+bool AudioPipelineDeviceFake::GetStatistics(Statistics* stats) const { |
+ return fake_pipeline_->GetStatistics(stats); |
+} |
+ |
+ |
+class VideoPipelineDeviceFake : public VideoPipelineDevice { |
+ public: |
+ explicit VideoPipelineDeviceFake(MediaClockDeviceFake* media_clock_device); |
+ virtual ~VideoPipelineDeviceFake(); |
+ |
+ // VideoPipelineDevice implementation. |
+ virtual void SetClient(const Client& client) override; |
+ virtual State GetState() const override; |
+ virtual bool SetState(State new_state) override; |
+ virtual bool SetStartPts(base::TimeDelta time) override; |
+ virtual FrameStatus PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) override; |
+ virtual base::TimeDelta GetRenderingTime() const override; |
+ virtual base::TimeDelta GetRenderingDelay() const override; |
+ virtual void SetVideoClient(const VideoClient& client) override; |
+ virtual bool SetConfig(const ::media::VideoDecoderConfig& config) override; |
+ virtual bool GetStatistics(Statistics* stats) const override; |
+ |
+ private: |
+ scoped_ptr<MediaComponentDeviceFake> fake_pipeline_; |
+ |
+ ::media::VideoDecoderConfig config_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VideoPipelineDeviceFake); |
+}; |
+ |
+VideoPipelineDeviceFake::VideoPipelineDeviceFake( |
+ MediaClockDeviceFake* media_clock_device) |
+ : fake_pipeline_(new MediaComponentDeviceFake(media_clock_device)) { |
+ DetachFromThread(); |
+} |
+ |
+VideoPipelineDeviceFake::~VideoPipelineDeviceFake() { |
+} |
+ |
+void VideoPipelineDeviceFake::SetClient(const Client& client) { |
+ fake_pipeline_->SetClient(client); |
+} |
+ |
+MediaComponentDevice::State VideoPipelineDeviceFake::GetState() const { |
+ return fake_pipeline_->GetState(); |
+} |
+ |
+bool VideoPipelineDeviceFake::SetState(State new_state) { |
+ bool success = fake_pipeline_->SetState(new_state); |
+ if (!success) |
+ return false; |
+ |
+ if (new_state == kStateIdle) { |
+ DCHECK(config_.IsValidConfig()); |
+ } |
+ if (new_state == kStateUninitialized) { |
+ config_ = ::media::VideoDecoderConfig(); |
+ } |
+ return true; |
+} |
+ |
+bool VideoPipelineDeviceFake::SetStartPts(base::TimeDelta time) { |
+ return fake_pipeline_->SetStartPts(time); |
+} |
+ |
+MediaComponentDevice::FrameStatus VideoPipelineDeviceFake::PushFrame( |
+ const scoped_refptr<DecryptContext>& decrypt_context, |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const FrameStatusCB& completion_cb) { |
+ return fake_pipeline_->PushFrame(decrypt_context, buffer, completion_cb); |
+} |
+ |
+base::TimeDelta VideoPipelineDeviceFake::GetRenderingTime() const { |
+ return fake_pipeline_->GetRenderingTime(); |
+} |
+ |
+base::TimeDelta VideoPipelineDeviceFake::GetRenderingDelay() const { |
+ return fake_pipeline_->GetRenderingDelay(); |
+} |
+ |
+void VideoPipelineDeviceFake::SetVideoClient(const VideoClient& client) { |
+} |
+ |
+bool VideoPipelineDeviceFake::SetConfig( |
+ const ::media::VideoDecoderConfig& config) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!config.IsValidConfig()) |
+ return false; |
+ config_ = config; |
+ return true; |
+} |
+ |
+bool VideoPipelineDeviceFake::GetStatistics(Statistics* stats) const { |
+ return fake_pipeline_->GetStatistics(stats); |
+} |
+ |
+ |
+MediaPipelineDeviceFake::MediaPipelineDeviceFake() |
+ : media_clock_device_(new MediaClockDeviceFake()), |
+ audio_pipeline_device_( |
+ new AudioPipelineDeviceFake(media_clock_device_.get())), |
+ video_pipeline_device_( |
+ new VideoPipelineDeviceFake(media_clock_device_.get())) { |
+} |
+ |
+MediaPipelineDeviceFake::~MediaPipelineDeviceFake() { |
+} |
+ |
+AudioPipelineDevice* MediaPipelineDeviceFake::GetAudioPipelineDevice() const { |
+ return audio_pipeline_device_.get(); |
+} |
+ |
+VideoPipelineDevice* MediaPipelineDeviceFake::GetVideoPipelineDevice() const { |
+ return video_pipeline_device_.get(); |
+} |
+ |
+MediaClockDevice* MediaPipelineDeviceFake::GetMediaClockDevice() const { |
+ return media_clock_device_.get(); |
+} |
+ |
+} // namespace media |
+} // namespace chromecast |