def test_basic_writing(self, value, encoded, exp_tell): f = BytesIO() w = bitstream.BitstreamWriter(f) w.write_sint(value) assert w.tell() == exp_tell w.flush() assert f.getvalue() == encoded
def test_writing_and_flush(self): f = BytesIO() w = bitstream.BitstreamWriter(f) assert f.getvalue() == b"" w.write_bit(0) w.flush() assert f.getvalue() == b"\x00" w.write_bit(1) w.flush() assert f.getvalue() == b"\x40" w.write_bit(0) w.flush() assert f.getvalue() == b"\x40" w.write_bit(1) w.flush() assert f.getvalue() == b"\x50" for _ in range(4): w.write_bit(1) # Ending the byte normally should flush automatically and retain # existing bits assert f.getvalue() == b"\x5F"
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 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_writing_whole_number_of_bytes(self): f = BytesIO() w = bitstream.BitstreamWriter(f) for bit in [1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1]: # 0xA5 # 0x0F w.write_bit(bit) assert f.getvalue() == b"\xA5\x0F"
def test_tell(self): w = bitstream.BitstreamWriter(BytesIO()) assert w.tell() == (0, 7) w.write_bit(0) assert w.tell() == (0, 6) for _ in range(6): w.write_bit(1) assert w.tell() == (0, 0) # Move into next byte w.write_bit(1) assert w.tell() == (1, 7) w.write_bit(1) assert w.tell() == (1, 6)
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_bounded_block_seek(self): f = BytesIO() w = bitstream.BitstreamWriter(f) w.bounded_block_begin(4) assert w.tell() == (0, 7) assert w.bits_remaining == 4 # Should be able to seek to current position and succeed w.seek(0, 7) assert w.tell() == (0, 7) assert w.bits_remaining == 4 # Should be able to seek to end of bounded block and succeed w.seek(0, 3) assert w.tell() == (0, 3) assert w.bits_remaining == 0 # Should be able to come back again w.seek(0, 4) assert w.tell() == (0, 4) assert w.bits_remaining == 1 # After writing past the end of the block, seeking to the end of the # block shouldn't change bits remaining w.write_nbits(5, 0b01111) assert w.tell() == (0, 3) assert w.bits_remaining == -4 w.seek(0, 3) assert w.tell() == (0, 3) assert w.bits_remaining == -4 # Moving before the end of block again should adjust the count # accordingly, however. w.seek(0, 4) assert w.tell() == (0, 4) assert w.bits_remaining == 1 # Should not be able to seek past the end of the block with pytest.raises(Exception): w.seek(0, 2) assert w.tell() == (0, 4) assert w.bits_remaining == 1
def test_seek(self): f = BytesIO() w = bitstream.BitstreamWriter(f) w.seek(0, 3) w.write_bit(1) w.flush() assert f.getvalue() == b"\x08" w.seek(1, 0) w.write_bit(1) w.flush() assert f.getvalue() == b"\x08\x01" # Should overwrite existing value of first byte... w.seek(0, 6) w.write_bit(1) w.flush() assert f.getvalue() == b"\x40\x01"
def test_write_out_of_range(self): w = bitstream.BitstreamWriter(BytesIO()) with pytest.raises(OutOfRangeError, match="-1 is negative, expected positive"): w.write_uint(-1)
def w(self, f): return bitstream.BitstreamWriter(f)
def test_bounded_block(self): f = BytesIO() w = bitstream.BitstreamWriter(f) w.bounded_block_begin(4) # Can't nest bounded blocks with pytest.raises(Exception, match=r".*nest.*"): w.bounded_block_begin(1) # Write bits to stream w.write_bit(1) assert w.bits_remaining == 3 assert w.tell() == (0, 6) w.write_bit(0) assert w.bits_remaining == 2 assert w.tell() == (0, 5) w.write_bit(1) assert w.bits_remaining == 1 assert w.tell() == (0, 4) w.write_bit(0) assert w.bits_remaining == 0 assert w.tell() == (0, 3) # End of bounded block, can write '1's but otherwise don't advance w.write_bit(1) assert w.bits_remaining == -1 assert w.tell() == (0, 3) w.write_bit(1) assert w.bits_remaining == -2 assert w.tell() == (0, 3) # Can't write 0s past end of bounded block with pytest.raises(ValueError, match=r".*bounded block.*"): w.write_bit(0) # Expected value should have been written w.flush() assert f.getvalue() == b"\xA0" # At end of bounded block, remaining bits are reported assert w.bounded_block_end() == 0 # Can't double-close bounded blocks with pytest.raises(Exception, match=r"Not in bounded block"): w.bounded_block_end() # Now outside of block can write again w.write_bit(0) assert w.bits_remaining is None assert w.tell() == (0, 2) w.write_bit(1) assert w.bits_remaining is None assert w.tell() == (0, 1) w.flush() assert f.getvalue() == b"\xA4" # If unused bits remain in block, those are reported w.bounded_block_begin(2) assert w.bounded_block_end() == 2
def test_is_end_of_stream(self): w = bitstream.BitstreamWriter(BytesIO()) assert w.is_end_of_stream() is True
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