docs: Update 2 files

This commit is contained in:
2025-10-12 20:37:39 +13:00
parent f4c69a1468
commit a8d2116808
2 changed files with 95 additions and 22 deletions

View File

@@ -330,12 +330,13 @@ class TestVideo:
class TranscodeJob:
"""Represents a single transcode job."""
def __init__(self, input_file: str, output_file: str, encoder: str, hwaccel_args: str, hw_decode: bool):
def __init__(self, input_file: str, output_file: str, encoder: str, hwaccel_args: str, hw_decode: bool, hevc_encoder: str):
self.input_file = input_file
self.output_file = output_file
self.encoder = encoder
self.hwaccel_args = hwaccel_args
self.hw_decode = hw_decode
self.hevc_encoder = hevc_encoder # May be hardware or software
self.process = None
self.start_time = None
self.end_time = None
@@ -354,19 +355,40 @@ class TranscodeJob:
cmd.extend(['-i', self.input_file])
# For VA-API without HW decode, we need to upload to hardware
if 'vaapi' in self.encoder and not self.hw_decode:
cmd.extend(['-vf', 'format=nv12,hwupload'])
# Build video filter chain for realistic transcoding
vf_filters = []
# Build encoding parameters
encode_params = ['-c:v', self.encoder]
# For VA-API, use CQP mode instead of bitrate if needed
# For VA-API, we need to scale and upload to hardware
if 'vaapi' in self.encoder:
# Use constant quality mode (lower is better quality, 20-30 is good)
encode_params.extend(['-qp', '23'])
if self.hw_decode:
# HW decode: download from GPU, scale on CPU, upload back
# (GPU scaling not widely supported, so use CPU)
vf_filters.append('hwdownload')
vf_filters.append('format=nv12')
# Scale on CPU (works everywhere)
vf_filters.append('scale=1280x720')
vf_filters.append('format=nv12')
vf_filters.append('hwupload')
else:
# Use bitrate mode for other encoders
# Other encoders: just scale
vf_filters.append('scale=1280x720')
if vf_filters:
cmd.extend(['-vf', ','.join(vf_filters)])
# Build encoding parameters - use HEVC/H.265 for output
encode_params = ['-c:v', self.hevc_encoder]
# Configure encoding parameters based on encoder type
if 'vaapi' in self.hevc_encoder:
# VA-API: use CQP mode
encode_params.extend(['-qp', '23'])
elif self.hevc_encoder == 'libx265':
# Software HEVC: use CRF mode
encode_params.extend(['-preset', 'medium', '-crf', '23'])
else:
# Other hardware encoders: use bitrate
encode_params.extend(['-b:v', '4M'])
cmd.extend(encode_params)
@@ -467,6 +489,51 @@ class Benchmark:
self.accel_name = accel_name
self.hw_decode = hw_decode
# Determine HEVC encoder (hardware or software fallback)
self.hevc_encoder = self._detect_hevc_encoder()
def _detect_hevc_encoder(self) -> str:
"""Detect if hardware HEVC encoding is available, otherwise use software."""
# Try hardware HEVC encoder first
hw_hevc_encoder = self.encoder.replace('h264', 'hevc').replace('264', '265')
# Test if hardware HEVC works
try:
with tempfile.NamedTemporaryFile(suffix='.yuv', delete=False) as f:
yuv_file = f.name
# Generate 1 frame
cmd1 = [
'ffmpeg', '-y', '-hide_banner', '-loglevel', 'error',
'-f', 'lavfi', '-i', 'testsrc2=size=1920x1080:duration=0.1:rate=1',
'-frames:v', '1', '-pix_fmt', 'nv12', yuv_file
]
subprocess.run(cmd1, capture_output=True, timeout=5)
# Try to encode with hardware HEVC
cmd2 = ['ffmpeg', '-y', '-hide_banner', '-loglevel', 'error']
if self.hwaccel_args:
cmd2.extend(self.hwaccel_args.split())
cmd2.extend([
'-f', 'rawvideo', '-pix_fmt', 'nv12', '-s:v', '1920x1080', '-i', yuv_file,
'-vf', 'scale=1280x720,format=nv12,hwupload' if 'vaapi' in hw_hevc_encoder else 'scale=1280x720',
'-frames:v', '1', '-c:v', hw_hevc_encoder, '-qp', '23',
'-f', 'null', '-'
])
result = subprocess.run(cmd2, capture_output=True, timeout=5)
os.unlink(yuv_file)
if result.returncode == 0:
print(f" Using hardware HEVC encoder: {hw_hevc_encoder}")
return hw_hevc_encoder
except:
pass
# Fall back to software
print(f" Hardware HEVC not available, falling back to software (libx265)")
return 'libx265'
def run_parallel_transcodes(self, num_streams: int, timeout: int = 60) -> Tuple[bool, float]:
"""
Run multiple parallel transcode jobs.
@@ -478,7 +545,7 @@ class Benchmark:
# Create jobs
for i in range(num_streams):
output_file = f'/dev/null' if os.name != 'nt' else 'NUL'
job = TranscodeJob(self.test_video, output_file, self.encoder, self.hwaccel_args, self.hw_decode)
job = TranscodeJob(self.test_video, output_file, self.encoder, self.hwaccel_args, self.hw_decode, self.hevc_encoder)
jobs.append(job)
# Start all jobs in parallel
@@ -522,7 +589,7 @@ class Benchmark:
A stream is considered "real-time" if it achieves >= min_fps.
"""
print(f"\nBenchmarking with {self.accel_name}...")
print("Finding maximum simultaneous 1080p streams at real-time or better...\n")
print("Finding maximum simultaneous 1080p H.264 → 720p H.265 transcode streams at real-time or better...\n")
# First verify that 1 stream works
print(f"Testing 1 simultaneous stream...", end=' ', flush=True)
@@ -641,7 +708,9 @@ def main():
print("BENCHMARK RESULTS")
print("=" * 60)
print(f"Hardware Acceleration: {accel_name}")
print(f"Maximum Simultaneous 1080p Streams: {max_streams}")
print(f"HEVC Encoder: {benchmark.hevc_encoder}")
print(f"Transcode Task: 1080p H.264 → 720p H.265")
print(f"Maximum Simultaneous Streams: {max_streams}")
print(f"(at {args.min_fps} FPS or better)")
print("=" * 60)