1- cimport libav as lib
1+ import cython
2+ import cython .cimports .libav as lib
3+ from cython .cimports .av .stream import Stream
24
35
46def _flatten (input_ ):
@@ -9,7 +11,9 @@ def _flatten(input_):
911 else :
1012 yield x
1113
12- cdef lib.AVMediaType _get_media_type_enum(str type ):
14+
15+ @cython .cfunc
16+ def _get_media_type_enum (type : str ) -> lib .AVMediaType :
1317 if type == "video" :
1418 return lib .AVMEDIA_TYPE_VIDEO
1519 elif type == "audio" :
@@ -23,7 +27,28 @@ def _flatten(input_):
2327 else :
2428 raise ValueError (f"Invalid stream type: { type } " )
2529
26- cdef class StreamContainer:
30+
31+ @cython .cfunc
32+ @cython .exceptval (check = False )
33+ def _get_best_stream_index (
34+ fmtctx : cython .pointer [lib .AVFormatContext ],
35+ enumtype : lib .AVMediaType ,
36+ related : Stream | None ,
37+ ) -> cython .int :
38+ stream_index : cython .int
39+
40+ if related is None :
41+ stream_index = lib .av_find_best_stream (fmtctx , enumtype , - 1 , - 1 , cython .NULL , 0 )
42+ else :
43+ stream_index = lib .av_find_best_stream (
44+ fmtctx , enumtype , - 1 , related .ptr .index , cython .NULL , 0
45+ )
46+
47+ return stream_index
48+
49+
50+ @cython .cclass
51+ class StreamContainer :
2752 """
2853
2954 A tuple-like container of :class:`Stream`.
@@ -40,31 +65,12 @@ def _flatten(input_):
4065
4166 def __cinit__ (self ):
4267 self ._streams = []
43- self .video = ()
44- self .audio = ()
45- self .subtitles = ()
46- self .data = ()
47- self .attachments = ()
48- self .other = ()
49-
50- cdef add_stream(self , Stream stream):
5168
69+ @cython .cfunc
70+ def add_stream (self , stream : Stream ) -> cython .void :
5271 assert stream .ptr .index == len (self ._streams )
5372 self ._streams .append (stream )
5473
55- if stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_VIDEO:
56- self .video = self .video + (stream, )
57- elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_AUDIO:
58- self .audio = self .audio + (stream, )
59- elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_SUBTITLE:
60- self .subtitles = self .subtitles + (stream, )
61- elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_ATTACHMENT:
62- self .attachments = self .attachments + (stream, )
63- elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_DATA:
64- self .data = self .data + (stream, )
65- else :
66- self .other = self .other + (stream, )
67-
6874 # Basic tuple interface.
6975 def __len__ (self ):
7076 return len (self ._streams )
@@ -78,6 +84,26 @@ def __getitem__(self, index):
7884 else :
7985 return self .get (index )
8086
87+ @property
88+ def video (self ):
89+ return tuple (s for s in self ._streams if s .type == "video" )
90+
91+ @property
92+ def audio (self ):
93+ return tuple (s for s in self ._streams if s .type == "audio" )
94+
95+ @property
96+ def subtitles (self ):
97+ return tuple (s for s in self ._streams if s .type == "subtitle" )
98+
99+ @property
100+ def data (self ):
101+ return tuple (s for s in self ._streams if s .type == "data" )
102+
103+ @property
104+ def attachments (self ):
105+ return tuple (s for s in self ._streams if s .type == "attachment" )
106+
81107 def get (self , * args , ** kwargs ):
82108 """get(streams=None, video=None, audio=None, subtitles=None, data=None)
83109
@@ -120,7 +146,9 @@ def get(self, *args, **kwargs):
120146
121147 elif isinstance (x , dict ):
122148 for type_ , indices in x .items ():
123- if type_ == " streams" : # For compatibility with the pseudo signature
149+ if (
150+ type_ == "streams"
151+ ): # For compatibility with the pseudo signature
124152 streams = self ._streams
125153 else :
126154 streams = getattr (self , type_ )
@@ -134,17 +162,7 @@ def get(self, *args, **kwargs):
134162
135163 return selection or self ._streams [:]
136164
137- cdef int _get_best_stream_index(self , Container container, lib.AVMediaType type_enum, Stream related) noexcept:
138- cdef int stream_index
139-
140- if related is None :
141- stream_index = lib.av_find_best_stream(container.ptr, type_enum, - 1 , - 1 , NULL , 0 )
142- else :
143- stream_index = lib.av_find_best_stream(container.ptr, type_enum, - 1 , related.ptr.index, NULL , 0 )
144-
145- return stream_index
146-
147- def best (self , str type , /, Stream related = None ):
165+ def best (self , type : str , / , related : Stream | None = None ):
148166 """best(type: Literal["video", "audio", "subtitle", "attachment", "data"], /, related: Stream | None)
149167 Finds the "best" stream in the file. Wraps :ffmpeg:`av_find_best_stream`. Example::
150168
@@ -155,14 +173,14 @@ def best(self, str type, /, Stream related = None):
155173 :return: The best stream of the specified type
156174 :rtype: Stream | None
157175 """
158- cdef type_enum = _get_media_type_enum(type )
159-
160176 if len (self ._streams ) == 0 :
161177 return None
162178
163- cdef container = self ._streams[0 ].container
164-
165- cdef int stream_index = self ._get_best_stream_index(container, type_enum, related)
179+ first_stream : Stream = cython .cast (Stream , self ._streams [0 ])
180+ container : cython .pointer [lib .AVFormatContext ] = first_stream .container .ptr
181+ stream_index : cython .int = _get_best_stream_index (
182+ container , _get_media_type_enum (type ), related
183+ )
166184
167185 if stream_index < 0 :
168186 return None
0 commit comments