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: 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):