| 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") |
|
|