Skip to content
Snippets Groups Projects
mpegts.c 42.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * MPEG2 transport stream (aka DVB) demux
    
     * This library 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 of the License, or (at your option) any later version.
    
     * This library 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 this library; 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;
        char *provider_name;
        char *name;
    } MpegTSService;
    
        int raw_packet_size; /* raw packet size, including FEC if present */
    
        int auto_guess; /* if true, all pids are analized to find streams */
        int set_service_ret;
    
    
        int mpeg2ts_raw;  /* force raw MPEG2 transport stream output, if possible */
        int mpeg2ts_compute_pcr; /* compute exact PCR for each transport stream packet */
    
        /* used to estimate the exact PCR */
        int64_t cur_pcr;
        int pcr_incr;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int pcr_pid;
    
        /* data needed to handle file based ts */
        int stop_parse; /* stop parsing loop */
        AVPacket *pkt; /* packet containing av data */
    
        /******************************************/
        /* private mpegts data */
        /* scan context */
        MpegTSFilter *sdt_filter;
        int nb_services;
        MpegTSService **services;
    
        /* set service context (XXX: allocated it ?) */
        SetServiceCallback *set_service_cb;
        void *set_service_opaque;
        MpegTSFilter *pat_filter;
        MpegTSFilter *pmt_filter;
        int req_sid;
    
        MpegTSFilter *pids[NB_PID_MAX];
    
    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(tss->opaque, tss->section_buf, tss->section_h_size);
    
    MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid,
    
                                             SectionCallback *section_cb, void *opaque,
                                             int check_crc)
    
    {
        MpegTSFilter *filter;
        MpegTSSectionFilter *sec;
    
    #ifdef DEBUG_SI
        printf("Filter: pid=0x%x\n", pid);
    #endif
    
        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;
    }
    
    
    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;
    }
    
    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,
    
                                      char *provider_name, char *name)
    {
        MpegTSService *service;
    
    #ifdef DEBUG_SI
    
        printf("new_service: sid=0x%04x provider='%s' name='%s'\n",
    
               sid, provider_name, name);
    #endif
    
        service = av_mallocz(sizeof(MpegTSService));
        if (!service)
            return NULL;
        service->sid = sid;
        service->provider_name = provider_name;
        service->name = name;
        dynarray_add(&ts->services, &ts->nb_services, service);
        return service;
    }
    
    static void pmt_cb(void *opaque, const uint8_t *section, int section_len)
    {
        MpegTSContext *ts = opaque;
        SectionHeader h1, *h = &h1;
    
        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 */
    
        av_hex_dump(stdout, (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
        printf("sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num);
    #endif
    
        if (h->tid != PMT_TID || (ts->req_sid >= 0 && h->id != ts->req_sid) )
    
            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;
    
    #ifdef DEBUG_SI
        printf("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 &&
                    ((desc_tag == 0x6A) || (desc_tag == 0x7A))) {
                        /*assume DVB AC-3 Audio*/
                        stream_type = STREAM_TYPE_AUDIO_AC3;
                }
    
                desc_len = get8(&p, desc_list_end);
                desc_end = p + desc_len;
                if (desc_end > desc_list_end)
                    break;
    #ifdef DEBUG_SI
                printf("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;
    
                    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;
    
    
    #ifdef DEBUG_SI
            printf("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_AUDIO_AAC:
    
            case STREAM_TYPE_SUBTITLE_DVB:
                pes = add_pes_stream(ts, pid, stream_type);
                if (pes)
                    st = new_pes_av_stream(pes, 0);
    
    
            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;
    
        }
        /* all parameters are there */
        ts->set_service_cb(ts->set_service_opaque, 0);
        mpegts_close_filter(ts, ts->pmt_filter);
        ts->pmt_filter = NULL;
    }
    
    static void pat_cb(void *opaque, const uint8_t *section, int section_len)
    {
        MpegTSContext *ts = opaque;
        SectionHeader h1, *h = &h1;
        const uint8_t *p, *p_end;
        int sid, pmt_pid;
    
    #ifdef DEBUG_SI
        printf("PAT:\n");
    
        av_hex_dump(stdout, (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
            printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
    #endif
            if (sid == 0x0000) {
                /* NIT info */
            } else {
    
                    ts->pmt_filter = mpegts_open_section_filter(ts, pmt_pid,
    
                                                                pmt_cb, ts, 1);
                    goto found;
                }
            }
        }
        /* not found */
        ts->set_service_cb(ts->set_service_opaque, -1);
    
     found:
        mpegts_close_filter(ts, ts->pat_filter);
        ts->pat_filter = NULL;
    }
    
    
    /* add all services found in the PAT */
    static void pat_scan_cb(void *opaque, const uint8_t *section, int section_len)
    {
        MpegTSContext *ts = opaque;
        SectionHeader h1, *h = &h1;
        const uint8_t *p, *p_end;
        int sid, pmt_pid;
        char *provider_name, *name;
        char buf[256];
    
    #ifdef DEBUG_SI
        printf("PAT:\n");
    
        av_hex_dump(stdout, (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
            printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
    #endif
            if (sid == 0x0000) {
                /* NIT info */
            } else {
                /* add the service with a dummy name */
                snprintf(buf, sizeof(buf), "Service %x\n", sid);
                name = av_strdup(buf);
                provider_name = av_strdup("");
                if (name && provider_name) {
                    new_service(ts, sid, provider_name, name);
                } else {
                    av_freep(&name);
                    av_freep(&provider_name);
                }
            }
        }
        ts->stop_parse = 1;
    
        /* remove filter */
        mpegts_close_filter(ts, ts->pat_filter);
        ts->pat_filter = NULL;
    }
    
    
    void mpegts_set_service(MpegTSContext *ts, int sid,
                            SetServiceCallback *set_service_cb, void *opaque)
    {
        ts->set_service_cb = set_service_cb;
        ts->set_service_opaque = opaque;
        ts->req_sid = sid;
    
        ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID,
    
                                                    pat_cb, ts, 1);
    }
    
    static void sdt_cb(void *opaque, const uint8_t *section, int section_len)
    {
        MpegTSContext *ts = 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
        printf("SDT:\n");
    
        av_hex_dump(stdout, (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
                printf("tag: 0x%02x len=%d\n", desc_tag, desc_len);
    #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;
        }
        ts->stop_parse = 1;
    
        /* remove filter */
        mpegts_close_filter(ts, ts->sdt_filter);
        ts->sdt_filter = NULL;
    }
    
    
    /* scan services in a transport stream by looking at the SDT */
    
        ts->sdt_filter = mpegts_open_section_filter(ts, SDT_PID,
    
    /* scan services in a transport stream by looking at the PAT (better
       than nothing !) */
    void mpegts_scan_pat(MpegTSContext *ts)
    {
    
        ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID,
    
    
    /* 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)
    
    
        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];
    
    
    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(void *opaque,
                                 const uint8_t *buf, int buf_size, int is_start)
    
        }
        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
    
                    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) ||
    
                              (code >= 0x1e0 && code <= 0x1ef) ||
                              (code == 0x1bd)))
    
                            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;
        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 = 1;
            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;