From cc1fbb02028c4e5baaf72deaa01b34748686e273 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 8 Feb 2015 18:19:25 +0100 Subject: [PATCH] parsers: improve AAC/LATM parser --- src/parsers/bitstream.c | 39 ++++------ src/parsers/bitstream.h | 14 +++- src/parsers/parser_latm.c | 138 ++++++++++++++++++++++------------- src/parsers/parsers.c | 77 ++++++++++++------- src/plumbing/globalheaders.c | 2 +- src/plumbing/transcoding.c | 6 +- 6 files changed, 169 insertions(+), 107 deletions(-) diff --git a/src/parsers/bitstream.c b/src/parsers/bitstream.c index 73f7e189..ffb7eef8 100644 --- a/src/parsers/bitstream.c +++ b/src/parsers/bitstream.c @@ -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) { diff --git a/src/parsers/bitstream.h b/src/parsers/bitstream.h index 8718e80b..f43f036f 100644 --- a/src/parsers/bitstream.h +++ b/src/parsers/bitstream.h @@ -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_ */ diff --git a/src/parsers/parser_latm.c b/src/parsers/parser_latm.c index 0dc2105b..b54170ff 100644 --- a/src/parsers/parser_latm.c +++ b/src/parsers/parser_latm.c @@ -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); diff --git a/src/parsers/parsers.c b/src/parsers/parsers.c index a1d630c5..bb1b1af8 100644 --- a/src/parsers/parsers.c +++ b/src/parsers/parsers.c @@ -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; diff --git a/src/plumbing/globalheaders.c b/src/plumbing/globalheaders.c index b18b9c7d..f50132e9 100644 --- a/src/plumbing/globalheaders.c +++ b/src/plumbing/globalheaders.c @@ -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); } diff --git a/src/plumbing/transcoding.c b/src/plumbing/transcoding.c index 81da7501..49b90e5e 100644 --- a/src/plumbing/transcoding.c +++ b/src/plumbing/transcoding.c @@ -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 }