bit_writer_c: let writer manage memory on its own

This commit is contained in:
Moritz Bunkus 2017-07-19 23:07:27 +02:00
parent 319c8009e0
commit 9baa010848
4 changed files with 118 additions and 129 deletions

View File

@ -18,24 +18,23 @@
class bit_writer_c {
private:
unsigned char *m_end_of_data;
unsigned char *m_byte_position;
unsigned char *m_start_of_data;
std::size_t m_mask;
bool m_out_of_data;
memory_cptr m_buffer;
unsigned char *m_data{};
std::size_t m_size{}, m_byte_position{}, m_mask{0x80u};
public:
bit_writer_c(unsigned char *data, std::size_t len)
: m_end_of_data(data + len)
, m_byte_position(data)
, m_start_of_data(data)
, m_mask(0x80)
, m_out_of_data(m_byte_position >= m_end_of_data)
bit_writer_c()
: m_buffer{memory_c::alloc(100)}
, m_data{m_buffer->get_buffer()}
{
std::memset(m_buffer->get_buffer(), 0, m_buffer->get_size());
}
uint64_t copy_bits(std::size_t n, bit_reader_c &src) {
inline memory_cptr get_buffer() {
return memory_c::clone(m_buffer->get_buffer(), m_size);
}
inline uint64_t copy_bits(std::size_t n, bit_reader_c &src) {
uint64_t value = src.get_bits(n);
put_bits(n, value);
@ -62,52 +61,43 @@ public:
return v & 1 ? (v + 1) / 2 : -(v / 2);
}
void put_bits(std::size_t n, uint64_t value) {
inline void put_bits(std::size_t n, uint64_t value) {
while (0 < n) {
put_bit(value & (1 << (n - 1)));
put_bit(value & (1ull << (n - 1)));
--n;
}
}
void put_bit(bool bit) {
if (m_byte_position >= m_end_of_data) {
m_out_of_data = true;
throw mtx::mm_io::end_of_file_x();
}
inline void put_bit(bool bit) {
extend_buffer_if_needed();
if (bit)
*m_byte_position |= m_mask;
m_data[m_byte_position] |= m_mask;
else
*m_byte_position &= ~m_mask;
m_data[m_byte_position] &= ~m_mask;
m_mask >>= 1;
if (0 == m_mask) {
m_mask = 0x80;
++m_byte_position;
if (m_byte_position == m_end_of_data)
m_out_of_data = true;
}
update_size();
}
void byte_align() {
inline void byte_align() {
while (0x80 != m_mask)
put_bit(0);
}
void set_bit_position(std::size_t pos) {
if (pos > (static_cast<std::size_t>(m_end_of_data - m_start_of_data) * 8)) {
m_byte_position = m_end_of_data;
m_out_of_data = true;
throw mtx::mm_io::seek_x();
}
m_byte_position = m_start_of_data + (pos / 8);
inline void set_bit_position(std::size_t pos) {
m_byte_position = pos / 8;
m_mask = 0x80 >> (pos % 8);
m_out_of_data = m_byte_position == m_end_of_data;
}
std::size_t get_bit_position() const {
std::size_t pos = (m_byte_position - m_start_of_data) * 8;
inline std::size_t get_bit_position() const {
std::size_t pos = m_byte_position * 8;
for (auto i = 0u; 8 > i; ++i)
if ((0x80u >> i) == m_mask) {
pos += i;
@ -116,20 +106,28 @@ public:
return pos;
}
std::size_t get_remaining_bits() const {
return (m_end_of_data - m_start_of_data) * 8 - get_bit_position();
}
void skip_bits(unsigned int num) {
inline void skip_bits(unsigned int num) {
set_bit_position(get_bit_position() + num);
}
void skip_bit() {
inline void skip_bit() {
set_bit_position(get_bit_position() + 1);
}
bool eof() const {
return (m_byte_position == m_end_of_data) || m_out_of_data;
protected:
inline void extend_buffer_if_needed() {
if (m_byte_position < m_buffer->get_size())
return;
auto new_size = (m_byte_position / 100 + 1) * 100;
m_buffer->resize(new_size);
m_data = m_buffer->get_buffer();
std::memset(&m_data[m_size], 0, m_buffer->get_size() - m_size);
}
inline void update_size() {
m_size = std::max(m_size, m_byte_position + (m_mask == 0x80 ? 0 : 1));
}
};
using bit_writer_cptr = std::shared_ptr<bit_writer_c>;

View File

@ -186,36 +186,32 @@ hevcc_c::parse_sei_list() {
if (size == 100)
return true;
auto mcptr_newsei = memory_c::alloc(size);
auto newsei = mcptr_newsei->get_buffer();
bit_writer_c w(newsei, size);
mm_mem_io_c byte_writer{newsei, static_cast<uint64_t>(size), 100};
bit_writer_c w{};
w.put_bits(1, 0); // forbidden_zero_bit
w.put_bits(6, HEVC_NALU_TYPE_PREFIX_SEI); // nal_unit_type
w.put_bits(6, 0); // nuh_reserved_zero_6bits
w.put_bits(3, 1); // nuh_temporal_id_plus1
byte_writer.skip(2); // skip the nalu header
for (iter = m_user_data.begin(); iter != m_user_data.end(); ++iter) {
int payload_size = iter->second.size();
byte_writer.write_uint8(HEVC_SEI_USER_DATA_UNREGISTERED);
w.put_bits(8, HEVC_SEI_USER_DATA_UNREGISTERED);
while (payload_size >= 255) {
byte_writer.write_uint8(255);
w.put_bits(8, 255);
payload_size -= 255;
}
byte_writer.write_uint8(payload_size);
byte_writer.write(&iter->second[0], iter->second.size());
w.put_bits(8, payload_size);
auto data = &iter->second[0];
payload_size = iter->second.size();
for (int idx = 0; idx < payload_size; ++idx)
w.put_bits(8, data[idx]);
}
w.set_bit_position(byte_writer.getFilePointer() * 8);
w.put_bit(1);
w.byte_align();
mcptr_newsei->set_size(w.get_bit_position() / 8);
m_sei_list.push_back(mpeg::rbsp_to_nalu(mcptr_newsei));
m_sei_list.push_back(mpeg::rbsp_to_nalu(w.get_buffer()));
return true;
}
@ -931,11 +927,9 @@ slice_info_t::dump()
bool
parse_vps(memory_cptr const &buffer,
vps_info_t &vps) {
auto size = buffer->get_size();
auto mcptr_newvps = memory_c::alloc(size + 100);
auto newvps = mcptr_newvps->get_buffer();
auto size = buffer->get_size();
bit_reader_c r(buffer->get_buffer(), size);
bit_writer_c w(newvps, size + 100);
bit_writer_c w{};
unsigned int i, j;
memset(&vps, 0, sizeof(vps));
@ -994,8 +988,7 @@ parse_vps(memory_cptr const &buffer,
// Given we don't change the NALU while writing to w,
// then we don't need to replace buffer with the bits we've written into w.
// Leaving this code as reference if we ever do change the NALU while writing to w.
//buffer = mcptr_newvps;
//buffer->set_size(w.get_bit_position() / 8);
//buffer = w.get_buffer();
vps.checksum = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, *buffer);
@ -1007,11 +1000,9 @@ parse_sps(memory_cptr const &buffer,
sps_info_t &sps,
std::vector<vps_info_t> &m_vps_info_list,
bool keep_ar_info) {
auto size = buffer->get_size();
auto mcptr_newsps = memory_c::alloc(size + 100);
auto newsps = mcptr_newsps->get_buffer();
auto size = buffer->get_size();
bit_reader_c r(buffer->get_buffer(), size);
bit_writer_c w(newsps, size + 100);
bit_writer_c w{};
unsigned int i;
keep_ar_info = !hack_engaged(ENGAGE_REMOVE_BITSTREAM_AR_INFO);
@ -1126,10 +1117,9 @@ parse_sps(memory_cptr const &buffer,
w.byte_align();
// We potentially changed the NALU data with regards to the handling of keep_ar_info.
// Therefore, we replace buffer with the changed NALU that exists in w.
mcptr_newsps->resize(w.get_bit_position() / 8);
sps.checksum = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, *mcptr_newsps);
// Therefore, we return the changed NALU that exists in w.
auto new_sps = w.get_buffer();
sps.checksum = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, *new_sps);
// See ITU-T H.265 section 7.4.3.2 for the width/height calculation
// formula.
@ -1141,7 +1131,7 @@ parse_sps(memory_cptr const &buffer,
sps.height -= std::min<unsigned int>((sub_height_c * sps.conf_win_bottom_offset) + (sub_height_c * sps.conf_win_top_offset), sps.height);
}
return mcptr_newsps;
return new_sps;
}
bool

View File

@ -396,12 +396,9 @@ mpeg4::p10::parse_sps(memory_cptr const &buffer,
{ 44, true }, { 83, true }, { 86, true }, { 100, true }, { 110, true }, { 118, true }, { 122, true }, { 128, true }, { 244, true }
};
int const add_space = 100;
int size = buffer->get_size();
auto mcptr_newsps = memory_c::alloc(size + add_space);
auto newsps = mcptr_newsps->get_buffer();
int size = buffer->get_size();
bit_reader_c r(buffer->get_buffer(), size);
bit_writer_c w(newsps, size + add_space);
bit_writer_c w{};
int i, nref, mb_width, mb_height;
keep_ar_info = !hack_engaged(ENGAGE_REMOVE_BITSTREAM_AR_INFO);
@ -657,11 +654,10 @@ mpeg4::p10::parse_sps(memory_cptr const &buffer,
w.put_bit(1);
w.byte_align();
mcptr_newsps->resize(w.get_bit_position() / 8);
auto new_sps = w.get_buffer();
sps.checksum = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, *new_sps);
sps.checksum = mtx::checksum::calculate_as_uint(mtx::checksum::algorithm_e::adler32, *mcptr_newsps);
return mcptr_newsps;
return new_sps;
}
bool

View File

@ -12,9 +12,7 @@ namespace {
// 1111 0111 0010 0011 0100 1010 1000 0001
TEST(BitWriter, PutBit) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
auto b = bit_writer_c{};
// f
EXPECT_NO_THROW(b.put_bit(1));
@ -38,28 +36,28 @@ TEST(BitWriter, PutBit) {
EXPECT_NO_THROW(b.put_bit(1));
EXPECT_EQ(16, b.get_bit_position());
EXPECT_EQ(16, b.get_remaining_bits());
EXPECT_FALSE(b.eof());
EXPECT_EQ(0xf723, get_uint16_be(&value[0]));
EXPECT_EQ(0x0000, get_uint16_be(&value[2]));
auto buffer = b.get_buffer();
ASSERT_EQ(2, buffer->get_size());
EXPECT_EQ(0xf723, get_uint16_be(buffer->get_buffer()));
}
TEST(BitWriter, PutBits) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
auto b = bit_writer_c{};
// f7
EXPECT_NO_THROW(b.put_bits(4, 0x0f));
EXPECT_NO_THROW(b.put_bits(4, 0x07));
EXPECT_EQ(0xf7, value[0]);
EXPECT_EQ(0x000000, get_uint24_be(&value[1]));
auto buffer = b.get_buffer();
ASSERT_EQ(1, buffer->get_size());
EXPECT_EQ(0xf7, buffer->get_buffer()[0]);
// 234
EXPECT_NO_THROW(b.put_bits(12, 0x234));
EXPECT_EQ(0xf72340, get_uint24_be(&value[0]));
EXPECT_EQ(0x00, value[3]);
buffer = b.get_buffer();
ASSERT_EQ(3, buffer->get_size());
EXPECT_EQ(0xf72340, get_uint24_be(buffer->get_buffer()));
// a81 = 101 010 000…
EXPECT_NO_THROW(b.put_bits(3, 0x05));
@ -67,36 +65,36 @@ TEST(BitWriter, PutBits) {
EXPECT_NO_THROW(b.put_bits(3, 0x00));
EXPECT_EQ(29, b.get_bit_position());
EXPECT_EQ( 3, b.get_remaining_bits());
EXPECT_FALSE(b.eof());
EXPECT_EQ(0xf7234a80, get_uint32_be(&value[0]));
buffer = b.get_buffer();
ASSERT_EQ(4, buffer->get_size());
EXPECT_EQ(0xf7234a80, get_uint32_be(buffer->get_buffer()));
}
TEST(BitWriter, ByteAlign) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
auto b = bit_writer_c{};
// f7
b.byte_align();
EXPECT_EQ( 0, b.get_bit_position());
EXPECT_EQ(0x00000000, get_uint32_be(value));
EXPECT_EQ(0, b.get_bit_position());
ASSERT_EQ(0, b.get_buffer()->get_size());
EXPECT_NO_THROW(b.put_bits(2, 0x03));
b.byte_align();
EXPECT_EQ( 8, b.get_bit_position());
EXPECT_EQ(0xc0000000, get_uint32_be(value));
auto buffer = b.get_buffer();
EXPECT_EQ(8, b.get_bit_position());
ASSERT_EQ(1, buffer->get_size());
EXPECT_EQ(0xc0, buffer->get_buffer()[0]);
EXPECT_NO_THROW(b.put_bits(7, 0x11));
b.byte_align();
buffer = b.get_buffer();
EXPECT_EQ( 16, b.get_bit_position());
EXPECT_EQ(0xc0220000, get_uint32_be(value));
ASSERT_EQ(2, buffer->get_size());
EXPECT_EQ(0xc022, get_uint16_be(buffer->get_buffer()));
}
TEST(BitWriter, SkipBits) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
auto b = bit_writer_c{};
EXPECT_EQ(0, b.get_bit_position());
EXPECT_NO_THROW(b.skip_bits(3));
@ -105,13 +103,11 @@ TEST(BitWriter, SkipBits) {
EXPECT_EQ(10, b.get_bit_position());
EXPECT_NO_THROW(b.skip_bit());
EXPECT_EQ(11, b.get_bit_position());
ASSERT_EQ(0, b.get_buffer()->get_size());
}
TEST(BitWriter, SetBitPosition) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
auto b = bit_writer_c{};
EXPECT_EQ(0, b.get_bit_position());
@ -127,23 +123,32 @@ TEST(BitWriter, SetBitPosition) {
EXPECT_NO_THROW(b.set_bit_position(31));
EXPECT_EQ(31, b.get_bit_position());
EXPECT_NO_THROW(b.set_bit_position(32));
EXPECT_THROW(b.set_bit_position(33), mtx::mm_io::seek_x);
}
ASSERT_EQ(0, b.get_buffer()->get_size());
TEST(BitWriter, ExceptionOnReadingBeyondEOF) {
unsigned char value[4];
std::memset(value, 0, 4);
auto b = bit_writer_c{value, 4};
EXPECT_NO_THROW(b.set_bit_position(31));
EXPECT_NO_THROW(b.put_bit(1));
EXPECT_THROW(b.put_bit(1), mtx::mm_io::end_of_file_x);
auto buffer = b.get_buffer();
ASSERT_EQ(4, buffer->get_size());
EXPECT_EQ(0x00000001, get_uint32_be(buffer->get_buffer()));
}
b = bit_writer_c{value, 4};
EXPECT_NO_THROW(b.set_bit_position(24));
EXPECT_NO_THROW(b.put_bits(8, 0x81));
EXPECT_THROW(b.put_bit(1), mtx::mm_io::end_of_file_x);
TEST(BitWriter, ExtendingTheBuffer) {
auto b = bit_writer_c{};
for (int idx = 0; idx < 24; ++idx)
ASSERT_NO_THROW(b.put_bits(32, 0x01234567u));
ASSERT_EQ(96 * 8, b.get_bit_position());
ASSERT_NO_THROW(b.put_bits(64, 0x0123456789abcdefull));
ASSERT_EQ(104 * 8, b.get_bit_position());
auto buffer = b.get_buffer();
ASSERT_EQ(104, buffer->get_size());
EXPECT_EQ(0x01234567u, get_uint32_be(&buffer->get_buffer()[92]));
EXPECT_EQ(0x01234567u, get_uint32_be(&buffer->get_buffer()[96]));
EXPECT_EQ(0x89abcdefu, get_uint32_be(&buffer->get_buffer()[100]));
}
}