encoders/ffmpeg/nvenc: Ensure compatibility with more than just FFmpeg 4.2

When FFmpeg Encoders was originally written, FFmpeg 4.2 was still new and OBS Studio did not seem to want to update to anything newer for a while. This led to code being fine-tuned for FFmpeg 4.2, which stops working the moment OBS Studio upgrades FFmpeg. This removes the dependency on FFmpeg 4.2 hopefully, and allows using newer FFmpeg versions - or perhaps even older versions.

Additionally the nonsensical behavior of the Target Quality slider was fixed. It is now from 0 to 51, instead of from 0 to 100, and as such matches FFmpeg exactly.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2022-02-17 04:30:17 +01:00
parent 7f6047f43f
commit 36aec3be54
5 changed files with 277 additions and 318 deletions

View File

@ -166,27 +166,40 @@ Encoder.FFmpeg.AMF.Other.AccessUnitDelimiter="Access Unit Delimiter"
# Encoder/FFmpeg/NVENC
Encoder.FFmpeg.NVENC.Preset="Preset"
Encoder.FFmpeg.NVENC.Preset.Default="Default"
Encoder.FFmpeg.NVENC.Preset.Slow="Slow"
Encoder.FFmpeg.NVENC.Preset.Medium="Medium"
Encoder.FFmpeg.NVENC.Preset.Fast="Fast"
Encoder.FFmpeg.NVENC.Preset.HighPerformance="High Performance"
Encoder.FFmpeg.NVENC.Preset.HighQuality="High Quality"
Encoder.FFmpeg.NVENC.Preset.BluRayDisc="BluRay Disc"
Encoder.FFmpeg.NVENC.Preset.LowLatency="Low Latency"
Encoder.FFmpeg.NVENC.Preset.LowLatencyHighPerformance="Low Latency High Performance"
Encoder.FFmpeg.NVENC.Preset.LowLatencyHighQuality="Low Latency High Quality"
Encoder.FFmpeg.NVENC.Preset.Lossless="Lossless"
Encoder.FFmpeg.NVENC.Preset.LosslessHighPerformance="Lossless High Performance"
Encoder.FFmpeg.NVENC.Preset.default="Default"
Encoder.FFmpeg.NVENC.Preset.slow="Slow"
Encoder.FFmpeg.NVENC.Preset.medium="Medium"
Encoder.FFmpeg.NVENC.Preset.fast="Fast"
Encoder.FFmpeg.NVENC.Preset.hp="High Performance"
Encoder.FFmpeg.NVENC.Preset.hq="High Quality"
Encoder.FFmpeg.NVENC.Preset.bd="BluRay Disc"
Encoder.FFmpeg.NVENC.Preset.ll="Low Latency"
Encoder.FFmpeg.NVENC.Preset.llhq="Low Latency High Quality"
Encoder.FFmpeg.NVENC.Preset.llhp="Low Latency High Performance"
Encoder.FFmpeg.NVENC.Preset.lossless="Lossless"
Encoder.FFmpeg.NVENC.Preset.losslesshp="Lossless High Performance"
Encoder.FFmpeg.NVENC.Preset.p1="Fastest (P1)"
Encoder.FFmpeg.NVENC.Preset.p2="Faster (P2)"
Encoder.FFmpeg.NVENC.Preset.p3="Fast (P3)"
Encoder.FFmpeg.NVENC.Preset.p4="Medium (P4)"
Encoder.FFmpeg.NVENC.Preset.p5="Slow (P5)"
Encoder.FFmpeg.NVENC.Preset.p6="Slower (P6)"
Encoder.FFmpeg.NVENC.Preset.p7="Slowest (P7)"
Encoder.FFmpeg.NVENC.Tune="Tune"
Encoder.FFmpeg.NVENC.Tune.hq="High Quality"
Encoder.FFmpeg.NVENC.Tune.ll="Low Latency"
Encoder.FFmpeg.NVENC.Tune.ull="Ultra Low Latency"
Encoder.FFmpeg.NVENC.Tune.lossless="Lossless"
Encoder.FFmpeg.NVENC.RateControl="Rate Control Options"
Encoder.FFmpeg.NVENC.RateControl.Mode="Mode"
Encoder.FFmpeg.NVENC.RateControl.Mode.CQP="Constant Quantization Parameter"
Encoder.FFmpeg.NVENC.RateControl.Mode.VBR="Variable Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.VBR_HQ="High Quality Variable Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.CBR="Constant Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.CBR_HQ="High Quality Constant Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.CBR_LD_HQ="Low Delay High Quality Constant Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.constqp="Constant Quantization Parameter"
Encoder.FFmpeg.NVENC.RateControl.Mode.vbr="Variable Bitrate"
Encoder.FFmpeg.NVENC.RateControl.Mode.cbr="Constant Bitrate"
Encoder.FFmpeg.NVENC.RateControl.TwoPass="Two Pass"
Encoder.FFmpeg.NVENC.RateControl.MultiPass="Multi-Pass"
Encoder.FFmpeg.NVENC.RateControl.MultiPass.disabled="Single Pass"
Encoder.FFmpeg.NVENC.RateControl.MultiPass.qres="Two Pass at Quarter Resolution"
Encoder.FFmpeg.NVENC.RateControl.MultiPass.fullres="Two Pass at Full Resolution"
Encoder.FFmpeg.NVENC.RateControl.LookAhead="Look Ahead"
Encoder.FFmpeg.NVENC.RateControl.AdaptiveI="Adaptive I-Frames"
Encoder.FFmpeg.NVENC.RateControl.AdaptiveB="Adaptive B-Frames"
@ -208,12 +221,14 @@ Encoder.FFmpeg.NVENC.AQ.Temporal="Temporal Adaptive Quantization"
Encoder.FFmpeg.NVENC.Other="Other Options"
Encoder.FFmpeg.NVENC.Other.BFrames="Maximum B-Frames"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode="B-Frame Reference Mode"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode.Middle="Use only middle B-Frames as reference"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode.Each="Use all B-Frames as reference"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode.disabled="No B-Frames will be used as Reference"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode.middle="Only B-Frames/2 will be used as Reference"
Encoder.FFmpeg.NVENC.Other.BFrameReferenceMode.each="Each B-Frame will be used as Reference"
Encoder.FFmpeg.NVENC.Other.ZeroLatency="Zero Latency"
Encoder.FFmpeg.NVENC.Other.WeightedPrediction="Weighted Prediction"
Encoder.FFmpeg.NVENC.Other.NonReferencePFrames="Non-reference P-Frames"
Encoder.FFmpeg.NVENC.Other.ReferenceFrames="Reference Frames"
Encoder.FFmpeg.NVENC.Other.LowDelayKeyFrameScale="Low Delay Key-Frame Scale"
# Blur
Blur.Type.Box="Box"

