code: Migrate encoder::ffmpeg::nvenc to new loader

This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2023-05-16 03:52:25 +02:00 committed by Xaymar
parent 51282b7b85
commit 1c76169821
8 changed files with 584 additions and 649 deletions

View File

@ -1171,12 +1171,8 @@ if(T_CHECK)
is_feature_enabled(ENCODER_FFMPEG_NVENC T_CHECK)
if(T_CHECK)
list(APPEND PROJECT_PRIVATE_SOURCE
"source/encoders/ffmpeg/nvenc_shared.hpp"
"source/encoders/ffmpeg/nvenc_shared.cpp"
"source/encoders/ffmpeg/nvenc_h264_handler.hpp"
"source/encoders/ffmpeg/nvenc_h264_handler.cpp"
"source/encoders/ffmpeg/nvenc_hevc_handler.hpp"
"source/encoders/ffmpeg/nvenc_hevc_handler.cpp"
"source/encoders/ffmpeg/nvenc.hpp"
"source/encoders/ffmpeg/nvenc.cpp"
)
list(APPEND PROJECT_DEFINITIONS
ENABLE_ENCODER_FFMPEG_NVENC

View File

@ -1,17 +1,22 @@
// AUTOGENERATED COPYRIGHT HEADER START
// AUTOGENERATED COPYRIGHT HEADER END
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
// AUTOGENERATED COPYRIGHT HEADER END
#include "nvenc_shared.hpp"
#include "nvenc.hpp"
#include "common.hpp"
#include "strings.hpp"
#include "encoders/codecs/h264.hpp"
#include "encoders/codecs/hevc.hpp"
#include "encoders/encoder-ffmpeg.hpp"
#include "ffmpeg/tools.hpp"
#include "plugin.hpp"
extern "C" {
#include "warning-disable.hpp"
extern "C" {
#include <libavutil/opt.h>
#include "warning-enable.hpp"
}
#include "warning-enable.hpp"
#define ST_I18N_PRESET "Encoder.FFmpeg.NVENC.Preset"
#define ST_I18N_PRESET_(x) ST_I18N_PRESET "." D_VSTR(x)
@ -77,7 +82,15 @@ extern "C" {
#define ST_I18N_OTHER_LOWDELAYKEYFRAMESCALE ST_I18N_OTHER ".LowDelayKeyFrameScale"
#define ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE "Other.LowDelayKeyFrameScale"
using namespace streamfx::encoder::ffmpeg::handler;
#define ST_KEY_H264_PROFILE "H264.Profile"
#define ST_KEY_H264_LEVEL "H264.Level"
#define ST_KEY_H265_PROFILE "H265.Profile"
#define ST_KEY_H265_TIER "H265.Tier"
#define ST_KEY_H264_LEVEL "H265.Level"
using namespace streamfx::encoder::ffmpeg;
using namespace streamfx::encoder::codec;
inline bool is_cqp(std::string_view rc)
{
@ -94,7 +107,7 @@ inline bool is_vbr(std::string_view rc)
return std::string_view("vbr") == rc;
}
bool streamfx::encoder::ffmpeg::handler::nvenc::is_available()
bool nvenc::is_available()
{
#if defined(D_PLATFORM_WINDOWS)
#if defined(D_PLATFORM_64BIT)
@ -113,37 +126,7 @@ bool streamfx::encoder::ffmpeg::handler::nvenc::is_available()
}
}
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*)
void nvenc::defaults(ffmpeg_factory* factory, obs_data_t* settings)
{
obs_data_set_default_string(settings, ST_KEY_PRESET, "default");
obs_data_set_default_string(settings, ST_I18N_TUNE, "hq");
@ -232,8 +215,10 @@ 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*, const AVCodecContext* context)
void nvenc::properties_before(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props, AVCodecContext* context)
{
auto codec = factory->get_avcodec();
{
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) {
@ -253,8 +238,10 @@ void nvenc::get_properties_pre(obs_properties_t* props, const AVCodec*, const AV
}
}
void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, const AVCodecContext* context)
void nvenc::properies_after(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props, AVCodecContext* context)
{
auto codec = factory->get_avcodec();
{ // Rate Control
obs_properties_t* grp = props;
if (!streamfx::util::are_property_groups_broken()) {
@ -420,7 +407,7 @@ void nvenc::get_properties_post(obs_properties_t* props, const AVCodec* codec, c
}
}
void nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCodecContext*)
void nvenc::properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
obs_property_set_enabled(obs_properties_get(props, ST_KEY_PRESET), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_TUNE), false);
@ -456,8 +443,94 @@ void nvenc::get_runtime_properties(obs_properties_t* props, const AVCodec*, AVCo
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)
void nvenc::migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version)
{
// 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
}
void nvenc::update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
if (const char* v = obs_data_get_string(settings, ST_KEY_PRESET); !context->internal && (v != nullptr) && (v[0] != '\0')) {
av_opt_set(context->priv_data, "preset", v, AV_OPT_SEARCH_CHILDREN);
}
@ -667,8 +740,41 @@ void nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* c
}
}
void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* context)
void nvenc::override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
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::log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
using namespace ::streamfx::ffmpeg;
DLOG_INFO("[%s] NVIDIA NVENC:", codec->name);
@ -729,85 +835,364 @@ void nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* conte
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)
// H264/AVC Handler
//-------------------
nvenc_h264::nvenc_h264() : handler("h264_nvenc"){};
nvenc_h264::~nvenc_h264(){};
bool nvenc_h264::has_keyframes(ffmpeg_factory*)
{
// 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); \
return true;
}
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);
bool nvenc_h264::has_threading(ffmpeg_factory*)
{
return false;
}
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");
bool nvenc_h264::is_hardware(ffmpeg_factory*)
{
return true;
}
bool nvenc_h264::is_reconfigurable(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes)
{
threads = false;
gpu = false;
keyframes = false;
return true;
}
void nvenc_h264::adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec)
{
name = "NVIDIA NVENC H.264/AVC (via FFmpeg)";
if (!nvenc::is_available()) // If we don't have NVENC, don't even allow listing it.
factory->get_info()->caps |= OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL;
}
void nvenc_h264::defaults(ffmpeg_factory* factory, obs_data_t* settings)
{
nvenc::defaults(factory, settings);
obs_data_set_default_string(settings, ST_KEY_H264_PROFILE, "");
obs_data_set_default_string(settings, ST_KEY_H264_LEVEL, "auto");
}
void nvenc_h264::properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
if (!instance) {
this->properties_encoder(factory, instance, props);
} else {
this->properties_runtime(factory, instance, props);
}
}
void nvenc_h264::migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version)
{
nvenc::migrate(factory, instance, settings, version);
if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) {
// Preset
if (auto v = obs_data_get_int(settings, ST_KEY_PRESET); v != -1) {
// Profile
if (auto v = obs_data_get_int(settings, ST_KEY_H264_PROFILE); v != -1) {
if (!obs_data_has_user_value(settings, ST_KEY_H264_PROFILE))
v = 3;
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"},
{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_PRESET, k->second.data());
obs_data_set_string(settings, ST_KEY_H264_PROFILE, 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;
// Level
obs_data_set_string(settings, ST_KEY_H264_LEVEL, "auto");
}
}
// 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.);
void nvenc_h264::update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
nvenc::update(factory, instance, settings);
if (!context->internal) {
if (const char* v = obs_data_get_string(settings, ST_KEY_H264_PROFILE); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_H264_LEVEL); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN);
}
}
}
// B-Frame Reference Modes
if (auto v = obs_data_get_int(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE); v != -1) {
void nvenc_h264::override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
nvenc::override_update(factory, instance, settings);
}
void nvenc_h264::log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
nvenc::log(factory, instance, settings);
DLOG_INFO("[%s] H.264/AVC:", codec->name);
::streamfx::ffmpeg::tools::print_av_option_string2(context, context->priv_data, "profile", " Profile", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, context->priv_data, "level", " Level", [](int64_t v, std::string_view o) { return std::string(o); });
}
void nvenc_h264::properties_encoder(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
auto codec = factory->get_avcodec();
AVCodecContext* context = avcodec_alloc_context3(codec);
if (!context->priv_data) {
avcodec_free_context(&context);
return;
}
nvenc::properties_before(factory, instance, props, context);
{
obs_properties_t* grp = props;
if (!streamfx::util::are_property_groups_broken()) {
grp = obs_properties_create();
obs_properties_add_group(props, S_CODEC_H264, D_TRANSLATE(S_CODEC_H264), OBS_GROUP_NORMAL, grp);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_H264_PROFILE, D_TRANSLATE(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", 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_H264_LEVEL, D_TRANSLATE(S_CODEC_H264_LEVEL), OBS_COMBO_TYPE_LIST, 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);
}
});
}
}
nvenc::properies_after(factory, instance, props, context);
if (context) {
avcodec_free_context(&context);
}
}
void nvenc_h264::properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
nvenc::properties_runtime(factory, instance, props);
}
static auto inst_h264 = nvenc_h264();
// H265/HEVC Handler
//-------------------
nvenc_hevc::nvenc_hevc() : handler("hevc_nvenc"){};
nvenc_hevc::~nvenc_hevc(){};
bool nvenc_hevc::has_keyframes(ffmpeg_factory*)
{
return true;
}
bool nvenc_hevc::has_threading(ffmpeg_factory* instance)
{
return false;
}
bool nvenc_hevc::is_hardware(ffmpeg_factory* instance)
{
return true;
}
bool nvenc_hevc::is_reconfigurable(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes)
{
threads = false;
gpu = false;
keyframes = false;
return true;
}
void nvenc_hevc::adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec)
{
name = "NVIDIA NVENC H.265/HEVC (via FFmpeg)";
if (!nvenc::is_available())
factory->get_info()->caps |= OBS_ENCODER_CAP_DEPRECATED;
}
void nvenc_hevc::defaults(ffmpeg_factory* factory, obs_data_t* settings)
{
nvenc::defaults(factory, settings);
obs_data_set_default_string(settings, ST_KEY_H265_PROFILE, "");
obs_data_set_default_string(settings, ST_KEY_H265_TIER, "");
obs_data_set_default_string(settings, ST_KEY_H264_LEVEL, "auto");
}
void nvenc_hevc::properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
if (!instance) {
this->properties_encoder(factory, instance, props);
} else {
this->properties_runtime(factory, instance, props);
}
}
void nvenc_hevc::migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version)
{
nvenc::migrate(factory, instance, settings, version);
if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) {
// Profile
if (auto v = obs_data_get_int(settings, ST_KEY_H265_PROFILE); v != -1) {
if (!obs_data_has_user_value(settings, ST_KEY_H265_PROFILE))
v = 0;
std::map<int64_t, std::string> preset{
{0, "default"},
{1, "each"},
{2, "middle"},
{0, "main"},
{1, "main10"},
{2, "rext"},
};
if (auto k = preset.find(v); k != preset.end()) {
obs_data_set_string(settings, ST_KEY_OTHER_BFRAMEREFERENCEMODE, k->second.data());
obs_data_set_string(settings, ST_KEY_H265_PROFILE, k->second.data());
}
}
// Tier
if (auto v = obs_data_get_int(settings, ST_KEY_H265_TIER); v != -1) {
if (!obs_data_has_user_value(settings, ST_KEY_H265_TIER))
v = 0;
std::map<int64_t, std::string> preset{
{0, "main"},
{1, "high"},
};
if (auto k = preset.find(v); k != preset.end()) {
obs_data_set_string(settings, ST_KEY_H265_TIER, k->second.data());
}
}
// Level
obs_data_set_string(settings, ST_KEY_H264_LEVEL, "auto");
}
}
void nvenc_hevc::update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
nvenc::update(factory, instance, settings);
if (!context->internal) {
if (const char* v = obs_data_get_string(settings, ST_KEY_H265_PROFILE); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_H265_TIER); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "tier", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_H264_LEVEL); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN);
}
}
}
#undef COPY_UNSET
void nvenc_hevc::override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
nvenc::override_update(factory, instance, settings);
}
void nvenc_hevc::log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings)
{
auto codec = factory->get_avcodec();
auto context = instance->get_avcodeccontext();
nvenc::log(factory, instance, settings);
DLOG_INFO("[%s] H.265/HEVC:", codec->name);
::streamfx::ffmpeg::tools::print_av_option_string2(context, "profile", " Profile", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, "level", " Level", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, "tier", " Tier", [](int64_t v, std::string_view o) { return std::string(o); });
}
void nvenc_hevc::properties_encoder(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
auto codec = factory->get_avcodec();
AVCodecContext* context = avcodec_alloc_context3(codec);
if (!context->priv_data) {
avcodec_free_context(&context);
return;
}
nvenc::properties_before(factory, instance, props, context);
{
obs_properties_t* grp = props;
if (!streamfx::util::are_property_groups_broken()) {
grp = obs_properties_create();
obs_properties_add_group(props, S_CODEC_HEVC, D_TRANSLATE(S_CODEC_HEVC), OBS_GROUP_NORMAL, grp);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_H265_PROFILE, D_TRANSLATE(S_CODEC_HEVC_PROFILE), 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](const AVOption* opt) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s.%s", 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_H265_TIER, D_TRANSLATE(S_CODEC_HEVC_TIER), 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, "tier", [&p](const AVOption* opt) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s.%s", 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_H264_LEVEL, D_TRANSLATE(S_CODEC_HEVC_LEVEL), OBS_COMBO_TYPE_LIST, 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);
}
});
}
}
nvenc::properies_after(factory, instance, props, context);
if (context) {
avcodec_free_context(&context);
}
}
void nvenc_hevc::properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props)
{
nvenc::properties_runtime(factory, instance, props);
}
static auto inst_hevc = nvenc_hevc();

