blob: 91aa426eecf19f0cf4b5fcdd495f9893b4e4876b [file] [edit]
// Copyright 2024 The Chromium Authors
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "av1_decoder_delegate.h"
#include "dav1d/dav1d.h"
#include "fake_buffer.h"
#include "fake_surface.h"
#include "libyuv.h"
namespace libvafake {
namespace {
// std::unique_ptr release helpers. We need to release both the containing
// structs as well as refs held within the structures.
struct ScopedDav1dDataFree {
void operator()(void* x) const {
auto* data = static_cast<Dav1dData*>(x);
dav1d_data_unref(data);
delete data;
}
};
struct ScopedDav1dPictureFree {
void operator()(void* x) const {
auto* pic = static_cast<Dav1dPicture*>(x);
dav1d_picture_unref(pic);
delete pic;
}
};
void NullFreeCallback(const uint8_t* buffer, void* opaque) {}
} // namespace
void Av1DecoderDelegate::Dav1dContextDeleter::operator()(Dav1dContext* ptr) {
dav1d_close(&ptr);
}
Av1DecoderDelegate::Av1DecoderDelegate(VAProfile profile) {
CHECK_EQ(profile, VAProfileAV1Profile0);
Dav1dSettings settings;
dav1d_default_settings(&settings);
settings.max_frame_delay = 1;
Dav1dContext* temp_david_context_pointer = nullptr;
CHECK_EQ(dav1d_open(&temp_david_context_pointer, &settings), 0);
dav1d_context_ = std::unique_ptr<Dav1dContext, Dav1dContextDeleter>(
temp_david_context_pointer);
}
Av1DecoderDelegate::~Av1DecoderDelegate() {
dav1d_context_.reset();
}
void Av1DecoderDelegate::SetRenderTarget(const FakeSurface& surface) {
render_target_ = &surface;
}
void Av1DecoderDelegate::EnqueueWork(
const std::vector<const FakeBuffer*>& buffers) {
CHECK(render_target_);
CHECK(!encoded_data_buffer_);
for (auto buffer : buffers) {
if (buffer->GetType() == VASliceDataBufferType) {
encoded_data_buffer_ = buffer;
return;
}
}
}
void Av1DecoderDelegate::Run() {
CHECK(render_target_);
CHECK(encoded_data_buffer_);
using ScopedPtrDav1dData = std::unique_ptr<Dav1dData, ScopedDav1dDataFree>;
ScopedPtrDav1dData input_buffer(new Dav1dData{0});
// TODO(bchoobineh): Currently this wrap logic does not account for
// superframes which would require specifying a data offset and updated
// data size. Will need to find/compute the data offset/size from data
// passed by Chrome to support these bitstreams.
CHECK_EQ(
dav1d_data_wrap(input_buffer.get(),
static_cast<uint8_t*>(encoded_data_buffer_->GetData()),
encoded_data_buffer_->GetDataSize(),
/*free_callback=*/&NullFreeCallback,
/*cookie=*/nullptr),
0);
CHECK_EQ(dav1d_send_data(dav1d_context_.get(), input_buffer.get()), 0);
CHECK_EQ(input_buffer->sz, 0u);
using ScopedPtrDav1dPicture =
std::unique_ptr<Dav1dPicture, ScopedDav1dPictureFree>;
ScopedPtrDav1dPicture av1_pic(new Dav1dPicture{0});
CHECK_EQ(dav1d_get_picture(dav1d_context_.get(), av1_pic.get()), 0);
// We currently only support reading from I420 and into NV12.
CHECK_EQ(av1_pic->p.layout, DAV1D_PIXEL_LAYOUT_I420);
CHECK_EQ(render_target_->GetVAFourCC(),
static_cast<uint32_t>(VA_FOURCC_NV12));
const ScopedBOMapping& bo_mapping = render_target_->GetMappedBO();
CHECK(bo_mapping.IsValid());
const ScopedBOMapping::ScopedAccess mapped_bo = bo_mapping.BeginAccess();
const int convert_result = libyuv::I420ToNV12(
/*src_y=*/static_cast<uint8_t*>(av1_pic->data[0]),
/*src_stride_y=*/static_cast<int>(av1_pic->stride[0]),
/*src_u=*/static_cast<uint8_t*>(av1_pic->data[1]),
/*src_stride_u=*/static_cast<int>(av1_pic->stride[1]),
/*src_v=*/static_cast<uint8_t*>(av1_pic->data[2]),
/*src_stride_v=*/static_cast<int>(av1_pic->stride[1]),
/*dst_y=*/mapped_bo.GetData(0),
/*dst_stride_y=*/static_cast<int>(mapped_bo.GetStride(0)),
/*dst_uv=*/mapped_bo.GetData(1),
/*dst_stride_uv=*/static_cast<int>(mapped_bo.GetStride(1)),
/*width=*/av1_pic->p.w,
/*height=*/av1_pic->p.h);
CHECK_EQ(convert_result, 0);
encoded_data_buffer_ = nullptr;
}
} // namespace libvafake