From 03b16786e7450e61549e3a53c043ac2ff461c9e3 Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Sun, 20 Feb 2022 19:45:41 +0100 Subject: [PATCH] encoders/ffmpeg/nvenc: Improve compatibility with FFmpeg Replaces some very specific code with generic support for FFmpeg, which should last us much longer than the old way. Also improves the migration of settings, which wasn't quite working with the previous way. --- .../encoders/handlers/nvenc_h264_handler.cpp | 60 ++++--- .../encoders/handlers/nvenc_hevc_handler.cpp | 88 ++++++++--- source/encoders/handlers/nvenc_shared.cpp | 149 +++++++++++------- 3 files changed, 201 insertions(+), 96 deletions(-) diff --git a/source/encoders/handlers/nvenc_h264_handler.cpp b/source/encoders/handlers/nvenc_h264_handler.cpp index 3eeedecd..d395c418 100644 --- a/source/encoders/handlers/nvenc_h264_handler.cpp +++ b/source/encoders/handlers/nvenc_h264_handler.cpp @@ -56,8 +56,8 @@ void nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec { nvenc::get_defaults(settings, codec, context); - obs_data_set_default_int(settings, ST_KEY_PROFILE, static_cast(profile::HIGH)); - obs_data_set_default_int(settings, ST_KEY_LEVEL, static_cast(level::UNKNOWN)); + obs_data_set_default_string(settings, ST_KEY_PROFILE, ""); + obs_data_set_default_string(settings, ST_KEY_LEVEL, "auto"); } bool nvenc_h264_handler::has_keyframe_support(ffmpeg_factory*) @@ -94,13 +94,11 @@ void nvenc_h264_handler::update(obs_data_t* settings, const AVCodec* codec, AVCo nvenc::update(settings, codec, context); if (!context->internal) { - 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); + if (auto v = obs_data_get_string(settings, ST_KEY_PROFILE); v && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN); } - 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); + if (auto v = obs_data_get_string(settings, ST_KEY_LEVEL); v && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN); } } } @@ -140,20 +138,26 @@ 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), -1); - streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "profile", p, S_CODEC_H264_PROFILE); + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_DEFAULT), ""); + streamfx::ffmpeg::tools::avoption_list_add_entries( + context->priv_data, "profile", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", S_CODEC_H264_PROFILE, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } { 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), 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; - }); + OBS_COMBO_FORMAT_STRING); + + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "level", [&p](const AVOption* opt) { + if (opt->default_val.i64 == 0) { + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_AUTOMATIC), "auto"); + } else { + obs_property_list_add_string(p, opt->name, opt->name); + } + }); } } @@ -173,6 +177,24 @@ void streamfx::encoder::ffmpeg::handler::nvenc_h264_handler::migrate(obs_data_t* const AVCodec* codec, AVCodecContext* context) { nvenc::migrate(settings, version, codec, context); + + if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) { + // Profile + if (auto v = obs_data_get_int(settings, ST_KEY_PROFILE); v != -1) { + if (!obs_data_has_user_value(settings, ST_KEY_PROFILE)) + v = 3; + + std::map preset{ + {0, "baseline"}, {1, "baseline"}, {2, "main"}, {3, "high"}, {4, "high444p"}, + }; + if (auto k = preset.find(v); k != preset.end()) { + obs_data_set_string(settings, ST_KEY_PROFILE, k->second.data()); + } + } + + // Level + obs_data_set_string(settings, ST_KEY_LEVEL, "auto"); + } } bool nvenc_h264_handler::supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes) diff --git a/source/encoders/handlers/nvenc_hevc_handler.cpp b/source/encoders/handlers/nvenc_hevc_handler.cpp index 5c3cb7ba..7c8915cd 100644 --- a/source/encoders/handlers/nvenc_hevc_handler.cpp +++ b/source/encoders/handlers/nvenc_hevc_handler.cpp @@ -53,9 +53,9 @@ void nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec { nvenc::get_defaults(settings, codec, context); - obs_data_set_default_int(settings, ST_KEY_PROFILE, static_cast(profile::MAIN)); - obs_data_set_default_int(settings, ST_KEY_TIER, static_cast(profile::MAIN)); - obs_data_set_default_int(settings, ST_KEY_LEVEL, static_cast(level::UNKNOWN)); + obs_data_set_default_string(settings, ST_KEY_PROFILE, ""); + obs_data_set_default_string(settings, ST_KEY_TIER, ""); + obs_data_set_default_string(settings, ST_KEY_LEVEL, "auto"); } bool nvenc_hevc_handler::has_keyframe_support(ffmpeg_factory*) @@ -92,16 +92,14 @@ void nvenc_hevc_handler::update(obs_data_t* settings, const AVCodec* codec, AVCo nvenc::update(settings, codec, context); if (!context->internal) { - 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); + if (auto v = obs_data_get_string(settings, ST_KEY_PROFILE); v && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN); } - 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); + if (auto v = obs_data_get_string(settings, ST_KEY_TIER); v && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "tier", v, AV_OPT_SEARCH_CHILDREN); } - 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); + if (auto v = obs_data_get_string(settings, ST_KEY_LEVEL); v && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN); } } } @@ -143,26 +141,36 @@ 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_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); 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); + streamfx::ffmpeg::tools::avoption_list_add_entries( + context->priv_data, "profile", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", S_CODEC_HEVC_PROFILE, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } { 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_COMBO_FORMAT_STRING); 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); + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "tier", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", S_CODEC_HEVC_TIER, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } { 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), 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; - }); + OBS_COMBO_FORMAT_STRING); + + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "level", [&p](const AVOption* opt) { + if (opt->default_val.i64 == 0) { + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_AUTOMATIC), "auto"); + } else { + obs_property_list_add_string(p, opt->name, opt->name); + } + }); } } @@ -182,6 +190,40 @@ void streamfx::encoder::ffmpeg::handler::nvenc_hevc_handler::migrate(obs_data_t* const AVCodec* codec, AVCodecContext* context) { nvenc::migrate(settings, version, codec, context); + + if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) { + // Profile + if (auto v = obs_data_get_int(settings, ST_KEY_PROFILE); v != -1) { + if (!obs_data_has_user_value(settings, ST_KEY_PROFILE)) + v = 0; + + std::map preset{ + {0, "main"}, + {1, "main10"}, + {2, "rext"}, + }; + if (auto k = preset.find(v); k != preset.end()) { + obs_data_set_string(settings, ST_KEY_PROFILE, k->second.data()); + } + } + + // Tier + if (auto v = obs_data_get_int(settings, ST_KEY_TIER); v != -1) { + if (!obs_data_has_user_value(settings, ST_KEY_TIER)) + v = 0; + + std::map preset{ + {0, "main"}, + {1, "high"}, + }; + if (auto k = preset.find(v); k != preset.end()) { + obs_data_set_string(settings, ST_KEY_TIER, k->second.data()); + } + } + + // Level + obs_data_set_string(settings, ST_KEY_LEVEL, "auto"); + } } bool nvenc_hevc_handler::supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes) diff --git a/source/encoders/handlers/nvenc_shared.cpp b/source/encoders/handlers/nvenc_shared.cpp index 4426ac8f..2f6de97b 100644 --- a/source/encoders/handlers/nvenc_shared.cpp +++ b/source/encoders/handlers/nvenc_shared.cpp @@ -162,10 +162,12 @@ 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, -1); + obs_data_set_default_string(settings, ST_KEY_PRESET, "default"); + obs_data_set_default_string(settings, ST_I18N_TUNE, "hq"); - obs_data_set_default_int(settings, ST_KEY_RATECONTROL_MODE, -1); + obs_data_set_default_string(settings, ST_KEY_RATECONTROL_MODE, "cbr"); obs_data_set_default_int(settings, ST_KEY_RATECONTROL_TWOPASS, -1); + obs_data_set_default_int(settings, ST_KEY_RATECONTROL_MULTIPASS, -1); obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LOOKAHEAD, -1); obs_data_set_default_int(settings, ST_KEY_RATECONTROL_ADAPTIVEI, -1); obs_data_set_default_int(settings, ST_KEY_RATECONTROL_ADAPTIVEB, -1); @@ -200,21 +202,21 @@ 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); + auto value = obs_data_get_string(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; - if (value == 2) { + if (value == std::string_view("cbr")) { have_bitrate = true; - } else if (value == 1) { + } else if (value == std::string_view("vbr")) { have_bitrate = true; have_bitrate_range = true; have_quality = true; have_qp_limits = true; have_qp = true; - } else if (value == 0) { + } else if (value == std::string_view("constqp")) { have_qp = true; } else { have_bitrate = true; @@ -251,14 +253,22 @@ void nvenc::get_properties_pre(obs_properties_t* props, const AVCodec*, const AV { { 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); + OBS_COMBO_FORMAT_STRING); + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "preset", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", ST_I18N_PRESET, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } 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); + OBS_COMBO_FORMAT_STRING); + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "tune", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", ST_I18N_TUNE, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } } @@ -274,24 +284,31 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, c { 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_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_set_modified_callback(p, modified_ratecontrol); - auto filter = [](const AVOption* opt) { + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_DEFAULT), ""); + streamfx::ffmpeg::tools::avoption_list_add_entries(context->priv_data, "rc", [&p](const AVOption* opt) { + // Ignore options that are "deprecated" but not flagged as such. 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); + return; + + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", ST_I18N_RATECONTROL_MODE, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } 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); + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_DEFAULT), ""); + streamfx::ffmpeg::tools::avoption_list_add_entries( + context->priv_data, "multipass", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", ST_I18N_RATECONTROL_MULTIPASS, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } else { auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_RATECONTROL_TWOPASS, D_TRANSLATE(ST_I18N_RATECONTROL_TWOPASS)); @@ -419,10 +436,14 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, c { 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); - 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); + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, D_TRANSLATE(S_STATE_DEFAULT), ""); + streamfx::ffmpeg::tools::avoption_list_add_entries( + context->priv_data, "b_ref_mode", [&p](const AVOption* opt) { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "%s.%s\0", ST_I18N_OTHER_BFRAMEREFERENCEMODE, opt->name); + obs_property_list_add_string(p, D_TRANSLATE(buffer), opt->name); + }); } { @@ -492,27 +513,15 @@ void nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCo void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context) { - if (!context->internal) { - 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); - } - } + if (auto v = obs_data_get_string(settings, ST_KEY_PRESET); + !context->internal && (v != nullptr) && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "preset", v, 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); - } + auto v = obs_data_get_string(settings, ST_KEY_RATECONTROL_MODE); + if (!context->internal && (v != nullptr) && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "rc", v, AV_OPT_SEARCH_CHILDREN); } // Decode the name into useful flags. @@ -521,7 +530,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c bool have_quality = false; bool have_qp_limits = false; bool have_qp = false; - if (is_cbr(name)) { + if (v && is_cbr(v)) { have_bitrate = true; if (!context->internal) @@ -529,7 +538,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c // Support for OBS Studio obs_data_set_string(settings, "rate_control", "CBR"); - } else if (is_vbr(name)) { + } else if (v && is_vbr(v)) { have_bitrate = true; have_bitrate_range = true; have_quality = true; @@ -541,7 +550,7 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c // Support for OBS Studio obs_data_set_string(settings, "rate_control", "VBR"); - } else if (is_cqp(name)) { + } else if (v && is_cqp(v)) { have_qp = true; if (!context->internal) @@ -563,8 +572,9 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c if (!context->internal) { if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "multipass")) { // Multi-Pass - if (int tp = static_cast(obs_data_get_int(settings, ST_KEY_RATECONTROL_MULTIPASS)); tp > -1) { - av_opt_set_int(context->priv_data, "multipass", tp, AV_OPT_SEARCH_CHILDREN); + if (auto v = obs_data_get_string(settings, ST_KEY_RATECONTROL_MULTIPASS); + (v != nullptr) && (strlen(v) > 0)) { + av_opt_set(context->priv_data, "multipass", v, AV_OPT_SEARCH_CHILDREN); av_opt_set_int(context->priv_data, "2pass", 0, AV_OPT_SEARCH_CHILDREN); } } else { @@ -707,8 +717,9 @@ 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); } - 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_string(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE); + (v != nullptr) && (strlen(v) > 0)) { + av_opt_set(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) { @@ -810,29 +821,59 @@ void streamfx::encoder::ffmpeg::handler::nvenc::migrate(obs_data_t* settings, ui } if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) { + // Preset + if (auto v = obs_data_get_int(settings, ST_KEY_PRESET); v != -1) { + std::map preset{ + {0, "default"}, {1, "slow"}, {2, "medium"}, {3, "fast"}, {4, "hp"}, {5, "hq"}, + {6, "bd"}, {7, "ll"}, {8, "llhq"}, {9, "llhp"}, {10, "lossless"}, {11, "losslesshp"}, + }; + if (auto k = preset.find(v); k != preset.end()) { + obs_data_set_string(settings, ST_KEY_PRESET, k->second.data()); + } + } + + // Rate Control Mode if (auto v = obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE); v != -1) { + if (!obs_data_has_user_value(settings, ST_KEY_RATECONTROL_MODE)) + v = 4; + switch (v) { case 0: // CQP + obs_data_set_string(settings, ST_KEY_RATECONTROL_MODE, "constqp"); 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); + obs_data_set_string(settings, ST_KEY_RATECONTROL_MULTIPASS, "qres"); case 1: // VBR + obs_data_set_string(settings, ST_KEY_RATECONTROL_MODE, "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); + obs_data_set_string(settings, ST_KEY_RATECONTROL_MULTIPASS, "qres"); case 3: // CBR + obs_data_set_string(settings, ST_KEY_RATECONTROL_MODE, "cbr"); break; } } + + // Target Quality 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.); } + + // B-Frame Reference Modes + if (auto v = obs_data_get_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE); v != -1) { + std::map preset{ + {0, "default"}, + {1, "each"}, + {2, "middle"}, + }; + if (auto k = preset.find(v); k != preset.end()) { + obs_data_set_string(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE, k->second.data()); + } + } } #undef COPY_UNSET