""" FFmpeg capabilities - Discover available codecs and formats using ffmpeg-python. """ from dataclasses import dataclass from functools import lru_cache from typing import Any, Dict, List import ffmpeg @dataclass class Codec: """An FFmpeg encoder or decoder.""" name: str description: str type: str # 'video' or 'audio' @dataclass class Format: """An FFmpeg format (muxer/demuxer).""" name: str description: str can_demux: bool can_mux: bool @lru_cache(maxsize=1) def _get_ffmpeg_info() -> Dict[str, Any]: """Get FFmpeg capabilities info.""" # ffmpeg-python doesn't have a direct way to get codecs/formats # but we can use probe on a dummy or parse -codecs output # For now, return common codecs that are typically available return { "video_encoders": [ {"name": "libx264", "description": "H.264 / AVC"}, {"name": "libx265", "description": "H.265 / HEVC"}, {"name": "mpeg4", "description": "MPEG-4 Part 2"}, {"name": "libvpx", "description": "VP8"}, {"name": "libvpx-vp9", "description": "VP9"}, {"name": "h264_nvenc", "description": "NVIDIA NVENC H.264"}, {"name": "hevc_nvenc", "description": "NVIDIA NVENC H.265"}, {"name": "h264_vaapi", "description": "VAAPI H.264"}, {"name": "prores_ks", "description": "Apple ProRes"}, {"name": "dnxhd", "description": "Avid DNxHD/DNxHR"}, {"name": "copy", "description": "Stream copy (no encoding)"}, ], "audio_encoders": [ {"name": "aac", "description": "AAC"}, {"name": "libmp3lame", "description": "MP3"}, {"name": "libopus", "description": "Opus"}, {"name": "libvorbis", "description": "Vorbis"}, {"name": "pcm_s16le", "description": "PCM signed 16-bit little-endian"}, {"name": "flac", "description": "FLAC"}, {"name": "copy", "description": "Stream copy (no encoding)"}, ], "formats": [ {"name": "mp4", "description": "MP4", "can_demux": True, "can_mux": True}, { "name": "mov", "description": "QuickTime / MOV", "can_demux": True, "can_mux": True, }, { "name": "mkv", "description": "Matroska", "can_demux": True, "can_mux": True, }, {"name": "webm", "description": "WebM", "can_demux": True, "can_mux": True}, {"name": "avi", "description": "AVI", "can_demux": True, "can_mux": True}, {"name": "flv", "description": "FLV", "can_demux": True, "can_mux": True}, { "name": "ts", "description": "MPEG-TS", "can_demux": True, "can_mux": True, }, { "name": "mpegts", "description": "MPEG-TS", "can_demux": True, "can_mux": True, }, {"name": "hls", "description": "HLS", "can_demux": True, "can_mux": True}, ], } def get_encoders() -> List[Codec]: """Get available encoders (video + audio).""" info = _get_ffmpeg_info() codecs = [] for c in info["video_encoders"]: codecs.append(Codec(name=c["name"], description=c["description"], type="video")) for c in info["audio_encoders"]: codecs.append(Codec(name=c["name"], description=c["description"], type="audio")) return codecs def get_decoders() -> List[Codec]: """Get available decoders.""" # Most encoders can also decode return get_encoders() def get_formats() -> List[Format]: """Get available formats.""" info = _get_ffmpeg_info() return [ Format( name=f["name"], description=f["description"], can_demux=f["can_demux"], can_mux=f["can_mux"], ) for f in info["formats"] ] def get_video_encoders() -> List[Codec]: """Get available video encoders.""" return [c for c in get_encoders() if c.type == "video"] def get_audio_encoders() -> List[Codec]: """Get available audio encoders.""" return [c for c in get_encoders() if c.type == "audio"] def get_muxers() -> List[Format]: """Get available output formats (muxers).""" return [f for f in get_formats() if f.can_mux] def get_demuxers() -> List[Format]: """Get available input formats (demuxers).""" return [f for f in get_formats() if f.can_demux]