static const int kNumConfigs = 3;
static const int kNumBuffersInOneConfig = 5;
-static const int kDecodingDelay = 7;
namespace media {
+struct VideoFrameStreamTestParams {
+ VideoFrameStreamTestParams(bool is_encrypted,
+ bool enable_get_decode_output,
+ int decoding_delay,
+ int parallel_decoding)
+ : is_encrypted(is_encrypted),
+ enable_get_decode_output(enable_get_decode_output),
+ decoding_delay(decoding_delay),
+ parallel_decoding(parallel_decoding) {}
+
+ bool is_encrypted;
+ bool enable_get_decode_output;
+ int decoding_delay;
+ int parallel_decoding;
+};
+
class VideoFrameStreamTest
: public testing::Test,
- public testing::WithParamInterface<std::tr1::tuple<bool, bool> > {
+ public testing::WithParamInterface<VideoFrameStreamTestParams> {
public:
VideoFrameStreamTest()
: demuxer_stream_(new FakeDemuxerStream(kNumConfigs,
kNumBuffersInOneConfig,
- IsEncrypted())),
+ GetParam().is_encrypted)),
decryptor_(new NiceMock<MockDecryptor>()),
- decoder_(
- new FakeVideoDecoder(kDecodingDelay, IsGetDecodeOutputEnabled())),
+ decoder_(new FakeVideoDecoder(GetParam().decoding_delay,
+ GetParam().enable_get_decode_output,
+ GetParam().parallel_decoding)),
is_initialized_(false),
num_decoded_frames_(0),
pending_initialize_(false),
EXPECT_FALSE(is_initialized_);
}
- bool IsEncrypted() {
- return std::tr1::get<0>(GetParam());
- }
-
- bool IsGetDecodeOutputEnabled() {
- return std::tr1::get<1>(GetParam());
- }
-
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) {
+ void OnInitialized(bool success) {
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(pending_initialize_);
pending_initialize_ = true;
video_frame_stream_->Initialize(
demuxer_stream_.get(),
+ false,
base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)),
base::Bind(&VideoFrameStreamTest::OnInitialized,
base::Unretained(this)));
}
DCHECK_EQ(stream_type, Decryptor::kVideo);
- scoped_refptr<DecoderBuffer> decrypted = DecoderBuffer::CopyFrom(
- encrypted->data(), encrypted->data_size());
+ 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);
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;
+ last_read_status_ = status;
if (frame.get() && !frame->end_of_stream())
num_decoded_frames_++;
pending_read_ = false;
}
+ void FrameReadyHoldDemuxer(VideoFrameStream::Status status,
+ const scoped_refptr<VideoFrame>& frame) {
+ FrameReady(status, frame);
+
+ }
+
void OnReset() {
DCHECK(!pending_read_);
DCHECK(pending_reset_);
}
void OnStopped() {
+ DCHECK(!pending_initialize_);
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(pending_stop_);
DECRYPTOR_NO_KEY,
DECODER_INIT,
DECODER_REINIT,
- DECODER_READ,
- DECODER_RESET,
- DECODER_STOP
+ DECODER_DECODE,
+ DECODER_RESET
};
void EnterPendingState(PendingState state) {
ReadUntilPending();
break;
- case DECODER_READ:
- decoder_->HoldNextRead();
+ case DECODER_DECODE:
+ decoder_->HoldDecode();
ReadUntilPending();
break;
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;
decoder_->SatisfyInit();
break;
- case DECODER_READ:
- decoder_->SatisfyRead();
+ case DECODER_DECODE:
+ decoder_->SatisfyDecode();
break;
case DECODER_RESET:
decoder_->SatisfyReset();
break;
- case DECODER_STOP:
- DCHECK(pending_stop_);
- decoder_->SatisfyStop();
- break;
-
case NOT_PENDING:
NOTREACHED();
break;
}
void Read() {
- EnterPendingState(DECODER_READ);
- SatisfyPendingCallback(DECODER_READ);
+ EnterPendingState(DECODER_DECODE);
+ SatisfyPendingCallback(DECODER_DECODE);
}
void Reset() {
}
void Stop() {
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_STOP);
+ // 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();
}
base::MessageLoop message_loop_;
bool pending_stop_;
int total_bytes_decoded_;
scoped_refptr<VideoFrame> frame_read_;
+ VideoFrameStream::Status last_read_status_;
// Decryptor has no key to decrypt a frame.
bool has_no_key_;
DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest);
};
-INSTANTIATE_TEST_CASE_P(Clear, VideoFrameStreamTest,
- testing::Combine(testing::Values(false), testing::Values(false)));
-INSTANTIATE_TEST_CASE_P(Clear_GetDecodeOutput, VideoFrameStreamTest,
- testing::Combine(testing::Values(false), testing::Values(true)));
-INSTANTIATE_TEST_CASE_P(Encrypted, VideoFrameStreamTest,
- testing::Combine(testing::Values(true), testing::Values(false)));
-INSTANTIATE_TEST_CASE_P(Encrypted_GetDecodeOutput, VideoFrameStreamTest,
- testing::Combine(testing::Values(true), testing::Values(true)));
+INSTANTIATE_TEST_CASE_P(
+ Clear,
+ VideoFrameStreamTest,
+ ::testing::Values(
+ VideoFrameStreamTestParams(false, false, 0, 1),
+ VideoFrameStreamTestParams(false, false, 3, 1),
+ VideoFrameStreamTestParams(false, false, 7, 1)));
+INSTANTIATE_TEST_CASE_P(
+ Clear_GetDecodeOutput,
+ VideoFrameStreamTest,
+ ::testing::Values(
+ VideoFrameStreamTestParams(false, true, 0, 1),
+ VideoFrameStreamTestParams(false, true, 3, 1),
+ VideoFrameStreamTestParams(false, true, 7, 1)));
+INSTANTIATE_TEST_CASE_P(
+ Encrypted,
+ VideoFrameStreamTest,
+ ::testing::Values(
+ VideoFrameStreamTestParams(true, false, 7, 1)));
+INSTANTIATE_TEST_CASE_P(
+ Encrypted_GetDecodeOutput,
+ VideoFrameStreamTest,
+ ::testing::Values(
+ VideoFrameStreamTestParams(true, true, 7, 1)));
+
+INSTANTIATE_TEST_CASE_P(
+ Clear_Parallel,
+ VideoFrameStreamTest,
+ ::testing::Values(
+ VideoFrameStreamTestParams(false, false, 0, 3),
+ VideoFrameStreamTestParams(false, false, 2, 3)));
+
TEST_P(VideoFrameStreamTest, Initialization) {
Initialize();
Read();
}
+TEST_P(VideoFrameStreamTest, Read_BlockedDemuxer) {
+ Initialize();
+ demuxer_stream_->HoldNextRead();
+ ReadOneFrame();
+ EXPECT_TRUE(pending_read_);
+
+ int demuxed_buffers = 0;
+
+ // Pass frames from the demuxer to the VideoFrameStream until the first read
+ // request is satisfied.
+ while (pending_read_) {
+ ++demuxed_buffers;
+ demuxer_stream_->SatisfyReadAndHoldNext();
+ message_loop_.RunUntilIdle();
+ }
+
+ EXPECT_EQ(std::min(GetParam().decoding_delay + 1, kNumBuffersInOneConfig + 1),
+ demuxed_buffers);
+
+ // At this point the stream is waiting on read from the demuxer, but there is
+ // no pending read from the stream. The stream should be blocked if we try
+ // reading from it again.
+ ReadUntilPending();
+
+ demuxer_stream_->SatisfyRead();
+ message_loop_.RunUntilIdle();
+ EXPECT_FALSE(pending_read_);
+}
+
+TEST_P(VideoFrameStreamTest, Read_BlockedDemuxerAndDecoder) {
+ // Test applies only when the decoder allows multiple parallel requests.
+ if (GetParam().parallel_decoding == 1)
+ return;
+
+ Initialize();
+ demuxer_stream_->HoldNextRead();
+ decoder_->HoldDecode();
+ ReadOneFrame();
+ EXPECT_TRUE(pending_read_);
+
+ int demuxed_buffers = 0;
+
+ // Pass frames from the demuxer to the VideoFrameStream until the first read
+ // request is satisfied, while always keeping one decode request pending.
+ while (pending_read_) {
+ ++demuxed_buffers;
+ demuxer_stream_->SatisfyReadAndHoldNext();
+ message_loop_.RunUntilIdle();
+
+ // Always keep one decode request pending.
+ if (demuxed_buffers > 1) {
+ decoder_->SatisfySingleDecode();
+ message_loop_.RunUntilIdle();
+ }
+ }
+
+ ReadUntilPending();
+ EXPECT_TRUE(pending_read_);
+
+ // Unblocking one decode request should unblock read even when demuxer is
+ // still blocked.
+ decoder_->SatisfySingleDecode();
+ message_loop_.RunUntilIdle();
+ EXPECT_FALSE(pending_read_);
+
+ // Stream should still be blocked on the demuxer after unblocking the decoder.
+ decoder_->SatisfyDecode();
+ ReadUntilPending();
+ EXPECT_TRUE(pending_read_);
+
+ // Verify that the stream has returned all frames that have been demuxed,
+ // accounting for the decoder delay.
+ EXPECT_EQ(demuxed_buffers - GetParam().decoding_delay, num_decoded_frames_);
+
+ // Unblocking the demuxer will unblock the stream.
+ demuxer_stream_->SatisfyRead();
+ message_loop_.RunUntilIdle();
+ EXPECT_FALSE(pending_read_);
+}
+
// No Reset() before initialization is successfully completed.
TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) {
Read();
}
-TEST_P(VideoFrameStreamTest, Reset_DuringNormalDecoderRead) {
+TEST_P(VideoFrameStreamTest, Reset_DuringNormalDecoderDecode) {
Initialize();
- EnterPendingState(DECODER_READ);
+ EnterPendingState(DECODER_DECODE);
EnterPendingState(DECODER_RESET);
- SatisfyPendingCallback(DECODER_READ);
+ SatisfyPendingCallback(DECODER_DECODE);
SatisfyPendingCallback(DECODER_RESET);
Read();
}
}
TEST_P(VideoFrameStreamTest, Stop_DuringSetDecryptor) {
- if (!IsEncrypted()) {
+ if (!GetParam().is_encrypted) {
DVLOG(1) << "SetDecryptor test only runs when the stream is encrytped.";
return;
}
TEST_P(VideoFrameStreamTest, Stop_DuringInitialization) {
EnterPendingState(DECODER_INIT);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_INIT);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterInitialization) {
TEST_P(VideoFrameStreamTest, Stop_DuringReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_REINIT);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterReinitialization) {
TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_Normal) {
Initialize();
EnterPendingState(DEMUXER_READ_NORMAL);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DEMUXER_READ_NORMAL);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
-TEST_P(VideoFrameStreamTest, Stop_DuringNormalDecoderRead) {
+TEST_P(VideoFrameStreamTest, Stop_DuringNormalDecoderDecode) {
Initialize();
- EnterPendingState(DECODER_READ);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_READ);
- SatisfyPendingCallback(DECODER_STOP);
+ EnterPendingState(DECODER_DECODE);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterNormalRead) {
TEST_P(VideoFrameStreamTest, Stop_DuringReset) {
Initialize();
EnterPendingState(DECODER_RESET);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_RESET);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterReset) {
TEST_P(VideoFrameStreamTest, Stop_DuringRead_DuringReset) {
Initialize();
- EnterPendingState(DECODER_READ);
+ EnterPendingState(DECODER_DECODE);
EnterPendingState(DECODER_RESET);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_READ);
- SatisfyPendingCallback(DECODER_RESET);
- SatisfyPendingCallback(DECODER_STOP);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_DuringReset) {
Initialize();
- EnterPendingState(DECODER_READ);
+ EnterPendingState(DECODER_DECODE);
EnterPendingState(DECODER_RESET);
- SatisfyPendingCallback(DECODER_READ);
- EnterPendingState(DECODER_STOP);
- SatisfyPendingCallback(DECODER_RESET);
- SatisfyPendingCallback(DECODER_STOP);
+ SatisfyPendingCallback(DECODER_DECODE);
+ Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_AfterReset) {
Stop();
}
+TEST_P(VideoFrameStreamTest, DecoderErrorWhenReading) {
+ Initialize();
+ EnterPendingState(DECODER_DECODE);
+ decoder_->SimulateError();
+ message_loop_.RunUntilIdle();
+ ASSERT_FALSE(pending_read_);
+ ASSERT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
+}
+
+TEST_P(VideoFrameStreamTest, DecoderErrorWhenNotReading) {
+ Initialize();
+
+ decoder_->HoldDecode();
+ ReadOneFrame();
+ EXPECT_TRUE(pending_read_);
+
+ // Satisfy decode requests until we get the first frame out.
+ while (pending_read_) {
+ decoder_->SatisfySingleDecode();
+ message_loop_.RunUntilIdle();
+ }
+
+ // Trigger an error in the decoding.
+ decoder_->SimulateError();
+
+ // The error must surface from Read() as DECODE_ERROR.
+ while (last_read_status_ == VideoFrameStream::OK) {
+ ReadOneFrame();
+ message_loop_.RunUntilIdle();
+ EXPECT_FALSE(pending_read_);
+ }
+ EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_);
+}
+
} // namespace media