#include "media/base/android/media_codec_loop.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "media/base/media_serializers.h"
#include "media/base/timestamp_constants.h"
namespace media {
namespace {
constexpr base::TimeDelta kDecodePollDelay = base::Milliseconds(10);
constexpr base::TimeDelta kNoWaitTimeout = base::Microseconds(0);
constexpr base::TimeDelta kIdleTimerTimeout = base::Seconds(1);
}
MediaCodecLoop::MediaCodecLoop(
int sdk_int,
Client* client,
std::unique_ptr<MediaCodecBridge> media_codec,
scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner,
bool disable_timer)
: state_(STATE_READY),
client_(client),
media_codec_(std::move(media_codec)),
pending_input_buf_index_(kInvalidBufferIndex),
sdk_int_(sdk_int),
disable_timer_(disable_timer) {
if (timer_task_runner)
io_timer_.SetTaskRunner(timer_task_runner);
if (media_codec_ == nullptr)
SetState(STATE_ERROR);
}
MediaCodecLoop::~MediaCodecLoop() {
io_timer_.Stop();
}
void MediaCodecLoop::SetTestTickClock(const base::TickClock* test_tick_clock) {
test_tick_clock_ = test_tick_clock;
}
void MediaCodecLoop::OnKeyAdded() {
if (state_ == STATE_WAITING_FOR_KEY)
SetState(STATE_READY);
ExpectWork();
}
bool MediaCodecLoop::TryFlush() {
if (state_ == STATE_ERROR || state_ == STATE_DRAINED)
return false;
io_timer_.Stop();
if (!media_codec_->Flush().is_ok()) {
SetState(STATE_ERROR);
return false;
}
SetState(STATE_READY);
return true;
}
void MediaCodecLoop::ExpectWork() {
ManageTimer(true);
DoPendingWork();
}
void MediaCodecLoop::DoPendingWork() {
if (state_ == STATE_ERROR)
return;
bool did_work = false, did_input = false, did_output = false;
do {
did_input = ProcessOneInputBuffer();
did_output = ProcessOneOutputBuffer();
if (did_input || did_output)
did_work = true;
} while (did_input || did_output);
ManageTimer(did_work);
}
bool MediaCodecLoop::ProcessOneInputBuffer() {
if (state_ != STATE_READY)
return false;
if (pending_input_buf_index_ == kInvalidBufferIndex &&
!client_->IsAnyInputPending()) {
return false;
}
InputBuffer input_buffer = DequeueInputBuffer();
if (input_buffer.index == kInvalidBufferIndex)
return false;
EnqueueInputBuffer(input_buffer);
return state_ != STATE_ERROR;
}
MediaCodecLoop::InputBuffer MediaCodecLoop::DequeueInputBuffer() {
DVLOG(2) << __func__;
if (pending_input_buf_index_ != kInvalidBufferIndex) {
InputBuffer result(pending_input_buf_index_, true);
pending_input_buf_index_ = kInvalidBufferIndex;
return result;
}
int input_buf_index = kInvalidBufferIndex;
MediaCodecResult result =
media_codec_->DequeueInputBuffer(kNoWaitTimeout, &input_buf_index);
switch (result.code()) {
case MediaCodecResult::Codes::kTryAgainLater:
break;
case MediaCodecResult::Codes::kError:
DLOG(ERROR) << __func__ << ": " << result.message();
SetState(STATE_ERROR);
break;
case MediaCodecResult::Codes::kOk:
break;
default:
NOTREACHED() << "Unexpected DequeueInputBuffer result: "
<< result.message();
}
return InputBuffer(input_buf_index, false);
}
void MediaCodecLoop::EnqueueInputBuffer(const InputBuffer& input_buffer) {
DCHECK_NE(input_buffer.index, kInvalidBufferIndex);
bool already_filled = false;
scoped_refptr<DecoderBuffer> input_data;
if (input_buffer.is_pending) {
input_data = std::move(pending_input_buf_data_);
already_filled = true;
} else {
input_data = client_->ProvideInputData();
}
if (input_data->end_of_stream()) {
media_codec_->QueueEOS(input_buffer.index);
SetState(STATE_DRAINING);
client_->OnInputDataQueued(true);
return;
}
MediaCodecResult result = OkStatus();
if (input_data->decrypt_config()) {
result = media_codec_->QueueSecureInputBuffer(
input_buffer.index,
already_filled ? base::span<const uint8_t>() : *input_data,
input_data->timestamp(), *input_data->decrypt_config());
} else {
result = media_codec_->QueueInputBuffer(input_buffer.index, *input_data,
input_data->timestamp());
}
switch (result.code()) {
case MediaCodecResult::Codes::kError:
DLOG(ERROR)
<< __func__
<< ": MediaCodecResult::Codes::kError from QueueInputBuffer : "
<< result.message();
client_->OnInputDataQueued(false);
SetState(STATE_ERROR);
break;
case MediaCodecResult::Codes::kNoKey:
pending_input_buf_index_ = input_buffer.index;
pending_input_buf_data_ = std::move(input_data);
client_->OnWaiting(WaitingReason::kNoDecryptionKey);
SetState(STATE_WAITING_FOR_KEY);
break;
case MediaCodecResult::Codes::kOk:
client_->OnInputDataQueued(true);
break;
default:
NOTREACHED() << "Unknown Queue(Secure)InputBuffer status "
<< result.message();
}
}
bool MediaCodecLoop::ProcessOneOutputBuffer() {
if (state_ == STATE_ERROR)
return false;
OutputBuffer out;
MediaCodecResult result = media_codec_->DequeueOutputBuffer(
kNoWaitTimeout, &out.index, &out.offset, &out.size, &out.pts, &out.is_eos,
&out.is_key_frame);
bool did_work = false;
switch (result.code()) {
case MediaCodecResult::Codes::kOutputBuffersChanged:
did_work = true;
break;
case MediaCodecResult::Codes::kOutputFormatChanged:
if (!client_->OnOutputFormatChanged())
SetState(STATE_ERROR);
did_work = state_ != STATE_ERROR;
break;
case MediaCodecResult::Codes::kOk:
if (out.is_eos) {
DCHECK_EQ(state_, STATE_DRAINING);
SetState(STATE_DRAINED);
DCHECK_NE(out.index, kInvalidBufferIndex);
DCHECK(media_codec_);
media_codec_->ReleaseOutputBuffer(out.index, false);
if (!client_->OnDecodedEos(out))
SetState(STATE_ERROR);
} else {
if (!client_->OnDecodedFrame(out))
SetState(STATE_ERROR);
}
did_work = true;
break;
case MediaCodecResult::Codes::kTryAgainLater:
break;
case MediaCodecResult::Codes::kError:
DLOG(ERROR) << __func__
<< ": MediaCodecResult::Codes::kError from "
"DequeueOutputBuffer, result: "
<< result.message();
SetState(STATE_ERROR);
break;
default:
NOTREACHED() << "Unexpected DequeueOutputBuffer result: "
<< result.message();
}
return did_work;
}
void MediaCodecLoop::ManageTimer(bool did_work) {
if (disable_timer_)
return;
bool should_be_running = true;
base::TimeTicks now = (test_tick_clock_ ? test_tick_clock_->NowTicks()
: base::TimeTicks::Now());
if (did_work || idle_time_begin_ == base::TimeTicks()) {
idle_time_begin_ = now;
} else {
if (now - idle_time_begin_ > kIdleTimerTimeout)
should_be_running = false;
}
if (should_be_running && !io_timer_.IsRunning()) {
io_timer_.Start(FROM_HERE, kDecodePollDelay, this,
&MediaCodecLoop::DoPendingWork);
} else if (!should_be_running && io_timer_.IsRunning()) {
io_timer_.Stop();
}
}
void MediaCodecLoop::SetState(State new_state) {
const State old_state = state_;
state_ = new_state;
if (old_state != new_state && new_state == STATE_ERROR)
client_->OnCodecLoopError();
}
MediaCodecBridge* MediaCodecLoop::GetCodec() const {
return media_codec_.get();
}
const char* MediaCodecLoop::AsString(State state) {
#define RETURN_STRING(x) \
case x: \
return #x;
switch (state) {
RETURN_STRING(STATE_READY);
RETURN_STRING(STATE_WAITING_FOR_KEY);
RETURN_STRING(STATE_DRAINING);
RETURN_STRING(STATE_DRAINED);
RETURN_STRING(STATE_ERROR);
}
#undef RETURN_STRING
NOTREACHED() << "Unknown state " << state;
}
}