From 9baa0108480db00310a7761a902add3962e406f1 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 19 Jul 2017 23:07:27 +0200 Subject: [PATCH] bit_writer_c: let writer manage memory on its own --- src/common/bit_writer.h | 88 ++++++++++++++--------------- src/common/hevc.cpp | 48 +++++++--------- src/common/mpeg4_p10.cpp | 14 ++--- tests/unit/common/bit_writer.cpp | 97 +++++++++++++++++--------------- 4 files changed, 118 insertions(+), 129 deletions(-) diff --git a/src/common/bit_writer.h b/src/common/bit_writer.h index a02198cc5..ccea649c6 100644 --- a/src/common/bit_writer.h +++ b/src/common/bit_writer.h @@ -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(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; diff --git a/src/common/hevc.cpp b/src/common/hevc.cpp index 6b8dd82d5..51aca8356 100644 --- a/src/common/hevc.cpp +++ b/src/common/hevc.cpp @@ -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(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 &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((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 diff --git a/src/common/mpeg4_p10.cpp b/src/common/mpeg4_p10.cpp index 487dfa64b..30222596b 100644 --- a/src/common/mpeg4_p10.cpp +++ b/src/common/mpeg4_p10.cpp @@ -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 diff --git a/tests/unit/common/bit_writer.cpp b/tests/unit/common/bit_writer.cpp index ce1766210..252775bfd 100644 --- a/tests/unit/common/bit_writer.cpp +++ b/tests/unit/common/bit_writer.cpp @@ -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])); } }