Refactoring: avcc_c class for packing/unpacking AvcC structures

This commit is contained in:
Moritz Bunkus 2013-01-11 00:18:36 +01:00
parent e64c9acfce
commit 620d8756bf
6 changed files with 223 additions and 127 deletions

View File

@ -34,6 +34,163 @@
namespace mpeg4 {
namespace p10 {
avcc_c::avcc_c()
: m_profile_idc{}
, m_profile_compat{}
, m_level_idc{}
, m_nalu_size_length{}
{
}
avcc_c::avcc_c(unsigned int nalu_size_length,
std::vector<memory_cptr> const &sps_list,
std::vector<memory_cptr> const &pps_list)
: m_profile_idc{}
, m_profile_compat{}
, m_level_idc{}
, m_nalu_size_length{nalu_size_length}
, m_sps_list{sps_list}
, m_pps_list{pps_list}
{
}
avcc_c::operator bool()
const {
return m_nalu_size_length
&& !m_sps_list.empty()
&& !m_pps_list.empty()
&& (m_sps_info_list.empty() || (m_sps_info_list.size() == m_sps_list.size()))
&& (m_pps_info_list.empty() || (m_pps_info_list.size() == m_pps_list.size()));
}
bool
avcc_c::parse_sps_list(bool ignore_errors) {
if (m_sps_info_list.size() == m_sps_list.size())
return true;
m_sps_info_list.clear();
for (auto &sps: m_sps_list) {
sps_info_t sps_info;
auto sps_as_rbsp = sps->clone();
nalu_to_rbsp(sps_as_rbsp);
if (ignore_errors) {
try {
parse_sps(sps_as_rbsp, sps_info);
} catch (mtx::mm_io::end_of_file_x &) {
}
} else if (!parse_sps(sps_as_rbsp, sps_info))
return false;
m_sps_info_list.push_back(sps_info);
}
return true;
}
bool
avcc_c::parse_pps_list(bool ignore_errors) {
if (m_pps_info_list.size() == m_pps_list.size())
return true;
m_pps_info_list.clear();
for (auto &pps: m_pps_list) {
pps_info_t pps_info;
auto pps_as_rbsp = pps->clone();
nalu_to_rbsp(pps_as_rbsp);
if (ignore_errors) {
try {
parse_pps(pps_as_rbsp, pps_info);
} catch (mtx::mm_io::end_of_file_x &) {
}
} else if (!parse_pps(pps_as_rbsp, pps_info))
return false;
m_pps_info_list.push_back(pps_info);
}
return true;
}
memory_cptr
avcc_c::pack() {
parse_sps_list(true);
if (!*this)
return memory_cptr{};
unsigned int total_size = 6 + 1;
for (auto &mem : m_sps_list)
total_size += mem->get_size() + 2;
for (auto &mem : m_pps_list)
total_size += mem->get_size() + 2;
auto destination = memory_c::alloc(total_size);
auto buffer = destination->get_buffer();
auto &sps = *m_sps_info_list.begin();
auto write_list = [&buffer](std::vector<memory_cptr> const &list, uint8_t num_byte_bits = 0) {
*buffer = list.size() | num_byte_bits;
for (auto &mem : list) {
auto size = mem->get_size();
put_uint16_be(buffer + 1, size);
memcpy(buffer + 3, mem->get_buffer(), size);
buffer += 3 + size;
}
};
buffer[0] = 1;
buffer[1] = m_profile_idc ? m_profile_idc : sps.profile_idc;
buffer[2] = m_profile_compat ? m_profile_compat : sps.profile_compat;
buffer[3] = m_level_idc ? m_level_idc : sps.level_idc;
buffer[4] = 0xfc | (m_nalu_size_length - 1);
buffer += 5;
write_list(m_sps_list, 0xe0);
write_list(m_pps_list);
return destination;
}
avcc_c
avcc_c::unpack(memory_cptr const &mem) {
avcc_c avcc;
if (!mem || (6 > mem->get_size()))
return avcc;
try {
mm_mem_io_c in{*mem};
auto read_list = [&in](std::vector<memory_cptr> &list, unsigned int num_entries_mask = 0xff) {
auto num_entries = in.read_uint8() & num_entries_mask;
while (num_entries) {
auto size = in.read_uint16_be();
list.push_back(in.read(size));
--num_entries;
}
};
in.skip(1); // always 1
avcc.m_profile_idc = in.read_uint8();
avcc.m_profile_compat = in.read_uint8();
avcc.m_level_idc = in.read_uint8();
avcc.m_nalu_size_length = (in.read_uint8() & 0x03) + 1;
read_list(avcc.m_sps_list, 0x0f);
read_list(avcc.m_pps_list);
return avcc;
} catch (mtx::mm_io::exception &) {
return avcc_c{};
}
}
static const struct {
int numerator, denominator;
} s_predefined_pars[AVC_NUM_PREDEFINED_PARS] = {
@ -531,55 +688,42 @@ mpeg4::p10::extract_par(uint8_t *&buffer,
uint32_t &par_num,
uint32_t &par_den) {
try {
mm_mem_io_c avcc(buffer, buffer_size), new_avcc(nullptr, buffer_size, 1024);
memory_cptr nalu(new memory_c());
auto avcc = avcc_c::unpack(memory_cptr{new memory_c{buffer, buffer_size, false}});
auto new_avcc = avcc;
par_num = 1;
par_den = 1;
bool ar_found = false;
avcc.read(nalu, 5);
new_avcc.write(nalu);
new_avcc.m_sps_list.clear();
unsigned int num_sps = avcc.read_uint8();
new_avcc.write_uint8(num_sps);
num_sps &= 0x1f;
mxverb(4, boost::format("mpeg4_p10_extract_par: num_sps %1%\n") % num_sps);
unsigned int sps;
for (sps = 0; sps < num_sps; sps++) {
unsigned int length = avcc.read_uint16_be();
if ((length + avcc.getFilePointer()) >= buffer_size)
length = buffer_size - avcc.getFilePointer();
avcc.read(nalu, length);
bool abort = false;
if ((0 < length) && ((nalu->get_buffer()[0] & 0x1f) == 7)) {
for (auto &nalu : avcc.m_sps_list) {
if (!ar_found) {
nalu_to_rbsp(nalu);
sps_info_t sps_info;
if (mpeg4::p10::parse_sps(nalu, sps_info)) {
ar_found = sps_info.ar_found;
if (ar_found) {
par_num = sps_info.par_num;
par_den = sps_info.par_den;
try {
sps_info_t sps_info;
if (mpeg4::p10::parse_sps(nalu, sps_info)) {
ar_found = sps_info.ar_found;
if (ar_found) {
par_num = sps_info.par_num;
par_den = sps_info.par_den;
}
}
} catch (mtx::mm_io::end_of_file_x &) {
}
rbsp_to_nalu(nalu);
abort = true;
}
new_avcc.write_uint16_be(nalu->get_size());
new_avcc.write(nalu);
if (abort) {
avcc.read(nalu, buffer_size - avcc.getFilePointer());
new_avcc.write(nalu);
break;
}
new_avcc.m_sps_list.push_back(nalu);
}
buffer_size = new_avcc.getFilePointer();
buffer = new_avcc.get_and_lock_buffer();
auto packed_new_avcc = new_avcc.pack();
if (packed_new_avcc) {
buffer_size = packed_new_avcc->get_size();
buffer = packed_new_avcc->get_buffer();
packed_new_avcc->lock();
}
return ar_found;
@ -1391,51 +1535,10 @@ mpeg4::p10::avc_es_parser_c::create_nalu_with_size(const memory_cptr &src,
return memory_cptr(new memory_c(buffer, final_size, true));
}
// TODO:DRY
memory_cptr
mpeg4::p10::avc_es_parser_c::get_avcc()
const {
int final_size = 6 + 1;
int offset = 6;
for (auto &mem : m_sps_list)
final_size += mem->get_size() + 2;
for (auto &mem : m_pps_list)
final_size += mem->get_size() + 2;
memory_cptr destination = memory_c::alloc(final_size);
unsigned char *buffer = destination->get_buffer();
assert(!m_sps_list.empty());
sps_info_t const &sps = *m_sps_info_list.begin();
buffer[0] = 1;
buffer[1] = sps.profile_idc;
buffer[2] = sps.profile_compat;
buffer[3] = sps.level_idc;
buffer[4] = 0xfc | (m_nalu_size_length - 1);
buffer[5] = 0xe0 | m_sps_list.size();
for (auto &mem : m_sps_list) {
int size = mem->get_size();
write_nalu_size(buffer + offset, size, 2);
memcpy(&buffer[offset + 2], mem->get_buffer(), size);
offset += 2 + size;
}
buffer[offset] = m_pps_list.size();
++offset;
for (auto &mem : m_pps_list) {
int size = mem->get_size();
write_nalu_size(buffer + offset, size, 2);
memcpy(&buffer[offset + 2], mem->get_buffer(), size);
offset += 2 + size;
}
return destination;
return avcc_c{static_cast<unsigned int>(m_nalu_size_length), m_sps_list, m_pps_list}.pack();
}
bool

View File

@ -196,6 +196,26 @@ public:
}
};
class avcc_c {
public:
unsigned int m_profile_idc, m_profile_compat, m_level_idc, m_nalu_size_length;
std::vector<memory_cptr> m_sps_list, m_pps_list;
std::vector<sps_info_t> m_sps_info_list;
std::vector<pps_info_t> m_pps_info_list;
public:
avcc_c();
avcc_c(unsigned int nalu_size_len, std::vector<memory_cptr> const &sps_list, std::vector<memory_cptr> const &pps_list);
explicit operator bool() const;
memory_cptr pack();
bool parse_sps_list(bool ignore_errors = false);
bool parse_pps_list(bool ignore_errors = false);
static avcc_c unpack(memory_cptr const &mem);
};
class avc_es_parser_c {
protected:
int m_nalu_size_length;
@ -216,7 +236,7 @@ protected:
int64_t m_max_timecode;
std::map<int64_t, int64_t> m_duration_frequency;
std::deque<memory_cptr> m_sps_list, m_pps_list, m_extra_data;
std::vector<memory_cptr> m_sps_list, m_pps_list, m_extra_data;
std::vector<sps_info_t> m_sps_info_list;
std::vector<pps_info_t> m_pps_info_list;

View File

@ -75,6 +75,7 @@
#include "common/matroska.h"
#include "common/mm_io.h"
#include "common/mm_io_x.h"
#include "common/mpeg4_p10.h"
#include "common/stereo_mode.h"
#include "common/strings/editing.h"
#include "common/strings/formatting.h"
@ -337,26 +338,24 @@ create_codec_dependent_private_info(KaxCodecPrivate &c_priv,
return (boost::format(Y(" (format tag: 0x%|1$04x|)")) % get_uint16_le(&wfe->w_format_tag)).str();
} else if ((codec_id == MKV_V_MPEG4_AVC) && ('v' == track_type) && (c_priv.GetSize() >= 4)) {
unsigned char *avcc = c_priv.GetBuffer();
unsigned int profile_idc = avcc[1];
unsigned int level_idc = avcc[3];
auto avcc = mpeg4::p10::avcc_c::unpack(memory_cptr{new memory_c(c_priv.GetBuffer(), c_priv.GetSize(), false)});
return (boost::format(Y(" (h.264 profile: %1% @L%2%.%3%)"))
% ( profile_idc == 44 ? "CAVLC 4:4:4 Intra"
: profile_idc == 66 ? "Baseline"
: profile_idc == 77 ? "Main"
: profile_idc == 83 ? "Scalable Baseline"
: profile_idc == 86 ? "Scalable High"
: profile_idc == 88 ? "Extended"
: profile_idc == 100 ? "High"
: profile_idc == 110 ? "High 10"
: profile_idc == 118 ? "Multiview High"
: profile_idc == 122 ? "High 4:2:2"
: profile_idc == 128 ? "Stereo High"
: profile_idc == 144 ? "High 4:4:4"
: profile_idc == 244 ? "High 4:4:4 Predictive"
: Y("Unknown"))
% (level_idc / 10) % (level_idc % 10)).str();
% ( avcc.m_profile_idc == 44 ? "CAVLC 4:4:4 Intra"
: avcc.m_profile_idc == 66 ? "Baseline"
: avcc.m_profile_idc == 77 ? "Main"
: avcc.m_profile_idc == 83 ? "Scalable Baseline"
: avcc.m_profile_idc == 86 ? "Scalable High"
: avcc.m_profile_idc == 88 ? "Extended"
: avcc.m_profile_idc == 100 ? "High"
: avcc.m_profile_idc == 110 ? "High 10"
: avcc.m_profile_idc == 118 ? "Multiview High"
: avcc.m_profile_idc == 122 ? "High 4:2:2"
: avcc.m_profile_idc == 128 ? "Stereo High"
: avcc.m_profile_idc == 144 ? "High 4:4:4"
: avcc.m_profile_idc == 244 ? "High 4:4:4 Predictive"
: Y("Unknown"))
% (avcc.m_level_idc / 10) % (avcc.m_level_idc % 10)).str();
}
return "";

View File

@ -384,25 +384,10 @@ bool
flv_reader_c::new_stream_v_avc(flv_track_cptr &track,
memory_cptr const &data) {
try {
mm_mem_io_c in(*data.get());
unsigned char buf[6];
if (in.read(buf, 6) != 6)
return false;
auto num_sps = static_cast<unsigned int>(buf[5] & 0x1f);
for (auto sps = 0u; sps < num_sps; sps++) {
auto sps_length = in.read_uint16_be();
auto sps_buf = in.read(sps_length);
mpeg4::p10::sps_info_t sps_info;
try {
if (!mpeg4::p10::parse_sps(sps_buf, sps_info, true))
return false;
} catch (mtx::mm_io::end_of_file_x &) {
}
auto avcc = mpeg4::p10::avcc_c::unpack(data);
avcc.parse_sps_list(true);
for (auto &sps_info : avcc.m_sps_info_list) {
if (!track->m_v_width)
track->m_v_width = sps_info.width;
if (!track->m_v_height)
@ -411,16 +396,6 @@ flv_reader_c::new_stream_v_avc(flv_track_cptr &track,
track->m_v_frame_rate = sps_info.time_scale / sps_info.num_units_in_tick;
}
auto num_pps = in.read_uint8();
for (auto pps = 0u; pps < num_pps; pps++) {
auto pps_length = in.read_uint16_be();
auto pps_buf = in.read(pps_length);
mpeg4::p10::pps_info_t pps_info;
if (!mpeg4::p10::parse_pps(pps_buf, pps_info))
return false;
}
if (!track->m_v_frame_rate)
track->m_v_frame_rate = 25;

View File

@ -116,7 +116,6 @@ struct mpeg_ps_track_t {
bool v_interlaced;
int v_version, v_width, v_height, v_dwidth, v_dheight;
double v_frame_rate, v_aspect_ratio;
memory_cptr v_avcc;
unsigned char *raw_seq_hdr;
int raw_seq_hdr_size;

View File

@ -283,7 +283,7 @@ public:
bool v_interlaced;
int v_version, v_width, v_height, v_dwidth, v_dheight;
double v_frame_rate, v_aspect_ratio;
memory_cptr v_avcc, raw_seq_hdr;
memory_cptr raw_seq_hdr;
// audio related parameters
int a_channels, a_sample_rate, a_bits_per_sample, a_bsid;