Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 68 additions & 51 deletions av/codec/codec.pyx → av/codec/codec.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
cimport libav as lib

from av.audio.format cimport get_audio_format
from av.codec.hwaccel cimport wrap_hwconfig
from av.descriptor cimport wrap_avclass
from av.utils cimport avrational_to_fraction
from av.video.format cimport VideoFormat, get_pix_fmt, get_video_format

from enum import Flag, IntEnum
from libc.stdlib cimport free, malloc

import cython
from cython.cimports import libav as lib
from cython.cimports.av.audio.format import get_audio_format
from cython.cimports.av.codec.hwaccel import wrap_hwconfig
from cython.cimports.av.descriptor import wrap_avclass
from cython.cimports.av.utils import avrational_to_fraction
from cython.cimports.av.video.format import VideoFormat, get_pix_fmt, get_video_format
from cython.cimports.libc.stdlib import free, malloc

_cinit_sentinel = cython.declare(object, object())

cdef object _cinit_sentinel = object()

cdef Codec wrap_codec(const lib.AVCodec *ptr):
cdef Codec codec = Codec(_cinit_sentinel)
@cython.cfunc
def wrap_codec(ptr: cython.pointer[cython.const[lib.AVCodec]]) -> Codec:
codec: Codec = Codec(_cinit_sentinel)
codec.ptr = ptr
codec.is_encoder = lib.av_codec_is_encoder(ptr)
codec._init()
return codec


class Properties(Flag):
NONE = 0
INTRA_ONLY = lib.AV_CODEC_PROP_INTRA_ONLY
Expand Down Expand Up @@ -57,7 +59,8 @@ class UnknownCodecError(ValueError):
pass


cdef class Codec:
@cython.cclass
class Codec:
"""Codec(name, mode='r')

:param str name: The codec name.
Expand Down Expand Up @@ -105,7 +108,8 @@ def __cinit__(self, name, mode="r"):
if (mode == "w") != self.is_encoder:
raise RuntimeError("Found codec does not match mode.", name, mode)

cdef _init(self, name=None):
@cython.cfunc
def _init(self, name=None):
if not self.ptr:
raise UnknownCodecError(name)

Expand All @@ -124,12 +128,13 @@ def __repr__(self):
mode = self.mode
return f"<av.{self.__class__.__name__} {self.name} {mode=}>"

def create(self, kind = None):
def create(self, kind=None):
"""Create a :class:`.CodecContext` for this codec.

:param str kind: Gives a hint to static type checkers for what exact CodecContext is used.
"""
from .context import CodecContext

return CodecContext.create(self)

@property
Expand All @@ -141,10 +146,12 @@ def is_decoder(self):
return not self.is_encoder

@property
def descriptor(self): return wrap_avclass(self.ptr.priv_class)
def descriptor(self):
return wrap_avclass(self.ptr.priv_class)

@property
def name(self): return self.ptr.name or ""
def name(self):
return self.ptr.name or ""

@property
def canonical_name(self):
Expand All @@ -154,7 +161,8 @@ def canonical_name(self):
return lib.avcodec_get_name(self.ptr.id)

@property
def long_name(self): return self.ptr.long_name or ""
def long_name(self):
return self.ptr.long_name or ""

@property
def type(self):
Expand All @@ -167,18 +175,21 @@ def type(self):
return lib.av_get_media_type_string(self.ptr.type)

@property
def id(self): return self.ptr.id
def id(self):
return self.ptr.id

@property
def frame_rates(self):
"""A list of supported frame rates (:class:`fractions.Fraction`), or ``None``."""
if not self.ptr.supported_framerates:
return

ret = []
cdef int i = 0
ret: list = []
i: cython.int = 0
while self.ptr.supported_framerates[i].denum:
ret.append(avrational_to_fraction(&self.ptr.supported_framerates[i]))
ret.append(
avrational_to_fraction(cython.address(self.ptr.supported_framerates[i]))
)
i += 1
return ret

Expand All @@ -188,8 +199,8 @@ def audio_rates(self):
if not self.ptr.supported_samplerates:
return

ret = []
cdef int i = 0
ret: list = []
i: cython.int = 0
while self.ptr.supported_samplerates[i]:
ret.append(self.ptr.supported_samplerates[i])
i += 1
Expand All @@ -201,8 +212,8 @@ def video_formats(self):
if not self.ptr.pix_fmts:
return

ret = []
cdef int i = 0
ret: list = []
i: cython.int = 0
while self.ptr.pix_fmts[i] != -1:
ret.append(get_video_format(self.ptr.pix_fmts[i], 0, 0))
i += 1
Expand All @@ -214,8 +225,8 @@ def audio_formats(self):
if not self.ptr.sample_fmts:
return

ret = []
cdef int i = 0
ret: list = []
i: cython.int = 0
while self.ptr.sample_fmts[i] != -1:
ret.append(get_audio_format(self.ptr.sample_fmts[i]))
i += 1
Expand All @@ -225,9 +236,9 @@ def audio_formats(self):
def hardware_configs(self):
if self._hardware_configs:
return self._hardware_configs
ret = []
cdef int i = 0
cdef const lib.AVCodecHWConfig *ptr
ret: list = []
i: cython.int = 0
ptr: cython.pointer[cython.const[lib.AVCodecHWConfig]]
while True:
ptr = lib.avcodec_get_hw_config(self.ptr, i)
if not ptr:
Expand Down Expand Up @@ -309,12 +320,14 @@ def delay(self):
"""
return bool(self.ptr.capabilities & lib.AV_CODEC_CAP_DELAY)

