143 lines
3.5 KiB
Python
143 lines
3.5 KiB
Python
import sys
|
|
|
|
import cython
|
|
|
|
container_format_postfix: str = "le" if sys.byteorder == "little" else "be"
|
|
_cinit_bypass_sentinel = object()
|
|
|
|
|
|
@cython.cfunc
|
|
def get_audio_format(c_format: lib.AVSampleFormat) -> AudioFormat:
|
|
"""Get an AudioFormat without going through a string."""
|
|
|
|
if c_format < 0:
|
|
return None
|
|
|
|
format: AudioFormat = AudioFormat(_cinit_bypass_sentinel)
|
|
format.sample_fmt = c_format
|
|
return format
|
|
|
|
|
|
@cython.cclass
|
|
class AudioFormat:
|
|
"""Descriptor of audio formats."""
|
|
|
|
def __cinit__(self, name):
|
|
if name is _cinit_bypass_sentinel:
|
|
return
|
|
|
|
sample_fmt: lib.AVSampleFormat
|
|
if isinstance(name, AudioFormat):
|
|
sample_fmt = cython.cast(AudioFormat, name).sample_fmt
|
|
else:
|
|
sample_fmt = lib.av_get_sample_fmt(name)
|
|
|
|
if sample_fmt < 0:
|
|
raise ValueError(f"Not a sample format: {name!r}")
|
|
|
|
self.sample_fmt = sample_fmt
|
|
|
|
def __repr__(self):
|
|
return f"<av.AudioFormat {self.name}>"
|
|
|
|
@property
|
|
def name(self):
|
|
"""Canonical name of the sample format.
|
|
|
|
>>> AudioFormat('s16p').name
|
|
's16p'
|
|
|
|
"""
|
|
return lib.av_get_sample_fmt_name(self.sample_fmt)
|
|
|
|
@property
|
|
def bytes(self):
|
|
"""Number of bytes per sample.
|
|
|
|
>>> AudioFormat('s16p').bytes
|
|
2
|
|
|
|
"""
|
|
return lib.av_get_bytes_per_sample(self.sample_fmt)
|
|
|
|
@property
|
|
def bits(self):
|
|
"""Number of bits per sample.
|
|
|
|
>>> AudioFormat('s16p').bits
|
|
16
|
|
|
|
"""
|
|
return lib.av_get_bytes_per_sample(self.sample_fmt) << 3
|
|
|
|
@property
|
|
def is_planar(self):
|
|
"""Is this a planar format?
|
|
|
|
Strictly opposite of :attr:`is_packed`.
|
|
|
|
"""
|
|
return bool(lib.av_sample_fmt_is_planar(self.sample_fmt))
|
|
|
|
@property
|
|
def is_packed(self):
|
|
"""Is this a packed format?
|
|
|
|
Strictly opposite of :attr:`is_planar`.
|
|
|
|
"""
|
|
return not lib.av_sample_fmt_is_planar(self.sample_fmt)
|
|
|
|
@property
|
|
def planar(self):
|
|
"""The planar variant of this format.
|
|
|
|
Is itself when planar:
|
|
|
|
>>> fmt = AudioFormat('s16p')
|
|
>>> fmt.planar is fmt
|
|
True
|
|
|
|
"""
|
|
if self.is_planar:
|
|
return self
|
|
return get_audio_format(lib.av_get_planar_sample_fmt(self.sample_fmt))
|
|
|
|
@property
|
|
def packed(self):
|
|
"""The packed variant of this format.
|
|
|
|
Is itself when packed:
|
|
|
|
>>> fmt = AudioFormat('s16')
|
|
>>> fmt.packed is fmt
|
|
True
|
|
|
|
"""
|
|
if self.is_packed:
|
|
return self
|
|
return get_audio_format(lib.av_get_packed_sample_fmt(self.sample_fmt))
|
|
|
|
@property
|
|
def container_name(self):
|
|
"""The name of a :class:`ContainerFormat` which directly accepts this data.
|
|
|
|
:raises ValueError: when planar, since there are no such containers.
|
|
|
|
"""
|
|
if self.is_planar:
|
|
raise ValueError("no planar container formats")
|
|
|
|
if self.sample_fmt == lib.AV_SAMPLE_FMT_U8:
|
|
return "u8"
|
|
elif self.sample_fmt == lib.AV_SAMPLE_FMT_S16:
|
|
return "s16" + container_format_postfix
|
|
elif self.sample_fmt == lib.AV_SAMPLE_FMT_S32:
|
|
return "s32" + container_format_postfix
|
|
elif self.sample_fmt == lib.AV_SAMPLE_FMT_FLT:
|
|
return "f32" + container_format_postfix
|
|
elif self.sample_fmt == lib.AV_SAMPLE_FMT_DBL:
|
|
return "f64" + container_format_postfix
|
|
|
|
raise ValueError("unknown layout")
|