summaryrefslogtreecommitdiff
path: root/libavcodec/nvenc.c
diff options
context:
space:
mode:
authorTimo Rothenpieler <timo@rothenpieler.org>2022-11-05 21:17:37 +0100
committerTimo Rothenpieler <timo@rothenpieler.org>2022-11-05 21:17:37 +0100
commit5c288a44ad16087c3d3a7563490cb634790e751f (patch)
tree45fa050b35591de30e7144620fcb490d8e4aebae /libavcodec/nvenc.c
parent28680e2f41b93b75863720e42397441dfcc29f94 (diff)
downloadffmpeg-5c288a44ad16087c3d3a7563490cb634790e751f.tar.gz
avcodec/nvenc: add AV1 encoding support
The encoder seems to be trading blows with hevc_nvenc. In terms of quality at low bitrate cbr settings, it seems to outperform it even. It produces fewer artifacts and the ones it does produce are less jarring to my perception. At higher bitrates I had a hard time finding differences between the two encoders in terms of subjective visual quality. Using the 'slow' preset, av1_nvenc outperformed hevc_nvenc in terms of encoding speed by 75% to 100% while performing above tests. Needless to say, it always massively outperformed h264_nvenc in terms of quality for a given bitrate, while also being slightly faster.
Diffstat (limited to 'libavcodec/nvenc.c')
-rw-r--r--libavcodec/nvenc.c134
1 files changed, 126 insertions, 8 deletions
diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c
index 73c05fcd37..8228c66f64 100644
--- a/libavcodec/nvenc.c
+++ b/libavcodec/nvenc.c
@@ -1,5 +1,5 @@
/*
- * H.264/HEVC hardware encoding using nvidia nvenc
+ * H.264/HEVC/AV1 hardware encoding using nvidia nvenc
* Copyright (c) 2016 Timo Rothenpieler <timo@rothenpieler.org>
*
* This file is part of FFmpeg.
@@ -222,8 +222,14 @@ static void nvenc_map_preset(NvencContext *ctx)
static void nvenc_print_driver_requirement(AVCodecContext *avctx, int level)
{
-#if NVENCAPI_CHECK_VERSION(11, 2)
+#if NVENCAPI_CHECK_VERSION(12, 1)
const char *minver = "(unknown)";
+#elif NVENCAPI_CHECK_VERSION(12, 0)
+# if defined(_WIN32) || defined(__CYGWIN__)
+ const char *minver = "522.25";
+# else
+ const char *minver = "520.56.06";
+# endif
#elif NVENCAPI_CHECK_VERSION(11, 1)
# if defined(_WIN32) || defined(__CYGWIN__)
const char *minver = "471.41";
@@ -658,6 +664,11 @@ static av_cold int nvenc_setup_device(AVCodecContext *avctx)
case AV_CODEC_ID_HEVC:
ctx->init_encode_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID;
break;
+#if CONFIG_AV1_NVENC_ENCODER
+ case AV_CODEC_ID_AV1:
+ ctx->init_encode_params.encodeGUID = NV_ENC_CODEC_AV1_GUID;
+ break;
+#endif
default:
return AVERROR_BUG;
}
@@ -761,6 +772,11 @@ static av_cold void set_constqp(AVCodecContext *avctx)
{
NvencContext *ctx = avctx->priv_data;
NV_ENC_RC_PARAMS *rc = &ctx->encode_config.rcParams;
+#if CONFIG_AV1_NVENC_ENCODER
+ int qmax = avctx->codec->id == AV_CODEC_ID_AV1 ? 255 : 51;
+#else
+ int qmax = 51;
+#endif
rc->rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
@@ -771,9 +787,9 @@ static av_cold void set_constqp(AVCodecContext *avctx)
rc->constQP.qpInterB = ctx->init_qp_b;
} else if (avctx->i_quant_factor != 0.0 && avctx->b_quant_factor != 0.0) {
rc->constQP.qpIntra = av_clip(
- rc->constQP.qpInterP * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, 51);
+ rc->constQP.qpInterP * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, qmax);
rc->constQP.qpInterB = av_clip(
- rc->constQP.qpInterP * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, 51);
+ rc->constQP.qpInterP * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, qmax);
} else {
rc->constQP.qpIntra = rc->constQP.qpInterP;
rc->constQP.qpInterB = rc->constQP.qpInterP;
@@ -781,9 +797,9 @@ static av_cold void set_constqp(AVCodecContext *avctx)
} else if (ctx->cqp >= 0) {
rc->constQP.qpInterP = rc->constQP.qpInterB = rc->constQP.qpIntra = ctx->cqp;
if (avctx->b_quant_factor != 0.0)
- rc->constQP.qpInterB = av_clip(ctx->cqp * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, 51);
+ rc->constQP.qpInterB = av_clip(ctx->cqp * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, qmax);
if (avctx->i_quant_factor != 0.0)
- rc->constQP.qpIntra = av_clip(ctx->cqp * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, 51);
+ rc->constQP.qpIntra = av_clip(ctx->cqp * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, qmax);
}
avctx->qmin = -1;
@@ -795,6 +811,11 @@ static av_cold void set_vbr(AVCodecContext *avctx)
NvencContext *ctx = avctx->priv_data;
NV_ENC_RC_PARAMS *rc = &ctx->encode_config.rcParams;
int qp_inter_p;
+#if CONFIG_AV1_NVENC_ENCODER
+ int qmax = avctx->codec->id == AV_CODEC_ID_AV1 ? 255 : 51;
+#else
+ int qmax = 51;
+#endif
if (avctx->qmin >= 0 && avctx->qmax >= 0) {
rc->enableMinQP = 1;
@@ -832,7 +853,7 @@ static av_cold void set_vbr(AVCodecContext *avctx)
if (ctx->init_qp_i < 0) {
if (avctx->i_quant_factor != 0.0 && avctx->b_quant_factor != 0.0) {
rc->initialRCQP.qpIntra = av_clip(
- rc->initialRCQP.qpInterP * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, 51);
+ rc->initialRCQP.qpInterP * fabs(avctx->i_quant_factor) + avctx->i_quant_offset + 0.5, 0, qmax);
} else {
rc->initialRCQP.qpIntra = rc->initialRCQP.qpInterP;
}
@@ -843,7 +864,7 @@ static av_cold void set_vbr(AVCodecContext *avctx)
if (ctx->init_qp_b < 0) {
if (avctx->i_quant_factor != 0.0 && avctx->b_quant_factor != 0.0) {
rc->initialRCQP.qpInterB = av_clip(
- rc->initialRCQP.qpInterP * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, 51);
+ rc->initialRCQP.qpInterP * fabs(avctx->b_quant_factor) + avctx->b_quant_offset + 0.5, 0, qmax);
} else {
rc->initialRCQP.qpInterB = rc->initialRCQP.qpInterP;
}
@@ -1327,6 +1348,86 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx)
return 0;
}
+#if CONFIG_AV1_NVENC_ENCODER
+static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx)
+{
+ NvencContext *ctx = avctx->priv_data;
+ NV_ENC_CONFIG *cc = &ctx->encode_config;
+ NV_ENC_CONFIG_AV1 *av1 = &cc->encodeCodecConfig.av1Config;
+
+ const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(ctx->data_pix_fmt);
+
+ if ((pixdesc->flags & AV_PIX_FMT_FLAG_RGB) && !IS_GBRP(ctx->data_pix_fmt)) {
+ av1->matrixCoefficients = AVCOL_SPC_BT470BG;
+ av1->colorPrimaries = avctx->color_primaries;
+ av1->transferCharacteristics = avctx->color_trc;
+ av1->colorRange = 0;
+ } else {
+ av1->matrixCoefficients = IS_GBRP(ctx->data_pix_fmt) ? AVCOL_SPC_RGB : avctx->colorspace;
+ av1->colorPrimaries = avctx->color_primaries;
+ av1->transferCharacteristics = avctx->color_trc;
+ av1->colorRange = (avctx->color_range == AVCOL_RANGE_JPEG
+ || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ420P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ422P || ctx->data_pix_fmt == AV_PIX_FMT_YUVJ444P);
+ }
+
+ if (IS_YUV444(ctx->data_pix_fmt)) {
+ cc->profileGUID = NV_ENC_AV1_PROFILE_HIGH_GUID;
+ avctx->profile = FF_PROFILE_AV1_HIGH;
+ } else {
+ cc->profileGUID = NV_ENC_AV1_PROFILE_MAIN_GUID;
+ avctx->profile = FF_PROFILE_AV1_MAIN;
+ }
+
+ if (ctx->dpb_size >= 0) {
+ /* 0 means "let the hardware decide" */
+ av1->maxNumRefFramesInDPB = ctx->dpb_size;
+ }
+
+ if (ctx->intra_refresh) {
+ av1->enableIntraRefresh = 1;
+ av1->intraRefreshPeriod = avctx->gop_size;
+ av1->intraRefreshCnt = avctx->gop_size - 1;
+
+ av1->idrPeriod = NVENC_INFINITE_GOPLENGTH;
+ } else if (avctx->gop_size >= 0) {
+ av1->idrPeriod = avctx->gop_size;
+ }
+
+ if (IS_CBR(cc->rcParams.rateControlMode)) {
+ av1->enableBitstreamPadding = 1;
+ }
+
+ if (ctx->tile_cols >= 0)
+ av1->numTileColumns = ctx->tile_cols;
+ if (ctx->tile_rows >= 0)
+ av1->numTileRows = ctx->tile_rows;
+
+ av1->outputAnnexBFormat = 0;
+
+ av1->level = ctx->level;
+ av1->tier = ctx->tier;
+
+ av1->enableTimingInfo = ctx->timing_info;
+
+ /* mp4 encapsulation requires sequence headers to be present on all keyframes for AV1 */
+ av1->disableSeqHdr = 0;
+ av1->repeatSeqHdr = 1;
+
+ av1->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : 1;
+
+ av1->inputPixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0;
+ av1->pixelBitDepthMinus8 = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? 2 : 0;
+
+ if (ctx->b_ref_mode >= 0)
+ av1->useBFramesAsRef = ctx->b_ref_mode;
+
+ av1->numFwdRefs = avctx->refs;
+ av1->numBwdRefs = avctx->refs;
+
+ return 0;
+}
+#endif
+
static av_cold int nvenc_setup_codec_config(AVCodecContext *avctx)
{
switch (avctx->codec->id) {
@@ -1334,6 +1435,10 @@ static av_cold int nvenc_setup_codec_config(AVCodecContext *avctx)
return nvenc_setup_h264_config(avctx);
case AV_CODEC_ID_HEVC:
return nvenc_setup_hevc_config(avctx);
+#if CONFIG_AV1_NVENC_ENCODER
+ case AV_CODEC_ID_AV1:
+ return nvenc_setup_av1_config(avctx);
+#endif
/* Earlier switch/case will return if unknown codec is passed. */
}
@@ -2025,6 +2130,19 @@ static void nvenc_codec_specific_pic_params(AVCodecContext *avctx,
}
break;
+#if CONFIG_AV1_NVENC_ENCODER
+ case AV_CODEC_ID_AV1:
+ params->codecPicParams.av1PicParams.numTileColumns =
+ ctx->encode_config.encodeCodecConfig.av1Config.numTileColumns;
+ params->codecPicParams.av1PicParams.numTileRows =
+ ctx->encode_config.encodeCodecConfig.av1Config.numTileRows;
+ if (sei_count > 0) {
+ params->codecPicParams.av1PicParams.obuPayloadArray = sei_data;
+ params->codecPicParams.av1PicParams.obuPayloadArrayCnt = sei_count;
+ }
+
+ break;
+#endif
}
}