1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
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")
|