Skip to content
Snippets Groups Projects
mpegts.c 43 KiB
Newer Older
  • Learn to ignore specific revisions
  •             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;
    #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);
        fec_score= analyze(p->buf, TS_FEC_PACKET_SIZE*CHECK_COUNT, TS_FEC_PACKET_SIZE, NULL);
    //    av_log(NULL, AV_LOG_DEBUG, "score: %d, fec_score: %d \n", 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 > 6) return AVPROBE_SCORE_MAX + 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
    
    }
    
    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;
    
                /* first do a scaning to get all the services */
    
                handle_packets(ts, MAX_SCAN_PACKETS);
                
                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);
                    
                    handle_packets(ts, MAX_SCAN_PACKETS);
                }
                
    
                if (ts->nb_services <= 0) {
    		/* raw transport stream */
    		ts->auto_guess = 1;
    		s->ctx_flags |= AVFMTCTX_NOHEADER;
    		goto do_pcr;
    	    }
    
                
                /* tune to first service found */
                service = ts->services[0];
                sid = service->sid;
    
                
                /* 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);
                
                handle_packets(ts, MAX_SCAN_PACKETS);
                
                /* if could not find service, exit */
                if (ts->set_service_ret != 0)
                    return -1;
                
    #ifdef DEBUG_SI
                printf("tuning done\n");
    #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];
            
            /* only read packets */
    
            st = av_new_stream(s, 0);
            if (!st)
                goto fail;
            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->codec.bit_rate = s->bit_rate;
            st->start_time = ts->cur_pcr * 1000000.0 / 27.0e6;
    #if 0
            printf("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;
        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]);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, 
                                  int64_t *ppos, int find_next)
    {
        MpegTSContext *ts = s->priv_data;
        int64_t pos, timestamp;
        uint8_t buf[TS_PACKET_SIZE];
        int pcr_l, pid;
    
        pos = *ppos;
        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;
        return timestamp;
    }
    
    typedef int64_t ReadTimestampFunc(AVFormatContext *s, int stream_index, 
                                      int64_t *ppos, int find_next);
    
    static int64_t do_block_align(int64_t val, int block_align)
    {
        return (val / block_align) * block_align;
    }
    
    /* XXX: use it in other formats */
    static int timestamp_read_seek(AVFormatContext *s, 
                                   int stream_index, int64_t timestamp,
                                   ReadTimestampFunc *read_timestamp,
                                   int block_align)
    {
        int64_t pos_min, pos_max, pos;
        int64_t dts_min, dts_max, dts;
    
    #ifdef DEBUG_SEEK
        printf("read_seek: %d %0.3f\n", stream_index, timestamp / 90000.0);
    #endif
    
        pos_min = 0;
        dts_min = read_timestamp(s, stream_index, &pos_min, 1);
        if (dts_min == AV_NOPTS_VALUE) {
            /* we can reach this case only if no PTS are present in
               the whole stream */
            return -1;
        }
        pos_max = do_block_align(url_filesize(url_fileno(&s->pb)), block_align) - 
            block_align;
        dts_max = read_timestamp(s, stream_index, &pos_max, 0);
        
        while (pos_min <= pos_max) {
    #ifdef DEBUG_SEEK
            printf("pos_min=0x%llx pos_max=0x%llx dts_min=%0.3f dts_max=%0.3f\n", 
                   pos_min, pos_max,
                   dts_min / 90000.0, dts_max / 90000.0);
    #endif
            if (timestamp <= dts_min) {
                pos = pos_min;
                goto found;
            } else if (timestamp >= dts_max) {
                pos = pos_max;
                goto found;
            } else {
                /* interpolate position (better than dichotomy) */
                pos = (int64_t)((double)(pos_max - pos_min) * 
                                (double)(timestamp - dts_min) /
                                (double)(dts_max - dts_min)) + pos_min;
                pos = do_block_align(pos, block_align);
            }
    #ifdef DEBUG_SEEK
            printf("pos=0x%llx\n", pos);
    #endif
            /* read the next timestamp */
            dts = read_timestamp(s, stream_index, &pos, 1);
            /* check if we are lucky */
            if (dts == AV_NOPTS_VALUE) {
                /* should never happen */
                pos = pos_min;
                goto found;
            } else if (timestamp == dts) {
                goto found;
            } else if (timestamp < dts) {
                pos_max = pos;
                dts_max = read_timestamp(s, stream_index, &pos_max, 0);
                if (dts_max == AV_NOPTS_VALUE) {
                    /* should never happen */
                    break;
                } else if (timestamp >= dts_max) {
                    pos = pos_max;
                    goto found;
                }
            } else {
                pos_min = pos + block_align;
                dts_min = read_timestamp(s, stream_index, &pos_min, 1);
                if (dts_min == AV_NOPTS_VALUE) {
                    /* should never happen */
                    goto found;
                } else if (timestamp <= dts_min) {
                    goto found;
                }
            }
        }
        pos = pos_min;
     found:
    #ifdef DEBUG_SEEK
        pos_min = pos;
        dts_min = read_timestamp(s, stream_index, &pos_min, 1);
        pos_min += block_align;
        dts_max = read_timestamp(s, stream_index, &pos_min, 1);
        printf("pos=0x%llx %0.3f<=%0.3f<=%0.3f\n", 
               pos, dts_min / 90000.0, timestamp / 90000.0, dts_max / 90000.0);
    #endif
        /* do the seek */
        url_fseek(&s->pb, pos, SEEK_SET);
        return 0;
    }
    
    static int mpegts_read_seek(AVFormatContext *s, 
                                int stream_index, int64_t timestamp)
    {
        MpegTSContext *ts = s->priv_data;
    
        timestamp = (timestamp * 90000) / AV_TIME_BASE;
    
        return timestamp_read_seek(s, stream_index, timestamp, 
                                   mpegts_get_pcr, ts->raw_packet_size);
    }
    
    
    /**************************************************************/
    /* 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) {
                buf--;
                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_demux = {
        "mpegts",
        "MPEG2 transport stream format",
        sizeof(MpegTSContext),
        mpegts_probe,
        mpegts_read_header,
        mpegts_read_packet,
        mpegts_read_close,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        mpegts_read_seek,
    
    };
    
    int mpegts_init(void)
    {
        av_register_input_format(&mpegts_demux);