Skip to content

obs-nvenc: SDK 13.0 Features #11798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/obs-nvenc/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ SplitEncode.Auto="Auto"
SplitEncode.Disabled="Disabled"
SplitEncode.Enabled="Two-way split"
SplitEncode.ThreeWay="Three-way split"
SplitEncode.FourWay="Four-way split"

Opts="Custom Encoder Options"
Opts.TT="Space-separated list of options to apply to the rate control and codec settings,\nbased their names in the nvEncodeAPI header.\ne.g. \"lookaheadDepth=16 aqStrength=4\""
Expand Down
2 changes: 2 additions & 0 deletions plugins/obs-nvenc/nvenc-helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ static void read_codec_caps(config_t *config, enum codec_type codec, const char
caps->temporal_aq = config_get_bool(config, section, "temporal_aq");
caps->ten_bit = config_get_bool(config, section, "10bit");
caps->four_four_four = config_get_bool(config, section, "yuv_444");
caps->four_two_two = config_get_bool(config, section, "yuv_422");
caps->uhq = config_get_bool(config, section, "uhq");
}

static bool nvenc_check(void)
Expand Down
11 changes: 8 additions & 3 deletions plugins/obs-nvenc/nvenc-helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#define NVENC_12_2_OR_LATER
#endif

#if NVENCAPI_MAJOR_VERSION >= 13
#define NVENC_13_0_OR_LATER
#endif

enum codec_type {
CODEC_H264,
CODEC_HEVC,
Expand Down Expand Up @@ -46,18 +50,19 @@ struct encoder_caps {
int max_width;
int max_height;

/* These don't seem to work correctly, thanks NVIDIA. */
int temporal_filter;
int lookahead_level;
int temporal_filter; /* Broken prior to the 551.21 driver. */
int lookahead_level; /* Broken prior to the 570.20 driver. */

bool dyn_bitrate;
bool lookahead;
bool lossless;
bool temporal_aq;
bool uhq;

/* Yeah... */
bool ten_bit;
bool four_four_four;
bool four_two_two;
};

typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)(NV_ENCODE_API_FUNCTION_LIST *);
Expand Down
5 changes: 5 additions & 0 deletions plugins/obs-nvenc/nvenc-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ struct nvenc_data {
size_t roi_map_size;
uint32_t roi_increment;

#ifdef NVENC_13_0_OR_LATER
CONTENT_LIGHT_LEVEL *cll;
MASTERING_DISPLAY_INFO *mdi;
#endif

struct nvenc_properties props;

CUcontext cu_ctx;
Expand Down
6 changes: 6 additions & 0 deletions plugins/obs-nvenc/nvenc-opts-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ static bool apply_h264_opt(struct obs_option *opt, NV_ENC_CONFIG_H264 *nv_conf)

APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
#ifdef NVENC_13_0_OR_LATER
APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
#endif

APPLY_BIT_OPT(enableFillerDataInsertion, 1)

Expand Down Expand Up @@ -162,6 +165,9 @@ static bool apply_av1_opt(struct obs_option *opt, NV_ENC_CONFIG_AV1 *nv_conf)
APPLY_INT_OPT(numTileRows, uint32_t, PRIu32)
APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
#ifdef NVENC_13_0_OR_LATER
APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
#endif

APPLY_BIT_OPT(enableBitstreamPadding, 1)

Expand Down
19 changes: 13 additions & 6 deletions plugins/obs-nvenc/nvenc-properties.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,10 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
OBS_COMBO_FORMAT_STRING);

#define add_tune(val) obs_property_list_add_string(p, obs_module_text("Tuning." val), val)
#ifdef NVENC_12_2_OR_LATER
/* The UHQ tune is only supported on Turing or later.
* It uses the temporal filtering feature, so we can use its
* availability as an indicator that we are on a supported GPU. */
if (codec == CODEC_HEVC && caps->temporal_filter)
/* The UHQ tune is only supported on Turing or later. */
if (caps->uhq)
add_tune("uhq");
#endif

add_tune("hq");
add_tune("ll");
add_tune("ull");
Expand All @@ -189,6 +186,10 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
} else if (codec == CODEC_AV1) {
add_profile("main");
} else {
#ifdef NVENC_13_0_OR_LATER
if (caps->ten_bit)
add_profile("high10");
#endif
add_profile("high");
add_profile("main");
add_profile("baseline");
Expand Down Expand Up @@ -244,6 +245,12 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
obs_property_list_add_int(p, obs_module_text("SplitEncode.ThreeWay"),
NV_ENC_SPLIT_THREE_FORCED_MODE);
}
#ifdef NVENC_13_0_OR_LATER
if (caps->engines > 3) {
obs_property_list_add_int(p, obs_module_text("SplitEncode.FourWay"),
NV_ENC_SPLIT_FOUR_FORCED_MODE);
}
#endif
}
#endif

Expand Down
83 changes: 82 additions & 1 deletion plugins/obs-nvenc/nvenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ static bool is_10_bit(const struct nvenc_data *enc)
: obs_encoder_video_tex_active(enc->encoder, VIDEO_FORMAT_P010);
}

static bool is_hdr(const enum video_colorspace space)
{
return space == VIDEO_CS_2100_HLG || space == VIDEO_CS_2100_PQ;
}

