Example #1
0
    def test_profile_version_restriction(self):
        # A sequence with no pictures but (incorrectly) major_version 1 and
        # profile high quality.
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    parse_parameters=bitstream.ParseParameters(
                        major_version=1,
                        profile=tables.Profiles.high_quality,
                    ),
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=4,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=4,
                        ),
                    ),
                ),
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        populate_parse_offsets(seq)

        state = bytes_to_state(serialise_to_bytes(seq))
        with pytest.raises(decoder.ProfileNotSupportedByVersion):
            decoder.parse_stream(state)
    def test_non_empty_sequence(self):
        sh = bitstream.SequenceHeader()

        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=sh,
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=sh,
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])

        sh_length = len(serialise_to_bytes(sh))
        populate_parse_offsets(seq)

        assert seq["data_units"][0]["parse_info"]["next_parse_offset"] == (
            tables.PARSE_INFO_HEADER_BYTES + sh_length)
        assert seq["data_units"][0]["parse_info"]["previous_parse_offset"] == 0

        assert seq["data_units"][1]["parse_info"]["next_parse_offset"] == (
            tables.PARSE_INFO_HEADER_BYTES + sh_length)
        assert seq["data_units"][1]["parse_info"]["previous_parse_offset"] == (
            tables.PARSE_INFO_HEADER_BYTES + sh_length)

        assert seq["data_units"][2]["parse_info"]["next_parse_offset"] == 0
        assert seq["data_units"][2]["parse_info"]["previous_parse_offset"] == (
            tables.PARSE_INFO_HEADER_BYTES + sh_length)
def padding_sequence_bitstream_fname(tmpdir):
    fname = str(tmpdir.join("bitstream.vc2"))

    with open(fname, "wb") as f:
        context = bitstream.Sequence(
            data_units=[
                bitstream.DataUnit(
                    parse_info=bitstream.ParseInfo(
                        parse_code=tables.ParseCodes.padding_data,
                        next_parse_offset=tables.PARSE_INFO_HEADER_BYTES + 2,
                    ),
                    padding=bitstream.Padding(bytes=b"\xAA\xFF"),
                ),
                bitstream.DataUnit(
                    parse_info=bitstream.ParseInfo(
                        parse_code=tables.ParseCodes.end_of_sequence,
                    )
                ),
            ]
        )
        w = bitstream.BitstreamWriter(f)
        with bitstream.Serialiser(w, context, bitstream.vc2_default_values) as ser:
            bitstream.parse_sequence(ser, State())

    return fname
Example #4
0
    def test_parse_code_version_restriction(self):
        # A sequence with 1 HQ picture fragment but (incorrectly) major_version 2
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    parse_parameters=bitstream.ParseParameters(major_version=2,
                                                               ),
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=4,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=4,
                        ),
                    ),
                ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        fragment_slice_count=0, ),
                    transform_parameters=bitstream.TransformParameters(
                        slice_parameters=bitstream.SliceParameters(
                            slices_x=1,
                            slices_y=1,
                        ), ),
                ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        fragment_slice_count=1, ), ),
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        populate_parse_offsets(seq)

        state = bytes_to_state(serialise_to_bytes(seq))
        with pytest.raises(decoder.ParseCodeNotSupportedByVersion):
            decoder.parse_stream(state)
