Skip to content

Commit b618b2d

Browse files
committed
Stop side data holding its frame in a reference cycle
1 parent b348aac commit b618b2d

8 files changed

Lines changed: 12 additions & 25 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ We are operating with `semantic versioning <https://semver.org>`_.
2929
- $CHANGE by :gh-user:`mikeboers` in (:pr:`1`).
3030

3131

32-
v19.0.1
33-
-------
32+
v19.0.1 (Unreleased)
33+
--------------------
3434

3535
Fixes:
3636

37+
- 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`.
38+
- :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`.
39+
- :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`.
3740

3841

3942
v19.0.0

‎av/frame.pxd‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
cimport libav as lib
22

33
from av.packet cimport Packet
4-
from av.sidedata.sidedata cimport _SideDataContainer
54

65

76
cdef class Frame:
87
cdef lib.AVFrame *ptr
98
# We define our own time.
109
cdef lib.AVRational _time_base
1110
cdef void _rebase_time(self, lib.AVRational)
12-
cdef _SideDataContainer _side_data
1311
cdef void _copy_internal_attributes(self, Frame source, bint data_layout=?)
1412
cdef void _init_user_attributes(self)

‎av/frame.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ def key_frame(self, v):
181181

182182
@property
183183
def side_data(self):
184-
if self._side_data is None:
185-
self._side_data = SideDataContainer(self)
186-
return self._side_data
184+
return SideDataContainer(self)
187185

188186
@property
189187
def metadata(self):

‎av/frame.pyi‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ class Frame:
1111
def time_base(self) -> AVRational: ...
1212
@time_base.setter
1313
def time_base(self, value: AVRational | Fraction | int) -> None: ...
14-
side_data: SideDataContainer
14+
@property
15+
def side_data(self) -> SideDataContainer: ...
1516
opaque: object
1617
@property
1718
def metadata(self) -> dict[str, str]: ...

‎av/sidedata/motionvectors.pxd‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ from av.sidedata.sidedata cimport SideData
55

66

77
cdef class MotionVectors(SideData):
8-
cdef dict _vectors
98
cdef Py_ssize_t _len
109

1110

‎av/sidedata/motionvectors.py‎

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
class MotionVectors(SideData):
1212
def __init__(self, sentinel, frame: Frame, index: cython.int):
1313
SideData.__init__(self, sentinel, frame, index)
14-
self._vectors = {}
1514
self._len = self.ptr.size // cython.sizeof(lib.AVMotionVector)
1615

1716
def __repr__(self):
@@ -29,15 +28,7 @@ def __getitem__(self, index: cython.Py_ssize_t):
2928
if index < 0 or index >= self._len:
3029
raise IndexError(index)
3130

32-
try:
33-
return self._vectors[index]
34-
except KeyError:
35-
pass
36-
37-
vector = self._vectors[index] = MotionVector(
38-
_cinit_bypass_sentinel, self, index
39-
)
40-
return vector
31+
return MotionVector(_cinit_bypass_sentinel, self, index)
4132

4233
def __iter__(self):
4334
"""Iterate over all motion vectors."""

‎av/sidedata/motionvectors.pyi‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
from typing import Any, overload
1+
from collections.abc import Iterator
2+
from typing import Any
23

34
import numpy as np
45

56
from .sidedata import SideData
67

78
class MotionVectors(SideData):
8-
@overload
99
def __getitem__(self, index: int) -> MotionVector: ...
10-
@overload
11-
def __getitem__(self, index: slice) -> list[MotionVector]: ...
10+
def __iter__(self) -> Iterator[MotionVector]: ...
1211
def __len__(self) -> int: ...
1312
def to_ndarray(self) -> np.ndarray[Any, Any]: ...
1413

‎av/sidedata/sidedata.pyi‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ from enum import Enum
33
from typing import ClassVar, cast, overload
44

55
from av.buffer import Buffer
6-
from av.frame import Frame
76

87
class Type(Enum):
98
PANSCAN = cast(ClassVar[Type], ...)
@@ -43,7 +42,6 @@ class SideData(Buffer):
4342
type: Type
4443

4544
class SideDataContainer(Mapping):
46-
frame: Frame
4745
def __len__(self) -> int: ...
4846
def __iter__(self) -> Iterator[Type]: ...
4947
@overload

0 commit comments

Comments
 (0)