diff options
Diffstat (limited to 'libmpdemux')
-rw-r--r-- | libmpdemux/demux_ts.c | 3533 | ||||
-rw-r--r-- | libmpdemux/demux_ts.h | 24 | ||||
-rw-r--r-- | libmpdemux/demuxer.c | 15 | ||||
-rw-r--r-- | libmpdemux/stheader.h | 2 |
4 files changed, 3573 insertions, 1 deletions
diff --git a/libmpdemux/demux_ts.c b/libmpdemux/demux_ts.c new file mode 100644 index 0000000000..dbf12b7456 --- /dev/null +++ b/libmpdemux/demux_ts.c @@ -0,0 +1,3533 @@ +/* + * Demultiplexer for MPEG2 Transport Streams. + * + * Written by Nico <nsabbi@libero.it> + * Kind feedback is appreciated; 'sucks' and alike is not. + * Originally based on demux_pva.c written by Matteo Giani and FFmpeg (libavformat) sources + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "mp_msg.h" +#include "options.h" + +#include "libmpcodecs/dec_audio.h" +#include "stream/stream.h" +#include "demuxer.h" +#include "parse_es.h" +#include "stheader.h" +#include "ms_hdr.h" +#include "mpeg_hdr.h" +#include "demux_ts.h" + +#define TS_PH_PACKET_SIZE 192 +#define TS_FEC_PACKET_SIZE 204 +#define TS_PACKET_SIZE 188 +#define NB_PID_MAX 8192 + +#define MAX_HEADER_SIZE 6 /* enough for PES header + length */ +#define MAX_CHECK_SIZE 65535 +#define NUM_CONSECUTIVE_TS_PACKETS 32 +#define NUM_CONSECUTIVE_AUDIO_PACKETS 348 +#define MAX_A52_FRAME_SIZE 3840 + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif + +#define TYPE_AUDIO 1 +#define TYPE_VIDEO 2 +#define TYPE_SUB 3 + +int ts_prog; +int ts_keep_broken=0; +off_t ts_probe = 0; +int audio_substream_id = -1; + +typedef enum +{ + UNKNOWN = -1, + VIDEO_MPEG1 = 0x10000001, + VIDEO_MPEG2 = 0x10000002, + VIDEO_MPEG4 = 0x10000004, + VIDEO_H264 = 0x10000005, + VIDEO_AVC = mmioFOURCC('a', 'v', 'c', '1'), + VIDEO_DIRAC = mmioFOURCC('d', 'r', 'a', 'c'), + VIDEO_VC1 = mmioFOURCC('W', 'V', 'C', '1'), + AUDIO_MP2 = 0x50, + AUDIO_A52 = 0x2000, + AUDIO_DTS = 0x2001, + AUDIO_LPCM_BE = 0x10001, + AUDIO_AAC = mmioFOURCC('M', 'P', '4', 'A'), + AUDIO_AAC_LATM = mmioFOURCC('M', 'P', '4', 'L'), + AUDIO_TRUEHD = mmioFOURCC('T', 'R', 'H', 'D'), + AUDIO_S302M = mmioFOURCC('B', 'S', 'S', 'D'), + SPU_DVD = 0x3000000, + SPU_DVB = 0x3000001, + SPU_TELETEXT = 0x3000002, + SPU_PGS = 0x3000003, + PES_PRIVATE1 = 0xBD00000, + SL_PES_STREAM = 0xD000000, + SL_SECTION = 0xD100000, + MP4_OD = 0xD200000, +} es_stream_type_t; + +typedef struct { + uint8_t *buffer; + uint16_t buffer_len; +} ts_section_t; + +typedef struct { + int size; + unsigned char *start; + uint16_t payload_size; + es_stream_type_t type, subtype; + double pts, last_pts; + int pid; + char lang[4]; + int last_cc; // last cc code (-1 if first packet) + int is_synced; + ts_section_t section; + uint8_t *extradata; + int extradata_alloc, extradata_len; + struct { + uint8_t au_start, au_end, last_au_end; + } sl; +} ES_stream_t; + +typedef struct { + void *sh; + int id; + int type; +} sh_av_t; + +typedef struct MpegTSContext { + int packet_size; // raw packet size, including FEC if present e.g. 188 bytes + ES_stream_t *pids[NB_PID_MAX]; + sh_av_t streams[NB_PID_MAX]; +} MpegTSContext; + + +typedef struct { + demux_stream_t *ds; + demux_packet_t *pack; + int offset, buffer_size; +} av_fifo_t; + +#define MAX_EXTRADATA_SIZE 64*1024 +typedef struct { + int32_t object_type; //aka codec used + int32_t stream_type; //video, audio etc. + uint8_t buf[MAX_EXTRADATA_SIZE]; + uint16_t buf_size; + uint8_t szm1; +} mp4_decoder_config_t; + +typedef struct { + //flags + uint8_t flags; + uint8_t au_start; + uint8_t au_end; + uint8_t random_accesspoint; + uint8_t random_accesspoint_only; + uint8_t padding; + uint8_t use_ts; + uint8_t idle; + uint8_t duration; + + uint32_t ts_resolution, ocr_resolution; + uint8_t ts_len, ocr_len, au_len, instant_bitrate_len, degr_len, au_seqnum_len, packet_seqnum_len; + uint32_t timescale; + uint16_t au_duration, cts_duration; + uint64_t ocr, dts, cts; +} mp4_sl_config_t; + +typedef struct { + uint16_t id; + uint8_t flags; + mp4_decoder_config_t decoder; + mp4_sl_config_t sl; +} mp4_es_descr_t; + +typedef struct { + uint16_t id; + uint8_t flags; + mp4_es_descr_t *es; + uint16_t es_cnt; +} mp4_od_t; + +typedef struct { + uint8_t skip; + uint8_t table_id; + uint8_t ssi; + uint16_t section_length; + uint16_t ts_id; + uint8_t version_number; + uint8_t curr_next; + uint8_t section_number; + uint8_t last_section_number; + struct pat_progs_t { + uint16_t id; + uint16_t pmt_pid; + } *progs; + uint16_t progs_cnt; + ts_section_t section; +} pat_t; + +typedef struct { + uint16_t progid; + uint8_t skip; + uint8_t table_id; + uint8_t ssi; + uint16_t section_length; + uint8_t version_number; + uint8_t curr_next; + uint8_t section_number; + uint8_t last_section_number; + uint16_t PCR_PID; + uint16_t prog_descr_length; + ts_section_t section; + uint16_t es_cnt; + struct pmt_es_t { + uint16_t pid; + uint32_t type; //it's 8 bit long, but cast to the right type as FOURCC + uint16_t descr_length; + uint8_t format_descriptor[5]; + uint8_t lang[4]; + uint16_t mp4_es_id; + } *es; + mp4_od_t iod, *od; + mp4_es_descr_t *mp4es; + int od_cnt, mp4es_cnt; +} pmt_t; + +typedef struct { + uint64_t size; + float duration; + double first_pts; + double last_pts; +} TS_stream_info; + +typedef struct { + MpegTSContext ts; + int last_pid; + av_fifo_t fifo[3]; //0 for audio, 1 for video, 2 for subs + pat_t pat; + pmt_t *pmt; + uint16_t pmt_cnt; + uint32_t prog; + uint32_t vbitrate; + int keep_broken; + int last_aid; + int last_vid; + int last_sid; + char packet[TS_FEC_PACKET_SIZE]; + TS_stream_info vstr, astr; +} ts_priv_t; + + +typedef struct { + es_stream_type_t type; + ts_section_t section; +} TS_pids_t; + + +static int IS_AUDIO(es_stream_type_t type) +{ + switch (type) { + case AUDIO_MP2: + case AUDIO_A52: + case AUDIO_LPCM_BE: + case AUDIO_AAC: + case AUDIO_AAC_LATM: + case AUDIO_DTS: + case AUDIO_TRUEHD: + case AUDIO_S302M: + return 1; + } + return 0; +} + +static int IS_VIDEO(es_stream_type_t type) +{ + switch (type) { + case VIDEO_MPEG1: + case VIDEO_MPEG2: + case VIDEO_MPEG4: + case VIDEO_H264: + case VIDEO_AVC: + case VIDEO_DIRAC: + case VIDEO_VC1: + return 1; + } + return 0; +} + +static int IS_SUB(es_stream_type_t type) +{ + switch (type) { + case SPU_DVD: + case SPU_DVB: + case SPU_PGS: + case SPU_TELETEXT: + return 1; + } + return 0; +} + +static int ts_parse(demuxer_t *demuxer, ES_stream_t *es, unsigned char *packet, int probe); + +static uint8_t get_packet_size(const unsigned char *buf, int size) +{ + int i; + + if (size < (TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS)) + return 0; + + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_PACKET_SIZE] != 0x47) + { + mp_msg(MSGT_DEMUX, MSGL_DBG2, "GET_PACKET_SIZE, pos %d, char: %2x\n", i, buf[i * TS_PACKET_SIZE]); + goto try_fec; + } + } + return TS_PACKET_SIZE; + +try_fec: + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_FEC_PACKET_SIZE] != 0x47){ + mp_msg(MSGT_DEMUX, MSGL_DBG2, "GET_PACKET_SIZE, pos %d, char: %2x\n", i, buf[i * TS_PACKET_SIZE]); + goto try_philips; + } + } + return TS_FEC_PACKET_SIZE; + + try_philips: + for(i=0; i<NUM_CONSECUTIVE_TS_PACKETS; i++) + { + if (buf[i * TS_PH_PACKET_SIZE] != 0x47) + return 0; + } + return TS_PH_PACKET_SIZE; +} + +static int parse_avc_sps(uint8_t *buf, int len, int *w, int *h); +static inline uint8_t *pid_lang_from_pmt(ts_priv_t *priv, int pid); + +static void ts_add_stream(demuxer_t * demuxer, ES_stream_t *es) +{ + int i; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + + if(priv->ts.streams[es->pid].sh) + return; + + if((IS_AUDIO(es->type) || IS_AUDIO(es->subtype)) && priv->last_aid+1 < MAX_A_STREAMS) + { + sh_audio_t *sh = new_sh_audio_aid(demuxer, priv->last_aid, es->pid); + if(sh) + { + const char *lang = pid_lang_from_pmt(priv, es->pid); + sh->needs_parsing = 1; + sh->format = IS_AUDIO(es->type) ? es->type : es->subtype; + sh->ds = demuxer->audio; + + priv->ts.streams[es->pid].id = priv->last_aid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_AUDIO; + mp_msg(MSGT_DEMUX, MSGL_V, "\r\nADDED AUDIO PID %d, type: %x stream n. %d\r\n", es->pid, sh->format, priv->last_aid); + if (lang && lang[0]) + mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_AID_%d_LANG=%s\n", es->pid, lang); + priv->last_aid++; + } + + if(es->extradata && es->extradata_len) + { + sh->wf = malloc(sizeof(*sh->wf) + es->extradata_len); + sh->wf->cbSize = es->extradata_len; + memcpy(sh->wf + 1, es->extradata, es->extradata_len); + } + } + + if((IS_VIDEO(es->type) || IS_VIDEO(es->subtype)) && priv->last_vid+1 < MAX_V_STREAMS) + { + sh_video_t *sh = new_sh_video_vid(demuxer, priv->last_vid, es->pid); + if(sh) + { + sh->format = IS_VIDEO(es->type) ? es->type : es->subtype; + sh->ds = demuxer->video; + + priv->ts.streams[es->pid].id = priv->last_vid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_VIDEO; + mp_msg(MSGT_DEMUX, MSGL_V, "\r\nADDED VIDEO PID %d, type: %x stream n. %d\r\n", es->pid, sh->format, priv->last_vid); + priv->last_vid++; + + + if(sh->format == VIDEO_AVC && es->extradata && es->extradata_len) + { + int w = 0, h = 0; + sh->bih = calloc(1, sizeof(*sh->bih) + es->extradata_len); + sh->bih->biSize= sizeof(*sh->bih) + es->extradata_len; + sh->bih->biCompression = sh->format; + memcpy(sh->bih + 1, es->extradata, es->extradata_len); + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "EXTRADATA(%d BYTES): \n", es->extradata_len); + for(i = 0;i < es->extradata_len; i++) + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "%02x ", (int) es->extradata[i]); + mp_msg(MSGT_DEMUXER,MSGL_DBG2,"\n"); + if(parse_avc_sps(es->extradata, es->extradata_len, &w, &h)) + { + sh->bih->biWidth = w; + sh->bih->biHeight = h; + } + } + } + } + + if(IS_SUB(es->type) && priv->last_sid+1 < MAX_S_STREAMS) + { + sh_sub_t *sh = new_sh_sub_sid_lang(demuxer, priv->last_sid, es->pid, pid_lang_from_pmt(priv, es->pid)); + if (sh) { + switch (es->type) { + case SPU_DVB: + sh->type = 'b'; break; + case SPU_DVD: + sh->type = 'v'; break; + case SPU_PGS: + sh->type = 'p'; break; + } + priv->ts.streams[es->pid].id = priv->last_sid; + priv->ts.streams[es->pid].sh = sh; + priv->ts.streams[es->pid].type = TYPE_SUB; + priv->last_sid++; + } + } +} + +static int ts_check_file(demuxer_t * demuxer) +{ + const int buf_size = (TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS); + unsigned char buf[TS_FEC_PACKET_SIZE * NUM_CONSECUTIVE_TS_PACKETS], done = 0, *ptr; + uint32_t _read, i, count = 0, is_ts; + int cc[NB_PID_MAX], last_cc[NB_PID_MAX], pid, cc_ok, c, good, bad; + uint8_t size = 0; + off_t pos = 0; + off_t init_pos; + + mp_msg(MSGT_DEMUX, MSGL_V, "Checking for MPEG-TS...\n"); + + init_pos = stream_tell(demuxer->stream); + is_ts = 0; + while(! done) + { + i = 1; + c = 0; + + while(((c=stream_read_char(demuxer->stream)) != 0x47) + && (c >= 0) + && (i < MAX_CHECK_SIZE) + && ! demuxer->stream->eof + ) i++; + + + if(c != 0x47) + { + mp_msg(MSGT_DEMUX, MSGL_V, "THIS DOESN'T LOOK LIKE AN MPEG-TS FILE!\n"); + is_ts = 0; + done = 1; + continue; + } + + pos = stream_tell(demuxer->stream) - 1; + buf[0] = c; + _read = stream_read(demuxer->stream, &buf[1], buf_size-1); + + if(_read < buf_size-1) + { + mp_msg(MSGT_DEMUX, MSGL_V, "COULDN'T READ ENOUGH DATA, EXITING TS_CHECK\n"); + stream_reset(demuxer->stream); + return 0; + } + + size = get_packet_size(buf, buf_size); + if(size) + { + done = 1; + is_ts = 1; + } + + if(pos - init_pos >= MAX_CHECK_SIZE) + { + done = 1; + is_ts = 0; + } + } + + mp_msg(MSGT_DEMUX, MSGL_V, "TRIED UP TO POSITION %"PRIu64", FOUND %x, packet_size= %d, SEEMS A TS? %d\n", (uint64_t) pos, c, size, is_ts); + stream_seek(demuxer->stream, pos); + + if(! is_ts) + return 0; + + //LET'S CHECK continuity counters + good = bad = 0; + for(count = 0; count < NB_PID_MAX; count++) + { + cc[count] = last_cc[count] = -1; + } + + for(count = 0; count < NUM_CONSECUTIVE_TS_PACKETS; count++) + { + ptr = &(buf[size * count]); + pid = ((ptr[1] & 0x1f) << 8) | ptr[2]; + mp_msg(MSGT_DEMUX, MSGL_DBG2, "BUF: %02x %02x %02x %02x, PID %d, SIZE: %d \n", + ptr[0], ptr[1], ptr[2], ptr[3], pid, size); + + if((pid == 8191) || (pid < 16)) + continue; + + cc[pid] = (ptr[3] & 0xf); + cc_ok = (last_cc[pid] < 0) || ((((last_cc[pid] + 1) & 0x0f) == cc[pid])); + mp_msg(MSGT_DEMUX, MSGL_DBG2, "PID %d, COMPARE CC %d AND LAST_CC %d\n", pid, cc[pid], last_cc[pid]); + if(! cc_ok) + //return 0; + bad++; + else + good++; + + last_cc[pid] = cc[pid]; + } + + mp_msg(MSGT_DEMUX, MSGL_V, "GOOD CC: %d, BAD CC: %d\n", good, bad); + + if(good >= bad) + return size; + else + return 0; +} + + +static inline int32_t progid_idx_in_pmt(ts_priv_t *priv, uint16_t progid) +{ + int x; + + if(priv->pmt == NULL) + return -1; + + for(x = 0; x < priv->pmt_cnt; x++) + { + if(priv->pmt[x].progid == progid) + return x; + } + + return -1; +} + + +static inline int32_t progid_for_pid(ts_priv_t *priv, int pid, int32_t req) //finds the first program listing a pid +{ + int i, j; + pmt_t *pmt; + + + if(priv->pmt == NULL) + return -1; + + + for(i=0; i < priv->pmt_cnt; i++) + { + pmt = &(priv->pmt[i]); + + if(pmt->es == NULL) + return -1; + + for(j = 0; j < pmt->es_cnt; j++) + { + if(pmt->es[j].pid == pid) + { + if((req == 0) || (req == pmt->progid)) + return pmt->progid; + } + } + + } + return -1; +} + +static inline int32_t prog_pcr_pid(ts_priv_t *priv, int progid) +{ + int i; + + if(priv->pmt == NULL) + return -1; + for(i=0; i < priv->pmt_cnt; i++) + { + if(priv->pmt[i].progid == progid) + return priv->pmt[i].PCR_PID; + } + return -1; +} + + +static inline int pid_match_lang(ts_priv_t *priv, uint16_t pid, char *lang) +{ + uint16_t i, j; + pmt_t *pmt; + + if(priv->pmt == NULL) + return -1; + + for(i=0; i < priv->pmt_cnt; i++) + { + pmt = &(priv->pmt[i]); + + if(pmt->es == NULL) + return -1; + + for(j = 0; j < pmt->es_cnt; j++) + { + if(pmt->es[j].pid != pid) + continue; + + mp_msg(MSGT_DEMUXER, MSGL_V, "CMP LANG %s AND %s, pids: %d %d\n",pmt->es[j].lang, lang, pmt->es[j].pid, pid); + if(strncmp(pmt->es[j].lang, lang, 3) == 0) + { + return 1; + } + } + + } + + return -1; +} + +typedef struct { + int32_t atype, vtype, stype; //types + int32_t apid, vpid, spid; //stream ids + char alang[4]; //languages + uint16_t prog; + off_t probe; +} tsdemux_init_t; + +//second stage: returns the count of A52 syncwords found +static int a52_check(char *buf, int len) +{ + int cnt, frame_length = 0, ok, srate; + + cnt = ok = 0; + if(len < 8) + return 0; + + while(cnt < len - 7) + { + if(buf[cnt] == 0x0B && buf[cnt+1] == 0x77) + { + frame_length = mp_a52_framesize(&buf[cnt], &srate); + if(frame_length>=7 && frame_length<=3840) + { + cnt += frame_length; + ok++; + } + else + cnt++; + } + else + cnt++; + } + + mp_msg(MSGT_DEMUXER, MSGL_V, "A52_CHECK(%d input bytes), found %d frame syncwords of %d bytes length\n", len, ok, frame_length); + return ok; +} + + +static off_t ts_detect_streams(demuxer_t *demuxer, tsdemux_init_t *param) +{ + int video_found = 0, audio_found = 0, i, num_packets = 0, req_apid, req_vpid, req_spid; + int is_audio, is_video, is_sub, has_tables; + int32_t p, chosen_pid = 0; + off_t pos=0, ret = 0, init_pos, end_pos; + ES_stream_t es; + unsigned char tmp[TS_FEC_PACKET_SIZE]; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + struct { + char *buf; + int pos; + } pes_priv1[8192], *pptr; + char *tmpbuf; + + priv->last_pid = 8192; //invalid pid + + req_apid = param->apid; + req_vpid = param->vpid; + req_spid = param->spid; + + has_tables = 0; + memset(pes_priv1, 0, sizeof(pes_priv1)); + init_pos = stream_tell(demuxer->stream); + mp_msg(MSGT_DEMUXER, MSGL_V, "PROBING UP TO %"PRIu64", PROG: %d\n", (uint64_t) param->probe, param->prog); + end_pos = init_pos + (param->probe ? param->probe : TS_MAX_PROBE_SIZE); + while(1) + { + pos = stream_tell(demuxer->stream); + if(pos > end_pos || demuxer->stream->eof) + break; + + if(ts_parse(demuxer, &es, tmp, 1)) + { + //Non PES-aligned A52 audio may escape detection if PMT is not present; + //in this case we try to find at least 3 A52 syncwords + if((es.type == PES_PRIVATE1) && (! audio_found) && req_apid > -2) + { + pptr = &pes_priv1[es.pid]; + if(pptr->pos < 64*1024) + { + tmpbuf = realloc(pptr->buf, pptr->pos + es.size); + if(tmpbuf != NULL) + { + pptr->buf = tmpbuf; + memcpy(&(pptr->buf[ pptr->pos ]), es.start, es.size); + pptr->pos += es.size; + if(a52_check(pptr->buf, pptr->pos) > 2) + { + param->atype = AUDIO_A52; + param->apid = es.pid; + es.type = AUDIO_A52; + } + } + } + } + + is_audio = IS_AUDIO(es.type) || ((es.type==SL_PES_STREAM) && IS_AUDIO(es.subtype)); + is_video = IS_VIDEO(es.type) || ((es.type==SL_PES_STREAM) && IS_VIDEO(es.subtype)); + is_sub = IS_SUB(es.type); + + + if((! is_audio) && (! is_video) && (! is_sub)) + continue; + if(is_audio && req_apid==-2) + continue; + + if(is_video) + { + chosen_pid = (req_vpid == es.pid); + if((! chosen_pid) && (req_vpid > 0)) + continue; + } + else if(is_audio) + { + if(req_apid > 0) + { + chosen_pid = (req_apid == es.pid); + if(! chosen_pid) + continue; + } + else if(param->alang[0] > 0 && es.lang[0] > 0) + { + if(pid_match_lang(priv, es.pid, param->alang) == -1) + continue; + + chosen_pid = 1; + param->apid = req_apid = es.pid; + } + } + else if(is_sub) + { + chosen_pid = (req_spid == es.pid); + if((! chosen_pid) && (req_spid > 0)) + continue; + } + + if(req_apid < 0 && (param->alang[0] == 0) && req_vpid < 0 && req_spid < 0) + chosen_pid = 1; + + if((ret == 0) && chosen_pid) + { + ret = stream_tell(demuxer->stream); + } + + p = progid_for_pid(priv, es.pid, param->prog); + if(p != -1) + { + has_tables++; + if(!param->prog && chosen_pid) + param->prog = p; + } + + if((param->prog > 0) && (param->prog != p)) + { + if(audio_found) + { + if(is_video && (req_vpid == es.pid)) + { + param->vtype = IS_VIDEO(es.type) ? es.type : es.subtype; + param->vpid = es.pid; + video_found = 1; + break; + } + } + + if(video_found) + { + if(is_audio && (req_apid == es.pid)) + { + param->atype = IS_AUDIO(es.type) ? es.type : es.subtype; + param->apid = es.pid; + audio_found = 1; + break; + } + } + + + continue; + } + + + mp_msg(MSGT_DEMUXER, MSGL_DBG2, "TYPE: %x, PID: %d, PROG FOUND: %d\n", es.type, es.pid, param->prog); + + + if(is_video) + { + if((req_vpid == -1) || (req_vpid == es.pid)) + { + param->vtype = IS_VIDEO(es.type) ? es.type : es.subtype; + param->vpid = es.pid; + video_found = 1; + } + } + + + if(((req_vpid == -2) || (num_packets >= NUM_CONSECUTIVE_AUDIO_PACKETS)) && audio_found && !param->probe) + { + //novideo or we have at least 348 audio packets (64 KB) without video (TS with audio only) + param->vtype = 0; + break; + } + + if(is_sub) + { + if((req_spid == -1) || (req_spid == es.pid)) + { + param->stype = es.type; + param->spid = es.pid; + } + } + + if(is_audio) + { + if((req_apid == -1) || (req_apid == es.pid)) + { + param->atype = IS_AUDIO(es.type) ? es.type : es.subtype; + param->apid = es.pid; + audio_found = 1; + } + } + + if(audio_found && (param->apid == es.pid) && (! video_found)) + num_packets++; + + if((has_tables==0) && (video_found && audio_found) && (pos >= 1000000)) + break; + } + } + + for(i=0; i<8192; i++) + { + if(pes_priv1[i].buf != NULL) + { + free(pes_priv1[i].buf); + pes_priv1[i].buf = NULL; + pes_priv1[i].pos = 0; + } + } + + if(video_found) + { + if(param->vtype == VIDEO_MPEG1) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG1(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_MPEG2) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG2(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_MPEG4) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO MPEG4(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_H264) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO H264(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_VC1) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO VC1(pid=%d) ", param->vpid); + else if(param->vtype == VIDEO_AVC) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "VIDEO AVC(NAL-H264, pid=%d) ", param->vpid); + } + else + { + param->vtype = UNKNOWN; + //WE DIDN'T MATCH ANY VIDEO STREAM + mp_msg(MSGT_DEMUXER, MSGL_INFO, "NO VIDEO! "); + } + + if(param->atype == AUDIO_MP2) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO MPA(pid=%d)", param->apid); + else if(param->atype == AUDIO_A52) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO A52(pid=%d)", param->apid); + else if(param->atype == AUDIO_DTS) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO DTS(pid=%d)", param->apid); + else if(param->atype == AUDIO_LPCM_BE) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO LPCM(pid=%d)", param->apid); + else if(param->atype == AUDIO_AAC) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO AAC(pid=%d)", param->apid); + else if(param->atype == AUDIO_AAC_LATM) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO AAC LATM(pid=%d)", param->apid); + else if(param->atype == AUDIO_TRUEHD) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO TRUEHD(pid=%d)", param->apid); + else if(param->atype == AUDIO_S302M) + mp_msg(MSGT_DEMUXER, MSGL_INFO, "AUDIO S302M(pid=%d)", param->apid); + else + { + audio_found = 0; + param->atype = UNKNOWN; + //WE DIDN'T MATCH ANY AUDIO STREAM, SO WE FORCE THE DEMUXER TO IGNORE AUDIO + mp_msg(MSGT_DEMUXER, MSGL_INFO, "NO AUDIO! (try increasing -tsprobe)"); + } + + if(IS_SUB(param->stype)) + mp_msg(MSGT_DEMUXER, MSGL_INFO, " SUB %s(pid=%d) ", (param->stype==SPU_DVD ? "DVD" : param->stype==SPU_DVB ? "DVB" : "Teletext"), param->spid); + else + { + param->stype = UNKNOWN; + mp_msg(MSGT_DEMUXER, MSGL_INFO, " NO SUBS (yet)! "); + } + + if(video_found || audio_found) + { + if(!param->prog) + { + p = progid_for_pid(priv, video_found ? param->vpid : param->apid, 0); + if(p != -1) + param->prog = p; + } + + if(demuxer->stream->eof && (ret == 0)) + ret = init_pos; + mp_msg(MSGT_DEMUXER, MSGL_INFO, " PROGRAM N. %d\n", param->prog); + } + else + mp_msg(MSGT_DEMUXER, MSGL_INFO, "\n"); + + + for(i=0; i<NB_PID_MAX; i++) + { + if(priv->ts.pids[i] != NULL) + { + priv->ts.pids[i]->payload_size = 0; + priv->ts.pids[i]->pts = priv->ts.pids[i]->last_pts = 0; + priv->ts.pids[i]->last_cc = -1; + priv->ts.pids[i]->is_synced = 0; + } + } + + return ret; +} + +static int parse_avc_sps(uint8_t *buf, int len, int *w, int *h) +{ + int sps, sps_len; + unsigned char *ptr; + mp_mpeg_header_t picture; + if(len < 6) + return 0; + sps = buf[5] & 0x1f; + if(!sps) + return 0; + sps_len = (buf[6] << 8) | buf[7]; + if(!sps_len || (sps_len > len - 8)) + return 0; + ptr = &(buf[8]); + picture.display_picture_width = picture.display_picture_height = 0; + h264_parse_sps(&picture, ptr, len - 8); + if(!picture.display_picture_width || !picture.display_picture_height) + return 0; + *w = picture.display_picture_width; + *h = picture.display_picture_height; + return 1; +} + +static demuxer_t *demux_open_ts(demuxer_t * demuxer) +{ + int i; + uint8_t packet_size; + sh_video_t *sh_video; + sh_audio_t *sh_audio; + off_t start_pos; + tsdemux_init_t params; + ts_priv_t * priv = demuxer->priv; + + mp_msg(MSGT_DEMUX, MSGL_V, "DEMUX OPEN, AUDIO_ID: %d, VIDEO_ID: %d, SUBTITLE_ID: %d,\n", + demuxer->audio->id, demuxer->video->id, demuxer->sub->id); + + + demuxer->type= DEMUXER_TYPE_MPEG_TS; + + + stream_reset(demuxer->stream); + + packet_size = ts_check_file(demuxer); + if(!packet_size) + return NULL; + + priv = calloc(1, sizeof(ts_priv_t)); + if(priv == NULL) + { + mp_msg(MSGT_DEMUX, MSGL_FATAL, "DEMUX_OPEN_TS, couldn't allocate enough memory for ts->priv, exit\n"); + return NULL; + } + + for(i=0; i < NB_PID_MAX; i++) + { + priv->ts.pids[i] = NULL; + priv->ts.streams[i].id = -3; + } + priv->pat.progs = NULL; + priv->pat.progs_cnt = 0; + priv->pat.section.buffer = NULL; + priv->pat.section.buffer_len = 0; + + priv->pmt = NULL; + priv->pmt_cnt = 0; + + priv->keep_broken = ts_keep_broken; + priv->ts.packet_size = packet_size; + + + demuxer->priv = priv; + if(demuxer->stream->type != STREAMTYPE_FILE) + demuxer->seekable = 1; + else + demuxer->seekable = 1; + + + params.atype = params.vtype = params.stype = UNKNOWN; + params.apid = demuxer->audio->id; + params.vpid = demuxer->video->id; + params.spid = demuxer->sub->id; + params.prog = ts_prog; + params.probe = ts_probe; + + if(demuxer->opts->audio_lang != NULL) + { + strncpy(params.alang, demuxer->opts->audio_lang[0], 3); + params.alang[3] = 0; + } + else + memset(params.alang, 0, 4); + + start_pos = ts_detect_streams(demuxer, ¶ms); + + demuxer->sub->id = params.spid; + priv->prog = params.prog; + + if(params.vtype != UNKNOWN) + { + ts_add_stream(demuxer, priv->ts.pids[params.vpid]); + sh_video = priv->ts.streams[params.vpid].sh; + demuxer->video->id = priv->ts.streams[params.vpid].id; + sh_video->ds = demuxer->video; + sh_video->format = params.vtype; + demuxer->video->sh = sh_video; + } + + if(params.atype != UNKNOWN) + { + ES_stream_t *es = priv->ts.pids[params.apid]; + + if(!IS_AUDIO(es->type) && !IS_AUDIO(es->subtype) && IS_AUDIO(params.atype)) es->subtype = params.atype; + ts_add_stream(demuxer, priv->ts.pids[params.apid]); + sh_audio = priv->ts.streams[params.apid].sh; + demuxer->audio->id = priv->ts.streams[params.apid].id; + sh_audio->ds = demuxer->audio; + sh_audio->format = params.atype; + demuxer->audio->sh = sh_audio; + } + + + mp_msg(MSGT_DEMUXER,MSGL_V, "Opened TS demuxer, audio: %x(pid %d), video: %x(pid %d)...POS=%"PRIu64", PROBE=%"PRIu64"\n", params.atype, demuxer->audio->id, params.vtype, demuxer->video->id, (uint64_t) start_pos, ts_probe); + + + start_pos = start_pos <= priv->ts.packet_size ? + demuxer->stream->start_pos : + start_pos - priv->ts.packet_size; + demuxer->movi_start = start_pos; + demuxer->reference_clock = MP_NOPTS_VALUE; + stream_reset(demuxer->stream); + stream_seek(demuxer->stream, start_pos); //IF IT'S FROM A PIPE IT WILL FAIL, BUT WHO CARES? + + + priv->last_pid = 8192; //invalid pid + + for(i = 0; i < 3; i++) + { + priv->fifo[i].pack = NULL; + priv->fifo[i].offset = 0; + } + priv->fifo[0].ds = demuxer->audio; + priv->fifo[1].ds = demuxer->video; + priv->fifo[2].ds = demuxer->sub; + + priv->fifo[0].buffer_size = 1536; + priv->fifo[1].buffer_size = 32767; + priv->fifo[2].buffer_size = 32767; + + priv->pat.section.buffer_len = 0; + for(i = 0; i < priv->pmt_cnt; i++) + priv->pmt[i].section.buffer_len = 0; + + demuxer->filepos = stream_tell(demuxer->stream); + return demuxer; +} + +static void demux_close_ts(demuxer_t * demuxer) +{ + uint16_t i; + ts_priv_t *priv = (ts_priv_t*) demuxer->priv; + + if(priv) + { + free(priv->pat.section.buffer); + free(priv->pat.progs); + + if(priv->pmt) + { + for(i = 0; i < priv->pmt_cnt; i++) + { + free(priv->pmt[i].section.buffer); + free(priv->pmt[i].es); + } + free(priv->pmt); + } + for (i = 0; i < NB_PID_MAX; i++) + { + free(priv->ts.pids[i]); + priv->ts.pids[i] = NULL; + } + for (i = 0; i < 3; i++) + { + if (priv->fifo[i].pack) + free_demux_packet(priv->fifo[i].pack); + priv->fifo[i].pack = NULL; + } + free(priv); + } + demuxer->priv=NULL; +} + + +#define getbits mp_getbits + +static int mp4_parse_sl_packet(pmt_t *pmt, uint8_t *buf, uint16_t packet_len, int pid, ES_stream_t *pes_es) +{ + int i, n, m, mp4_es_id = -1; + uint64_t v = 0; + uint32_t pl_size = 0; + int deg_flag = 0; + mp4_es_descr_t *es = NULL; + mp4_sl_config_t *sl = NULL; + uint8_t au_start = 0, au_end = 0, rap_flag = 0, ocr_flag = 0, padding = 0, padding_bits = 0, idle = 0; + + pes_es->is_synced = 0; + mp_msg(MSGT_DEMUXER,MSGL_V, "mp4_parse_sl_packet, pid: %d, pmt: %pm, packet_len: %d\n", pid, pmt, packet_len); + if(! pmt || !packet_len) + return 0; + + for(i = 0; i < pmt->es_cnt; i++) + { + if(pmt->es[i].pid == pid) + mp4_es_id = pmt->es[i].mp4_es_id; + } + if(mp4_es_id < 0) + return -1; + + for(i = 0; i < pmt->mp4es_cnt; i++) + { + if(pmt->mp4es[i].id == mp4_es_id) + es = &(pmt->mp4es[i]); + } + if(! es) + return -1; + + pes_es->subtype = es->decoder.object_type; + + sl = &(es->sl); + if(!sl) + return -1; + + //now es is the complete es_descriptor of out mp4 ES stream + mp_msg(MSGT_DEMUXER,MSGL_DBG2, "ID: %d, FLAGS: 0x%x, subtype: %x\n", es->id, sl->flags, pes_es->subtype); + + n = 0; + if(sl->au_start) + pes_es->sl.au_start = au_start = getbits(buf, n++, 1); + else + pes_es->sl.au_start = (pes_es->sl.last_au_end ? 1 : 0); + if(sl->au_end) |