| 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;
|
|
|