Skip to content

Commit 75981c3

Browse files
committed
Make video/codeccontext pure
1 parent 714be34 commit 75981c3

7 files changed

Lines changed: 201 additions & 137 deletions

File tree

av/audio/codeccontext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@cython.cclass
1111
class AudioCodecContext(CodecContext):
1212
@cython.cfunc
13-
def _prepare_frames_for_encode(self, input_frame: Frame | None):
13+
def _prepare_frames_for_encode(self, input_frame: Frame | None) -> list:
1414
frame: AudioFrame | None = input_frame
1515
allow_var_frame_size: cython.bint = (
1616
self.ptr.codec.capabilities & lib.AV_CODEC_CAP_VARIABLE_FRAME_SIZE

av/codec/context.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ cdef class CodecContext:
4040
# TODO: Remove the `Packet` from `_setup_decoded_frame` (because flushing packets
4141
# are bogus). It should take all info it needs from the context and/or stream.
4242
cdef _prepare_and_time_rebase_frames_for_encode(self, Frame frame)
43-
cdef _prepare_frames_for_encode(self, Frame frame)
43+
cdef list _prepare_frames_for_encode(self, Frame frame)
4444
cdef _setup_encoded_packet(self, Packet)
4545
cdef _setup_decoded_frame(self, Frame, Packet)
4646

av/codec/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ def _send_packet_and_recv(self, packet: Packet | None):
386386
return out
387387

388388
@cython.cfunc
389-
def _prepare_frames_for_encode(self, frame: Frame | None):
389+
def _prepare_frames_for_encode(self, frame: Frame | None) -> list:
390390
return [frame]
391391

392392
@cython.cfunc

av/container/input.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,11 @@ def seek(
236236
self,
237237
offset,
238238
*,
239-
backward: bint = True,
240-
any_frame: bint = False,
239+
backward: cython.bint = True,
240+
any_frame: cython.bint = False,
241241
stream: Stream | None = None,
242-
unsupported_frame_offset: bint = False,
243-
unsupported_byte_offset: bint = False,
242+
unsupported_frame_offset: cython.bint = False,
243+
unsupported_byte_offset: cython.bint = False,
244244
):
245245
"""seek(offset, *, backward=True, any_frame=False, stream=None)
246246

av/logging.pyx renamed to av/logging.py

Lines changed: 105 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# type: ignore
12
"""
23
FFmpeg has a logging system that it uses extensively. It's very noisy, so PyAV turns it
34
off by default. This unfortunately has the effect of making raised errors have less
@@ -38,14 +39,15 @@
3839
3940
"""
4041

41-
cimport libav as lib
42-
from libc.stdio cimport fprintf, stderr
43-
from libc.stdlib cimport free, malloc
44-
4542
import logging
4643
import sys
4744
from 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.
5052
PANIC = lib.AV_LOG_PANIC # 0
5153
FATAL = lib.AV_LOG_FATAL # 8
@@ -60,9 +62,9 @@
6062
CRITICAL = 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:
@@ -79,7 +81,7 @@
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).
8587
if "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

142144
def 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+
332381
lib.av_log_set_callback(nolog_callback)

av/video/codeccontext.pxd

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,11 @@ cdef struct AVCodecPrivateData:
1616

1717

1818
cdef class VideoCodecContext(CodecContext):
19-
2019
cdef AVCodecPrivateData _private_data
21-
2220
cdef VideoFormat _format
2321
cdef _build_format(self)
24-
2522
cdef int last_w
2623
cdef int last_h
2724
cdef readonly VideoReformatter reformatter
28-
29-
# For encoding.
3025
cdef readonly int encoded_frame_count
31-
32-
# For decoding.
3326
cdef VideoFrame next_frame

0 commit comments

Comments
 (0)