summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-23 21:42:55 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-23 21:42:55 +0000
commitc7ecea8ae559e894509c18fa27018a98836974db (patch)
tree600fb011a89ad93f4969db2cf22d749f8e2a8aa7
parentf040795567a5df300bfc522e5f6db2051c4cee27 (diff)
downloadchromium_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.cc23
-rw-r--r--media/filters/decrypting_demuxer_stream.h4
-rw-r--r--media/filters/decrypting_demuxer_stream_unittest.cc13
-rw-r--r--media/filters/video_decoder_selector.cc108
-rw-r--r--media/filters/video_decoder_selector.h10
-rw-r--r--media/filters/video_decoder_selector_unittest.cc94
-rw-r--r--media/filters/video_frame_stream.cc27
-rw-r--r--media/filters/video_frame_stream_unittest.cc77
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);