libheif/plugins/decoder_aom.cc (210 lines of code) (raw):
/*
* AVIF codec.
* Copyright (c) 2019 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 "decoder_aom.h"
#include <memory>
#include <cstring>
#include <cassert>
#include <aom/aom_decoder.h>
#include <aom/aomdx.h>
struct aom_decoder
{
aom_codec_ctx_t codec;
bool codec_initialized = false;
aom_codec_iface_t* iface;
bool strict_decoding = false;
};
static const char kSuccess[] = "Success";
static const char kEmptyString[] = "";
static const int AOM_PLUGIN_PRIORITY = 100;
#define MAX_PLUGIN_NAME_LENGTH 80
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
static const char* aom_plugin_name()
{
if (strlen(aom_codec_iface_name(aom_codec_av1_dx())) < MAX_PLUGIN_NAME_LENGTH) {
strcpy(plugin_name, aom_codec_iface_name(aom_codec_av1_dx()));
}
else {
strcpy(plugin_name, "AOMedia AV1 decoder");
}
return plugin_name;
}
static void aom_init_plugin()
{
}
static void aom_deinit_plugin()
{
}
static int aom_does_support_format(enum heif_compression_format format)
{
if (format == heif_compression_AV1) {
return AOM_PLUGIN_PRIORITY;
}
else {
return 0;
}
}
struct heif_error aom_new_decoder(void** dec, int nthreads)
{
struct aom_decoder* decoder = new aom_decoder();
decoder->iface = aom_codec_av1_dx();
aom_codec_err_t aomerr = aom_codec_dec_init(&decoder->codec, decoder->iface, NULL, 0);
if (aomerr) {
*dec = NULL;
delete decoder;
struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
return err;
}
decoder->codec_initialized = true;
*dec = decoder;
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
void aom_free_decoder(void* decoder_raw)
{
struct aom_decoder* decoder = (aom_decoder*) decoder_raw;
if (!decoder) {
return;
}
if (decoder->codec_initialized) {
aom_codec_destroy(&decoder->codec);
decoder->codec_initialized = false;
}
delete decoder;
}
void aom_set_strict_decoding(void* decoder_raw, int flag)
{
struct aom_decoder* decoder = (aom_decoder*) decoder_raw;
decoder->strict_decoding = flag;
}
struct heif_error aom_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
{
struct aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
const char* ver = aom_codec_version_str();
(void)ver;
aom_codec_err_t aomerr;
aomerr = aom_codec_decode(&decoder->codec, (const uint8_t*) frame_data, frame_size, NULL);
if (aomerr) {
struct heif_error err = {heif_error_Invalid_input, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
return err;
}
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
struct heif_error aom_decode_image(void* decoder_raw, struct heif_image** out_img)
{
struct aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
aom_codec_iter_t iter = NULL;
aom_image_t* img = NULL;
img = aom_codec_get_frame(&decoder->codec, &iter);
if (img == NULL) {
struct heif_error err = {heif_error_Decoder_plugin_error,
heif_suberror_Unspecified,
kEmptyString};
return err;
}
if (img->fmt != AOM_IMG_FMT_I420 &&
img->fmt != AOM_IMG_FMT_I42016 &&
img->fmt != AOM_IMG_FMT_I422 &&
img->fmt != AOM_IMG_FMT_I42216 &&
img->fmt != AOM_IMG_FMT_I444 &&
img->fmt != AOM_IMG_FMT_I44416) {
struct heif_error err = {heif_error_Decoder_plugin_error,
heif_suberror_Unsupported_image_type,
kEmptyString};
return err;
}
heif_chroma chroma;
heif_colorspace colorspace;
if (img->monochrome) {
chroma = heif_chroma_monochrome;
colorspace = heif_colorspace_monochrome;
}
else {
if (img->fmt == AOM_IMG_FMT_I444 ||
img->fmt == AOM_IMG_FMT_I44416) {
chroma = heif_chroma_444;
}
else if (img->fmt == AOM_IMG_FMT_I422 ||
img->fmt == AOM_IMG_FMT_I42216) {
chroma = heif_chroma_422;
}
else {
chroma = heif_chroma_420;
}
colorspace = heif_colorspace_YCbCr;
}
struct heif_image* heif_img = nullptr;
struct heif_error err = heif_image_create(img->d_w, img->d_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;
nclx.version = 1;
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_color_primaries(&nclx, static_cast<uint16_t>(img->cp)), { heif_image_release(heif_img); });
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_transfer_characteristics(&nclx, static_cast<uint16_t>(img->tc)), { heif_image_release(heif_img); });
HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_matrix_coefficients(&nclx, static_cast<uint16_t>(img->mc)), { heif_image_release(heif_img); });
nclx.full_range_flag = (img->range == AOM_CR_FULL_RANGE);
heif_image_set_nclx_color_profile(heif_img, &nclx);
// --- transfer data from aom_image_t to HeifPixelImage
heif_channel channel2plane[3] = {
heif_channel_Y,
heif_channel_Cb,
heif_channel_Cr
};
int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
for (int c = 0; c < num_planes; c++) {
int bpp = img->bit_depth;
const uint8_t* data = img->planes[c];
int stride = img->stride[c];
int w = img->d_w;
int h = img->d_h;
if (c > 0 && chroma == heif_chroma_420) {
w = (w + 1) / 2;
h = (h + 1) / 2;
}
else if (c > 0 && chroma == heif_chroma_422) {
w = (w + 1) / 2;
}
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);
}
}
*out_img = heif_img;
return err;
}
static const struct heif_decoder_plugin decoder_aom
{
3,
aom_plugin_name,
aom_init_plugin,
aom_deinit_plugin,
aom_does_support_format,
aom_new_decoder,
aom_free_decoder,
aom_push_data,
aom_decode_image,
aom_set_strict_decoding,
"aom"
};
const struct heif_decoder_plugin* get_decoder_plugin_aom()
{
return &decoder_aom;
}
#if PLUGIN_AOM_DECODER
heif_plugin_info plugin_info {
1,
heif_plugin_type_decoder,
&decoder_aom
};
#endif