Example #5
0
    def test_minimal_major_version_requirement(
        self,
        num_pictures,
        major_version,
        expected_major_version,
        exp_fail,
    ):
        # A sequence with 1 (or zero) HQ pictures
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    parse_parameters=bitstream.ParseParameters(
                        major_version=major_version, ),
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=4,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=4,
                        ),
                    ),
                ),
            ),
        ] + [
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture, ),
                picture_parse=bitstream.PictureParse(
                    picture_header=bitstream.PictureHeader(picture_number=n,
                                                           ), ),
            ) for n in range(num_pictures)
        ] + [
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        populate_parse_offsets(seq)

        state = bytes_to_state(serialise_to_bytes(seq))
        if exp_fail:
            with pytest.raises(decoder.MajorVersionTooHigh):
                decoder.parse_stream(state)
        else:
            decoder.parse_stream(state)
        assert state["_expected_major_version"] == expected_major_version
Example #6
0
    def test_odd_number_of_fields_disallowed(self, picture_coding_mode,
                                             num_pictures, exp_fail):
        # A sequence with num_pictures HQ pictures
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    picture_coding_mode=picture_coding_mode,
                    parse_parameters=bitstream.ParseParameters(
                        major_version=2),
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=4,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=4,
                        ),
                    ),
                ),
            ),
        ] + [
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture, ),
                picture_parse=bitstream.PictureParse(
                    picture_header=bitstream.PictureHeader(picture_number=n,
                                                           ), ),
            ) for n in range(num_pictures)
        ] + [
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        populate_parse_offsets(seq)

        state = bytes_to_state(serialise_to_bytes(seq))
        if exp_fail:
            with pytest.raises(
                    decoder.OddNumberOfFieldsInSequence) as exc_info:
                decoder.parse_stream(state)
            assert exc_info.value.num_fields_in_sequence == num_pictures
        else:
            decoder.parse_stream(state)
Example #7
0
 def test_bad_parse_code(self):
     state = bytes_to_state(
         serialise_to_bytes(bitstream.ParseInfo(parse_code=0x11)))
     state["_generic_sequence_matcher"] = Matcher(".*")
     with pytest.raises(decoder.BadParseCode) as exc_info:
         decoder.parse_info(state)
     assert exc_info.value.parse_code == 0x11
Example #8
0
 def sh_data_unit_bytes(self, sh_bytes, sh_parse_offset):
     # parse_info + sequence header
     return (serialise_to_bytes(
         bitstream.ParseInfo(
             parse_code=tables.ParseCodes.sequence_header,
             next_parse_offset=sh_parse_offset,
         )) + sh_bytes)
def missing_sequence_header_bitstream_fname(tmpdir):
    fname = str(tmpdir.join("bitstream.vc2"))

    with open(fname, "wb") as f:
        w = bitstream.BitstreamWriter(f)

        state = State(major_version=3)

        context = bitstream.ParseInfo(parse_code=tables.ParseCodes.high_quality_picture)
        with bitstream.Serialiser(w, context, bitstream.vc2_default_values) as ser:
            bitstream.parse_info(ser, state)

        context = bitstream.PictureParse(
            wavelet_transform=bitstream.WaveletTransform(
                transform_parameters=bitstream.TransformParameters(
                    slice_parameters=bitstream.SliceParameters(
                        slices_x=0,
                        slices_y=0,
                    ),
                ),
            ),
        )
        with bitstream.Serialiser(w, context, bitstream.vc2_default_values) as ser:
            bitstream.picture_parse(ser, state)

    return fname
Example #10
0
 def test_allowed_zero_next_parse_offset_for_pictures(self, parse_code):
     state = bytes_to_state(
         serialise_to_bytes(
             bitstream.ParseInfo(
                 parse_code=parse_code,
                 next_parse_offset=0,
             )))
     state["major_version"] = 3
     state["_generic_sequence_matcher"] = Matcher(".*")
     decoder.parse_info(state)
    def test_empty_sequence(self):
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])

        populate_parse_offsets(seq)

        assert seq["data_units"][0]["parse_info"]["next_parse_offset"] == 0
        assert seq["data_units"][0]["parse_info"]["previous_parse_offset"] == 0
Example #12
0
 def test_never_allowed_invalid_offset(self, parse_code, next_parse_offset):
     state = bytes_to_state(
         serialise_to_bytes(
             bitstream.ParseInfo(
                 parse_code=parse_code,
                 next_parse_offset=next_parse_offset,
             )))
     state["_generic_sequence_matcher"] = Matcher(".*")
     with pytest.raises(decoder.InvalidNextParseOffset) as exc_info:
         decoder.parse_info(state)
     assert exc_info.value.next_parse_offset == next_parse_offset
Example #13
0
 def test_not_allowed_zero_next_parse_offset_for_non_pictures(
         self, parse_code):
     state = bytes_to_state(
         serialise_to_bytes(
             bitstream.ParseInfo(
                 parse_code=parse_code,
                 next_parse_offset=0,
             )))
     state["_generic_sequence_matcher"] = Matcher(".*")
     with pytest.raises(decoder.MissingNextParseOffset) as exc_info:
         decoder.parse_info(state)
     assert exc_info.value.parse_code == parse_code
