Ejemplo n.º 1
0
    def test_picture_not_allowed_to_change_between_fragments(self):
        fh1 = serialise_to_bytes(
            bitstream.FragmentHeader(
                fragment_slice_count=1,
                picture_number=1000,
            ))
        fh2 = serialise_to_bytes(
            bitstream.FragmentHeader(
                fragment_slice_count=1,
                picture_number=1001,
            ))

        state = bytes_to_state(fh1 + fh2)
        state["_last_picture_number"] = 1000
        state["_last_picture_number_offset"] = (-1, 4)
        state["_picture_initial_fragment_offset"] = (-1, 0)
        state[
            "picture_coding_mode"] = tables.PictureCodingModes.pictures_are_frames
        state["_num_pictures_in_sequence"] = 0
        state["_fragment_slices_remaining"] = 1
        state["fragment_slices_received"] = 0
        state["slices_x"] = 1
        state["slices_y"] = 1

        decoder.fragment_header(state)
        with pytest.raises(
                decoder.PictureNumberChangedMidFragmentedPicture) as exc_info:
            decoder.fragment_header(state)
        assert exc_info.value.last_picture_number_offset == (-1, 4)
        assert exc_info.value.picture_number_offset == (len(fh1) + 4, 7)
        assert exc_info.value.last_picture_number == 1000
        assert exc_info.value.picture_number == 1001
Ejemplo n.º 2
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)
Ejemplo n.º 3
0
    def test_first_fragment_must_have_slice_count_zero(
        self,
        fragment_slice_count,
        fragment_slices_remaining,
        exp_fail,
    ):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.FragmentHeader(
                    fragment_slice_count=fragment_slice_count, )))
        state["_picture_initial_fragment_offset"] = (-1, 7)
        state["fragment_slices_received"] = 0
        state["_fragment_slices_remaining"] = fragment_slices_remaining

        # Required only when not failing
        state["_num_pictures_in_sequence"] = 0
        state[
            "picture_coding_mode"] = tables.PictureCodingModes.pictures_are_frames

        if exp_fail:
            with pytest.raises(decoder.FragmentedPictureRestarted) as exc_info:
                decoder.fragment_header(state)
            assert exc_info.value.initial_fragment_offset == (-1, 7)
            assert exc_info.value.this_fragment_offset == (0, 7)
            assert exc_info.value.fragment_slices_received == 0
            assert exc_info.value.fragment_slices_remaining == fragment_slices_remaining
        else:
            decoder.fragment_header(state)
Ejemplo n.º 4
0
    def test_must_not_have_too_many_slices(
        self,
        fragment_slice_count,
        fragment_slices_remaining,
        exp_fail,
    ):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.FragmentHeader(
                    fragment_slice_count=fragment_slice_count,
                    picture_number=0,
                )))
        state["_picture_initial_fragment_offset"] = (-1, 7)
        state["_fragment_slices_remaining"] = fragment_slices_remaining
        state["fragment_slices_received"] = 0
        state["_last_picture_number"] = 0

        # Only required in non-failing cases
        state["slices_x"] = 1
        state["slices_y"] = 1

        if exp_fail:
            with pytest.raises(
                    decoder.TooManySlicesInFragmentedPicture) as exc_info:
                decoder.fragment_header(state)
            assert exc_info.value.initial_fragment_offset == (-1, 7)
            assert exc_info.value.this_fragment_offset == (0, 7)
            assert exc_info.value.fragment_slices_received == 0
            assert exc_info.value.fragment_slices_remaining == fragment_slices_remaining
            assert exc_info.value.fragment_slice_count == fragment_slice_count
        else:
            decoder.fragment_header(state)
Ejemplo n.º 5
0
    def test_picture_numbering_sanity_check(self):
        # Only a sanity check as assert_picture_number_incremented_as_expected
        # (which performs these checks) is tested fully elsewhere
        fh1 = serialise_to_bytes(bitstream.FragmentHeader(picture_number=1000))
        fh2 = serialise_to_bytes(bitstream.FragmentHeader(picture_number=1001))
        fh3 = serialise_to_bytes(bitstream.FragmentHeader(picture_number=1003))

        state = bytes_to_state(fh1 + fh2 + fh3)
        state[
            "picture_coding_mode"] = tables.PictureCodingModes.pictures_are_frames
        state["_num_pictures_in_sequence"] = 0
        state["_fragment_slices_remaining"] = 0

        decoder.fragment_header(state)
        decoder.fragment_header(state)
        with pytest.raises(decoder.NonConsecutivePictureNumbers) as exc_info:
            decoder.fragment_header(state)
        assert exc_info.value.last_picture_number_offset == (len(fh1) + 4, 7)
        assert exc_info.value.picture_number_offset == (len(fh1) + len(fh2) +
                                                        4, 7)
        assert exc_info.value.last_picture_number == 1001
        assert exc_info.value.picture_number == 1003
