mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-08-29 02:03:03 +08:00
obs-ffmpeg: Remove native/disable FFmpeg NVENC
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(¶ms, &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;
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
Reference in New Issue
Block a user