blob: c075191e62f9a125b4a756fbc16be7ab35984e98 [file] [log] [blame]
// 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