From 73dd4c2cc74b669634627fcac539a5dc0c8006be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Thu, 15 Sep 2011 15:12:54 +0200 Subject: [PATCH 1/6] Initial support for parsing AAC streams inside mp4a container --- src/parsers.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++--- src/psi.c | 12 +++++-- src/service.c | 7 ++++ src/tvheadend.h | 3 +- 4 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/parsers.c b/src/parsers.c index 9b7337dc..d6538aeb 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -113,6 +113,9 @@ static int parse_ac3(service_t *t, elementary_stream_t *st, size_t len, static int parse_eac3(service_t *t, elementary_stream_t *st, size_t len, uint32_t next_startcode, int sc_offset); +static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, + uint32_t next_startcode, int sc_offset); + static void parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt); static int parse_pes_header(service_t *t, elementary_stream_t *st, @@ -150,6 +153,10 @@ parse_mpeg_ts(service_t *t, elementary_stream_t *st, const uint8_t *data, parse_sc(t, st, data, len, parse_eac3); break; + case SCT_MP4A: + parse_sc(t, st, data, len, parse_mp4a); + break; + case SCT_DVBSUB: parse_subtitles(t, st, data, len, start); break; @@ -263,9 +270,6 @@ parse_aac(service_t *t, elementary_stream_t *st, const uint8_t *data, } - - - /** * Generic video parser * @@ -429,7 +433,80 @@ makeapkt(service_t *t, elementary_stream_t *st, const void *buf, st->es_curdts = PTS_UNSET; st->es_nextdts = dts + duration; } - + +/** + * Parse AAC MP4A + */ + +static const int aac_sample_rates[12] = +{ + 96000, + 88200, + 64000, + 48000, + 44100, + 32000, + 24000, + 22050, + 16000, + 12000, + 11025, + 8000 +}; + +/** + * Inspect ADTS header + */ +static int +mp4a_valid_frame(const uint8_t *buf) +{ + return (buf[0] == 0xff) && ((buf[1] & 0xf6) == 0xf0); +} + +static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, + uint32_t next_startcode, int sc_offset) +{ + int i, len; + const uint8_t *buf; + + if((i = depacketize(t, st, ilen, next_startcode, sc_offset)) != 0) + return i; + + again: + buf = st->es_buf_a.sb_data; + len = st->es_buf_a.sb_ptr; + + for(i = 0; i < len - 6; i++) { + const uint8_t *p = buf + i; + if(mp4a_valid_frame(p)) { + + int fsize = ((p[3] & 0x03) << 11) | (p[4] << 3) | ((p[5] & 0xe0) >> 5); + int sr_index = (p[2] & 0x3c) >> 2; + int sr = aac_sample_rates[sr_index]; + + + if(sr) { + int duration = 90000 * 1536 / sr; + int64_t dts = st->es_curdts; + int sri = rate_to_sri(sr); + + if(dts == PTS_UNSET) + dts = st->es_nextdts; + + if(dts != PTS_UNSET && len >= i + fsize + 6 && + mp4a_valid_frame(p + fsize)) { + + int channels = ((p[2] & 0x01) << 2) | ((p[3] & 0xc0) >> 6); + + makeapkt(t, st, p, fsize, dts, duration, channels, sri); + sbuf_cut(&st->es_buf_a, i + fsize); + goto again; + } + } + } + } + return 1; +} const static int mpa_br[16] = { diff --git a/src/psi.c b/src/psi.c index 5c792a58..51831d06 100644 --- a/src/psi.c +++ b/src/psi.c @@ -519,6 +519,10 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, case 0x81: hts_stream_type = SCT_AC3; break; + + case 0x0f: + hts_stream_type = SCT_MP4A; + break; case 0x11: hts_stream_type = SCT_AAC; @@ -568,7 +572,9 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, break; case DVB_DESC_AAC: - if(estype == 0x11) + if(estype == 0x0f) + hts_stream_type = SCT_MP4A; + else if(estype == 0x11) hts_stream_type = SCT_AAC; break; @@ -592,7 +598,6 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, } len -= dlen; ptr += dlen; dllen -= dlen; } - if(hts_stream_type == SCT_UNKNOWN && estype == 0x06 && pid == 3401 && t->s_dvb_service_id == 10510) { @@ -736,6 +741,7 @@ psi_build_pmt(streaming_start_t *ss, uint8_t *buf0, int maxlen, int pcrpid) c = 0x06; break; + case SCT_MP4A: case SCT_AAC: c = 0x11; break; @@ -764,6 +770,7 @@ psi_build_pmt(streaming_start_t *ss, uint8_t *buf0, int maxlen, int pcrpid) switch(ssc->ssc_type) { case SCT_MPEG2AUDIO: + case SCT_MP4A: case SCT_AAC: buf[0] = DVB_DESC_LANGUAGE; buf[1] = 4; @@ -886,6 +893,7 @@ static struct strtab streamtypetab[] = { { "MPEGTS", SCT_MPEGTS }, { "TEXTSUB", SCT_TEXTSUB }, { "EAC3", SCT_EAC3 }, + { "MP4A", SCT_MP4A }, }; diff --git a/src/service.c b/src/service.c index b7eec881..6cba2da7 100644 --- a/src/service.c +++ b/src/service.c @@ -605,6 +605,7 @@ service_stream_create(service_t *t, int pid, st = calloc(1, sizeof(elementary_stream_t)); st->es_index = idx + 1; + st->es_type = type; TAILQ_INSERT_TAIL(&t->s_components, st, es_link); @@ -854,6 +855,12 @@ service_build_stream_start(service_t *t) streaming_start_component_t *ssc = &ss->ss_components[n++]; ssc->ssc_index = st->es_index; ssc->ssc_type = st->es_type; + + // Jernej says: I don't know how else?! + if (ssc->ssc_type == SCT_MP4A) { + ssc->ssc_type = SCT_AAC; + } + memcpy(ssc->ssc_lang, st->es_lang, 4); ssc->ssc_composition_id = st->es_composition_id; ssc->ssc_ancillary_id = st->es_ancillary_id; diff --git a/src/tvheadend.h b/src/tvheadend.h index 4dd4e2b9..a73b6661 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -163,11 +163,12 @@ typedef enum { SCT_MPEGTS, SCT_TEXTSUB, SCT_EAC3, + SCT_MP4A, } streaming_component_type_t; #define SCT_ISVIDEO(t) ((t) == SCT_MPEG2VIDEO || (t) == SCT_H264) #define SCT_ISAUDIO(t) ((t) == SCT_MPEG2AUDIO || (t) == SCT_AC3 || \ - (t) == SCT_AAC) + (t) == SCT_AAC || (t) == SCT_MP4A) /** * The signal status of a tuner From 29f68b588016e9635f5308b5d873a3226e95dc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Fri, 16 Sep 2011 10:16:30 +0200 Subject: [PATCH 2/6] Some small improvements --- src/psi.c | 7 ++++++- src/service.c | 5 ----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/psi.c b/src/psi.c index 51831d06..130678d5 100644 --- a/src/psi.c +++ b/src/psi.c @@ -612,6 +612,11 @@ psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid, st = service_stream_create(t, pid, hts_stream_type); } + // Jernej: I don't know why. But it seems that sometimes the stream is created with a wrong es_type?? + if(st->es_type != hts_stream_type) { + st->es_type = hts_stream_type; + } + st->es_delete_me = 0; if(st->es_position != position) { @@ -893,7 +898,7 @@ static struct strtab streamtypetab[] = { { "MPEGTS", SCT_MPEGTS }, { "TEXTSUB", SCT_TEXTSUB }, { "EAC3", SCT_EAC3 }, - { "MP4A", SCT_MP4A }, + { "AAC", SCT_MP4A }, }; diff --git a/src/service.c b/src/service.c index 6cba2da7..1a405d1a 100644 --- a/src/service.c +++ b/src/service.c @@ -856,11 +856,6 @@ service_build_stream_start(service_t *t) ssc->ssc_index = st->es_index; ssc->ssc_type = st->es_type; - // Jernej says: I don't know how else?! - if (ssc->ssc_type == SCT_MP4A) { - ssc->ssc_type = SCT_AAC; - } - memcpy(ssc->ssc_lang, st->es_lang, 4); ssc->ssc_composition_id = st->es_composition_id; ssc->ssc_ancillary_id = st->es_ancillary_id; From f24594a8f76cb310724db911cc1de83369a3ec91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Fri, 16 Sep 2011 11:25:10 +0200 Subject: [PATCH 3/6] Correct frame duration --- src/parsers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers.c b/src/parsers.c index d6538aeb..c9a16775 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -486,7 +486,7 @@ static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, if(sr) { - int duration = 90000 * 1536 / sr; + int duration = 90000 * 1024 / sr; int64_t dts = st->es_curdts; int sri = rate_to_sri(sr); From 2790f61836d388613e60c2c236372dfe6b781e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Fri, 16 Sep 2011 13:58:20 +0200 Subject: [PATCH 4/6] Support sound recording of MP4A streams --- src/dvr/mkmux.c | 4 +++- src/plumbing/globalheaders.c | 2 ++ src/webui/extjs.c | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dvr/mkmux.c b/src/dvr/mkmux.c index d0f94bf4..ac305f77 100644 --- a/src/dvr/mkmux.c +++ b/src/dvr/mkmux.c @@ -215,6 +215,7 @@ mk_build_tracks(mk_mux_t *mkm, const struct streaming_start *ss) codec_id = "A_EAC3"; break; + case SCT_MP4A: case SCT_AAC: tracktype = 2; codec_id = "A_AAC"; @@ -252,6 +253,7 @@ mk_build_tracks(mk_mux_t *mkm, const struct streaming_start *ss) switch(ssc->ssc_type) { case SCT_H264: case SCT_MPEG2VIDEO: + case SCT_MP4A: case SCT_AAC: if(ssc->ssc_gh) ebml_append_bin(t, 0x63a2, @@ -723,7 +725,7 @@ mk_write_frame_i(mk_mux_t *mkm, mk_track *t, th_pkt_t *pkt) data = pktbuf_ptr(pkt->pkt_payload); len = pktbuf_len(pkt->pkt_payload); - if(t->type == SCT_AAC) { + if(t->type == SCT_AAC || t->type == SCT_MP4A) { // Skip ADTS header if(len < 7) return; diff --git a/src/plumbing/globalheaders.c b/src/plumbing/globalheaders.c index 703591bd..314839e4 100644 --- a/src/plumbing/globalheaders.c +++ b/src/plumbing/globalheaders.c @@ -78,6 +78,7 @@ apply_header(streaming_start_component_t *ssc, th_pkt_t *pkt) return; switch(ssc->ssc_type) { + case SCT_MP4A: case SCT_AAC: ssc->ssc_gh = pktbuf_alloc(NULL, 2); d = pktbuf_ptr(ssc->ssc_gh); @@ -121,6 +122,7 @@ header_complete(streaming_start_component_t *ssc, int not_so_picky) if(ssc->ssc_gh == NULL && (ssc->ssc_type == SCT_H264 || ssc->ssc_type == SCT_MPEG2VIDEO || + ssc->ssc_type == SCT_MP4A || ssc->ssc_type == SCT_AAC)) return 0; return 1; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index b959214f..3ed9f8b4 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1189,6 +1189,7 @@ extjs_servicedetails(http_connection_t *hc, break; case SCT_AC3: + case SCT_MP4A: case SCT_AAC: case SCT_MPEG2AUDIO: htsmsg_add_str(c, "details", st->es_lang); From 81e53b3156e2e8d115d89294ece3c701813c456b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 20 Sep 2011 15:03:04 +0800 Subject: [PATCH 5/6] Fix a crash caused by pull req #39 --- src/dvr/dvr_db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 5e5f4373..24805170 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -643,7 +643,7 @@ dvr_event_replaced(event_t *e, event_t *new_e) ude = dvr_entry_find_by_event_fuzzy(new_e); if (ude == NULL && de->de_sched_state == DVR_SCHEDULED) dvr_entry_cancel(de); - else + else if(new_e->e_title != NULL) dvr_entry_update(de, new_e->e_title, new_e->e_start, new_e->e_stop); } From a5a353ff518b36e1c9cce447623bcf32ff4c4e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jernej=20Fija=C4=8Dko?= Date: Wed, 12 Oct 2011 13:40:07 +0200 Subject: [PATCH 6/6] Ignore packet if fsize equals zero --- src/parsers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers.c b/src/parsers.c index c9a16775..53fb5316 100644 --- a/src/parsers.c +++ b/src/parsers.c @@ -485,7 +485,7 @@ static int parse_mp4a(service_t *t, elementary_stream_t *st, size_t ilen, int sr = aac_sample_rates[sr_index]; - if(sr) { + if(sr && fsize) { int duration = 90000 * 1024 / sr; int64_t dts = st->es_curdts; int sri = rate_to_sri(sr);