Example #14
0
 def test_non_zero_previous_parse_offset_for_start_of_sequence(self):
     state = bytes_to_state(
         serialise_to_bytes(
             bitstream.ParseInfo(
                 parse_code=tables.ParseCodes.end_of_sequence,
                 previous_parse_offset=1,
             )))
     state["_generic_sequence_matcher"] = Matcher(".*")
     with pytest.raises(decoder.NonZeroPreviousParseOffsetAtStartOfSequence
                        ) as exc_info:
         decoder.parse_info(state)
     assert exc_info.value.previous_parse_offset == 1
Example #15
0
    def test_inconsistent_next_parse_offset(self):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.padding_data,
                    next_parse_offset=tables.PARSE_INFO_HEADER_BYTES + 10,
                )) + b"\x00" * 9 +
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.end_of_sequence,
                    previous_parse_offset=tables.PARSE_INFO_HEADER_BYTES + 9,
                )))
        state["_generic_sequence_matcher"] = Matcher(".*")

        decoder.parse_info(state)
        decoder.read_uint_lit(state, 9)
        with pytest.raises(decoder.InconsistentNextParseOffset) as exc_info:
            decoder.parse_info(state)

        assert exc_info.value.parse_info_offset == 0
        assert exc_info.value.next_parse_offset == tables.PARSE_INFO_HEADER_BYTES + 10
        assert exc_info.value.true_parse_offset == tables.PARSE_INFO_HEADER_BYTES + 9
Example #16
0
    def test_immediate_end_of_sequence(self, sh_data_unit_bytes):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.end_of_sequence)))
        with pytest.raises(decoder.GenericInvalidSequence) as exc_info:
            decoder.parse_stream(state)

        assert exc_info.value.parse_code is tables.ParseCodes.end_of_sequence
        assert exc_info.value.expected_parse_codes == [
            tables.ParseCodes.sequence_header
        ]
        assert exc_info.value.expected_end is False
Example #17
0
    def test_no_sequence_header(self, sh_data_unit_bytes):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.padding_data,
                    next_parse_offset=tables.PARSE_INFO_HEADER_BYTES,
                )))
        with pytest.raises(decoder.GenericInvalidSequence) as exc_info:
            decoder.parse_stream(state)

        assert exc_info.value.parse_code is tables.ParseCodes.padding_data
        assert exc_info.value.expected_parse_codes == [
            tables.ParseCodes.sequence_header
        ]
        assert exc_info.value.expected_end is False
Example #18
0
    def test_invalid_generic_sequence(self):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.end_of_sequence, )))
        state["_generic_sequence_matcher"] = Matcher("sequence_header")

        with pytest.raises(decoder.GenericInvalidSequence) as exc_info:
            decoder.parse_info(state)

        assert exc_info.value.parse_code is tables.ParseCodes.end_of_sequence
        assert exc_info.value.expected_parse_codes == [
            tables.ParseCodes.sequence_header
        ]
        assert exc_info.value.expected_end is False
def minimal_sequence_bitstream_fname(tmpdir):
    fname = str(tmpdir.join("bitstream.vc2"))

    with open(fname, "wb") as f:
        context = bitstream.Sequence(
            data_units=[
                bitstream.DataUnit(
                    parse_info=bitstream.ParseInfo(
                        parse_code=tables.ParseCodes.end_of_sequence,
                    )
                ),
            ]
        )
        w = bitstream.BitstreamWriter(f)
        with bitstream.Serialiser(w, context, bitstream.vc2_default_values) as ser:
            bitstream.parse_sequence(ser, State())

    return fname
Example #20
0
    def test_level_restricts_sequence(self):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.end_of_sequence, )))
        state["_generic_sequence_matcher"] = Matcher(".*")
        state["_level_sequence_matcher"] = Matcher("sequence_header")
        state["level"] = tables.Levels.unconstrained

        with pytest.raises(decoder.LevelInvalidSequence) as exc_info:
            decoder.parse_info(state)

        assert exc_info.value.parse_code is tables.ParseCodes.end_of_sequence
        assert exc_info.value.expected_parse_codes == [
            tables.ParseCodes.sequence_header
        ]
        assert exc_info.value.expected_end is False
        assert exc_info.value.level == tables.Levels.unconstrained
