Files
CalamitousFelicitousness cce3a80b0c fix(video): correct codec option strings to the key=value form
parse_options reads : and , as separators and = as the only assignment, so a
segment without = becomes a valueless flag set to 1. The shipped strings used
ffmpeg command line spelling, which parses without error into other values.

- crf:16 parsed to {'crf': '1', '16': '1'}, encoding every api, framepack and
  seedvr video near lossless rather than at crf 16
- crf=23:b:v=0 pinned the generic bitrate option to 1 bit per second on vp8 and
  vp9, collapsing their output
- qscale:v=3 reached mpeg4 and mjpeg as nothing at all, replaced by an explicit
  quantizer range
- test-video-codecs.py asserts every preset segment carries an assignment
2026-08-17 03:04:59 +01:00

230 lines
7.1 KiB
Python

codecs_config = {
# --- Modern / Standard Distribution (CPU) ---
'libx264': {
'name': 'H.264 / AVC',
'desc': 'Standard for web and streaming compatibility.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'crf=18:preset=medium',
},
'libx264rgb': {
'name': 'H.264 Lossless RGB',
'desc': 'Lossless recording for screen capture / pure RGB.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov'],
'options': 'crf=0:preset=fast',
},
'libx265': {
'name': 'HEVC / H.265',
'desc': 'High efficiency compression for 4K video.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'crf=22:preset=medium',
},
'libvpx': {
'name': 'VP8 Video',
'desc': 'Legacy open video format for WebM.',
'ext': 'webm',
'allowed_exts': ['webm', 'mkv'],
'options': 'crf=10:b=0',
},
'libvpx-vp9': {
'name': 'VP9 Video',
'desc': 'Royalty-free web video format.',
'ext': 'webm',
'allowed_exts': ['webm', 'mkv'],
'options': 'crf=23:b=0',
},
'libsvtav1': {
'name': 'AV1 (SVT-AV1)',
'desc': 'Next-gen open codec with high compression.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'webm'],
'options': 'preset=5:crf=26',
},
'mpeg4': {
'name': 'MPEG-4 Part 2',
'desc': 'Legacy format for old media players.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'avi', 'mkv'],
'options': 'qmin=3:qmax=3',
},
'mjpeg': {
'name': 'Motion JPEG',
'desc': 'Sequence of JPEG frames; minimal CPU load.',
'ext': 'avi',
'allowed_exts': ['avi', 'mov', 'mkv'],
'options': 'qmin=3:qmax=3',
},
# --- Hardware Accelerated: NVIDIA NVENC ---
'h264_nvenc': {
'name': 'H.264 (NVIDIA)',
'desc': 'Fast NVIDIA GPU encoding.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'rc=constqp:qp=19:preset=p5',
},
'hevc_nvenc': {
'name': 'HEVC / H.265 (NVIDIA)',
'desc': 'Fast high-efficiency NVIDIA GPU encoding.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'rc=constqp:qp=23:preset=p5',
},
'av1_nvenc': {
'name': 'AV1 (NVIDIA)',
'desc': 'Fast AV1 encoding for NVIDIA RTX 40+.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'webm'],
'options': 'rc=constqp:qp=26:preset=p5',
},
# --- Hardware Accelerated: Intel QSV ---
'h264_qsv': {
'name': 'H.264 (Intel QuickSync)',
'desc': 'Hardware encoding via Intel GPU.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'global_quality=20:preset=medium',
},
'hevc_qsv': {
'name': 'HEVC / H.265 (Intel QuickSync)',
'desc': 'Hardware HEVC encoding via Intel GPU.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'global_quality=23:preset=medium',
},
'av1_qsv': {
'name': 'AV1 (Intel QuickSync)',
'desc': 'Hardware AV1 encoding via Intel Arc/iGPU.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'webm'],
'options': 'global_quality=26:preset=medium',
},
'vp9_qsv': {
'name': 'VP9 (Intel QuickSync)',
'desc': 'Hardware VP9 encoding via Intel GPU.',
'ext': 'webm',
'allowed_exts': ['webm', 'mkv'],
'options': 'global_quality=24',
},
# --- Hardware Accelerated: AMD AMF (Windows) ---
'h264_amf': {
'name': 'H.264 (AMD AMF)',
'desc': 'Hardware encoding for AMD GPUs on Windows.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'rc=cqp:qp_i=18:qp_p=18:quality=balanced',
},
'hevc_amf': {
'name': 'HEVC / H.265 (AMD AMF)',
'desc': 'Hardware HEVC encoding for AMD GPUs on Windows.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov', 'ts'],
'options': 'rc=cqp:qp_i=22:qp_p=22:quality=balanced',
},
'av1_amf': {
'name': 'AV1 (AMD AMF)',
'desc': 'Hardware AV1 encoding for AMD RX 7000+.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'webm'],
'options': 'rc=cqp:qp_i=26:qp_p=26:quality=balanced',
},
# --- Hardware Accelerated: Linux (VAAPI) ---
'h264_vaapi': {
'name': 'H.264 (VAAPI Linux)',
'desc': 'Hardware encoding via Linux VAAPI.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov'],
'options': 'qp=20',
},
'hevc_vaapi': {
'name': 'HEVC / H.265 (VAAPI Linux)',
'desc': 'Hardware HEVC encoding via Linux VAAPI.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'mov'],
'options': 'qp=25',
},
'av1_vaapi': {
'name': 'AV1 (VAAPI Linux)',
'desc': 'Hardware AV1 encoding via Linux VAAPI.',
'ext': 'mp4',
'allowed_exts': ['mp4', 'mkv', 'webm'],
'options': 'qp=28',
},
# --- Professional / Editing / Archival ---
'prores_ks': {
'name': 'Apple ProRes (iCodec)',
'desc': 'Universal ProRes encoder for editing suites.',
'ext': 'mov',
'allowed_exts': ['mov', 'mkv'],
'options': 'profile=3', # 3 = ProRes 422 HQ
},
'ffv1': {
'name': 'Lossless Archival (FFV1)',
'desc': 'Lossless codec for long-term storage.',
'ext': 'mkv',
'allowed_exts': ['mkv', 'avi'],
'options': 'level=3:coder=1:context=1:slices=16',
},
'huffyuv': {
'name': 'HuffYUV Lossless',
'desc': 'Fast, simple intra-frame lossless codec.',
'ext': 'avi',
'allowed_exts': ['avi', 'mkv'],
'options': '',
},
'utvideo': {
'name': 'Ut Video Lossless',
'desc': 'Efficient lossless format for editing.',
'ext': 'avi',
'allowed_exts': ['avi', 'mkv', 'mov'],
'options': 'pred=left',
},
'magicyuv': {
'name': 'MagicYUV Lossless',
'desc': 'Real-time lossless recording codec.',
'ext': 'avi',
'allowed_exts': ['avi', 'mkv'],
'options': '',
},
}
def get_codec_list():
return ['none'] + list(codecs_config.keys())
def get_codec_name(codec_name):
codec_info = codecs_config.get(codec_name)
if codec_info is None:
return ''
return codec_info['name']
def get_codec_ext(codec_name):
codec_info = codecs_config.get(codec_name)
if codec_info is None:
return ''
return codec_info['ext']
def get_codec_options(codec_name):
codec_info = codecs_config.get(codec_name)
if codec_info is None:
return ''
return codec_info['options']
def get_codec_allowed_exts(codec_name):
codec_info = codecs_config.get(codec_name)
if codec_info is None:
return []
return codec_info['allowed_exts']
def get_codec_dict(codec_name):
codec_info = codecs_config.get(codec_name)
if codec_info is None:
return {}
return codec_info