146 lines
4.5 KiB
Python
146 lines
4.5 KiB
Python
"""
|
|
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]
|