X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmpegts.c;h=031c460dd62fc48267dc9a29babc78e7cc302fb4;hb=8e6e9bacc4fd983741e4589bf5dd366c01ac4e15;hp=406e5bfb528acc7df20c73d325be661a1a9889e7;hpb=b474d1f3fa6d951b91962eb0eb26945c25988e5a;p=ffmpeg diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 406e5bfb528..031c460dd62 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -1,6 +1,6 @@ /* * MPEG2 transport stream (aka DVB) demuxer - * Copyright (c) 2002-2003 Fabrice Bellard. + * Copyright (c) 2002-2003 Fabrice Bellard * * This file is part of FFmpeg. * @@ -18,14 +18,17 @@ * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avformat.h" -#include "crc.h" -#include "mpegts.h" -#include "allformats.h" -//#define DEBUG_SI +//#define DEBUG //#define DEBUG_SEEK +#include "libavutil/crc.h" +#include "libavutil/intreadwrite.h" +#include "libavcodec/bytestream.h" +#include "avformat.h" +#include "mpegts.h" +#include "internal.h" + /* 1.0 second at 24Mbit/s */ #define MAX_SCAN_PACKETS 32000 @@ -33,10 +36,11 @@ synchronisation is lost */ #define MAX_RESYNC_SIZE 4096 +#define MAX_PES_PAYLOAD 200*1024 + typedef struct PESContext PESContext; static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type); -static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code); enum MpegTSFilterType { MPEGTS_PES, @@ -45,7 +49,7 @@ enum MpegTSFilterType { typedef struct MpegTSFilter MpegTSFilter; -typedef void PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start); +typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos); typedef struct MpegTSPESFilter { PESCallback *pes_cb; @@ -60,8 +64,8 @@ typedef struct MpegTSSectionFilter { int section_index; int section_h_size; uint8_t *section_buf; - int check_crc:1; - int end_of_section_reached:1; + unsigned int check_crc:1; + unsigned int end_of_section_reached:1; SectionCallback *section_cb; void *opaque; } MpegTSSectionFilter; @@ -76,18 +80,21 @@ struct MpegTSFilter { } u; }; -typedef struct MpegTSService { - int running:1; - int sid; /**< MPEG Program Number of stream */ - char *provider_name; /**< DVB Network name, "" if not DVB stream */ - char *name; /**< DVB Service name, "MPEG Program [sid]" if not DVB stream*/ -} MpegTSService; +#define MAX_PIDS_PER_PROGRAM 64 +struct Program { + unsigned int id; //program id/service id + unsigned int nb_pids; + unsigned int pids[MAX_PIDS_PER_PROGRAM]; +}; struct MpegTSContext { /* user data */ AVFormatContext *stream; /** raw packet size, including FEC if present */ int raw_packet_size; + + int pos47; + /** if true, all pids are analyzed to find streams */ int auto_guess; @@ -102,14 +109,15 @@ struct MpegTSContext { int stop_parse; /** packet containing Audio/Video data */ AVPacket *pkt; + /** to detect seek */ + int64_t last_pos; /******************************************/ /* private mpegts data */ /* scan context */ - /** number of PMTs in the last PAT seen */ - int nb_services; - /** list of PMTs in the last PAT seen */ - MpegTSService **services; + /** structure to keep track of Program->pids mapping */ + unsigned int nb_prg; + struct Program *prg; /** filters for various streams specified by PMT + for the PAT and PMT */ @@ -120,13 +128,15 @@ struct MpegTSContext { enum MpegTSState { MPEGTS_HEADER = 0, + MPEGTS_PESHEADER, MPEGTS_PESHEADER_FILL, MPEGTS_PAYLOAD, MPEGTS_SKIP, }; /* enough for PES header + length */ -#define PES_START_SIZE 9 +#define PES_START_SIZE 6 +#define PES_HEADER_SIZE 9 #define MAX_PES_HEADER_SIZE (9 + 255) struct PESContext { @@ -142,9 +152,92 @@ struct PESContext { int total_size; int pes_header_size; int64_t pts, dts; + int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */ uint8_t header[MAX_PES_HEADER_SIZE]; + uint8_t *buffer; }; +extern AVInputFormat mpegts_demuxer; + +static void clear_program(MpegTSContext *ts, unsigned int programid) +{ + int i; + + for(i=0; inb_prg; i++) + if(ts->prg[i].id == programid) + ts->prg[i].nb_pids = 0; +} + +static void clear_programs(MpegTSContext *ts) +{ + av_freep(&ts->prg); + ts->nb_prg=0; +} + +static void add_pat_entry(MpegTSContext *ts, unsigned int programid) +{ + struct Program *p; + void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(struct Program)); + if(!tmp) + return; + ts->prg = tmp; + p = &ts->prg[ts->nb_prg]; + p->id = programid; + p->nb_pids = 0; + ts->nb_prg++; +} + +static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid) +{ + int i; + struct Program *p = NULL; + for(i=0; inb_prg; i++) { + if(ts->prg[i].id == programid) { + p = &ts->prg[i]; + break; + } + } + if(!p) + return; + + if(p->nb_pids >= MAX_PIDS_PER_PROGRAM) + return; + p->pids[p->nb_pids++] = pid; +} + +/** + * \brief discard_pid() decides if the pid is to be discarded according + * to caller's programs selection + * \param ts : - TS context + * \param pid : - pid + * \return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL + * 0 otherwise + */ +static int discard_pid(MpegTSContext *ts, unsigned int pid) +{ + int i, j, k; + int used = 0, discarded = 0; + struct Program *p; + for(i=0; inb_prg; i++) { + p = &ts->prg[i]; + for(j=0; jnb_pids; j++) { + if(p->pids[j] != pid) + continue; + //is program with id p->id set to be discarded? + for(k=0; kstream->nb_programs; k++) { + if(ts->stream->programs[k]->id == p->id) { + if(ts->stream->programs[k]->discard == AVDISCARD_ALL) + discarded++; + else + used++; + } + } + } + } + + return !used && discarded; +} + /** * Assembles PES packets out of TS packets, and then calls the "section_cb" * function when they are complete. @@ -181,7 +274,8 @@ static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { tss->end_of_section_reached = 1; if (!tss->check_crc || - av_crc(av_crc04C11DB7, -1, tss->section_buf, tss->section_h_size) == 0) + av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, + tss->section_buf, tss->section_h_size) == 0) tss->section_cb(tss1, tss->section_buf, tss->section_h_size); } } @@ -194,9 +288,8 @@ static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int MpegTSFilter *filter; MpegTSSectionFilter *sec; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "Filter: pid=0x%x\n", pid); -#endif + dprintf(ts->stream, "Filter: pid=0x%x\n", pid); + if (pid >= NB_PID_MAX || ts->pids[pid]) return NULL; filter = av_mallocz(sizeof(MpegTSFilter)); @@ -247,8 +340,15 @@ static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) pid = filter->pid; if (filter->type == MPEGTS_SECTION) av_freep(&filter->u.section_filter.section_buf); - else if (filter->type == MPEGTS_PES) - av_freep(&filter->u.pes_filter.opaque); + else if (filter->type == MPEGTS_PES) { + PESContext *pes = filter->u.pes_filter.opaque; + av_freep(&pes->buffer); + /* referenced private data will be freed later in + * av_close_input_stream */ + if (!((PESContext *)filter->u.pes_filter.opaque)->st) { + av_freep(&filter->u.pes_filter.opaque); + } + } av_free(filter); ts->pids[pid] = NULL; @@ -262,8 +362,8 @@ static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ memset(stat, 0, packet_size*sizeof(int)); - for(x=i=0; i best_score){ best_score= stat[x]; @@ -384,37 +484,102 @@ static int parse_section_header(SectionHeader *h, return 0; } -static MpegTSService *new_service(MpegTSContext *ts, int sid, - char *provider_name, char *name) -{ - MpegTSService *service=NULL; - int i; +typedef struct { + uint32_t stream_type; + enum CodecType codec_type; + enum CodecID codec_id; +} StreamType; + +static const StreamType ISO_types[] = { + { 0x01, CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, + { 0x02, CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO }, + { 0x03, CODEC_TYPE_AUDIO, CODEC_ID_MP3 }, + { 0x04, CODEC_TYPE_AUDIO, CODEC_ID_MP3 }, + { 0x0f, CODEC_TYPE_AUDIO, CODEC_ID_AAC }, + { 0x10, CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, + { 0x1b, CODEC_TYPE_VIDEO, CODEC_ID_H264 }, + { 0xd1, CODEC_TYPE_VIDEO, CODEC_ID_DIRAC }, + { 0xea, CODEC_TYPE_VIDEO, CODEC_ID_VC1 }, + { 0 }, +}; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "new_service: " - "sid=0x%04x provider='%s' name='%s'\n", - sid, provider_name, name); -#endif +static const StreamType HDMV_types[] = { + { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, + { 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, + { 0 }, +}; - for(i=0; inb_services; i++) - if(ts->services[i]->sid == sid) - service= ts->services[i]; +/* ATSC ? */ +static const StreamType MISC_types[] = { + { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, + { 0x8a, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, + { 0 }, +}; - if(!service){ - service = av_mallocz(sizeof(MpegTSService)); - if (!service) - return NULL; - dynarray_add(&ts->services, &ts->nb_services, service); +static const StreamType REGD_types[] = { + { MKTAG('d','r','a','c'), CODEC_TYPE_VIDEO, CODEC_ID_DIRAC }, + { MKTAG('A','C','-','3'), CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, + { 0 }, +}; + +/* descriptor present */ +static const StreamType DESC_types[] = { + { 0x6a, CODEC_TYPE_AUDIO, CODEC_ID_AC3 }, /* AC-3 descriptor */ + { 0x7a, CODEC_TYPE_AUDIO, CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ + { 0x7b, CODEC_TYPE_AUDIO, CODEC_ID_DTS }, + { 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ +}; + +static void mpegts_find_stream_type(AVStream *st, + uint32_t stream_type, const StreamType *types) +{ + for (; types->stream_type; types++) { + if (stream_type == types->stream_type) { + st->codec->codec_type = types->codec_type; + st->codec->codec_id = types->codec_id; + return; + } } - service->sid = sid; - assert((!provider_name) == (!name)); - if(name){ - av_free(service->provider_name); - av_free(service-> name); - service->provider_name = provider_name; - service-> name = name; +} + +static AVStream *new_pes_av_stream(PESContext *pes, uint32_t prog_reg_desc, uint32_t code) +{ + AVStream *st = av_new_stream(pes->stream, pes->pid); + + if (!st) + return NULL; + + av_set_pts_info(st, 33, 1, 90000); + st->priv_data = pes; + st->codec->codec_type = CODEC_TYPE_DATA; + st->codec->codec_id = CODEC_ID_PROBE; + st->need_parsing = AVSTREAM_PARSE_FULL; + pes->st = st; + + dprintf(pes->stream, "stream_type=%x pid=%x prog_reg_desc=%.4s\n", + pes->stream_type, pes->pid, (char*)&prog_reg_desc); + + st->codec->codec_tag = pes->stream_type; + + mpegts_find_stream_type(st, pes->stream_type, ISO_types); + if (prog_reg_desc == AV_RL32("HDMV") && + st->codec->codec_id == CODEC_ID_PROBE) + mpegts_find_stream_type(st, pes->stream_type, HDMV_types); + if (st->codec->codec_id == CODEC_ID_PROBE) + mpegts_find_stream_type(st, pes->stream_type, MISC_types); + + /* stream was not present in PMT, guess based on PES start code */ + if (st->codec->codec_id == CODEC_ID_PROBE) { + if (code >= 0x1c0 && code <= 0x1df) { + st->codec->codec_type = CODEC_TYPE_AUDIO; + st->codec->codec_id = CODEC_ID_MP2; + } else if (code == 0x1bd) { + st->codec->codec_type = CODEC_TYPE_AUDIO; + st->codec->codec_id = CODEC_ID_AC3; + } } - return service; + + return st; } static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) @@ -426,38 +591,55 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len const uint8_t *p, *p_end, *desc_list_end, *desc_end; int program_info_length, pcr_pid, pid, stream_type; int desc_list_len, desc_len, desc_tag; - int comp_page = 0, anc_page = 0; /* initialize to kill warnings */ - char language[4] = {0}; /* initialize to kill warnings */ + int comp_page, anc_page; + char language[4]; + uint32_t prog_reg_desc = 0; /* registration descriptor */ -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "PMT: len %i\n", section_len); +#ifdef DEBUG + dprintf(ts->stream, "PMT: len %i\n", section_len); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif + p_end = section + section_len - 4; p = section; if (parse_section_header(h, &p, p_end) < 0) return; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "sid=0x%x sec_num=%d/%d\n", + + dprintf(ts->stream, "sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num); -#endif + if (h->tid != PMT_TID) return; + clear_program(ts, h->id); pcr_pid = get16(&p, p_end) & 0x1fff; if (pcr_pid < 0) return; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "pcr_pid=0x%x\n", pcr_pid); -#endif + add_pid_to_pmt(ts, h->id, pcr_pid); + + dprintf(ts->stream, "pcr_pid=0x%x\n", pcr_pid); + program_info_length = get16(&p, p_end) & 0xfff; if (program_info_length < 0) return; + while(program_info_length >= 2) { + uint8_t tag, len; + tag = get8(&p, p_end); + len = get8(&p, p_end); + if(len > program_info_length - 2) + //something else is broken, exit the program_descriptors_loop + break; + program_info_length -= len + 2; + if(tag == 0x05 && len >= 4) { // registration descriptor + prog_reg_desc = bytestream_get_le32(&p); + len -= 4; + } + p += len; + } p += program_info_length; if (p >= p_end) return; for(;;) { - language[0] = 0; st = 0; stream_type = get8(&p, p_end); if (stream_type < 0) @@ -465,6 +647,25 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len pid = get16(&p, p_end) & 0x1fff; if (pid < 0) break; + + /* now create ffmpeg stream */ + if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { + pes = ts->pids[pid]->u.pes_filter.opaque; + st = pes->st; + } else { + if (ts->pids[pid]) mpegts_close_filter(ts, ts->pids[pid]); //wrongly added sdt filter probably + pes = add_pes_stream(ts, pid, pcr_pid, stream_type); + if (pes) + st = new_pes_av_stream(pes, prog_reg_desc, 0); + } + + if (!st) + return; + + add_pid_to_pmt(ts, h->id, pid); + + av_program_add_stream_index(ts->stream, h->id, st->index); + desc_list_len = get16(&p, p_end) & 0xfff; if (desc_list_len < 0) break; @@ -475,28 +676,22 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len desc_tag = get8(&p, desc_list_end); if (desc_tag < 0) break; - if (stream_type == STREAM_TYPE_PRIVATE_DATA) { - if((desc_tag == 0x6A) || (desc_tag == 0x7A)) { - /*assume DVB AC-3 Audio*/ - stream_type = STREAM_TYPE_AUDIO_AC3; - } else if(desc_tag == 0x7B) { - /* DVB DTS audio */ - stream_type = STREAM_TYPE_AUDIO_DTS; - } - } desc_len = get8(&p, desc_list_end); + if (desc_len < 0) + break; desc_end = p + desc_len; if (desc_end > desc_list_end) break; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n", + + dprintf(ts->stream, "tag: 0x%02x len=%d\n", desc_tag, desc_len); -#endif - switch(desc_tag) { - case DVB_SUBT_DESCID: - if (stream_type == STREAM_TYPE_PRIVATE_DATA) - stream_type = STREAM_TYPE_SUBTITLE_DVB; + if (st->codec->codec_id == CODEC_ID_PROBE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type(st, desc_tag, DESC_types); + + switch(desc_tag) { + case 0x59: /* subtitling descriptor */ language[0] = get8(&p, desc_end); language[1] = get8(&p, desc_end); language[2] = get8(&p, desc_end); @@ -504,13 +699,22 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len get8(&p, desc_end); comp_page = get16(&p, desc_end); anc_page = get16(&p, desc_end); - + st->codec->sub_id = (anc_page << 16) | comp_page; + av_metadata_set(&st->metadata, "language", language); break; case 0x0a: /* ISO 639 language descriptor */ language[0] = get8(&p, desc_end); language[1] = get8(&p, desc_end); language[2] = get8(&p, desc_end); language[3] = 0; + av_metadata_set(&st->metadata, "language", language); + break; + case 0x05: /* registration descriptor */ + st->codec->codec_tag = bytestream_get_le32(&p); + dprintf(ts->stream, "reg_desc=%.4s\n", (char*)&st->codec->codec_tag); + if (st->codec->codec_id == CODEC_ID_PROBE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types); break; default: break; @@ -518,49 +722,6 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len p = desc_end; } p = desc_list_end; - -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "stream_type=%d pid=0x%x\n", - stream_type, pid); -#endif - - /* now create ffmpeg stream */ - switch(stream_type) { - case STREAM_TYPE_AUDIO_MPEG1: - case STREAM_TYPE_AUDIO_MPEG2: - case STREAM_TYPE_VIDEO_MPEG1: - case STREAM_TYPE_VIDEO_MPEG2: - case STREAM_TYPE_VIDEO_MPEG4: - case STREAM_TYPE_VIDEO_H264: - case STREAM_TYPE_VIDEO_VC1: - case STREAM_TYPE_AUDIO_AAC: - case STREAM_TYPE_AUDIO_AC3: - case STREAM_TYPE_AUDIO_DTS: - case STREAM_TYPE_SUBTITLE_DVB: - if(ts->pids[pid]){ - assert(ts->pids[pid].type == MPEGTS_PES); - pes= ts->pids[pid]->u.pes_filter.opaque; - st= pes->st; - }else{ - pes = add_pes_stream(ts, pid, pcr_pid, stream_type); - if (pes) - st = new_pes_av_stream(pes, 0); - } - break; - default: - /* we ignore the other streams */ - break; - } - - if (st) { - if (language[0] != 0) { - memcpy(st->language, language, 4); - } - - if (stream_type == STREAM_TYPE_SUBTITLE_DVB) { - st->codec->sub_id = (anc_page << 16) | comp_page; - } - } } /* all parameters are there */ ts->stop_parse++; @@ -574,8 +735,8 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len const uint8_t *p, *p_end; int sid, pmt_pid; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "PAT:\n"); +#ifdef DEBUG + dprintf(ts->stream, "PAT:\n"); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif p_end = section + section_len - 4; @@ -585,6 +746,7 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (h->tid != PAT_TID) return; + clear_programs(ts); for(;;) { sid = get16(&p, p_end); if (sid < 0) @@ -592,15 +754,18 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len pmt_pid = get16(&p, p_end) & 0x1fff; if (pmt_pid < 0) break; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "sid=0x%x pid=0x%x\n", sid, pmt_pid); -#endif + + dprintf(ts->stream, "sid=0x%x pid=0x%x\n", sid, pmt_pid); + if (sid == 0x0000) { /* NIT info */ } else { - new_service(ts, sid, NULL, NULL); + av_new_program(ts->stream, sid); ts->stop_parse--; mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); + add_pat_entry(ts, sid); + add_pid_to_pmt(ts, sid, 0); //add pat pid to program + add_pid_to_pmt(ts, sid, pmt_pid); } } /* not found */ @@ -609,12 +774,6 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len mpegts_close_filter(ts, filter); } -static void mpegts_set_service(MpegTSContext *ts) -{ - mpegts_open_section_filter(ts, PAT_PID, - pat_cb, ts, 1); -} - static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) { MpegTSContext *ts = filter->u.section_filter.opaque; @@ -623,8 +782,8 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len int onid, val, sid, desc_list_len, desc_tag, desc_len, service_type; char *name, *provider_name; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "SDT:\n"); +#ifdef DEBUG + dprintf(ts->stream, "SDT:\n"); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (uint8_t *)section, section_len); #endif @@ -661,10 +820,10 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len desc_end = p + desc_len; if (desc_end > desc_list_end) break; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n", + + dprintf(ts->stream, "tag: 0x%02x len=%d\n", desc_tag, desc_len); -#endif + switch(desc_tag) { case 0x48: service_type = get8(&p, p_end); @@ -674,9 +833,15 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (!provider_name) break; name = getstr8(&p, p_end); - if (!name) - break; - new_service(ts, sid, provider_name, name); + if (name) { + AVProgram *program = av_new_program(ts->stream, sid); + if(program) { + av_metadata_set(&program->metadata, "name", name); + av_metadata_set(&program->metadata, "provider_name", provider_name); + } + } + av_free(name); + av_free(provider_name); break; default: break; @@ -687,13 +852,6 @@ static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len } } -/* scan services in a transport stream by looking at the SDT */ -static void mpegts_scan_sdt(MpegTSContext *ts) -{ - mpegts_open_section_filter(ts, SDT_PID, - sdt_cb, ts, 1); -} - static int64_t get_pts(const uint8_t *p) { int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30; @@ -702,9 +860,32 @@ static int64_t get_pts(const uint8_t *p) return pts; } +static void new_pes_packet(PESContext *pes, AVPacket *pkt) +{ + av_init_packet(pkt); + + pkt->destruct = av_destruct_packet; + pkt->data = pes->buffer; + pkt->size = pes->data_index; + memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + pkt->stream_index = pes->st->index; + pkt->pts = pes->pts; + pkt->dts = pes->dts; + /* store position of first TS packet of this PES packet */ + pkt->pos = pes->ts_packet_pos; + + /* reset pts values */ + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + pes->buffer = NULL; + pes->data_index = 0; +} + /* return non zero if a packet could be constructed */ -static void mpegts_push_data(MpegTSFilter *filter, - const uint8_t *buf, int buf_size, int is_start) +static int mpegts_push_data(MpegTSFilter *filter, + const uint8_t *buf, int buf_size, int is_start, + int64_t pos) { PESContext *pes = filter->u.pes_filter.opaque; MpegTSContext *ts = pes->ts; @@ -712,11 +893,16 @@ static void mpegts_push_data(MpegTSFilter *filter, int len, code; if(!ts->pkt) - return; + return 0; if (is_start) { + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, ts->pkt); + ts->stop_parse = 1; + } pes->state = MPEGTS_HEADER; pes->data_index = 0; + pes->ts_packet_pos = pos; } p = buf; while (buf_size > 0) { @@ -739,21 +925,39 @@ static void mpegts_push_data(MpegTSFilter *filter, pes->header[2] == 0x01) { /* it must be an mpeg2 PES stream */ code = pes->header[3] | 0x100; - if (!((code >= 0x1c0 && code <= 0x1df) || - (code >= 0x1e0 && code <= 0x1ef) || - (code == 0x1bd) || (code == 0x1fd))) + dprintf(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code); + + if ((!pes->st && pes->stream->nb_streams == MAX_STREAMS) || + (pes->st && pes->st->discard == AVDISCARD_ALL) || + code == 0x1be) /* padding_stream */ goto skip; - if (!pes->st) { - /* allocate stream */ - new_pes_av_stream(pes, code); - } - pes->state = MPEGTS_PESHEADER_FILL; + + /* stream not present in PMT */ + if (!pes->st) + pes->st = new_pes_av_stream(pes, 0, code); + if (!pes->st) + return AVERROR(ENOMEM); + pes->total_size = AV_RB16(pes->header + 4); /* NOTE: a zero total size means the PES size is unbounded */ - if (pes->total_size) - pes->total_size += 6; - pes->pes_header_size = pes->header[8] + 9; + if (!pes->total_size) + pes->total_size = MAX_PES_PAYLOAD; + + /* allocate pes buffer */ + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + + if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */ + code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */ + code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ + code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ + pes->state = MPEGTS_PESHEADER; + } else { + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + } } else { /* otherwise, it should be a table */ /* skip packet */ @@ -765,8 +969,25 @@ static void mpegts_push_data(MpegTSFilter *filter, break; /**********************************************/ /* PES packing parsing */ + case MPEGTS_PESHEADER: + len = PES_HEADER_SIZE - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_HEADER_SIZE) { + pes->pes_header_size = pes->header[8] + 9; + pes->state = MPEGTS_PESHEADER_FILL; + } + break; case MPEGTS_PESHEADER_FILL: len = pes->pes_header_size - pes->data_index; + if (len < 0) + return -1; if (len > buf_size) len = buf_size; memcpy(pes->header + pes->data_index, p, len); @@ -782,7 +1003,7 @@ static void mpegts_push_data(MpegTSFilter *filter, pes->pts = AV_NOPTS_VALUE; pes->dts = AV_NOPTS_VALUE; if ((flags & 0xc0) == 0x80) { - pes->pts = get_pts(r); + pes->dts = pes->pts = get_pts(r); r += 5; } else if ((flags & 0xc0) == 0xc0) { pes->pts = get_pts(r); @@ -790,31 +1011,24 @@ static void mpegts_push_data(MpegTSFilter *filter, pes->dts = get_pts(r); r += 5; } + /* we got the full header. We parse it and get the payload */ pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; } break; case MPEGTS_PAYLOAD: - if (pes->total_size) { - len = pes->total_size - pes->data_index; - if (len > buf_size) - len = buf_size; - } else { - len = buf_size; - } - if (len > 0) { - AVPacket *pkt = ts->pkt; - if (pes->st && av_new_packet(pkt, len) == 0) { - memcpy(pkt->data, p, len); - pkt->stream_index = pes->st->index; - pkt->pts = pes->pts; - pkt->dts = pes->dts; - /* reset pts values */ - pes->pts = AV_NOPTS_VALUE; - pes->dts = AV_NOPTS_VALUE; + if (buf_size > 0) { + if (pes->data_index+buf_size > pes->total_size) { + new_pes_packet(pes, ts->pkt); + pes->total_size = MAX_PES_PAYLOAD; + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); ts->stop_parse = 1; - return; } + memcpy(pes->buffer+pes->data_index, p, buf_size); + pes->data_index += buf_size; } buf_size = 0; break; @@ -823,78 +1037,10 @@ static void mpegts_push_data(MpegTSFilter *filter, break; } } -} -static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code) -{ - AVStream *st; - int codec_type, codec_id; - - switch(pes->stream_type){ - case STREAM_TYPE_AUDIO_MPEG1: - case STREAM_TYPE_AUDIO_MPEG2: - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_MP3; - break; - case STREAM_TYPE_VIDEO_MPEG1: - case STREAM_TYPE_VIDEO_MPEG2: - codec_type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_MPEG2VIDEO; - break; - case STREAM_TYPE_VIDEO_MPEG4: - codec_type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_MPEG4; - break; - case STREAM_TYPE_VIDEO_H264: - codec_type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_H264; - break; - case STREAM_TYPE_VIDEO_VC1: - codec_type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_VC1; - break; - case STREAM_TYPE_AUDIO_AAC: - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_AAC; - break; - case STREAM_TYPE_AUDIO_AC3: - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_AC3; - break; - case STREAM_TYPE_AUDIO_DTS: - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_DTS; - break; - case STREAM_TYPE_SUBTITLE_DVB: - codec_type = CODEC_TYPE_SUBTITLE; - codec_id = CODEC_ID_DVB_SUBTITLE; - break; - default: - if (code >= 0x1c0 && code <= 0x1df) { - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_MP2; - } else if (code == 0x1bd) { - codec_type = CODEC_TYPE_AUDIO; - codec_id = CODEC_ID_AC3; - } else { - codec_type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_MPEG1VIDEO; - } - break; - } - st = av_new_stream(pes->stream, pes->pid); - if (st) { - av_set_pts_info(st, 33, 1, 90000); - st->priv_data = pes; - st->codec->codec_type = codec_type; - st->codec->codec_id = codec_id; - st->need_parsing = AVSTREAM_PARSE_FULL; - pes->st = st; - } - return st; + return 0; } - static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type) { MpegTSFilter *tss; @@ -909,6 +1055,7 @@ static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int s pes->pid = pid; pes->pcr_pid = pcr_pid; pes->stream_type = stream_type; + pes->state = MPEGTS_SKIP; tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); if (!tss) { av_free(pes); @@ -918,14 +1065,17 @@ static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int s } /* handle one TS packet */ -static void handle_packet(MpegTSContext *ts, const uint8_t *packet) +static int handle_packet(MpegTSContext *ts, const uint8_t *packet) { AVFormatContext *s = ts->stream; MpegTSFilter *tss; int len, pid, cc, cc_ok, afc, is_start; const uint8_t *p, *p_end; + int64_t pos; pid = AV_RB16(packet + 1) & 0x1fff; + if(pid && discard_pid(ts, pid)) + return 0; is_start = packet[1] & 0x40; tss = ts->pids[pid]; if (ts->auto_guess && tss == NULL && is_start) { @@ -933,7 +1083,7 @@ static void handle_packet(MpegTSContext *ts, const uint8_t *packet) tss = ts->pids[pid]; } if (!tss) - return; + return 0; /* continuity check (currently not used) */ cc = (packet[3] & 0xf); @@ -944,9 +1094,9 @@ static void handle_packet(MpegTSContext *ts, const uint8_t *packet) afc = (packet[3] >> 4) & 3; p = packet + 4; if (afc == 0) /* reserved value */ - return; + return 0; if (afc == 2) /* adaptation field only */ - return; + return 0; if (afc == 3) { /* skip adapation field */ p += p[0] + 1; @@ -954,21 +1104,24 @@ static void handle_packet(MpegTSContext *ts, const uint8_t *packet) /* if past the end of packet, ignore */ p_end = packet + TS_PACKET_SIZE; if (p >= p_end) - return; + return 0; + + pos = url_ftell(ts->stream->pb); + ts->pos47= pos % ts->raw_packet_size; if (tss->type == MPEGTS_SECTION) { if (is_start) { /* pointer field present */ len = *p++; if (p + len > p_end) - return; + return 0; if (len && cc_ok) { /* write remaining section bytes */ write_section_data(s, tss, p, len, 0); /* check whether filter has been closed */ if (!ts->pids[pid]) - return; + return 0; } p += len; if (p < p_end) { @@ -982,9 +1135,14 @@ static void handle_packet(MpegTSContext *ts, const uint8_t *packet) } } } else { - tss->u.pes_filter.pes_cb(tss, - p, p_end - p, is_start); + int ret; + // Note: The position here points actually behind the current packet. + if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, + pos - ts->raw_packet_size)) < 0) + return ret; } + + return 0; } /* XXX: try to find a better synchro over several packets (use @@ -1014,7 +1172,7 @@ static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size) for(;;) { len = get_buffer(pb, buf, TS_PACKET_SIZE); if (len != TS_PACKET_SIZE) - return AVERROR_IO; + return AVERROR(EIO); /* check paquet sync byte */ if (buf[0] != 0x47) { /* find a new packet start */ @@ -1036,7 +1194,7 @@ static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size) static int handle_packets(MpegTSContext *ts, int nb_packets) { AVFormatContext *s = ts->stream; - ByteIOContext *pb = &s->pb; + ByteIOContext *pb = s->pb; uint8_t packet[TS_PACKET_SIZE]; int packet_num, ret; @@ -1051,7 +1209,9 @@ static int handle_packets(MpegTSContext *ts, int nb_packets) ret = read_packet(pb, packet, ts->raw_packet_size); if (ret != 0) return ret; - handle_packet(ts, packet); + ret = handle_packet(ts, packet); + if (ret != 0) + return ret; } return 0; } @@ -1061,14 +1221,15 @@ static int mpegts_probe(AVProbeData *p) #if 1 const int size= p->buf_size; int score, fec_score, dvhs_score; + int check_count= size / TS_FEC_PACKET_SIZE; #define CHECK_COUNT 10 - if (size < (TS_FEC_PACKET_SIZE * CHECK_COUNT)) + if (check_count < CHECK_COUNT) return -1; - score = analyze(p->buf, TS_PACKET_SIZE *CHECK_COUNT, TS_PACKET_SIZE, NULL); - dvhs_score = analyze(p->buf, TS_DVHS_PACKET_SIZE *CHECK_COUNT, TS_DVHS_PACKET_SIZE, NULL); - fec_score= analyze(p->buf, TS_FEC_PACKET_SIZE*CHECK_COUNT, TS_FEC_PACKET_SIZE, NULL); + score = analyze(p->buf, TS_PACKET_SIZE *check_count, TS_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; + dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)*CHECK_COUNT/check_count; + fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)*CHECK_COUNT/check_count; // av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score); // we need a clear definition for the returned score otherwise things will become messy sooner or later @@ -1085,7 +1246,7 @@ static int mpegts_probe(AVProbeData *p) #endif } -/* return the 90 kHz PCR and the extension for the 27 MHz PCR. return +/* return the 90kHz PCR and the extension for the 27MHz PCR. return (-1) if not available */ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet) @@ -1118,8 +1279,8 @@ static int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap) { MpegTSContext *ts = s->priv_data; - ByteIOContext *pb = &s->pb; - uint8_t buf[1024]; + ByteIOContext *pb = s->pb; + uint8_t buf[5*1024]; int len; int64_t pos; @@ -1147,18 +1308,18 @@ static int mpegts_read_header(AVFormatContext *s, /* first do a scaning to get all the services */ url_fseek(pb, pos, SEEK_SET); - mpegts_scan_sdt(ts); - mpegts_set_service(ts); + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); handle_packets(ts, s->probesize); /* if could not find service, enable auto_guess */ ts->auto_guess = 1; -#ifdef DEBUG_SI - av_log(ts->stream, AV_LOG_DEBUG, "tuning done\n"); -#endif + dprintf(ts->stream, "tuning done\n"); + s->ctx_flags |= AVFMTCTX_NOHEADER; } else { AVStream *st; @@ -1181,7 +1342,7 @@ static int mpegts_read_header(AVFormatContext *s, nb_pcrs = 0; nb_packets = 0; for(;;) { - ret = read_packet(&s->pb, packet, ts->raw_packet_size); + ret = read_packet(s->pb, packet, ts->raw_packet_size); if (ret < 0) return -1; pid = AV_RB16(packet + 1) & 0x1fff; @@ -1229,8 +1390,8 @@ static int mpegts_raw_read_packet(AVFormatContext *s, if (av_new_packet(pkt, TS_PACKET_SIZE) < 0) return AVERROR(ENOMEM); - pkt->pos= url_ftell(&s->pb); - ret = read_packet(&s->pb, pkt->data, ts->raw_packet_size); + pkt->pos= url_ftell(s->pb); + ret = read_packet(s->pb, pkt->data, ts->raw_packet_size); if (ret < 0) { av_free_packet(pkt); return ret; @@ -1239,10 +1400,10 @@ static int mpegts_raw_read_packet(AVFormatContext *s, /* compute exact PCR for each packet */ if (parse_pcr(&pcr_h, &pcr_l, pkt->data) == 0) { /* we read the next PCR (XXX: optimize it by using a bigger buffer */ - pos = url_ftell(&s->pb); + pos = url_ftell(s->pb); for(i = 0; i < MAX_PACKET_READAHEAD; i++) { - url_fseek(&s->pb, pos + i * ts->raw_packet_size, SEEK_SET); - get_buffer(&s->pb, pcr_buf, 12); + url_fseek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET); + get_buffer(s->pb, pcr_buf, 12); if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) { /* XXX: not precise enough */ ts->pcr_incr = ((next_pcr_h - pcr_h) * 300 + (next_pcr_l - pcr_l)) / @@ -1250,7 +1411,7 @@ static int mpegts_raw_read_packet(AVFormatContext *s, break; } } - url_fseek(&s->pb, pos, SEEK_SET); + url_fseek(s->pb, pos, SEEK_SET); /* no next PCR found: we use previous increment */ ts->cur_pcr = pcr_h * 300 + pcr_l; } @@ -1266,25 +1427,51 @@ static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt) { MpegTSContext *ts = s->priv_data; + int ret, i; + + if (url_ftell(s->pb) != ts->last_pos) { + /* seek detected, flush pes buffer */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + av_freep(&pes->buffer); + pes->data_index = 0; + pes->state = MPEGTS_SKIP; /* skip until pes header */ + } + } + } ts->pkt = pkt; - return handle_packets(ts, 0); + ret = handle_packets(ts, 0); + if (ret < 0) { + /* flush pes data left */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, pkt); + ret = 0; + break; + } + } + } + } + + ts->last_pos = url_ftell(s->pb); + + return ret; } static int mpegts_read_close(AVFormatContext *s) { MpegTSContext *ts = s->priv_data; int i; + + clear_programs(ts); + for(i=0;ipids[i]) mpegts_close_filter(ts, ts->pids[i]); - for(i = 0; i < ts->nb_services; i++){ - av_free(ts->services[i]->provider_name); - av_free(ts->services[i]->name); - av_free(ts->services[i]); - } - av_freep(&ts->services); - return 0; } @@ -1296,11 +1483,11 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, uint8_t buf[TS_PACKET_SIZE]; int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; const int find_next= 1; - pos = ((*ppos + ts->raw_packet_size - 1) / ts->raw_packet_size) * ts->raw_packet_size; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; if (find_next) { for(;;) { - url_fseek(&s->pb, pos, SEEK_SET); - if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + url_fseek(s->pb, pos, SEEK_SET); + if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) return AV_NOPTS_VALUE; if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && parse_pcr(×tamp, &pcr_l, buf) == 0) { @@ -1313,8 +1500,8 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, pos -= ts->raw_packet_size; if (pos < 0) return AV_NOPTS_VALUE; - url_fseek(&s->pb, pos, SEEK_SET); - if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + url_fseek(s->pb, pos, SEEK_SET); + if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) return AV_NOPTS_VALUE; if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && parse_pcr(×tamp, &pcr_l, buf) == 0) { @@ -1335,17 +1522,17 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, in if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0) return -1; - pos= url_ftell(&s->pb); + pos= url_ftell(s->pb); for(;;) { - url_fseek(&s->pb, pos, SEEK_SET); - if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + url_fseek(s->pb, pos, SEEK_SET); + if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) return -1; // pid = AV_RB16(buf + 1) & 0x1fff; if(buf[1] & 0x40) break; pos += ts->raw_packet_size; } - url_fseek(&s->pb, pos, SEEK_SET); + url_fseek(s->pb, pos, SEEK_SET); return 0; } @@ -1405,7 +1592,7 @@ void mpegts_parse_close(MpegTSContext *ts) AVInputFormat mpegts_demuxer = { "mpegts", - "MPEG2 transport stream format", + NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"), sizeof(MpegTSContext), mpegts_probe, mpegts_read_header, @@ -1413,18 +1600,18 @@ AVInputFormat mpegts_demuxer = { mpegts_read_close, read_seek, mpegts_get_pcr, - .flags = AVFMT_SHOW_IDS, + .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, }; AVInputFormat mpegtsraw_demuxer = { "mpegtsraw", - "MPEG2 raw transport stream format", + NULL_IF_CONFIG_SMALL("MPEG-2 raw transport stream format"), sizeof(MpegTSContext), - mpegts_probe, + NULL, mpegts_read_header, mpegts_raw_read_packet, mpegts_read_close, read_seek, mpegts_get_pcr, - .flags = AVFMT_SHOW_IDS, + .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, };