From c87eaaa8cd7068ebf00d1ba9c50757882e028ccc Mon Sep 17 00:00:00 2001 From: Eitan Date: Tue, 9 May 2023 14:09:14 +0300 Subject: [PATCH 1/2] Implemented the PyAVReaderTimed.close method, and an indicator of whether the object's container is closed (_is_container_closed). The __del__ method handles a case where _container is closed (and hence a NoneType). --- pims/pyav_reader.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pims/pyav_reader.py b/pims/pyav_reader.py index 172c53e..af04e9d 100644 --- a/pims/pyav_reader.py +++ b/pims/pyav_reader.py @@ -143,6 +143,12 @@ def __init__(self, file, cache_size=16, fast_forward_thresh=32, self._reset_demuxer() + self._is_container_closed = False + + def __del__(self): + if not self._is_container_closed: + self._container.close() + def __len__(self): return int(self._duration * self._frame_rate) @@ -151,6 +157,10 @@ def _reset_demuxer(self): self._frame_generator = _gen_frames(demuxer, self._stream.time_base, self._frame_rate, self._first_pts) + def close(self): + self._container.close() + self._is_container_closed = True + @property def duration(self): """The video duration in seconds.""" @@ -175,6 +185,10 @@ def get_frame(self, i): if cached_i == i: return cached_frame.to_frame() + # Stop before seeking to frame, to avoid crashing + if self._is_container_closed: + raise ValueError("Cannot read uncached frame from closed file. Try other frame index or re-open file.") + # check if we will have to seek to the frame if self._last_frame >= i or \ self._last_frame < i - self._fast_forward_thresh: From 2f831ff13d39823c25004ceeeedce9b46a0054c9 Mon Sep 17 00:00:00 2001 From: Eitan Date: Tue, 9 May 2023 14:20:36 +0300 Subject: [PATCH 2/2] Added tests for using the PyAVReaderTimed.close method, and the context manager functionality to close the file. --- pims/tests/test_common.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pims/tests/test_common.py b/pims/tests/test_common.py index c183d9c..6da71f0 100644 --- a/pims/tests/test_common.py +++ b/pims/tests/test_common.py @@ -419,6 +419,28 @@ def setUp(self): self.expected_shape = (424, 640, 3) self.expected_len = 480 + def test_close(self): + vid = self.klass(self.filename) + + assert vid[10].shape == self.expected_shape + + vid.close() + + assert vid[9].shape == self.expected_shape + + with self.assertRaises(ValueError): + vid[11].shape + + def test_context_manager(self): + + with self.klass(self.filename) as vid: + assert vid[10].shape == self.expected_shape + + assert vid[9].shape == self.expected_shape + + with self.assertRaises(ValueError): + vid[11].shape + class TestVideo_PyAV_indexed(_image_series, unittest.TestCase): def check_skip(self):