static int64_t
-getpts(uint8_t *p)
+getpts(const uint8_t *p)
{
int a = p[0];
int b = (p[1] << 8) | p[2];
static int parse_h264(th_transport_t *t, th_stream_t *st, size_t len,
uint32_t next_startcode, int sc_offset);
-typedef int (vparser_t)(th_transport_t *t, th_stream_t *st, size_t len,
- uint32_t next_startcode, int sc_offset);
+typedef int (packet_parser_t)(th_transport_t *t, th_stream_t *st, size_t len,
+ uint32_t next_startcode, int sc_offset);
typedef void (aparser_t)(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
-static void parse_video(th_transport_t *t, th_stream_t *st, const uint8_t *data,
- int len, vparser_t *vp);
+static void parse_sc(th_transport_t *t, th_stream_t *st, const uint8_t *data,
+ int len, packet_parser_t *vp);
-static void parse_audio_with_lavc(th_transport_t *t, th_stream_t *st,
- const uint8_t *data, int len, aparser_t *ap);
-
-static void parse_audio(th_transport_t *t, th_stream_t *st, const uint8_t *data,
- int len, int start, aparser_t *vp);
static void parse_aac(th_transport_t *t, th_stream_t *st, const uint8_t *data,
int len, int start);
static void parse_subtitles(th_transport_t *t, th_stream_t *st,
const uint8_t *data, int len, int start);
-static void parse_mpegaudio(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
-
-static void parse_ac3(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
+static int parse_mpa(th_transport_t *t, th_stream_t *st, size_t len,
+ uint32_t next_startcode, int sc_offset);
+static int parse_ac3(th_transport_t *t, th_stream_t *st, size_t len,
+ uint32_t next_startcode, int sc_offset);
static void parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt);
-static int parse_pes_header(th_transport_t *t, th_stream_t *st, uint8_t *buf,
- size_t len);
+static int parse_pes_header(th_transport_t *t, th_stream_t *st,
+ const uint8_t *buf, size_t len);
/**
* Parse raw mpeg data
switch(st->st_type) {
case SCT_MPEG2VIDEO:
- parse_video(t, st, data, len, parse_mpeg2video);
+ parse_sc(t, st, data, len, parse_mpeg2video);
break;
case SCT_H264:
- parse_video(t, st, data, len, parse_h264);
+ parse_sc(t, st, data, len, parse_h264);
break;
case SCT_MPEG2AUDIO:
- parse_audio(t, st, data, len, start, parse_mpegaudio);
+ parse_sc(t, st, data, len, parse_mpa);
break;
case SCT_AC3:
- parse_audio(t, st, data, len, start, parse_ac3);
+ parse_sc(t, st, data, len, parse_ac3);
break;
case SCT_DVBSUB:
switch(st->st_type) {
case SCT_MPEG2AUDIO:
- parse_audio_with_lavc(t, st, data, len, parse_mpegaudio);
+ parse_sc(t, st, data, len, parse_mpa);
break;
case SCT_MPEG2VIDEO:
- parse_video(t, st, data, len, parse_mpeg2video);
+ parse_sc(t, st, data, len, parse_mpeg2video);
break;
default:
}
+/**
+ *
+ */
+static void
+buffer_alloc(th_stream_t *st, int len)
+{
+ if(st->st_buffer == NULL) {
+ st->st_buffer_size = 4000;
+ st->st_buffer = malloc(st->st_buffer_size);
+ }
+
+ if(st->st_buffer_ptr + len >= st->st_buffer_size) {
+ st->st_buffer_size += len * 4;
+ st->st_buffer = realloc(st->st_buffer, st->st_buffer_size);
+ }
+}
+
+
+/**
+ *
+ */
+static void
+buffer_append(th_stream_t *st, const uint8_t *data, int len)
+{
+ buffer_alloc(st, len);
+ memcpy(st->st_buffer + st->st_buffer_ptr, data, len);
+ st->st_buffer_ptr += len;
+}
+
+
+/**
+ *
+ */
+static void
+buffer3_alloc(th_stream_t *st, int len)
+{
+ if(st->st_buffer3 == NULL) {
+ st->st_buffer3_size = 4000;
+ st->st_buffer3 = malloc(st->st_buffer3_size);
+ }
+
+ if(st->st_buffer3_ptr + len >= st->st_buffer3_size) {
+ st->st_buffer3_size += len * 4;
+ st->st_buffer3 = realloc(st->st_buffer3, st->st_buffer3_size);
+ }
+}
+
+
+/**
+ *
+ */
+static void
+buffer3_append(th_stream_t *st, const uint8_t *data, int len)
+{
+ buffer3_alloc(st, len);
+ memcpy(st->st_buffer3 + st->st_buffer3_ptr, data, len);
+ st->st_buffer3_ptr += len;
+}
+
+
+/**
+ *
+ */
+static void
+buffer3_cut(th_stream_t *st, int p)
+{
+ assert(p <= st->st_buffer3_ptr);
+ st->st_buffer3_ptr = st->st_buffer3_ptr - p;
+ memmove(st->st_buffer3, st->st_buffer3 + p, st->st_buffer3_ptr);
+}
+
+
+
/**
* Parse AAC LATM
*/
* derive further information.
*/
static void
-parse_video(th_transport_t *t, th_stream_t *st, const uint8_t *data, int len,
- vparser_t *vp)
+parse_sc(th_transport_t *t, th_stream_t *st, const uint8_t *data, int len,
+ packet_parser_t *vp)
{
- uint32_t sc;
+ uint32_t sc = st->st_startcond;
int i, r;
- sc = st->st_startcond;
-
- if(st->st_buffer == NULL) {
- st->st_buffer_size = 4000;
- st->st_buffer = malloc(st->st_buffer_size);
- }
-
- if(st->st_buffer_ptr + len + 4 >= st->st_buffer_size) {
- st->st_buffer_size += len * 4;
- st->st_buffer = realloc(st->st_buffer, st->st_buffer_size);
- }
+ buffer_alloc(st, len);
for(i = 0; i < len; i++) {
st->st_buffer[st->st_buffer_ptr++] = data[i];
if(r > 0 && st->st_startcode != 0) {
r = vp(t, st, r, sc, st->st_startcode_offset);
+ if(r == 3)
+ continue;
} else {
r = 1;
}
}
}
st->st_startcond = sc;
-
-
}
-
/**
- * Generic audio parser
- *
- * We trust 'start' to be set where a new frame starts, thats where we
- * can expect to find the system start code.
*
- * We then trust ffmpeg to parse and extract packets for use
*/
-static void
-parse_audio(th_transport_t *t, th_stream_t *st, const uint8_t *data,
- int len, int start, aparser_t *ap)
+static int
+depacketize(th_transport_t *t, th_stream_t *st, size_t len,
+ uint32_t next_startcode, int sc_offset)
{
- int hlen;
+ const uint8_t *buf = st->st_buffer + sc_offset;
+ uint32_t sc = st->st_startcode;
+ int hlen, plen;
- if(start) {
- /* Payload unit start */
- st->st_parser_state = 1;
- st->st_buffer_ptr = 0;
- }
+ if((sc != 0x1bd && sc != 0x1c0) || len < 9)
+ return 1;
- if(st->st_parser_state == 0)
- return;
+ plen = (buf[4] << 8) | buf[5];
- if(st->st_parser_state == 1) {
+ if(plen + 6 > len || next_startcode != sc)
+ return 3;
- if(st->st_buffer == NULL)
- st->st_buffer = malloc(1000);
+ if(plen + 6 < len)
+ return 1;
- if(st->st_buffer_ptr + len >= 1000)
- return;
+ buf += 6;
+ len -= 6;
- memcpy(st->st_buffer + st->st_buffer_ptr, data, len);
- st->st_buffer_ptr += len;
+ hlen = parse_pes_header(t, st, buf, len);
- if(st->st_buffer_ptr < 9)
- return;
+ if(hlen < 0)
+ return 1;
- if((hlen = parse_pes_header(t, st, st->st_buffer + 6,
- st->st_buffer_ptr - 6)) < 0)
- return;
+ buf += hlen;
+ len -= hlen;
- data = st->st_buffer + hlen + 6;
- len = st->st_buffer_ptr - hlen - 6;
+ buffer3_append(st, buf, len);
+ return 0;
+}
- st->st_parser_state = 2;
- assert(len >= 0);
- if(len == 0)
- return;
- }
- parse_audio_with_lavc(t, st, data, len, ap);
-}
/**
- * Use libavcodec's parsers for audio parsing
+ *
*/
-static void
-parse_audio_with_lavc(th_transport_t *t, th_stream_t *st, const uint8_t *data,
- int len, aparser_t *ap)
+static void
+makeapkt(th_transport_t *t, th_stream_t *st, const void *buf,
+ int len, int64_t dts, int duration)
{
- uint8_t *outbuf;
- int outlen, rlen;
- th_pkt_t *pkt;
- int64_t dts;
- while(len > 0) {
+ th_pkt_t *pkt = pkt_alloc(buf, len, dts, dts);
+
+ pkt->pkt_commercial = t->tht_tt_commercial_advice;
+ pkt->pkt_duration = duration;
+
+ parser_deliver(t, st, pkt);
+
+ st->st_curdts = AV_NOPTS_VALUE;
+ st->st_nextdts = dts + duration;
+}
+
- rlen = av_parser_parse(st->st_parser, st->st_ctx,
- &outbuf, &outlen, data, len,
- st->st_curpts, st->st_curdts);
- st->st_curdts = AV_NOPTS_VALUE;
- st->st_curpts = AV_NOPTS_VALUE;
-
- if(outlen) {
- dts = st->st_parser->dts;
- if(dts == AV_NOPTS_VALUE)
- dts = st->st_nextdts;
+const static int mpa_br[16] = {
+ 0, 32, 48, 56,
+ 64, 80, 96, 112,
+ 128, 160, 192, 224,
+ 256, 320, 384, 0
+};
- pkt = pkt_alloc(outbuf, outlen, dts, dts);
- pkt->pkt_commercial = t->tht_tt_commercial_advice;
+const static int mpa_sr[4] = {44100, 48000, 32000, 0};
- ap(t, st, pkt);
- }
- data += rlen;
- len -= rlen;
- }
+static int
+mpa_valid_frame(const uint8_t *buf)
+{
+ return buf[0] == 0xff && (buf[1] & 0xfe) == 0xfc;
}
-/**
- * Mpeg audio parser
- */
-const static int mpegaudio_freq_tab[4] = {44100, 48000, 32000, 0};
-
-static void
-parse_mpegaudio(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
+static int
+parse_mpa(th_transport_t *t, th_stream_t *st, size_t ilen,
+ uint32_t next_startcode, int sc_offset)
{
- uint8_t *buf = pkt->pkt_payload;
- uint32_t header;
- int sample_rate, duration;
+ int i, len;
+ const uint8_t *buf;
- header = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);
+ if((i = depacketize(t, st, ilen, next_startcode, sc_offset)) != 0)
+ return i;
- sample_rate = mpegaudio_freq_tab[(header >> 10) & 3];
- if(sample_rate == 0) {
- pkt_ref_dec(pkt);
- return;
- }
-
- duration = 90000 * 1152 / sample_rate;
- pkt->pkt_duration = duration;
- st->st_nextdts = pkt->pkt_dts + duration;
+ again:
+ buf = st->st_buffer3;
+ len = st->st_buffer3_ptr;
- parser_deliver(t, st, pkt);
+ for(i = 0; i < len - 4; i++) {
+ if(mpa_valid_frame(buf + i)) {
+ int br = mpa_br[ buf[i+2] >> 4 ];
+ int sr = mpa_sr[(buf[i+2] >> 2) & 3];
+ int pad = buf[i+2] & 1;
+
+ if(br && sr) {
+ int fsize = 144000 * br / sr + pad;
+ int duration = 90000 * 1152 / sr;
+ int64_t dts = st->st_curdts;
+
+ if(dts == AV_NOPTS_VALUE)
+ dts = st->st_nextdts;
+
+ if(dts != AV_NOPTS_VALUE &&
+ len >= i + fsize + 4 &&
+ mpa_valid_frame(buf + i + fsize)) {
+
+ makeapkt(t, st, buf + i, fsize, dts, duration);
+ buffer3_cut(st, i + fsize);
+ goto again;
+ }
+ }
+ }
+ }
+ return 1;
}
-
/**
* AC3 audio parser
*/
{ 1280, 1394, 1920 },
};
-static void
-parse_ac3(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt)
+/**
+ * Inspect 6 bytes AC3 header
+ */
+static int
+ac3_valid_frame(const uint8_t *buf)
{
- uint8_t *buf = pkt->pkt_payload;
- uint32_t src, fsc;
- int sample_rate, frame_size, duration, bsid;
-
- src = buf[4] >> 6;
- fsc = buf[4] & 0x3f;
- bsid = (buf[5] & 0xf) - 8;
- if(bsid < 0)
- bsid = 0;
-
- sample_rate = ac3_freq_tab[src] >> bsid;
- if(sample_rate == 0) {
- pkt_ref_dec(pkt);
- return;
+ if(buf[0] != 0x0b || buf[1] != 0x77)
+ return 0;
+
+ int bsid = buf[5] & 0xf;
+
+ if(bsid <= 10) {
+ // Normal AC3
+ return (buf[4] & 0xc0) != 0xc0 && (buf[4] & 0x3f) < 0x26;
+ } else {
+ // E-AC3
+ return (buf[2] & 0xc0) != 0xc0;
}
+}
- frame_size = ac3_frame_size_tab[fsc][src] * 2;
- duration = 90000 * 1536 / sample_rate;
+static int
+parse_ac3(th_transport_t *t, th_stream_t *st, size_t ilen,
+ uint32_t next_startcode, int sc_offset)
+{
+ int i, len;
+ const uint8_t *buf;
- pkt->pkt_duration = duration;
- st->st_nextdts = pkt->pkt_dts + duration;
- parser_deliver(t, st, pkt);
+ if((i = depacketize(t, st, ilen, next_startcode, sc_offset)) != 0)
+ return i;
+
+ again:
+ buf = st->st_buffer3;
+ len = st->st_buffer3_ptr;
+
+ for(i = 0; i < len - 6; i++) {
+ if(ac3_valid_frame(buf + i)) {
+ int bsid = buf[5] & 0xf;
+ int fsize, sr;
+
+ if(bsid <= 10) {
+ int fscod = buf[4] >> 6;
+ int frmsizcod = buf[4] & 0x3f;
+
+ fsize = ac3_frame_size_tab[frmsizcod][fscod] * 2;
+
+ bsid -= 8;
+ if(bsid < 0)
+ bsid = 0;
+ sr = ac3_freq_tab[fscod] >> bsid;
+
+ } else {
+
+ fsize = ((((buf[2] & 0x7) << 8) | buf[3]) + 1) * 2;
+
+
+ if((buf[4] & 0xc0) == 0xc0) {
+ sr = ac3_freq_tab[(buf[4] >> 4) & 3] / 2;
+ } else {
+ sr = ac3_freq_tab[(buf[4] >> 6) & 3];
+ }
+ }
+
+ if(sr) {
+ int duration = 90000 * 1536 / sr;
+ int64_t dts = st->st_curdts;
+
+ if(dts == AV_NOPTS_VALUE)
+ dts = st->st_nextdts;
+
+ if(dts != AV_NOPTS_VALUE &&
+ len >= i + fsize + 6 &&
+ ac3_valid_frame(buf + i + fsize)) {
+ makeapkt(t, st, buf + i, fsize, dts, duration);
+ buffer3_cut(st, i + fsize);
+ goto again;
+ }
+ }
+ }
+ }
+ return 1;
}
* Extract DTS and PTS and update current values in stream
*/
static int
-parse_pes_header(th_transport_t *t, th_stream_t *st, uint8_t *buf, size_t len)
+parse_pes_header(th_transport_t *t, th_stream_t *st,
+ const uint8_t *buf, size_t len)
{
int64_t dts, pts, d;
int hdr, flags, hlen;
if(st->st_parser_state == 0)
return;
- if(st->st_buffer == NULL) {
- st->st_buffer_size = 4000;
- st->st_buffer = malloc(st->st_buffer_size);
- }
-
- if(st->st_buffer_ptr + len >= st->st_buffer_size) {
- st->st_buffer_size += len * 4;
- st->st_buffer = realloc(st->st_buffer, st->st_buffer_size);
- }
-
- memcpy(st->st_buffer + st->st_buffer_ptr, data, len);
- st->st_buffer_ptr += len;
+ buffer_append(st, data, len);
if(st->st_buffer_ptr < 6)
return;