mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-28 13:57:22 +00:00
bit_writer_c: let writer manage memory on its own
This commit is contained in:
parent
319c8009e0
commit
9baa010848
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user