From 8fda320d50734b0342645cb2a5bb3b876ff7ccd8 Mon Sep 17 00:00:00 2001 From: linyq Date: Mon, 19 May 2025 02:50:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20ffmpeg=20=E7=A1=AC?= =?UTF-8?q?=E4=BB=B6=E5=8A=A0=E9=80=9F=20=E7=8B=AC=E6=98=BE=20=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/merger_video.py | 31 ++++++++---- app/utils/ffmpeg_utils.py | 93 +++++++++++++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 16 deletions(-) diff --git a/app/services/merger_video.py b/app/services/merger_video.py index af2c526..6d688bf 100644 --- a/app/services/merger_video.py +++ b/app/services/merger_video.py @@ -216,21 +216,32 @@ def process_single_video( use_software_encoder = True if hwaccel: - if hwaccel == 'cuda' or hwaccel == 'nvenc': + # 获取硬件加速类型和编码器信息 + hwaccel_type = ffmpeg_utils.get_ffmpeg_hwaccel_type() + hwaccel_encoder = ffmpeg_utils.get_ffmpeg_hwaccel_encoder() + + if hwaccel_type == 'cuda' or hwaccel_type == 'nvenc': try: - # 在使用NVIDIA编码器前先检查其可用性 - subprocess.run(['ffmpeg', '-hide_banner', '-encoders'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - command.extend(['-c:v', 'h264_nvenc', '-preset', 'p4', '-profile:v', 'high']) - use_software_encoder = False - except Exception: - logger.warning("NVENC编码器不可用,将使用软件编码") - elif hwaccel == 'qsv' and not is_windows: # 在Windows上避免使用QSV + # 检查NVENC编码器是否可用 + encoders_cmd = subprocess.run( + ["ffmpeg", "-hide_banner", "-encoders"], + stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False + ) + + if "h264_nvenc" in encoders_cmd.stdout.lower(): + command.extend(['-c:v', 'h264_nvenc', '-preset', 'p4', '-profile:v', 'high']) + use_software_encoder = False + else: + logger.warning("NVENC编码器不可用,将使用软件编码") + except Exception as e: + logger.warning(f"NVENC编码器检测失败: {str(e)},将使用软件编码") + elif hwaccel_type == 'qsv': command.extend(['-c:v', 'h264_qsv', '-preset', 'medium']) use_software_encoder = False - elif hwaccel == 'videotoolbox': # macOS + elif hwaccel_type == 'videotoolbox': # macOS command.extend(['-c:v', 'h264_videotoolbox', '-profile:v', 'high']) use_software_encoder = False - elif hwaccel == 'vaapi' and not is_windows: # Linux VA-API + elif hwaccel_type == 'vaapi': # Linux VA-API command.extend(['-c:v', 'h264_vaapi', '-profile', '100']) use_software_encoder = False diff --git a/app/utils/ffmpeg_utils.py b/app/utils/ffmpeg_utils.py index 59c7321..0425054 100644 --- a/app/utils/ffmpeg_utils.py +++ b/app/utils/ffmpeg_utils.py @@ -138,18 +138,48 @@ def _detect_windows_acceleration(supported_hwaccels: str) -> None: # 检测NVIDIA CUDA支持 if 'cuda' in supported_hwaccels and 'nvidia' in gpu_info.lower(): + # 添加调试日志 + logger.debug(f"Windows检测到NVIDIA显卡,尝试CUDA加速") try: - test_cmd = subprocess.run( - ["ffmpeg", "-hwaccel", "cuda", "-i", "/dev/null", "-f", "null", "-"], + # 先检查NVENC编码器是否可用 + encoders_cmd = subprocess.run( + ["ffmpeg", "-hide_banner", "-encoders"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False ) - if test_cmd.returncode == 0: + has_nvenc = "h264_nvenc" in encoders_cmd.stdout.lower() + logger.debug(f"NVENC编码器检测结果: {'可用' if has_nvenc else '不可用'}") + + # 测试CUDA硬件加速 + test_cmd = subprocess.run( + ["ffmpeg", "-hwaccel", "cuda", "-i", "NUL", "-f", "null", "-t", "0.1", "-"], + stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False + ) + + # 记录详细的返回信息以便调试 + logger.debug(f"CUDA测试返回码: {test_cmd.returncode}") + logger.debug(f"CUDA测试错误输出: {test_cmd.stderr[:200]}..." if len(test_cmd.stderr) > 200 else f"CUDA测试错误输出: {test_cmd.stderr}") + + if test_cmd.returncode == 0 or has_nvenc: _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 + + # 如果上面的测试失败,尝试另一种方式 + test_cmd2 = subprocess.run( + ["ffmpeg", "-hide_banner", "-loglevel", "error", "-hwaccel", "cuda", "-hwaccel_output_format", "cuda", "-i", "NUL", "-f", "null", "-t", "0.1", "-"], + stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False + ) + + if test_cmd2.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", "-hwaccel_output_format", "cuda"] + _FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True + return except Exception as e: logger.debug(f"测试CUDA失败: {str(e)}") @@ -172,11 +202,16 @@ def _detect_windows_acceleration(supported_hwaccels: str) -> None: # 检测D3D11VA支持 if 'd3d11va' in supported_hwaccels: + logger.debug("Windows尝试D3D11VA加速") try: test_cmd = subprocess.run( - ["ffmpeg", "-hwaccel", "d3d11va", "-i", "/dev/null", "-f", "null", "-"], + ["ffmpeg", "-hwaccel", "d3d11va", "-i", "NUL", "-f", "null", "-t", "0.1", "-"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False ) + + # 记录详细的返回信息以便调试 + logger.debug(f"D3D11VA测试返回码: {test_cmd.returncode}") + if test_cmd.returncode == 0: _FFMPEG_HW_ACCEL_INFO["available"] = True _FFMPEG_HW_ACCEL_INFO["type"] = "d3d11va" @@ -189,11 +224,16 @@ def _detect_windows_acceleration(supported_hwaccels: str) -> None: # 检测DXVA2支持 if 'dxva2' in supported_hwaccels: + logger.debug("Windows尝试DXVA2加速") try: test_cmd = subprocess.run( - ["ffmpeg", "-hwaccel", "dxva2", "-i", "/dev/null", "-f", "null", "-"], + ["ffmpeg", "-hwaccel", "dxva2", "-i", "NUL", "-f", "null", "-t", "0.1", "-"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False ) + + # 记录详细的返回信息以便调试 + logger.debug(f"DXVA2测试返回码: {test_cmd.returncode}") + if test_cmd.returncode == 0: _FFMPEG_HW_ACCEL_INFO["available"] = True _FFMPEG_HW_ACCEL_INFO["type"] = "dxva2" @@ -204,6 +244,36 @@ def _detect_windows_acceleration(supported_hwaccels: str) -> None: except Exception as e: logger.debug(f"测试DXVA2失败: {str(e)}") + # 如果检测到NVIDIA显卡但前面的测试都失败,尝试直接使用NVENC编码器 + if 'nvidia' in gpu_info.lower(): + logger.debug("Windows检测到NVIDIA显卡,尝试直接使用NVENC编码器") + try: + # 检查NVENC编码器是否可用 + encoders_cmd = subprocess.run( + ["ffmpeg", "-hide_banner", "-encoders"], + stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False + ) + + if "h264_nvenc" in encoders_cmd.stdout.lower(): + logger.debug("NVENC编码器可用,尝试直接使用") + # 测试NVENC编码器 + test_cmd = subprocess.run( + ["ffmpeg", "-f", "lavfi", "-i", "color=c=black:s=640x360:r=30", "-c:v", "h264_nvenc", "-t", "0.1", "-f", "null", "-"], + stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True, check=False + ) + + logger.debug(f"NVENC编码器测试返回码: {test_cmd.returncode}") + + if test_cmd.returncode == 0: + _FFMPEG_HW_ACCEL_INFO["available"] = True + _FFMPEG_HW_ACCEL_INFO["type"] = "nvenc" + _FFMPEG_HW_ACCEL_INFO["encoder"] = "h264_nvenc" + _FFMPEG_HW_ACCEL_INFO["hwaccel_args"] = [] # 不使用hwaccel参数,直接使用编码器 + _FFMPEG_HW_ACCEL_INFO["is_dedicated_gpu"] = True + return + except Exception as e: + logger.debug(f"测试NVENC编码器失败: {str(e)}") + _FFMPEG_HW_ACCEL_INFO["message"] = f"Windows系统未检测到可用的硬件加速,显卡信息: {gpu_info}" @@ -295,10 +365,21 @@ def _get_windows_gpu_info() -> str: str: 显卡信息字符串 """ try: + # 使用PowerShell获取更可靠的显卡信息 gpu_info = subprocess.run( - ['wmic', 'path', 'win32_VideoController', 'get', 'name'], + ['powershell', '-Command', "Get-WmiObject Win32_VideoController | Select-Object Name | Format-List"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, 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, 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)}")