From 620d8756bff8e2292dd68bbb506e5fd291a6fca2 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 11 Jan 2013 00:18:36 +0100 Subject: [PATCH] Refactoring: avcc_c class for packing/unpacking AvcC structures --- src/common/mpeg4_p10.cpp | 259 +++++++++++++++++++++++++++------------ src/common/mpeg4_p10.h | 22 +++- src/info/mkvinfo.cpp | 35 +++--- src/input/r_flv.cpp | 31 +---- src/input/r_mpeg_ps.h | 1 - src/input/r_mpeg_ts.h | 2 +- 6 files changed, 223 insertions(+), 127 deletions(-) diff --git a/src/common/mpeg4_p10.cpp b/src/common/mpeg4_p10.cpp index 2cad1efe9..f5554bb26 100644 --- a/src/common/mpeg4_p10.cpp +++ b/src/common/mpeg4_p10.cpp @@ -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 const &sps_list, + std::vector 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 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 &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(m_nalu_size_length), m_sps_list, m_pps_list}.pack(); } bool diff --git a/src/common/mpeg4_p10.h b/src/common/mpeg4_p10.h index a4cd834d6..9a43b5290 100644 --- a/src/common/mpeg4_p10.h +++ b/src/common/mpeg4_p10.h @@ -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 m_sps_list, m_pps_list; + std::vector m_sps_info_list; + std::vector m_pps_info_list; + +public: + avcc_c(); + avcc_c(unsigned int nalu_size_len, std::vector const &sps_list, std::vector 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 m_duration_frequency; - std::deque m_sps_list, m_pps_list, m_extra_data; + std::vector m_sps_list, m_pps_list, m_extra_data; std::vector m_sps_info_list; std::vector m_pps_info_list; diff --git a/src/info/mkvinfo.cpp b/src/info/mkvinfo.cpp index a1a5a5796..f6cae34e3 100644 --- a/src/info/mkvinfo.cpp +++ b/src/info/mkvinfo.cpp @@ -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 ""; diff --git a/src/input/r_flv.cpp b/src/input/r_flv.cpp index 18181a19c..795362c10 100755 --- a/src/input/r_flv.cpp +++ b/src/input/r_flv.cpp @@ -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(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; diff --git a/src/input/r_mpeg_ps.h b/src/input/r_mpeg_ps.h index 4fed727b3..e13ab5ee2 100644 --- a/src/input/r_mpeg_ps.h +++ b/src/input/r_mpeg_ps.h @@ -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; diff --git a/src/input/r_mpeg_ts.h b/src/input/r_mpeg_ts.h index 065496480..6522e97d9 100644 --- a/src/input/r_mpeg_ts.h +++ b/src/input/r_mpeg_ts.h @@ -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;