Source code for cutcutcodec.core.classes.encoder
"""The codecs and encoders."""
import subprocess
import av
from cutcutcodec.core.opti.cache.singleton import MetaSingleton
[docs]
class AllEncoders(metaclass=MetaSingleton):
"""Equivalent to parse ``ffmpeg -codecs`` and ``ffmpeg -encoders`` but use av insted.
Parameters
----------
audio : frozenset[str]
All the referenced audio encoders (readonly).
set : frozenset[str]
All the referenced encoders (readonly).
subtitle : frozenset[str]
All the referenced subtitle encoders (readonly).
video : frozenset[str]
All the referenced video encoders (readonly).
"""
def __init__(self):
self._encoders = set()
for encodec in av.codec.codecs_available:
if encodec in {
"anull", # empty audio
"libjxl", # create segfault
"msrle", # old on microsoft windows only, segfault on linux
"rle", # alias to 'msrle'
"smc", # old on microsoft windows only, segfault on linux
"vnull", # empty video
}:
continue
try:
encoder = av.codec.Codec(encodec, "w").name
except av.codec.codec.UnknownCodecError:
continue
self._encoders.add(encoder)
def _select(self, encoder_type: str) -> frozenset[str]:
"""Select the encoders of a specific type.
Parameters
----------
encoder_type : str
The codec type ``audio``, ``video`` or ``subtitle``.
Returns
-------
encoders : frozenset[str]
All the encoders of the provide type.
"""
assert isinstance(encoder_type, str), encoder_type.__class__.__name__
assert encoder_type in {"audio", "video", "subtitle"}, encoder_type
return frozenset(e for e in self._encoders if av.codec.Codec(e, "w").type == encoder_type)
@property
def audio(self) -> frozenset[str]:
"""All the referenced audio encoders.
Examples
--------
>>> from cutcutcodec.core.classes.encoder import AllEncoders
>>> sorted(AllEncoders().audio) # doctest: +ELLIPSIS
['aac', 'ac3', ..., 'wmav1', 'wmav2']
>>>
"""
return self._select("audio")
@property
def set(self) -> frozenset[str]:
"""All the referenced encoders."""
return self._encoders
@property
def subtitle(self) -> frozenset[str]:
"""All the referenced subtitle encoders.
Examples
--------
>>> from cutcutcodec.core.classes.encoder import AllEncoders
>>> sorted(AllEncoders().subtitle) # doctest: +ELLIPSIS
['ass', 'dvbsub', ..., 'webvtt', 'xsub']
>>>
"""
return self._select("subtitle")
@property
def video(self) -> frozenset[str]:
"""All the referenced video encoders.
Examples
--------
>>> from cutcutcodec.core.classes.encoder import AllEncoders
>>> sorted(AllEncoders().video) # doctest: +ELLIPSIS
['a64multi', 'a64multi5', ..., 'zlib', 'zmbv']
>>>
"""
return self._select("video")
[docs]
class Encoder(av.codec.Codec):
"""A specific encoder.
Attributes
----------
doc : str
The documentation of the encoder (readonly).
"""
def __new__(cls, name: str):
"""Initialise and create the class.
Parameters
----------
name : str
The name of the encoder.
"""
assert isinstance(name, str), name.__class__.__name__
assert name in AllEncoders().set, f"{name} encoder is not in {AllEncoders().set}"
encoder = super().__new__(cls, name, "w")
return encoder
@property
def doc(self) -> str:
"""Return the documentation of the encoder.
Based on ffmpeg, it parse ``ffmpeg -h encoder=...``.
Examples
--------
>>> from cutcutcodec.core.classes.encoder import Encoder
>>> print(Encoder("libopus").doc) # doctest: +ELLIPSIS
Encoder libopus [libopus Opus]:
General capabilities: ...
Threading capabilities: none
Supported sample rates: 48000 24000 16000 12000 8000
Supported sample formats: s16 flt
libopus AVOptions:
...
>>>
"""
doc = subprocess.run(
["ffmpeg", "-v", "error", "-h", f"encoder={self.name}"],
capture_output=True, check=True,
).stdout.decode().strip()
return doc