blob: b18cedafd851f977f4549f05f2fac2eadc8e65ec [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/filters/video_frame_stream.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "media/base/bind_to_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/demuxer_stream.h"
#include "media/base/video_decoder_config.h"
#include "media/filters/decrypting_demuxer_stream.h"
#include "media/filters/video_decoder_selector.h"
namespace media {
VideoFrameStream::VideoFrameStream(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
ScopedVector<VideoDecoder> decoders,
const SetDecryptorReadyCB& set_decryptor_ready_cb)
: message_loop_(message_loop),
weak_factory_(this),
state_(STATE_UNINITIALIZED),
stream_(NULL),
decoder_selector_(new VideoDecoderSelector(message_loop,
decoders.Pass(),
set_decryptor_ready_cb)) {
}
VideoFrameStream::~VideoFrameStream() {
DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_STOPPED) << state_;
}
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());
DCHECK(!init_cb.is_null());
statistics_cb_ = statistics_cb;
init_cb_ = init_cb;
stream_ = stream;
state_ = STATE_INITIALIZING;
// TODO(xhwang): VideoDecoderSelector only needs a config to select a decoder.
decoder_selector_->SelectVideoDecoder(
stream,
base::Bind(&VideoFrameStream::OnDecoderSelected,
weak_factory_.GetWeakPtr()));
}
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_;
// No two reads in the flight at any time.
DCHECK(read_cb_.is_null());
// No read during resetting or stopping process.
DCHECK(reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
if (state_ == STATE_ERROR) {
message_loop_->PostTask(FROM_HERE, base::Bind(
read_cb, DECODE_ERROR, scoped_refptr<VideoFrame>()));
return;
}
read_cb_ = read_cb;
if (state_ == STATE_FLUSHING_DECODER) {
FlushDecoder();
return;
}
ReadFromDemuxerStream();
}
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());
DCHECK(stop_cb_.is_null());
reset_cb_ = closure;
// 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)
return;
// During pending demuxer read and when not using DecryptingDemuxerStream,
// VideoDecoder will be reset after demuxer read is returned
// (in OnBufferReady()).
if (state_ == STATE_PENDING_DEMUXER_READ && !decrypting_demuxer_stream_)
return;
// 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_factory_.GetWeakPtr()));
return;
}
ResetDecoder();
}
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;
}
DCHECK(init_cb_.is_null());
// All pending callbacks will be dropped.
weak_factory_.InvalidateWeakPtrs();
// Post callbacks to prevent reentrance into this object.
if (!read_cb_.is_null())
message_loop_->PostTask(FROM_HERE, base::Bind(
base::ResetAndReturn(&read_cb_), ABORTED, scoped_refptr<VideoFrame>()));
if (!reset_cb_.is_null())
message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_));
if (decrypting_demuxer_stream_) {
decrypting_demuxer_stream_->Stop(base::Bind(
&VideoFrameStream::StopDecoder, weak_factory_.GetWeakPtr()));
return;
}
// We may not have a |decoder_| if Stop() was called during initialization.
if (decoder_) {
StopDecoder();
return;
}
state_ = STATE_STOPPED;
stream_ = NULL;
decoder_.reset();
decrypting_demuxer_stream_.reset();
message_loop_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_));
}
bool VideoFrameStream::CanReadWithoutStalling() const {
DCHECK(message_loop_->BelongsToCurrentThread());
return decoder_->CanReadWithoutStalling();
}
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());
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
decoder_selector_.reset();
if (!selected_decoder) {
state_ = STATE_UNINITIALIZED;
base::ResetAndReturn(&init_cb_).Run(false, false);
} else {
state_ = STATE_NORMAL;
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass();
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());
}
// Stop() called during initialization.
if (!stop_cb_.is_null()) {
Stop(base::ResetAndReturn(&stop_cb_));
return;
}
}
void VideoFrameStream::SatisfyRead(Status status,
const scoped_refptr<VideoFrame>& frame) {
DCHECK(!read_cb_.is_null());
base::ResetAndReturn(&read_cb_).Run(status, frame);
}
void VideoFrameStream::AbortRead() {
// Abort read during pending reset. It is safe to fire the |read_cb_| directly
// instead of posting it because VideoRenderBase won't call into this class
// again when it's in kFlushing state.
// TODO(xhwang): Improve the resetting process to avoid this dependency on the
// caller.
DCHECK(!reset_cb_.is_null());
SatisfyRead(ABORTED, NULL);
}
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());
DCHECK(stop_cb_.is_null());
DCHECK(buffer);
int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size();
TRACE_EVENT_ASYNC_BEGIN0("media", "VideoFrameStream::Decode", this);
decoder_->Decode(buffer, base::Bind(&VideoFrameStream::OnFrameReady,
weak_factory_.GetWeakPtr(), buffer_size));
}
void VideoFrameStream::FlushDecoder() {
Decode(DecoderBuffer::CreateEOSBuffer());
}
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());
DCHECK(stop_cb_.is_null());
TRACE_EVENT_ASYNC_END0("media", "VideoFrameStream::Decode", this);
if (status == VideoDecoder::kDecodeError) {
DCHECK(!frame.get());
state_ = STATE_ERROR;
SatisfyRead(DECODE_ERROR, NULL);
return;
}
if (status == VideoDecoder::kDecryptError) {
DCHECK(!frame.get());
state_ = STATE_ERROR;
SatisfyRead(DECRYPT_ERROR, NULL);
return;
}
// Any successful decode counts!
if (buffer_size > 0) {
PipelineStatistics statistics;
statistics.video_bytes_decoded = buffer_size;
statistics_cb_.Run(statistics);
}
// Drop decoding result if Reset() was called during decoding.
// The resetting process will be handled when the decoder is reset.
if (!reset_cb_.is_null()) {
AbortRead();
return;
}
// Decoder flushed. Reinitialize the video decoder.
if (state_ == STATE_FLUSHING_DECODER &&
status == VideoDecoder::kOk && frame->end_of_stream()) {
ReinitializeDecoder();
return;
}
if (status == VideoDecoder::kNotEnoughData) {
if (state_ == STATE_NORMAL)
ReadFromDemuxerStream();
else if (state_ == STATE_FLUSHING_DECODER)
FlushDecoder();
return;
}
SatisfyRead(OK, frame);
}
void VideoFrameStream::ReadFromDemuxerStream() {
DVLOG(2) << __FUNCTION__;
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_factory_.GetWeakPtr()));
}
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;
DCHECK(!read_cb_.is_null());
DCHECK(stop_cb_.is_null());
state_ = STATE_NORMAL;
if (status == DemuxerStream::kConfigChanged) {
state_ = STATE_FLUSHING_DECODER;
if (!reset_cb_.is_null()) {
AbortRead();
// If we are using DecryptingDemuxerStream, we already called DDS::Reset()
// which will continue the resetting process in it's callback.
if (!decrypting_demuxer_stream_)
Reset(base::ResetAndReturn(&reset_cb_));
// Reinitialization will continue after Reset() is done.
} else {
FlushDecoder();
}
return;
}
if (!reset_cb_.is_null()) {
AbortRead();
// If we are using DecryptingDemuxerStream, we already called DDS::Reset()
// which will continue the resetting process in it's callback.
if (!decrypting_demuxer_stream_)
Reset(base::ResetAndReturn(&reset_cb_));
return;
}
if (status == DemuxerStream::kAborted) {
SatisfyRead(DEMUXER_READ_ABORTED, NULL);
return;
}
DCHECK(status == DemuxerStream::kOk) << status;
Decode(buffer);
}
void VideoFrameStream::ReinitializeDecoder() {
DVLOG(2) << __FUNCTION__;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_;
DCHECK(stream_->video_decoder_config().IsValidConfig());
state_ = STATE_REINITIALIZING_DECODER;
decoder_->Initialize(stream_->video_decoder_config(),
base::Bind(&VideoFrameStream::OnDecoderReinitialized,
weak_factory_.GetWeakPtr()));
}
void VideoFrameStream::OnDecoderReinitialized(PipelineStatus status) {
DVLOG(2) << __FUNCTION__;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER) << state_;
DCHECK(stop_cb_.is_null());
// ReinitializeDecoder() can be called in two cases:
// 1, Flushing decoder finished (see OnFrameReady()).
// 2, Reset() was called during flushing decoder (see OnDecoderReset()).
// Also, Reset() 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())
AbortRead();
base::ResetAndReturn(&reset_cb_).Run();
}
if (read_cb_.is_null())
return;
if (state_ == STATE_ERROR) {
SatisfyRead(DECODE_ERROR, NULL);
return;
}
ReadFromDemuxerStream();
}
void VideoFrameStream::ResetDecoder() {
DVLOG(2) << __FUNCTION__;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER ||
state_ == STATE_ERROR) << state_;
DCHECK(!reset_cb_.is_null());
decoder_->Reset(base::Bind(&VideoFrameStream::OnDecoderReset,
weak_factory_.GetWeakPtr()));
}
void VideoFrameStream::OnDecoderReset() {
DVLOG(2) << __FUNCTION__;
DCHECK(message_loop_->BelongsToCurrentThread());
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.
DCHECK(read_cb_.is_null());
DCHECK(!reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
if (state_ != STATE_FLUSHING_DECODER) {
base::ResetAndReturn(&reset_cb_).Run();
return;
}
// The resetting process will be continued in OnDecoderReinitialized().
ReinitializeDecoder();
}
void VideoFrameStream::StopDecoder() {
DVLOG(2) << __FUNCTION__;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_;
DCHECK(!stop_cb_.is_null());
decoder_->Stop(base::Bind(&VideoFrameStream::OnDecoderStopped,
weak_factory_.GetWeakPtr()));
}
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
// be fired before the stop callback is fired.
DCHECK(read_cb_.is_null());
DCHECK(reset_cb_.is_null());
DCHECK(!stop_cb_.is_null());
state_ = STATE_STOPPED;
stream_ = NULL;
decoder_.reset();
decrypting_demuxer_stream_.reset();
base::ResetAndReturn(&stop_cb_).Run();
}
} // namespace media