diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 21:42:55 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-23 21:42:55 +0000 |
commit | c7ecea8ae559e894509c18fa27018a98836974db (patch) | |
tree | 600fb011a89ad93f4969db2cf22d749f8e2a8aa7 | |
parent | f040795567a5df300bfc522e5f6db2051c4cee27 (diff) | |
download | chromium_src-c7ecea8ae559e894509c18fa27018a98836974db.zip chromium_src-c7ecea8ae559e894509c18fa27018a98836974db.tar.gz chromium_src-c7ecea8ae559e894509c18fa27018a98836974db.tar.bz2 |
Add VideoDecoderSelector::Abort().
This is useful to cancel the video decoder selection process when a video
decoder or the DecryptingDemuxerStream is waiting for initialization to be
completed.
BUG=222054,chrome-os-partner:22518
TEST=Added unittest to cover this.
R=scherkus@chromium.org
Review URL: https://codereview.chromium.org/23849007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224798 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/filters/decrypting_demuxer_stream.cc | 23 | ||||
-rw-r--r-- | media/filters/decrypting_demuxer_stream.h | 4 | ||||
-rw-r--r-- | media/filters/decrypting_demuxer_stream_unittest.cc | 13 | ||||
-rw-r--r-- | media/filters/video_decoder_selector.cc | 108 | ||||
-rw-r--r-- | media/filters/video_decoder_selector.h | 10 | ||||
-rw-r--r-- | media/filters/video_decoder_selector_unittest.cc | 94 | ||||
-rw-r--r-- | media/filters/video_frame_stream.cc | 27 | ||||
-rw-r--r-- | media/filters/video_frame_stream_unittest.cc | 77 |
8 files changed, 286 insertions, 70 deletions
diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc index 7a9dd61..5502148 100644 --- a/media/filters/decrypting_demuxer_stream.cc +++ b/media/filters/decrypting_demuxer_stream.cc @@ -77,12 +77,19 @@ void DecryptingDemuxerStream::Read(const ReadCB& read_cb) { void DecryptingDemuxerStream::Reset(const base::Closure& closure) { DVLOG(2) << __FUNCTION__ << " - state: " << state_; DCHECK(message_loop_->BelongsToCurrentThread()); - DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; - DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. + DCHECK(state_ != kUninitialized) << state_; DCHECK(reset_cb_.is_null()); reset_cb_ = BindToCurrentLoop(closure); + if (state_ == kDecryptorRequested) { + DCHECK(!init_cb_.is_null()); + set_decryptor_ready_cb_.Run(DecryptorReadyCB()); + base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); + DoReset(); + return; + } + decryptor_->CancelDecrypt(GetDecryptorStreamType()); // Reset() cannot complete if the read callback is still pending. @@ -126,9 +133,7 @@ void DecryptingDemuxerStream::EnableBitstreamConverter() { } DecryptingDemuxerStream::~DecryptingDemuxerStream() { - DVLOG(2) << __FUNCTION__; - if (!set_decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&set_decryptor_ready_cb_).Run(DecryptorReadyCB()); + DVLOG(2) << __FUNCTION__ << " : state_ = " << state_; } void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { @@ -275,9 +280,15 @@ void DecryptingDemuxerStream::OnKeyAdded() { } void DecryptingDemuxerStream::DoReset() { + DCHECK(state_ != kUninitialized); DCHECK(init_cb_.is_null()); DCHECK(read_cb_.is_null()); - state_ = kIdle; + + if (state_ == kDecryptorRequested) + state_ = kUninitialized; + else + state_ = kIdle; + base::ResetAndReturn(&reset_cb_).Run(); } diff --git a/media/filters/decrypting_demuxer_stream.h b/media/filters/decrypting_demuxer_stream.h index bd75d66..cc34c04 100644 --- a/media/filters/decrypting_demuxer_stream.h +++ b/media/filters/decrypting_demuxer_stream.h @@ -35,6 +35,10 @@ class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream { void Initialize(DemuxerStream* stream, const PipelineStatusCB& status_cb); + + // Cancels all pending operations and fires all pending callbacks. Sets + // |this| to kUninitialized state if |this| hasn't been initialized, or to + // kIdle state otherwise. void Reset(const base::Closure& closure); // DemuxerStream implementation. diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc index e971aa6..585f3d0 100644 --- a/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/media/filters/decrypting_demuxer_stream_unittest.cc @@ -326,6 +326,19 @@ TEST_F(DecryptingDemuxerStreamTest, KeyAdded_DruingPendingDecrypt) { message_loop_.RunUntilIdle(); } +// Test resetting when the DecryptingDemuxerStream is in kDecryptorRequested +// state. +TEST_F(DecryptingDemuxerStreamTest, Reset_DuringDecryptorRequested) { + // One for decryptor request, one for canceling request during Reset(). + EXPECT_CALL(*this, RequestDecryptorNotification(_)) + .Times(2); + AudioDecoderConfig input_config( + kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, + NULL, 0, true); + InitializeAudioAndExpectStatus(input_config, PIPELINE_ERROR_ABORT); + Reset(); +} + // Test resetting when the DecryptingDemuxerStream is in kIdle state but has // not returned any buffer. TEST_F(DecryptingDemuxerStreamTest, Reset_DuringIdleAfterInitialization) { diff --git a/media/filters/video_decoder_selector.cc b/media/filters/video_decoder_selector.cc index f75a95d..e961a31 100644 --- a/media/filters/video_decoder_selector.cc +++ b/media/filters/video_decoder_selector.cc @@ -28,12 +28,15 @@ VideoDecoderSelector::VideoDecoderSelector( weak_ptr_factory_(this) { } -VideoDecoderSelector::~VideoDecoderSelector() {} +VideoDecoderSelector::~VideoDecoderSelector() { + DVLOG(2) << __FUNCTION__; + DCHECK(select_decoder_cb_.is_null()); +} void VideoDecoderSelector::SelectVideoDecoder( DemuxerStream* stream, const SelectDecoderCB& select_decoder_cb) { - DVLOG(2) << "SelectVideoDecoder()"; + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(stream); @@ -43,22 +46,20 @@ void VideoDecoderSelector::SelectVideoDecoder( const VideoDecoderConfig& config = stream->video_decoder_config(); if (!config.IsValidConfig()) { DLOG(ERROR) << "Invalid video stream config."; - base::ResetAndReturn(&select_decoder_cb_).Run( - scoped_ptr<VideoDecoder>(), scoped_ptr<DecryptingDemuxerStream>()); + ReturnNullDecoder(); return; } input_stream_ = stream; if (!config.is_encrypted()) { - InitializeDecoder(decoders_.begin()); + InitializeDecoder(); return; } // This could happen if Encrypted Media Extension (EME) is not enabled. if (set_decryptor_ready_cb_.is_null()) { - base::ResetAndReturn(&select_decoder_cb_).Run( - scoped_ptr<VideoDecoder>(), scoped_ptr<DecryptingDemuxerStream>()); + ReturnNullDecoder(); return; } @@ -67,13 +68,46 @@ void VideoDecoderSelector::SelectVideoDecoder( video_decoder_->Initialize( input_stream_->video_decoder_config(), - BindToCurrentLoop(base::Bind( - &VideoDecoderSelector::DecryptingVideoDecoderInitDone, - weak_ptr_factory_.GetWeakPtr()))); + base::Bind(&VideoDecoderSelector::DecryptingVideoDecoderInitDone, + weak_ptr_factory_.GetWeakPtr())); +} + +void VideoDecoderSelector::Abort() { + DVLOG(2) << __FUNCTION__; + DCHECK(message_loop_->BelongsToCurrentThread()); + + // This could happen when SelectVideoDecoder() was not called or when + // |select_decoder_cb_| was already posted but not fired (e.g. in the + // message loop queue). + if (select_decoder_cb_.is_null()) + return; + + // We must be trying to initialize the |video_decoder_| or the + // |decrypted_stream_|. Invalid all weak pointers so that all initialization + // callbacks won't fire. + weak_ptr_factory_.InvalidateWeakPtrs(); + + if (video_decoder_) { + // |decrypted_stream_| is either NULL or already initialized. We don't + // need to Reset() |decrypted_stream_| in either case. + video_decoder_->Stop(base::Bind(&VideoDecoderSelector::ReturnNullDecoder, + weak_ptr_factory_.GetWeakPtr())); + return; + } + + if (decrypted_stream_) { + decrypted_stream_->Reset( + base::Bind(&VideoDecoderSelector::ReturnNullDecoder, + weak_ptr_factory_.GetWeakPtr())); + return; + } + + NOTREACHED(); } void VideoDecoderSelector::DecryptingVideoDecoderInitDone( PipelineStatus status) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); if (status == PIPELINE_OK) { @@ -82,63 +116,69 @@ void VideoDecoderSelector::DecryptingVideoDecoderInitDone( return; } + video_decoder_.reset(); + decrypted_stream_.reset(new DecryptingDemuxerStream( message_loop_, set_decryptor_ready_cb_)); decrypted_stream_->Initialize( input_stream_, - BindToCurrentLoop(base::Bind( - &VideoDecoderSelector::DecryptingDemuxerStreamInitDone, - weak_ptr_factory_.GetWeakPtr()))); + base::Bind(&VideoDecoderSelector::DecryptingDemuxerStreamInitDone, + weak_ptr_factory_.GetWeakPtr())); } void VideoDecoderSelector::DecryptingDemuxerStreamInitDone( PipelineStatus status) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); if (status != PIPELINE_OK) { - decrypted_stream_.reset(); - base::ResetAndReturn(&select_decoder_cb_).Run( - scoped_ptr<VideoDecoder>(), scoped_ptr<DecryptingDemuxerStream>()); + ReturnNullDecoder(); return; } DCHECK(!decrypted_stream_->video_decoder_config().is_encrypted()); input_stream_ = decrypted_stream_.get(); - InitializeDecoder(decoders_.begin()); + InitializeDecoder(); } -void VideoDecoderSelector::InitializeDecoder( - ScopedVector<VideoDecoder>::iterator iter) { +void VideoDecoderSelector::InitializeDecoder() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(!video_decoder_); - if (iter == decoders_.end()) { - base::ResetAndReturn(&select_decoder_cb_).Run( - scoped_ptr<VideoDecoder>(), scoped_ptr<DecryptingDemuxerStream>()); + if (decoders_.empty()) { + ReturnNullDecoder(); return; } - (*iter)->Initialize( - input_stream_->video_decoder_config(), - BindToCurrentLoop(base::Bind(&VideoDecoderSelector::DecoderInitDone, - weak_ptr_factory_.GetWeakPtr(), - iter))); + video_decoder_.reset(decoders_.front()); + decoders_.weak_erase(decoders_.begin()); + + video_decoder_->Initialize(input_stream_->video_decoder_config(), + base::Bind(&VideoDecoderSelector::DecoderInitDone, + weak_ptr_factory_.GetWeakPtr())); } -void VideoDecoderSelector::DecoderInitDone( - ScopedVector<VideoDecoder>::iterator iter, PipelineStatus status) { +void VideoDecoderSelector::DecoderInitDone(PipelineStatus status) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); if (status != PIPELINE_OK) { - InitializeDecoder(++iter); + video_decoder_.reset(); + InitializeDecoder(); return; } - scoped_ptr<VideoDecoder> video_decoder(*iter); - decoders_.weak_erase(iter); - - base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder.Pass(), + base::ResetAndReturn(&select_decoder_cb_).Run(video_decoder_.Pass(), decrypted_stream_.Pass()); } +void VideoDecoderSelector::ReturnNullDecoder() { + DVLOG(2) << __FUNCTION__; + DCHECK(message_loop_->BelongsToCurrentThread()); + base::ResetAndReturn(&select_decoder_cb_).Run( + scoped_ptr<VideoDecoder>(), scoped_ptr<DecryptingDemuxerStream>()); +} + } // namespace media diff --git a/media/filters/video_decoder_selector.h b/media/filters/video_decoder_selector.h index 105b513..90e0dd5 100644 --- a/media/filters/video_decoder_selector.h +++ b/media/filters/video_decoder_selector.h @@ -57,12 +57,16 @@ class MEDIA_EXPORT VideoDecoderSelector { void SelectVideoDecoder(DemuxerStream* stream, const SelectDecoderCB& select_decoder_cb); + // Aborts pending VideoDecoder selection and fires |select_decoder_cb| with + // NULL and NULL immediately if it's pending. + void Abort(); + private: void DecryptingVideoDecoderInitDone(PipelineStatus status); void DecryptingDemuxerStreamInitDone(PipelineStatus status); - void InitializeDecoder(ScopedVector<VideoDecoder>::iterator iter); - void DecoderInitDone(ScopedVector<VideoDecoder>::iterator iter, - PipelineStatus status); + void InitializeDecoder(); + void DecoderInitDone(PipelineStatus status); + void ReturnNullDecoder(); scoped_refptr<base::MessageLoopProxy> message_loop_; ScopedVector<VideoDecoder> decoders_; diff --git a/media/filters/video_decoder_selector_unittest.cc b/media/filters/video_decoder_selector_unittest.cc index f42c583..ddb53bc 100644 --- a/media/filters/video_decoder_selector_unittest.cc +++ b/media/filters/video_decoder_selector_unittest.cc @@ -26,6 +26,10 @@ class VideoDecoderSelectorTest : public ::testing::Test { public: enum DecryptorCapability { kNoDecryptor, + // Used to test Abort() during DecryptingVideoDecoder::Initialize() and + // DecryptingDemuxerStream::Initialize(). We don't need this for normal + // VideoDecoders since we use MockVideoDecoder. + kHoldSetDecryptor, kDecryptOnly, kDecryptAndDecode }; @@ -76,12 +80,14 @@ class VideoDecoderSelectorTest : public ::testing::Test { void InitializeDecoderSelector(DecryptorCapability decryptor_capability, int num_decoders) { SetDecryptorReadyCB set_decryptor_ready_cb; + if (decryptor_capability != kNoDecryptor) { + set_decryptor_ready_cb = + base::Bind(&VideoDecoderSelectorTest::SetDecryptorReadyCallback, + base::Unretained(this)); + } + if (decryptor_capability == kDecryptOnly || decryptor_capability == kDecryptAndDecode) { - set_decryptor_ready_cb = base::Bind( - &VideoDecoderSelectorTest::SetDecryptorReadyCallback, - base::Unretained(this)); - EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) .WillRepeatedly(RunCallback<0>(decryptor_.get())); @@ -92,6 +98,10 @@ class VideoDecoderSelectorTest : public ::testing::Test { EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) .WillRepeatedly(RunCallback<1>(true)); } + } else if (decryptor_capability == kHoldSetDecryptor) { + // Set and cancel DecryptorReadyCB but the callback is never fired. + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) + .Times(2); } DCHECK_GE(all_decoders_.size(), static_cast<size_t>(num_decoders)); @@ -112,6 +122,14 @@ class VideoDecoderSelectorTest : public ::testing::Test { message_loop_.RunUntilIdle(); } + void SelectDecoderAndAbort() { + SelectDecoder(); + + EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); + decoder_selector_->Abort(); + message_loop_.RunUntilIdle(); + } + // Fixture members. scoped_ptr<VideoDecoderSelector> decoder_selector_; scoped_ptr<StrictMock<MockDemuxerStream> > demuxer_stream_; @@ -154,6 +172,16 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, + Abort_ClearStream_NoDecryptor_OneClearDecoder) { + UseClearStream(); + InitializeDecoderSelector(kNoDecryptor, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _)); + + SelectDecoderAndAbort(); +} + // The stream is not encrypted and we have multiple clear decoders. The first // decoder that can decode the input stream will be selected. TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) { @@ -169,6 +197,18 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, + Abort_ClearStream_NoDecryptor_MultipleClearDecoder) { + UseClearStream(); + InitializeDecoderSelector(kNoDecryptor, 2); + + EXPECT_CALL(*decoder_1_, Initialize(_, _)) + .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); + EXPECT_CALL(*decoder_2_, Initialize(_, _)); + + SelectDecoderAndAbort(); +} + // There is a decryptor but the stream is not encrypted. The decoder will be // selected. TEST_F(VideoDecoderSelectorTest, ClearStream_HasDecryptor) { @@ -182,6 +222,15 @@ TEST_F(VideoDecoderSelectorTest, ClearStream_HasDecryptor) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, Abort_ClearStream_HasDecryptor) { + UseClearStream(); + InitializeDecoderSelector(kDecryptOnly, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _)); + + SelectDecoderAndAbort(); +} + // The stream is encrypted and there's no decryptor. No decoder can be selected. TEST_F(VideoDecoderSelectorTest, EncryptedStream_NoDecryptor) { UseEncryptedStream(); @@ -203,6 +252,14 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptOnly_NoClearDecoder) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, + Abort_EncryptedStream_DecryptOnly_NoClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kHoldSetDecryptor, 0); + + SelectDecoderAndAbort(); +} + // Decryptor can do decryption-only and there's a decoder available. The decoder // will be selected and a DecryptingDemuxerStream will be created. TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) { @@ -216,6 +273,16 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, + Abort_EncryptedStream_DecryptOnly_OneClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptOnly, 1); + + EXPECT_CALL(*decoder_1_, Initialize(_, _)); + + SelectDecoderAndAbort(); +} + // Decryptor can only do decryption and there are multiple decoders available. // The first decoder that can decode the input stream will be selected and // a DecryptingDemuxerStream will be created. @@ -233,6 +300,18 @@ TEST_F(VideoDecoderSelectorTest, SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, + Abort_EncryptedStream_DecryptOnly_MultipleClearDecoder) { + UseEncryptedStream(); + InitializeDecoderSelector(kDecryptOnly, 2); + + EXPECT_CALL(*decoder_1_, Initialize(_, _)) + .WillOnce(RunCallback<1>(DECODER_ERROR_NOT_SUPPORTED)); + EXPECT_CALL(*decoder_2_, Initialize(_, _)); + + SelectDecoderAndAbort(); +} + // Decryptor can do decryption and decoding. A DecryptingVideoDecoder will be // created and selected. The clear decoders should not be touched at all. // No DecryptingDemuxerStream should to be created. @@ -245,4 +324,11 @@ TEST_F(VideoDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { SelectDecoder(); } +TEST_F(VideoDecoderSelectorTest, Abort_EncryptedStream_DecryptAndDecode) { + UseEncryptedStream(); + InitializeDecoderSelector(kHoldSetDecryptor, 1); + + SelectDecoderAndAbort(); +} + } // namespace media diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc index 73b136c..80e5937 100644 --- a/media/filters/video_frame_stream.cc +++ b/media/filters/video_frame_stream.cc @@ -28,7 +28,8 @@ VideoFrameStream::VideoFrameStream( stream_(NULL), decoder_selector_(new VideoDecoderSelector(message_loop, decoders.Pass(), - set_decryptor_ready_cb)) {} + set_decryptor_ready_cb)) { +} VideoFrameStream::~VideoFrameStream() { DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_STOPPED) << state_; @@ -37,6 +38,7 @@ VideoFrameStream::~VideoFrameStream() { void VideoFrameStream::Initialize(DemuxerStream* stream, const StatisticsCB& statistics_cb, const InitCB& init_cb) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_; DCHECK(init_cb_.is_null()); @@ -55,6 +57,7 @@ void VideoFrameStream::Initialize(DemuxerStream* stream, } void VideoFrameStream::Read(const ReadCB& read_cb) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || state_ == STATE_ERROR) << state_; @@ -81,6 +84,7 @@ void VideoFrameStream::Read(const ReadCB& read_cb) { } void VideoFrameStream::Reset(const base::Closure& closure) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; DCHECK(reset_cb_.is_null()); @@ -113,16 +117,20 @@ void VideoFrameStream::Reset(const base::Closure& closure) { } void VideoFrameStream::Stop(const base::Closure& closure) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_NE(state_, STATE_STOPPED) << state_; DCHECK(stop_cb_.is_null()); stop_cb_ = closure; + if (state_ == STATE_INITIALIZING) { + decoder_selector_->Abort(); + return; + } + // The stopping process will continue after the pending operation is finished. - // TODO(xhwang): Now we cannot stop the initialization process through - // VideoDecoderSelector. Fix this. See: http://crbug.com/222054 - if (state_ == STATE_INITIALIZING || state_ == STATE_PENDING_DEMUXER_READ) + if (state_ == STATE_PENDING_DEMUXER_READ) return; // VideoDecoder API guarantees that if VideoDecoder::Stop() is called during @@ -157,6 +165,7 @@ bool VideoFrameStream::CanReadWithoutStalling() const { void VideoFrameStream::OnDecoderSelected( scoped_ptr<VideoDecoder> selected_decoder, scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_INITIALIZING) << state_; DCHECK(!init_cb_.is_null()); @@ -199,6 +208,7 @@ void VideoFrameStream::AbortRead() { } void VideoFrameStream::Decode(const scoped_refptr<DecoderBuffer>& buffer) { + DVLOG(2) << __FUNCTION__; DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; DCHECK(!read_cb_.is_null()); DCHECK(reset_cb_.is_null()); @@ -217,6 +227,7 @@ void VideoFrameStream::FlushDecoder() { void VideoFrameStream::OnFrameReady(int buffer_size, const VideoDecoder::Status status, const scoped_refptr<VideoFrame>& frame) { + DVLOG(2) << __FUNCTION__; DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; DCHECK(!read_cb_.is_null()); @@ -268,6 +279,7 @@ void VideoFrameStream::OnFrameReady(int buffer_size, } void VideoFrameStream::ReadFromDemuxerStream() { + DVLOG(2) << __FUNCTION__; DCHECK_EQ(state_, STATE_NORMAL) << state_; DCHECK(!read_cb_.is_null()); DCHECK(reset_cb_.is_null()); @@ -280,6 +292,7 @@ void VideoFrameStream::ReadFromDemuxerStream() { void VideoFrameStream::OnBufferReady( DemuxerStream::Status status, const scoped_refptr<DecoderBuffer>& buffer) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_PENDING_DEMUXER_READ) << state_; DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status; @@ -326,6 +339,7 @@ void VideoFrameStream::OnBufferReady( } void VideoFrameStream::ReinitializeDecoder() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_; @@ -337,6 +351,7 @@ void VideoFrameStream::ReinitializeDecoder() { } void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER) << state_; @@ -373,6 +388,7 @@ void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) { } void VideoFrameStream::ResetDecoder() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || state_ == STATE_ERROR) << state_; @@ -382,6 +398,7 @@ void VideoFrameStream::ResetDecoder() { } void VideoFrameStream::OnDecoderReset() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || state_ == STATE_ERROR) << state_; @@ -400,6 +417,7 @@ void VideoFrameStream::OnDecoderReset() { } void VideoFrameStream::StopDecoder() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; DCHECK(!stop_cb_.is_null()); @@ -408,6 +426,7 @@ void VideoFrameStream::StopDecoder() { } void VideoFrameStream::OnDecoderStopped() { + DVLOG(2) << __FUNCTION__; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; // If Stop() was called during pending read/reset, read/reset callback should diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc index 524f6aa..e575105 100644 --- a/media/filters/video_frame_stream_unittest.cc +++ b/media/filters/video_frame_stream_unittest.cc @@ -36,6 +36,7 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { decoder_(new FakeVideoDecoder(kDecodingDelay)), is_initialized_(false), num_decoded_frames_(0), + pending_initialize_(false), pending_read_(false), pending_reset_(false), pending_stop_(false), @@ -49,9 +50,6 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback, base::Unretained(this)))); - EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) - .WillRepeatedly(RunCallback<0>(decryptor_.get())); - // Decryptor can only decrypt (not decrypt-and-decode) so that // DecryptingDemuxerStream will be used. EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) @@ -61,26 +59,43 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { } ~VideoFrameStreamTest() { + DCHECK(!pending_initialize_); DCHECK(!pending_read_); DCHECK(!pending_reset_); DCHECK(!pending_stop_); - // Check that the pipeline statistics callback was fired correctly. - if (decoder_) - EXPECT_EQ(decoder_->total_bytes_decoded(), total_bytes_decoded_); - if (is_initialized_) Stop(); EXPECT_FALSE(is_initialized_); } MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); - MOCK_METHOD2(OnInitialized, void(bool, bool)); 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, @@ -120,6 +135,7 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { DCHECK(pending_stop_); pending_stop_ = false; is_initialized_ = false; + decoder_ = NULL; } void ReadUntilPending() { @@ -136,6 +152,7 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { NOT_PENDING, DEMUXER_READ_NORMAL, DEMUXER_READ_CONFIG_CHANGE, + SET_DECRYPTOR, DECODER_INIT, DECODER_REINIT, DECODER_READ, @@ -156,15 +173,19 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { ReadUntilPending(); break; + case SET_DECRYPTOR: + // Hold DecryptorReadyCB. + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) + .Times(2); + // Initialize will fail because no decryptor is available. + InitializeVideoFrameStream(); + break; + case DECODER_INIT: + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) + .WillRepeatedly(RunCallback<0>(decryptor_.get())); decoder_->HoldNextInit(); - video_frame_stream_->Initialize( - demuxer_stream_.get(), - base::Bind(&VideoFrameStreamTest::OnStatistics, - base::Unretained(this)), - base::Bind(&VideoFrameStreamTest::OnInitialized, - base::Unretained(this))); - message_loop_.RunUntilIdle(); + InitializeVideoFrameStream(); break; case DECODER_REINIT: @@ -187,6 +208,8 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { 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))); @@ -207,9 +230,13 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { demuxer_stream_->SatisfyRead(); break; + case SET_DECRYPTOR: + // VideoFrameStream::Stop() does not wait for pending DecryptorReadyCB. + // Therefore there's no need to satisfy a callback. + NOTREACHED(); + break; + case DECODER_INIT: - EXPECT_CALL(*this, OnInitialized(true, false)) - .WillOnce(SaveArg<0>(&is_initialized_)); decoder_->SatisfyInit(); break; @@ -238,8 +265,6 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { } message_loop_.RunUntilIdle(); - if (!is_initialized_) - decoder_ = NULL; } void Initialize() { @@ -273,6 +298,7 @@ class VideoFrameStreamTest : public testing::TestWithParam<bool> { bool is_initialized_; int num_decoded_frames_; + bool pending_initialize_; bool pending_read_; bool pending_reset_; bool pending_stop_; @@ -389,6 +415,19 @@ TEST_P(VideoFrameStreamTest, Stop_BeforeInitialization) { 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); |