From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id F1C164C89F for ; Wed, 6 Aug 2025 10:52:33 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id D493B68C356; Wed, 6 Aug 2025 13:52:29 +0300 (EEST) Received: from 264e01bdf0e5 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 1C74F68C1FB for ; Wed, 6 Aug 2025 13:52:28 +0300 (EEST) MIME-Version: 1.0 From: dkozinski To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] =?utf-8?q?=5BPATCH=5D_avcodec/oapv=5Fdecoder=3A_P?= =?utf-8?q?rovided_support_for_libopenapv_APV_decoder_=28PR_=2320133=29?= X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Message-Id: <20250806105229.D493B68C356@ffbox0-bg.ffmpeg.org> Date: Wed, 6 Aug 2025 13:52:29 +0300 (EEST) Archived-At: List-Archive: List-Post: 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 >From 7154a577236798c4719d8ea563b9ce04e654f15d Mon Sep 17 00:00:00 2001 From: Dawid Kozinski 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 --- 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 + * + * 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 + +#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; ipayload_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; iframes_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".