Skip to content

Commit ea6199f

Browse files
committed
Build filter pads on access instead of caching them
1 parent b618b2d commit ea6199f

8 files changed

Lines changed: 48 additions & 30 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Fixes:
3737
- Reading :attr:`.Frame.side_data` no longer holds the frame in a reference cycle, which kept its picture buffer alive until gc ran. By :gh-user:`WyattBlue`.
3838
- :class:`.MotionVectors` no longer memoizes the vectors it hands out, a cache that never evicted and put every one of them in a reference cycle with the side data, retaining 1640 objects for a single 640x360 frame. By :gh-user:`WyattBlue`.
3939
- :attr:`.Stream.index_entries` is now a view built on access rather than an object allocated for every stream whether or not anything reads it, which also takes the stream out of a reference cycle with it. Fixes (:issue:`2425`) by :gh-user:`WyattBlue`.
40+
- :class:`.Filter` and :class:`.FilterContext` pads are now built on access rather than cached, which takes a filter out of a reference cycle with its pads and makes a pad compare equal by what it denotes. By :gh-user:`WyattBlue`.
4041

4142

4243
v19.0.0

‎av/filter/context.pxd‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ cdef class FilterContext:
88
cdef lib.AVFilterContext *ptr
99
cdef readonly Graph graph
1010
cdef readonly Filter filter
11-
cdef tuple _inputs
12-
cdef tuple _outputs
1311
cdef bint inited
1412
cdef unsigned char _kind
1513

‎av/filter/context.py‎

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,11 @@ def name(self):
6464

6565
@property
6666
def inputs(self):
67-
if self._inputs is None:
68-
self._inputs = alloc_filter_pads(
69-
self.filter, self.ptr.input_pads, True, self
70-
)
71-
return self._inputs
67+
return alloc_filter_pads(self.filter, self.ptr.input_pads, True, self)
7268

7369
@property
7470
def outputs(self):
75-
if self._outputs is None:
76-
self._outputs = alloc_filter_pads(
77-
self.filter, self.ptr.output_pads, False, self
78-
)
79-
return self._outputs
71+
return alloc_filter_pads(self.filter, self.ptr.output_pads, False, self)
8072

8173
def init(self, args=None, **kwargs):
8274
if self.inited:
@@ -126,13 +118,14 @@ def push(self, frame: Frame | None):
126118
return
127119

128120
# Delegate to the input.
129-
if len(self.inputs) != 1:
121+
inputs = self.inputs
122+
if len(inputs) != 1:
130123
raise ValueError(
131-
f"cannot delegate push without single input; found {len(self.inputs)}"
124+
f"cannot delegate push without single input; found {len(inputs)}"
132125
)
133-
if not self.inputs[0].link:
126+
if not inputs[0].link:
134127
raise ValueError("cannot delegate push without linked input")
135-
self.inputs[0].linked.context.push(frame)
128+
inputs[0].linked.context.push(frame)
136129

137130
def pull(self):
138131
frame: Frame
@@ -143,13 +136,14 @@ def pull(self):
143136
frame = alloc_audio_frame()
144137
else:
145138
# Delegate to the output.
146-
if len(self.outputs) != 1:
139+
outputs = self.outputs
140+
if len(outputs) != 1:
147141
raise ValueError(
148-
f"cannot delegate pull without single output; found {len(self.outputs)}"
142+
f"cannot delegate pull without single output; found {len(outputs)}"
149143
)
150-
if not self.outputs[0].link:
144+
if not outputs[0].link:
151145
raise ValueError("cannot delegate pull without linked output")
152-
return self.outputs[0].linked.context.pull()
146+
return outputs[0].linked.context.pull()
153147

154148
self.graph.configure()
155149

‎av/filter/filter.pxd‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ cimport libav as lib
33

44
cdef class Filter:
55
cdef const lib.AVFilter *ptr
6-
cdef tuple _inputs
7-
cdef tuple _outputs
86

97

108
cdef Filter wrap_filter(const lib.AVFilter *ptr)

‎av/filter/filter.py‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,11 @@ def flags(self):
3939

4040
@property
4141
def inputs(self):
42-
if self._inputs is None:
43-
self._inputs = alloc_filter_pads(self, self.ptr.inputs, True)
44-
return self._inputs
42+
return alloc_filter_pads(self, self.ptr.inputs, True)
4543

4644
@property
4745
def outputs(self):
48-
if self._outputs is None:
49-
self._outputs = alloc_filter_pads(self, self.ptr.outputs, False)
50-
return self._outputs
46+
return alloc_filter_pads(self, self.ptr.outputs, False)
5147

5248

5349
@cython.cfunc

‎av/filter/link.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import cython
22
import cython.cimports.libav as lib
33
from cython.cimports.av.filter.graph import Graph
4+
from cython.cimports.libc.stdint import uintptr_t
45

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

@@ -65,6 +66,34 @@ def __repr__(self):
6566
f"<av.FilterPad {_filter}.{_io}[{self.index}]: {self.name} ({self.type})>"
6667
)
6768

69+
def __eq__(self, other):
70+
"""Two pads are equal when they denote the same pad.
71+
72+
Pads are built on access rather than cached, so ``is`` compares the
73+
wrappers while this compares what they point at.
74+
"""
75+
if not isinstance(other, FilterPad):
76+
return NotImplemented
77+
78+
o: FilterPad = other
79+
if self.base_ptr != o.base_ptr or self.index != o.index:
80+
return False
81+
if self.is_input != o.is_input:
82+
return False
83+
if (self.context is None) != (o.context is None):
84+
return False
85+
return self.context is None or self.context.ptr == o.context.ptr
86+
87+
def __hash__(self):
88+
return hash(
89+
(
90+
cython.cast(uintptr_t, self.base_ptr),
91+
self.index,
92+
self.is_input,
93+
0 if self.context is None else cython.cast(uintptr_t, self.context.ptr),
94+
)
95+
)
96+
6897
@property
6998
def is_output(self):
7099
return not self.is_input

‎av/filter/link.pyi‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class FilterPad:
77
context: FilterContext
88
is_input: bool
99
index: int
10+
def __eq__(self, other: object) -> bool: ...
11+
def __hash__(self) -> int: ...
1012
@property
1113
def is_output(self) -> bool: ...
1214
@property

‎tests/test_filters.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ def test_generator_graph(self):
7474
lutrgb.link_to(sink)
7575

7676
# pads and links
77-
assert src.outputs[0].link.output is lutrgb.inputs[0]
78-
assert lutrgb.inputs[0].link.input is src.outputs[0]
77+
assert src.outputs[0].link.output == lutrgb.inputs[0]
78+
assert lutrgb.inputs[0].link.input == src.outputs[0]
7979

8080
frame = sink.pull()
8181
assert isinstance(frame, VideoFrame)

0 commit comments

Comments
 (0)