Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(671)

Unified Diff: media/filters/decoder_stream.cc

Issue 1879353003: Attempt decoder fallback if first decode fails (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: new test for EOS flushing edge case Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/decoder_stream.h ('k') | media/filters/video_frame_stream_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/decoder_stream.cc
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 19ae054d64e7ab73d786a44d5050bca508ca7390..160d6dfa8ecce2f4a9f325278a80e8296bd32091 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -59,7 +59,8 @@ DecoderStream<StreamType>::DecoderStream(
decoding_eos_(false),
pending_decode_requests_(0),
duration_tracker_(8),
- weak_factory_(this) {}
+ weak_factory_(this),
+ fallback_weak_factory_(this) {}
template <DemuxerStream::Type StreamType>
DecoderStream<StreamType>::~DecoderStream() {
@@ -231,7 +232,7 @@ void DecoderStream<StreamType>::SelectDecoder(CdmContext* cdm_context) {
base::Bind(&DecoderStream<StreamType>::OnDecoderSelected,
weak_factory_.GetWeakPtr()),
base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady,
- weak_factory_.GetWeakPtr()),
+ fallback_weak_factory_.GetWeakPtr()),
waiting_for_decryption_key_cb_);
}
@@ -243,13 +244,15 @@ void DecoderStream<StreamType>::OnDecoderSelected(
<< (selected_decoder ? selected_decoder->GetDisplayName()
: "No decoder selected.");
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(state_ == STATE_INITIALIZING || state_ == STATE_REINITIALIZING_DECODER)
+ DCHECK(state_ == STATE_INITIALIZING ||
+ state_ == STATE_REINITIALIZING_DECODER ||
+ state_ == STATE_CONFIG_CHANGE_RECEIVED_WHILE_REINITIALIZING_DECODER)
<< state_;
if (state_ == STATE_INITIALIZING) {
DCHECK(!init_cb_.is_null());
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
- } else {
+ } else if (state_ == STATE_REINITIALIZING_DECODER) {
DCHECK(decoder_);
}
@@ -261,6 +264,16 @@ void DecoderStream<StreamType>::OnDecoderSelected(
stream_ = decrypting_demuxer_stream_.get();
}
+ // TODO(tguilbert): crbug.com/603713 support config changes on decoder reinit.
+ if (state_ == STATE_CONFIG_CHANGE_RECEIVED_WHILE_REINITIALIZING_DECODER) {
+ CompleteDecoderReinitialization(false);
+ return;
+ }
+
+ // Attempt to decode buffers from previous decoders (when those decoders have
+ // never successfully outputed a frame).
+ fallback_buffers_ = pending_buffers_;
+
if (!decoder_) {
if (state_ == STATE_INITIALIZING) {
state_ = STATE_UNINITIALIZED;
@@ -302,6 +315,19 @@ template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::Decode(
const scoped_refptr<DecoderBuffer>& buffer) {
FUNCTION_DVLOG(2);
+
+ // We don't know if the decoder will error out on first decode yet. Save the
+ // buffer to feed it to the fallback decoder later if needed.
+ if (!decoded_frames_since_fallback_)
+ pending_buffers_.push_back(buffer);
+
+ DecodeInternal(buffer);
+}
+
+template <DemuxerStream::Type StreamType>
+void DecoderStream<StreamType>::DecodeInternal(
+ const scoped_refptr<DecoderBuffer>& buffer) {
+ FUNCTION_DVLOG(2);
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_;
DCHECK_LT(pending_decode_requests_, GetMaxDecodeRequests());
DCHECK(reset_cb_.is_null());
@@ -320,16 +346,16 @@ void DecoderStream<StreamType>::Decode(
duration_tracker_.AddSample(buffer->duration());
++pending_decode_requests_;
- decoder_->Decode(buffer,
- base::Bind(&DecoderStream<StreamType>::OnDecodeDone,
- weak_factory_.GetWeakPtr(),
- buffer_size,
- buffer->end_of_stream()));
+ decoder_->Decode(buffer, base::Bind(&DecoderStream<StreamType>::OnDecodeDone,
+ fallback_weak_factory_.GetWeakPtr(),
+ buffer_size, buffer->end_of_stream()));
}
template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::FlushDecoder() {
- Decode(DecoderBuffer::CreateEOSBuffer());
+ // Send the EOS directly to the decoder, bypassing a potential add to
+ // |pending_buffers_|.
+ DecodeInternal(DecoderBuffer::CreateEOSBuffer());
}
template <DemuxerStream::Type StreamType>
@@ -363,6 +389,23 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size,
switch (status) {
case DecodeStatus::DECODE_ERROR:
+ if (!decoded_frames_since_fallback_) {
+ pending_decode_requests_ = 0;
+
+ // Prevent all pending decode requests and outputs form those requests
+ // from being called back.
+ fallback_weak_factory_.InvalidateWeakPtrs();
+
+ state_ = STATE_REINITIALIZING_DECODER;
+ decoder_selector_->SelectDecoder(
+ stream_, nullptr,
+ base::Bind(&DecoderStream<StreamType>::OnDecoderSelected,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady,
+ fallback_weak_factory_.GetWeakPtr()),
+ waiting_for_decryption_key_cb_);
+ return;
+ }
state_ = STATE_ERROR;
MEDIA_LOG(ERROR, media_log_) << GetStreamTypeString() << " decode error";
ready_outputs_.clear();
@@ -418,6 +461,14 @@ void DecoderStream<StreamType>::OnDecodeOutputReady(
if (!reset_cb_.is_null())
return;
+ ++decoded_frames_since_fallback_;
+
+ // |decoder_| sucessfully decoded a frame. No need to keep buffers for a
+ // fallback decoder.
+ // Note: |fallback_buffers_| might still have buffers, and we will keep
+ // reading from there before requesting new buffers from |stream_|.
+ pending_buffers_.clear();
+
if (!read_cb_.is_null()) {
// If |ready_outputs_| was non-empty, the read would have already been
// satisifed by Read().
@@ -432,7 +483,7 @@ void DecoderStream<StreamType>::OnDecodeOutputReady(
// Destruct any previous decoder once we've decoded enough frames to ensure
// that it's no longer in use.
if (previous_decoder_ &&
- ++decoded_frames_since_fallback_ > limits::kMaxVideoFrames) {
+ decoded_frames_since_fallback_ > limits::kMaxVideoFrames) {
previous_decoder_.reset();
}
}
@@ -444,6 +495,15 @@ void DecoderStream<StreamType>::ReadFromDemuxerStream() {
DCHECK(CanDecodeMore());
DCHECK(reset_cb_.is_null());
+ if (!fallback_buffers_.empty()) {
+ scoped_refptr<DecoderBuffer> buffer = fallback_buffers_.front();
+ fallback_buffers_.pop_front();
+
+ // Decode the buffer without re-appending it to |pending_buffers_|.
+ DecodeInternal(buffer);
+ return;
+ }
+
state_ = STATE_PENDING_DEMUXER_READ;
stream_->Read(base::Bind(&DecoderStream<StreamType>::OnBufferReady,
weak_factory_.GetWeakPtr()));
@@ -458,10 +518,45 @@ void DecoderStream<StreamType>::OnBufferReady(
: "NULL");
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR)
- << state_;
+ if (decoded_frames_since_fallback_) {
+ DCHECK(state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR)
+ << state_;
+ } else {
+ DCHECK(state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR ||
+ STATE_REINITIALIZING_DECODER)
+ << state_;
+ }
DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status;
+ // If parallel decode requests are supported, multiple read requests might
+ // have been sent to the demuxer. The buffers might arrive while the decoder
+ // is reinitializing after falling back on first decode error.
+ if (state_ == STATE_REINITIALIZING_DECODER &&
+ !decoded_frames_since_fallback_) {
+ switch (status) {
+ case DemuxerStream::kOk:
+ // Save valid buffers to be consumed by the new decoder.
+ // |pending_buffers_| is copied to |fallback_buffers| in
+ // OnDecoderSelected().
+ pending_buffers_.push_back(buffer);
+ break;
+ case DemuxerStream::kConfigChanged:
+ // TODO(tguilbert): crbug.com/603713
+ // |decoder_| might have a stale config by the time it is reinitialized.
+ // Ideally, we would save the config from |stream_| and reinitialize the
+ // decoder by playing back the sequence of buffers and config changes.
+ state_ = STATE_CONFIG_CHANGE_RECEIVED_WHILE_REINITIALIZING_DECODER;
+ pending_buffers_.clear();
+ break;
+ case DemuxerStream::kAborted:
+ // |this| will read from the demuxer stream again in OnDecoderSelected()
+ // and receive a kAborted then.
+ pending_buffers_.clear();
+ break;
+ }
+ return;
+ }
+
// Decoding has been stopped (e.g due to an error).
if (state_ != STATE_PENDING_DEMUXER_READ) {
DCHECK(state_ == STATE_ERROR);
@@ -475,6 +570,26 @@ void DecoderStream<StreamType>::OnBufferReady(
FUNCTION_DVLOG(2) << ": " << "ConfigChanged";
DCHECK(stream_->SupportsConfigChanges());
+ // Pending buffers might not match the reinitialized decoder's new config.
+ //
+ // Note: as part of crbug.com/603713, we should record the config in order
+ // to play it back to the fallback decoder.
+ //
+ // Clearing the buffers is an acceptable workaround for the time being. It
+ // assures us that we maintain a consistent state, at the cost of
+ // potentially dropping some frames. Flushing the decoder will cause one of
+ // the following outcomes:
+ // - The decoder outputs a valid frame during flushing (we no longer
+ // care about |pending_buffers_| and fallback scenarios).
+ // - The decoder returns a DECODE_ERROR via OnDecodeDone() without having
+ // outputted a frame (we fallback to a new decoder which will read
+ // straight from the demuxer, dropping some frames).
+ // - The decoder is flushed without returning a frame or without a
+ // DECODE_ERROR (we reinitialize the decoder as if a normal flush
+ // happened, and read straight from the demuxer, which could lead to some
+ // lost frames if we were to fallback then).
+ pending_buffers_.clear();
+
if (!config_change_observer_cb_.is_null())
config_change_observer_cb_.Run();
@@ -535,7 +650,7 @@ void DecoderStream<StreamType>::ReinitializeDecoder() {
base::Bind(&DecoderStream<StreamType>::OnDecoderReinitialized,
weak_factory_.GetWeakPtr()),
base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady,
- weak_factory_.GetWeakPtr()));
+ fallback_weak_factory_.GetWeakPtr()));
}
template <DemuxerStream::Type StreamType>
@@ -611,6 +726,10 @@ void DecoderStream<StreamType>::OnDecoderReset() {
DCHECK(read_cb_.is_null());
DCHECK(!reset_cb_.is_null());
+ // Make sure we read directly from the demuxer after a reset.
+ fallback_buffers_.clear();
+ pending_buffers_.clear();
+
if (state_ != STATE_FLUSHING_DECODER) {
state_ = STATE_NORMAL;
active_splice_ = false;
« no previous file with comments | « media/filters/decoder_stream.h ('k') | media/filters/video_frame_stream_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698