diff options
-rw-r--r-- | media/filters/audio_decoder_selector.cc | 4 | ||||
-rw-r--r-- | media/filters/decrypting_demuxer_stream.cc | 35 | ||||
-rw-r--r-- | media/filters/decrypting_demuxer_stream.h | 15 | ||||
-rw-r--r-- | media/filters/decrypting_demuxer_stream_unittest.cc | 102 | ||||
-rw-r--r-- | media/filters/video_decoder_selector.cc | 4 | ||||
-rw-r--r-- | media/filters/video_frame_stream.cc | 2 |
6 files changed, 143 insertions, 19 deletions
diff --git a/media/filters/audio_decoder_selector.cc b/media/filters/audio_decoder_selector.cc index cc5e518..a08d3c7 100644 --- a/media/filters/audio_decoder_selector.cc +++ b/media/filters/audio_decoder_selector.cc @@ -91,14 +91,14 @@ void AudioDecoderSelector::Abort() { if (audio_decoder_) { // AudioDecoder doesn't provide a Stop() method. Also, |decrypted_stream_| - // is either NULL or already initialized. We don't need to Reset() + // is either NULL or already initialized. We don't need to Stop() // |decrypted_stream_| in either case. ReturnNullDecoder(); return; } if (decrypted_stream_) { - decrypted_stream_->Reset( + decrypted_stream_->Stop( base::Bind(&AudioDecoderSelector::ReturnNullDecoder, weak_ptr_factory_.GetWeakPtr())); return; diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc index 98eba5f..a26498cd 100644 --- a/media/filters/decrypting_demuxer_stream.cc +++ b/media/filters/decrypting_demuxer_stream.cc @@ -78,10 +78,13 @@ void DecryptingDemuxerStream::Reset(const base::Closure& closure) { DVLOG(2) << __FUNCTION__ << " - state: " << state_; DCHECK(message_loop_->BelongsToCurrentThread()); DCHECK(state_ != kUninitialized) << state_; + DCHECK(state_ != kStopped) << state_; DCHECK(reset_cb_.is_null()); reset_cb_ = BindToCurrentLoop(closure); + // TODO(xhwang): This should not happen. Remove it, DCHECK against the + // condition and clean up related tests. if (state_ == kDecryptorRequested) { DCHECK(!init_cb_.is_null()); set_decryptor_ready_cb_.Run(DecryptorReadyCB()); @@ -111,6 +114,38 @@ void DecryptingDemuxerStream::Reset(const base::Closure& closure) { DoReset(); } +void DecryptingDemuxerStream::Stop(const base::Closure& closure) { + DVLOG(2) << __FUNCTION__ << " - state: " << state_; + DCHECK(message_loop_->BelongsToCurrentThread()); + DCHECK(state_ != kUninitialized) << state_; + + // Invalidate all weak pointers so that pending callbacks won't fire. + weak_factory_.InvalidateWeakPtrs(); + + // At this point the render thread is likely paused (in WebMediaPlayerImpl's + // Destroy()), so running |closure| can't wait for anything that requires the + // render thread to process messages to complete (such as PPAPI methods). + if (decryptor_) { + // Clear the callback. + decryptor_->RegisterNewKeyCB(GetDecryptorStreamType(), + Decryptor::NewKeyCB()); + decryptor_->CancelDecrypt(GetDecryptorStreamType()); + decryptor_ = NULL; + } + if (!set_decryptor_ready_cb_.is_null()) + base::ResetAndReturn(&set_decryptor_ready_cb_).Run(DecryptorReadyCB()); + if (!init_cb_.is_null()) + base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); + if (!read_cb_.is_null()) + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); + if (!reset_cb_.is_null()) + base::ResetAndReturn(&reset_cb_).Run(); + pending_buffer_to_decrypt_ = NULL; + + state_ = kStopped; + BindToCurrentLoop(closure).Run(); +} + AudioDecoderConfig DecryptingDemuxerStream::audio_decoder_config() { DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_; CHECK_EQ(demuxer_stream_->type(), AUDIO); diff --git a/media/filters/decrypting_demuxer_stream.h b/media/filters/decrypting_demuxer_stream.h index cc34c04..394cb5b 100644 --- a/media/filters/decrypting_demuxer_stream.h +++ b/media/filters/decrypting_demuxer_stream.h @@ -36,11 +36,19 @@ 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. + // Cancels all pending operations and fires all pending callbacks. If in + // kPendingDemuxerRead or kPendingDecrypt state, waits for the pending + // operation to finish before satisfying |closure|. Sets the state to + // kUninitialized if |this| hasn't been initialized, or to kIdle otherwise. void Reset(const base::Closure& closure); + // Cancels all pending operations immediately and fires all pending callbacks + // and sets the state to kStopped. Does NOT wait for any pending operations. + // Note: During the teardown process, media pipeline will be waiting on the + // render main thread. If a Decryptor depends on the render main thread + // (e.g. PpapiDecryptor), the pending DecryptCB would not be satisfied. + void Stop(const base::Closure& closure); + // DemuxerStream implementation. virtual void Read(const ReadCB& read_cb) OVERRIDE; virtual AudioDecoderConfig audio_decoder_config() OVERRIDE; @@ -60,6 +68,7 @@ class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream { kPendingDemuxerRead, kPendingDecrypt, kWaitingForKey, + kStopped }; // Callback for DecryptorHost::RequestDecryptor(). diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc index 031ebe1..3e41734 100644 --- a/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/media/filters/decrypting_demuxer_stream_unittest.cc @@ -50,9 +50,11 @@ ACTION_P(ReturnBuffer, buffer) { arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); } -ACTION_P(RunCallbackIfNotNull, param) { +ACTION_P2(SetDecryptorIfNotNull, decryptor, is_decryptor_set) { if (!arg0.is_null()) - arg0.Run(param); + arg0.Run(decryptor); + + *is_decryptor_set = !arg0.is_null() && decryptor; } ACTION_P2(ResetAndRunCallback, callback, param) { @@ -74,6 +76,7 @@ class DecryptingDemuxerStreamTest : public testing::Test { &DecryptingDemuxerStreamTest::RequestDecryptorNotification, base::Unretained(this)))), decryptor_(new StrictMock<MockDecryptor>()), + is_decryptor_set_(false), input_audio_stream_( new StrictMock<MockDemuxerStream>(DemuxerStream::AUDIO)), input_video_stream_( @@ -106,9 +109,9 @@ class DecryptingDemuxerStreamTest : public testing::Test { void Initialize() { EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(RunCallbackIfNotNull(decryptor_.get())); + .WillOnce(SetDecryptorIfNotNull(decryptor_.get(), &is_decryptor_set_)); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); + .WillRepeatedly(SaveArg<1>(&key_added_cb_)); AudioDecoderConfig input_config( kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, @@ -221,14 +224,25 @@ class DecryptingDemuxerStreamTest : public testing::Test { } void Reset() { - EXPECT_CALL(*decryptor_, CancelDecrypt(Decryptor::kAudio)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingDemuxerStreamTest::AbortPendingDecryptCB)); + if (is_decryptor_set_) { + EXPECT_CALL(*decryptor_, CancelDecrypt(Decryptor::kAudio)) + .WillRepeatedly(InvokeWithoutArgs( + this, &DecryptingDemuxerStreamTest::AbortPendingDecryptCB)); + } demuxer_stream_->Reset(NewExpectedClosure()); message_loop_.RunUntilIdle(); } + // Stops the |demuxer_stream_| without satisfying/aborting any pending + // operations. + void Stop() { + if (is_decryptor_set_) + EXPECT_CALL(*decryptor_, CancelDecrypt(Decryptor::kAudio)); + demuxer_stream_->Stop(NewExpectedClosure()); + message_loop_.RunUntilIdle(); + } + MOCK_METHOD1(RequestDecryptorNotification, void(const DecryptorReadyCB&)); MOCK_METHOD2(BufferReady, void(DemuxerStream::Status, @@ -237,6 +251,8 @@ class DecryptingDemuxerStreamTest : public testing::Test { base::MessageLoop message_loop_; scoped_ptr<DecryptingDemuxerStream> demuxer_stream_; scoped_ptr<StrictMock<MockDecryptor> > decryptor_; + // Whether a valid Decryptor is set to the |demuxer_stream_|. + bool is_decryptor_set_; scoped_ptr<StrictMock<MockDemuxerStream> > input_audio_stream_; scoped_ptr<StrictMock<MockDemuxerStream> > input_video_stream_; @@ -260,9 +276,9 @@ TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalAudio) { TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(RunCallbackIfNotNull(decryptor_.get())); + .WillOnce(SetDecryptorIfNotNull(decryptor_.get(), &is_decryptor_set_)); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); + .WillOnce(SaveArg<1>(&key_added_cb_)); VideoDecoderConfig input_config = TestVideoConfig::NormalEncrypted(); InitializeVideoAndExpectStatus(input_config, PIPELINE_OK); @@ -287,7 +303,8 @@ TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { TEST_F(DecryptingDemuxerStreamTest, Initialize_NullDecryptor) { EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillRepeatedly(RunCallbackIfNotNull(static_cast<Decryptor*>(NULL))); + .WillRepeatedly(SetDecryptorIfNotNull(static_cast<Decryptor*>(NULL), + &is_decryptor_set_)); AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true); @@ -467,7 +484,7 @@ TEST_F(DecryptingDemuxerStreamTest, DemuxerRead_ConfigChanged) { ReadAndExpectBufferReadyWith(DemuxerStream::kConfigChanged, NULL); } -// Test resetting when DecryptingDemuxerStream is waiting for an config changed +// Test resetting when DecryptingDemuxerStream is waiting for a config changed // read. TEST_F(DecryptingDemuxerStreamTest, Reset_DuringConfigChangedDemuxerRead) { Initialize(); @@ -481,4 +498,67 @@ TEST_F(DecryptingDemuxerStreamTest, Reset_DuringConfigChangedDemuxerRead) { message_loop_.RunUntilIdle(); } +// Test stopping when the DecryptingDemuxerStream is in kDecryptorRequested +// state. +TEST_F(DecryptingDemuxerStreamTest, Stop_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); + Stop(); +} + +// Test stopping when the DecryptingDemuxerStream is in kIdle state but has +// not returned any buffer. +TEST_F(DecryptingDemuxerStreamTest, Stop_DuringIdleAfterInitialization) { + Initialize(); + Stop(); +} + +// Test stopping when the DecryptingDemuxerStream is in kIdle state after it +// has returned one buffer. +TEST_F(DecryptingDemuxerStreamTest, Stop_DuringIdleAfterReadOneBuffer) { + Initialize(); + EnterNormalReadingState(); + Stop(); +} + +// Test stopping when DecryptingDemuxerStream is in kPendingDemuxerRead state. +TEST_F(DecryptingDemuxerStreamTest, Stop_DuringPendingDemuxerRead) { + Initialize(); + EnterPendingReadState(); + + EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); + Stop(); +} + +// Test stopping when the DecryptingDemuxerStream is in kPendingDecrypt state. +TEST_F(DecryptingDemuxerStreamTest, Stop_DuringPendingDecrypt) { + Initialize(); + EnterPendingDecryptState(); + + EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); + Stop(); +} + +// Test stopping when the DecryptingDemuxerStream is in kWaitingForKey state. +TEST_F(DecryptingDemuxerStreamTest, Stop_DuringWaitingForKey) { + Initialize(); + EnterWaitingForKeyState(); + + EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); + Stop(); +} + +// Test stopping after the DecryptingDemuxerStream has been reset. +TEST_F(DecryptingDemuxerStreamTest, Stop_AfterReset) { + Initialize(); + EnterNormalReadingState(); + Reset(); + Stop(); +} + } // namespace media diff --git a/media/filters/video_decoder_selector.cc b/media/filters/video_decoder_selector.cc index e961a31..9e646a7 100644 --- a/media/filters/video_decoder_selector.cc +++ b/media/filters/video_decoder_selector.cc @@ -89,14 +89,14 @@ void VideoDecoderSelector::Abort() { if (video_decoder_) { // |decrypted_stream_| is either NULL or already initialized. We don't - // need to Reset() |decrypted_stream_| in either case. + // need to Stop() |decrypted_stream_| in either case. video_decoder_->Stop(base::Bind(&VideoDecoderSelector::ReturnNullDecoder, weak_ptr_factory_.GetWeakPtr())); return; } if (decrypted_stream_) { - decrypted_stream_->Reset( + decrypted_stream_->Stop( base::Bind(&VideoDecoderSelector::ReturnNullDecoder, weak_ptr_factory_.GetWeakPtr())); return; diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc index 4b13ac9..b18ceda 100644 --- a/media/filters/video_frame_stream.cc +++ b/media/filters/video_frame_stream.cc @@ -145,7 +145,7 @@ void VideoFrameStream::Stop(const base::Closure& closure) { message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_)); if (decrypting_demuxer_stream_) { - decrypting_demuxer_stream_->Reset(base::Bind( + decrypting_demuxer_stream_->Stop(base::Bind( &VideoFrameStream::StopDecoder, weak_factory_.GetWeakPtr())); return; } |