Exemplo n.º 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)
Exemplo n.º 2
0
def output_decoder_test_case(output_dir, codec_features, test_case):
    """
    Write a decoder test case to disk.

    Parameters
    ==========
    output_dir : str
        Output directory to write test cases to.
    codec_features : :py:class:`~vc2_conformance.codec_features.CodecFeatures`
    test_case : :py:class:`~vc2_conformance.test_cases.TestCase`
    """
    # Serialise bitstream
    bitstream_filename = os.path.join(
        output_dir,
        "{}.vc2".format(test_case.name),
    )
    with open(bitstream_filename, "wb") as f:
        autofill_and_serialise_stream(f, test_case.value)

    # Decode model answer
    model_answer_directory = os.path.join(
        output_dir,
        "{}_expected".format(test_case.name),
    )
    makedirs(model_answer_directory, exist_ok=True)
    with open(bitstream_filename, "rb") as f:
        index = [0]

        def output_picture(picture, video_parameters, picture_coding_mode):
            file_format.write(
                picture,
                video_parameters,
                picture_coding_mode,
                os.path.join(
                    model_answer_directory,
                    "picture_{}.raw".format(index[0]),
                ),
            )
            index[0] += 1

        state = State(_output_picture_callback=output_picture)
        init_io(state, f)
        parse_stream(state)

    # Write metadata
    if test_case.metadata is not None:
        with open(
            os.path.join(
                output_dir,
                "{}_metadata.json".format(test_case.name),
            ),
            "w",
        ) as f:
            json.dump(test_case.metadata, f)

    logging.info(
        "Generated decoder test case %s for %s",
        test_case.name,
        codec_features["name"],
    )
def test_all_decoder_test_cases(codec_features, test_case):
    # Every test case for every basic video mode must produce a valid bitstream
    # containing pictures with the correct format. Any JSON metadata must also
    # be seriallisable.

    # Must return a Stream
    assert isinstance(test_case.value, Stream)

    # Mustn't crash!
    json.dumps(test_case.metadata)

    # Serialise
    f = BytesIO()
    autofill_and_serialise_stream(f, test_case.value)
    f.seek(0)

    # Deserialise/validate
    def output_picture_callback(picture, video_parameters,
                                picture_coding_mode):
        assert video_parameters == codec_features["video_parameters"]
        assert picture_coding_mode == codec_features["picture_coding_mode"]

    state = State(_output_picture_callback=output_picture_callback, )

    with alternative_level_1():
        init_io(state, f)
        parse_stream(state)
def test_iter_sequence_headers(codec_features):
    sequence_headers = list(iter_sequence_headers(codec_features))
    assert sequence_headers

    pi = ParseInfo(
        parse_code=ParseCodes.sequence_header,
        # An arbitrary non-zero value; this won't get picked up by the
        # conformance checker since it'll hit the end-of-file first
        next_parse_offset=999,
    )

    for sh in sequence_headers:
        # Set the version to one compatible with the levels tried in this test
        sh.setdefault("parse_parameters", {})["major_version"] = 2

        f = BytesIO()
        state = State()
        serialise(pi, parse_info, state, f)
        video_parameters = serialise(sh, sequence_header, state, f)

        # Check the encoded video parameters are as requested
        assert video_parameters == codec_features["video_parameters"]

        # Check that the header does everything that the level requires. Here we
        # just check we reach the end of the sequence header without a conformance
        # error.
        f.seek(0)
        state = State()
        init_io(state, f)
        with pytest.raises((UnexpectedEndOfStream, InconsistentNextParseOffset)):
            parse_stream(state)
        assert f.tell() == len(f.getvalue())
Exemplo n.º 5
0
    def test_qindex_matters(self):
        codec_features = deepcopy(MINIMAL_CODEC_FEATURES)
        codec_features["lossless"] = True
        codec_features["picture_bytes"] = None

        # Sanity check: Make sure we're outputting some kind of picture which
        # really does depend on quantization
        pictures = {False: [], True: []}
        for override_qindex in [False, True]:
            stream = lossless_quantization(codec_features)

            if override_qindex:
                for _state, _sx, _sy, hq_slice in iter_slices_in_sequence(
                    codec_features,
                    stream["sequences"][0],
                ):
                    hq_slice["qindex"] = 0

            # Serialise
            f = BytesIO()
            autofill_and_serialise_stream(f, stream)
            f.seek(0)

            # Decode
            def output_picture_callback(picture, video_parameters, picture_coding_mode):
                pictures[override_qindex].append(picture)

            state = State(_output_picture_callback=output_picture_callback)
            init_io(state, f)
            parse_stream(state)

        # Make sure that the qindex mattered by checking that decoding with
        # qindex clamped to 0 resulted in different pictures
        assert pictures[False] != pictures[True]
Exemplo n.º 6
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)
    def serialise_and_decode_pictures(self, stream):
        f = BytesIO()
        autofill_and_serialise_stream(f, stream)

        pictures = []
        state = State(
            _output_picture_callback=lambda pic, vp, pcm: pictures.append(pic))
        f.seek(0)
        decoder.init_io(state, f)
        decoder.parse_stream(state)

        return pictures
def encode_and_decode(stream):
    f = BytesIO()
    autofill_and_serialise_stream(f, stream)
    f.seek(0)

    pictures = []
    state = State(
        _output_picture_callback=lambda p, vp, pcm: pictures.append(p))
    init_io(state, f)
    parse_stream(state)

    return pictures
