Commit 671bdd4b authored by Philip Langdale's avatar Philip Langdale Committed by BtbN

avcodec/nvenc: Add support for H.264 High 444 Predictive encoding

Newer versions of the nvenc hardware support The High 444 Predictive profile
of H.264, and can also do lossless encoding under this profile if desired.

This change introduces support for the profile, and exposes the appropriate
presets for requesting lossless encoding.

I tested lossless by generating a baseline sample with testsrc converted
to raw yuv444p, then encoded the sample with nvenc, then did a framemd5
comparision of both the raw video and the nvenc encode. The framemd5
reports were identical.
Signed-off-by: 's avatarPhilip Langdale <philipl@overt.org>
Signed-off-by: 's avatarTimo Rothenpieler <timo@rothenpieler.org>
parent 5233f253
...@@ -401,7 +401,7 @@ static av_cold int nvenc_check_cuda(AVCodecContext *avctx) ...@@ -401,7 +401,7 @@ static av_cold int nvenc_check_cuda(AVCodecContext *avctx)
switch (avctx->codec->id) { switch (avctx->codec->id) {
case AV_CODEC_ID_H264: case AV_CODEC_ID_H264:
target_smver = 0x30; target_smver = avctx->pix_fmt == AV_PIX_FMT_YUV444P ? 0x52 : 0x30;
break; break;
case AV_CODEC_ID_H265: case AV_CODEC_ID_H265:
target_smver = 0x52; target_smver = 0x52;
...@@ -552,6 +552,7 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -552,6 +552,7 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
int surfaceCount = 0; int surfaceCount = 0;
int i, num_mbs; int i, num_mbs;
int isLL = 0; int isLL = 0;
int lossless = 0;
int res = 0; int res = 0;
int dw, dh; int dw, dh;
...@@ -627,10 +628,16 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -627,10 +628,16 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
} else if (!strcmp(ctx->preset, "llhq")) { } else if (!strcmp(ctx->preset, "llhq")) {
encoder_preset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID; encoder_preset = NV_ENC_PRESET_LOW_LATENCY_HQ_GUID;
isLL = 1; isLL = 1;
} else if (!strcmp(ctx->preset, "lossless")) {
encoder_preset = NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID;
lossless = 1;
} else if (!strcmp(ctx->preset, "losslesshp")) {
encoder_preset = NV_ENC_PRESET_LOSSLESS_HP_GUID;
lossless = 1;
} else if (!strcmp(ctx->preset, "default")) { } else if (!strcmp(ctx->preset, "default")) {
encoder_preset = NV_ENC_PRESET_DEFAULT_GUID; encoder_preset = NV_ENC_PRESET_DEFAULT_GUID;
} else { } else {
av_log(avctx, AV_LOG_FATAL, "Preset \"%s\" is unknown! Supported presets: hp, hq, bd, ll, llhp, llhq, default\n", ctx->preset); av_log(avctx, AV_LOG_FATAL, "Preset \"%s\" is unknown! Supported presets: hp, hq, bd, ll, llhp, llhq, lossless, losslesshp, default\n", ctx->preset);
res = AVERROR(EINVAL); res = AVERROR(EINVAL);
goto error; goto error;
} }
...@@ -753,7 +760,16 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -753,7 +760,16 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
if (avctx->rc_max_rate > 0) if (avctx->rc_max_rate > 0)
ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate; ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate;
if (ctx->cbr) { if (lossless) {
ctx->encode_config.encodeCodecConfig.h264Config.qpPrimeYZeroTransformBypassFlag = 1;
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
ctx->encode_config.rcParams.constQP.qpInterB = 0;
ctx->encode_config.rcParams.constQP.qpInterP = 0;
ctx->encode_config.rcParams.constQP.qpIntra = 0;
avctx->qmin = -1;
avctx->qmax = -1;
} else if (ctx->cbr) {
if (!ctx->twopass) { if (!ctx->twopass) {
ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; ctx->encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
} else if (ctx->twopass == 1 || isLL) { } else if (ctx->twopass == 1 || isLL) {
...@@ -817,6 +833,9 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -817,6 +833,9 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
if (!ctx->profile) { if (!ctx->profile) {
switch (avctx->profile) { switch (avctx->profile) {
case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
break;
case FF_PROFILE_H264_BASELINE: case FF_PROFILE_H264_BASELINE:
ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID;
break; break;
...@@ -842,6 +861,9 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -842,6 +861,9 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
} else if (!strcmp(ctx->profile, "baseline")) { } else if (!strcmp(ctx->profile, "baseline")) {
ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID;
avctx->profile = FF_PROFILE_H264_BASELINE; avctx->profile = FF_PROFILE_H264_BASELINE;
} else if (!strcmp(ctx->profile, "high444p")) {
ctx->encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE;
} else { } else {
av_log(avctx, AV_LOG_FATAL, "Profile \"%s\" is unknown! Supported profiles: high, main, baseline\n", ctx->profile); av_log(avctx, AV_LOG_FATAL, "Profile \"%s\" is unknown! Supported profiles: high, main, baseline\n", ctx->profile);
res = AVERROR(EINVAL); res = AVERROR(EINVAL);
...@@ -849,6 +871,8 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx) ...@@ -849,6 +871,8 @@ static av_cold int nvenc_encode_init(AVCodecContext *avctx)
} }
} }
ctx->encode_config.encodeCodecConfig.h264Config.chromaFormatIDC = avctx->profile == FF_PROFILE_H264_HIGH_444_PREDICTIVE ? 3 : 1;
if (ctx->level) { if (ctx->level) {
res = input_string_to_uint32(avctx, nvenc_h264_level_pairs, ctx->level, &ctx->encode_config.encodeCodecConfig.h264Config.level); res = input_string_to_uint32(avctx, nvenc_h264_level_pairs, ctx->level, &ctx->encode_config.encodeCodecConfig.h264Config.level);
...@@ -1378,6 +1402,7 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, ...@@ -1378,6 +1402,7 @@ static int nvenc_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
static const enum AVPixelFormat pix_fmts_nvenc[] = { static const enum AVPixelFormat pix_fmts_nvenc[] = {
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NV12, AV_PIX_FMT_NV12,
AV_PIX_FMT_YUV444P,
AV_PIX_FMT_NONE AV_PIX_FMT_NONE
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment