Skip to content
Snippets Groups Projects
mpegts.c 44.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •     }
        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,
    
            tss->u.pes_filter.pes_cb(tss->u.pes_filter.opaque,
    
    /* XXX: try to find a better synchro over several packets (use
       get_packet_size() ?) */
    
    {
        int c, i;
    
        for(i = 0;i < MAX_RESYNC_SIZE; i++) {
            c = url_fgetc(pb);
            if (c < 0)
                return -1;
            if (c == 0x47) {
                url_fseek(pb, -1, SEEK_CUR);
                return 0;
            }
        }
        /* no sync found */
        return -1;
    }
    
    
    /* return -1 if error or EOF. Return 0 if OK. */
    static int read_packet(ByteIOContext *pb, uint8_t *buf, int raw_packet_size)
    
        int skip, len;
    
        for(;;) {
            len = get_buffer(pb, buf, TS_PACKET_SIZE);
            if (len != TS_PACKET_SIZE)
                return AVERROR_IO;
            /* check paquet sync byte */
            if (buf[0] != 0x47) {
                /* find a new packet start */
                url_fseek(pb, -TS_PACKET_SIZE, SEEK_CUR);
                if (mpegts_resync(pb) < 0)
                    return AVERROR_INVALIDDATA;
                else
                    continue;
            } else {
                skip = raw_packet_size - TS_PACKET_SIZE;
                if (skip > 0)
                    url_fskip(pb, skip);
                break;
            }
        }
        return 0;
    }
    
    static int handle_packets(MpegTSContext *ts, int nb_packets)
    {
        AVFormatContext *s = ts->stream;
    
        uint8_t packet[TS_PACKET_SIZE];
        int packet_num, ret;
    
            if (ts->stop_parse)
                break;
            packet_num++;
            if (nb_packets != 0 && packet_num >= nb_packets)
                break;
    
            ret = read_packet(pb, packet, ts->raw_packet_size);
            if (ret != 0)
                return ret;
            handle_packet(ts, packet);
    
        const int size= p->buf_size;
    
        int score, fec_score, dvhs_score;
    
    #define CHECK_COUNT 10
    
        if (size < (TS_FEC_PACKET_SIZE * CHECK_COUNT))
            return -1;
    
        score    = analyze(p->buf, TS_PACKET_SIZE    *CHECK_COUNT, TS_PACKET_SIZE, NULL);
    
        dvhs_score  = analyze(p->buf, TS_DVHS_PACKET_SIZE    *CHECK_COUNT, TS_DVHS_PACKET_SIZE, NULL);
    
        fec_score= analyze(p->buf, TS_FEC_PACKET_SIZE*CHECK_COUNT, TS_FEC_PACKET_SIZE, NULL);
    
    //    av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
    
    // we need a clear definition for the returned score otherwise things will become messy sooner or later
    
        if     (score > fec_score && score > dvhs_score && score > 6) return AVPROBE_SCORE_MAX + score     - CHECK_COUNT;
        else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) return AVPROBE_SCORE_MAX + dvhs_score  - CHECK_COUNT;
    
        else if(                 fec_score > 6) return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
        else                                    return -1;
    
    #else
        /* only use the extension for safer guess */
        if (match_ext(p->filename, "ts"))
            return AVPROBE_SCORE_MAX;
        else
            return 0;
    #endif
    
    static void set_service_cb(void *opaque, int ret)
    
    {
        MpegTSContext *ts = opaque;
        ts->set_service_ret = ret;
        ts->stop_parse = 1;
    }
    
    
    /* return the 90 kHz PCR and the extension for the 27 MHz PCR. return
       (-1) if not available */
    
    static int parse_pcr(int64_t *ppcr_high, int *ppcr_low,
    
                         const uint8_t *packet)
    {
        int afc, len, flags;
        const uint8_t *p;
        unsigned int v;
    
        afc = (packet[3] >> 4) & 3;
        if (afc <= 1)
            return -1;
        p = packet + 4;
        len = p[0];
        p++;
        if (len == 0)
            return -1;
        flags = *p++;
        len--;
        if (!(flags & 0x10))
            return -1;
        if (len < 6)
            return -1;
        v = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
        *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7);
        *ppcr_low = ((p[4] & 1) << 8) | p[5];
        return 0;
    }
    
    
    static int mpegts_read_header(AVFormatContext *s,
                                  AVFormatParameters *ap)
    {
        MpegTSContext *ts = s->priv_data;
        ByteIOContext *pb = &s->pb;
        uint8_t buf[1024];
    
        if (ap) {
            ts->mpeg2ts_raw = ap->mpeg2ts_raw;
            ts->mpeg2ts_compute_pcr = ap->mpeg2ts_compute_pcr;
        }
    
    
        /* read the first 1024 bytes to get packet size */
        pos = url_ftell(pb);
        len = get_buffer(pb, buf, sizeof(buf));
        if (len != sizeof(buf))
            goto fail;
        ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
        if (ts->raw_packet_size <= 0)
            goto fail;
    
    goto_auto_guess:
    
                /* first do a scaning to get all the services */
    
                if (ts->nb_services <= 0) {
                    /* no SDT found, we try to look at the PAT */
    
                    /* First remove the SDT filters from each PID */
                    int i;
                    for (i=0; i < NB_PID_MAX; i++) {
                        if (ts->pids[i])
                            mpegts_close_filter(ts, ts->pids[i]);
                    }
                    url_fseek(pb, pos, SEEK_SET);
                    mpegts_scan_pat(ts);
    
                    /* raw transport stream */
                    ts->auto_guess = 1;
                    s->ctx_flags |= AVFMTCTX_NOHEADER;
                    goto do_pcr;
                }
    
                for(i=0; i<ts->nb_services && ts->set_service_ret; i++){
                    service = ts->services[i];
                    sid = service->sid;
    
                    av_log(NULL, AV_LOG_DEBUG, "tuning to '%s'\n", service->name);
    
                    /* now find the info for the first service if we found any,
                    otherwise try to filter all PATs */
    
                    url_fseek(pb, pos, SEEK_SET);
                    mpegts_set_service(ts, sid, set_service_cb, ts);
    
                if (ts->set_service_ret != 0) {
                    if(ts->auto_guess)
                      return -1;
                    else {
                      //let's retry with auto_guess set
                     ts->auto_guess = 1;
                     goto goto_auto_guess;
                    }
                }
    
    #endif
            }
            s->ctx_flags |= AVFMTCTX_NOHEADER;
        } else {
            AVStream *st;
            int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l;
            int64_t pcrs[2], pcr_h;
            int packet_count[2];
            uint8_t packet[TS_PACKET_SIZE];
    
            av_set_pts_info(st, 60, 1, 27000000);
    
            st->codec->codec_type = CODEC_TYPE_DATA;
            st->codec->codec_id = CODEC_ID_MPEG2TS;
    
            /* we iterate until we find two PCRs to estimate the bitrate */
            pcr_pid = -1;
            nb_pcrs = 0;
            nb_packets = 0;
            for(;;) {
                ret = read_packet(&s->pb, packet, ts->raw_packet_size);
                if (ret < 0)
                    return -1;
                pid = ((packet[1] & 0x1f) << 8) | packet[2];
                if ((pcr_pid == -1 || pcr_pid == pid) &&
                    parse_pcr(&pcr_h, &pcr_l, packet) == 0) {
                    pcr_pid = pid;
                    packet_count[nb_pcrs] = nb_packets;
                    pcrs[nb_pcrs] = pcr_h * 300 + pcr_l;
                    nb_pcrs++;
                    if (nb_pcrs >= 2)
                        break;
                }
                nb_packets++;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            ts->pcr_pid = pcr_pid;
    
            /* NOTE1: the bitrate is computed without the FEC */
            /* NOTE2: it is only the bitrate of the start of the stream */
            ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]);
            ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0];
            s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr;
    
            st->start_time = ts->cur_pcr;
    
            av_log(NULL, AV_LOG_DEBUG, "start=%0.3f pcr=%0.3f incr=%d\n",
    
                   st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
    
    #define MAX_PACKET_READAHEAD ((128 * 1024) / 188)
    
    static int mpegts_raw_read_packet(AVFormatContext *s,
                                      AVPacket *pkt)
    {
        MpegTSContext *ts = s->priv_data;
        int ret, i;
        int64_t pcr_h, next_pcr_h, pos;
        int pcr_l, next_pcr_l;
        uint8_t pcr_buf[12];
    
        if (av_new_packet(pkt, TS_PACKET_SIZE) < 0)
            return -ENOMEM;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        pkt->pos= url_ftell(&s->pb);
    
        ret = read_packet(&s->pb, pkt->data, ts->raw_packet_size);
        if (ret < 0) {
            av_free_packet(pkt);
            return ret;
        }
        if (ts->mpeg2ts_compute_pcr) {
            /* compute exact PCR for each packet */
            if (parse_pcr(&pcr_h, &pcr_l, pkt->data) == 0) {
                /* we read the next PCR (XXX: optimize it by using a bigger buffer */
                pos = url_ftell(&s->pb);
                for(i = 0; i < MAX_PACKET_READAHEAD; i++) {
                    url_fseek(&s->pb, pos + i * ts->raw_packet_size, SEEK_SET);
                    get_buffer(&s->pb, pcr_buf, 12);
                    if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) {
                        /* XXX: not precise enough */
    
                        ts->pcr_incr = ((next_pcr_h - pcr_h) * 300 + (next_pcr_l - pcr_l)) /
    
                            (i + 1);
                        break;
                    }
                }
                url_fseek(&s->pb, pos, SEEK_SET);
                /* no next PCR found: we use previous increment */
                ts->cur_pcr = pcr_h * 300 + pcr_l;
            }
            pkt->pts = ts->cur_pcr;
            pkt->duration = ts->pcr_incr;
            ts->cur_pcr += ts->pcr_incr;
        }
        pkt->stream_index = 0;
        return 0;
    }
    
    
    static int mpegts_read_packet(AVFormatContext *s,
                                  AVPacket *pkt)
    {
        MpegTSContext *ts = s->priv_data;
    
    
        if (!ts->mpeg2ts_raw) {
            ts->pkt = pkt;
            return handle_packets(ts, 0);
        } else {
            return mpegts_raw_read_packet(s, pkt);
        }
    
    }
    
    static int mpegts_read_close(AVFormatContext *s)
    {
        MpegTSContext *ts = s->priv_data;
        int i;
        for(i=0;i<NB_PID_MAX;i++)
    
            if (ts->pids[i]) mpegts_close_filter(ts, ts->pids[i]);
    
    
        for(i = 0; i < ts->nb_services; i++){
            av_free(ts->services[i]->provider_name);
            av_free(ts->services[i]->name);
            av_free(ts->services[i]);
        }
        av_freep(&ts->services);
    
    
    static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index,
    
                                  int64_t *ppos, int64_t pos_limit)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        MpegTSContext *ts = s->priv_data;
        int64_t pos, timestamp;
        uint8_t buf[TS_PACKET_SIZE];
        int pcr_l, pid;
    
        const int find_next= 1;
        pos = ((*ppos  + ts->raw_packet_size - 1) / ts->raw_packet_size) * ts->raw_packet_size;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (find_next) {
            for(;;) {
                url_fseek(&s->pb, pos, SEEK_SET);
                if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
                    return AV_NOPTS_VALUE;
                pid = ((buf[1] & 0x1f) << 8) | buf[2];
                if (pid == ts->pcr_pid &&
                    parse_pcr(&timestamp, &pcr_l, buf) == 0) {
                    break;
                }
                pos += ts->raw_packet_size;
            }
        } else {
            for(;;) {
                pos -= ts->raw_packet_size;
                if (pos < 0)
                    return AV_NOPTS_VALUE;
                url_fseek(&s->pb, pos, SEEK_SET);
                if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
                    return AV_NOPTS_VALUE;
                pid = ((buf[1] & 0x1f) << 8) | buf[2];
                if (pid == ts->pcr_pid &&
                    parse_pcr(&timestamp, &pcr_l, buf) == 0) {
                    break;
                }
            }
        }
        *ppos = pos;
    
    
    static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
    
        MpegTSContext *ts = s->priv_data;
        uint8_t buf[TS_PACKET_SIZE];
        int64_t pos;
    
    
        if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0)
    
            return -1;
    
        pos= url_ftell(&s->pb);
    
        for(;;) {
            url_fseek(&s->pb, pos, SEEK_SET);
            if (get_buffer(&s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
                return -1;
    //        pid = ((buf[1] & 0x1f) << 8) | buf[2];
            if(buf[1] & 0x40) break;
            pos += ts->raw_packet_size;
    
        url_fseek(&s->pb, pos, SEEK_SET);
    
        return 0;
    }
    
    
    /**************************************************************/
    /* parsing functions - called from other demuxers such as RTP */
    
    MpegTSContext *mpegts_parse_open(AVFormatContext *s)
    {
        MpegTSContext *ts;
    
        ts = av_mallocz(sizeof(MpegTSContext));
        if (!ts)
            return NULL;
        /* no stream case, currently used by RTP */
        ts->raw_packet_size = TS_PACKET_SIZE;
        ts->stream = s;
        ts->auto_guess = 1;
        return ts;
    }
    
    /* return the consumed length if a packet was output, or -1 if no
       packet is output */
    int mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt,
                            const uint8_t *buf, int len)
    {
        int len1;
    
        len1 = len;
        ts->pkt = pkt;
        ts->stop_parse = 0;
        for(;;) {
            if (ts->stop_parse)
                break;
            if (len < TS_PACKET_SIZE)
                return -1;
            if (buf[0] != 0x47) {
    
                len--;
            } else {
                handle_packet(ts, buf);
                buf += TS_PACKET_SIZE;
                len -= TS_PACKET_SIZE;
            }
        }
        return len1 - len;
    }
    
    void mpegts_parse_close(MpegTSContext *ts)
    {
        int i;
    
        for(i=0;i<NB_PID_MAX;i++)
            av_free(ts->pids[i]);
        av_free(ts);
    }
    
    
    AVInputFormat mpegts_demuxer = {
    
        "mpegts",
        "MPEG2 transport stream format",
        sizeof(MpegTSContext),
        mpegts_probe,
        mpegts_read_header,
        mpegts_read_packet,
        mpegts_read_close,