changeset 555:0a997108f384 libavformat

use native timebase for seeking direction flag for seeking
author michael
date Sun, 10 Oct 2004 22:05:43 +0000
parents e1f17fcfb92c
children cb5f220888c0
files asf.c avformat.h mpegts.c nut.c utils.c
diffstat 5 files changed, 119 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/asf.c	Fri Oct 08 20:09:52 2004 +0000
+++ b/asf.c	Sun Oct 10 22:05:43 2004 +0000
@@ -757,14 +757,14 @@
     return pts;
 }
 
-static int asf_read_seek(AVFormatContext *s, int stream_index, int64_t pts)
+static int asf_read_seek(AVFormatContext *s, int stream_index, int64_t pts, int flags)
 {
     ASFContext *asf = s->priv_data;
     
     if (asf->packet_size <= 0)
         return -1;
 
-    if(av_seek_frame_binary(s, stream_index, pts)<0)
+    if(av_seek_frame_binary(s, stream_index, pts, flags)<0)
         return -1;
 
     asf_reset_header(s);
--- a/avformat.h	Fri Oct 08 20:09:52 2004 +0000
+++ b/avformat.h	Sun Oct 10 22:05:43 2004 +0000
@@ -5,7 +5,7 @@
 extern "C" {
 #endif
 
-#define LIBAVFORMAT_BUILD       4618
+#define LIBAVFORMAT_BUILD       4619
 
 #define LIBAVFORMAT_VERSION_INT FFMPEG_VERSION_INT
 #define LIBAVFORMAT_VERSION     FFMPEG_VERSION
@@ -165,10 +165,15 @@
     /* close the stream. The AVFormatContext and AVStreams are not
        freed by this function */
     int (*read_close)(struct AVFormatContext *);
-    /* seek at or before a given timestamp (given in AV_TIME_BASE
-       units) relative to the frames in stream component stream_index */
+    /** 
+     * seek to a given timestamp relative to the frames in 
+     * stream component stream_index
+     * @param stream_index must not be -1
+     * @param flags selects which direction should be preferred if no exact 
+     *              match is available
+     */
     int (*read_seek)(struct AVFormatContext *, 
-                     int stream_index, int64_t timestamp);
+                     int stream_index, int64_t timestamp, int flags);
     /**
      * gets the next timestamp in AV_TIME_BASE units.
      */
@@ -553,7 +558,7 @@
 int av_find_stream_info(AVFormatContext *ic);
 int av_read_packet(AVFormatContext *s, AVPacket *pkt);
 int av_read_frame(AVFormatContext *s, AVPacket *pkt);
-int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp);
+int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
 int av_read_play(AVFormatContext *s);
 int av_read_pause(AVFormatContext *s);
 void av_close_input_file(AVFormatContext *s);
@@ -561,11 +566,14 @@
 void av_set_pts_info(AVStream *s, int pts_wrap_bits,
                      int pts_num, int pts_den);
 
+#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
+#define AVSEEK_FLAG_BYTE     2 ///< seeking based on position in bytes
+
 int av_find_default_stream_index(AVFormatContext *s);
-int av_index_search_timestamp(AVStream *st, int timestamp);
+int av_index_search_timestamp(AVStream *st, int timestamp, int flags);
 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);
+int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags);
 
 /* media file output */
 int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap);
--- a/mpegts.c	Fri Oct 08 20:09:52 2004 +0000
+++ b/mpegts.c	Sun Oct 10 22:05:43 2004 +0000
@@ -1331,12 +1331,12 @@
     return timestamp;
 }
 
-static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts){
+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) < 0)
+    if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0)
         return -1;
 
     pos= url_ftell(&s->pb);
--- a/nut.c	Fri Oct 08 20:09:52 2004 +0000
+++ b/nut.c	Sun Oct 10 22:05:43 2004 +0000
@@ -1373,11 +1373,11 @@
     return AV_NOPTS_VALUE;
 }
 
-static int nut_read_seek(AVFormatContext *s, int stream_index, int64_t target_ts){
+static int nut_read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
 //    NUTContext *nut = s->priv_data;
     int64_t pos;
 
-    if(av_seek_frame_binary(s, stream_index, target_ts) < 0)
+    if(av_seek_frame_binary(s, stream_index, target_ts, flags) < 0)
         return -1;
 
     pos= url_ftell(&s->pb);
--- a/utils.c	Fri Oct 08 20:09:52 2004 +0000
+++ b/utils.c	Sun Oct 10 22:05:43 2004 +0000
@@ -907,31 +907,22 @@
                               sizeof(AVIndexEntry));
     st->index_entries= entries;
 
-    if(st->nb_index_entries){
-        index= av_index_search_timestamp(st, timestamp);
-        ie= &entries[index];
+    index= av_index_search_timestamp(st, timestamp, 0);
 
-        if(ie->timestamp != timestamp){
-            if(ie->timestamp < timestamp){
-                index++; //index points to next instead of previous entry, maybe nonexistant
-                ie= &st->index_entries[index];
-            }else
-                assert(index==0);
-                
-            if(index != st->nb_index_entries){
-                assert(index < st->nb_index_entries);
-                memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
-            }
-            st->nb_index_entries++;
-        }else{
-            if(ie->pos == pos && distance < ie->min_distance) //dont reduce the distance
-                distance= ie->min_distance;
-        }
-    }else{
+    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){
+            assert(ie->timestamp > timestamp);
+            memmove(entries + index + 1, entries + index, sizeof(AVIndexEntry)*(st->nb_index_entries - index));
+            st->nb_index_entries++;
+        }else if(ie->pos == pos && distance < ie->min_distance) //dont reduce the distance
+            distance= ie->min_distance;
     }
-    
+
     ie->pos = pos;
     ie->timestamp = timestamp;
     ie->min_distance= distance;
@@ -979,31 +970,36 @@
     return 1;
 }
 
-/* return the largest index entry whose timestamp is <=
-   wanted_timestamp */
-int av_index_search_timestamp(AVStream *st, int wanted_timestamp)
+/**
+ * gets the index for a specific timestamp.
+ * @param backward if non zero then the returned index will correspond to 
+ *                 the timestamp which is <= the requested one, if backward is 0 
+ *                 then it will be >=
+ * @return < 0 if no such timestamp could be found
+ */
+int av_index_search_timestamp(AVStream *st, int wanted_timestamp, int backward)
 {
     AVIndexEntry *entries= st->index_entries;
     int nb_entries= st->nb_index_entries;
     int a, b, m;
     int64_t timestamp;
 
-    if (nb_entries <= 0)
-        return -1;
-    
-    a = 0;
-    b = nb_entries - 1;
+    a = - 1;
+    b = nb_entries;
 
-    while (a < b) {
-        m = (a + b + 1) >> 1;
+    while (b - a > 1) {
+        m = (a + b) >> 1;
         timestamp = entries[m].timestamp;
-        if (timestamp > wanted_timestamp) {
-            b = m - 1;
-        } else {
+        if(timestamp >= wanted_timestamp)
+            b = m;
+        if(timestamp <= wanted_timestamp)
             a = m;
-        }
     }
-    return a;
+    m= backward ? a : b;
+
+    if(m == nb_entries) 
+        return -1;
+    return  m;
 }
 
 #define DEBUG_SEEK
@@ -1014,7 +1010,7 @@
  * @param target_ts target timestamp in the time base of the given stream
  * @param stream_index stream number
  */
-int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts){
+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;
@@ -1037,7 +1033,8 @@
     if(st->index_entries){
         AVIndexEntry *e;
 
-        index= av_index_search_timestamp(st, target_ts);
+        index= av_index_search_timestamp(st, target_ts, 1);
+        index= FFMAX(index, 0);
         e= &st->index_entries[index];
 
         if(e->timestamp <= target_ts || e->pos == e->min_distance){
@@ -1105,9 +1102,8 @@
         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;
+            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;
@@ -1130,20 +1126,19 @@
 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) {
+        if (target_ts <= ts) {
             pos_limit = start_pos - 1;
             pos_max = pos;
             ts_max = ts;
-        } else {
+        }
+        if (target_ts >= ts) {
             pos_min = pos;
             ts_min = ts;
-            /* check if we are lucky */
-            if (target_ts == ts)
-                break;
         }
     }
     
-    pos = pos_min;
+    pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
+    ts  = (flags & AVSEEK_FLAG_BACKWARD) ?  ts_min :  ts_max;
 #ifdef DEBUG_SEEK
     pos_min = pos;
     ts_min = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX);
@@ -1155,18 +1150,51 @@
     /* do the seek */
     url_fseek(&s->pb, pos, SEEK_SET);
 
-    ts= av_rescale(ts_min, AV_TIME_BASE*(int64_t)st->time_base.num, st->time_base.den);
     for(i = 0; i < s->nb_streams; i++) {
-        st = s->streams[i];
+        AVStream *st2 = s->streams[i];
 
-        st->cur_dts = av_rescale(ts, st->time_base.den, AV_TIME_BASE*(int64_t)st->time_base.num);
+        st->cur_dts = av_rescale(ts, 
+                                 st2->time_base.den * (int64_t)st ->time_base.num,
+                                 st ->time_base.den * (int64_t)st2->time_base.num);
     }
 
     return 0;
 }
 
