blob: 25295ff44ca073b451c7dedca79d15924ae7cdb5 [file] [log] [blame]
// Copyright 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 "cobalt/media/filters/fake_video_decoder.h"
#include "base/location.h"
#include "cobalt/media/base/bind_to_current_loop.h"
#include "cobalt/media/base/test_helpers.h"
namespace cobalt {
namespace media {
FakeVideoDecoder::FakeVideoDecoder(int decoding_delay,
int max_parallel_decoding_requests,
const BytesDecodedCB& bytes_decoded_cb)
: decoding_delay_(decoding_delay),
max_parallel_decoding_requests_(max_parallel_decoding_requests),
bytes_decoded_cb_(bytes_decoded_cb),
state_(STATE_UNINITIALIZED),
hold_decode_(false),
total_bytes_decoded_(0),
fail_to_initialize_(false),
weak_factory_(this) {
DCHECK_GE(decoding_delay, 0);
}
FakeVideoDecoder::~FakeVideoDecoder() {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == STATE_UNINITIALIZED) return;
if (!init_cb_.IsNull()) SatisfyInit();
if (!held_decode_callbacks_.empty()) SatisfyDecode();
if (!reset_cb_.IsNull()) SatisfyReset();
decoded_frames_.clear();
}
std::string FakeVideoDecoder::GetDisplayName() const {
return "FakeVideoDecoder";
}
void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config,
bool low_delay, CdmContext* /* cdm_context */,
const InitCB& init_cb,
const OutputCB& output_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(config.IsValidConfig());
DCHECK(held_decode_callbacks_.empty())
<< "No reinitialization during pending decode.";
DCHECK(reset_cb_.IsNull()) << "No reinitialization during pending reset.";
current_config_ = config;
init_cb_.SetCallback(BindToCurrentLoop(init_cb));
// Don't need BindToCurrentLoop() because |output_cb_| is only called from
// RunDecodeCallback() which is posted from Decode().
output_cb_ = output_cb;
if (!decoded_frames_.empty()) {
DVLOG(1) << "Decoded frames dropped during reinitialization.";
decoded_frames_.clear();
}
if (fail_to_initialize_) {
state_ = STATE_ERROR;
init_cb_.RunOrHold(false);
} else {
state_ = STATE_NORMAL;
init_cb_.RunOrHold(true);
}
}
void FakeVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB& decode_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(reset_cb_.IsNull());
DCHECK_LE(decoded_frames_.size(),
decoding_delay_ + held_decode_callbacks_.size());
DCHECK_LT(static_cast<int>(held_decode_callbacks_.size()),
max_parallel_decoding_requests_);
DCHECK_NE(state_, STATE_END_OF_STREAM);
int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size();
DecodeCB wrapped_decode_cb =
base::Bind(&FakeVideoDecoder::OnFrameDecoded, weak_factory_.GetWeakPtr(),
buffer_size, BindToCurrentLoop(decode_cb));
if (state_ == STATE_ERROR) {
wrapped_decode_cb.Run(DecodeStatus::DECODE_ERROR);
return;
}
if (buffer->end_of_stream()) {
state_ = STATE_END_OF_STREAM;
} else {
DCHECK(VerifyFakeVideoBufferForTest(buffer, current_config_));
scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateColorFrame(
current_config_.coded_size(), 0, 0, 0, buffer->timestamp());
decoded_frames_.push_back(video_frame);
}
RunOrHoldDecode(wrapped_decode_cb);
}
void FakeVideoDecoder::Reset(const base::Closure& closure) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(reset_cb_.IsNull());
reset_cb_.SetCallback(BindToCurrentLoop(closure));
decoded_frames_.clear();
// Defer the reset if a decode is pending.
if (!held_decode_callbacks_.empty()) return;
DoReset();
}
void FakeVideoDecoder::HoldNextInit() {
DCHECK(thread_checker_.CalledOnValidThread());
init_cb_.HoldCallback();
}
void FakeVideoDecoder::HoldDecode() {
DCHECK(thread_checker_.CalledOnValidThread());
hold_decode_ = true;
}
void FakeVideoDecoder::HoldNextReset() {
DCHECK(thread_checker_.CalledOnValidThread());
reset_cb_.HoldCallback();
}
void FakeVideoDecoder::SatisfyInit() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(held_decode_callbacks_.empty());
DCHECK(reset_cb_.IsNull());
init_cb_.RunHeldCallback();
}
void FakeVideoDecoder::SatisfyDecode() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(hold_decode_);
hold_decode_ = false;
while (!held_decode_callbacks_.empty()) {
SatisfySingleDecode();
}
}
void FakeVideoDecoder::SatisfySingleDecode() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!held_decode_callbacks_.empty());
DecodeCB decode_cb = held_decode_callbacks_.front();
held_decode_callbacks_.pop_front();
RunDecodeCallback(decode_cb);
if (!reset_cb_.IsNull() && held_decode_callbacks_.empty()) DoReset();
}
void FakeVideoDecoder::SatisfyReset() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(held_decode_callbacks_.empty());
reset_cb_.RunHeldCallback();
}
void FakeVideoDecoder::SimulateError() {
DCHECK(thread_checker_.CalledOnValidThread());
state_ = STATE_ERROR;
while (!held_decode_callbacks_.empty()) {
held_decode_callbacks_.front().Run(DecodeStatus::DECODE_ERROR);
held_decode_callbacks_.pop_front();
}
decoded_frames_.clear();
}
void FakeVideoDecoder::SimulateFailureToInit() { fail_to_initialize_ = true; }
int FakeVideoDecoder::GetMaxDecodeRequests() const {
return max_parallel_decoding_requests_;
}
void FakeVideoDecoder::OnFrameDecoded(int buffer_size,
const DecodeCB& decode_cb,
DecodeStatus status) {
DCHECK(thread_checker_.CalledOnValidThread());
if (status == DecodeStatus::OK) {
total_bytes_decoded_ += buffer_size;
bytes_decoded_cb_.Run(buffer_size);
}
decode_cb.Run(status);
}
void FakeVideoDecoder::RunOrHoldDecode(const DecodeCB& decode_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
if (hold_decode_) {
held_decode_callbacks_.push_back(decode_cb);
} else {
DCHECK(held_decode_callbacks_.empty());
RunDecodeCallback(decode_cb);
}
}
void FakeVideoDecoder::RunDecodeCallback(const DecodeCB& decode_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!reset_cb_.IsNull()) {
DCHECK(decoded_frames_.empty());
decode_cb.Run(DecodeStatus::ABORTED);
return;
}
// Make sure we leave decoding_delay_ frames in the queue and also frames for
// all pending decode callbacks, except the current one.
if (decoded_frames_.size() >
decoding_delay_ + held_decode_callbacks_.size()) {
output_cb_.Run(decoded_frames_.front());
decoded_frames_.pop_front();
} else if (state_ == STATE_END_OF_STREAM) {
// Drain the queue if this was the last request in the stream, otherwise
// just pop the last frame from the queue.
if (held_decode_callbacks_.empty()) {
while (!decoded_frames_.empty()) {
output_cb_.Run(decoded_frames_.front());
decoded_frames_.pop_front();
}
state_ = STATE_NORMAL;
} else if (!decoded_frames_.empty()) {
output_cb_.Run(decoded_frames_.front());
decoded_frames_.pop_front();
}
}
decode_cb.Run(DecodeStatus::OK);
}
void FakeVideoDecoder::DoReset() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(held_decode_callbacks_.empty());
DCHECK(!reset_cb_.IsNull());
reset_cb_.RunOrHold();
}
} // namespace media
} // namespace cobalt