def test_symbol_priority(self): assert (make_matching_sequence( [], "(a|b|c) . .", ". (d|e|f) .", ". . (g|h|i)", symbol_priority=[ "b", "a", "c", "e", "d", "f", # NB: G, H and I not specified so should be alphabetically # ordered ], ) == ["b", "e", "g"])
def make_sequence(codec_features, pictures, *data_unit_patterns, **kwargs): """ Generate a complete VC-2 bitstream based on the provided set of codec features and containing compressed versions of the specified set of pictures. This function also takes a small number of additional parameters which override certain encoder behaviours as may be required by some test case generators. Parameters ========== codec_features : :py:class:`~vc2_conformance.codec_features.CodecFeatures` pictures : [{"Y": [[s, ...], ...], "C1": ..., "C2": ..., "pic_num": int}, ...] The pictures to be encoded in the bitstream. If ``pic_num`` is omitted, ``picture_number`` fields will also be omitted in the output (and left for, e.g. :py:func:`vc2_conformance.bitstream.autofill_and_serialise_stream` to assign). See :py:mod:`vc2_conformance.encoder.pictures` for details of the picture compression process. *data_unit_patterns : str Force the generated sequence of data units to match a specified regular expression. For example, ``"(. padding_data)+ end_of_sequence"`` will force a padding data unit to be inserted between each data unit. See the :py:mod:`vc2_conformance.symbol_re` module for details of the regular expression format. A sequence of data units matching all specified patterns while meeting the requirements of the VC-2 standard will be generated. If this is not possible, :py:exc:`vc2_conformance.encoder.exceptions.IncompatibleLevelAndDataUnitError` will be raised. minimum_qindex : int or [int, ...] Keyword-only argument. Default 0. Specifies the minimum quantization index to be used for all picture slices. If a list is provided, specifies the minimum quantization index separately for each picture. This option may be used by test cases where a particular (very high) quantization index must be used. Note that the encoder may still use larger quantization indices if a set of transform coefficients still do not fit into a slice so the caller must check that this has not occurred. Must be 0 for lossless coding modes. minimum_slice_size_scaler : int Keyword-only argument. Default 1. Specifies the minimum slice size scaler to use. For almost all sensible coding modes, the ``slice_size_scaler`` can be set to '1' -- and this encoder will do so if possible. To facilitate the production of test cases verifying higher values are supported, this option may be used to pick a larger value. The encoder may still use larger ``slice_size_scaler`` values if this is necessary, however. Only has an effect on high quality profile coding modes, will be ignored for the low delay profile modes. Returns ======= sequence : :py:class:`vc2_conformance.bitstream.Sequence` The VC-2 bitstream sequence. This may be serialised by encapsulating it in a :py:class:`vc2_conformance.bitstream.Stream` and serialising it with :py:func:`~vc2_conformance.bitstream.vc2_autofill.autofill_and_serialise_stream`. Raises ====== UnsatisfiableCodecFeaturesError Raised if a sequence could not be generated according to the requirements given. """ minimum_qindices = kwargs.pop("minimum_qindex", 0) minimum_slice_size_scaler = kwargs.pop("minimum_slice_size_scaler", 1) assert not kwargs, "Unexpected arguments: {}".format(kwargs) if not isinstance(minimum_qindices, list): minimum_qindices = repeat(minimum_qindices) pictures_only_sequence = Sequence(data_units=[]) for picture, minimum_qindex in zip(pictures, minimum_qindices): pictures_only_sequence["data_units"].extend( make_picture_data_units( codec_features, picture, minimum_qindex, minimum_slice_size_scaler, )) # Fill in all other required bitstream data units picture_only_data_unit_names = [ data_unit["parse_info"]["parse_code"].name for data_unit in pictures_only_sequence["data_units"] ] try: required_data_unit_names = make_matching_sequence( picture_only_data_unit_names, # (10.4.1) Sequences start with a sequence header and end with an end # of sequence data unit "sequence_header .* end_of_sequence", # Certain levels may provide additional constraints LEVEL_SEQUENCE_RESTRICTIONS[codec_features["level"] ].sequence_restriction_regex, *data_unit_patterns, symbol_priority=["padding_data", "sequence_header"]) except ImpossibleSequenceError: raise IncompatibleLevelAndDataUnitError(codec_features) # Functions for producing the necessary data units # # {data_unit_name: fn() -> DataUnit, ...} data_unit_makers = { "sequence_header": partial(make_sequence_header_data_unit, codec_features), "end_of_sequence": make_end_of_sequence_data_unit, "auxiliary_data": make_auxiliary_data_unit, "padding_data": make_padding_data_unit, } if pictures_only_sequence["data_units"]: picture_parse_code = pictures_only_sequence["data_units"][0][ "parse_info"]["parse_code"] data_unit_makers[picture_parse_code.name] = partial( pictures_only_sequence["data_units"].pop, 0, ) return Sequence(data_units=[ data_unit_makers[data_unit_name]() for data_unit_name in required_data_unit_names ])
def test_sequence_not_allowed_by_patterns(self, patterns): with pytest.raises(ImpossibleSequenceError): make_matching_sequence(["a", "b", "b", "c"], *patterns, depth_limit=3)
def test_search_depth_limit(self, symbols, patterns, works): if works: make_matching_sequence(symbols, *patterns, depth_limit=3) else: with pytest.raises(ImpossibleSequenceError): make_matching_sequence(symbols, *patterns, depth_limit=3)
def test_mixture_of_matched_and_fillled_in_values(self): assert make_matching_sequence(["b"], "a .* c") == ["a", "b", "c"]
def test_prefer_wildcard_value_if_empty_symbol_priority(self): assert make_matching_sequence([], "a . c", "a (.|b) c") == ["a", ".", "c"]
def test_prefer_highest_symbol_priority_over_wildcard(self): assert (make_matching_sequence( [], "a . c", symbol_priority=["X", "a", "c"], ) == ["a", "X", "c"])
def test_find_shortest_possible_filler_values(self, patterns): assert make_matching_sequence([], *patterns) == ["a", "b"]
def test_fully_matched_pattern(self, patterns): # Patterns which match the test sequence exactly assert make_matching_sequence(["a", "b", "c"], *patterns) == ["a", "b", "c"]
def test_patterns_allow_empty_sequence(self, patterns): assert make_matching_sequence([], *patterns) == []
def test_arbitrary_no_patterns(self): # When no patterns are supplied, any sequence should match assert make_matching_sequence(["a", "b", "c"]) == ["a", "b", "c"]
def test_empty_no_patterns(self): assert make_matching_sequence([]) == []