fix: use gpu
This commit is contained in:
parent
5f29392c2f
commit
6bb679f4d8
5 changed files with 533 additions and 84 deletions
214
core/utils/ffmpeg_detector.py
Normal file
214
core/utils/ffmpeg_detector.py
Normal 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)
|
Loading…
Add table
Add a link
Reference in a new issue