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
44-
4542import logging
4643import sys
4744from threading import Lock , get_ident
4845
46+ import cython
47+ import cython .cimports .libav as lib
48+ from cython .cimports .libc .stdio import fprintf , stderr
49+ from cython .cimports .libc .stdlib import free , malloc
50+
4951# Library levels.
5052PANIC = lib .AV_LOG_PANIC # 0
5153FATAL = lib .AV_LOG_FATAL # 8
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 , {})
168172
169- cdef class Capture:
173+
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 = cython . declare ( list , visibility = "readonly" )
191+ captures = cython . declare ( list , visibility = "private" )
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 :
@@ -197,56 +202,73 @@ def __enter__(self):
197202 return self .logs
198203
199204 def __exit__ (self , type_ , value , traceback ):
200- self .captures.pop(- 1 )
205+ self .captures .pop ()
206+
207+
208+ log_context = cython .struct (
209+ class_ = cython .pointer [lib .AVClass ],
210+ name = cython .p_char ,
211+ )
201212
213+ item_name_func = cython .typedef ("const char *(*item_name_func)(void *) noexcept nogil" )
202214
203- cdef struct log_context:
204- lib.AVClass * class_
205- const char * name
206215
207- cdef const char * log_context_name(void * ptr) noexcept nogil:
208- cdef log_context * obj = < log_context* > ptr
216+ @cython .cfunc
217+ @cython .nogil
218+ @cython .exceptval (check = False )
219+ def log_context_name (ptr : cython .p_void ) -> cython .p_char :
220+ obj : cython .pointer [log_context ] = cython .cast (cython .pointer [log_context ], ptr )
209221 return obj .name
210222
211- cdef lib.AVClass log_class
212- log_class.item_name = log_context_name
213223
214- cpdef log(int level, str name, str message):
224+ log_class = cython .declare (lib .AVClass )
225+ log_class .item_name = cython .cast (item_name_func , log_context_name )
226+
227+
228+ @cython .ccall
229+ def log (level : cython .int , name : str , message : str ):
215230 """Send a log through the library logging system.
216231
217232 This is mostly for testing.
218-
219233 """
220-
221- cdef log_context * obj = < log_context* > malloc(sizeof(log_context))
222- obj.class_ = & log_class
234+ obj : cython .pointer [log_context ] = cython .cast (
235+ cython .pointer [log_context ], malloc (cython .sizeof (log_context ))
236+ )
237+ obj .class_ = cython .address (log_class )
223238 obj .name = name
224- cdef bytes message_bytes = message.encode(" utf-8" )
225-
226- lib.av_log(< void * > obj, level, " %s " , < char * > message_bytes)
239+ message_bytes : bytes = message .encode ("utf-8" )
240+
241+ lib .av_log (
242+ cython .cast (cython .p_void , obj ),
243+ level ,
244+ "%s" ,
245+ cython .cast (cython .p_char , message_bytes ),
246+ )
227247 free (obj )
228248
229249
230- cdef log_callback_gil(int level, const char * c_name, const char * c_message):
250+ @cython .cfunc
251+ def log_callback_gil (
252+ level : cython .int , c_name : cython .p_const_char , c_message : cython .p_char
253+ ):
231254 global error_count
232255 global skip_count
233256 global last_log
234257 global last_error
235258
236- name = < str > c_name if c_name is not NULL else " "
237- message = ( < bytes> c_message).decode(" utf8" , " backslashreplace" )
259+ name = cython . cast ( str , c_name ) if c_name is not cython . NULL else ""
260+ message = cython . cast ( bytes , c_message ).decode ("utf8" , "backslashreplace" )
238261 log = (level , name , message )
239262
240263 # We have to filter it ourselves, but we will still process it in general so
241264 # it is available to our error handling.
242265 # Note that FFmpeg's levels are backwards from Python's.
243- cdef bint is_interesting = level <= level_threshold
266+ is_interesting : cython . bint = level <= level_threshold
244267
245268 # Skip messages which are identical to the previous.
246269 # TODO: Be smarter about threads.
247- cdef bint is_repeated = False
248-
249- cdef object repeat_log = None
270+ is_repeated : cython .bint = False
271+ repeat_log : object = None
250272
251273 with skip_lock :
252274 if is_interesting :
@@ -263,7 +285,7 @@ def __exit__(self, type_, value, traceback):
263285 repeat_log = (
264286 last_log [0 ],
265287 last_log [1 ],
266- " %s (repeated %d more times)" % (last_log[2 ], skip_count)
288+ "%s (repeated %d more times)" % (last_log [2 ], skip_count ),
267289 )
268290 skip_count = 0
269291
@@ -281,7 +303,8 @@ def __exit__(self, type_, value, traceback):
281303 log_callback_emit (log )
282304
283305
284- cdef log_callback_emit(log):
306+ @cython .cfunc
307+ def log_callback_emit (log ):
285308 lib_level , name , message = log
286309
287310 captures = thread_captures .get (get_ident ()) or global_captures
@@ -296,37 +319,63 @@ def __exit__(self, type_, value, traceback):
296319 logger .log (py_level , message .strip ())
297320
298321
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()
322+ @cython .cfunc
323+ @cython .nogil
324+ @cython .exceptval (check = False )
325+ def log_callback (
326+ ptr : cython .p_void ,
327+ level : cython .int ,
328+ format : cython .p_const_char ,
329+ args : lib .va_list ,
330+ ) -> cython .void :
331+ inited : cython .bint = lib .Py_IsInitialized ()
301332 if not inited :
302333 return
303334
304- with gil:
335+ with cython . gil :
305336 if level > level_threshold and level != lib .AV_LOG_ERROR :
306337 return
307338
308339 # Format the message.
309- cdef char message [1024 ]
340+ message : cython . char [1024 ]
310341 lib .vsnprintf (message , 1023 , format , args )
311342
312343 # Get the name.
313- cdef const char * name = NULL
314- cdef lib.AVClass * cls = (< lib.AVClass** > ptr)[0 ] if ptr else NULL
344+ name : cython .p_const_char = cython .NULL
345+ cls : cython .pointer [lib .AVClass ] = (
346+ cython .cast (cython .pointer [cython .pointer [lib .AVClass ]], ptr )[0 ]
347+ if ptr
348+ else cython .NULL
349+ )
315350 if cls and cls .item_name :
316351 name = cls .item_name (ptr )
317352
318- with gil:
353+ with cython . gil :
319354 try :
320355 log_callback_gil (level , name , message )
321356 except Exception :
322- fprintf(stderr, " av.logging: exception while handling %s [%d ]: %s \n " ,
323- name, level, message)
357+ fprintf (
358+ stderr ,
359+ "av.logging: exception while handling %s[%d]: %s\n " ,
360+ name ,
361+ level ,
362+ message ,
363+ )
324364 # For some reason lib.PyErr_PrintEx(0) won't work.
325365 exc , type_ , tb = sys .exc_info ()
326366 lib .PyErr_Display (exc , type_ , tb )
327367
328368
329- cdef void nolog_callback(void * ptr, int level, const char * format, lib.va_list args) noexcept nogil:
369+ @cython .cfunc
370+ @cython .nogil
371+ @cython .exceptval (check = False )
372+ def nolog_callback (
373+ ptr : cython .p_void ,
374+ level : cython .int ,
375+ format : cython .p_const_char ,
376+ args : lib .va_list ,
377+ ) -> cython .void :
330378 pass
331379
380+
332381lib .av_log_set_callback (nolog_callback )
0 commit comments