View File

@ -45,20 +45,6 @@ extern "C" {
using namespace streamfx::encoder::ffmpeg::handler;
using namespace streamfx::encoder::codec::h264;
static std::map<profile, std::string> profiles{
{profile::BASELINE, "baseline"},
{profile::MAIN, "main"},
{profile::HIGH, "high"},
{profile::HIGH444_PREDICTIVE, "high444p"},
};
static std::map<level, std::string> levels{
{level::L1_0, "1.0"}, {level::L1_0b, "1.0b"}, {level::L1_1, "1.1"}, {level::L1_2, "1.2"},
{level::L1_3, "1.3"}, {level::L2_0, "2.0"}, {level::L2_1, "2.1"}, {level::L2_2, "2.2"},
{level::L3_0, "3.0"}, {level::L3_1, "3.1"}, {level::L3_2, "3.2"}, {level::L4_0, "4.0"},
{level::L4_1, "4.1"}, {level::L4_2, "4.2"}, {level::L5_0, "5.0"}, {level::L5_1, "5.1"},
};
void nvenc_h264_handler::adjust_info(ffmpeg_factory* fac, const AVCodec*, std::string&, std::string& name, std::string&)
{
name = "NVIDIA NVENC H.264/AVC (via FFmpeg)";
@ -108,19 +94,13 @@ void nvenc_h264_handler::update(obs_data_t* settings, const AVCodec* codec, AVCo
nvenc::update(settings, codec, context);
if (!context->internal) {
{
auto found = profiles.find(static_cast<profile>(obs_data_get_int(settings, ST_KEY_PROFILE)));
if (found != profiles.end()) {
av_opt_set(context->priv_data, "profile", found->second.c_str(), 0);
}
if (auto value = obs_data_get_int(settings, ST_KEY_PROFILE); value > -1) {
av_opt_set_int(context->priv_data, "profile", value, AV_OPT_SEARCH_CHILDREN);
}
{
auto found = levels.find(static_cast<level>(obs_data_get_int(settings, ST_KEY_LEVEL)));
if (found != levels.end()) {
av_opt_set(context->priv_data, "level", found->second.c_str(), 0);
} else {
av_opt_set(context->priv_data, "level", "auto", 0);
}
if (auto value = obs_data_get_int(settings, ST_KEY_LEVEL); value > -1) {
av_opt_set_int(context->priv_data, "level", value, AV_OPT_SEARCH_CHILDREN);
} else {
av_opt_set(context->priv_data, "level", "auto", AV_OPT_SEARCH_CHILDREN);
}
}
}
@ -143,7 +123,13 @@ void nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec,
void nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
{
nvenc::get_properties_pre(props, codec);
AVCodecContext* context = avcodec_alloc_context3(codec);
if (!context->priv_data) {
avcodec_free_context(&context);
return;
}
nvenc::get_properties_pre(props, codec, context);
{
obs_properties_t* grp = props;
@ -155,23 +141,27 @@ void nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const A
{
auto p = obs_properties_add_list(grp, ST_KEY_PROFILE, D_TRANSLATE(S_CODEC_H264_PROFILE),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), static_cast<int64_t>(profile::UNKNOWN));
for (auto const kv : profiles) {
std::string trans = std::string(S_CODEC_H264_PROFILE) + "." + kv.second;
obs_property_list_add_int(p, D_TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "profile", p, S_CODEC_H264_PROFILE);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_LEVEL, D_TRANSLATE(S_CODEC_H264_LEVEL), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_AUTOMATIC), static_cast<int64_t>(level::UNKNOWN));
for (auto const kv : levels) {
obs_property_list_add_int(p, kv.second.c_str(), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_AUTOMATIC), 0);
streamfx::ffmpeg::tools::avoption_list_add_entries_unnamed(context->priv_data, "level", p,
[](const AVOption* opt) {
if (opt->default_val.i64 == 0)
return true;
return false;
});
}
}
nvenc::get_properties_post(props, codec);
nvenc::get_properties_post(props, codec, context);
if (context) {
avcodec_free_context(&context);
}
}
void nvenc_h264_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context)

View File

@ -42,23 +42,6 @@ extern "C" {
using namespace streamfx::encoder::ffmpeg::handler;
using namespace streamfx::encoder::codec::hevc;
static std::map<profile, std::string> profiles{
{profile::MAIN, "main"},
{profile::MAIN10, "main10"},
{profile::RANGE_EXTENDED, "rext"},
};
static std::map<tier, std::string> tiers{
{tier::MAIN, "main"},
{tier::HIGH, "high"},
};
static std::map<level, std::string> levels{
{level::L1_0, "1.0"}, {level::L2_0, "2.0"}, {level::L2_1, "2.1"}, {level::L3_0, "3.0"}, {level::L3_1, "3.1"},
{level::L4_0, "4.0"}, {level::L4_1, "4.1"}, {level::L5_0, "5.0"}, {level::L5_1, "5.1"}, {level::L5_2, "5.2"},
{level::L6_0, "6.0"}, {level::L6_1, "6.1"}, {level::L6_2, "6.2"},
};
void nvenc_hevc_handler::adjust_info(ffmpeg_factory* fac, const AVCodec*, std::string&, std::string& name, std::string&)
{
name = "NVIDIA NVENC H.265/HEVC (via FFmpeg)";
@ -109,25 +92,16 @@ void nvenc_hevc_handler::update(obs_data_t* settings, const AVCodec* codec, AVCo
nvenc::update(settings, codec, context);
if (!context->internal) {
{ // HEVC Options
auto found = profiles.find(static_cast<profile>(obs_data_get_int(settings, ST_KEY_PROFILE)));
if (found != profiles.end()) {
av_opt_set(context->priv_data, "profile", found->second.c_str(), 0);
}
if (auto v = obs_data_get_int(settings, ST_KEY_PROFILE); v > -1) {
av_opt_set_int(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN);
}
{
auto found = tiers.find(static_cast<tier>(obs_data_get_int(settings, ST_KEY_TIER)));
if (found != tiers.end()) {
av_opt_set(context->priv_data, "tier", found->second.c_str(), 0);
}
if (auto v = obs_data_get_int(settings, ST_KEY_TIER); v > -1) {
av_opt_set_int(context->priv_data, "tier", v, AV_OPT_SEARCH_CHILDREN);
}
{
auto found = levels.find(static_cast<level>(obs_data_get_int(settings, ST_KEY_LEVEL)));
if (found != levels.end()) {
av_opt_set(context->priv_data, "level", found->second.c_str(), 0);
} else {
av_opt_set(context->priv_data, "level", "auto", 0);
}
if (auto v = obs_data_get_int(settings, ST_KEY_LEVEL); v > -1) {
av_opt_set_int(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN);
} else {
av_opt_set(context->priv_data, "level", "auto", AV_OPT_SEARCH_CHILDREN);
}
}
}
@ -152,7 +126,13 @@ void nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec,
void nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
{
nvenc::get_properties_pre(props, codec);
AVCodecContext* context = avcodec_alloc_context3(codec);
if (!context->priv_data) {
avcodec_free_context(&context);
return;
}
nvenc::get_properties_pre(props, codec, context);
{
obs_properties_t* grp = props;
@ -164,32 +144,33 @@ void nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const A
{
auto p = obs_properties_add_list(grp, ST_KEY_PROFILE, D_TRANSLATE(S_CODEC_HEVC_PROFILE),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), static_cast<int64_t>(profile::UNKNOWN));
for (auto const kv : profiles) {
std::string trans = std::string(S_CODEC_HEVC_PROFILE) + "." + kv.second;
obs_property_list_add_int(p, D_TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "profile", p, S_CODEC_HEVC_PROFILE);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_TIER, D_TRANSLATE(S_CODEC_HEVC_TIER), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), static_cast<int64_t>(tier::UNKNOWN));
for (auto const kv : tiers) {
std::string trans = std::string(S_CODEC_HEVC_TIER) + "." + kv.second;
obs_property_list_add_int(p, D_TRANSLATE(trans.c_str()), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "tier", p, S_CODEC_HEVC_TIER);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_LEVEL, D_TRANSLATE(S_CODEC_HEVC_LEVEL), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_AUTOMATIC), static_cast<int64_t>(level::UNKNOWN));
for (auto const kv : levels) {
obs_property_list_add_int(p, kv.second.c_str(), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_AUTOMATIC), 0);
streamfx::ffmpeg::tools::avoption_list_add_entries_unnamed(context->priv_data, "level", p,
[](const AVOption* opt) {
if (opt->default_val.i64 == 0)
return true;
return false;
});
}
}
nvenc::get_properties_post(props, codec);
nvenc::get_properties_post(props, codec, context);
if (context) {
avcodec_free_context(&context);
}
}
void nvenc_hevc_handler::get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context)

View File

@ -33,12 +33,17 @@ extern "C" {
#define ST_I18N_PRESET "Encoder.FFmpeg.NVENC.Preset"
#define ST_I18N_PRESET_(x) ST_I18N_PRESET "." D_VSTR(x)
#define ST_KEY_PRESET "Preset"
#define ST_I18N_TUNE "Encoder.FFmpeg.NVENC.Tune"
#define ST_I18N_TUNE_(x) ST_I18N_TUNE "." D_VSTR(x)
#define ST_KEY_TUNE "Tune"
#define ST_I18N_RATECONTROL "Encoder.FFmpeg.NVENC.RateControl"
#define ST_I18N_RATECONTROL_MODE ST_I18N_RATECONTROL ".Mode"
#define ST_I18N_RATECONTROL_MODE_(x) ST_I18N_RATECONTROL_MODE "." D_VSTR(x)
#define ST_KEY_RATECONTROL_MODE "RateControl.Mode"
#define ST_I18N_RATECONTROL_TWOPASS ST_I18N_RATECONTROL ".TwoPass"
#define ST_KEY_RATECONTROL_TWOPASS "RateControl.TwoPass"
#define ST_I18N_RATECONTROL_MULTIPASS ST_I18N_RATECONTROL ".MultiPass"
#define ST_KEY_RATECONTROL_MULTIPASS "RateControl.MultiPass"
#define ST_I18N_RATECONTROL_LOOKAHEAD ST_I18N_RATECONTROL ".LookAhead"
#define ST_KEY_RATECONTROL_LOOKAHEAD "RateControl.LookAhead"
#define ST_I18N_RATECONTROL_ADAPTIVEI ST_I18N_RATECONTROL ".AdaptiveI"
@ -86,66 +91,25 @@ extern "C" {
#define ST_KEY_OTHER_NONREFERENCEPFRAMES "Other.NonReferencePFrames"
#define ST_I18N_OTHER_REFERENCEFRAMES ST_I18N_OTHER ".ReferenceFrames"
#define ST_KEY_OTHER_REFERENCEFRAMES "Other.ReferenceFrames"
#define ST_I18N_OTHER_LOWDELAYKEYFRAMESCALE ST_I18N_OTHER ".LowDelayKeyFrameScale"
#define ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE "Other.LowDelayKeyFrameScale"
using namespace streamfx::encoder::ffmpeg::handler;
std::map<nvenc::preset, std::string> nvenc::presets{
{nvenc::preset::DEFAULT, ST_I18N_PRESET_(Default)},
{nvenc::preset::SLOW, ST_I18N_PRESET_(Slow)},
{nvenc::preset::MEDIUM, ST_I18N_PRESET_(Medium)},
{nvenc::preset::FAST, ST_I18N_PRESET_(Fast)},
{nvenc::preset::HIGH_PERFORMANCE, ST_I18N_PRESET_(HighPerformance)},
{nvenc::preset::HIGH_QUALITY, ST_I18N_PRESET_(HighQuality)},
{nvenc::preset::BLURAYDISC, ST_I18N_PRESET_(BluRayDisc)},
{nvenc::preset::LOW_LATENCY, ST_I18N_PRESET_(LowLatency)},
{nvenc::preset::LOW_LATENCY_HIGH_PERFORMANCE, ST_I18N_PRESET_(LowLatencyHighPerformance)},
{nvenc::preset::LOW_LATENCY_HIGH_QUALITY, ST_I18N_PRESET_(LowLatencyHighQuality)},
{nvenc::preset::LOSSLESS, ST_I18N_PRESET_(Lossless)},
{nvenc::preset::LOSSLESS_HIGH_PERFORMANCE, ST_I18N_PRESET_(LosslessHighPerformance)},
};
inline bool is_cqp(std::string_view rc)
{
return std::string_view("constqp") == rc;
}
std::map<nvenc::preset, std::string> nvenc::preset_to_opt{
{nvenc::preset::DEFAULT, "default"},
{nvenc::preset::SLOW, "slow"},
{nvenc::preset::MEDIUM, "medium"},
{nvenc::preset::FAST, "fast"},
{nvenc::preset::HIGH_PERFORMANCE, "hp"},
{nvenc::preset::HIGH_QUALITY, "hq"},
{nvenc::preset::BLURAYDISC, "bd"},
{nvenc::preset::LOW_LATENCY, "ll"},
{nvenc::preset::LOW_LATENCY_HIGH_PERFORMANCE, "llhp"},
{nvenc::preset::LOW_LATENCY_HIGH_QUALITY, "llhq"},
{nvenc::preset::LOSSLESS, "lossless"},
{nvenc::preset::LOSSLESS_HIGH_PERFORMANCE, "losslesshp"},
};
inline bool is_cbr(std::string_view rc)
{
return std::string_view("cbr") == rc;
}
std::map<nvenc::ratecontrolmode, std::string> nvenc::ratecontrolmodes{
{nvenc::ratecontrolmode::CQP, ST_I18N_RATECONTROL_MODE_(CQP)},
{nvenc::ratecontrolmode::VBR, ST_I18N_RATECONTROL_MODE_(VBR)},
{nvenc::ratecontrolmode::VBR_HQ, ST_I18N_RATECONTROL_MODE_(VBR_HQ)},
{nvenc::ratecontrolmode::CBR, ST_I18N_RATECONTROL_MODE_(CBR)},
{nvenc::ratecontrolmode::CBR_HQ, ST_I18N_RATECONTROL_MODE_(CBR_HQ)},
{nvenc::ratecontrolmode::CBR_LD_HQ, ST_I18N_RATECONTROL_MODE_(CBR_LD_HQ)},
};
std::map<nvenc::ratecontrolmode, std::string> nvenc::ratecontrolmode_to_opt{
{nvenc::ratecontrolmode::CQP, "constqp"}, {nvenc::ratecontrolmode::VBR, "vbr"},
{nvenc::ratecontrolmode::VBR_HQ, "vbr_hq"}, {nvenc::ratecontrolmode::CBR, "cbr"},
{nvenc::ratecontrolmode::CBR_HQ, "cbr_hq"}, {nvenc::ratecontrolmode::CBR_LD_HQ, "cbr_ld_hq"},
};
std::map<nvenc::b_ref_mode, std::string> nvenc::b_ref_modes{
{nvenc::b_ref_mode::INVALID, S_STATE_DEFAULT},
{nvenc::b_ref_mode::DISABLED, S_STATE_DISABLED},
{nvenc::b_ref_mode::EACH, ST_I18N_OTHER_BFRAMEREFERENCEMODE ".Each"},
{nvenc::b_ref_mode::MIDDLE, ST_I18N_OTHER_BFRAMEREFERENCEMODE ".Middle"},
};
std::map<nvenc::b_ref_mode, std::string> nvenc::b_ref_mode_to_opt{
{nvenc::b_ref_mode::DISABLED, "disabled"},
{nvenc::b_ref_mode::EACH, "each"},
{nvenc::b_ref_mode::MIDDLE, "middle"},
};
inline bool is_vbr(std::string_view rc)
{
return std::string_view("vbr") == rc;
}
bool streamfx::encoder::ffmpeg::handler::nvenc::is_available()
{
@ -198,9 +162,9 @@ void nvenc::override_update(ffmpeg_instance* instance, obs_data_t*)
void nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
{
obs_data_set_default_int(settings, ST_KEY_PRESET, static_cast<int64_t>(nvenc::preset::DEFAULT));
obs_data_set_default_int(settings, ST_KEY_PRESET, -1);
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_MODE, static_cast<int64_t>(ratecontrolmode::CBR_HQ));
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_MODE, -1);
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_TWOPASS, -1);
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LOOKAHEAD, -1);
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_ADAPTIVEI, -1);
@ -222,11 +186,12 @@ void nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
obs_data_set_default_int(settings, ST_KEY_AQ_TEMPORAL, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_BFRAMES, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE, static_cast<int64_t>(b_ref_mode::INVALID));
obs_data_set_default_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_ZEROLATENCY, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_WEIGHTEDPREDICTION, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_NONREFERENCEPFRAMES, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_REFERENCEFRAMES, -1);
obs_data_set_default_int(settings, ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE, -1);
// Replay Buffer
obs_data_set_default_int(settings, "bitrate", 0);
@ -234,33 +199,29 @@ void nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
{
// Decode the name into useful flags.
auto value = obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE);
bool have_bitrate = false;
bool have_bitrate_range = false;
bool have_quality = false;
bool have_qp_limits = false;
bool have_qp = false;
nvenc::ratecontrolmode rc =
static_cast<nvenc::ratecontrolmode>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE));
switch (rc) {
case nvenc::ratecontrolmode::CQP:
have_qp = true;
break;
case nvenc::ratecontrolmode::INVALID:
case nvenc::ratecontrolmode::CBR:
case nvenc::ratecontrolmode::CBR_HQ:
case nvenc::ratecontrolmode::CBR_LD_HQ:
have_bitrate = true;
have_qp_limits = true;
break;
case nvenc::ratecontrolmode::VBR:
case nvenc::ratecontrolmode::VBR_HQ:
if (value == 2) {
have_bitrate = true;
} else if (value == 1) {
have_bitrate = true;
have_bitrate_range = true;
have_quality = true;
have_qp_limits = true;
have_qp = true;
} else if (value == 0) {
have_qp = true;
} else {
have_bitrate = true;
have_bitrate_range = true;
have_quality = true;
have_qp_limits = true;
have_qp = true;
break;
}
obs_property_set_visible(obs_properties_get(props, ST_I18N_RATECONTROL_LIMITS), have_bitrate || have_quality);
@ -286,16 +247,22 @@ static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* se
return true;
}
void nvenc::get_properties_pre(obs_properties_t* props, const AVCodec*)
void nvenc::get_properties_pre(obs_properties_t* props, const AVCodec*, const AVCodecContext* context)
{
auto p = obs_properties_add_list(props, ST_KEY_PRESET, D_TRANSLATE(ST_I18N_PRESET), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
for (auto kv : presets) {
obs_property_list_add_int(p, D_TRANSLATE(kv.second.c_str()), static_cast<int64_t>(kv.first));
{
auto p = obs_properties_add_list(props, ST_KEY_PRESET, D_TRANSLATE(ST_I18N_PRESET), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "preset", p, ST_I18N_PRESET);
}
if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "tune")) {
auto p = obs_properties_add_list(props, ST_KEY_TUNE, D_TRANSLATE(ST_I18N_TUNE), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "tune", p, ST_I18N_TUNE);
}
}
void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, const AVCodecContext* context)
{
{ // Rate Control
obs_properties_t* grp = props;
@ -309,12 +276,23 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
auto p = obs_properties_add_list(grp, ST_KEY_RATECONTROL_MODE, D_TRANSLATE(ST_I18N_RATECONTROL_MODE),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_modified_callback(p, modified_ratecontrol);
for (auto kv : ratecontrolmodes) {
obs_property_list_add_int(p, D_TRANSLATE(kv.second.c_str()), static_cast<int64_t>(kv.first));
}
auto filter = [](const AVOption* opt) {
if (opt->default_val.i64 & (1 << 23))
return true;
return false;
};
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "rc", p, ST_I18N_RATECONTROL_MODE,
filter);
}
{
if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "multipass")) {
auto p =
obs_properties_add_list(grp, ST_KEY_RATECONTROL_MULTIPASS, D_TRANSLATE(ST_I18N_RATECONTROL_MULTIPASS),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "multipass", p,
ST_I18N_RATECONTROL_MULTIPASS);
} else {
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_RATECONTROL_TWOPASS,
D_TRANSLATE(ST_I18N_RATECONTROL_TWOPASS));
}
@ -347,7 +325,7 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
{
auto p = obs_properties_add_float_slider(grp, ST_KEY_RATECONTROL_LIMITS_QUALITY,
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUALITY), 0, 100, 0.01);
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUALITY), 0, 51, 0.01);
}
{
@ -442,13 +420,9 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
auto p = obs_properties_add_list(grp, ST_KEY_OTHER_BFRAMEREFERENCEMODE,
D_TRANSLATE(ST_I18N_OTHER_BFRAMEREFERENCEMODE), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
for (auto kv : b_ref_modes) {
if (kv.first == nvenc::b_ref_mode::EACH && (std::string_view("h264_nvenc") == codec->name)) {
// H.264 does not support using all B-Frames as reference.
continue;
}
obs_property_list_add_int(p, D_TRANSLATE(kv.second.c_str()), static_cast<int64_t>(kv.first));
}
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "b_ref_mode", p,
ST_I18N_OTHER_BFRAMEREFERENCEMODE);
}
{
@ -472,15 +446,22 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec)
(strcmp(codec->name, "h264_nvenc") == 0) ? 16 : 4, 1);
obs_property_int_set_suffix(p, " frames");
}
if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "ldkfs")) {
auto p = obs_properties_add_int_slider(grp, ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE,
D_TRANSLATE(ST_I18N_OTHER_LOWDELAYKEYFRAMESCALE), -1, 255, 1);
}
}
}
void nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCodecContext*)
{
obs_property_set_enabled(obs_properties_get(props, ST_KEY_PRESET), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_TUNE), false);
obs_property_set_enabled(obs_properties_get(props, ST_I18N_RATECONTROL), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_MODE), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_TWOPASS), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_MULTIPASS), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_LOOKAHEAD), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_ADAPTIVEI), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_ADAPTIVEB), false);
@ -506,84 +487,91 @@ void nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCo
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_WEIGHTEDPREDICTION), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_NONREFERENCEPFRAMES), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_REFERENCEFRAMES), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE), false);
}
void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{
if (!context->internal) {
preset c_preset = static_cast<preset>(obs_data_get_int(settings, ST_KEY_PRESET));
auto found = preset_to_opt.find(c_preset);
if (found != preset_to_opt.end()) {
av_opt_set(context->priv_data, "preset", found->second.c_str(), 0);
} else {
av_opt_set(context->priv_data, "preset", nullptr, 0);
auto value = obs_data_get_int(settings, ST_KEY_PRESET);
if (value != -1) {
auto name = streamfx::ffmpeg::tools::avoption_name_from_unit_value(context->priv_data, "preset", value);
if (name) {
av_opt_set(context->priv_data, "preset", name, AV_OPT_SEARCH_CHILDREN);
} else {
av_opt_set_int(context->priv_data, "preset", value, AV_OPT_SEARCH_CHILDREN);
}
}
}
{ // Rate Control
auto value = obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE);
auto name = streamfx::ffmpeg::tools::avoption_name_from_unit_value(context->priv_data, "rc", value);
if (value != -1) {
if (name && !context->internal) {
av_opt_set(context->priv_data, "rc", name, AV_OPT_SEARCH_CHILDREN);
} else {
av_opt_set_int(context->priv_data, "rc", value, AV_OPT_SEARCH_CHILDREN);
}
}
// Decode the name into useful flags.
bool have_bitrate = false;
bool have_bitrate_range = false;
bool have_quality = false;
bool have_qp_limits = false;
bool have_qp = false;
if (is_cbr(name)) {
have_bitrate = true;
ratecontrolmode rc = static_cast<ratecontrolmode>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE));
auto rcopt = ratecontrolmode_to_opt.find(rc);
if (rcopt != ratecontrolmode_to_opt.end()) {
if (!context->internal) {
av_opt_set(context->priv_data, "rc", rcopt->second.c_str(), AV_OPT_SEARCH_CHILDREN);
}
if (!context->internal)
av_opt_set_int(context->priv_data, "cbr", 1, AV_OPT_SEARCH_CHILDREN);
// Support for OBS Studio
obs_data_set_string(settings, "rate_control", "CBR");
} else if (is_vbr(name)) {
have_bitrate = true;
have_bitrate_range = true;
have_quality = true;
have_qp_limits = true;
have_qp = true;
if (!context->internal)
av_opt_set_int(context->priv_data, "cbr", 0, AV_OPT_SEARCH_CHILDREN);
// Support for OBS Studio
obs_data_set_string(settings, "rate_control", "VBR");
} else if (is_cqp(name)) {
have_qp = true;
if (!context->internal)
av_opt_set_int(context->priv_data, "cbr", 0, AV_OPT_SEARCH_CHILDREN);
// Support for OBS Studio
obs_data_set_string(settings, "rate_control", "CQP");
} else {
have_bitrate = true;
have_bitrate_range = true;
have_quality = true;
have_qp_limits = true;
have_qp = true;
if (!context->internal)
av_opt_set_int(context->priv_data, "cbr", 0, AV_OPT_SEARCH_CHILDREN);
}
if (!context->internal) {
av_opt_set_int(context->priv_data, "cbr", 0, AV_OPT_SEARCH_CHILDREN);
}
switch (rc) {
case ratecontrolmode::CQP:
have_qp = true;
{ // Support for OBS Studio
obs_data_set_string(settings, "rate_control", "CQP");
}
break;
case ratecontrolmode::INVALID:
case ratecontrolmode::CBR:
case ratecontrolmode::CBR_HQ:
case ratecontrolmode::CBR_LD_HQ:
have_bitrate = true;
have_qp_limits = true;
if (!context->internal) {
av_opt_set_int(context->priv_data, "cbr", 1, AV_OPT_SEARCH_CHILDREN);
}
{ // Support for OBS Studio
obs_data_set_string(settings, "rate_control", "CBR");
}
break;
case ratecontrolmode::VBR:
case ratecontrolmode::VBR_HQ:
have_bitrate_range = true;
have_bitrate = true;
have_quality = true;
have_qp = true;
have_qp_limits = true;
{ // Support for OBS Studio
obs_data_set_string(settings, "rate_control", "VBR");
}
break;
}
if (!context->internal) {
// Two Pass
if (int tp = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_TWOPASS)); tp > -1) {
av_opt_set_int(context->priv_data, "2pass", tp ? 1 : 0, AV_OPT_SEARCH_CHILDREN);
if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "multipass")) {
// Multi-Pass
if (int tp = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MULTIPASS)); tp > -1) {
av_opt_set_int(context->priv_data, "multipass", tp, AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(context->priv_data, "2pass", 0, AV_OPT_SEARCH_CHILDREN);
}
} else {
// Two-Pass
if (int tp = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_TWOPASS)); tp > -1) {
av_opt_set_int(context->priv_data, "2pass", tp ? 1 : 0, AV_OPT_SEARCH_CHILDREN);
}
}
// Look Ahead # of Frames
@ -600,8 +588,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
}
// Adaptive B-Frames
constexpr std::string_view h264_encoder_name = "h264_nvenc";
if (h264_encoder_name == codec->name) {
if (std::string_view("h264_nvenc") == codec->name) {
if (int64_t adapt_b = obs_data_get_int(settings, ST_KEY_RATECONTROL_ADAPTIVEB);
!streamfx::util::is_tristate_default(adapt_b) && (la != 0)) {
av_opt_set_int(context->priv_data, "b_adapt", adapt_b, AV_OPT_SEARCH_CHILDREN);
@ -661,8 +648,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
// Quality Target
if (have_quality) {
if (double_t v = obs_data_get_double(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY) / 100.0 * 51.0;
v > 0) {
if (double_t v = obs_data_get_double(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY); v > 0) {
av_opt_set_double(context->priv_data, "cq", v, AV_OPT_SEARCH_CHILDREN);
}
} else {
@ -703,7 +689,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
if (!context->internal) { // Other
if (int64_t bf = obs_data_get_int(settings, ST_KEY_OTHER_BFRAMES); bf > -1)
context->max_b_frames = static_cast<int>(bf);
av_opt_set_int(context, "bf", bf, AV_OPT_SEARCH_CHILDREN);
if (int64_t zl = obs_data_get_int(settings, ST_KEY_OTHER_ZEROLATENCY); !streamfx::util::is_tristate_default(zl))
av_opt_set_int(context->priv_data, "zerolatency", zl, AV_OPT_SEARCH_CHILDREN);
@ -711,7 +697,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
!streamfx::util::is_tristate_default(nrp))
av_opt_set_int(context->priv_data, "nonref_p", nrp, AV_OPT_SEARCH_CHILDREN);
if (int64_t v = obs_data_get_int(settings, ST_KEY_OTHER_REFERENCEFRAMES); v > -1)
context->refs = v;
av_opt_set_int(context, "refs", v, AV_OPT_SEARCH_CHILDREN);
int64_t wp = obs_data_get_int(settings, ST_KEY_OTHER_WEIGHTEDPREDICTION);
if ((context->max_b_frames > 0) && streamfx::util::is_tristate_enabled(wp)) {
@ -721,12 +707,12 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
av_opt_set_int(context->priv_data, "weighted_pred", wp, AV_OPT_SEARCH_CHILDREN);
}
{
auto found = b_ref_mode_to_opt.find(
static_cast<b_ref_mode>(obs_data_get_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE)));
if (found != b_ref_mode_to_opt.end()) {
av_opt_set(context->priv_data, "b_ref_mode", found->second.c_str(), AV_OPT_SEARCH_CHILDREN);
}
if (auto v = obs_data_get_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE); v > -1) {
av_opt_set_int(context->priv_data, "b_ref_mode", v, AV_OPT_SEARCH_CHILDREN);
}
if (auto v = obs_data_get_int(settings, ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE); v > -1) {
av_opt_set_int(context->priv_data, "ldkfs", v, AV_OPT_SEARCH_CHILDREN);
}
}
}
@ -741,6 +727,8 @@ void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* conte
tools::print_av_option_string2(context, "rc", " Rate Control",
[](int64_t v, std::string_view o) { return std::string(o); });
tools::print_av_option_bool(context, "2pass", " Two Pass");
tools::print_av_option_string2(context, "multipass", " Multi-Pass",
[](int64_t v, std::string_view o) { return std::string(o); });
tools::print_av_option_int(context, "rc-lookahead", " Look-Ahead", "Frames");
tools::print_av_option_bool(context, "no-scenecut", " Adaptive I-Frames", true);
if (strcmp(codec->name, "h264_nvenc") == 0)
@ -759,6 +747,8 @@ void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* conte
tools::print_av_option_int(context, "init_qpI", " I-Frame", "");
tools::print_av_option_int(context, "init_qpP", " P-Frame", "");
tools::print_av_option_int(context, "init_qpB", " B-Frame", "");
tools::print_av_option_int(context, "qp_cb_offset", " CB Offset", "");
tools::print_av_option_int(context, "qp_cr_offset", " CR Offset", "");
tools::print_av_option_int(context, "bf", " B-Frames", "Frames");
tools::print_av_option_string2(context, "b_ref_mode", " Reference Mode",
@ -783,9 +773,14 @@ void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* conte
tools::print_av_option_bool(context, "strict_gop", " Strict GOP");
tools::print_av_option_bool(context, "aud", " Access Unit Delimiters");
tools::print_av_option_bool(context, "bluray-compat", " Bluray Compatibility");
if (strcmp(codec->name, "h264_nvenc") == 0)
tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
tools::print_av_option_int(context, "dpb_size", " DPB Size", "Frames");
tools::print_av_option_int(context, "ldkfs", " DPB Size", "Frames");
tools::print_av_option_bool(context, "extra_sei", " Extra SEI Data");
tools::print_av_option_bool(context, "udu_sei", " User SEI Data");
tools::print_av_option_bool(context, "intra-refresh", " Intra-Refresh");
tools::print_av_option_bool(context, "single-slice-intra-refresh", " Single Slice Intra-Refresh");
tools::print_av_option_bool(context, "constrained-encoding", " Constrained Encoding");
}
void streamfx::encoder::ffmpeg::handler::nvenc::migrate(obs_data_t* settings, uint64_t version, const AVCodec* codec,
@ -814,5 +809,31 @@ void streamfx::encoder::ffmpeg::handler::nvenc::migrate(obs_data_t* settings, ui
obs_data_unset_user_value(settings, "Other.DecodedPictureBufferSize");
}
if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) {
if (auto v = obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE); v != -1) {
switch (v) {
case 0: // CQP
break;
case 2: // VBR_HQ
obs_data_set_int(settings, ST_KEY_RATECONTROL_MODE, 1);
obs_data_set_int(settings, ST_KEY_RATECONTROL_TWOPASS, 1);
obs_data_set_int(settings, ST_KEY_RATECONTROL_MULTIPASS, 1);
case 1: // VBR
break;
case 5: // CBR_LD_HQ
obs_data_set_int(settings, ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE, 1);
case 4: // CBR_HQ
obs_data_set_int(settings, ST_KEY_RATECONTROL_MODE, 2);
obs_data_set_int(settings, ST_KEY_RATECONTROL_TWOPASS, 1);
obs_data_set_int(settings, ST_KEY_RATECONTROL_MULTIPASS, 1);
case 3: // CBR
break;
}
}
if (auto v = obs_data_get_double(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY); v > 0) {
obs_data_set_double(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY, (v / 100.) * 51.);
}
}
#undef COPY_UNSET
}

