obs-ffmpeg: Remove native/disable FFmpeg NVENC

This commit is contained in:
derrod
2024-04-05 05:23:50 +02:00
parent 00c495b203
commit af555b9372
12 changed files with 39 additions and 3444 deletions
+8 -6
View File
@@ -5,8 +5,8 @@ legacy_check()
option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF)
option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON)
if(OS_LINUX)
option(ENABLE_NATIVE_NVENC "Use native NVENC implementation" ON)
if(OS_LINUX OR OS_WINDOWS)
option(ENABLE_FFMPEG_NVENC "Enable legacy FFmpeg NVENC encoder" OFF)
endif()
include(cmake/dependencies.cmake)
@@ -20,6 +20,7 @@ target_sources(
obs-ffmpeg
PRIVATE # cmake-format: sortable
$<$<BOOL:${ENABLE_FFMPEG_LOGGING}>:obs-ffmpeg-logging.c>
$<$<BOOL:${ENABLE_FFMPEG_NVENC}>:obs-ffmpeg-nvenc.c>
$<$<BOOL:${ENABLE_NEW_MPEGTS_OUTPUT}>:obs-ffmpeg-mpegts.c>
$<$<BOOL:${ENABLE_NEW_MPEGTS_OUTPUT}>:obs-ffmpeg-rist.h>
$<$<BOOL:${ENABLE_NEW_MPEGTS_OUTPUT}>:obs-ffmpeg-srt.h>
@@ -36,7 +37,6 @@ target_sources(
obs-ffmpeg-hls-mux.c
obs-ffmpeg-mux.c
obs-ffmpeg-mux.h
obs-ffmpeg-nvenc.c
obs-ffmpeg-output.c
obs-ffmpeg-output.h
obs-ffmpeg-source.c
@@ -44,8 +44,11 @@ target_sources(
obs-ffmpeg.c)
target_compile_options(obs-ffmpeg PRIVATE $<$<COMPILE_LANG_AND_ID:C,AppleClang,Clang>:-Wno-shorten-64-to-32>)
target_compile_definitions(obs-ffmpeg PRIVATE $<$<BOOL:${ENABLE_FFMPEG_LOGGING}>:ENABLE_FFMPEG_LOGGING>
$<$<BOOL:${ENABLE_NEW_MPEGTS_OUTPUT}>:NEW_MPEGTS_OUTPUT>)
target_compile_definitions(
obs-ffmpeg
PRIVATE $<$<BOOL:${ENABLE_FFMPEG_LOGGING}>:ENABLE_FFMPEG_LOGGING>
$<$<BOOL:${ENABLE_FFMPEG_NVENC}>:ENABLE_FFMPEG_NVENC>
$<$<BOOL:${ENABLE_NEW_MPEGTS_OUTPUT}>:NEW_MPEGTS_OUTPUT>)
target_link_libraries(
obs-ffmpeg
@@ -59,7 +62,6 @@ target_link_libraries(
FFmpeg::avutil
FFmpeg::swscale
FFmpeg::swresample
$<TARGET_NAME_IF_EXISTS:OBS::obs-nvenc-native>
$<$<PLATFORM_ID:Windows>:OBS::w32-pthreads>
$<$<PLATFORM_ID:Windows>:AMF::AMF>
$<$<PLATFORM_ID:Windows>:ws2_32>
@@ -36,32 +36,6 @@ elseif(
find_package(Libdrm REQUIRED)
endif()
if(OS_WINDOWS OR (OS_LINUX AND ENABLE_NATIVE_NVENC))
add_library(obs-nvenc-version INTERFACE)
add_library(OBS::obs-nvenc-version ALIAS obs-nvenc-version)
target_sources(obs-nvenc-version INTERFACE obs-nvenc-ver.h)
target_include_directories(obs-nvenc-version INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
find_package(FFnvcodec 12.0.0.0...<12.2.0.0 REQUIRED)
if(OS_LINUX AND NOT TARGET OBS::glad)
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/glad" "${CMAKE_BINARY_DIR}/deps/glad")
endif()
add_library(obs-nvenc-native INTERFACE)
add_library(OBS::obs-nvenc-native ALIAS obs-nvenc-native)
target_sources(obs-nvenc-native INTERFACE obs-nvenc-helpers.c obs-nvenc.c obs-nvenc.h)
target_compile_definitions(obs-nvenc-native INTERFACE $<$<PLATFORM_ID:Linux>:NVCODEC_AVAILABLE>)
target_include_directories(obs-nvenc-native INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(obs-nvenc-native INTERFACE FFnvcodec::FFnvcodec OBS::obs-nvenc-version
$<$<PLATFORM_ID:Linux>:OBS::glad>)
if(OS_WINDOWS)
add_subdirectory(obs-nvenc-test)
endif()
endif()
if(ENABLE_NEW_MPEGTS_OUTPUT)
find_package(Librist QUIET)
find_package(Libsrt QUIET)
+10 -19
View File
@@ -2,7 +2,10 @@ project(obs-ffmpeg)
option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF)
option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON)
option(ENABLE_NATIVE_NVENC "Use native NVENC implementation" ON)
if(OS_LINUX OR OS_WINDOWS)
option(ENABLE_FFMPEG_NVENC "Enables legacy FFmpeg NVENC encoder" OFF)
endif()
find_package(
FFmpeg REQUIRED
@@ -49,7 +52,6 @@ target_sources(
obs-ffmpeg-video-encoders.c
obs-ffmpeg-audio-encoders.c
obs-ffmpeg-av1.c
obs-ffmpeg-nvenc.c
obs-ffmpeg-output.c
obs-ffmpeg-output.h
obs-ffmpeg-mux.c
@@ -85,6 +87,11 @@ if(ENABLE_NEW_MPEGTS_OUTPUT)
target_compile_definitions(obs-ffmpeg PRIVATE NEW_MPEGTS_OUTPUT)
endif()
if(ENABLE_FFMPEG_NVENC)
target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-nvenc.c)
target_compile_definitions(obs-ffmpeg PRIVATE ENABLE_FFMPEG_NVENC)
endif()
if(ENABLE_FFMPEG_LOGGING)
target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-logging.c)
endif()
@@ -96,7 +103,6 @@ if(OS_WINDOWS)
find_package(FFnvcodec 12 REQUIRED)
add_subdirectory(obs-amf-test)
add_subdirectory(obs-nvenc-test)
if(MSVC)
target_link_libraries(obs-ffmpeg PRIVATE OBS::w32-pthreads)
@@ -106,15 +112,7 @@ if(OS_WINDOWS)
set(MODULE_DESCRIPTION "OBS FFmpeg module")
configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-ffmpeg.rc)
target_sources(
obs-ffmpeg
PRIVATE texture-amf.cpp
texture-amf-opts.hpp
obs-nvenc.c
obs-nvenc.h
obs-nvenc-helpers.c
obs-nvenc-ver.h
obs-ffmpeg.rc)
target_sources(obs-ffmpeg PRIVATE texture-amf.cpp texture-amf-opts.hpp obs-ffmpeg.rc)
elseif(OS_POSIX AND NOT OS_MACOS)
find_package(Libva REQUIRED)
@@ -122,13 +120,6 @@ elseif(OS_POSIX AND NOT OS_MACOS)
find_package(Libdrm REQUIRED)
target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c vaapi-utils.h)
target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI Libdrm::Libdrm)
if(ENABLE_NATIVE_NVENC)
find_package(FFnvcodec 12.0.0.0...<12.2.0.0 REQUIRED)
target_sources(obs-ffmpeg PRIVATE obs-nvenc.c obs-nvenc.h obs-nvenc-helpers.c obs-nvenc-ver.h)
target_link_libraries(obs-ffmpeg PRIVATE FFnvcodec::FFnvcodec OBS::obsglad)
target_compile_definitions(obs-ffmpeg PRIVATE NVCODEC_AVAILABLE)
endif()
endif()
setup_plugin_target(obs-ffmpeg)
+3 -40
View File
@@ -494,7 +494,7 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p,
return true;
}
obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg)
obs_properties_t *nvenc_properties_internal(enum codec_type codec)
{
obs_properties_t *props = obs_properties_create();
obs_property_t *p;
@@ -587,15 +587,6 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg)
}
#undef add_profile
if (!ffmpeg) {
p = obs_properties_add_bool(props, "lookahead",
obs_module_text("NVENC.LookAhead"));
obs_property_set_long_description(
p, obs_module_text("NVENC.LookAhead.ToolTip"));
p = obs_properties_add_bool(props, "repeat_headers",
"repeat_headers");
obs_property_set_visible(p, false);
}
p = obs_properties_add_bool(
props, "psycho_aq",
obs_module_text("NVENC.PsychoVisualTuning"));
@@ -610,37 +601,17 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg)
return props;
}
obs_properties_t *h264_nvenc_properties(void *unused)
{
UNUSED_PARAMETER(unused);
return nvenc_properties_internal(CODEC_H264, false);
}
#ifdef ENABLE_HEVC
obs_properties_t *hevc_nvenc_properties(void *unused)
{
UNUSED_PARAMETER(unused);
return nvenc_properties_internal(CODEC_HEVC, false);
}
#endif
obs_properties_t *av1_nvenc_properties(void *unused)
{
UNUSED_PARAMETER(unused);
return nvenc_properties_internal(CODEC_AV1, false);
}
obs_properties_t *h264_nvenc_properties_ffmpeg(void *unused)
{
UNUSED_PARAMETER(unused);
return nvenc_properties_internal(CODEC_H264, true);
return nvenc_properties_internal(CODEC_H264);
}
#ifdef ENABLE_HEVC
obs_properties_t *hevc_nvenc_properties_ffmpeg(void *unused)
{
UNUSED_PARAMETER(unused);
return nvenc_properties_internal(CODEC_HEVC, true);
return nvenc_properties_internal(CODEC_HEVC);
}
#endif
@@ -676,11 +647,7 @@ struct obs_encoder_info h264_nvenc_encoder_info = {
.get_extra_data = nvenc_extra_data,
.get_sei_data = nvenc_sei_data,
.get_video_info = nvenc_video_info,
#if defined(_WIN32) || defined(NVCODEC_AVAILABLE)
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE,
#endif
};
#ifdef ENABLE_HEVC
@@ -698,10 +665,6 @@ struct obs_encoder_info hevc_nvenc_encoder_info = {
.get_extra_data = nvenc_extra_data,
.get_sei_data = nvenc_sei_data,
.get_video_info = nvenc_video_info,
#if defined(_WIN32) || defined(NVCODEC_AVAILABLE)
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE,
#endif
};
#endif
+18 -36
View File
@@ -5,14 +5,8 @@
#include <libavformat/avformat.h>
#ifdef _WIN32
#define INITGUID
#include <dxgi.h>
#include <util/windows/win-version.h>
#endif
#if defined(_WIN32) || defined(NVCODEC_AVAILABLE)
#include "obs-nvenc.h"
#define OBS_NVENC_AVAILABLE
#endif
#if !defined(_WIN32) && !defined(__APPLE__)
@@ -41,10 +35,12 @@ extern struct obs_encoder_info pcm24_encoder_info;
extern struct obs_encoder_info pcm32_encoder_info;
extern struct obs_encoder_info alac_encoder_info;
extern struct obs_encoder_info flac_encoder_info;
#ifdef ENABLE_FFMPEG_NVENC
extern struct obs_encoder_info h264_nvenc_encoder_info;
#ifdef ENABLE_HEVC
extern struct obs_encoder_info hevc_nvenc_encoder_info;
#endif
#endif
extern struct obs_encoder_info svt_av1_encoder_info;
extern struct obs_encoder_info aom_av1_encoder_info;
@@ -59,11 +55,10 @@ extern struct obs_encoder_info hevc_vaapi_encoder_tex_info;
#endif
#endif
#ifndef __APPLE__
#ifdef ENABLE_FFMPEG_NVENC
static const char *nvenc_check_name = "nvenc_check";
#if defined(_WIN32) || defined(__linux__)
static const int blacklisted_adapters[] = {
0x1298, // GK208M [GeForce GT 720M]
0x1140, // GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
@@ -127,9 +122,8 @@ static bool is_blacklisted(const int device_id)
return false;
}
#endif
#if defined(_WIN32)
#ifdef _WIN32
typedef HRESULT(WINAPI *create_dxgi_proc)(const IID *, IDXGIFactory1 **);
static bool nvenc_device_available(void)
@@ -243,10 +237,6 @@ static bool nvenc_device_available(void)
}
#endif
#ifdef OBS_NVENC_AVAILABLE
extern bool load_nvenc_lib(void);
#endif
static bool nvenc_codec_exists(const char *name, const char *fallback)
{
const AVCodec *nvenc = avcodec_find_encoder_by_name(name);
@@ -256,7 +246,7 @@ static bool nvenc_codec_exists(const char *name, const char *fallback)
return nvenc != NULL;
}
static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1)
static bool nvenc_supported(bool *out_h264, bool *out_hevc)
{
profile_start(nvenc_check_name);
@@ -267,13 +257,18 @@ static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1)
const bool hevc = false;
#endif
bool av1 = false;
bool success = h264 || hevc;
if (success) {
#ifdef OBS_NVENC_AVAILABLE
success = nvenc_device_available() && load_nvenc_lib();
av1 = success && (get_nvenc_ver() >= ((12 << 4) | 0));
#ifdef _WIN32
success = nvenc_device_available();
#elif defined(__linux__)
success = nvenc_device_available();
if (success) {
void *const lib = os_dlopen("libnvidia-encode.so.1");
success = lib != NULL;
if (success)
os_dlclose(lib);
}
#else
void *const lib = os_dlopen("libnvidia-encode.so.1");
success = lib != NULL;
@@ -284,7 +279,6 @@ static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1)
if (success) {
*out_h264 = h264;
*out_hevc = hevc;
*out_av1 = av1;
}
}
@@ -334,11 +328,6 @@ static bool hevc_vaapi_supported(void)
#endif
#endif
#ifdef OBS_NVENC_AVAILABLE
extern void obs_nvenc_load(bool h264, bool hevc, bool av1);
extern void obs_nvenc_unload(void);
#endif
#ifdef _WIN32
extern void amf_load(void);
extern void amf_unload(void);
@@ -375,16 +364,12 @@ bool obs_module_load(void)
obs_register_encoder(&pcm32_encoder_info);
obs_register_encoder(&alac_encoder_info);
obs_register_encoder(&flac_encoder_info);
#ifndef __APPLE__
#ifdef ENABLE_FFMPEG_NVENC
bool h264 = false;
bool hevc = false;
bool av1 = false;
if (nvenc_supported(&h264, &hevc, &av1)) {
if (nvenc_supported(&h264, &hevc)) {
blog(LOG_INFO, "NVENC supported");
#ifdef OBS_NVENC_AVAILABLE
obs_nvenc_load(h264, hevc, av1);
#endif
if (h264)
obs_register_encoder(&h264_nvenc_encoder_info);
#ifdef ENABLE_HEVC
@@ -447,7 +432,4 @@ void obs_module_unload(void)
#ifdef _WIN32
amf_unload();
#endif
#ifdef OBS_NVENC_AVAILABLE
obs_nvenc_unload();
#endif
}
-489
View File
@@ -1,489 +0,0 @@
#include "obs-nvenc.h"
#include <util/platform.h>
#include <util/threading.h>
#include <util/config-file.h>
#include <util/dstr.h>
#include <util/pipe.h>
#ifdef _WIN32
#include <util/windows/device-enum.h>
#endif
static void *nvenc_lib = NULL;
static void *cuda_lib = NULL;
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER};
NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL;
CudaFunctions *cu = NULL;
#define error(format, ...) blog(LOG_ERROR, "[obs-nvenc] " format, ##__VA_ARGS__)
bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...)
{
UNUSED_PARAMETER(session);
struct dstr message = {0};
struct dstr error_message = {0};
va_list args;
va_start(args, format);
dstr_vprintf(&message, format, args);
va_end(args);
dstr_printf(&error_message, "NVENC Error: %s", message.array);
obs_encoder_set_last_error(encoder, error_message.array);
error("%s", error_message.array);
dstr_free(&error_message);
dstr_free(&message);
return true;
}
bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err,
const char *func, const char *call)
{
struct dstr error_message = {0};
const char *nvenc_error = NULL;
if (err == NV_ENC_SUCCESS)
return false;
if (session) {
nvenc_error = nv.nvEncGetLastErrorString(session);
if (nvenc_error) {
// Some NVENC errors begin with :: which looks
// odd to users. Strip it off.
while (*nvenc_error == ':')
nvenc_error++;
}
}
switch (err) {
case NV_ENC_ERR_OUT_OF_MEMORY:
obs_encoder_set_last_error(
encoder, obs_module_text("NVENC.TooManySessions"));
break;
case NV_ENC_ERR_NO_ENCODE_DEVICE:
case NV_ENC_ERR_UNSUPPORTED_DEVICE:
obs_encoder_set_last_error(
encoder, obs_module_text("NVENC.UnsupportedDevice"));
break;
case NV_ENC_ERR_INVALID_VERSION:
obs_encoder_set_last_error(
encoder, obs_module_text("NVENC.OutdatedDriver"));
break;
default:
if (nvenc_error && *nvenc_error) {
dstr_printf(&error_message, "NVENC Error: %s (%s)",
nvenc_error, nv_error_name(err));
} else {
dstr_printf(&error_message,
"NVENC Error: %s: %s failed: %d (%s)", func,
call, (int)err, nv_error_name(err));
}
obs_encoder_set_last_error(encoder, error_message.array);
dstr_free(&error_message);
break;
}
if (nvenc_error && *nvenc_error) {
error("%s: %s failed: %d (%s): %s", func, call, (int)err,
nv_error_name(err), nvenc_error);
} else {
error("%s: %s failed: %d (%s)", func, call, (int)err,
nv_error_name(err));
}
return true;
}
#define NV_FAILED(e, x) nv_failed2(e, NULL, x, __FUNCTION__, #x)
bool load_nvenc_lib(void)
{
#ifdef _WIN32
nvenc_lib = os_dlopen("nvEncodeAPI64.dll");
#else
nvenc_lib = os_dlopen("libnvidia-encode.so.1");
#endif
return nvenc_lib != NULL;
}
static void *load_nv_func(const char *func)
{
void *func_ptr = os_dlsym(nvenc_lib, func);
if (!func_ptr) {
error("Could not load function: %s", func);
}
return func_ptr;
}
bool load_cuda_lib(void)
{
#ifdef _WIN32
cuda_lib = os_dlopen("nvcuda.dll");
#else
cuda_lib = os_dlopen("libcuda.so.1");
#endif
return cuda_lib != NULL;
}
static void *load_cuda_func(const char *func)
{
void *func_ptr = os_dlsym(cuda_lib, func);
if (!func_ptr) {
error("Could not load function: %s", func);
}
return func_ptr;
}
typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *);
uint32_t get_nvenc_ver(void)
{
static NV_MAX_VER_FUNC nv_max_ver = NULL;
static bool failed = false;
static uint32_t ver = 0;
if (!failed && ver)
return ver;
if (!nv_max_ver) {
if (failed)
return 0;
nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func(
"NvEncodeAPIGetMaxSupportedVersion");
if (!nv_max_ver) {
failed = true;
return 0;
}
}
if (nv_max_ver(&ver) != NV_ENC_SUCCESS) {
return 0;
}
return ver;
}
const char *nv_error_name(NVENCSTATUS err)
{
#define RETURN_CASE(x) \
case x: \
return #x
switch (err) {
RETURN_CASE(NV_ENC_SUCCESS);
RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE);
RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE);
RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE);
RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE);
RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST);
RETURN_CASE(NV_ENC_ERR_INVALID_PTR);
RETURN_CASE(NV_ENC_ERR_INVALID_EVENT);
RETURN_CASE(NV_ENC_ERR_INVALID_PARAM);
RETURN_CASE(NV_ENC_ERR_INVALID_CALL);
RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY);
RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED);
RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM);
RETURN_CASE(NV_ENC_ERR_LOCK_BUSY);
RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER);
RETURN_CASE(NV_ENC_ERR_INVALID_VERSION);
RETURN_CASE(NV_ENC_ERR_MAP_FAILED);
RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT);
RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY);
RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD);
RETURN_CASE(NV_ENC_ERR_GENERIC);
RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY);
RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED);
RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED);
RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED);
RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED);
}
#undef RETURN_CASE
return "Unknown Error";
}
static inline bool init_nvenc_internal(obs_encoder_t *encoder)
{
static bool initialized = false;
static bool success = false;
if (initialized)
return success;
initialized = true;
uint32_t ver = get_nvenc_ver();
if (ver == 0) {
obs_encoder_set_last_error(
encoder,
"Missing NvEncodeAPIGetMaxSupportedVersion, check "
"your video card drivers are up to date.");
return false;
}
uint32_t supported_ver = (NVENC_COMPAT_MAJOR_VER << 4) |
NVENC_COMPAT_MINOR_VER;
if (supported_ver > ver) {
obs_encoder_set_last_error(
encoder, obs_module_text("NVENC.OutdatedDriver"));
error("Current driver version does not support this NVENC "
"version, please upgrade your driver");
return false;
}
nv_create_instance = (NV_CREATE_INSTANCE_FUNC)load_nv_func(
"NvEncodeAPICreateInstance");
if (!nv_create_instance) {
obs_encoder_set_last_error(
encoder, "Missing NvEncodeAPICreateInstance, check "
"your video card drivers are up to date.");
return false;
}
if (NV_FAILED(encoder, nv_create_instance(&nv))) {
return false;
}
success = true;
return true;
}
typedef struct cuda_function {
ptrdiff_t offset;
const char *name;
} cuda_function;
static const cuda_function cuda_functions[] = {
{offsetof(CudaFunctions, cuInit), "cuInit"},
{offsetof(CudaFunctions, cuDeviceGetCount), "cuDeviceGetCount"},
{offsetof(CudaFunctions, cuDeviceGet), "cuDeviceGet"},
{offsetof(CudaFunctions, cuDeviceGetAttribute), "cuDeviceGetAttribute"},
{offsetof(CudaFunctions, cuCtxCreate), "cuCtxCreate_v2"},
{offsetof(CudaFunctions, cuCtxDestroy), "cuCtxDestroy_v2"},
{offsetof(CudaFunctions, cuCtxPushCurrent), "cuCtxPushCurrent_v2"},
{offsetof(CudaFunctions, cuCtxPopCurrent), "cuCtxPopCurrent_v2"},
{offsetof(CudaFunctions, cuArray3DCreate), "cuArray3DCreate_v2"},
{offsetof(CudaFunctions, cuArrayDestroy), "cuArrayDestroy"},
{offsetof(CudaFunctions, cuMemcpy2D), "cuMemcpy2D_v2"},
{offsetof(CudaFunctions, cuGetErrorName), "cuGetErrorName"},
{offsetof(CudaFunctions, cuGetErrorString), "cuGetErrorString"},
{offsetof(CudaFunctions, cuMemHostRegister), "cuMemHostRegister_v2"},
{offsetof(CudaFunctions, cuMemHostUnregister), "cuMemHostUnregister"},
#ifndef _WIN32
{offsetof(CudaFunctions, cuGLGetDevices), "cuGLGetDevices_v2"},
{offsetof(CudaFunctions, cuGraphicsGLRegisterImage),
"cuGraphicsGLRegisterImage"},
{offsetof(CudaFunctions, cuGraphicsUnregisterResource),
"cuGraphicsUnregisterResource"},
{offsetof(CudaFunctions, cuGraphicsMapResources),
"cuGraphicsMapResources"},
{offsetof(CudaFunctions, cuGraphicsUnmapResources),
"cuGraphicsUnmapResources"},
{offsetof(CudaFunctions, cuGraphicsSubResourceGetMappedArray),
"cuGraphicsSubResourceGetMappedArray"},
#endif
};
static const size_t num_cuda_funcs =
sizeof(cuda_functions) / sizeof(cuda_function);
static bool init_cuda_internal(obs_encoder_t *encoder)
{
static bool initialized = false;
static bool success = false;
if (initialized)
return success;
initialized = true;
if (!load_cuda_lib()) {
obs_encoder_set_last_error(encoder,
"Loading CUDA library failed.");
return false;
}
cu = bzalloc(sizeof(CudaFunctions));
for (size_t idx = 0; idx < num_cuda_funcs; idx++) {
const cuda_function func = cuda_functions[idx];
void *fptr = load_cuda_func(func.name);
if (!fptr) {
error("Failed to find CUDA function: %s", func.name);
obs_encoder_set_last_error(
encoder, "Loading CUDA functions failed.");
return false;
}
*(uintptr_t *)((uintptr_t)cu + func.offset) = (uintptr_t)fptr;
}
success = true;
return true;
}
bool cuda_get_error_desc(CUresult res, const char **name, const char **desc)
{
if (cu->cuGetErrorName(res, name) != CUDA_SUCCESS ||
cu->cuGetErrorString(res, desc) != CUDA_SUCCESS)
return false;
return true;
}
bool init_nvenc(obs_encoder_t *encoder)
{
bool success;
pthread_mutex_lock(&init_mutex);
success = init_nvenc_internal(encoder);
pthread_mutex_unlock(&init_mutex);
return success;
}
bool init_cuda(obs_encoder_t *encoder)
{
bool success;
pthread_mutex_lock(&init_mutex);
success = init_cuda_internal(encoder);
pthread_mutex_unlock(&init_mutex);
return success;
}
extern struct obs_encoder_info h264_nvenc_info;
#ifdef ENABLE_HEVC
extern struct obs_encoder_info hevc_nvenc_info;
#endif
extern struct obs_encoder_info av1_nvenc_info;
extern struct obs_encoder_info h264_nvenc_soft_info;
#ifdef ENABLE_HEVC
extern struct obs_encoder_info hevc_nvenc_soft_info;
#endif
extern struct obs_encoder_info av1_nvenc_soft_info;
#ifdef _WIN32
static bool enum_luids(void *param, uint32_t idx, uint64_t luid)
{
struct dstr *cmd = param;
dstr_catf(cmd, " %llX", luid);
UNUSED_PARAMETER(idx);
return true;
}
static bool av1_supported(void)
{
char *test_exe = os_get_executable_path_ptr("obs-nvenc-test.exe");
struct dstr cmd = {0};
struct dstr caps_str = {0};
bool av1_supported = false;
config_t *config = NULL;
dstr_init_move_array(&cmd, test_exe);
dstr_insert_ch(&cmd, 0, '\"');
dstr_cat(&cmd, "\"");
enum_graphics_device_luids(enum_luids, &cmd);
os_process_pipe_t *pp = os_process_pipe_create(cmd.array, "r");
if (!pp) {
blog(LOG_WARNING, "[NVENC] Failed to launch the NVENC "
"test process I guess");
goto fail;
}
for (;;) {
char data[2048];
size_t len =
os_process_pipe_read(pp, (uint8_t *)data, sizeof(data));
if (!len)
break;
dstr_ncat(&caps_str, data, len);
}
os_process_pipe_destroy(pp);
if (dstr_is_empty(&caps_str)) {
blog(LOG_WARNING,
"[NVENC] Seems the NVENC test subprocess crashed. "
"Better there than here I guess. Let's just "
"skip NVENC AV1 detection then I suppose.");
goto fail;
}
if (config_open_string(&config, caps_str.array) != 0) {
blog(LOG_WARNING, "[NVENC] Failed to open config string");
goto fail;
}
const char *error = config_get_string(config, "error", "string");
if (error) {
blog(LOG_WARNING, "[NVENC] AV1 test process failed: %s", error);
goto fail;
}
uint32_t adapter_count = (uint32_t)config_num_sections(config);
bool avc_supported = false;
bool hevc_supported = false;
/* for now, just check AV1 support on device 0 */
av1_supported = config_get_bool(config, "0", "supports_av1");
fail:
if (config)
config_close(config);
dstr_free(&caps_str);
dstr_free(&cmd);
return av1_supported;
}
#else
bool av1_supported()
{
return get_nvenc_ver() >= ((12 << 4) | 0);
}
#endif
void obs_nvenc_load(bool h264, bool hevc, bool av1)
{
pthread_mutex_init(&init_mutex, NULL);
if (h264) {
obs_register_encoder(&h264_nvenc_info);
obs_register_encoder(&h264_nvenc_soft_info);
}
#ifdef ENABLE_HEVC
if (hevc) {
obs_register_encoder(&hevc_nvenc_info);
obs_register_encoder(&hevc_nvenc_soft_info);
}
#endif
if (av1 && av1_supported()) {
obs_register_encoder(&av1_nvenc_info);
obs_register_encoder(&av1_nvenc_soft_info);
} else {
blog(LOG_WARNING, "[NVENC] AV1 is not supported");
}
}
void obs_nvenc_unload(void)
{
bfree(cu);
pthread_mutex_destroy(&init_mutex);
}
@@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 3.24...3.25)
legacy_check()
find_package(FFnvcodec 12 REQUIRED)
add_executable(obs-nvenc-test)
target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.c)
target_link_libraries(obs-nvenc-test OBS::obs-nvenc-version FFnvcodec::FFnvcodec d3d11 dxgi dxguid)
# cmake-format: off
set_target_properties_obs(obs-nvenc-test PROPERTIES FOLDER plugins/obs-ffmpeg)
# cmake-format: on
@@ -1,13 +0,0 @@
project(obs-nvenc-test)
add_executable(obs-nvenc-test)
find_package(FFnvcodec 12 REQUIRED)
target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.c ../obs-nvenc-ver.h)
target_compile_definitions(obs-nvenc-test PRIVATE OBS_LEGACY)
target_link_libraries(obs-nvenc-test d3d11 dxgi dxguid FFnvcodec::FFnvcodec)
set_target_properties(obs-nvenc-test PROPERTIES FOLDER "plugins/obs-ffmpeg")
setup_binary_target(obs-nvenc-test)
@@ -1,238 +0,0 @@
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ffnvcodec/nvEncodeAPI.h>
#ifdef OBS_LEGACY
#include "../obs-nvenc-ver.h"
#else
#include <obs-nvenc-ver.h>
#endif
#include <dxgi.h>
#include <d3d11.h>
#include <d3d11_1.h>
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER};
static void *nvenc_lib = NULL;
static bool av1_supported = false;
#define NVIDIA_VENDOR_ID 0x10DE
struct nvenc_info {
bool is_nvidia;
bool supports_av1;
};
#define MAX_CAPS 10
static uint32_t luid_count = 0;
static uint64_t luid_order[MAX_CAPS] = {0};
static struct nvenc_info adapter_info[MAX_CAPS] = {0};
bool load_nvenc_lib(void)
{
const char *const file = (sizeof(void *) == 8) ? "nvEncodeAPI64.dll"
: "nvEncodeAPI.dll";
nvenc_lib = LoadLibraryA(file);
return nvenc_lib != NULL;
}
static inline void *load_nv_func(const char *func)
{
void *func_ptr = (void *)GetProcAddress(nvenc_lib, func);
return func_ptr;
}
static inline uint32_t get_adapter_idx(uint32_t adapter_idx, LUID luid)
{
for (uint32_t i = 0; i < luid_count; i++) {
if (luid_order[i] == *(uint64_t *)&luid) {
return i;
}
}
return adapter_idx;
}
static bool get_adapter_caps(IDXGIFactory *factory, uint32_t adapter_idx)
{
struct nvenc_info *caps;
IDXGIAdapter *adapter = NULL;
ID3D11Device *device = NULL;
ID3D11DeviceContext *context = NULL;
GUID *guids = NULL;
void *session = NULL;
HRESULT hr;
if (adapter_idx == MAX_CAPS)
return false;
hr = factory->lpVtbl->EnumAdapters(factory, adapter_idx, &adapter);
if (FAILED(hr))
return false;
DXGI_ADAPTER_DESC desc;
adapter->lpVtbl->GetDesc(adapter, &desc);
caps = &adapter_info[get_adapter_idx(adapter_idx, desc.AdapterLuid)];
if (desc.VendorId != NVIDIA_VENDOR_ID)
return true;
caps->is_nvidia = true;
hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL,
0, D3D11_SDK_VERSION, &device, NULL, &context);
if (FAILED(hr))
goto finish;
/* ---------------------------------------------------------------- */
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = {
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER};
params.device = device;
params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
params.apiVersion = NVENCAPI_VERSION;
NVENCSTATUS stat = nv.nvEncOpenEncodeSessionEx(&params, &session);
if (stat != NV_ENC_SUCCESS)
goto finish;
uint32_t guid_count = 0;
if (nv.nvEncGetEncodeGUIDCount(session, &guid_count) != NV_ENC_SUCCESS)
goto finish;
guids = malloc(guid_count * sizeof(GUID));
stat = nv.nvEncGetEncodeGUIDs(session, guids, guid_count, &guid_count);
if (stat != NV_ENC_SUCCESS)
goto finish;
for (uint32_t i = 0; i < guid_count; i++) {
GUID *guid = &guids[i];
if (memcmp(guid, &NV_ENC_CODEC_AV1_GUID, sizeof(GUID)) == 0) {
caps->supports_av1 = true;
break;
}
}
finish:
if (guids)
free(guids);
if (session)
nv.nvEncDestroyEncoder(session);
if (context)
context->lpVtbl->Release(context);
if (device)
device->lpVtbl->Release(device);
if (adapter)
adapter->lpVtbl->Release(adapter);
return true;
}
typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *);
typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)(
NV_ENCODE_API_FUNCTION_LIST *);
static inline uint32_t get_nvenc_ver(void)
{
NV_MAX_VER_FUNC nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func(
"NvEncodeAPIGetMaxSupportedVersion");
if (!nv_max_ver) {
return 0;
}
uint32_t ver = 0;
if (nv_max_ver(&ver) != NV_ENC_SUCCESS) {
return 0;
}
return ver;
}
static inline bool init_nvenc_internal(void)
{
if (!load_nvenc_lib())
return false;
uint32_t ver = get_nvenc_ver();
if (ver == 0)
return false;
uint32_t supported_ver = (NVENC_COMPAT_MAJOR_VER << 4) |
NVENC_COMPAT_MINOR_VER;
if (supported_ver > ver)
return false;
NV_CREATE_INSTANCE_FUNC nv_create_instance =
(NV_CREATE_INSTANCE_FUNC)load_nv_func(
"NvEncodeAPICreateInstance");
if (!nv_create_instance)
return false;
return nv_create_instance(&nv) == NV_ENC_SUCCESS;
}
DWORD WINAPI TimeoutThread(LPVOID param)
{
HANDLE hMainThread = (HANDLE)param;
DWORD ret = WaitForSingleObject(hMainThread, 2500);
if (ret == WAIT_TIMEOUT)
TerminateProcess(GetCurrentProcess(), STATUS_TIMEOUT);
CloseHandle(hMainThread);
return 0;
}
int main(int argc, char *argv[])
{
IDXGIFactory *factory = NULL;
HRESULT hr;
HANDLE hMainThread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hMainThread, 0, FALSE,
DUPLICATE_SAME_ACCESS);
DWORD threadId;
HANDLE hThread;
hThread =
CreateThread(NULL, 0, TimeoutThread, hMainThread, 0, &threadId);
CloseHandle(hThread);
/* --------------------------------------------------------- */
/* try initializing nvenc, I guess */
if (!init_nvenc_internal())
return 0;
/* --------------------------------------------------------- */
/* parse expected LUID order */
luid_count = argc - 1;
for (int i = 1; i < argc; i++) {
luid_order[i - 1] = strtoull(argv[i], NULL, 16);
}
/* --------------------------------------------------------- */
/* obtain adapter compatibility information */
hr = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&factory);
if (FAILED(hr))
return 0;
uint32_t idx = 0;
while (get_adapter_caps(factory, idx++))
;
for (uint32_t i = 0; i < idx; i++) {
struct nvenc_info caps = adapter_info[i];
printf("[%u]\n", i);
printf("is_nvidia=%s\n", caps.is_nvidia ? "true" : "false");
printf("supports_av1=%s\n",
caps.supports_av1 ? "true" : "false");
}
factory->lpVtbl->Release(factory);
return 0;
}
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#define NVENC_COMPAT_MAJOR_VER 11
#define NVENC_COMPAT_MINOR_VER 1
#define NVENC_COMPAT_VER \
(NVENC_COMPAT_MAJOR_VER | (NVENC_COMPAT_MINOR_VER << 24))
File diff suppressed because it is too large Load Diff
-71
View File
@@ -1,71 +0,0 @@
#pragma once
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <obs-module.h>
#include <ffnvcodec/nvEncodeAPI.h>
#include <ffnvcodec/dynlink_cuda.h>
#include "obs-nvenc-ver.h"
/* Missing from FFmpeg headers */
typedef CUresult CUDAAPI tcuMemHostRegister(void *p, size_t bytesize,
unsigned int Flags);
typedef CUresult CUDAAPI tcuMemHostUnregister(void *p);
typedef struct CudaFunctions {
tcuInit *cuInit;
tcuDeviceGetCount *cuDeviceGetCount;
tcuDeviceGet *cuDeviceGet;
tcuDeviceGetAttribute *cuDeviceGetAttribute;
tcuCtxCreate_v2 *cuCtxCreate;
tcuCtxDestroy_v2 *cuCtxDestroy;
tcuCtxPushCurrent_v2 *cuCtxPushCurrent;
tcuCtxPopCurrent_v2 *cuCtxPopCurrent;
tcuArray3DCreate *cuArray3DCreate;
tcuArrayDestroy *cuArrayDestroy;
tcuMemcpy2D_v2 *cuMemcpy2D;
tcuGetErrorName *cuGetErrorName;
tcuGetErrorString *cuGetErrorString;
tcuMemHostRegister *cuMemHostRegister;
tcuMemHostUnregister *cuMemHostUnregister;
#ifndef _WIN32
tcuGLGetDevices_v2 *cuGLGetDevices;
tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
tcuGraphicsMapResources *cuGraphicsMapResources;
tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
tcuGraphicsSubResourceGetMappedArray
*cuGraphicsSubResourceGetMappedArray;
#endif
} CudaFunctions;
typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)(
NV_ENCODE_API_FUNCTION_LIST *);
extern const char *nv_error_name(NVENCSTATUS err);
extern NV_ENCODE_API_FUNCTION_LIST nv;
extern NV_CREATE_INSTANCE_FUNC nv_create_instance;
extern CudaFunctions *cu;
extern uint32_t get_nvenc_ver(void);
extern bool init_nvenc(obs_encoder_t *encoder);
extern bool init_cuda(obs_encoder_t *encoder);
bool cuda_get_error_desc(CUresult res, const char **name, const char **desc);
bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...);
bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err,
const char *func, const char *call);
#define nv_fail(encoder, format, ...) \
nv_fail2(encoder, enc->session, format, ##__VA_ARGS__)
#define nv_failed(encoder, err, func, call) \
nv_failed2(encoder, enc->session, err, func, call)