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