View File

@ -0,0 +1,95 @@
// AUTOGENERATED COPYRIGHT HEADER START
// AUTOGENERATED COPYRIGHT HEADER END
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
#pragma once
#include "encoders/encoder-ffmpeg.hpp"
#include "encoders/ffmpeg/handler.hpp"
#include "warning-disable.hpp"
#include <cinttypes>
#include <string>
extern "C" {
#include <libavcodec/avcodec.h>
}
#include "warning-enable.hpp"
/* NVENC has multiple compression modes:
- CBR: Constant Bitrate (rc=cbr)
- VBR: Variable Bitrate (rc=vbr)
- CQP: Constant QP (rc=cqp)
- CQ: Constant Quality (rc=vbr b=0 maxrate=0 qmin=0 qmax=51 cq=qp), this is basically CRF in X264.
*/
namespace streamfx::encoder::ffmpeg {
namespace nvenc {
bool is_available();
void defaults(ffmpeg_factory* factory, obs_data_t* settings);
void properties_before(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props, AVCodecContext* context);
void properies_after(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props, AVCodecContext* context);
void properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
void migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version);
void update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
void override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
void log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
} // namespace nvenc
class nvenc_h264 : public handler {
public:
nvenc_h264();
virtual ~nvenc_h264();
bool has_keyframes(ffmpeg_factory* factory) override;
bool has_threading(ffmpeg_factory* factory) override;
bool is_hardware(ffmpeg_factory* factory) override;
bool is_reconfigurable(ffmpeg_factory* factory, bool& threads, bool& gpu, bool& keyframes) override;
void adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec) override;
std::string help(ffmpeg_factory* factory) override
{
return "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-FFmpeg-NVENC";
};
void defaults(ffmpeg_factory* factory, obs_data_t* settings) override;
void properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props) override;
void migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version) override;
void update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
void override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
void log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
private:
void properties_encoder(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
void properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
};
class nvenc_hevc : public handler {
public:
nvenc_hevc();
virtual ~nvenc_hevc();
bool has_keyframes(ffmpeg_factory* factory) override;
bool has_threading(ffmpeg_factory* factory) override;
bool is_hardware(ffmpeg_factory* factory) override;
bool is_reconfigurable(ffmpeg_factory* factory, bool& threads, bool& gpu, bool& keyframes) override;
void adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec) override;
std::string help(ffmpeg_factory* factory) override
{
return "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-FFmpeg-NVENC";
};
void defaults(ffmpeg_factory* factory, obs_data_t* settings) override;
void properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props) override;
void migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version) override;
void update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
void override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
void log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) override;
private:
void properties_encoder(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
void properties_runtime(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
};
} // namespace streamfx::encoder::ffmpeg

