parsers: improve AAC/LATM parser

This commit is contained in:
Jaroslav Kysela 2015-02-08 18:19:25 +01:00
parent cd1f081909
commit cc1fbb0202
6 changed files with 169 additions and 107 deletions

View file

@ -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)
{

View file

@ -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_ */

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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
}