summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-13 09:45:25 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-13 09:45:25 +0000
commit57b44c9239e672bbc6cb698b66a7809383222c04 (patch)
tree04655d8bc7072c4b25360e518708c83f77bc3aae /media
parentf101378f9d28d49f50b42e8004f2e8385e43c094 (diff)
downloadchromium_src-57b44c9239e672bbc6cb698b66a7809383222c04.zip
chromium_src-57b44c9239e672bbc6cb698b66a7809383222c04.tar.gz
chromium_src-57b44c9239e672bbc6cb698b66a7809383222c04.tar.bz2
Reland r210741 "Separate DemuxerStream and VideoDecoder."
The original CL r210741 was reverted by r210765 due to a test failure. This CL relands r210741 with the fix. BUG=141788 Review URL: https://chromiumcodereview.appspot.com/18414008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@211559 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/mock_filters.h7
-rw-r--r--media/base/test_helpers.cc38
-rw-r--r--media/base/test_helpers.h13
-rw-r--r--media/base/video_decoder.h38
-rw-r--r--media/filters/decrypting_demuxer_stream.h10
-rw-r--r--media/filters/decrypting_video_decoder.cc75
-rw-r--r--media/filters/decrypting_video_decoder.h19
-rw-r--r--media/filters/decrypting_video_decoder_unittest.cc132
-rw-r--r--media/filters/fake_demuxer_stream.cc16
-rw-r--r--media/filters/fake_demuxer_stream.h3
-rw-r--r--media/filters/fake_video_decoder.cc112
-rw-r--r--media/filters/fake_video_decoder.h18
-rw-r--r--media/filters/fake_video_decoder_unittest.cc228
-rw-r--r--media/filters/ffmpeg_video_decoder.cc80
-rw-r--r--media/filters/ffmpeg_video_decoder.h19
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc270
-rw-r--r--media/filters/gpu_video_decoder.cc141
-rw-r--r--media/filters/gpu_video_decoder.h18
-rw-r--r--media/filters/video_decoder_selector.cc4
-rw-r--r--media/filters/video_frame_stream.cc237
-rw-r--r--media/filters/video_frame_stream.h37
-rw-r--r--media/filters/video_frame_stream_unittest.cc28
-rw-r--r--media/filters/video_renderer_base_unittest.cc15
-rw-r--r--media/filters/vpx_video_decoder.cc113
-rw-r--r--media/filters/vpx_video_decoder.h21
-rw-r--r--media/media.gyp10
26 files changed, 693 insertions, 1009 deletions
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 5522681..e335477 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -44,7 +44,7 @@ class MockDemuxer : public Demuxer {
class MockDemuxerStream : public DemuxerStream {
public:
- MockDemuxerStream(DemuxerStream::Type type);
+ explicit MockDemuxerStream(DemuxerStream::Type type);
virtual ~MockDemuxerStream();
// DemuxerStream implementation.
@@ -71,10 +71,11 @@ class MockVideoDecoder : public VideoDecoder {
virtual ~MockVideoDecoder();
// VideoDecoder implementation.
- MOCK_METHOD3(Initialize, void(DemuxerStream*,
+ MOCK_METHOD3(Initialize, void(const VideoDecoderConfig& config,
const PipelineStatusCB&,
const StatisticsCB&));
- MOCK_METHOD1(Read, void(const ReadCB&));
+ MOCK_METHOD2(Decode, void(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB&));
MOCK_METHOD1(Reset, void(const base::Closure&));
MOCK_METHOD1(Stop, void(const base::Closure&));
MOCK_CONST_METHOD0(HasAlpha, bool());
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc
index 5f7604b..333624b 100644
--- a/media/base/test_helpers.cc
+++ b/media/base/test_helpers.cc
@@ -7,11 +7,13 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop.h"
+#include "base/pickle.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/base/audio_buffer.h"
#include "media/base/bind_to_loop.h"
+#include "media/base/decoder_buffer.h"
#include "ui/gfx/rect.h"
using ::testing::_;
@@ -242,4 +244,40 @@ DEFINE_INTERLEAVED_INSTANCE(float);
DEFINE_PLANAR_INSTANCE(int16);
DEFINE_PLANAR_INSTANCE(float);
+static const char kFakeVideoBufferHeader[] = "FakeVideoBufferForTest";
+
+scoped_refptr<DecoderBuffer> CreateFakeVideoBufferForTest(
+ const VideoDecoderConfig& config,
+ base::TimeDelta timestamp, base::TimeDelta duration) {
+ Pickle pickle;
+ pickle.WriteString(kFakeVideoBufferHeader);
+ pickle.WriteInt(config.coded_size().width());
+ pickle.WriteInt(config.coded_size().height());
+ pickle.WriteInt64(timestamp.InMilliseconds());
+
+ scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom(
+ static_cast<const uint8*>(pickle.data()),
+ static_cast<int>(pickle.size()));
+ buffer->SetTimestamp(timestamp);
+ buffer->SetDuration(duration);
+
+ return buffer;
+}
+
+bool VerifyFakeVideoBufferForTest(
+ const scoped_refptr<DecoderBuffer>& buffer,
+ const VideoDecoderConfig& config) {
+ // Check if the input |buffer| matches the |config|.
+ PickleIterator pickle(Pickle(reinterpret_cast<const char*>(buffer->GetData()),
+ buffer->GetDataSize()));
+ std::string header;
+ int width = 0;
+ int height = 0;
+ bool success = pickle.ReadString(&header) && pickle.ReadInt(&width) &&
+ pickle.ReadInt(&height);
+ return (success && header == kFakeVideoBufferHeader &&
+ width == config.coded_size().width() &&
+ height == config.coded_size().height());
+}
+
} // namespace media
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index a7eb8f5..872d08d 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -21,6 +21,7 @@ class TimeDelta;
namespace media {
class AudioBuffer;
+class DecoderBuffer;
// Return a callback that expects to be run once.
base::Closure NewExpectedClosure();
@@ -130,6 +131,18 @@ scoped_refptr<AudioBuffer> MakePlanarAudioBuffer(
base::TimeDelta start_time,
base::TimeDelta duration);
+// Create a fake video DecoderBuffer for testing purpose. The buffer contains
+// part of video decoder config info embedded so that the testing code can do
+// some sanity check.
+scoped_refptr<DecoderBuffer> CreateFakeVideoBufferForTest(
+ const VideoDecoderConfig& config,
+ base::TimeDelta timestamp,
+ base::TimeDelta duration);
+
+// Verify if a fake video DecoderBuffer is valid.
+bool VerifyFakeVideoBufferForTest(const scoped_refptr<DecoderBuffer>& buffer,
+ const VideoDecoderConfig& config);
+
} // namespace media
#endif // MEDIA_BASE_TEST_HELPERS_H_
diff --git a/media/base/video_decoder.h b/media/base/video_decoder.h
index 11d8a7f..1c45a28 100644
--- a/media/base/video_decoder.h
+++ b/media/base/video_decoder.h
@@ -13,14 +13,16 @@
namespace media {
-class DemuxerStream;
+class DecoderBuffer;
+class VideoDecoderConfig;
class VideoFrame;
class MEDIA_EXPORT VideoDecoder {
public:
- // Status codes for read operations on VideoDecoder.
+ // Status codes for decode operations on VideoDecoder.
enum Status {
kOk, // Everything went as planned.
+ kNotEnoughData, // Not enough data to produce a video frame.
kDecodeError, // Decoding error happened.
kDecryptError // Decrypting error happened.
};
@@ -28,40 +30,38 @@ class MEDIA_EXPORT VideoDecoder {
VideoDecoder();
virtual ~VideoDecoder();
- // Initializes a VideoDecoder with the given DemuxerStream, executing the
+ // Initializes a VideoDecoder with the given |config|, executing the
// |status_cb| upon completion.
// |statistics_cb| is used to update the global pipeline statistics.
//
// Note:
// 1) The VideoDecoder will be reinitialized if it was initialized before.
// Upon reinitialization, all internal buffered frames will be dropped.
- // 2) This method should not be called during any pending read, reset or stop.
+ // 2) This method should not be called during pending decode, reset or stop.
// 3) No VideoDecoder calls except for Stop() should be made before
// |status_cb| is executed.
- // 4) DemuxerStream should not be accessed after the VideoDecoder is stopped.
- //
- // TODO(xhwang): Make all VideoDecoder implementations reinitializable.
- // See http://crbug.com/233608
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) = 0;
- // Requests a frame to be decoded. The status of the decoder and decoded frame
- // are returned via the provided callback. Only one read may be in flight at
- // any given time.
+ // Requests a |buffer| to be decoded. The status of the decoder and decoded
+ // frame are returned via the provided callback. Only one decode may be in
+ // flight at any given time.
//
// Implementations guarantee that the callback will not be called from within
// this method.
//
- // If the returned status is not kOk, some error has occurred in the video
- // decoder. In this case, the returned frame should always be NULL.
+ // If the returned status is kOk:
+ // - Non-EOS (end of stream) frame contains decoded video data.
+ // - EOS frame indicates the end of the stream.
+ // - NULL frame indicates an aborted decode. This can happen if Reset() or
+ // Stop() is called during the decoding process.
+ // Otherwise the returned frame must be NULL.
//
- // Otherwise, the video decoder is in good shape. In this case, Non-NULL
- // frames contain decoded video data or may indicate the end of the stream.
- // NULL video frames indicate an aborted read. This can happen if the
- // DemuxerStream gets flushed and doesn't have any more data to return.
+ // TODO(xhwang): Rename this to DecodeCB.
typedef base::Callback<void(Status, const scoped_refptr<VideoFrame>&)> ReadCB;
- virtual void Read(const ReadCB& read_cb) = 0;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) = 0;
// Resets decoder state, fulfilling all pending ReadCB and dropping extra
// queued decoded data. After this call, the decoder is back to an initialized
diff --git a/media/filters/decrypting_demuxer_stream.h b/media/filters/decrypting_demuxer_stream.h
index 27ed736..bd75d66 100644
--- a/media/filters/decrypting_demuxer_stream.h
+++ b/media/filters/decrypting_demuxer_stream.h
@@ -37,12 +37,6 @@ class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream {
const PipelineStatusCB& status_cb);
void Reset(const base::Closure& closure);
- // Creates and initializes either |audio_config_| or |video_config_| based on
- // |demuxer_stream_|.
- // TODO(xhwang): Make this private after the hack in VideoFrameStream is
- // removed.
- void InitializeDecoderConfig();
-
// DemuxerStream implementation.
virtual void Read(const ReadCB& read_cb) OVERRIDE;
virtual AudioDecoderConfig audio_decoder_config() OVERRIDE;
@@ -87,6 +81,10 @@ class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream {
// Returns Decryptor::StreamType converted from |stream_type_|.
Decryptor::StreamType GetDecryptorStreamType() const;
+ // Creates and initializes either |audio_config_| or |video_config_| based on
+ // |demuxer_stream_|.
+ void InitializeDecoderConfig();
+
scoped_refptr<base::MessageLoopProxy> message_loop_;
base::WeakPtrFactory<DecryptingDemuxerStream> weak_factory_;
base::WeakPtr<DecryptingDemuxerStream> weak_this_;
diff --git a/media/filters/decrypting_video_decoder.cc b/media/filters/decrypting_video_decoder.cc
index 3c39e33..72f8d48 100644
--- a/media/filters/decrypting_video_decoder.cc
+++ b/media/filters/decrypting_video_decoder.cc
@@ -13,7 +13,6 @@
#include "media/base/bind_to_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decryptor.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/pipeline.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
@@ -26,7 +25,6 @@ DecryptingVideoDecoder::DecryptingVideoDecoder(
: message_loop_(message_loop),
weak_factory_(this),
state_(kUninitialized),
- demuxer_stream_(NULL),
set_decryptor_ready_cb_(set_decryptor_ready_cb),
decryptor_(NULL),
key_added_while_decode_pending_(false),
@@ -34,7 +32,7 @@ DecryptingVideoDecoder::DecryptingVideoDecoder(
}
void DecryptingVideoDecoder::Initialize(
- DemuxerStream* stream,
+ const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) {
DVLOG(2) << "Initialize()";
@@ -44,17 +42,14 @@ void DecryptingVideoDecoder::Initialize(
state_ == kDecodeFinished) << state_;
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
- DCHECK(stream);
+ DCHECK(config.IsValidConfig());
+ DCHECK(config.is_encrypted());
init_cb_ = BindToCurrentLoop(status_cb);
weak_this_ = weak_factory_.GetWeakPtr();
- demuxer_stream_ = stream;
+ config_ = config;
statistics_cb_ = statistics_cb;
- const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config();
- DCHECK(config.IsValidConfig());
- DCHECK(config.is_encrypted());
-
if (state_ == kUninitialized) {
state_ = kDecryptorRequested;
set_decryptor_ready_cb_.Run(BindToCurrentLoop(base::Bind(
@@ -69,14 +64,16 @@ void DecryptingVideoDecoder::Initialize(
&DecryptingVideoDecoder::FinishInitialization, weak_this_)));
}
-void DecryptingVideoDecoder::Read(const ReadCB& read_cb) {
- DVLOG(3) << "Read()";
+void DecryptingVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) {
+ DVLOG(3) << "Decode()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == kIdle ||
state_ == kDecodeFinished ||
state_ == kError) << state_;
DCHECK(!read_cb.is_null());
CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
+
read_cb_ = BindToCurrentLoop(read_cb);
if (state_ == kError) {
@@ -90,15 +87,15 @@ void DecryptingVideoDecoder::Read(const ReadCB& read_cb) {
return;
}
- state_ = kPendingDemuxerRead;
- ReadFromDemuxerStream();
+ pending_buffer_to_decode_ = buffer;
+ state_ = kPendingDecode;
+ DecodePendingBuffer();
}
void DecryptingVideoDecoder::Reset(const base::Closure& closure) {
DVLOG(2) << "Reset() - state: " << state_;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == kIdle ||
- state_ == kPendingDemuxerRead ||
state_ == kPendingDecode ||
state_ == kWaitingForKey ||
state_ == kDecodeFinished ||
@@ -114,7 +111,7 @@ void DecryptingVideoDecoder::Reset(const base::Closure& closure) {
// Defer the resetting process in this case. The |reset_cb_| will be fired
// after the read callback is fired - see DecryptAndDecodeBuffer() and
// DeliverFrame().
- if (state_ == kPendingDemuxerRead || state_ == kPendingDecode) {
+ if (state_ == kPendingDecode) {
DCHECK(!read_cb_.is_null());
return;
}
@@ -181,7 +178,8 @@ void DecryptingVideoDecoder::SetDecryptor(Decryptor* decryptor) {
state_ = kPendingDecoderInit;
decryptor_->InitializeVideoDecoder(
- demuxer_stream_->video_decoder_config(), BindToCurrentLoop(base::Bind(
+ config_,
+ BindToCurrentLoop(base::Bind(
&DecryptingVideoDecoder::FinishInitialization, weak_this_)));
}
@@ -211,47 +209,6 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) {
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
-void DecryptingVideoDecoder::ReadFromDemuxerStream() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
- DCHECK(!read_cb_.is_null());
-
- demuxer_stream_->Read(
- base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, weak_this_));
-}
-
-void DecryptingVideoDecoder::DecryptAndDecodeBuffer(
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- DVLOG(3) << "DecryptAndDecodeBuffer()";
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- if (state_ == kStopped)
- return;
-
- DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
- DCHECK(!read_cb_.is_null());
- DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status;
-
- if (!reset_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- DoReset();
- return;
- }
-
- if (status == DemuxerStream::kAborted) {
- DVLOG(2) << "DecryptAndDecodeBuffer() - kAborted";
- state_ = kIdle;
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- return;
- }
-
- // VideoFrameStream ensures no kConfigChanged is passed to VideoDecoders.
- DCHECK_EQ(status, DemuxerStream::kOk) << status;
- pending_buffer_to_decode_ = buffer;
- state_ = kPendingDecode;
- DecodePendingBuffer();
-}
void DecryptingVideoDecoder::DecodePendingBuffer() {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -339,8 +296,8 @@ void DecryptingVideoDecoder::DeliverFrame(
return;
}
- state_ = kPendingDemuxerRead;
- ReadFromDemuxerStream();
+ state_ = kIdle;
+ base::ResetAndReturn(&read_cb_).Run(kNotEnoughData, NULL);
return;
}
diff --git a/media/filters/decrypting_video_decoder.h b/media/filters/decrypting_video_decoder.h
index 6cbf9bc..af9099f 100644
--- a/media/filters/decrypting_video_decoder.h
+++ b/media/filters/decrypting_video_decoder.h
@@ -8,8 +8,8 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "media/base/decryptor.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
+#include "media/base/video_decoder_config.h"
namespace base {
class MessageLoopProxy;
@@ -32,10 +32,11 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
virtual ~DecryptingVideoDecoder();
// VideoDecoder implementation.
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) OVERRIDE;
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) OVERRIDE;
virtual void Reset(const base::Closure& closure) OVERRIDE;
virtual void Stop(const base::Closure& closure) OVERRIDE;
@@ -48,7 +49,6 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
kDecryptorRequested,
kPendingDecoderInit,
kIdle,
- kPendingDemuxerRead,
kPendingDecode,
kWaitingForKey,
kDecodeFinished,
@@ -62,12 +62,6 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
// Callback for Decryptor::InitializeVideoDecoder() during initialization.
void FinishInitialization(bool success);
- void ReadFromDemuxerStream();
-
- // Callback for DemuxerStream::Read().
- void DecryptAndDecodeBuffer(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer);
-
void DecodePendingBuffer();
// Callback for Decryptor::DecryptAndDecodeVideo().
@@ -96,15 +90,14 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
ReadCB read_cb_;
base::Closure reset_cb_;
- // Pointer to the demuxer stream that will feed us compressed buffers.
- DemuxerStream* demuxer_stream_;
+ VideoDecoderConfig config_;
// Callback to request/cancel decryptor creation notification.
SetDecryptorReadyCB set_decryptor_ready_cb_;
Decryptor* decryptor_;
- // The buffer returned by the demuxer that needs decrypting/decoding.
+ // The buffer that needs decrypting/decoding.
scoped_refptr<media::DecoderBuffer> pending_buffer_to_decode_;
// Indicates the situation where new key is added during pending decode
diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc
index 27d4e53..9d86b4d 100644
--- a/media/filters/decrypting_video_decoder_unittest.cc
+++ b/media/filters/decrypting_video_decoder_unittest.cc
@@ -46,10 +46,6 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
// times across multiple test files. Sadly we can't use static for them.
namespace {
-ACTION_P(ReturnBuffer, buffer) {
- arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
-}
-
ACTION_P(RunCallbackIfNotNull, param) {
if (!arg0.is_null())
arg0.Run(param);
@@ -74,7 +70,6 @@ class DecryptingVideoDecoderTest : public testing::Test {
&DecryptingVideoDecoderTest::RequestDecryptorNotification,
base::Unretained(this)))),
decryptor_(new StrictMock<MockDecryptor>()),
- demuxer_(new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)),
encrypted_buffer_(CreateFakeEncryptedBuffer()),
decoded_video_frame_(VideoFrame::CreateBlackFrame(
TestVideoConfig::NormalCodedSize())),
@@ -90,8 +85,7 @@ class DecryptingVideoDecoderTest : public testing::Test {
void InitializeAndExpectStatus(const VideoDecoderConfig& config,
PipelineStatus status) {
- demuxer_->set_video_decoder_config(config);
- decoder_->Initialize(demuxer_.get(), NewExpectedStatusCB(status),
+ decoder_->Initialize(config, NewExpectedStatusCB(status),
base::Bind(&MockStatisticsCB::OnStatistics,
base::Unretained(&statistics_cb_)));
message_loop_.RunUntilIdle();
@@ -112,6 +106,7 @@ class DecryptingVideoDecoderTest : public testing::Test {
}
void ReadAndExpectFrameReadyWith(
+ const scoped_refptr<DecoderBuffer>& buffer,
VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& video_frame) {
if (status != VideoDecoder::kOk)
@@ -121,68 +116,55 @@ class DecryptingVideoDecoderTest : public testing::Test {
else
EXPECT_CALL(*this, FrameReady(status, video_frame));
- decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
- base::Unretained(this)));
+ decoder_->Decode(buffer,
+ base::Bind(&DecryptingVideoDecoderTest::FrameReady,
+ base::Unretained(this)));
message_loop_.RunUntilIdle();
}
// Sets up expectations and actions to put DecryptingVideoDecoder in an
// active normal decoding state.
void EnterNormalDecodingState() {
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(encrypted_buffer_))
- .WillRepeatedly(ReturnBuffer(DecoderBuffer::CreateEOSBuffer()));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
.WillOnce(RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_))
.WillRepeatedly(RunCallback<1>(Decryptor::kNeedMoreData,
scoped_refptr<VideoFrame>()));
EXPECT_CALL(statistics_cb_, OnStatistics(_));
- ReadAndExpectFrameReadyWith(VideoDecoder::kOk, decoded_video_frame_);
+ ReadAndExpectFrameReadyWith(
+ encrypted_buffer_, VideoDecoder::kOk, decoded_video_frame_);
}
// Sets up expectations and actions to put DecryptingVideoDecoder in an end
// of stream state. This function must be called after
// EnterNormalDecodingState() to work.
void EnterEndOfStreamState() {
- ReadAndExpectFrameReadyWith(VideoDecoder::kOk, end_of_stream_video_frame_);
- }
-
- // Make the read callback pending by saving and not firing it.
- void EnterPendingReadState() {
- EXPECT_TRUE(pending_demuxer_read_cb_.is_null());
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(SaveArg<0>(&pending_demuxer_read_cb_));
- decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
- base::Unretained(this)));
- message_loop_.RunUntilIdle();
- // Make sure the Read() on the decoder triggers a Read() on the demuxer.
- EXPECT_FALSE(pending_demuxer_read_cb_.is_null());
+ ReadAndExpectFrameReadyWith(DecoderBuffer::CreateEOSBuffer(),
+ VideoDecoder::kOk,
+ end_of_stream_video_frame_);
}
// Make the video decode callback pending by saving and not firing it.
void EnterPendingDecodeState() {
EXPECT_TRUE(pending_video_decode_cb_.is_null());
- EXPECT_CALL(*demuxer_, Read(_))
- .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(encrypted_buffer_, _))
.WillOnce(SaveArg<1>(&pending_video_decode_cb_));
- decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
- base::Unretained(this)));
+ decoder_->Decode(encrypted_buffer_,
+ base::Bind(&DecryptingVideoDecoderTest::FrameReady,
+ base::Unretained(this)));
message_loop_.RunUntilIdle();
- // Make sure the Read() on the decoder triggers a DecryptAndDecode() on the
- // decryptor.
+ // Make sure the Decode() on the decoder triggers a DecryptAndDecode() on
+ // the decryptor.
EXPECT_FALSE(pending_video_decode_cb_.is_null());
}
void EnterWaitingForKeyState() {
- EXPECT_CALL(*demuxer_, Read(_))
- .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
.WillRepeatedly(RunCallback<1>(Decryptor::kNoKey, null_video_frame_));
- decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
- base::Unretained(this)));
+ decoder_->Decode(encrypted_buffer_,
+ base::Bind(&DecryptingVideoDecoderTest::FrameReady,
+ base::Unretained(this)));
message_loop_.RunUntilIdle();
}
@@ -232,15 +214,13 @@ class DecryptingVideoDecoderTest : public testing::Test {
base::MessageLoop message_loop_;
scoped_ptr<DecryptingVideoDecoder> decoder_;
scoped_ptr<StrictMock<MockDecryptor> > decryptor_;
- scoped_ptr<StrictMock<MockDemuxerStream> > demuxer_;
MockStatisticsCB statistics_cb_;
- DemuxerStream::ReadCB pending_demuxer_read_cb_;
Decryptor::DecoderInitCB pending_init_cb_;
Decryptor::NewKeyCB key_added_cb_;
Decryptor::VideoDecodeCB pending_video_decode_cb_;
- // Constant buffer/frames to be returned by the |demuxer_| and |decryptor_|.
+ // Constant buffer/frames.
scoped_refptr<DecoderBuffer> encrypted_buffer_;
scoped_refptr<VideoFrame> decoded_video_frame_;
scoped_refptr<VideoFrame> null_video_frame_;
@@ -300,16 +280,16 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_Normal) {
TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) {
Initialize();
- EXPECT_CALL(*demuxer_, Read(_))
- .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
.WillRepeatedly(RunCallback<1>(Decryptor::kError,
- scoped_refptr<VideoFrame>(NULL)));
+ scoped_refptr<VideoFrame>(NULL)));
- ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
+ ReadAndExpectFrameReadyWith(
+ encrypted_buffer_, VideoDecoder::kDecodeError, null_video_frame_);
// After a decode error occurred, all following read returns kDecodeError.
- ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
+ ReadAndExpectFrameReadyWith(
+ encrypted_buffer_, VideoDecoder::kDecodeError, null_video_frame_);
}
// Test the case where the decryptor returns kNeedMoreData to ask for more
@@ -317,9 +297,6 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) {
TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_NeedMoreData) {
Initialize();
- EXPECT_CALL(*demuxer_, Read(_))
- .Times(2)
- .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
.WillOnce(RunCallback<1>(Decryptor::kNeedMoreData,
scoped_refptr<VideoFrame>()))
@@ -328,7 +305,10 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_NeedMoreData) {
EXPECT_CALL(statistics_cb_, OnStatistics(_))
.Times(2);
- ReadAndExpectFrameReadyWith(VideoDecoder::kOk, decoded_video_frame_);
+ ReadAndExpectFrameReadyWith(
+ encrypted_buffer_, VideoDecoder::kNotEnoughData, decoded_video_frame_);
+ ReadAndExpectFrameReadyWith(
+ encrypted_buffer_, VideoDecoder::kOk, decoded_video_frame_);
}
// Test the case where the decryptor receives end-of-stream buffer.
@@ -338,17 +318,6 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) {
EnterEndOfStreamState();
}
-// Test aborted read on the demuxer stream.
-TEST_F(DecryptingVideoDecoderTest, DemuxerRead_Aborted) {
- Initialize();
-
- // ReturnBuffer() with NULL triggers aborted demuxer read.
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>()));
-
- ReadAndExpectFrameReadyWith(VideoDecoder::kOk, null_video_frame_);
-}
-
// Test the case where the a key is added when the decryptor is in
// kWaitingForKey state.
TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) {
@@ -398,35 +367,6 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) {
Reset();
}
-// Test resetting when the decoder is in kPendingDemuxerRead state and the read
-// callback is returned with kOk.
-TEST_F(DecryptingVideoDecoderTest, Reset_DuringDemuxerRead_Ok) {
- Initialize();
- EnterPendingReadState();
-
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
-
- Reset();
- base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kOk,
- encrypted_buffer_);
- message_loop_.RunUntilIdle();
-}
-
-// Test resetting when the decoder is in kPendingDemuxerRead state and the read
-// callback is returned with kAborted.
-TEST_F(DecryptingVideoDecoderTest, Reset_DuringDemuxerRead_Aborted) {
- Initialize();
- EnterPendingReadState();
-
- // Make sure we get a NULL video frame returned.
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
-
- Reset();
- base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kAborted,
- NULL);
- message_loop_.RunUntilIdle();
-}
-
// Test resetting when the decoder is in kPendingDecode state.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecode) {
Initialize();
@@ -466,11 +406,10 @@ TEST_F(DecryptingVideoDecoderTest, Reset_AfterReset) {
// Test stopping when the decoder is in kDecryptorRequested state.
TEST_F(DecryptingVideoDecoderTest, Stop_DuringDecryptorRequested) {
- demuxer_->set_video_decoder_config(TestVideoConfig::NormalEncrypted());
DecryptorReadyCB decryptor_ready_cb;
EXPECT_CALL(*this, RequestDecryptorNotification(_))
.WillOnce(SaveArg<0>(&decryptor_ready_cb));
- decoder_->Initialize(demuxer_.get(),
+ decoder_->Initialize(TestVideoConfig::NormalEncrypted(),
NewExpectedStatusCB(DECODER_ERROR_NOT_SUPPORTED),
base::Bind(&MockStatisticsCB::OnStatistics,
base::Unretained(&statistics_cb_)));
@@ -513,19 +452,6 @@ TEST_F(DecryptingVideoDecoderTest, Stop_DuringIdleAfterDecodedOneFrame) {
Stop();
}
-// Test stopping when the decoder is in kPendingDemuxerRead state.
-TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDemuxerRead) {
- Initialize();
- EnterPendingReadState();
-
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
-
- Stop();
- base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kOk,
- encrypted_buffer_);
- message_loop_.RunUntilIdle();
-}
-
// Test stopping when the decoder is in kPendingDecode state.
TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDecode) {
Initialize();
diff --git a/media/filters/fake_demuxer_stream.cc b/media/filters/fake_demuxer_stream.cc
index 1d5915c..8cab3a3 100644
--- a/media/filters/fake_demuxer_stream.cc
+++ b/media/filters/fake_demuxer_stream.cc
@@ -9,9 +9,9 @@
#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/test_helpers.h"
#include "media/base/video_frame.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
@@ -24,7 +24,6 @@ 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,
@@ -139,19 +138,10 @@ void FakeDemuxerStream::DoRead() {
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());
+ scoped_refptr<DecoderBuffer> buffer = CreateFakeVideoBufferForTest(
+ video_decoder_config_, current_timestamp_, duration_);
// 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_--;
diff --git a/media/filters/fake_demuxer_stream.h b/media/filters/fake_demuxer_stream.h
index 2a82f37..4ed4796 100644
--- a/media/filters/fake_demuxer_stream.h
+++ b/media/filters/fake_demuxer_stream.h
@@ -9,7 +9,6 @@
#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 {
@@ -18,7 +17,7 @@ class MessageLoopProxy;
namespace media {
-class MEDIA_EXPORT FakeDemuxerStream : public DemuxerStream {
+class 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
diff --git a/media/filters/fake_video_decoder.cc b/media/filters/fake_video_decoder.cc
index 9a59d1c..6e0577c 100644
--- a/media/filters/fake_video_decoder.cc
+++ b/media/filters/fake_video_decoder.cc
@@ -9,7 +9,7 @@
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "media/base/bind_to_loop.h"
-#include "media/base/demuxer_stream.h"
+#include "media/base/test_helpers.h"
namespace media {
@@ -17,8 +17,7 @@ FakeVideoDecoder::FakeVideoDecoder(int decoding_delay)
: message_loop_(base::MessageLoopProxy::current()),
weak_factory_(this),
decoding_delay_(decoding_delay),
- state_(UNINITIALIZED),
- demuxer_stream_(NULL) {
+ state_(UNINITIALIZED) {
DCHECK_GE(decoding_delay, 0);
}
@@ -26,20 +25,18 @@ FakeVideoDecoder::~FakeVideoDecoder() {
DCHECK_EQ(state_, UNINITIALIZED);
}
-void FakeVideoDecoder::Initialize(DemuxerStream* stream,
+void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(stream);
- DCHECK(stream->video_decoder_config().IsValidConfig());
+ DCHECK(config.IsValidConfig());
DCHECK(read_cb_.IsNull()) << "No reinitialization during pending read.";
DCHECK(reset_cb_.IsNull()) << "No reinitialization during pending reset.";
weak_this_ = weak_factory_.GetWeakPtr();
- demuxer_stream_ = stream;
statistics_cb_ = statistics_cb;
- current_config_ = stream->video_decoder_config();
+ current_config_ = config;
init_cb_.SetCallback(BindToCurrentLoop(status_cb));
if (!decoded_frames_.empty()) {
@@ -51,14 +48,35 @@ void FakeVideoDecoder::Initialize(DemuxerStream* stream,
init_cb_.RunOrHold(PIPELINE_OK);
}
-void FakeVideoDecoder::Read(const ReadCB& read_cb) {
+void FakeVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(read_cb_.IsNull()) << "Overlapping decodes are not supported.";
DCHECK(reset_cb_.IsNull());
DCHECK_LE(decoded_frames_.size(), static_cast<size_t>(decoding_delay_));
read_cb_.SetCallback(BindToCurrentLoop(read_cb));
- ReadFromDemuxerStream();
+
+ if (buffer->IsEndOfStream() && decoded_frames_.empty()) {
+ read_cb_.RunOrHold(kOk, VideoFrame::CreateEmptyFrame());
+ return;
+ }
+
+ if (!buffer->IsEndOfStream()) {
+ DCHECK(VerifyFakeVideoBufferForTest(buffer, current_config_));
+ scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateColorFrame(
+ current_config_.coded_size(), 0, 0, 0, buffer->GetTimestamp());
+ decoded_frames_.push_back(video_frame);
+
+ if (decoded_frames_.size() <= static_cast<size_t>(decoding_delay_)) {
+ read_cb_.RunOrHold(kNotEnoughData, scoped_refptr<VideoFrame>());
+ return;
+ }
+ }
+
+ scoped_refptr<VideoFrame> frame = decoded_frames_.front();
+ decoded_frames_.pop_front();
+ read_cb_.RunOrHold(kOk, frame);
}
void FakeVideoDecoder::Reset(const base::Closure& closure) {
@@ -142,79 +160,6 @@ void FakeVideoDecoder::SatisfyStop() {
stop_cb_.RunHeldCallback();
}
-void FakeVideoDecoder::ReadFromDemuxerStream() {
- DCHECK_EQ(state_, NORMAL);
- DCHECK(!read_cb_.IsNull());
- demuxer_stream_->Read(base::Bind(&FakeVideoDecoder::BufferReady, weak_this_));
-}
-
-void FakeVideoDecoder::BufferReady(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_EQ(state_, NORMAL);
- DCHECK(!read_cb_.IsNull());
- DCHECK_EQ(status != DemuxerStream::kOk, !buffer.get()) << status;
-
- if (!stop_cb_.IsNull()) {
- read_cb_.RunOrHold(kOk, scoped_refptr<VideoFrame>());
- if (!reset_cb_.IsNull()) {
- DoReset();
- }
- DoStop();
- return;
- }
-
- if (status == DemuxerStream::kConfigChanged) {
- DCHECK(demuxer_stream_->video_decoder_config().IsValidConfig());
- current_config_ = demuxer_stream_->video_decoder_config();
-
- if (reset_cb_.IsNull()) {
- ReadFromDemuxerStream();
- return;
- }
- }
-
- if (!reset_cb_.IsNull()) {
- read_cb_.RunOrHold(kOk, scoped_refptr<VideoFrame>());
- DoReset();
- return;
- }
-
- if (status == DemuxerStream::kAborted) {
- read_cb_.RunOrHold(kOk, scoped_refptr<VideoFrame>());
- return;
- }
-
- DCHECK_EQ(status, DemuxerStream::kOk);
-
- if (buffer->IsEndOfStream() && decoded_frames_.empty()) {
- read_cb_.RunOrHold(kOk, VideoFrame::CreateEmptyFrame());
- return;
- }
-
- if (!buffer->IsEndOfStream()) {
- // Make sure the decoder is always configured with the latest config.
- DCHECK(current_config_.Matches(demuxer_stream_->video_decoder_config()))
- << "Decoder's Current Config: "
- << current_config_.AsHumanReadableString()
- << "DemuxerStream's Current Config: "
- << demuxer_stream_->video_decoder_config().AsHumanReadableString();
-
- scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateColorFrame(
- current_config_.coded_size(), 0, 0, 0, buffer->GetTimestamp());
- decoded_frames_.push_back(video_frame);
-
- if (decoded_frames_.size() <= static_cast<size_t>(decoding_delay_)) {
- ReadFromDemuxerStream();
- return;
- }
- }
-
- scoped_refptr<VideoFrame> frame = decoded_frames_.front();
- decoded_frames_.pop_front();
- read_cb_.RunOrHold(kOk, frame);
-}
-
void FakeVideoDecoder::DoReset() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(read_cb_.IsNull());
@@ -231,7 +176,6 @@ void FakeVideoDecoder::DoStop() {
DCHECK(!stop_cb_.IsNull());
state_ = UNINITIALIZED;
- demuxer_stream_ = NULL;
decoded_frames_.clear();
stop_cb_.RunOrHold();
}
diff --git a/media/filters/fake_video_decoder.h b/media/filters/fake_video_decoder.h
index c82679c..c20e44d 100644
--- a/media/filters/fake_video_decoder.h
+++ b/media/filters/fake_video_decoder.h
@@ -13,8 +13,6 @@
#include "base/memory/weak_ptr.h"
#include "media/base/callback_holder.h"
#include "media/base/decoder_buffer.h"
-#include "media/base/demuxer_stream.h"
-#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"
@@ -29,17 +27,18 @@ class MessageLoopProxy;
namespace media {
-class MEDIA_EXPORT FakeVideoDecoder : public VideoDecoder {
+class FakeVideoDecoder : public VideoDecoder {
public:
// Constructs an object with a decoding delay of |decoding_delay| frames.
explicit FakeVideoDecoder(int decoding_delay);
virtual ~FakeVideoDecoder();
// VideoDecoder implementation.
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) OVERRIDE;
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) OVERRIDE;
virtual void Reset(const base::Closure& closure) OVERRIDE;
virtual void Stop(const base::Closure& closure) OVERRIDE;
@@ -62,12 +61,6 @@ class MEDIA_EXPORT FakeVideoDecoder : public VideoDecoder {
NORMAL
};
- void ReadFromDemuxerStream();
-
- // Callback for DemuxerStream::Read().
- void BufferReady(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer);
-
void DoReset();
void DoStop();
@@ -86,9 +79,6 @@ class MEDIA_EXPORT FakeVideoDecoder : public VideoDecoder {
CallbackHolder<base::Closure> reset_cb_;
CallbackHolder<base::Closure> stop_cb_;
- // Pointer to the demuxer stream that will feed us compressed buffers.
- DemuxerStream* demuxer_stream_;
-
VideoDecoderConfig current_config_;
std::list<scoped_refptr<VideoFrame> > decoded_frames_;
diff --git a/media/filters/fake_video_decoder_unittest.cc b/media/filters/fake_video_decoder_unittest.cc
index fc1cb14..9bd62b1 100644
--- a/media/filters/fake_video_decoder_unittest.cc
+++ b/media/filters/fake_video_decoder_unittest.cc
@@ -9,25 +9,23 @@
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/base/video_frame.h"
-#include "media/filters/fake_demuxer_stream.h"
#include "media/filters/fake_video_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
static const int kDecodingDelay = 9;
-static const int kNumConfigs = 3;
-static const int kNumBuffersInOneConfig = 9;
-static const int kNumInputBuffers = kNumConfigs * kNumBuffersInOneConfig;
+static const int kTotalBuffers = 12;
+static const int kDurationMs = 30;
class FakeVideoDecoderTest : public testing::Test {
public:
FakeVideoDecoderTest()
: decoder_(new FakeVideoDecoder(kDecodingDelay)),
- demuxer_stream_(
- new FakeDemuxerStream(kNumConfigs, kNumBuffersInOneConfig, false)),
+ num_input_buffers_(0),
num_decoded_frames_(0),
- is_read_pending_(false),
+ decode_status_(VideoDecoder::kNotEnoughData),
+ is_decode_pending_(false),
is_reset_pending_(false),
is_stop_pending_(false) {}
@@ -35,12 +33,17 @@ class FakeVideoDecoderTest : public testing::Test {
StopAndExpect(OK);
}
- void Initialize() {
- decoder_->Initialize(demuxer_stream_.get(),
+ void InitializeWithConfig(const VideoDecoderConfig& config) {
+ decoder_->Initialize(config,
NewExpectedStatusCB(PIPELINE_OK),
base::Bind(&MockStatisticsCB::OnStatistics,
base::Unretained(&statistics_cb_)));
message_loop_.RunUntilIdle();
+ current_config_ = config;
+ }
+
+ void Initialize() {
+ InitializeWithConfig(TestVideoConfig::Normal());
}
void EnterPendingInitState() {
@@ -56,19 +59,21 @@ class FakeVideoDecoderTest : public testing::Test {
// Callback for VideoDecoder::Read().
void FrameReady(VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame) {
- DCHECK(is_read_pending_);
- ASSERT_EQ(VideoDecoder::kOk, status);
-
- is_read_pending_ = false;
- frame_read_ = frame;
-
- if (frame.get() && !frame->IsEndOfStream())
+ DCHECK(is_decode_pending_);
+ ASSERT_TRUE(status == VideoDecoder::kOk ||
+ status == VideoDecoder::kNotEnoughData);
+ is_decode_pending_ = false;
+ decode_status_ = status;
+ frame_decoded_ = frame;
+
+ if (frame && !frame->IsEndOfStream())
num_decoded_frames_++;
}
enum CallbackResult {
PENDING,
OK,
+ NOT_ENOUGH_DATA,
ABROTED,
EOS
};
@@ -76,50 +81,86 @@ class FakeVideoDecoderTest : public testing::Test {
void ExpectReadResult(CallbackResult result) {
switch (result) {
case PENDING:
- EXPECT_TRUE(is_read_pending_);
- ASSERT_FALSE(frame_read_.get());
+ EXPECT_TRUE(is_decode_pending_);
+ ASSERT_FALSE(frame_decoded_);
break;
case OK:
- EXPECT_FALSE(is_read_pending_);
- ASSERT_TRUE(frame_read_.get());
- EXPECT_FALSE(frame_read_->IsEndOfStream());
+ EXPECT_FALSE(is_decode_pending_);
+ ASSERT_EQ(VideoDecoder::kOk, decode_status_);
+ ASSERT_TRUE(frame_decoded_);
+ EXPECT_FALSE(frame_decoded_->IsEndOfStream());
+ break;
+ case NOT_ENOUGH_DATA:
+ EXPECT_FALSE(is_decode_pending_);
+ ASSERT_EQ(VideoDecoder::kNotEnoughData, decode_status_);
+ ASSERT_FALSE(frame_decoded_);
break;
case ABROTED:
- EXPECT_FALSE(is_read_pending_);
- EXPECT_FALSE(frame_read_.get());
+ EXPECT_FALSE(is_decode_pending_);
+ ASSERT_EQ(VideoDecoder::kOk, decode_status_);
+ EXPECT_FALSE(frame_decoded_);
break;
case EOS:
- EXPECT_FALSE(is_read_pending_);
- ASSERT_TRUE(frame_read_.get());
- EXPECT_TRUE(frame_read_->IsEndOfStream());
+ EXPECT_FALSE(is_decode_pending_);
+ ASSERT_EQ(VideoDecoder::kOk, decode_status_);
+ ASSERT_TRUE(frame_decoded_);
+ EXPECT_TRUE(frame_decoded_->IsEndOfStream());
break;
}
}
- void ReadOneFrame() {
- frame_read_ = NULL;
- is_read_pending_ = true;
- decoder_->Read(
+ void Decode() {
+ scoped_refptr<DecoderBuffer> buffer;
+
+ if (num_input_buffers_ < kTotalBuffers) {
+ buffer = CreateFakeVideoBufferForTest(
+ current_config_,
+ base::TimeDelta::FromMilliseconds(kDurationMs * num_input_buffers_),
+ base::TimeDelta::FromMilliseconds(kDurationMs));
+ num_input_buffers_++;
+ } else {
+ buffer = DecoderBuffer::CreateEOSBuffer();
+ }
+
+ decode_status_ = VideoDecoder::kDecodeError;
+ frame_decoded_ = NULL;
+ is_decode_pending_ = true;
+
+ decoder_->Decode(
+ buffer,
base::Bind(&FakeVideoDecoderTest::FrameReady, base::Unretained(this)));
message_loop_.RunUntilIdle();
}
+ void ReadOneFrame() {
+ do {
+ Decode();
+ } while (decode_status_ == VideoDecoder::kNotEnoughData &&
+ !is_decode_pending_);
+ }
+
void ReadUntilEOS() {
do {
ReadOneFrame();
- } while (frame_read_.get() && !frame_read_->IsEndOfStream());
+ } while (frame_decoded_ && !frame_decoded_->IsEndOfStream());
}
void EnterPendingReadState() {
+ // Pass the initial NOT_ENOUGH_DATA stage.
+ ReadOneFrame();
decoder_->HoldNextRead();
ReadOneFrame();
ExpectReadResult(PENDING);
}
- void SatisfyRead() {
+ void SatisfyReadAndExpect(CallbackResult result) {
decoder_->SatisfyRead();
message_loop_.RunUntilIdle();
- ExpectReadResult(OK);
+ ExpectReadResult(result);
+ }
+
+ void SatisfyRead() {
+ SatisfyReadAndExpect(OK);
}
// Callback for VideoDecoder::Reset().
@@ -198,49 +239,19 @@ class FakeVideoDecoderTest : public testing::Test {
ExpectStopResult(OK);
}
- // Callback for DemuxerStream::Read so that we can skip frames to trigger a
- // config change.
- void BufferReady(bool* config_changed,
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- if (status == DemuxerStream::kConfigChanged)
- *config_changed = true;
- }
-
- void ChangeConfig() {
- bool config_changed = false;
- while (!config_changed) {
- demuxer_stream_->Read(base::Bind(&FakeVideoDecoderTest::BufferReady,
- base::Unretained(this),
- &config_changed));
- message_loop_.RunUntilIdle();
- }
- }
-
- void EnterPendingDemuxerReadState() {
- demuxer_stream_->HoldNextRead();
- ReadOneFrame();
- }
-
- void SatisfyDemuxerRead() {
- demuxer_stream_->SatisfyRead();
- message_loop_.RunUntilIdle();
- }
-
- void AbortDemuxerRead() {
- demuxer_stream_->Reset();
- message_loop_.RunUntilIdle();
- }
-
base::MessageLoop message_loop_;
+ VideoDecoderConfig current_config_;
+
scoped_ptr<FakeVideoDecoder> decoder_;
- scoped_ptr<FakeDemuxerStream> demuxer_stream_;
MockStatisticsCB statistics_cb_;
+
+ int num_input_buffers_;
int num_decoded_frames_;
// Callback result/status.
- scoped_refptr<VideoFrame> frame_read_;
- bool is_read_pending_;
+ VideoDecoder::Status decode_status_;
+ scoped_refptr<VideoFrame> frame_decoded_;
+ bool is_decode_pending_;
bool is_reset_pending_;
bool is_stop_pending_;
@@ -255,24 +266,15 @@ TEST_F(FakeVideoDecoderTest, Initialize) {
TEST_F(FakeVideoDecoderTest, Read_AllFrames) {
Initialize();
ReadUntilEOS();
- EXPECT_EQ(kNumInputBuffers, num_decoded_frames_);
-}
-
-TEST_F(FakeVideoDecoderTest, Read_AbortedDemuxerRead) {
- Initialize();
- demuxer_stream_->HoldNextRead();
- ReadOneFrame();
- AbortDemuxerRead();
- ExpectReadResult(ABROTED);
+ EXPECT_EQ(kTotalBuffers, num_decoded_frames_);
}
TEST_F(FakeVideoDecoderTest, Read_DecodingDelay) {
Initialize();
- while (demuxer_stream_->num_buffers_returned() < kNumInputBuffers) {
+ while (num_input_buffers_ < kTotalBuffers) {
ReadOneFrame();
- EXPECT_EQ(demuxer_stream_->num_buffers_returned(),
- num_decoded_frames_ + kDecodingDelay);
+ EXPECT_EQ(num_input_buffers_, num_decoded_frames_ + kDecodingDelay);
}
}
@@ -280,25 +282,32 @@ TEST_F(FakeVideoDecoderTest, Read_ZeroDelay) {
decoder_.reset(new FakeVideoDecoder(0));
Initialize();
- while (demuxer_stream_->num_buffers_returned() < kNumInputBuffers) {
+ while (num_input_buffers_ < kTotalBuffers) {
ReadOneFrame();
- EXPECT_EQ(demuxer_stream_->num_buffers_returned(), num_decoded_frames_);
+ EXPECT_EQ(num_input_buffers_, num_decoded_frames_);
}
}
-TEST_F(FakeVideoDecoderTest, Read_Pending) {
+TEST_F(FakeVideoDecoderTest, Read_Pending_NotEnoughData) {
Initialize();
+ decoder_->HoldNextRead();
+ ReadOneFrame();
+ ExpectReadResult(PENDING);
+ SatisfyReadAndExpect(NOT_ENOUGH_DATA);
+}
+
+TEST_F(FakeVideoDecoderTest, Read_Pending_OK) {
+ Initialize();
+ ReadOneFrame();
EnterPendingReadState();
- SatisfyRead();
+ SatisfyReadAndExpect(OK);
}
TEST_F(FakeVideoDecoderTest, Reinitialize) {
Initialize();
- VideoDecoderConfig old_config = demuxer_stream_->video_decoder_config();
- ChangeConfig();
- VideoDecoderConfig new_config = demuxer_stream_->video_decoder_config();
- EXPECT_FALSE(new_config.Matches(old_config));
- Initialize();
+ ReadOneFrame();
+ InitializeWithConfig(TestVideoConfig::Large());
+ ReadOneFrame();
}
// Reinitializing the decoder during the middle of the decoding process can
@@ -308,7 +317,7 @@ TEST_F(FakeVideoDecoderTest, Reinitialize_FrameDropped) {
ReadOneFrame();
Initialize();
ReadUntilEOS();
- EXPECT_LT(num_decoded_frames_, kNumInputBuffers);
+ EXPECT_LT(num_decoded_frames_, kTotalBuffers);
}
TEST_F(FakeVideoDecoderTest, Reset) {
@@ -317,22 +326,6 @@ TEST_F(FakeVideoDecoderTest, Reset) {
ResetAndExpect(OK);
}
-TEST_F(FakeVideoDecoderTest, Reset_DuringPendingDemuxerRead) {
- Initialize();
- EnterPendingDemuxerReadState();
- ResetAndExpect(PENDING);
- SatisfyDemuxerRead();
- ExpectReadResult(ABROTED);
-}
-
-TEST_F(FakeVideoDecoderTest, Reset_DuringPendingDemuxerRead_Aborted) {
- Initialize();
- EnterPendingDemuxerReadState();
- ResetAndExpect(PENDING);
- AbortDemuxerRead();
- ExpectReadResult(ABROTED);
-}
-
TEST_F(FakeVideoDecoderTest, Reset_DuringPendingRead) {
Initialize();
EnterPendingReadState();
@@ -368,25 +361,6 @@ TEST_F(FakeVideoDecoderTest, Stop_DuringPendingInitialization) {
SatisfyStop();
}
-TEST_F(FakeVideoDecoderTest, Stop_DuringPendingDemuxerRead) {
- Initialize();
- EnterPendingDemuxerReadState();
- StopAndExpect(PENDING);
- SatisfyDemuxerRead();
- ExpectReadResult(ABROTED);
-}
-
-TEST_F(FakeVideoDecoderTest, Stop_DuringPendingDemuxerRead_Aborted) {
- Initialize();
- EnterPendingDemuxerReadState();
- ResetAndExpect(PENDING);
- StopAndExpect(PENDING);
- SatisfyDemuxerRead();
- ExpectReadResult(ABROTED);
- ExpectResetResult(OK);
- ExpectStopResult(OK);
-}
-
TEST_F(FakeVideoDecoderTest, Stop_DuringPendingRead) {
Initialize();
EnterPendingReadState();
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
index ed1e6fd..bc7773a 100644
--- a/media/filters/ffmpeg_video_decoder.cc
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -15,7 +15,6 @@
#include "base/strings/string_number_conversions.h"
#include "media/base/bind_to_loop.h"
#include "media/base/decoder_buffer.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/limits.h"
#include "media/base/media_switches.h"
#include "media/base/pipeline.h"
@@ -61,8 +60,7 @@ FFmpegVideoDecoder::FFmpegVideoDecoder(
weak_factory_(this),
state_(kUninitialized),
codec_context_(NULL),
- av_frame_(NULL),
- demuxer_stream_(NULL) {
+ av_frame_(NULL) {
}
int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context,
@@ -88,7 +86,7 @@ int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context,
codec_context->sample_aspect_ratio.num,
codec_context->sample_aspect_ratio.den);
} else {
- natural_size = demuxer_stream_->video_decoder_config().natural_size();
+ natural_size = config_.natural_size();
}
if (!VideoFrame::IsValidConfig(format, size, gfx::Rect(size), natural_size))
@@ -131,22 +129,22 @@ static void ReleaseVideoBufferImpl(AVCodecContext* s, AVFrame* frame) {
frame->opaque = NULL;
}
-void FFmpegVideoDecoder::Initialize(DemuxerStream* stream,
+void FFmpegVideoDecoder::Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(stream);
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
+ DCHECK(!config.is_encrypted());
FFmpegGlue::InitializeFFmpeg();
weak_this_ = weak_factory_.GetWeakPtr();
- demuxer_stream_ = stream;
+ config_ = config;
statistics_cb_ = statistics_cb;
PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb);
- if (!ConfigureDecoder()) {
+ if (!config.IsValidConfig() || !ConfigureDecoder()) {
initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
@@ -156,7 +154,8 @@ void FFmpegVideoDecoder::Initialize(DemuxerStream* stream,
initialize_cb.Run(PIPELINE_OK);
}
-void FFmpegVideoDecoder::Read(const ReadCB& read_cb) {
+void FFmpegVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(!read_cb.is_null());
CHECK_NE(state_, kUninitialized);
@@ -174,7 +173,7 @@ void FFmpegVideoDecoder::Read(const ReadCB& read_cb) {
return;
}
- ReadFromDemuxerStream();
+ DecodeBuffer(buffer);
}
void FFmpegVideoDecoder::Reset(const base::Closure& closure) {
@@ -221,45 +220,6 @@ FFmpegVideoDecoder::~FFmpegVideoDecoder() {
DCHECK(!av_frame_);
}
-void FFmpegVideoDecoder::ReadFromDemuxerStream() {
- DCHECK_NE(state_, kUninitialized);
- DCHECK_NE(state_, kDecodeFinished);
- DCHECK_NE(state_, kError);
- DCHECK(!read_cb_.is_null());
-
- demuxer_stream_->Read(base::Bind(
- &FFmpegVideoDecoder::BufferReady, weak_this_));
-}
-
-void FFmpegVideoDecoder::BufferReady(
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_NE(state_, kDecodeFinished);
- DCHECK_NE(state_, kError);
- DCHECK_EQ(status != DemuxerStream::kOk, !buffer.get()) << status;
-
- if (state_ == kUninitialized)
- return;
-
- DCHECK(!read_cb_.is_null());
-
- if (!reset_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- DoReset();
- return;
- }
-
- if (status == DemuxerStream::kAborted) {
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- return;
- }
-
- // VideoFrameStream ensures no kConfigChanged is passed to VideoDecoders.
- DCHECK_EQ(status, DemuxerStream::kOk) << status;
- DecodeBuffer(buffer);
-}
-
void FFmpegVideoDecoder::DecodeBuffer(
const scoped_refptr<DecoderBuffer>& buffer) {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -268,7 +228,7 @@ void FFmpegVideoDecoder::DecodeBuffer(
DCHECK_NE(state_, kError);
DCHECK(reset_cb_.is_null());
DCHECK(!read_cb_.is_null());
- DCHECK(buffer.get());
+ DCHECK(buffer);
// During decode, because reads are issued asynchronously, it is possible to
// receive multiple end of stream buffers since each read is acked. When the
@@ -304,7 +264,7 @@ void FFmpegVideoDecoder::DecodeBuffer(
}
scoped_refptr<VideoFrame> video_frame;
- if (!Decode(buffer, &video_frame)) {
+ if (!FFmpegDecode(buffer, &video_frame)) {
state_ = kError;
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
return;
@@ -325,14 +285,14 @@ void FFmpegVideoDecoder::DecodeBuffer(
return;
}
- ReadFromDemuxerStream();
+ base::ResetAndReturn(&read_cb_).Run(kNotEnoughData, NULL);
return;
}
base::ResetAndReturn(&read_cb_).Run(kOk, video_frame);
}
-bool FFmpegVideoDecoder::Decode(
+bool FFmpegVideoDecoder::FFmpegDecode(
const scoped_refptr<DecoderBuffer>& buffer,
scoped_refptr<VideoFrame>* video_frame) {
DCHECK(video_frame);
@@ -417,24 +377,12 @@ void FFmpegVideoDecoder::ReleaseFFmpegResources() {
}
bool FFmpegVideoDecoder::ConfigureDecoder() {
- const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config();
-
- if (!config.IsValidConfig()) {
- DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString();
- return false;
- }
-
- if (config.is_encrypted()) {
- DLOG(ERROR) << "Encrypted video stream not supported.";
- return false;
- }
-
// Release existing decoder resources if necessary.
ReleaseFFmpegResources();
// Initialize AVCodecContext structure.
codec_context_ = avcodec_alloc_context3(NULL);
- VideoDecoderConfigToAVCodecContext(config, codec_context_);
+ VideoDecoderConfigToAVCodecContext(config_, codec_context_);
// Enable motion vector search (potentially slow), strong deblocking filter
// for damaged macroblocks, and set our error detection sensitivity.
diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h
index 2db72a2..c2b0898 100644
--- a/media/filters/ffmpeg_video_decoder.h
+++ b/media/filters/ffmpeg_video_decoder.h
@@ -9,8 +9,8 @@
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
+#include "media/base/video_decoder_config.h"
struct AVCodecContext;
struct AVFrame;
@@ -30,10 +30,11 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder {
virtual ~FFmpegVideoDecoder();
// VideoDecoder implementation.
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) OVERRIDE;
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) OVERRIDE;
virtual void Reset(const base::Closure& closure) OVERRIDE;
virtual void Stop(const base::Closure& closure) OVERRIDE;
@@ -51,15 +52,10 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder {
kError
};
- // Reads from the demuxer stream and corresponding read callback.
- void ReadFromDemuxerStream();
- void BufferReady(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer);
-
// Handles decoding an unencrypted encoded buffer.
void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer);
- bool Decode(const scoped_refptr<DecoderBuffer>& buffer,
- scoped_refptr<VideoFrame>* video_frame);
+ bool FFmpegDecode(const scoped_refptr<DecoderBuffer>& buffer,
+ scoped_refptr<VideoFrame>* video_frame);
// Handles (re-)initializing the decoder with a (new) config.
// Returns true if initialization was successful.
@@ -87,8 +83,7 @@ class MEDIA_EXPORT FFmpegVideoDecoder : public VideoDecoder {
AVCodecContext* codec_context_;
AVFrame* av_frame_;
- // Pointer to the demuxer stream that will feed us compressed buffers.
- DemuxerStream* demuxer_stream_;
+ VideoDecoderConfig config_;
DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoder);
};
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index f6ac974..c3f5fd5 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -46,7 +47,6 @@ class FFmpegVideoDecoderTest : public testing::Test {
public:
FFmpegVideoDecoderTest()
: decoder_(new FFmpegVideoDecoder(message_loop_.message_loop_proxy())),
- demuxer_(new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)),
read_cb_(base::Bind(&FFmpegVideoDecoderTest::FrameReady,
base::Unretained(this))) {
FFmpegGlue::InitializeFFmpeg();
@@ -68,11 +68,9 @@ class FFmpegVideoDecoderTest : public testing::Test {
void InitializeWithConfigAndStatus(const VideoDecoderConfig& config,
PipelineStatus status) {
- demuxer_->set_video_decoder_config(config);
- decoder_->Initialize(demuxer_.get(), NewExpectedStatusCB(status),
+ decoder_->Initialize(config, NewExpectedStatusCB(status),
base::Bind(&MockStatisticsCB::OnStatistics,
base::Unretained(&statistics_cb_)));
-
message_loop_.RunUntilIdle();
}
@@ -109,14 +107,58 @@ class FFmpegVideoDecoderTest : public testing::Test {
// Sets up expectations and actions to put FFmpegVideoDecoder in an end
// of stream state.
void EnterEndOfStreamState() {
- scoped_refptr<VideoFrame> video_frame;
VideoDecoder::Status status;
- Read(&status, &video_frame);
+ scoped_refptr<VideoFrame> video_frame;
+ DecodeSingleFrame(end_of_stream_buffer_, &status, &video_frame);
EXPECT_EQ(VideoDecoder::kOk, status);
ASSERT_TRUE(video_frame.get());
EXPECT_TRUE(video_frame->IsEndOfStream());
}
+ typedef std::vector<scoped_refptr<DecoderBuffer> > InputBuffers;
+ typedef std::vector<scoped_refptr<VideoFrame> > OutputFrames;
+
+ // Decodes all buffers in |input_buffers| and push all successfully decoded
+ // output frames (excluding EOS frames) into |output_frames|.
+ // Returns the last decode status returned by the decoder.
+ VideoDecoder::Status DecodeMultipleFrames(const InputBuffers& input_buffers,
+ OutputFrames* output_frames) {
+ InputBuffers::const_iterator input_iter = input_buffers.begin();
+
+ for (;;) {
+ // Prepare input buffer.
+ scoped_refptr<DecoderBuffer> buffer;
+ if (input_iter != input_buffers.end()) {
+ buffer = *input_iter;
+ ++input_iter;
+ } else {
+ buffer = end_of_stream_buffer_;
+ }
+
+ VideoDecoder::Status status;
+ scoped_refptr<VideoFrame> frame;
+ Decode(buffer, &status, &frame);
+
+ switch (status) {
+ case VideoDecoder::kOk:
+ DCHECK(frame);
+ if (!frame->IsEndOfStream()) {
+ output_frames->push_back(frame);
+ continue;
+ } else { // EOS
+ return status;
+ }
+ case VideoDecoder::kNotEnoughData:
+ DCHECK(!frame);
+ continue;
+ case VideoDecoder::kDecodeError:
+ case VideoDecoder::kDecryptError:
+ DCHECK(!frame);
+ return status;
+ }
+ }
+ }
+
// Decodes the single compressed frame in |buffer| and writes the
// uncompressed output to |video_frame|. This method works with single
// and multithreaded decoders. End of stream buffers are used to trigger
@@ -124,13 +166,23 @@ class FFmpegVideoDecoderTest : public testing::Test {
void DecodeSingleFrame(const scoped_refptr<DecoderBuffer>& buffer,
VideoDecoder::Status* status,
scoped_refptr<VideoFrame>* video_frame) {
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(buffer))
- .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+ InputBuffers input_buffers;
+ input_buffers.push_back(buffer);
+
+ if (!buffer->IsEndOfStream())
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
- EXPECT_CALL(statistics_cb_, OnStatistics(_));
+ OutputFrames output_frames;
+ *status = DecodeMultipleFrames(input_buffers, &output_frames);
- Read(status, video_frame);
+ if (*status != VideoDecoder::kOk)
+ return;
+
+ ASSERT_LE(output_frames.size(), 1U);
+ if (output_frames.size() == 1U)
+ *video_frame = output_frames[0];
+ else
+ *video_frame = VideoFrame::CreateEmptyFrame();
}
// Decodes |i_frame_buffer_| and then decodes the data contained in
@@ -140,44 +192,40 @@ class FFmpegVideoDecoderTest : public testing::Test {
int expected_width,
int expected_height) {
Initialize();
-
- VideoDecoder::Status status_a;
- VideoDecoder::Status status_b;
- scoped_refptr<VideoFrame> video_frame_a;
- scoped_refptr<VideoFrame> video_frame_b;
-
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(test_file_name);
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(i_frame_buffer_))
- .WillOnce(ReturnBuffer(buffer))
- .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+ InputBuffers input_buffers;
+ input_buffers.push_back(i_frame_buffer_);
+ input_buffers.push_back(buffer);
EXPECT_CALL(statistics_cb_, OnStatistics(_))
.Times(2);
- Read(&status_a, &video_frame_a);
- Read(&status_b, &video_frame_b);
+ OutputFrames output_frames;
+ VideoDecoder::Status status =
+ DecodeMultipleFrames(input_buffers, &output_frames);
+
+ EXPECT_EQ(VideoDecoder::kOk, status);
+ ASSERT_EQ(2U, output_frames.size());
gfx::Size original_size = kVisibleRect.size();
- EXPECT_EQ(VideoDecoder::kOk, status_a);
- EXPECT_EQ(VideoDecoder::kOk, status_b);
- ASSERT_TRUE(video_frame_a.get());
- ASSERT_TRUE(video_frame_b.get());
EXPECT_EQ(original_size.width(),
- video_frame_a->visible_rect().size().width());
+ output_frames[0]->visible_rect().size().width());
EXPECT_EQ(original_size.height(),
- video_frame_a->visible_rect().size().height());
- EXPECT_EQ(expected_width, video_frame_b->visible_rect().size().width());
- EXPECT_EQ(expected_height, video_frame_b->visible_rect().size().height());
+ output_frames[0]->visible_rect().size().height());
+ EXPECT_EQ(expected_width,
+ output_frames[1]->visible_rect().size().width());
+ EXPECT_EQ(expected_height,
+ output_frames[1]->visible_rect().size().height());
}
- void Read(VideoDecoder::Status* status,
- scoped_refptr<VideoFrame>* video_frame) {
+ void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ VideoDecoder::Status* status,
+ scoped_refptr<VideoFrame>* video_frame) {
EXPECT_CALL(*this, FrameReady(_, _))
.WillOnce(DoAll(SaveArg<0>(status), SaveArg<1>(video_frame)));
- decoder_->Read(read_cb_);
+ decoder_->Decode(buffer, read_cb_);
message_loop_.RunUntilIdle();
}
@@ -187,7 +235,6 @@ class FFmpegVideoDecoderTest : public testing::Test {
base::MessageLoop message_loop_;
scoped_ptr<FFmpegVideoDecoder> decoder_;
- scoped_ptr<StrictMock<MockDemuxerStream> > demuxer_;
MockStatisticsCB statistics_cb_;
VideoDecoder::ReadCB read_cb_;
@@ -213,7 +260,7 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedDecoder) {
}
TEST_F(FFmpegVideoDecoderTest, Initialize_UnsupportedPixelFormat) {
- // Ensure decoder handles unsupport pixel formats without crashing.
+ // Ensure decoder handles unsupported pixel formats without crashing.
VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
VideoFrame::INVALID,
kCodedSize, kVisibleRect, kNaturalSize,
@@ -331,63 +378,46 @@ TEST_F(FFmpegVideoDecoderTest, DecodeFrame_0ByteFrame) {
scoped_refptr<DecoderBuffer> zero_byte_buffer = new DecoderBuffer(0);
- VideoDecoder::Status status_a;
- VideoDecoder::Status status_b;
- VideoDecoder::Status status_c;
- scoped_refptr<VideoFrame> video_frame_a;
- scoped_refptr<VideoFrame> video_frame_b;
- scoped_refptr<VideoFrame> video_frame_c;
-
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(i_frame_buffer_))
- .WillOnce(ReturnBuffer(zero_byte_buffer))
- .WillOnce(ReturnBuffer(i_frame_buffer_))
- .WillRepeatedly(ReturnBuffer(end_of_stream_buffer_));
+ InputBuffers input_buffers;
+ input_buffers.push_back(i_frame_buffer_);
+ input_buffers.push_back(zero_byte_buffer);
+ input_buffers.push_back(i_frame_buffer_);
EXPECT_CALL(statistics_cb_, OnStatistics(_))
.Times(2);
- Read(&status_a, &video_frame_a);
- Read(&status_b, &video_frame_b);
- Read(&status_c, &video_frame_c);
+ OutputFrames output_frames;
+ VideoDecoder::Status status =
+ DecodeMultipleFrames(input_buffers, &output_frames);
- EXPECT_EQ(VideoDecoder::kOk, status_a);
- EXPECT_EQ(VideoDecoder::kOk, status_b);
- EXPECT_EQ(VideoDecoder::kOk, status_c);
-
- ASSERT_TRUE(video_frame_a.get());
- ASSERT_TRUE(video_frame_b.get());
- ASSERT_TRUE(video_frame_c.get());
+ EXPECT_EQ(VideoDecoder::kOk, status);
+ ASSERT_EQ(2U, output_frames.size());
- EXPECT_FALSE(video_frame_a->IsEndOfStream());
- EXPECT_FALSE(video_frame_b->IsEndOfStream());
- EXPECT_TRUE(video_frame_c->IsEndOfStream());
+ EXPECT_FALSE(output_frames[0]->IsEndOfStream());
+ EXPECT_FALSE(output_frames[1]->IsEndOfStream());
}
TEST_F(FFmpegVideoDecoderTest, DecodeFrame_DecodeError) {
Initialize();
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(corrupt_i_frame_buffer_))
- .WillRepeatedly(ReturnBuffer(i_frame_buffer_));
+ InputBuffers input_buffers;
+ input_buffers.push_back(corrupt_i_frame_buffer_);
+ input_buffers.push_back(i_frame_buffer_);
+ input_buffers.push_back(i_frame_buffer_);
// The error is only raised on the second decode attempt, so we expect at
// least one successful decode but we don't expect FrameReady() to be
// executed as an error is raised instead.
EXPECT_CALL(statistics_cb_, OnStatistics(_));
- // Our read should still get satisfied with end of stream frame during an
- // error.
- VideoDecoder::Status status;
- scoped_refptr<VideoFrame> video_frame;
- Read(&status, &video_frame);
- EXPECT_EQ(VideoDecoder::kDecodeError, status);
- EXPECT_FALSE(video_frame.get());
+ OutputFrames output_frames;
+ VideoDecoder::Status status =
+ DecodeMultipleFrames(input_buffers, &output_frames);
- // After a decode error occurred, all following read will return kDecodeError.
- Read(&status, &video_frame);
EXPECT_EQ(VideoDecoder::kDecodeError, status);
- EXPECT_FALSE(video_frame.get());
+ // After a decode error occurred, all following decodes will return
+ // kDecodeError. Therefore, no |output_frames| will be available.
+ ASSERT_TRUE(output_frames.empty());
}
// Multi-threaded decoders have different behavior than single-threaded
@@ -452,25 +482,6 @@ TEST_F(FFmpegVideoDecoderTest, Reset_EndOfStream) {
Reset();
}
-// Test resetting when there is a pending read on the demuxer.
-TEST_F(FFmpegVideoDecoderTest, Reset_DuringPendingRead) {
- Initialize();
-
- // Request a read on the decoder and ensure the demuxer has been called.
- DemuxerStream::ReadCB read_cb;
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(SaveArg<0>(&read_cb));
- decoder_->Read(read_cb_);
- ASSERT_FALSE(read_cb.is_null());
-
- // Reset the decoder.
- Reset();
-
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
-
- read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
-}
-
// Test stopping when decoder has initialized but not decoded.
TEST_F(FFmpegVideoDecoderTest, Stop_Initialized) {
Initialize();
@@ -492,77 +503,4 @@ TEST_F(FFmpegVideoDecoderTest, Stop_EndOfStream) {
Stop();
}
-// Test stopping when there is a pending read on the demuxer.
-TEST_F(FFmpegVideoDecoderTest, Stop_DuringPendingRead) {
- Initialize();
-
- // Request a read on the decoder and ensure the demuxer has been called.
- DemuxerStream::ReadCB read_cb;
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(SaveArg<0>(&read_cb));
- decoder_->Read(read_cb_);
- ASSERT_FALSE(read_cb.is_null());
-
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
-
- Stop();
-
- read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
-}
-
-// Test stopping when there is a pending read on the demuxer.
-TEST_F(FFmpegVideoDecoderTest, Stop_DuringPendingReadAndReset) {
- Initialize();
-
- // Request a read on the decoder and ensure the demuxer has been called.
- DemuxerStream::ReadCB read_cb;
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(SaveArg<0>(&read_cb));
- decoder_->Read(read_cb_);
- ASSERT_FALSE(read_cb.is_null());
-
- Reset();
-
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
- Stop();
-
- read_cb.Run(DemuxerStream::kOk, i_frame_buffer_);
-}
-
-// Test aborted read on the demuxer stream.
-TEST_F(FFmpegVideoDecoderTest, DemuxerRead_Aborted) {
- Initialize();
-
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>()));
-
- VideoDecoder::Status status;
- scoped_refptr<VideoFrame> video_frame;
-
- Read(&status, &video_frame);
-
- EXPECT_EQ(VideoDecoder::kOk, status);
- EXPECT_FALSE(video_frame.get());
-}
-
-// Test aborted read on the demuxer stream during pending reset.
-TEST_F(FFmpegVideoDecoderTest, DemuxerRead_AbortedDuringReset) {
- Initialize();
-
- // Request a read on the decoder and ensure the demuxer has been called.
- DemuxerStream::ReadCB read_cb;
- EXPECT_CALL(*demuxer_, Read(_))
- .WillOnce(SaveArg<0>(&read_cb));
- decoder_->Read(read_cb_);
- ASSERT_FALSE(read_cb.is_null());
-
- // Reset while there is still an outstanding read on the demuxer.
- Reset();
-
- // Signal an aborted demuxer read: a NULL video frame should be returned.
- EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
- read_cb.Run(DemuxerStream::kAborted, NULL);
- message_loop_.RunUntilIdle();
-}
-
} // namespace media
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 8cf51ad..fcb837d 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -4,6 +4,8 @@
#include "media/filters/gpu_video_decoder.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/cpu.h"
@@ -12,7 +14,6 @@
#include "base/task_runner_util.h"
#include "media/base/bind_to_loop.h"
#include "media/base/decoder_buffer.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/pipeline.h"
#include "media/base/pipeline_status.h"
#include "media/base/video_decoder_config.h"
@@ -160,14 +161,12 @@ GpuVideoDecoder::BufferData::~BufferData() {}
GpuVideoDecoder::GpuVideoDecoder(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
const scoped_refptr<Factories>& factories)
- : demuxer_stream_(NULL),
- needs_bitstream_conversion_(false),
+ : needs_bitstream_conversion_(false),
gvd_loop_proxy_(message_loop),
weak_factory_(this),
vda_loop_proxy_(factories->GetMessageLoop()),
factories_(factories),
state_(kNormal),
- demuxer_read_in_progress_(false),
decoder_texture_target_(0),
next_picture_buffer_id_(0),
next_bitstream_buffer_id_(0),
@@ -176,6 +175,7 @@ GpuVideoDecoder::GpuVideoDecoder(
}
void GpuVideoDecoder::Reset(const base::Closure& closure) {
+ DVLOG(3) << "Reset()";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
if (state_ == kDrainingDecoder && !factories_->IsAborted()) {
@@ -214,7 +214,6 @@ void GpuVideoDecoder::Stop(const base::Closure& closure) {
EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
if (!pending_reset_cb_.is_null())
base::ResetAndReturn(&pending_reset_cb_).Run();
- demuxer_stream_ = NULL;
BindToCurrentLoop(closure).Run();
}
@@ -234,11 +233,13 @@ static bool IsCodedSizeSupported(const gfx::Size& coded_size) {
return os_large_video_support && hw_large_video_support;
}
-void GpuVideoDecoder::Initialize(DemuxerStream* stream,
+void GpuVideoDecoder::Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& orig_status_cb,
const StatisticsCB& statistics_cb) {
+ DVLOG(3) << "Initialize()";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
- DCHECK(stream);
+ DCHECK(config.IsValidConfig());
+ DCHECK(!config.is_encrypted());
weak_this_ = weak_factory_.GetWeakPtr();
@@ -246,7 +247,7 @@ void GpuVideoDecoder::Initialize(DemuxerStream* stream,
"Media.GpuVideoDecoderInitializeStatus",
BindToCurrentLoop(orig_status_cb));
- if (demuxer_stream_) {
+ if (config_.IsValidConfig()) {
// TODO(xhwang): Make GpuVideoDecoder reinitializable.
// See http://crbug.com/233608
DVLOG(1) << "GpuVideoDecoder reinitialization not supported.";
@@ -254,10 +255,6 @@ void GpuVideoDecoder::Initialize(DemuxerStream* stream,
return;
}
- const VideoDecoderConfig& config = stream->video_decoder_config();
- DCHECK(config.IsValidConfig());
- DCHECK(!config.is_encrypted());
-
if (!IsCodedSizeSupported(config.coded_size())) {
status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
return;
@@ -271,11 +268,11 @@ void GpuVideoDecoder::Initialize(DemuxerStream* stream,
return;
}
- demuxer_stream_ = stream;
+ config_ = config;
statistics_cb_ = statistics_cb;
needs_bitstream_conversion_ = (config.codec() == kCodecH264);
- DVLOG(1) << "GpuVideoDecoder::Initialize() succeeded.";
+ DVLOG(3) << "GpuVideoDecoder::Initialize() succeeded.";
PostTaskAndReplyWithResult(
vda_loop_proxy_.get(),
FROM_HERE,
@@ -333,72 +330,37 @@ void GpuVideoDecoder::DestroyVDA() {
DestroyTextures();
}
-void GpuVideoDecoder::Read(const ReadCB& read_cb) {
+void GpuVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) {
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
DCHECK(pending_reset_cb_.is_null());
DCHECK(pending_read_cb_.is_null());
+
pending_read_cb_ = BindToCurrentLoop(read_cb);
- if (state_ == kError) {
+ if (state_ == kError || !vda_) {
base::ResetAndReturn(&pending_read_cb_).Run(kDecodeError, NULL);
return;
}
- // TODO(xhwang): It's odd that we return kOk after VDA has been released.
- // Fix this and simplify cases.
- if (!vda_) {
- base::ResetAndReturn(&pending_read_cb_).Run(
- kOk, VideoFrame::CreateEmptyFrame());
- return;
- }
-
- if (!ready_video_frames_.empty()) {
- EnqueueFrameAndTriggerFrameDelivery(NULL);
- return;
- }
-
switch (state_) {
case kDecoderDrained:
+ if (!ready_video_frames_.empty()) {
+ EnqueueFrameAndTriggerFrameDelivery(NULL);
+ return;
+ }
state_ = kNormal;
// Fall-through.
case kNormal:
- EnsureDemuxOrDecode();
break;
case kDrainingDecoder:
+ DCHECK(buffer->IsEndOfStream());
// Do nothing. Will be satisfied either by a PictureReady or
// NotifyFlushDone below.
- break;
+ return;
case kError:
NOTREACHED();
- break;
- }
-}
-
-bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() {
- return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes;
-}
-
-void GpuVideoDecoder::RequestBufferDecode(
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
- DCHECK_EQ(status != DemuxerStream::kOk, !buffer.get()) << status;
-
- demuxer_read_in_progress_ = false;
-
- if (status == DemuxerStream::kAborted) {
- if (pending_read_cb_.is_null())
return;
- base::ResetAndReturn(&pending_read_cb_).Run(kOk, NULL);
- return;
- }
-
- // VideoFrameStream ensures no kConfigChanged is passed to VideoDecoders.
- DCHECK_EQ(status, DemuxerStream::kOk) << status;
-
- if (!vda_) {
- EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
- return;
}
if (buffer->IsEndOfStream()) {
@@ -410,13 +372,12 @@ void GpuVideoDecoder::RequestBufferDecode(
return;
}
- if (!pending_reset_cb_.is_null())
- return;
-
size_t size = buffer->GetDataSize();
SHMBuffer* shm_buffer = GetSHM(size);
- if (!shm_buffer)
+ if (!shm_buffer) {
+ base::ResetAndReturn(&pending_read_cb_).Run(kDecodeError, NULL);
return;
+ }
memcpy(shm_buffer->shm->memory(), buffer->GetData(), size);
BitstreamBuffer bitstream_buffer(
@@ -431,19 +392,25 @@ void GpuVideoDecoder::RequestBufferDecode(
vda_loop_proxy_->PostTask(FROM_HERE, base::Bind(
&VideoDecodeAccelerator::Decode, weak_vda_, bitstream_buffer));
- if (CanMoreDecodeWorkBeDone()) {
- // Force post here to prevent reentrancy into DemuxerStream.
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind(
- &GpuVideoDecoder::EnsureDemuxOrDecode, weak_this_));
+ if (!ready_video_frames_.empty()) {
+ EnqueueFrameAndTriggerFrameDelivery(NULL);
+ return;
}
+
+ if (CanMoreDecodeWorkBeDone())
+ base::ResetAndReturn(&pending_read_cb_).Run(kNotEnoughData, NULL);
+}
+
+bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() {
+ return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes;
}
-void GpuVideoDecoder::RecordBufferData(
- const BitstreamBuffer& bitstream_buffer, const DecoderBuffer& buffer) {
- input_buffer_data_.push_front(BufferData(
- bitstream_buffer.id(), buffer.GetTimestamp(),
- demuxer_stream_->video_decoder_config().visible_rect(),
- demuxer_stream_->video_decoder_config().natural_size()));
+void GpuVideoDecoder::RecordBufferData(const BitstreamBuffer& bitstream_buffer,
+ const DecoderBuffer& buffer) {
+ input_buffer_data_.push_front(BufferData(bitstream_buffer.id(),
+ buffer.GetTimestamp(),
+ config_.visible_rect(),
+ config_.natural_size()));
// Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but
// that's too small for some pathological B-frame test videos. The cost of
// using too-high a value is low (192 bits per extra slot).
@@ -492,6 +459,8 @@ void GpuVideoDecoder::NotifyInitializeDone() {
void GpuVideoDecoder::ProvidePictureBuffers(uint32 count,
const gfx::Size& size,
uint32 texture_target) {
+ DVLOG(3) << "ProvidePictureBuffers(" << count << ", "
+ << size.width() << "x" << size.height() << ")";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
std::vector<uint32> texture_ids;
@@ -530,6 +499,7 @@ void GpuVideoDecoder::ProvidePictureBuffers(uint32 count,
}
void GpuVideoDecoder::DismissPictureBuffer(int32 id) {
+ DVLOG(3) << "DismissPictureBuffer(" << id << ")";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
std::map<int32, PictureBuffer>::iterator it =
@@ -559,6 +529,7 @@ void GpuVideoDecoder::DismissPictureBuffer(int32 id) {
}
void GpuVideoDecoder::PictureReady(const media::Picture& picture) {
+ DVLOG(3) << "PictureReady()";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
std::map<int32, PictureBuffer>::iterator it =
@@ -625,6 +596,7 @@ void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery(
void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id,
uint32 sync_point) {
+ DVLOG(3) << "ReusePictureBuffer(" << picture_buffer_id << ")";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
if (!vda_)
@@ -648,7 +620,6 @@ void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id,
}
factories_->WaitSyncPoint(sync_point);
-
++available_pictures_;
vda_loop_proxy_->PostTask(FROM_HERE, base::Bind(
@@ -678,6 +649,7 @@ void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) {
}
void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) {
+ DVLOG(3) << "NotifyEndOfBitstreamBuffer(" << id << ")";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
std::map<int32, BufferPair>::iterator it =
@@ -698,8 +670,8 @@ void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) {
bitstream_buffers_in_decoder_.erase(it);
if (pending_reset_cb_.is_null() && state_ != kDrainingDecoder &&
- CanMoreDecodeWorkBeDone()) {
- EnsureDemuxOrDecode();
+ CanMoreDecodeWorkBeDone() && !pending_read_cb_.is_null()) {
+ base::ResetAndReturn(&pending_read_cb_).Run(kNotEnoughData, NULL);
}
}
@@ -721,22 +693,8 @@ GpuVideoDecoder::~GpuVideoDecoder() {
DestroyTextures();
}
-void GpuVideoDecoder::EnsureDemuxOrDecode() {
- DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
-
- // The second condition can happen during the tear-down process.
- // GpuVideoDecoder::Stop() returns the |pending_read_cb_| immediately without
- // waiting for the demuxer read to be returned. Therefore, this function could
- // be called even after the decoder has been stopped.
- if (demuxer_read_in_progress_ || !demuxer_stream_)
- return;
-
- demuxer_read_in_progress_ = true;
- demuxer_stream_->Read(base::Bind(
- &GpuVideoDecoder::RequestBufferDecode, weak_this_));
-}
-
void GpuVideoDecoder::NotifyFlushDone() {
+ DVLOG(3) << "NotifyFlushDone()";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
DCHECK_EQ(state_, kDrainingDecoder);
state_ = kDecoderDrained;
@@ -744,6 +702,7 @@ void GpuVideoDecoder::NotifyFlushDone() {
}
void GpuVideoDecoder::NotifyResetDone() {
+ DVLOG(3) << "NotifyResetDone()";
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
DCHECK(ready_video_frames_.empty());
diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h
index 225e19d..90f336d 100644
--- a/media/filters/gpu_video_decoder.h
+++ b/media/filters/gpu_video_decoder.h
@@ -13,7 +13,6 @@
#include "base/memory/weak_ptr.h"
#include "media/base/pipeline_status.h"
-#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
#include "media/video/video_decode_accelerator.h"
@@ -82,10 +81,11 @@ class MEDIA_EXPORT GpuVideoDecoder
const scoped_refptr<Factories>& factories);
// VideoDecoder implementation.
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) OVERRIDE;
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) OVERRIDE;
virtual void Reset(const base::Closure& closure) OVERRIDE;
virtual void Stop(const base::Closure& closure) OVERRIDE;
virtual bool HasAlpha() const OVERRIDE;
@@ -115,17 +115,9 @@ class MEDIA_EXPORT GpuVideoDecoder
kError
};
- // If no demuxer read is in flight and no bitstream buffers are in the
- // decoder, kick some off demuxing/decoding.
- void EnsureDemuxOrDecode();
-
// Return true if more decode work can be piled on to the VDA.
bool CanMoreDecodeWorkBeDone();
- // Callback to pass to demuxer_stream_->Read() for receiving encoded bits.
- void RequestBufferDecode(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer);
-
// Enqueue a frame for later delivery (or drop it on the floor if a
// vda->Reset() is in progress) and trigger out-of-line delivery of the oldest
// ready frame to the client if there is a pending read. A NULL |frame|
@@ -170,8 +162,6 @@ class MEDIA_EXPORT GpuVideoDecoder
StatisticsCB statistics_cb_;
- // Pointer to the demuxer stream that will feed us compressed buffers.
- DemuxerStream* demuxer_stream_;
bool needs_bitstream_conversion_;
@@ -205,6 +195,8 @@ class MEDIA_EXPORT GpuVideoDecoder
State state_;
+ VideoDecoderConfig config_;
+
// Is a demuxer read in flight?
bool demuxer_read_in_progress_;
diff --git a/media/filters/video_decoder_selector.cc b/media/filters/video_decoder_selector.cc
index d62b295..1c222f5 100644
--- a/media/filters/video_decoder_selector.cc
+++ b/media/filters/video_decoder_selector.cc
@@ -68,7 +68,7 @@ void VideoDecoderSelector::SelectVideoDecoder(
message_loop_, set_decryptor_ready_cb_));
video_decoder_->Initialize(
- input_stream_,
+ input_stream_->video_decoder_config(),
BindToCurrentLoop(base::Bind(
&VideoDecoderSelector::DecryptingVideoDecoderInitDone,
weak_ptr_factory_.GetWeakPtr())),
@@ -122,7 +122,7 @@ void VideoDecoderSelector::InitializeDecoder(
}
(*iter)->Initialize(
- input_stream_,
+ input_stream_->video_decoder_config(),
BindToCurrentLoop(base::Bind(
&VideoDecoderSelector::DecoderInitDone,
weak_ptr_factory_.GetWeakPtr(),
diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc
index 28aaac6..b3e7aec 100644
--- a/media/filters/video_frame_stream.cc
+++ b/media/filters/video_frame_stream.cc
@@ -49,7 +49,8 @@ void VideoFrameStream::Initialize(DemuxerStream* stream,
stream_ = stream;
state_ = STATE_INITIALIZING;
- decoder_selector_->SelectVideoDecoder(this, statistics_cb, base::Bind(
+ // TODO(xhwang): VideoDecoderSelector only needs a config to select a decoder.
+ decoder_selector_->SelectVideoDecoder(stream, statistics_cb, base::Bind(
&VideoFrameStream::OnDecoderSelected, weak_this_));
}
@@ -70,17 +71,15 @@ void VideoFrameStream::ReadFrame(const VideoDecoder::ReadCB& read_cb) {
}
read_cb_ = read_cb;
- decoder_->Read(base::Bind(&VideoFrameStream::OnFrameReady, weak_this_));
+
+ if (state_ == STATE_FLUSHING_DECODER) {
+ FlushDecoder();
+ return;
+ }
+
+ ReadFromDemuxerStream();
}
-// VideoDecoder API guarantees that if VideoDecoder::Reset() is called during
-// a pending read, the read callback must be fired before the reset callback is
-// fired. Therefore, we can call VideoDecoder::Reset() regardless of if we have
-// a pending read and always satisfy the reset callback when the decoder reset
-// is finished. The only exception is when Reset() is called during decoder
-// reinitialization. In this case we cannot and don't need to reset the decoder.
-// We should just wait for the reinitialization to finish to satisfy the reset
-// callback.
void VideoFrameStream::Reset(const base::Closure& closure) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_;
@@ -89,14 +88,21 @@ void VideoFrameStream::Reset(const base::Closure& closure) {
reset_cb_ = closure;
- // VideoDecoder does not need to be and cannot be Reset() during
- // reinitialization. |decrypting_demuxer_stream_| was reset before decoder
+ // During decoder reinitialization, VideoDecoder does not need to be and
+ // cannot be Reset(). |decrypting_demuxer_stream_| was reset before decoder
// reinitialization.
- if (state_ == STATE_REINITIALIZING_DECODER)
+ // During pending demuxer read, VideoDecoder will be reset after demuxer read
+ // is returned (in OnBufferReady()).
+ if (state_ == STATE_REINITIALIZING_DECODER ||
+ state_ == STATE_PENDING_DEMUXER_READ) {
return;
+ }
- // We may or may not have pending read, but we'll start to reset everything
- // regardless.
+ // VideoDecoder API guarantees that if VideoDecoder::Reset() is called during
+ // a pending decode, the decode callback must be fired before the reset
+ // callback is fired. Therefore, we can call VideoDecoder::Reset() regardless
+ // of if we have a pending decode and always satisfy the reset callback when
+ // the decoder reset is finished.
if (decrypting_demuxer_stream_) {
decrypting_demuxer_stream_->Reset(base::Bind(
&VideoFrameStream::ResetDecoder, weak_this_));
@@ -113,22 +119,24 @@ void VideoFrameStream::Stop(const base::Closure& closure) {
stop_cb_ = closure;
- // The stopping will continue after all of the following pending callbacks
- // (if they are not null) are satisfied.
+ // The stopping process will continue after the pending operation is finished.
// TODO(xhwang): Now we cannot stop the initialization process through
// VideoDecoderSelector. Fix this. See: http://crbug.com/222054
- if (state_ == STATE_INITIALIZING)
+ if (state_ == STATE_INITIALIZING || state_ == STATE_PENDING_DEMUXER_READ)
return;
- // We may or may not have pending read and/or pending reset, but we'll start
- // to stop everything regardless.
-
+ // VideoDecoder API guarantees that if VideoDecoder::Stop() is called during
+ // a pending reset or a pending decode, the callbacks are always fired in the
+ // decode -> reset -> stop order. Therefore, we can call VideoDecoder::Stop()
+ // regardless of if we have a pending decode or reset and always satisfy the
+ // stop callback when the decoder decode/reset is finished.
if (decrypting_demuxer_stream_) {
decrypting_demuxer_stream_->Reset(base::Bind(
&VideoFrameStream::StopDecoder, weak_this_));
return;
}
+ // We may not have a |decoder_| if Stop() was called during initialization.
if (decoder_) {
StopDecoder();
return;
@@ -146,46 +154,15 @@ bool VideoFrameStream::CanReadWithoutStalling() const {
return decoder_->CanReadWithoutStalling();
}
-void VideoFrameStream::Read(const DemuxerStream::ReadCB& demuxer_read_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- if (state_ == STATE_FLUSHING_DECODER) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- demuxer_read_cb, DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()));
- return;
- }
-
- stream_->Read(base::Bind(
- &VideoFrameStream::OnBufferReady, weak_this_, demuxer_read_cb));
-}
-
-AudioDecoderConfig VideoFrameStream::audio_decoder_config() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- LOG(FATAL) << "Method audio_decoder_config() called on VideoFrameStream";
- return stream_->audio_decoder_config();
-}
-
-VideoDecoderConfig VideoFrameStream::video_decoder_config() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- return stream_->video_decoder_config();
-}
-
-DemuxerStream::Type VideoFrameStream::type() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- return VIDEO;
-}
-
-void VideoFrameStream::EnableBitstreamConverter() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- NOTREACHED();
-}
-
void VideoFrameStream::OnDecoderSelected(
scoped_ptr<VideoDecoder> selected_decoder,
scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_INITIALIZING) << state_;
DCHECK(!init_cb_.is_null());
+ DCHECK(read_cb_.is_null());
+ DCHECK(reset_cb_.is_null());
+
decoder_selector_.reset();
if (!selected_decoder) {
@@ -193,11 +170,12 @@ void VideoFrameStream::OnDecoderSelected(
base::ResetAndReturn(&init_cb_).Run(false, false);
} else {
state_ = STATE_NORMAL;
- decoder_ = selected_decoder.Pass();
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass();
- if (decoder_->NeedsBitstreamConversion()) {
+ if (decrypting_demuxer_stream_)
+ stream_ = decrypting_demuxer_stream_.get();
+ decoder_ = selected_decoder.Pass();
+ if (decoder_->NeedsBitstreamConversion())
stream_->EnableBitstreamConverter();
- }
// TODO(xhwang): We assume |decoder_->HasAlpha()| does not change after
// reinitialization. Check this condition.
base::ResetAndReturn(&init_cb_).Run(true, decoder_->HasAlpha());
@@ -210,75 +188,136 @@ void VideoFrameStream::OnDecoderSelected(
}
}
+void VideoFrameStream::SatisfyRead(VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& frame) {
+ DCHECK(!read_cb_.is_null());
+ base::ResetAndReturn(&read_cb_).Run(status, frame);
+}
+
+void VideoFrameStream::AbortRead() {
+ SatisfyRead(VideoDecoder::kOk, NULL);
+}
+
+void VideoFrameStream::Decode(const scoped_refptr<DecoderBuffer>& buffer) {
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_;
+ DCHECK(!read_cb_.is_null());
+ DCHECK(reset_cb_.is_null());
+ DCHECK(stop_cb_.is_null());
+ DCHECK(buffer);
+
+ decoder_->Decode(buffer, base::Bind(&VideoFrameStream::OnFrameReady,
+ weak_this_));
+}
+
+void VideoFrameStream::FlushDecoder() {
+ Decode(DecoderBuffer::CreateEOSBuffer());
+}
+
void VideoFrameStream::OnFrameReady(const VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame) {
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_;
DCHECK(!read_cb_.is_null());
- if (status != VideoDecoder::kOk) {
+ if (status == VideoDecoder::kDecodeError ||
+ status == VideoDecoder::kDecryptError) {
DCHECK(!frame.get());
state_ = STATE_ERROR;
- base::ResetAndReturn(&read_cb_).Run(status, NULL);
+ SatisfyRead(status, NULL);
return;
}
+ // Drop decoding result if Reset()/Stop() was called during decoding.
// The stopping/resetting process will be handled when the decoder is
// stopped/reset.
if (!stop_cb_.is_null() || !reset_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL);
+ AbortRead();
return;
}
- // Decoder flush finished. Reinitialize the video decoder.
+ // Decoder flushed. Reinitialize the video decoder.
if (state_ == STATE_FLUSHING_DECODER &&
status == VideoDecoder::kOk && frame->IsEndOfStream()) {
ReinitializeDecoder();
return;
}
- base::ResetAndReturn(&read_cb_).Run(status, frame);
+ if (status == VideoDecoder::kNotEnoughData) {
+ if (state_ == STATE_NORMAL)
+ ReadFromDemuxerStream();
+ else if (state_ == STATE_FLUSHING_DECODER)
+ FlushDecoder();
+ return;
+ }
+
+ SatisfyRead(status, frame);
+}
+
+void VideoFrameStream::ReadFromDemuxerStream() {
+ DCHECK_EQ(state_, STATE_NORMAL) << state_;
+ DCHECK(!read_cb_.is_null());
+ DCHECK(reset_cb_.is_null());
+ DCHECK(stop_cb_.is_null());
+
+ state_ = STATE_PENDING_DEMUXER_READ;
+ stream_->Read(base::Bind(&VideoFrameStream::OnBufferReady, weak_this_));
}
void VideoFrameStream::OnBufferReady(
- const DemuxerStream::ReadCB& demuxer_read_cb,
DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
DCHECK(message_loop_->BelongsToCurrentThread());
- // VideoFrameStream reads from demuxer stream only when in NORMAL state.
- DCHECK_EQ(state_, STATE_NORMAL) << state_;
+ DCHECK_EQ(state_, STATE_PENDING_DEMUXER_READ) << state_;
DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status;
+ DCHECK(!read_cb_.is_null());
+
+ state_ = STATE_NORMAL;
+
+ // Reset()/Stop() was postponed during STATE_PENDING_DEMUXER_READ state.
+ // We need to handle them in this function.
+
+ if (!stop_cb_.is_null()) {
+ AbortRead();
+ if (!reset_cb_.is_null())
+ Reset(base::ResetAndReturn(&reset_cb_));
+ Stop(base::ResetAndReturn(&stop_cb_));
+ return;
+ }
if (status == DemuxerStream::kConfigChanged) {
- DVLOG(2) << "OnBufferReady() - kConfigChanged";
state_ = STATE_FLUSHING_DECODER;
- demuxer_read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer());
+ if (!reset_cb_.is_null()) {
+ AbortRead();
+ Reset(base::ResetAndReturn(&reset_cb_));
+ // Reinitialization will continue after Reset() is done.
+ } else {
+ FlushDecoder();
+ }
+ return;
+ }
+
+ if (!reset_cb_.is_null()) {
+ AbortRead();
+ Reset(base::ResetAndReturn(&reset_cb_));
return;
}
- DCHECK(status == DemuxerStream::kOk || status == DemuxerStream::kAborted);
- demuxer_read_cb.Run(status, buffer);
+ if (status == DemuxerStream::kAborted) {
+ AbortRead();
+ return;
+ }
+
+ DCHECK(status == DemuxerStream::kOk) << status;
+ Decode(buffer);
}
void VideoFrameStream::ReinitializeDecoder() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_;
- DemuxerStream* stream = this;
- if (decrypting_demuxer_stream_) {
- // TODO(xhwang): Remove this hack! Since VideoFrameStream handles
- // kConfigChange internally and hides it from downstream filters. The
- // DecryptingDemuxerStream never receives kConfigChanged to reset it's
- // internal VideoDecoderConfig. Call InitializeDecoderConfig() here
- // explicitly to solve this. This will be removed when we separate the
- // DemuxerStream from the VideoDecoder.
- decrypting_demuxer_stream_->InitializeDecoderConfig();
- stream = decrypting_demuxer_stream_.get();
- }
-
- DCHECK(stream->video_decoder_config().IsValidConfig());
+ DCHECK(stream_->video_decoder_config().IsValidConfig());
state_ = STATE_REINITIALIZING_DECODER;
decoder_->Initialize(
- stream,
+ stream_->video_decoder_config(),
base::Bind(&VideoFrameStream::OnDecoderReinitialized, weak_this_),
statistics_cb_);
}
@@ -287,16 +326,24 @@ void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER) << state_;
+ // ReinitializeDecoder() can be called in two cases:
+ // 1, Flushing decoder finished (see OnFrameReady()).
+ // 2, Reset() was called during flushing decoder (see OnDecoderReset()).
+ // Also, Reset()/Stop() can be called during pending ReinitializeDecoder().
+ // This function needs to handle them all!
+
state_ = (status == PIPELINE_OK) ? STATE_NORMAL : STATE_ERROR;
- if (!reset_cb_.is_null()) {
- if (!read_cb_.is_null())
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL);
+ if (!read_cb_.is_null() && (!stop_cb_.is_null() || !reset_cb_.is_null()))
+ AbortRead();
+
+ if (!reset_cb_.is_null())
base::ResetAndReturn(&reset_cb_).Run();
- return;
- }
- DCHECK(!read_cb_.is_null());
+ // If !stop_cb_.is_null(), it will be handled in OnDecoderStopped().
+
+ if (read_cb_.is_null())
+ return;
if (!stop_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL);
@@ -304,17 +351,16 @@ void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) {
}
if (state_ == STATE_ERROR) {
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kDecodeError, NULL);
+ SatisfyRead(VideoDecoder::kDecodeError, NULL);
return;
}
- decoder_->Read(base::Bind(&VideoFrameStream::OnFrameReady, weak_this_));
+ ReadFromDemuxerStream();
}
void VideoFrameStream::ResetDecoder() {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(state_ == STATE_NORMAL ||
- state_ == STATE_FLUSHING_DECODER ||
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER ||
state_ == STATE_ERROR) << state_;
DCHECK(!reset_cb_.is_null());
@@ -323,8 +369,7 @@ void VideoFrameStream::ResetDecoder() {
void VideoFrameStream::OnDecoderReset() {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(state_ == STATE_NORMAL ||
- state_ == STATE_FLUSHING_DECODER ||
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER ||
state_ == STATE_ERROR) << state_;
// If Reset() was called during pending read, read callback should be fired
// before the reset callback is fired.
diff --git a/media/filters/video_frame_stream.h b/media/filters/video_frame_stream.h
index c384bca..5673cff 100644
--- a/media/filters/video_frame_stream.h
+++ b/media/filters/video_frame_stream.h
@@ -5,8 +5,6 @@
#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/compiler_specific.h"
@@ -30,7 +28,7 @@ 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 DemuxerStream {
+class MEDIA_EXPORT VideoFrameStream {
public:
// Indicates completion of VideoFrameStream initialization.
typedef base::Callback<void(bool success, bool has_alpha)> InitCB;
@@ -50,6 +48,7 @@ class MEDIA_EXPORT VideoFrameStream : public DemuxerStream {
// |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().
+ // TODO(xhwang): Rename this back to Read().
void ReadFrame(const VideoDecoder::ReadCB& read_cb);
// Resets the decoder, flushes all decoded frames and/or internal buffers,
@@ -70,19 +69,13 @@ class MEDIA_EXPORT VideoFrameStream : public DemuxerStream {
// a VideoFrame.
bool CanReadWithoutStalling() const;
- // DemuxerStream implementation.
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
- virtual AudioDecoderConfig audio_decoder_config() OVERRIDE;
- virtual VideoDecoderConfig video_decoder_config() OVERRIDE;
- virtual Type type() OVERRIDE;
- virtual void EnableBitstreamConverter() OVERRIDE;
-
private:
enum State {
STATE_UNINITIALIZED,
STATE_INITIALIZING,
- STATE_NORMAL,
+ STATE_NORMAL, // Includes idle, pending decoder decode/reset/stop.
STATE_FLUSHING_DECODER,
+ STATE_PENDING_DEMUXER_READ,
STATE_REINITIALIZING_DECODER,
STATE_STOPPED,
STATE_ERROR
@@ -95,13 +88,29 @@ class MEDIA_EXPORT VideoFrameStream : public DemuxerStream {
scoped_ptr<VideoDecoder> selected_decoder,
scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream);
- // Callback for VideoDecoder::Read().
+ // Satisfy pending |read_cb_| with |status| and |frame|.
+ void SatisfyRead(VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& frame);
+
+ // Abort pending |read_cb_|.
+ void AbortRead();
+
+ // Decodes |buffer| and returns the result via OnFrameReady().
+ void Decode(const scoped_refptr<DecoderBuffer>& buffer);
+
+ // Flushes the decoder with an EOS buffer to retrieve internally buffered
+ // video frames.
+ void FlushDecoder();
+
+ // Callback for VideoDecoder::Decode().
void OnFrameReady(const VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame);
+ // Reads a buffer from |stream_| and returns the result via OnBufferReady().
+ void ReadFromDemuxerStream();
+
// Callback for DemuxerStream::Read().
- void OnBufferReady(const DemuxerStream::ReadCB& demuxer_read_cb,
- DemuxerStream::Status status,
+ void OnBufferReady(DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer);
void ReinitializeDecoder();
diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc
index a67c9f3..37c19bcc 100644
--- a/media/filters/video_frame_stream_unittest.cc
+++ b/media/filters/video_frame_stream_unittest.cc
@@ -300,6 +300,8 @@ TEST_P(VideoFrameStreamTest, Read_AfterReset) {
ReadFrame();
}
+// No Reset() before initialization is successfully completed.
+
TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) {
Initialize();
Reset();
@@ -314,9 +316,18 @@ TEST_P(VideoFrameStreamTest, Reset_DuringReinitialization) {
video_frame_stream_->Reset(
base::Bind(&VideoFrameStreamTest::OnReset, base::Unretained(this)));
SatisfyPendingCallback(DECODER_REINIT);
+ ReadFrame();
}
-TEST_P(VideoFrameStreamTest, Reset_DuringNormalDemuxerRead) {
+TEST_P(VideoFrameStreamTest, Reset_AfterReinitialization) {
+ Initialize();
+ EnterPendingState(DECODER_REINIT);
+ SatisfyPendingCallback(DECODER_REINIT);
+ Reset();
+ ReadFrame();
+}
+
+TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_Normal) {
Initialize();
EnterPendingState(DEMUXER_READ_NORMAL);
EnterPendingState(DECODER_RESET);
@@ -325,7 +336,7 @@ TEST_P(VideoFrameStreamTest, Reset_DuringNormalDemuxerRead) {
ReadFrame();
}
-TEST_P(VideoFrameStreamTest, Reset_DuringConfigChangeDemuxerRead) {
+TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
EnterPendingState(DECODER_RESET);
@@ -350,7 +361,7 @@ TEST_P(VideoFrameStreamTest, Reset_AfterNormalRead) {
ReadFrame();
}
-TEST_P(VideoFrameStreamTest, Reset_AfterConfigChangeRead) {
+TEST_P(VideoFrameStreamTest, Reset_AfterDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
@@ -385,7 +396,14 @@ TEST_P(VideoFrameStreamTest, Stop_DuringReinitialization) {
SatisfyPendingCallback(DECODER_STOP);
}
-TEST_P(VideoFrameStreamTest, Stop_DuringNormalDemuxerRead) {
+TEST_P(VideoFrameStreamTest, Stop_AfterReinitialization) {
+ Initialize();
+ EnterPendingState(DECODER_REINIT);
+ SatisfyPendingCallback(DECODER_REINIT);
+ Stop();
+}
+
+TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_Normal) {
Initialize();
EnterPendingState(DEMUXER_READ_NORMAL);
EnterPendingState(DECODER_STOP);
@@ -393,7 +411,7 @@ TEST_P(VideoFrameStreamTest, Stop_DuringNormalDemuxerRead) {
SatisfyPendingCallback(DECODER_STOP);
}
-TEST_P(VideoFrameStreamTest, Stop_DuringConfigChangeDemuxerRead) {
+TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
EnterPendingState(DECODER_STOP);
diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc
index 8044a11..8e6dfb7 100644
--- a/media/filters/video_renderer_base_unittest.cc
+++ b/media/filters/video_renderer_base_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <utility>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
@@ -24,6 +26,7 @@ using ::testing::_;
using ::testing::AnyNumber;
using ::testing::InSequence;
using ::testing::Invoke;
+using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::StrictMock;
@@ -52,6 +55,9 @@ class VideoRendererBaseTest : public ::testing::Test {
demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal());
// We expect these to be called but we don't care how/when.
+ EXPECT_CALL(demuxer_stream_, Read(_))
+ .WillRepeatedly(RunCallback<0>(DemuxerStream::kOk,
+ DecoderBuffer::CreateEOSBuffer()));
EXPECT_CALL(*decoder_, Stop(_))
.WillRepeatedly(Invoke(this, &VideoRendererBaseTest::StopRequested));
EXPECT_CALL(statistics_cb_object_, OnStatistics(_))
@@ -78,8 +84,8 @@ class VideoRendererBaseTest : public ::testing::Test {
void InitializeWithDuration(int duration_ms) {
duration_ = base::TimeDelta::FromMilliseconds(duration_ms);
- // Monitor reads from the decoder.
- EXPECT_CALL(*decoder_, Read(_))
+ // Monitor decodes from the decoder.
+ EXPECT_CALL(*decoder_, Decode(_, _))
.WillRepeatedly(Invoke(this, &VideoRendererBaseTest::FrameRequested));
EXPECT_CALL(*decoder_, Reset(_))
@@ -289,7 +295,7 @@ class VideoRendererBaseTest : public ::testing::Test {
// Fixture members.
scoped_ptr<VideoRendererBase> renderer_;
MockVideoDecoder* decoder_; // Owned by |renderer_|.
- MockDemuxerStream demuxer_stream_;
+ NiceMock<MockDemuxerStream> demuxer_stream_;
MockStatisticsCB statistics_cb_object_;
private:
@@ -307,7 +313,8 @@ class VideoRendererBaseTest : public ::testing::Test {
current_frame_ = frame;
}
- void FrameRequested(const VideoDecoder::ReadCB& read_cb) {
+ void FrameRequested(const scoped_refptr<DecoderBuffer>& buffer,
+ const VideoDecoder::ReadCB& read_cb) {
DCHECK_EQ(&message_loop_, base::MessageLoop::current());
CHECK(read_cb_.is_null());
read_cb_ = read_cb;
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
index 4062352..1754f02 100644
--- a/media/filters/vpx_video_decoder.cc
+++ b/media/filters/vpx_video_decoder.cc
@@ -4,6 +4,9 @@
#include "media/filters/vpx_video_decoder.h"
+#include <algorithm>
+#include <string>
+
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
@@ -61,7 +64,6 @@ VpxVideoDecoder::VpxVideoDecoder(
: message_loop_(message_loop),
weak_factory_(this),
state_(kUninitialized),
- demuxer_stream_(NULL),
vpx_codec_(NULL),
vpx_codec_alpha_(NULL) {
}
@@ -71,26 +73,25 @@ VpxVideoDecoder::~VpxVideoDecoder() {
CloseDecoder();
}
-void VpxVideoDecoder::Initialize(
- DemuxerStream* stream,
- const PipelineStatusCB& status_cb,
- const StatisticsCB& statistics_cb) {
+void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config,
+ const PipelineStatusCB& status_cb,
+ const StatisticsCB& statistics_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(stream);
+ DCHECK(config.IsValidConfig());
+ DCHECK(!config.is_encrypted());
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
weak_this_ = weak_factory_.GetWeakPtr();
- demuxer_stream_ = stream;
- statistics_cb_ = statistics_cb;
-
- if (!ConfigureDecoder()) {
+ if (!ConfigureDecoder(config)) {
status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
// Success!
+ config_ = config;
+ statistics_cb_ = statistics_cb;
state_ = kNormal;
status_cb.Run(PIPELINE_OK);
}
@@ -117,10 +118,7 @@ static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context,
return context;
}
-bool VpxVideoDecoder::ConfigureDecoder() {
- const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config();
- DCHECK(config.IsValidConfig());
-
+bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
bool can_handle = false;
if (config.codec() == kCodecVP9)
@@ -160,25 +158,27 @@ void VpxVideoDecoder::CloseDecoder() {
}
}
-void VpxVideoDecoder::Read(const ReadCB& read_cb) {
+void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(!read_cb.is_null());
CHECK_NE(state_, kUninitialized);
CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
+
read_cb_ = BindToCurrentLoop(read_cb);
if (state_ == kError) {
- read_cb.Run(kDecodeError, NULL);
+ base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
return;
}
// Return empty frames if decoding has finished.
if (state_ == kDecodeFinished) {
- read_cb.Run(kOk, VideoFrame::CreateEmptyFrame());
+ base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame());
return;
}
- ReadFromDemuxerStream();
+ DecodeBuffer(buffer);
}
void VpxVideoDecoder::Reset(const base::Closure& closure) {
@@ -210,57 +210,18 @@ void VpxVideoDecoder::Stop(const base::Closure& closure) {
state_ = kUninitialized;
}
-void VpxVideoDecoder::ReadFromDemuxerStream() {
- DCHECK_NE(state_, kUninitialized);
- DCHECK_NE(state_, kDecodeFinished);
- DCHECK_NE(state_, kError);
- DCHECK(!read_cb_.is_null());
-
- demuxer_stream_->Read(base::Bind(
- &VpxVideoDecoder::DoDecryptOrDecodeBuffer, weak_this_));
-}
-
bool VpxVideoDecoder::HasAlpha() const {
return vpx_codec_alpha_ != NULL;
}
-void VpxVideoDecoder::DoDecryptOrDecodeBuffer(
- DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_NE(state_, kDecodeFinished);
- DCHECK_EQ(status != DemuxerStream::kOk, !buffer.get()) << status;
-
- if (state_ == kUninitialized)
- return;
-
- DCHECK(!read_cb_.is_null());
-
- if (!reset_cb_.is_null()) {
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- DoReset();
- return;
- }
-
- if (status == DemuxerStream::kAborted) {
- base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
- return;
- }
-
- // VideoFrameStream ensures no kConfigChanged is passed to VideoDecoders.
- DCHECK_EQ(status, DemuxerStream::kOk) << status;
- DecodeBuffer(buffer);
-}
-
-void VpxVideoDecoder::DecodeBuffer(
- const scoped_refptr<DecoderBuffer>& buffer) {
+void VpxVideoDecoder::DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer) {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_NE(state_, kUninitialized);
DCHECK_NE(state_, kDecodeFinished);
DCHECK_NE(state_, kError);
DCHECK(reset_cb_.is_null());
DCHECK(!read_cb_.is_null());
- DCHECK(buffer.get());
+ DCHECK(buffer);
// Transition to kDecodeFinished on the first end of stream buffer.
if (state_ == kNormal && buffer->IsEndOfStream()) {
@@ -270,7 +231,7 @@ void VpxVideoDecoder::DecodeBuffer(
}
scoped_refptr<VideoFrame> video_frame;
- if (!Decode(buffer, &video_frame)) {
+ if (!VpxDecode(buffer, &video_frame)) {
state_ = kError;
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
return;
@@ -283,18 +244,16 @@ void VpxVideoDecoder::DecodeBuffer(
statistics_cb_.Run(statistics);
}
- // If we didn't get a frame we need more data.
if (!video_frame.get()) {
- ReadFromDemuxerStream();
+ base::ResetAndReturn(&read_cb_).Run(kNotEnoughData, NULL);
return;
}
base::ResetAndReturn(&read_cb_).Run(kOk, video_frame);
}
-bool VpxVideoDecoder::Decode(
- const scoped_refptr<DecoderBuffer>& buffer,
- scoped_refptr<VideoFrame>* video_frame) {
+bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
+ scoped_refptr<VideoFrame>* video_frame) {
DCHECK(video_frame);
DCHECK(!buffer->IsEndOfStream());
@@ -374,10 +333,9 @@ void VpxVideoDecoder::DoReset() {
reset_cb_.Reset();
}
-void VpxVideoDecoder::CopyVpxImageTo(
- const vpx_image* vpx_image,
- const struct vpx_image* vpx_image_alpha,
- scoped_refptr<VideoFrame>* video_frame) {
+void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image,
+ const struct vpx_image* vpx_image_alpha,
+ scoped_refptr<VideoFrame>* video_frame) {
CHECK(vpx_image);
CHECK_EQ(vpx_image->d_w % 2, 0U);
CHECK_EQ(vpx_image->d_h % 2, 0U);
@@ -385,16 +343,13 @@ void VpxVideoDecoder::CopyVpxImageTo(
vpx_image->fmt == VPX_IMG_FMT_YV12);
gfx::Size size(vpx_image->d_w, vpx_image->d_h);
- gfx::Size natural_size =
- demuxer_stream_->video_decoder_config().natural_size();
-
- *video_frame = VideoFrame::CreateFrame(vpx_codec_alpha_ ?
- VideoFrame::YV12A :
- VideoFrame::YV12,
- size,
- gfx::Rect(size),
- natural_size,
- kNoTimestamp());
+
+ *video_frame = VideoFrame::CreateFrame(
+ vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12,
+ size,
+ gfx::Rect(size),
+ config_.natural_size(),
+ kNoTimestamp());
CopyYPlane(vpx_image->planes[VPX_PLANE_Y],
vpx_image->stride[VPX_PLANE_Y],
diff --git a/media/filters/vpx_video_decoder.h b/media/filters/vpx_video_decoder.h
index dae98447..ebceab6 100644
--- a/media/filters/vpx_video_decoder.h
+++ b/media/filters/vpx_video_decoder.h
@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder.h"
+#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
struct vpx_codec_ctx;
@@ -31,10 +32,11 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder {
virtual ~VpxVideoDecoder();
// VideoDecoder implementation.
- virtual void Initialize(DemuxerStream* stream,
+ virtual void Initialize(const VideoDecoderConfig& config,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) OVERRIDE;
- virtual void Read(const ReadCB& read_cb) OVERRIDE;
+ virtual void Decode(const scoped_refptr<DecoderBuffer>& buffer,
+ const ReadCB& read_cb) OVERRIDE;
virtual void Reset(const base::Closure& closure) OVERRIDE;
virtual void Stop(const base::Closure& closure) OVERRIDE;
virtual bool HasAlpha() const OVERRIDE;
@@ -50,19 +52,13 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder {
// Handles (re-)initializing the decoder with a (new) config.
// Returns true when initialization was successful.
- bool ConfigureDecoder();
+ bool ConfigureDecoder(const VideoDecoderConfig& config);
void CloseDecoder();
- void ReadFromDemuxerStream();
-
- // Carries out the buffer processing operation scheduled by
- // DecryptOrDecodeBuffer().
- void DoDecryptOrDecodeBuffer(DemuxerStream::Status status,
- const scoped_refptr<DecoderBuffer>& buffer);
void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer);
- bool Decode(const scoped_refptr<DecoderBuffer>& buffer,
- scoped_refptr<VideoFrame>* video_frame);
+ bool VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
+ scoped_refptr<VideoFrame>* video_frame);
// Reset decoder and call |reset_cb_|.
void DoReset();
@@ -81,8 +77,7 @@ class MEDIA_EXPORT VpxVideoDecoder : public VideoDecoder {
ReadCB read_cb_;
base::Closure reset_cb_;
- // Pointer to the demuxer stream that will feed us compressed buffers.
- DemuxerStream* demuxer_stream_;
+ VideoDecoderConfig config_;
vpx_codec_ctx* vpx_codec_;
vpx_codec_ctx* vpx_codec_alpha_;
diff --git a/media/media.gyp b/media/media.gyp
index e8d72ed..e59743ee 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -233,7 +233,6 @@
'base/buffers.h',
'base/byte_queue.cc',
'base/byte_queue.h',
- 'base/callback_holder.h',
'base/channel_mixer.cc',
'base/channel_mixer.h',
'base/clock.cc',
@@ -337,10 +336,6 @@
'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/fake_video_decoder.cc',
- 'filters/fake_video_decoder.h',
'filters/ffmpeg_audio_decoder.cc',
'filters/ffmpeg_audio_decoder.h',
'filters/ffmpeg_demuxer.cc',
@@ -923,6 +918,7 @@
'base/audio_timestamp_helper_unittest.cc',
'base/bind_to_loop_unittest.cc',
'base/bit_reader_unittest.cc',
+ 'base/callback_holder.h',
'base/callback_holder_unittest.cc',
'base/channel_mixer_unittest.cc',
'base/clock_unittest.cc',
@@ -957,7 +953,11 @@
'filters/decrypting_audio_decoder_unittest.cc',
'filters/decrypting_demuxer_stream_unittest.cc',
'filters/decrypting_video_decoder_unittest.cc',
+ 'filters/fake_demuxer_stream.cc',
+ 'filters/fake_demuxer_stream.h',
'filters/fake_demuxer_stream_unittest.cc',
+ 'filters/fake_video_decoder.cc',
+ 'filters/fake_video_decoder.h',
'filters/fake_video_decoder_unittest.cc',
'filters/ffmpeg_audio_decoder_unittest.cc',
'filters/ffmpeg_demuxer_unittest.cc',