Skip to content
Snippets Groups Projects
mpegts.c 41 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * MPEG2 transport stream (aka DVB) demuxer
    
     * 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
    
    #include "crc.h"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    //#define DEBUG_SEEK
    
    #define MAX_SCAN_PACKETS 32000
    
    /* maximum size in which we look for synchronisation if
       synchronisation is lost */
    #define MAX_RESYNC_SIZE 4096
    
    typedef struct PESContext PESContext;
    
    static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int stream_type);
    static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code);
    
    typedef void PESCallback(void *opaque, const uint8_t *buf, int len, int is_start);
    
    typedef struct MpegTSPESFilter {
        PESCallback *pes_cb;
        void *opaque;
    } MpegTSPESFilter;
    
    typedef void SectionCallback(void *opaque, 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;
        int check_crc:1;
        int end_of_section_reached:1;
        SectionCallback *section_cb;
        void *opaque;
    } MpegTSSectionFilter;
    
    typedef struct MpegTSFilter {
    
        int pid;
        int last_cc; /* last cc code (-1 if first packet) */
    
        enum MpegTSFilterType type;
        union {
            MpegTSPESFilter pes_filter;
            MpegTSSectionFilter section_filter;
        } u;
    } MpegTSFilter;
    
    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*/
    
        /** 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  */
        int pcr_pid;        /**< used to estimate the exact PCR  */
    
        /** stop parsing loop                                    */
        int stop_parse;
        /** packet containing Audio/Video data                   */
        AVPacket *pkt;
    
    
        /******************************************/
        /* private mpegts data */
        /* scan context */
    
        /** number of PMTs in the last PAT seen                  */
    
        /** list of PMTs in the last PAT seen                    */
    
        /** filters for various streams specified by PMT + for the PAT and PMT */
    
    /* TS stream handling */
    
    enum MpegTSState {
        MPEGTS_HEADER = 0,
        MPEGTS_PESHEADER_FILL,
        MPEGTS_PAYLOAD,
        MPEGTS_SKIP,
    };
    
    /* enough for PES header + length */
    #define PES_START_SIZE 9
    #define MAX_PES_HEADER_SIZE (9 + 255)
    
    struct PESContext {
        int pid;
        int stream_type;
        MpegTSContext *ts;
        AVFormatContext *stream;
        AVStream *st;
        enum MpegTSState state;
        /* used to get the format */
        int data_index;
        int total_size;
        int pes_header_size;
        int64_t pts, dts;
        uint8_t header[MAX_PES_HEADER_SIZE];
    };
    
    
    /**
     *  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 = (((tss->section_buf[1] & 0xf) << 8) | tss->section_buf[2]) + 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;
    
                av_crc(av_crc04C11DB7, -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;
    
        av_log(ts->stream, AV_LOG_DEBUG, "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)
            av_freep(&filter->u.pes_filter.opaque);
    
    
    static int analyze(const uint8_t *buf, int size, int packet_size, int *index){
        int stat[packet_size];
        int i;
        int x=0;
        int best_score=0;
    
        memset(stat, 0, packet_size*sizeof(int));
    
        for(x=i=0; i<size; i++){
            if(buf[i] == 0x47){
                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 = (p[0] << 8) | p[1];
        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;
    
    static MpegTSService *new_service(MpegTSContext *ts, int sid,
    
        MpegTSService *service=NULL;
        int i;
    
        av_log(ts->stream, AV_LOG_DEBUG, "new_service: "
    
        for(i=0; i<ts->nb_services; i++)
            if(ts->services[i]->sid == sid)
                service= ts->services[i];
    
        if(!service){
            service = av_mallocz(sizeof(MpegTSService));
            if (!service)
                return NULL;
            dynarray_add(&ts->services, &ts->nb_services, service);
        }
    
        assert((!provider_name) == (!name));
        if(name){
            av_free(service->provider_name);
            av_free(service->         name);
            service->provider_name = provider_name;
            service->         name =          name;
        }
    
    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 = 0, anc_page = 0; /* initialize to kill warnings */
    
        char language[4] = {0}; /* initialize to kill warnings */
    
        av_log(ts->stream, AV_LOG_DEBUG, "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",
    
            return;
    
        pcr_pid = get16(&p, p_end) & 0x1fff;
        if (pcr_pid < 0)
            return;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        ts->pcr_pid = pcr_pid;
    
        av_log(ts->stream, AV_LOG_DEBUG, "pcr_pid=0x%x\n", pcr_pid);
    
    #endif
        program_info_length = get16(&p, p_end) & 0xfff;
        if (program_info_length < 0)
            return;
        p += program_info_length;
        if (p >= p_end)
            return;
        for(;;) {
    
            stream_type = get8(&p, p_end);
            if (stream_type < 0)
                break;
            pid = get16(&p, p_end) & 0x1fff;
            if (pid < 0)
                break;
    
            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;
    
                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);
                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",
    
    #endif
                switch(desc_tag) {
                case DVB_SUBT_DESCID:
                    if (stream_type == STREAM_TYPE_PRIVATE_DATA)
                        stream_type = STREAM_TYPE_SUBTITLE_DVB;
    
                    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);
    
                    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;
                    break;
                default:
                    break;
                }
                p = desc_end;
            }
            p = desc_list_end;
    
            av_log(ts->stream, AV_LOG_DEBUG, "stream_type=%d pid=0x%x\n",
    
            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:
    
    Nico Sabbi's avatar
    Nico Sabbi committed
            case STREAM_TYPE_VIDEO_VC1:
    
            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{
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                    pes = add_pes_stream(ts, pid, stream_type);
                    if (pes)
                        st = new_pes_av_stream(pes, 0);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                break;
    
    
            if (st) {
                if (language[0] != 0) {
                    st->language[0] = language[0];
                    st->language[1] = language[1];
                    st->language[2] = language[2];
                    st->language[3] = language[3];
                }
    
                if (stream_type == STREAM_TYPE_SUBTITLE_DVB) {
    
                    st->codec->sub_id = (anc_page << 16) | comp_page;
    
    static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
    
        MpegTSContext *ts = filter->u.section_filter.opaque;
    
        SectionHeader h1, *h = &h1;
        const uint8_t *p, *p_end;
        int sid, pmt_pid;
    
    #ifdef DEBUG_SI
    
        av_log(ts->stream, AV_LOG_DEBUG, "PAT:\n");
    
        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;
        if (h->tid != PAT_TID)
            return;
    
        for(;;) {
            sid = get16(&p, p_end);
            if (sid < 0)
                break;
            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);
    
                new_service(ts, sid, NULL, NULL);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
    
    static void mpegts_set_service(MpegTSContext *ts)
    
        mpegts_open_section_filter(ts, PAT_PID,
    
    static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
    
        MpegTSContext *ts = filter->u.section_filter.opaque;
    
        SectionHeader h1, *h = &h1;
        const uint8_t *p, *p_end, *desc_list_end, *desc_end;
        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");
    
        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;
        if (h->tid != SDT_TID)
            return;
        onid = get16(&p, p_end);
        if (onid < 0)
            return;
        val = get8(&p, p_end);
        if (val < 0)
            return;
        for(;;) {
            sid = get16(&p, p_end);
            if (sid < 0)
                break;
            val = get8(&p, p_end);
            if (val < 0)
                break;
            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)
                break;
            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;
    #ifdef DEBUG_SI
    
                av_log(ts->stream, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n",
    
    #endif
                switch(desc_tag) {
                case 0x48:
                    service_type = get8(&p, p_end);
                    if (service_type < 0)
                        break;
                    provider_name = getstr8(&p, p_end);
                    if (!provider_name)
                        break;
                    name = getstr8(&p, p_end);
                    if (!name)
                        break;
                    new_service(ts, sid, provider_name, name);
                    break;
                default:
                    break;
                }
                p = desc_end;
            }
            p = desc_list_end;
        }
    }
    
    
    /* scan services in a transport stream by looking at the SDT */
    
    static void mpegts_scan_sdt(MpegTSContext *ts)
    
                                                    sdt_cb, ts, 1);
    }
    
    static int64_t get_pts(const uint8_t *p)
    {
        int64_t pts;
        int val;
    
        pts = (int64_t)((p[0] >> 1) & 0x07) << 30;
        val = (p[1] << 8) | p[2];
        pts |= (int64_t)(val >> 1) << 15;
        val = (p[3] << 8) | p[4];
        pts |= (int64_t)(val >> 1);
        return pts;
    
    }
    
    /* return non zero if a packet could be constructed */
    
    static void mpegts_push_data(MpegTSFilter *filter,
    
        PESContext *pes = filter->u.pes_filter.opaque;
    
        }
        p = buf;
        while (buf_size > 0) {
    
                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;
    
                    /* 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);
    
                    if (pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
                        pes->header[2] == 0x01) {
    
                        /* it must be an mpeg2 PES stream */
    
                        if (!((code >= 0x1c0 && code <= 0x1df) ||
    
    Nico Sabbi's avatar
    Nico Sabbi committed
                              (code == 0x1bd) || (code == 0x1fd)))
    
                            new_pes_av_stream(pes, code);
    
                        pes->state = MPEGTS_PESHEADER_FILL;
                        pes->total_size = (pes->header[4] << 8) | pes->header[5];
                        /* 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;
    
                    } else {
                        /* otherwise, it should be a table */
                        /* skip packet */
                    skip:
    
                        continue;
                    }
                }
                break;
                /**********************************************/
                /* PES packing parsing */
            case MPEGTS_PESHEADER_FILL:
    
                len = pes->pes_header_size - pes->data_index;
                if (len > buf_size)
                    len = buf_size;
                memcpy(pes->header + pes->data_index, p, len);
                pes->data_index += len;
    
                if (pes->data_index == pes->pes_header_size) {
                    const uint8_t *r;
                    unsigned int flags;
    
                    flags = pes->header[7];
                    r = pes->header + 9;
                    pes->pts = AV_NOPTS_VALUE;
                    pes->dts = AV_NOPTS_VALUE;
                    if ((flags & 0xc0) == 0x80) {
                        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;
                    }
                    /* we got the full header. We parse it and get the payload */
                    pes->state = MPEGTS_PAYLOAD;
                }
    
                if (pes->total_size) {
                    len = pes->total_size - pes->data_index;
                    if (len > buf_size)
                        len = buf_size;
                } else {
                    len = buf_size;
                }
    
                    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;
    
                        /* reset pts values */
                        pes->pts = AV_NOPTS_VALUE;
                        pes->dts = AV_NOPTS_VALUE;
                        ts->stop_parse = 1;
                        return;
    
                    }
                }
                buf_size = 0;
                break;
            case MPEGTS_SKIP:
                buf_size = 0;
                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;
    
    Nico Sabbi's avatar
    Nico Sabbi committed
        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;
    }
    
    
    static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int stream_type)
    
    {
        MpegTSFilter *tss;
        PESContext *pes;
    
        /* if no pid found, then add a pid context */
        pes = av_mallocz(sizeof(PESContext));
        if (!pes)
    
        tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
        if (!tss) {
            av_free(pes);
    
    static void handle_packet(MpegTSContext *ts, const uint8_t *packet)
    
        MpegTSFilter *tss;
        int len, pid, cc, cc_ok, afc, is_start;
        const uint8_t *p, *p_end;
    
        pid = ((packet[1] & 0x1f) << 8) | packet[2];
        is_start = packet[1] & 0x40;
        tss = ts->pids[pid];
        if (ts->auto_guess && tss == NULL && is_start) {
    
            tss = ts->pids[pid];
        }
        if (!tss)
            return;
    
        /* continuity check (currently not used) */
        cc = (packet[3] & 0xf);
        cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
        tss->last_cc = cc;
    
        /* skip adaptation field */
        afc = (packet[3] >> 4) & 3;
        p = packet + 4;
        if (afc == 0) /* reserved value */
            return;
        if (afc == 2) /* adaptation field only */
            return;
        if (afc == 3) {
            /* skip adapation field */
            p += p[0] + 1;
        }
        /* if past the end of packet, ignore */
        p_end = packet + TS_PACKET_SIZE;
        if (p >= p_end)
            return;
    
        if (tss->type == MPEGTS_SECTION) {
            if (is_start) {
                /* pointer field present */
                len = *p++;
                if (p + len > p_end)
                    return;
                if (len && cc_ok) {
    
                    write_section_data(s, tss,
    
                    /* check whether filter has been closed */
                    if (!ts->pids[pid])
                        return;
    
                    write_section_data(s, tss,
    
                    write_section_data(s, tss,
    
    /* XXX: try to find a better synchro over several packets (use
       get_packet_size() ?) */