View File

@ -42,63 +42,15 @@ extern "C" {
*/
namespace streamfx::encoder::ffmpeg::handler::nvenc {
enum class preset : int64_t {
DEFAULT,
SLOW,
MEDIUM,
FAST,
HIGH_PERFORMANCE,
HIGH_QUALITY,
BLURAYDISC,
LOW_LATENCY,
LOW_LATENCY_HIGH_PERFORMANCE,
LOW_LATENCY_HIGH_QUALITY,
LOSSLESS,
LOSSLESS_HIGH_PERFORMANCE,
// Append things before this.
INVALID = -1,
};
enum class ratecontrolmode : int64_t {
CQP,
VBR,
VBR_HQ,
CBR,
CBR_HQ,
CBR_LD_HQ,
// Append things before this.
INVALID = -1,
};
enum class b_ref_mode : int64_t {
DISABLED,
EACH,
MIDDLE,
// Append things before this.
INVALID = -1,
};
extern std::map<preset, std::string> presets;
extern std::map<preset, std::string> preset_to_opt;
extern std::map<ratecontrolmode, std::string> ratecontrolmodes;
extern std::map<ratecontrolmode, std::string> ratecontrolmode_to_opt;
extern std::map<b_ref_mode, std::string> b_ref_modes;
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
bool is_available();
void override_update(ffmpeg_instance* instance, obs_data_t* settings);
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
void get_properties_pre(obs_properties_t* props, const AVCodec* codec);
void get_properties_pre(obs_properties_t* props, const AVCodec* codec, const AVCodecContext* context);
void get_properties_post(obs_properties_t* props, const AVCodec* codec);
void get_properties_post(obs_properties_t* props, const AVCodec* codec, const AVCodecContext* context);
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);