diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2004-10-10 22:05:43 +0000 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2004-10-10 22:05:43 +0000 |
commit | 3ba1438dec553ab106aac8895ddebc01e42c5b71 (patch) | |
tree | c3675504b449b68aa6b99a8429a1abb740507ae2 | |
parent | ac8b03c0a807f9cc9d9bd4a14f290efb98bd87ec (diff) | |
download | ffmpeg-3ba1438dec553ab106aac8895ddebc01e42c5b71.tar.gz |
use native timebase for seeking
direction flag for seeking
Originally committed as revision 3577 to svn://svn.ffmpeg.org/ffmpeg/trunk
-rw-r--r-- | ffmpeg.c | 2 | ||||
-rw-r--r-- | ffplay.c | 12 | ||||
-rw-r--r-- | libavformat/asf.c | 4 | ||||
-rw-r--r-- | libavformat/avformat.h | 22 | ||||
-rw-r--r-- | libavformat/mpegts.c | 4 | ||||
-rw-r--r-- | libavformat/nut.c | 4 | ||||
-rw-r--r-- | libavformat/utils.c | 162 |
7 files changed, 127 insertions, 83 deletions
@@ -2777,7 +2777,7 @@ static void opt_input_file(const char *filename) /* if seeking requested, we execute it */ if (start_time != 0) { - ret = av_seek_frame(ic, -1, timestamp); + ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); if (ret < 0) { fprintf(stderr, "%s: could not seek to position %0.3f\n", filename, (double)timestamp / AV_TIME_BASE); @@ -92,6 +92,7 @@ typedef struct VideoState { int paused; int last_paused; int seek_req; + int seek_flags; int64_t seek_pos; AVFormatContext *ic; int dtg_active_format; @@ -589,10 +590,11 @@ static double get_master_clock(VideoState *is) } /* seek in the stream */ -static void stream_seek(VideoState *is, int64_t pos) +static void stream_seek(VideoState *is, int64_t pos, int rel) { is->seek_pos = pos; is->seek_req = 1; + is->seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0; } /* pause or resume the video */ @@ -1335,7 +1337,7 @@ static int decode_thread(void *arg) /* add the stream start time */ if (ic->start_time != AV_NOPTS_VALUE) timestamp += ic->start_time; - ret = av_seek_frame(ic, -1, timestamp); + ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); if (ret < 0) { fprintf(stderr, "%s: could not seek to position %0.3f\n", is->filename, (double)timestamp / AV_TIME_BASE); @@ -1412,7 +1414,7 @@ static int decode_thread(void *arg) #endif if (is->seek_req) { /* XXX: must lock decoder threads */ - ret = av_seek_frame(is->ic, -1, is->seek_pos); + ret = av_seek_frame(is->ic, -1, is->seek_pos, is->seek_flags); if (ret < 0) { fprintf(stderr, "%s: error while seeking\n", is->ic->filename); }else{ @@ -1682,7 +1684,7 @@ void event_loop(void) if (cur_stream) { pos = get_master_clock(cur_stream); pos += incr; - stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE)); + stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), incr); } break; default: @@ -1704,7 +1706,7 @@ void event_loop(void) ss = (ns%60); fprintf(stderr, "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d) \n", frac*100, hh, mm, ss, thh, tmm, tss); - stream_seek(cur_stream, (int64_t)(cur_stream->ic->start_time+frac*cur_stream->ic->duration)); + stream_seek(cur_stream, (int64_t)(cur_stream->ic->start_time+frac*cur_stream->ic->duration), 0); } break; case SDL_VIDEORESIZE: diff --git a/libavformat/asf.c b/libavformat/asf.c index 36a3aa119f..b804bd7b5a 100644 --- a/libavformat/asf.c +++ b/libavformat/asf.c @@ -757,14 +757,14 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos, 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); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 4cd9da59e8..997756b539 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -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 @@ typedef struct AVInputFormat { /* 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 @@ AVFormatContext *av_alloc_format_context(void); 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 @@ AVStream *av_new_stream(AVFormatContext *s, int id); 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); diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 3dbd9478e2..f48055db44 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -1331,12 +1331,12 @@ static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, 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); diff --git a/libavformat/nut.c b/libavformat/nut.c index 0a4e9c82bf..544036670b 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -1373,11 +1373,11 @@ av_log(s, AV_LOG_DEBUG, "syncing from %lld\n", nut->packet_start[2]+1); 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); diff --git a/libavformat/utils.c b/libavformat/utils.c index 788f4ab2c9..331f42c05f 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -907,31 +907,22 @@ int av_add_index_entry(AVStream *st, 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 @@ static int is_raw_stream(AVFormatContext *s) 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 @@ int av_index_search_timestamp(AVStream *st, int wanted_timestamp) * @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 @@ int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts 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 @@ int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts 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 @@ int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts 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 @@ av_log(s, AV_LOG_DEBUG, "%Ld %Ld %Ld / %Ld %Ld %Ld target:%Ld limit:%Ld start:%L /* 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 @@ static int av_seek_frame_generic(AVFormatContext *s, } 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 @@ static int av_seek_frame_generic(AVFormatContext *s, 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 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp) } 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); } /*******************************************************/ |