Skip to content
Snippets Groups Projects
mpegts.c 44.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • {
        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 = AV_RB16(packet + 1) & 0x1fff;
    
        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;
    
        pos = url_ftell(ts->stream->pb);
        ts->pos47= pos % ts->raw_packet_size;
    
        if (tss->type == MPEGTS_SECTION) {
            if (is_start) {
                /* pointer field present */
                len = *p++;
                if (p + len > p_end)
                    return;
                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,
    
            // Note: The position here points actually behind the current packet.
    
                                     p, p_end - p, is_start, pos - ts->raw_packet_size);
    
    /* 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)
    
            /* 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;
    
                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;
    
        int check_count= size / TS_FEC_PACKET_SIZE;
    
    #define CHECK_COUNT 10
    
        score     = analyze(p->buf, TS_PACKET_SIZE     *check_count, TS_PACKET_SIZE     , NULL)*CHECK_COUNT/check_count;
        dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)*CHECK_COUNT/check_count;
        fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)*CHECK_COUNT/check_count;
    
    //    av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
    
    // we need a clear definition for the returned score otherwise things will become messy sooner or later
    
        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
    
    Diego Biurrun's avatar
    Diego Biurrun committed
    /* return the 90kHz PCR and the extension for the 27MHz PCR. return
    
    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 = AV_RB32(p);
    
        *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;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        int len;
    
        if (ap) {
            ts->mpeg2ts_compute_pcr = ap->mpeg2ts_compute_pcr;
    
            if(ap->mpeg2ts_raw){
                av_log(s, AV_LOG_ERROR, "use mpegtsraw_demuxer!\n");
                return -1;
            }
    
        /* 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;
    
    Benoit Fouet's avatar
    Benoit Fouet committed
            /* first do a scaning to get all the services */
            url_fseek(pb, pos, SEEK_SET);
    
    
            mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
    
            mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
            handle_packets(ts, s->probesize);
    
    Benoit Fouet's avatar
    Benoit Fouet committed
            /* if could not find service, enable auto_guess */
    
    Benoit Fouet's avatar
    Benoit Fouet committed
            ts->auto_guess = 1;
    
            dprintf(ts->stream, "tuning done\n");
    
    
            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);
    
                pid = AV_RB16(packet + 1) & 0x1fff;
    
                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++;
            }
    
            /* 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(ts->stream, 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)
    
        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 */
    
                    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)) /
    
                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;
    
    }
    
    static int mpegts_read_close(AVFormatContext *s)
    {
        MpegTSContext *ts = s->priv_data;
        int i;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
    
        clear_programs(ts);
    
    
        for(i=0;i<NB_PID_MAX;i++)
    
            if (ts->pids[i]) mpegts_close_filter(ts, ts->pids[i]);
    
    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, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid;
    
        pos = ((*ppos  + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47;
    
    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)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return AV_NOPTS_VALUE;
    
                if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return AV_NOPTS_VALUE;
    
                if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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)
    
            url_fseek(s->pb, pos, SEEK_SET);
            if (get_buffer(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE)
    
    //        pid = AV_RB16(buf + 1) & 0x1fff;
    
            if(buf[1] & 0x40) break;
            pos += ts->raw_packet_size;
    
        url_fseek(s->pb, pos, SEEK_SET);
    
    /**************************************************************/
    /* 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(;;) {
    
                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 = {
    
        NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
    
        sizeof(MpegTSContext),
        mpegts_probe,
        mpegts_read_header,
        mpegts_read_packet,
        mpegts_read_close,
    
        .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,
    
        NULL_IF_CONFIG_SMALL("MPEG-2 raw transport stream format"),
    
        mpegts_read_header,
        mpegts_raw_read_packet,
        mpegts_read_close,
        read_seek,
        mpegts_get_pcr,
    
        .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,