diff --git a/libavformat/asf.c b/libavformat/asf.c index 19542e5a0c..b4088c9add 100644 --- a/libavformat/asf.c +++ b/libavformat/asf.c @@ -422,6 +422,8 @@ static int asf_get_packet(AVFormatContext *s) 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 @@ static void asf_reset_header(AVFormatContext *s) 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 @@ static int64_t asf_read_pts(AVFormatContext *s, int64_t *ppos, int stream_index) for(i=0; inb_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 @@ static int64_t asf_read_pts(AVFormatContext *s, int64_t *ppos, int stream_index) 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 int64_t asf_read_pts(AVFormatContext *s, int64_t *ppos, int stream_index) 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 + if(av_seek_frame_binary(s, stream_index, pts)<0) + return -1; - 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; - } - - 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 @@ static AVInputFormat asf_iformat = { asf_read_packet, asf_read_close, asf_read_seek, + asf_read_pts, }; #ifdef CONFIG_ENCODERS diff --git a/libavformat/avformat.h b/libavformat/avformat.h index d1db5f3a55..52b202779b 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -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 @@ typedef struct AVInputFormat { 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_find_default_stream_index(AVFormatContext *s); 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); diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 092790c69a..05957e6f6e 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -1076,12 +1076,12 @@ static int find_prev_start_code(ByteIOContext *pb, int *size_ptr) 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 @@ static int mpegps_read_pes_header(AVFormatContext *s, 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 @@ static int mpegps_read_pes_header(AVFormatContext *s, int i; for(i=0; inb_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 @@ static int mpegps_read_packet(AVFormatContext *s, 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 int mpegps_read_close(AVFormatContext *s) } 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 @@ static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, #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 @@ static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, 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 @@ AVInputFormat mpegps_demux = { mpegps_read_header, mpegps_read_packet, mpegps_read_close, - mpegps_read_seek, + NULL, //mpegps_read_seek, + mpegps_read_dts, }; int mpegps_init(void) diff --git a/libavformat/nut.c b/libavformat/nut.c index 83bb8841ea..56d733f7cb 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -1107,10 +1107,11 @@ static int decode_frame(NUTContext *nut, AVPacket *pkt, int frame_code, int fram 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 @@ av_log(s, AV_LOG_DEBUG, "steping back to %lld next %d\n", pos, size); } } -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 @@ av_log(s, AV_LOG_DEBUG, "read_timestamp(X,%d,%lld,%lld)\n", stream_index, *pos_a 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 @@ av_log(s, AV_LOG_DEBUG, "syncing from %lld\n", nut->packet_start+1); 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; + int64_t pos; + int i; - 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; - } - - 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; inb_streams; i++) nut->stream[i].last_sync_pos= pos; @@ -1513,6 +1377,7 @@ static AVInputFormat nut_iformat = { nut_read_packet, nut_read_close, nut_read_seek, + nut_read_timestamp, .extensions = "nut", }; diff --git a/libavformat/utils.c b/libavformat/utils.c index ad51b04beb..ee91b42f19 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -997,6 +997,156 @@ int av_index_search_timestamp(AVStream *st, int wanted_timestamp) 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 @@ int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp) 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); } /*******************************************************/