cdef get_codec_names():
names = set()
cdef const lib.AVCodec *ptr
cdef void *opaque = NULL

@cython.cfunc
def get_codec_names():
names: cython.set = set()
ptr = cython.declare(cython.pointer[cython.const[lib.AVCodec]])
opaque: cython.p_void = cython.NULL
while True:
ptr = lib.av_codec_iterate(&opaque)
ptr = lib.av_codec_iterate(cython.address(opaque))
if ptr:
names.add(ptr.name)
else:
Expand Down Expand Up @@ -373,6 +386,7 @@ def dump_codecs():
except Exception as e:
print(f"...... {codec.name:<18} ERROR: {e}")


def dump_hwconfigs():
print("Hardware configs:")
for name in sorted(codecs_available):
Expand Down Expand Up @@ -402,13 +416,13 @@ def find_best_pix_fmt_of_list(pix_fmts, src_pix_fmt, has_alpha=False):
:return: (best_format, loss)
:rtype: (VideoFormat | None, int)
"""
cdef lib.AVPixelFormat src
cdef lib.AVPixelFormat best
cdef lib.AVPixelFormat *c_list = NULL
cdef Py_ssize_t n
cdef Py_ssize_t i
cdef object item
cdef int c_loss
src: lib.AVPixelFormat
best: lib.AVPixelFormat
c_list: cython.pointer[lib.AVPixelFormat] = cython.NULL
n: cython.Py_ssize_t
i: cython.Py_ssize_t
item: object
c_loss: cython.int

if pix_fmts is None:
raise TypeError("pix_fmts must not be None")
Expand All @@ -418,29 +432,32 @@ def find_best_pix_fmt_of_list(pix_fmts, src_pix_fmt, has_alpha=False):
return None, 0

if isinstance(src_pix_fmt, VideoFormat):
src = (<VideoFormat>src_pix_fmt).pix_fmt
src = cython.cast(VideoFormat, src_pix_fmt).pix_fmt
else:
src = get_pix_fmt(<str>src_pix_fmt)
src = get_pix_fmt(cython.cast(str, src_pix_fmt))

n = len(pix_fmts)
c_list = <lib.AVPixelFormat *>malloc((n + 1) * sizeof(lib.AVPixelFormat))
if c_list == NULL:
c_list = cython.cast(
cython.pointer[lib.AVPixelFormat],
malloc((n + 1) * cython.sizeof(lib.AVPixelFormat)),
)
if c_list == cython.NULL:
raise MemoryError()

try:
for i in range(n):
item = pix_fmts[i]
if isinstance(item, VideoFormat):
c_list[i] = (<VideoFormat>item).pix_fmt
c_list[i] = cython.cast(VideoFormat, item).pix_fmt
else:
c_list[i] = get_pix_fmt(<str>item)
c_list[i] = get_pix_fmt(cython.cast(str, item))
c_list[n] = lib.AV_PIX_FMT_NONE

c_loss = 0
best = lib.avcodec_find_best_pix_fmt_of_list(
c_list, src, 1 if has_alpha else 0, &c_loss
c_list, src, 1 if has_alpha else 0, cython.address(c_loss)
)
return get_video_format(best, 0, 0), c_loss
finally:
if c_list != NULL:
if c_list != cython.NULL:
free(c_list)
Loading