/*
* Seeking and index - related functions
* Copyright ( c ) 2000 , 2001 , 2002 Fabrice Bellard
*
* This file is part of FFmpeg .
*
* FFmpeg is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* FFmpeg is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <stdint.h>
# include "libavutil/avassert.h"
# include "libavutil/mathematics.h"
# include "libavutil/timestamp.h"
# include "avformat.h"
# include "avio_internal.h"
# include "internal.h"
void avpriv_update_cur_dts ( AVFormatContext * s , AVStream * ref_st , int64_t timestamp )
{
for ( unsigned i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * const st = s - > streams [ i ] ;
FFStream * const sti = ffstream ( st ) ;
sti - > cur_dts =
av_rescale ( timestamp ,
st - > time_base . den * ( int64_t ) ref_st - > time_base . num ,
st - > time_base . num * ( int64_t ) ref_st - > time_base . den ) ;
}
}
void ff_reduce_index ( AVFormatContext * s , int stream_index )
{
AVStream * const st = s - > streams [ stream_index ] ;
FFStream * const sti = ffstream ( st ) ;
unsigned int max_entries = s - > max_index_size / sizeof ( AVIndexEntry ) ;
if ( ( unsigned ) sti - > nb_index_entries > = max_entries ) {
int i ;
for ( i = 0 ; 2 * i < sti - > nb_index_entries ; i + + )
sti - > index_entries [ i ] = sti - > index_entries [ 2 * i ] ;
sti - > nb_index_entries = i ;
}
}
int ff_add_index_entry ( AVIndexEntry * * index_entries ,
int * nb_index_entries ,
unsigned int * index_entries_allocated_size ,
int64_t pos , int64_t timestamp ,
int size , int distance , int flags )
{
AVIndexEntry * entries , * ie ;
int index ;
if ( ( unsigned ) * nb_index_entries + 1 > = UINT_MAX / sizeof ( AVIndexEntry ) )
return - 1 ;
if ( timestamp = = AV_NOPTS_VALUE )
return AVERROR ( EINVAL ) ;
if ( size < 0 | | size > 0x3FFFFFFF )
return AVERROR ( EINVAL ) ;
if ( is_relative ( timestamp ) ) //FIXME this maintains previous behavior but we should shift by the correct offset once known
timestamp - = RELATIVE_TS_BASE ;
entries = av_fast_realloc ( * index_entries ,
index_entries_allocated_size ,
( * nb_index_entries + 1 ) *
sizeof ( AVIndexEntry ) ) ;
if ( ! entries )
return - 1 ;
* index_entries = entries ;
index = ff_index_search_timestamp ( * index_entries , * nb_index_entries ,
timestamp , AVSEEK_FLAG_ANY ) ;
if ( index < 0 ) {
index = ( * nb_index_entries ) + + ;
ie = & entries [ index ] ;
av_assert0 ( 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 ) * ( * nb_index_entries - index ) ) ;
( * nb_index_entries ) + + ;
} else if ( ie - > pos = = pos & & distance < ie - > min_distance )
// do not reduce the distance
distance = ie - > min_distance ;
}
ie - > pos = pos ;
ie - > timestamp = timestamp ;
ie - > min_distance = distance ;
ie - > size = size ;
ie - > flags = flags ;
return index ;
}
int av_add_index_entry ( AVStream * st , int64_t pos , int64_t timestamp ,
int size , int distance , int flags )
{
FFStream * const sti = ffstream ( st ) ;
timestamp = ff_wrap_timestamp ( st , timestamp ) ;
return ff_add_index_entry ( & sti - > index_entries , & sti - > nb_index_entries ,
& sti - > index_entries_allocated_size , pos ,
timestamp , size , distance , flags ) ;
}
int ff_index_search_timestamp ( const AVIndexEntry * entries , int nb_entries ,
int64_t wanted_timestamp , int flags )
{
int a , b , m ;
int64_t timestamp ;
a = - 1 ;
b = nb_entries ;
// Optimize appending index entries at the end.
if ( b & & entries [ b - 1 ] . timestamp < wanted_timestamp )
a = b - 1 ;
while ( b - a > 1 ) {
m = ( a + b ) > > 1 ;
// Search for the next non-discarded packet.
while ( ( entries [ m ] . flags & AVINDEX_DISCARD_FRAME ) & & m < b & & m < nb_entries - 1 ) {
m + + ;
if ( m = = b & & entries [ m ] . timestamp > = wanted_timestamp ) {
m = b - 1 ;
break ;
}
}
timestamp = entries [ m ] . timestamp ;
if ( timestamp > = wanted_timestamp )
b = m ;
if ( timestamp < = wanted_timestamp )
a = m ;
}
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 ;
}
void ff_configure_buffers_for_index ( AVFormatContext * s , int64_t time_tolerance )
{
int64_t pos_delta = 0 ;
int64_t skip = 0 ;
//We could use URLProtocol flags here but as many user applications do not use URLProtocols this would be unreliable
const char * proto = avio_find_protocol_name ( s - > url ) ;
FFIOContext * ctx ;
av_assert0 ( time_tolerance > = 0 ) ;
if ( ! proto ) {
av_log ( s , AV_LOG_INFO ,
" Protocol name not provided, cannot determine if input is local or "
" a network protocol, buffers and access patterns cannot be configured "
" optimally without knowing the protocol \n " ) ;
}
if ( proto & & ! ( strcmp ( proto , " file " ) & & strcmp ( proto , " pipe " ) & & strcmp ( proto , " cache " ) ) )
return ;
for ( unsigned ist1 = 0 ; ist1 < s - > nb_streams ; ist1 + + ) {
AVStream * const st1 = s - > streams [ ist1 ] ;
FFStream * const sti1 = ffstream ( st1 ) ;
for ( unsigned ist2 = 0 ; ist2 < s - > nb_streams ; ist2 + + ) {
AVStream * const st2 = s - > streams [ ist2 ] ;
FFStream * const sti2 = ffstream ( st2 ) ;
if ( ist1 = = ist2 )
continue ;
for ( int i1 = 0 , i2 = 0 ; i1 < sti1 - > nb_index_entries ; i1 + + ) {
const AVIndexEntry * const e1 = & sti1 - > index_entries [ i1 ] ;
int64_t e1_pts = av_rescale_q ( e1 - > timestamp , st1 - > time_base , AV_TIME_BASE_Q ) ;
skip = FFMAX ( skip , e1 - > size ) ;
for ( ; i2 < sti2 - > nb_index_entries ; i2 + + ) {
const AVIndexEntry * const e2 = & sti2 - > index_entries [ i2 ] ;
int64_t e2_pts = av_rescale_q ( e2 - > timestamp , st2 - > time_base , AV_TIME_BASE_Q ) ;
if ( e2_pts < e1_pts | | e2_pts - ( uint64_t ) e1_pts < time_tolerance )
continue ;
pos_delta = FFMAX ( pos_delta , e1 - > pos - e2 - > pos ) ;
break ;
}
}
}
}
pos_delta * = 2 ;
ctx = ffiocontext ( s - > pb ) ;
/* XXX This could be adjusted depending on protocol*/
if ( s - > pb - > buffer_size < pos_delta & & pos_delta < ( 1 < < 24 ) ) {
av_log ( s , AV_LOG_VERBOSE , " Reconfiguring buffers to size % " PRId64 " \n " , pos_delta ) ;
/* realloc the buffer and the original data will be retained */
if ( ffio_realloc_buf ( s - > pb , pos_delta ) ) {
av_log ( s , AV_LOG_ERROR , " Realloc buffer fail. \n " ) ;
return ;
}
ctx - > short_seek_threshold = FFMAX ( ctx - > short_seek_threshold , pos_delta / 2 ) ;
}
if ( skip < ( 1 < < 23 ) ) {
ctx - > short_seek_threshold = FFMAX ( ctx - > short_seek_threshold , skip ) ;
}
}
int av_index_search_timestamp ( AVStream * st , int64_t wanted_timestamp , int flags )
{
const FFStream * const sti = ffstream ( st ) ;
return ff_index_search_timestamp ( sti - > index_entries , sti - > nb_index_entries ,
wanted_timestamp , flags ) ;
}
int avformat_index_get_entries_count ( const AVStream * st )
{
return cffstream ( st ) - > nb_index_entries ;
}
const AVIndexEntry * avformat_index_get_entry ( AVStream * st , int idx )
{
const FFStream * const sti = ffstream ( st ) ;
if ( idx < 0 | | idx > = sti - > nb_index_entries )
return NULL ;
return & sti - > index_entries [ idx ] ;
}
const AVIndexEntry * avformat_index_get_entry_from_timestamp ( AVStream * st ,
int64_t wanted_timestamp ,
int flags )
{
const FFStream * const sti = ffstream ( st ) ;
int idx = ff_index_search_timestamp ( sti - > index_entries ,
sti - > nb_index_entries ,
wanted_timestamp , flags ) ;
if ( idx < 0 )
return NULL ;
return & sti - > index_entries [ idx ] ;
}
static int64_t read_timestamp ( AVFormatContext * s , int stream_index , int64_t * ppos , int64_t pos_limit ,
int64_t ( * read_timestamp ) ( struct AVFormatContext * , int , int64_t * , int64_t ) )
{
int64_t ts = read_timestamp ( s , stream_index , ppos , pos_limit ) ;
if ( stream_index > = 0 )
ts = ff_wrap_timestamp ( s - > streams [ stream_index ] , ts ) ;
return ts ;
}
int ff_seek_frame_binary ( AVFormatContext * s , int stream_index ,
int64_t target_ts , int flags )
{
const AVInputFormat * const avif = s - > iformat ;
int64_t pos_min = 0 , pos_max = 0 , pos , pos_limit ;
int64_t ts_min , ts_max , ts ;
int index ;
int64_t ret ;
AVStream * st ;
FFStream * sti ;
if ( stream_index < 0 )
return - 1 ;
av_log ( s , AV_LOG_TRACE , " read_seek: %d %s \n " , stream_index , av_ts2str ( target_ts ) ) ;
ts_max =
ts_min = AV_NOPTS_VALUE ;
pos_limit = - 1 ; // GCC falsely says it may be uninitialized.
st = s - > streams [ stream_index ] ;
sti = ffstream ( st ) ;
if ( sti - > index_entries ) {
const AVIndexEntry * e ;
/* FIXME: Whole function must be checked for non-keyframe entries in
* index case , especially read_timestamp ( ) . */
index = av_index_search_timestamp ( st , target_ts ,
flags | AVSEEK_FLAG_BACKWARD ) ;
index = FFMAX ( index , 0 ) ;
e = & sti - > index_entries [ index ] ;
if ( e - > timestamp < = target_ts | | e - > pos = = e - > min_distance ) {
pos_min = e - > pos ;
ts_min = e - > timestamp ;
av_log ( s , AV_LOG_TRACE , " using cached pos_min=0x% " PRIx64 " dts_min=%s \n " ,
pos_min , av_ts2str ( ts_min ) ) ;
} else {
av_assert1 ( index = = 0 ) ;
}
index = av_index_search_timestamp ( st , target_ts ,
flags & ~ AVSEEK_FLAG_BACKWARD ) ;
av_assert0 ( index < sti - > nb_index_entries ) ;
if ( index > = 0 ) {
e = & sti - > index_entries [ index ] ;
av_assert1 ( e - > timestamp > = target_ts ) ;
pos_max = e - > pos ;
ts_max = e - > timestamp ;
pos_limit = pos_max - e - > min_distance ;
av_log ( s , AV_LOG_TRACE , " using cached pos_max=0x% " PRIx64 " pos_limit=0x% " PRIx64
" dts_max=%s \n " , pos_max , pos_limit , av_ts2str ( ts_max ) ) ;
}
}
pos = ff_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 */
if ( ( ret = avio_seek ( s - > pb , pos , SEEK_SET ) ) < 0 )
return ret ;
ff_read_frame_flush ( s ) ;
avpriv_update_cur_dts ( s , st , ts ) ;
return 0 ;
}
int ff_find_last_ts ( AVFormatContext * s , int stream_index , int64_t * ts , int64_t * pos ,
int64_t ( * read_timestamp_func ) ( struct AVFormatContext * , int , int64_t * , int64_t ) )
{
int64_t step = 1024 ;
int64_t limit , ts_max ;
int64_t filesize = avio_size ( s - > pb ) ;
int64_t pos_max = filesize - 1 ;
do {
limit = pos_max ;
pos_max = FFMAX ( 0 , ( pos_max ) - step ) ;
ts_max = read_timestamp ( s , stream_index ,
& pos_max , limit , read_timestamp_func ) ;
step + = step ;
} while ( ts_max = = AV_NOPTS_VALUE & & 2 * limit > 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 , read_timestamp_func ) ;
if ( tmp_ts = = AV_NOPTS_VALUE )
break ;
av_assert0 ( tmp_pos > pos_max ) ;
ts_max = tmp_ts ;
pos_max = tmp_pos ;
if ( tmp_pos > = filesize )
break ;
}
if ( ts )
* ts = ts_max ;
if ( pos )
* pos = pos_max ;
return 0 ;
}
int64_t ff_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_func ) ( struct AVFormatContext * ,
int , int64_t * , int64_t ) )
{
FFFormatContext * const si = ffformatcontext ( s ) ;
int64_t pos , ts ;
int64_t start_pos ;
int no_change ;
int ret ;
av_log ( s , AV_LOG_TRACE , " gen_seek: %d %s \n " , stream_index , av_ts2str ( target_ts ) ) ;
if ( ts_min = = AV_NOPTS_VALUE ) {
pos_min = si - > data_offset ;
ts_min = read_timestamp ( s , stream_index , & pos_min , INT64_MAX , read_timestamp_func ) ;
if ( ts_min = = AV_NOPTS_VALUE )
return - 1 ;
}
if ( ts_min > = target_ts ) {
* ts_ret = ts_min ;
return pos_min ;
}
if ( ts_max = = AV_NOPTS_VALUE ) {
if ( ( ret = ff_find_last_ts ( s , stream_index , & ts_max , & pos_max , read_timestamp_func ) ) < 0 )
return ret ;
pos_limit = pos_max ;
}
if ( ts_max < = target_ts ) {
* ts_ret = ts_max ;
return pos_max ;
}
av_assert0 ( ts_min < ts_max ) ;
no_change = 0 ;
while ( pos_min < pos_limit ) {
av_log ( s , AV_LOG_TRACE ,
" pos_min=0x% " PRIx64 " pos_max=0x% " PRIx64 " dts_min=%s dts_max=%s \n " ,
pos_min , pos_max , av_ts2str ( ts_min ) , av_ts2str ( ts_max ) ) ;
av_assert0 ( 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 did not change min / 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 ;
// May pass pos_limit instead of -1.
ts = read_timestamp ( s , stream_index , & pos , INT64_MAX , read_timestamp_func ) ;
if ( pos = = pos_max )
no_change + + ;
else
no_change = 0 ;
av_log ( s , AV_LOG_TRACE , " % " PRId64 " % " PRId64 " % " PRId64 " / %s %s %s "
" target:%s limit:% " PRId64 " start:% " PRId64 " noc:%d \n " ,
pos_min , pos , pos_max ,
av_ts2str ( ts_min ) , av_ts2str ( ts ) , av_ts2str ( ts_max ) , av_ts2str ( 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_min = pos ;
ts_min = ts ;
}
}
pos = ( flags & AVSEEK_FLAG_BACKWARD ) ? pos_min : pos_max ;
ts = ( flags & AVSEEK_FLAG_BACKWARD ) ? ts_min : ts_max ;
#if 0
pos_min = pos ;
ts_min = read_timestamp ( s , stream_index , & pos_min , INT64_MAX , read_timestamp_func ) ;
pos_min + + ;
ts_max = read_timestamp ( s , stream_index , & pos_min , INT64_MAX , read_timestamp_func ) ;
av_log ( s , AV_LOG_TRACE , " pos=0x% " PRIx64 " %s<=%s<=%s \n " ,
pos , av_ts2str ( ts_min ) , av_ts2str ( target_ts ) , av_ts2str ( ts_max ) ) ;
# endif
* ts_ret = ts ;
return pos ;
}
static int seek_frame_byte ( AVFormatContext * s , int stream_index ,
int64_t pos , int flags )
{
FFFormatContext * const si = ffformatcontext ( s ) ;
int64_t pos_min , pos_max ;
pos_min = si - > data_offset ;
pos_max = avio_size ( s - > pb ) - 1 ;
if ( pos < pos_min )
pos = pos_min ;
else if ( pos > pos_max )
pos = pos_max ;
avio_seek ( s - > pb , pos , SEEK_SET ) ;
s - > io_repositioned = 1 ;
return 0 ;
}
static int seek_frame_generic ( AVFormatContext * s , int stream_index ,
int64_t timestamp , int flags )
{
FFFormatContext * const si = ffformatcontext ( s ) ;
AVStream * const st = s - > streams [ stream_index ] ;
FFStream * const sti = ffstream ( st ) ;
const AVIndexEntry * ie ;
int index ;
int64_t ret ;
index = av_index_search_timestamp ( st , timestamp , flags ) ;
if ( index < 0 & & sti - > nb_index_entries & &
timestamp < sti - > index_entries [ 0 ] . timestamp )
return - 1 ;
if ( index < 0 | | index = = sti - > nb_index_entries - 1 ) {
AVPacket * const pkt = si - > pkt ;
int nonkey = 0 ;
if ( sti - > nb_index_entries ) {
av_assert0 ( sti - > index_entries ) ;
ie = & sti - > index_entries [ sti - > nb_index_entries - 1 ] ;
if ( ( ret = avio_seek ( s - > pb , ie - > pos , SEEK_SET ) ) < 0 )
return ret ;
s - > io_repositioned = 1 ;
avpriv_update_cur_dts ( s , st , ie - > timestamp ) ;
} else {
if ( ( ret = avio_seek ( s - > pb , si - > data_offset , SEEK_SET ) ) < 0 )
return ret ;
s - > io_repositioned = 1 ;
}
av_packet_unref ( pkt ) ;
for ( ; ; ) {
int read_status ;
do {
read_status = av_read_frame ( s , pkt ) ;
} while ( read_status = = AVERROR ( EAGAIN ) ) ;
if ( read_status < 0 )
break ;
if ( stream_index = = pkt - > stream_index & & pkt - > dts > timestamp ) {
if ( pkt - > flags & AV_PKT_FLAG_KEY ) {
av_packet_unref ( pkt ) ;
break ;
}
if ( nonkey + + > 1000 & & st - > codecpar - > codec_id ! = AV_CODEC_ID_CDGRAPHICS ) {
av_log ( s , AV_LOG_ERROR , " seek_frame_generic failed as this stream seems to contain no keyframes after the target timestamp, %d non keyframes found \n " , nonkey ) ;
av_packet_unref ( pkt ) ;
break ;
}
}
av_packet_unref ( pkt ) ;
}
index = av_index_search_timestamp ( st , timestamp , flags ) ;
}
if ( index < 0 )
return - 1 ;
ff_read_frame_flush ( s ) ;
if ( s - > iformat - > read_seek )
if ( s - > iformat - > read_seek ( s , stream_index , timestamp , flags ) > = 0 )
return 0 ;
ie = & sti - > index_entries [ index ] ;
if ( ( ret = avio_seek ( s - > pb , ie - > pos , SEEK_SET ) ) < 0 )
return ret ;
s - > io_repositioned = 1 ;
avpriv_update_cur_dts ( s , st , ie - > timestamp ) ;
return 0 ;
}
static int seek_frame_internal ( AVFormatContext * s , int stream_index ,
int64_t timestamp , int flags )
{
AVStream * st ;
int ret ;
if ( flags & AVSEEK_FLAG_BYTE ) {
if ( s - > iformat - > flags & AVFMT_NO_BYTE_SEEK )
return - 1 ;
ff_read_frame_flush ( s ) ;
return 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 ) {
ff_read_frame_flush ( s ) ;
ret = s - > iformat - > read_seek ( s , stream_index , timestamp , flags ) ;
} else
ret = - 1 ;
if ( ret > = 0 )
return 0 ;
if ( s - > iformat - > read_timestamp & &
! ( s - > iformat - > flags & AVFMT_NOBINSEARCH ) ) {
ff_read_frame_flush ( s ) ;
return ff_seek_frame_binary ( s , stream_index , timestamp , flags ) ;
} else if ( ! ( s - > iformat - > flags & AVFMT_NOGENSEARCH ) ) {
ff_read_frame_flush ( s ) ;
return seek_frame_generic ( s , stream_index , timestamp , flags ) ;
} else
return - 1 ;
}
int av_seek_frame ( AVFormatContext * s , int stream_index ,
int64_t timestamp , int flags )
{
int ret ;
if ( s - > iformat - > read_seek2 & & ! s - > iformat - > read_seek ) {
int64_t min_ts = INT64_MIN , max_ts = INT64_MAX ;
if ( ( flags & AVSEEK_FLAG_BACKWARD ) )
max_ts = timestamp ;
else
min_ts = timestamp ;
return avformat_seek_file ( s , stream_index , min_ts , timestamp , max_ts ,
flags & ~ AVSEEK_FLAG_BACKWARD ) ;
}
ret = seek_frame_internal ( s , stream_index , timestamp , flags ) ;
if ( ret > = 0 )
ret = avformat_queue_attached_pictures ( s ) ;
return ret ;
}
int avformat_seek_file ( AVFormatContext * s , int stream_index , int64_t min_ts ,
int64_t ts , int64_t max_ts , int flags )
{
if ( min_ts > ts | | max_ts < ts )
return - 1 ;
if ( stream_index < - 1 | | stream_index > = ( int ) s - > nb_streams )
return AVERROR ( EINVAL ) ;
if ( s - > seek2any > 0 )
flags | = AVSEEK_FLAG_ANY ;
flags & = ~ AVSEEK_FLAG_BACKWARD ;
if ( s - > iformat - > read_seek2 ) {
int ret ;
ff_read_frame_flush ( s ) ;
if ( stream_index = = - 1 & & s - > nb_streams = = 1 ) {
AVRational time_base = s - > streams [ 0 ] - > time_base ;
ts = av_rescale_q ( ts , AV_TIME_BASE_Q , time_base ) ;
min_ts = av_rescale_rnd ( min_ts , time_base . den ,
time_base . num * ( int64_t ) AV_TIME_BASE ,
AV_ROUND_UP | AV_ROUND_PASS_MINMAX ) ;
max_ts = av_rescale_rnd ( max_ts , time_base . den ,
time_base . num * ( int64_t ) AV_TIME_BASE ,
AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX ) ;
stream_index = 0 ;
}
ret = s - > iformat - > read_seek2 ( s , stream_index , min_ts ,
ts , max_ts , flags ) ;
if ( ret > = 0 )
ret = avformat_queue_attached_pictures ( s ) ;
return ret ;
}
if ( s - > iformat - > read_timestamp ) {
// try to seek via read_timestamp()
}
// Fall back on old API if new is not implemented but old is.
// Note the old API has somewhat different semantics.
if ( s - > iformat - > read_seek | | 1 ) {
int dir = ( ts - ( uint64_t ) min_ts > ( uint64_t ) max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0 ) ;
int ret = av_seek_frame ( s , stream_index , ts , flags | dir ) ;
if ( ret < 0 & & ts ! = min_ts & & max_ts ! = ts ) {
ret = av_seek_frame ( s , stream_index , dir ? max_ts : min_ts , flags | dir ) ;
if ( ret > = 0 )
ret = av_seek_frame ( s , stream_index , ts , flags | ( dir ^ AVSEEK_FLAG_BACKWARD ) ) ;
}
return ret ;
}
// try some generic seek like seek_frame_generic() but with new ts semantics
return - 1 ; //unreachable
}
/** Flush the frame reader. */
void ff_read_frame_flush ( AVFormatContext * s )
{
FFFormatContext * const si = ffformatcontext ( s ) ;
ff_flush_packet_queue ( s ) ;
/* Reset read state for each stream. */
for ( unsigned i = 0 ; i < s - > nb_streams ; i + + ) {
AVStream * const st = s - > streams [ i ] ;
FFStream * const sti = ffstream ( st ) ;
if ( sti - > parser ) {
av_parser_close ( sti - > parser ) ;
sti - > parser = NULL ;
}
sti - > last_IP_pts = AV_NOPTS_VALUE ;
sti - > last_dts_for_order_check = AV_NOPTS_VALUE ;
if ( sti - > first_dts = = AV_NOPTS_VALUE )
sti - > cur_dts = RELATIVE_TS_BASE ;
else
/* We set the current DTS to an unspecified origin. */
sti - > cur_dts = AV_NOPTS_VALUE ;
sti - > probe_packets = s - > max_probe_packets ;
for ( int j = 0 ; j < MAX_REORDER_DELAY + 1 ; j + + )
sti - > pts_buffer [ j ] = AV_NOPTS_VALUE ;
if ( si - > inject_global_side_data )
sti - > inject_global_side_data = 1 ;
sti - > skip_samples = 0 ;
}
}
int avformat_flush ( AVFormatContext * s )
{
ff_read_frame_flush ( s ) ;
return 0 ;
}
void ff_rescale_interval ( AVRational tb_in , AVRational tb_out ,
int64_t * min_ts , int64_t * ts , int64_t * max_ts )
{
* ts = av_rescale_q ( * ts , tb_in , tb_out ) ;
* min_ts = av_rescale_q_rnd ( * min_ts , tb_in , tb_out ,
AV_ROUND_UP | AV_ROUND_PASS_MINMAX ) ;
* max_ts = av_rescale_q_rnd ( * max_ts , tb_in , tb_out ,
AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX ) ;
}