summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-24 04:06:44 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-24 04:06:44 +0000
commit84fb5a06710f34a48ee3f460083ec7fdcf4025ea (patch)
tree6937b8892a71ccdcee8dec744c932de37abc4a97 /media
parentdf89b4315db9c45c24aa5123e1833ebc17be1aa8 (diff)
downloadchromium_src-84fb5a06710f34a48ee3f460083ec7fdcf4025ea.zip
chromium_src-84fb5a06710f34a48ee3f460083ec7fdcf4025ea.tar.gz
chromium_src-84fb5a06710f34a48ee3f460083ec7fdcf4025ea.tar.bz2
Introduce VideoFrameStream, the video frame provider in media pipeline.
VideoFrameStream wraps DemuxerStreams and VideoDecoders to provide VideoFrames to it's client (e.g. VideoRendererBase). It hides details of the interraction between DemuxerStream and VideoDecoder. To it's client, VideoFrameStream works as a reliable VideoFrame source which, after initialization, simply returns VideoFrames until end-of-stream (EOS). BUG=141788 TEST=none Review URL: https://chromiumcodereview.appspot.com/12818004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190181 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/video_frame_stream.cc206
-rw-r--r--media/filters/video_frame_stream.h123
-rw-r--r--media/filters/video_frame_stream_unittest.cc332
-rw-r--r--media/filters/video_renderer_base.cc75
-rw-r--r--media/filters/video_renderer_base.h38
-rw-r--r--media/filters/video_renderer_base_unittest.cc16
-rw-r--r--media/media.gyp3
7 files changed, 699 insertions, 94 deletions
diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc
new file mode 100644
index 0000000..c9cca1b
--- /dev/null
+++ b/media/filters/video_frame_stream.cc
@@ -0,0 +1,206 @@
+// 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/video_frame_stream.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "media/base/bind_to_loop.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/video_decoder_config.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+#include "media/filters/video_decoder_selector.h"
+
+namespace media {
+
+VideoFrameStream::VideoFrameStream(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ const SetDecryptorReadyCB& set_decryptor_ready_cb)
+ : message_loop_(message_loop),
+ weak_factory_(this),
+ state_(UNINITIALIZED),
+ set_decryptor_ready_cb_(set_decryptor_ready_cb) {
+}
+
+VideoFrameStream::~VideoFrameStream() {
+ DCHECK(state_ == UNINITIALIZED || state_ == STOPPED) << state_;
+}
+
+void VideoFrameStream::Initialize(const scoped_refptr<DemuxerStream>& stream,
+ const VideoDecoderList& decoders,
+ const StatisticsCB& statistics_cb,
+ const InitCB& init_cb) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, UNINITIALIZED);
+
+ weak_this_ = weak_factory_.GetWeakPtr();
+
+ DCHECK(init_cb_.is_null());
+ DCHECK(!init_cb.is_null());
+ init_cb_ = init_cb;
+
+ scoped_ptr<VideoDecoderSelector> decoder_selector(
+ new VideoDecoderSelector(message_loop_,
+ decoders,
+ set_decryptor_ready_cb_));
+
+ // To avoid calling |decoder_selector| methods and passing ownership of
+ // |decoder_selector| in the same line.
+ VideoDecoderSelector* decoder_selector_ptr = decoder_selector.get();
+
+ decoder_selector_ptr->SelectVideoDecoder(
+ stream,
+ statistics_cb,
+ base::Bind(&VideoFrameStream::OnDecoderSelected, weak_this_,
+ base::Passed(&decoder_selector)));
+}
+
+void VideoFrameStream::ReadFrame(const VideoDecoder::ReadCB& read_cb) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ // No two reads in the flight at any time.
+ DCHECK(read_cb_.is_null());
+ // No read during resetting or stopping process.
+ DCHECK(reset_cb_.is_null());
+ DCHECK(stop_cb_.is_null());
+
+ read_cb_ = read_cb;
+
+ decoder_->Read(base::Bind(&VideoFrameStream::OnFrameRead, weak_this_));
+}
+
+void VideoFrameStream::Reset(const base::Closure& closure) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ DCHECK(reset_cb_.is_null());
+ DCHECK(stop_cb_.is_null());
+
+ reset_cb_ = closure;
+
+ // We may or may not have pending read, but we'll start to reset everything
+ // regardless.
+
+ if (decrypting_demuxer_stream_) {
+ decrypting_demuxer_stream_->Reset(base::Bind(
+ &VideoFrameStream::ResetDecoder, weak_this_));
+ return;
+ }
+
+ ResetDecoder();
+}
+
+void VideoFrameStream::Stop(const base::Closure& closure) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_NE(state_, STOPPED);
+ DCHECK(stop_cb_.is_null());
+
+ stop_cb_ = closure;
+
+ // The stopping will continue after all of the following pending callbacks
+ // (if they are not null) are satisfied.
+ // TODO(xhwang): Now we cannot stop the initialization process through
+ // VideoDecoderSelector. Fix this. See: http://crbug.com/222054
+ if (!init_cb_.is_null())
+ return;
+
+ // We may or may not have pending read and/or pending reset, but we'll start
+ // to stop everything regardless.
+
+ if (decrypting_demuxer_stream_) {
+ decrypting_demuxer_stream_->Reset(base::Bind(
+ &VideoFrameStream::StopDecoder, weak_this_));
+ return;
+ }
+
+ if (decoder_) {
+ StopDecoder();
+ return;
+ }
+
+ state_ = STOPPED;
+ message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_));
+}
+
+bool VideoFrameStream::HasOutputFrameAvailable() const {
+ return decoder_->HasOutputFrameAvailable();
+}
+
+void VideoFrameStream::OnDecoderSelected(
+ scoped_ptr<VideoDecoderSelector> decoder_selector,
+ const scoped_refptr<VideoDecoder>& selected_decoder,
+ const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, UNINITIALIZED);
+ DCHECK(!init_cb_.is_null());
+
+ if (!selected_decoder) {
+ state_ = UNINITIALIZED;
+ base::ResetAndReturn(&init_cb_).Run(false, false);
+ } else {
+ decoder_ = selected_decoder;
+ decrypting_demuxer_stream_ = decrypting_demuxer_stream;
+ state_ = NORMAL;
+ base::ResetAndReturn(&init_cb_).Run(true, decoder_->HasAlpha());
+ }
+
+ // Stop() called during initialization.
+ if (!stop_cb_.is_null()) {
+ Stop(base::ResetAndReturn(&stop_cb_));
+ return;
+ }
+}
+
+void VideoFrameStream::OnFrameRead(const VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& frame) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ DCHECK(!read_cb_.is_null());
+
+ base::ResetAndReturn(&read_cb_).Run(status, frame);
+}
+
+void VideoFrameStream::ResetDecoder() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ DCHECK(!reset_cb_.is_null());
+
+ decoder_->Reset(base::Bind(&VideoFrameStream::OnDecoderReset, weak_this_));
+}
+
+void VideoFrameStream::OnDecoderReset() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ // If Reset() was called during pending read, read callback should be fired
+ // before the reset callback is fired.
+ DCHECK(read_cb_.is_null());
+ DCHECK(!reset_cb_.is_null());
+
+ base::ResetAndReturn(&reset_cb_).Run();
+}
+
+void VideoFrameStream::StopDecoder() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ DCHECK(!stop_cb_.is_null());
+
+ decoder_->Stop(base::Bind(&VideoFrameStream::OnDecoderStopped, weak_this_));
+}
+
+void VideoFrameStream::OnDecoderStopped() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, NORMAL);
+ // If Stop() was called during pending read/reset, read/reset callback should
+ // be fired before the stop callback is fired.
+ DCHECK(read_cb_.is_null());
+ DCHECK(reset_cb_.is_null());
+ DCHECK(!stop_cb_.is_null());
+
+ state_ = STOPPED;
+ base::ResetAndReturn(&stop_cb_).Run();
+}
+
+} // namespace media
diff --git a/media/filters/video_frame_stream.h b/media/filters/video_frame_stream.h
new file mode 100644
index 0000000..1fe2a07
--- /dev/null
+++ b/media/filters/video_frame_stream.h
@@ -0,0 +1,123 @@
+// 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_VIDEO_FRAME_STREAM_H_
+#define MEDIA_FILTERS_VIDEO_FRAME_STREAM_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/decryptor.h"
+#include "media/base/media_export.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/video_decoder.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace media {
+
+class DecryptingDemuxerStream;
+class DemuxerStream;
+class VideoDecoderSelector;
+
+// Wraps a DemuxerStream and a list of VideoDecoders and provides decoded
+// VideoFrames to its client (e.g. VideoRendererBase).
+class MEDIA_EXPORT VideoFrameStream {
+ public:
+ typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList;
+
+ // Indicates completion of VideoFrameStream initialization.
+ typedef base::Callback<void(bool success, bool has_alpha)> InitCB;
+
+ VideoFrameStream(const scoped_refptr<base::MessageLoopProxy>& message_loop,
+ const SetDecryptorReadyCB& set_decryptor_ready_cb);
+
+ ~VideoFrameStream();
+
+ // Initializes the VideoFrameStream and returns the initialization result
+ // through |init_cb|. Note that |init_cb| is always called asynchronously.
+ void Initialize(const scoped_refptr<DemuxerStream>& stream,
+ const VideoDecoderList& decoders,
+ const StatisticsCB& statistics_cb,
+ const InitCB& init_cb);
+
+ // Reads a decoded VideoFrame and returns it via the |read_cb|. Note that
+ // |read_cb| is always called asynchronously. This method should only be
+ // called after initialization has succeeded and must not be called during
+ // any pending Reset() and/or Stop().
+ void ReadFrame(const VideoDecoder::ReadCB& read_cb);
+
+ // Resets the decoder, flushes all decoded frames and/or internal buffers,
+ // fires any existing pending read callback and calls |closure| on completion.
+ // Note that |closure| is always called asynchronously. This method should
+ // only be called after initialization has succeeded and must not be called
+ // during any pending Reset() and/or Stop().
+ void Reset(const base::Closure& closure);
+
+ // Stops the decoder, fires any existing pending read callback or reset
+ // callback and calls |closure| on completion. Note that |closure| is always
+ // called asynchronously. The VideoFrameStream cannot be used anymore after
+ // it is stopped. This method can be called at any time but not during another
+ // pending Stop().
+ void Stop(const base::Closure& closure);
+
+ // Returns true if the decoder currently has the ability to decode and return
+ // a VideoFrame.
+ bool HasOutputFrameAvailable() const;
+
+ private:
+ enum State {
+ UNINITIALIZED,
+ NORMAL,
+ STOPPED
+ };
+
+ // Called when |decoder_selector_| selected the |selected_decoder|.
+ // |decrypting_demuxer_stream| was also populated if a DecryptingDemuxerStream
+ // is created to help decrypt the encrypted stream.
+ // Note: |decoder_selector| is passed here to keep the VideoDecoderSelector
+ // alive until OnDecoderSelected() finishes.
+ void OnDecoderSelected(
+ scoped_ptr<VideoDecoderSelector> decoder_selector,
+ const scoped_refptr<VideoDecoder>& selected_decoder,
+ const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream);
+
+ // Callback for VideoDecoder::Read().
+ void OnFrameRead(const VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& frame);
+
+ void ResetDecoder();
+ void OnDecoderReset();
+
+ void StopDecoder();
+ void OnDecoderStopped();
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ base::WeakPtrFactory<VideoFrameStream> weak_factory_;
+ base::WeakPtr<VideoFrameStream> weak_this_;
+
+ State state_;
+
+ InitCB init_cb_;
+ VideoDecoder::ReadCB read_cb_;
+ base::Closure reset_cb_;
+ base::Closure stop_cb_;
+
+ SetDecryptorReadyCB set_decryptor_ready_cb_;
+
+ // These two will be set by VideoDecoderSelector::SelectVideoDecoder().
+ scoped_refptr<VideoDecoder> decoder_;
+ scoped_refptr<DecryptingDemuxerStream> decrypting_demuxer_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameStream);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_VIDEO_FRAME_STREAM_H_
diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc
new file mode 100644
index 0000000..134becb
--- /dev/null
+++ b/media/filters/video_frame_stream_unittest.cc
@@ -0,0 +1,332 @@
+// 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/bind.h"
+#include "base/callback_helpers.h"
+#include "base/message_loop.h"
+#include "media/base/gmock_callback_support.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "media/filters/video_frame_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Assign;
+using ::testing::AtMost;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+namespace media {
+
+static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
+static const gfx::Size kCodedSize(320, 240);
+static const gfx::Rect kVisibleRect(320, 240);
+static const gfx::Size kNaturalSize(320, 240);
+
+class VideoFrameStreamTest : public testing::TestWithParam<bool> {
+ public:
+ VideoFrameStreamTest()
+ : video_frame_stream_(
+ message_loop_.message_loop_proxy(),
+ base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback,
+ base::Unretained(this))),
+ video_config_(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat,
+ kCodedSize, kVisibleRect, kNaturalSize, NULL, 0,
+ GetParam()),
+ demuxer_stream_(new StrictMock<MockDemuxerStream>()),
+ decryptor_(new NiceMock<MockDecryptor>()),
+ decoder_(new StrictMock<MockVideoDecoder>()),
+ is_initialized_(false) {
+ decoders_.push_back(decoder_);
+
+ EXPECT_CALL(*demuxer_stream_, type())
+ .WillRepeatedly(Return(DemuxerStream::VIDEO));
+ EXPECT_CALL(*demuxer_stream_, video_decoder_config())
+ .WillRepeatedly(ReturnRef(video_config_));
+
+ EXPECT_CALL(*this, SetDecryptorReadyCallback(_))
+ .WillRepeatedly(RunCallback<0>(decryptor_.get()));
+
+ // Decryptor can only decrypt (not decrypt-and-decode) so that
+ // DecryptingDemuxerStream will be used.
+ EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
+ .WillRepeatedly(RunCallback<1>(false));
+ EXPECT_CALL(*decryptor_, Decrypt(_, _, _))
+ .WillRepeatedly(RunCallback<2>(Decryptor::kSuccess,
+ scoped_refptr<DecoderBuffer>()));
+ }
+
+ ~VideoFrameStreamTest() {
+ if (is_initialized_)
+ Stop();
+
+ EXPECT_FALSE(is_initialized_);
+ EXPECT_TRUE(decoder_init_cb_.is_null());
+ EXPECT_TRUE(decoder_read_cb_.is_null());
+ EXPECT_TRUE(decoder_reset_cb_.is_null());
+ EXPECT_TRUE(decoder_stop_cb_.is_null());
+ }
+
+ MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
+ MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&));
+ MOCK_METHOD2(OnInitialized, void(bool, bool));
+ MOCK_METHOD2(OnFrameRead, void(VideoDecoder::Status,
+ const scoped_refptr<VideoFrame>&));
+ MOCK_METHOD0(OnReset, void());
+ MOCK_METHOD0(OnStopped, void());
+
+ void EnterPendingInitializationState() {
+ EXPECT_CALL(*decoder_, Initialize(_, _, _))
+ .WillOnce(SaveArg<1>(&decoder_init_cb_));
+ video_frame_stream_.Initialize(
+ demuxer_stream_,
+ decoders_,
+ base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)),
+ base::Bind(&VideoFrameStreamTest::OnInitialized,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ }
+
+ void SatisfyPendingInitialization(bool success) {
+ EXPECT_CALL(*this, OnInitialized(success, false))
+ .WillOnce(SaveArg<0>(&is_initialized_));
+ base::ResetAndReturn(&decoder_init_cb_).Run(
+ success ? PIPELINE_OK : DECODER_ERROR_NOT_SUPPORTED);
+ message_loop_.RunUntilIdle();
+ }
+
+ void EnterPendingReadFrameState() {
+ EXPECT_CALL(*decoder_, Read(_))
+ .WillOnce(SaveArg<0>(&decoder_read_cb_));
+ EXPECT_CALL(*this, OnFrameRead(VideoDecoder::kOk, _));
+ video_frame_stream_.ReadFrame(base::Bind(&VideoFrameStreamTest::OnFrameRead,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ }
+
+ void SatisfyPendingReadFrame() {
+ base::ResetAndReturn(&decoder_read_cb_).Run(VideoDecoder::kOk,
+ scoped_refptr<VideoFrame>());
+ message_loop_.RunUntilIdle();
+ }
+
+ void EnterPendingResetState() {
+ EXPECT_CALL(*decoder_, Reset(_))
+ .WillOnce(SaveArg<0>(&decoder_reset_cb_));
+ EXPECT_CALL(*this, OnReset());
+ video_frame_stream_.Reset(base::Bind(&VideoFrameStreamTest::OnReset,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ }
+
+ void SatisfyPendingReset() {
+ base::ResetAndReturn(&decoder_reset_cb_).Run();
+ message_loop_.RunUntilIdle();
+ }
+
+ void EnterPendingStopState() {
+ // If initialization failed, we won't call VideoDecoder::Stop() during
+ // the stopping process.
+ EXPECT_CALL(*decoder_, Stop(_))
+ .Times(AtMost(1))
+ .WillRepeatedly(SaveArg<0>(&decoder_stop_cb_));
+ EXPECT_CALL(*this, OnStopped())
+ .WillOnce(Assign(&is_initialized_, false));
+ video_frame_stream_.Stop(base::Bind(&VideoFrameStreamTest::OnStopped,
+ base::Unretained(this)));
+ message_loop_.RunUntilIdle();
+ }
+
+ void SatisfyPendingStop() {
+ // If decoder is not initialized, |decoder_stop_cb_| can be null. In that
+ // case, we don't actually need to satisfy the stop callback.
+ if (decoder_stop_cb_.is_null())
+ return;
+ base::ResetAndReturn(&decoder_stop_cb_).Run();
+ message_loop_.RunUntilIdle();
+ }
+
+ void Initialize() {
+ EnterPendingInitializationState();
+ SatisfyPendingInitialization(true);
+ }
+
+ void InitializeAndFail() {
+ EnterPendingInitializationState();
+ SatisfyPendingInitialization(false);
+ }
+
+ void ReadFrame() {
+ EnterPendingReadFrameState();
+ SatisfyPendingReadFrame();
+ }
+
+ void Reset() {
+ EnterPendingResetState();
+ SatisfyPendingReset();
+ }
+
+ void Stop() {
+ EnterPendingStopState();
+ SatisfyPendingStop();
+ }
+
+ private:
+ MessageLoop message_loop_;
+
+ VideoFrameStream video_frame_stream_;
+ VideoDecoderConfig video_config_;
+ scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_stream_;
+ // Use NiceMock since we don't care about most of calls on the decryptor, e.g.
+ // RegisterNewKeyCB().
+ scoped_ptr<NiceMock<MockDecryptor> > decryptor_;
+ scoped_refptr<StrictMock<MockVideoDecoder> > decoder_;
+ VideoFrameStream::VideoDecoderList decoders_;
+
+ // Callbacks to simulate pending decoder operations.
+ PipelineStatusCB decoder_init_cb_;
+ VideoDecoder::ReadCB decoder_read_cb_;
+ base::Closure decoder_reset_cb_;
+ base::Closure decoder_stop_cb_;
+
+ bool is_initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest);
+};
+
+INSTANTIATE_TEST_CASE_P(Clear, VideoFrameStreamTest, testing::Values(false));
+INSTANTIATE_TEST_CASE_P(Encrypted, VideoFrameStreamTest, testing::Values(true));
+
+TEST_P(VideoFrameStreamTest, Initialization) {
+ Initialize();
+}
+
+TEST_P(VideoFrameStreamTest, Initialization_Failed) {
+ InitializeAndFail();
+}
+
+TEST_P(VideoFrameStreamTest, ReadFrame) {
+ Initialize();
+ ReadFrame();
+}
+
+TEST_P(VideoFrameStreamTest, ReadFrame_Multiple) {
+ Initialize();
+ for (int i = 0; i < 10; ++i)
+ ReadFrame();
+}
+
+TEST_P(VideoFrameStreamTest, ReadFrame_AfterReset) {
+ Initialize();
+ Reset();
+ ReadFrame();
+}
+
+TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) {
+ Initialize();
+ Reset();
+}
+
+TEST_P(VideoFrameStreamTest, Reset_DuringReadFrame) {
+ Initialize();
+ EnterPendingReadFrameState();
+ EnterPendingResetState();
+ SatisfyPendingReadFrame();
+ SatisfyPendingReset();
+}
+
+TEST_P(VideoFrameStreamTest, Reset_AfterReadFrame) {
+ Initialize();
+ ReadFrame();
+ Reset();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_BeforeInitialization) {
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringInitialization) {
+ EnterPendingInitializationState();
+ EnterPendingStopState();
+ SatisfyPendingInitialization(true);
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringFailedInitialization) {
+ EnterPendingInitializationState();
+ EnterPendingStopState();
+ SatisfyPendingInitialization(false);
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterInitialization) {
+ Initialize();
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterFailedInitialization) {
+ InitializeAndFail();
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringReadFrame) {
+ Initialize();
+ EnterPendingReadFrameState();
+ EnterPendingStopState();
+ SatisfyPendingReadFrame();
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame) {
+ Initialize();
+ ReadFrame();
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringReset) {
+ Initialize();
+ EnterPendingResetState();
+ EnterPendingStopState();
+ SatisfyPendingReset();
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterReset) {
+ Initialize();
+ Reset();
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringReadFrame_DuringReset) {
+ Initialize();
+ EnterPendingReadFrameState();
+ EnterPendingResetState();
+ EnterPendingStopState();
+ SatisfyPendingReadFrame();
+ SatisfyPendingReset();
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame_DuringReset) {
+ Initialize();
+ EnterPendingReadFrameState();
+ EnterPendingResetState();
+ SatisfyPendingReadFrame();
+ EnterPendingStopState();
+ SatisfyPendingReset();
+ SatisfyPendingStop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame_AfterReset) {
+ Initialize();
+ ReadFrame();
+ Reset();
+ Stop();
+}
+
+} // namespace media
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc
index ca8f3429..4a5af4d 100644
--- a/media/filters/video_renderer_base.cc
+++ b/media/filters/video_renderer_base.cc
@@ -14,7 +14,6 @@
#include "media/base/pipeline.h"
#include "media/base/video_frame.h"
#include "media/filters/decrypting_demuxer_stream.h"
-#include "media/filters/video_decoder_selector.h"
namespace media {
@@ -30,7 +29,7 @@ VideoRendererBase::VideoRendererBase(
bool drop_frames)
: message_loop_(message_loop),
weak_factory_(this),
- set_decryptor_ready_cb_(set_decryptor_ready_cb),
+ video_frame_stream_(message_loop, set_decryptor_ready_cb),
received_end_of_stream_(false),
frame_available_(&lock_),
state_(kUninitialized),
@@ -73,21 +72,8 @@ void VideoRendererBase::Flush(const base::Closure& callback) {
flush_cb_ = callback;
state_ = kFlushingDecoder;
- if (decrypting_demuxer_stream_) {
- decrypting_demuxer_stream_->Reset(base::Bind(
- &VideoRendererBase::ResetDecoder, weak_this_));
- return;
- }
-
- decoder_->Reset(base::Bind(
- &VideoRendererBase::OnDecoderResetDone, weak_this_));
-}
-
-void VideoRendererBase::ResetDecoder() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- base::AutoLock auto_lock(lock_);
- decoder_->Reset(base::Bind(
- &VideoRendererBase::OnDecoderResetDone, weak_this_));
+ video_frame_stream_.Reset(base::Bind(
+ &VideoRendererBase::OnVideoFrameStreamResetDone, weak_this_));
}
void VideoRendererBase::Stop(const base::Closure& callback) {
@@ -121,24 +107,7 @@ void VideoRendererBase::Stop(const base::Closure& callback) {
base::PlatformThread::Join(thread_to_join);
}
- if (decrypting_demuxer_stream_) {
- decrypting_demuxer_stream_->Reset(base::Bind(
- &VideoRendererBase::StopDecoder, weak_this_, callback));
- return;
- }
-
- if (decoder_) {
- decoder_->Stop(callback);
- return;
- }
-
- callback.Run();
-}
-
-void VideoRendererBase::StopDecoder(const base::Closure& callback) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- base::AutoLock auto_lock(lock_);
- decoder_->Stop(callback);
+ video_frame_stream_.Stop(callback);
}
void VideoRendererBase::SetPlaybackRate(float playback_rate) {
@@ -196,26 +165,16 @@ void VideoRendererBase::Initialize(const scoped_refptr<DemuxerStream>& stream,
get_duration_cb_ = get_duration_cb;
state_ = kInitializing;
- scoped_ptr<VideoDecoderSelector> decoder_selector(
- new VideoDecoderSelector(base::MessageLoopProxy::current(),
- decoders,
- set_decryptor_ready_cb_));
-
- // To avoid calling |decoder_selector| methods and passing ownership of
- // |decoder_selector| in the same line.
- VideoDecoderSelector* decoder_selector_ptr = decoder_selector.get();
-
- decoder_selector_ptr->SelectVideoDecoder(
+ video_frame_stream_.Initialize(
stream,
+ decoders,
statistics_cb,
- base::Bind(&VideoRendererBase::OnDecoderSelected, weak_this_,
- base::Passed(&decoder_selector)));
+ base::Bind(&VideoRendererBase::OnVideoFrameStreamInitialized,
+ weak_this_));
}
-void VideoRendererBase::OnDecoderSelected(
- scoped_ptr<VideoDecoderSelector> decoder_selector,
- const scoped_refptr<VideoDecoder>& selected_decoder,
- const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream) {
+void VideoRendererBase::OnVideoFrameStreamInitialized(bool success,
+ bool has_alpha) {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
@@ -224,22 +183,19 @@ void VideoRendererBase::OnDecoderSelected(
DCHECK_EQ(state_, kInitializing);
- if (!selected_decoder) {
+ if (!success) {
state_ = kUninitialized;
base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
- decoder_ = selected_decoder;
- decrypting_demuxer_stream_ = decrypting_demuxer_stream;
-
// We're all good! Consider ourselves flushed. (ThreadMain() should never
// see us in the kUninitialized state).
// Since we had an initial Preroll(), we consider ourself flushed, because we
// have not populated any buffers yet.
state_ = kFlushed;
- set_opaque_cb_.Run(!decoder_->HasAlpha());
+ set_opaque_cb_.Run(!has_alpha);
set_opaque_cb_.Reset();
// Create our video thread.
@@ -437,7 +393,7 @@ void VideoRendererBase::FrameReady(VideoDecoder::Status status,
AddReadyFrame_Locked(frame);
if (state_ == kPrerolling) {
- if (!decoder_->HasOutputFrameAvailable() ||
+ if (!video_frame_stream_.HasOutputFrameAvailable() ||
ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames)) {
TransitionToPrerolled_Locked();
}
@@ -501,7 +457,8 @@ void VideoRendererBase::AttemptRead_Locked() {
case kPrerolling:
case kPlaying:
pending_read_ = true;
- decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, weak_this_));
+ video_frame_stream_.ReadFrame(base::Bind(&VideoRendererBase::FrameReady,
+ weak_this_));
return;
case kUninitialized:
@@ -516,7 +473,7 @@ void VideoRendererBase::AttemptRead_Locked() {
}
}
-void VideoRendererBase::OnDecoderResetDone() {
+void VideoRendererBase::OnVideoFrameStreamResetDone() {
base::AutoLock auto_lock(lock_);
if (state_ == kStopped)
return;
diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h
index 8fb6182..b1a7a3e 100644
--- a/media/filters/video_renderer_base.h
+++ b/media/filters/video_renderer_base.h
@@ -18,6 +18,7 @@
#include "media/base/video_decoder.h"
#include "media/base/video_frame.h"
#include "media/base/video_renderer.h"
+#include "media/filters/video_frame_stream.h"
namespace base {
class MessageLoopProxy;
@@ -83,15 +84,8 @@ class MEDIA_EXPORT VideoRendererBase
virtual void ThreadMain() OVERRIDE;
private:
- // Called when |decoder_selector_| selected the |selected_decoder|.
- // |decrypting_demuxer_stream| was also populated if a DecryptingDemuxerStream
- // created to help decrypt the encrypted stream.
- // Note: |decoder_selector| is passed here to keep the VideoDecoderSelector
- // alive until OnDecoderSelected() finishes.
- void OnDecoderSelected(
- scoped_ptr<VideoDecoderSelector> decoder_selector,
- const scoped_refptr<VideoDecoder>& selected_decoder,
- const scoped_refptr<DecryptingDemuxerStream>& decrypting_demuxer_stream);
+ // Callback for |video_frame_stream_| initialization.
+ void OnVideoFrameStreamInitialized(bool success, bool has_alpha);
// Callback from the video decoder delivering decoded video frames and
// reporting video decoder status.
@@ -106,8 +100,8 @@ class MEDIA_EXPORT VideoRendererBase
void AttemptRead();
void AttemptRead_Locked();
- // Called when VideoDecoder::Reset() completes.
- void OnDecoderResetDone();
+ // Called when VideoFrameStream::Reset() completes.
+ void OnVideoFrameStreamResetDone();
// Attempts to complete flushing and transition into the flushed state.
void AttemptFlush_Locked();
@@ -135,21 +129,6 @@ class MEDIA_EXPORT VideoRendererBase
// A read is scheduled to replace the frame.
void DropNextReadyFrame_Locked();
- void ResetDecoder();
- void StopDecoder(const base::Closure& callback);
-
- // Pops the front of |decoders|, assigns it to |decoder_| and then
- // calls initialize on the new decoder.
- void InitializeNextDecoder(const scoped_refptr<DemuxerStream>& demuxer_stream,
- scoped_ptr<VideoDecoderList> decoders);
-
- // Called when |decoder_| initialization completes.
- // |demuxer_stream| & |decoders| are used if initialization failed and
- // InitializeNextDecoder() needs to be called again.
- void OnDecoderInitDone(const scoped_refptr<DemuxerStream>& demuxer_stream,
- scoped_ptr<VideoDecoderList> decoders,
- PipelineStatus status);
-
void TransitionToPrerolled_Locked();
scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -159,11 +138,8 @@ class MEDIA_EXPORT VideoRendererBase
// Used for accessing data members.
base::Lock lock_;
- SetDecryptorReadyCB set_decryptor_ready_cb_;
-
- // These two will be set by VideoDecoderSelector::SelectVideoDecoder().
- scoped_refptr<VideoDecoder> decoder_;
- scoped_refptr<DecryptingDemuxerStream> decrypting_demuxer_stream_;
+ // Provides video frames to VideoRendererBase.
+ VideoFrameStream video_frame_stream_;
// Queue of incoming frames yet to be painted.
typedef std::deque<scoped_refptr<VideoFrame> > VideoFrameQueue;
diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc
index 31a2b7b..c498c50 100644
--- a/media/filters/video_renderer_base_unittest.cc
+++ b/media/filters/video_renderer_base_unittest.cc
@@ -59,7 +59,7 @@ class VideoRendererBaseTest : public ::testing::Test {
// We expect these to be called but we don't care how/when.
EXPECT_CALL(*decoder_, Stop(_))
- .WillRepeatedly(RunClosure<0>());
+ .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::StopRequested));
EXPECT_CALL(statistics_cb_object_, OnStatistics(_))
.Times(AnyNumber());
EXPECT_CALL(*this, OnTimeUpdate(_))
@@ -340,6 +340,17 @@ class VideoRendererBaseTest : public ::testing::Test {
message_loop_.PostTask(FROM_HERE, callback);
}
+ void StopRequested(const base::Closure& callback) {
+ DCHECK_EQ(&message_loop_, MessageLoop::current());
+ decode_results_.clear();
+ if (!read_cb_.is_null()) {
+ QueueAbortedRead();
+ SatisfyPendingRead();
+ }
+
+ message_loop_.PostTask(FROM_HERE, callback);
+ }
+
MessageLoop message_loop_;
VideoDecoderConfig video_config_;
@@ -591,9 +602,6 @@ TEST_F(VideoRendererBaseTest, StopDuringOutstandingRead) {
WaitableMessageLoopEvent event;
renderer_->Stop(event.GetClosure());
- QueueEndOfStream();
- SatisfyPendingRead();
-
event.RunAndWait();
}
diff --git a/media/media.gyp b/media/media.gyp
index ee117a0..97ef02b 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -335,6 +335,8 @@
'filters/stream_parser_factory.h',
'filters/video_decoder_selector.cc',
'filters/video_decoder_selector.h',
+ 'filters/video_frame_stream.cc',
+ 'filters/video_frame_stream.h',
'filters/video_renderer_base.cc',
'filters/video_renderer_base.h',
'filters/vpx_video_decoder.cc',
@@ -929,6 +931,7 @@
'filters/skcanvas_video_renderer_unittest.cc',
'filters/source_buffer_stream_unittest.cc',
'filters/video_decoder_selector_unittest.cc',
+ 'filters/video_frame_stream_unittest.cc',
'filters/video_renderer_base_unittest.cc',
'video/capture/screen/differ_block_unittest.cc',
'video/capture/screen/differ_unittest.cc',