From: James Almer <jamrial@gmail.com> To: ffmpeg-devel@ffmpeg.org Subject: Re: [FFmpeg-devel] [PATCH] Provided support for MPEG-5 EVC (Essential Video Coding) codec Date: Tue, 5 Apr 2022 15:52:32 -0300 Message-ID: <fa125379-9918-eea1-6790-6209553782d1@gmail.com> (raw) In-Reply-To: <006701d8481f$9ccdf900$d669eb00$@samsung.com> On 4/4/2022 9:29 AM, Dawid Kozinski wrote: > diff --git a/Changelog b/Changelog > index 5a32cf0d5c..21ebc11ff4 100644 > --- a/Changelog > +++ b/Changelog > @@ -106,7 +106,8 @@ version 5.0: > - VideoToolbox ProRes encoder > - anlmf audio filter > - IMF demuxer (experimental) > - > +- eXtra-fast Essential Video Encoder (XEVE) > +- eXtra-fast Essential Video Decoder (XEVD) > > version 4.4: > - AudioToolbox output device > diff --git a/configure b/configure > index 7a62f0c248..7491d3af6b 100755 > --- a/configure > +++ b/configure > @@ -289,6 +289,8 @@ External library support: > --enable-libwebp enable WebP encoding via libwebp [no] > --enable-libx264 enable H.264 encoding via x264 [no] > --enable-libx265 enable HEVC encoding via x265 [no] > + --enable-libxeve enable XEVE encoding via xeve [no] > + --enable-libxevd enable XEVD decoding via xevd [no] > --enable-libxavs enable AVS encoding via xavs [no] > --enable-libxavs2 enable AVS2 encoding via xavs2 [no] > --enable-libxcb enable X11 grabbing using XCB [autodetect] > @@ -1880,6 +1882,8 @@ EXTERNAL_LIBRARY_LIST=" > openssl > pocketsphinx > vapoursynth > + libxeve > + libxevd > " > > HWACCEL_AUTODETECT_LIBRARY_LIST=" > @@ -2453,6 +2457,7 @@ CONFIG_EXTRA=" > h264pred > h264qpel > hevcparse > + evcparse This is unused, so remove it from here and below. > hpeldsp > huffman > huffyuvdsp > @@ -3252,6 +3257,7 @@ mpegaudio_parser_select="mpegaudioheader" > mpegvideo_parser_select="mpegvideo" > mpeg4video_parser_select="h263dsp mpegvideo qpeldsp" > vc1_parser_select="vc1dsp" > +evc_parser_select="evcparse" > > # bitstream_filters > aac_adtstoasc_bsf_select="adts_header mpeg4audio" > @@ -3377,6 +3383,8 @@ libx264_encoder_select="atsc_a53" > libx264rgb_encoder_deps="libx264" > libx264rgb_encoder_select="libx264_encoder" > libx265_encoder_deps="libx265" > +libxeve_encoder_deps="libxeve" > +libxevd_decoder_deps="libxevd" > libxavs_encoder_deps="libxavs" > libxavs2_encoder_deps="libxavs2" > libxvid_encoder_deps="libxvid" > @@ -6659,6 +6667,8 @@ enabled libx264 && { check_pkg_config libx264 x264 "stdint.h x264.h" x > check_cpp_condition libx262 x264.h "X264_MPEG2" > enabled libx265 && require_pkg_config libx265 x265 x265.h x265_api_get && > require_cpp_condition libx265 x265.h "X265_BUILD >= 70" > +enabled libxeve && require_pkg_config libxeve "xeve >= 1.0.0" "xeve.h" xeve_encode > +enabled libxevd && require_pkg_config libxevd "xevd >= 1.0.0" "xevd.h" xevd_decode > enabled libxavs && require libxavs "stdint.h xavs.h" xavs_encoder_encode "-lxavs $pthreads_extralibs $libm_extralibs" > enabled libxavs2 && require_pkg_config libxavs2 "xavs2 >= 1.3.0" "stdint.h xavs2.h" xavs2_api_get > enabled libxvid && require libxvid xvid.h xvid_global -lxvidcore [...] > diff --git a/libavcodec/evc_parser.c b/libavcodec/evc_parser.c > new file mode 100644 > index 0000000000..f68c4cc3a0 > --- /dev/null > +++ b/libavcodec/evc_parser.c > @@ -0,0 +1,452 @@ > +/* > + * EVC AVC format parser > + * > + * Copyright (C) 2021 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 "libavutil/common.h" > + > +#include "golomb.h" > +#include "parser.h" > +#include "xevd.h" A parser must not depend on external libraries or headers. > +#include <stdint.h> > + > +#define EVC_NAL_HEADER_SIZE 2 /* byte */ > +#define MAX_SPS_CNT 16 /* defined value in EVC standard */ > + > +typedef struct _EVCParserSPS { > + int sps_id; > + int profile_idc; > + int level_idc; > + int chroma_format_idc; > + int pic_width_in_luma_samples; > + int pic_height_in_luma_samples; > + int bit_depth_luma; > + int bit_depth_chroma; > + > + int picture_cropping_flag; > + int picture_crop_left_offset; > + int picture_crop_right_offset; > + int picture_crop_top_offset; > + int picture_crop_bottom_offset; > +} EVCParserSPS; > + > +typedef struct EVCParserContext { > + ParseContext pc; > + EVCParserSPS sps[MAX_SPS_CNT]; > + int is_avc; > + int nal_length_size; > + int to_read; > + int incomplete_nalu_prefix_read; // The flag is set to 1 when incomplete NAL unit prefix has been read > + > + int parsed_extradata; > + > + int poc; > + int pocTid0; > + > + int got_sps; > + int got_pps; > + int got_sei; > + int got_slice; > +} EVCParserContext; > + > +static int get_nalu_type(const uint8_t *bs, int bs_size) > +{ > + GetBitContext gb; > + int fzb, nut; > + init_get_bits(&gb, bs, bs_size * 8); > + fzb = get_bits1(&gb); > + if(fzb != 0) { > + av_log(NULL, AV_LOG_DEBUG, "forbidden_zero_bit is not clear\n"); > + } > + nut = get_bits(&gb, 6); /* nal_unit_type_plus1 */ > + return nut - 1; > +} > + > +static int get_nalu_type2(const uint8_t *bs, int bs_size) > +{ > + int nalu_type = 0; > + XEVD_INFO info; > + int ret; > + > + if(bs_size>=EVC_NAL_HEADER_SIZE) { > + ret = xevd_info((void*)bs, EVC_NAL_HEADER_SIZE, 1, &info); You will need to implement this parsing in lavc. I recommend you doing it as a CBS module, which can be reused by different components, and will make writing this parser easier. See cbs_h2645.c and so. > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "Cannot get bitstream information\n"); > + return 0; > + } > + nalu_type = info.nalu_type; > + > + } > + return nalu_type-1; > +} > + > +static EVCParserSPS * parse_sps(const uint8_t *bs, int bs_size, EVCParserContext *ev) > +{ > + GetBitContext gb; > + EVCParserSPS *sps; > + int sps_id; > + > + init_get_bits(&gb, bs, bs_size*8); > + > + sps_id = get_ue_golomb(&gb); > + if(sps_id >= MAX_SPS_CNT) goto ERR; > + sps = &ev->sps[sps_id]; > + sps->sps_id = sps_id; > + av_log(NULL, AV_LOG_DEBUG, "[EVC Parser] sps_id=%d\n", sps->sps_id); > + > + sps->profile_idc = get_bits(&gb, 8); > + av_log(NULL, AV_LOG_DEBUG, "[EVC Parser] profile=%d\n", sps->profile_idc); > + sps->level_idc = get_bits(&gb, 8); > + > + skip_bits_long(&gb, 32); /* skip toolset_idc_h */ > + skip_bits_long(&gb, 32); /* skip toolset_idc_l */ > + > + sps->chroma_format_idc = get_ue_golomb(&gb); > + sps->pic_width_in_luma_samples = get_ue_golomb(&gb); > + av_log(NULL, AV_LOG_DEBUG, "[EVC Parser] width=%d\n", sps->pic_width_in_luma_samples); > + sps->pic_height_in_luma_samples = get_ue_golomb(&gb); > + > + av_log(NULL, AV_LOG_DEBUG, "[EVC Parser] height=%d\n", sps->pic_height_in_luma_samples); > + sps->bit_depth_luma = get_ue_golomb(&gb); > + sps->bit_depth_chroma = get_ue_golomb(&gb); > + > + // @todo we need to parse crop and vui information here > + > + return sps; > + > +ERR: > + return NULL; > +} > + > +/** > + * Read NAL unit length > + * @param bs input data (bitstream) > + * @return the lenghth of NAL unit on success, 0 value on failure > + */ > +static uint32_t read_nal_unit_length(const uint8_t *bs, int bs_size) > +{ > + uint32_t len = 0; > + XEVD_INFO info; > + int ret; > + > + if(bs_size>=XEVD_NAL_UNIT_LENGTH_BYTE) { > + ret = xevd_info((void*)bs, XEVD_NAL_UNIT_LENGTH_BYTE, 1, &info); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "Cannot get bitstream information\n"); > + return 0; > + } > + len = info.nalu_len; > + if(len == 0) > + { > + av_log(NULL, AV_LOG_ERROR, "Invalid bitstream size! 1 [%d] [%d]\n", len, bs_size); > + return 0; > + } > + } > + return len; > +} > + > +static int parse_nal_units(AVCodecParserContext *s, const uint8_t *bs, > + int bs_size, AVCodecContext *ctx) > +{ > + EVCParserContext *ev = s->priv_data; > + int nalu_type, nalu_size; > + unsigned char * bits = (unsigned char *)bs; > + int bits_size = bs_size; > + > + ctx->codec_id = AV_CODEC_ID_EVC; > + > + nalu_size = read_nal_unit_length(bits, bits_size); > + if(nalu_size==0) { > + av_log(ctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", nalu_size); > + return -1; > + } > + > + bits += XEVD_NAL_UNIT_LENGTH_BYTE; > + bits_size -= XEVD_NAL_UNIT_LENGTH_BYTE; > + > + nalu_type = get_nalu_type2(bits, bits_size); > + > + bits += EVC_NAL_HEADER_SIZE; > + bits_size -= EVC_NAL_HEADER_SIZE; > + > + > + if (nalu_type == XEVD_NUT_SPS) { > + EVCParserSPS * sps; > + > + sps = parse_sps(bits, bits_size, ev); > + > + ctx->coded_width = sps->pic_width_in_luma_samples; > + ctx->coded_height = sps->pic_height_in_luma_samples; > + ctx->width = sps->pic_width_in_luma_samples; > + ctx->height = sps->pic_height_in_luma_samples; > + > + if(sps->profile_idc == 0) ctx->profile = FF_PROFILE_EVC_BASELINE; > + else if (sps->profile_idc == 1) ctx->profile = FF_PROFILE_EVC_MAIN; > + else{ > + av_log(ctx, AV_LOG_ERROR, "not supported profile (%d)\n", sps->profile_idc); > + return -1; > + } > + > + switch(sps->chroma_format_idc) > + { > + case 0: /* YCBCR400_10LE */ > + /* @todo support this */ > + ctx->pix_fmt = AV_PIX_FMT_GRAY10LE; > + return -1; > + break; > + case 1: /* YCBCR420_10LE */ > + ctx->pix_fmt = AV_PIX_FMT_YUV420P10LE; > + break; > + case 2: /* YCBCR422_10LE */ > + /* @todo support this */ > + ctx->pix_fmt = AV_PIX_FMT_YUV422P10LE; > + return -1; > + break; > + case 3: /* YCBCR444_10LE */ > + /* @todo support this */ > + ctx->pix_fmt = AV_PIX_FMT_YUV444P10LE; > + return -1; > + break; > + default: > + ctx->pix_fmt = AV_PIX_FMT_NONE; > + av_log(NULL, AV_LOG_ERROR, "unknown color space\n"); > + return -1; > + } > + > + //avctx->has_b_frames = 1; // @todo FIX-ME > + > + ev->got_sps = 1; > + > + } > + else if (nalu_type == XEVD_NUT_PPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_PPS \n"); > + ev->got_pps = 1; > + } > + else if(nalu_type == XEVD_NUT_SEI) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_SEI \n"); > + ev->got_sei = 1; This is a write only field. > + } > + else if (nalu_type == XEVD_NUT_IDR || nalu_type == XEVD_NUT_NONIDR) { > + av_log(ctx, AV_LOG_DEBUG, "XEVD_NUT_NONIDR\n"); > + ev->got_slice++; This too. Maybe you meant to set ctx->pict_type based on nalu_type? > + } else { > + av_log(ctx, AV_LOG_ERROR, "Invalid NAL unit type: %d\n", nalu_type); > + return -1; > + } > + return 0; > +} > + > + > +/** > + * Find the end of the current frame in the bitstream. > + * @return the position of the first byte of the next frame, or END_NOT_FOUND > + */ > +static int evc_find_frame_end(AVCodecParserContext *s, const uint8_t *buf, > + int buf_size) > +{ > + EVCParserContext *ev = s->priv_data; > + > + if(!ev->to_read) > + { > + int nal_unit_size = 0; > + int next = END_NOT_FOUND; > + > + // This is the case when buffer size is not enough for buffer to store NAL unit length > + if(buf_size < XEVD_NAL_UNIT_LENGTH_BYTE) { > + ev->to_read = XEVD_NAL_UNIT_LENGTH_BYTE; > + ev->nal_length_size = buf_size; > + ev->incomplete_nalu_prefix_read = 1; > + > + return END_NOT_FOUND; > + } > + > + nal_unit_size = read_nal_unit_length(buf, buf_size); > + av_log(NULL, AV_LOG_DEBUG, "nal_unit_size: %d | buf_size: %d \n", nal_unit_size, buf_size); > + ev->nal_length_size = XEVD_NAL_UNIT_LENGTH_BYTE; > + > + next = nal_unit_size + XEVD_NAL_UNIT_LENGTH_BYTE; > + ev->to_read = next; > + if(next<buf_size) > + return next; > + else > + return END_NOT_FOUND; > + } else if(ev->to_read > buf_size) { > + /// @todo Consider handling the following case > + // if(ev->incomplete_nalu_prefix_read == 1) { > + // } > + return END_NOT_FOUND; > + } else { > + if(ev->incomplete_nalu_prefix_read == 1) { > + EVCParserContext *ev = s->priv_data; > + ParseContext *pc = &ev->pc; > + uint8_t nalu_len[XEVD_NAL_UNIT_LENGTH_BYTE] = {0}; > + int nal_unit_size = 0; > + > + // 1. pc->buffer contains previously read bytes of NALU prefix > + // 2. buf contains the rest of NAL unit prefix bytes > + // > + // ~~~~~~~ > + // EXAMPLE > + // ~~~~~~~ > + // > + // In the following example we assumed that the number of already read NAL Unit prefix bytes is equal 1 > + // > + // ---------- > + // pc->buffer -> conatins already read bytes > + // ---------- > + // __ pc->index == 1 > + // | > + // V > + // ------------------------------------------------------- > + // | 0 | 1 | 2 | 3 | 4 | ... | N | > + // ------------------------------------------------------- > + // | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | ... | 0xXX | > + // ------------------------------------------------------- > + // > + // ---------- > + // buf -> contains newly read bytes > + // ---------- > + // ------------------------------------------------------- > + // | 0 | 1 | 2 | 3 | 4 | ... | N | > + // ------------------------------------------------------- > + // | 0x00 | 0x00 | 0x3C | 0xXX | 0xXX | ... | 0xXX | > + // ------------------------------------------------------- > + // > + for(int i=0;i<XEVD_NAL_UNIT_LENGTH_BYTE;i++) { > + if(i<pc->index) { > + nalu_len[i] = pc->buffer[i]; > + } else { > + nalu_len[i] = buf[i-pc->index]; > + } > + } > + > + // ---------- > + // nalu_len > + // ---------- > + // --------------------------------- > + // | 0 | 1 | 2 | 3 | > + // --------------------------------- > + // | 0x00 | 0x00 | 0x00 | 0x3C | > + // --------------------------------- > + // | NALU LENGTH | > + // --------------------------------- > + // NAL Unit lenght = 60 (0x0000003C) > + > + nal_unit_size = read_nal_unit_length(nalu_len, XEVD_NAL_UNIT_LENGTH_BYTE); > + av_log(NULL, AV_LOG_DEBUG, "nal_unit_size: %d | buf_size: %d \n", nal_unit_size, buf_size); > + > + ev->to_read = nal_unit_size + XEVD_NAL_UNIT_LENGTH_BYTE - pc->index; > + > + ev->incomplete_nalu_prefix_read = 0; > + > + if(ev->to_read > buf_size) { > + return END_NOT_FOUND; > + } else { > + return ev->to_read; > + } > + } > + return ev->to_read; > + } > + return END_NOT_FOUND; > +} > + > +static int evc_parser_init(AVCodecParserContext *s) { > + > + EVCParserContext *ev = s->priv_data; > + > + av_log(NULL, AV_LOG_DEBUG, "eXtra-fast Essential Video Parser\n"); Unnecessary log message. > + > + ev->got_sps = 0; > + ev->got_pps = 0; > + ev->got_sei = 0; > + ev->got_slice = 0; > + ev->nal_length_size = XEVD_NAL_UNIT_LENGTH_BYTE; > + ev->incomplete_nalu_prefix_read = 0; > + > + return 0; > +} > + > +static int evc_parse(AVCodecParserContext *s, AVCodecContext *ctx, > + const uint8_t **poutbuf, int *poutbuf_size, > + const uint8_t *buf, int buf_size) > +{ > + int next; > + EVCParserContext *ev = s->priv_data; > + ParseContext *pc = &ev->pc; > + int is_dummy_buf = !buf_size; > + const uint8_t *dummy_buf = buf; > + > + if (ctx->extradata && !ev->parsed_extradata) { > + // @todo consider handling extradata > + // > + // ff_evc_decode_extradata(avctx->extradata, avctx->extradata_size, &ctx->ps, &ctx->sei, > + // &ctx->is_avc, &ctx->nal_length_size, avctx->err_recognition, > + // 1, avctx); > + ev->parsed_extradata = 1; > + } > + > + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { > + next = buf_size; > + } else { > + next = evc_find_frame_end(s, buf, buf_size); > + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { > + *poutbuf = NULL; > + *poutbuf_size = 0; > + ev->to_read -= buf_size; > + return buf_size; > + } > + } > +#if 1 > + is_dummy_buf &= (dummy_buf == buf); > + > + if (!is_dummy_buf) { > + parse_nal_units(s, buf, buf_size, ctx); > + } > +#else > + if(next != END_NOT_FOUND) { > + parse_nal_units(s, buf, buf_size, avctx); > + } > +#endif > + > + *poutbuf = buf; > + *poutbuf_size = buf_size; > + ev->to_read -= next; > + return next; > +} > + > +// Split after the parameter sets at the beginning of the stream if they exist. > +static int evc_split(AVCodecContext *ctx, const uint8_t *bs, int bs_size) Remove this, not only because it's a no-op the way you wrote it, but also because AVCodecParser.split() is unused. > +{ > + return 0; > +} > + > +static av_cold void evc_parser_close(AVCodecParserContext *s) > +{ > + /* EVCParserContext *ctx = s->priv_data; */ > +} > + > +AVCodecParser ff_evc_parser = { > + .codec_ids = { AV_CODEC_ID_EVC }, > + .priv_data_size = sizeof(EVCParserContext), > + .parser_init = evc_parser_init, > + .parser_parse = evc_parse, > + .parser_close = evc_parser_close, > + .split = evc_split, > +}; > diff --git a/libavcodec/libxevd.c b/libavcodec/libxevd.c > new file mode 100644 > index 0000000000..ff9ee5c16f > --- /dev/null > +++ b/libavcodec/libxevd.c > @@ -0,0 +1,724 @@ > +/* > + * libxevd decoder > + * EVC (MPEG-5 Essential Video Coding) decoding using XEVD MPEG-5 EVC decoder library > + * > + * Copyright (C) 2021 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 > + */ > + > +#if defined(_MSC_VER) > +#define XEVD_API_IMPORTS 1 > +#endif > + > +#include <xevd.h> > + > +#include <float.h> > +#include <stdlib.h> > + > +#include "libavutil/internal.h" > +#include "libavutil/common.h" > +#include "libavutil/opt.h" > +#include "libavutil/pixdesc.h" > +#include "libavutil/pixfmt.h" > +#include "libavutil/imgutils.h" > + > +// #define USE_EXP_GOLOMB_STUFF > +#ifdef USE_EXP_GOLOMB_STUFF Is this a remnant of debuging code? Just remove it and either use golomb or not. > +#include "golomb.h" > +#endif > + > +#include "avcodec.h" > +#include "internal.h" > +#include "packet_internal.h" > + > +#define UNUSED(x) (void)(x) > + > +#define XEVD_PARAM_BAD_NAME -1 > +#define XEVD_PARAM_BAD_VALUE -2 > + > +/** > + * The structure stores all the state associated with the instance of Xeve MPEG-5 EVC decoder > + * The first field is a pointer to an AVClass struct (@see https://ffmpeg.org/doxygen/trunk/structAVClass.html#details). > + */ > +typedef struct XevdContext { > + const AVClass *class; > + > + XEVD id; // XEVD instance identifier @see xevd.h > + XEVD_CDSC cdsc; // decoding parameters @see xevd.h > + > + int decod_frames; // number of decoded frames > + int packet_count; // number of packets created by decoder > + > + // configuration parameters > + AVDictionary *xevd_opts; // xevd configuration read from a :-separated list of key=value parameters > + > +} XevdContext; > + > +static int op_threads = 1; // Default value > + > +#ifdef USE_EXP_GOLOMB_STUFF > +static int get_nalu_type(const uint8_t *bs, int bs_size) > +{ > + GetBitContext gb; > + int fzb, nut; > + init_get_bits(&gb, bs, bs_size * 8); > + fzb = get_bits1(&gb); > + if(fzb != 0) { > + av_log(NULL, AV_LOG_DEBUG, "forbidden_zero_bit is not clear\n"); > + } > + nut = get_bits(&gb, 6); /* nal_unit_type_plus1 */ > + return nut - 1; > +} > +#endif > + > +#ifdef PRINT_NALU_INFO > +static void print_nalu_info(XEVD_STAT * stat) > +{ > + if(stat->nalu_type < XEVD_NUT_SPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_SPS \n"); > + > + av_log(NULL, AV_LOG_DEBUG, "%c-slice\n", stat->stype == XEVD_ST_I ? 'I' : stat->stype == XEVD_ST_P ? 'P' : 'B'); > + > + av_log(NULL, AV_LOG_DEBUG, " %d bytes\n", stat->read); > + av_log(NULL, AV_LOG_DEBUG, ", poc=%d, tid=%d, ", (int)stat->poc, (int)stat->tid); > + > + for (int i = 0; i < 2; i++) { > + av_log(NULL, AV_LOG_DEBUG, "[L%d ", i); > + for (int j = 0; j < stat->refpic_num[i]; j++) av_log(NULL, AV_LOG_DEBUG,"%d ", stat->refpic[i][j]); > + av_log(NULL, AV_LOG_DEBUG,"] \n"); > + } > + } else if(stat->nalu_type == XEVD_NUT_SPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_SPS \n"); > + } else if (stat->nalu_type == XEVD_NUT_PPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_PPS \n"); > + } else if (stat->nalu_type == XEVD_NUT_SEI) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_SEI \n"); > + } else { > + av_log(NULL, AV_LOG_DEBUG, "Unknown bitstream !!!! \n"); > + } > +} > +#endif > + > +// @todo consider moving following function to separate file containing helper functions for EVC decoder > +#ifdef PRINT_FRAME_INFO Again, no checks for defines that are not defined anywhere. > +static void print_frame_info(const AVFrame* f) > +{ > + int level = AV_LOG_DEBUG; > + av_log(NULL, level, "frame->width: %d\n", f->width); > + av_log(NULL, level, "frame->height: %d\n", f->height); Don't print to NULL. > + > + av_log(NULL, level, "frame->linesize[0]: %d\n", f->linesize[0]); > + av_log(NULL, level, "frame->linesize[1]: %d\n", f->linesize[1]); > + av_log(NULL, level, "frame->linesize[2]: %d\n", f->linesize[2]); > + av_log(NULL, level, "frame->buf[0]: %p\n", f->buf[0]); > + av_log(NULL, level, "frame->buf[1]: %p\n", f->buf[1]); > + av_log(NULL, level, "frame->buf[2]: %p\n", f->buf[2]); > + av_log(NULL, level, "frame->data[0]: %p\n", f->data[0]); > + av_log(NULL, level, "frame->data[1]: %p\n", f->data[1]); > + av_log(NULL, level, "frame->data[2]: %p\n", f->data[2]); > +} > +#endif > + > +// @todo consider moving following function to separate file containing helper functions for EVC decoder > +#ifdef PRINT_XEVD_IMGB_INFO Ditto. > +static void print_xevd_imgb_info(const XEVD_IMGB* imgb) > +{ > + av_log(NULL, AV_LOG_DEBUG, "imgb->np: %d\n", imgb->np); > + av_log(NULL, AV_LOG_DEBUG, "imgb->bsize[0]: %d\n", imgb->bsize[0]); > + av_log(NULL, AV_LOG_DEBUG, "imgb->bsize[1]: %d\n", imgb->bsize[1]); > + av_log(NULL, AV_LOG_DEBUG, "imgb->bsize[2]: %d\n", imgb->bsize[2]); > +} > +#endif > + > +// @todo consider moving following function to separate file containing helper functions for EVC decoder > +#ifdef PRINT_AVCTX Ditto, and every other case below. > +static void print_avctx(const AVCodecContext *avctx) > +{ > + if( AVMEDIA_TYPE_UNKNOWN == avctx->codec_type) { > + av_log(NULL, AV_LOG_DEBUG, "avctx->codec_type: AVMEDIA_TYPE_UNKNOWN\n"); > + } else if(AVMEDIA_TYPE_VIDEO == avctx->codec_type) > + av_log(NULL, AV_LOG_DEBUG, "avctx->codec_type: AVMEDIA_TYPE_VIDEO \n"); > + else { > + av_log(NULL, AV_LOG_DEBUG, "avctx->codec_type: AVMEDIA_TYPE_UNKNOWN\n"); > + } > + > + av_log(NULL, AV_LOG_DEBUG, "avctx->codec_id: %s\n",avcodec_get_name(avctx->codec_id)); > + av_log(NULL, AV_LOG_DEBUG, "avctx->width: %d\n", avctx->width); > + av_log(NULL, AV_LOG_DEBUG, "avctx->height: %d\n", avctx->height); > + av_log(NULL, AV_LOG_DEBUG, "avctx->pix_fmt: %d\n", avctx->pix_fmt); > +} > +#endif > + > +/** > + * Read options > + * > + * @param avctx codec context > + * @return 0 on success > + */ > +static int read_options(const AVCodecContext* avctx) > +{ > + > + op_threads = (avctx->thread_count>0)?avctx->thread_count:1; > + > + return 0; > +} > + > +/** > + * Parse :-separated list of key=value parameters > + * > + * @param key > + * @param value > + * > + * @return 0 on success, negative value on failure > + * > + * @todo Consider removing the function > + */ > +static int xevd_params_parse(const char* key, const char* value) > +{ > + if(!key) { > + av_log(NULL, AV_LOG_ERROR, "Ivalid argument: key string is NULL\n"); > + return XEVD_ERR_INVALID_ARGUMENT; > + } > + if(!value) { > + av_log(NULL, AV_LOG_ERROR, "Ivalid argument: value string is NULL\n"); > + return XEVD_ERR_INVALID_ARGUMENT; > + } > + > + else { > + av_log(NULL, AV_LOG_ERROR, "Unknown xevd codec option: %s\n", key); > + return XEVD_PARAM_BAD_NAME; > + } > + return 0; > +} > + > +/** > + * The function returns a pointer to variable of type XEVD_CDSC. > + * XEVD_CDSC contains all decoder parameters that should be initialized before its use. > + * > + * The field values of the XEVD_CDSC structure are populated based on: > + * - the corresponding field values of the AvCodecConetxt structure, > + * - the xevd decoder specific option values, > + * (the full list of options available for xevd encoder is displayed after executing the command ./ffmpeg --help decoder = libxevd) > + * - and the xevd encoder options specified as a list of key value pairs following xevd-params option > + * > + * Order of input processing and populating the XEVD_CDSC structure > + * 1. first, the corresponding fields of the AVCodecContext structure are processed, (i.e -threads 4) > + * 2. then xevd-specific options added as AVOption to the xevd AVCodec implementation (i.e -threads 3) > + * 3. finally, the options specified after the xevd-params option as the parameter list of type key value are processed (i.e -xevd-params "m=2") > + * > + * There are options that can be set in different ways. In this case, please follow the above-mentioned order of processing. > + * The most recent assignments overwrite the previous values. > + * > + * @param avctx codec context > + * @param cdsc contains all encoder parameters that should be initialized before its use. > + * > + * @return 0 on success, negative error code on failure > + */ > +static int get_conf(const AVCodecContext* avctx, XEVD_CDSC* cdsc) > +{ > + int cpu_count = av_cpu_count(); > + > + /* read options from AVCodecContext & from XEVD_CDSC */ > + read_options(avctx); > + > + /* parse :-separated list of key=value parameters and set values for created descriptor (XEVD_CDSC) */ > + { > + XevdContext *ctx = avctx->priv_data; > + AVDictionaryEntry *en = NULL; > + while ((en = av_dict_get(ctx->xevd_opts, "", en, AV_DICT_IGNORE_SUFFIX))) { > + int parse_ret = xevd_params_parse(en->key, en->value); > + > + switch (parse_ret) { > + case XEVD_PARAM_BAD_NAME: > + av_log((AVCodecContext*)avctx, AV_LOG_WARNING, > + "Unknown option: %s.\n", en->key); > + break; > + case XEVD_PARAM_BAD_VALUE: > + av_log((AVCodecContext*)avctx, AV_LOG_WARNING, > + "Invalid value for %s: %s.\n", en->key, en->value); > + break; > + default: > + break; > + } > + } > + } > + > + /* clear XEVS_CDSC structure */ > + memset(cdsc, 0, sizeof(XEVD_CDSC)); > + > + /* init XEVD_CDSC */ > + if(avctx->thread_count <= 0) { > + cdsc->threads = (cpu_count<XEVD_MAX_TASK_CNT)?cpu_count:XEVD_MAX_TASK_CNT; > + } else if(avctx->thread_count > XEVD_MAX_TASK_CNT) { > + cdsc->threads = XEVD_MAX_TASK_CNT; > + } else { > + cdsc->threads = avctx->thread_count; > + } > + > + return XEVD_OK; > +} > + > +/** > + * Read NAL unit length > + * @param bs input data (bitstream) > + * @return the lenghth of NAL unit on success, 0 value on failure > + */ > +static uint32_t read_nal_unit_length(const uint8_t *bs, int bs_size) > +{ > + uint32_t len = 0; > + XEVD_INFO info; > + int ret; > + > + if(bs_size==XEVD_NAL_UNIT_LENGTH_BYTE) { > + ret = xevd_info((void*)bs, XEVD_NAL_UNIT_LENGTH_BYTE, 1, &info); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "Cannot get bitstream information\n"); > + return 0; > + } > + len = info.nalu_len; > + if(len == 0) > + { > + av_log(NULL, AV_LOG_ERROR, "Invalid bitstream size! [%d]\n", bs_size); > + return 0; > + } > + } > + return len; > +} > + > +/** > + * @param avctx codec context > + * @param ctx the structure that stores all the state associated with the instance of Xeve MPEG-5 EVC decoder > + * @return 0 on success, negative value on failure > + */ > +static int export_stream_params(AVCodecContext *avctx, const XevdContext *ctx) > +{ > + // unsigned int num = 0, den = 0; > + // @todo support other formats > + > + int ret; > + int size; > + int color_space; > + > + avctx->pix_fmt = AV_PIX_FMT_YUV420P10; > + > + // @todo The AVCodecContext should be initialized here using data from the object of XEVD_SPS type. > + // > + // It seems to be impossible right now since XEVD API limitation. > + // The extension for the XEVD API is needed. > + // To be more precise, what we need is access to the object of XEVD_SPS type being a part of XEVD_CTX object. > + // The object of XEVD_CTX type is created while the function xevd_create() being a part of public API is called. > + // > + // @todo remove the following hardoced has_b_frames; consider using sps->num_reorder_pics value instead > + // > + // avctx->has_b_frames = 1; // (sps->num_reorder_pics)?1:0; > + size = 4; > + ret = xevd_config(ctx->id, XEVD_CFG_GET_CODED_WIDTH, &avctx->coded_width, &size); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to get coded_width\n"); > + return -1; > + } > + > + ret = xevd_config(ctx->id, XEVD_CFG_GET_CODED_HEIGHT, &avctx->coded_height, &size); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to get coded_height\n"); > + return -1; > + } > + > + ret = xevd_config(ctx->id, XEVD_CFG_GET_WIDTH, &avctx->width, &size); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to get width\n"); > + return -1; > + } > + > + ret = xevd_config(ctx->id, XEVD_CFG_GET_HEIGHT, &avctx->height, &size); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to get height\n"); > + return -1; > + } > + > + ret = xevd_config(ctx->id, XEVD_CFG_GET_COLOR_SPACE, &color_space, &size); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to get color_space\n"); > + return -1; > + } > + switch(color_space) { > + case XEVD_CS_YCBCR400_10LE: > + av_log(NULL, AV_LOG_DEBUG, "color_space = XEVD_CS_YCBCR400_10LE\n"); > + avctx->pix_fmt = AV_PIX_FMT_GRAY10LE; > + break; > + case XEVD_CS_YCBCR420_10LE: > + av_log(NULL, AV_LOG_DEBUG, "color_space = XEVD_CS_YCBCR420_10LE\n"); > + avctx->pix_fmt = AV_PIX_FMT_YUV420P10LE; > + break; > + case XEVD_CS_YCBCR422_10LE: > + av_log(NULL, AV_LOG_DEBUG, "color_space = XEVD_CS_YCBCR422_10LE\n"); > + avctx->pix_fmt = AV_PIX_FMT_YUV422P10LE; > + break; > + case XEVD_CS_YCBCR444_10LE: > + av_log(NULL, AV_LOG_DEBUG, "color_space = XEVD_CS_YCBCR444_10LE\n"); > + avctx->pix_fmt = AV_PIX_FMT_YUV444P10LE; > + break; > + default: > + av_log(NULL, AV_LOG_ERROR, "unknown color space\n"); > + avctx->pix_fmt = AV_PIX_FMT_NONE; > + return -1; > + } > + > +// @todo Use _XEVD_SPS fields to initialize AVCodecContext when it is possible > +#ifdef USE_XEVD_SPS_FIELDS > + avctx->profile = sps->profile_idc; > + avctx->level = sps->level_idc; > + > + ff_set_sar(avctx, sps->vui_parameters.sar); > + > + if (sps->vui_parametersvui.video_signal_type_present_flag) > + avctx->color_range = sps->vui_parameters.video_full_range_flag ? AVCOL_RANGE_JPEG > + : AVCOL_RANGE_MPEG; > + else > + avctx->color_range = AVCOL_RANGE_MPEG; > + > + if (sps->vui_parameters.colour_description_present_flag) { > + avctx->color_primaries = sps->vui_parameters.colour_primaries; > + avctx->color_trc = sps->vui_parameters.transfer_characteristic; > + avctx->colorspace = sps->vui_parameters.matrix_coeffs; > + } else { > + avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; > + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; > + avctx->colorspace = AVCOL_SPC_UNSPECIFIED; > + } > + > + if (sps->vui_parameters.timing_info_present_flag) { > + num = sps->vui_parameters.num_units_in_tick; > + den = sps->vui_parameters.time_scale; > + } > + > + if (s->sei.alternative_transfer.present && > + av_color_transfer_name(s->sei.alternative_transfer.preferred_transfer_characteristics) && > + s->sei.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { > + avctx->color_trc = s->sei.alternative_transfer.preferred_transfer_characteristics; > + } > +#else > + avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; > + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; > + avctx->colorspace = AVCOL_SPC_UNSPECIFIED; > + > +#endif > + return 0; > +} > + > +/** > + * Initialize decoder static data > + * > + * @todo Consider removing unused function > + */ > +static av_cold void libxevd_init_static_data(AVCodec *codec) > +{ > + UNUSED(codec); > +} > + > +/** > + * Initialize decoder > + * Create 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 libxevd_init(AVCodecContext *avctx) > +{ > + XevdContext *ctx = avctx->priv_data; > + int val = 0; > + XEVD_CDSC *cdsc = &(ctx->cdsc); > + > + av_log(NULL, AV_LOG_DEBUG, "eXtra-fast Essential Video Decoder\n"); > +#ifdef PRINT_AVCTX > + print_avctx(avctx); > +#endif > + > + /* read configurations and set values for created descriptor (XEVD_CDSC) */ > + val = get_conf(avctx, cdsc); > + if (val != XEVD_OK) { > + av_log(NULL, AV_LOG_ERROR,"Cannot get configuration\n"); > + return -1; > + } > + > + /* create decoder */ > + ctx->id = xevd_create(&(ctx->cdsc), NULL); > + if(ctx->id == NULL) { > + av_log(NULL, AV_LOG_ERROR, "cannot create XEVD encoder\n"); > + return -1; > + } > + > + ctx->packet_count = 0; > + ctx->decod_frames = 0; > + return 0; > +} > + > +/** > + * Dncode picture > + * > + * @param avctx codec context > + * @param data codec type dependent output struct > + * @param[out] got_frame decoder sets to 0 or 1 to indicate that a > + * non-empty frame or subtitle was returned in > + * outdata. > + * @param[in] pkt AVPacket containing the data to be decoded > + * @return amount of bytes read from the packet on success, negative error > + * code on failure > + */ > +static int libxevd_decode(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *pkt) > +{ > + AVFrame *frame = data; > + XevdContext *ctx = NULL; > + XEVD_IMGB * imgb = NULL; > + XEVD_STAT stat; > + XEVD_BITB bitb; > + int ret, nalu_size, bs_read_pos; > + > + if(avctx == NULL) { > + av_log(NULL, AV_LOG_ERROR, "Invalid input parameter: AVCodecContext\n"); > + return -1; > + } > + ctx = avctx->priv_data; > + if(ctx == NULL) { > + av_log(avctx, AV_LOG_ERROR, "Invalid XEVD context\n"); > + return -1; > + } > + > + if(pkt->size > 0) { > + bs_read_pos = 0; > + imgb = NULL; > + while(pkt->size > (bs_read_pos + XEVD_NAL_UNIT_LENGTH_BYTE)) { > + int nal_type = 0; > + > + memset(&stat, 0, sizeof(XEVD_STAT)); > + memset(&bitb, 0, sizeof(XEVD_BITB)); > + > + nalu_size = read_nal_unit_length(pkt->data + bs_read_pos, XEVD_NAL_UNIT_LENGTH_BYTE); > + if(nalu_size == 0) { > + av_log(avctx, AV_LOG_ERROR, "Invalid bitstream\n"); > + goto ERR; > + } > + bs_read_pos += XEVD_NAL_UNIT_LENGTH_BYTE; > + > + bitb.addr = pkt->data + bs_read_pos; > + bitb.ssize = nalu_size; > + > + // Read NAL Unit Type from NAL Unit Header > + // > + // The structure of NAL Unit Header looks like follows > + // > + //Â +---------------+---------------+ > + //Â |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| > + //Â +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ > + //Â |F| Type | TID | Reserve |E| > + //Â +-------------+-----------------+ > + // > + // F: 1 bit - forbidden_zero_bit. Required to be zero in [EVC]. > + // Type: 6 bits - nal_unit_type_plus1 (This field specifies the NAL unit type as defined in Table 4 of [EVC]) > + // TID: 3 bits - nuh_temporal_id. This field specifies the temporal identifier of the NAL unit. > + // Reserve: 5 bits - nuh_reserved_zero_5bits. This field shall be equal to the version of the [EVC] specification. > + // E: 1 bit - nuh_extension_flag. This field shall be equal the version of the [EVC] specification. > + // > + // @see https://datatracker.ietf.org/doc/html/draft-ietf-avtcore-rtp-evc-01#section-1.1.4 > + > +#ifdef USE_EXP_GOLOMB_STUFF > + nal_type = get_nalu_type(bitb.addr, 1); > + av_log(avctx, AV_LOG_DEBUG, "NALU Type: %d\n", nal_type); > +#else > + memcpy(&nal_type,bitb.addr,1); > + nal_type = nal_type & 0x7E; > + nal_type = nal_type >> 1; > + nal_type -= 1; > + av_log(avctx, AV_LOG_DEBUG, "NALU Type: %d\n", nal_type); > +#endif > + > + /* main decoding block */ > + ret = xevd_decode(ctx->id, &bitb, &stat); > + if(XEVD_FAILED(ret)) { > + av_log(avctx, AV_LOG_ERROR, "failed to decode bitstream\n"); > + goto ERR; > + } > + > + bs_read_pos += nalu_size; > + > +#ifdef PRINT_NALU_INFO > + print_nalu_info(ctx); > +#endif > + > + if(stat.nalu_type == XEVD_NUT_SPS) { > + av_log(avctx, AV_LOG_DEBUG, "EVC stream parameters changed\n"); > + > + if(export_stream_params(avctx, ctx)!=0) { > + goto ERR; > + } > + av_log(avctx, AV_LOG_DEBUG, "width: %d\n",avctx->width); > + av_log(avctx, AV_LOG_DEBUG, "height: %d\n",avctx->height); > + > + } > + > + if(stat.read != nalu_size) { > + av_log(avctx, AV_LOG_INFO, "different reading of bitstream (in:%d, read:%d)\n,", nalu_size, stat.read); > + } > + if(stat.fnum >= 0) { > + if (imgb) { /* already has a decoded image */ > + imgb->release(imgb); So if there's a decoded image and this loop is run again because bs_read_pos is still smaller than pkt->size, you just discard it? You probably should write this decoder using the decoupled input/output API (AVCodec.receive_frame instead of AVCodec.decode) > + imgb = NULL; > + } > + ret = xevd_pull(ctx->id, &imgb); > + if(XEVD_FAILED(ret)) { > + av_log(avctx, AV_LOG_ERROR, "failed to pull the decoded image (err:%d, frame#=%d)\n", ret, stat.fnum); > + goto ERR; > + } else if (ret == XEVD_OK_FRM_DELAYED) { > + av_log(avctx, AV_LOG_DEBUG, "delayed frame\n"); > + if(imgb) { > + imgb->release(imgb); > + imgb = NULL; > + } > + } > + } > + } > + } else { > + av_log(NULL, AV_LOG_DEBUG, "bumping ...\n"); > + ret = xevd_pull(ctx->id, &(imgb)); > + if(ret == XEVD_ERR_UNEXPECTED) { > + av_log(avctx, AV_LOG_DEBUG, "Bumping process completed\n"); > + *got_frame = 0; > + return 0; > + } else if(XEVD_FAILED(ret)) { > + av_log(avctx, AV_LOG_ERROR, "failed to pull the decoded image (err:%d)\n", ret); > + goto ERR; > + } else { > + av_log(avctx, AV_LOG_DEBUG, "bumping success\n"); > + } > + } > + > + if(imgb) { > + /* @todo supports other color space and bit depth */ > + if(imgb->cs != XEVD_CS_YCBCR420_10LE) { > + av_log(avctx, AV_LOG_ERROR, "Not supported pixel format: %s\n", av_get_pix_fmt_name(avctx->pix_fmt)); > + goto ERR; > + } > + > + if (imgb->w[0] != avctx->width || imgb->h[0] != avctx->height) { > + av_log(avctx, AV_LOG_DEBUG, "resolution changed %dx%d -> %dx%d\n", > + avctx->width, avctx->height, imgb->w[0], imgb->h[0]); > + if(ff_set_dimensions(avctx, imgb->w[0], imgb->h[0]) < 0) { > + av_log(avctx, AV_LOG_ERROR, "cannot set new dimension\n"); > + goto ERR; > + } > + } > + > + frame->coded_picture_number++; > + frame->display_picture_number++; > + frame->format = AV_PIX_FMT_YUV420P10LE; > + > +#ifdef PRINT_XEVD_IMGB_INFO > + print_xevd_imgb_info(imgb); > +#endif > + > + if (ff_get_buffer(avctx, frame, 0) < 0) { > + av_log(avctx, AV_LOG_ERROR, "cannot get AV buffer\n"); > + goto ERR; > + } > + > + frame->pts = pkt->pts; ff_get_buffer() already fills fields like this. > + av_log(avctx, AV_LOG_DEBUG, "frame->pts = %ld\n", frame->pts); > + > + av_image_copy(frame->data, frame->linesize, (const uint8_t **)imgb->a, > + imgb->s, avctx->pix_fmt, > + imgb->w[0], imgb->h[0]); > + > + ctx->decod_frames++; > + *got_frame = 1; > + > +#ifdef PRINT_FRAME_INFO > + print_frame_info(frame); > +#endif > + imgb->release(imgb); > + imgb = NULL; > + } else { > + *got_frame = 0; > + } > + > + ctx->packet_count++; > + return pkt->size; > + > +ERR: > + if(imgb) { > + imgb->release(imgb); > + imgb = NULL; > + } > + *got_frame = 0; > + return -1; Return proper AVERROR codes, here and everywhere else. > +} > + > +/** > + * Destroy decoder > + * > + * @param avctx codec context > + * @return 0 on success > + */ > +static av_cold int libxevd_close(AVCodecContext *avctx) > +{ > + XevdContext *ctx = avctx->priv_data; > + if(ctx->id) { > + xevd_delete(ctx->id); > + ctx->id = NULL; > + } > + > + return 0; > +} > + > +#define OFFSET(x) offsetof(XevdContext, x) > +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM > + > +// @todo consider using following options (./ffmpeg --help decoder=libxevd) > +// > +static const AVOption options[] = { > + { "xevd-params", "override the xevd configuration using a :-separated list of key=value parameters", OFFSET(xevd_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VD }, > + { NULL } > +}; > + > +static const AVClass xevd_class = { > + .class_name = "libxevd", > + .item_name = av_default_item_name, > + .option = options, > + .version = LIBAVUTIL_VERSION_INT, > +}; > + > +/// @todo provide implementation > +static const AVCodecDefault xevd_defaults[] = { > + { "b", "0" }, > + { NULL }, > +}; > + > +AVCodec ff_libxevd_decoder = { > + .name = "evc", > + .long_name = NULL_IF_CONFIG_SMALL("EVC / MPEG-5 Essential Video Coding (EVC)"), > + .type = AVMEDIA_TYPE_VIDEO, > + .id = AV_CODEC_ID_EVC, > + .init = libxevd_init, > + .init_static_data = libxevd_init_static_data, > + .decode = libxevd_decode, > + .close = libxevd_close, > + .priv_data_size = sizeof(XevdContext), > + .priv_class = &xevd_class, > + .defaults = xevd_defaults, > + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_AVOID_PROBING, Missing AV_CODEC_CAP_DR1 since you're using ff_get_buffer(). > + .wrapper_name = "libxevd", > +}; > diff --git a/libavcodec/libxeve.c b/libavcodec/libxeve.c > new file mode 100644 > index 0000000000..f34a1d6efd > --- /dev/null > +++ b/libavcodec/libxeve.c > @@ -0,0 +1,1185 @@ > +/* > + * libxeve encoder > + * EVC (MPEG-5 Essential Video Coding) encoding using XEVE MPEG-5 EVC encoder library > + * > + * Copyright (C) 2021 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 > + */ > + > +#if defined(_MSC_VER) > +#define XEVE_API_IMPORTS 1 > +#endif > + > +#include <xeve.h> > + > +#include <float.h> > +#include <stdlib.h> > + > +#include "libavutil/internal.h" > +#include "libavutil/common.h" > +#include "libavutil/opt.h" > +#include "libavutil/pixdesc.h" > +#include "libavutil/pixfmt.h" > +#include "libavutil/time.h" > + > +#include "avcodec.h" > +#include "internal.h" > +#include "packet_internal.h" > + > +#define MAX_BS_BUF (16*1024*1024) > + > +/** > + * Error codes > + */ > +#define XEVE_PARAM_BAD_NAME -100 > +#define XEVE_PARAM_BAD_VALUE -200 > + > +/** > + * Macro for eliminating the unused variable warning > + */ > +#define UNUSED(x) (void)(x) > + > +/** > + * Encoder states > + * > + * STATE_ENCODING - the encoder receives and processes input frames > + * STATE_BUMPING - there are no more input frames, however the encoder still processes previously received data > + * STATE_SKIPPING - skipping input frames > + */ > +typedef enum State { > + STATE_ENCODING, > + STATE_BUMPING, > + STATE_SKIPPING > +} State; > + > +/** > + * The structure stores all the state associated with the instance of Xeve MPEG-5 EVC encoder > + * The first field is a pointer to an AVClass struct (@see https://ffmpeg.org/doxygen/trunk/structAVClass.html#details). > + */ > +typedef struct XeveContext { > + const AVClass *class; > + > + XEVE id; // XEVE instance identifier > + XEVE_CDSC cdsc; // coding parameters i.e profile, width & height of input frame, num of therads, frame rate ... > + XEVE_BITB bitb; // bitstream buffer (output) > + XEVE_STAT stat; // encoding status (output) > + XEVE_IMGB imgb; // image buffer (input) > + > + State state; // encoder state (skipping, encoding, bumping) > + > + int encod_frames; // num of encoded frames > + double bytes_total; // encoded bitstream byte size > + double bitrate; // bits per second > + int packet_count; // num of packets created by encoder > + > + // Chroma subsampling > + int width_luma; > + int height_luma; > + int width_chroma; > + int height_chroma; > + > + int profile_id; // encoder profile (main, baseline) > + int preset_id; // preset of xeve ( fast, medium, slow, placebo) > + int tune_id; // tune of xeve (psnr, zerolatency) > + int input_depth; // input bit-depth: 8bit, 10bit > + int hash; > + > + /* variables for input parameter */ > + char * op_preset; > + char * op_tune; > + int op_qp; > + int op_crf; > + > + // configuration parameters > + // xeve configuration read from a :-separated list of key=value parameters > + AVDictionary *xeve_params; > +} XeveContext; > + > +/** > + * Gets Xeve encoder pre-defined profile > + * > + * @param profile string describing Xeve encoder profile (baseline, main) > + * @return XEVE pre-defined profile on success, negative value on failure > + */ > +static int get_profile_id(const char * profile) > +{ > + if (!strcmp(profile, "baseline")) { > + return XEVE_PROFILE_BASELINE; > + } else if (!strcmp(profile, "main")) { > + return XEVE_PROFILE_MAIN; > + } else { > + return -1; > + } > +} > + > +/** > + * Gets Xeve pre-defined preset > + * > + * @param preset string describing Xeve encoder preset (fast, medium, slow, placebo ) > + * @return XEVE pre-defined profile on success, negative value on failure > + */ > +static int get_preset_id(const char * preset) > +{ > + if((!strcmp(preset, "fast"))) { > + return XEVE_PRESET_FAST; > + } else if (!strcmp(preset, "medium")) { > + return XEVE_PRESET_MEDIUM; > + } else if (!strcmp(preset, "slow")) { > + return XEVE_PRESET_SLOW; > + } else if (!strcmp(preset, "placebo")) { > + return XEVE_PRESET_PLACEBO; > + } else { > + return -1; > + } > +} > + > +/** > + * Gets Xeve pre-defined tune id > + * > + * @param preset string describing Xeve encoder preset (fast, medium, slow, placebo ) > + * @return XEVE pre-defined profile on success, negative value on failure > + */ > +static int get_tune_id(const char * tune) > +{ > + if((!strcmp(tune, "psnr"))) { > + return XEVE_TUNE_PSNR; > + } else if (!strcmp(tune, "zerolatency")) { > + return XEVE_TUNE_ZEROLATENCY; > + } else { > + return -1; > + } > +} > + > +static int kbps_str_to_int(char *str) > +{ > + int kbps = 0; > + if (strchr(str, 'K') || strchr(str, 'k')) { > + char *tmp = strtok(str, "Kk "); > + kbps = (int)(atof(tmp)); > + } else if (strchr(str, 'M') || strchr(str, 'm')) { > + char *tmp = strtok(str, "Mm "); > + kbps = (int)(atof(tmp) * 1000); > + } else { > + kbps = atoi(str); > + } > + return kbps; > +} > + > +/** > + * Parse :-separated list of key=value parameters > + * > + * @param key > + * @param value > + * @param xe pointer to the structure that stores all the state associated with > + * the instance of Xeve MPEG-5 EVC encoder > + * @param param coding parameters > + * > + * @return 0 on success, negative value on failure > + */ > +static int parse_xeve_params(const char* key, const char* value, XeveContext* xe, XEVE_PARAM* param) > +{ > + if(!key) { > + av_log(NULL, AV_LOG_ERROR, "Ivalid argument: key string is NULL\n"); > + return XEVE_PARAM_BAD_VALUE; > + } > + if(!value) { > + if (strcmp(key, "hash") == 0) { > + xe->hash = 1; > + av_log(NULL, AV_LOG_INFO, "embedding signature is enabled\n"); > + } else { > + av_log(NULL, AV_LOG_ERROR, "Ivalid argument: value string is NULL\n"); > + return XEVE_PARAM_BAD_VALUE; > + } > + } else if (strcmp(key, "vbv-bufsize") == 0 ) { > + param->vbv_bufsize = kbps_str_to_int((char*)value); > + av_log(NULL, AV_LOG_INFO, "VBV buffer size: %dkbits\n", param->vbv_bufsize); > + } else if (strcmp(key, "rc-type") == 0 ) { > + int rc_type = atoi(value); > + if(rc_type < 0 || rc_type > 2) { > + av_log(NULL, AV_LOG_ERROR, "Rate control type [ 0(rc_off) / 1(CBR) ] bad value: %d\n", rc_type); > + return XEVE_PARAM_BAD_VALUE; > + } > + param->rc_type = rc_type; > + av_log(NULL, AV_LOG_INFO, "Rate control type [ 0(rc_off) / 1(CBR) ] : %d\n", rc_type); > + } else if (strcmp(key, "bframes") == 0 ) { > + int bframes = atoi(value); > + if(bframes < 0) { > + av_log(NULL, AV_LOG_ERROR, "bframes: bad value: %d\n", bframes); > + return XEVE_PARAM_BAD_VALUE; > + } > + param->bframes = bframes; > + av_log(NULL, AV_LOG_INFO, "bframes : %d\n", bframes); > + } else if (strcmp(key, "profile") == 0 ) { > + const char* profile = value; > + int profile_id; > + av_log(NULL, AV_LOG_INFO, "profile (baseline, main): %s\n", profile); > + profile_id = get_profile_id(profile); > + if (profile_id < 0) { > + av_log(NULL, AV_LOG_ERROR, "Invalid xeve param: profile(%s)\n", profile); > + return XEVE_PARAM_BAD_VALUE; > + } > + xe->profile_id = profile_id; > + } else if (strcmp(key, "preset") == 0 ) { > + const char* preset = value; > + int preset_id; > + av_log(NULL, AV_LOG_INFO, "Preset of xeve (fast, medium, slow, placebo): %s\n", preset); > + preset_id = get_preset_id(preset); > + if( preset_id < 0) { > + av_log(NULL, AV_LOG_ERROR, "Invalid xeve param: preset(%s)\n", preset); > + return XEVE_PARAM_BAD_VALUE; > + } > + xe->preset_id = preset_id; > + } else if (strcmp(key, "tune") == 0 ) { > + const char* tune = value; > + int tune_id; > + av_log(NULL, AV_LOG_INFO, "Tune of xeve (psnr, zerolatency): %s\n", tune); > + tune_id= get_tune_id(tune); > + if( tune_id < 0) { > + av_log(NULL, AV_LOG_ERROR, "Invalid xeve param: tune(%s)\n", tune); > + return XEVE_PARAM_BAD_VALUE; > + } > + xe->tune_id = tune_id; > + } else if (strcmp(key, "bitrate") == 0 ) { > + param->bitrate = kbps_str_to_int((char *)value); > + av_log(NULL, AV_LOG_INFO, "Bitrate = %dkbps\n", param->bitrate); > + } else if (strcmp(key, "q") == 0 || strcmp(key, "qp") == 0) { > + int qp = atoi(value); > + if(qp < 0 || qp > 51) { > + av_log(NULL, AV_LOG_ERROR, "Invalid QP value (0~51) :%d\n", qp); > + return XEVE_PARAM_BAD_VALUE; > + } > + param->qp = qp; > + av_log(NULL, AV_LOG_INFO, "QP value (0~51): %d\n", param->qp); > + } else { > + av_log(NULL, AV_LOG_ERROR, "Unknown xeve codec option: %s\n", key); > + return XEVE_PARAM_BAD_NAME; > + } > + return 0; > +} > + > +/** > + * Convert ffmpeg pixel format (AVPixelFormat) to XEVE pre-defined color format > + * > + * @param[in] px_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5) > + * @param[out] color_format XEVE pre-defined color format (@see xeve.h) > + * @param[out] bit_depth bit depth > + * > + * @return 0 on success, negative value on failure > + */ > +static int get_pix_fmt(enum AVPixelFormat pix_fmt, int *color_format, int *bit_depth) > +{ > + switch (pix_fmt) { > + case AV_PIX_FMT_YUV420P: > + *color_format = XEVE_CF_YCBCR420; > + *bit_depth = 8; > + break; > + case AV_PIX_FMT_YUV422P: > + *color_format = XEVE_CF_YCBCR422; > + *bit_depth = 8; > + break; > + case AV_PIX_FMT_YUV444P: > + *color_format = XEVE_CF_YCBCR444; > + *bit_depth = 8; > + break; > + case AV_PIX_FMT_YUV420P10: > + *color_format = XEVE_CF_YCBCR420; > + *bit_depth = 10; > + break; > + case AV_PIX_FMT_YUV422P10: > + *color_format = XEVE_CF_YCBCR422; > + *bit_depth = 10; > + break; > + case AV_PIX_FMT_YUV444P10: > + *color_format = XEVE_CF_YCBCR444; > + *bit_depth = 10; > + break; > + default: > + *color_format = XEVE_CF_UNKNOWN; > + return -1; > + } > + return 0; > +} > + > +/** > + * The function returns a pointer to variable of type XEVE_CDSC. > + * XEVE_CDSC contains all encoder parameters that should be initialized before its use. > + * > + * The field values of the XEVE_CDSC structure are populated based on: > + * - the corresponding field values of the AvCodecConetxt structure, > + * - the xeve encoder specific option values, > + * (the full list of options available for xeve encoder is displayed after executing the command ./ffmpeg --help encoder = libxeve) > + * - and the xeve encoder options specified as a list of key value pairs following xeve-params option > + * > + * Order of input processing and populating the XEVE_CDSC structure > + * 1. first, the corresponding fields of the AVCodecContext structure are processed, (i.e -pix_fmt yuv420p -s:v 1920x1080 -r 30 -profile:v 0) > + * 2. then xeve-specific options added as AVOption to the xeve AVCodec implementation (i.e -threads_cnt 3 -preset 0) > + * 3. finally, the options specified after the xeve-params option as the parameter list of type key value are processed (i.e -xeve-params "m=2:q=17") > + * > + * There are options that can be set in different ways. In this case, please follow the above-mentioned order of processing. > + * The most recent assignments overwrite the previous values. > + * > + * @param ctx codec context > + * @param cdsc contains all encoder parameters that should be initialized before its use. > + * > + * @return 0 on success, negative error code on failure > + */ > +static int get_conf(const AVCodecContext *ctx, XEVE_CDSC *cdsc) > +{ > + XEVE_PARAM * param = NULL; > + XeveContext *xe = NULL; > + int color_format; > + int cpu_count = av_cpu_count(); > + int ret; > + > + xe = ctx->priv_data; > + param = &cdsc->param; > + > + /* set defualt value in priv_data */ > + memset(cdsc, 0, sizeof(XEVE_CDSC)); > + xe->hash = 0; > + > + /* set default parameters */ > + ret = xeve_param_default(param); > + if (XEVE_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "cannot set_default parameter\n"); > + goto ERR; > + } > + > + /* read options from AVCodecContext */ > + if(ctx->width > 0) { > + param->w = ctx->width; > + xe->width_luma = ctx->width; > + } > + > + if(ctx->height > 0) { > + param->h = ctx->height; > + xe->height_luma = ctx->height; > + } > + > + if(ctx->framerate.num > 0) { > + /* @todo: fps can be float number, but xeve API doesn't support it */ > + param->fps = (int)(((float)ctx->framerate.num / ctx->framerate.den) + 0.5); > + } > + > + if(ctx->gop_size >= 0) { /* key-frame interval */ > + param->keyint = ctx->gop_size; // 0: only one I-frame at the first time; 1: every frame is coded in I-frame > + av_log(NULL, AV_LOG_INFO, "GOP size (key-frame interval): %d\n", ctx->gop_size); > + } > + if (ctx->max_b_frames == 0 || ctx->max_b_frames == 1 || ctx->max_b_frames == 3 || > + ctx->max_b_frames == 7 || ctx->max_b_frames == 15) { /* number of b-frame */ > + param->bframes = ctx->max_b_frames; > + av_log(NULL, AV_LOG_INFO, "Number of max b-frames: %d\n", ctx->max_b_frames); > + } else { > + av_log(NULL, AV_LOG_ERROR, "Incorrect value for maximum number of B frames: (%d) \n" > + "Acceptable values for bf option (maximum number of B frames) are 0,1,3,7 or 15\n", ctx->max_b_frames); > + goto ERR; > + } > + > + if (ctx->level >= 0) { > + param->level_idc = ctx->level; > + } > + ret = get_pix_fmt(ctx->pix_fmt, &color_format, &xe->input_depth); > + if (ret!=0) { > + av_log((AVCodecContext*)ctx, AV_LOG_ERROR, "Unsupported pixel format.\n"); > + goto ERR; > + } > + param->cs = XEVE_CS_SET(color_format, xe->input_depth, 0); > + > + if (ctx->rc_buffer_size > 0) { > + param->vbv_bufsize = (int)(ctx->rc_buffer_size/ 1000); > + av_log(NULL, AV_LOG_INFO, "VBV buf size: %d\n", ctx->rc_buffer_size); > + } > + if (ctx->bit_rate > 0) { > + if (ctx->bit_rate / 1000 > INT_MAX || ctx->rc_max_rate / 1000 > INT_MAX) { > + av_log(NULL, AV_LOG_ERROR, "not supported bitrate bit_rate and rc_max_rate > %d000\n", INT_MAX); > + goto ERR; > + } > + param->bitrate = (int)(ctx->bit_rate / 1000); > + param->rc_type = XEVE_RC_ABR; > + } > + if (xe->op_crf >= 0) { > + param->crf = xe->op_crf; > + param->rc_type = XEVE_RC_CRF; > + } > + > + if(ctx->thread_count <= 0) { > + av_log(NULL, AV_LOG_DEBUG, "cpu_count: %d\n", cpu_count); > + param->threads = (cpu_count < XEVE_MAX_THREADS)? cpu_count: XEVE_MAX_THREADS; > + } else if(ctx->thread_count > XEVE_MAX_THREADS) { > + param->threads = XEVE_MAX_THREADS; > + } else { > + param->threads = ctx->thread_count; > + } > + av_log(NULL, AV_LOG_INFO, "param->threads: %d\n", param->threads); > + > + cdsc->param.cs = XEVE_CS_SET(color_format, param->codec_bit_depth, 0); > + cdsc->max_bs_buf_size = MAX_BS_BUF; > + > + if(ctx->profile == FF_PROFILE_EVC_BASELINE) { > + xe->profile_id = XEVE_PROFILE_BASELINE; > + } else if(ctx->profile == FF_PROFILE_EVC_MAIN) { > + xe->profile_id = XEVE_PROFILE_MAIN; > + } else { > + av_log(NULL, AV_LOG_ERROR, "Unknown encoder profile (%d)\n" > + "Acceptable values for profile option are 0 and 1 (0: baseline profile; 1: main profile)\n", ctx->profile); > + goto ERR; > + } > + if (xe->op_preset) { > + xe->preset_id = get_preset_id(xe->op_preset); > + av_log(NULL, AV_LOG_INFO, "Preset : %s\n", xe->op_preset); > + } > + if (xe->op_tune) { > + xe->tune_id = get_tune_id(xe->op_tune); > + av_log(NULL, AV_LOG_INFO, "Tune : %s\n", xe->op_tune); > + } > + > + ret = xeve_param_ppt(param, xe->profile_id, xe->preset_id, xe->tune_id); > + if (XEVE_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "cannot set profile(%d), preset(%d), tune(%d)\n", xe->profile_id, xe->preset_id, xe->tune_id); > + goto ERR; > + } > + > + /* parse :-separated list of key=value parameters and set values for created descriptor (XEVE_CDSC) */ > + { > + AVDictionaryEntry *en = NULL; > + av_log(NULL, AV_LOG_INFO, "### Start to parse xeve_params ###\n"); > + while ((en = av_dict_get(xe->xeve_params, "", en, AV_DICT_IGNORE_SUFFIX))) { > + int parse_ret = parse_xeve_params(en->key, en->value, xe, param); > + > + switch (parse_ret) { > + case XEVE_PARAM_BAD_NAME: > + av_log((AVCodecContext*)ctx, AV_LOG_WARNING, > + "Unknown option: %s.\n", en->key); > + break; > + case XEVE_PARAM_BAD_VALUE: > + av_log((AVCodecContext*)ctx, AV_LOG_WARNING, > + "Invalid value for %s: %s.\n", en->key, en->value); > + break; > + default: > + break; > + } > + } > + av_log(NULL, AV_LOG_INFO, "### End of parsing xeve_params ###\n"); > + } > + > + av_log(NULL, AV_LOG_INFO, "Rate control type [ 0(CQP) / 1(ABR) / 2(CRF) ] : %d\n", param->rc_type); > + av_log(NULL, AV_LOG_INFO, "crf=%d, bitrate=%d, vbv_bufsize=%d, fps=%d\n", param->crf, param->bitrate, param->vbv_bufsize, param->fps); > + > + return 0; > + > +ERR: > + return AVERROR(EINVAL); > +} > + > +/** > + * Check codec configuration > + * > + * @param ctx codec context > + * @param cdsc contains all encoder parameters that should be initialized before its use. > + * > + * @return 0 on success, negative error code on failure > + */ > +static int check_conf(AVCodecContext *ctx, XEVE_CDSC *cdsc) > +{ > + int ret = 0; > + int min_block_size = 4; > + int pic_m; > + > + if(cdsc->param.profile == XEVE_PROFILE_BASELINE) { > + if (cdsc->param.tool_amvr == 1) { > + av_log(ctx, AV_LOG_ERROR, "AMVR cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_mmvd == 1) { > + av_log(ctx, AV_LOG_ERROR, "MMVD cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_affine == 1) { > + av_log(ctx, AV_LOG_ERROR, "Affine cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_dmvr == 1) { > + av_log(ctx, AV_LOG_ERROR, "DMVR cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_admvp == 1) { > + av_log(ctx, AV_LOG_ERROR, "ADMVP cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_hmvp == 1) { > + av_log(ctx, AV_LOG_ERROR, "HMVP cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_addb == 1) { > + av_log(ctx, AV_LOG_ERROR, "ADDB cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_alf == 1) { > + av_log(ctx, AV_LOG_ERROR, "ALF cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_htdf == 1) { > + av_log(ctx, AV_LOG_ERROR, "HTDF cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.btt == 1) { > + av_log(ctx, AV_LOG_ERROR, "BTT cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.suco == 1) { > + av_log(ctx, AV_LOG_ERROR, "SUCO cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_eipd == 1) { > + av_log(ctx, AV_LOG_ERROR, "EIPD cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_iqt == 1) { > + av_log(ctx, AV_LOG_ERROR, "IQT cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_cm_init == 1) { > + av_log(ctx, AV_LOG_ERROR, "CM_INIT cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_adcc == 1) { > + av_log(ctx, AV_LOG_ERROR, "ADCC cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_ats == 1) { > + av_log(ctx, AV_LOG_ERROR, "ATS_INTRA cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.ibc_flag == 1) { > + av_log(ctx, AV_LOG_ERROR, "IBC cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_rpl == 1) { > + av_log(ctx, AV_LOG_ERROR, "RPL cannot be on in base profile\n"); > + ret = -1; > + } > + if (cdsc->param.tool_pocs == 1) { > + av_log(ctx, AV_LOG_ERROR, "POCS cannot be on in base profile\n"); > + ret = -1; > + } > + } else { > + if (cdsc->param.tool_admvp == 0 && cdsc->param.tool_affine == 1) { > + av_log(ctx, AV_LOG_ERROR, "AFFINE cannot be on when ADMVP is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_admvp == 0 && cdsc->param.tool_amvr == 1) { > + av_log(ctx, AV_LOG_ERROR, "AMVR cannot be on when ADMVP is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_admvp == 0 && cdsc->param.tool_dmvr == 1) { > + av_log(ctx, AV_LOG_ERROR, "DMVR cannot be on when ADMVP is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_admvp == 0 && cdsc->param.tool_mmvd == 1) { > + av_log(ctx, AV_LOG_ERROR, "MMVD cannot be on when ADMVP is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_eipd == 0 && cdsc->param.ibc_flag == 1) { > + av_log(ctx, AV_LOG_ERROR, "IBC cannot be on when EIPD is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_iqt == 0 && cdsc->param.tool_ats == 1) { > + av_log(ctx, AV_LOG_ERROR, "ATS cannot be on when IQT is off\n"); > + ret = -1; > + } > + if (cdsc->param.tool_cm_init == 0 && cdsc->param.tool_adcc == 1) { > + av_log(ctx, AV_LOG_ERROR, "ADCC cannot be on when CM_INIT is off\n"); > + ret = -1; > + } > + } > + > + if (cdsc->param.btt == 1) { > + if (cdsc->param.framework_cb_max && cdsc->param.framework_cb_max < 5) { > + av_log(NULL, AV_LOG_ERROR, "Maximun Coding Block size cannot be smaller than 5\n"); > + ret = -1; > + } > + if (cdsc->param.framework_cb_max > 7) { > + av_log(NULL, AV_LOG_ERROR, "Maximun Coding Block size cannot be greater than 7\n"); > + ret = -1; > + } > + if (cdsc->param.framework_cb_min && cdsc->param.framework_cb_min < 2) { > + av_log(NULL, AV_LOG_ERROR, "Minimum Coding Block size cannot be smaller than 2\n"); > + ret = -1; > + } > + if ((cdsc->param.framework_cb_max || cdsc->param.framework_cb_min) && > + cdsc->param.framework_cb_min > cdsc->param.framework_cb_max) { > + av_log(NULL, AV_LOG_ERROR, "Minimum Coding Block size cannot be greater than Maximum coding Block size\n"); > + ret = -1; > + } > + if (cdsc->param.framework_cu14_max > 6) { > + av_log(NULL, AV_LOG_ERROR, "Maximun 1:4 Coding Block size cannot be greater than 6\n"); > + ret = -1; > + } > + if ((cdsc->param.framework_cb_max || cdsc->param.framework_cu14_max) && > + cdsc->param.framework_cu14_max > cdsc->param.framework_cb_max) { > + av_log(NULL, AV_LOG_ERROR, "Maximun 1:4 Coding Block size cannot be greater than Maximum coding Block size\n"); > + ret = -1; > + } > + if (cdsc->param.framework_tris_max > 6) { > + av_log(NULL, AV_LOG_ERROR, "Maximun Tri-split Block size be greater than 6\n"); > + ret = -1; > + } > + if ((cdsc->param.framework_tris_max || cdsc->param.framework_cb_max) && > + cdsc->param.framework_tris_max > cdsc->param.framework_cb_max) { > + av_log(NULL, AV_LOG_ERROR, "Maximun Tri-split Block size cannot be greater than Maximum coding Block size\n"); > + ret = -1; > + } > + if ((cdsc->param.framework_tris_min || cdsc->param.framework_cb_min) && > + cdsc->param.framework_tris_min < cdsc->param.framework_cb_min + 2) { > + av_log(NULL, AV_LOG_ERROR, "Maximun Tri-split Block size cannot be smaller than Minimum Coding Block size plus two\n"); > + ret = -1; > + } > + if(cdsc->param.framework_cb_min) min_block_size = 1 << cdsc->param.framework_cb_min; > + else min_block_size = 8; > + } > + > + if (cdsc->param.suco == 1) { > + if (cdsc->param.framework_suco_max > 6) { > + av_log(NULL, AV_LOG_ERROR, "Maximun SUCO size cannot be greater than 6\n"); > + ret = -1; > + } > + if (cdsc->param.framework_cb_max && cdsc->param.framework_suco_max > cdsc->param.framework_cb_max) { > + av_log(NULL, AV_LOG_ERROR,"Maximun SUCO size cannot be greater than Maximum coding Block size\n"); > + ret = -1; > + } > + if (cdsc->param.framework_suco_min < 4) { > + av_log(NULL, AV_LOG_ERROR, "Minimun SUCO size cannot be smaller than 4\n"); > + ret = -1; > + } > + if (cdsc->param.framework_cb_min && cdsc->param.framework_suco_min < cdsc->param.framework_cb_min) { > + av_log(NULL, AV_LOG_ERROR,"Minimun SUCO size cannot be smaller than Minimum coding Block size\n"); > + ret = -1; > + } > + if (cdsc->param.framework_suco_min > cdsc->param.framework_suco_max) { > + av_log(NULL, AV_LOG_ERROR, "Minimum SUCO size cannot be greater than Maximum SUCO size\n"); > + ret = -1; > + } > + } > + > + pic_m = (8 > min_block_size) ? min_block_size : 8; > + if ((cdsc->param.w & (pic_m - 1)) != 0) { > + av_log(NULL, AV_LOG_ERROR, "Current encoder does not support picture width, not multiple of max(8, minimum CU size)\n"); > + ret = -1; > + } > + if ((cdsc->param.h & (pic_m - 1)) != 0) { > + av_log(NULL, AV_LOG_ERROR, "Current encoder does not support picture height, not multiple of max(8, minimum CU size)\n"); > + ret = -1; > + } > + > + return ret; > +} > + > +/** > + * Set XEVE_CFG_SET_USE_PIC_SIGNATURE for encoder > + * > + * @param id XEVE instance identifier > + * @param ctx the structure stores all the state associated with the instance of Xeve MPEG-5 EVC encoder > + * @return XEVE pre-defined color space (@see xeve.h) on success, XEVE_CF_UNKNOWN on failure > + * > + * @todo consider removing the function > + */ > +static int set_extra_config(XEVE id, XeveContext *ctx) > +{ > + int ret, size, value; > + > + if(ctx->hash) { > + value = 1; > + size = 4; > + ret = xeve_config(id, XEVE_CFG_SET_USE_PIC_SIGNATURE, &value, &size); > + if(XEVE_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "failed to set config for picture signature\n"); > + return -1; > + } > + } > + > + return 0; > +} > + > +/** > + * Convert ffmpeg pixel format (AVPixelFormat) into XEVE pre-defined color space > + * > + * @param px_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5) > + * @return XEVE pre-defined color space (@see xeve.h) on success, XEVE_CF_UNKNOWN on failure > + */ > +static int xeve_color_space(enum AVPixelFormat pix_fmt) > +{ > + /* color space of input image */ > + int cs = XEVE_CF_UNKNOWN; > + > + switch (pix_fmt) { > + case AV_PIX_FMT_YUV420P: > + cs = XEVE_CS_YCBCR420; > + break; > + case AV_PIX_FMT_YUV420P10: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR420, 10, 1); > +#else > + cs = XEVE_CS_YCBCR420_10LE; > +#endif > + > + break; > + case AV_PIX_FMT_YUV420P12: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR420, 12, 1); > +#else > + cs = XEVE_CS_YCBCR420_12LE; > +#endif > + > + break; > + case AV_PIX_FMT_YUV422P: > + cs = XEVE_CS_YCBCR422; > + break; > + case AV_PIX_FMT_YUV422P10: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR422, 10, 1); > +#else > + cs = XEVE_CS_YCBCR422_10LE; > +#endif > + > + break; > + case AV_PIX_FMT_YUV422P12: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR422, 12, 1); > +#else > + cs = XEVE_CS_SET(XEVE_CF_YCBCR422, 12, 0); > +#endif > + > + break; > + case AV_PIX_FMT_GBRP: > + case AV_PIX_FMT_GBRP10: > + case AV_PIX_FMT_GBRP12: > + cs = XEVE_CF_UNKNOWN; > + break; > + case AV_PIX_FMT_YUV444P: > + cs = XEVE_CF_YCBCR444; > + break; > + case AV_PIX_FMT_YUV444P10: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR444, 10, 1); > +#else > + cs = XEVE_CS_YCBCR444_10LE; > +#endif > + > + break; > + case AV_PIX_FMT_YUV444P12: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR444, 12, 1); > +#else > + cs = XEVE_CS_SET(XEVE_CF_YCBCR444, 12, 0); > +#endif > + > + break; > + case AV_PIX_FMT_GRAY8: > + cs = XEVE_CF_YCBCR400; > + break; > + case AV_PIX_FMT_GRAY10: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR400, 10, 1); > +#else > + cs = XEVE_CS_YCBCR400_10LE; > +#endif > + > + break; > + case AV_PIX_FMT_GRAY12: > +#if AV_HAVE_BIGENDIAN > + cs = XEVE_CS_SET(XEVE_CF_YCBCR400, 12, 1); > +#else > + cs = XEVE_CS_YCBCR400_12LE; > +#endif > + > + break; > + default: > + cs = XEVE_CF_UNKNOWN; > + break; > + } > + return cs; > +} > + > +static int setup_bumping(XEVE id) > +{ > + int val, size; > + val = 1; > + size = sizeof(int); > + if(XEVE_FAILED(xeve_config(id, XEVE_CFG_SET_FORCE_OUT, (void *)(&val), &size))) { > + return -1; > + } > + return 0; > +} > + > +static const char* slice_type(enum AVPictureType av_pic_type) > +{ > + if(av_pic_type == AV_PICTURE_TYPE_I) { > + return "Slice Type I"; > + } else if(av_pic_type == AV_PICTURE_TYPE_P) { > + return "Slice Type P"; > + } else if(av_pic_type == AV_PICTURE_TYPE_B) { > + return "Slice Type B"; > + } > + return "Slice Type UNDEFINED"; > +} > + > +/** > + * Initialize codec static data > + * > + * @todo consider removing unused function > + */ > +static av_cold void libxeve_init_static_data(AVCodec *codec) > +{ > + UNUSED(codec); > +} > + > +/** > + * Initialize codec > + * Create encoder instance and allocate all the needed resources > + * > + * @param ctx codec context > + * @return 0 on success, negative error code on failure > + */ > +static av_cold int libxeve_init(AVCodecContext *ctx) > +{ > + XeveContext *xe = ctx->priv_data; > + unsigned char *bs_buf = NULL; > + int i, val = 0; > + int shift_h = 0; > + int shift_v = 0; > + XEVE_IMGB * imgb = NULL; > + > + XEVE_CDSC *cdsc = &(xe->cdsc); > + > + av_log(NULL, AV_LOG_DEBUG, "eXtra-fast Essential Video Encoder\n"); > + > + if(ctx->pix_fmt != AV_PIX_FMT_YUV420P && ctx->pix_fmt != AV_PIX_FMT_YUV420P10) { > + av_log(ctx, AV_LOG_ERROR, "Invalid pixel format: %s\n", av_get_pix_fmt_name(ctx->pix_fmt)); > + goto ERR; > + } > + > + /* allocate bitstream buffer */ > + bs_buf = (unsigned char*)malloc(MAX_BS_BUF); > + if(bs_buf == NULL) { > + av_log(NULL, AV_LOG_ERROR, "cannot allocate bitstream buffer, size=%d", MAX_BS_BUF); > + goto ERR; > + } > + > + /* read configurations and set values for created descriptor (XEVE_CDSC) */ > + val = get_conf(ctx, cdsc); > + if (val != XEVE_OK) { > + av_log(NULL, AV_LOG_ERROR,"cannot get configuration\n"); > + goto ERR; > + } > + > + if (check_conf(ctx, cdsc) != 0) { > + av_log(NULL, AV_LOG_ERROR,"invalid configuration\n"); > + goto ERR; > + } > + > + /* create encoder */ > + xe->id = xeve_create(cdsc, NULL); > + if(xe->id == NULL) { > + av_log(NULL, AV_LOG_ERROR, "cannot create XEVE encoder\n"); > + goto ERR; > + } > + > + if(set_extra_config(xe->id, xe)) { > + av_log(NULL, AV_LOG_ERROR, "cannot set extra configurations\n"); > + goto ERR; > + } > + > + xe->bitb.addr = bs_buf; > + xe->bitb.bsize = MAX_BS_BUF; > + > + if(av_pix_fmt_get_chroma_sub_sample(ctx->pix_fmt, &shift_h, &shift_v)) { > + av_log(ctx, AV_LOG_ERROR, "failed to get chroma shift\n"); > + goto ERR; > + } > + // YUV format explanation > + // shift_h == 1 && shift_v == 1 : YUV420 > + // shift_h == 1 && shift_v == 0 : YUV422 > + // shift_h == 0 && shift_v == 0 : YUV444 > + // > + xe->width_chroma = AV_CEIL_RSHIFT(ctx->width, shift_h); > + xe->height_chroma = AV_CEIL_RSHIFT(ctx->height, shift_v); > + > + /* set default values for input image buffer */ > + imgb = &xe->imgb; > + imgb->cs = xeve_color_space(ctx->pix_fmt); > + imgb->np = 3; /* only for yuv420p, yuv420ple */ > + for (i=0; i<imgb->np; i++) { > + imgb->x[i] = imgb->y[i] = 0; > + } > + imgb->w[0] = imgb->aw[0] = xe->width_luma; > + imgb->w[1] = imgb->w[2] = imgb->aw[1]= imgb->aw[2] = xe->width_chroma; > + imgb->h[0] = imgb->ah[0] = xe->height_luma; > + imgb->h[1] = imgb->h[2] = imgb->ah[1] = imgb->ah[2] = xe->height_chroma; > + > + xe->encod_frames = 0; > + xe->bytes_total = 0; > + xe->state = STATE_ENCODING; > + xe->packet_count = 0; > + xe->bitrate = 0; > + return 0; > + > +ERR: > + if(bs_buf) free(bs_buf); > + return -1; > +} > + > +/** > + * Encode data to an AVPacket. > + * > + * @param ctx codec context > + * @param pkt output AVPacket > + * @param[in] frame AVFrame containing the raw data to be encoded > + * @param[out] got_packet encoder sets to 0 or 1 to indicate that a > + * non-empty packet was returned in avpkt. > + * @return 0 on success, negative error code on failure > + */ > +static int libxeve_encode(AVCodecContext *ctx, AVPacket *pkt, > + const AVFrame *frame, int *got_packet) > +{ > + XeveContext *xe = NULL; > + int ret = -1; > + int xeve_cs; > + if(ctx == NULL || pkt == NULL || got_packet==NULL) { Unnecessary checks. > + av_log(ctx, AV_LOG_ERROR, "Invalid arguments\n"); > + return -1; > + } > + xe = ctx->priv_data; Same. > + if(xe == NULL) { > + av_log(ctx, AV_LOG_ERROR, "Invalid XEVE context\n"); > + return -1; > + } > + if(xe->state == STATE_SKIPPING && frame ) { > + av_log(ctx, AV_LOG_DEBUG, "Empty frame -> Entering encoding process...\n"); > + xe->state = STATE_ENCODING; > + } else if(xe->state == STATE_ENCODING && frame == NULL) { > + av_log(ctx, AV_LOG_DEBUG, "Empty frame -> Entering bumping process...\n"); > + if (setup_bumping(xe->id) == 0) { > + xe->state = STATE_BUMPING; > + } else { > + av_log(ctx, AV_LOG_ERROR,"Failed to setup bumping\n"); > + xe->state = STATE_SKIPPING; > + } > + } > + > + if(xe->state == STATE_ENCODING) { > + const AVPixFmtDescriptor *pixel_fmt_desc = av_pix_fmt_desc_get (frame->format); > + if(!pixel_fmt_desc) { > + av_log(NULL, AV_LOG_ERROR, "Invalid pixel format descriptor for pixel format: %s\n", av_get_pix_fmt_name(ctx->pix_fmt)); > + return -1; > + } > + > + xeve_cs = xeve_color_space(ctx->pix_fmt); > + if(xeve_cs != XEVE_CS_YCBCR420 && xeve_cs != XEVE_CS_YCBCR420_10LE) { > + av_log(ctx, AV_LOG_ERROR, "Invalid pixel format: %s\n", av_get_pix_fmt_name(ctx->pix_fmt)); > + return -1; > + } > + > + { > + int i; > + XEVE_IMGB * imgb = NULL; > + int xeve_byte_depth = 0; > + > + imgb = &xe->imgb; > + > + xeve_byte_depth = XEVE_CS_GET_BYTE_DEPTH(xeve_cs); > + av_log(ctx, AV_LOG_DEBUG, "byte depth: %d\n",xeve_byte_depth); > + > + for (i=0; i<imgb->np; i++) { > + imgb->a[i] = frame->data[i]; > + imgb->s[i] = frame->linesize[i]; > + } > + > + if(xe->id == NULL) { > + av_log(ctx, AV_LOG_ERROR, "Invalid XEVE encoder\n"); > + return -1; > + } > + > + imgb->ts[0] = frame->pts; > + imgb->ts[1] = 0; > + imgb->ts[2] = 0; > + imgb->ts[3] = 0; > + > + /* push image to encoder */ > + av_log(ctx, AV_LOG_DEBUG, "INPUT | RAW frame | timestamps | %lld | %lld | %lld | %lld |\n", imgb->ts[0], imgb->ts[1], imgb->ts[2], imgb->ts[3]); > + > + ret = xeve_push(xe->id, imgb); > + if(XEVE_FAILED(ret)) { > + av_log(ctx, AV_LOG_ERROR, "xeve_push() failed\n"); > + return -1; > + } > + } > + } > + if(xe->state == STATE_ENCODING || xe->state == STATE_BUMPING) { > + > + /* encoding */ > + ret = xeve_encode(xe->id, &(xe->bitb), &(xe->stat)); > + if(XEVE_FAILED(ret)) { > + av_log(ctx, AV_LOG_ERROR, "xeve_encode() failed\n"); > + return -1; > + } > + > + xe->encod_frames++; > + > + /* store bitstream */ > + if (ret == XEVE_OK_OUT_NOT_AVAILABLE) { > + av_log(ctx, AV_LOG_DEBUG, "RETURN OK BUT PICTURE IS NOT AVAILABLE YET (%d) frame: %d\n", ret, xe->encod_frames); > + *got_packet = 0; > + return 0; > + } else if(ret == XEVE_OK) { > + int av_pic_type; > + > + if(xe->stat.write > 0) { > + xe->bytes_total+=xe->stat.write; > + // av_log(ctx, AV_LOG_DEBUG, "frame: %d | Bytes written: %d | bytes total: %f | fnum %d | %lld | %lld | %lld | %lld |\n", xe->encod_frames, xe->stat.write, xe->bytes_total, xe->stat.fnum, xe->bitb.ts[0],xe->bitb.ts[1],xe->bitb.ts[2],xe->bitb.ts[3]); > + > + ret = av_grow_packet(pkt, xe->stat.write); ff_get_encode_buffer(). > + if (ret < 0) { > + av_log(ctx, AV_LOG_ERROR, "Can't allocate memory for AVPacket data\n"); > + return ret; > + } > + > + memcpy(pkt->data, xe->bitb.addr, xe->stat.write); > + > + pkt->pts = xe->bitb.ts[0]; > + pkt->dts = xe->bitb.ts[1]; > + > + av_log(NULL, AV_LOG_DEBUG, "PTS: %ld | DTS: %ld\n", pkt->pts, pkt->dts); > + > + xe->bitrate += (xe->stat.write - xe->stat.sei_size); > + > + switch(xe->stat.stype) { > + case XEVE_ST_I: > + av_pic_type = AV_PICTURE_TYPE_I; > + pkt->flags |= AV_PKT_FLAG_KEY; > + break; > + case XEVE_ST_P: > + av_pic_type = AV_PICTURE_TYPE_P; > + break; > + case XEVE_ST_B: > + av_pic_type = AV_PICTURE_TYPE_B; > + break; > + case XEVE_ST_UNKNOWN: > + av_log(NULL, AV_LOG_ERROR, "unknown slice type\n"); > + return -1; > + } > + > + av_log(ctx, AV_LOG_DEBUG, "OUTPUT | Encoded | slice type: %s | fnum: %ld | poc: %d | Bytes written: %d | bytes total: %f | timestamps | %lld | %lld | %lld | %lld |\n", > + slice_type(av_pic_type), > + xe->stat.fnum, > + xe->stat.poc, > + xe->stat.write, > + xe->bytes_total, > + xe->bitb.ts[0], > + xe->bitb.ts[1], > + xe->bitb.ts[2], > + xe->bitb.ts[3]); > + > + ff_side_data_set_encoder_stats(pkt, xe->stat.qp*FF_QP2LAMBDA, NULL, 0, av_pic_type); > + > + xe->bitrate += (xe->stat.write - xe->stat.sei_size); > + > + *got_packet = 1; > + xe->packet_count++; > + } > + } else if (ret == XEVE_OK_NO_MORE_FRM) { > + av_log(ctx, AV_LOG_INFO, "Return OK but no more frames (%d)\n", ret); > + return 0; > + } else { > + av_log(ctx, AV_LOG_DEBUG, "Invalid return value (%d)\n", ret); > + return -1; > + } > + } else { > + av_log(NULL, AV_LOG_ERROR, "Udefined state: %d\n", xe->state); > + return -1; > + } > + return 0; > +} > + > +/** > + * Destroy encoder and release all the allocated resources > + * > + * @param ctx codec context > + * @return 0 on success, negative error code on failure > + */ > +static av_cold int libxeve_close(AVCodecContext *ctx) > +{ > + XeveContext *xe = ctx->priv_data; > + > + xeve_delete(xe->id); > + > + if(xe->bitb.addr) free(xe->bitb.addr); /* release bitstream buffer */ > + > + return 0; > +} > + > +#define OFFSET(x) offsetof(XeveContext, x) > +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM > + > +// Example of using: ./ffmpeg -xeve-params "m=2:q=17" > +// Consider using following options (./ffmpeg --help encoder=libxeve) > +// > +static const AVOption xeve_options[] = { > + { "preset", "Encoding preset for setting encoding speed [fast, medium, slow, placebo]", OFFSET(op_preset), AV_OPT_TYPE_STRING, { .str = "medium" }, 0, 0, VE }, > + { "tune", "Tuneing parameter for special purpose operation [psnr, zerolatency]", OFFSET(op_tune), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE}, > + { "qp", "quantization parameter qp <0..51> [default: 32]", OFFSET(op_qp), AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE }, > + { "crf", "constant rate factor <-1..51> [default: 32]", OFFSET(op_crf), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE }, > + { "xeve-params", "override the xeve configuration using a :-separated list of key=value parameters", OFFSET(xeve_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, > + { NULL } > +}; > + > +static const AVClass xeve_class = { > + .class_name = "libxeve", > + .item_name = av_default_item_name, > + .option = xeve_options, > + .version = LIBAVUTIL_VERSION_INT, > +}; > + > +/** > + * libavcodec generic global options, which can be set on all the encoders and decoders > + * @see https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options > + */ > +static const AVCodecDefault xeve_defaults[] = { > + { "b", "0" }, // bitrate > + { "g", "0" }, // gop_size (key-frame interval 0: only one I-frame at the first time; 1: every frame is coded in I-frame) > + { "bf", "15"}, // bframes (0: no B-frames) > + { "profile", "0"}, // encoder codec profile (0: baselie; 1: main) > + { "threads", "0"}, // number of threads to be used (0: automatically select the number of threads to set) > + { NULL }, > +}; > + > +AVCodec ff_libxeve_encoder = { > + .name = "libxeve", > + .long_name = NULL_IF_CONFIG_SMALL("libxeve MPEG-5 EVC"), > + .type = AVMEDIA_TYPE_VIDEO, > + .id = AV_CODEC_ID_EVC, > + .init = libxeve_init, > + .init_static_data = libxeve_init_static_data, > + .encode2 = libxeve_encode, > + .close = libxeve_close, > + .priv_data_size = sizeof(XeveContext), > + .priv_class = &xeve_class, > + .defaults = xeve_defaults, > + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS | > + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, You're not setting reordered_opaque, so why signal this codec capability? Also add AV_CODEC_CAP_DR1 if you use ff_get_encode_buffer(). > + .wrapper_name = "libxeve", > +}; > diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c > index 6b40c18d80..295fa33f52 100644 > --- a/libavcodec/parsers.c > +++ b/libavcodec/parsers.c > @@ -73,6 +73,7 @@ extern const AVCodecParser ff_vp9_parser; > extern const AVCodecParser ff_webp_parser; > extern const AVCodecParser ff_xbm_parser; > extern const AVCodecParser ff_xma_parser; > +extern const AVCodecParser ff_evc_parser; > > #include "libavcodec/parser_list.c" > > diff --git a/libavcodec/profiles.c b/libavcodec/profiles.c > index 7af7fbeb13..a31244e0db 100644 > --- a/libavcodec/profiles.c > +++ b/libavcodec/profiles.c > @@ -181,4 +181,10 @@ const AVProfile ff_arib_caption_profiles[] = { > { FF_PROFILE_UNKNOWN } > }; > > +const AVProfile ff_evc_profiles[] = { > + { FF_PROFILE_EVC_BASELINE, "Baseline" }, > + { FF_PROFILE_EVC_MAIN, "Main" }, > + { FF_PROFILE_UNKNOWN }, > +}; > + > #endif /* !CONFIG_SMALL */ > diff --git a/libavcodec/profiles.h b/libavcodec/profiles.h > index 41a19aa9ad..cf92b5f126 100644 > --- a/libavcodec/profiles.h > +++ b/libavcodec/profiles.h > @@ -72,5 +72,6 @@ extern const AVProfile ff_sbc_profiles[]; > extern const AVProfile ff_prores_profiles[]; > extern const AVProfile ff_mjpeg_profiles[]; > extern const AVProfile ff_arib_caption_profiles[]; > +extern const AVProfile ff_evc_profiles[]; > > #endif /* AVCODEC_PROFILES_H */ > diff --git a/libavcodec/version.h b/libavcodec/version.h > index 4d77431842..fb1a0feec6 100644 > --- a/libavcodec/version.h > +++ b/libavcodec/version.h > @@ -28,7 +28,7 @@ > #include "libavutil/version.h" > > #define LIBAVCODEC_VERSION_MAJOR 59 > -#define LIBAVCODEC_VERSION_MINOR 18 > +#define LIBAVCODEC_VERSION_MINOR 19 > #define LIBAVCODEC_VERSION_MICRO 100 > > #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ > diff --git a/libavformat/Makefile b/libavformat/Makefile > index 84e73e3c63..db461e79b7 100644 > --- a/libavformat/Makefile > +++ b/libavformat/Makefile > @@ -241,6 +241,8 @@ OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o pcm.o > OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o > OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o > OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o > +OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o > +OBJS-$(CONFIG_EVC_MUXER) += rawenc.o > OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o > OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o > OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o > diff --git a/libavformat/allformats.c b/libavformat/allformats.c > index d066a7745b..1148024e71 100644 > --- a/libavformat/allformats.c > +++ b/libavformat/allformats.c > @@ -145,6 +145,8 @@ extern const AVInputFormat ff_ea_cdata_demuxer; > extern const AVInputFormat ff_eac3_demuxer; > extern const AVOutputFormat ff_eac3_muxer; > extern const AVInputFormat ff_epaf_demuxer; > +extern const AVInputFormat ff_evc_demuxer; > +extern const AVOutputFormat ff_evc_muxer; > extern const AVOutputFormat ff_f4v_muxer; > extern const AVInputFormat ff_ffmetadata_demuxer; > extern const AVOutputFormat ff_ffmetadata_muxer; > diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c > new file mode 100644 > index 0000000000..dd9102cdd4 > --- /dev/null > +++ b/libavformat/evcdec.c > @@ -0,0 +1,136 @@ > +/* > + * RAW EVC video demuxer > + * > + * Copyright (c) 2021 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 "libavcodec/get_bits.h" > +#include "libavcodec/golomb.h" > +#include "avformat.h" > +#include "rawdec.h" > +#include "libavcodec/internal.h" > +#include "xevd.h" Like with the parser, the demuxer and muxer can't depend on external libraries. > + > +typedef struct EVCParserContext { > + int got_sps; > + int got_pps; > + int got_idr; > + int got_nonidr; > +} EVCParserContext; > + > +static int get_nalu_type(const uint8_t *bs, int bs_size) > +{ > + GetBitContext gb; > + int fzb, nut; > + init_get_bits(&gb, bs, bs_size * 8); > + fzb = get_bits1(&gb); > + if(fzb != 0) { > + av_log(NULL, AV_LOG_DEBUG, "forbidden_zero_bit is not clear\n"); > + } > + nut = get_bits(&gb, 6); /* nal_unit_type_plus1 */ > + return nut - 1; > +} > + > +/** > + * Read NAL unit length > + * @param bs input data (bitstream) > + * @return the lenghth of NAL unit on success, 0 value on failure > + */ > +static uint32_t read_nal_unit_length(const uint8_t *bs, int bs_size) > +{ > + uint32_t len = 0; > + XEVD_INFO info; > + int ret; > + > + if(bs_size>=XEVD_NAL_UNIT_LENGTH_BYTE) { > + ret = xevd_info((void*)bs, XEVD_NAL_UNIT_LENGTH_BYTE, 1, &info); > + if (XEVD_FAILED(ret)) { > + av_log(NULL, AV_LOG_ERROR, "Cannot get bitstream information\n"); > + return 0; > + } > + len = info.nalu_len; > + if(len == 0) > + { > + av_log(NULL, AV_LOG_ERROR, "Invalid bitstream size! [%d]\n", bs_size); > + return 0; > + } > + } > + return len; > +} > + > +static int parse_nal_units(const AVProbeData *p, EVCParserContext *ev) > +{ > + int nalu_type; > + size_t nalu_size; > + unsigned char* bits = (unsigned char *)p->buf; > + int bytes_to_read = p->buf_size; > + > + av_log(NULL, AV_LOG_DEBUG, "bytes_to_read: %d \n", bytes_to_read); > + > + while(bytes_to_read > XEVD_NAL_UNIT_LENGTH_BYTE) { > + > + nalu_size = read_nal_unit_length(bits, XEVD_NAL_UNIT_LENGTH_BYTE); > + if(nalu_size == 0) break; > + > + bits += XEVD_NAL_UNIT_LENGTH_BYTE; > + bytes_to_read -= XEVD_NAL_UNIT_LENGTH_BYTE; > + > + av_log(NULL, AV_LOG_DEBUG, "nalu_size: %ld \n", nalu_size); > + > + if(bytes_to_read < nalu_size) break; > + > + nalu_type = get_nalu_type(bits, bytes_to_read); > + > + bits += nalu_size; > + bytes_to_read -= nalu_size; > + > + if (nalu_type == XEVD_NUT_SPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_SPS \n"); > + ev->got_sps++; > + } > + else if (nalu_type == XEVD_NUT_PPS) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_PPS \n"); > + ev->got_pps++; > + } > + else if (nalu_type == XEVD_NUT_IDR ) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_IDR\n"); > + ev->got_idr++; > + } > + else if (nalu_type == XEVD_NUT_NONIDR) { > + av_log(NULL, AV_LOG_DEBUG, "XEVD_NUT_NONIDR\n"); > + ev->got_nonidr++; > + } > + } > + return 0; > +} > + > +static int evc_probe(const AVProbeData *p) > +{ > + EVCParserContext ev = {}; > + int ret = parse_nal_units(p, &ev); > + > + av_log(NULL, AV_LOG_DEBUG, "sps:%d pps:%d idr:%d sli:%d\n", ev.got_sps, ev.got_pps, ev.got_idr, ev.got_nonidr); > + > + if (ret == 0 && ev.got_sps && ev.got_pps && (ev.got_idr || ev.got_nonidr > 3)) > + return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg > + > + return 0; > +} > + > +FF_DEF_RAWVIDEO_DEMUXER(evc, "raw EVC video", evc_probe, "evc", AV_CODEC_ID_EVC) > diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c > index 62e60470a8..0245cfb999 100644 > --- a/libavformat/isom_tags.c > +++ b/libavformat/isom_tags.c > @@ -145,6 +145,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = { > { AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', '1') }, /* AVC-based Dolby Vision derived from avc1 */ > { AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', 'v') }, /* AVC-based Dolby Vision derived from avc3 */ > > + { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, /* EVC/MPEG-5 */ > + > { AV_CODEC_ID_VP8, MKTAG('v', 'p', '0', '8') }, /* VP8 */ > { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, /* VP9 */ > { AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') }, /* AV1 */ > diff --git a/libavformat/mov.c b/libavformat/mov.c > index 6fb09df7e1..e83872fbe2 100644 > --- a/libavformat/mov.c > +++ b/libavformat/mov.c > @@ -8568,7 +8568,7 @@ const AVInputFormat ff_mov_demuxer = { > .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), > .priv_class = &mov_class, > .priv_data_size = sizeof(MOVContext), > - .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v", > + .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,evc", > .flags_internal = FF_FMT_INIT_CLEANUP, > .read_probe = mov_probe, > .read_header = mov_read_header, > diff --git a/libavformat/movenc.c b/libavformat/movenc.c > index 4c868919ae..30e5037f06 100644 > --- a/libavformat/movenc.c > +++ b/libavformat/movenc.c > @@ -1342,6 +1342,16 @@ static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track) > return update_size(pb, pos); > } > > +static int mov_write_evcc_tag(AVIOContext *pb, MOVTrack *track) > +{ > + int64_t pos = avio_tell(pb); > + > + avio_wb32(pb, 0); > + ffio_wfourcc(pb, "evcC"); > + ff_isom_write_avcc(pb, track->vos_data, track->vos_len); Why are you writing h264 extradata? Especially when you did not define any as exported by the encoder or the mov demuxer. > + return update_size(pb, pos); > +} > + > /* also used by all avid codecs (dv, imx, meridien) and their variants */ > static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) > { > @@ -1591,6 +1601,19 @@ static int mov_get_h264_codec_tag(AVFormatContext *s, MOVTrack *track) > return tag; > } > > +static int mov_get_evc_codec_tag(AVFormatContext *s, MOVTrack *track) > +{ > + int tag = track->par->codec_tag; > + int interlaced = track->par->field_order > AV_FIELD_PROGRESSIVE; > + AVStream *st = track->st; > + int rate = defined_frame_rate(s, st); > + > + if (!tag) > + tag = MKTAG('e', 'v', 'c', 'i'); //fallback tag > + > + return tag; > +} > + > static const struct { > enum AVPixelFormat pix_fmt; > uint32_t tag; > @@ -1672,6 +1695,8 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) > tag = mov_get_mpeg2_xdcam_codec_tag(s, track); > else if (track->par->codec_id == AV_CODEC_ID_H264) > tag = mov_get_h264_codec_tag(s, track); > + else if (track->par->codec_id == AV_CODEC_ID_EVC) > + tag = mov_get_evc_codec_tag(s, track); > else if (track->par->codec_id == AV_CODEC_ID_DNXHD) > tag = mov_get_dnxhd_codec_tag(s, track); > else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { > @@ -2208,6 +2233,9 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex > mov_write_avcc_tag(pb, track); > if (track->mode == MODE_IPOD) > mov_write_uuid_tag_ipod(pb); > + } > + else if (track->par->codec_id ==AV_CODEC_ID_EVC) { > + mov_write_evcc_tag(pb, track); > } else if (track->par->codec_id == AV_CODEC_ID_VP9) { > mov_write_vpcc_tag(mov->fc, pb, track); > } else if (track->par->codec_id == AV_CODEC_ID_AV1) { > @@ -5737,6 +5765,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) > if ((par->codec_id == AV_CODEC_ID_DNXHD || > par->codec_id == AV_CODEC_ID_H264 || > par->codec_id == AV_CODEC_ID_HEVC || > + par->codec_id == AV_CODEC_ID_EVC || > par->codec_id == AV_CODEC_ID_TRUEHD || > par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len && > !TAG_IS_AVCI(trk->tag)) { > @@ -7311,6 +7340,7 @@ static const AVCodecTag codec_mp4_tags[] = { > { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') }, > { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') }, > { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, > + { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, > { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') }, > { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, > { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, > diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c > index 4bbae7717b..322ab583ab 100644 > --- a/libavformat/rawenc.c > +++ b/libavformat/rawenc.c > @@ -386,6 +386,20 @@ const AVOutputFormat ff_hevc_muxer = { > }; > #endif > > +#if CONFIG_EVC_MUXER > +AVOutputFormat ff_evc_muxer = { > + .name = "evc", > + .long_name = NULL_IF_CONFIG_SMALL("raw EVC video"), > + .extensions = "evc", > + .audio_codec = AV_CODEC_ID_NONE, > + .video_codec = AV_CODEC_ID_EVC, > + .write_header = force_one_stream, > + .write_packet = ff_raw_write_packet, > + .flags = AVFMT_NOTIMESTAMPS, > +}; > +#endif > + > + > #if CONFIG_M4V_MUXER > const AVOutputFormat ff_m4v_muxer = { > .name = "m4v", _______________________________________________ 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".
next prev parent reply other threads:[~2022-04-05 18:52 UTC|newest] Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <CGME20220404122922eucas1p1013831b43a516e8460cfe56737a38884@eucas1p1.samsung.com> 2022-04-04 12:29 ` Dawid Kozinski 2022-04-05 17:37 ` Andreas Rheinhardt 2022-05-12 6:06 ` Dawid Kozinski/Robot SDK (PLT) /SRPOL/Staff Engineer/삼성전자 2022-04-05 18:52 ` James Almer [this message] 2022-05-12 6:07 ` Dawid Kozinski/Robot SDK (PLT) /SRPOL/Staff Engineer/삼성전자 [not found] <CGME20220304123116eucas1p1f85bc6a4b84bd6bf94db0d0ecbb8cec0@eucas1p1.samsung.com> 2022-03-04 12:31 ` Dawid Kozinski [not found] <CGME20220304113931eucas1p1933f1dcea20d4f5f871c438b94bcb6b8@eucas1p1.samsung.com> 2022-03-04 11:39 ` Dawid Kozinski
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=fa125379-9918-eea1-6790-6209553782d1@gmail.com \ --to=jamrial@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
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