libheif/plugins/decoder_vvdec.cc (244 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_vvdec.h"
#include <cstring>
#include <cassert>
#include <vector>
#include <algorithm>
#include <vvdec/vvdec.h>
#if 0
#include <iostream>
#include <logging.h>
#endif
struct vvdec_decoder
{
vvdecDecoder* decoder = nullptr;
vvdecAccessUnit* au = nullptr;
bool strict_decoding = false;
std::vector<std::vector<uint8_t>> nalus;
};
static const char kSuccess[] = "Success";
static const int VVDEC_PLUGIN_PRIORITY = 100;
#define MAX_PLUGIN_NAME_LENGTH 80
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
static const char* vvdec_plugin_name()
{
const char* version = vvdec_get_version();
if (strlen(version) < 60) {
sprintf(plugin_name, "VVCDEC decoder (%s)", version);
}
else {
strcpy(plugin_name, "VVDEC decoder");
}
return plugin_name;
}
static void vvdec_init_plugin()
{
}
static void vvdec_deinit_plugin()
{
}
static int vvdec_does_support_format(enum heif_compression_format format)
{
if (format == heif_compression_VVC) {
return VVDEC_PLUGIN_PRIORITY;
}
else {
return 0;
}
}
struct heif_error vvdec_new_decoder(void** dec, int nthreads)
{
auto* decoder = new vvdec_decoder();
vvdecParams params;
vvdec_params_default(¶ms);
params.logLevel = VVDEC_INFO;
decoder->decoder = vvdec_decoder_open(¶ms);
const int MaxNaluSize = 256 * 1024;
decoder->au = vvdec_accessUnit_alloc();
vvdec_accessUnit_default(decoder->au);
vvdec_accessUnit_alloc_payload(decoder->au, MaxNaluSize);
*dec = decoder;
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
void vvdec_free_decoder(void* decoder_raw)
{
auto* decoder = (vvdec_decoder*) decoder_raw;
if (!decoder) {
return;
}
if (decoder->au) {
vvdec_accessUnit_free(decoder->au);
decoder->au = nullptr;
}
if (decoder->decoder) {
vvdec_decoder_close(decoder->decoder);
decoder->decoder = nullptr;
}
delete decoder;
}
void vvdec_set_strict_decoding(void* decoder_raw, int flag)
{
auto* decoder = (vvdec_decoder*) decoder_raw;
decoder->strict_decoding = flag;
}
struct heif_error vvdec_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
{
auto* decoder = (struct vvdec_decoder*) decoder_raw;
const auto* data = (const uint8_t*) frame_data;
for (;;) {
uint32_t size = ((((uint32_t) data[0]) << 24) |
(((uint32_t) data[1]) << 16) |
(((uint32_t) data[2]) << 8) |
(data[3]));
data += 4;
std::vector<uint8_t> nalu;
nalu.push_back(0);
nalu.push_back(0);
nalu.push_back(1);
nalu.insert(nalu.end(), data, data + size);
decoder->nalus.push_back(nalu);
data += size;
frame_size -= 4 + size;
if (frame_size == 0) {
break;
}
}
struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
return err;
}
struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_img)
{
auto* decoder = (struct vvdec_decoder*) decoder_raw;
vvdecFrame* frame = nullptr;
// --- prepare AU payload with maximum NALU size
size_t max_payload_size = 0;
for (const auto& nalu : decoder->nalus) {
max_payload_size = std::max(max_payload_size, nalu.size());
}
if (decoder->au == nullptr || max_payload_size > (size_t) decoder->au->payloadSize) {
if (decoder->au) {
vvdec_accessUnit_free(decoder->au);
}
decoder->au = vvdec_accessUnit_alloc();
vvdec_accessUnit_default(decoder->au);
vvdec_accessUnit_alloc_payload(decoder->au, (int)max_payload_size);
}
// --- feed NALUs into decoder, flush when done
for (int i = 0;; i++) {
int ret;
if (i < (int) decoder->nalus.size()) {
const auto& nalu = decoder->nalus[i];
memcpy(decoder->au->payload, nalu.data(), nalu.size());
decoder->au->payloadUsedSize = (int) nalu.size();
ret = vvdec_decode(decoder->decoder, decoder->au, &frame);
}
else {
ret = vvdec_flush(decoder->decoder, &frame);
}
if (ret != VVDEC_OK && ret != VVDEC_EOF && ret != VVDEC_TRY_AGAIN) {
return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "vvdec decoding error"};
}
if (frame) {
break;
}
if (ret == VVDEC_EOF) {
return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "no frame decoded"};
}
}
decoder->nalus.clear();
// --- convert decoded frame to heif_image
heif_chroma chroma;
heif_colorspace colorspace;
if (frame->colorFormat == VVDEC_CF_YUV400_PLANAR) {
chroma = heif_chroma_monochrome;
colorspace = heif_colorspace_monochrome;
}
else {
if (frame->colorFormat == VVDEC_CF_YUV444_PLANAR) {
chroma = heif_chroma_444;
}
else if (frame->colorFormat == VVDEC_CF_YUV422_PLANAR) {
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((int)frame->width,
(int)frame->height,
colorspace,
chroma,
&heif_img);
if (err.code != heif_error_Ok) {
assert(heif_img == nullptr);
return err;
}
// --- read nclx parameters from decoded AV1 bitstream
#if 0
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);
#endif
// --- transfer data from vvdecFrame 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 = (int)frame->bitDepth;
const auto& plane = frame->planes[c];
const uint8_t* data = plane.ptr;
int stride = (int)plane.stride;
int w = (int)plane.width;
int h = (int)plane.height;
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);
}
#if 0
std::cout << "DATA " << c << " " << w << " " << h << " bpp:" << bpp << "\n";
std::cout << write_raw_data_as_hex(dst_mem, w*h, {}, {});
std::cout << "---\n";
#endif
}
*out_img = heif_img;
vvdec_frame_unref(decoder->decoder, frame);
return err;
}
static const struct heif_decoder_plugin decoder_vvdec
{
3,
vvdec_plugin_name,
vvdec_init_plugin,
vvdec_deinit_plugin,
vvdec_does_support_format,
vvdec_new_decoder,
vvdec_free_decoder,
vvdec_push_data,
vvdec_decode_image,
vvdec_set_strict_decoding,
"vvdec"
};
const struct heif_decoder_plugin* get_decoder_plugin_vvdec()
{
return &decoder_vvdec;
}
#if PLUGIN_VVDEC
heif_plugin_info plugin_info {
1,
heif_plugin_type_decoder,
&decoder_vvdec
};
#endif