| @@ -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 | }; |