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

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: 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
Index: media/filters/decoder_stream.cc
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 19ae054d64e7ab73d786a44d5050bca508ca7390..8fd7c72a6cfbdaa5389dd69ec280d9505beeebd8 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),
+ decode_weak_factory_(this) {}
template <DemuxerStream::Type StreamType>
DecoderStream<StreamType>::~DecoderStream() {
@@ -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,21 @@ 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;
+ }
+
+ // Push the pending buffers to the front of the fallback buffers
+ if (fallback_buffers_.empty()) {
+ fallback_buffers_.swap(pending_buffers_);
+ } else {
+ fallback_buffers_.insert(fallback_buffers_.begin(),
DaleCurtis 2016/04/15 01:19:16 Is it better to just insert at the end of pending_
tguilbert 2016/04/15 20:45:51 While I assume that the amortized cost would be fi
+ pending_buffers_.begin(), pending_buffers_.end());
+ pending_buffers_.clear();
+ }
+
if (!decoder_) {
if (state_ == STATE_INITIALIZING) {
state_ = STATE_UNINITIALIZED;
@@ -302,6 +320,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 +351,15 @@ 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,
+ decode_weak_factory_.GetWeakPtr(),
+ buffer_size, buffer->end_of_stream()));
}
template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::FlushDecoder() {
- Decode(DecoderBuffer::CreateEOSBuffer());
+ // Decode directly and bypass saving the EOS into |pending_buffers_|.
+ DecodeInternal(DecoderBuffer::CreateEOSBuffer());
}
template <DemuxerStream::Type StreamType>
@@ -337,6 +367,7 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size,
bool end_of_stream,
DecodeStatus status) {
FUNCTION_DVLOG(2) << ": " << status;
+
DaleCurtis 2016/04/15 01:19:17 Delete?
tguilbert 2016/04/15 20:45:52 Done.
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER ||
state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR)
<< state_;
@@ -363,6 +394,22 @@ 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 from being called back.
+ decode_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,
+ 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 +465,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 +487,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 +499,13 @@ 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(buffer);
+ return;
+ }
+
state_ = STATE_PENDING_DEMUXER_READ;
stream_->Read(base::Bind(&DecoderStream<StreamType>::OnBufferReady,
weak_factory_.GetWeakPtr()));
@@ -458,10 +520,48 @@ 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;
+ default:
DaleCurtis 2016/04/15 01:19:17 No need for default? We typically prefer to enumer
tguilbert 2016/04/15 20:45:52 Good to know. TY!
+ NOTREACHED();
+ break;
+ }
+ return;
+ }
+
// Decoding has been stopped (e.g due to an error).
if (state_ != STATE_PENDING_DEMUXER_READ) {
DCHECK(state_ == STATE_ERROR);
@@ -475,6 +575,9 @@ void DecoderStream<StreamType>::OnBufferReady(
FUNCTION_DVLOG(2) << ": " << "ConfigChanged";
DCHECK(stream_->SupportsConfigChanges());
+ // Pending buffers might not match the reinitialiazed decoder's new config
DaleCurtis 2016/04/15 01:19:17 Reinitialized.
tguilbert 2016/04/15 20:45:52 Done.
+ pending_buffers_.clear();
+
if (!config_change_observer_cb_.is_null())
config_change_observer_cb_.Run();
@@ -611,6 +714,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;

Powered by Google App Engine
This is Rietveld 408576698