View File

@ -1,179 +0,0 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
// AUTOGENERATED COPYRIGHT HEADER END
#include "nvenc_h264_handler.hpp"
#include "common.hpp"
#include "strings.hpp"
#include "../codecs/h264.hpp"
#include "../encoder-ffmpeg.hpp"
#include "ffmpeg/tools.hpp"
#include "nvenc_shared.hpp"
#include "plugin.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavutil/opt.h>
#include "warning-enable.hpp"
}
#define ST_KEY_PROFILE "H264.Profile"
#define ST_KEY_LEVEL "H264.Level"
using namespace streamfx::encoder::ffmpeg::handler;
using namespace streamfx::encoder::codec::h264;
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)";
if (!nvenc::is_available())
fac->get_info()->caps |= OBS_ENCODER_CAP_DEPRECATED;
}
void nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool)
{
nvenc::get_defaults(settings, codec, context);
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*)
{
return true;
}
bool nvenc_h264_handler::is_hardware_encoder(ffmpeg_factory*)
{
return true;
}
bool nvenc_h264_handler::has_threading_support(ffmpeg_factory*)
{
return false;
}
bool nvenc_h264_handler::has_pixel_format_support(ffmpeg_factory*)
{
return true;
}
void nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context, bool)
{
if (!context) {
this->get_encoder_properties(props, codec);
} else {
this->get_runtime_properties(props, codec, context);
}
}
void nvenc_h264_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{
nvenc::update(settings, codec, context);
if (!context->internal) {
if (const char* v = obs_data_get_string(settings, ST_KEY_PROFILE); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_LEVEL); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN);
}
}
}
void nvenc_h264_handler::override_update(ffmpeg_instance* instance, obs_data_t* settings)
{
nvenc::override_update(instance, settings);
}
void nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{
nvenc::log_options(settings, codec, context);
DLOG_INFO("[%s] H.264/AVC:", codec->name);
::streamfx::ffmpeg::tools::print_av_option_string2(context, context->priv_data, "profile", " Profile", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, context->priv_data, "level", " Level", [](int64_t v, std::string_view o) { return std::string(o); });
}
void nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* 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;
if (!streamfx::util::are_property_groups_broken()) {
grp = obs_properties_create();
obs_properties_add_group(props, S_CODEC_H264, D_TRANSLATE(S_CODEC_H264), OBS_GROUP_NORMAL, grp);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_PROFILE, D_TRANSLATE(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", 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_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);
}
});
}
}
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)
{
nvenc::get_runtime_properties(props, codec, context);
}
void streamfx::encoder::ffmpeg::handler::nvenc_h264_handler::migrate(obs_data_t* settings, uint64_t version, 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<int64_t, std::string> 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)
{
threads = false;
gpu = false;
keyframes = false;
return true;
}

