Skip to content
Snippets Groups Projects
utils.c 91.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Michael Niedermayer's avatar
    Michael Niedermayer committed
                                int64_t pos, int64_t timestamp, int size, int distance, int flags)
    
    {
        AVIndexEntry *entries, *ie;
    
        if((unsigned)st->nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
            return -1;
    
        entries = av_fast_realloc(st->index_entries,
                                  &st->index_entries_allocated_size,
    
                                  (st->nb_index_entries + 1) *
    
        index= av_index_search_timestamp(st, timestamp, AVSEEK_FLAG_ANY);
    
        if(index<0){
    
            index= st->nb_index_entries++;
            ie= &entries[index];
    
            assert(index==0 || ie[-1].timestamp < timestamp);
        }else{
            ie= &entries[index];
            if(ie->timestamp != timestamp){
    
                if(ie->timestamp <= timestamp)
                    return -1;
    
                memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
                st->nb_index_entries++;
    
    Diego Biurrun's avatar
    Diego Biurrun committed
            }else if(ie->pos == pos && distance < ie->min_distance) //do not reduce the distance
    
                distance= ie->min_distance;
    
        ie->pos = pos;
        ie->timestamp = timestamp;
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
        ie->size= size;
    
    int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp,
    
        AVIndexEntry *entries= st->index_entries;
        int nb_entries= st->nb_index_entries;
    
        a = - 1;
        b = nb_entries;
    
        while (b - a > 1) {
            m = (a + b) >> 1;
    
            timestamp = entries[m].timestamp;
    
            if(timestamp >= wanted_timestamp)
                b = m;
            if(timestamp <= wanted_timestamp)
    
        m= (flags & AVSEEK_FLAG_BACKWARD) ? a : b;
    
        if(!(flags & AVSEEK_FLAG_ANY)){
            while(m>=0 && m<nb_entries && !(entries[m].flags & AVINDEX_KEYFRAME)){
                m += (flags & AVSEEK_FLAG_BACKWARD) ? -1 : 1;
            }
        }
    
        if(m == nb_entries)
    
            return -1;
        return  m;
    
    Benoit Fouet's avatar
    Benoit Fouet committed
    #define DEBUG_SEEK
    
    int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
    
        AVInputFormat *avif= s->iformat;
        int64_t pos_min, pos_max, pos, pos_limit;
        int64_t ts_min, ts_max, ts;
    
        av_log(s, AV_LOG_DEBUG, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
    
    Diego Biurrun's avatar
    Diego Biurrun committed
        pos_limit= -1; //gcc falsely says it may be uninitialized
    
    
        st= s->streams[stream_index];
        if(st->index_entries){
            AVIndexEntry *e;
    
    
            index= av_index_search_timestamp(st, target_ts, flags | AVSEEK_FLAG_BACKWARD); //FIXME whole func must be checked for non keyframe entries in index case, especially read_timestamp()
    
            index= FFMAX(index, 0);
    
            e= &st->index_entries[index];
    
            if(e->timestamp <= target_ts || e->pos == e->min_distance){
                pos_min= e->pos;
                ts_min= e->timestamp;
    #ifdef DEBUG_SEEK
    
            av_log(s, AV_LOG_DEBUG, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
    
    
            index= av_index_search_timestamp(st, target_ts, flags & ~AVSEEK_FLAG_BACKWARD);
    
            assert(index < st->nb_index_entries);
            if(index >= 0){
    
                e= &st->index_entries[index];
                assert(e->timestamp >= target_ts);
                pos_max= e->pos;
                ts_max= e->timestamp;
                pos_limit= pos_max - e->min_distance;
    #ifdef DEBUG_SEEK
    
            av_log(s, AV_LOG_DEBUG, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n",
    
        pos= av_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit, ts_min, ts_max, flags, &ts, avif->read_timestamp);
        if(pos<0)
            return -1;
    
        /* do the seek */
    
        url_fseek(s->pb, pos, SEEK_SET);
    
    
        av_update_cur_dts(s, st, ts);
    
        return 0;
    }
    
    int64_t av_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, int64_t pos_min, int64_t pos_max, int64_t pos_limit, int64_t ts_min, int64_t ts_max, int flags, int64_t *ts_ret, int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )){
        int64_t pos, ts;
        int64_t start_pos, filesize;
        int no_change;
    
    #ifdef DEBUG_SEEK
        av_log(s, AV_LOG_DEBUG, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
    #endif
    
    
        if(ts_min == AV_NOPTS_VALUE){
            pos_min = s->data_offset;
    
            ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
    
            if (ts_min == AV_NOPTS_VALUE)
                return -1;
        }
    
        if(ts_max == AV_NOPTS_VALUE){
            int step= 1024;
    
            filesize = url_fsize(s->pb);
    
                ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step);
    
                step += step;
            }while(ts_max == AV_NOPTS_VALUE && pos_max >= step);
            if (ts_max == AV_NOPTS_VALUE)
                return -1;
    
                int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
    
                if(tmp_ts == AV_NOPTS_VALUE)
                    break;
                ts_max= tmp_ts;
                pos_max= tmp_pos;
    
        if(ts_min > ts_max){
            return -1;
        }else if(ts_min == ts_max){
            pos_limit= pos_min;
        }
    
    
        no_change=0;
        while (pos_min < pos_limit) {
    #ifdef DEBUG_SEEK
    
            av_log(s, AV_LOG_DEBUG, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n",
    
                   pos_min, pos_max,
                   ts_min, ts_max);
    #endif
            assert(pos_limit <= pos_max);
    
            if(no_change==0){
                int64_t approximate_keyframe_distance= pos_max - pos_limit;
                // interpolate position (better than dichotomy)
    
                pos = av_rescale(target_ts - ts_min, pos_max - pos_min, ts_max - ts_min)
                    + pos_min - approximate_keyframe_distance;
    
            }else if(no_change==1){
                // bisection, if interpolation failed to change min or max pos last time
                pos = (pos_min + pos_limit)>>1;
            }else{
                // linear search if bisection failed, can only happen if there are very few or no keframes between min/max
                pos=pos_min;
            }
            if(pos <= pos_min)
                pos= pos_min + 1;
            else if(pos > pos_limit)
                pos= pos_limit;
            start_pos= pos;
    
    
            ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1
    
            if(pos == pos_max)
                no_change++;
            else
                no_change=0;
    #ifdef DEBUG_SEEK
    
    av_log(s, AV_LOG_DEBUG, "%"PRId64" %"PRId64" %"PRId64" / %"PRId64" %"PRId64" %"PRId64" target:%"PRId64" limit:%"PRId64" start:%"PRId64" noc:%d\n", pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit, start_pos, no_change);
    
            if(ts == AV_NOPTS_VALUE){
                av_log(s, AV_LOG_ERROR, "read_timestamp() failed in the middle\n");
                return -1;
            }
    
            if (target_ts <= ts) {
    
                pos_limit = start_pos - 1;
                pos_max = pos;
                ts_max = ts;
    
            }
            if (target_ts >= ts) {
    
        pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
        ts  = (flags & AVSEEK_FLAG_BACKWARD) ?  ts_min :  ts_max;
    
        ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
    
        ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
    
        av_log(s, AV_LOG_DEBUG, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n",
    
    static int av_seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, int flags){
        int64_t pos_min, pos_max;
    #if 0
        AVStream *st;
    
        if (stream_index < 0)
            return -1;
    
        st= s->streams[stream_index];
    #endif
    
        pos_min = s->data_offset;
    
        pos_max = url_fsize(s->pb) - 1;
    
    
        if     (pos < pos_min) pos= pos_min;
        else if(pos > pos_max) pos= pos_max;
    
    
        url_fseek(s->pb, pos, SEEK_SET);
    
        av_update_cur_dts(s, st, ts);
    
    static int av_seek_frame_generic(AVFormatContext *s,
    
                                     int stream_index, int64_t timestamp, int flags)
    
        int index;
    
        AVStream *st;
        AVIndexEntry *ie;
    
        st = s->streams[stream_index];
    
        index = av_index_search_timestamp(st, timestamp, flags);
    
        if(index < 0 || index==st->nb_index_entries-1){
    
            int i;
            AVPacket pkt;
    
            if(st->index_entries && st->nb_index_entries){
                ie= &st->index_entries[st->nb_index_entries-1];
    
                url_fseek(s->pb, ie->pos, SEEK_SET);
    
                av_update_cur_dts(s, st, ie->timestamp);
            }else
    
                url_fseek(s->pb, 0, SEEK_SET);
    
    
            for(i=0;; i++) {
                int ret = av_read_frame(s, &pkt);
                if(ret<0)
                    break;
                av_free_packet(&pkt);
                if(stream_index == pkt.stream_index){
                    if((pkt.flags & PKT_FLAG_KEY) && pkt.dts > timestamp)
                        break;
                }
            }
            index = av_index_search_timestamp(st, timestamp, flags);
        }
    
        if (index < 0)
            return -1;
    
        av_read_frame_flush(s);
    
        if (s->iformat->read_seek){
            if(s->iformat->read_seek(s, stream_index, timestamp, flags) >= 0)
                return 0;
        }
        ie = &st->index_entries[index];
    
        url_fseek(s->pb, ie->pos, SEEK_SET);
    
        av_update_cur_dts(s, st, ie->timestamp);
    
    int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
    
        av_read_frame_flush(s);
    
        if(flags & AVSEEK_FLAG_BYTE)
            return av_seek_frame_byte(s, stream_index, timestamp, flags);
    
        if(stream_index < 0){
            stream_index= av_find_default_stream_index(s);
            if(stream_index < 0)
                return -1;
    
            st= s->streams[stream_index];
    
           /* timestamp for default must be expressed in AV_TIME_BASE units */
    
            timestamp = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
    
        /* first, we try the format specific seek */
        if (s->iformat->read_seek)
    
            ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
    
            return av_seek_frame_binary(s, stream_index, timestamp, flags);
    
            return av_seek_frame_generic(s, stream_index, timestamp, flags);
    
    /*******************************************************/
    
     * Returns TRUE if the stream has accurate duration in any stream.
    
     * @return TRUE if the stream has accurate duration for at least one component.
    
    static int av_has_duration(AVFormatContext *ic)
    
    {
        int i;
        AVStream *st;
    
        for(i = 0;i < ic->nb_streams; i++) {
            st = ic->streams[i];
    
            if (st->duration != AV_NOPTS_VALUE)
    
    /**
     * Estimate the stream timings from the one of each components.
     *
     * Also computes the global bitrate if possible.
     */
    
    static void av_update_stream_timings(AVFormatContext *ic)
    {
    
        int64_t start_time, start_time1, end_time, end_time1;
    
        start_time = INT64_MAX;
        end_time = INT64_MIN;
    
        for(i = 0;i < ic->nb_streams; i++) {
            st = ic->streams[i];
            if (st->start_time != AV_NOPTS_VALUE) {
    
                start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
                if (start_time1 < start_time)
                    start_time = start_time1;
    
                    end_time1 = start_time1
                              + av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
    
            if (st->duration != AV_NOPTS_VALUE) {
                duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
                if (duration1 > duration)
                    duration = duration1;
            }
    
        if (start_time != INT64_MAX) {
    
            if (end_time != INT64_MIN) {
    
                if (end_time - start_time > duration)
                    duration = end_time - start_time;
            }
        }
        if (duration != INT64_MIN) {
            ic->duration = duration;
            if (ic->file_size > 0) {
                /* compute the bit rate */
                ic->bit_rate = (double)ic->file_size * 8.0 * AV_TIME_BASE /
                    (double)ic->duration;
    
            }
        }
    }
    
    static void fill_all_stream_timings(AVFormatContext *ic)
    {
        int i;
        AVStream *st;
    
        av_update_stream_timings(ic);
        for(i = 0;i < ic->nb_streams; i++) {
            st = ic->streams[i];
            if (st->start_time == AV_NOPTS_VALUE) {
    
                if(ic->start_time != AV_NOPTS_VALUE)
                    st->start_time = av_rescale_q(ic->start_time, AV_TIME_BASE_Q, st->time_base);
                if(ic->duration != AV_NOPTS_VALUE)
                    st->duration = av_rescale_q(ic->duration, AV_TIME_BASE_Q, st->time_base);
    
            }
        }
    }
    
    static void av_estimate_timings_from_bit_rate(AVFormatContext *ic)
    {
        int64_t filesize, duration;
        int bit_rate, i;
        AVStream *st;
    
        /* if bit_rate is already set, we believe it */
        if (ic->bit_rate == 0) {
            bit_rate = 0;
            for(i=0;i<ic->nb_streams;i++) {
                st = ic->streams[i];
    
            }
            ic->bit_rate = bit_rate;
        }
    
        /* if duration is already set, we believe it */
    
        if (ic->duration == AV_NOPTS_VALUE &&
            ic->bit_rate != 0 &&
    
            ic->file_size != 0)  {
            filesize = ic->file_size;
            if (filesize > 0) {
                for(i = 0; i < ic->nb_streams; i++) {
                    st = ic->streams[i];
    
                    duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num);
    
                    if (st->duration == AV_NOPTS_VALUE)
    
                        st->duration = duration;
                }
            }
        }
    }
    
    #define DURATION_MAX_READ_SIZE 250000
    
    /* only usable for MPEG-PS streams */
    
    static void av_estimate_timings_from_pts(AVFormatContext *ic, offset_t old_offset)
    
    {
        AVPacket pkt1, *pkt = &pkt1;
        AVStream *st;
        int read_size, i, ret;
    
        int64_t end_time;
    
        /* free previous packet */
        if (ic->cur_st && ic->cur_st->parser)
            av_free_packet(&ic->cur_pkt);
        ic->cur_st = NULL;
    
        /* flush packet queue */
        flush_packet_queue(ic);
    
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
            if (st->parser) {
                av_parser_close(st->parser);
                st->parser= NULL;
            }
        }
    
        /* we read the first packets to get the first PTS (not fully
           accurate, but it is enough now) */
    
        url_fseek(ic->pb, 0, SEEK_SET);
    
        read_size = 0;
        for(;;) {
            if (read_size >= DURATION_MAX_READ_SIZE)
                break;
            /* if all info is available, we can stop */
            for(i = 0;i < ic->nb_streams; i++) {
                st = ic->streams[i];
                if (st->start_time == AV_NOPTS_VALUE)
                    break;
            }
            if (i == ic->nb_streams)
                break;
    
            ret = av_read_packet(ic, pkt);
            if (ret != 0)
                break;
            read_size += pkt->size;
            st = ic->streams[pkt->stream_index];
            if (pkt->pts != AV_NOPTS_VALUE) {
                if (st->start_time == AV_NOPTS_VALUE)
    
                    st->start_time = pkt->pts;
    
    
        /* estimate the end time (duration) */
        /* XXX: may need to support wrapping */
        filesize = ic->file_size;
        offset = filesize - DURATION_MAX_READ_SIZE;
        if (offset < 0)
            offset = 0;
    
    
        url_fseek(ic->pb, offset, SEEK_SET);
    
        read_size = 0;
        for(;;) {
            if (read_size >= DURATION_MAX_READ_SIZE)
                break;
    
            ret = av_read_packet(ic, pkt);
            if (ret != 0)
                break;
            read_size += pkt->size;
            st = ic->streams[pkt->stream_index];
    
            if (pkt->pts != AV_NOPTS_VALUE &&
                st->start_time != AV_NOPTS_VALUE) {
    
                end_time = pkt->pts;
    
                duration = end_time - st->start_time;
                if (duration > 0) {
                    if (st->duration == AV_NOPTS_VALUE ||
                        st->duration < duration)
                        st->duration = duration;
                }
            }
            av_free_packet(pkt);
        }
    
        fill_all_stream_timings(ic);
    
        url_fseek(ic->pb, old_offset, SEEK_SET);
    
        for(i=0; i<ic->nb_streams; i++){
            st= ic->streams[i];
            st->cur_dts= st->first_dts;
    
            st->last_IP_pts = AV_NOPTS_VALUE;
    
    static void av_estimate_timings(AVFormatContext *ic, offset_t old_offset)
    
    {
        int64_t file_size;
    
        /* get the file size, if possible */
        if (ic->iformat->flags & AVFMT_NOFILE) {
            file_size = 0;
        } else {
    
            file_size = url_fsize(ic->pb);
    
            if (file_size < 0)
                file_size = 0;
        }
        ic->file_size = file_size;
    
    
        if ((!strcmp(ic->iformat->name, "mpeg") ||
             !strcmp(ic->iformat->name, "mpegts")) &&
    
            file_size && !ic->pb->is_streamed) {
    
            /* get accurate estimate from the PTSes */
    
            av_estimate_timings_from_pts(ic, old_offset);
    
        } else if (av_has_duration(ic)) {
    
            /* at least one components has timings - we use them for all
               the components */
            fill_all_stream_timings(ic);
        } else {
            /* less precise: use bit rate info */
            av_estimate_timings_from_bit_rate(ic);
        }
        av_update_stream_timings(ic);
    
    #if 0
        {
            int i;
            AVStream *st;
            for(i = 0;i < ic->nb_streams; i++) {
                st = ic->streams[i];
    
            printf("%d: start_time: %0.3f duration: %0.3f\n",
                   i, (double)st->start_time / AV_TIME_BASE,
    
            printf("stream: start_time: %0.3f duration: %0.3f bitrate=%d kb/s\n",
                   (double)ic->start_time / AV_TIME_BASE,
    
                   (double)ic->duration / AV_TIME_BASE,
                   ic->bit_rate / 1000);
        }
    #endif
    }
    
    
    static int has_codec_parameters(AVCodecContext *enc)
    {
        int val;
        switch(enc->codec_type) {
        case CODEC_TYPE_AUDIO:
            val = enc->sample_rate;
            break;
        case CODEC_TYPE_VIDEO:
    
            val = enc->width && enc->pix_fmt != PIX_FMT_NONE;
    
        return (enc->codec_id != CODEC_ID_NONE && val != 0);
    
    static int try_decode_frame(AVStream *st, const uint8_t *data, int size)
    {
        int16_t *samples;
        AVCodec *codec;
    
        int got_picture, data_size, ret=0;
    
      if(!st->codec->codec){
        codec = avcodec_find_decoder(st->codec->codec_id);
    
      if(!has_codec_parameters(st->codec)){
        switch(st->codec->codec_type) {
    
            ret = avcodec_decode_video(st->codec, &picture,
    
                                       &got_picture, (uint8_t *)data, size);
            break;
        case CODEC_TYPE_AUDIO:
    
            data_size = FFMAX(size, AVCODEC_MAX_AUDIO_FRAME_SIZE);
            samples = av_malloc(data_size);
    
            ret = avcodec_decode_audio2(st->codec, samples,
                                        &data_size, (uint8_t *)data, size);
    
    John Donaghy's avatar
    John Donaghy committed
    static int set_codec_from_probe_data(AVStream *st, AVProbeData *pd, int score)
    {
        AVInputFormat *fmt;
        fmt = av_probe_input_format2(pd, 1, &score);
    
        if (fmt) {
            if (strncmp(fmt->name, "mp3", 3) == 0)
                st->codec->codec_id = CODEC_ID_MP3;
            else if (strncmp(fmt->name, "ac3", 3) == 0)
                st->codec->codec_id = CODEC_ID_AC3;
        }
    
    unsigned int codec_get_tag(const AVCodecTag *tags, int id)
    {
        while (tags->id != CODEC_ID_NONE) {
            if (tags->id == id)
                return tags->tag;
            tags++;
        }
        return 0;
    }
    
    enum CodecID codec_get_id(const AVCodecTag *tags, unsigned int tag)
    {
    
        int i;
        for(i=0; tags[i].id != CODEC_ID_NONE;i++) {
            if(tag == tags[i].tag)
                return tags[i].id;
        }
        for(i=0; tags[i].id != CODEC_ID_NONE; i++) {
            if(   toupper((tag >> 0)&0xFF) == toupper((tags[i].tag >> 0)&0xFF)
               && toupper((tag >> 8)&0xFF) == toupper((tags[i].tag >> 8)&0xFF)
               && toupper((tag >>16)&0xFF) == toupper((tags[i].tag >>16)&0xFF)
               && toupper((tag >>24)&0xFF) == toupper((tags[i].tag >>24)&0xFF))
                return tags[i].id;
    
        }
        return CODEC_ID_NONE;
    }
    
    unsigned int av_codec_get_tag(const AVCodecTag *tags[4], enum CodecID id)
    {
        int i;
        for(i=0; tags && tags[i]; i++){
            int tag= codec_get_tag(tags[i], id);
            if(tag) return tag;
        }
        return 0;
    }
    
    enum CodecID av_codec_get_id(const AVCodecTag *tags[4], unsigned int tag)
    {
        int i;
        for(i=0; tags && tags[i]; i++){
            enum CodecID id= codec_get_id(tags[i], tag);
            if(id!=CODEC_ID_NONE) return id;
        }
        return CODEC_ID_NONE;
    }
    
    
    /* absolute maximum size we read until we abort */
    #define MAX_READ_SIZE        5000000
    
    
    #define MAX_STD_TIMEBASES (60*12+5)
    
    static int get_std_framerate(int i){
        if(i<60*12) return i*1001;
    
        else        return ((int[]){24,30,60,12,15})[i-60*12]*1000*12;
    
        int i, count, ret, read_size, j;
    
        int64_t last_dts[MAX_STREAMS];
    
        int duration_count[MAX_STREAMS]={0};
    
        double (*duration_error)[MAX_STD_TIMEBASES];
    
        offset_t old_offset = url_ftell(ic->pb);
    
        int64_t codec_info_duration[MAX_STREAMS]={0};
        int codec_info_nb_frames[MAX_STREAMS]={0};
    
    John Donaghy's avatar
    John Donaghy committed
        AVProbeData probe_data[MAX_STREAMS];
        int codec_identified[MAX_STREAMS]={0};
    
        duration_error = av_mallocz(MAX_STREAMS * sizeof(*duration_error));
    
        if (!duration_error) return AVERROR(ENOMEM);
    
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
    
            if(st->codec->codec_type == CODEC_TYPE_VIDEO){
    
    /*            if(!st->time_base.num)
                    st->time_base= */
    
                if(!st->codec->time_base.num)
                    st->codec->time_base= st->time_base;
    
                st->parser = av_parser_init(st->codec->codec_id);
    
                if(st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser){
    
                    st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
                }
    
        for(i=0;i<MAX_STREAMS;i++){
            last_dts[i]= AV_NOPTS_VALUE;
        }
    
    John Donaghy's avatar
    John Donaghy committed
        memset(probe_data, 0, sizeof(probe_data));
    
        count = 0;
        read_size = 0;
        for(;;) {
            /* check if one codec still needs to be handled */
            for(i=0;i<ic->nb_streams;i++) {
                st = ic->streams[i];
    
                /* variable fps and no guess at the real fps */
    
                if(   (st->codec->time_base.den >= 101LL*st->codec->time_base.num || st->codec->codec_id == CODEC_ID_MPEG2VIDEO)
    
                   && duration_count[i]<20 && st->codec->codec_type == CODEC_TYPE_VIDEO)
    
                if(st->parser && st->parser->parser->split && !st->codec->extradata)
    
                if(st->first_dts == AV_NOPTS_VALUE)
                    break;
    
            }
            if (i == ic->nb_streams) {
                /* NOTE: if the format has no header, then we need to read
                   some packets to get most of the streams, so we cannot
                   stop here */
    
                if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
    
                    /* if we found the info for all the codecs, we can stop */
                    ret = count;
                    break;
                }
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
            /* we did not get all the codec info, but we read too much data */
            if (read_size >= MAX_READ_SIZE) {
                ret = count;
                break;
            }
    
            /* NOTE: a new stream can be added there if no header in file
               (AVFMTCTX_NOHEADER) */
            ret = av_read_frame_internal(ic, &pkt1);
            if (ret < 0) {
                /* EOF or error */
                ret = -1; /* we could not have all the codec parameters before EOF */
    
                for(i=0;i<ic->nb_streams;i++) {
                    st = ic->streams[i];
    
                    if (!has_codec_parameters(st->codec)){
                        char buf[256];
                        avcodec_string(buf, sizeof(buf), st->codec, 0);
                        av_log(ic, AV_LOG_INFO, "Could not find codec parameters (%s)\n", buf);
    
            pkt= add_to_pktbuf(ic, &pkt1);
            if(av_dup_packet(pkt) < 0)
                return AVERROR(ENOMEM);
    
            read_size += pkt->size;
    
            if(codec_info_nb_frames[st->index]>1)
                codec_info_duration[st->index] += pkt->duration;
    
            if (pkt->duration != 0)
    
                codec_info_nb_frames[st->index]++;
    
                int index= pkt->stream_index;
                int64_t last= last_dts[index];
                int64_t duration= pkt->dts - last;
    
                if(pkt->dts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && duration>0){
    
                    double dur= duration * av_q2d(st->time_base);
    
    //                if(st->codec->codec_type == CODEC_TYPE_VIDEO)
    //                    av_log(NULL, AV_LOG_ERROR, "%f\n", dur);
    
                    if(duration_count[index] < 2)
    
                        memset(duration_error, 0, MAX_STREAMS * sizeof(*duration_error));
    
    Michael Niedermayer's avatar
    Michael Niedermayer committed
                    for(i=1; i<MAX_STD_TIMEBASES; i++){
                        int framerate= get_std_framerate(i);
                        int ticks= lrintf(dur*framerate/(1001*12));
                        double error= dur - ticks*1001*12/(double)framerate;
                        duration_error[index][i] += error*error;
                    }
    
                    duration_count[index]++;
    
                if(last == AV_NOPTS_VALUE || duration_count[index]<=1)
                    last_dts[pkt->stream_index]= pkt->dts;
    
    John Donaghy's avatar
    John Donaghy committed
    
                if (st->codec->codec_id == CODEC_ID_NONE) {
                    AVProbeData *pd = &(probe_data[st->index]);
    
                    pd->buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);
    
    John Donaghy's avatar
    John Donaghy committed
                    memcpy(pd->buf+pd->buf_size, pkt->data, pkt->size);
                    pd->buf_size += pkt->size;
    
                    memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE);
    
            if(st->parser && st->parser->parser->split && !st->codec->extradata){
                int i= st->parser->parser->split(st->codec, pkt->data, pkt->size);
    
                    st->codec->extradata= av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
    
                    memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size);
    
                    memset(st->codec->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE);
    
            /* if still no information, we try to open the codec and to
               decompress the frame. We try to avoid that in most cases as
               it takes longer and uses more memory. For MPEG4, we need to
               decompress for Quicktime. */
    
            if (!has_codec_parameters(st->codec) /*&&
                (st->codec->codec_id == CODEC_ID_FLV1 ||
                 st->codec->codec_id == CODEC_ID_H264 ||
                 st->codec->codec_id == CODEC_ID_H263 ||
                 st->codec->codec_id == CODEC_ID_H261 ||
                 st->codec->codec_id == CODEC_ID_VORBIS ||
                 st->codec->codec_id == CODEC_ID_MJPEG ||
                 st->codec->codec_id == CODEC_ID_PNG ||
                 st->codec->codec_id == CODEC_ID_PAM ||
                 st->codec->codec_id == CODEC_ID_PGM ||
                 st->codec->codec_id == CODEC_ID_PGMYUV ||
                 st->codec->codec_id == CODEC_ID_PBM ||
                 st->codec->codec_id == CODEC_ID_PPM ||
                 st->codec->codec_id == CODEC_ID_SHORTEN ||
                 (st->codec->codec_id == CODEC_ID_MPEG4 && !st->need_parsing))*/)
    
                try_decode_frame(st, pkt->data, pkt->size);
    
            if (st->time_base.den > 0 && av_rescale_q(codec_info_duration[st->index], st->time_base, AV_TIME_BASE_Q) >= ic->max_analyze_duration) {
    
        // close codecs which where opened in try_decode_frame()
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
    
            if(st->codec->codec)
                avcodec_close(st->codec);
    
            if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
                if(st->codec->codec_id == CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_sample)
                    st->codec->codec_tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
    
                   && (st->codec->time_base.num*101LL <= st->codec->time_base.den || st->codec->codec_id == CODEC_ID_MPEG2VIDEO) /*&&
    
                   //FIXME we should not special case mpeg2, but this needs testing with non mpeg2 ...
    
                   st->time_base.num*duration_sum[i]/duration_count[i]*101LL > st->time_base.den*/){
                    double best_error= 2*av_q2d(st->time_base);
                    best_error= best_error*best_error*duration_count[i]*1000*12*30;
    
                    for(j=1; j<MAX_STD_TIMEBASES; j++){
                        double error= duration_error[i][j] * get_std_framerate(j);
    //                    if(st->codec->codec_type == CODEC_TYPE_VIDEO)
    //                        av_log(NULL, AV_LOG_ERROR, "%f %f\n", get_std_framerate(j) / 12.0/1001, error);
    
                        if(error < best_error){
                            best_error= error;
    
                            av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, get_std_framerate(j), 12*1001, INT_MAX);
    
                if (!st->r_frame_rate.num){
    
                    if(    st->codec->time_base.den * (int64_t)st->time_base.num
                        <= st->codec->time_base.num * (int64_t)st->time_base.den){
                        st->r_frame_rate.num = st->codec->time_base.den;
                        st->r_frame_rate.den = st->codec->time_base.num;
                    }else{
                        st->r_frame_rate.num = st->time_base.den;
                        st->r_frame_rate.den = st->time_base.num;
                    }
    
            }else if(st->codec->codec_type == CODEC_TYPE_AUDIO) {
    
                if (st->codec->codec_id == CODEC_ID_NONE && probe_data[st->index].buf_size > 0) {
    
                    codec_identified[st->index] = set_codec_from_probe_data(st, &(probe_data[st->index]), 1);
    
    John Donaghy's avatar
    John Donaghy committed
                    if (codec_identified[st->index]) {
    
                        st->need_parsing = AVSTREAM_PARSE_FULL;
    
                if(!st->codec->bits_per_sample)
                    st->codec->bits_per_sample= av_get_bits_per_sample(st->codec->codec_id);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        av_estimate_timings(ic, old_offset);
    
    John Donaghy's avatar
    John Donaghy committed
    
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];
    
            if (codec_identified[st->index])
                break;
        }
        //FIXME this is a mess
        if(i!=ic->nb_streams){
            av_read_frame_flush(ic);
            for(i=0;i<ic->nb_streams;i++) {
                st = ic->streams[i];
                if (codec_identified[st->index]) {
                    av_seek_frame(ic, st->index, 0.0, 0);
                }
                st->cur_dts= st->first_dts;
    
            url_fseek(ic->pb, ic->data_offset, SEEK_SET);
    
    #if 0
        /* correct DTS for b frame streams with no timestamps */
        for(i=0;i<ic->nb_streams;i++) {
            st = ic->streams[i];