in libheif/plugins/decoder_vvdec.cc [176:326]
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;
}