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