changeset 437:50bae308f71e libavformat

moving nearly identical binary search code from nut/mpeg/asf to utils.c
author michael
date Mon, 12 Apr 2004 16:50:03 +0000
parents dd042d607ec9
children b3fdae08c556
files asf.c avformat.h mpeg.c nut.c utils.c
diffstat 5 files changed, 198 insertions(+), 413 deletions(-) [+]
line wrap: on
line diff
--- a/asf.c	Sun Apr 11 19:32:24 2004 +0000
+++ b/asf.c	Mon Apr 12 16:50:03 2004 +0000
@@ -422,6 +422,8 @@
     int rsize = 9;
     int c;
     
+    if((url_ftell(&s->pb) - s->data_offset) % asf->packet_size)
+        return -1;
     assert((url_ftell(&s->pb) - s->data_offset) % asf->packet_size == 0);
     
     c = get_byte(pb);
@@ -705,7 +707,7 @@
     asf->asf_st= NULL;
 }
 
-static int64_t asf_read_pts(AVFormatContext *s, int64_t *ppos, int stream_index)
+static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit)
 {
     ASFContext *asf = s->priv_data;
     AVPacket pkt1, *pkt = &pkt1;
@@ -718,15 +720,19 @@
     for(i=0; i<s->nb_streams; i++){
         start_pos[i]= pos;
     }
-
+    
+    pos= (pos+asf->packet_size-1-s->data_offset)/asf->packet_size*asf->packet_size+ s->data_offset;
+    *ppos= pos;
+    url_fseek(&s->pb, pos, SEEK_SET);
+    
 //printf("asf_read_pts\n");
-    url_fseek(&s->pb, pos*asf->packet_size + s->data_offset, SEEK_SET);
     asf_reset_header(s);
     for(;;){
         if (av_read_frame(s, pkt) < 0){
             av_log(s, AV_LOG_INFO, "seek failed\n");
     	    return AV_NOPTS_VALUE;
         }
+        
         pts= pkt->pts;
 
         av_free_packet(pkt);
@@ -736,10 +742,10 @@
             asf_st= s->streams[i]->priv_data;
 
             assert((asf_st->packet_pos - s->data_offset) % asf->packet_size == 0);
-            pos= (asf_st->packet_pos - s->data_offset) / asf->packet_size;
+            pos= asf_st->packet_pos;
 
             av_add_index_entry(s->streams[i], pos, pts, pos - start_pos[i] + 1, AVINDEX_KEYFRAME);
-            start_pos[i]= pos + 1;
+            start_pos[i]= asf_st->packet_pos + 1;
             
             if(pkt->stream_index == stream_index)
                break;
@@ -755,112 +761,13 @@
 static int asf_read_seek(AVFormatContext *s, int stream_index, int64_t pts)
 {
     ASFContext *asf = s->priv_data;
-    AVStream *st;
-    int64_t pos;
-    int64_t pos_min, pos_max, pts_min, pts_max, cur_pts, pos_limit;
-    int no_change;
-    
-    if (stream_index == -1)
-        stream_index= av_find_default_stream_index(s);
     
     if (asf->packet_size <= 0)
         return -1;
 
-    pts_max=
-    pts_min= AV_NOPTS_VALUE;
-    pos_max= pos_limit= -1; // gcc thinks its uninitalized
-
-    st= s->streams[stream_index];
-    if(st->index_entries){
-        AVIndexEntry *e;
-        int index;
-
-        index= av_index_search_timestamp(st, pts);
-        e= &st->index_entries[index];
-        if(e->timestamp <= pts){
-            pos_min= e->pos;
-            pts_min= e->timestamp;
-#ifdef DEBUG_SEEK
-        printf("unsing cached pos_min=0x%llx dts_min=%0.3f\n", 
-               pos_min,pts_min / 90000.0);
-#endif
-        }else{
-            assert(index==0);
-        }
-        index++;
-        if(index < st->nb_index_entries){
-            e= &st->index_entries[index];
-            assert(e->timestamp >= pts);
-            pos_max= e->pos;
-            pts_max= e->timestamp;
-            pos_limit= pos_max - e->min_distance;
-#ifdef DEBUG_SEEK
-        printf("unsing cached pos_max=0x%llx dts_max=%0.3f\n", 
-               pos_max,pts_max / 90000.0);
-#endif
-        }
-    }
-
-    if(pts_min == AV_NOPTS_VALUE){
-        pos_min = 0;
-        pts_min = asf_read_pts(s, &pos_min, stream_index);
-        if (pts_min == AV_NOPTS_VALUE) return -1;
-    }
-    if(pts_max == AV_NOPTS_VALUE){
-        pos_max = (url_filesize(url_fileno(&s->pb)) - 1 - s->data_offset) / asf->packet_size; //FIXME wrong
-        pts_max = s->duration; //FIXME wrong
-        pos_limit= pos_max;
-    } 
+    if(av_seek_frame_binary(s, stream_index, pts)<0)
+        return -1;
 
-    no_change=0;
-    while (pos_min < pos_limit) {
-        int64_t start_pos;
-        assert(pos_limit <= pos_max);
-
-        if(no_change==0){
-            int64_t approximate_keyframe_distance= pos_max - pos_limit;
-            // interpolate position (better than dichotomy)
-            pos = (int64_t)((double)(pos_max - pos_min) *
-                            (double)(pts - pts_min) /
-                            (double)(pts_max - pts_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 keyframes 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;
-
-        // read the next timestamp 
-    	cur_pts = asf_read_pts(s, &pos, stream_index);    
-        if(pos == pos_max)
-            no_change++;
-        else
-            no_change=0;
-
-#ifdef DEBUG_SEEK
-printf("%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld\n", pos_min, pos, pos_max, pts_min, cur_pts, pts_max, pts, pos_limit, start_pos);
-#endif
-        assert (cur_pts != AV_NOPTS_VALUE);
-        if (pts < cur_pts) {
-            pos_limit = start_pos - 1;
-            pos_max = pos;
-            pts_max = cur_pts;
-        } else {
-            pos_min = pos;
-            pts_min = cur_pts;
-            /* check if we are lucky */
-            if (pts == cur_pts)
-                break;
-        }
-    }
-    pos = pos_min;
-    url_fseek(&s->pb, pos*asf->packet_size + s->data_offset, SEEK_SET);
     asf_reset_header(s);
     return 0;
 }
@@ -874,6 +781,7 @@
     asf_read_packet,
     asf_read_close,
     asf_read_seek,
+    asf_read_pts,
 };
 
 #ifdef CONFIG_ENCODERS
--- a/avformat.h	Sun Apr 11 19:32:24 2004 +0000
+++ b/avformat.h	Mon Apr 12 16:50:03 2004 +0000
@@ -5,7 +5,7 @@
 extern "C" {
 #endif
 
-#define LIBAVFORMAT_BUILD       4611
+#define LIBAVFORMAT_BUILD       4612
 
 #define LIBAVFORMAT_VERSION_INT FFMPEG_VERSION_INT
 #define LIBAVFORMAT_VERSION     FFMPEG_VERSION
@@ -167,6 +167,11 @@
        units) relative to the frames in stream component stream_index */
     int (*read_seek)(struct AVFormatContext *, 
                      int stream_index, int64_t timestamp);
+    /**
+     * gets the next timestamp in AV_TIME_BASE units.
+     */
+    int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,
+                              int64_t *pos, int64_t pos_limit);
     /* can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER */
     int flags;
     /* if extensions are defined, then no probe is done. You should
@@ -555,6 +560,7 @@
 int av_index_search_timestamp(AVStream *st, int timestamp);
 int av_add_index_entry(AVStream *st,
                        int64_t pos, int64_t timestamp, int distance, int flags);
+int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts);
 
 /* media file output */
 int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap);
--- a/mpeg.c	Sun Apr 11 19:32:24 2004 +0000
+++ b/mpeg.c	Mon Apr 12 16:50:03 2004 +0000
@@ -1076,12 +1076,12 @@
     return start_code;
 }
 
-/* read the next (or previous) PES header. Return its position in ppos 
+/* read the next PES header. Return its position in ppos 
    (if not NULL), and its start code, pts and dts.
  */
 static int mpegps_read_pes_header(AVFormatContext *s,
                                   int64_t *ppos, int *pstart_code, 
-                                  int64_t *ppts, int64_t *pdts, int find_next)
+                                  int64_t *ppts, int64_t *pdts)
 {
     MpegDemuxContext *m = s->priv_data;
     int len, size, startcode, c, flags, header_len;
@@ -1089,18 +1089,10 @@
 
     last_pos = -1;
  redo:
-    if (find_next) {
         /* next start code (should be immediately after) */
         m->header_state = 0xff;
         size = MAX_SYNC_SIZE;
         startcode = find_next_start_code(&s->pb, &size, &m->header_state);
-    } else {
-        if (last_pos >= 0)
-            url_fseek(&s->pb, last_pos, SEEK_SET);
-        size = MAX_SYNC_SIZE;
-        startcode = find_prev_start_code(&s->pb, &size);
-        last_pos = url_ftell(&s->pb) - 4;
-    }
     //printf("startcode=%x pos=0x%Lx\n", startcode, url_ftell(&s->pb));
     if (startcode < 0)
         return -EIO;
@@ -1205,7 +1197,7 @@
         int i;
         for(i=0; i<s->nb_streams; i++){
             if(startcode == s->streams[i]->id) {
-                av_add_index_entry(s->streams[i], *ppos, dts, 0, 0 /* FIXME keyframe? */);
+                av_add_index_entry(s->streams[i], *ppos, dts*AV_TIME_BASE/90000, 0, 0 /* FIXME keyframe? */);
             }
         }
     }