Example #21
0
    def test_profile_restricts_allowed_parse_codes(self, parse_code, allowed):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.ParseInfo(
                    parse_code=parse_code,
                    next_parse_offset=tables.PARSE_INFO_HEADER_BYTES,
                )))
        state["_generic_sequence_matcher"] = Matcher(".*")
        state["profile"] = tables.Profiles.high_quality

        if allowed:
            decoder.parse_info(state)
        else:
            with pytest.raises(
                    decoder.ParseCodeNotAllowedInProfile) as exc_info:
                decoder.parse_info(state)
            assert exc_info.value.parse_code == tables.ParseCodes.low_delay_picture
            assert exc_info.value.profile == tables.Profiles.high_quality
    def valid_bitstream(self, filename):
        """
        Creates a valid bitstream in the file whose name is returned by this
        fixture. This bitstream consists of two small pictures.


        A minimal bitstream with a pair of 4x4 pixel 4:2:0 8-bit pictures with
        picture numbers 100 and 101.
        """
        with open(filename, "wb") as f:
            w = bitstream.BitstreamWriter(f)
            seq = bitstream.Sequence(
                data_units=[
                    bitstream.DataUnit(
                        parse_info=bitstream.ParseInfo(
                            parse_code=tables.ParseCodes.sequence_header,
                            next_parse_offset=19,
                        ),
                        sequence_header=bitstream.SequenceHeader(
                            parse_parameters=bitstream.ParseParameters(
                                major_version=2,
                            ),
                            video_parameters=bitstream.SourceParameters(
                                frame_size=bitstream.FrameSize(
                                    custom_dimensions_flag=True,
                                    frame_width=4,
                                    frame_height=4,
                                ),
                                clean_area=bitstream.CleanArea(
                                    custom_clean_area_flag=True,
                                    left_offset=0,
                                    top_offset=0,
                                    clean_width=4,
                                    clean_height=4,
                                ),
                                color_diff_sampling_format=bitstream.ColorDiffSamplingFormat(  # noqa: E501
                                    custom_color_diff_format_flag=True,
                                    color_diff_format_index=tables.ColorDifferenceSamplingFormats.color_4_2_0,  # noqa: E501
                                ),
                            ),
                            picture_coding_mode=tables.PictureCodingModes.pictures_are_frames,  # noqa: E501
                        ),
                    ),
                    bitstream.DataUnit(
                        parse_info=bitstream.ParseInfo(
                            parse_code=tables.ParseCodes.high_quality_picture,
                            next_parse_offset=24,
                            previous_parse_offset=19,
                        ),
                        picture_parse=bitstream.PictureParse(
                            picture_header=bitstream.PictureHeader(
                                picture_number=100,
                            ),
                        ),
                    ),
                    bitstream.DataUnit(
                        parse_info=bitstream.ParseInfo(
                            parse_code=tables.ParseCodes.high_quality_picture,
                            next_parse_offset=24,
                            previous_parse_offset=24,
                        ),
                        picture_parse=bitstream.PictureParse(
                            picture_header=bitstream.PictureHeader(
                                picture_number=101,
                            ),
                        ),
                    ),
                    bitstream.DataUnit(
                        parse_info=bitstream.ParseInfo(
                            parse_code=tables.ParseCodes.end_of_sequence,
                            previous_parse_offset=24,
                        )
                    ),
                ]
            )
            with bitstream.Serialiser(w, seq, bitstream.vc2_default_values) as ser:
                bitstream.parse_sequence(ser, State())
            w.flush()

        return filename
    def test_print_value(self, capsys):
        v = BitstreamViewer(None)

        v._reader = Mock()
        v._reader.bits_remaining = None

        v._serdes = Mock()
        v._serdes.path.return_value = ["foo", "parse_info", "parse_code"]
        v._serdes.cur_context = bitstream.ParseInfo(
            parse_info_prefix=tables.PARSE_INFO_PREFIX,
            parse_code=0x10,
            next_parse_offset=123,
            previous_parse_offset=0,
        )

        # Test that:
        # * First time, the whole path should be printed
        # * Formatted value should be shown
        v._print_value(123, bitarray("1010"), "parse_code", 0x10)
        assert capsys.readouterr().out == (
            "                                                  +- foo:\n"
            "                                                  | +- parse_info:\n"
            "000000000123: 1010                                | | +- parse_code: end_of_sequence (0x10)\n"
        )

        # Test that when only the tail of the the path changes, no headers are
        # repeated.
        v._serdes.path.return_value = ["foo", "parse_info", "next_parse_offset"]
        v._print_value(123, bitarray("1010"), "next_parse_offset", 123)
        assert capsys.readouterr().out == (
            "000000000123: 1010                                | | +- next_parse_offset: 123\n"
        )

        # Test that:
        # * When the path changes only the minimum set of changes are shown
        #   ordinary dict values can be printed
        # * Ordinary dict values are printed using 'str'
        v._serdes.path.return_value = ["foo", "bar", "baz", "qux"]
        v._serdes.cur_context = {"qux": 321}  # An ordinary dict
        v._print_value(123, bitarray("1010"), "qux", 321)
        assert capsys.readouterr().out == (
            "                                                  | +- bar:\n"
            "                                                  | | +- baz:\n"
            "000000000123: 1010                                | | | +- qux: 321\n"
        )

        # Test that when changing only an intermediate path entry, the whole
        # subtree is shown
        v._serdes.path.return_value = ["foo", "BAR", "baz", "qux"]
        v._print_value(123, bitarray("1010"), "qux", 321)
        assert capsys.readouterr().out == (
            "                                                  | +- BAR:\n"
            "                                                  | | +- baz:\n"
            "000000000123: 1010                                | | | +- qux: 321\n"
        )

        # Test that:
        # * Array values are formatted appropriately
        # * Paths can become less deeply nested
        v._serdes.path.return_value = ["slice", "y_transform", 1]
        v._serdes.cur_context = bitstream.LDSlice(y_transform=[0, 123])
        v._print_value(123, bitarray("1010"), "y_transform", 123)
        assert capsys.readouterr().out == (
            "                                                  +- slice:\n"
            "                                                  | +- y_transform:\n"
            "000000000123: 1010                                | | +- 1: 123\n"
        )
