parsers: improve AAC/LATM parser
This commit is contained in:
parent
cd1f081909
commit
cc1fbb0202
6 changed files with 169 additions and 107 deletions
|
@ -40,23 +40,10 @@ init_wbits(bitstream_t *bs, uint8_t *data, int bits)
|
|||
bs->len = bits;
|
||||
}
|
||||
|
||||
void
|
||||
skip_bits(bitstream_t *bs, int num)
|
||||
{
|
||||
bs->offset += num;
|
||||
}
|
||||
|
||||
int
|
||||
bs_eof(const bitstream_t *bs)
|
||||
{
|
||||
return bs->offset >= bs->len;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
read_bits(bitstream_t *bs, int num)
|
||||
{
|
||||
int r = 0;
|
||||
unsigned int r = 0;
|
||||
|
||||
while(num > 0) {
|
||||
if(bs->offset >= bs->len)
|
||||
|
@ -73,9 +60,22 @@ read_bits(bitstream_t *bs, int num)
|
|||
}
|
||||
|
||||
unsigned int
|
||||
read_bits1(bitstream_t *bs)
|
||||
show_bits(bitstream_t *bs, int num)
|
||||
{
|
||||
return read_bits(bs, 1);
|
||||
unsigned int r = 0, offset = bs->offset;
|
||||
|
||||
while(num > 0) {
|
||||
if(offset >= bs->len)
|
||||
return 0;
|
||||
|
||||
num--;
|
||||
|
||||
if(bs->rdata[offset / 8] & (1 << (7 - (offset & 7))))
|
||||
r |= 1 << num;
|
||||
|
||||
offset++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
|
@ -106,13 +106,6 @@ read_golomb_se(bitstream_t *bs)
|
|||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
remaining_bits(bitstream_t *bs)
|
||||
{
|
||||
return bs->len - bs->offset;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
put_bits(bitstream_t *bs, int val, int num)
|
||||
{
|
||||
|
|
|
@ -26,7 +26,8 @@ typedef struct bitstream {
|
|||
int len;
|
||||
} bitstream_t;
|
||||
|
||||
void skip_bits(bitstream_t *bs, int num);
|
||||
static inline void skip_bits(bitstream_t *bs, int num)
|
||||
{ bs->offset += num; }
|
||||
|
||||
void init_rbits(bitstream_t *bs, const uint8_t *data, int bits);
|
||||
|
||||
|
@ -34,16 +35,21 @@ void init_wbits(bitstream_t *bs, uint8_t *data, int bits);
|
|||
|
||||
unsigned int read_bits(bitstream_t *gb, int num);
|
||||
|
||||
unsigned int read_bits1(bitstream_t *gb);
|
||||
unsigned int show_bits(bitstream_t *gb, int num);
|
||||
|
||||
static inline unsigned int read_bits1(bitstream_t *gb)
|
||||
{ return read_bits(gb, 1); }
|
||||
|
||||
unsigned int read_golomb_ue(bitstream_t *gb);
|
||||
|
||||
signed int read_golomb_se(bitstream_t *gb);
|
||||
|
||||
unsigned int remaining_bits(bitstream_t *gb);
|
||||
static inline unsigned int remaining_bits(bitstream_t *gb)
|
||||
{ return gb->len - gb->offset; }
|
||||
|
||||
void put_bits(bitstream_t *bs, int val, int num);
|
||||
|
||||
int bs_eof(const bitstream_t *bs);
|
||||
static inline int bs_eof(const bitstream_t *bs)
|
||||
{ return bs->offset >= bs->len; }
|
||||
|
||||
#endif /* BITSTREAM_H_ */
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef struct latm_private {
|
|||
|
||||
int configured;
|
||||
int audio_mux_version_A;
|
||||
int aot;
|
||||
int frame_length_type;
|
||||
|
||||
int sample_rate_index;
|
||||
|
@ -55,74 +56,110 @@ latm_get_value(bitstream_t *bs)
|
|||
return read_bits(bs, read_bits(bs, 2) * 8);
|
||||
}
|
||||
|
||||
#define AOT_AAC_MAIN 1
|
||||
#define AOT_AAC_LC 2
|
||||
#define AOT_AAC_SSR 3
|
||||
#define AOT_AAC_LTP 4
|
||||
#define AOT_SBR 5
|
||||
#define AOT_PS 26
|
||||
#define AOT_ESCAPE 28
|
||||
|
||||
static void
|
||||
static inline int adts_aot(int aot)
|
||||
{
|
||||
switch (aot) {
|
||||
case AOT_AAC_MAIN: return 0;
|
||||
case AOT_AAC_LC : return 1;
|
||||
case AOT_AAC_SSR : return 2;
|
||||
default: return 3; /* reserved or LTP */
|
||||
}
|
||||
}
|
||||
|
||||
static inline int read_aot(bitstream_t *bs)
|
||||
{
|
||||
int aot = read_bits(bs, 5);
|
||||
if (aot == AOT_ESCAPE)
|
||||
aot = read_bits(bs, 6);
|
||||
return aot;
|
||||
}
|
||||
|
||||
static inline int read_sr(bitstream_t *bs, int *sri)
|
||||
{
|
||||
*sri = read_bits(bs, 4);
|
||||
if (*sri == 0x0f)
|
||||
return read_bits(bs, 24);
|
||||
else
|
||||
return sri_to_rate(*sri);
|
||||
}
|
||||
|
||||
static int
|
||||
read_audio_specific_config(elementary_stream_t *st, latm_private_t *latm,
|
||||
bitstream_t *bs)
|
||||
{
|
||||
int aot, sr;
|
||||
|
||||
aot = read_bits(bs, 5);
|
||||
if ((bs->offset % 8) != 0)
|
||||
return -1;
|
||||
|
||||
latm->sample_rate_index = read_bits(bs, 4);
|
||||
|
||||
if(latm->sample_rate_index == 0xf)
|
||||
sr = read_bits(bs, 24);
|
||||
else
|
||||
sr = sri_to_rate(latm->sample_rate_index);
|
||||
|
||||
if(sr == 0)
|
||||
return;
|
||||
aot = read_aot(bs);
|
||||
sr = read_sr(bs, &latm->sample_rate_index);
|
||||
latm->channel_config = read_bits(bs, 4);
|
||||
|
||||
st->es_frame_duration = 1024 * 90000 / sr;
|
||||
|
||||
latm->channel_config = read_bits(bs, 4);
|
||||
|
||||
if (aot == 5) { // AOT_SBR
|
||||
if (read_bits(bs, 4) == 0xf) { // extensionSamplingFrequencyIndex
|
||||
skip_bits(bs, 24);
|
||||
}
|
||||
aot = read_bits(bs, 5); // this is the main object type (i.e. non-extended)
|
||||
if (aot == AOT_SBR ||
|
||||
(aot == AOT_PS && !(show_bits(bs, 3) & 3 && !(show_bits(bs, 9) & 0x3f)))) {
|
||||
sr = read_sr(bs, &latm->sample_rate_index);
|
||||
aot = read_aot(bs); // this is the main object type (i.e. non-extended)
|
||||
}
|
||||
|
||||
if(aot != 2)
|
||||
return;
|
||||
if (sr == 0 || latm->channel_config == 0)
|
||||
return -1;
|
||||
if (aot != AOT_AAC_MAIN && aot != AOT_AAC_LC &&
|
||||
aot != AOT_AAC_SSR && aot != AOT_AAC_LTP)
|
||||
return -1;
|
||||
latm->aot = aot;
|
||||
|
||||
skip_bits(bs, 1); //framelen_flag
|
||||
if(read_bits1(bs)) // depends_on_coder
|
||||
if (read_bits1(bs)) // framelen_flag
|
||||
return -1;
|
||||
if (read_bits1(bs)) // depends_on_coder
|
||||
skip_bits(bs, 14);
|
||||
|
||||
if(read_bits(bs, 1)) // ext_flag
|
||||
if (read_bits1(bs)) // ext_flag
|
||||
skip_bits(bs, 1); // ext3_flag
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
static int
|
||||
read_stream_mux_config(elementary_stream_t *st, latm_private_t *latm, bitstream_t *bs)
|
||||
{
|
||||
int audio_mux_version = read_bits(bs, 1);
|
||||
int audio_mux_version = read_bits1(bs);
|
||||
latm->audio_mux_version_A = 0;
|
||||
if(audio_mux_version) // audioMuxVersion
|
||||
latm->audio_mux_version_A = read_bits(bs, 1);
|
||||
if(audio_mux_version) // audioMuxVersion
|
||||
latm->audio_mux_version_A = read_bits1(bs);
|
||||
|
||||
if(latm->audio_mux_version_A)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if(audio_mux_version)
|
||||
latm_get_value(bs); // taraFullness
|
||||
latm_get_value(bs); // taraFullness
|
||||
|
||||
skip_bits(bs, 1); // allStreamSameTimeFraming = 1
|
||||
skip_bits(bs, 6); // numSubFrames = 0
|
||||
skip_bits(bs, 4); // numPrograms = 0
|
||||
|
||||
// for each program (which there is only on in DVB)
|
||||
skip_bits(bs, 3); // numLayer = 0
|
||||
if (read_bits(bs, 4)) // numPrograms = 0
|
||||
return -1;
|
||||
|
||||
// for each program (only one in DVB)
|
||||
if (read_bits(bs, 3)) // numLayer = 0
|
||||
return -1;
|
||||
|
||||
// for each layer (which there is only on in DVB)
|
||||
if(!audio_mux_version)
|
||||
read_audio_specific_config(st, latm, bs);
|
||||
else {
|
||||
return;
|
||||
// for each layer (which there is only one in DVB)
|
||||
if(!audio_mux_version) {
|
||||
if (read_audio_specific_config(st, latm, bs) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
#if 0 // see bellow (look for dead code)
|
||||
uint32_t ascLen = latm_get_value(bs);
|
||||
abort(); // ascLen -= read_audio_specific_config(filter, gb);
|
||||
|
@ -150,24 +187,25 @@ read_stream_mux_config(elementary_stream_t *st, latm_private_t *latm, bitstream_
|
|||
break;
|
||||
}
|
||||
|
||||
if(read_bits(bs, 1)) { // other data?
|
||||
if(read_bits1(bs)) { // other data?
|
||||
#if 0 // coverity - dead code - see above
|
||||
if(audio_mux_version)
|
||||
latm_get_value(bs); // other_data_bits
|
||||
latm_get_value(bs); // other_data_bits
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int esc;
|
||||
do {
|
||||
esc = read_bits(bs, 1);
|
||||
esc = read_bits1(bs);
|
||||
skip_bits(bs, 8);
|
||||
} while (esc);
|
||||
}
|
||||
}
|
||||
|
||||
if(read_bits(bs, 1)) // crc present?
|
||||
skip_bits(bs, 8); // config_crc
|
||||
if(read_bits1(bs)) // crc present?
|
||||
skip_bits(bs, 8); // config_crc
|
||||
latm->configured = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +226,8 @@ parse_latm_audio_mux_element(service_t *t, elementary_stream_t *st,
|
|||
latm = st->es_priv = calloc(1, sizeof(latm_private_t));
|
||||
|
||||
if(!read_bits1(&bs))
|
||||
read_stream_mux_config(st, latm, &bs);
|
||||
if (read_stream_mux_config(st, latm, &bs) < 0)
|
||||
return NULL;
|
||||
|
||||
if(!latm->configured)
|
||||
return NULL;
|
||||
|
@ -202,8 +241,7 @@ parse_latm_audio_mux_element(service_t *t, elementary_stream_t *st,
|
|||
slot_len += tmp;
|
||||
} while (tmp == 255);
|
||||
|
||||
|
||||
if(slot_len * 8 > remaining_bits(&bs))
|
||||
if (slot_len * 8 > remaining_bits(&bs))
|
||||
return NULL;
|
||||
|
||||
if(st->es_curdts == PTS_UNSET)
|
||||
|
@ -214,16 +252,16 @@ parse_latm_audio_mux_element(service_t *t, elementary_stream_t *st,
|
|||
pkt->pkt_commercial = t->s_tt_commercial_advice;
|
||||
pkt->pkt_duration = st->es_frame_duration;
|
||||
pkt->pkt_sri = latm->sample_rate_index;
|
||||
pkt->pkt_channels = latm->channel_config;
|
||||
pkt->pkt_channels = latm->channel_config == 7 ? 8 : latm->channel_config;
|
||||
|
||||
/* 7 bytes of ADTS header */
|
||||
init_wbits(&out, pktbuf_ptr(pkt->pkt_payload), 56);
|
||||
init_wbits(&out, pktbuf_ptr(pkt->pkt_payload), 7 * 8);
|
||||
|
||||
put_bits(&out, 0xfff, 12); // Sync marker
|
||||
put_bits(&out, 1, 1); // ID 0 = MPEG 4, 1 = MPEG 2
|
||||
put_bits(&out, 0, 1); // ID 0 = MPEG 4, 1 = MPEG 2
|
||||
put_bits(&out, 0, 2); // Layer
|
||||
put_bits(&out, 1, 1); // Protection absent
|
||||
put_bits(&out, 2, 2); // AOT, 2 = AAC LC (for MPEG 2 bit)
|
||||
put_bits(&out, adts_aot(latm->aot), 2);
|
||||
put_bits(&out, latm->sample_rate_index, 4);
|
||||
put_bits(&out, 1, 1); // Private bit
|
||||
put_bits(&out, latm->channel_config, 3);
|
||||
|
@ -233,7 +271,7 @@ parse_latm_audio_mux_element(service_t *t, elementary_stream_t *st,
|
|||
put_bits(&out, 1, 1); // Copyright identification bit
|
||||
put_bits(&out, 1, 1); // Copyright identification start
|
||||
put_bits(&out, slot_len + 7, 13);
|
||||
put_bits(&out, 0, 11); // Buffer fullness
|
||||
put_bits(&out, 0x7ff, 11); // Buffer fullness
|
||||
put_bits(&out, 0, 2); // RDB in frame
|
||||
|
||||
assert(remaining_bits(&out) == 0);
|
||||
|
|
|
@ -227,35 +227,48 @@ static void
|
|||
parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data,
|
||||
int len, int start)
|
||||
{
|
||||
int l, muxlen, p;
|
||||
int l, muxlen, p, hdr = 0;
|
||||
th_pkt_t *pkt;
|
||||
int64_t olddts = PTS_UNSET, oldpts = PTS_UNSET;
|
||||
int64_t newdts = PTS_UNSET, newpts = PTS_UNSET;
|
||||
|
||||
if(start) {
|
||||
/* Payload unit start */
|
||||
st->es_parser_state = 1;
|
||||
st->es_parser_ptr = 0;
|
||||
sbuf_reset(&st->es_buf, 4000);
|
||||
if(st->es_parser_state == 0) {
|
||||
if (start) {
|
||||
/* Payload unit start */
|
||||
st->es_parser_state = 1;
|
||||
st->es_parser_ptr = 0;
|
||||
sbuf_reset(&st->es_buf, 4000);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(st->es_parser_state == 0)
|
||||
return;
|
||||
if(start) {
|
||||
int hlen;
|
||||
|
||||
if(len < 9)
|
||||
return;
|
||||
|
||||
olddts = st->es_curdts;
|
||||
oldpts = st->es_curpts;
|
||||
hlen = parse_pes_header(t, st, data + 6, len - 6);
|
||||
if (hlen >= 0 && st->es_buf.sb_ptr) {
|
||||
newdts = st->es_curdts;
|
||||
newpts = st->es_curpts;
|
||||
st->es_curdts = olddts;
|
||||
st->es_curpts = oldpts;
|
||||
}
|
||||
|
||||
if(hlen < 0)
|
||||
return;
|
||||
|
||||
data += 6 + hlen;
|
||||
len -= 6 + hlen;
|
||||
}
|
||||
|
||||
sbuf_append(&st->es_buf, data, len);
|
||||
|
||||
if(st->es_parser_ptr == 0) {
|
||||
int hlen;
|
||||
|
||||
if(st->es_buf.sb_ptr < 9)
|
||||
return;
|
||||
|
||||
hlen = parse_pes_header(t, st,
|
||||
st->es_buf.sb_data + 6, st->es_buf.sb_ptr - 6);
|
||||
if(hlen < 0)
|
||||
return;
|
||||
st->es_parser_ptr += 6 + hlen;
|
||||
}
|
||||
|
||||
p = st->es_parser_ptr;
|
||||
p = 0;
|
||||
|
||||
while((l = st->es_buf.sb_ptr - p) > 3) {
|
||||
const uint8_t *d = st->es_buf.sb_data + p;
|
||||
|
@ -274,12 +287,17 @@ parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data,
|
|||
st->es_buf.sb_err = 0;
|
||||
}
|
||||
|
||||
if (hdr && newdts != PTS_UNSET) {
|
||||
st->es_curdts = newdts;
|
||||
st->es_curpts = newpts;
|
||||
}
|
||||
p += muxlen + 3;
|
||||
/* ADTS */
|
||||
} else if(d[0] == 0xff && (d[1] & 0xf0) == 0xf0) {
|
||||
} else if(p == 0 && d[0] == 0xff && (d[1] & 0xf0) == 0xf0) {
|
||||
|
||||
if (l < 7)
|
||||
break;
|
||||
|
||||
muxlen = ((uint16_t)(d[3] & 0x03) << 11) | ((uint16_t)d[4] << 3) | (d[5] >> 5);
|
||||
if (l < muxlen)
|
||||
break;
|
||||
|
@ -288,6 +306,10 @@ parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data,
|
|||
sbuf_append(&st->es_buf_a, d, muxlen);
|
||||
parse_mp4a_data(t, st, 1);
|
||||
|
||||
if (hdr && newdts != PTS_UNSET) {
|
||||
st->es_curdts = newdts;
|
||||
st->es_curpts = newpts;
|
||||
}
|
||||
p += muxlen;
|
||||
|
||||
/* Wrong bytestream */
|
||||
|
@ -296,7 +318,12 @@ parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data,
|
|||
}
|
||||
}
|
||||
|
||||
st->es_parser_ptr = p;
|
||||
if (hdr && newdts != PTS_UNSET) {
|
||||
st->es_curdts = newdts;
|
||||
st->es_curpts = newpts;
|
||||
}
|
||||
if (p > 0)
|
||||
sbuf_cut(&st->es_buf, p);
|
||||
}
|
||||
|
||||
|
||||
|
@ -549,7 +576,6 @@ static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen,
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
const static int mpa_br[16] = {
|
||||
0, 32, 48, 56,
|
||||
64, 80, 96, 112,
|
||||
|
@ -850,7 +876,6 @@ parse_pes_header(service_t *t, elementary_stream_t *st,
|
|||
pts = getpts(buf);
|
||||
dts = getpts(buf + 5);
|
||||
|
||||
|
||||
d = (pts - dts) & PTS_MASK;
|
||||
if(d > 180000) // More than two seconds of PTS/DTS delta, probably corrupt
|
||||
pts = dts = PTS_UNSET;
|
||||
|
|
|
@ -108,7 +108,7 @@ apply_header(streaming_start_component_t *ssc, th_pkt_t *pkt)
|
|||
ssc->ssc_gh = pktbuf_alloc(NULL, 2);
|
||||
uint8_t *d = pktbuf_ptr(ssc->ssc_gh);
|
||||
|
||||
const int profile = 2;
|
||||
const int profile = 2; /* AAC LC */
|
||||
d[0] = (profile << 3) | ((pkt->pkt_sri & 0xe) >> 1);
|
||||
d[1] = ((pkt->pkt_sri & 0x1) << 7) | (pkt->pkt_channels << 3);
|
||||
}
|
||||
|
|
|
@ -331,10 +331,10 @@ create_adts_header(pktbuf_t *pb, int sri, int channels)
|
|||
init_wbits(&bs, pktbuf_ptr(pb), 56);
|
||||
|
||||
put_bits(&bs, 0xfff, 12); // Sync marker
|
||||
put_bits(&bs, 1, 1); // ID 0 = MPEG 4, 1 = MPEG 2
|
||||
put_bits(&bs, 0, 1); // ID 0 = MPEG 4, 1 = MPEG 2
|
||||
put_bits(&bs, 0, 2); // Layer
|
||||
put_bits(&bs, 1, 1); // Protection absent
|
||||
put_bits(&bs, 2, 2); // AOT, 2 = AAC LC (for MPEG 2 bit)
|
||||
put_bits(&bs, 1, 2); // AOT, 1 = AAC LC
|
||||
put_bits(&bs, sri, 4);
|
||||
put_bits(&bs, 1, 1); // Private bit
|
||||
put_bits(&bs, channels, 3);
|
||||
|
@ -344,7 +344,7 @@ create_adts_header(pktbuf_t *pb, int sri, int channels)
|
|||
put_bits(&bs, 1, 1); // Copyright identification bit
|
||||
put_bits(&bs, 1, 1); // Copyright identification start
|
||||
put_bits(&bs, pktbuf_len(pb), 13);
|
||||
put_bits(&bs, 0, 11); // Buffer fullness
|
||||
put_bits(&bs, 0x7ff, 11); // Buffer fullness
|
||||
put_bits(&bs, 0, 2); // RDB in frame
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue