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) { ...@@ -86,6 +86,7 @@ void DecryptingVideoDecoder::Reset(const base::Closure& closure) {
DVLOG(2) << "Reset() - state: " << state_; DVLOG(2) << "Reset() - state: " << state_;
DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == kIdle || DCHECK(state_ == kIdle ||
state_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead || state_ == kPendingDemuxerRead ||
state_ == kPendingDecode || state_ == kPendingDecode ||
state_ == kWaitingForKey || state_ == kWaitingForKey ||
...@@ -101,7 +102,9 @@ void DecryptingVideoDecoder::Reset(const base::Closure& closure) { ...@@ -101,7 +102,9 @@ void DecryptingVideoDecoder::Reset(const base::Closure& closure) {
// Defer the resetting process in this case. The |reset_cb_| will be fired // Defer the resetting process in this case. The |reset_cb_| will be fired
// after the read callback is fired - see DecryptAndDecodeBuffer() and // after the read callback is fired - see DecryptAndDecodeBuffer() and
// DeliverFrame(). // DeliverFrame().
if (state_ == kPendingDemuxerRead || state_ == kPendingDecode) { if (state_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead ||
state_ == kPendingDecode) {
DCHECK(!read_cb_.is_null()); DCHECK(!read_cb_.is_null());
return; return;
} }
...@@ -195,6 +198,35 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) { ...@@ -195,6 +198,35 @@ void DecryptingVideoDecoder::FinishInitialization(bool success) {
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); 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() { void DecryptingVideoDecoder::ReadFromDemuxerStream() {
DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDemuxerRead) << state_; DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
...@@ -217,10 +249,23 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer( ...@@ -217,10 +249,23 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer(
DCHECK(!read_cb_.is_null()); DCHECK(!read_cb_.is_null());
DCHECK_EQ(buffer != NULL, status == DemuxerStream::kOk) << status; 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()) { if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kOk, NULL); base::ResetAndReturn(&read_cb_).Run(kOk, NULL);
if (!reset_cb_.is_null()) DoReset();
DoReset();
return; return;
} }
...@@ -231,16 +276,6 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer( ...@@ -231,16 +276,6 @@ void DecryptingVideoDecoder::DecryptAndDecodeBuffer(
return; 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); DCHECK_EQ(status, DemuxerStream::kOk);
pending_buffer_to_decode_ = buffer; pending_buffer_to_decode_ = buffer;
state_ = kPendingDecode; state_ = kPendingDecode;
......
...@@ -55,6 +55,7 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { ...@@ -55,6 +55,7 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
kDecryptorRequested, kDecryptorRequested,
kPendingDecoderInit, kPendingDecoderInit,
kIdle, kIdle,
kPendingConfigChange,
kPendingDemuxerRead, kPendingDemuxerRead,
kPendingDecode, kPendingDecode,
kWaitingForKey, kWaitingForKey,
...@@ -65,9 +66,12 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { ...@@ -65,9 +66,12 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder {
// Callback for DecryptorHost::RequestDecryptor(). // Callback for DecryptorHost::RequestDecryptor().
void SetDecryptor(Decryptor* decryptor); void SetDecryptor(Decryptor* decryptor);
// Callback for Decryptor::InitializeVideoDecoder(). // Callback for Decryptor::InitializeVideoDecoder() during initialization.
void FinishInitialization(bool success); void FinishInitialization(bool success);
// Callback for Decryptor::InitializeVideoDecoder() during config change.
void FinishConfigChange(bool success);
void ReadFromDemuxerStream(); void ReadFromDemuxerStream();
// Callback for DemuxerStream::Read(). // Callback for DemuxerStream::Read().
......
...@@ -339,6 +339,51 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) { ...@@ -339,6 +339,51 @@ TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) {
EnterEndOfStreamState(); 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 // Test the case where the a key is added when the decryptor is in
// kWaitingForKey state. // kWaitingForKey state.
TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) { TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) {
...@@ -388,8 +433,9 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) { ...@@ -388,8 +433,9 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) {
Reset(); Reset();
} }
// Test resetting when the decoder is in kPendingDemuxerRead state. // Test resetting when the decoder is in kPendingDemuxerRead state and the read
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) { // callback is returned with kOk.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringDemuxerRead_Ok) {
Initialize(); Initialize();
EnterPendingReadState(); EnterPendingReadState();
...@@ -401,6 +447,85 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) { ...@@ -401,6 +447,85 @@ TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDemuxerRead) {
message_loop_.RunUntilIdle(); 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 resetting when the decoder is in kPendingDecode state.
TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecode) { TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecode) {
Initialize(); Initialize();
...@@ -493,6 +618,28 @@ TEST_F(DecryptingVideoDecoderTest, Stop_DuringIdleAfterDecodedOneFrame) { ...@@ -493,6 +618,28 @@ TEST_F(DecryptingVideoDecoderTest, Stop_DuringIdleAfterDecodedOneFrame) {
Stop(); 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 stopping when the decoder is in kPendingDemuxerRead state.
TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDemuxerRead) { TEST_F(DecryptingVideoDecoderTest, Stop_DuringPendingDemuxerRead) {
Initialize(); Initialize();
...@@ -565,42 +712,4 @@ TEST_F(DecryptingVideoDecoderTest, Stop_AfterStop) { ...@@ -565,42 +712,4 @@ TEST_F(DecryptingVideoDecoderTest, Stop_AfterStop) {
Stop(); 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 } // 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