| /* |
| * 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 |
| */ |
| |
| #include <float.h> |
| #include <stdlib.h> |
| |
| #include <xeve.h> |
| |
| #include "libavutil/internal.h" |
| #include "libavutil/common.h" |
| #include "libavutil/mem.h" |
| #include "libavutil/opt.h" |
| #include "libavutil/pixdesc.h" |
| #include "libavutil/pixfmt.h" |
| #include "libavutil/time.h" |
| #include "libavutil/cpu.h" |
| #include "libavutil/avstring.h" |
| |
| #include "avcodec.h" |
| #include "internal.h" |
| #include "packet_internal.h" |
| #include "codec_internal.h" |
| #include "profiles.h" |
| #include "encode.h" |
| |
| #define MAX_BS_BUF (16*1024*1024) |
| |
| /** |
| * Error codes |
| */ |
| #define XEVE_PARAM_BAD_NAME -100 |
| #define XEVE_PARAM_BAD_VALUE -200 |
| |
| /** |
| * 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 |
| */ |
| typedef enum State { |
| STATE_ENCODING, |
| STATE_BUMPING, |
| } State; |
| |
| /** |
| * The structure stores all the states associated with the instance of Xeve MPEG-5 EVC encoder |
| */ |
| 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 profile_id; // encoder profile (main, baseline) |
| int preset_id; // preset of xeve ( fast, medium, slow, placebo) |
| int tune_id; // tune of xeve (psnr, zerolatency) |
| |
| // variables for rate control modes |
| int rc_mode; // Rate control mode [ 0(CQP) / 1(ABR) / 2(CRF) ] |
| int qp; // quantization parameter (QP) [0,51] |
| int crf; // constant rate factor (CRF) [10,49] |
| |
| int hash; // embed picture signature (HASH) for conformance checking in decoding |
| int sei_info; // embed Supplemental enhancement information while encoding |
| |
| int color_format; // input data color format: currently only XEVE_CF_YCBCR420 is supported |
| |
| AVDictionary *xeve_params; |
| } XeveContext; |
| |
| /** |
| * Convert FFmpeg pixel format (AVPixelFormat) to XEVE pre-defined color format |
| * |
| * @param[in] av_pix_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5) |
| * @param[out] xeve_col_fmt XEVE pre-defined color format (@see xeve.h) |
| * |
| * @return 0 on success, negative value on failure |
| */ |
| static int libxeve_color_fmt(enum AVPixelFormat av_pix_fmt, int *xeve_col_fmt) |
| { |
| switch (av_pix_fmt) { |
| case AV_PIX_FMT_YUV420P: |
| *xeve_col_fmt = XEVE_CF_YCBCR420; |
| break; |
| case AV_PIX_FMT_YUV420P10: |
| *xeve_col_fmt = XEVE_CF_YCBCR420; |
| break; |
| default: |
| *xeve_col_fmt = XEVE_CF_UNKNOWN; |
| return AVERROR_INVALIDDATA; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Convert FFmpeg pixel format (AVPixelFormat) into XEVE pre-defined color space |
| * |
| * @param[in] 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 libxeve_color_space(enum AVPixelFormat av_pix_fmt) |
| { |
| /* color space of input image */ |
| int cs = XEVE_CF_UNKNOWN; |
| |
| switch (av_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; |
| default: |
| cs = XEVE_CF_UNKNOWN; |
| break; |
| } |
| |
| return cs; |
| } |
| |
| /** |
| * The function returns a pointer to the object of the XEVE_CDSC type. |
| * XEVE_CDSC contains all encoder parameters that should be initialized before the encoder is used. |
| * |
| * 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) |
| * |
| * The order of processing input data and populating the XEVE_CDSC structure |
| * 1) first, the fields of the AVCodecContext structure corresponding to the provided input options 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 -preset 0) |
| * |
| * Keep in mind that, 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[in] avctx codec context (AVCodecContext) |
| * @param[out] cdsc contains all Xeve MPEG-5 EVC encoder encoder parameters that should be initialized before the encoder is use |
| * |
| * @return 0 on success, negative error code on failure |
| */ |
| static int get_conf(AVCodecContext *avctx, XEVE_CDSC *cdsc) |
| { |
| XeveContext *xectx = NULL; |
| int ret; |
| |
| xectx = avctx->priv_data; |
| |
| /* initialize xeve_param struct with default values */ |
| ret = xeve_param_default(&cdsc->param); |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot set_default parameter\n"); |
| return AVERROR_EXTERNAL; |
| } |
| |
| /* read options from AVCodecContext */ |
| if (avctx->width > 0) |
| cdsc->param.w = avctx->width; |
| |
| if (avctx->height > 0) |
| cdsc->param.h = avctx->height; |
| |
| if (avctx->framerate.num > 0) { |
| // fps can be float number, but xeve API doesn't support it |
| cdsc->param.fps = lrintf(av_q2d(avctx->framerate)); |
| } |
| |
| // GOP size (key-frame interval, I-picture period) |
| cdsc->param.keyint = avctx->gop_size; // 0: only one I-frame at the first time; 1: every frame is coded in I-frame |
| |
| if (avctx->max_b_frames == 0 || avctx->max_b_frames == 1 || avctx->max_b_frames == 3 || |
| avctx->max_b_frames == 7 || avctx->max_b_frames == 15) // number of b-frames |
| cdsc->param.bframes = avctx->max_b_frames; |
| else { |
| av_log(avctx, 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", avctx->max_b_frames); |
| return AVERROR_INVALIDDATA; |
| } |
| |
| cdsc->param.level_idc = avctx->level; |
| |
| if (avctx->rc_buffer_size) // VBV buf size |
| cdsc->param.vbv_bufsize = (int)(avctx->rc_buffer_size / 1000); |
| |
| cdsc->param.rc_type = xectx->rc_mode; |
| |
| if (xectx->rc_mode == XEVE_RC_CQP) |
| cdsc->param.qp = xectx->qp; |
| else if (xectx->rc_mode == XEVE_RC_ABR) { |
| if (avctx->bit_rate / 1000 > INT_MAX || avctx->rc_max_rate / 1000 > INT_MAX) { |
| av_log(avctx, AV_LOG_ERROR, "Not supported bitrate bit_rate and rc_max_rate > %d000\n", INT_MAX); |
| return AVERROR_INVALIDDATA; |
| } |
| cdsc->param.bitrate = (int)(avctx->bit_rate / 1000); |
| } else if (xectx->rc_mode == XEVE_RC_CRF) |
| cdsc->param.crf = xectx->crf; |
| else { |
| av_log(avctx, AV_LOG_ERROR, "Not supported rate control type: %d\n", xectx->rc_mode); |
| return AVERROR_INVALIDDATA; |
| } |
| |
| if (avctx->thread_count <= 0) { |
| int cpu_count = av_cpu_count(); |
| cdsc->param.threads = (cpu_count < XEVE_MAX_THREADS) ? cpu_count : XEVE_MAX_THREADS; |
| } else if (avctx->thread_count > XEVE_MAX_THREADS) |
| cdsc->param.threads = XEVE_MAX_THREADS; |
| else |
| cdsc->param.threads = avctx->thread_count; |
| |
| |
| libxeve_color_fmt(avctx->pix_fmt, &xectx->color_format); |
| |
| cdsc->param.cs = XEVE_CS_SET(xectx->color_format, cdsc->param.codec_bit_depth, AV_HAVE_BIGENDIAN); |
| |
| cdsc->max_bs_buf_size = MAX_BS_BUF; |
| |
| ret = xeve_param_ppt(&cdsc->param, xectx->profile_id, xectx->preset_id, xectx->tune_id); |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot set profile(%d), preset(%d), tune(%d)\n", xectx->profile_id, xectx->preset_id, xectx->tune_id); |
| return AVERROR_EXTERNAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Set XEVE_CFG_SET_USE_PIC_SIGNATURE for encoder |
| * |
| * @param[in] logger context |
| * @param[in] id XEVE encodec instance identifier |
| * @param[in] ctx the structure stores all the states associated with the instance of Xeve MPEG-5 EVC encoder |
| * |
| * @return 0 on success, negative error code on failure |
| */ |
| static int set_extra_config(AVCodecContext *avctx, XEVE id, XeveContext *ctx) |
| { |
| int ret, size; |
| size = 4; |
| |
| // embed SEI messages identifying encoder parameters and command line arguments |
| // - 0: off\n" |
| // - 1: emit sei info" |
| // |
| // SEI - Supplemental enhancement information contains information |
| // that is not necessary to decode the samples of coded pictures from VCL NAL units. |
| // Some SEI message information is required to check bitstream conformance |
| // and for output timing decoder conformance. |
| // @see ISO_IEC_23094-1_2020 7.4.3.5 |
| // @see ISO_IEC_23094-1_2020 Annex D |
| ret = xeve_config(id, XEVE_CFG_SET_SEI_CMD, &ctx->sei_info, &size); // sei_cmd_info |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to set config for sei command info messages\n"); |
| return AVERROR_EXTERNAL; |
| } |
| |
| ret = xeve_config(id, XEVE_CFG_SET_USE_PIC_SIGNATURE, &ctx->hash, &size); |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to set config for picture signature\n"); |
| return AVERROR_EXTERNAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Switch encoder to bumping mode |
| * |
| * @param id XEVE encodec instance identifier |
| * @return 0 on success, negative error code on failure |
| */ |
| static int setup_bumping(XEVE id) |
| { |
| int val = 1; |
| int size = sizeof(int); |
| if (XEVE_FAILED(xeve_config(id, XEVE_CFG_SET_FORCE_OUT, (void *)(&val), &size))) |
| return AVERROR_EXTERNAL; |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Initialize eXtra-fast Essential Video Encoder codec |
| * Create an encoder instance and allocate all the needed resources |
| * |
| * @param avctx codec context |
| * @return 0 on success, negative error code on failure |
| */ |
| static av_cold int libxeve_init(AVCodecContext *avctx) |
| { |
| XeveContext *xectx = avctx->priv_data; |
| unsigned char *bs_buf = NULL; |
| int i; |
| int shift_h = 0; |
| int shift_v = 0; |
| int width_chroma = 0; |
| int height_chroma = 0; |
| XEVE_IMGB *imgb = NULL; |
| int ret = 0; |
| |
| XEVE_CDSC *cdsc = &(xectx->cdsc); |
| |
| /* allocate bitstream buffer */ |
| bs_buf = av_malloc(MAX_BS_BUF); |
| if (bs_buf == NULL) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot allocate bitstream buffer\n"); |
| return AVERROR(ENOMEM); |
| } |
| xectx->bitb.addr = bs_buf; |
| xectx->bitb.bsize = MAX_BS_BUF; |
| |
| /* read configurations and set values for created descriptor (XEVE_CDSC) */ |
| if ((ret = get_conf(avctx, cdsc)) != 0) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot get configuration\n"); |
| return AVERROR(EINVAL); |
| } |
| |
| if ((ret = xeve_param_check(&cdsc->param)) != 0) { |
| av_log(avctx, AV_LOG_ERROR, "Invalid configuration\n"); |
| return AVERROR(EINVAL); |
| } |
| |
| { |
| const AVDictionaryEntry *en = NULL; |
| while (en = av_dict_iterate(xectx->xeve_params, en)) { |
| if ((ret = xeve_param_parse(&cdsc->param, en->key, en->value)) < 0) { |
| av_log(avctx, AV_LOG_WARNING, |
| "Error parsing option '%s = %s'.\n", |
| en->key, en->value); |
| } |
| } |
| } |
| |
| /* create encoder */ |
| xectx->id = xeve_create(cdsc, NULL); |
| if (xectx->id == NULL) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot create XEVE encoder\n"); |
| return AVERROR_EXTERNAL; |
| } |
| |
| if ((ret = set_extra_config(avctx, xectx->id, xectx)) != 0) { |
| av_log(avctx, AV_LOG_ERROR, "Cannot set extra configuration\n"); |
| return AVERROR(EINVAL); |
| } |
| |
| if ((ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &shift_h, &shift_v)) != 0) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to get chroma shift\n"); |
| return AVERROR(EINVAL); |
| } |
| |
| // Chroma subsampling |
| // |
| // YUV format explanation |
| // shift_h == 1 && shift_v == 1 : YUV420 |
| // shift_h == 1 && shift_v == 0 : YUV422 |
| // shift_h == 0 && shift_v == 0 : YUV444 |
| // |
| width_chroma = AV_CEIL_RSHIFT(avctx->width, shift_h); |
| height_chroma = AV_CEIL_RSHIFT(avctx->height, shift_v); |
| |
| /* set default values for input image buffer */ |
| imgb = &xectx->imgb; |
| imgb->cs = libxeve_color_space(avctx->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] = avctx->width; // width luma |
| imgb->w[1] = imgb->w[2] = imgb->aw[1] = imgb->aw[2] = width_chroma; |
| imgb->h[0] = imgb->ah[0] = avctx->height; // height luma |
| imgb->h[1] = imgb->h[2] = imgb->ah[1] = imgb->ah[2] = height_chroma; |
| |
| xectx->state = STATE_ENCODING; |
| |
| return 0; |
| } |
| |
| /** |
| * Encode raw data frame into EVC packet |
| * |
| * @param[in] avctx codec context |
| * @param[out] avpkt output AVPacket containing encoded data |
| * @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 pkt |
| * |
| * @return 0 on success, negative error code on failure |
| */ |
| static int libxeve_encode(AVCodecContext *avctx, AVPacket *avpkt, |
| const AVFrame *frame, int *got_packet) |
| { |
| XeveContext *xectx = avctx->priv_data; |
| int ret = -1; |
| |
| // No more input frames are available but encoder still can have some data in its internal buffer to process |
| // and some frames to dump. |
| if (xectx->state == STATE_ENCODING && frame == NULL) { |
| if (setup_bumping(xectx->id) == 0) |
| xectx->state = STATE_BUMPING; // Entering bumping process |
| else { |
| av_log(avctx, AV_LOG_ERROR, "Failed to setup bumping\n"); |
| return 0; |
| } |
| } |
| |
| if (xectx->state == STATE_ENCODING) { |
| int i; |
| XEVE_IMGB *imgb = NULL; |
| |
| imgb = &xectx->imgb; |
| |
| for (i = 0; i < imgb->np; i++) { |
| imgb->a[i] = frame->data[i]; |
| imgb->s[i] = frame->linesize[i]; |
| } |
| |
| imgb->ts[XEVE_TS_PTS] = frame->pts; |
| |
| /* push image to encoder */ |
| ret = xeve_push(xectx->id, imgb); |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "xeve_push() failed\n"); |
| return AVERROR_EXTERNAL; |
| } |
| } |
| if (xectx->state == STATE_ENCODING || xectx->state == STATE_BUMPING) { |
| /* encoding */ |
| ret = xeve_encode(xectx->id, &(xectx->bitb), &(xectx->stat)); |
| if (XEVE_FAILED(ret)) { |
| av_log(avctx, AV_LOG_ERROR, "xeve_encode() failed\n"); |
| return AVERROR_EXTERNAL; |
| } |
| |
| /* store bitstream */ |
| if (ret == XEVE_OK_OUT_NOT_AVAILABLE) { // Return OK but picture is not available yet |
| *got_packet = 0; |
| return 0; |
| } else if (ret == XEVE_OK) { |
| int av_pic_type; |
| |
| if (xectx->stat.write > 0) { |
| |
| ret = ff_get_encode_buffer(avctx, avpkt, xectx->stat.write, 0); |
| if (ret < 0) |
| return ret; |
| |
| memcpy(avpkt->data, xectx->bitb.addr, xectx->stat.write); |
| |
| avpkt->time_base.num = 1; |
| avpkt->time_base.den = xectx->cdsc.param.fps; |
| |
| avpkt->pts = xectx->bitb.ts[XEVE_TS_PTS]; |
| avpkt->dts = xectx->bitb.ts[XEVE_TS_DTS]; |
| |
| switch(xectx->stat.stype) { |
| case XEVE_ST_I: |
| av_pic_type = AV_PICTURE_TYPE_I; |
| avpkt->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(avctx, AV_LOG_ERROR, "Unknown slice type\n"); |
| return AVERROR_INVALIDDATA; |
| } |
| |
| ff_side_data_set_encoder_stats(avpkt, xectx->stat.qp * FF_QP2LAMBDA, NULL, 0, av_pic_type); |
| |
| *got_packet = 1; |
| } |
| } else if (ret == XEVE_OK_NO_MORE_FRM) { |
| // Return OK but no more frames |
| return 0; |
| } else { |
| av_log(avctx, AV_LOG_ERROR, "Invalid return value: %d\n", ret); |
| return AVERROR_EXTERNAL; |
| } |
| } else { |
| av_log(avctx, AV_LOG_ERROR, "Udefined encoder state\n"); |
| return AVERROR_INVALIDDATA; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * Destroy the encoder and release all the allocated resources |
| * |
| * @param avctx codec context |
| * @return 0 on success, negative error code on failure |
| */ |
| static av_cold int libxeve_close(AVCodecContext *avctx) |
| { |
| XeveContext *xectx = avctx->priv_data; |
| |
| if (xectx->id) { |
| xeve_delete(xectx->id); |
| xectx->id = NULL; |
| } |
| |
| av_free(xectx->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 |
| |
| static const enum AVPixelFormat supported_pixel_formats[] = { |
| AV_PIX_FMT_YUV420P, |
| AV_PIX_FMT_YUV420P10, |
| AV_PIX_FMT_NONE |
| }; |
| |
| // Consider using following options (./ffmpeg --help encoder=libxeve) |
| // |
| static const AVOption libxeve_options[] = { |
| { "preset", "Encoding preset for setting encoding speed", OFFSET(preset_id), AV_OPT_TYPE_INT, { .i64 = XEVE_PRESET_MEDIUM }, XEVE_PRESET_DEFAULT, XEVE_PRESET_PLACEBO, VE, .unit = "preset" }, |
| { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_DEFAULT }, INT_MIN, INT_MAX, VE, .unit = "preset" }, |
| { "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_FAST }, INT_MIN, INT_MAX, VE, .unit = "preset" }, |
| { "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_MEDIUM }, INT_MIN, INT_MAX, VE, .unit = "preset" }, |
| { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_SLOW }, INT_MIN, INT_MAX, VE, .unit = "preset" }, |
| { "placebo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_PLACEBO }, INT_MIN, INT_MAX, VE, .unit = "preset" }, |
| { "tune", "Tuning parameter for special purpose operation", OFFSET(tune_id), AV_OPT_TYPE_INT, { .i64 = XEVE_TUNE_NONE }, XEVE_TUNE_NONE, XEVE_TUNE_PSNR, VE, .unit = "tune"}, |
| { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_NONE }, INT_MIN, INT_MAX, VE, .unit = "tune" }, |
| { "zerolatency", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_ZEROLATENCY }, INT_MIN, INT_MAX, VE, .unit = "tune" }, |
| { "psnr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_PSNR }, INT_MIN, INT_MAX, VE, .unit = "tune" }, |
| { "profile", "Encoding profile", OFFSET(profile_id), AV_OPT_TYPE_INT, { .i64 = XEVE_PROFILE_BASELINE }, XEVE_PROFILE_BASELINE, XEVE_PROFILE_MAIN, VE, .unit = "profile" }, |
| { "baseline", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PROFILE_BASELINE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, |
| { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PROFILE_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, |
| { "rc_mode", "Rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = XEVE_RC_CQP }, XEVE_RC_CQP, XEVE_RC_CRF, VE, .unit = "rc_mode" }, |
| { "CQP", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_CQP }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, |
| { "ABR", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_ABR }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, |
| { "CRF", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_CRF }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, |
| { "qp", "Quantization parameter value for CQP rate control mode", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE }, |
| { "crf", "Constant rate factor value for CRF rate control mode", OFFSET(crf), AV_OPT_TYPE_INT, { .i64 = 32 }, 10, 49, VE }, |
| { "hash", "Embed picture signature (HASH) for conformance checking in decoding", OFFSET(hash), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, |
| { "sei_info", "Embed SEI messages identifying encoder parameters and command line arguments", OFFSET(sei_info), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, 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 libxeve_class = { |
| .class_name = "libxeve", |
| .item_name = av_default_item_name, |
| .option = libxeve_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 FFCodecDefault libxeve_defaults[] = { |
| { "b", "0" }, // bitrate in terms of kilo-bits per second |
| { "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"}, // maximum number of B frames (0: no B-frames, 1,3,7,15) |
| { "threads", "0"}, // number of threads to be used (0: automatically select the number of threads to set) |
| { NULL }, |
| }; |
| |
| const FFCodec ff_libxeve_encoder = { |
| .p.name = "libxeve", |
| .p.long_name = NULL_IF_CONFIG_SMALL("libxeve MPEG-5 EVC"), |
| .p.type = AVMEDIA_TYPE_VIDEO, |
| .p.id = AV_CODEC_ID_EVC, |
| .init = libxeve_init, |
| FF_CODEC_ENCODE_CB(libxeve_encode), |
| .close = libxeve_close, |
| .priv_data_size = sizeof(XeveContext), |
| .p.priv_class = &libxeve_class, |
| .defaults = libxeve_defaults, |
| .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1, |
| .p.profiles = NULL_IF_CONFIG_SMALL(ff_evc_profiles), |
| .p.wrapper_name = "libxeve", |
| .p.pix_fmts = supported_pixel_formats, |
| .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_NOT_INIT_THREADSAFE, |
| }; |