libheif/plugins/decoder_dav1d.cc (222 lines of code) (raw):
/*
* AVIF codec.
* Copyright (c) 2020 Dirk Farin <dirk.farin@gmail.com>
*
* This file is part of libheif.
*
* libheif is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* libheif is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libheif. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libheif/heif.h"
#include "libheif/heif_plugin.h"
#include "security_limits.h"
#include "common_utils.h"
#include "decoder_dav1d.h"
#include <memory>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <dav1d/version.h>
#include <dav1d/dav1d.h>
struct dav1d_decoder
{
Dav1dSettings settings;
Dav1dContext* context;
Dav1dData data;
bool strict_decoding = false;
};
static const char kEmptyString[] = "";
static const char kSuccess[] = "Success";
static const int DAV1D_PLUGIN_PRIORITY = 150;
#define MAX_PLUGIN_NAME_LENGTH 80
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
static const char* dav1d_plugin_name()
{
snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "dav1d v%d.%d.%d",
DAV1D_API_VERSION_MAJOR,
DAV1D_API_VERSION_MINOR,
DAV1D_API_VERSION_PATCH);
// make sure that the string is null-terminated
plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
return plugin_name;
}
static void dav1d_init_plugin()
{
}
static void dav1d_deinit_plugin()
{
}
static int dav1d_does_support_format(enum heif_compression_format format)
{
if (format == heif_compression_AV1) {
return DAV1D_PLUGIN_PRIORITY;
}
else {
return 0;
}
}
struct heif_error dav1d_new_decoder(void** dec, int nthreads)
{
auto* decoder = new dav1d_decoder();
dav1d_default_settings(&decoder->settings);
decoder->settings.frame_size_limit = MAX_IMAGE_SIZE;
decoder->settings.all_layers = 0;
if (dav1d_open(&decoder->context, &decoder->settings) != 0) {
struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, kSuccess};
return err;
}
memset(&decoder->data, 0, sizeof(Dav1dData));
*dec = decoder;
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
void dav1d_free_decoder(void* decoder_raw)
{
auto* decoder = (dav1d_decoder*) decoder_raw;
if (!decoder) {
return;
}
if (decoder->data.sz) {
dav1d_data_unref(&decoder->data);
}
if (decoder->context) {
dav1d_close(&decoder->context);
}
delete decoder;
}
void dav1d_set_strict_decoding(void* decoder_raw, int flag)
{
struct dav1d_decoder* decoder = (dav1d_decoder*) decoder_raw;
decoder->strict_decoding = flag;
}
struct heif_error dav1d_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
{
auto* decoder = (struct dav1d_decoder*) decoder_raw;
assert(decoder->data.sz == 0);
uint8_t* d = dav1d_data_create(&decoder->data, frame_size);
if (d == nullptr) {
struct heif_error err = {heif_error_Memory_allocation_error, heif_suberror_Unspecified, kSuccess};
return err;
}
memcpy(d, frame_data, frame_size);
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
struct heif_error dav1d_decode_image(void* decoder_raw, struct heif_image** out_img)
{
auto* decoder = (struct dav1d_decoder*) decoder_raw;
struct heif_error err;
Dav1dPicture frame;
memset(&frame, 0, sizeof(Dav1dPicture));
bool flushed = false;
for (;;) {
int res = dav1d_send_data(decoder->context, &decoder->data);
if ((res < 0) && (res != DAV1D_ERR(EAGAIN))) {
err = {heif_error_Decoder_plugin_error,
heif_suberror_Unspecified,
kEmptyString};
return err;
}
res = dav1d_get_picture(decoder->context, &frame);
if (!flushed && res == DAV1D_ERR(EAGAIN)) {
if (decoder->data.sz == 0) {
flushed = true;
}
continue;
}
else if (res < 0) {
err = {heif_error_Decoder_plugin_error,
heif_suberror_Unspecified,
kEmptyString};
return err;
}
else {
break;
}
}
heif_chroma chroma;
heif_colorspace colorspace;
switch (frame.p.layout) {
case DAV1D_PIXEL_LAYOUT_I420:
chroma = heif_chroma_420;
colorspace = heif_colorspace_YCbCr;
break;
case DAV1D_PIXEL_LAYOUT_I422:
chroma = heif_chroma_422;
colorspace = heif_colorspace_YCbCr;
break;
case DAV1D_PIXEL_LAYOUT_I444:
chroma = heif_chroma_444;
colorspace = heif_colorspace_YCbCr;
break;
case DAV1D_PIXEL_LAYOUT_I400:
chroma = heif_chroma_monochrome;
colorspace = heif_colorspace_monochrome;
break;
default: {
err = {heif_error_Decoder_plugin_error,
heif_suberror_Unspecified,
kEmptyString};
return err;
}
}
struct heif_image* heif_img = nullptr;
err = heif_image_create(frame.p.w, frame.p.h,
colorspace,
chroma,
&heif_img);
if (err.code != heif_error_Ok) {
assert(heif_img == nullptr);
return err;
}
// --- read nclx parameters from decoded AV1 bitstream
heif_color_profile_nclx nclx;
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_color_primaries(&nclx, static_cast<uint16_t>(frame.seq_hdr->pri)), {});
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_transfer_characteristics(&nclx, static_cast<uint16_t>(frame.seq_hdr->trc)), {});
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_matrix_coefficients(&nclx, static_cast<uint16_t>(frame.seq_hdr->mtrx)), {});
nclx.full_range_flag = (frame.seq_hdr->color_range != 0);
heif_image_set_nclx_color_profile(heif_img, &nclx);
// --- transfer data from Dav1dPicture to HeifPixelImage
heif_channel channel2plane[3] = {
heif_channel_Y,
heif_channel_Cb,
heif_channel_Cr
};
// --- copy image data
int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
for (int c = 0; c < num_planes; c++) {
int bpp = frame.p.bpc;
const uint8_t* data = (uint8_t*) frame.data[c];
int stride = (int) frame.stride[c > 0 ? 1 : 0];
int w, h;
get_subsampled_size(frame.p.w, frame.p.h,
channel2plane[c], chroma, &w, &h);
err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp);
if (err.code != heif_error_Ok) {
heif_image_release(heif_img);
return err;
}
int dst_stride;
uint8_t* dst_mem = heif_image_get_plane(heif_img, channel2plane[c], &dst_stride);
int bytes_per_pixel = (bpp + 7) / 8;
for (int y = 0; y < h; y++) {
memcpy(dst_mem + y * dst_stride, data + y * stride, w * bytes_per_pixel);
}
}
dav1d_picture_unref(&frame);
*out_img = heif_img;
err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
static const struct heif_decoder_plugin decoder_dav1d
{
3,
dav1d_plugin_name,
dav1d_init_plugin,
dav1d_deinit_plugin,
dav1d_does_support_format,
dav1d_new_decoder,
dav1d_free_decoder,
dav1d_push_data,
dav1d_decode_image,
dav1d_set_strict_decoding,
"dav1d"
};
const struct heif_decoder_plugin* get_decoder_plugin_dav1d()
{
return &decoder_dav1d;
}
#if PLUGIN_DAV1D
heif_plugin_info plugin_info {
1,
heif_plugin_type_decoder,
&decoder_dav1d
};
#endif