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:
mofeng-git
2025-12-31 19:47:08 +08:00
parent a8a3b6c66b
commit d0e2e13b35
441 changed files with 467 additions and 143421 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}