Chromium Code Reviews| 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..c33714d1fb153f4b2a08e1587d8f52dcc7ab1c0e |
| --- /dev/null |
| +++ b/chromecast/media/cma/filters/demuxer_stream_adapter_unittest.cc |
| @@ -0,0 +1,312 @@ |
| +// 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: |
| + // Create a demuxer stream which provides |delayed_frame_count| |
| + // frames with a delay every |frame_cycle| frames. |
| + // Special cases: |
| + // - all frames are delayed: |delayed_frame_count| = |frame_cycle| |
| + // - 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 frame_cycle, |
| + 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); |
| + |
| + const int frame_cycle_; |
| + const int delayed_frame_count_; |
| + std::list<int> config_idx_; |
| + |
| + int frame_count_; |
|
damienv1
2014/09/05 20:15:48
DISALLOW_COPY_AND_ASSIGN
damienv1
2014/09/05 21:15:57
Done.
|
| +}; |
| + |
| +DummyDemuxerStream::DummyDemuxerStream( |
| + int frame_cycle, |
| + int delayed_frame_count, |
| + const std::list<int>& config_idx) |
| + : frame_cycle_(frame_cycle), |
| + delayed_frame_count_(delayed_frame_count), |
| + frame_count_(0), |
| + config_idx_(config_idx) { |
| +} |
| + |
| +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_ % frame_cycle_) < 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(); |
| + |
| + // 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_; |
|
damienv1
2014/09/05 20:15:48
DISALLOW_COPY_AND_ASSIGN
damienv1
2014/09/05 21:15:57
Done.
|
| +}; |
| + |
| +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 frame_cycle = 1; |
| + int delayed_frame_count = 0; |
| + scoped_ptr<DummyDemuxerStream> demuxer_stream( |
| + new DummyDemuxerStream( |
| + frame_cycle, 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 frame_cycle = 1; |
| + int delayed_frame_count = 1; |
| + scoped_ptr<DummyDemuxerStream> demuxer_stream( |
| + new DummyDemuxerStream( |
| + frame_cycle, 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 frame_cycle = 1; |
| + int delayed_frame_count = 1; |
| + scoped_ptr<DummyDemuxerStream> demuxer_stream( |
| + new DummyDemuxerStream( |
| + frame_cycle, 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 |