View File

@ -1,56 +0,0 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#pragma once
#include "handler.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavcodec/avcodec.h>
#include "warning-enable.hpp"
}
namespace streamfx::encoder::ffmpeg::handler {
class nvenc_h264_handler : public handler {
public:
virtual ~nvenc_h264_handler(){};
public /*factory*/:
virtual void adjust_info(ffmpeg_factory* factory, const AVCodec* codec, std::string& id, std::string& name, std::string& codec_id);
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
virtual std::string_view get_help_url(const AVCodec* codec) override
{
return "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-FFmpeg-NVENC";
};
public /*support tests*/:
virtual bool has_keyframe_support(ffmpeg_factory* instance);
virtual bool is_hardware_encoder(ffmpeg_factory* instance);
virtual bool has_threading_support(ffmpeg_factory* instance);
virtual bool has_pixel_format_support(ffmpeg_factory* instance);
virtual bool supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes);
public /*settings*/:
virtual void get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
virtual void migrate(obs_data_t* settings, uint64_t version, const AVCodec* codec, AVCodecContext* context);
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
virtual void override_update(ffmpeg_instance* instance, obs_data_t* settings);
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
private:
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
};
} // namespace streamfx::encoder::ffmpeg::handler

View File

@ -1,210 +0,0 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
// AUTOGENERATED COPYRIGHT HEADER END
#include "nvenc_hevc_handler.hpp"
#include "common.hpp"
#include "strings.hpp"
#include "../codecs/hevc.hpp"
#include "../encoder-ffmpeg.hpp"
#include "ffmpeg/tools.hpp"
#include "nvenc_shared.hpp"
#include "plugin.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavutil/opt.h>
#include "warning-enable.hpp"
}
#define ST_KEY_PROFILE "H265.Profile"
#define ST_KEY_TIER "H265.Tier"
#define ST_KEY_LEVEL "H265.Level"
using namespace streamfx::encoder::ffmpeg::handler;
using namespace streamfx::encoder::codec::hevc;
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)";
if (!nvenc::is_available())
fac->get_info()->caps |= OBS_ENCODER_CAP_DEPRECATED;
}
void nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool)
{
nvenc::get_defaults(settings, codec, context);
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*)
{
return true;
}
bool nvenc_hevc_handler::is_hardware_encoder(ffmpeg_factory* instance)
{
return true;
}
bool nvenc_hevc_handler::has_threading_support(ffmpeg_factory* instance)
{
return false;
}
bool nvenc_hevc_handler::has_pixel_format_support(ffmpeg_factory* instance)
{
return true;
}
void nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context, bool)
{
if (!context) {
this->get_encoder_properties(props, codec);
} else {
this->get_runtime_properties(props, codec, context);
}
}
void nvenc_hevc_handler::update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{
nvenc::update(settings, codec, context);
if (!context->internal) {
if (const char* v = obs_data_get_string(settings, ST_KEY_PROFILE); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "profile", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_TIER); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "tier", v, AV_OPT_SEARCH_CHILDREN);
}
if (const char* v = obs_data_get_string(settings, ST_KEY_LEVEL); v && (v[0] != '\0')) {
av_opt_set(context->priv_data, "level", v, AV_OPT_SEARCH_CHILDREN);
}
}
}
void nvenc_hevc_handler::override_update(ffmpeg_instance* instance, obs_data_t* settings)
{
nvenc::override_update(instance, settings);
}
void nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{
nvenc::log_options(settings, codec, context);
DLOG_INFO("[%s] H.265/HEVC:", codec->name);
::streamfx::ffmpeg::tools::print_av_option_string2(context, "profile", " Profile", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, "level", " Level", [](int64_t v, std::string_view o) { return std::string(o); });
::streamfx::ffmpeg::tools::print_av_option_string2(context, "tier", " Tier", [](int64_t v, std::string_view o) { return std::string(o); });
}
void nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* 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;
if (!streamfx::util::are_property_groups_broken()) {
grp = obs_properties_create();
obs_properties_add_group(props, S_CODEC_HEVC, D_TRANSLATE(S_CODEC_HEVC), OBS_GROUP_NORMAL, grp);
}
{
auto p = obs_properties_add_list(grp, ST_KEY_PROFILE, D_TRANSLATE(S_CODEC_HEVC_PROFILE), 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](const AVOption* opt) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s.%s", 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_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](const AVOption* opt) {
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s.%s", 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_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);
}
});
}
}
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)
{
nvenc::get_runtime_properties(props, codec, context);
}
void streamfx::encoder::ffmpeg::handler::nvenc_hevc_handler::migrate(obs_data_t* settings, uint64_t version, 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<int64_t, std::string> 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<int64_t, std::string> 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)
{
threads = false;
gpu = false;
keyframes = false;
return true;
}

