From 19404bf68e83dfce0f10de37fcbcedb5823672e0 Mon Sep 17 00:00:00 2001 From: James Weaver Date: Thu, 7 Nov 2019 16:51:02 +0000 Subject: [PATCH] v2.7.2: Restore some legacy behaviour with regard to adding grains to segments --- CHANGELOG.md | 3 +++ mediagrains/gsf.py | 33 +++++++++++++++++++++++++++------ setup.py | 2 +- tests/test_gsf.py | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eaf3f0..d25d703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Mediagrains Library Changelog +## 2.7.2 +- Bugfix: Restore behaviour whereby grains can be added to a segment object during an active progressive encode + ## 2.7.1 - Bugfix: Restore behaviour whereby `gsf.GSFEncoder.dump` calls `gsf.GSFEncoder.start_dump` and `gsf.GSFEncoder.end_dump` (this matters if subclasses have overridden these methods) diff --git a/mediagrains/gsf.py b/mediagrains/gsf.py index 534c3cb..9b814a4 100644 --- a/mediagrains/gsf.py +++ b/mediagrains/gsf.py @@ -1071,7 +1071,7 @@ def add_segment(self, id: Optional[UUID] = None, local_id: Optional[int] = None, if self._active_dump: raise GSFEncodeAddToActiveDump("Cannot add a new segment {} ({!s}) to an encoder that is currently dumping".format(local_id, id)) - seg = GSFEncoderSegment(id, local_id, tags=tags) + seg = GSFEncoderSegment(id, local_id, tags=tags, parent=self) self._segments[local_id] = seg return seg @@ -1471,7 +1471,7 @@ def add_segment(self, id: Optional[UUID] = None, local_id: Optional[int] = None, if id is None: id = uuid1() - seg = GSFEncoderSegment(id, local_id, tags=tags) + seg = GSFEncoderSegment(id, local_id, tags=tags, parent=self) self._segments[local_id] = seg return seg @@ -1585,7 +1585,11 @@ def __eq__(self, other: object) -> bool: class GSFEncoderSegment(object): """A class to represent a segment within a GSF file, used for constructing them.""" - def __init__(self, id: UUID, local_id: int, tags: Iterable[Tuple[str, str]] = None): + def __init__(self, + id: UUID, + local_id: int, + tags: Iterable[Tuple[str, str]] = None, + parent: Optional[Union[GSFEncoder, OpenGSFEncoderBase]] = None): self.id = id self.local_id = local_id self._write_count = 0 @@ -1593,6 +1597,7 @@ def __init__(self, id: UUID, local_id: int, tags: Iterable[Tuple[str, str]] = No self._active_dump: bool = False self._tags: List[GSFEncoderTag] = [] self._grains: List[GRAIN] = [] + self._parent = parent if tags is not None: for tag in tags: @@ -1601,6 +1606,14 @@ def __init__(self, id: UUID, local_id: int, tags: Iterable[Tuple[str, str]] = No except (TypeError, IndexError): raise GSFEncodeError("No idea how to turn {!r} into a tag".format(tag)) + def _get_parent_open_encoder(self) -> Optional[OpenGSFEncoder]: + if isinstance(self._parent, OpenGSFEncoder): + return self._parent + elif isinstance(self._parent, GSFEncoder): + return self._parent._open_encoder + else: + return None + @property def count(self) -> int: return len(self._grains) + self._write_count @@ -1833,10 +1846,18 @@ def add_tag(self, key: str, value: str): def add_grain(self, grain: GRAIN): """Add a grain to the segment, which should be a Grain object""" - self._grains.append(grain) + parent = self._get_parent_open_encoder() + if parent is not None and parent._active_dump: + parent.add_grain(grain, segment_id=self.id, segment_local_id=self.local_id) + else: + self._grains.append(grain) def add_grains(self, grains: Iterable[GRAIN]): """Add several grains to the segment, the parameter should be an iterable of grain objects""" - for grain in grains: - self.add_grain(grain) + parent = self._get_parent_open_encoder() + if parent is not None and parent._active_dump: + parent.add_grains(grains, segment_id=self.id, segment_local_id=self.local_id) + else: + for grain in grains: + self.add_grain(grain) diff --git a/setup.py b/setup.py index 8b4c697..cb67dd7 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ ] setup(name="mediagrains", - version="2.7.1", + version="2.7.2", python_requires='>=3.6.0', description="Simple utility for grain-based media", url='https://github.com/bbc/rd-apmm-python-lib-mediagrains', diff --git a/tests/test_gsf.py b/tests/test_gsf.py index 886b05c..a0e5a11 100644 --- a/tests/test_gsf.py +++ b/tests/test_gsf.py @@ -1040,6 +1040,47 @@ def test_dump_progressively__deprecated(self): self.assertEqual(len(segments2[1]), 2) self.assertEqual(len(segments3[1]), 2) + @suppress_deprecation_warnings + def test_dump_progressively_with_segments__deprecated(self): + src_id = UUID('e14e9d58-1567-11e8-8dd3-831a068eb034') + flow_id = UUID('ee1eed58-1567-11e8-a971-3b901a2dd8ab') + grain0 = VideoGrain(src_id, flow_id, cog_frame_format=CogFrameFormat.S16_422_10BIT, width=1920, height=1080) + grain1 = VideoGrain(src_id, flow_id, cog_frame_format=CogFrameFormat.S16_422_10BIT, width=1920, height=1080) + + uuids = [UUID('7920b394-1565-11e8-86e0-8b42d4647ba8'), + UUID('80af875c-1565-11e8-8f44-87ef081b48cd')] + created = datetime(1983, 3, 29, 15, 15) + + file = BytesIO() + with mock.patch('mediagrains.gsf.datetime', side_effect=datetime, now=mock.MagicMock(return_value=created)): + with mock.patch('mediagrains.gsf.uuid1', side_effect=uuids): + enc = GSFEncoder(file) + seg = enc.add_segment() + self.assertEqual(len(file.getvalue()), 0) + enc.start_dump() + dump0 = file.getvalue() + (head0, segments0) = loads(dump0) + seg.add_grain(grain0) + dump1 = file.getvalue() + (head1, segments1) = loads(dump1) + seg.add_grain(grain1, segment_local_id=1) + dump2 = file.getvalue() + (head2, segments2) = loads(dump2) + enc.end_dump() + dump3 = file.getvalue() + (head3, segments3) = loads(dump3) + + self.assertEqual(head0['segments'][0]['count'], -1) + self.assertEqual(head1['segments'][0]['count'], -1) + self.assertEqual(head2['segments'][0]['count'], -1) + self.assertEqual(head3['segments'][0]['count'], 2) + + if 1 in segments0: + self.assertEqual(len(segments0[1]), 0) + self.assertEqual(len(segments1[1]), 1) + self.assertEqual(len(segments2[1]), 2) + self.assertEqual(len(segments3[1]), 2) + def test_dump_progressively(self): src_id = UUID('e14e9d58-1567-11e8-8dd3-831a068eb034') flow_id = UUID('ee1eed58-1567-11e8-a971-3b901a2dd8ab')