| Index: chromecast/media/cma/backend/media_component_device_feeder_for_test.cc
|
| diff --git a/chromecast/media/cma/backend/media_component_device_feeder_for_test.cc b/chromecast/media/cma/backend/media_component_device_feeder_for_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..87b6c0803d326761fee3283e4b3f89fa9eb67ec6
|
| --- /dev/null
|
| +++ b/chromecast/media/cma/backend/media_component_device_feeder_for_test.cc
|
| @@ -0,0 +1,209 @@
|
| +// 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_component_device_feeder_for_test.h"
|
| +
|
| +#include <list>
|
| +#include <vector>
|
| +
|
| +#include "base/basictypes.h"
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/message_loop/message_loop_proxy.h"
|
| +#include "base/time/time.h"
|
| +#include "chromecast/media/base/decrypt_context.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_pipeline_device.h"
|
| +#include "chromecast/media/cma/backend/video_pipeline_device.h"
|
| +#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
|
| +#include "chromecast/media/cma/base/decoder_buffer_base.h"
|
| +#include "chromecast/media/cma/test/frame_segmenter_for_test.h"
|
| +#include "media/base/audio_decoder_config.h"
|
| +#include "media/base/buffers.h"
|
| +#include "media/base/decoder_buffer.h"
|
| +#include "media/base/video_decoder_config.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace chromecast {
|
| +namespace media {
|
| +
|
| +MediaComponentDeviceFeeder::MediaComponentDeviceFeeder(
|
| + MediaComponentDevice *device,
|
| + MediaClockDevice *clock,
|
| + const FrameList& frames)
|
| + : media_component_device_(device),
|
| + media_clock_device_(clock),
|
| + rendering_frame_idx_(1),
|
| + clock_frame_idx_(1),
|
| + eos_cb_(),
|
| + feeding_completed_(false),
|
| + frame_count_since_last_pause_(0),
|
| + frame_count_between_pause_(0),
|
| + delayed_feed_pattern_(),
|
| + delayed_feed_pattern_idx_(0) {
|
| + // Copy pointers over, claiming ownership on buffers
|
| + frames_ = frames;
|
| + delayed_feed_pattern_.push_back(false);
|
| +}
|
| +
|
| +MediaComponentDeviceFeeder::~MediaComponentDeviceFeeder() {
|
| +}
|
| +
|
| +// Accessors
|
| +void MediaComponentDeviceFeeder::SetEosCB(base::Callback<void()> eos_cb) {
|
| + eos_cb_ = eos_cb;
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::SetFrameCountBetweenPause(int frame_count) {
|
| + ASSERT_GT(frame_count,0);
|
| + frame_count_between_pause_ = frame_count;
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::SetDelayedFeedPattern(
|
| + const std::vector<bool>& pattern) {
|
| + // Copy pattern over
|
| + delayed_feed_pattern_ = pattern;
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::Initialize() {
|
| + MediaComponentDevice::Client client;
|
| + client.eos_cb =
|
| + base::Bind(&MediaComponentDeviceFeeder::OnEos, base::Unretained(this));
|
| + media_component_device_->SetClient(client);
|
| +
|
| + bool success =
|
| + media_component_device_->SetState(MediaComponentDevice::kStateIdle);
|
| + ASSERT_TRUE(success);
|
| + success = media_component_device_->SetStartPts(base::TimeDelta());
|
| + ASSERT_TRUE(success);
|
| + success =
|
| + media_component_device_->SetState(MediaComponentDevice::kStatePaused);
|
| + ASSERT_TRUE(success);
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::Feed() {
|
| + // Start rendering if needed.
|
| + if (rendering_frame_idx_ == 0) {
|
| + media_component_device_->SetState(MediaComponentDevice::kStateRunning);
|
| + } else {
|
| + rendering_frame_idx_--;
|
| + }
|
| +
|
| + // Start the clock if needed.
|
| + if (clock_frame_idx_ == 0) {
|
| + media_clock_device_->SetState(MediaClockDevice::kStateRunning);
|
| + } else {
|
| + clock_frame_idx_--;
|
| + }
|
| +
|
| + // Possibly feed one frame
|
| + DCHECK(!frames_.empty());
|
| + scoped_refptr<DecoderBufferBase> buffer = frames_.front();
|
| +
|
| + MediaComponentDevice::FrameStatus status =
|
| + media_component_device_->PushFrame(
|
| + scoped_refptr<DecryptContext>(),
|
| + buffer,
|
| + base::Bind(&MediaComponentDeviceFeeder::OnFramePushed,
|
| + base::Unretained(this)));
|
| + EXPECT_NE(status, MediaComponentDevice::kFrameFailed);
|
| + frames_.pop_front();
|
| +
|
| + // Feeding is done, just wait for the end of stream callback.
|
| + if (buffer->end_of_stream() || frames_.empty()) {
|
| + if (frames_.empty() && !buffer->end_of_stream()) {
|
| + LOG(WARNING) << "Stream emptied without feeding EOS frame";
|
| + }
|
| +
|
| + feeding_completed_ = true;
|
| + return;
|
| + }
|
| +
|
| + if (status == MediaComponentDevice::kFramePending)
|
| + return;
|
| +
|
| + OnFramePushed(MediaComponentDevice::kFrameSuccess);
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::OnFramePushed(
|
| + MediaComponentDevice::FrameStatus status) {
|
| + EXPECT_NE(status, MediaComponentDevice::kFrameFailed);
|
| + if (feeding_completed_)
|
| + return;
|
| +
|
| + // Introduce some pauses in the playback at regular intervals.
|
| + if (frame_count_between_pause_) {
|
| + frame_count_since_last_pause_ =
|
| + (frame_count_since_last_pause_ + 1) % frame_count_between_pause_;
|
| + if (frame_count_since_last_pause_ == 0) {
|
| + Pause();
|
| + return;
|
| + }
|
| + }
|
| +
|
| + // Feeder loop.
|
| + bool delayed_feed = delayed_feed_pattern_[delayed_feed_pattern_idx_];
|
| + delayed_feed_pattern_idx_ =
|
| + (delayed_feed_pattern_idx_ + 1) % delayed_feed_pattern_.size();
|
| +
|
| + if (delayed_feed) {
|
| + base::MessageLoopProxy::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&MediaComponentDeviceFeeder::Feed,
|
| + base::Unretained(this)),
|
| + base::TimeDelta::FromMilliseconds(50));
|
| + } else {
|
| + base::MessageLoopProxy::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&MediaComponentDeviceFeeder::Feed,
|
| + base::Unretained(this)));
|
| + }
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::Pause() {
|
| + media_clock_device_->SetRate(0.0);
|
| + media_time_ = media_clock_device_->GetTime();
|
| + base::MessageLoopProxy::current()->PostDelayedTask(
|
| + FROM_HERE,
|
| + base::Bind(&MediaComponentDeviceFeeder::OnPauseCompleted,
|
| + base::Unretained(this)),
|
| + base::TimeDelta::FromMilliseconds(500));
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::OnPauseCompleted() {
|
| + // Make sure the media time didn't move during that time.
|
| + base::TimeDelta media_time = media_clock_device_->GetTime();
|
| +
|
| + // TODO(gunsch): this should be EXPECT_EQ(media_time, media_time_);
|
| + // However, some backends may move the time forward when rendering the first
|
| + // frame while in paused mode. This behaviour is not intended.
|
| + EXPECT_GE(media_time, media_time_);
|
| + EXPECT_LE(media_time, media_time_ + base::TimeDelta::FromMilliseconds(50));
|
| +
|
| + // Resume playback and frame feeding.
|
| + media_clock_device_->SetRate(1.0);
|
| +
|
| + Feed();
|
| +}
|
| +
|
| +void MediaComponentDeviceFeeder::OnEos() {
|
| + // Shutdown the pipeline.
|
| + bool success = media_component_device_->SetState(
|
| + MediaComponentDevice::kStateIdle);
|
| + ASSERT_TRUE(success);
|
| + success = media_component_device_->SetState(
|
| + MediaComponentDevice::kStateUninitialized);
|
| + ASSERT_TRUE(success);
|
| +
|
| + if(!eos_cb_.is_null()) {
|
| + eos_cb_.Run();
|
| + }
|
| +}
|
| +
|
| +} // namespace media
|
| +} // namespace chromecast
|
|
|