Index: chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc |
diff --git a/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc b/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a5661a48ce1f04ce99a62a42f7c0c86caa485997 |
--- /dev/null |
+++ b/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc |
@@ -0,0 +1,324 @@ |
+// 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 <list> |
+ |
+#include "base/basictypes.h" |
+#include "base/bind.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/threading/thread.h" |
+#include "base/time/time.h" |
+#include "chromecast/media/cma/base/balanced_media_task_runner_factory.h" |
+#include "chromecast/media/cma/base/decoder_buffer_base.h" |
+#include "chromecast/media/cma/filters/demuxer_stream_adapter.h" |
+#include "media/base/audio_decoder_config.h" |
+#include "media/base/decoder_buffer.h" |
+#include "media/base/demuxer_stream.h" |
+#include "media/base/video_decoder_config.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace chromecast { |
+namespace media { |
+ |
+namespace { |
+ |
+class DummyDemuxerStream : public ::media::DemuxerStream { |
+ public: |
+ // Creates a demuxer stream which provides frames either with a delay |
+ // or instantly. The scheduling pattern is the following: |
+ // - provides |delayed_frame_count| frames with a delay, |
+ // - then provides the following |cycle_count| - |delayed_frame_count| |
+ // instantly, |
+ // - then provides |delayed_frame_count| frames with a delay, |
+ // - ... and so on. |
+ // Special cases: |
+ // - all frames are delayed: |delayed_frame_count| = |cycle_count| |
+ // - all frames are provided instantly: |delayed_frame_count| = 0 |
+ // |config_idx| is a list of frame index before which there is |
+ // a change of decoder configuration. |
+ DummyDemuxerStream(int cycle_count, |
+ int delayed_frame_count, |
+ const std::list<int>& config_idx); |
+ virtual ~DummyDemuxerStream(); |
+ |
+ // ::media::DemuxerStream implementation. |
+ virtual void Read(const ReadCB& read_cb) OVERRIDE; |
+ virtual ::media::AudioDecoderConfig audio_decoder_config() OVERRIDE; |
+ virtual ::media::VideoDecoderConfig video_decoder_config() OVERRIDE; |
+ virtual Type type() OVERRIDE; |
+ virtual void EnableBitstreamConverter() OVERRIDE; |
+ virtual bool SupportsConfigChanges() OVERRIDE; |
+ virtual ::media::VideoRotation video_rotation() OVERRIDE; |
+ |
+ private: |
+ void DoRead(const ReadCB& read_cb); |
+ |
+ // Demuxer configuration. |
+ const int cycle_count_; |
+ const int delayed_frame_count_; |
+ std::list<int> config_idx_; |
+ |
+ // Number of frames sent so far. |
+ int frame_count_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DummyDemuxerStream); |
+}; |
+ |
+DummyDemuxerStream::DummyDemuxerStream( |
+ int cycle_count, |
+ int delayed_frame_count, |
+ const std::list<int>& config_idx) |
+ : cycle_count_(cycle_count), |
+ delayed_frame_count_(delayed_frame_count), |
+ frame_count_(0), |
+ config_idx_(config_idx) { |
+ DCHECK_LE(delayed_frame_count, cycle_count); |
+} |
+ |
+DummyDemuxerStream::~DummyDemuxerStream() { |
+} |
+ |
+void DummyDemuxerStream::Read(const ReadCB& read_cb) { |
+ if (!config_idx_.empty() && config_idx_.front() == frame_count_) { |
+ config_idx_.pop_front(); |
+ read_cb.Run(kConfigChanged, |
+ scoped_refptr< ::media::DecoderBuffer>()); |
+ return; |
+ } |
+ |
+ if ((frame_count_ % cycle_count_) < delayed_frame_count_) { |
+ base::MessageLoopProxy::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&DummyDemuxerStream::DoRead, base::Unretained(this), |
+ read_cb), |
+ base::TimeDelta::FromMilliseconds(20)); |
+ return; |
+ } |
+ DoRead(read_cb); |
+} |
+ |
+::media::AudioDecoderConfig DummyDemuxerStream::audio_decoder_config() { |
+ LOG(FATAL) << "DummyDemuxerStream is a video DemuxerStream"; |
+ return ::media::AudioDecoderConfig(); |
+} |
+ |
+::media::VideoDecoderConfig DummyDemuxerStream::video_decoder_config() { |
+ gfx::Size coded_size(640, 480); |
+ gfx::Rect visible_rect(640, 480); |
+ gfx::Size natural_size(640, 480); |
+ return ::media::VideoDecoderConfig( |
+ ::media::kCodecH264, |
+ ::media::VIDEO_CODEC_PROFILE_UNKNOWN, |
+ ::media::VideoFrame::YV12, |
+ coded_size, |
+ visible_rect, |
+ natural_size, |
+ NULL, 0, |
+ false); |
+} |
+ |
+::media::DemuxerStream::Type DummyDemuxerStream::type() { |
+ return VIDEO; |
+} |
+ |
+void DummyDemuxerStream::EnableBitstreamConverter() { |
+} |
+ |
+bool DummyDemuxerStream::SupportsConfigChanges() { |
+ return true; |
+} |
+ |
+::media::VideoRotation DummyDemuxerStream::video_rotation() { |
+ return ::media::VIDEO_ROTATION_0; |
+} |
+ |
+void DummyDemuxerStream::DoRead(const ReadCB& read_cb) { |
+ scoped_refptr< ::media::DecoderBuffer> buffer( |
+ new ::media::DecoderBuffer(16)); |
+ buffer->set_timestamp(frame_count_ * base::TimeDelta::FromMilliseconds(40)); |
+ frame_count_++; |
+ read_cb.Run(kOk, buffer); |
+} |
+ |
+} // namespace |
+ |
+class DemuxerStreamAdapterTest : public testing::Test { |
+ public: |
+ DemuxerStreamAdapterTest(); |
+ virtual ~DemuxerStreamAdapterTest(); |
+ |
+ void Initialize(::media::DemuxerStream* demuxer_stream); |
+ void Start(); |
+ |
+ protected: |
+ void OnTestTimeout(); |
+ void OnNewFrame(const scoped_refptr<DecoderBufferBase>& buffer, |
+ const ::media::AudioDecoderConfig& audio_config, |
+ const ::media::VideoDecoderConfig& video_config); |
+ void OnFlushCompleted(); |
+ |
+ // Total number of frames to request. |
+ int total_frames_; |
+ |
+ // Number of demuxer read before issuing an early flush. |
+ int early_flush_idx_; |
+ |
+ // Number of expected read frames. |
+ int total_expected_frames_; |
+ |
+ // Number of frames actually read so far. |
+ int frame_received_count_; |
+ |
+ // List of expected frame indices with decoder config changes. |
+ std::list<int> config_idx_; |
+ |
+ scoped_ptr<CodedFrameProvider> coded_frame_provider_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DemuxerStreamAdapterTest); |
+}; |
+ |
+DemuxerStreamAdapterTest::DemuxerStreamAdapterTest() { |
+} |
+ |
+DemuxerStreamAdapterTest::~DemuxerStreamAdapterTest() { |
+} |
+ |
+void DemuxerStreamAdapterTest::Initialize( |
+ ::media::DemuxerStream* demuxer_stream) { |
+ coded_frame_provider_.reset( |
+ new DemuxerStreamAdapter( |
+ base::MessageLoopProxy::current(), |
+ scoped_refptr<BalancedMediaTaskRunnerFactory>(), |
+ demuxer_stream)); |
+} |
+ |
+void DemuxerStreamAdapterTest::Start() { |
+ frame_received_count_ = 0; |
+ |
+ // TODO(damienv): currently, test assertions which fail do not trigger the |
+ // exit of the unit test, the message loop is still running. Find a different |
+ // way to exit the unit test. |
+ base::MessageLoop::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&DemuxerStreamAdapterTest::OnTestTimeout, |
+ base::Unretained(this)), |
+ base::TimeDelta::FromSeconds(5)); |
+ |
+ coded_frame_provider_->Read( |
+ base::Bind(&DemuxerStreamAdapterTest::OnNewFrame, |
+ base::Unretained(this))); |
+} |
+ |
+void DemuxerStreamAdapterTest::OnTestTimeout() { |
+ ADD_FAILURE() << "Test timed out"; |
+ if (base::MessageLoop::current()) |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+} |
+ |
+void DemuxerStreamAdapterTest::OnNewFrame( |
+ const scoped_refptr<DecoderBufferBase>& buffer, |
+ const ::media::AudioDecoderConfig& audio_config, |
+ const ::media::VideoDecoderConfig& video_config) { |
+ if (video_config.IsValidConfig()) { |
+ ASSERT_GT(config_idx_.size(), 0); |
+ ASSERT_EQ(frame_received_count_, config_idx_.front()); |
+ config_idx_.pop_front(); |
+ } |
+ |
+ ASSERT_TRUE(buffer.get() != NULL); |
+ ASSERT_EQ(buffer->timestamp(), |
+ frame_received_count_ * base::TimeDelta::FromMilliseconds(40)); |
+ frame_received_count_++; |
+ |
+ if (frame_received_count_ >= total_frames_) { |
+ coded_frame_provider_->Flush( |
+ base::Bind(&DemuxerStreamAdapterTest::OnFlushCompleted, |
+ base::Unretained(this))); |
+ return; |
+ } |
+ |
+ coded_frame_provider_->Read( |
+ base::Bind(&DemuxerStreamAdapterTest::OnNewFrame, |
+ base::Unretained(this))); |
+ |
+ ASSERT_LE(frame_received_count_, early_flush_idx_); |
+ if (frame_received_count_ == early_flush_idx_) { |
+ coded_frame_provider_->Flush( |
+ base::Bind(&DemuxerStreamAdapterTest::OnFlushCompleted, |
+ base::Unretained(this))); |
+ return; |
+ } |
+} |
+ |
+void DemuxerStreamAdapterTest::OnFlushCompleted() { |
+ ASSERT_EQ(frame_received_count_, total_expected_frames_); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+} |
+ |
+TEST_F(DemuxerStreamAdapterTest, NoDelay) { |
+ total_frames_ = 10; |
+ early_flush_idx_ = total_frames_; // No early flush. |
+ total_expected_frames_ = 10; |
+ config_idx_.push_back(0); |
+ config_idx_.push_back(5); |
+ |
+ int cycle_count = 1; |
+ int delayed_frame_count = 0; |
+ scoped_ptr<DummyDemuxerStream> demuxer_stream( |
+ new DummyDemuxerStream( |
+ cycle_count, delayed_frame_count, config_idx_)); |
+ |
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); |
+ Initialize(demuxer_stream.get()); |
+ message_loop->PostTask( |
+ FROM_HERE, |
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this))); |
+ message_loop->Run(); |
+} |
+ |
+TEST_F(DemuxerStreamAdapterTest, AllDelayed) { |
+ total_frames_ = 10; |
+ early_flush_idx_ = total_frames_; // No early flush. |
+ total_expected_frames_ = 10; |
+ config_idx_.push_back(0); |
+ config_idx_.push_back(5); |
+ |
+ int cycle_count = 1; |
+ int delayed_frame_count = 1; |
+ scoped_ptr<DummyDemuxerStream> demuxer_stream( |
+ new DummyDemuxerStream( |
+ cycle_count, delayed_frame_count, config_idx_)); |
+ |
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); |
+ Initialize(demuxer_stream.get()); |
+ message_loop->PostTask( |
+ FROM_HERE, |
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this))); |
+ message_loop->Run(); |
+} |
+ |
+TEST_F(DemuxerStreamAdapterTest, AllDelayedEarlyFlush) { |
+ total_frames_ = 10; |
+ early_flush_idx_ = 5; |
+ total_expected_frames_ = 5; |
+ config_idx_.push_back(0); |
+ config_idx_.push_back(5); |
+ |
+ int cycle_count = 1; |
+ int delayed_frame_count = 1; |
+ scoped_ptr<DummyDemuxerStream> demuxer_stream( |
+ new DummyDemuxerStream( |
+ cycle_count, delayed_frame_count, config_idx_)); |
+ |
+ scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoop()); |
+ Initialize(demuxer_stream.get()); |
+ message_loop->PostTask( |
+ FROM_HERE, |
+ base::Bind(&DemuxerStreamAdapterTest::Start, base::Unretained(this))); |
+ message_loop->Run(); |
+} |
+ |
+} // namespace media |
+} // namespace chromecast |