mirror of
https://github.com/pacnpal/Pac-cogs.git
synced 2025-12-20 02:41:06 -05:00
Enhanced FFmpeg Integration:
Added robust error handling and logging Improved binary verification and initialization Added proper GPU detection and hardware acceleration Optimized encoding parameters for different content types Improved File Operations: Added retry mechanisms for file operations Enhanced temporary directory management Improved cleanup of failed downloads Added proper permission handling Enhanced Queue Management: Fixed queue manager initialization Added better error recovery Improved status tracking and logging Enhanced cleanup of failed items Better Error Handling: Added comprehensive exception hierarchy Improved error logging and reporting Added fallback mechanisms for failures Enhanced error recovery strategies
This commit is contained in:
@@ -1,130 +1,271 @@
|
||||
"""GPU detection functionality for FFmpeg"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import logging
|
||||
import platform
|
||||
import re
|
||||
from typing import Dict, List
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
logger = logging.getLogger("VideoArchiver")
|
||||
|
||||
class GPUDetector:
|
||||
def __init__(self, ffmpeg_path: Path):
|
||||
self.ffmpeg_path = ffmpeg_path
|
||||
"""Initialize GPU detector
|
||||
|
||||
Args:
|
||||
ffmpeg_path: Path to FFmpeg binary
|
||||
"""
|
||||
self.ffmpeg_path = Path(ffmpeg_path)
|
||||
if not self.ffmpeg_path.exists():
|
||||
raise FileNotFoundError(f"FFmpeg not found at {self.ffmpeg_path}")
|
||||
|
||||
def detect_gpu(self) -> Dict[str, bool]:
|
||||
"""Detect available GPU and its capabilities"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False, "arm": False}
|
||||
"""Detect available GPU acceleration support
|
||||
|
||||
Returns:
|
||||
Dict containing boolean flags for each GPU type
|
||||
"""
|
||||
gpu_info = {
|
||||
"nvidia": False,
|
||||
"amd": False,
|
||||
"intel": False
|
||||
}
|
||||
|
||||
try:
|
||||
if os.name == "posix": # Linux/Unix
|
||||
gpu_info.update(self._detect_linux_gpu())
|
||||
elif os.name == "nt": # Windows
|
||||
# Check system-specific GPU detection first
|
||||
system = platform.system().lower()
|
||||
if system == "windows":
|
||||
gpu_info.update(self._detect_windows_gpu())
|
||||
elif system == "linux":
|
||||
gpu_info.update(self._detect_linux_gpu())
|
||||
elif system == "darwin":
|
||||
gpu_info.update(self._detect_macos_gpu())
|
||||
|
||||
# Verify GPU support in FFmpeg
|
||||
gpu_info.update(self._verify_ffmpeg_gpu_support())
|
||||
|
||||
# Log detection results
|
||||
detected_gpus = [name for name, detected in gpu_info.items() if detected]
|
||||
if detected_gpus:
|
||||
logger.info(f"Detected GPUs: {', '.join(detected_gpus)}")
|
||||
else:
|
||||
logger.info("No GPU acceleration support detected")
|
||||
|
||||
return gpu_info
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"GPU detection failed: {str(e)}")
|
||||
|
||||
return gpu_info
|
||||
|
||||
def _test_encoder(self, encoder: str) -> bool:
|
||||
"""Test if a specific encoder works"""
|
||||
try:
|
||||
test_cmd = [
|
||||
str(self.ffmpeg_path),
|
||||
"-f", "lavfi",
|
||||
"-i", "testsrc=duration=1:size=1280x720:rate=30",
|
||||
"-c:v", encoder,
|
||||
"-f", "null",
|
||||
"-"
|
||||
]
|
||||
result = subprocess.run(
|
||||
test_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=5
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _detect_linux_gpu(self) -> Dict[str, bool]:
|
||||
"""Detect GPUs on Linux systems"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False, "arm": False}
|
||||
|
||||
# Check for NVIDIA GPU
|
||||
try:
|
||||
nvidia_smi = subprocess.run(
|
||||
["nvidia-smi", "-q", "-d", "ENCODER"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=5
|
||||
)
|
||||
if nvidia_smi.returncode == 0 and b"Encoder" in nvidia_smi.stdout:
|
||||
gpu_info["nvidia"] = self._test_encoder("h264_nvenc")
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
# Check for AMD GPU
|
||||
try:
|
||||
if os.path.exists("/dev/dri/renderD128"):
|
||||
with open("/sys/class/drm/renderD128/device/vendor", "r") as f:
|
||||
vendor = f.read().strip()
|
||||
if vendor == "0x1002": # AMD vendor ID
|
||||
gpu_info["amd"] = self._test_encoder("h264_amf")
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
|
||||
# Check for Intel GPU
|
||||
try:
|
||||
lspci = subprocess.run(
|
||||
["lspci"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=5
|
||||
)
|
||||
output = lspci.stdout.decode().lower()
|
||||
if "intel" in output and ("vga" in output or "display" in output):
|
||||
gpu_info["intel"] = self._test_encoder("h264_qsv")
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
# Check for ARM GPU
|
||||
if os.uname().machine.startswith(("aarch64", "armv7")):
|
||||
gpu_info["arm"] = True
|
||||
|
||||
return gpu_info
|
||||
logger.error(f"Error during GPU detection: {str(e)}")
|
||||
return {"nvidia": False, "amd": False, "intel": False}
|
||||
|
||||
def _detect_windows_gpu(self) -> Dict[str, bool]:
|
||||
"""Detect GPUs on Windows systems"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False, "arm": False}
|
||||
|
||||
"""Detect GPUs on Windows using PowerShell"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False}
|
||||
|
||||
try:
|
||||
# Use PowerShell to get GPU info
|
||||
ps_command = "Get-WmiObject Win32_VideoController | ConvertTo-Json"
|
||||
result = subprocess.run(
|
||||
["powershell", "-Command", ps_command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
cmd = ["powershell", "-Command", "Get-WmiObject Win32_VideoController | Select-Object Name"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
gpu_data = json.loads(result.stdout)
|
||||
if not isinstance(gpu_data, list):
|
||||
gpu_data = [gpu_data]
|
||||
output = result.stdout.lower()
|
||||
gpu_info["nvidia"] = "nvidia" in output
|
||||
gpu_info["amd"] = any(x in output for x in ["amd", "radeon"])
|
||||
gpu_info["intel"] = "intel" in output
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Windows GPU detection failed: {str(e)}")
|
||||
|
||||
return gpu_info
|
||||
|
||||
for gpu in gpu_data:
|
||||
name = gpu.get("Name", "").lower()
|
||||
if "nvidia" in name:
|
||||
gpu_info["nvidia"] = self._test_encoder("h264_nvenc")
|
||||
if "amd" in name or "radeon" in name:
|
||||
gpu_info["amd"] = self._test_encoder("h264_amf")
|
||||
if "intel" in name:
|
||||
gpu_info["intel"] = self._test_encoder("h264_qsv")
|
||||
def _detect_linux_gpu(self) -> Dict[str, bool]:
|
||||
"""Detect GPUs on Linux using lspci and other tools"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False}
|
||||
|
||||
try:
|
||||
# Try lspci first
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["lspci", "-v"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
if result.returncode == 0:
|
||||
output = result.stdout.lower()
|
||||
gpu_info["nvidia"] = "nvidia" in output
|
||||
gpu_info["amd"] = any(x in output for x in ["amd", "radeon"])
|
||||
gpu_info["intel"] = "intel" in output
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Check for NVIDIA using nvidia-smi
|
||||
if not gpu_info["nvidia"]:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["nvidia-smi"],
|
||||
capture_output=True,
|
||||
timeout=10
|
||||
)
|
||||
gpu_info["nvidia"] = result.returncode == 0
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Check for AMD using rocm-smi
|
||||
if not gpu_info["amd"]:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["rocm-smi"],
|
||||
capture_output=True,
|
||||
timeout=10
|
||||
)
|
||||
gpu_info["amd"] = result.returncode == 0
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Check for Intel using intel_gpu_top
|
||||
if not gpu_info["intel"]:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["intel_gpu_top", "-L"],
|
||||
capture_output=True,
|
||||
timeout=10
|
||||
)
|
||||
gpu_info["intel"] = result.returncode == 0
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during Windows GPU detection: {str(e)}")
|
||||
logger.error(f"Linux GPU detection failed: {str(e)}")
|
||||
|
||||
return gpu_info
|
||||
|
||||
def _detect_macos_gpu(self) -> Dict[str, bool]:
|
||||
"""Detect GPUs on macOS using system_profiler"""
|
||||
gpu_info = {"nvidia": False, "amd": False, "intel": False}
|
||||
|
||||
try:
|
||||
cmd = ["system_profiler", "SPDisplaysDataType"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
output = result.stdout.lower()
|
||||
gpu_info["nvidia"] = "nvidia" in output
|
||||
gpu_info["amd"] = any(x in output for x in ["amd", "radeon"])
|
||||
gpu_info["intel"] = "intel" in output
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"macOS GPU detection failed: {str(e)}")
|
||||
|
||||
return gpu_info
|
||||
|
||||
def _verify_ffmpeg_gpu_support(self) -> Dict[str, bool]:
|
||||
"""Verify GPU support in FFmpeg installation"""
|
||||
gpu_support = {"nvidia": False, "amd": False, "intel": False}
|
||||
|
||||
try:
|
||||
# Check FFmpeg encoders
|
||||
cmd = [str(self.ffmpeg_path), "-hide_banner", "-encoders"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
output = result.stdout.lower()
|
||||
|
||||
# Check for specific GPU encoders
|
||||
gpu_support["nvidia"] = "h264_nvenc" in output
|
||||
gpu_support["amd"] = "h264_amf" in output
|
||||
gpu_support["intel"] = "h264_qsv" in output
|
||||
|
||||
# Log available encoders
|
||||
encoders = []
|
||||
if gpu_support["nvidia"]:
|
||||
encoders.append("NVENC")
|
||||
if gpu_support["amd"]:
|
||||
encoders.append("AMF")
|
||||
if gpu_support["intel"]:
|
||||
encoders.append("QSV")
|
||||
|
||||
if encoders:
|
||||
logger.info(f"FFmpeg supports GPU encoders: {', '.join(encoders)}")
|
||||
else:
|
||||
logger.info("No GPU encoders available in FFmpeg")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"FFmpeg GPU support verification failed: {str(e)}")
|
||||
|
||||
return gpu_support
|
||||
|
||||
def get_gpu_info(self) -> Dict[str, List[str]]:
|
||||
"""Get detailed GPU information
|
||||
|
||||
Returns:
|
||||
Dict containing lists of GPU names by type
|
||||
"""
|
||||
gpu_info = {
|
||||
"nvidia": [],
|
||||
"amd": [],
|
||||
"intel": []
|
||||
}
|
||||
|
||||
try:
|
||||
system = platform.system().lower()
|
||||
|
||||
if system == "windows":
|
||||
cmd = ["powershell", "-Command", "Get-WmiObject Win32_VideoController | Select-Object Name"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
for line in result.stdout.splitlines():
|
||||
line = line.strip().lower()
|
||||
if line:
|
||||
if "nvidia" in line:
|
||||
gpu_info["nvidia"].append(line)
|
||||
elif any(x in line for x in ["amd", "radeon"]):
|
||||
gpu_info["amd"].append(line)
|
||||
elif "intel" in line:
|
||||
gpu_info["intel"].append(line)
|
||||
|
||||
elif system == "linux":
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["lspci", "-v"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
if result.returncode == 0:
|
||||
for line in result.stdout.splitlines():
|
||||
if "vga" in line.lower() or "3d" in line.lower():
|
||||
if "nvidia" in line.lower():
|
||||
gpu_info["nvidia"].append(line.strip())
|
||||
elif any(x in line.lower() for x in ["amd", "radeon"]):
|
||||
gpu_info["amd"].append(line.strip())
|
||||
elif "intel" in line.lower():
|
||||
gpu_info["intel"].append(line.strip())
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
elif system == "darwin":
|
||||
cmd = ["system_profiler", "SPDisplaysDataType"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
current_gpu = None
|
||||
for line in result.stdout.splitlines():
|
||||
line = line.strip().lower()
|
||||
if "chipset model" in line:
|
||||
if "nvidia" in line:
|
||||
current_gpu = "nvidia"
|
||||
gpu_info["nvidia"].append(line.split(":")[1].strip())
|
||||
elif any(x in line for x in ["amd", "radeon"]):
|
||||
current_gpu = "amd"
|
||||
gpu_info["amd"].append(line.split(":")[1].strip())
|
||||
elif "intel" in line:
|
||||
current_gpu = "intel"
|
||||
gpu_info["intel"].append(line.split(":")[1].strip())
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting detailed GPU info: {str(e)}")
|
||||
|
||||
return gpu_info
|
||||
|
||||
Reference in New Issue
Block a user