Example #24
0
    def test_picture_and_incomplete_fragment_interleaving_disallowed(
            self, num_slices_to_send, exp_fail):
        # A sequence with a 3x2 slice picture fragment with num_slices_to_send slices in
        # it followed by an HQ picture
        seq = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized pictures
                            custom_dimensions_flag=True,
                            frame_width=8,
                            frame_height=8,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=8,
                            clean_height=8,
                        ),
                    ), ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        picture_number=0,
                        fragment_slice_count=0,
                    ),
                    transform_parameters=bitstream.TransformParameters(
                        slice_parameters=bitstream.SliceParameters(
                            slices_x=3,
                            slices_y=2,
                        ), ),
                ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        picture_number=0,
                        fragment_slice_count=num_slices_to_send,
                    ), ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture, ),
                picture_parse=bitstream.PictureParse(
                    picture_header=bitstream.PictureHeader(picture_number=1,
                                                           ), ),
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        # Don't include second (non-header) fragment if sending no slices
        if num_slices_to_send == 0:
            del seq["data_units"][2]

        populate_parse_offsets(seq)

        state = bytes_to_state(serialise_to_bytes(seq))
        if exp_fail:
            with pytest.raises(decoder.PictureInterleavedWithFragmentedPicture
                               ) as exc_info:
                decoder.parse_stream(state)
            first_fragment_offset = (
                seq["data_units"][0]["parse_info"]["next_parse_offset"] +
                tables.PARSE_INFO_HEADER_BYTES)
            assert exc_info.value.initial_fragment_offset == (
                first_fragment_offset, 7)
            picture_offset = (
                sum(seq["data_units"][i]["parse_info"]["next_parse_offset"]
                    for i in range(len(seq["data_units"]) - 2)) +
                tables.PARSE_INFO_HEADER_BYTES)
            assert exc_info.value.this_offset == (picture_offset, 7)
            assert exc_info.value.fragment_slices_received == num_slices_to_send
            assert exc_info.value.fragment_slices_remaining == 6 - num_slices_to_send
        else:
            decoder.parse_stream(state)
