diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-08 22:58:24 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-08 22:58:24 +0000 |
commit | ea76ed7addcdb14ec0e57fc3e5172bafe0f1375c (patch) | |
tree | 953794d08b2aedffb2c57d13350493659d491c3c /media | |
parent | eb35ffb7c6bbd725ad395cce3f922e5e89060914 (diff) | |
download | chromium_src-ea76ed7addcdb14ec0e57fc3e5172bafe0f1375c.zip chromium_src-ea76ed7addcdb14ec0e57fc3e5172bafe0f1375c.tar.gz chromium_src-ea76ed7addcdb14ec0e57fc3e5172bafe0f1375c.tar.bz2 |
Reland "Add FakeDemuxerStream."
FakeVideoDecoder will be added later. Hopefully these will make tests in
VideoFrameStream cleaner.
TBR=scherkus@chromium.org
BUG=141788
Review URL: https://codereview.chromium.org/14837007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@199037 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/fake_demuxer_stream.cc | 149 | ||||
-rw-r--r-- | media/filters/fake_demuxer_stream.h | 78 | ||||
-rw-r--r-- | media/filters/fake_demuxer_stream_unittest.cc | 235 | ||||
-rw-r--r-- | media/media.gyp | 3 |
4 files changed, 465 insertions, 0 deletions
diff --git a/media/filters/fake_demuxer_stream.cc b/media/filters/fake_demuxer_stream.cc new file mode 100644 index 0000000..3cc7fa6 --- /dev/null +++ b/media/filters/fake_demuxer_stream.cc @@ -0,0 +1,149 @@ +// Copyright (c) 2013 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 "media/filters/fake_demuxer_stream.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/pickle.h" +#include "media/base/bind_to_loop.h" +#include "media/base/decoder_buffer.h" +#include "media/base/video_frame.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" + +namespace media { + +static const int kStartTimestampMs = 0; +static const int kDurationMs = 30; +static const int kStartWidth = 320; +static const int kStartHeight = 240; +static const int kWidthDelta = 4; +static const int kHeightDelta = 3; +static const char kFakeBufferHeader[] = "Fake Buffer"; + +FakeDemuxerStream::FakeDemuxerStream(int num_configs, + int num_buffers_in_one_config, + bool is_encrypted) + : message_loop_(base::MessageLoopProxy::current()), + num_configs_left_(num_configs), + num_buffers_in_one_config_(num_buffers_in_one_config), + is_encrypted_(is_encrypted), + num_buffers_left_in_current_config_(num_buffers_in_one_config), + current_timestamp_(base::TimeDelta::FromMilliseconds(kStartTimestampMs)), + duration_(base::TimeDelta::FromMilliseconds(kDurationMs)), + next_coded_size_(kStartWidth, kStartHeight), + hold_next_read_(false) { + DCHECK_GT(num_configs_left_, 0); + DCHECK_GT(num_buffers_in_one_config_, 0); + UpdateVideoDecoderConfig(); +} + +FakeDemuxerStream::~FakeDemuxerStream() {} + +void FakeDemuxerStream::Read(const ReadCB& read_cb) { + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(read_cb_.is_null()); + + read_cb_ = BindToCurrentLoop(read_cb); + + if (!hold_next_read_) + DoRead(); +} + +const AudioDecoderConfig& FakeDemuxerStream::audio_decoder_config() { + DCHECK(message_loop_->BelongsToCurrentThread()); + NOTREACHED(); + return audio_decoder_config_; +} + +const VideoDecoderConfig& FakeDemuxerStream::video_decoder_config() { + DCHECK(message_loop_->BelongsToCurrentThread()); + return video_decoder_config_; +} + +// TODO(xhwang): Support audio if needed. +DemuxerStream::Type FakeDemuxerStream::type() { + DCHECK(message_loop_->BelongsToCurrentThread()); + return VIDEO; +} + +void FakeDemuxerStream::EnableBitstreamConverter() { + DCHECK(message_loop_->BelongsToCurrentThread()); +} + +void FakeDemuxerStream::HoldNextRead() { + DCHECK(message_loop_->BelongsToCurrentThread()); + hold_next_read_ = true; +} + +void FakeDemuxerStream::SatisfyRead() { + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(hold_next_read_); + DCHECK(!read_cb_.is_null()); + + DoRead(); +} + +void FakeDemuxerStream::Reset() { + hold_next_read_ = false; + + if (!read_cb_.is_null()) + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); +} + +void FakeDemuxerStream::UpdateVideoDecoderConfig() { + const gfx::Rect kVisibleRect(kStartWidth, kStartHeight); + video_decoder_config_.Initialize( + kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, VideoFrame::YV12, + next_coded_size_, kVisibleRect, next_coded_size_, + NULL, 0, is_encrypted_, false); + next_coded_size_.Enlarge(kWidthDelta, kHeightDelta); +} + +void FakeDemuxerStream::DoRead() { + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(!read_cb_.is_null()); + + if (num_buffers_left_in_current_config_ == 0) { + // End of stream. + if (num_configs_left_ == 0) { + base::ResetAndReturn(&read_cb_).Run(kOk, + DecoderBuffer::CreateEOSBuffer()); + return; + } + + // Config change. + num_buffers_left_in_current_config_ = num_buffers_in_one_config_; + UpdateVideoDecoderConfig(); + base::ResetAndReturn(&read_cb_).Run(kConfigChanged, NULL); + return; + } + + // Prepare the next buffer. + Pickle pickle; + pickle.WriteString(kFakeBufferHeader); + pickle.WriteInt(video_decoder_config_.coded_size().width()); + pickle.WriteInt(video_decoder_config_.coded_size().height()); + pickle.WriteInt64(current_timestamp_.InMilliseconds()); + + scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom( + static_cast<const uint8*>(pickle.data()), pickle.size()); + + // TODO(xhwang): Output out-of-order buffers if needed. + buffer->SetTimestamp(current_timestamp_); + buffer->SetDuration(duration_); + current_timestamp_ += duration_; + + num_buffers_left_in_current_config_--; + if (num_buffers_left_in_current_config_ == 0) + num_configs_left_--; + + base::ResetAndReturn(&read_cb_).Run(kOk, buffer); +} + +} // namespace media diff --git a/media/filters/fake_demuxer_stream.h b/media/filters/fake_demuxer_stream.h new file mode 100644 index 0000000..1b20ba7 --- /dev/null +++ b/media/filters/fake_demuxer_stream.h @@ -0,0 +1,78 @@ +// Copyright (c) 2013 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. + +#ifndef MEDIA_FILTERS_FAKE_DEMUXER_STREAM_H_ +#define MEDIA_FILTERS_FAKE_DEMUXER_STREAM_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/demuxer_stream.h" +#include "media/base/media_export.h" +#include "media/base/video_decoder_config.h" + +namespace base { +class MessageLoopProxy; +} // namespace base + +namespace media { + +class MEDIA_EXPORT FakeDemuxerStream : public DemuxerStream { + public: + // Constructs an object that outputs |num_configs| different configs in + // sequence with |num_frames_in_one_config| buffers for each config. The + // output buffers are encrypted if |is_encrypted| is true. + FakeDemuxerStream(int num_configs, + int num_buffers_in_one_config, + bool is_encrypted); + virtual ~FakeDemuxerStream(); + + // DemuxerStream implementation. + virtual void Read(const ReadCB& read_cb) OVERRIDE; + virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; + virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; + virtual Type type() OVERRIDE; + virtual void EnableBitstreamConverter() OVERRIDE; + + // Upon the next read, holds the read callback until SatisfyRead() or Reset() + // is called. + void HoldNextRead(); + + // Satisfies the pending read with the next scheduled status and buffer. + void SatisfyRead(); + + // Satisfies the pending read (if any) with kAborted and NULL. This call + // always clears |hold_next_read_|. + void Reset(); + + private: + void UpdateVideoDecoderConfig(); + void DoRead(); + + scoped_refptr<base::MessageLoopProxy> message_loop_; + + int num_configs_left_; + int num_buffers_in_one_config_; + bool is_encrypted_; + + // Number of frames left with the current decoder config. + int num_buffers_left_in_current_config_; + + base::TimeDelta current_timestamp_; + base::TimeDelta duration_; + + AudioDecoderConfig audio_decoder_config_; + + gfx::Size next_coded_size_; + VideoDecoderConfig video_decoder_config_; + + ReadCB read_cb_; + bool hold_next_read_; + + DISALLOW_COPY_AND_ASSIGN(FakeDemuxerStream); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_FAKE_DEMUXER_STREAM_H_ diff --git a/media/filters/fake_demuxer_stream_unittest.cc b/media/filters/fake_demuxer_stream_unittest.cc new file mode 100644 index 0000000..3a63e96 --- /dev/null +++ b/media/filters/fake_demuxer_stream_unittest.cc @@ -0,0 +1,235 @@ +// Copyright (c) 2012 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 "base/basictypes.h" +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "media/base/decoder_buffer.h" +#include "media/base/demuxer_stream.h" +#include "media/filters/fake_demuxer_stream.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +static const int kNumFramesInOneConfig = 9; +static const int kNumFramesToReadFirst = 5; +static const int kNumConfigs = 3; +COMPILE_ASSERT(kNumFramesToReadFirst < kNumFramesInOneConfig, + do_not_read_too_many_buffers); +COMPILE_ASSERT(kNumConfigs > 0, need_multiple_configs_to_trigger_config_change); + +class FakeDemuxerStreamTest : public testing::Test { + public: + FakeDemuxerStreamTest() + : status_(DemuxerStream::kAborted), + read_pending_(false) {} + virtual ~FakeDemuxerStreamTest() {} + + void BufferReady(DemuxerStream::Status status, + const scoped_refptr<DecoderBuffer>& buffer) { + DCHECK(read_pending_); + read_pending_ = false; + status_ = status; + buffer_ = buffer; + } + + enum ReadResult { + OK, + ABORTED, + CONFIG_CHANGED, + EOS, + PENDING + }; + + void EnterNormalReadState() { + stream_.reset( + new FakeDemuxerStream(kNumConfigs, kNumFramesInOneConfig, false)); + for (int i = 0; i < kNumFramesToReadFirst; ++i) + ReadAndExpect(OK); + } + + void EnterBeforeConfigChangedState() { + stream_.reset( + new FakeDemuxerStream(kNumConfigs, kNumFramesInOneConfig, false)); + for (int i = 0; i < kNumFramesInOneConfig; ++i) + ReadAndExpect(OK); + } + + void EnterBeforeEOSState() { + stream_.reset(new FakeDemuxerStream(1, kNumFramesInOneConfig, false)); + for (int i = 0; i < kNumFramesInOneConfig; ++i) + ReadAndExpect(OK); + } + + void ExpectReadResult(ReadResult result) { + switch (result) { + case OK: + EXPECT_FALSE(read_pending_); + EXPECT_EQ(DemuxerStream::kOk, status_); + ASSERT_TRUE(buffer_); + EXPECT_FALSE(buffer_->IsEndOfStream()); + break; + + case ABORTED: + EXPECT_FALSE(read_pending_); + EXPECT_EQ(DemuxerStream::kAborted, status_); + EXPECT_FALSE(buffer_); + break; + + case CONFIG_CHANGED: + EXPECT_FALSE(read_pending_); + EXPECT_EQ(DemuxerStream::kConfigChanged, status_); + EXPECT_FALSE(buffer_); + break; + + case EOS: + EXPECT_FALSE(read_pending_); + EXPECT_EQ(DemuxerStream::kOk, status_); + ASSERT_TRUE(buffer_); + EXPECT_TRUE(buffer_->IsEndOfStream()); + break; + + case PENDING: + EXPECT_TRUE(read_pending_); + break; + } + } + + void ReadAndExpect(ReadResult result) { + EXPECT_FALSE(read_pending_); + read_pending_ = true; + stream_->Read(base::Bind(&FakeDemuxerStreamTest::BufferReady, + base::Unretained(this))); + message_loop_.RunUntilIdle(); + ExpectReadResult(result); + } + + void SatisfyReadAndExpect(ReadResult result) { + EXPECT_TRUE(read_pending_); + stream_->SatisfyRead(); + message_loop_.RunUntilIdle(); + ExpectReadResult(result); + } + + void Reset() { + bool had_read_pending = read_pending_; + stream_->Reset(); + message_loop_.RunUntilIdle(); + + EXPECT_FALSE(read_pending_); + if (had_read_pending) + ExpectReadResult(ABORTED); + } + + void TestRead(int num_configs, + int num_frames_in_one_config, + bool is_encrypted) { + stream_.reset(new FakeDemuxerStream( + num_configs, num_frames_in_one_config, is_encrypted)); + + const VideoDecoderConfig& config = stream_->video_decoder_config(); + EXPECT_TRUE(config.IsValidConfig()); + EXPECT_EQ(is_encrypted, config.is_encrypted()); + + for (int i = 0; i < num_configs; ++i) { + for (int j = 0; j < num_frames_in_one_config; ++j) + ReadAndExpect(OK); + + if (i == num_configs - 1) + ReadAndExpect(EOS); + else + ReadAndExpect(CONFIG_CHANGED); + } + + // Will always get EOS after we hit EOS. + ReadAndExpect(EOS); + } + + base::MessageLoop message_loop_; + scoped_ptr<FakeDemuxerStream> stream_; + + DemuxerStream::Status status_; + scoped_refptr<DecoderBuffer> buffer_; + bool read_pending_; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeDemuxerStreamTest); +}; + +TEST_F(FakeDemuxerStreamTest, Read_OneConfig) { + TestRead(1, 5, false); +} + +TEST_F(FakeDemuxerStreamTest, Read_MultipleConfigs) { + TestRead(3, 5, false); +} + +TEST_F(FakeDemuxerStreamTest, Read_OneFramePerConfig) { + TestRead(3, 1, false); +} + +TEST_F(FakeDemuxerStreamTest, Read_Encrypted) { + TestRead(6, 3, true); +} + +TEST_F(FakeDemuxerStreamTest, HoldRead_Normal) { + EnterNormalReadState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + SatisfyReadAndExpect(OK); +} + +TEST_F(FakeDemuxerStreamTest, HoldRead_BeforeConfigChanged) { + EnterBeforeConfigChangedState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + SatisfyReadAndExpect(CONFIG_CHANGED); +} + +TEST_F(FakeDemuxerStreamTest, HoldRead_BeforeEOS) { + EnterBeforeEOSState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + SatisfyReadAndExpect(EOS); +} + +TEST_F(FakeDemuxerStreamTest, Reset_Normal) { + EnterNormalReadState(); + Reset(); + ReadAndExpect(OK); +} + +TEST_F(FakeDemuxerStreamTest, Reset_AfterHoldRead) { + EnterNormalReadState(); + stream_->HoldNextRead(); + Reset(); + ReadAndExpect(OK); +} + +TEST_F(FakeDemuxerStreamTest, Reset_DuringPendingRead) { + EnterNormalReadState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + Reset(); + ReadAndExpect(OK); +} + +TEST_F(FakeDemuxerStreamTest, Reset_BeforeConfigChanged) { + EnterBeforeConfigChangedState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + Reset(); + ReadAndExpect(CONFIG_CHANGED); +} + +TEST_F(FakeDemuxerStreamTest, Reset_BeforeEOS) { + EnterBeforeEOSState(); + stream_->HoldNextRead(); + ReadAndExpect(PENDING); + Reset(); + ReadAndExpect(EOS); +} + +} // namespace media diff --git a/media/media.gyp b/media/media.gyp index d2dd550..089778c 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -318,6 +318,8 @@ 'filters/decrypting_demuxer_stream.h', 'filters/decrypting_video_decoder.cc', 'filters/decrypting_video_decoder.h', + 'filters/fake_demuxer_stream.cc', + 'filters/fake_demuxer_stream.h', 'filters/ffmpeg_audio_decoder.cc', 'filters/ffmpeg_audio_decoder.h', 'filters/ffmpeg_demuxer.cc', @@ -968,6 +970,7 @@ 'filters/decrypting_audio_decoder_unittest.cc', 'filters/decrypting_demuxer_stream_unittest.cc', 'filters/decrypting_video_decoder_unittest.cc', + 'filters/fake_demuxer_stream_unittest.cc', 'filters/ffmpeg_audio_decoder_unittest.cc', 'filters/ffmpeg_demuxer_unittest.cc', 'filters/ffmpeg_glue_unittest.cc', |