blob: 39c43d6d5d6211bad408f56657d753a16cd4ee8f [file] [log] [blame]
// Copyright 2018 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/fuchsia/fuchsia_video_decoder.h"
#include <fuchsia/media/cpp/fidl.h>
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <zircon/rights.h>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/service_directory_client.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "third_party/libyuv/include/libyuv/video_common.h"
namespace media {
namespace {
const zx_rights_t kReadOnlyVmoRights =
ZX_DEFAULT_VMO_RIGHTS &
~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY);
// Value passed to the codec as packet_count_for_client. It's number of output
// buffers that we expect to hold on to in the renderer.
//
// TODO(sergeyu): Figure out the right number of buffers to request. Currently
// the codec doesn't allow to reserve more than 2 client buffers, but it still
// works properly when the client holds to more than that.
const uint32_t kMaxUsedOutputFrames = 8;
zx::vmo CreateContiguousVmo(size_t size, const zx::handle& bti_handle) {
zx::vmo vmo;
zx_status_t status =
zx_vmo_create_contiguous(bti_handle.get(), size, /*alignment_log2=*/0,
vmo.reset_and_get_address());
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmo_create_contiguous";
return zx::vmo();
}
return vmo;
}
zx::vmo CreateVmo(size_t size) {
zx::vmo vmo;
zx_status_t status = zx::vmo::create(size, ZX_VMO_NON_RESIZABLE, &vmo);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmo_create";
return zx::vmo();
}
return vmo;
}
class PendingDecode {
public:
PendingDecode(scoped_refptr<DecoderBuffer> buffer,
VideoDecoder::DecodeCB decode_cb)
: buffer_(buffer), decode_cb_(decode_cb) {
DCHECK(buffer_);
}
~PendingDecode() {
if (decode_cb_) {
std::move(decode_cb_).Run(DecodeStatus::ABORTED);
}
}
PendingDecode(PendingDecode&& other) = default;
PendingDecode& operator=(PendingDecode&& other) = default;
const DecoderBuffer& buffer() { return *buffer_; }
const uint8_t* data() const { return buffer_->data() + buffer_pos_; }
size_t bytes_left() const { return buffer_->data_size() - buffer_pos_; }
void AdvanceCurrentPos(size_t bytes) {
DCHECK_LE(bytes, bytes_left());
buffer_pos_ += bytes;
}
VideoDecoder::DecodeCB TakeDecodeCallback() { return std::move(decode_cb_); }
private:
scoped_refptr<DecoderBuffer> buffer_;
size_t buffer_pos_ = 0;
VideoDecoder::DecodeCB decode_cb_;
DISALLOW_COPY_AND_ASSIGN(PendingDecode);
};
class CodecBuffer {
public:
CodecBuffer() = default;
bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) {
if (!constraints.has_per_packet_buffer_bytes_recommended()) {
return false;
}
size_ = constraints.per_packet_buffer_bytes_recommended();
if (constraints.has_is_physically_contiguous_required() &&
constraints.is_physically_contiguous_required()) {
if (!constraints.has_very_temp_kludge_bti_handle()) {
return false;
}
vmo_ =
CreateContiguousVmo(size_, constraints.very_temp_kludge_bti_handle());
} else {
vmo_ = CreateVmo(size_);
}
return vmo_.is_valid();
}
const zx::vmo& vmo() const { return vmo_; }
size_t size() const { return size_; }
bool ToFidlCodecBuffer(uint64_t buffer_lifetime_ordinal,
uint32_t buffer_index,
bool read_only,
fuchsia::media::StreamBuffer* buffer) {
zx::vmo vmo_dup;
zx_status_t status = vmo_.duplicate(
read_only ? kReadOnlyVmoRights : ZX_RIGHT_SAME_RIGHTS, &vmo_dup);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_handle_duplicate";
return false;
}
fuchsia::media::StreamBufferDataVmo buf_data;
buf_data.set_vmo_handle(std::move(vmo_dup));
buf_data.set_vmo_usable_start(0);
buf_data.set_vmo_usable_size(size_);
buffer->mutable_data()->set_vmo(std::move(buf_data));
buffer->set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
buffer->set_buffer_index(buffer_index);
return true;
}
private:
zx::vmo vmo_;
size_t size_ = 0;
DISALLOW_COPY_AND_ASSIGN(CodecBuffer);
};
class InputBuffer {
public:
InputBuffer() = default;
~InputBuffer() { CallDecodeCallbackIfAny(DecodeStatus::ABORTED); }
bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) {
return buffer_.Initialize(constraints);
}
CodecBuffer& buffer() { return buffer_; }
bool is_used() const { return is_used_; }
// Copies as much data as possible from |pending_decode| to this input buffer.
size_t FillFromDecodeBuffer(PendingDecode* pending_decode) {
DCHECK(!is_used_);
is_used_ = true;
size_t bytes_to_fill =
std::min(buffer_.size(), pending_decode->bytes_left());
zx_status_t status =
buffer_.vmo().write(pending_decode->data(), 0, bytes_to_fill);
ZX_CHECK(status == ZX_OK, status) << "zx_vmo_write";
pending_decode->AdvanceCurrentPos(bytes_to_fill);
if (pending_decode->bytes_left() == 0) {
DCHECK(!decode_cb_);
decode_cb_ = pending_decode->TakeDecodeCallback();
}
return bytes_to_fill;
}
void CallDecodeCallbackIfAny(DecodeStatus status) {
if (decode_cb_) {
std::move(decode_cb_).Run(status);
}
}
void OnDoneDecoding(DecodeStatus status) {
DCHECK(is_used_);
is_used_ = false;
CallDecodeCallbackIfAny(status);
}
private:
CodecBuffer buffer_;
// Set to true when this buffer is being used by the codec.
bool is_used_ = false;
// Decode callback for the DecodeBuffer of which this InputBuffer is a part.
// This is only set on the final InputBuffer in each DecodeBuffer.
VideoDecoder::DecodeCB decode_cb_;
DISALLOW_COPY_AND_ASSIGN(InputBuffer);
};
// Output buffer used to pass decoded frames from the decoder. Ref-counted
// to make it possible to share the buffers with VideoFrames, in case when a
// frame outlives the decoder.UnsafeSharedMemoryRegion
class OutputBuffer : public base::RefCountedThreadSafe<OutputBuffer> {
public:
OutputBuffer() = default;
bool Initialize(const fuchsia::media::StreamBufferConstraints& constraints) {
if (!buffer_.Initialize(constraints)) {
return false;
}
zx_status_t status = zx::vmar::root_self()->map(
/*vmar_offset=*/0, buffer_.vmo(), 0, buffer_.size(),
ZX_VM_REQUIRE_NON_RESIZABLE | ZX_VM_PERM_READ, &mapped_memory_);
if (status != ZX_OK) {
ZX_DLOG(ERROR, status) << "zx_vmar_map";
mapped_memory_ = 0;
return false;
}
return true;
}
CodecBuffer& buffer() { return buffer_; }
const uint8_t* mapped_memory() {
DCHECK(mapped_memory_);
return reinterpret_cast<uint8_t*>(mapped_memory_);
}
private:
friend class RefCountedThreadSafe<OutputBuffer>;
~OutputBuffer() {
if (mapped_memory_) {
zx_status_t status =
zx::vmar::root_self()->unmap(mapped_memory_, buffer_.size());
if (status != ZX_OK) {
ZX_LOG(FATAL, status) << "zx_vmar_unmap";
}
}
}
CodecBuffer buffer_;
uintptr_t mapped_memory_ = 0;
DISALLOW_COPY_AND_ASSIGN(OutputBuffer);
};
} // namespace
class FuchsiaVideoDecoder : public VideoDecoder {
public:
explicit FuchsiaVideoDecoder(bool enable_sw_decoding);
~FuchsiaVideoDecoder() override;
// VideoDecoder implementation.
std::string GetDisplayName() const override;
void Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) override;
void Reset(const base::Closure& closure) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
private:
// Event handlers for |codec_|.
void OnStreamFailed(uint64_t stream_lifetime_ordinal);
void OnInputConstraints(
fuchsia::media::StreamBufferConstraints input_constraints);
void OnFreeInputPacket(fuchsia::media::PacketHeader free_input_packet);
void OnOutputConstraints(
fuchsia::media::StreamOutputConstraints output_constraints);
void OnOutputFormat(fuchsia::media::StreamOutputFormat output_format);
void OnOutputPacket(fuchsia::media::Packet output_packet,
bool error_detected_before,
bool error_detected_during);
void OnOutputEndOfStream(uint64_t stream_lifetime_ordinal,
bool error_detected_before);
void OnError();
// Called by OnInputConstraints() to initialize input buffers.
bool InitializeInputBuffers(
fuchsia::media::StreamBufferConstraints constraints);
// Pumps |pending_decodes_| to the decoder.
void PumpInput();
// Called by OnInputConstraints() to initialize input buffers.
bool InitializeOutputBuffers(
fuchsia::media::StreamBufferConstraints constraints);
// Destruction callback for the output VideoFrame instances.
void OnFrameDestroyed(scoped_refptr<OutputBuffer> buffer,
uint64_t buffer_lifetime_ordinal,
uint32_t packet_index);
const bool enable_sw_decoding_;
OutputCB output_cb_;
// Aspect ratio specified in container, or 1.0 if it's not specified. This
// value is used only if the aspect ratio is not specified in the bitstream.
float container_pixel_aspect_ratio_ = 1.0;
fuchsia::media::StreamProcessorPtr codec_;
uint64_t stream_lifetime_ordinal_ = 1;
// Set to true if we've sent an input packet with the current
// stream_lifetime_ordinal_.
bool active_stream_ = false;
std::list<PendingDecode> pending_decodes_;
uint64_t input_buffer_lifetime_ordinal_ = 1;
std::vector<InputBuffer> input_buffers_;
int num_used_input_buffers_ = 0;
fuchsia::media::VideoUncompressedFormat output_format_;
uint64_t output_buffer_lifetime_ordinal_ = 1;
std::vector<scoped_refptr<OutputBuffer>> output_buffers_;
int num_used_output_buffers_ = 0;
int max_used_output_buffers_ = 0;
// Non-null when flush is pending.
VideoDecoder::DecodeCB pending_flush_cb_;
base::WeakPtr<FuchsiaVideoDecoder> weak_this_;
base::WeakPtrFactory<FuchsiaVideoDecoder> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FuchsiaVideoDecoder);
};
FuchsiaVideoDecoder::FuchsiaVideoDecoder(bool enable_sw_decoding)
: enable_sw_decoding_(enable_sw_decoding), weak_factory_(this) {
weak_this_ = weak_factory_.GetWeakPtr();
}
FuchsiaVideoDecoder::~FuchsiaVideoDecoder() = default;
std::string FuchsiaVideoDecoder::GetDisplayName() const {
return "FuchsiaVideoDecoder";
}
void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) {
output_cb_ = output_cb;
container_pixel_aspect_ratio_ = config.GetPixelAspectRatio();
auto done_callback = BindToCurrentLoop(init_cb);
fuchsia::mediacodec::CreateDecoder_Params codec_params;
codec_params.mutable_input_details()->set_format_details_version_ordinal(0);
switch (config.codec()) {
case kCodecH264:
codec_params.mutable_input_details()->set_mime_type("video/h264");
break;
case kCodecVP8:
codec_params.mutable_input_details()->set_mime_type("video/vp8");
break;
case kCodecVP9:
codec_params.mutable_input_details()->set_mime_type("video/vp9");
break;
case kCodecHEVC:
codec_params.mutable_input_details()->set_mime_type("video/hevc");
break;
case kCodecAV1:
codec_params.mutable_input_details()->set_mime_type("video/av1");
break;
default:
done_callback.Run(false);
return;
}
codec_params.set_promise_separate_access_units_on_input(true);
codec_params.set_require_hw(!enable_sw_decoding_);
auto codec_factory =
base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
->ConnectToService<fuchsia::mediacodec::CodecFactory>();
codec_factory->CreateDecoder(std::move(codec_params), codec_.NewRequest());
codec_.set_error_handler(
[this](zx_status_t status) {
ZX_LOG(ERROR, status)
<< "The fuchsia.mediacodec.Codec channel was terminated.";
OnError();
});
codec_.events().OnStreamFailed =
fit::bind_member(this, &FuchsiaVideoDecoder::OnStreamFailed);
codec_.events().OnInputConstraints =
fit::bind_member(this, &FuchsiaVideoDecoder::OnInputConstraints);
codec_.events().OnFreeInputPacket =
fit::bind_member(this, &FuchsiaVideoDecoder::OnFreeInputPacket);
codec_.events().OnOutputConstraints =
fit::bind_member(this, &FuchsiaVideoDecoder::OnOutputConstraints);
codec_.events().OnOutputFormat =
fit::bind_member(this, &FuchsiaVideoDecoder::OnOutputFormat);
codec_.events().OnOutputPacket =
fit::bind_member(this, &FuchsiaVideoDecoder::OnOutputPacket);
codec_.events().OnOutputEndOfStream =
fit::bind_member(this, &FuchsiaVideoDecoder::OnOutputEndOfStream);
codec_->EnableOnStreamFailed();
done_callback.Run(true);
}
void FuchsiaVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) {
DCHECK_LT(static_cast<int>(pending_decodes_.size()) + num_used_input_buffers_,
GetMaxDecodeRequests());
if (!codec_) {
// Post the callback to the current sequence as DecoderStream doesn't expect
// Decode() to complete synchronously.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(decode_cb, DecodeStatus::DECODE_ERROR));
return;
}
pending_decodes_.push_back(PendingDecode(buffer, decode_cb));
PumpInput();
}
void FuchsiaVideoDecoder::Reset(const base::Closure& closure) {
// Call DecodeCB(ABORTED) for all active decode requests.
for (auto& buffer : input_buffers_) {
buffer.CallDecodeCallbackIfAny(DecodeStatus::ABORTED);
}
// Will call DecodeCB(ABORTED) for all pending decode requests.
pending_decodes_.clear();
if (active_stream_) {
codec_->CloseCurrentStream(stream_lifetime_ordinal_,
/*release_input_buffers=*/false,
/*release_output_buffers=*/false);
stream_lifetime_ordinal_ += 2;
active_stream_ = false;
}
BindToCurrentLoop(closure).Run();
}
bool FuchsiaVideoDecoder::NeedsBitstreamConversion() const {
return true;
}
bool FuchsiaVideoDecoder::CanReadWithoutStalling() const {
return num_used_output_buffers_ < max_used_output_buffers_;
}
int FuchsiaVideoDecoder::GetMaxDecodeRequests() const {
// Add one extra request to be able to send new InputBuffer immediately after
// OnFreeInputPacket().
return input_buffers_.size() + 1;
}
void FuchsiaVideoDecoder::OnStreamFailed(uint64_t stream_lifetime_ordinal) {
if (stream_lifetime_ordinal_ != stream_lifetime_ordinal) {
return;
}
OnError();
}
void FuchsiaVideoDecoder::OnInputConstraints(
fuchsia::media::StreamBufferConstraints input_constraints) {
if (!InitializeInputBuffers(std::move(input_constraints))) {
DLOG(ERROR) << "Failed to initialize input buffers.";
OnError();
return;
}
PumpInput();
}
void FuchsiaVideoDecoder::OnFreeInputPacket(
fuchsia::media::PacketHeader free_input_packet) {
if (!free_input_packet.has_buffer_lifetime_ordinal() ||
!free_input_packet.has_packet_index()) {
DLOG(ERROR) << "Received OnFreeInputPacket() with missing required fields.";
OnError();
return;
}
if (free_input_packet.buffer_lifetime_ordinal() !=
input_buffer_lifetime_ordinal_) {
return;
}
if (free_input_packet.packet_index() >= input_buffers_.size()) {
DLOG(ERROR) << "fuchsia.mediacodec sent OnFreeInputPacket() for an unknown "
"packet: buffer_lifetime_ordinal="
<< free_input_packet.buffer_lifetime_ordinal()
<< " packet_index=" << free_input_packet.packet_index();
OnError();
return;
}
DCHECK_GT(num_used_input_buffers_, 0);
num_used_input_buffers_--;
input_buffers_[free_input_packet.packet_index()].OnDoneDecoding(
DecodeStatus::OK);
// Try to pump input in case it was blocked.
PumpInput();
}
void FuchsiaVideoDecoder::OnOutputConstraints(
fuchsia::media::StreamOutputConstraints output_constraints) {
if (!output_constraints.has_stream_lifetime_ordinal()) {
DLOG(ERROR) << "Received OnOutputConstraints() with missing required "
"fields.";
OnError();
return;
}
if (output_constraints.stream_lifetime_ordinal() !=
stream_lifetime_ordinal_) {
return;
}
if (output_constraints.has_buffer_constraints_action_required() &&
output_constraints.buffer_constraints_action_required()) {
if (!output_constraints.has_buffer_constraints()) {
DLOG(ERROR) << "Received OnOutputConstraints() which requires buffer "
"constraints action, but without buffer constraints.";
OnError();
return;
}
if (!InitializeOutputBuffers(
std::move(*output_constraints.mutable_buffer_constraints()))) {
DLOG(ERROR) << "Failed to initialize output buffers.";
OnError();
return;
}
}
}
void FuchsiaVideoDecoder::OnOutputFormat(
fuchsia::media::StreamOutputFormat output_format) {
if (!output_format.has_stream_lifetime_ordinal() ||
!output_format.has_format_details()) {
DLOG(ERROR) << "Received OnOutputFormat() with missing required fields.";
OnError();
return;
}
if (output_format.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
return;
}
auto* format = output_format.mutable_format_details();
if (!format->has_domain() || !format->domain().is_video() ||
!format->domain().video().is_uncompressed()) {
DLOG(ERROR) << "Received OnOutputFormat() with invalid format.";
OnError();
return;
}
output_format_ = std::move(format->mutable_domain()->video().uncompressed());
}
void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet,
bool error_detected_before,
bool error_detected_during) {
if (!output_packet.has_header() ||
!output_packet.header().has_buffer_lifetime_ordinal() ||
!output_packet.header().has_packet_index() ||
!output_packet.has_buffer_index()) {
DLOG(ERROR) << "Received OnOutputPacket() with missing required fields.";
OnError();
return;
}
if (output_packet.header().buffer_lifetime_ordinal() !=
output_buffer_lifetime_ordinal_) {
return;
}
auto coded_size = gfx::Size(output_format_.primary_width_pixels,
output_format_.primary_height_pixels);
base::Optional<VideoFrameLayout> layout;
switch (output_format_.fourcc) {
case libyuv::FOURCC_NV12:
layout = VideoFrameLayout::CreateWithPlanes(
PIXEL_FORMAT_NV12, coded_size,
std::vector<VideoFrameLayout::Plane>{
VideoFrameLayout::Plane(output_format_.primary_line_stride_bytes,
output_format_.primary_start_offset),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.secondary_start_offset)});
DCHECK(layout);
break;
case libyuv::FOURCC_YV12:
layout = VideoFrameLayout::CreateWithPlanes(
PIXEL_FORMAT_YV12, coded_size,
std::vector<VideoFrameLayout::Plane>{
VideoFrameLayout::Plane(output_format_.primary_line_stride_bytes,
output_format_.primary_start_offset),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.secondary_start_offset),
VideoFrameLayout::Plane(
output_format_.secondary_line_stride_bytes,
output_format_.tertiary_start_offset),
});
DCHECK(layout);
break;
default:
LOG(ERROR) << "unknown fourcc: "
<< std::string(reinterpret_cast<char*>(&output_format_.fourcc),
4);
}
if (!layout) {
codec_->RecycleOutputPacket(fidl::Clone(output_packet.header()));
return;
}
base::TimeDelta timestamp;
if (output_packet.has_timestamp_ish()) {
timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
}
auto packet_index = output_packet.header().packet_index();
auto buffer_index = output_packet.buffer_index();
auto& buffer = output_buffers_[buffer_index];
// We're not using single buffer mode, so packet count will be equal to buffer
// count.
DCHECK_LT(num_used_output_buffers_, static_cast<int>(output_buffers_.size()));
num_used_output_buffers_++;
float pixel_aspect_ratio;
if (output_format_.has_pixel_aspect_ratio) {
pixel_aspect_ratio =
static_cast<float>(output_format_.pixel_aspect_ratio_width) /
static_cast<float>(output_format_.pixel_aspect_ratio_height);
} else {
pixel_aspect_ratio = container_pixel_aspect_ratio_;
}
auto display_rect = gfx::Rect(output_format_.primary_display_width_pixels,
output_format_.primary_display_height_pixels);
// TODO(sergeyu): Create ReadOnlySharedMemoryRegion for the VMO and pass
// it to the frame.
auto frame = VideoFrame::WrapExternalDataWithLayout(
*layout, display_rect, GetNaturalSize(display_rect, pixel_aspect_ratio),
const_cast<uint8_t*>(buffer->mapped_memory()) +
output_format_.primary_start_offset,
buffer->buffer().size() - output_format_.primary_start_offset, timestamp);
// Pass a reference to the buffer to the destruction callback to ensure it's
// not destroyed while the frame is being used.
frame->AddDestructionObserver(BindToCurrentLoop(
base::BindOnce(&FuchsiaVideoDecoder::OnFrameDestroyed, weak_this_, buffer,
output_buffer_lifetime_ordinal_, packet_index)));
output_cb_.Run(std::move(frame));
}
void FuchsiaVideoDecoder::OnOutputEndOfStream(uint64_t stream_lifetime_ordinal,
bool error_detected_before) {
if (stream_lifetime_ordinal != stream_lifetime_ordinal_) {
return;
}
stream_lifetime_ordinal_ += 2;
active_stream_ = false;
std::move(pending_flush_cb_).Run(DecodeStatus::OK);
}
void FuchsiaVideoDecoder::OnError() {
codec_.Unbind();
auto weak_this = weak_this_;
// Call all decode callback with DECODE_ERROR before clearing input_buffers_
// and pending_decodes_. Otherwise PendingDecode and InputBuffer destructors
// would the callbacks with ABORTED.
for (auto& buffer : input_buffers_) {
if (buffer.is_used()) {
buffer.OnDoneDecoding(DecodeStatus::DECODE_ERROR);
// DecodeCB(DECODE_ERROR) may destroy |this|.
if (!weak_this) {
return;
}
}
}
for (auto& pending_decode : pending_decodes_) {
pending_decode.TakeDecodeCallback().Run(DecodeStatus::DECODE_ERROR);
if (!weak_this) {
return;
}
}
pending_decodes_.clear();
num_used_input_buffers_ = 0;
input_buffers_.clear();
num_used_output_buffers_ = 0;
output_buffers_.clear();
}
bool FuchsiaVideoDecoder::InitializeInputBuffers(
fuchsia::media::StreamBufferConstraints constraints) {
input_buffer_lifetime_ordinal_ += 2;
if (!constraints.has_default_settings() ||
!constraints.default_settings().has_packet_count_for_server() ||
!constraints.default_settings().has_packet_count_for_client()) {
DLOG(ERROR)
<< "Received InitializeInputBuffers() with missing required fields.";
OnError();
return false;
}
auto settings = fidl::Clone(constraints.default_settings());
settings.set_buffer_lifetime_ordinal(input_buffer_lifetime_ordinal_);
codec_->SetInputBufferSettings(fidl::Clone(settings));
int total_buffers =
settings.packet_count_for_server() + settings.packet_count_for_client();
std::vector<InputBuffer> new_buffers(total_buffers);
for (int i = 0; i < total_buffers; ++i) {
fuchsia::media::StreamBuffer codec_buffer;
if (!new_buffers[i].Initialize(constraints) ||
!new_buffers[i].buffer().ToFidlCodecBuffer(
input_buffer_lifetime_ordinal_, i, /*read_only=*/true,
&codec_buffer)) {
return false;
}
codec_->AddInputBuffer(std::move(codec_buffer));
}
num_used_input_buffers_ = 0;
input_buffers_ = std::move(new_buffers);
return true;
}
void FuchsiaVideoDecoder::PumpInput() {
// Nothing to do if a codec error has occurred or input buffers have not been
// initialized (which happens in response to OnInputConstraints() event).
if (!codec_ || input_buffers_.empty())
return;
while (!pending_decodes_.empty()) {
// Decode() is not supposed to be called while Decode(EOS) is pending.
DCHECK(!pending_flush_cb_);
if (pending_decodes_.front().buffer().end_of_stream()) {
active_stream_ = true;
codec_->QueueInputEndOfStream(stream_lifetime_ordinal_);
codec_->FlushEndOfStreamAndCloseStream(stream_lifetime_ordinal_);
pending_flush_cb_ = pending_decodes_.front().TakeDecodeCallback();
pending_decodes_.pop_front();
continue;
}
DCHECK_LE(num_used_input_buffers_, static_cast<int>(input_buffers_.size()));
if (num_used_input_buffers_ == static_cast<int>(input_buffers_.size())) {
// No input buffer available.
return;
}
auto input_buffer =
std::find_if(input_buffers_.begin(), input_buffers_.end(),
[](const InputBuffer& buf) { return !buf.is_used(); });
CHECK(input_buffer != input_buffers_.end());
num_used_input_buffers_++;
size_t bytes_filled =
input_buffer->FillFromDecodeBuffer(&pending_decodes_.front());
fuchsia::media::Packet packet;
packet.mutable_header()->set_buffer_lifetime_ordinal(
input_buffer_lifetime_ordinal_);
packet.mutable_header()->set_packet_index(input_buffer -
input_buffers_.begin());
packet.set_buffer_index(packet.header().packet_index());
packet.set_timestamp_ish(
pending_decodes_.front().buffer().timestamp().InNanoseconds());
packet.set_stream_lifetime_ordinal(stream_lifetime_ordinal_);
packet.set_start_offset(0);
packet.set_valid_length_bytes(bytes_filled);
active_stream_ = true;
codec_->QueueInputPacket(std::move(packet));
if (pending_decodes_.front().bytes_left() == 0) {
pending_decodes_.pop_front();
}
}
}
bool FuchsiaVideoDecoder::InitializeOutputBuffers(
fuchsia::media::StreamBufferConstraints constraints) {
if (!constraints.has_default_settings() ||
!constraints.has_packet_count_for_client_max() ||
!constraints.default_settings().has_packet_count_for_server() ||
!constraints.default_settings().has_packet_count_for_client()) {
DLOG(ERROR)
<< "Received InitializeOutputBuffers() with missing required fields.";
OnError();
return false;
}
// mediacodec API expects odd buffer lifetime ordinal, which is incremented by
// 2 for each buffer generation.
output_buffer_lifetime_ordinal_ += 2;
auto settings = fidl::Clone(constraints.default_settings());
settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
max_used_output_buffers_ =
std::min(kMaxUsedOutputFrames, constraints.packet_count_for_client_max());
settings.set_packet_count_for_client(max_used_output_buffers_);
codec_->SetOutputBufferSettings(fidl::Clone(settings));
int total_buffers =
settings.packet_count_for_server() + settings.packet_count_for_client();
std::vector<scoped_refptr<OutputBuffer>> new_buffers(total_buffers);
for (int i = 0; i < total_buffers; ++i) {
fuchsia::media::StreamBuffer codec_buffer;
new_buffers[i] = new OutputBuffer();
if (!new_buffers[i]->Initialize(constraints) ||
!new_buffers[i]->buffer().ToFidlCodecBuffer(
output_buffer_lifetime_ordinal_, i, /*read_only=*/false,
&codec_buffer)) {
return false;
}
codec_->AddOutputBuffer(std::move(codec_buffer));
}
num_used_output_buffers_ = 0;
output_buffers_ = std::move(new_buffers);
return true;
}
void FuchsiaVideoDecoder::OnFrameDestroyed(scoped_refptr<OutputBuffer> buffer,
uint64_t buffer_lifetime_ordinal,
uint32_t packet_index) {
if (!codec_)
return;
if (buffer_lifetime_ordinal == output_buffer_lifetime_ordinal_) {
DCHECK_GT(num_used_output_buffers_, 0);
num_used_output_buffers_--;
fuchsia::media::PacketHeader header;
header.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
header.set_packet_index(packet_index);
codec_->RecycleOutputPacket(std::move(header));
}
}
std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoder() {
return std::make_unique<FuchsiaVideoDecoder>(/*enable_sw_decoding=*/false);
}
std::unique_ptr<VideoDecoder> CreateFuchsiaVideoDecoderForTests(
bool enable_sw_decoding) {
return std::make_unique<FuchsiaVideoDecoder>(enable_sw_decoding);
}
} // namespace media