Ejemplo n.º 6
0
    def test_must_have_contiguous_slices(
        self,
        fragment_x_offset,
        fragment_y_offset,
        expected_fragment_x_offset,
        expected_fragment_y_offset,
        exp_fail,
    ):
        state = bytes_to_state(
            serialise_to_bytes(
                bitstream.FragmentHeader(
                    picture_number=0,
                    fragment_slice_count=1,
                    fragment_x_offset=fragment_x_offset,
                    fragment_y_offset=fragment_y_offset,
                )))
        state["_picture_initial_fragment_offset"] = (-1, 7)
        state["_fragment_slices_remaining"] = 1
        state["fragment_slices_received"] = (expected_fragment_y_offset * 10 +
                                             expected_fragment_x_offset)
        state["_last_picture_number"] = 0
        state["slices_x"] = 10
        state["slices_y"] = 10

        if exp_fail:
            with pytest.raises(
                    decoder.FragmentSlicesNotContiguous) as exc_info:
                decoder.fragment_header(state)
            assert exc_info.value.initial_fragment_offset == (-1, 7)
            assert exc_info.value.this_fragment_offset == (0, 7)
            assert exc_info.value.fragment_x_offset == fragment_x_offset
            assert exc_info.value.fragment_y_offset == fragment_y_offset
            assert (exc_info.value.expected_fragment_x_offset ==
                    expected_fragment_x_offset)
            assert (exc_info.value.expected_fragment_y_offset ==
                    expected_fragment_y_offset)
        else:
            decoder.fragment_header(state)
Ejemplo n.º 7
0
def test_whole_picture(parse_code, fragment_slice_counts):
    # A sanity check which runs fragmented picture decoding for whole pictures
    # and makes sure nothing crashes

    # Serialise a sample stream
    sh = bitstream.SequenceHeader(
        video_parameters=bitstream.SourceParameters(
            frame_size=bitstream.FrameSize(
                # Don't waste time on full-sized frames
                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,
            ),
        ), )
    serialisation_state = State()
    sh_bytes = serialise_to_bytes(sh, serialisation_state)
    serialisation_state["parse_code"] = parse_code
    frag_bytes = b""

    # Add the first (header) fragment in the picture
    frag_bytes += serialise_to_bytes(
        bitstream.FragmentParse(
            fragment_header=bitstream.FragmentHeader(fragment_slice_count=0, ),
            transform_parameters=bitstream.TransformParameters(
                slice_parameters=bitstream.SliceParameters(
                    slices_x=3,
                    slices_y=2,
                )),
        ),
        serialisation_state,
    )

    # Add the slice-containing fragments
    num_slices = 0
    for fragment_slice_count in fragment_slice_counts:
        x = num_slices % 3
        y = num_slices // 3
        num_slices += fragment_slice_count

        frag_bytes += serialise_to_bytes(
            bitstream.FragmentParse(fragment_header=bitstream.FragmentHeader(
                fragment_slice_count=fragment_slice_count,
                fragment_x_offset=x,
                fragment_y_offset=y,
            ), ),
            serialisation_state,
        )

    # Check it is parsed without failiures
    state = bytes_to_state(sh_bytes + frag_bytes)
    state["_num_pictures_in_sequence"] = 0
    state["_fragment_slices_remaining"] = 0
    decoder.sequence_header(state)

    # Parse header fragment
    decoder.byte_align(state)
    state["parse_code"] = parse_code
    decoder.fragment_parse(state)

    # Parse slice-containing fragments
    num_slices = 0
    for fragment_slice_count in fragment_slice_counts:
        assert state["fragmented_picture_done"] is False

        decoder.byte_align(state)
        state["parse_code"] = parse_code
        decoder.fragment_parse(state)

        num_slices += fragment_slice_count
        assert state["fragment_slices_received"] == num_slices
        assert state["_fragment_slices_remaining"] == 6 - num_slices

    assert state["fragmented_picture_done"] is True
Ejemplo n.º 8
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
Ejemplo n.º 9
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)