From ba7f2560fd91d0cdfe9b7caa63e310aedb013a2f Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Tue, 29 Dec 2015 14:49:30 +0100 Subject: [PATCH] truehd_parser_c: re-factor header parsing to use bit_reader_c This is laying the groundwork for easier support for detecting Atmos. --- src/common/truehd.cpp | 161 ++++++++++++++++++++++++++---------------- src/common/truehd.h | 14 +++- 2 files changed, 114 insertions(+), 61 deletions(-) diff --git a/src/common/truehd.cpp b/src/common/truehd.cpp index 7faa4e44d..73aedfc9c 100644 --- a/src/common/truehd.cpp +++ b/src/common/truehd.cpp @@ -13,10 +13,103 @@ #include "common/common_pch.h" +#include "common/bit_cursor.h" #include "common/endian.h" +#include "common/list_utils.h" #include "common/memory.h" #include "common/truehd.h" +int const truehd_frame_t::ms_sampling_rates[16] = { 48000, 96000, 192000, 0, 0, 0, 0, 0, 44100, 88200, 176400, 0, 0, 0, 0, 0 }; +uint8_t const truehd_frame_t::ms_mlp_channels[32] = { 1, 2, 3, 4, 3, 4, 5, 3, 4, 5, 4, 5, 6, 4, 5, 4, + 5, 6, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +constexpr std::size_t PARSER_MIN_HEADER_SIZE = 12; + +bool +truehd_frame_t::parse_header(unsigned char const *data, + std::size_t size) { + if (size < PARSER_MIN_HEADER_SIZE) + return false; + + auto const first_word = get_uint16_be(&data[0]); + auto const sync_word = get_uint32_be(&data[4]); + + if (!mtx::included_in(sync_word, TRUEHD_SYNC_WORD, MLP_SYNC_WORD) && (AC3_SYNC_WORD == first_word)) + return parse_ac3_header(data, size); + + m_codec = !mtx::included_in(sync_word, TRUEHD_SYNC_WORD, MLP_SYNC_WORD) ? truehd : TRUEHD_SYNC_WORD == sync_word ? truehd : mlp; + m_type = mtx::included_in(sync_word, TRUEHD_SYNC_WORD, MLP_SYNC_WORD) ? sync : normal; + m_size = (first_word & 0xfff) * 2; + + if (normal == m_type) + return true; + + return m_codec == truehd ? parse_truehd_header(data, size) : parse_mlp_header(data, size); +} + +bool +truehd_frame_t::parse_ac3_header(unsigned char const *data, + std::size_t size) { + if (!m_ac3_header.decode_header(data, size)) + return false; + + m_codec = truehd_frame_t::ac3; + m_type = truehd_frame_t::sync; + m_size = m_ac3_header.m_bytes; + + return size >= m_ac3_header.m_bytes; +} + +bool +truehd_frame_t::parse_mlp_header(unsigned char const *data, + std::size_t) { + m_sampling_rate = ms_sampling_rates[data[9] >> 4]; + m_samples_per_frame = 40 << ((data[9] >> 4) & 0x07); + m_channels = ms_mlp_channels[data[11] & 0x1f]; + + return true; +} + +bool +truehd_frame_t::parse_truehd_header(unsigned char const *data, + std::size_t size) { + try { + bit_reader_c r{&data[8], size - 8}; + + m_samples_per_frame = 40 << (r.get_bits(4) & 0x07); + m_sampling_rate = ms_sampling_rates[r.get_bits(4)]; + + auto chanmap_substream_1 = r.skip_get_bits(4, 5); + auto chanmap_substream_2 = r.skip_get_bits(2, 13); + m_channels = decode_channel_map(chanmap_substream_2 ? chanmap_substream_2 : chanmap_substream_1); + + return true; + + } catch (...) { + } + + return false; +} + +// Code for truehd_parser_c::decode_channel_map was taken from +// the ffmpeg project, source file "libavcodec/mlp_parser.c". +int +truehd_frame_t::decode_channel_map(int channel_map) { + static const int s_channel_count[13] = { + // LR C LFE LRs LRvh LRc LRrs Cs Ts LRsd LRw Cvh LFE2 + 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1 + }; + + int channels = 0, i; + + for (i = 0; 13 > i; ++i) + channels += s_channel_count[i] * ((channel_map >> i) & 1); + + return channels; +} + +// ---------------------------------------------------------------------- + truehd_parser_c::truehd_parser_c() : m_sync_state(state_unsynced) { @@ -26,23 +119,6 @@ truehd_parser_c::~truehd_parser_c() { } -// Code for truehd_parser_c::decode_channel_map was taken from -// the ffmpeg project, source file "libavcodec/mlp_parser.c". -int -truehd_parser_c::decode_channel_map(int channel_map) { - static const int channel_count[13] = { - // LR C LFE LRs LRvh LRc LRrs Cs Ts LRsd LRw Cvh LFE2 - 2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1 - }; - - int channels = 0, i; - - for (i = 0; 13 > i; ++i) - channels += channel_count[i] * ((channel_map >> i) & 1); - - return channels; -} - void truehd_parser_c::add_data(const unsigned char *new_data, unsigned int new_size) { @@ -60,7 +136,7 @@ truehd_parser_c::parse(bool end_of_stream) { unsigned int size = m_buffer.get_size(); unsigned int offset = 0; - if (10 > size) + if (PARSER_MIN_HEADER_SIZE > size) return; if (state_unsynced == m_sync_state) { @@ -69,49 +145,14 @@ truehd_parser_c::parse(bool end_of_stream) { return; } - static const int sampling_rates[] = { 48000, 96000, 192000, 0, 0, 0, 0, 0, 44100, 88200, 176400, 0, 0, 0, 0, 0 }; - static const uint8_t mlp_channels[32] = { 1, 2, 3, 4, 3, 4, 5, 3, 4, 5, 4, 5, 6, 4, 5, 4, - 5, 6, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + while ((size - offset) >= PARSER_MIN_HEADER_SIZE) { + auto frame = std::make_shared(); + auto result = frame->parse_header(&data[offset], size - offset); - while ((size - offset) >= 10) { - truehd_frame_cptr frame(new truehd_frame_t); - - uint32_t sync_word = get_uint32_be(&data[offset + 4]); - if ((TRUEHD_SYNC_WORD == sync_word) || (MLP_SYNC_WORD == sync_word)) { - frame->m_codec = TRUEHD_SYNC_WORD == sync_word ? truehd_frame_t::truehd : truehd_frame_t::mlp; - frame->m_type = truehd_frame_t::sync; - frame->m_size = (((data[offset] << 8) | data[offset + 1]) & 0xfff) * 2; - - if (frame->is_truehd()) { - frame->m_sampling_rate = sampling_rates[data[offset + 8] >> 4]; - frame->m_samples_per_frame = 40 << ((data[offset + 8] >> 4) & 0x07); - int chanmap_substream_1 = ((data[offset + 9] & 0x0f) << 1) | (data[offset + 10] >> 7); - int chanmap_substream_2 = ((data[offset + 10] & 0x1f) << 8) | data[offset + 11]; - frame->m_channels = decode_channel_map(chanmap_substream_2 ? chanmap_substream_2 : chanmap_substream_1); - - } else { - frame->m_sampling_rate = sampling_rates[data[offset + 9] >> 4]; - frame->m_samples_per_frame = 40 << ((data[offset + 9] >> 4) & 0x07); - frame->m_channels = mlp_channels[data[offset + 11] & 0x1f]; - } - - } else if (get_uint16_be(&data[offset]) == AC3_SYNC_WORD) { - if (frame->m_ac3_header.decode_header(&data[offset], size - offset)) { - if (((size - offset) < frame->m_ac3_header.m_bytes) && !end_of_stream) - break; - - if (((size - offset) >= frame->m_ac3_header.m_bytes) && verify_ac3_checksum(&data[offset], size - offset)) { - frame->m_codec = truehd_frame_t::ac3; - frame->m_type = truehd_frame_t::sync; - frame->m_size = frame->m_ac3_header.m_bytes; - } - } - } - - if (truehd_frame_t::invalid == frame->m_type) { - frame->m_type = truehd_frame_t::normal; - frame->m_size = (((data[offset] << 8) | data[offset + 1]) & 0xfff) * 2; - } + if ( !result + && ( !frame->is_ac3() + || (frame->is_ac3() && !end_of_stream))) + break; if (8 > frame->m_size) { unsigned int synced_at = resync(offset + 1); diff --git a/src/common/truehd.h b/src/common/truehd.h index 46a14c798..70e279603 100644 --- a/src/common/truehd.h +++ b/src/common/truehd.h @@ -26,6 +26,9 @@ #define MLP_SYNC_WORD 0xf8726fbb struct truehd_frame_t { + static int const ms_sampling_rates[16]; + static uint8_t const ms_mlp_channels[32]; + enum codec_e { truehd, mlp, @@ -71,6 +74,16 @@ struct truehd_frame_t { : is_mlp() ? codec_c::look_up(codec_c::type_e::A_MLP) : codec_c::look_up(codec_c::type_e::A_TRUEHD); } + + bool parse_header(unsigned char const *data, std::size_t size); + +protected: + bool parse_ac3_header(unsigned char const *data, std::size_t size); + bool parse_mlp_header(unsigned char const *data, std::size_t size); + bool parse_truehd_header(unsigned char const *data, std::size_t size); + +public: + static int decode_channel_map(int channel_map); }; using truehd_frame_cptr = std::shared_ptr; @@ -95,7 +108,6 @@ public: protected: virtual unsigned int resync(unsigned int offset); - virtual int decode_channel_map(int channel_map); }; using truehd_parser_cptr = std::shared_ptr;