@@ -1224,7 +1216,7 @@
     int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work
 
  redo:
-    len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts, 1);
+    len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts);
     if (len < 0)
         return len;
     
@@ -1295,7 +1287,7 @@
 }
 
 static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, 
-                               int64_t *ppos, int find_next)
+                               int64_t *ppos, int64_t pos_limit)
 {
     int len, startcode;
     int64_t pos, pts, dts;
@@ -1306,7 +1298,7 @@
 #endif
     url_fseek(&s->pb, pos, SEEK_SET);
     for(;;) {
-        len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts, find_next);
+        len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts);
         if (len < 0) {
 #ifdef DEBUG_SEEK
             printf("none (ret=%d)\n", len);
@@ -1317,153 +1309,13 @@
             dts != AV_NOPTS_VALUE) {
             break;
         }
-        if (find_next) {
-            url_fskip(&s->pb, len);
-        } else {
-            url_fseek(&s->pb, pos, SEEK_SET);
-        }
+        url_fskip(&s->pb, len);
     }
 #ifdef DEBUG_SEEK
     printf("pos=0x%llx dts=0x%llx %0.3f\n", pos, dts, dts / 90000.0);
 #endif
     *ppos = pos;
-    return dts;
-}
-
-static int mpegps_read_seek(AVFormatContext *s, 
-                            int stream_index, int64_t timestamp)
-{
-    int64_t pos_min, pos_max, pos, pos_limit;
-    int64_t dts_min, dts_max, dts;
-    int index, no_change;
-    AVStream *st;
-
-    timestamp = (timestamp * 90000) / AV_TIME_BASE;
-
-#ifdef DEBUG_SEEK
-    printf("read_seek: %d %0.3f\n", stream_index, timestamp / 90000.0);
-#endif
-
-    /* XXX: find stream_index by looking at the first PES packet found */
-    if (stream_index < 0) {
-        stream_index = av_find_default_stream_index(s);
-        if (stream_index < 0)
-            return -1;
-    }
-    
-    dts_max=
-    dts_min= AV_NOPTS_VALUE;
-    pos_limit= -1; //gcc falsely says it may be uninitalized
-
-    st= s->streams[stream_index];
-    if(st->index_entries){
-        AVIndexEntry *e;
-
-        index= av_index_search_timestamp(st, timestamp);
-        e= &st->index_entries[index];
-        if(e->timestamp <= timestamp){
-            pos_min= e->pos;
-            dts_min= e->timestamp;
-#ifdef DEBUG_SEEK
-        printf("unsing cached pos_min=0x%llx dts_min=%0.3f\n", 
-               pos_min,dts_min / 90000.0);
-#endif
-        }else{
-            assert(index==0);
-        }
-        index++;
-        if(index < st->nb_index_entries){
-            e= &st->index_entries[index];
-            assert(e->timestamp >= timestamp);
-            pos_max= e->pos;
-            dts_max= e->timestamp;
-            pos_limit= pos_max - e->min_distance;
-#ifdef DEBUG_SEEK
-        printf("unsing cached pos_max=0x%llx dts_max=%0.3f\n", 
-               pos_max,dts_max / 90000.0);
-#endif
-        }
-    }
-
-    if(dts_min == AV_NOPTS_VALUE){
-        pos_min = 0;
-        dts_min = mpegps_read_dts(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;
-        }
-    }
-    if(dts_max == AV_NOPTS_VALUE){
-        pos_max = url_filesize(url_fileno(&s->pb)) - 1;
-        dts_max = mpegps_read_dts(s, stream_index, &pos_max, 0);
-        pos_limit= pos_max;
-    }
-
-    no_change=0;
-    while (pos_min < pos_limit) {
-#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
-        int64_t start_pos;
-        assert(pos_limit <= pos_max);
-
-        if(no_change==0){
-            int64_t approximate_keyframe_distance= pos_max - pos_limit;
-            // interpolate position (better than dichotomy)
-            pos = (int64_t)((double)(pos_max - pos_min) *
-                            (double)(timestamp - dts_min) /
-                            (double)(dts_max - dts_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;
-
-        // read the next timestamp 
-        dts = mpegps_read_dts(s, stream_index, &pos, 1);
-        if(pos == pos_max)
-            no_change++;
-        else
-            no_change=0;
-#ifdef DEBUG_SEEK
-printf("%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, dts_min, dts, dts_max, timestamp, pos_limit, start_pos, no_change);
-#endif
-        assert(dts != AV_NOPTS_VALUE);
-        if (timestamp < dts) {
-            pos_limit = start_pos - 1;
-            pos_max = pos;
-            dts_max = dts;
-        } else {
-            pos_min = pos;
-            dts_min = dts;
-            /* check if we are lucky */
-            if (timestamp == dts)
-                break;
-        }
-    }
-    
-    pos = pos_min;
-#ifdef DEBUG_SEEK
-    pos_min = pos;
-    dts_min = mpegps_read_dts(s, stream_index, &pos_min, 1);
-    pos_min++;
-    dts_max = mpegps_read_dts(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;
+    return dts*AV_TIME_BASE/90000;
 }
 
 #ifdef CONFIG_ENCODERS
@@ -1532,7 +1384,8 @@
     mpegps_read_header,
     mpegps_read_packet,
     mpegps_read_close,
-    mpegps_read_seek,
+    NULL, //mpegps_read_seek,
+    mpegps_read_dts,
 };
 
 int mpegps_init(void)
--- a/nut.c	Sun Apr 11 19:32:24 2004 +0000
+++ b/nut.c	Mon Apr 12 16:50:03 2004 +0000
@@ -1107,10 +1107,11 @@
         if(flags & FLAG_FULL_PTS){
             pts= get_v(bc);
             if(frame_type && key_frame){
+                int64_t av_pts= pts * AV_TIME_BASE * stream->rate_den / stream->rate_num;
                 av_add_index_entry(
                     s->streams[stream_id], 
                     frame_start, 
-                    pts, 
+                    av_pts, 
                     frame_start - nut->stream[stream_id].last_sync_pos,
                     AVINDEX_KEYFRAME);
                 nut->stream[stream_id].last_sync_pos= frame_start;
@@ -1231,8 +1232,9 @@
     }
 }
 
-static int64_t read_timestamp(AVFormatContext *s, int stream_index, int64_t *pos_arg, int64_t pos_limit){
+static int64_t nut_read_timestamp(AVFormatContext *s, int stream_index, int64_t *pos_arg, int64_t pos_limit){
     NUTContext *nut = s->priv_data;
+    StreamContext *stream;
     ByteIOContext *bc = &s->pb;
     int64_t pos, pts;
     uint64_t code;
@@ -1305,7 +1307,8 @@
             if(stream_id >= s->nb_streams)
                 goto resync;
                 
-            pts= get_v(bc);
+            stream= &nut->stream[stream_id];
+            pts= get_v(bc) * AV_TIME_BASE * stream->rate_den / stream->rate_num;
     
             if(flags & FLAG_KEY_FRAME){
                 av_add_index_entry(
@@ -1336,155 +1339,16 @@
     return AV_NOPTS_VALUE;
 }
 
-#define DEBUG_SEEK
 static int nut_read_seek(AVFormatContext *s, int stream_index, int64_t target_ts){
     NUTContext *nut = s->priv_data;
-    StreamContext *stream;
-    int64_t pos_min, pos_max, pos, pos_limit;
-    int64_t ts_min, ts_max, ts;
-    int64_t start_pos;
-    int index, no_change,i;
-    AVStream *st;
-
-    if (stream_index < 0) {
-        stream_index = av_find_default_stream_index(s);
-        if (stream_index < 0)
-            return -1;
-    }
-    stream= &nut->stream[stream_index];
-    target_ts= (av_rescale(target_ts, stream->rate_num, stream->rate_den) + AV_TIME_BASE/2) / AV_TIME_BASE;
-
-#ifdef DEBUG_SEEK
-    av_log(s, AV_LOG_DEBUG, "read_seek: %d %lld\n", stream_index, target_ts);
-#endif
-
-    ts_max=
-    ts_min= AV_NOPTS_VALUE;
-    pos_limit= -1; //gcc falsely says it may be uninitalized
-
-    st= s->streams[stream_index];
-    if(st->index_entries){
-        AVIndexEntry *e;
-
-        index= av_index_search_timestamp(st, target_ts);
-        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, "unsing cached pos_min=0x%llx dts_min=%lld\n", 
-               pos_min,ts_min);
-#endif
-        }else{
-            assert(index==0);
-        }
-        index++;
-        if(index < st->nb_index_entries){
-            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, "unsing cached pos_max=0x%llx pos_limit=0x%llx dts_max=%lld\n", 
-               pos_max,pos_limit, ts_max);
-#endif
-        }
-    }
-
-    if(ts_min == AV_NOPTS_VALUE){
-        pos_min = 0;
-        ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
-        if (ts_min == AV_NOPTS_VALUE)
-            return -1;
-    }
+    int64_t pos;
+    int i;
 
-    if(ts_max == AV_NOPTS_VALUE){
-        int step= 1024;
-        pos_max = url_filesize(url_fileno(&s->pb)) - 1;
-        do{
-            pos_max -= step;
-            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;
-        
-        for(;;){
-            int64_t tmp_pos= pos_max + 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;
-        }
-        pos_limit= pos_max;
-    }
-
-    no_change=0;
-    while (pos_min < pos_limit) {
-#ifdef DEBUG_SEEK
-        av_log(s, AV_LOG_DEBUG, "pos_min=0x%llx pos_max=0x%llx dts_min=%lld dts_max=%lld\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 = (int64_t)((double)(pos_max - pos_min) *
-                            (double)(target_ts - ts_min) /
-                            (double)(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, "%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit, start_pos, no_change);
-#endif
-        assert(ts != AV_NOPTS_VALUE);
-        if (target_ts < ts) {
-            pos_limit = start_pos - 1;
-            pos_max = pos;
-            ts_max = ts;
-        } else {
-            pos_min = pos;
-            ts_min = ts;
-            /* check if we are lucky */
-            if (target_ts == ts)
-                break;
-        }
-    }
-    
-    pos = pos_min;
-#ifdef DEBUG_SEEK
-    pos_min = pos;
-    ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
-    pos_min++;
-    ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
-    av_log(s, AV_LOG_DEBUG, "pos=0x%llx %lld<=%lld<=%lld\n", 
-           pos, ts_min, target_ts, ts_max);
-#endif
-    /* do the seek */
-    url_fseek(&s->pb, pos, SEEK_SET);
+    if(av_seek_frame_binary(s, stream_index, target_ts) < 0)
+        return -1;
 
     nut->written_packet_size= -1;
+    pos= url_ftell(&s->pb);
     for(i=0; i<s->nb_streams; i++)
         nut->stream[i].last_sync_pos= pos;
 
@@ -1513,6 +1377,7 @@
     nut_read_packet,
     nut_read_close,
     nut_read_seek,
+    nut_read_timestamp,
     .extensions = "nut",
 };
 
--- a/utils.c	Sun Apr 11 19:32:24 2004 +0000
+++ b/utils.c	Mon Apr 12 16:50:03 2004 +0000
@@ -997,6 +997,156 @@
     return a;
 }
 
+#define DEBUG_SEEK
+
+int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts){
+    AVInputFormat *avif= s->iformat;
+    int64_t pos_min, pos_max, pos, pos_limit;
+    int64_t ts_min, ts_max, ts;
+    int64_t start_pos;
+    int index, no_change;
+    AVStream *st;
+
+    if (stream_index < 0) {
+        stream_index = av_find_default_stream_index(s);
+        if (stream_index < 0)
+            return -1;
+    }
+
+#ifdef DEBUG_SEEK
+    av_log(s, AV_LOG_DEBUG, "read_seek: %d %lld\n", stream_index, target_ts);
+#endif
+
+    ts_max=
+    ts_min= AV_NOPTS_VALUE;
+    pos_limit= -1; //gcc falsely says it may be uninitalized
+
+    st= s->streams[stream_index];
+    if(st->index_entries){
+        AVIndexEntry *e;
+
+        index= av_index_search_timestamp(st, target_ts);
+        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, "unsing cached pos_min=0x%llx dts_min=%lld\n", 
+               pos_min,ts_min);
+#endif
+        }else{
+            assert(index==0);
+        }
+        index++;
+        if(index < st->nb_index_entries){
+            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, "unsing cached pos_max=0x%llx pos_limit=0x%llx dts_max=%lld\n", 
+               pos_max,pos_limit, ts_max);
+#endif
+        }
+    }
+
+    if(ts_min == AV_NOPTS_VALUE){
+        pos_min = s->data_offset;
+        ts_min = avif->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;
+        pos_max = url_filesize(url_fileno(&s->pb)) - 1;
+        do{
+            pos_max -= step;
+            ts_max = avif->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;
+        
+        for(;;){
+            int64_t tmp_pos= pos_max + 1;
+            int64_t tmp_ts= avif->read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
+            if(tmp_ts == AV_NOPTS_VALUE)
+                break;
+            ts_max= tmp_ts;
+            pos_max= tmp_pos;
+        }
+        pos_limit= pos_max;
+    }
+
+    no_change=0;
+    while (pos_min < pos_limit) {
+#ifdef DEBUG_SEEK
+        av_log(s, AV_LOG_DEBUG, "pos_min=0x%llx pos_max=0x%llx dts_min=%lld dts_max=%lld\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 = (int64_t)((double)(pos_max - pos_min) *
+                            (double)(target_ts - ts_min) /
+                            (double)(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 = avif->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, "%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%Ld noc:%d\n", pos_min, pos, pos_max, ts_min, ts, ts_max, target_ts, pos_limit, start_pos, no_change);
+#endif
+        assert(ts != AV_NOPTS_VALUE);
+        if (target_ts < ts) {
+            pos_limit = start_pos - 1;
+            pos_max = pos;
+            ts_max = ts;
+        } else {
+            pos_min = pos;
+            ts_min = ts;
+            /* check if we are lucky */
+            if (target_ts == ts)
+                break;
+        }
+    }
+    
+    pos = pos_min;
+#ifdef DEBUG_SEEK
+    pos_min = pos;
+    ts_min = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+    pos_min++;
+    ts_max = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX);
+    av_log(s, AV_LOG_DEBUG, "pos=0x%llx %lld<=%lld<=%lld\n", 
+           pos, ts_min, target_ts, ts_max);
+#endif
+    /* do the seek */
+    url_fseek(&s->pb, pos, SEEK_SET);
+    st->cur_dts = ts_min;
+
+    return 0;
+}
+
 static int av_seek_frame_generic(AVFormatContext *s, 
                                  int stream_index, int64_t timestamp)
 {
@@ -1047,8 +1197,11 @@
     if (ret >= 0) {
         return 0;
     }
-    
-    return av_seek_frame_generic(s, stream_index, timestamp);
+
+    if(s->iformat->read_timestamp)
+        return av_seek_frame_binary(s, stream_index, timestamp);
+    else
+        return av_seek_frame_generic(s, stream_index, timestamp);
 }
 
 /*******************************************************/