def test_generate_exp_golomb_numbers_with_ascending_lengths(sign):
    for length, number in islice(
            generate_exp_golomb_with_ascending_lengths(sign),
            128,
    ):
        # Use a known-good implementation of a signed exp-golmb encoder and
        # check length is correct.
        f = BytesIO()
        w = BitstreamWriter(f)
        w.write_sint(number)
        actual_length = to_bit_offset(*w.tell())
        assert actual_length == length

        # Check sign of number
        if sign < 0:
            assert number <= 0
        else:
            assert number >= 0
예제 #2
0
    def test_finalizer_works(self):
        f = BytesIO()
        w = BitstreamWriter(f)

        # Sequence with every data unit type and fully automatic numbers
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.sequence_header
                            ),
                            sequence_header=SequenceHeader(
                                parse_parameters=ParseParameters(major_version=3),
                                video_parameters=SourceParameters(
                                    # Tiny custom frame-size used to reduce test suite
                                    # runtime
                                    frame_size=FrameSize(
                                        custom_dimensions_flag=True,
                                        frame_width=4,
                                        frame_height=4,
                                    )
                                ),
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture
                            ),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.low_delay_picture
                            ),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture_fragment
                            ),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture_fragment
                            ),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.padding_data
                            ),
                            padding=Padding(bytes=b"123"),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.auxiliary_data
                            ),
                            auxiliary_data=AuxiliaryData(bytes=b"123"),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.end_of_sequence
                            ),
                        ),
                    ]
                )
            ]
        )

        (
            next_parse_offsets_to_autofill,
            previous_parse_offsets_to_autofill,
        ) = autofill_parse_offsets(stream)

        with Serialiser(w, stream, vc2_default_values_with_auto) as serdes:
            vc2.parse_stream(serdes, State())
        w.flush()

        offset_before = w.tell()
        autofill_parse_offsets_finalize(
            w,
            serdes.context,
            next_parse_offsets_to_autofill,
            previous_parse_offsets_to_autofill,
        )
        assert w.tell() == offset_before

        f.seek(0)
        r = BitstreamReader(f)
        with Deserialiser(r) as serdes:
            vc2.parse_stream(serdes, State())

        parse_infos = [
            data_unit["parse_info"]
            for sequence in serdes.context["sequences"]
            for data_unit in sequence["data_units"]
        ]

        # Check for start/end offsets being zero
        assert parse_infos[0]["previous_parse_offset"] == 0
        assert parse_infos[-1]["next_parse_offset"] == 0

        # Check for consistency and plusibility of offsets
        for pi1, pi2 in zip(parse_infos, parse_infos[1:]):
            assert pi1["next_parse_offset"] > 13
            assert pi2["previous_parse_offset"] > 13

            assert pi1["next_parse_offset"] == pi2["previous_parse_offset"]
    def test_happy_cases(self, block_bits, num_values, magnitude):
        value_sets = {
            dangle_type: generate_dangling_transform_values(
                block_bits,
                num_values,
                dangle_type,
                magnitude,
            )
            for dangle_type in DanglingTransformValueType
        }

        values_and_bits_beyond_ends = {}
        for description, values in value_sets.items():
            # Should all have required number of values
            assert len(values) == num_values

            # Should correctly encode into bounded block
            f = BytesIO()
            w = BitstreamWriter(f)
            w.bounded_block_begin(block_bits)
            for value in values:
                w.write_sint(value)

            # Should completely fill the block
            length_used = to_bit_offset(*w.tell())
            assert length_used == block_bits

            # Check we actually wrote 'beyond' the end of the block
            assert w.bits_remaining < 0

            # Work out which value and which bits actually first crossed the
            # end-of-block boundary (we'll later check that these actually
            # match our expectations later)
            w.flush()
            f.seek(0)
            r = BitstreamReader(f)
            r.bounded_block_begin(block_bits)
            value_beyond_end = None
            bits_beyond_end = None
            while r.bits_remaining >= 0:
                value_beyond_end = r.read_sint()
                bits_beyond_end = -r.bits_remaining
            values_and_bits_beyond_ends[description] = (
                value_beyond_end,
                bits_beyond_end,
            )

        # Check that the dangling value dangles in the expected way
        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.zero_dangling]
        assert v == 0
        assert b == 1

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.sign_dangling]
        assert v != 0
        assert (-v).bit_length() == magnitude
        assert b == 1

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.stop_and_sign_dangling]
        assert v != 0
        assert (-v).bit_length() == magnitude
        assert b == 2

        v, b = values_and_bits_beyond_ends[
            DanglingTransformValueType.lsb_stop_and_sign_dangling]
        assert v != 0
        # NB: Larger due to exp-golmb code needing to end in 1
        assert (-v).bit_length() == magnitude + 1
        assert b == 3