View File

@ -1,56 +0,0 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#pragma once
#include "handler.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavcodec/avcodec.h>
#include "warning-enable.hpp"
}
namespace streamfx::encoder::ffmpeg::handler {
class nvenc_hevc_handler : public handler {
public:
virtual ~nvenc_hevc_handler(){};
public /*factory*/:
virtual void adjust_info(ffmpeg_factory* factory, const AVCodec* codec, std::string& id, std::string& name, std::string& codec_id);
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
virtual std::string_view get_help_url(const AVCodec* codec) override
{
return "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-FFmpeg-NVENC";
};
public /*support tests*/:
virtual bool has_keyframe_support(ffmpeg_factory* instance);
virtual bool is_hardware_encoder(ffmpeg_factory* instance);
virtual bool has_threading_support(ffmpeg_factory* instance);
virtual bool has_pixel_format_support(ffmpeg_factory* instance);
virtual bool supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes);
public /*settings*/:
virtual void get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
virtual void migrate(obs_data_t* settings, uint64_t version, const AVCodec* codec, AVCodecContext* context);
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
virtual void override_update(ffmpeg_instance* instance, obs_data_t* settings);
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
private:
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
void get_runtime_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context);
};
} // namespace streamfx::encoder::ffmpeg::handler

View File

@ -1,40 +0,0 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#pragma once
#include "common.hpp"
#include "handler.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavcodec/avcodec.h>
#include "warning-enable.hpp"
}
/* NVENC has multiple compression modes:
- CBR: Constant Bitrate (rc=cbr)
- VBR: Variable Bitrate (rc=vbr)
- CQP: Constant QP (rc=cqp)
- CQ: Constant Quality (rc=vbr b=0 maxrate=0 qmin=0 qmax=51 cq=qp), this is basically CRF in X264.
*/
namespace streamfx::encoder::ffmpeg::handler::nvenc {
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, const AVCodecContext* context);
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);
void migrate(obs_data_t* settings, uint64_t version, const AVCodec* codec, AVCodecContext* context);
void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
} // namespace streamfx::encoder::ffmpeg::handler::nvenc