Index: media/filters/video_frame_stream.cc |
diff --git a/media/filters/video_frame_stream.cc b/media/filters/video_frame_stream.cc |
index 28aaac6d4fd56d2e8d5c892c11f58df078c13c48..a302827363e6f99c4fffb7b916b52d87ee0f49a0 100644 |
--- a/media/filters/video_frame_stream.cc |
+++ b/media/filters/video_frame_stream.cc |
@@ -49,7 +49,8 @@ void VideoFrameStream::Initialize(DemuxerStream* stream, |
stream_ = stream; |
state_ = STATE_INITIALIZING; |
- decoder_selector_->SelectVideoDecoder(this, statistics_cb, base::Bind( |
+ // TODO(xhwang): VideoDecoderSelector only needs a config to select a decoder. |
+ decoder_selector_->SelectVideoDecoder(stream, statistics_cb, base::Bind( |
&VideoFrameStream::OnDecoderSelected, weak_this_)); |
} |
@@ -63,24 +64,22 @@ void VideoFrameStream::ReadFrame(const VideoDecoder::ReadCB& read_cb) { |
DCHECK(reset_cb_.is_null()); |
DCHECK(stop_cb_.is_null()); |
+ read_cb_ = read_cb; |
+ |
if (state_ == STATE_ERROR) { |
message_loop_->PostTask(FROM_HERE, base::Bind( |
read_cb, VideoDecoder::kDecodeError, scoped_refptr<VideoFrame>())); |
return; |
} |
- read_cb_ = read_cb; |
- decoder_->Read(base::Bind(&VideoFrameStream::OnFrameReady, weak_this_)); |
+ if (state_ == STATE_FLUSHING_DECODER) { |
+ FlushDecoder(); |
+ return; |
+ } |
+ |
+ ReadFromDemuxerStream(); |
} |
-// VideoDecoder API guarantees that if VideoDecoder::Reset() is called during |
-// a pending read, the read callback must be fired before the reset callback is |
-// fired. Therefore, we can call VideoDecoder::Reset() regardless of if we have |
-// a pending read and always satisfy the reset callback when the decoder reset |
-// is finished. The only exception is when Reset() is called during decoder |
-// reinitialization. In this case we cannot and don't need to reset the decoder. |
-// We should just wait for the reinitialization to finish to satisfy the reset |
-// callback. |
void VideoFrameStream::Reset(const base::Closure& closure) { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; |
@@ -89,14 +88,21 @@ void VideoFrameStream::Reset(const base::Closure& closure) { |
reset_cb_ = closure; |
- // VideoDecoder does not need to be and cannot be Reset() during |
- // reinitialization. |decrypting_demuxer_stream_| was reset before decoder |
+ // During decoder reinitialization, VideoDecoder does not need to be and |
+ // cannot be Reset(). |decrypting_demuxer_stream_| was reset before decoder |
// reinitialization. |
- if (state_ == STATE_REINITIALIZING_DECODER) |
+ // During pending demuxer read, VideoDecoder will be reset after demuxer read |
+ // is returned (in OnBufferReady()). |
+ if (state_ == STATE_REINITIALIZING_DECODER || |
+ state_ == STATE_PENDING_DEMUXER_READ) { |
return; |
+ } |
- // We may or may not have pending read, but we'll start to reset everything |
- // regardless. |
+ // VideoDecoder API guarantees that if VideoDecoder::Reset() is called during |
+ // a pending decode, the decode callback must be fired before the reset |
+ // callback is fired. Therefore, we can call VideoDecoder::Reset() regardless |
+ // of if we have a pending decode and always satisfy the reset callback when |
+ // the decoder reset is finished. |
if (decrypting_demuxer_stream_) { |
decrypting_demuxer_stream_->Reset(base::Bind( |
&VideoFrameStream::ResetDecoder, weak_this_)); |
@@ -113,22 +119,24 @@ void VideoFrameStream::Stop(const base::Closure& closure) { |
stop_cb_ = closure; |
- // The stopping will continue after all of the following pending callbacks |
- // (if they are not null) are satisfied. |
+ // 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) |
+ if (state_ == STATE_INITIALIZING || state_ == STATE_PENDING_DEMUXER_READ) |
return; |
- // We may or may not have pending read and/or pending reset, but we'll start |
- // to stop everything regardless. |
- |
+ // VideoDecoder API guarantees that if VideoDecoder::Stop() is called during |
+ // a pending reset or a pending decode, the callbacks are always fired in the |
+ // decode -> reset -> stop order. Therefore, we can call VideoDecoder::Stop() |
+ // regardless of if we have a pending decode or reset and always satisfy the |
+ // stop callback when the decoder decode/reset is finished. |
if (decrypting_demuxer_stream_) { |
decrypting_demuxer_stream_->Reset(base::Bind( |
&VideoFrameStream::StopDecoder, weak_this_)); |
return; |
} |
+ // We may not have a |decoder_| if Stop() was called during initialization. |
if (decoder_) { |
StopDecoder(); |
return; |
@@ -146,46 +154,15 @@ bool VideoFrameStream::CanReadWithoutStalling() const { |
return decoder_->CanReadWithoutStalling(); |
} |
-void VideoFrameStream::Read(const DemuxerStream::ReadCB& demuxer_read_cb) { |
- DCHECK(message_loop_->BelongsToCurrentThread()); |
- |
- if (state_ == STATE_FLUSHING_DECODER) { |
- message_loop_->PostTask(FROM_HERE, base::Bind( |
- demuxer_read_cb, DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer())); |
- return; |
- } |
- |
- stream_->Read(base::Bind( |
- &VideoFrameStream::OnBufferReady, weak_this_, demuxer_read_cb)); |
-} |
- |
-AudioDecoderConfig VideoFrameStream::audio_decoder_config() { |
- DCHECK(message_loop_->BelongsToCurrentThread()); |
- LOG(FATAL) << "Method audio_decoder_config() called on VideoFrameStream"; |
- return stream_->audio_decoder_config(); |
-} |
- |
-VideoDecoderConfig VideoFrameStream::video_decoder_config() { |
- DCHECK(message_loop_->BelongsToCurrentThread()); |
- return stream_->video_decoder_config(); |
-} |
- |
-DemuxerStream::Type VideoFrameStream::type() { |
- DCHECK(message_loop_->BelongsToCurrentThread()); |
- return VIDEO; |
-} |
- |
-void VideoFrameStream::EnableBitstreamConverter() { |
- DCHECK(message_loop_->BelongsToCurrentThread()); |
- NOTREACHED(); |
-} |
- |
void VideoFrameStream::OnDecoderSelected( |
scoped_ptr<VideoDecoder> selected_decoder, |
scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
DCHECK_EQ(state_, STATE_INITIALIZING) << state_; |
DCHECK(!init_cb_.is_null()); |
+ DCHECK(read_cb_.is_null()); |
+ DCHECK(reset_cb_.is_null()); |
+ |
decoder_selector_.reset(); |
if (!selected_decoder) { |
@@ -193,11 +170,12 @@ void VideoFrameStream::OnDecoderSelected( |
base::ResetAndReturn(&init_cb_).Run(false, false); |
} else { |
state_ = STATE_NORMAL; |
- decoder_ = selected_decoder.Pass(); |
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass(); |
- if (decoder_->NeedsBitstreamConversion()) { |
+ if (decrypting_demuxer_stream_) |
+ stream_ = decrypting_demuxer_stream_.get(); |
+ decoder_ = selected_decoder.Pass(); |
+ if (decoder_->NeedsBitstreamConversion()) |
stream_->EnableBitstreamConverter(); |
- } |
// TODO(xhwang): We assume |decoder_->HasAlpha()| does not change after |
// reinitialization. Check this condition. |
base::ResetAndReturn(&init_cb_).Run(true, decoder_->HasAlpha()); |
@@ -210,75 +188,136 @@ void VideoFrameStream::OnDecoderSelected( |
} |
} |
+void VideoFrameStream::SatisfyRead(VideoDecoder::Status status, |
+ const scoped_refptr<VideoFrame>& frame) { |
+ DCHECK(!read_cb_.is_null()); |
+ base::ResetAndReturn(&read_cb_).Run(status, frame); |
+} |
+ |
+void VideoFrameStream::AbortRead() { |
+ SatisfyRead(VideoDecoder::kOk, NULL); |
+} |
+ |
+void VideoFrameStream::Decode(const scoped_refptr<DecoderBuffer>& buffer) { |
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; |
+ DCHECK(!read_cb_.is_null()); |
+ DCHECK(reset_cb_.is_null()); |
+ DCHECK(stop_cb_.is_null()); |
+ DCHECK(buffer); |
+ |
+ decoder_->Decode(buffer, base::Bind(&VideoFrameStream::OnFrameReady, |
+ weak_this_)); |
+} |
+ |
+void VideoFrameStream::FlushDecoder() { |
+ Decode(DecoderBuffer::CreateEOSBuffer()); |
+} |
+ |
void VideoFrameStream::OnFrameReady(const VideoDecoder::Status status, |
const scoped_refptr<VideoFrame>& frame) { |
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; |
DCHECK(!read_cb_.is_null()); |
- if (status != VideoDecoder::kOk) { |
+ if (status == VideoDecoder::kDecodeError || |
+ status == VideoDecoder::kDecryptError) { |
DCHECK(!frame.get()); |
state_ = STATE_ERROR; |
- base::ResetAndReturn(&read_cb_).Run(status, NULL); |
+ SatisfyRead(status, NULL); |
return; |
} |
+ // Drop decoding result if Reset()/Stop() was called during decoding. |
// The stopping/resetting process will be handled when the decoder is |
// stopped/reset. |
if (!stop_cb_.is_null() || !reset_cb_.is_null()) { |
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL); |
+ AbortRead(); |
return; |
} |
- // Decoder flush finished. Reinitialize the video decoder. |
+ // Decoder flushed. Reinitialize the video decoder. |
if (state_ == STATE_FLUSHING_DECODER && |
status == VideoDecoder::kOk && frame->IsEndOfStream()) { |
ReinitializeDecoder(); |
return; |
} |
- base::ResetAndReturn(&read_cb_).Run(status, frame); |
+ if (status == VideoDecoder::kNotEnoughData) { |
+ if (state_ == STATE_NORMAL) |
+ ReadFromDemuxerStream(); |
+ else if (state_ == STATE_FLUSHING_DECODER) |
+ FlushDecoder(); |
+ return; |
+ } |
+ |
+ SatisfyRead(status, frame); |
+} |
+ |
+void VideoFrameStream::ReadFromDemuxerStream() { |
+ DCHECK_EQ(state_, STATE_NORMAL) << state_; |
+ DCHECK(!read_cb_.is_null()); |
+ DCHECK(reset_cb_.is_null()); |
+ DCHECK(stop_cb_.is_null()); |
+ |
+ state_ = STATE_PENDING_DEMUXER_READ; |
+ stream_->Read(base::Bind(&VideoFrameStream::OnBufferReady, weak_this_)); |
} |
void VideoFrameStream::OnBufferReady( |
- const DemuxerStream::ReadCB& demuxer_read_cb, |
DemuxerStream::Status status, |
const scoped_refptr<DecoderBuffer>& buffer) { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
- // VideoFrameStream reads from demuxer stream only when in NORMAL state. |
- DCHECK_EQ(state_, STATE_NORMAL) << state_; |
+ DCHECK_EQ(state_, STATE_PENDING_DEMUXER_READ) << state_; |
DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status; |
+ DCHECK(!read_cb_.is_null()); |
+ |
+ state_ = STATE_NORMAL; |
+ |
+ // Reset()/Stop() was postponed during STATE_PENDING_DEMUXER_READ state. |
+ // We need to handle them in this function. |
+ |
+ if (!stop_cb_.is_null()) { |
+ AbortRead(); |
+ if (!reset_cb_.is_null()) |
+ Reset(base::ResetAndReturn(&reset_cb_)); |
+ Stop(base::ResetAndReturn(&stop_cb_)); |
+ return; |
+ } |
if (status == DemuxerStream::kConfigChanged) { |
- DVLOG(2) << "OnBufferReady() - kConfigChanged"; |
state_ = STATE_FLUSHING_DECODER; |
- demuxer_read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
+ if (!reset_cb_.is_null()) { |
+ AbortRead(); |
+ Reset(base::ResetAndReturn(&reset_cb_)); |
+ // Reinitialization will continue after Reset() is done. |
+ } else { |
+ FlushDecoder(); |
+ } |
+ return; |
+ } |
+ |
+ if (!reset_cb_.is_null()) { |
+ AbortRead(); |
+ Reset(base::ResetAndReturn(&reset_cb_)); |
+ return; |
+ } |
+ |
+ if (status == DemuxerStream::kAborted) { |
+ AbortRead(); |
return; |
} |
- DCHECK(status == DemuxerStream::kOk || status == DemuxerStream::kAborted); |
- demuxer_read_cb.Run(status, buffer); |
+ DCHECK(status == DemuxerStream::kOk) << status; |
+ Decode(buffer); |
} |
void VideoFrameStream::ReinitializeDecoder() { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_; |
- DemuxerStream* stream = this; |
- if (decrypting_demuxer_stream_) { |
- // TODO(xhwang): Remove this hack! Since VideoFrameStream handles |
- // kConfigChange internally and hides it from downstream filters. The |
- // DecryptingDemuxerStream never receives kConfigChanged to reset it's |
- // internal VideoDecoderConfig. Call InitializeDecoderConfig() here |
- // explicitly to solve this. This will be removed when we separate the |
- // DemuxerStream from the VideoDecoder. |
- decrypting_demuxer_stream_->InitializeDecoderConfig(); |
- stream = decrypting_demuxer_stream_.get(); |
- } |
- |
- DCHECK(stream->video_decoder_config().IsValidConfig()); |
+ DCHECK(stream_->video_decoder_config().IsValidConfig()); |
state_ = STATE_REINITIALIZING_DECODER; |
decoder_->Initialize( |
- stream, |
+ stream_->video_decoder_config(), |
base::Bind(&VideoFrameStream::OnDecoderReinitialized, weak_this_), |
statistics_cb_); |
} |
@@ -287,16 +326,24 @@ void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER) << state_; |
+ // ReinitializeDecoder() can be called in two cases: |
+ // 1, Flushing decoder finished (see OnFrameReady()). |
+ // 2, Reset() was called during flushing decoder (see OnDecoderReset()). |
+ // Also, Reset()/Stop() can be called during pending ReinitializeDecoder(). |
+ // This function needs to handle them all! |
+ |
state_ = (status == PIPELINE_OK) ? STATE_NORMAL : STATE_ERROR; |
- if (!reset_cb_.is_null()) { |
- if (!read_cb_.is_null()) |
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL); |
+ if (!read_cb_.is_null() && (!stop_cb_.is_null() || !reset_cb_.is_null())) |
+ AbortRead(); |
+ |
+ if (!reset_cb_.is_null()) |
base::ResetAndReturn(&reset_cb_).Run(); |
- return; |
- } |
- DCHECK(!read_cb_.is_null()); |
+ // If !stop_cb_.is_null(), it will be handled in OnDecoderStopped(). |
+ |
+ if (read_cb_.is_null()) |
+ return; |
if (!stop_cb_.is_null()) { |
base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kOk, NULL); |
@@ -304,17 +351,16 @@ void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) { |
} |
if (state_ == STATE_ERROR) { |
- base::ResetAndReturn(&read_cb_).Run(VideoDecoder::kDecodeError, NULL); |
+ SatisfyRead(VideoDecoder::kDecodeError, NULL); |
return; |
} |
- decoder_->Read(base::Bind(&VideoFrameStream::OnFrameReady, weak_this_)); |
+ ReadFromDemuxerStream(); |
} |
void VideoFrameStream::ResetDecoder() { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
- DCHECK(state_ == STATE_NORMAL || |
- state_ == STATE_FLUSHING_DECODER || |
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || |
state_ == STATE_ERROR) << state_; |
DCHECK(!reset_cb_.is_null()); |
@@ -323,8 +369,7 @@ void VideoFrameStream::ResetDecoder() { |
void VideoFrameStream::OnDecoderReset() { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
- DCHECK(state_ == STATE_NORMAL || |
- state_ == STATE_FLUSHING_DECODER || |
+ DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || |
state_ == STATE_ERROR) << state_; |
// If Reset() was called during pending read, read callback should be fired |
// before the reset callback is fired. |