code: Migrate encoder::ffmpeg::nvenc to new loader
This commit is contained in:
parent
51282b7b85
commit
1c76169821
|
@ -1171,12 +1171,8 @@ if(T_CHECK)
|
||||||
is_feature_enabled(ENCODER_FFMPEG_NVENC T_CHECK)
|
is_feature_enabled(ENCODER_FFMPEG_NVENC T_CHECK)
|
||||||
if(T_CHECK)
|
if(T_CHECK)
|
||||||
list(APPEND PROJECT_PRIVATE_SOURCE
|
list(APPEND PROJECT_PRIVATE_SOURCE
|
||||||
"source/encoders/ffmpeg/nvenc_shared.hpp"
|
"source/encoders/ffmpeg/nvenc.hpp"
|
||||||
"source/encoders/ffmpeg/nvenc_shared.cpp"
|
"source/encoders/ffmpeg/nvenc.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"
|
|
||||||
)
|
)
|
||||||
list(APPEND PROJECT_DEFINITIONS
|
list(APPEND PROJECT_DEFINITIONS
|
||||||
ENABLE_ENCODER_FFMPEG_NVENC
|
ENABLE_ENCODER_FFMPEG_NVENC
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
// AUTOGENERATED COPYRIGHT HEADER START
|
// AUTOGENERATED COPYRIGHT HEADER START
|
||||||
|
// AUTOGENERATED COPYRIGHT HEADER END
|
||||||
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
|
// 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 "encoders/encoder-ffmpeg.hpp"
|
||||||
#include "ffmpeg/tools.hpp"
|
#include "ffmpeg/tools.hpp"
|
||||||
|
#include "plugin.hpp"
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "warning-disable.hpp"
|
#include "warning-disable.hpp"
|
||||||
|
extern "C" {
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
#include "warning-enable.hpp"
|
|
||||||
}
|
}
|
||||||
|
#include "warning-enable.hpp"
|
||||||
|
|
||||||
#define ST_I18N_PRESET "Encoder.FFmpeg.NVENC.Preset"
|
#define ST_I18N_PRESET "Encoder.FFmpeg.NVENC.Preset"
|
||||||
#define ST_I18N_PRESET_(x) ST_I18N_PRESET "." D_VSTR(x)
|
#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_I18N_OTHER_LOWDELAYKEYFRAMESCALE ST_I18N_OTHER ".LowDelayKeyFrameScale"
|
||||||
#define ST_KEY_OTHER_LOWDELAYKEYFRAMESCALE "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)
|
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;
|
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_WINDOWS)
|
||||||
#if defined(D_PLATFORM_64BIT)
|
#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*)
|
void nvenc::defaults(ffmpeg_factory* factory, 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::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_KEY_PRESET, "default");
|
||||||
obs_data_set_default_string(settings, ST_I18N_TUNE, "hq");
|
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;
|
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);
|
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) {
|
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
|
{ // Rate Control
|
||||||
obs_properties_t* grp = props;
|
obs_properties_t* grp = props;
|
||||||
if (!streamfx::util::are_property_groups_broken()) {
|
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_PRESET), false);
|
||||||
obs_property_set_enabled(obs_properties_get(props, ST_KEY_TUNE), 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);
|
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')) {
|
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);
|
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;
|
using namespace ::streamfx::ffmpeg;
|
||||||
|
|
||||||
DLOG_INFO("[%s] NVIDIA NVENC:", codec->name);
|
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");
|
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
|
return true;
|
||||||
version = version & STREAMFX_MASK_UPDATE;
|
}
|
||||||
|
|
||||||
#define COPY_UNSET(TYPE, FROM, TO) \
|
bool nvenc_h264::has_threading(ffmpeg_factory*)
|
||||||
if (obs_data_has_user_value(settings, FROM)) { \
|
{
|
||||||
obs_data_set_##TYPE(settings, TO, obs_data_get_##TYPE(settings, FROM)); \
|
return false;
|
||||||
obs_data_unset_user_value(settings, FROM); \
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (version <= STREAMFX_MAKE_VERSION(0, 8, 0, 0)) {
|
bool nvenc_h264::is_hardware(ffmpeg_factory*)
|
||||||
COPY_UNSET(int, "RateControl.Bitrate.Target", ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET);
|
{
|
||||||
COPY_UNSET(int, "RateControl.Bitrate.Maximum", ST_KEY_RATECONTROL_LIMITS_BITRATE_TARGET);
|
return true;
|
||||||
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)) {
|
bool nvenc_h264::is_reconfigurable(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes)
|
||||||
obs_data_unset_user_value(settings, "Other.AccessUnitDelimiter");
|
{
|
||||||
obs_data_unset_user_value(settings, "Other.DecodedPictureBufferSize");
|
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)) {
|
if (version < STREAMFX_MAKE_VERSION(0, 11, 1, 0)) {
|
||||||
// Preset
|
// Profile
|
||||||
if (auto v = obs_data_get_int(settings, ST_KEY_PRESET); v != -1) {
|
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{
|
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()) {
|
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
|
// Level
|
||||||
if (auto v = obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE); v != -1) {
|
obs_data_set_string(settings, ST_KEY_H264_LEVEL, "auto");
|
||||||
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_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "main"},
|
||||||
|
{1, "main10"},
|
||||||
|
{2, "rext"},
|
||||||
|
};
|
||||||
|
if (auto k = preset.find(v); k != preset.end()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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
|
|
Loading…
Reference in New Issue