Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [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