Skip to content
Snippets Groups Projects
mpegts.c 53.2 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * MPEG2 transport stream (aka DVB) demuxer
    
     * Copyright (c) 2002-2003 Fabrice Bellard
    
     * This file is part of FFmpeg.
     *
     * FFmpeg is free software; you can redistribute it and/or
    
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
    
     * version 2.1 of the License, or (at your option) any later version.
    
     * FFmpeg 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
     * Lesser General Public License for more details.
    
     * You should have received a copy of the GNU Lesser General Public
    
     * License along with FFmpeg; if not, write to the Free Software
    
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    
    //#define DEBUG
    //#define DEBUG_SEEK
    
    #include "libavutil/crc.h"
    
    #include "libavutil/intreadwrite.h"
    
    #define MAX_SCAN_PACKETS 32000
    
    /* maximum size in which we look for synchronisation if
       synchronisation is lost */
    
    #define MAX_PES_PAYLOAD 200*1024
    
    
    typedef struct MpegTSFilter MpegTSFilter;
    
    
    typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos);
    
    typedef struct MpegTSPESFilter {
        PESCallback *pes_cb;
        void *opaque;
    } MpegTSPESFilter;
    
    
    typedef void SectionCallback(MpegTSFilter *f, const uint8_t *buf, int len);
    
    
    typedef void SetServiceCallback(void *opaque, int ret);
    
    typedef struct MpegTSSectionFilter {
        int section_index;
        int section_h_size;
        uint8_t *section_buf;
    
        unsigned int check_crc:1;
        unsigned int end_of_section_reached:1;
    
        SectionCallback *section_cb;
        void *opaque;
    } MpegTSSectionFilter;
    
    
        int pid;
        int last_cc; /* last cc code (-1 if first packet) */
    
        enum MpegTSFilterType type;
        union {
            MpegTSPESFilter pes_filter;
            MpegTSSectionFilter section_filter;
        } u;
    
        unsigned int id; //program id/service id
        unsigned int nb_pids;
        unsigned int pids[MAX_PIDS_PER_PROGRAM];
    
    typedef struct MpegTSContext {
    
        /** raw packet size, including FEC if present            */
        int raw_packet_size;
    
        /** if true, all pids are analyzed to find streams       */
        int auto_guess;
    
        /** compute exact PCR for each transport stream packet   */
        int mpeg2ts_compute_pcr;
    
        int64_t cur_pcr;    /**< used to estimate the exact PCR  */
        int pcr_incr;       /**< used to estimate the exact PCR  */
    
        /** stop parsing loop                                    */
        int stop_parse;
        /** packet containing Audio/Video data                   */
        AVPacket *pkt;
    
        /** to detect seek                                       */
        int64_t last_pos;
    
    
        /******************************************/
        /* private mpegts data */
        /* scan context */
    
        /** structure to keep track of Program->pids mapping     */
        unsigned int nb_prg;
    
        /** filters for various streams specified by PMT + for the PAT and PMT */
    
    } MpegTSContext;
    
    /* TS stream handling */
    
    enum MpegTSState {
        MPEGTS_HEADER = 0,
    
        MPEGTS_PESHEADER_FILL,
        MPEGTS_PAYLOAD,
        MPEGTS_SKIP,
    };
    
    /* enough for PES header + length */
    
    #define PES_START_SIZE  6
    #define PES_HEADER_SIZE 9
    
    #define MAX_PES_HEADER_SIZE (9 + 255)
    
    
    typedef struct PESContext {
    
        int pcr_pid; /**< if -1 then all packets containing PCR are considered */
    
        int stream_type;
        MpegTSContext *ts;
        AVFormatContext *stream;
        AVStream *st;
    
        AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */
    
        enum MpegTSState state;
        /* used to get the format */
        int data_index;
        int total_size;
        int pes_header_size;
    
        int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */
    
        uint8_t header[MAX_PES_HEADER_SIZE];
    
        uint8_t *buffer;
    
    } PESContext;
    
    extern AVInputFormat mpegts_demuxer;
    
    
    static void clear_program(MpegTSContext *ts, unsigned int programid)
    {
        int i;
    
        for(i=0; i<ts->nb_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; i<ts->nb_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;
    
        for(i=0; i<ts->nb_prg; i++) {
            p = &ts->prg[i];
            for(j=0; j<p->nb_pids; j++) {
                if(p->pids[j] != pid)
                    continue;
                //is program with id p->id set to be discarded?
                for(k=0; k<ts->stream->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.
     */
    
    static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1,
                                   const uint8_t *buf, int buf_size, int is_start)
    {
        MpegTSSectionFilter *tss = &tss1->u.section_filter;
        int len;
    
        if (is_start) {
            memcpy(tss->section_buf, buf, buf_size);
            tss->section_index = buf_size;
            tss->section_h_size = -1;
            tss->end_of_section_reached = 0;
        } else {
            if (tss->end_of_section_reached)
                return;
            len = 4096 - tss->section_index;
            if (buf_size < len)
                len = buf_size;
            memcpy(tss->section_buf + tss->section_index, buf, len);
            tss->section_index += len;
        }
    
        /* compute section length if possible */
        if (tss->section_h_size == -1 && tss->section_index >= 3) {
    
            len = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3;
    
            if (len > 4096)
                return;
            tss->section_h_size = len;
        }
    
        if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) {
            tss->end_of_section_reached = 1;
    
    Aurelien Jacobs's avatar
    Aurelien Jacobs committed
                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);
    
    static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid,
    
                                             SectionCallback *section_cb, void *opaque,
                                             int check_crc)
    
    {
        MpegTSFilter *filter;
        MpegTSSectionFilter *sec;
    
        dprintf(ts->stream, "Filter: pid=0x%x\n", pid);
    
    
        if (pid >= NB_PID_MAX || ts->pids[pid])
            return NULL;
        filter = av_mallocz(sizeof(MpegTSFilter));
    
            return NULL;
        ts->pids[pid] = filter;
        filter->type = MPEGTS_SECTION;
        filter->pid = pid;
        filter->last_cc = -1;
        sec = &filter->u.section_filter;
        sec->section_cb = section_cb;
        sec->opaque = opaque;
        sec->section_buf = av_malloc(MAX_SECTION_SIZE);
        sec->check_crc = check_crc;
        if (!sec->section_buf) {
            av_free(filter);
            return NULL;
        }
        return filter;
    }
    
    
    static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid,
    
                                         PESCallback *pes_cb,
                                         void *opaque)
    {
        MpegTSFilter *filter;
        MpegTSPESFilter *pes;
    
        if (pid >= NB_PID_MAX || ts->pids[pid])
            return NULL;
        filter = av_mallocz(sizeof(MpegTSFilter));
    
            return NULL;
        ts->pids[pid] = filter;
        filter->type = MPEGTS_PES;
        filter->pid = pid;
        filter->last_cc = -1;
        pes = &filter->u.pes_filter;
        pes->pes_cb = pes_cb;
        pes->opaque = opaque;
        return filter;
    }
    
    
    static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
    
    {
        int pid;
    
        pid = filter->pid;
        if (filter->type == MPEGTS_SECTION)
            av_freep(&filter->u.section_filter.section_buf);
    
        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);
            }
        }
    
    static int analyze(const uint8_t *buf, int size, int packet_size, int *index){
    
        int stat[TS_MAX_PACKET_SIZE];
    
        int i;
        int x=0;
        int best_score=0;
    
        memset(stat, 0, packet_size*sizeof(int));
    
    
        for(x=i=0; i<size-3; i++){
            if(buf[i] == 0x47 && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30)){
    
                stat[x]++;
                if(stat[x] > best_score){
                    best_score= stat[x];
                    if(index) *index= x;
                }
            }
    
            x++;
            if(x == packet_size) x= 0;
        }
    
        return best_score;
    }
    
    
    /* autodetect fec presence. Must have at least 1024 bytes  */
    
    static int get_packet_size(const uint8_t *buf, int size)
    
        int score, fec_score, dvhs_score;
    
    
        if (size < (TS_FEC_PACKET_SIZE * 5 + 1))
            return -1;
    
        score    = analyze(buf, size, TS_PACKET_SIZE, NULL);
    
        dvhs_score    = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL);
    
        fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL);
    
    //    av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
    
        if     (score > fec_score && score > dvhs_score) return TS_PACKET_SIZE;
        else if(dvhs_score > score && dvhs_score > fec_score) return TS_DVHS_PACKET_SIZE;
        else if(score < fec_score && dvhs_score < fec_score) return TS_FEC_PACKET_SIZE;
    
        else                       return -1;
    
    typedef struct SectionHeader {
        uint8_t tid;
        uint16_t id;
        uint8_t version;
        uint8_t sec_num;
        uint8_t last_sec_num;
    } SectionHeader;
    
    static inline int get8(const uint8_t **pp, const uint8_t *p_end)
    
        const uint8_t *p;
        int c;
    
        p = *pp;
        if (p >= p_end)
            return -1;
        c = *p++;
        *pp = p;
        return c;
    
    static inline int get16(const uint8_t **pp, const uint8_t *p_end)
    {
        const uint8_t *p;
        int c;
    
        p = *pp;
        if ((p + 1) >= p_end)
            return -1;
    
        c = AV_RB16(p);
    
        p += 2;
        *pp = p;
        return c;
    }
    
    /* read and allocate a DVB string preceeded by its length */
    static char *getstr8(const uint8_t **pp, const uint8_t *p_end)
    
        p = *pp;
        len = get8(&p, p_end);
        if (len < 0)
            return NULL;
        if ((p + len) > p_end)
            return NULL;
        str = av_malloc(len + 1);
        if (!str)
            return NULL;
        memcpy(str, p, len);
        str[len] = '\0';
        p += len;
        *pp = p;
        return str;
    }
    
    
    static int parse_section_header(SectionHeader *h,
    
                                    const uint8_t **pp, const uint8_t *p_end)
    {
        int val;
    
        val = get8(pp, p_end);
        if (val < 0)
            return -1;
        h->tid = val;
        *pp += 2;
        val = get16(pp, p_end);
        if (val < 0)
            return -1;
        h->id = val;
        val = get8(pp, p_end);
        if (val < 0)
            return -1;
        h->version = (val >> 1) & 0x1f;
        val = get8(pp, p_end);
        if (val < 0)
            return -1;
        h->sec_num = val;
        val = get8(pp, p_end);
        if (val < 0)
            return -1;
        h->last_sec_num = val;
    
    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 },
    
        { 0x11, CODEC_TYPE_AUDIO,        CODEC_ID_AAC }, /* LATM syntax */
    
        { 0x1b, CODEC_TYPE_VIDEO,       CODEC_ID_H264 },
        { 0xd1, CODEC_TYPE_VIDEO,      CODEC_ID_DIRAC },
        { 0xea, CODEC_TYPE_VIDEO,        CODEC_ID_VC1 },
        { 0 },
    };
    
    static const StreamType HDMV_types[] = {
    
        { 0x80, CODEC_TYPE_AUDIO, CODEC_ID_PCM_BLURAY },
    
        { 0x81, CODEC_TYPE_AUDIO, CODEC_ID_AC3 },
        { 0x82, CODEC_TYPE_AUDIO, CODEC_ID_DTS },
    
        { 0x84, CODEC_TYPE_AUDIO, CODEC_ID_EAC3 },
    
        { 0x90, CODEC_TYPE_SUBTITLE, CODEC_ID_HDMV_PGS_SUBTITLE },
    
        { 0 },
    };
    
    /* ATSC ? */
    static const StreamType MISC_types[] = {
        { 0x81, CODEC_TYPE_AUDIO,   CODEC_ID_AC3 },
        { 0x8a, CODEC_TYPE_AUDIO,   CODEC_ID_DTS },
        { 0 },
    };
    
    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 },
    };
    
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    /* 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 */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        { 0x7b, CODEC_TYPE_AUDIO,             CODEC_ID_DTS },
    
        { 0x56, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_TELETEXT },
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        { 0x59, CODEC_TYPE_SUBTITLE, CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    };
    
    
    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;
            }
        }
    }
    
    
    static int mpegts_set_stream_info(AVStream *st, PESContext *pes,
                                      uint32_t stream_type, uint32_t prog_reg_desc)
    
    {
        av_set_pts_info(st, 33, 1, 90000);
        st->priv_data = pes;
        st->codec->codec_type = CODEC_TYPE_DATA;
    
        st->need_parsing = AVSTREAM_PARSE_FULL;
        pes->st = st;
    
        av_log(pes->stream, AV_LOG_DEBUG,
               "stream=%d stream_type=%x pid=%x prog_reg_desc=%.4s\n",
               st->index, pes->stream_type, pes->pid, (char*)&prog_reg_desc);
    
        st->codec->codec_tag = pes->stream_type;
    
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        mpegts_find_stream_type(st, pes->stream_type, ISO_types);
        if (prog_reg_desc == AV_RL32("HDMV") &&
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            mpegts_find_stream_type(st, pes->stream_type, HDMV_types);
    
            if (pes->stream_type == 0x83) {
                // HDMV TrueHD streams also contain an AC3 coded version of the
                // audio track - add a second stream for this
                AVStream *sub_st;
                // priv_data cannot be shared between streams
                PESContext *sub_pes = av_malloc(sizeof(*sub_pes));
                if (!sub_pes)
    
                memcpy(sub_pes, pes, sizeof(*sub_pes));
    
                sub_st = av_new_stream(pes->stream, pes->pid);
                if (!sub_st) {
                    av_free(sub_pes);
    
                }
    
                av_set_pts_info(sub_st, 33, 1, 90000);
                sub_st->priv_data = sub_pes;
                sub_st->codec->codec_type = CODEC_TYPE_AUDIO;
                sub_st->codec->codec_id   = CODEC_ID_AC3;
                sub_st->need_parsing = AVSTREAM_PARSE_FULL;
                sub_pes->sub_st = pes->sub_st = sub_st;
            }
        }
    
        if (st->codec->codec_id == CODEC_ID_NONE)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            mpegts_find_stream_type(st, pes->stream_type, MISC_types);
    
    static int64_t get_pts(const uint8_t *p)
    {
        int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30;
        pts |= (AV_RB16(p + 1) >> 1) << 15;
        pts |=  AV_RB16(p + 3) >> 1;
        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);
    
        // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID
        if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76)
            pkt->stream_index = pes->sub_st->index;
        else
            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 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;
        const uint8_t *p;
        int len, code;
    
        if(!ts->pkt)
            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) {
            switch(pes->state) {
            case MPEGTS_HEADER:
                len = PES_START_SIZE - pes->data_index;
                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_START_SIZE) {
                    /* we got all the PES or section header. We can now
                       decide */
    #if 0
                    av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index);
    #endif
                    if (pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
                        pes->header[2] == 0x01) {
                        /* it must be an mpeg2 PES stream */
                        code = pes->header[3] | 0x100;
                        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;
    
                        /* stream not present in PMT */
                        if (!pes->st) {
                            pes->st = av_new_stream(ts->stream, pes->pid);
                            if (!pes->st)
                                return AVERROR(ENOMEM);
                            mpegts_set_stream_info(pes->st, pes, 0, 0);
                        }
    
                        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 = 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;
                            if (pes->st->codec->codec_id == CODEC_ID_NONE) {
                                dprintf(pes->stream, "pid=%x stream_type=%x probing\n",
                                        pes->pid, pes->stream_type);
                                pes->st->codec->codec_id = CODEC_ID_PROBE;
                            }
                        } else {
                            pes->state = MPEGTS_PAYLOAD;
                            pes->data_index = 0;
                        }
                    } else {
                        /* otherwise, it should be a table */
                        /* skip packet */
                    skip:
                        pes->state = MPEGTS_SKIP;
                        continue;
                    }
                }
                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);
                pes->data_index += len;
                p += len;
                buf_size -= len;
                if (pes->data_index == pes->pes_header_size) {
                    const uint8_t *r;
                    unsigned int flags, pes_ext, skip;
    
                    flags = pes->header[7];
                    r = pes->header + 9;
                    pes->pts = AV_NOPTS_VALUE;
                    pes->dts = AV_NOPTS_VALUE;
                    if ((flags & 0xc0) == 0x80) {
                        pes->dts = pes->pts = get_pts(r);
                        r += 5;
                    } else if ((flags & 0xc0) == 0xc0) {
                        pes->pts = get_pts(r);
                        r += 5;
                        pes->dts = get_pts(r);
                        r += 5;
                    }
                    pes->extended_stream_id = -1;
                    if (flags & 0x01) { /* PES extension */
                        pes_ext = *r++;
                        /* Skip PES private data, program packet sequence counter and P-STD buffer */
                        skip = (pes_ext >> 4) & 0xb;
                        skip += skip & 0x9;
                        r += skip;
                        if ((pes_ext & 0x41) == 0x01 &&
                            (r + 2) <= (pes->header + pes->pes_header_size)) {
                            /* PES extension 2 */
                            if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0)
                                pes->extended_stream_id = r[1];
                        }
                    }
    
                    /* 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 (buf_size > 0 && pes->buffer) {
                    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;
                    }
                    memcpy(pes->buffer+pes->data_index, p, buf_size);
                    pes->data_index += buf_size;
                }
                buf_size = 0;
                break;
            case MPEGTS_SKIP:
                buf_size = 0;
                break;
            }
        }
    
        return 0;
    }
    
    static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid)
    {
        MpegTSFilter *tss;
        PESContext *pes;
    
        /* if no pid found, then add a pid context */
        pes = av_mallocz(sizeof(PESContext));
        if (!pes)
            return 0;
        pes->ts = ts;
        pes->stream = ts->stream;
        pes->pid = pid;
        pes->pcr_pid = pcr_pid;
        pes->state = MPEGTS_SKIP;
        pes->pts = AV_NOPTS_VALUE;
        pes->dts = AV_NOPTS_VALUE;
        tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
        if (!tss) {
            av_free(pes);
            return 0;
        }
        return pes;
    }
    
    
    static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
    
        MpegTSContext *ts = filter->u.section_filter.opaque;
    
        PESContext *pes;
        AVStream *st;
        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, anc_page;
        char language[4];
    
        uint32_t prog_reg_desc = 0; /* registration descriptor */
    
    #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);
    
        p_end = section + section_len - 4;
        p = section;
        if (parse_section_header(h, &p, p_end) < 0)
            return;
    
    
        dprintf(ts->stream, "sid=0x%x sec_num=%d/%d\n",
    
        pcr_pid = get16(&p, p_end) & 0x1fff;
        if (pcr_pid < 0)
            return;
    
    
        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;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            if(tag == 0x05 && len >= 4) { // registration descriptor
    
                prog_reg_desc = bytestream_get_le32(&p);
    
    
        // stop parsing after pmt, we found header
        if (!ts->stream->nb_streams)
            ts->stop_parse = 1;
    
    
            stream_type = get8(&p, p_end);
            if (stream_type < 0)
                break;
            pid = get16(&p, p_end) & 0x1fff;
            if (pid < 0)
                break;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    
            /* 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);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                if (pes)
    
                    st = av_new_stream(pes->stream, pes->pid);
    
            if (!pes->stream_type)
                mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);
    
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            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;
            desc_list_end = p + desc_list_len;
    
            if (desc_list_end > p_end)
    
            for(;;) {
                desc_tag = get8(&p, desc_list_end);
                if (desc_tag < 0)
                    break;
                desc_len = get8(&p, desc_list_end);
    
                desc_end = p + desc_len;
                if (desc_end > desc_list_end)
                    break;
    
    
                dprintf(ts->stream, "tag: 0x%02x len=%d\n",
    
                if (st->codec->codec_id == CODEC_ID_NONE &&
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    stream_type == STREAM_TYPE_PRIVATE_DATA)
                    mpegts_find_stream_type(st, desc_tag, DESC_types);
    
    
                case 0x56: /* DVB teletext 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;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                case 0x59: /* subtitling descriptor */
    
                    language[0] = get8(&p, desc_end);
                    language[1] = get8(&p, desc_end);
                    language[2] = get8(&p, desc_end);
                    language[3] = 0;
                    get8(&p, desc_end);
                    comp_page = get16(&p, desc_end);
                    anc_page = get16(&p, desc_end);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    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;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    av_metadata_set(&st->metadata, "language", language);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                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_NONE &&
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        stream_type == STREAM_TYPE_PRIVATE_DATA)
    
                        mpegts_find_stream_type(st, st->codec->codec_tag, REGD_types);
    
    
                if (prog_reg_desc == AV_RL32("HDMV") && stream_type == 0x83 && pes->sub_st) {
                    av_program_add_stream_index(ts->stream, h->id, pes->sub_st->index);
                    pes->sub_st->codec->codec_tag = st->codec->codec_tag;
                }