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
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 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
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)
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
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
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
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
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
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
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
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
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
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
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
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" )
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)
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