def test_format_parse_code_traceback():
    # Capture a traceback resulting from the I/O subsystem not being
    # initialised

    try:
        parse_stream(State())
    except KeyError:
        exc_type, exc_value, exc_tb = sys.exc_info()
        tb = traceback.extract_tb(exc_tb)

    assert format_pseudocode_traceback(tb) == (
        "* parse_stream (10.3)\n" "  * is_end_of_stream (A.2.5)"
    )
Exemplo n.º 10
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
Exemplo n.º 11
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
Exemplo n.º 12
0
def check_codec_features_valid(codec_feature_sets):
    """
    Verify that the codec features requested don't themselves violate the spec
    (e.g. violate a level constraint). This is done by generating then
    validating a bitstream containing a single mid-gray frame.

    Prints an error to stderr and calls :py:func:`sys.exit` if a problem is
    encountered.
    """
    logging.info("Checking codec feature sets are valid...")
    for name, codec_features in codec_feature_sets.items():
        logging.info("Checking %r...", name)
        f = BytesIO()

        # Sanity-check the color format (since this won't raise a validation
        # error but will result in a useless gamut being available).
        sanity = sanity_check_video_parameters(codec_features["video_parameters"])
        if not sanity:
            logging.warning(
                "Color specification for codec configuration %r be malformed: %s",
                name,
                sanity.explain(),
            )

        # Generate a minimal bitstream
        try:
            autofill_and_serialise_stream(f, static_gray(codec_features))
        except UnsatisfiableCodecFeaturesError as e:
            sys.stderr.write(
                "Error: Codec configuration {!r} is invalid:\n".format(name)
            )
            terminal_width = get_terminal_size()[0]
            sys.stderr.write(wrap_paragraphs(e.explain(), terminal_width))
            sys.stderr.write("\n")
            sys.exit(4)
        f.seek(0)

        # Validate it meets the spec
        state = State()
        init_io(state, f)
        try:
            parse_stream(state)
        except ConformanceError as e:
            sys.stderr.write(
                "Error: Codec configuration {!r} is invalid:\n".format(name)
            )
            terminal_width = get_terminal_size()[0]
            sys.stderr.write(wrap_paragraphs(e.explain(), terminal_width))
            sys.stderr.write("\n")
            sys.exit(4)
Exemplo n.º 13
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
Exemplo n.º 14
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)
Exemplo n.º 15
0
    def run(self):
        try:
            self._file = open(self._filename, "rb")
            self._filesize_bytes = os.path.getsize(self._filename)
        except Exception as e:
            # Catch-all exception handler excuse: Catching only file-related
            # exceptions is challenging, particularly in a backward-compatible
            # manner. However, none of the above are known to produce
            # exceptions *except* due to file-related issues.
            self._print_error(str(e))
            return 1

        self._state = State(_output_picture_callback=self._output_picture)
        init_io(self._state, self._file)

        if self._show_status:
            self._update_status_line("Starting bitstream validation...")

        try:
            parse_stream(self._state)
            self._hide_status_line()
            if tell(self._state) == (0, 7):
                sys.stdout.flush()
                sys.stderr.write(
                    "Warning: 0 bytes read, bitstream is empty.\n")
            print(
                "No errors found in bitstream. Verify decoded pictures to confirm conformance."
            )
            return 0
        except ConformanceError as e:
            # Bitstream failed validation
            exc_type, exc_value, exc_tb = sys.exc_info()
            self._hide_status_line()
            self._print_conformance_error(e, traceback.extract_tb(exc_tb))
            self._print_error("non-conformant bitstream (see above)")
            return 2
        except Exception as e:
            # Internal error (shouldn't happen(!))
            self._hide_status_line()
            self._print_error("internal error in bitstream validator: {}: {} "
                              "(probably a bug in this program)".format(
                                  type(e).__name__,
                                  str(e),
                              ))
            return 3
Exemplo n.º 16
0
def serialize_and_decode(sequence):
    # Serialise
    f = BytesIO()
    autofill_and_serialise_stream(f, Stream(sequences=[sequence]))

    # Setup callback to capture decoded pictures
    decoded_pictures = []

    def output_picture_callback(picture, video_parameters,
                                picture_coding_mode):
        decoded_pictures.append(picture)

    # Feed to conformance checking decoder
    f.seek(0)
    state = State(_output_picture_callback=output_picture_callback)
    init_io(state, f)
    parse_stream(state)

    return decoded_pictures
Exemplo n.º 17
0
def check_for_signal_clipping(sequence):
    """
    Given a :py:class:`vc2_conformance.bitstream.Sequence`, return True if any
    picture component signal was clipped during decoding.
    """
    # NB: Internally we just check for saturated signal levels. This way we
    # avoid the need to modify the decoder to remove the clipper and all that
    # faff...

    # Serialise
    f = BytesIO()
    # NB: Deepcopy required due to autofill_and_serialise_stream mutating the
    # stream
    stream = Stream(sequences=[deepcopy(sequence)])
    autofill_and_serialise_stream(f, stream)
    f.seek(0)

    # Decode and look for saturated pixel values
    state = State()
    may_have_clipped = [False]

    def output_picture_callback(picture, video_parameters, picture_coding_mode):
        components_and_depths = [
            ("Y", state["luma_depth"]),
            ("C1", state["color_diff_depth"]),
            ("C2", state["color_diff_depth"]),
        ]

        for component, depth in components_and_depths:
            min_value = min(min(row) for row in picture[component])
            max_value = max(max(row) for row in picture[component])
            if min_value == 0:
                may_have_clipped[0] = True
            if max_value == (1 << depth) - 1:
                may_have_clipped[0] = True

    state["_output_picture_callback"] = output_picture_callback
    init_io(state, f)
    parse_stream(state)

    return may_have_clipped[0]
Exemplo n.º 18
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)
Exemplo n.º 19
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