Example #25
0
    def test_output_picture(self):
        # This test adds a callback for output_picture and makes sure that both
        # fragments and pictures call it correctly (and that sanity-checks very
        # loosely that decoding etc. is happening). Finally, it also checks
        # that two concatenated sequences are read one after another.

        # A sequence with a HQ picture followed by a HQ fragment (both all
        # zeros)
        seq1 = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=2,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=2,
                        ),
                        color_diff_sampling_format=bitstream.
                        ColorDiffSamplingFormat(
                            custom_color_diff_format_flag=True,
                            color_diff_format_index=tables.
                            ColorDifferenceSamplingFormats.
                            color_4_2_2,  # noqa: E501
                        ),
                        # Output values will be treated as 8-bit (and thus all
                        # decode to 128)
                        signal_range=bitstream.SignalRange(
                            custom_signal_range_flag=True,
                            index=tables.PresetSignalRanges.
                            video_8bit_full_range,
                        ),
                    ),
                    picture_coding_mode=tables.PictureCodingModes.
                    pictures_are_frames,
                ),
            ),
            # A HQ picture
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture, ),
                picture_parse=bitstream.PictureParse(
                    picture_header=bitstream.PictureHeader(picture_number=10,
                                                           ), ),
            ),
            # A fragmented HQ picture (sent over two fragments to ensure the
            # callback only fires after a whole picture arrives)
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        picture_number=11,
                        fragment_slice_count=0,
                    ),
                    transform_parameters=bitstream.TransformParameters(
                        slice_parameters=bitstream.SliceParameters(
                            slices_x=2,
                            slices_y=1,
                        ), ),
                ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        picture_number=11,
                        fragment_slice_count=1,
                        fragment_x_offset=0,
                        fragment_y_offset=0,
                    ), ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture_fragment,
                ),
                fragment_parse=bitstream.FragmentParse(
                    fragment_header=bitstream.FragmentHeader(
                        picture_number=11,
                        fragment_slice_count=1,
                        fragment_x_offset=1,
                        fragment_y_offset=0,
                    ), ),
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])

        # Another (HQ) picture in a separate sequence
        seq2 = bitstream.Sequence(data_units=[
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.sequence_header, ),
                sequence_header=bitstream.SequenceHeader(
                    parse_parameters=bitstream.ParseParameters(
                        major_version=2),
                    video_parameters=bitstream.SourceParameters(
                        frame_size=bitstream.FrameSize(
                            # Don't waste time on full-sized frames
                            custom_dimensions_flag=True,
                            frame_width=4,
                            frame_height=2,
                        ),
                        clean_area=bitstream.CleanArea(
                            custom_clean_area_flag=True,
                            clean_width=4,
                            clean_height=2,
                        ),
                        color_diff_sampling_format=bitstream.
                        ColorDiffSamplingFormat(
                            custom_color_diff_format_flag=True,
                            color_diff_format_index=tables.
                            ColorDifferenceSamplingFormats.
                            color_4_2_2,  # noqa: E501
                        ),
                        # Output values will be treated as 8-bit (and thus all
                        # decode to 128)
                        signal_range=bitstream.SignalRange(
                            custom_signal_range_flag=True,
                            index=tables.PresetSignalRanges.
                            video_8bit_full_range,
                        ),
                    ),
                    picture_coding_mode=tables.PictureCodingModes.
                    pictures_are_frames,
                ),
            ),
            bitstream.DataUnit(
                parse_info=bitstream.ParseInfo(
                    parse_code=tables.ParseCodes.high_quality_picture, ),
                picture_parse=bitstream.PictureParse(
                    picture_header=bitstream.PictureHeader(
                        picture_number=12), ),
            ),
            bitstream.DataUnit(parse_info=bitstream.ParseInfo(
                parse_code=tables.ParseCodes.end_of_sequence, ), ),
        ])
        populate_parse_offsets(seq1)
        populate_parse_offsets(seq2)

        state = bytes_to_state(
            serialise_to_bytes(seq1) + serialise_to_bytes(seq2))
        state["_output_picture_callback"] = Mock()
        decoder.parse_stream(state)

        assert state["_output_picture_callback"].call_count == 3

        for i, (args, kwargs) in enumerate(
                state["_output_picture_callback"].call_args_list):
            assert kwargs == {}
            # Should get a 4x2 mid-gray frame with 4:2:2 color difference sampling
            assert args[0] == {
                "pic_num": 10 + i,
                "Y": [[128, 128, 128, 128], [128, 128, 128, 128]],
                "C1": [[128, 128], [128, 128]],
                "C2": [[128, 128], [128, 128]],
            }
            # Just sanity check the second argument looks like a set of video parameters
            assert args[1]["frame_width"] == 4
            assert args[1]["frame_height"] == 2
            assert args[1]["luma_offset"] == 0
            assert args[1]["luma_offset"] == 0
            assert args[1]["luma_excursion"] == 255
            assert args[1]["color_diff_offset"] == 128
            assert args[1]["color_diff_excursion"] == 255
            # And the picture coding mode too...
            assert args[2] == tables.PictureCodingModes.pictures_are_frames