+static int av_seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, int flags){
+    AVInputFormat *avif= s->iformat;
+    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_filesize(url_fileno(&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);
+
+#if 0
+    for(i = 0; i < s->nb_streams; i++) {
+        st2 = s->streams[i];
+
+        st->cur_dts = av_rescale(ie->timestamp, 
+                                 st2->time_base.den * (int64_t)st ->time_base.num,
+                                 st ->time_base.den * (int64_t)st2->time_base.num);
+    }
+#endif
+    return 0;
+}
+
 static int av_seek_frame_generic(AVFormatContext *s, 
-                                 int stream_index, int64_t timestamp)
+                                 int stream_index, int64_t timestamp, int flags)
 {
     int index, i;
     AVStream *st;
@@ -1182,7 +1210,7 @@
     }
 
     st = s->streams[stream_index];
-    index = av_index_search_timestamp(st, timestamp);
+    index = av_index_search_timestamp(st, timestamp, flags & AVSEEK_FLAG_BACKWARD);
     if (index < 0)
         return -1;
 
@@ -1190,44 +1218,50 @@
     ie = &st->index_entries[index];
     av_read_frame_flush(s);
     url_fseek(&s->pb, ie->pos, SEEK_SET);
-    
-    timestamp= av_rescale(ie->timestamp, AV_TIME_BASE*(int64_t)st->time_base.num, st->time_base.den);
+
     for(i = 0; i < s->nb_streams; i++) {
-        st = s->streams[i];
+        AVStream *st2 = s->streams[i];
 
-        st->cur_dts = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE*(int64_t)st->time_base.num);
+        st->cur_dts = av_rescale(ie->timestamp, 
+                                 st2->time_base.den * (int64_t)st ->time_base.num,
+                                 st ->time_base.den * (int64_t)st2->time_base.num);
     }
 
     return 0;
 }
 
 /**
- * Seek to the key frame just before the frame at timestamp
+ * Seek to the key frame at timestamp.
  * 'timestamp' in 'stream_index'.
  * @param stream_index If stream_index is (-1), a default
  * stream is selected
- * @param timestamp timestamp in AV_TIME_BASE units
+ * @param timestamp timestamp in AVStream.time_base units
+ * @param flags flags which select direction and seeking mode
  * @return >= 0 on success
  */
-int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp)
+int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
 {
     int ret;
     AVStream *st;
     
     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 = av_rescale(timestamp, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num);
     }
     st= s->streams[stream_index];
 
-    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);
+        ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
     else
         ret = -1;
     if (ret >= 0) {
@@ -1235,9 +1269,9 @@
     }
 
     if(s->iformat->read_timestamp)
-        return av_seek_frame_binary(s, stream_index, timestamp);
+        return av_seek_frame_binary(s, stream_index, timestamp, flags);
     else
-        return av_seek_frame_generic(s, stream_index, timestamp);
+        return av_seek_frame_generic(s, stream_index, timestamp, flags);
 }
 
 /*******************************************************/