Commit 658e0c12 authored by xhwang@chromium.org's avatar xhwang@chromium.org

Encrypted Media: Add config change support in DecryptingVideoDecoder.

BUG=168128,168129
TEST=Added unittests; demo page works.

Review URL: https://codereview.chromium.org/11745026

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175299 0039d316-1c4b-4281-b951-d872f2087c98
parent 9a2e8c3b
......@@ -86,6 +86,7 @@ void DecryptingVideoDecoder::Reset(const base::Closure& closure) {
DVLOG(2) << "Reset() - state: " << state_;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == kIdle ||
state_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead ||
state_ == kPendingDecode ||
state_ == kWaitingForKey ||
......@@ -101,7 +102,9 @@ 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_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead ||
state_ == kPendingDecode) {
DCHECK(!read_cb_.is_null());
return;
}
......@@ -195,6 +198,35 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) {
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
void DecryptingVideoDecoder::FinishConfigChange(bool success) {
DVLOG(2) << "FinishConfigChange()";
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ == kStopped)
return;
DCHECK_EQ(state_, kPendingConfigChange) << state_;
DCHECK(!read_cb_.is_null());
if (!success) {
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
state_ = kDecodeFinished;
if (!reset_cb_.is_null())
base::ResetAndReturn(&reset_cb_).Run();
return;
}
// Config change succeeded.
if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
DoReset();
return;
}
state_ = kPendingDemuxerRead;
ReadFromDemuxerStream();
}
void DecryptingVideoDecoder::ReadFromDemuxerStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
......@@ -217,10 +249,23 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer(
DCHECK(!read_cb_.is_null());
DCHECK_EQ(buffer != NULL, status == DemuxerStream::kOk) << status;
if (status == DemuxerStream::kConfigChanged) {
DVLOG(2) << "DecryptAndDecodeBuffer() - kConfigChanged";
scoped_ptr<VideoDecoderConfig> scoped_config(new VideoDecoderConfig());
scoped_config->CopyFrom(demuxer_stream_->video_decoder_config());
state_ = kPendingConfigChange;
decryptor_->DeinitializeDecoder(Decryptor::kVideo);
decryptor_->InitializeVideoDecoder(
scoped_config.Pass(), BindToCurrentLoop(base::Bind(
&DecryptingVideoDecoder::FinishConfigChange, this)));
return;
}
if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
if (!reset_cb_.is_null())
DoReset();
DoReset();
return;
}
......@@ -231,16 +276,6 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer(
return;
}
if (status == DemuxerStream::kConfigChanged) {
// TODO(xhwang): Add config change support.
// The |state_| is chosen to be kDecodeFinished here to be consistent with
// the implementation of FFmpegVideoDecoder.
DVLOG(2) << "DecryptAndDecodeBuffer() - kConfigChanged";
state_ = kDecodeFinished;
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
return;
}
DCHECK_EQ(status, DemuxerStream::kOk);
pending_buffer_to_decode_ = buffer;
state_ = kPendingDecode;
......
......@@ -55,6 +55,7 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
kDecryptorRequested,
kPendingDecoderInit,
kIdle,
kPendingConfigChange,
kPendingDemuxerRead,
kPendingDecode,
kWaitingForKey,
......@@ -65,9 +66,12 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
// Callback for DecryptorHost::RequestDecryptor().
void SetDecryptor(Decryptor* decryptor);
// Callback for Decryptor::InitializeVideoDecoder().
// Callback for Decryptor::InitializeVideoDecoder() during initialization.
void FinishInitialization(bool success);
// Callback for Decryptor::InitializeVideoDecoder() during config change.
void FinishConfigChange(bool success);
void ReadFromDemuxerStream();
// Callback for DemuxerStream::Read().
......
......@@ -339,6 +339,51 @@ 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 config change on the demuxer stream.
TEST_F(DecryptingVideoDecoderTest, DemuxerRead_ConfigChange) {
Initialize();
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(RunCallback<1>(true));
EXPECT_CALL(*demuxer_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged,
scoped_refptr<DecoderBuffer>()))
.WillRepeatedly(ReturnBuffer(encrypted_buffer_));
EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _))
.WillRepeatedly(RunCallback<1>(Decryptor::kSuccess,
decoded_video_frame_));
EXPECT_CALL(statistics_cb_, OnStatistics(_));
ReadAndExpectFrameReadyWith(VideoDecoder::kOk, decoded_video_frame_);
}
// Test config change failure.
TEST_F(DecryptingVideoDecoderTest, DemuxerRead_ConfigChangeFailed) {
Initialize();
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(RunCallback<1>(false));
EXPECT_CALL(*demuxer_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged,
scoped_refptr<DecoderBuffer>()))
.WillRepeatedly(ReturnBuffer(encrypted_buffer_));
ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
}
// Test the case where the a key is added when the decryptor is in
// kWaitingForKey state.
TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) {
......@@ -388,8 +433,9 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) {
Reset();
}
// Test resetting when the decoder is in kPendingDemuxerRead state.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) {
// 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();
......@@ -401,6 +447,85 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) {
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 kPendingDemuxerRead state and the read
// callback is returned with kConfigChanged.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingReadState();
Reset();
// Even during pending reset, the decoder still needs to be initialized with
// the new config.
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(RunCallback<1>(true));
EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, null_video_frame_));
base::ResetAndReturn(&pending_demuxer_read_cb_)
.Run(DemuxerStream::kConfigChanged, NULL);
message_loop_.RunUntilIdle();
}
// Test resetting when the decoder is in kPendingDemuxerRead state, the read
// callback is returned with kConfigChanged and the config change fails.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringDemuxerRead_ConfigChangeFailed) {
Initialize();
EnterPendingReadState();
Reset();
// Even during pending reset, the decoder still needs to be initialized with
// the new config.
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(RunCallback<1>(false));
EXPECT_CALL(*this, FrameReady(VideoDecoder::kDecodeError, null_video_frame_));
base::ResetAndReturn(&pending_demuxer_read_cb_)
.Run(DemuxerStream::kConfigChanged, NULL);
message_loop_.RunUntilIdle();
}
// Test resetting when the decoder is in kPendingConfigChange state.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingConfigChange) {
Initialize();
EnterNormalDecodingState();
EXPECT_CALL(*demuxer_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged,
scoped_refptr<DecoderBuffer>()));
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(SaveArg<1>(&pending_init_cb_));
decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
base::Unretained(this)));
message_loop_.RunUntilIdle();
EXPECT_FALSE(pending_init_cb_.is_null());
EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
Reset();
base::ResetAndReturn(&pending_init_cb_).Run(true);
message_loop_.RunUntilIdle();
}
// Test resetting when the decoder is in kPendingDecode state.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecode) {
Initialize();
......@@ -493,6 +618,28 @@ TEST_F(DecryptingVideoDecoderTest, Stop_DuringIdleAfterDecodedOneFrame) {
Stop();
}
// Test stopping when the decoder is in kPendingConfigChange state.
TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingConfigChange) {
Initialize();
EnterNormalDecodingState();
EXPECT_CALL(*demuxer_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged,
scoped_refptr<DecoderBuffer>()));
EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo));
EXPECT_CALL(*decryptor_, InitializeVideoDecoderMock(_, _))
.WillOnce(SaveArg<1>(&pending_init_cb_));
decoder_->Read(base::Bind(&DecryptingVideoDecoderTest::FrameReady,
base::Unretained(this)));
message_loop_.RunUntilIdle();
EXPECT_FALSE(pending_init_cb_.is_null());
EXPECT_CALL(*this, FrameReady(VideoDecoder::kOk, IsNull()));
Stop();
}
// Test stopping when the decoder is in kPendingDemuxerRead state.
TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDemuxerRead) {
Initialize();
......@@ -565,42 +712,4 @@ TEST_F(DecryptingVideoDecoderTest, Stop_AfterStop) {
Stop();
}
// 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 aborted read on the demuxer stream when the decoder is being reset.
TEST_F(DecryptingVideoDecoderTest, DemuxerRead_AbortedDuringReset) {
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 config change on the demuxer stream.
TEST_F(DecryptingVideoDecoderTest, DemuxerRead_ConfigChanged) {
Initialize();
EXPECT_CALL(*demuxer_, Read(_))
.WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged,
scoped_refptr<DecoderBuffer>()));
// TODO(xhwang): Update this test when kConfigChanged is supported in
// DecryptingVideoDecoder.
ReadAndExpectFrameReadyWith(VideoDecoder::kDecodeError, null_video_frame_);
}
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment