* [FFmpeg-devel] [PATCH] avcodec/oapv_decoder: Provided support for libopenapv APV decoder (PR #20133)
@ 2025-08-06 10:52 dkozinski
0 siblings, 0 replies; only message in thread
From: dkozinski @ 2025-08-06 10:52 UTC (permalink / raw)
To: ffmpeg-devel
PR #20133 opened by dkozinski
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20133
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20133.patch
- Added APV decoder wrapper
- Changes in project configuration file and libavcodec Makefile
- Added documentation for APV decoder wrapper
Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
From 7154a577236798c4719d8ea563b9ce04e654f15d Mon Sep 17 00:00:00 2001
From: Dawid Kozinski <d.kozinski@samsung.com>
Date: Wed, 6 Aug 2025 12:41:37 +0200
Subject: [PATCH] avcodec/oapv_decoder: Provided support for libopenapv APV
decoder
- Added APV decoder wrapper
- Changes in project configuration file and libavcodec Makefile
- Added documentation for APV decoder wrapper
Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com>
---
configure | 1 +
doc/decoders.texi | 27 ++
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/liboapvdec.c | 913 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 943 insertions(+)
create mode 100644 libavcodec/liboapvdec.c
diff --git a/configure b/configure
index d5e0bb6936..11f449d9ee 100755
--- a/configure
+++ b/configure
@@ -3612,6 +3612,7 @@ libmodplug_demuxer_deps="libmodplug"
libmp3lame_encoder_deps="libmp3lame"
libmp3lame_encoder_select="audio_frame_queue mpegaudioheader"
liboapv_encoder_deps="liboapv"
+liboapv_decoder_deps="liboapv"
libopencore_amrnb_decoder_deps="libopencore_amrnb"
libopencore_amrnb_encoder_deps="libopencore_amrnb"
libopencore_amrnb_encoder_select="audio_frame_queue"
diff --git a/doc/decoders.texi b/doc/decoders.texi
index 13eb40f4dd..265011f472 100644
--- a/doc/decoders.texi
+++ b/doc/decoders.texi
@@ -397,6 +397,33 @@ without this library.
@chapter Subtitles Decoders
@c man begin SUBTITLES DECODERS
+@section liboapv
+
+Advanced Professional Video codec decoder wrapper.
+
+This decoder requires the presence of the liboapv headers and library
+during configuration. You need to explicitly configure the build with
+@option{--enable-liboapv}.
+
+@float NOTE
+Many liboapv decoder options are mapped to FFmpeg global codec options,
+while unique decoder options are provided through private options.
+Additionally the apv-params private options allows one to pass a list
+of key=value tuples as accepted by the liboapv @code{parse_apv_params} function.
+@end float
+
+The apv project website is at @url{https://github.com/AcademySoftwareFoundation/openapv}.
+
+@subsection Options
+
+The following options are supported by the liboapv wrapper.
+The apv-equivalent options or values are listed in parentheses for easy migration.
+
+@float NOTE
+To get a more accurate and extensive documentation of the liboapv options,
+invoke the command @code{apv_app_dec --help} or consult the liboapv documentation.
+@end float
+
@section libaribb24
ARIB STD-B24 caption decoder.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index f935cfbe0d..db7a65ef04 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1161,6 +1161,7 @@ OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o
OBJS-$(CONFIG_LIBLC3_DECODER) += liblc3dec.o
OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o
OBJS-$(CONFIG_LIBOAPV_ENCODER) += liboapvenc.o
+OBJS-$(CONFIG_LIBOAPV_DECODER) += liboapvdec.o
OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o
OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o
OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER) += libopencore-amr.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 3aaa351157..ac0162c75f 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -790,6 +790,7 @@ extern const FFCodec ff_liblc3_encoder;
extern const FFCodec ff_liblc3_decoder;
extern const FFCodec ff_libmp3lame_encoder;
extern const FFCodec ff_liboapv_encoder;
+extern const FFCodec ff_liboapv_decoder;
extern const FFCodec ff_libopencore_amrnb_encoder;
extern const FFCodec ff_libopencore_amrnb_decoder;
extern const FFCodec ff_libopencore_amrwb_decoder;
diff --git a/libavcodec/liboapvdec.c b/libavcodec/liboapvdec.c
new file mode 100644
index 0000000000..497ba43bd8
--- /dev/null
+++ b/libavcodec/liboapvdec.c
@@ -0,0 +1,913 @@
+/*
+ * APV (Advanced Professional Video) decoder using Open APV library (liboapv)
+ *
+ * Copyright (C) 2025 Dawid Kozinski <d.kozinski@samsung.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <oapv/oapv.h>
+
+#include "libavutil/mastering_display_metadata.h"
+#include "libavutil/mem.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/container_fifo.h"
+
+#include "codec_internal.h"
+#include "profiles.h"
+#include "decode.h"
+#include "cbs_apv.h"
+
+#define OAPV_IMG_CLIP_VAL(n, min, max) (((n) > (max)) ? (max) : (((n) < (min)) ? (min) : (n)))
+
+/**
+ * The structure stores all the states associated with the instance of Open APV decoder
+ */
+typedef struct ApvDecContext {
+ const AVClass *class;
+
+ oapvd_t id; // apvd instance identifier @see apvd_t.h
+ oapvd_cdesc_t cdsc; // decoding parameters @see apvd_t.h
+
+ oapvm_t mid; // OAPV metadata container
+
+ int output_depth;
+
+ struct AVContainerFifo *output_fifo;
+
+ AVFrame* frames[OAPV_MAX_NUM_FRAMES];
+ uint32_t frames_count; // primary frames
+
+ APVRawMetadataPayload payloads[CBS_APV_MAX_METADATA_PAYLOADS];
+ uint32_t metadata_count;
+
+ AVPacket *pkt; // frame data
+} ApvDecContext;
+
+static int apv_imgb_release(oapv_imgb_t *imgb)
+{
+ int refcnt = --imgb->refcnt;
+ if (refcnt == 0) {
+ for (int i = 0; i < imgb->np; i++)
+ av_freep(&imgb->baddr[i]);
+ av_freep(&imgb);
+ }
+
+ return refcnt;
+}
+
+static int apv_imgb_addref(oapv_imgb_t * imgb)
+{
+ int refcnt = ++imgb->refcnt;
+ return refcnt;
+}
+
+static int apv_imgb_getref(oapv_imgb_t * imgb)
+{
+ return imgb->refcnt;
+}
+
+static void apv_imgb_cpy_plane(oapv_imgb_t *dst, oapv_imgb_t *src)
+{
+ int i, j;
+ unsigned char *s, *d;
+ int numbyte = OAPV_CS_GET_BYTE_DEPTH(src->cs);
+
+ for(i = 0; i < src->np; i++) {
+ s = (unsigned char *)src->a[i];
+ d = (unsigned char *)dst->a[i];
+
+ for(j = 0; j < src->ah[i]; j++) {
+ memcpy(d, s, numbyte * src->aw[i]);
+ s += src->s[i];
+ d += dst->s[i];
+ }
+ }
+}
+
+static void apv_imgb_cpy_shift_left_8b(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
+{
+ int i, j, k;
+
+ unsigned char *s;
+ short *d;
+
+ for(i = 0; i < dst->np; i++) {
+ s = (unsigned char *)src->a[i];
+ d = (short *)dst->a[i];
+
+ for(j = 0; j < src->ah[i]; j++) {
+ for(k = 0; k < src->aw[i]; k++) {
+ d[k] = (short)(s[k] << shift);
+ }
+ s = s + src->s[i];
+ d = (short *)(((unsigned char *)d) + dst->s[i]);
+ }
+ }
+}
+
+static void apv_imgb_cpy_shift_right_8b(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
+{
+ int i, j, k, t0, add;
+
+ short *s;
+ unsigned char *d;
+
+ if(shift)
+ add = 1 << (shift - 1);
+ else
+ add = 0;
+
+ for(i = 0; i < dst->np; i++) {
+ s = (short *)src->a[i];
+ d = (unsigned char *)dst->a[i];
+
+ for(j = 0; j < src->ah[i]; j++) {
+ for(k = 0; k < src->aw[i]; k++) {
+ t0 = ((s[k] + add) >> shift);
+ d[k] = (unsigned char)(OAPV_IMG_CLIP_VAL(t0, 0, 255));
+ }
+ s = (short *)(((unsigned char *)s) + src->s[i]);
+ d = d + dst->s[i];
+ }
+ }
+}
+
+static void apv_imgb_cpy_shift_left(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
+{
+ int i, j, k;
+
+ unsigned short *s;
+ unsigned short *d;
+
+ for(i = 0; i < dst->np; i++) {
+ s = (unsigned short *)src->a[i];
+ d = (unsigned short *)dst->a[i];
+
+ for(j = 0; j < src->h[i]; j++) {
+ for(k = 0; k < src->w[i]; k++) {
+ d[k] = (unsigned short)(s[k] << shift);
+ }
+ s = (unsigned short *)(((unsigned char *)s) + src->s[i]);
+ d = (unsigned short *)(((unsigned char *)d) + dst->s[i]);
+ }
+ }
+}
+
+static void apv_imgb_cpy_shift_right(oapv_imgb_t *dst, oapv_imgb_t *src, int shift)
+{
+ int i, j, k, t0, add;
+
+ int clip_min = 0;
+ int clip_max = 0;
+
+ unsigned short *s;
+ unsigned short *d;
+
+ if(shift)
+ add = 1 << (shift - 1);
+ else
+ add = 0;
+
+ clip_max = (1 << (OAPV_CS_GET_BIT_DEPTH(dst->cs))) - 1;
+
+ for(i = 0; i < dst->np; i++) {
+ s = (unsigned short *)src->a[i];
+ d = (unsigned short *)dst->a[i];
+
+ for(j = 0; j < src->h[i]; j++) {
+ for(k = 0; k < src->w[i]; k++) {
+ t0 = ((s[k] + add) >> shift);
+ d[k] = (OAPV_IMG_CLIP_VAL(t0, clip_min, clip_max));
+ }
+ s = (unsigned short *)(((unsigned char *)s) + src->s[i]);
+ d = (unsigned short *)(((unsigned char *)d) + dst->s[i]);
+ }
+ }
+}
+
+static void apv_imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src, AVCodecContext *avctx)
+{
+ int i, bd_src, bd_dst;
+ bd_src = OAPV_CS_GET_BIT_DEPTH(src->cs);
+ bd_dst = OAPV_CS_GET_BIT_DEPTH(dst->cs);
+
+ if(src->cs == dst->cs) {
+ apv_imgb_cpy_plane(dst, src);
+ }
+ else if(bd_src == 8 && bd_dst > 8) {
+ apv_imgb_cpy_shift_left_8b(dst, src, bd_dst - bd_src);
+ }
+ else if(bd_src > 8 && bd_dst == 8) {
+ apv_imgb_cpy_shift_right_8b(dst, src, bd_src - bd_dst);
+ }
+ else if(bd_src < bd_dst) {
+ apv_imgb_cpy_shift_left(dst, src, bd_dst - bd_src);
+ }
+ else if(bd_src > bd_dst) {
+ apv_imgb_cpy_shift_right(dst, src, bd_src - bd_dst);
+ }
+ else {
+ av_log(avctx, AV_LOG_ERROR, "ERROR: unsupported image copy\n");
+ return;
+ }
+ for(i = 0; i < OAPV_MAX_CC; i++) {
+ dst->x[i] = src->x[i];
+ dst->y[i] = src->y[i];
+ dst->w[i] = src->w[i];
+ dst->h[i] = src->h[i];
+ dst->ts[i] = src->ts[i];
+ }
+}
+
+static oapv_imgb_t *apv_imgb_create(int w, int h, int cs, AVCodecContext *avctx)
+{
+ oapv_imgb_t *imgb = NULL;
+
+ imgb = av_mallocz(sizeof(oapv_imgb_t));
+ if (!imgb)
+ goto fail;
+
+ memset(imgb, 0, sizeof(oapv_imgb_t));
+
+ if(OAPV_CS_GET_BIT_DEPTH(cs)!=10 && OAPV_CS_GET_BIT_DEPTH(cs)!=12) {
+ av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n");
+ goto fail;
+ }
+
+ imgb->w[0] = w;
+ imgb->h[0] = h;
+
+ switch(OAPV_CS_GET_FORMAT(cs))
+ {
+ case OAPV_CF_YCBCR422: // profile 33 (10bit), profile 44 (12bit)
+ imgb->w[1] = imgb->w[2] = (w + 1) >> 1;
+ imgb->h[1] = imgb->h[2] = h;
+ imgb->np = 3;
+ break;
+ case OAPV_CF_YCBCR444: // profile 55 (10bit), profile 66 (12bit)
+ imgb->w[1] = imgb->w[2] = w;
+ imgb->h[1] = imgb->h[2] = h;
+ imgb->np = 3;
+ break;
+ case OAPV_CF_YCBCR4444: // profile 77 (10bit), profile 88 (12bit)
+ imgb->w[1] = imgb->w[2] = imgb->w[3] = w;
+ imgb->h[1] = imgb->h[2] = imgb->h[3] = h;
+ imgb->np = 4;
+ break;
+ case OAPV_CF_YCBCR400: // profile 99 (10bit)
+ imgb->w[1] = imgb->w[2] = w;
+ imgb->h[1] = imgb->h[2] = h;
+ imgb->np = 1;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format\n");
+ goto fail;
+ }
+
+ for(int i = 0; i < imgb->np; i++)
+ {
+ imgb->aw[i] = FFALIGN(imgb->w[i], OAPV_MB_W);
+ imgb->ah[i] = FFALIGN(imgb->h[i], OAPV_MB_H);
+ imgb->s[i] = imgb->aw[i] * OAPV_CS_GET_BYTE_DEPTH(cs);
+ imgb->e[i] = imgb->ah[i];
+
+ imgb->bsize[i] = imgb->s[i] * imgb->e[i];
+ imgb->a[i] = imgb->baddr[i] = av_mallocz(imgb->bsize[i]);
+ if (imgb->a[i] == NULL)
+ goto fail;
+
+ memset(imgb->a[i], 0, imgb->bsize[i]);
+ }
+ imgb->cs = cs;
+ imgb->addref = apv_imgb_addref;
+ imgb->getref = apv_imgb_getref;
+ imgb->release = apv_imgb_release;
+
+ imgb->addref(imgb); /* increase reference count */
+ return imgb;
+
+fail:
+ av_log(avctx, AV_LOG_ERROR, "Cannot create image buffer\n");
+
+ if (imgb) {
+ for (int i = 0; i < imgb->np; i++)
+ av_freep(&imgb->a[i]);
+ av_freep(&imgb);
+ }
+ return NULL;
+}
+
+/**
+ * @brief The function populates the cdsc
+ * apvd_cdsc contains all decoder parameters that should be initialized before its use.
+ *
+ * @param[in] avctx codec context
+ * @param[out] cdsc contains all decoder parameters that should be initialized before its use
+ *
+ */
+static void get_conf(AVCodecContext *avctx, oapvd_cdesc_t *cdsc)
+{
+ /* clear apvd_cdsc structure */
+ memset(cdsc, 0, sizeof(oapvd_cdesc_t));
+
+ /* init apvd_cdsc structure */
+ cdsc->threads = OAPV_CDESC_THREADS_AUTO;
+}
+
+/**
+ * @brief The function populates avctx fields based on information from frame
+ *
+ * @param[out] avctx codec context
+ * @param[in] frame stores information of bitstream
+ */
+static void export_stream_params(AVCodecContext *avctx, const AVFrame* frame)
+{
+ avctx->width = frame->width;
+ avctx->height = frame->height;
+
+ avctx->pix_fmt = frame->format;
+
+ avctx->color_primaries = frame->color_primaries;
+ avctx->color_trc = frame->color_trc;
+ avctx->colorspace = frame->colorspace;
+ avctx->color_range = frame->color_range;
+}
+
+/**
+ * @brief The function populates frame based on information from frm_info
+ *
+ * @param avctx codec context
+ * @param[out] frame
+ * @param[in] frm_info
+ * @return 0 on success, negative value on failure
+ */
+static int set_frame_metadata(AVCodecContext *avctx, AVFrame* frame, const oapv_frm_info_t* frm_info)
+{
+ frame->width = frm_info->w;
+ frame->height = frm_info->h;
+
+ switch(frm_info->cs) {
+ case OAPV_CS_SET(OAPV_CF_YCBCR422, 10, 0): // profile 33
+ frame->format = AV_PIX_FMT_YUV422P10LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR422, 10, 1):
+ frame->format = AV_PIX_FMT_YUV422P10BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR422, 12, 0): // profile 44
+ frame->format = AV_PIX_FMT_YUV422P12LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR422, 12, 1):
+ frame->format = AV_PIX_FMT_YUV422P12BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR444, 10, 0): // profile 55
+ frame->format = AV_PIX_FMT_YUV444P10LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR444, 10, 1):
+ frame->format = AV_PIX_FMT_YUV444P10BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR444, 12, 0): // profile 66
+ frame->format = AV_PIX_FMT_YUV444P12LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR444, 12, 1):
+ frame->format = AV_PIX_FMT_YUV444P12BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR4444, 10, 0): // profile 77
+ frame->format = AV_PIX_FMT_YUVA444P10LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR4444, 10, 1):
+ frame->format = AV_PIX_FMT_YUVA444P10BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR4444, 12, 0): // profile 88
+ frame->format = AV_PIX_FMT_YUVA444P12LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR4444, 12, 1):
+ frame->format = AV_PIX_FMT_YUVA444P12BE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR400, 10, 0): // profile 99
+ frame->format = AV_PIX_FMT_GRAY10LE;
+ break;
+ case OAPV_CS_SET(OAPV_CF_YCBCR400, 10, 1):
+ frame->format = AV_PIX_FMT_GRAY10BE;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Unknown color space\n");
+ frame->format = AV_PIX_FMT_NONE;
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (frm_info->color_description_present_flag) {
+ frame->color_primaries = frm_info->color_primaries;
+ frame->color_trc = frm_info->transfer_characteristics;
+ frame->colorspace = frm_info->matrix_coefficients;
+ frame->color_range = frm_info->full_range_flag ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
+ }
+ return 0;
+}
+
+/**
+ * @brief The function copies image data from imgb into frame
+ *
+ * @param avctx codec context
+ * @param[out] frame
+ * @param[in] imgb
+ * @return 0 on success, negative value on failure
+ */
+static int set_frame_data(AVCodecContext *avctx, AVFrame *frame, const oapv_imgb_t *imgb)
+{
+ int ret;
+
+ if (imgb->w[0] != avctx->width || imgb->h[0] != avctx->height) { // stream resolution changed
+ if (ff_set_dimensions(avctx, imgb->w[0], imgb->h[0]) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot set new dimension\n");
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ if (ret = ff_get_buffer(avctx, frame, 0) < 0)
+ return ret;
+
+ av_image_copy(frame->data, frame->linesize, (const uint8_t **)imgb->a,
+ imgb->s, avctx->pix_fmt,
+ imgb->w[0], imgb->h[0]);
+
+ return 0;
+}
+
+static int decode_metadata(AVCodecContext *avctx, AVFrame *frame, const APVRawMetadataPayload *payloads, uint32_t metadata_count)
+{
+ int err;
+ for(int i=0; i<metadata_count; i++) {
+ const APVRawMetadataPayload *pld = &payloads[i];
+ switch (pld->payload_type) {
+ case APV_METADATA_MDCV: {
+ const APVRawMetadataMDCV *mdcv = &pld->mdcv;
+ AVMasteringDisplayMetadata *mdm;
+
+ err = ff_decode_mastering_display_new(avctx, frame, &mdm);
+ if (err < 0)
+ return err;
+
+ if (mdm) {
+ for (int j = 0; j < 3; j++) {
+ mdm->display_primaries[j][0] =
+ av_make_q(mdcv->primary_chromaticity_x[j], 1 << 16);
+ mdm->display_primaries[j][1] =
+ av_make_q(mdcv->primary_chromaticity_y[j], 1 << 16);
+ }
+
+ mdm->white_point[0] =
+ av_make_q(mdcv->white_point_chromaticity_x, 1 << 16);
+ mdm->white_point[1] =
+ av_make_q(mdcv->white_point_chromaticity_y, 1 << 16);
+
+ mdm->max_luminance =
+ av_make_q(mdcv->max_mastering_luminance, 1 << 8);
+ mdm->min_luminance =
+ av_make_q(mdcv->min_mastering_luminance, 1 << 14);
+
+ mdm->has_primaries = 1;
+ mdm->has_luminance = 1;
+ }
+ }
+ break;
+ case APV_METADATA_CLL:
+ {
+ const APVRawMetadataCLL *cll = &pld->cll;
+ AVContentLightMetadata *clm;
+
+ err = ff_decode_content_light_new(avctx, frame, &clm);
+ if (err < 0)
+ return err;
+
+ if (clm) {
+ clm->MaxCLL = cll->max_cll;
+ clm->MaxFALL = cll->max_fall;
+ }
+ }
+ break;
+ case APV_METADATA_ITU_T_T35:
+ case APV_METADATA_USER_DEFINED:
+ {
+ av_log(avctx, AV_LOG_WARNING, "Not supported metadata type\n");
+ }
+ break;
+ default:
+ // Ignore other types of metadata.
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Initialize OpenAPV decoder
+ * Create a decoder instance and allocate all the needed resources
+ *
+ * @param avctx codec context
+ * @return 0 on success, negative error code on failure
+ */
+static av_cold int liboapvd_init(AVCodecContext *avctx)
+{
+ ApvDecContext *apvctx = avctx->priv_data;
+ oapvd_cdesc_t *cdsc = &(apvctx->cdsc);
+ int ret = 0;
+
+ /* read configurations from AVCodecContext and populate the apvd_cdsc structure */
+ get_conf(avctx, cdsc);
+
+ /* create decoder instance */
+ apvctx->id = oapvd_create(&(apvctx->cdsc), NULL);
+ if (apvctx->id == NULL) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot create oapv decoder\n");
+ return AVERROR_EXTERNAL;
+ }
+
+ /* create metadata container */
+ apvctx->mid = oapvm_create(&ret);
+ if(OAPV_FAILED(ret)) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot create oapv metadata container (err=%d)\n", ret);
+ return AVERROR_EXTERNAL;
+ }
+
+ apvctx->pkt = av_packet_alloc();
+
+ // Allocate an AVContainerFifo instance for AVFrames
+ apvctx->output_fifo = av_container_fifo_alloc_avframe(0);
+ if (!apvctx->output_fifo)
+ return AVERROR(ENOMEM);
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(apvctx->frames); i++) {
+ apvctx->frames[i] = NULL;
+ }
+
+ apvctx->frames_count = 0;
+
+ return 0;
+}
+
+/**
+ * Decode frame with decoupled packet/frame dataflow
+ *
+ * @param avctx codec context
+ * @param[out] frame decoded frame
+ *
+ * @return 0 on success, negative error code on failure
+ */
+static int liboapvd_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+ ApvDecContext *apvctx = avctx->priv_data;
+ AVPacket *pkt = apvctx->pkt;
+ int ret = 0;
+
+ uint8_t *bs_buf = NULL;
+ uint32_t bs_buf_size = 0;
+
+ oapvd_stat_t stat;
+ oapv_bitb_t bitb;
+ oapv_frms_t ofrms;
+ oapv_imgb_t *imgb_w = NULL;
+ oapv_imgb_t *imgb_o = NULL;
+ oapv_frm_t *frm = NULL;
+
+ oapv_au_info_t aui;
+ oapv_frm_info_t *finfo = NULL;
+
+ AVPacket* pkt_fd; // encoded frame data
+
+ if (av_container_fifo_can_read(apvctx->output_fifo))
+ goto do_output;
+
+ for(int i =0; i<apvctx->frames_count;i++ ) {
+ av_frame_unref(apvctx->frames[i]);
+ apvctx->frames_count = 0;
+ }
+
+ // frame data (input data) - AU
+ ret = ff_decode_get_packet(avctx, pkt);
+ if (ret < 0 && ret != AVERROR_EOF) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+
+ if (pkt->size <= 0) {
+ av_packet_unref(pkt);
+ return ret;
+ }
+
+ memset(&ofrms, 0, sizeof(oapv_frms_t));
+ memset(&aui, 0, sizeof(oapv_au_info_t));
+
+ pkt_fd = av_packet_clone(pkt);
+ av_packet_unref(pkt);
+
+ bs_buf = pkt_fd->data;
+ bs_buf_size = pkt_fd->size;
+
+ if (OAPV_FAILED(oapvd_info(bs_buf, bs_buf_size, &aui)))
+ {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitstream\n");
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+
+ /* create decoding frame buffers */
+ ofrms.num_frms = aui.num_frms;
+ for(int i = 0; i < ofrms.num_frms; i++) {
+
+ finfo = &aui.frm_info[i];
+
+ ofrms.frm[i].imgb = apv_imgb_create(finfo->w, finfo->h, finfo->cs, avctx);
+ if(ofrms.frm[i].imgb == NULL) {
+ av_log(avctx, AV_LOG_ERROR, "cannot allocate image buffer (w:%d, h:%d, cs:%d)\n",
+ finfo->w, finfo->h, finfo->cs);
+
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ }
+
+ if(apvctx->output_depth == 0) {
+ apvctx->output_depth = OAPV_CS_GET_BIT_DEPTH(finfo->cs);
+ }
+
+ /* main decoding block */
+ bitb.addr = bs_buf;
+ bitb.ssize = bs_buf_size;
+ memset(&stat, 0, sizeof(oapvd_stat_t));
+
+ ret = oapvd_decode(apvctx->id, &bitb, &ofrms, apvctx->mid, &stat);
+ if(OAPV_FAILED(ret)) {
+ av_log(avctx, AV_LOG_ERROR,"failed to decode bitstream\n");
+
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ if(stat.read != bs_buf_size) {
+ av_log(avctx, AV_LOG_ERROR,"\t=> different reading of bitstream (in:%d, read:%d)\n",
+ bs_buf_size, stat.read);
+ }
+
+ if(apvctx->mid) {
+ oapvm_payload_t *pld = NULL; // metadata payload
+ int num_plds = 0; // number of metadata payload
+
+ ret = oapvm_get_all(apvctx->mid, NULL, &num_plds);
+
+ if(OAPV_FAILED(ret)) {
+ av_log(avctx, AV_LOG_ERROR,"failed to read metadata\n");
+
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ if(num_plds > 0) {
+ pld = av_mallocz(sizeof(oapvm_payload_t) * num_plds);
+ ret = oapvm_get_all(apvctx->mid, pld, &num_plds);
+ if(OAPV_FAILED(ret)) {
+ av_log(avctx, AV_LOG_ERROR,"failed to read metadata\n");
+
+ if(pld != NULL)
+ av_freep(&pld);
+
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ for(int i = 0; i < num_plds; i++) {
+ oapvm_payload_t *oapv_pld = &pld[i];
+
+ APVRawMetadataPayload pld = apvctx->payloads[i];
+ pld.payload_type = oapv_pld->type;
+ pld.payload_size = oapv_pld->size;
+
+ apvctx->metadata_count = num_plds;
+
+ switch(oapv_pld->type) {
+ case OAPV_METADATA_ITU_T_T35:
+ {
+ memcpy(&pld.itu_t_t35, oapv_pld->data, oapv_pld->size);
+ break;
+ }
+ case OAPV_METADATA_MDCV:
+ {
+ memcpy(&pld.mdcv, oapv_pld->data, oapv_pld->size);
+ break;
+ }
+ case OAPV_METADATA_CLL:
+ {
+ memcpy(&pld.cll, oapv_pld->data, oapv_pld->size);
+ break;
+ }
+ case OAPV_METADATA_FILLER:
+ {
+ memcpy(&pld.filler, oapv_pld->data, oapv_pld->size);
+ break;
+ }
+ case OAPV_METADATA_USER_DEFINED:
+ {
+ memcpy(&pld.user_defined, oapv_pld->data, oapv_pld->size);
+ break;
+ }
+ default:
+ // Ignore other types of metadata.
+ break;
+ }
+ }
+ }
+
+ if(pld != NULL)
+ av_freep(&pld);
+ }
+
+ /* Write decoded frames into AVFrame objects */
+ for(int i = 0, j = 0; i < ofrms.num_frms; i++) {
+
+ frm = &ofrms.frm[i];
+ if(frm->pbu_type != OAPV_PBU_TYPE_PRIMARY_FRAME) {
+ av_log(avctx, AV_LOG_WARNING,
+ "Stream contains additional non-primary frames "
+ "which will be ignored by the decoder.\n");
+ } else {
+ // if the bit depth of the encoded frame is different than the requested bit depth of the output stream frames,
+ // set by the output_depth option
+ if(OAPV_CS_GET_BIT_DEPTH(frm->imgb->cs) != apvctx->output_depth) {
+ if(imgb_w == NULL) {
+ imgb_w = apv_imgb_create(frm->imgb->w[0], frm->imgb->h[0],
+ OAPV_CS_SET(OAPV_CS_GET_FORMAT(frm->imgb->cs), apvctx->output_depth, 0), avctx);
+ if(imgb_w == NULL) {
+ av_log(avctx, AV_LOG_ERROR,"cannot allocate image buffer (w:%d, h:%d, cs:%d)\n",
+ frm->imgb->w[0], frm->imgb->h[0], frm->imgb->cs);
+
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ }
+ }
+ apv_imgb_cpy(imgb_w, frm->imgb, avctx);
+
+ imgb_o = imgb_w;
+ }
+ else {
+ imgb_o = frm->imgb;
+ }
+
+ if(apvctx->frames[j] == NULL) {
+ apvctx->frames[j] = av_frame_alloc();
+ }
+
+ /* Set frame info into AVFrame object */
+ ret = set_frame_metadata(avctx, apvctx->frames[j], &stat.aui.frm_info[i]);
+ if(ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Frame info setting error\n");
+ av_frame_unref(apvctx->frames[j]);
+
+ goto end;
+ }
+
+ /* Copy decoded image into AVFrame object */
+ ret = set_frame_data(avctx, apvctx->frames[j], imgb_o);
+ if(ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Image copying error\n");
+ av_frame_unref(apvctx->frames[j]);
+
+ goto end;
+ }
+
+ /* Use ff_decode_frame_props_from_pkt() to fill frame properties */
+ ret = ff_decode_frame_props_from_pkt(avctx, apvctx->frames[j], pkt_fd);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "ff_decode_frame_props_from_pkt error\n");
+ av_frame_unref(apvctx->frames[j]);
+
+ goto end;
+ }
+
+ /* set metadata */
+ ret = decode_metadata(avctx, apvctx->frames[j], apvctx->payloads, apvctx->metadata_count);
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "decode_metadata error\n");
+ av_frame_unref(apvctx->frames[j]);
+
+ goto end;
+ }
+
+ if (pkt_fd->flags & AV_PKT_FLAG_KEY) {
+ apvctx->frames[j]->pict_type = AV_PICTURE_TYPE_I;
+ apvctx->frames[j]->flags |= AV_FRAME_FLAG_KEY;
+ }
+
+ apvctx->frames_count++;
+
+ /* Write the AVFrame data to the FIFO */
+ ret = av_container_fifo_write(apvctx->output_fifo, apvctx->frames[j], AV_CONTAINER_FIFO_FLAG_REF);
+ j++;
+ }
+ }
+
+end:
+ av_packet_unref(pkt_fd);
+
+ for(int i = 0; i < ofrms.num_frms; i++) {
+ if(ofrms.frm[i].imgb != NULL) {
+ ofrms.frm[i].imgb->release(ofrms.frm[i].imgb);
+ ofrms.frm[i].imgb = NULL;
+ }
+ }
+ if (imgb_w) {
+ imgb_w->release(imgb_w);
+ imgb_w = NULL;
+ }
+
+ imgb_o = NULL;
+
+ if (av_container_fifo_can_read(apvctx->output_fifo))
+ goto do_output;
+
+ return AVERROR(EAGAIN);
+
+do_output:
+ /* Read the next available object from the FIFO into frame */
+ if ((ret = av_container_fifo_read(apvctx->output_fifo, frame, 0)) >= 0) {
+ export_stream_params(avctx, frame);
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Destroy decoder
+ *
+ * @param avctx codec context
+ * @return 0 on success
+ */
+static av_cold int liboapvd_close(AVCodecContext *avctx)
+{
+ ApvDecContext *apvctx = avctx->priv_data;
+ if (apvctx->id) {
+ oapvd_delete(apvctx->id);
+ apvctx->id = NULL;
+ }
+
+ if (apvctx->mid) {
+ oapvm_rem_all(apvctx->mid);
+ oapvm_delete(apvctx->mid);
+ apvctx->mid = NULL;
+ }
+
+ av_packet_free(&apvctx->pkt);
+
+ av_container_fifo_free(&apvctx->output_fifo);
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(apvctx->frames); i++) {
+ av_frame_free(&apvctx->frames[i]);
+ }
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(ApvDecContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption liboapvd_options[] = {
+ { "output_depth", "Output depth", OFFSET(output_depth), AV_OPT_TYPE_INT, { .i64 = 10 }, 10, 12, VD, .unit = "output_depth" },
+ { "10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 10 }, INT_MIN, INT_MAX, VD, .unit = "output_depth" },
+ { "12", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 12 }, INT_MIN, INT_MAX, VD, .unit = "output_depth" },
+ { NULL }
+};
+
+static const AVClass liboapvd_class = {
+ .class_name = "liboapv",
+ .item_name = av_default_item_name,
+ .option = liboapvd_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_liboapv_decoder = {
+ .p.name = "liboapv",
+ .p.long_name = NULL_IF_CONFIG_SMALL("liboapv APV"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_APV,
+ .init = liboapvd_init,
+ FF_CODEC_RECEIVE_FRAME_CB(liboapvd_receive_frame),
+ .close = liboapvd_close,
+ .priv_data_size = sizeof(ApvDecContext),
+ .p.priv_class = &liboapvd_class,
+ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_AVOID_PROBING,
+ .p.wrapper_name = "liboapv",
+ .p.profiles = NULL_IF_CONFIG_SMALL(ff_apv_profiles),
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_SETS_FRAME_PROPS
+};
\ No newline at end of file
--
2.49.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-08-06 10:52 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-06 10:52 [FFmpeg-devel] [PATCH] avcodec/oapv_decoder: Provided support for libopenapv APV decoder (PR #20133) dkozinski
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git