mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-11 14:25:05 +00:00
03b16786e7
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.
880 lines
38 KiB
C++
880 lines
38 KiB
C++
// FFMPEG Video Encoder Integration for OBS Studio
|
|
// Copyright (c) 2019 Michael Fabian Dirks <info@xaymar.com>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
#include "nvenc_shared.hpp"
|
|
#include "encoders/encoder-ffmpeg.hpp"
|
|
#include "ffmpeg/tools.hpp"
|
|
|
|
extern "C" {
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4244)
|
|
#include <libavutil/opt.h>
|
|
#pragma warning(pop)
|
|
}
|
|
|
|
#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"
|
|
#define ST_KEY_RATECONTROL_ADAPTIVEI "RateControl.AdaptiveI"
|
|
#define ST_I18N_RATECONTROL_ADAPTIVEB ST_I18N_RATECONTROL ".AdaptiveB"
|
|
#define ST_KEY_RATECONTROL_ADAPTIVEB "RateControl.AdaptiveB"
|
|
#define ST_I18N_RATECONTROL_LIMITS ST_I18N_RATECONTROL ".Limits"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BUFFERSIZE ST_I18N_RATECONTROL_LIMITS ".BufferSize"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE "RateControl.Limits.BufferSize"
|
|
#define ST_I18N_RATECONTROL_LIMITS_QUALITY ST_I18N_RATECONTROL_LIMITS ".Quality"
|
|
#define ST_KEY_RATECONTROL_LIMITS_QUALITY "RateControl.Limits.Quality"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE ST_I18N_RATECONTROL_LIMITS ".Bitrate"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE_TARGET ST_I18N_RATECONTROL_LIMITS_BITRATE ".Target"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET "RateControl.Limits.Bitrate.Target"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE_MAXIMUM ST_I18N_RATECONTROL_LIMITS_BITRATE ".Maximum"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM "RateControl.Limits.Bitrate.Maximum"
|
|
#define ST_I18N_RATECONTROL_QP ST_I18N_RATECONTROL ".QP"
|
|
#define ST_I18N_RATECONTROL_QP_MINIMUM ST_I18N_RATECONTROL_QP ".Minimum"
|
|
#define ST_KEY_RATECONTROL_QP_MINIMUM "RateControl.Quality.Minimum"
|
|
#define ST_I18N_RATECONTROL_QP_MAXIMUM ST_I18N_RATECONTROL_QP ".Maximum"
|
|
#define ST_KEY_RATECONTROL_QP_MAXIMUM "RateControl.Quality.Maximum"
|
|
#define ST_I18N_RATECONTROL_QP_I ST_I18N_RATECONTROL_QP ".I"
|
|
#define ST_KEY_RATECONTROL_QP_I "RateControl.QP.I"
|
|
#define ST_I18N_RATECONTROL_QP_P ST_I18N_RATECONTROL_QP ".P"
|
|
#define ST_KEY_RATECONTROL_QP_P "RateControl.QP.P"
|
|
#define ST_I18N_RATECONTROL_QP_B ST_I18N_RATECONTROL_QP ".B"
|
|
#define ST_KEY_RATECONTROL_QP_B "RateControl.QP.B"
|
|
#define ST_I18N_AQ "Encoder.FFmpeg.NVENC.AQ"
|
|
#define ST_I18N_AQ_SPATIAL ST_I18N_AQ ".Spatial"
|
|
#define ST_KEY_AQ_SPATIAL "AQ.Spatial"
|
|
#define ST_I18N_AQ_TEMPORAL ST_I18N_AQ ".Temporal"
|
|
#define ST_KEY_AQ_TEMPORAL "AQ.Temporal"
|
|
#define ST_I18N_AQ_STRENGTH ST_I18N_AQ ".Strength"
|
|
#define ST_KEY_AQ_STRENGTH "AQ.Strength"
|
|
#define ST_I18N_OTHER "Encoder.FFmpeg.NVENC.Other"
|
|
#define ST_I18N_OTHER_BFRAMES ST_I18N_OTHER ".BFrames"
|
|
#define ST_KEY_OTHER_BFRAMES "Other.BFrames"
|
|
#define ST_I18N_OTHER_BFRAMEREFERENCEMODE ST_I18N_OTHER ".BFrameReferenceMode"
|
|
#define ST_KEY_OTHER_BFRAMEREFERENCEMODE "Other.BFrameReferenceMode"
|
|
#define ST_I18N_OTHER_ZEROLATENCY ST_I18N_OTHER ".ZeroLatency"
|
|
#define ST_KEY_OTHER_ZEROLATENCY "Other.ZeroLatency"
|
|
#define ST_I18N_OTHER_WEIGHTEDPREDICTION ST_I18N_OTHER ".WeightedPrediction"
|
|
#define ST_KEY_OTHER_WEIGHTEDPREDICTION "Other.WeightedPrediction"
|
|
#define ST_I18N_OTHER_NONREFERENCEPFRAMES ST_I18N_OTHER ".NonReferencePFrames"
|
|
#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;
|
|
|
|
inline bool is_cqp(std::string_view rc)
|
|
{
|
|
return std::string_view("constqp") == rc;
|
|
}
|
|
|
|
inline bool is_cbr(std::string_view rc)
|
|
{
|
|
return std::string_view("cbr") == rc;
|
|
}
|
|
|
|
inline bool is_vbr(std::string_view rc)
|
|
{
|
|
return std::string_view("vbr") == rc;
|
|
}
|
|
|
|
bool streamfx::encoder::ffmpeg::handler::nvenc::is_available()
|
|
{
|
|
#if defined(D_PLATFORM_WINDOWS)
|
|
#if defined(D_PLATFORM_64BIT)
|
|
std::filesystem::path lib_name = "nvEncodeAPI64.dll";
|
|
#else
|
|
std::filesystem::path lib_name = "nvEncodeAPI.dll";
|
|
#endif
|
|
#else
|
|
std::filesystem::path lib_name = "libnvidia-encode.so.1";
|
|
#endif
|
|
try {
|
|
streamfx::util::library::load(lib_name);
|
|
return true;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void nvenc::override_update(ffmpeg_instance* instance, obs_data_t*)
|
|
{
|
|
AVCodecContext* context = const_cast<AVCodecContext*>(instance->get_avcodeccontext());
|
|
|
|
int64_t rclookahead = 0;
|
|
int64_t surfaces = 0;
|
|
int64_t async_depth = 0;
|
|
|
|
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rclookahead);
|
|
av_opt_get_int(context, "surfaces", AV_OPT_SEARCH_CHILDREN, &surfaces);
|
|
av_opt_get_int(context, "async_depth", AV_OPT_SEARCH_CHILDREN, &async_depth);
|
|
|
|
// Calculate and set the number of surfaces to allocate (if not user overridden).
|
|
if (surfaces == 0) {
|
|
surfaces = std::max<int64_t>(4ll, (context->max_b_frames + 1ll) * 4ll);
|
|
if (rclookahead > 0) {
|
|
surfaces = std::max<int64_t>(1ll, std::max<int64_t>(surfaces, rclookahead + (context->max_b_frames + 5ll)));
|
|
} else if (context->max_b_frames > 0) {
|
|
surfaces = std::max<int64_t>(4ll, (context->max_b_frames + 1ll) * 4ll);
|
|
} else {
|
|
surfaces = 4;
|
|
}
|
|
|
|
av_opt_set_int(context, "surfaces", surfaces, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
|
|
// Set delay
|
|
context->delay = std::min<int>(std::max<int>(static_cast<int>(async_depth), 3), static_cast<int>(surfaces - 1));
|
|
}
|
|
|
|
void nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
|
{
|
|
obs_data_set_default_string(settings, ST_KEY_PRESET, "default");
|
|
obs_data_set_default_string(settings, ST_I18N_TUNE, "hq");
|
|
|
|
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);
|
|
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET, 6000);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM, 0);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE, 0);
|
|
obs_data_set_default_double(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY, 0);
|
|
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_QP_MINIMUM, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_QP_MAXIMUM, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_QP_I, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_QP_P, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_QP_B, -1);
|
|
|
|
obs_data_set_default_int(settings, ST_KEY_AQ_SPATIAL, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_AQ_STRENGTH, -1);
|
|
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, -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);
|
|
}
|
|
|
|
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_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 == std::string_view("cbr")) {
|
|
have_bitrate = true;
|
|
} 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 == std::string_view("constqp")) {
|
|
have_qp = true;
|
|
} else {
|
|
have_bitrate = true;
|
|
have_bitrate_range = true;
|
|
have_quality = true;
|
|
have_qp_limits = true;
|
|
have_qp = true;
|
|
}
|
|
|
|
obs_property_set_visible(obs_properties_get(props, ST_I18N_RATECONTROL_LIMITS), have_bitrate || have_quality);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE), have_bitrate);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_QUALITY), have_quality);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET), have_bitrate);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM), have_bitrate_range);
|
|
|
|
obs_property_set_visible(obs_properties_get(props, ST_I18N_RATECONTROL_QP), have_qp || have_qp_limits);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_QP_MINIMUM), have_qp_limits);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_QP_MAXIMUM), have_qp_limits);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_QP_I), have_qp);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_QP_P), have_qp);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_QP_B), have_qp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
|
|
{
|
|
bool spatial_aq = streamfx::util::is_tristate_enabled(obs_data_get_int(settings, ST_KEY_AQ_SPATIAL));
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_AQ_STRENGTH), spatial_aq);
|
|
return true;
|
|
}
|
|
|
|
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_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_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);
|
|
});
|
|
}
|
|
}
|
|
|
|
void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, const AVCodecContext* context)
|
|
{
|
|
{ // Rate Control
|
|
obs_properties_t* grp = props;
|
|
if (!streamfx::util::are_property_groups_broken()) {
|
|
grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_RATECONTROL, D_TRANSLATE(ST_I18N_RATECONTROL), OBS_GROUP_NORMAL,
|
|
grp);
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_list(grp, ST_KEY_RATECONTROL_MODE, D_TRANSLATE(ST_I18N_RATECONTROL_MODE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
|
obs_property_set_modified_callback(p, modified_ratecontrol);
|
|
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;
|
|
|
|
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_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));
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_LOOKAHEAD,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LOOKAHEAD), -1, 32, 1);
|
|
obs_property_int_set_suffix(p, " frames");
|
|
//obs_property_set_modified_callback(p, modified_lookahead);
|
|
}
|
|
|
|
{
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_RATECONTROL_ADAPTIVEI,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_ADAPTIVEI));
|
|
}
|
|
|
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_RATECONTROL_ADAPTIVEB,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_ADAPTIVEB));
|
|
}
|
|
}
|
|
|
|
{
|
|
obs_properties_t* grp = props;
|
|
if (!streamfx::util::are_property_groups_broken()) {
|
|
grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_RATECONTROL_LIMITS, D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS),
|
|
OBS_GROUP_NORMAL, grp);
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_float_slider(grp, ST_KEY_RATECONTROL_LIMITS_QUALITY,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUALITY), 0, 51, 0.01);
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int(grp, ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BITRATE_TARGET), -1,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " kbit/s");
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int(grp, ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BITRATE_MAXIMUM), -1,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " kbit/s");
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int(grp, ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BUFFERSIZE), 0,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " kbit");
|
|
}
|
|
}
|
|
|
|
{
|
|
obs_properties_t* grp = props;
|
|
if (!streamfx::util::are_property_groups_broken()) {
|
|
grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_RATECONTROL_QP, D_TRANSLATE(ST_I18N_RATECONTROL_QP),
|
|
OBS_GROUP_NORMAL, grp);
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_QP_MINIMUM,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_QP_MINIMUM), -1, 51, 1);
|
|
}
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_QP_MAXIMUM,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_QP_MAXIMUM), -1, 51, 1);
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_QP_I, D_TRANSLATE(ST_I18N_RATECONTROL_QP_I),
|
|
-1, 51, 1);
|
|
}
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_QP_P, D_TRANSLATE(ST_I18N_RATECONTROL_QP_P),
|
|
-1, 51, 1);
|
|
}
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_RATECONTROL_QP_B, D_TRANSLATE(ST_I18N_RATECONTROL_QP_B),
|
|
-1, 51, 1);
|
|
}
|
|
}
|
|
|
|
{
|
|
obs_properties_t* grp = props;
|
|
if (!streamfx::util::are_property_groups_broken()) {
|
|
grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_AQ, D_TRANSLATE(ST_I18N_AQ), OBS_GROUP_NORMAL, grp);
|
|
}
|
|
|
|
{
|
|
auto p =
|
|
streamfx::util::obs_properties_add_tristate(grp, ST_KEY_AQ_SPATIAL, D_TRANSLATE(ST_I18N_AQ_SPATIAL));
|
|
obs_property_set_modified_callback(p, modified_aq);
|
|
}
|
|
{
|
|
auto p =
|
|
obs_properties_add_int_slider(grp, ST_KEY_AQ_STRENGTH, D_TRANSLATE(ST_I18N_AQ_STRENGTH), -1, 15, 1);
|
|
}
|
|
{
|
|
auto p =
|
|
streamfx::util::obs_properties_add_tristate(grp, ST_KEY_AQ_TEMPORAL, D_TRANSLATE(ST_I18N_AQ_TEMPORAL));
|
|
}
|
|
}
|
|
|
|
{
|
|
obs_properties_t* grp = props;
|
|
if (!streamfx::util::are_property_groups_broken()) {
|
|
grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_OTHER, D_TRANSLATE(ST_I18N_OTHER), OBS_GROUP_NORMAL, grp);
|
|
}
|
|
|
|
{
|
|
auto p =
|
|
obs_properties_add_int_slider(grp, ST_KEY_OTHER_BFRAMES, D_TRANSLATE(ST_I18N_OTHER_BFRAMES), -1, 4, 1);
|
|
obs_property_int_set_suffix(p, " frames");
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_list(grp, ST_KEY_OTHER_BFRAMEREFERENCEMODE,
|
|
D_TRANSLATE(ST_I18N_OTHER_BFRAMEREFERENCEMODE), 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, "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);
|
|
});
|
|
}
|
|
|
|
{
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_OTHER_ZEROLATENCY,
|
|
D_TRANSLATE(ST_I18N_OTHER_ZEROLATENCY));
|
|
}
|
|
|
|
{
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_OTHER_WEIGHTEDPREDICTION,
|
|
D_TRANSLATE(ST_I18N_OTHER_WEIGHTEDPREDICTION));
|
|
}
|
|
|
|
{
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_OTHER_NONREFERENCEPFRAMES,
|
|
D_TRANSLATE(ST_I18N_OTHER_NONREFERENCEPFRAMES));
|
|
}
|
|
|
|
{
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_OTHER_REFERENCEFRAMES,
|
|
D_TRANSLATE(ST_I18N_OTHER_REFERENCEFRAMES), -1,
|
|
(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);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_I18N_RATECONTROL_LIMITS), true);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE), true);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET), true);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM), true);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_I18N_RATECONTROL_LIMITS_QUALITY), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_I18N_RATECONTROL_QP), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_QP_MINIMUM), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_QP_MAXIMUM), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_QP_I), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_QP_P), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_RATECONTROL_QP_B), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_I18N_AQ), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_AQ_SPATIAL), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_AQ_STRENGTH), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_AQ_TEMPORAL), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_I18N_OTHER), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_BFRAMES), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_BFRAMEREFERENCEMODE), false);
|
|
obs_property_set_enabled(obs_properties_get(props, ST_KEY_OTHER_ZEROLATENCY), false);
|
|
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 (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 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.
|
|
bool have_bitrate = false;
|
|
bool have_bitrate_range = false;
|
|
bool have_quality = false;
|
|
bool have_qp_limits = false;
|
|
bool have_qp = false;
|
|
if (v && is_cbr(v)) {
|
|
have_bitrate = 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");
|
|
} else if (v && is_vbr(v)) {
|
|
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 (v && is_cqp(v)) {
|
|
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) {
|
|
if (streamfx::ffmpeg::tools::avoption_exists(context->priv_data, "multipass")) {
|
|
// Multi-Pass
|
|
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 {
|
|
// 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
|
|
int la = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LOOKAHEAD));
|
|
if (!streamfx::util::is_tristate_default(la)) {
|
|
av_opt_set_int(context->priv_data, "rc-lookahead", la, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
|
|
// Adaptive I-Frames
|
|
if (int64_t adapt_i = obs_data_get_int(settings, ST_KEY_RATECONTROL_ADAPTIVEI);
|
|
!streamfx::util::is_tristate_default(adapt_i) && (la != 0)) {
|
|
// no-scenecut is inverted compared to our UI.
|
|
av_opt_set_int(context->priv_data, "no-scenecut", 1 - adapt_i, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
|
|
// Adaptive B-Frames
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (have_bitrate) {
|
|
int64_t v = obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET);
|
|
|
|
// Allow OBS to specify a maximum allowed bitrate.
|
|
if (obs_data_get_int(settings, "bitrate") != obs_data_get_default_int(settings, "bitrate")) {
|
|
// obs_data_has_user_value(X, Y) is also true if obs_data_set_Z(X, Y, obs_data_get_Z(X, Y))
|
|
v = std::clamp<int64_t>(v, -1, obs_data_get_int(settings, "bitrate"));
|
|
}
|
|
|
|
if (v > -1) {
|
|
context->bit_rate = static_cast<int>(v * 1000);
|
|
}
|
|
} else {
|
|
context->bit_rate = 0;
|
|
}
|
|
if (have_bitrate_range) {
|
|
if (int64_t max = obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_MAXIMUM); max > -1) {
|
|
context->rc_max_rate = static_cast<int>(max * 1000);
|
|
} else {
|
|
context->rc_max_rate = context->bit_rate;
|
|
}
|
|
context->rc_min_rate = context->bit_rate;
|
|
} else {
|
|
context->rc_min_rate = context->bit_rate;
|
|
context->rc_max_rate = context->bit_rate;
|
|
}
|
|
{ // Support for OBS Studio
|
|
obs_data_set_int(settings, "bitrate", context->rc_max_rate);
|
|
}
|
|
|
|
// Buffer Size
|
|
if (have_bitrate || have_bitrate_range) {
|
|
if (int64_t v = obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE); v > -1)
|
|
context->rc_buffer_size = static_cast<int>(v * 1000);
|
|
} else {
|
|
context->rc_buffer_size = 0;
|
|
}
|
|
|
|
if (!context->internal) {
|
|
// Quality Limits
|
|
if (have_qp_limits) {
|
|
if (int qmin = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_QP_MINIMUM)); qmin > -1)
|
|
context->qmin = qmin;
|
|
if (int qmax = static_cast<int>(obs_data_get_int(settings, ST_KEY_RATECONTROL_QP_MAXIMUM)); qmax > -1)
|
|
context->qmax = qmax;
|
|
} else {
|
|
context->qmin = -1;
|
|
context->qmax = -1;
|
|
}
|
|
|
|
// Quality Target
|
|
if (have_quality) {
|
|
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 {
|
|
av_opt_set_double(context->priv_data, "cq", 0, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
|
|
// QP Settings
|
|
if (have_qp) {
|
|
if (int64_t qp = obs_data_get_int(settings, ST_KEY_RATECONTROL_QP_I); qp > -1)
|
|
av_opt_set_int(context->priv_data, "init_qpI", static_cast<int>(qp), AV_OPT_SEARCH_CHILDREN);
|
|
if (int64_t qp = obs_data_get_int(settings, ST_KEY_RATECONTROL_QP_P); qp > -1)
|
|
av_opt_set_int(context->priv_data, "init_qpP", static_cast<int>(qp), AV_OPT_SEARCH_CHILDREN);
|
|
if (int64_t qp = obs_data_get_int(settings, ST_KEY_RATECONTROL_QP_B); qp > -1)
|
|
av_opt_set_int(context->priv_data, "init_qpB", static_cast<int>(qp), AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!context->internal) { // AQ
|
|
int64_t saq = obs_data_get_int(settings, ST_KEY_AQ_SPATIAL);
|
|
int64_t taq = obs_data_get_int(settings, ST_KEY_AQ_TEMPORAL);
|
|
|
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
|
if (!streamfx::util::is_tristate_default(saq))
|
|
av_opt_set_int(context->priv_data, "spatial-aq", saq, AV_OPT_SEARCH_CHILDREN);
|
|
if (!streamfx::util::is_tristate_default(taq))
|
|
av_opt_set_int(context->priv_data, "temporal-aq", taq, AV_OPT_SEARCH_CHILDREN);
|
|
} else {
|
|
if (!streamfx::util::is_tristate_default(saq))
|
|
av_opt_set_int(context->priv_data, "spatial_aq", saq, AV_OPT_SEARCH_CHILDREN);
|
|
if (!streamfx::util::is_tristate_default(taq))
|
|
av_opt_set_int(context->priv_data, "temporal_aq", taq, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
if (streamfx::util::is_tristate_enabled(saq))
|
|
if (int64_t aqs = obs_data_get_int(settings, ST_KEY_AQ_STRENGTH); aqs > -1)
|
|
av_opt_set_int(context->priv_data, "aq-strength", static_cast<int>(aqs), AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
|
|
if (!context->internal) { // Other
|
|
if (int64_t bf = obs_data_get_int(settings, ST_KEY_OTHER_BFRAMES); bf > -1)
|
|
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);
|
|
if (int64_t nrp = obs_data_get_int(settings, ST_KEY_OTHER_NONREFERENCEPFRAMES);
|
|
!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)
|
|
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)) {
|
|
DLOG_WARNING("[%s] Weighted Prediction disabled because of B-Frames being used.", codec->name);
|
|
av_opt_set_int(context->priv_data, "weighted_pred", 0, AV_OPT_SEARCH_CHILDREN);
|
|
} else if (!streamfx::util::is_tristate_default(wp)) {
|
|
av_opt_set_int(context->priv_data, "weighted_pred", wp, 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) {
|
|
av_opt_set_int(context->priv_data, "ldkfs", v, AV_OPT_SEARCH_CHILDREN);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* context)
|
|
{
|
|
using namespace ::streamfx::ffmpeg;
|
|
|
|
DLOG_INFO("[%s] NVIDIA NVENC:", codec->name);
|
|
tools::print_av_option_string2(context, "preset", " Preset",
|
|
[](int64_t v, std::string_view o) { return std::string(o); });
|
|
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)
|
|
tools::print_av_option_bool(context, "b_adapt", " Adaptive B-Frames");
|
|
|
|
DLOG_INFO("[%s] Bitrate:", codec->name);
|
|
tools::print_av_option_int(context, "b", " Target", "bits/sec");
|
|
tools::print_av_option_int(context, "minrate", " Minimum", "bits/sec");
|
|
tools::print_av_option_int(context, "maxrate", " Maximum", "bits/sec");
|
|
tools::print_av_option_int(context, "bufsize", " Buffer", "bits");
|
|
DLOG_INFO("[%s] Quality:", codec->name);
|
|
tools::print_av_option_int(context, "cq", " Target", "");
|
|
tools::print_av_option_int(context, "qmin", " Minimum", "");
|
|
tools::print_av_option_int(context, "qmax", " Maximum", "");
|
|
DLOG_INFO("[%s] Quantization Parameters:", codec->name);
|
|
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",
|
|
[](int64_t v, std::string_view o) { return std::string(o); });
|
|
|
|
DLOG_INFO("[%s] Adaptive Quantization:", codec->name);
|
|
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
|
tools::print_av_option_bool(context, "spatial-aq", " Spatial AQ");
|
|
tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
|
tools::print_av_option_bool(context, "temporal-aq", " Temporal AQ");
|
|
} else {
|
|
tools::print_av_option_bool(context, "spatial_aq", " Spatial AQ");
|
|
tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
|
tools::print_av_option_bool(context, "temporal_aq", " Temporal AQ");
|
|
}
|
|
|
|
DLOG_INFO("[%s] Other:", codec->name);
|
|
tools::print_av_option_bool(context, "zerolatency", " Zero Latency");
|
|
tools::print_av_option_bool(context, "weighted_pred", " Weighted Prediction");
|
|
tools::print_av_option_bool(context, "nonref_p", " Non-reference P-Frames");
|
|
tools::print_av_option_int(context, "refs", " Reference Frames", "Frames");
|
|
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");
|
|
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,
|
|
AVCodecContext* context)
|
|
{
|
|
// Only test for A.B.C in A.B.C.D
|
|
version = version & STREAMFX_MASK_UPDATE;
|
|
|
|
#define COPY_UNSET(TYPE, FROM, TO) \
|
|
if (obs_data_has_user_value(settings, FROM)) { \
|
|
obs_data_set_##TYPE(settings, TO, obs_data_get_##TYPE(settings, FROM)); \
|
|
obs_data_unset_user_value(settings, FROM); \
|
|
}
|
|
|
|
if (version <= STREAMFX_MAKE_VERSION(0, 8, 0, 0)) {
|
|
COPY_UNSET(int, "RateControl.Bitrate.Target", ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET);
|
|
COPY_UNSET(int, "RateControl.Bitrate.Maximum", ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET);
|
|
COPY_UNSET(int, "RateControl.BufferSize", ST_KEY_RATECONTROL_LIMITS_BUFFERSIZE);
|
|
COPY_UNSET(int, "RateControl.Quality.Minimum", ST_KEY_RATECONTROL_QP_MINIMUM);
|
|
COPY_UNSET(int, "RateControl.Quality.Maximum", ST_KEY_RATECONTROL_QP_MAXIMUM);
|
|
COPY_UNSET(double, "RateControl.Quality.Target", ST_KEY_RATECONTROL_LIMITS_QUALITY);
|
|
}
|
|
|
|
if (version < STREAMFX_MAKE_VERSION(0, 11, 0, 0)) {
|
|
obs_data_unset_user_value(settings, "Other.AccessUnitDelimiter");
|
|
obs_data_unset_user_value(settings, "Other.DecodedPictureBufferSize");
|
|
}
|
|
|
|
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<int64_t, std::string> 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_TWOPASS, 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_TWOPASS, 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<int64_t, std::string> 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
|
|
}
|