mirror of
https://github.com/linyqh/NarratoAI.git
synced 2025-12-10 18:02:51 +00:00
1121 lines
40 KiB
Python
1121 lines
40 KiB
Python
"""
|
||
FFmpeg 工具模块 - 提供 FFmpeg 相关的工具函数,特别是硬件加速检测
|
||
优化多平台兼容性,支持渐进式降级和智能错误处理
|
||
"""
|
||
import os
|
||
import platform
|
||
import subprocess
|
||
import tempfile
|
||
from typing import Dict, List, Optional, Tuple, Union
|
||
from loguru import logger
|
||
|
||
# 全局变量,存储检测到的硬件加速信息
|
||
_FFMPEG_HW_ACCEL_INFO = {
|
||
"available": False,
|
||
"type": None,
|
||
"encoder": None,
|
||
"hwaccel_args": [],
|
||
"message": "",
|
||
"is_dedicated_gpu": False,
|
||
"fallback_available": False, # 是否有备用方案
|
||
"fallback_encoder": None, # 备用编码器
|
||
"platform": None, # 平台信息
|
||
"gpu_vendor": None, # GPU厂商
|
||
"tested_methods": [] # 已测试的方法
|
||
}
|
||
|
||
# 硬件加速优先级配置(按平台和GPU类型)
|
||
HWACCEL_PRIORITY = {
|
||
"windows": {
|
||
"nvidia": ["cuda", "nvenc", "d3d11va", "dxva2"],
|
||
"amd": ["d3d11va", "dxva2", "amf"], # 不再完全禁用AMD
|
||
"intel": ["qsv", "d3d11va", "dxva2"],
|
||
"unknown": ["d3d11va", "dxva2"]
|
||
},
|
||
"darwin": {
|
||
"apple": ["videotoolbox"],
|
||
"nvidia": ["cuda", "videotoolbox"],
|
||
"amd": ["videotoolbox"],
|
||
"intel": ["videotoolbox"],
|
||
"unknown": ["videotoolbox"]
|
||
},
|
||
"linux": {
|
||
"nvidia": ["cuda", "nvenc", "vaapi"],
|
||
"amd": ["vaapi", "amf"],
|
||
"intel": ["qsv", "vaapi"],
|
||
"unknown": ["vaapi"]
|
||
}
|
||
}
|
||
|
||
# 编码器映射
|
||
ENCODER_MAPPING = {
|
||
"cuda": "h264_nvenc",
|
||
"nvenc": "h264_nvenc",
|
||
"videotoolbox": "h264_videotoolbox",
|
||
"qsv": "h264_qsv",
|
||
"vaapi": "h264_vaapi",
|
||
"amf": "h264_amf",
|
||
"d3d11va": "libx264", # D3D11VA只用于解码
|
||
"dxva2": "libx264", # DXVA2只用于解码
|
||
"software": "libx264"
|
||
}
|
||
|
||
|
||
def get_null_input() -> str:
|
||
"""
|
||
获取平台特定的空输入文件路径
|
||
|
||
Returns:
|
||
str: 平台特定的空输入路径
|
||
"""
|
||
system = platform.system().lower()
|
||
if system == "windows":
|
||
return "NUL"
|
||
else:
|
||
return "/dev/null"
|
||
|
||
|
||
def create_test_video() -> str:
|
||
"""
|
||
创建一个临时的测试视频文件,用于硬件加速测试
|
||
|
||
Returns:
|
||
str: 临时测试视频文件路径
|
||
"""
|
||
try:
|
||
# 创建临时文件
|
||
temp_file = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
|
||
temp_path = temp_file.name
|
||
temp_file.close()
|
||
|
||
# 生成一个简单的测试视频(1秒,黑色画面)
|
||
cmd = [
|
||
'ffmpeg', '-y', '-f', 'lavfi', '-i', 'color=black:size=320x240:duration=1',
|
||
'-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-t', '1', temp_path
|
||
]
|
||
|
||
subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||
return temp_path
|
||
except Exception as e:
|
||
logger.debug(f"创建测试视频失败: {str(e)}")
|
||
return get_null_input()
|
||
|
||
|
||
def cleanup_test_video(path: str) -> None:
|
||
"""
|
||
清理测试视频文件
|
||
|
||
Args:
|
||
path: 测试视频文件路径
|
||
"""
|
||
try:
|
||
if path != get_null_input() and os.path.exists(path):
|
||
os.unlink(path)
|
||
except Exception as e:
|
||
logger.debug(f"清理测试视频失败: {str(e)}")
|
||
|
||
|
||
def check_ffmpeg_installation() -> bool:
|
||
"""
|
||
检查ffmpeg是否已安装
|
||
|
||
Returns:
|
||
bool: 如果安装则返回True,否则返回False
|
||
"""
|
||
try:
|
||
# 在Windows系统上使用UTF-8编码
|
||
is_windows = os.name == 'nt'
|
||
if is_windows:
|
||
subprocess.run(['ffmpeg', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', check=True)
|
||
else:
|
||
subprocess.run(['ffmpeg', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
||
return True
|
||
except (subprocess.SubprocessError, FileNotFoundError):
|
||
logger.error("ffmpeg未安装或不在系统PATH中,请安装ffmpeg")
|
||
return False
|
||
|
||
|
||
def detect_gpu_vendor() -> str:
|
||
"""
|
||
检测GPU厂商
|
||
|
||
Returns:
|
||
str: GPU厂商 (nvidia, amd, intel, apple, unknown)
|
||
"""
|
||
system = platform.system().lower()
|
||
|
||
try:
|
||
if system == "windows":
|
||
gpu_info = _get_windows_gpu_info().lower()
|
||
if 'nvidia' in gpu_info or 'geforce' in gpu_info or 'quadro' in gpu_info:
|
||
return "nvidia"
|
||
elif 'amd' in gpu_info or 'radeon' in gpu_info:
|
||
return "amd"
|
||
elif 'intel' in gpu_info:
|
||
return "intel"
|
||
elif system == "darwin":
|
||
# macOS上检查是否为Apple Silicon
|
||
if platform.machine().lower() in ['arm64', 'aarch64']:
|
||
return "apple"
|
||
else:
|
||
# Intel Mac,可能有独立显卡
|
||
gpu_info = _get_macos_gpu_info().lower()
|
||
if 'nvidia' in gpu_info:
|
||
return "nvidia"
|
||
elif 'amd' in gpu_info or 'radeon' in gpu_info:
|
||
return "amd"
|
||
else:
|
||
return "intel"
|
||
elif system == "linux":
|
||
gpu_info = _get_linux_gpu_info().lower()
|
||
if 'nvidia' in gpu_info:
|
||
return "nvidia"
|
||
elif 'amd' in gpu_info or 'radeon' in gpu_info:
|
||
return "amd"
|
||
elif 'intel' in gpu_info:
|
||
return "intel"
|
||
except Exception as e:
|
||
logger.debug(f"检测GPU厂商失败: {str(e)}")
|
||
|
||
return "unknown"
|
||
|
||
|
||
def test_hwaccel_method(method: str, test_input: str) -> bool:
|
||
"""
|
||
测试特定的硬件加速方法
|
||
|
||
Args:
|
||
method: 硬件加速方法名称
|
||
test_input: 测试输入文件路径
|
||
|
||
Returns:
|
||
bool: 是否支持该方法
|
||
"""
|
||
try:
|
||
# 构建测试命令
|
||
cmd = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
|
||
|
||
# 添加硬件加速参数
|
||
if method == "cuda":
|
||
cmd.extend(["-hwaccel", "cuda", "-hwaccel_output_format", "cuda"])
|
||
elif method == "nvenc":
|
||
cmd.extend(["-hwaccel", "cuda"])
|
||
elif method == "videotoolbox":
|
||
cmd.extend(["-hwaccel", "videotoolbox"])
|
||
elif method == "qsv":
|
||
cmd.extend(["-hwaccel", "qsv"])
|
||
elif method == "vaapi":
|
||
# 尝试找到VAAPI设备
|
||
render_device = _find_vaapi_device()
|
||
if render_device:
|
||
cmd.extend(["-hwaccel", "vaapi", "-vaapi_device", render_device])
|
||
else:
|
||
cmd.extend(["-hwaccel", "vaapi"])
|
||
elif method == "d3d11va":
|
||
cmd.extend(["-hwaccel", "d3d11va"])
|
||
elif method == "dxva2":
|
||
cmd.extend(["-hwaccel", "dxva2"])
|
||
elif method == "amf":
|
||
cmd.extend(["-hwaccel", "auto"]) # AMF通常通过auto检测
|
||
else:
|
||
return False
|
||
|
||
# 添加输入和输出
|
||
cmd.extend(["-i", test_input, "-f", "null", "-t", "0.1", "-"])
|
||
|
||
# 执行测试
|
||
result = subprocess.run(
|
||
cmd,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
check=False,
|
||
timeout=10 # 10秒超时
|
||
)
|
||
|
||
success = result.returncode == 0
|
||
if success:
|
||
logger.debug(f"硬件加速方法 {method} 测试成功")
|
||
else:
|
||
logger.debug(f"硬件加速方法 {method} 测试失败: {result.stderr[:200]}")
|
||
|
||
return success
|
||
|
||
except subprocess.TimeoutExpired:
|
||
logger.debug(f"硬件加速方法 {method} 测试超时")
|
||
return False
|
||
except Exception as e:
|
||
logger.debug(f"硬件加速方法 {method} 测试异常: {str(e)}")
|
||
return False
|
||
|
||
|
||
def detect_hardware_acceleration() -> Dict[str, Union[bool, str, List[str], None]]:
|
||
"""
|
||
检测系统可用的硬件加速器,使用渐进式检测和智能降级
|
||
|
||
Returns:
|
||
Dict: 包含硬件加速信息的字典
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
# 如果已经检测过,直接返回结果
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is not None:
|
||
return _FFMPEG_HW_ACCEL_INFO
|
||
|
||
# 检查ffmpeg是否已安装
|
||
if not check_ffmpeg_installation():
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "FFmpeg未安装或不在系统PATH中"
|
||
return _FFMPEG_HW_ACCEL_INFO
|
||
|
||
# 检测平台和GPU信息
|
||
system = platform.system().lower()
|
||
gpu_vendor = detect_gpu_vendor()
|
||
|
||
_FFMPEG_HW_ACCEL_INFO["platform"] = system
|
||
_FFMPEG_HW_ACCEL_INFO["gpu_vendor"] = gpu_vendor
|
||
|
||
logger.debug(f"检测硬件加速 - 平台: {system}, GPU厂商: {gpu_vendor}")
|
||
|
||
# 获取FFmpeg支持的硬件加速器列表
|
||
try:
|
||
hwaccels_cmd = subprocess.run(
|
||
['ffmpeg', '-hide_banner', '-hwaccels'],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False
|
||
)
|
||
supported_hwaccels = hwaccels_cmd.stdout.lower() if hwaccels_cmd.returncode == 0 else ""
|
||
logger.debug(f"FFmpeg支持的硬件加速器: {supported_hwaccels}")
|
||
except Exception as e:
|
||
logger.warning(f"获取FFmpeg硬件加速器列表失败: {str(e)}")
|
||
supported_hwaccels = ""
|
||
|
||
# 创建测试输入
|
||
test_input = create_test_video()
|
||
|
||
try:
|
||
# 根据平台和GPU厂商获取优先级列表
|
||
priority_list = HWACCEL_PRIORITY.get(system, {}).get(gpu_vendor, [])
|
||
if not priority_list:
|
||
priority_list = HWACCEL_PRIORITY.get(system, {}).get("unknown", [])
|
||
|
||
logger.debug(f"硬件加速测试优先级: {priority_list}")
|
||
|
||
# 按优先级测试硬件加速方法
|
||
for method in priority_list:
|
||
# 检查FFmpeg是否支持该方法
|
||
if method not in supported_hwaccels and method != "nvenc": # nvenc可能不在hwaccels列表中
|
||
logger.debug(f"跳过不支持的硬件加速方法: {method}")
|
||
continue
|
||
|
||
_FFMPEG_HW_ACCEL_INFO["tested_methods"].append(method)
|
||
|
||
if test_hwaccel_method(method, test_input):
|
||
# 找到可用的硬件加速方法
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = method
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = ENCODER_MAPPING.get(method, "libx264")
|
||
|
||
# 构建硬件加速参数
|
||
if method == "cuda":
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "cuda", "-hwaccel_output_format", "cuda"]
|
||
elif method == "nvenc":
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "cuda"]
|
||
elif method == "videotoolbox":
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "videotoolbox"]
|
||
elif method == "qsv":
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "qsv"]
|
||
elif method == "vaapi":
|
||
render_device = _find_vaapi_device()
|
||
if render_device:
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "vaapi", "-vaapi_device", render_device]
|
||
else:
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "vaapi"]
|
||
elif method in ["d3d11va", "dxva2"]:
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", method]
|
||
elif method == "amf":
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "auto"]
|
||
|
||
# 判断是否为独立GPU
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = gpu_vendor in ["nvidia", "amd"] or (gpu_vendor == "intel" and "arc" in _get_gpu_info().lower())
|
||
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = f"使用 {method} 硬件加速 ({gpu_vendor} GPU)"
|
||
logger.debug(f"硬件加速检测成功: {method} ({gpu_vendor})")
|
||
break
|
||
|
||
# 如果没有找到硬件加速,设置软件编码作为备用
|
||
if not _FFMPEG_HW_ACCEL_INFO["available"]:
|
||
_FFMPEG_HW_ACCEL_INFO["fallback_available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["fallback_encoder"] = "libx264"
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = f"未找到可用的硬件加速,将使用软件编码 (平台: {system}, GPU: {gpu_vendor})"
|
||
logger.debug("未检测到硬件加速,将使用软件编码")
|
||
|
||
finally:
|
||
# 清理测试文件
|
||
cleanup_test_video(test_input)
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO
|
||
|
||
|
||
def _get_gpu_info() -> str:
|
||
"""
|
||
获取GPU信息的统一接口
|
||
|
||
Returns:
|
||
str: GPU信息字符串
|
||
"""
|
||
system = platform.system().lower()
|
||
|
||
if system == "windows":
|
||
return _get_windows_gpu_info()
|
||
elif system == "darwin":
|
||
return _get_macos_gpu_info()
|
||
elif system == "linux":
|
||
return _get_linux_gpu_info()
|
||
else:
|
||
return "unknown"
|
||
|
||
|
||
def _get_macos_gpu_info() -> str:
|
||
"""
|
||
获取macOS系统的GPU信息
|
||
|
||
Returns:
|
||
str: GPU信息字符串
|
||
"""
|
||
try:
|
||
# 使用system_profiler获取显卡信息
|
||
result = subprocess.run(
|
||
['system_profiler', 'SPDisplaysDataType'],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False
|
||
)
|
||
if result.returncode == 0:
|
||
return result.stdout
|
||
|
||
# 备用方法:检查是否为Apple Silicon
|
||
if platform.machine().lower() in ['arm64', 'aarch64']:
|
||
return "Apple Silicon GPU"
|
||
else:
|
||
return "Intel Mac GPU"
|
||
except Exception as e:
|
||
logger.debug(f"获取macOS GPU信息失败: {str(e)}")
|
||
return "unknown"
|
||
|
||
|
||
def _find_vaapi_device() -> Optional[str]:
|
||
"""
|
||
查找可用的VAAPI设备
|
||
|
||
Returns:
|
||
Optional[str]: VAAPI设备路径,如果没有找到则返回None
|
||
"""
|
||
try:
|
||
# 常见的VAAPI设备路径
|
||
possible_devices = [
|
||
"/dev/dri/renderD128",
|
||
"/dev/dri/renderD129",
|
||
"/dev/dri/card0",
|
||
"/dev/dri/card1"
|
||
]
|
||
|
||
for device in possible_devices:
|
||
if os.path.exists(device):
|
||
# 测试设备是否可用
|
||
test_cmd = subprocess.run(
|
||
["ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-hwaccel", "vaapi", "-vaapi_device", device,
|
||
"-f", "lavfi", "-i", "color=black:size=64x64:duration=0.1",
|
||
"-f", "null", "-"],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
|
||
)
|
||
if test_cmd.returncode == 0:
|
||
logger.debug(f"找到可用的VAAPI设备: {device}")
|
||
return device
|
||
|
||
logger.debug("未找到可用的VAAPI设备")
|
||
return None
|
||
except Exception as e:
|
||
logger.debug(f"查找VAAPI设备失败: {str(e)}")
|
||
return None
|
||
|
||
|
||
def _detect_macos_acceleration(supported_hwaccels: str) -> None:
|
||
"""
|
||
检测macOS系统的硬件加速
|
||
|
||
Args:
|
||
supported_hwaccels: FFmpeg支持的硬件加速器列表
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
if 'videotoolbox' in supported_hwaccels:
|
||
# 测试videotoolbox
|
||
try:
|
||
test_cmd = subprocess.run(
|
||
["ffmpeg", "-hwaccel", "videotoolbox", "-i", "/dev/null", "-f", "null", "-"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||
)
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "videotoolbox"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_videotoolbox"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "videotoolbox"]
|
||
# macOS的Metal GPU加速通常是集成GPU
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = False
|
||
return
|
||
except Exception as e:
|
||
logger.debug(f"测试videotoolbox失败: {str(e)}")
|
||
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "macOS系统未检测到可用的videotoolbox硬件加速"
|
||
|
||
|
||
def _detect_windows_acceleration(supported_hwaccels: str) -> None:
|
||
"""
|
||
检测Windows系统的硬件加速 - 基于实际测试结果优化
|
||
|
||
重要发现:CUDA硬件解码在视频裁剪场景下会导致滤镜链错误,
|
||
因此优先使用纯NVENC编码器方案,既保证性能又确保兼容性。
|
||
|
||
Args:
|
||
supported_hwaccels: FFmpeg支持的硬件加速器列表
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
# 在Windows上,首先检查显卡信息
|
||
gpu_info = _get_windows_gpu_info()
|
||
logger.debug(f"Windows GPU信息: {gpu_info}")
|
||
|
||
# 检查是否为Intel集成显卡
|
||
is_intel_integrated = False
|
||
if 'intel' in gpu_info.lower() and ('hd graphics' in gpu_info.lower() or 'uhd graphics' in gpu_info.lower()):
|
||
logger.info("检测到Intel集成显卡")
|
||
is_intel_integrated = True
|
||
|
||
# 1. 优先检测NVIDIA硬件加速 - 基于实际测试的最佳方案
|
||
if 'nvidia' in gpu_info.lower() or 'geforce' in gpu_info.lower() or 'quadro' in gpu_info.lower():
|
||
logger.info("检测到NVIDIA显卡,开始测试硬件加速")
|
||
|
||
# 检查NVENC编码器是否可用
|
||
try:
|
||
encoders_cmd = subprocess.run(
|
||
["ffmpeg", "-hide_banner", "-encoders"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False
|
||
)
|
||
has_nvenc = "h264_nvenc" in encoders_cmd.stdout.lower()
|
||
logger.debug(f"NVENC编码器检测结果: {'可用' if has_nvenc else '不可用'}")
|
||
|
||
if has_nvenc:
|
||
# 优先方案:纯NVENC编码器(测试证明最兼容)
|
||
logger.debug("测试纯NVENC编码器(推荐方案,避免滤镜链问题)")
|
||
test_cmd = subprocess.run([
|
||
"ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-f", "lavfi", "-i", "testsrc=duration=0.1:size=640x480:rate=30",
|
||
"-c:v", "h264_nvenc", "-preset", "medium", "-cq", "23",
|
||
"-pix_fmt", "yuv420p", "-f", "null", "-"
|
||
], stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False)
|
||
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "nvenc" # 使用nvenc类型标识纯编码器
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_nvenc"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = [] # 不使用硬件解码参数
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "纯NVENC编码器(最佳兼容性)"
|
||
logger.info("✓ 纯NVENC编码器测试成功")
|
||
return
|
||
|
||
# 备用方案:如果需要的话,可以测试CUDA硬件解码(但不推荐用于视频裁剪)
|
||
if 'cuda' in supported_hwaccels:
|
||
logger.debug("测试CUDA硬件解码(仅用于非裁剪场景)")
|
||
test_cmd = subprocess.run([
|
||
"ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-hwaccel", "cuda", "-hwaccel_output_format", "cuda",
|
||
"-f", "lavfi", "-i", "testsrc=duration=0.1:size=640x480:rate=30",
|
||
"-c:v", "h264_nvenc", "-preset", "medium", "-cq", "23",
|
||
"-pix_fmt", "yuv420p", "-f", "null", "-"
|
||
], stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False)
|
||
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "cuda" # 保留cuda类型用于特殊场景
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_nvenc"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "cuda", "-hwaccel_output_format", "cuda"]
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "CUDA+NVENC(限特殊场景使用)"
|
||
_FFMPEG_HW_ACCEL_INFO["fallback_available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["fallback_encoder"] = "h264_nvenc"
|
||
logger.info("✓ CUDA+NVENC硬件加速测试成功(备用方案)")
|
||
return
|
||
|
||
except Exception as e:
|
||
logger.debug(f"NVIDIA硬件加速测试失败: {str(e)}")
|
||
|
||
# 2. 检测AMD硬件加速
|
||
if 'amd' in gpu_info.lower() or 'radeon' in gpu_info.lower():
|
||
logger.info("检测到AMD显卡,开始测试硬件加速")
|
||
|
||
# 检查AMF编码器是否可用
|
||
try:
|
||
encoders_cmd = subprocess.run(
|
||
["ffmpeg", "-hide_banner", "-encoders"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False
|
||
)
|
||
has_amf = "h264_amf" in encoders_cmd.stdout.lower()
|
||
logger.debug(f"AMF编码器检测结果: {'可用' if has_amf else '不可用'}")
|
||
|
||
if has_amf:
|
||
# 测试AMF编码器
|
||
logger.debug("测试AMF编码器")
|
||
test_cmd = subprocess.run([
|
||
"ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-f", "lavfi", "-i", "testsrc=duration=0.1:size=640x480:rate=30",
|
||
"-c:v", "h264_amf", "-quality", "balanced", "-qp_i", "23",
|
||
"-pix_fmt", "yuv420p", "-f", "null", "-"
|
||
], stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False)
|
||
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "amf"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_amf"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = []
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "AMD AMF编码器"
|
||
logger.info("✓ AMD AMF编码器测试成功")
|
||
return
|
||
|
||
except Exception as e:
|
||
logger.debug(f"AMD硬件加速测试失败: {str(e)}")
|
||
|
||
# 3. 检测Intel硬件加速
|
||
if 'intel' in gpu_info.lower() and 'qsv' in supported_hwaccels:
|
||
logger.info("检测到Intel显卡,开始测试硬件加速")
|
||
|
||
try:
|
||
encoders_cmd = subprocess.run(
|
||
["ffmpeg", "-hide_banner", "-encoders"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False
|
||
)
|
||
has_qsv = "h264_qsv" in encoders_cmd.stdout.lower()
|
||
logger.debug(f"QSV编码器检测结果: {'可用' if has_qsv else '不可用'}")
|
||
|
||
if has_qsv:
|
||
# 测试QSV编码器
|
||
logger.debug("测试QSV编码器")
|
||
test_cmd = subprocess.run([
|
||
"ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-f", "lavfi", "-i", "testsrc=duration=0.1:size=640x480:rate=30",
|
||
"-c:v", "h264_qsv", "-preset", "medium", "-global_quality", "23",
|
||
"-pix_fmt", "yuv420p", "-f", "null", "-"
|
||
], stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False)
|
||
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "qsv"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_qsv"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = []
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = not is_intel_integrated
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "Intel QSV编码器"
|
||
logger.info("✓ Intel QSV编码器测试成功")
|
||
return
|
||
|
||
except Exception as e:
|
||
logger.debug(f"Intel硬件加速测试失败: {str(e)}")
|
||
|
||
# 4. 如果没有硬件编码器,使用软件编码
|
||
logger.info("未检测到可用的硬件编码器,使用软件编码")
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = False
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "software"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "libx264"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = []
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = False
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "使用软件编码"
|
||
|
||
|
||
def _detect_linux_acceleration(supported_hwaccels: str) -> None:
|
||
"""
|
||
检测Linux系统的硬件加速
|
||
|
||
Args:
|
||
supported_hwaccels: FFmpeg支持的硬件加速器列表
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
# 获取Linux显卡信息
|
||
gpu_info = _get_linux_gpu_info()
|
||
is_nvidia = 'nvidia' in gpu_info.lower()
|
||
is_intel = 'intel' in gpu_info.lower()
|
||
is_amd = 'amd' in gpu_info.lower() or 'radeon' in gpu_info.lower()
|
||
|
||
# 检测NVIDIA CUDA支持
|
||
if 'cuda' in supported_hwaccels and is_nvidia:
|
||
try:
|
||
test_cmd = subprocess.run(
|
||
["ffmpeg", "-hwaccel", "cuda", "-i", "/dev/null", "-f", "null", "-"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||
)
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "cuda"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_nvenc"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "cuda"]
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True
|
||
return
|
||
except Exception as e:
|
||
logger.debug(f"测试CUDA失败: {str(e)}")
|
||
|
||
# 检测VAAPI支持
|
||
if 'vaapi' in supported_hwaccels:
|
||
# 检查是否存在渲染设备
|
||
render_devices = ['/dev/dri/renderD128', '/dev/dri/renderD129']
|
||
render_device = None
|
||
for device in render_devices:
|
||
if os.path.exists(device):
|
||
render_device = device
|
||
break
|
||
|
||
if render_device:
|
||
try:
|
||
test_cmd = subprocess.run(
|
||
["ffmpeg", "-hwaccel", "vaapi", "-vaapi_device", render_device,
|
||
"-i", "/dev/null", "-f", "null", "-"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||
)
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "vaapi"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_vaapi"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "vaapi", "-vaapi_device", render_device]
|
||
# 根据显卡类型判断是否为独立显卡
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = is_nvidia or (is_amd and not is_intel)
|
||
return
|
||
except Exception as e:
|
||
logger.debug(f"测试VAAPI失败: {str(e)}")
|
||
|
||
# 检测Intel QSV支持
|
||
if 'qsv' in supported_hwaccels and is_intel:
|
||
try:
|
||
test_cmd = subprocess.run(
|
||
["ffmpeg", "-hwaccel", "qsv", "-i", "/dev/null", "-f", "null", "-"],
|
||
stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False
|
||
)
|
||
if test_cmd.returncode == 0:
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "qsv"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_qsv"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = ["-hwaccel", "qsv"]
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = False # Intel QSV通常是集成GPU
|
||
return
|
||
except Exception as e:
|
||
logger.debug(f"测试QSV失败: {str(e)}")
|
||
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = f"Linux系统未检测到可用的硬件加速,显卡信息: {gpu_info}"
|
||
|
||
|
||
def _get_windows_gpu_info() -> str:
|
||
"""
|
||
获取Windows系统的显卡信息
|
||
|
||
Returns:
|
||
str: 显卡信息字符串
|
||
"""
|
||
try:
|
||
# 使用PowerShell获取更可靠的显卡信息,并使用UTF-8编码
|
||
gpu_info = subprocess.run(
|
||
['powershell', '-Command', "Get-WmiObject Win32_VideoController | Select-Object Name | Format-List"],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', text=True, check=False
|
||
)
|
||
|
||
# 如果PowerShell失败,尝试使用wmic
|
||
if not gpu_info.stdout.strip():
|
||
gpu_info = subprocess.run(
|
||
['wmic', 'path', 'win32_VideoController', 'get', 'name'],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', text=True, check=False
|
||
)
|
||
|
||
# 记录详细的显卡信息以便调试
|
||
logger.debug(f"Windows显卡信息: {gpu_info.stdout}")
|
||
return gpu_info.stdout
|
||
except Exception as e:
|
||
logger.warning(f"获取Windows显卡信息失败: {str(e)}")
|
||
return "Unknown GPU"
|
||
|
||
|
||
def _get_linux_gpu_info() -> str:
|
||
"""
|
||
获取Linux系统的显卡信息
|
||
|
||
Returns:
|
||
str: 显卡信息字符串
|
||
"""
|
||
try:
|
||
# 尝试使用lspci命令
|
||
gpu_info = subprocess.run(
|
||
['lspci', '-v', '-nn', '|', 'grep', '-i', 'vga\\|display'],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=False
|
||
)
|
||
if gpu_info.stdout:
|
||
return gpu_info.stdout
|
||
|
||
# 如果lspci命令失败,尝试使用glxinfo
|
||
gpu_info = subprocess.run(
|
||
['glxinfo', '|', 'grep', '-i', 'vendor\\|renderer'],
|
||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=False
|
||
)
|
||
if gpu_info.stdout:
|
||
return gpu_info.stdout
|
||
|
||
return "Unknown GPU"
|
||
except Exception as e:
|
||
logger.warning(f"获取Linux显卡信息失败: {str(e)}")
|
||
return "Unknown GPU"
|
||
|
||
|
||
def get_ffmpeg_hwaccel_args() -> List[str]:
|
||
"""
|
||
获取FFmpeg硬件加速参数
|
||
|
||
Returns:
|
||
List[str]: FFmpeg硬件加速参数列表
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO["hwaccel_args"]
|
||
|
||
|
||
def get_ffmpeg_hwaccel_type() -> Optional[str]:
|
||
"""
|
||
获取FFmpeg硬件加速类型
|
||
|
||
Returns:
|
||
Optional[str]: 硬件加速类型,如果不支持则返回None
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO["type"] if _FFMPEG_HW_ACCEL_INFO["available"] else None
|
||
|
||
|
||
def get_ffmpeg_hwaccel_encoder() -> Optional[str]:
|
||
"""
|
||
获取FFmpeg硬件加速编码器
|
||
|
||
Returns:
|
||
Optional[str]: 硬件加速编码器,如果不支持则返回None
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO["encoder"] if _FFMPEG_HW_ACCEL_INFO["available"] else None
|
||
|
||
|
||
def get_ffmpeg_hwaccel_info() -> Dict[str, Union[bool, str, List[str], None]]:
|
||
"""
|
||
获取FFmpeg硬件加速信息
|
||
|
||
Returns:
|
||
Dict: 包含硬件加速信息的字典
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO
|
||
|
||
|
||
def is_ffmpeg_hwaccel_available() -> bool:
|
||
"""
|
||
检查是否有可用的FFmpeg硬件加速
|
||
|
||
Returns:
|
||
bool: 如果有可用的硬件加速则返回True,否则返回False
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO["available"]
|
||
|
||
|
||
def is_dedicated_gpu() -> bool:
|
||
"""
|
||
检查是否使用独立显卡进行硬件加速
|
||
|
||
Returns:
|
||
bool: 如果使用独立显卡则返回True,否则返回False
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
return _FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"]
|
||
|
||
|
||
def get_optimal_ffmpeg_encoder() -> str:
|
||
"""
|
||
获取最优的FFmpeg编码器
|
||
|
||
Returns:
|
||
str: 编码器名称
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
if _FFMPEG_HW_ACCEL_INFO["available"]:
|
||
return _FFMPEG_HW_ACCEL_INFO["encoder"]
|
||
elif _FFMPEG_HW_ACCEL_INFO["fallback_available"]:
|
||
return _FFMPEG_HW_ACCEL_INFO["fallback_encoder"]
|
||
else:
|
||
return "libx264" # 默认软件编码器
|
||
|
||
|
||
def get_ffmpeg_command_with_hwaccel(input_path: str, output_path: str, **kwargs) -> List[str]:
|
||
"""
|
||
生成带有硬件加速的FFmpeg命令
|
||
|
||
Args:
|
||
input_path: 输入文件路径
|
||
output_path: 输出文件路径
|
||
**kwargs: 其他FFmpeg参数
|
||
|
||
Returns:
|
||
List[str]: FFmpeg命令列表
|
||
"""
|
||
# 如果还没有检测过,先进行检测
|
||
if _FFMPEG_HW_ACCEL_INFO["type"] is None:
|
||
detect_hardware_acceleration()
|
||
|
||
cmd = ["ffmpeg", "-y"]
|
||
|
||
# 添加硬件加速参数
|
||
if _FFMPEG_HW_ACCEL_INFO["available"]:
|
||
cmd.extend(_FFMPEG_HW_ACCEL_INFO["hwaccel_args"])
|
||
|
||
# 添加输入文件
|
||
cmd.extend(["-i", input_path])
|
||
|
||
# 添加编码器
|
||
encoder = get_optimal_ffmpeg_encoder()
|
||
cmd.extend(["-c:v", encoder])
|
||
|
||
# 添加其他参数
|
||
for key, value in kwargs.items():
|
||
if key.startswith("_"): # 跳过内部参数
|
||
continue
|
||
if isinstance(value, list):
|
||
cmd.extend(value)
|
||
else:
|
||
cmd.extend([f"-{key}", str(value)])
|
||
|
||
# 添加输出文件
|
||
cmd.append(output_path)
|
||
|
||
return cmd
|
||
|
||
|
||
def test_ffmpeg_compatibility() -> Dict[str, any]:
|
||
"""
|
||
测试FFmpeg兼容性并返回详细报告
|
||
|
||
Returns:
|
||
Dict: 兼容性测试报告
|
||
"""
|
||
report = {
|
||
"ffmpeg_installed": False,
|
||
"platform": platform.system().lower(),
|
||
"gpu_vendor": "unknown",
|
||
"hardware_acceleration": {
|
||
"available": False,
|
||
"type": None,
|
||
"encoder": None,
|
||
"tested_methods": []
|
||
},
|
||
"software_fallback": {
|
||
"available": False,
|
||
"encoder": "libx264"
|
||
},
|
||
"recommendations": []
|
||
}
|
||
|
||
# 检查FFmpeg安装
|
||
report["ffmpeg_installed"] = check_ffmpeg_installation()
|
||
if not report["ffmpeg_installed"]:
|
||
report["recommendations"].append("请安装FFmpeg并确保其在系统PATH中")
|
||
return report
|
||
|
||
# 检测硬件加速
|
||
hwaccel_info = detect_hardware_acceleration()
|
||
report["gpu_vendor"] = hwaccel_info.get("gpu_vendor", "unknown")
|
||
report["hardware_acceleration"]["available"] = hwaccel_info.get("available", False)
|
||
report["hardware_acceleration"]["type"] = hwaccel_info.get("type")
|
||
report["hardware_acceleration"]["encoder"] = hwaccel_info.get("encoder")
|
||
report["hardware_acceleration"]["tested_methods"] = hwaccel_info.get("tested_methods", [])
|
||
|
||
# 检查软件备用方案
|
||
report["software_fallback"]["available"] = hwaccel_info.get("fallback_available", True)
|
||
report["software_fallback"]["encoder"] = hwaccel_info.get("fallback_encoder", "libx264")
|
||
|
||
# 生成建议
|
||
if not report["hardware_acceleration"]["available"]:
|
||
if report["gpu_vendor"] == "nvidia":
|
||
report["recommendations"].append("建议安装NVIDIA驱动和CUDA工具包以启用硬件加速")
|
||
elif report["gpu_vendor"] == "amd":
|
||
report["recommendations"].append("AMD显卡硬件加速支持有限,建议使用软件编码")
|
||
elif report["gpu_vendor"] == "intel":
|
||
report["recommendations"].append("建议更新Intel显卡驱动以启用QSV硬件加速")
|
||
else:
|
||
report["recommendations"].append("未检测到支持的GPU,将使用软件编码")
|
||
|
||
return report
|
||
|
||
|
||
def force_software_encoding() -> None:
|
||
"""
|
||
强制使用软件编码,禁用硬件加速
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
_FFMPEG_HW_ACCEL_INFO.update({
|
||
"available": False,
|
||
"type": "software",
|
||
"encoder": "libx264",
|
||
"hwaccel_args": [],
|
||
"message": "强制使用软件编码",
|
||
"is_dedicated_gpu": False,
|
||
"fallback_available": True,
|
||
"fallback_encoder": "libx264"
|
||
})
|
||
|
||
logger.info("已强制切换到软件编码模式")
|
||
|
||
|
||
def reset_hwaccel_detection() -> None:
|
||
"""
|
||
重置硬件加速检测结果,强制重新检测
|
||
|
||
这在以下情况下很有用:
|
||
1. 驱动程序更新后
|
||
2. 系统配置改变后
|
||
3. 需要重新测试硬件加速时
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
logger.info("🔄 重置硬件加速检测,将重新检测...")
|
||
_FFMPEG_HW_ACCEL_INFO = {
|
||
"available": False,
|
||
"type": None,
|
||
"encoder": None,
|
||
"hwaccel_args": [],
|
||
"message": "",
|
||
"is_dedicated_gpu": False,
|
||
"fallback_available": False,
|
||
"fallback_encoder": None,
|
||
"platform": None,
|
||
"gpu_vendor": None,
|
||
"tested_methods": []
|
||
}
|
||
|
||
|
||
def test_nvenc_directly() -> bool:
|
||
"""
|
||
直接测试NVENC编码器是否可用(无硬件解码)
|
||
|
||
Returns:
|
||
bool: NVENC是否可用
|
||
"""
|
||
try:
|
||
logger.info("🧪 直接测试NVENC编码器...")
|
||
|
||
# 测试纯NVENC编码器
|
||
test_cmd = subprocess.run([
|
||
"ffmpeg", "-hide_banner", "-loglevel", "error",
|
||
"-f", "lavfi", "-i", "testsrc=duration=1:size=640x480:rate=30",
|
||
"-c:v", "h264_nvenc", "-preset", "fast", "-profile:v", "main",
|
||
"-pix_fmt", "yuv420p", "-t", "1", "-f", "null", "-"
|
||
], stderr=subprocess.PIPE, stdout=subprocess.PIPE,
|
||
encoding='utf-8', text=True, check=False)
|
||
|
||
if test_cmd.returncode == 0:
|
||
logger.info("✅ NVENC编码器测试成功!")
|
||
return True
|
||
else:
|
||
logger.warning(f"❌ NVENC编码器测试失败: {test_cmd.stderr}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"NVENC测试异常: {str(e)}")
|
||
return False
|
||
|
||
|
||
def force_use_nvenc_pure() -> None:
|
||
"""
|
||
强制使用纯NVENC编码器模式
|
||
|
||
当自动检测失败但你确定NVENC可用时使用
|
||
"""
|
||
global _FFMPEG_HW_ACCEL_INFO
|
||
|
||
logger.info("🎯 强制启用纯NVENC编码器模式...")
|
||
|
||
# 先测试NVENC是否真的可用
|
||
if test_nvenc_directly():
|
||
_FFMPEG_HW_ACCEL_INFO["available"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["type"] = "nvenc_pure"
|
||
_FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_nvenc"
|
||
_FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = []
|
||
_FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True
|
||
_FFMPEG_HW_ACCEL_INFO["message"] = "强制启用纯NVENC编码器"
|
||
logger.info("✅ 已强制启用纯NVENC编码器模式")
|
||
else:
|
||
logger.error("❌ NVENC编码器不可用,无法强制启用")
|
||
|
||
|
||
def get_hwaccel_status() -> Dict[str, any]:
|
||
"""
|
||
获取当前硬件加速状态的详细信息
|
||
|
||
Returns:
|
||
Dict: 硬件加速状态信息
|
||
"""
|
||
hwaccel_info = get_ffmpeg_hwaccel_info()
|
||
|
||
status = {
|
||
"available": hwaccel_info.get("available", False),
|
||
"type": hwaccel_info.get("type", "software"),
|
||
"encoder": hwaccel_info.get("encoder", "libx264"),
|
||
"message": hwaccel_info.get("message", ""),
|
||
"is_dedicated_gpu": hwaccel_info.get("is_dedicated_gpu", False),
|
||
"platform": platform.system(),
|
||
"gpu_vendor": detect_gpu_vendor(),
|
||
"ffmpeg_available": check_ffmpeg_installation()
|
||
}
|
||
|
||
return status
|
||
|
||
|
||
# 自动重置检测(在模块导入时执行)
|
||
def _auto_reset_on_import():
|
||
"""模块导入时自动重置硬件加速检测"""
|
||
try:
|
||
# 只在平台真正改变时才重置,而不是初始化时
|
||
current_platform = platform.system()
|
||
cached_platform = _FFMPEG_HW_ACCEL_INFO.get("platform")
|
||
|
||
# 只有当已经有缓存的平台信息,且平台改变了,才需要重置
|
||
if cached_platform is not None and cached_platform != current_platform:
|
||
reset_hwaccel_detection()
|
||
except Exception as e:
|
||
logger.debug(f"自动重置检测失败: {str(e)}")
|
||
|
||
# 执行自动重置
|
||
_auto_reset_on_import()
|