Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1070)

Unified Diff: media/filters/decrypting_video_decoder_unittest.cc

Issue 10969028: Add video decoding methods in Decryptor and add DecryptingVideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: reorder methods in the unittest Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/decrypting_video_decoder.cc ('k') | media/filters/ffmpeg_video_decoder.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/decrypting_video_decoder_unittest.cc
diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0b947ddfe0e3900441ff06d4b41052480286e12c
--- /dev/null
+++ b/media/filters/decrypting_video_decoder_unittest.cc
@@ -0,0 +1,499 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/message_loop.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decrypt_config.h"
+#include "media/base/mock_callback.h"
+#include "media/base/mock_filters.h"
+#include "media/base/video_frame.h"
+#include "media/filters/ffmpeg_decoder_unittest.h"
+#include "media/filters/decrypting_video_decoder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::IsNull;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+namespace media {
+
+static const VideoFrame::Format kVideoFormat = VideoFrame::YV12;
+static const gfx::Size kCodedSize(320, 240);
+static const gfx::Rect kVisibleRect(320, 240);
+static const gfx::Size kNaturalSize(320, 240);
+static const uint8 kFakeKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 };
+static const uint8 kFakeIv[DecryptConfig::kDecryptionKeySize] = { 0 };
+
+// Create a fake non-empty encrypted buffer.
+static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
+ const int buffer_size = 16; // Need a non-empty buffer;
+ scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size));
+ buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
+ std::string(reinterpret_cast<const char*>(kFakeKeyId),
+ arraysize(kFakeKeyId)),
+ std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)),
+ 0,
+ std::vector<SubsampleEntry>())));
+ return buffer;
+}
+
+ACTION_P(ReturnBuffer, buffer) {
+ arg0.Run(buffer ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
+}
+
+ACTION(ReturnConfigChanged) {
+ arg0.Run(DemuxerStream::kConfigChanged, scoped_refptr<DecoderBuffer>(NULL));
+}
+
+ACTION_P(RunCallback1, param) {
+ arg1.Run(param);
+}
+
+ACTION_P2(RunCallback2, param1, param2) {
+ arg1.Run(param1, param2);
+}
+
+class DecryptingVideoDecoderTest : public testing::Test {
+ public:
+ DecryptingVideoDecoderTest()
+ : decryptor_(new StrictMock<MockDecryptor>()),
+ decoder_(new StrictMock<DecryptingVideoDecoder>(
+ base::Bind(&Identity<scoped_refptr<base::MessageLoopProxy> >,
+ message_loop_.message_loop_proxy()),
+ decryptor_.get())),
+ demuxer_(new StrictMock<MockDemuxerStream>()),
+ encrypted_buffer_(CreateFakeEncryptedBuffer()),
+ decoded_video_frame_(VideoFrame::CreateBlackFrame(kCodedSize)),
+ null_video_frame_(scoped_refptr<VideoFrame>()),
+ end_of_stream_video_frame_(VideoFrame::CreateEmptyFrame()) {
+ }
+
+ virtual ~DecryptingVideoDecoderTest() {
+ Stop();
+ }
+
+ void InitializeAndExpectStatus(const VideoDecoderConfig& config,
+ PipelineStatus status) {
+ EXPECT_CALL(*demuxer_, video_decoder_config())
+ .WillRepeatedly(ReturnRef(config));
+
+ decoder_->Initialize(demuxer_, NewExpectedStatusCB(status),
+ base::Bind(&MockStatisticsCB::OnStatistics,
+ base::Unretained(&statistics_cb_)));
+ message_loop_.RunAllPending();
+ }
+
+ void Initialize() {
+ EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
+ .WillOnce(RunCallback1(true));
+
+ config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat,
+ kCodedSize, kVisibleRect, kNaturalSize,
+ NULL, 0, true, true);
+
+ InitializeAndExpectStatus(config_, PIPELINE_OK);
+ }
+
+ void ReadAndExpectFrameReadyWith(
+ VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& video_frame) {
+ EXPECT_CALL(*this, FrameReady(status, video_frame));
+
+ decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
+ base::Unretained(this)));
+ message_loop_.RunAllPending();
+ }
+
+ // 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(RunCallback2(Decryptor::kSuccess, decoded_video_frame_))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess,
+ end_of_stream_video_frame_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+
+ ReadAndExpectFrameReadyWith(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_.RunAllPending();
+ // Make sure the Read() on the decoder triggers a Read() on the demuxer.
+ EXPECT_FALSE(pending_demuxer_read_cb_.is_null());
+ }
+
+ // Make the video decode callback pending by saving and not firing it.
+ void EnterPendingDecryptAndDecodeState() {
+ 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)));
+ message_loop_.RunAllPending();
+ // Make sure the Read() on the decoder triggers a DecryptAndDecode() on the
+ // decryptor.
+ EXPECT_FALSE(pending_video_decode_cb_.is_null());
+ }
+
+ void AbortPendingVideoDecodeCB() {
+ if (!pending_video_decode_cb_.is_null()) {
+ base::ResetAndReturn(&pending_video_decode_cb_).Run(
+ Decryptor::kSuccess, scoped_refptr<VideoFrame>(NULL));
+ }
+ }
+
+ void AbortAllPendingCBs() {
+ if (!pending_init_cb_.is_null()) {
+ ASSERT_TRUE(pending_video_decode_cb_.is_null());
+ base::ResetAndReturn(&pending_init_cb_).Run(false);
+ return;
+ }
+
+ AbortPendingVideoDecodeCB();
+ }
+
+ void Reset() {
+ EXPECT_CALL(*decryptor_, CancelDecryptAndDecodeVideo())
+ .WillOnce(Invoke(
+ this, &DecryptingVideoDecoderTest::AbortPendingVideoDecodeCB));
+
+ decoder_->Reset(NewExpectedClosure());
+ message_loop_.RunAllPending();
+ }
+
+ void Stop() {
+ EXPECT_CALL(*decryptor_, StopVideoDecoder())
+ .WillOnce(Invoke(
+ this, &DecryptingVideoDecoderTest::AbortAllPendingCBs));
+
+ decoder_->Stop(NewExpectedClosure());
+ message_loop_.RunAllPending();
+ }
+
+ MOCK_METHOD2(FrameReady, void(VideoDecoder::Status,
+ const scoped_refptr<VideoFrame>&));
+
+ MessageLoop message_loop_;
+ scoped_ptr<StrictMock<MockDecryptor> > decryptor_;
+ scoped_refptr<StrictMock<DecryptingVideoDecoder> > decoder_;
+ scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_;
+ MockStatisticsCB statistics_cb_;
+ VideoDecoderConfig config_;
+
+ DemuxerStream::ReadCB pending_demuxer_read_cb_;
+ Decryptor::DecoderInitCB pending_init_cb_;
+ Decryptor::VideoDecodeCB pending_video_decode_cb_;
+
+ // Constant buffer/frames to be returned by the |demuxer_| and |decryptor_|.
+ scoped_refptr<DecoderBuffer> encrypted_buffer_;
+ scoped_refptr<VideoFrame> decoded_video_frame_;
+ scoped_refptr<VideoFrame> null_video_frame_;
+ scoped_refptr<VideoFrame> end_of_stream_video_frame_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DecryptingVideoDecoderTest);
+};
+
+TEST_F(DecryptingVideoDecoderTest, Initialize_Normal) {
+ Initialize();
+}
+
+// Ensure that DecryptingVideoDecoder only accepts encrypted video.
+TEST_F(DecryptingVideoDecoderTest, Initialize_UnencryptedVideoConfig) {
+ VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
+ kVideoFormat,
+ kCodedSize, kVisibleRect, kNaturalSize,
+ NULL, 0, false);
+
+ InitializeAndExpectStatus(config, DECODER_ERROR_NOT_SUPPORTED);
+}
+
+// Ensure decoder handles invalid video configs without crashing.
+TEST_F(DecryptingVideoDecoderTest, Initialize_InvalidVideoConfig) {
+ VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
+ VideoFrame::INVALID,
+ kCodedSize, kVisibleRect, kNaturalSize,
+ NULL, 0, true);
+
+ InitializeAndExpectStatus(config, PIPELINE_ERROR_DECODE);
+}
+
+// Ensure decoder handles unsupported video configs without crashing.
+TEST_F(DecryptingVideoDecoderTest, Initialize_UnsupportedVideoConfig) {
+ EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
+ .WillOnce(RunCallback1(false));
+
+ VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
+ kVideoFormat,
+ kCodedSize, kVisibleRect, kNaturalSize,
+ NULL, 0, true);
+
+ InitializeAndExpectStatus(config, DECODER_ERROR_NOT_SUPPORTED);
+}
+
+// Test normal decrypt and decode case.
+TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_Normal) {
+ Initialize();
+ EnterNormalDecodingState();
+}
+
+// Test the case where the decryptor returns error when doing decrypt and
+// decode.
+TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kError,
+ scoped_refptr<VideoFrame>(NULL)));
+
+ ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
+}
+
+// Test the case where the decryptor does not have the decryption key to do
+// decrypt and decode.
+TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_NoKey) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kNoKey, null_video_frame_));
+
+ ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
+}
+
+// Test the case where the decryptor returns kNeedMoreData to ask for more
+// buffers before it can produce a frame.
+TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_NeedMoreData) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .Times(2)
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
+ .WillOnce(RunCallback2(Decryptor::kNeedMoreData,
+ scoped_refptr<VideoFrame>()))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess, decoded_video_frame_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_))
+ .Times(2);
+
+ ReadAndExpectFrameReadyWith(VideoDecoder::kOk, decoded_video_frame_);
+}
+
+// Test the case where the decryptor receives end-of-stream buffer.
+TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) {
+ Initialize();
+ EnterNormalDecodingState();
+ EnterEndOfStreamState();
+}
+
+// Test resetting when decoder has initialized but has not decoded any frame.
+TEST_F(DecryptingVideoDecoderTest, Reset_Initialized) {
+ Initialize();
+ Reset();
+}
+
+// Test resetting when decoder has decoded single frame.
+TEST_F(DecryptingVideoDecoderTest, Reset_Decoding) {
+ Initialize();
+ EnterNormalDecodingState();
+ Reset();
+}
+
+// Test resetting when decoder has hit end of stream.
+TEST_F(DecryptingVideoDecoderTest, Reset_EndOfStream) {
+ Initialize();
+ EnterNormalDecodingState();
+ EnterEndOfStreamState();
+ Reset();
+}
+
+// Test resetting after the decoder has been reset.
+TEST_F(DecryptingVideoDecoderTest, Reset_AfterReset) {
+ Initialize();
+ EnterNormalDecodingState();
+ Reset();
+ Reset();
+}
+
+// Test resetting when there is a pending read on the demuxer.
+TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) {
+ Initialize();
+ EnterPendingReadState();
+
+ EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
+
+ Reset();
+ base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kOk,
+ encrypted_buffer_);
+ message_loop_.RunAllPending();
+}
+
+// Test resetting when there is a pending video decode callback on the
+// decryptor.
+TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecryptAndDecode) {
+ Initialize();
+ EnterPendingDecryptAndDecodeState();
+
+ EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
+
+ Reset();
+}
+
+// Test stopping when decoder has initialized but has not decoded any frame.
+TEST_F(DecryptingVideoDecoderTest, Stop_Initialized) {
+ Initialize();
+ Stop();
+}
+
+// Test stopping when decoder has decoded single frame.
+TEST_F(DecryptingVideoDecoderTest, Stop_Decoding) {
+ Initialize();
+ EnterNormalDecodingState();
+ Stop();
+}
+
+// Test stopping when decoder has hit end of stream.
+TEST_F(DecryptingVideoDecoderTest, Stop_EndOfStream) {
+ Initialize();
+ EnterNormalDecodingState();
+ EnterEndOfStreamState();
+ Stop();
+}
+
+// Test stopping when there is a pending read on the demuxer.
+TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingInitialize) {
+ EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
+ .WillOnce(SaveArg<1>(&pending_init_cb_));
+
+ config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat,
+ kCodedSize, kVisibleRect, kNaturalSize, NULL, 0, true,
+ true);
+ InitializeAndExpectStatus(config_, DECODER_ERROR_NOT_SUPPORTED);
+ EXPECT_FALSE(pending_init_cb_.is_null());
+
+ Stop();
+}
+
+// Test stopping when there is a pending read on the demuxer.
+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_.RunAllPending();
+}
+
+// Test stopping when there is a pending video decode callback on the
+// decryptor.
+TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDecryptAndDecode) {
+ Initialize();
+ EnterPendingDecryptAndDecodeState();
+
+ EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
+
+ Stop();
+}
+
+// Test stopping when there is a pending reset on the decoder.
+// Reset is pending because it cannot complete when the video decode callback
+// is pending.
+TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingReset) {
+ Initialize();
+ EnterPendingDecryptAndDecodeState();
+
+ EXPECT_CALL(*decryptor_, CancelDecryptAndDecodeVideo());
+ EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
+
+ decoder_->Reset(NewExpectedClosure());
+ Stop();
+}
+
+// Test stopping after the decoder has been reset.
+TEST_F(DecryptingVideoDecoderTest, Stop_AfterReset) {
+ Initialize();
+ EnterNormalDecodingState();
+ Reset();
+ Stop();
+}
+
+// Test stopping after the decoder has been stopped.
+TEST_F(DecryptingVideoDecoderTest, Stop_AfterStop) {
+ Initialize();
+ EnterNormalDecodingState();
+ Stop();
+ Stop();
+}
+
+// Test aborted read on the demuxer stream.
+TEST_F(DecryptingVideoDecoderTest, AbortPendingDemuxerRead) {
+ Initialize();
+
+ // ReturnBuffer() with NULL triggers aborted demuxer read.
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>()));
+
+ ReadAndExpectFrameReadyWith(VideoDecoder::kOk, null_video_frame_);
+}
+
+// Test aborted read on the demuxer stream when the decoder is being reset.
+TEST_F(DecryptingVideoDecoderTest, AbortPendingDemuxerReadDuringReset) {
+ 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_.RunAllPending();
+}
+
+// Test config change on the demuxer stream.
+TEST_F(DecryptingVideoDecoderTest, ConfigChanged) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnConfigChanged());
+
+ // TODO(xhwang): Update this test when kConfigChanged is supported in
+ // DecryptingVideoDecoder.
+ ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
+}
+
+} // namespace media
« no previous file with comments | « media/filters/decrypting_video_decoder.cc ('k') | media/filters/ffmpeg_video_decoder.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698