static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings)
{
UNUSED_PARAMETER(settings);
Expand Down Expand Up @@ -480,6 +485,13 @@ static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings)
if (enc->in_format == VIDEO_FORMAT_I444) {
config->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
h264_config->chromaFormatIDC = 3;
#ifdef NVENC_13_0_OR_LATER
} else if (astrcmpi(enc->props.profile, "high10") == 0) {
config->profileGUID = NV_ENC_H264_PROFILE_HIGH_10_GUID;
} else if (is_10_bit(enc)) {
warn("Forcing high10 for P010");
config->profileGUID = NV_ENC_H264_PROFILE_HIGH_10_GUID;
#endif
} else if (astrcmpi(enc->props.profile, "main") == 0) {
config->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
} else if (astrcmpi(enc->props.profile, "baseline") == 0) {
Expand All @@ -488,6 +500,14 @@ static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings)
config->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
}

#ifdef NVENC_13_0_OR_LATER
/* Note: Only supported on Blackwell! */
h264_config->inputBitDepth = is_10_bit(enc) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
h264_config->outputBitDepth = memcmp(&config->profileGUID, &NV_ENC_H264_PROFILE_HIGH_10_GUID, sizeof(GUID)) == 0
? NV_ENC_BIT_DEPTH_10
: NV_ENC_BIT_DEPTH_8;
#endif

if (!apply_user_args(enc)) {
obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
return false;
Expand Down Expand Up @@ -586,7 +606,7 @@ static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings)
config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
profile_is_10bpc = true;
} else if (is_10_bit(enc)) {
blog(LOG_WARNING, "[obs-nvenc] Forcing main10 for P010");
warn("Forcing main10 for P010");
config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
profile_is_10bpc = true;
} else {
Expand All @@ -600,6 +620,13 @@ static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings)
hevc_config->outputBitDepth = profile_is_10bpc ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
#endif

#ifdef NVENC_13_0_OR_LATER
if (is_10_bit(enc) && is_hdr(voi->colorspace)) {
hevc_config->outputMasteringDisplay = 1;
hevc_config->outputMaxCll = 1;
}
#endif

if (!apply_user_args(enc)) {
obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
return false;
Expand Down Expand Up @@ -687,6 +714,13 @@ static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings)
av1_config->numBwdRefs = 1;
av1_config->repeatSeqHdr = 1;

#ifdef NVENC_13_0_OR_LATER
if (is_10_bit(enc) && is_hdr(voi->colorspace)) {
av1_config->outputMasteringDisplay = 1;
av1_config->outputMaxCll = 1;
}
#endif

if (!apply_user_args(enc)) {
obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
return false;
Expand Down Expand Up @@ -773,6 +807,31 @@ static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, obs_data
}
}

#ifdef NVENC_13_0_OR_LATER
const bool pq = voi->colorspace == VIDEO_CS_2100_PQ;
const bool hlg = voi->colorspace == VIDEO_CS_2100_HLG;
if (pq || hlg) {
enc->cll = bzalloc(sizeof(CONTENT_LIGHT_LEVEL));
enc->mdi = bzalloc(sizeof(MASTERING_DISPLAY_INFO));
const uint16_t hdr_nominal_peak_level = pq ? (uint16_t)obs_get_video_hdr_nominal_peak_level()
: (hlg ? 1000 : 0);
/* Currently these are hardcoded across all encoders. */
enc->mdi->r.x = 13250;
enc->mdi->r.y = 34500;
enc->mdi->g.x = 7500;
enc->mdi->g.y = 3000;
enc->mdi->b.x = 34000;
enc->mdi->b.y = 16000;
enc->mdi->whitePoint.x = 15635;
enc->mdi->whitePoint.y = 16450;
enc->mdi->maxLuma = hdr_nominal_peak_level * 10000;
enc->mdi->minLuma = 0;

enc->cll->maxContentLightLevel = hdr_nominal_peak_level;
enc->cll->maxPicAverageLightLevel = hdr_nominal_peak_level;
}
#endif

switch (enc->codec) {
case CODEC_HEVC:
return init_encoder_hevc(enc, settings);
Expand Down Expand Up @@ -985,6 +1044,13 @@ static void nvenc_destroy(void *data)
bfree(enc->sei);
bfree(enc->roi_map);

#ifdef NVENC_13_0_OR_LATER
if (enc->mdi)
bfree(enc->mdi);
if (enc->cll)
bfree(enc->cll);
#endif

deque_free(&enc->dts_list);

da_free(enc->surfaces);
Expand Down Expand Up @@ -1204,6 +1270,21 @@ bool nvenc_encode_base(struct nvenc_data *enc, struct nv_bitstream *bs, void *pi
: NV_ENC_BUFFER_FORMAT_NV12;
}

#ifdef NVENC_13_0_OR_LATER
if (enc->cll) {
if (enc->codec == CODEC_AV1)
params.codecPicParams.av1PicParams.pMaxCll = enc->cll;
else if (enc->codec == CODEC_HEVC)
params.codecPicParams.hevcPicParams.pMaxCll = enc->cll;
}
if (enc->mdi) {
if (enc->codec == CODEC_AV1)
params.codecPicParams.av1PicParams.pMasteringDisplay = enc->mdi;
else if (enc->codec == CODEC_HEVC)
params.codecPicParams.hevcPicParams.pMasteringDisplay = enc->mdi;
}
#endif

/* Add ROI map if enabled */
if (obs_encoder_has_roi(enc->encoder))
add_roi(enc, &params);
Expand Down
Loading
Loading