mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 09:01:54 +08:00
refactor(hwcodec): 简化 hwcodec 库以适配 IP-KVM 场景
移除 IP-KVM 场景不需要的功能模块: - 移除 VRAM 模块 (GPU 显存直接编解码) - 移除 Mux 模块 (视频混流) - 移除 macOS/Android 平台支持 - 移除外部 SDK 依赖 (~9MB) - 移除开发工具和示例程序 简化解码器为仅支持 MJPEG (采集卡输出格式) 简化 NVIDIA 检测代码 (使用 dlopen 替代 SDK) 更新版本号至 0.8.0 更新相关技术文档
This commit is contained in:
@@ -2,83 +2,50 @@
|
||||
#include "../../log.h"
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <dynlink_cuda.h>
|
||||
#include <dynlink_loader.h>
|
||||
#include <errno.h>
|
||||
#include <exception> // Include the necessary header file
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
void load_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
|
||||
CuvidFunctions **pp_cvdl)
|
||||
{
|
||||
if (cuda_load_functions(pp_cuda_dl, NULL) < 0)
|
||||
{
|
||||
LOG_TRACE(std::string("cuda_load_functions failed"));
|
||||
throw "cuda_load_functions failed";
|
||||
}
|
||||
if (nvenc_load_functions(pp_nvenc_dl, NULL) < 0)
|
||||
{
|
||||
LOG_TRACE(std::string("nvenc_load_functions failed"));
|
||||
throw "nvenc_load_functions failed";
|
||||
}
|
||||
if (cuvid_load_functions(pp_cvdl, NULL) < 0)
|
||||
{
|
||||
LOG_TRACE(std::string("cuvid_load_functions failed"));
|
||||
throw "cuvid_load_functions failed";
|
||||
}
|
||||
}
|
||||
|
||||
void free_driver(CudaFunctions **pp_cuda_dl, NvencFunctions **pp_nvenc_dl,
|
||||
CuvidFunctions **pp_cvdl)
|
||||
{
|
||||
if (*pp_cvdl)
|
||||
{
|
||||
cuvid_free_functions(pp_cvdl);
|
||||
*pp_cvdl = NULL;
|
||||
}
|
||||
if (*pp_nvenc_dl)
|
||||
{
|
||||
nvenc_free_functions(pp_nvenc_dl);
|
||||
*pp_nvenc_dl = NULL;
|
||||
}
|
||||
if (*pp_cuda_dl)
|
||||
{
|
||||
cuda_free_functions(pp_cuda_dl);
|
||||
*pp_cuda_dl = NULL;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Check for NVIDIA driver support by loading CUDA libraries
|
||||
int linux_support_nv()
|
||||
{
|
||||
try
|
||||
// Try to load NVIDIA CUDA runtime library
|
||||
void *handle = dlopen("libcuda.so.1", RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
CudaFunctions *cuda_dl = NULL;
|
||||
NvencFunctions *nvenc_dl = NULL;
|
||||
CuvidFunctions *cvdl = NULL;
|
||||
load_driver(&cuda_dl, &nvenc_dl, &cvdl);
|
||||
free_driver(&cuda_dl, &nvenc_dl, &cvdl);
|
||||
return 0;
|
||||
handle = dlopen("libcuda.so", RTLD_LAZY);
|
||||
}
|
||||
catch (...)
|
||||
if (!handle)
|
||||
{
|
||||
LOG_TRACE(std::string("nvidia driver not support"));
|
||||
LOG_TRACE(std::string("NVIDIA: libcuda.so not found"));
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
dlclose(handle);
|
||||
|
||||
// Also check for nvenc library
|
||||
handle = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
handle = dlopen("libnvidia-encode.so", RTLD_LAZY);
|
||||
}
|
||||
if (!handle)
|
||||
{
|
||||
LOG_TRACE(std::string("NVIDIA: libnvidia-encode.so not found"));
|
||||
return -1;
|
||||
}
|
||||
dlclose(handle);
|
||||
|
||||
LOG_TRACE(std::string("NVIDIA: driver support detected"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int linux_support_amd()
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
#define AMF_DLL_NAME L"libamfrt64.so.1"
|
||||
#define AMF_DLL_NAMEA "libamfrt64.so.1"
|
||||
#else
|
||||
#define AMF_DLL_NAME L"libamfrt32.so.1"
|
||||
#define AMF_DLL_NAMEA "libamfrt32.so.1"
|
||||
#endif
|
||||
void *handle = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
|
||||
@@ -160,4 +127,4 @@ int linux_support_v4l2m2m() {
|
||||
|
||||
LOG_TRACE(std::string("V4L2 M2M: No M2M device found"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
#include <AVFoundation/AVFoundation.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreMedia/CoreMedia.h>
|
||||
#include <MacTypes.h>
|
||||
#include <VideoToolbox/VideoToolbox.h>
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <ratio>
|
||||
#include <sys/_types/_int32_t.h>
|
||||
#include <sys/event.h>
|
||||
#include <unistd.h>
|
||||
#include "../../log.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
// ---------------------- Core: More Robust Hardware Encoder Detection ----------------------
|
||||
static int32_t hasHardwareEncoder(bool h265) {
|
||||
CMVideoCodecType codecType = h265 ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264;
|
||||
|
||||
// ---------- Path A: Quick Query with Enable + Require ----------
|
||||
// Note: Require implies Enable, but setting both here makes it easier to bypass the strategy on some models that default to a software encoder.
|
||||
CFMutableDictionaryRef spec = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||
|
||||
CFDictionaryRef properties = NULL;
|
||||
CFStringRef outID = NULL;
|
||||
|
||||
// Use 1280x720 for capability detection to reduce the probability of "no hardware encoding" due to resolution/level issues.
|
||||
OSStatus result = VTCopySupportedPropertyDictionaryForEncoder(1280, 720, codecType, spec, &outID, &properties);
|
||||
|
||||
if (properties) CFRelease(properties);
|
||||
if (outID) CFRelease(outID);
|
||||
if (spec) CFRelease(spec);
|
||||
|
||||
if (result == noErr) {
|
||||
// Explicitly found an encoder that meets the "hardware-only" specification.
|
||||
return 1;
|
||||
}
|
||||
// Reaching here means either no encoder satisfying Require was found (common), or another error occurred.
|
||||
// For all failure cases, continue with the safer "session-level confirmation" path to avoid misjudgment.
|
||||
|
||||
// ---------- Path B: Create Session and Read UsingHardwareAcceleratedVideoEncoder ----------
|
||||
CFMutableDictionaryRef enableOnly = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionarySetValue(enableOnly, kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||
|
||||
VTCompressionSessionRef session = NULL;
|
||||
// Also use 1280x720 to reduce profile/level interference
|
||||
OSStatus st = VTCompressionSessionCreate(kCFAllocatorDefault,
|
||||
1280, 720, codecType,
|
||||
enableOnly, /* encoderSpecification */
|
||||
NULL, /* sourceImageBufferAttributes */
|
||||
NULL, /* compressedDataAllocator */
|
||||
NULL, /* outputCallback */
|
||||
NULL, /* outputRefCon */
|
||||
&session);
|
||||
if (enableOnly) CFRelease(enableOnly);
|
||||
|
||||
if (st != noErr || !session) {
|
||||
// Creation failed, considered no hardware available.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// First, explicitly prepare the encoding process to give VideoToolbox a chance to choose between software/hardware.
|
||||
OSStatus prepareStatus = VTCompressionSessionPrepareToEncodeFrames(session);
|
||||
if (prepareStatus != noErr) {
|
||||
VTCompressionSessionInvalidate(session);
|
||||
CFRelease(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Query the session's read-only property: whether it is using a hardware encoder.
|
||||
CFBooleanRef usingHW = NULL;
|
||||
st = VTSessionCopyProperty(session,
|
||||
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder,
|
||||
kCFAllocatorDefault,
|
||||
(void **)&usingHW);
|
||||
|
||||
Boolean isHW = (st == noErr && usingHW && CFBooleanGetValue(usingHW));
|
||||
|
||||
if (usingHW) CFRelease(usingHW);
|
||||
VTCompressionSessionInvalidate(session);
|
||||
CFRelease(session);
|
||||
|
||||
return isHW ? 1 : 0;
|
||||
}
|
||||
|
||||
// -------------- Your Public Interface: Unchanged ------------------
|
||||
extern "C" void checkVideoToolboxSupport(int32_t *h264Encoder, int32_t *h265Encoder, int32_t *h264Decoder, int32_t *h265Decoder) {
|
||||
// https://stackoverflow.com/questions/50956097/determine-if-ios-device-can-support-hevc-encoding
|
||||
*h264Encoder = 0; // H.264 encoder support is disabled due to frequent reliability issues (see encode.rs)
|
||||
*h265Encoder = hasHardwareEncoder(true);
|
||||
|
||||
*h264Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_H264);
|
||||
*h265Decoder = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
extern "C" uint64_t GetHwcodecGpuSignature() {
|
||||
int32_t h264Encoder = 0;
|
||||
int32_t h265Encoder = 0;
|
||||
int32_t h264Decoder = 0;
|
||||
int32_t h265Decoder = 0;
|
||||
checkVideoToolboxSupport(&h264Encoder, &h265Encoder, &h264Decoder, &h265Decoder);
|
||||
return (uint64_t)h264Encoder << 24 | (uint64_t)h265Encoder << 16 | (uint64_t)h264Decoder << 8 | (uint64_t)h265Decoder;
|
||||
}
|
||||
|
||||
static void *parent_death_monitor_thread(void *arg) {
|
||||
int kq = (intptr_t)arg;
|
||||
struct kevent events[1];
|
||||
|
||||
int ret = kevent(kq, NULL, 0, events, 1, NULL);
|
||||
|
||||
if (ret > 0) {
|
||||
// Parent process died, terminate this process
|
||||
LOG_INFO("Parent process died, terminating hwcodec check process");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C" int setup_parent_death_signal() {
|
||||
// On macOS, use kqueue to monitor parent process death
|
||||
pid_t parent_pid = getppid();
|
||||
int kq = kqueue();
|
||||
|
||||
if (kq == -1) {
|
||||
LOG_DEBUG("Failed to create kqueue for parent monitoring");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct kevent event;
|
||||
EV_SET(&event, parent_pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0,
|
||||
NULL);
|
||||
|
||||
int ret = kevent(kq, &event, 1, NULL, 0, NULL);
|
||||
|
||||
if (ret == -1) {
|
||||
LOG_ERROR("Failed to register parent death monitoring on macOS\n");
|
||||
close(kq);
|
||||
return -1;
|
||||
} else {
|
||||
|
||||
// Spawn a thread to monitor parent death
|
||||
pthread_t monitor_thread;
|
||||
ret = pthread_create(&monitor_thread, NULL, parent_death_monitor_thread,
|
||||
(void *)(intptr_t)kq);
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("Failed to create parent death monitor thread");
|
||||
close(kq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Detach the thread so it can run independently
|
||||
pthread_detach(monitor_thread);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user