blob: c7d22acfa9e135e39154171d30dbd287b4b382bf [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/message_loop/message_loop.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/filters/fake_demuxer_stream.h"
#include "media/filters/fake_video_decoder.h"
#include "media/filters/video_frame_stream.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Assign;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
static const int kNumConfigs = 3;
static const int kNumBuffersInOneConfig = 5;
static const int kDecodingDelay = 7;
namespace media {
class VideoFrameStreamTest : public testing::TestWithParam<bool> {
public:
VideoFrameStreamTest()
: demuxer_stream_(new FakeDemuxerStream(kNumConfigs,
kNumBuffersInOneConfig,
GetParam())),
decryptor_(new NiceMock<MockDecryptor>()),
decoder_(new FakeVideoDecoder(kDecodingDelay)),
is_initialized_(false),
num_decoded_frames_(0),
pending_initialize_(false),
pending_read_(false),
pending_reset_(false),
pending_stop_(false),
total_bytes_decoded_(0),
has_no_key_(false) {
ScopedVector<VideoDecoder> decoders;
decoders.push_back(decoder_);
video_frame_stream_.reset(new VideoFrameStream(
message_loop_.message_loop_proxy(),
decoders.Pass(),
base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback,
base::Unretained(this))));
// Decryptor can only decrypt (not decrypt-and-decode) so that
// DecryptingDemuxerStream will be used.
EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _))
.WillRepeatedly(RunCallback<1>(false));
EXPECT_CALL(*decryptor_, Decrypt(_, _, _))
.WillRepeatedly(Invoke(this, &VideoFrameStreamTest::Decrypt));
}
~VideoFrameStreamTest() {
DCHECK(!pending_initialize_);
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(!pending_stop_);
if (is_initialized_)
Stop();
EXPECT_FALSE(is_initialized_);
}
MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&));
void OnStatistics(const PipelineStatistics& statistics) {
total_bytes_decoded_ += statistics.video_bytes_decoded;
}
void OnInitialized(bool success, bool has_alpha) {
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(pending_initialize_);
pending_initialize_ = false;
is_initialized_ = success;
if (!success)
decoder_ = NULL;
}
void InitializeVideoFrameStream() {
pending_initialize_ = true;
video_frame_stream_->Initialize(
demuxer_stream_.get(),
base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)),
base::Bind(&VideoFrameStreamTest::OnInitialized,
base::Unretained(this)));
message_loop_.RunUntilIdle();
}
// Fake Decrypt() function used by DecryptingDemuxerStream. It does nothing
// but removes the DecryptConfig to make the buffer unencrypted.
void Decrypt(Decryptor::StreamType stream_type,
const scoped_refptr<DecoderBuffer>& encrypted,
const Decryptor::DecryptCB& decrypt_cb) {
DCHECK(encrypted->decrypt_config());
if (has_no_key_) {
decrypt_cb.Run(Decryptor::kNoKey, NULL);
return;
}
DCHECK_EQ(stream_type, Decryptor::kVideo);
scoped_refptr<DecoderBuffer> decrypted = DecoderBuffer::CopyFrom(
encrypted->data(), encrypted->data_size());
decrypted->set_timestamp(encrypted->timestamp());
decrypted->set_duration(encrypted->duration());
decrypt_cb.Run(Decryptor::kSuccess, decrypted);
}
// Callback for VideoFrameStream::Read().
void FrameReady(VideoFrameStream::Status status,
const scoped_refptr<VideoFrame>& frame) {
DCHECK(pending_read_);
// TODO(xhwang): Add test cases where the fake decoder returns error or
// the fake demuxer aborts demuxer read.
ASSERT_TRUE(status == VideoFrameStream::OK ||
status == VideoFrameStream::ABORTED) << status;
frame_read_ = frame;
if (frame.get() && !frame->end_of_stream())
num_decoded_frames_++;
pending_read_ = false;
}
void OnReset() {
DCHECK(!pending_read_);
DCHECK(pending_reset_);
pending_reset_ = false;
}
void OnStopped() {
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(pending_stop_);
pending_stop_ = false;
is_initialized_ = false;
decoder_ = NULL;
}
void ReadOneFrame() {
frame_read_ = NULL;
pending_read_ = true;
video_frame_stream_->Read(base::Bind(
&VideoFrameStreamTest::FrameReady, base::Unretained(this)));
message_loop_.RunUntilIdle();
}
void ReadUntilPending() {
do {
ReadOneFrame();
} while (!pending_read_);
}
enum PendingState {
NOT_PENDING,
DEMUXER_READ_NORMAL,
DEMUXER_READ_CONFIG_CHANGE,
SET_DECRYPTOR,
DECRYPTOR_NO_KEY,
DECODER_INIT,
DECODER_REINIT,
DECODER_READ,
DECODER_RESET,
DECODER_STOP
};
void EnterPendingState(PendingState state) {
DCHECK_NE(state, NOT_PENDING);
switch (state) {
case DEMUXER_READ_NORMAL:
demuxer_stream_->HoldNextRead();
ReadUntilPending();
break;
case DEMUXER_READ_CONFIG_CHANGE:
demuxer_stream_->HoldNextConfigChangeRead();
ReadUntilPending();
break;
case SET_DECRYPTOR:
// Hold DecryptorReadyCB.
EXPECT_CALL(*this, SetDecryptorReadyCallback(_))
.Times(2);
// Initialize will fail because no decryptor is available.
InitializeVideoFrameStream();
break;
case DECRYPTOR_NO_KEY:
EXPECT_CALL(*this, SetDecryptorReadyCallback(_))
.WillRepeatedly(RunCallback<0>(decryptor_.get()));
has_no_key_ = true;
ReadOneFrame();
break;
case DECODER_INIT:
EXPECT_CALL(*this, SetDecryptorReadyCallback(_))
.WillRepeatedly(RunCallback<0>(decryptor_.get()));
decoder_->HoldNextInit();
InitializeVideoFrameStream();
break;
case DECODER_REINIT:
decoder_->HoldNextInit();
ReadUntilPending();
break;
case DECODER_READ:
decoder_->HoldNextRead();
ReadUntilPending();
break;
case DECODER_RESET:
decoder_->HoldNextReset();
pending_reset_ = true;
video_frame_stream_->Reset(base::Bind(&VideoFrameStreamTest::OnReset,
base::Unretained(this)));
message_loop_.RunUntilIdle();
break;
case DECODER_STOP:
decoder_->HoldNextStop();
// Check that the pipeline statistics callback was fired correctly.
EXPECT_EQ(decoder_->total_bytes_decoded(), total_bytes_decoded_);
pending_stop_ = true;
video_frame_stream_->Stop(base::Bind(&VideoFrameStreamTest::OnStopped,
base::Unretained(this)));
message_loop_.RunUntilIdle();
break;
case NOT_PENDING:
NOTREACHED();
break;
}
}
void SatisfyPendingCallback(PendingState state) {
DCHECK_NE(state, NOT_PENDING);
switch (state) {
case DEMUXER_READ_NORMAL:
case DEMUXER_READ_CONFIG_CHANGE:
demuxer_stream_->SatisfyRead();
break;
// These two cases are only interesting to test during
// VideoFrameStream::Stop(). There's no need to satisfy a callback.
case SET_DECRYPTOR:
case DECRYPTOR_NO_KEY:
NOTREACHED();
break;
case DECODER_INIT:
decoder_->SatisfyInit();
break;
case DECODER_REINIT:
decoder_->SatisfyInit();
break;
case DECODER_READ:
decoder_->SatisfyRead();
break;
case DECODER_RESET:
decoder_->SatisfyReset();
break;
case DECODER_STOP:
DCHECK(pending_stop_);
decoder_->SatisfyStop();
break;
case NOT_PENDING:
NOTREACHED();
break;
}
message_loop_.RunUntilIdle();
}
void Initialize() {
EnterPendingState(DECODER_INIT);
SatisfyPendingCallback(DECODER_INIT);
}
void Read() {
EnterPendingState(DECODER_READ);
SatisfyPendingCallback(DECODER_READ);
}
void Reset() {
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DECODER_RESET);
}
void Stop() {
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_STOP);
}
base::MessageLoop message_loop_;
scoped_ptr<VideoFrameStream> video_frame_stream_;
scoped_ptr<FakeDemuxerStream> demuxer_stream_;
// Use NiceMock since we don't care about most of calls on the decryptor,
// e.g. RegisterNewKeyCB().
scoped_ptr<NiceMock<MockDecryptor> > decryptor_;
FakeVideoDecoder* decoder_; // Owned by |video_frame_stream_|.
bool is_initialized_;
int num_decoded_frames_;
bool pending_initialize_;
bool pending_read_;
bool pending_reset_;
bool pending_stop_;
int total_bytes_decoded_;
scoped_refptr<VideoFrame> frame_read_;
// Decryptor has no key to decrypt a frame.
bool has_no_key_;
private:
DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest);
};
INSTANTIATE_TEST_CASE_P(Clear, VideoFrameStreamTest, testing::Values(false));
INSTANTIATE_TEST_CASE_P(Encrypted, VideoFrameStreamTest, testing::Values(true));
TEST_P(VideoFrameStreamTest, Initialization) {
Initialize();
}
TEST_P(VideoFrameStreamTest, ReadOneFrame) {
Initialize();
Read();
}
TEST_P(VideoFrameStreamTest, ReadAllFrames) {
Initialize();
do {
Read();
} while (frame_read_.get() && !frame_read_->end_of_stream());
const int total_num_frames = kNumConfigs * kNumBuffersInOneConfig;
DCHECK_EQ(num_decoded_frames_, total_num_frames);
}
TEST_P(VideoFrameStreamTest, Read_AfterReset) {
Initialize();
Reset();
Read();
Reset();
Read();
}
// No Reset() before initialization is successfully completed.
TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) {
Initialize();
Reset();
Read();
}
TEST_P(VideoFrameStreamTest, Reset_DuringReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
// VideoDecoder::Reset() is not called when we reset during reinitialization.
pending_reset_ = true;
video_frame_stream_->Reset(
base::Bind(&VideoFrameStreamTest::OnReset, base::Unretained(this)));
SatisfyPendingCallback(DECODER_REINIT);
Read();
}
TEST_P(VideoFrameStreamTest, Reset_AfterReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
SatisfyPendingCallback(DECODER_REINIT);
Reset();
Read();
}
TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_Normal) {
Initialize();
EnterPendingState(DEMUXER_READ_NORMAL);
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DEMUXER_READ_NORMAL);
SatisfyPendingCallback(DECODER_RESET);
Read();
}
TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DECODER_RESET);
Read();
}
TEST_P(VideoFrameStreamTest, Reset_DuringNormalDecoderRead) {
Initialize();
EnterPendingState(DECODER_READ);
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DECODER_READ);
SatisfyPendingCallback(DECODER_RESET);
Read();
}
TEST_P(VideoFrameStreamTest, Reset_AfterNormalRead) {
Initialize();
Read();
Reset();
Read();
}
TEST_P(VideoFrameStreamTest, Reset_AfterDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
Reset();
Read();
}
TEST_P(VideoFrameStreamTest, Reset_DuringNoKeyRead) {
Initialize();
EnterPendingState(DECRYPTOR_NO_KEY);
Reset();
}
TEST_P(VideoFrameStreamTest, Stop_BeforeInitialization) {
pending_stop_ = true;
video_frame_stream_->Stop(
base::Bind(&VideoFrameStreamTest::OnStopped, base::Unretained(this)));
message_loop_.RunUntilIdle();
}
TEST_P(VideoFrameStreamTest, Stop_DuringSetDecryptor) {
if (!GetParam()) {
DVLOG(1) << "SetDecryptor test only runs when the stream is encrytped.";
return;
}
EnterPendingState(SET_DECRYPTOR);
pending_stop_ = true;
video_frame_stream_->Stop(
base::Bind(&VideoFrameStreamTest::OnStopped, base::Unretained(this)));
message_loop_.RunUntilIdle();
}
TEST_P(VideoFrameStreamTest, Stop_DuringInitialization) {
EnterPendingState(DECODER_INIT);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_INIT);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_AfterInitialization) {
Initialize();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_REINIT);
SatisfyPendingCallback(DECODER_STOP);
}
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);
SatisfyPendingCallback(DEMUXER_READ_NORMAL);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_DuringNormalDecoderRead) {
Initialize();
EnterPendingState(DECODER_READ);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_READ);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_AfterNormalRead) {
Initialize();
Read();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterConfigChangeRead) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringNoKeyRead) {
Initialize();
EnterPendingState(DECRYPTOR_NO_KEY);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringReset) {
Initialize();
EnterPendingState(DECODER_RESET);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_RESET);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_AfterReset) {
Initialize();
Reset();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringRead_DuringReset) {
Initialize();
EnterPendingState(DECODER_READ);
EnterPendingState(DECODER_RESET);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_READ);
SatisfyPendingCallback(DECODER_RESET);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_DuringReset) {
Initialize();
EnterPendingState(DECODER_READ);
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DECODER_READ);
EnterPendingState(DECODER_STOP);
SatisfyPendingCallback(DECODER_RESET);
SatisfyPendingCallback(DECODER_STOP);
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_AfterReset) {
Initialize();
Read();
Reset();
Stop();
}
} // namespace media