summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-08 22:58:24 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-08 22:58:24 +0000
commitea76ed7addcdb14ec0e57fc3e5172bafe0f1375c (patch)
tree953794d08b2aedffb2c57d13350493659d491c3c /media
parenteb35ffb7c6bbd725ad395cce3f922e5e89060914 (diff)
downloadchromium_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.cc149
-rw-r--r--media/filters/fake_demuxer_stream.h78
-rw-r--r--media/filters/fake_demuxer_stream_unittest.cc235
-rw-r--r--media/media.gyp3
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',