| @@ -0,0 +1,491 @@ |
| 1 | +/* |
| 2 | + * H.266 encoding using the VVenC library |
| 3 | + * |
| 4 | + * Copyright (C) 2022, Thomas Siedel |
| 5 | + * |
| 6 | + * This file is part of FFmpeg. |
| 7 | + * |
| 8 | + * FFmpeg is free software; you can redistribute it and/or |
| 9 | + * modify it under the terms of the GNU Lesser General Public |
| 10 | + * License as published by the Free Software Foundation; either |
| 11 | + * version 2.1 of the License, or (at your option) any later version. |
| 12 | + * |
| 13 | + * FFmpeg is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | + * Lesser General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU Lesser General Public |
| 19 | + * License along with FFmpeg; if not, write to the Free Software |
| 20 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 21 | + */ |
| 22 | + |
| 23 | +#include <vvenc/vvenc.h> |
| 24 | +#include <vvenc/vvencCfg.h> |
| 25 | +#include <vvenc/version.h> |
| 26 | + |
| 27 | +#include "libavutil/avstring.h" |
| 28 | +#include "libavutil/avutil.h" |
| 29 | +#include "libavutil/common.h" |
| 30 | +#include "libavutil/frame.h" |
| 31 | +#include "libavutil/imgutils.h" |
| 32 | +#include "libavutil/log.h" |
| 33 | +#include "libavutil/mem.h" |
| 34 | +#include "libavutil/pixdesc.h" |
| 35 | +#include "libavutil/opt.h" |
| 36 | + |
| 37 | +#include "avcodec.h" |
| 38 | +#include "codec_internal.h" |
| 39 | +#include "encode.h" |
| 40 | +#include "internal.h" |
| 41 | +#include "packet_internal.h" |
| 42 | +#include "profiles.h" |
| 43 | + |
| 44 | +#define VVENC_VERSION_INT AV_VERSION_INT(VVENC_VERSION_MAJOR, \ |
| 45 | + VVENC_VERSION_MINOR, \ |
| 46 | + VVENC_VERSION_PATCH) |
| 47 | + |
| 48 | +typedef struct VVenCContext { |
| 49 | + AVClass *class; |
| 50 | + vvencEncoder *encoder; |
| 51 | + vvencAccessUnit *au; |
| 52 | + bool encode_done; |
| 53 | + int preset; |
| 54 | + int qp; |
| 55 | + int qpa; |
| 56 | + int intra_refresh_sec; |
| 57 | + char *level; |
| 58 | + int tier; |
| 59 | + char *stats; |
| 60 | + AVDictionary *vvenc_opts; |
| 61 | +} VVenCContext; |
| 62 | + |
| 63 | +static void vvenc_log_callback(void *ctx, int level, |
| 64 | + const char *fmt, va_list args) |
| 65 | +{ |
| 66 | + vvenc_config params; |
| 67 | + vvencEncoder *encoder = ctx; |
| 68 | + if (encoder) { |
| 69 | + vvenc_config_default(¶ms); |
| 70 | + vvenc_get_config(encoder, ¶ms); |
| 71 | + if ((int)params.m_verbosity >= level) |
| 72 | + vfprintf(level == 1 ? stderr : stdout, fmt, args); |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +static void vvenc_set_verbository(vvenc_config *params) |
| 77 | +{ |
| 78 | + int loglevel = av_log_get_level(); |
| 79 | + params->m_verbosity = VVENC_SILENT; |
| 80 | + if (loglevel >= AV_LOG_DEBUG) |
| 81 | + params->m_verbosity = VVENC_DETAILS; |
| 82 | + else if (loglevel >= AV_LOG_VERBOSE) |
| 83 | + params->m_verbosity = VVENC_NOTICE; |
| 84 | + else if (loglevel >= AV_LOG_INFO) |
| 85 | + params->m_verbosity = VVENC_WARNING; |
| 86 | +} |
| 87 | + |
| 88 | +static void vvenc_set_pic_format(AVCodecContext *avctx, vvenc_config *params) |
| 89 | +{ |
| 90 | + params->m_internChromaFormat = VVENC_CHROMA_420; |
| 91 | + params->m_inputBitDepth[0] = 10; |
| 92 | +} |
| 93 | + |
| 94 | +static void vvenc_set_color_format(AVCodecContext *avctx, vvenc_config *params) |
| 95 | +{ |
| 96 | + if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) |
| 97 | + params->m_colourPrimaries = (int) avctx->color_primaries; |
| 98 | + if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED) |
| 99 | + params->m_matrixCoefficients = (int) avctx->colorspace; |
| 100 | + if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) { |
| 101 | + params->m_transferCharacteristics = (int) avctx->color_trc; |
| 102 | + |
| 103 | + if (avctx->color_trc == AVCOL_TRC_SMPTE2084) |
| 104 | + params->m_HdrMode = (avctx->color_primaries == AVCOL_PRI_BT2020) ? |
| 105 | + VVENC_HDR_PQ_BT2020 : VVENC_HDR_PQ; |
| 106 | + else if (avctx->color_trc == AVCOL_TRC_BT2020_10 || avctx->color_trc == AVCOL_TRC_ARIB_STD_B67) |
| 107 | + params->m_HdrMode = (avctx->color_trc == AVCOL_TRC_BT2020_10 || |
| 108 | + avctx->color_primaries == AVCOL_PRI_BT2020 || |
| 109 | + avctx->colorspace == AVCOL_SPC_BT2020_NCL || |
| 110 | + avctx->colorspace == AVCOL_SPC_BT2020_CL) ? |
| 111 | + VVENC_HDR_HLG_BT2020 : VVENC_HDR_HLG; |
| 112 | + } |
| 113 | + |
| 114 | + if (params->m_HdrMode == VVENC_HDR_OFF && |
| 115 | + (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || avctx->colorspace != AVCOL_SPC_UNSPECIFIED)) { |
| 116 | + params->m_vuiParametersPresent = 1; |
| 117 | + params->m_colourDescriptionPresent = true; |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +static void vvenc_set_framerate(AVCodecContext *avctx, vvenc_config *params) |
| 122 | +{ |
| 123 | + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { |
| 124 | + params->m_FrameRate = avctx->framerate.num; |
| 125 | + params->m_FrameScale = avctx->framerate.den; |
| 126 | + } else { |
| 127 | + params->m_FrameRate = avctx->time_base.den; |
| 128 | + params->m_FrameScale = avctx->time_base.num; |
| 129 | + } |
| 130 | + |
| 131 | +FF_DISABLE_DEPRECATION_WARNINGS |
| 132 | + |
| 133 | +#if FF_API_TICKS_PER_FRAME |
| 134 | + if (avctx->ticks_per_frame == 1) { |
| 135 | +#endif |
| 136 | + params->m_TicksPerSecond = -1; /* auto mode for ticks per frame = 1 */ |
| 137 | +#if FF_API_TICKS_PER_FRAME |
| 138 | + } else { |
| 139 | + params->m_TicksPerSecond = |
| 140 | + ceil((avctx->time_base.den / (double) avctx->time_base.num) * |
| 141 | + (double) avctx->ticks_per_frame); |
| 142 | + } |
| 143 | +#endif |
| 144 | +FF_ENABLE_DEPRECATION_WARNINGS |
| 145 | +} |
| 146 | + |
| 147 | +static int vvenc_parse_vvenc_params(AVCodecContext *avctx, vvenc_config *params) |
| 148 | +{ |
| 149 | + VVenCContext *s = avctx->priv_data; |
| 150 | + const AVDictionaryEntry *en = NULL; |
| 151 | + int parse_ret; |
| 152 | + int ret = 0; |
| 153 | + |
| 154 | + while ((en = av_dict_iterate(s->vvenc_opts, en))) { |
| 155 | + av_log(avctx, AV_LOG_DEBUG, "vvenc_set_param: '%s:%s'\n", en->key, |
| 156 | + en->value); |
| 157 | + parse_ret = vvenc_set_param(params, en->key, en->value); |
| 158 | + switch (parse_ret) { |
| 159 | + case VVENC_PARAM_BAD_NAME: |
| 160 | + av_log(avctx, AV_LOG_ERROR, "Unknown vvenc option: %s.\n", en->key); |
| 161 | + ret = AVERROR(EINVAL); |
| 162 | + break; |
| 163 | + case VVENC_PARAM_BAD_VALUE: |
| 164 | + av_log(avctx, AV_LOG_ERROR, "Invalid vvenc value for %s: %s.\n", en->key, en->value); |
| 165 | + ret = AVERROR(EINVAL); |
| 166 | + break; |
| 167 | + default: |
| 168 | + break; |
| 169 | + } |
| 170 | + |
| 171 | + if (!av_strcasecmp(en->key, "rcstatsfile")) { |
| 172 | + av_log(avctx, AV_LOG_ERROR, "vvenc-params 2pass option 'rcstatsfile' " |
| 173 | + "not available. Use option 'passlogfile'\n"); |
| 174 | + ret = AVERROR(EINVAL); |
| 175 | + } |
| 176 | + if (!av_strcasecmp(en->key, "passes") || !av_strcasecmp(en->key, "pass")) { |
| 177 | + av_log(avctx, AV_LOG_ERROR, "vvenc-params 2pass option '%s' " |
| 178 | + "not available. Use option 'pass'\n", en->key); |
| 179 | + ret = AVERROR(EINVAL); |
| 180 | + } |
| 181 | + } |
| 182 | + return ret; |
| 183 | +} |
| 184 | + |
| 185 | +static int vvenc_set_rc_mode(AVCodecContext *avctx, vvenc_config *params) |
| 186 | +{ |
| 187 | + params->m_RCNumPasses = 1; |
| 188 | + if ((avctx->flags & AV_CODEC_FLAG_PASS1 || avctx->flags & AV_CODEC_FLAG_PASS2)) { |
| 189 | + if (!avctx->bit_rate) { |
| 190 | + av_log(avctx, AV_LOG_ERROR, "A bitrate must be set to use two pass mode.\n"); |
| 191 | + return AVERROR(EINVAL); |
| 192 | + } |
| 193 | + params->m_RCNumPasses = 2; |
| 194 | + if (avctx->flags & AV_CODEC_FLAG_PASS1) |
| 195 | + params->m_RCPass = 1; |
| 196 | + else |
| 197 | + params->m_RCPass = 2; |
| 198 | + } |
| 199 | + |
| 200 | + if (avctx->rc_max_rate) { |
| 201 | +#if VVENC_VERSION_INT >= AV_VERSION_INT(1,8,0) |
| 202 | + params->m_RCMaxBitrate = avctx->rc_max_rate; |
| 203 | +#endif |
| 204 | + |
| 205 | +#if VVENC_VERSION_INT < AV_VERSION_INT(1,11,0) |
| 206 | + /* rc_max_rate without a bit_rate enables capped CQF mode. |
| 207 | + (QP + subj. optimization + max. bitrate) */ |
| 208 | + if (!avctx->bit_rate) { |
| 209 | + av_log(avctx, AV_LOG_ERROR, "Capped Constant Quality Factor mode (capped CQF) " |
| 210 | + "needs at least vvenc version >= 1.11.0 (current version %s)\n", vvenc_get_version()); |
| 211 | + return AVERROR(EINVAL); |
| 212 | + } |
| 213 | +#endif |
| 214 | + } |
| 215 | + return 0; |
| 216 | +} |
| 217 | + |
| 218 | +static int vvenc_init_extradata(AVCodecContext *avctx, VVenCContext *s) |
| 219 | +{ |
| 220 | + int ret; |
| 221 | + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { |
| 222 | + ret = vvenc_get_headers(s->encoder, s->au); |
| 223 | + if (0 != ret) { |
| 224 | + av_log(avctx, AV_LOG_ERROR, "cannot get (SPS,PPS) headers: %s\n", |
| 225 | + vvenc_get_last_error(s->encoder)); |
| 226 | + return AVERROR(EINVAL); |
| 227 | + } |
| 228 | + |
| 229 | + if (s->au->payloadUsedSize <= 0) { |
| 230 | + return AVERROR_INVALIDDATA; |
| 231 | + } |
| 232 | + |
| 233 | + avctx->extradata_size = s->au->payloadUsedSize; |
| 234 | + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); |
| 235 | + if (!avctx->extradata) { |
| 236 | + return AVERROR(ENOMEM); |
| 237 | + } |
| 238 | + |
| 239 | + memcpy(avctx->extradata, s->au->payload, avctx->extradata_size); |
| 240 | + } |
| 241 | + return 0; |
| 242 | +} |
| 243 | + |
| 244 | +static av_cold int vvenc_init(AVCodecContext *avctx) |
| 245 | +{ |
| 246 | + int ret; |
| 247 | + int framerate; |
| 248 | + VVenCContext *s = avctx->priv_data; |
| 249 | + vvenc_config params; |
| 250 | + vvencPresetMode preset = (vvencPresetMode) s->preset; |
| 251 | + |
| 252 | + if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) { |
| 253 | + av_log(avctx, AV_LOG_ERROR, "interlaced not supported\n"); |
| 254 | + return AVERROR(EINVAL); |
| 255 | + } |
| 256 | + |
| 257 | + vvenc_config_default(¶ms); |
| 258 | + |
| 259 | + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) |
| 260 | + framerate = avctx->framerate.num / avctx->framerate.den; |
| 261 | + else |
| 262 | + framerate = avctx->time_base.den / avctx->time_base.num; |
| 263 | + |
| 264 | + vvenc_init_default(¶ms, avctx->width, avctx->height, framerate, |
| 265 | + avctx->bit_rate, s->qp, preset); |
| 266 | + |
| 267 | + vvenc_set_verbository(¶ms); |
| 268 | + |
| 269 | + if (avctx->thread_count > 0) |
| 270 | + params.m_numThreads = avctx->thread_count; |
| 271 | + |
| 272 | + /* GOP settings (IDR/CRA) */ |
| 273 | + if (avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) |
| 274 | + params.m_DecodingRefreshType = VVENC_DRT_IDR; |
| 275 | + |
| 276 | + if (avctx->gop_size == 1) { |
| 277 | + params.m_GOPSize = 1; |
| 278 | + params.m_IntraPeriod = 1; |
| 279 | + } else |
| 280 | + params.m_IntraPeriodSec = s->intra_refresh_sec; |
| 281 | + |
| 282 | + params.m_AccessUnitDelimiter = true; |
| 283 | + params.m_usePerceptQPA = s->qpa; |
| 284 | + params.m_levelTier = (vvencTier) s->tier; |
| 285 | + |
| 286 | + if (avctx->level > 0) |
| 287 | + params.m_level = (vvencLevel)avctx->level; |
| 288 | + |
| 289 | + if (s->level) { |
| 290 | + if (VVENC_PARAM_BAD_VALUE == vvenc_set_param(¶ms, "level", s->level)) { |
| 291 | + av_log(avctx, AV_LOG_ERROR, "Invalid level_idc: %s.\n", s->level); |
| 292 | + return AVERROR(EINVAL); |
| 293 | + } |
| 294 | + } |
| 295 | + |
| 296 | + vvenc_set_framerate(avctx, ¶ms); |
| 297 | + |
| 298 | + vvenc_set_pic_format(avctx, ¶ms); |
| 299 | + |
| 300 | + vvenc_set_color_format(avctx, ¶ms); |
| 301 | + |
| 302 | + ret = vvenc_parse_vvenc_params(avctx, ¶ms); |
| 303 | + if (ret != 0) |
| 304 | + return ret; |
| 305 | + |
| 306 | + ret = vvenc_set_rc_mode(avctx, ¶ms); |
| 307 | + if (ret != 0) |
| 308 | + return ret; |
| 309 | + |
| 310 | + s->encoder = vvenc_encoder_create(); |
| 311 | + if (!s->encoder) { |
| 312 | + av_log(avctx, AV_LOG_ERROR, "cannot create libvvenc encoder\n"); |
| 313 | + return AVERROR(ENOMEM); |
| 314 | + } |
| 315 | + |
| 316 | + vvenc_set_msg_callback(¶ms, s->encoder, vvenc_log_callback); |
| 317 | + ret = vvenc_encoder_open(s->encoder, ¶ms); |
| 318 | + if (ret != 0) { |
| 319 | + av_log(avctx, AV_LOG_ERROR, "cannot open libvvenc encoder: %s\n", |
| 320 | + vvenc_get_last_error(s->encoder)); |
| 321 | + return AVERROR_EXTERNAL; |
| 322 | + } |
| 323 | + |
| 324 | + vvenc_get_config(s->encoder, ¶ms); /* get the adapted config */ |
| 325 | + |
| 326 | + av_log(avctx, AV_LOG_INFO, "libvvenc version: %s\n", vvenc_get_version()); |
| 327 | + if (av_log_get_level() >= AV_LOG_VERBOSE) |
| 328 | + av_log(avctx, AV_LOG_INFO, "%s\n", vvenc_get_config_as_string(¶ms, params.m_verbosity)); |
| 329 | + |
| 330 | + if (params.m_RCNumPasses == 2) { |
| 331 | + ret = vvenc_init_pass(s->encoder, params.m_RCPass - 1, s->stats); |
| 332 | + if (ret != 0) { |
| 333 | + av_log(avctx, AV_LOG_ERROR, "cannot init pass %d: %s\n", params.m_RCPass, |
| 334 | + vvenc_get_last_error(s->encoder)); |
| 335 | + return AVERROR_EXTERNAL; |
| 336 | + } |
| 337 | + } |
| 338 | + |
| 339 | + s->au = vvenc_accessUnit_alloc(); |
| 340 | + if (!s->au) { |
| 341 | + av_log(avctx, AV_LOG_FATAL, "cannot allocate memory for AU payload\n"); |
| 342 | + return AVERROR(ENOMEM); |
| 343 | + } |
| 344 | + vvenc_accessUnit_alloc_payload(s->au, avctx->width * avctx->height); |
| 345 | + if (!s->au->payload) { |
| 346 | + av_log(avctx, AV_LOG_FATAL, "cannot allocate payload memory of size %d\n", |
| 347 | + avctx->width * avctx->height); |
| 348 | + return AVERROR(ENOMEM); |
| 349 | + } |
| 350 | + |
| 351 | + ret = vvenc_init_extradata(avctx, s); |
| 352 | + if (ret != 0) |
| 353 | + return ret; |
| 354 | + |
| 355 | + s->encode_done = false; |
| 356 | + return 0; |
| 357 | +} |
| 358 | + |
| 359 | +static av_cold int vvenc_close(AVCodecContext *avctx) |
| 360 | +{ |
| 361 | + VVenCContext *s = avctx->priv_data; |
| 362 | + |
| 363 | + if (s->au) |
| 364 | + vvenc_accessUnit_free(s->au, true); |
| 365 | + |
| 366 | + if (s->encoder) { |
| 367 | + vvenc_print_summary(s->encoder); |
| 368 | + |
| 369 | + if (0 != vvenc_encoder_close(s->encoder)) |
| 370 | + return AVERROR_EXTERNAL; |
| 371 | + } |
| 372 | + |
| 373 | + return 0; |
| 374 | +} |
| 375 | + |
| 376 | +static av_cold int vvenc_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, |
| 377 | + int *got_packet) |
| 378 | +{ |
| 379 | + VVenCContext *s = avctx->priv_data; |
| 380 | + vvencYUVBuffer *pyuvbuf; |
| 381 | + vvencYUVBuffer yuvbuf; |
| 382 | + int ret; |
| 383 | + |
| 384 | + pyuvbuf = NULL; |
| 385 | + if (frame) { |
| 386 | + vvenc_YUVBuffer_default(&yuvbuf); |
| 387 | + yuvbuf.planes[0].ptr = (int16_t *) frame->data[0]; |
| 388 | + yuvbuf.planes[1].ptr = (int16_t *) frame->data[1]; |
| 389 | + yuvbuf.planes[2].ptr = (int16_t *) frame->data[2]; |
| 390 | + |
| 391 | + yuvbuf.planes[0].width = frame->width; |
| 392 | + yuvbuf.planes[0].height = frame->height; |
| 393 | + yuvbuf.planes[0].stride = frame->linesize[0] >> 1; /* stride is used in 16bit samples in vvenc */ |
| 394 | + |
| 395 | + yuvbuf.planes[1].width = frame->width >> 1; |
| 396 | + yuvbuf.planes[1].height = frame->height >> 1; |
| 397 | + yuvbuf.planes[1].stride = frame->linesize[1] >> 1; |
| 398 | + |
| 399 | + yuvbuf.planes[2].width = frame->width >> 1; |
| 400 | + yuvbuf.planes[2].height = frame->height >> 1; |
| 401 | + yuvbuf.planes[2].stride = frame->linesize[2] >> 1; |
| 402 | + |
| 403 | + yuvbuf.cts = frame->pts; |
| 404 | + yuvbuf.ctsValid = true; |
| 405 | + pyuvbuf = &yuvbuf; |
| 406 | + } |
| 407 | + |
| 408 | + if (!s->encode_done) { |
| 409 | + if (vvenc_encode(s->encoder, pyuvbuf, s->au, &s->encode_done) != 0) |
| 410 | + return AVERROR_EXTERNAL; |
| 411 | + } else |
| 412 | + return 0; |
| 413 | + |
| 414 | + if (s->au->payloadUsedSize > 0) { |
| 415 | + ret = ff_get_encode_buffer(avctx, pkt, s->au->payloadUsedSize, 0); |
| 416 | + if (ret < 0) |
| 417 | + return ret; |
| 418 | + |
| 419 | + memcpy(pkt->data, s->au->payload, s->au->payloadUsedSize); |
| 420 | + |
| 421 | + if (s->au->ctsValid) |
| 422 | + pkt->pts = s->au->cts; |
| 423 | + if (s->au->dtsValid) |
| 424 | + pkt->dts = s->au->dts; |
| 425 | + pkt->flags |= AV_PKT_FLAG_KEY * s->au->rap; |
| 426 | + |
| 427 | + *got_packet = 1; |
| 428 | + return 0; |
| 429 | + } |
| 430 | + |
| 431 | + return 0; |
| 432 | +} |
| 433 | + |
| 434 | +static const enum AVPixelFormat pix_fmts_vvenc[] = { |
| 435 | + AV_PIX_FMT_YUV420P10, |
| 436 | + AV_PIX_FMT_NONE |
| 437 | +}; |
| 438 | + |
| 439 | +#define OFFSET(x) offsetof(VVenCContext, x) |
| 440 | +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM |
| 441 | +static const AVOption options[] = { |
| 442 | + { "preset", "set encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64 = 2}, 0, 4, VE, "preset"}, |
| 443 | + { "faster", "0", 0, AV_OPT_TYPE_CONST, {.i64 = VVENC_FASTER}, INT_MIN, INT_MAX, VE, "preset" }, |
| 444 | + { "fast", "1", 0, AV_OPT_TYPE_CONST, {.i64 = VVENC_FAST}, INT_MIN, INT_MAX, VE, "preset" }, |
| 445 | + { "medium", "2", 0, AV_OPT_TYPE_CONST, {.i64 = VVENC_MEDIUM}, INT_MIN, INT_MAX, VE, "preset" }, |
| 446 | + { "slow", "3", 0, AV_OPT_TYPE_CONST, {.i64 = VVENC_SLOW}, INT_MIN, INT_MAX, VE, "preset" }, |
| 447 | + { "slower", "4", 0, AV_OPT_TYPE_CONST, {.i64 = VVENC_SLOWER}, INT_MIN, INT_MAX, VE, "preset" }, |
| 448 | + { "qp", "set quantization", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 32}, -1, 63, VE }, |
| 449 | + { "qpa", "set subjective (perceptually motivated) optimization", OFFSET(qpa), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE}, |
| 450 | + { "passlogfile", "Filename for 2 pass stats", OFFSET(stats), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, VE}, |
| 451 | + { "stats", "Filename for 2 pass stats", OFFSET(stats), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, VE}, |
| 452 | + { "period", "set (intra) refresh period in seconds", OFFSET(intra_refresh_sec), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, VE }, |
| 453 | + { "vvenc-params", "set the vvenc configuration using a :-separated list of key=value parameters", OFFSET(vvenc_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, |
| 454 | + { "level", "Specify level (as defined by Annex A)", OFFSET(level), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, VE}, |
| 455 | + { "tier", "set vvc tier", OFFSET(tier), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, VE, "tier"}, |
| 456 | + { "main", "main", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, VE, "tier"}, |
| 457 | + { "high", "high", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, VE, "tier"}, |
| 458 | + {NULL} |
| 459 | +}; |
| 460 | + |
| 461 | +static const AVClass class = { |
| 462 | + .class_name = "libvvenc", |
| 463 | + .item_name = av_default_item_name, |
| 464 | + .option = options, |
| 465 | + .version = LIBAVUTIL_VERSION_INT, |
| 466 | +}; |
| 467 | + |
| 468 | +static const FFCodecDefault vvenc_defaults[] = { |
| 469 | + { "b", "0" }, |
| 470 | + { "g", "-1" }, |
| 471 | + { NULL }, |
| 472 | +}; |
| 473 | + |
| 474 | +const FFCodec ff_libvvenc_encoder = { |
| 475 | + .p.name = "libvvenc", |
| 476 | + CODEC_LONG_NAME("libvvenc H.266 / VVC"), |
| 477 | + .p.type = AVMEDIA_TYPE_VIDEO, |
| 478 | + .p.id = AV_CODEC_ID_VVC, |
| 479 | + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | |
| 480 | + AV_CODEC_CAP_OTHER_THREADS, |
| 481 | + .p.profiles = NULL_IF_CONFIG_SMALL(ff_vvc_profiles), |
| 482 | + .p.priv_class = &class, |
| 483 | + .p.wrapper_name = "libvvenc", |
| 484 | + .priv_data_size = sizeof(VVenCContext), |
| 485 | + .p.pix_fmts = pix_fmts_vvenc, |
| 486 | + .init = vvenc_init, |
| 487 | + FF_CODEC_ENCODE_CB(vvenc_frame), |
| 488 | + .close = vvenc_close, |
| 489 | + .defaults = vvenc_defaults, |
| 490 | + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS |
| 491 | +}; |