Added proper physical GPU detection on Linux/Windows/macOS

Separated physical GPU detection from FFmpeg encoder support
Only enable GPU encoding when both physical GPU and FFmpeg support exist
Added multiple detection methods for better reliability
This commit is contained in:
pacnpal
2024-11-15 05:26:10 +00:00
parent 8e581d660e
commit 887547473c

View File

@@ -5,7 +5,7 @@ import subprocess
import logging
import platform
import re
from typing import Dict, List
from typing import Dict, List, Tuple
from pathlib import Path
logger = logging.getLogger("VideoArchiver")
@@ -34,24 +34,23 @@ class GPUDetector:
}
try:
# 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())
# First detect physical GPUs
physical_gpus = self._detect_physical_gpus()
# Verify GPU support in FFmpeg
gpu_info.update(self._verify_ffmpeg_gpu_support())
# Then check FFmpeg support
ffmpeg_support = self._verify_ffmpeg_gpu_support()
# Only enable GPU if both physical GPU exists and FFmpeg supports it
gpu_info["nvidia"] = physical_gpus["nvidia"] and ffmpeg_support["nvidia"]
gpu_info["amd"] = physical_gpus["amd"] and ffmpeg_support["amd"]
gpu_info["intel"] = physical_gpus["intel"] and ffmpeg_support["intel"]
# 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)}")
logger.info(f"Detected GPUs with FFmpeg support: {', '.join(detected_gpus)}")
else:
logger.info("No GPU acceleration support detected")
logger.info("No GPU acceleration available")
return gpu_info
@@ -59,6 +58,17 @@ class GPUDetector:
logger.error(f"Error during GPU detection: {str(e)}")
return {"nvidia": False, "amd": False, "intel": False}
def _detect_physical_gpus(self) -> Dict[str, bool]:
"""Detect physical GPUs in the system"""
system = platform.system().lower()
if system == "windows":
return self._detect_windows_gpu()
elif system == "linux":
return self._detect_linux_gpu()
elif system == "darwin":
return self._detect_macos_gpu()
return {"nvidia": False, "amd": False, "intel": False}
def _detect_windows_gpu(self) -> Dict[str, bool]:
"""Detect GPUs on Windows using PowerShell"""
gpu_info = {"nvidia": False, "amd": False, "intel": False}
@@ -80,60 +90,56 @@ class GPUDetector:
return gpu_info
def _detect_linux_gpu(self) -> Dict[str, bool]:
"""Detect GPUs on Linux using lspci and other tools"""
"""Detect GPUs on Linux using multiple methods"""
gpu_info = {"nvidia": False, "amd": False, "intel": False}
try:
# Try lspci first
# Check for NVIDIA GPU
try:
result = subprocess.run(
["lspci", "-v"],
["nvidia-smi"],
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
gpu_info["nvidia"] = result.returncode == 0
except FileNotFoundError:
pass
# Check for NVIDIA using nvidia-smi
if not gpu_info["nvidia"]:
# Check for AMD GPU using DRI
if os.path.exists("/dev/dri"):
try:
result = subprocess.run(
["nvidia-smi"],
["ls", "/dev/dri/render*"],
capture_output=True,
text=True,
timeout=10
)
gpu_info["nvidia"] = result.returncode == 0
except FileNotFoundError:
if result.returncode == 0:
# Check device info using lspci
lspci_result = subprocess.run(
["lspci", "-v"],
capture_output=True,
text=True,
timeout=10
)
if lspci_result.returncode == 0:
output = lspci_result.stdout.lower()
gpu_info["amd"] = any(x in output for x in ["amd", "radeon", "advanced micro devices"])
gpu_info["intel"] = "intel" in output and "graphics" in output
except (FileNotFoundError, subprocess.SubprocessError):
pass
# Check for AMD using rocm-smi
if not gpu_info["amd"]:
# Additional check for Intel GPU
if not gpu_info["intel"] and os.path.exists("/sys/class/drm"):
try:
result = subprocess.run(
["rocm-smi"],
["find", "/sys/class/drm", "-name", "*i915*"],
capture_output=True,
text=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:
gpu_info["intel"] = bool(result.stdout.strip())
except subprocess.SubprocessError:
pass
except Exception as e:
@@ -187,7 +193,7 @@ class GPUDetector:
encoders.append("QSV")
if encoders:
logger.info(f"FFmpeg supports GPU encoders: {', '.join(encoders)}")
logger.info(f"FFmpeg compiled with GPU encoders: {', '.join(encoders)}")
else:
logger.info("No GPU encoders available in FFmpeg")
@@ -197,11 +203,7 @@ class GPUDetector:
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
"""
"""Get detailed GPU information"""
gpu_info = {
"nvidia": [],
"amd": [],
@@ -211,23 +213,9 @@ class GPUDetector:
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":
if system == "linux":
try:
# Try lspci first
result = subprocess.run(
["lspci", "-v"],
capture_output=True,
@@ -246,6 +234,35 @@ class GPUDetector:
except FileNotFoundError:
pass
# Try nvidia-smi for NVIDIA GPUs
if not gpu_info["nvidia"]:
try:
result = subprocess.run(
["nvidia-smi", "-L"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
gpu_info["nvidia"].extend(line.strip() for line in result.stdout.splitlines() if line.strip())
except FileNotFoundError:
pass
elif 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()
if line and not line.startswith("Name"):
if "nvidia" in line.lower():
gpu_info["nvidia"].append(line)
elif any(x in line.lower() for x in ["amd", "radeon"]):
gpu_info["amd"].append(line)
elif "intel" in line.lower():
gpu_info["intel"].append(line)
elif system == "darwin":
cmd = ["system_profiler", "SPDisplaysDataType"]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
@@ -253,17 +270,15 @@ class GPUDetector:
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())
line = line.strip()
if "Chipset Model:" in line:
model = line.split(":", 1)[1].strip()
if "nvidia" in model.lower():
gpu_info["nvidia"].append(model)
elif any(x in model.lower() for x in ["amd", "radeon"]):
gpu_info["amd"].append(model)
elif "intel" in model.lower():
gpu_info["intel"].append(model)
except Exception as e:
logger.error(f"Error getting detailed GPU info: {str(e)}")