| Index: media/crypto/decrypting_video_decoder_unittest.cc
|
| diff --git a/media/crypto/decrypting_video_decoder_unittest.cc b/media/crypto/decrypting_video_decoder_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a846100f58b8c85519e9b01fb588c7f0f4e8b18d
|
| --- /dev/null
|
| +++ b/media/crypto/decrypting_video_decoder_unittest.cc
|
| @@ -0,0 +1,515 @@
|
| +// 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/crypto/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;
|
| + const int encrypted_frame_offset = 1; // This should be non-zero.
|
| + 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),
|
| + DecryptConfig::kDecryptionKeySize),
|
| + encrypted_frame_offset,
|
| + 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_(NULL),
|
| + demuxer_(new StrictMock<MockDemuxerStream>()),
|
| + read_cb_(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
|
| + base::Unretained(this))) {
|
| +
|
| + decoder_ = new StrictMock<DecryptingVideoDecoder>(
|
| + base::Bind(&Identity<scoped_refptr<base::MessageLoopProxy> >,
|
| + message_loop_.message_loop_proxy()),
|
| + decryptor_.get());
|
| +
|
| + // Initialize various test buffers.
|
| + encrypted_buffer_ = CreateFakeEncryptedBuffer();
|
| + end_of_stream_buffer_ = DecoderBuffer::CreateEOSBuffer();
|
| + decoded_video_frame_ = VideoFrame::CreateBlackFrame(kCodedSize);
|
| + }
|
| +
|
| + virtual ~DecryptingVideoDecoderTest() {}
|
| +
|
| + void Initialize() {
|
| + EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
|
| + .WillOnce(RunCallback1(true));
|
| +
|
| + config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat,
|
| + kCodedSize, kVisibleRect, kNaturalSize,
|
| + NULL, 0, true, true);
|
| +
|
| + InitializeWithConfig(config_);
|
| + }
|
| +
|
| + void InitializeWithConfigAndStatus(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 InitializeWithConfig(const VideoDecoderConfig& config) {
|
| + InitializeWithConfigAndStatus(config, PIPELINE_OK);
|
| + }
|
| +
|
| + void RunPendingVideoDecodeCB() {
|
| + if (!video_decode_cb_.is_null()) {
|
| + base::ResetAndReturn(&video_decode_cb_).Run(
|
| + Decryptor::kSuccess, scoped_refptr<VideoFrame>(NULL));
|
| + }
|
| + }
|
| +
|
| + void Reset() {
|
| + EXPECT_CALL(*decryptor_, CancelDecryptAndDecodeVideo())
|
| + .WillOnce(Invoke(this,
|
| + &DecryptingVideoDecoderTest::RunPendingVideoDecodeCB));
|
| +
|
| + decoder_->Reset(NewExpectedClosure());
|
| + message_loop_.RunAllPending();
|
| + }
|
| +
|
| + void Stop() {
|
| + EXPECT_CALL(*decryptor_, StopVideoDecoder())
|
| + .WillOnce(Invoke(this,
|
| + &DecryptingVideoDecoderTest::RunPendingVideoDecodeCB));
|
| +
|
| + decoder_->Stop(NewExpectedClosure());
|
| + message_loop_.RunAllPending();
|
| + }
|
| +
|
| + // Sets up expectations and actions to put DecryptingVideoDecoder in an active
|
| + // decoding state.
|
| + void EnterDecodingState() {
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecryptAndDecodeSingleFrame(encrypted_buffer_, &status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame);
|
| + EXPECT_FALSE(video_frame->IsEndOfStream());
|
| + }
|
| +
|
| + // Sets up expectations and actions to put DecryptingVideoDecoder in an end
|
| + // of stream state.
|
| + void EnterEndOfStreamState() {
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + VideoDecoder::Status status;
|
| + Read(&status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame);
|
| + EXPECT_TRUE(video_frame->IsEndOfStream());
|
| + }
|
| +
|
| + // 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
|
| + // the frame to be returned in the multithreaded decoder case.
|
| + void DecryptAndDecodeSingleFrame(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_));
|
| +
|
| + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
|
| + .WillOnce(RunCallback2(Decryptor::kSuccess, decoded_video_frame_))
|
| + .WillRepeatedly(RunCallback2(Decryptor::kSuccess,
|
| + VideoFrame::CreateEmptyFrame()));
|
| +
|
| + EXPECT_CALL(statistics_cb_, OnStatistics(_));
|
| + Read(status, video_frame);
|
| + }
|
| +
|
| + void Read(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_);
|
| + 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_;
|
| +
|
| + VideoDecoder::ReadCB read_cb_;
|
| + Decryptor::VideoDecodeCB video_decode_cb_;
|
| +
|
| + scoped_refptr<DecoderBuffer> encrypted_buffer_;
|
| + scoped_refptr<DecoderBuffer> end_of_stream_buffer_;
|
| + scoped_refptr<VideoFrame> decoded_video_frame_;
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(DecryptingVideoDecoderTest);
|
| +};
|
| +
|
| +TEST_F(DecryptingVideoDecoderTest, Initialize_Normal) {
|
| + Initialize();
|
| +}
|
| +
|
| +// Ensure that DecryptingVideoDecoder only acceptes encrypted video.
|
| +TEST_F(DecryptingVideoDecoderTest, Initialize_UnencryptedVideoConfig) {
|
| + VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
|
| + kVideoFormat,
|
| + kCodedSize, kVisibleRect, kNaturalSize,
|
| + NULL, 0, false);
|
| +
|
| + InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
|
| +}
|
| +
|
| +// Ensure decoder handles unsupported video configs without crashing.
|
| +TEST_F(DecryptingVideoDecoderTest, Initialize_InvalidVideoConfig) {
|
| + VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
|
| + VideoFrame::INVALID,
|
| + kCodedSize, kVisibleRect, kNaturalSize,
|
| + NULL, 0, true);
|
| +
|
| + InitializeWithConfigAndStatus(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);
|
| +
|
| + InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED);
|
| +}
|
| +
|
| +// Test normal decrypt and decode case.
|
| +TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_Normal) {
|
| + Initialize();
|
| +
|
| + // Simulate decoding a single frame.
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + DecryptAndDecodeSingleFrame(encrypted_buffer_, &status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + ASSERT_TRUE(video_frame);
|
| + EXPECT_FALSE(video_frame->IsEndOfStream());
|
| +}
|
| +
|
| +// 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)));
|
| +
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + Read(&status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kDecodeError, status);
|
| + EXPECT_FALSE(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,
|
| + scoped_refptr<VideoFrame>(NULL)));
|
| +
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + Read(&status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kDecodeError, status);
|
| + EXPECT_FALSE(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>(NULL)))
|
| + .WillRepeatedly(RunCallback2(Decryptor::kSuccess, decoded_video_frame_));
|
| +
|
| + EXPECT_CALL(statistics_cb_, OnStatistics(_))
|
| + .Times(2);
|
| +
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| + Read(&status, &video_frame);
|
| +
|
| + EXPECT_EQ(VideoDecoder::kOk, status);
|
| + EXPECT_EQ(decoded_video_frame_, video_frame);
|
| +}
|
| +
|
| +// Test the case where the decryptor receives end-of-stream buffer.
|
| +TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) {
|
| + Initialize();
|
| + EnterDecodingState();
|
| + 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();
|
| + EnterDecodingState();
|
| + Reset();
|
| +}
|
| +
|
| +// Test resetting when decoder has hit end of stream.
|
| +TEST_F(DecryptingVideoDecoderTest, Reset_EndOfStream) {
|
| + Initialize();
|
| + EnterDecodingState();
|
| + EnterEndOfStreamState();
|
| + Reset();
|
| +}
|
| +
|
| +// Test resetting when there is a pending read on the demuxer.
|
| +TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingRead) {
|
| + Initialize();
|
| +
|
| + DemuxerStream::ReadCB read_cb;
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillOnce(SaveArg<0>(&read_cb));
|
| + decoder_->Read(read_cb_);
|
| + message_loop_.RunAllPending();
|
| + // Make sure the Read() on the decoder triggers a Read() on the demuxer.
|
| + EXPECT_FALSE(read_cb.is_null());
|
| +
|
| + Reset();
|
| +
|
| + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| + read_cb.Run(DemuxerStream::kOk, encrypted_buffer_);
|
| + message_loop_.RunAllPending();
|
| +}
|
| +
|
| +// Test resetting when there is a pending decrypt on the decryptor.
|
| +TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecryptAndDecode) {
|
| + Initialize();
|
| +
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
|
| +
|
| + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(encrypted_buffer_, _))
|
| + .WillOnce(SaveArg<1>(&video_decode_cb_));
|
| +
|
| + decoder_->Read(read_cb_);
|
| + message_loop_.RunAllPending();
|
| + // Make sure the Read() on the decoder triggers a DecryptAndDecode() on the
|
| + // decryptor.
|
| + EXPECT_FALSE(video_decode_cb_.is_null());
|
| +
|
| + 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();
|
| + EnterDecodingState();
|
| + Stop();
|
| +}
|
| +
|
| +// Test stopping when decoder has hit end of stream.
|
| +TEST_F(DecryptingVideoDecoderTest, Stop_EndOfStream) {
|
| + Initialize();
|
| + EnterDecodingState();
|
| + EnterEndOfStreamState();
|
| + Stop();
|
| +}
|
| +
|
| +// Test stopping when there is a pending read on the demuxer.
|
| +TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingRead) {
|
| + Initialize();
|
| +
|
| + DemuxerStream::ReadCB read_cb;
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillOnce(SaveArg<0>(&read_cb));
|
| +
|
| + decoder_->Read(read_cb_);
|
| + message_loop_.RunAllPending();
|
| +
|
| + // Make sure the Read() on the decoder triggers a Read() on the demuxer.
|
| + EXPECT_FALSE(read_cb.is_null());
|
| +
|
| + Stop();
|
| +
|
| + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| +
|
| + read_cb.Run(DemuxerStream::kOk, encrypted_buffer_);
|
| + message_loop_.RunAllPending();
|
| +}
|
| +
|
| +// Test stopping when there is a pending decrypt on the decryptor.
|
| +TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDecryptAndDecode) {
|
| + Initialize();
|
| +
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
|
| +
|
| + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(encrypted_buffer_, _))
|
| + .WillOnce(SaveArg<1>(&video_decode_cb_));
|
| +
|
| + decoder_->Read(read_cb_);
|
| + message_loop_.RunAllPending();
|
| + // Make sure the Read() on the decoder triggers a DecryptAndDecode() on the
|
| + // decryptor.
|
| + EXPECT_FALSE(video_decode_cb_.is_null());
|
| +
|
| + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| + Stop();
|
| +}
|
| +
|
| +// Test aborted read on the demuxer stream.
|
| +TEST_F(DecryptingVideoDecoderTest, AbortPendingRead) {
|
| + 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);
|
| +}
|
| +
|
| +// Test aborted read on the demuxer stream when the decoder is being reset.
|
| +TEST_F(DecryptingVideoDecoderTest, AbortPendingReadDuringReset) {
|
| + Initialize();
|
| +
|
| + // Issue Read on demuxer and save the ReadCB in |read_cb|.
|
| + DemuxerStream::ReadCB read_cb;
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillOnce(SaveArg<0>(&read_cb));
|
| + decoder_->Read(read_cb_);
|
| + message_loop_.RunAllPending();
|
| + ASSERT_FALSE(read_cb.is_null());
|
| +
|
| + Reset();
|
| +
|
| + // Signal an aborted demuxer read.
|
| + read_cb.Run(DemuxerStream::kAborted, NULL);
|
| +
|
| + // Make sure we get a NULL video frame returned.
|
| + EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
|
| + message_loop_.RunAllPending();
|
| +}
|
| +
|
| +// Test config change on the demuxer stream.
|
| +TEST_F(DecryptingVideoDecoderTest, ConfigChanged) {
|
| + Initialize();
|
| +
|
| + EXPECT_CALL(*demuxer_, Read(_))
|
| + .WillOnce(ReturnConfigChanged());
|
| +
|
| + VideoDecoder::Status status;
|
| + scoped_refptr<VideoFrame> video_frame;
|
| +
|
| + Read(&status, &video_frame);
|
| + // TODO(xhwang): Update this test when kConfigChanged is supported in
|
| + // DecryptingVideoDecoder.
|
| + EXPECT_EQ(VideoDecoder::kDecodeError, status);
|
| + EXPECT_FALSE(video_frame);
|
| +}
|
| +
|
| +} // namespace media
|
|
|