fix: use gpu

This commit is contained in:
ziesorx 2025-09-25 22:59:55 +07:00
parent 5f29392c2f
commit 6bb679f4d8
5 changed files with 533 additions and 84 deletions

View file

@ -199,23 +199,63 @@ class RTSPReader:
except Exception as e:
logger.debug(f"Camera {self.camera_id}: GStreamer NVDEC not available: {e}")
# Method 2: Try FFMPEG with NVIDIA CUVID hardware decoder
# Method 2: Try OpenCV CUDA VideoReader (if built with CUVID support)
if not hw_accel_success:
try:
import os
# Set FFMPEG to use NVIDIA CUVID decoder
os.environ['OPENCV_FFMPEG_CAPTURE_OPTIONS'] = 'video_codec;h264_cuvid|rtsp_transport;tcp|hwaccel;cuda'
# Check if OpenCV was built with CUDA codec support
build_info = cv2.getBuildInformation()
if 'cudacodec' in build_info or 'CUVID' in build_info:
logger.info(f"Attempting OpenCV CUDA VideoReader for camera {self.camera_id}")
# Use OpenCV's CUDA backend
self.cap = cv2.VideoCapture(self.rtsp_url, cv2.CAP_FFMPEG, [
cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY
])
if self.cap.isOpened():
hw_accel_success = True
logger.info(f"Camera {self.camera_id}: Using OpenCV CUDA hardware acceleration")
else:
logger.debug(f"Camera {self.camera_id}: OpenCV not built with CUDA codec support")
except Exception as e:
logger.debug(f"Camera {self.camera_id}: OpenCV CUDA not available: {e}")
# Method 3: Try FFMPEG with optimal hardware acceleration (CUVID/VAAPI)
if not hw_accel_success:
try:
from core.utils.ffmpeg_detector import get_optimal_rtsp_options
import os
# Get optimal FFmpeg options based on detected capabilities
optimal_options = get_optimal_rtsp_options(self.rtsp_url)
os.environ['OPENCV_FFMPEG_CAPTURE_OPTIONS'] = optimal_options
logger.info(f"Attempting FFMPEG with detected hardware acceleration for camera {self.camera_id}")
logger.debug(f"Camera {self.camera_id}: Using FFmpeg options: {optimal_options}")
logger.info(f"Attempting FFMPEG with h264_cuvid for camera {self.camera_id}")
self.cap = cv2.VideoCapture(self.rtsp_url, cv2.CAP_FFMPEG)
if self.cap.isOpened():
hw_accel_success = True
logger.info(f"Camera {self.camera_id}: Using FFMPEG with CUVID hardware acceleration")
# Try to get backend info to confirm hardware acceleration
backend = self.cap.getBackendName()
logger.info(f"Camera {self.camera_id}: Using FFMPEG hardware acceleration (backend: {backend})")
except Exception as e:
logger.debug(f"Camera {self.camera_id}: FFMPEG CUVID not available: {e}")
logger.debug(f"Camera {self.camera_id}: FFMPEG hardware acceleration not available: {e}")
# Method 3: Try VAAPI hardware acceleration (for Intel/AMD GPUs)
# Fallback to basic CUVID
try:
import os
os.environ['OPENCV_FFMPEG_CAPTURE_OPTIONS'] = 'video_codec;h264_cuvid|rtsp_transport;tcp|hwaccel;cuda'
self.cap = cv2.VideoCapture(self.rtsp_url, cv2.CAP_FFMPEG)
if self.cap.isOpened():
hw_accel_success = True
logger.info(f"Camera {self.camera_id}: Using basic FFMPEG CUVID hardware acceleration")
except Exception as e2:
logger.debug(f"Camera {self.camera_id}: Basic CUVID also failed: {e2}")
# Method 4: Try VAAPI hardware acceleration (for Intel/AMD GPUs)
if not hw_accel_success:
try:
gst_pipeline = (

View file

@ -0,0 +1,214 @@
"""
FFmpeg hardware acceleration detection and configuration
"""
import subprocess
import logging
import re
from typing import Dict, List, Optional
logger = logging.getLogger("detector_worker")
class FFmpegCapabilities:
"""Detect and configure FFmpeg hardware acceleration capabilities."""
def __init__(self):
"""Initialize FFmpeg capabilities detector."""
self.hwaccels = []
self.codecs = {}
self.nvidia_support = False
self.vaapi_support = False
self.qsv_support = False
self._detect_capabilities()
def _detect_capabilities(self):
"""Detect available hardware acceleration methods."""
try:
# Get hardware accelerators
result = subprocess.run(
['ffmpeg', '-hide_banner', '-hwaccels'],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
self.hwaccels = [line.strip() for line in result.stdout.strip().split('\n')[1:] if line.strip()]
logger.info(f"Available FFmpeg hardware accelerators: {', '.join(self.hwaccels)}")
# Check for NVIDIA support
self.nvidia_support = any(hw in self.hwaccels for hw in ['cuda', 'cuvid', 'nvdec'])
self.vaapi_support = 'vaapi' in self.hwaccels
self.qsv_support = 'qsv' in self.hwaccels
# Get decoder information
self._detect_decoders()
# Log capabilities
if self.nvidia_support:
logger.info("NVIDIA hardware acceleration available (CUDA/CUVID/NVDEC)")
if self.vaapi_support:
logger.info("VAAPI hardware acceleration available")
if self.qsv_support:
logger.info("Intel QuickSync hardware acceleration available")
except Exception as e:
logger.warning(f"Failed to detect FFmpeg capabilities: {e}")
def _detect_decoders(self):
"""Detect available hardware decoders."""
try:
result = subprocess.run(
['ffmpeg', '-hide_banner', '-decoders'],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
# Parse decoder output to find hardware decoders
for line in result.stdout.split('\n'):
if 'cuvid' in line or 'nvdec' in line:
match = re.search(r'(\w+)\s+.*?(\w+(?:_cuvid|_nvdec))', line)
if match:
codec_type, decoder = match.groups()
if 'h264' in decoder:
self.codecs['h264_hw'] = decoder
elif 'hevc' in decoder or 'h265' in decoder:
self.codecs['h265_hw'] = decoder
elif 'vaapi' in line:
match = re.search(r'(\w+)\s+.*?(\w+_vaapi)', line)
if match:
codec_type, decoder = match.groups()
if 'h264' in decoder:
self.codecs['h264_vaapi'] = decoder
except Exception as e:
logger.debug(f"Failed to detect decoders: {e}")
def get_optimal_capture_options(self, codec: str = 'h264') -> Dict[str, str]:
"""
Get optimal FFmpeg capture options for the given codec.
Args:
codec: Video codec (h264, h265, etc.)
Returns:
Dictionary of FFmpeg options
"""
options = {
'rtsp_transport': 'tcp',
'buffer_size': '1024k',
'max_delay': '500000', # 500ms
'fflags': '+genpts',
'flags': '+low_delay',
'probesize': '32',
'analyzeduration': '0'
}
# Add hardware acceleration if available
if self.nvidia_support:
if codec == 'h264' and 'h264_hw' in self.codecs:
options.update({
'hwaccel': 'cuda',
'hwaccel_device': '0',
'video_codec': 'h264_cuvid',
'hwaccel_output_format': 'cuda'
})
logger.debug("Using NVIDIA CUVID hardware acceleration for H.264")
elif codec == 'h265' and 'h265_hw' in self.codecs:
options.update({
'hwaccel': 'cuda',
'hwaccel_device': '0',
'video_codec': 'hevc_cuvid',
'hwaccel_output_format': 'cuda'
})
logger.debug("Using NVIDIA CUVID hardware acceleration for H.265")
elif self.vaapi_support:
if codec == 'h264':
options.update({
'hwaccel': 'vaapi',
'hwaccel_device': '/dev/dri/renderD128',
'video_codec': 'h264_vaapi'
})
logger.debug("Using VAAPI hardware acceleration")
return options
def format_opencv_options(self, options: Dict[str, str]) -> str:
"""
Format options for OpenCV FFmpeg backend.
Args:
options: Dictionary of FFmpeg options
Returns:
Formatted options string for OpenCV
"""
return '|'.join(f"{key};{value}" for key, value in options.items())
def get_hardware_encoder_options(self, codec: str = 'h264', quality: str = 'fast') -> Dict[str, str]:
"""
Get optimal hardware encoding options.
Args:
codec: Video codec for encoding
quality: Quality preset (fast, medium, slow)
Returns:
Dictionary of encoding options
"""
options = {}
if self.nvidia_support:
if codec == 'h264':
options.update({
'video_codec': 'h264_nvenc',
'preset': quality,
'tune': 'zerolatency',
'gpu': '0',
'rc': 'cbr_hq',
'surfaces': '64'
})
elif codec == 'h265':
options.update({
'video_codec': 'hevc_nvenc',
'preset': quality,
'tune': 'zerolatency',
'gpu': '0'
})
elif self.vaapi_support:
if codec == 'h264':
options.update({
'video_codec': 'h264_vaapi',
'vaapi_device': '/dev/dri/renderD128'
})
return options
# Global instance
_ffmpeg_caps = None
def get_ffmpeg_capabilities() -> FFmpegCapabilities:
"""Get or create the global FFmpeg capabilities instance."""
global _ffmpeg_caps
if _ffmpeg_caps is None:
_ffmpeg_caps = FFmpegCapabilities()
return _ffmpeg_caps
def get_optimal_rtsp_options(rtsp_url: str) -> str:
"""
Get optimal OpenCV FFmpeg options for RTSP streaming.
Args:
rtsp_url: RTSP stream URL
Returns:
Formatted options string for cv2.VideoCapture
"""
caps = get_ffmpeg_capabilities()
# Detect codec from URL or assume H.264
codec = 'h265' if any(x in rtsp_url.lower() for x in ['h265', 'hevc']) else 'h264'
options = caps.get_optimal_capture_options(codec)
return caps.format_opencv_options(options)