1+ # type: ignore
12"""
23FFmpeg has a logging system that it uses extensively. It's very noisy, so PyAV turns it
34off by default. This unfortunately has the effect of making raised errors have less
3839
3940"""
4041
41- cimport libav as lib
42- from libc.stdio cimport fprintf, stderr
43- from libc.stdlib cimport free, malloc
42+ import cython
43+ import cython .cimports .libav as lib
44+ from cython .cimports .libc .stdio import fprintf , stderr
45+ from cython .cimports .libc .stdlib import free , malloc
4446
4547import logging
4648import sys
6062CRITICAL = FATAL
6163
6264
63- cpdef adapt_level(int level):
65+ @cython .ccall
66+ def adapt_level (level : cython .int ):
6467 """Convert a library log level to a Python log level."""
65-
6668 if level <= lib .AV_LOG_FATAL : # Includes PANIC
6769 return 50 # logging.CRITICAL
6870 elif level <= lib .AV_LOG_ERROR :
7981 return 1
8082
8183
82- cdef object level_threshold = None
84+ level_threshold = cython . declare ( object , None )
8385
8486# ... but lets limit ourselves to WARNING (assuming nobody already did this).
8587if "libav" not in logging .Logger .manager .loggerDict :
@@ -133,10 +135,10 @@ def restore_default_callback():
133135 lib .av_log_set_callback (lib .av_log_default_callback )
134136
135137
136- cdef bint skip_repeated = True
137- cdef skip_lock = Lock()
138- cdef object last_log = None
139- cdef int skip_count = 0
138+ skip_repeated = cython . declare ( cython . bint , True )
139+ skip_lock = cython . declare ( object , Lock () )
140+ last_log = cython . declare ( object , None )
141+ skip_count = cython . declare ( cython . int , 0 )
140142
141143
142144def get_skip_repeated ():
@@ -151,10 +153,12 @@ def set_skip_repeated(v):
151153
152154
153155# For error reporting.
154- cdef object last_error = None
155- cdef int error_count = 0
156+ last_error = cython .declare (object , None )
157+ error_count = cython .declare (cython .int , 0 )
158+
156159
157- cpdef get_last_error():
160+ @cython .ccall
161+ def get_last_error ():
158162 """Get the last log that was at least ``ERROR``."""
159163 if error_count :
160164 with skip_lock :
@@ -163,10 +167,12 @@ def set_skip_repeated(v):
163167 return 0 , None
164168
165169
166- cdef global_captures = []
167- cdef thread_captures = {}
170+ global_captures = cython .declare (list , [])
171+ thread_captures = cython .declare (dict , {})
172+
168173
169- cdef class Capture:
174+ @cython .cclass
175+ class Capture :
170176 """A context manager for capturing logs.
171177
172178 :param bool local: Should logs from all threads be captured, or just one
@@ -181,12 +187,11 @@ def set_skip_repeated(v):
181187
182188 """
183189
184- cdef readonly list logs
185- cdef list captures
190+ logs : list
191+ captures : list
186192
187- def __init__ (self , bint local = True ):
193+ def __init__ (self , local : cython . bint = True ):
188194 self .logs = []
189-
190195 if local :
191196 self .captures = thread_captures .setdefault (get_ident (), [])
192197 else :
@@ -200,53 +205,67 @@ def __exit__(self, type_, value, traceback):
200205 self .captures .pop (- 1 )
201206
202207
203- cdef struct log_context:
204- lib.AVClass * class_
205- const char * name
208+ log_context = cython .struct (
209+ class_ = cython .pointer [lib .AVClass ],
210+ name = cython .p_char ,
211+ )
212+
213+ item_name_func = cython .typedef (
214+ "const char *(*item_name_func)(void *) noexcept nogil"
215+ )
216+
206217
207- cdef const char * log_context_name(void * ptr) noexcept nogil:
208- cdef log_context * obj = < log_context* > ptr
218+ @cython .cfunc
219+ @cython .nogil
220+ @cython .exceptval (check = False )
221+ def log_context_name (ptr : cython .p_void ) -> cython .p_char :
222+ obj : cython .pointer [log_context ] = cython .cast (cython .pointer [log_context ], ptr )
209223 return obj .name
210224
211- cdef lib.AVClass log_class
212- log_class.item_name = log_context_name
213225
214- cpdef log(int level, str name, str message):
226+ log_class = cython .declare (lib .AVClass )
227+ log_class .item_name = cython .cast (item_name_func , log_context_name )
228+
229+
230+ @cython .ccall
231+ def log (level : cython .int , name : str , message : str ):
215232 """Send a log through the library logging system.
216233
217234 This is mostly for testing.
218-
219235 """
220-
221- cdef log_context * obj = < log_context* > malloc(sizeof(log_context))
222- obj.class_ = & log_class
236+ obj : cython .pointer [log_context ] = cython .cast (
237+ cython .pointer [log_context ], malloc (cython .sizeof (log_context ))
238+ )
239+ obj .class_ = cython .address (log_class )
223240 obj .name = name
224- cdef bytes message_bytes = message.encode(" utf-8" )
241+ message_bytes : bytes = message .encode ("utf-8" )
225242
226- lib.av_log(< void * > obj, level, " %s " , < char * > message_bytes)
243+ lib .av_log (cython . cast ( cython . p_void , obj ) , level , "%s" , cython . cast ( cython . p_char , message_bytes ) )
227244 free (obj )
228245
229246
230- cdef log_callback_gil(int level, const char * c_name, const char * c_message):
247+ @cython .cfunc
248+ def log_callback_gil (
249+ level : cython .int , c_name : cython .p_const_char , c_message : cython .p_char
250+ ):
231251 global error_count
232252 global skip_count
233253 global last_log
234254 global last_error
235255
236- name = < str > c_name if c_name is not NULL else " "
237- message = ( < bytes> c_message).decode(" utf8" , " backslashreplace" )
256+ name = cython . cast ( str , c_name ) if c_name is not cython . NULL else ""
257+ message = cython . cast ( bytes , c_message ).decode ("utf8" , "backslashreplace" )
238258 log = (level , name , message )
239259
240260 # We have to filter it ourselves, but we will still process it in general so
241261 # it is available to our error handling.
242262 # Note that FFmpeg's levels are backwards from Python's.
243- cdef bint is_interesting = level <= level_threshold
263+ is_interesting : cython . bint = level <= level_threshold
244264
245265 # Skip messages which are identical to the previous.
246266 # TODO: Be smarter about threads.
247- cdef bint is_repeated = False
248-
249- cdef object repeat_log = None
267+ is_repeated : cython .bint = False
268+ repeat_log : object = None
250269
251270 with skip_lock :
252271 if is_interesting :
@@ -263,7 +282,7 @@ def __exit__(self, type_, value, traceback):
263282 repeat_log = (
264283 last_log [0 ],
265284 last_log [1 ],
266- " %s (repeated %d more times)" % (last_log[2 ], skip_count)
285+ "%s (repeated %d more times)" % (last_log [2 ], skip_count ),
267286 )
268287 skip_count = 0
269288
@@ -281,7 +300,8 @@ def __exit__(self, type_, value, traceback):
281300 log_callback_emit (log )
282301
283302
284- cdef log_callback_emit(log):
303+ @cython .cfunc
304+ def log_callback_emit (log ):
285305 lib_level , name , message = log
286306
287307 captures = thread_captures .get (get_ident ()) or global_captures
@@ -296,37 +316,63 @@ def __exit__(self, type_, value, traceback):
296316 logger .log (py_level , message .strip ())
297317
298318
299- cdef void log_callback(void * ptr, int level, const char * format, lib.va_list args) noexcept nogil:
300- cdef bint inited = lib.Py_IsInitialized()
319+ @cython .cfunc
320+ @cython .nogil
321+ @cython .exceptval (check = False )
322+ def log_callback (
323+ ptr : cython .p_void ,
324+ level : cython .int ,
325+ format : cython .p_const_char ,
326+ args : lib .va_list ,
327+ ) -> cython .void :
328+ inited : cython .bint = lib .Py_IsInitialized ()
301329 if not inited :
302330 return
303331
304- with gil:
332+ with cython . gil :
305333 if level > level_threshold and level != lib .AV_LOG_ERROR :
306334 return
307335
308336 # Format the message.
309- cdef char message [1024 ]
337+ message : cython . char [1024 ]
310338 lib .vsnprintf (message , 1023 , format , args )
311339
312340 # Get the name.
313- cdef const char * name = NULL
314- cdef lib.AVClass * cls = (< lib.AVClass** > ptr)[0 ] if ptr else NULL
341+ name : cython .p_const_char = cython .NULL
342+ cls : cython .pointer [lib .AVClass ] = (
343+ cython .cast (cython .pointer [cython .pointer [lib .AVClass ]], ptr )[0 ]
344+ if ptr
345+ else cython .NULL
346+ )
315347 if cls and cls .item_name :
316348 name = cls .item_name (ptr )
317349
318- with gil:
350+ with cython . gil :
319351 try :
320352 log_callback_gil (level , name , message )
321353 except Exception :
322- fprintf(stderr, " av.logging: exception while handling %s [%d ]: %s \n " ,
323- name, level, message)
354+ fprintf (
355+ stderr ,
356+ "av.logging: exception while handling %s[%d]: %s\n " ,
357+ name ,
358+ level ,
359+ message ,
360+ )
324361 # For some reason lib.PyErr_PrintEx(0) won't work.
325362 exc , type_ , tb = sys .exc_info ()
326363 lib .PyErr_Display (exc , type_ , tb )
327364
328365
329- cdef void nolog_callback(void * ptr, int level, const char * format, lib.va_list args) noexcept nogil:
366+ @cython .cfunc
367+ @cython .nogil
368+ @cython .exceptval (check = False )
369+ def nolog_callback (
370+ ptr : cython .p_void ,
371+ level : cython .int ,
372+ format : cython .p_const_char ,
373+ args : lib .va_list ,
374+ ) -> cython .void :
330375 pass
331376
377+
332378lib .av_log_set_callback (nolog_callback )
0 commit comments