Newer
Older
}
}
}
if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE ||
duration) {
/* presentation is not delayed : PTS and DTS are the same */
if (pkt->pts == AV_NOPTS_VALUE)
pkt->pts = pkt->dts;
update_initial_timestamps(s, pkt->stream_index, pkt->pts,
pkt->pts);
if (pkt->pts == AV_NOPTS_VALUE)
pkt->pts = st->cur_dts;
pkt->dts = pkt->pts;
if (pkt->pts != AV_NOPTS_VALUE)
st->cur_dts = pkt->pts + duration;
}
Baptiste Coudurier
committed
if(pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY){
st->pts_buffer[0]= pkt->pts;
for(i=0; i<delay && st->pts_buffer[i] > st->pts_buffer[i+1]; i++)
FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i+1]);
if(pkt->dts == AV_NOPTS_VALUE)
pkt->dts= st->pts_buffer[0];
if(st->codec->codec_id == AV_CODEC_ID_H264){ // we skipped it above so we try here
update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet
if(pkt->dts > st->cur_dts)
st->cur_dts = pkt->dts;
}
// av_log(NULL, AV_LOG_ERROR, "OUTdelayed:%d/%d pts:%"PRId64", dts:%"PRId64" cur_dts:%"PRId64"\n", presentation_delayed, delay, pkt->pts, pkt->dts, st->cur_dts);
/* update flags */
if(is_intra_only(st->codec))
pkt->flags |= AV_PKT_FLAG_KEY;
if (pc)
pkt->convergence_duration = pc->convergence_duration;
static void free_packet_buffer(AVPacketList **pkt_buf, AVPacketList **pkt_buf_end)
{
while (*pkt_buf) {
AVPacketList *pktl = *pkt_buf;
*pkt_buf = pktl->next;
av_free_packet(&pktl->pkt);
av_freep(&pktl);
}
*pkt_buf_end = NULL;
}
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
/**
* Parse a packet, add all split parts to parse_queue
*
* @param pkt packet to parse, NULL when flushing the parser at end of stream
*/
static int parse_packet(AVFormatContext *s, AVPacket *pkt, int stream_index)
{
AVPacket out_pkt = { 0 }, flush_pkt = { 0 };
AVStream *st = s->streams[stream_index];
uint8_t *data = pkt ? pkt->data : NULL;
int size = pkt ? pkt->size : 0;
int ret = 0, got_output = 0;
if (!pkt) {
av_init_packet(&flush_pkt);
pkt = &flush_pkt;
got_output = 1;
}
while (size > 0 || (pkt == &flush_pkt && got_output)) {
int len;
av_init_packet(&out_pkt);
len = av_parser_parse2(st->parser, st->codec,
&out_pkt.data, &out_pkt.size, data, size,
pkt->pts, pkt->dts, pkt->pos);
pkt->pts = pkt->dts = AV_NOPTS_VALUE;
/* increment read pointer */
data += len;
size -= len;
got_output = !!out_pkt.size;
if (!out_pkt.size)
continue;
/* set the duration */
out_pkt.duration = 0;
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
if (st->codec->sample_rate > 0) {
out_pkt.duration = av_rescale_q_rnd(st->parser->duration,
(AVRational){ 1, st->codec->sample_rate },
st->time_base,
AV_ROUND_DOWN);
}
} else if (st->codec->time_base.num != 0 &&
st->codec->time_base.den != 0) {
out_pkt.duration = av_rescale_q_rnd(st->parser->duration,
st->codec->time_base,
st->time_base,
AV_ROUND_DOWN);
}
out_pkt.stream_index = st->index;
out_pkt.pts = st->parser->pts;
out_pkt.dts = st->parser->dts;
out_pkt.pos = st->parser->pos;
if (st->parser->key_frame == 1 ||
(st->parser->key_frame == -1 &&
st->parser->pict_type == AV_PICTURE_TYPE_I))
out_pkt.flags |= AV_PKT_FLAG_KEY;
compute_pkt_fields(s, st, st->parser, &out_pkt);
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
out_pkt.flags & AV_PKT_FLAG_KEY) {
ff_reduce_index(s, st->index);
av_add_index_entry(st, st->parser->frame_offset, out_pkt.dts,
0, 0, AVINDEX_KEYFRAME);
}
if (out_pkt.data == pkt->data && out_pkt.size == pkt->size) {
out_pkt.destruct = pkt->destruct;
pkt->destruct = NULL;
}
if ((ret = av_dup_packet(&out_pkt)) < 0)
goto fail;
if (!add_to_pktbuf(&s->parse_queue, &out_pkt, &s->parse_queue_end)) {
av_free_packet(&out_pkt);
ret = AVERROR(ENOMEM);
goto fail;
}
}
/* end of the stream => close and free the parser */
if (pkt == &flush_pkt) {
av_parser_close(st->parser);
st->parser = NULL;
}
fail:
av_free_packet(pkt);
return ret;
}
static int read_from_packet_buffer(AVPacketList **pkt_buffer,
AVPacketList **pkt_buffer_end,
AVPacket *pkt)
{
AVPacketList *pktl;
av_assert0(*pkt_buffer);
pktl = *pkt_buffer;
*pkt = pktl->pkt;
*pkt_buffer = pktl->next;
if (!pktl->next)
*pkt_buffer_end = NULL;
av_freep(&pktl);
return 0;
}
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
int ret = 0, i, got_packet = 0;
while (!got_packet && !s->parse_queue) {
AVStream *st;
AVPacket cur_pkt;
if (ret < 0) {
if (ret == AVERROR(EAGAIN))
/* flush the parsers */
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser && st->need_parsing)
parse_packet(s, NULL, st->index);
/* all remaining packets are now in parse_queue =>
* really terminate parsing */
break;
}
ret = 0;
st = s->streams[cur_pkt.stream_index];
if (cur_pkt.pts != AV_NOPTS_VALUE &&
cur_pkt.dts != AV_NOPTS_VALUE &&
cur_pkt.pts < cur_pkt.dts) {
av_log(s, AV_LOG_WARNING, "Invalid timestamps stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d\n",
cur_pkt.stream_index,
cur_pkt.pts,
cur_pkt.dts,
cur_pkt.size);
}
if (s->debug & FF_FDEBUG_TS)
av_log(s, AV_LOG_DEBUG, "ff_read_packet stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n",
cur_pkt.stream_index,
cur_pkt.pts,
cur_pkt.dts,
cur_pkt.size,
cur_pkt.duration,
cur_pkt.flags);
if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) {
st->parser = av_parser_init(st->codec->codec_id);
if (!st->parser) {
/* no parser available: just output the raw packets */
st->need_parsing = AVSTREAM_PARSE_NONE;
} else if(st->need_parsing == AVSTREAM_PARSE_HEADERS) {
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
} else if(st->need_parsing == AVSTREAM_PARSE_FULL_ONCE) {
st->parser->flags |= PARSER_FLAG_ONCE;
if (!st->need_parsing || !st->parser) {
/* no parsing needed: we just output the packet as is */
*pkt = cur_pkt;
compute_pkt_fields(s, st, NULL, pkt);
if ((s->iformat->flags & AVFMT_GENERIC_INDEX) &&
(pkt->flags & AV_PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) {
ff_reduce_index(s, st->index);
av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
got_packet = 1;
} else if (st->discard < AVDISCARD_ALL) {
if ((ret = parse_packet(s, &cur_pkt, cur_pkt.stream_index)) < 0)
return ret;
} else {
/* free packet */
av_free_packet(&cur_pkt);
if (!got_packet && s->parse_queue)
ret = read_from_packet_buffer(&s->parse_queue, &s->parse_queue_end, pkt);
av_log(s, AV_LOG_DEBUG, "read_frame_internal stream=%d, pts=%"PRId64", dts=%"PRId64", size=%d, duration=%d, flags=%d\n",
pkt->stream_index,
pkt->pts,
pkt->dts,
}
int av_read_frame(AVFormatContext *s, AVPacket *pkt)
const int genpts = s->flags & AVFMT_FLAG_GENPTS;
int eof = 0;
if (!genpts)
return s->packet_buffer ? read_from_packet_buffer(&s->packet_buffer,
&s->packet_buffer_end,
pkt) :
read_frame_internal(s, pkt);
int ret;
AVPacketList *pktl = s->packet_buffer;
AVPacket *next_pkt = &pktl->pkt;
if (next_pkt->dts != AV_NOPTS_VALUE) {
int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;
while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
if (pktl->pkt.stream_index == next_pkt->stream_index &&
(av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) &&
av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame
next_pkt->pts = pktl->pkt.dts;
}
pktl = s->packet_buffer;
}
/* read packet from packet buffer, if there is data */
if (!(next_pkt->pts == AV_NOPTS_VALUE &&
next_pkt->dts != AV_NOPTS_VALUE && !eof))
return read_from_packet_buffer(&s->packet_buffer,
&s->packet_buffer_end, pkt);
ret = read_frame_internal(s, pkt);
if (ret < 0) {
if (pktl && ret != AVERROR(EAGAIN)) {
eof = 1;
continue;
} else
return ret;
if (av_dup_packet(add_to_pktbuf(&s->packet_buffer, pkt,
&s->packet_buffer_end)) < 0)
return AVERROR(ENOMEM);
}
}
/* XXX: suppress the packet queue */
static void flush_packet_queue(AVFormatContext *s)
{
free_packet_buffer(&s->parse_queue, &s->parse_queue_end);
free_packet_buffer(&s->packet_buffer, &s->packet_buffer_end);
free_packet_buffer(&s->raw_packet_buffer, &s->raw_packet_buffer_end);
s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
Fabrice Bellard
committed
}
/*******************************************************/
/* seek support */
int av_find_default_stream_index(AVFormatContext *s)
{
int first_audio_index = -1;
int i;
AVStream *st;
if (s->nb_streams <= 0)
return -1;
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
!(st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
return i;
}
if (first_audio_index < 0 && st->codec->codec_type == AVMEDIA_TYPE_AUDIO)
first_audio_index = i;
return first_audio_index >= 0 ? first_audio_index : 0;
Daniel Kristjansson
committed
/**
* Flush the frame reader.
*/
void ff_read_frame_flush(AVFormatContext *s)
{
AVStream *st;
flush_packet_queue(s);
/* for each stream, reset read state */
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (st->parser) {
av_parser_close(st->parser);
st->parser = NULL;
}
st->cur_dts = AV_NOPTS_VALUE; /* we set the current DTS to an unspecified origin */
st->reference_dts = AV_NOPTS_VALUE;
st->probe_packets = MAX_PROBE_PACKETS;
for(j=0; j<MAX_REORDER_DELAY+1; j++)
st->pts_buffer[j]= AV_NOPTS_VALUE;
void ff_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
{
int i;
for(i = 0; i < s->nb_streams; i++) {
Nathan Kurz
committed
AVStream *st = s->streams[i];
st->cur_dts = av_rescale(timestamp,
Nathan Kurz
committed
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 *st= s->streams[stream_index];
unsigned int max_entries= s->max_index_size / sizeof(AVIndexEntry);
if((unsigned)st->nb_index_entries >= max_entries){
int i;
for(i=0; 2*i<st->nb_index_entries; i++)
st->index_entries[i]= st->index_entries[2*i];
st->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))
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);
index= (*nb_index_entries)++;
Michael Niedermayer
committed
ie= &entries[index];
assert(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
Michael Niedermayer
committed
}
ie->pos = pos;
ie->timestamp = timestamp;
Michael Niedermayer
committed
ie->min_distance= distance;
ie->flags = flags;
Michael Niedermayer
committed
return index;
int av_add_index_entry(AVStream *st,
int64_t pos, int64_t timestamp, int size, int distance, int flags)
{
return ff_add_index_entry(&st->index_entries, &st->nb_index_entries,
&st->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;
//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;
timestamp = entries[m].timestamp;
if(timestamp >= wanted_timestamp)
b = m;
if(timestamp <= wanted_timestamp)
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;
}
}
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp,
int flags)
{
return ff_index_search_timestamp(st->index_entries, st->nb_index_entries,
wanted_timestamp, flags);
}
int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags)
{
Michael Niedermayer
committed
AVInputFormat *avif= s->iformat;
int64_t av_uninit(pos_min), av_uninit(pos_max), pos, pos_limit;
Michael Niedermayer
committed
int64_t ts_min, ts_max, ts;
Michael Niedermayer
committed
int index;
Michael Niedermayer
committed
AVStream *st;
if (stream_index < 0)
return -1;
av_dlog(s, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
Michael Niedermayer
committed
ts_max=
ts_min= AV_NOPTS_VALUE;
pos_limit= -1; //gcc falsely says it may be uninitialized
Michael Niedermayer
committed
st= s->streams[stream_index];
if(st->index_entries){
AVIndexEntry *e;
index= av_index_search_timestamp(st, target_ts, flags | AVSEEK_FLAG_BACKWARD); //FIXME whole func must be checked for non-keyframe entries in index case, especially read_timestamp()
Michael Niedermayer
committed
e= &st->index_entries[index];
if(e->timestamp <= target_ts || e->pos == e->min_distance){
pos_min= e->pos;
ts_min= e->timestamp;
av_dlog(s, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
pos_min,ts_min);
Michael Niedermayer
committed
}else{
assert(index==0);
}
index= av_index_search_timestamp(st, target_ts, flags & ~AVSEEK_FLAG_BACKWARD);
assert(index < st->nb_index_entries);
if(index >= 0){
Michael Niedermayer
committed
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;
av_dlog(s, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n",
pos_max,pos_limit, ts_max);
Michael Niedermayer
committed
}
}
pos= ff_gen_search(s, stream_index, target_ts, pos_min, pos_max, pos_limit, ts_min, ts_max, flags, &ts, avif->read_timestamp);
Michael Niedermayer
committed
if(pos<0)
return -1;
/* do the seek */
if ((ret = avio_seek(s->pb, pos, SEEK_SET)) < 0)
Michael Niedermayer
committed
ff_update_cur_dts(s, st, ts);
Michael Niedermayer
committed
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)(struct AVFormatContext *, int , int64_t *, int64_t ))
{
Michael Niedermayer
committed
int64_t pos, ts;
int64_t start_pos, filesize;
int no_change;
av_dlog(s, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
Michael Niedermayer
committed
Michael Niedermayer
committed
if(ts_min == AV_NOPTS_VALUE){
pos_min = s->data_offset;
Michael Niedermayer
committed
ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
Michael Niedermayer
committed
if (ts_min == AV_NOPTS_VALUE)
return -1;
}
if(ts_max == AV_NOPTS_VALUE){
int step= 1024;
Kenneth Aafløy
committed
pos_max = filesize - 1;
Michael Niedermayer
committed
do{
pos_max -= step;
Michael Niedermayer
committed
ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step);
Michael Niedermayer
committed
step += step;
}while(ts_max == AV_NOPTS_VALUE && pos_max >= step);
if (ts_max == AV_NOPTS_VALUE)
return -1;
Michael Niedermayer
committed
for(;;){
int64_t tmp_pos= pos_max + 1;
Michael Niedermayer
committed
int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
Michael Niedermayer
committed
if(tmp_ts == AV_NOPTS_VALUE)
break;
ts_max= tmp_ts;
pos_max= tmp_pos;
Kenneth Aafløy
committed
if(tmp_pos >= filesize)
break;
Michael Niedermayer
committed
}
pos_limit= pos_max;
}
if(ts_min > ts_max){
return -1;
}else if(ts_min == ts_max){
pos_limit= pos_min;
}
Michael Niedermayer
committed
no_change=0;
while (pos_min < pos_limit) {
av_dlog(s, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n",
pos_min, pos_max, ts_min, ts_max);
Michael Niedermayer
committed
assert(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;
Michael Niedermayer
committed
}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 */
Michael Niedermayer
committed
pos=pos_min;
}
if(pos <= pos_min)
pos= pos_min + 1;
else if(pos > pos_limit)
pos= pos_limit;
start_pos= pos;
Michael Niedermayer
committed
ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1
Michael Niedermayer
committed
if(pos == pos_max)
no_change++;
else
no_change=0;
av_dlog(s, "%"PRId64" %"PRId64" %"PRId64" / %"PRId64" %"PRId64" %"PRId64" target:%"PRId64" limit:%"PRId64" start:%"PRId64" noc:%d\n",
pos_min, pos, pos_max, ts_min, ts, ts_max, 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;
}
Michael Niedermayer
committed
assert(ts != AV_NOPTS_VALUE);
Michael Niedermayer
committed
pos_limit = start_pos - 1;
pos_max = pos;
ts_max = ts;
Michael Niedermayer
committed
pos_min = pos;
ts_min = ts;
}
}
pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max;
ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max;
Michael Niedermayer
committed
pos_min = pos;
Michael Niedermayer
committed
ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
Michael Niedermayer
committed
pos_min++;
Michael Niedermayer
committed
ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX);
av_dlog(s, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n",
pos, ts_min, target_ts, ts_max);
Michael Niedermayer
committed
*ts_ret= ts;
return pos;
Michael Niedermayer
committed
}
static int seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, int flags){
int64_t pos_min, pos_max;
pos_min = s->data_offset;
if (pos < pos_min) pos= pos_min;
else if(pos > pos_max) pos= pos_max;
static int seek_frame_generic(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
int index;
int64_t ret;
AVStream *st;
AVIndexEntry *ie;
st = s->streams[stream_index];
index = av_index_search_timestamp(st, timestamp, flags);
Michael Niedermayer
committed
if(index < 0 && st->nb_index_entries && timestamp < st->index_entries[0].timestamp)
return -1;
if(index < 0 || index==st->nb_index_entries-1){
AVPacket pkt;
if(st->nb_index_entries){
assert(st->index_entries);
ie= &st->index_entries[st->nb_index_entries-1];
if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0)
ff_update_cur_dts(s, st, ie->timestamp);
if ((ret = avio_seek(s->pb, s->data_offset, SEEK_SET)) < 0)
return ret;
}
read_status = av_read_frame(s, &pkt);
} while (read_status == AVERROR(EAGAIN));
if (read_status < 0)
break;
av_free_packet(&pkt);
if(stream_index == pkt.stream_index){
if((pkt.flags & AV_PKT_FLAG_KEY) && pkt.dts > timestamp)
break;
}
}
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 = &st->index_entries[index];
if ((ret = avio_seek(s->pb, ie->pos, SEEK_SET)) < 0)
ff_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;
Justin Ruggles
committed
if (flags & AVSEEK_FLAG_BYTE) {
if (s->iformat->flags & AVFMT_NO_BYTE_SEEK)
return -1;
Justin Ruggles
committed
ff_read_frame_flush(s);
return seek_frame_byte(s, stream_index, timestamp, flags);
Justin Ruggles
committed
}
if(stream_index < 0){
stream_index= av_find_default_stream_index(s);
if(stream_index < 0)
return -1;
/* 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 */
Justin Ruggles
committed
if (s->iformat->read_seek) {
ff_read_frame_flush(s);
ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
Justin Ruggles
committed
} else
ret = -1;
if (ret >= 0) {
return 0;
}
Michael Niedermayer
committed
Justin Ruggles
committed
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);
Justin Ruggles
committed
} else if (!(s->iformat->flags & AVFMT_NOGENSEARCH)) {
ff_read_frame_flush(s);
return seek_frame_generic(s, stream_index, timestamp, flags);
Justin Ruggles
committed
}
else
return -1;
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
{
int ret = seek_frame_internal(s, stream_index, timestamp, flags);
if (ret >= 0)
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;
Justin Ruggles
committed
if (s->iformat->read_seek2) {
Justin Ruggles
committed
ff_read_frame_flush(s);
ret = s->iformat->read_seek2(s, stream_index, min_ts, ts, max_ts, flags);
if (ret >= 0)
queue_attached_pictures(s);
return ret;
Justin Ruggles
committed
}
if(s->iformat->read_timestamp){
//try to seek via read_timestamp()
}
//Fallback to old API if new is not implemented but old is
//Note the old has somewat different sematics
if(s->iformat->read_seek || 1)
Michael Niedermayer
committed
return av_seek_frame(s, stream_index, ts, flags | (ts - min_ts > (uint64_t)(max_ts - ts) ? AVSEEK_FLAG_BACKWARD : 0));
// try some generic seek like seek_frame_generic() but with new ts semantics
/*******************************************************/
Fabrice Bellard
committed
Daniel Kristjansson
committed
/**
* Return TRUE if the stream has accurate duration in any stream.
Daniel Kristjansson
committed
*
* @return TRUE if the stream has accurate duration for at least one component.
Daniel Kristjansson
committed
*/
static int has_duration(AVFormatContext *ic)
Fabrice Bellard
committed
{
int i;
AVStream *st;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->duration != AV_NOPTS_VALUE)
Fabrice Bellard
committed
return 1;
}
if (ic->duration != AV_NOPTS_VALUE)
Fabrice Bellard
committed
return 0;
}
Daniel Kristjansson
committed
/**
* Estimate the stream timings from the one of each components.
*
* Also computes the global bitrate if possible.
*/
static void update_stream_timings(AVFormatContext *ic)
Fabrice Bellard
committed
{
int64_t start_time, start_time1, end_time, end_time1;
int64_t duration, duration1, filesize;
Fabrice Bellard
committed
int i;
AVStream *st;
start_time = INT64_MAX;
end_time = INT64_MIN;
duration = INT64_MIN;
Fabrice Bellard
committed
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time != AV_NOPTS_VALUE && st->time_base.den) {
start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
start_time = FFMIN(start_time, start_time1);
Fabrice Bellard
committed
if (st->duration != AV_NOPTS_VALUE) {
end_time1 = start_time1
+ av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
end_time = FFMAX(end_time, end_time1);
Fabrice Bellard
committed
}
}
if (st->duration != AV_NOPTS_VALUE) {
duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
duration = FFMAX(duration, duration1);
Fabrice Bellard
committed
}
if (start_time != INT64_MAX) {
Fabrice Bellard
committed
ic->start_time = start_time;
if (end_time != INT64_MIN)
duration = FFMAX(duration, end_time - start_time);
}
if (duration != INT64_MIN) {
ic->duration = duration;
if (ic->pb && (filesize = avio_size(ic->pb)) > 0) {
ic->bit_rate = (double)filesize * 8.0 * AV_TIME_BASE /
(double)ic->duration;
Fabrice Bellard
committed
}
}
}
static void fill_all_stream_timings(AVFormatContext *ic)
{
int i;
AVStream *st;
update_stream_timings(ic);
Fabrice Bellard
committed
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE) {
if(ic->start_time != AV_NOPTS_VALUE)
st->start_time = av_rescale_q(ic->start_time, AV_TIME_BASE_Q, st->time_base);
if(ic->duration != AV_NOPTS_VALUE)
st->duration = av_rescale_q(ic->duration, AV_TIME_BASE_Q, st->time_base);
Fabrice Bellard
committed
}
}
}
static void estimate_timings_from_bit_rate(AVFormatContext *ic)
Fabrice Bellard
committed
{
int64_t filesize, duration;
int bit_rate, i;
AVStream *st;
/* if bit_rate is already set, we believe it */
if (ic->bit_rate <= 0) {
Fabrice Bellard
committed
bit_rate = 0;
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (st->codec->bit_rate > 0)
Michael Niedermayer
committed
bit_rate += st->codec->bit_rate;
Fabrice Bellard
committed
}
ic->bit_rate = bit_rate;
}
/* if duration is already set, we believe it */
if (ic->duration == AV_NOPTS_VALUE &&
ic->bit_rate != 0) {
filesize = ic->pb ? avio_size(ic->pb) : 0;
Fabrice Bellard
committed
if (filesize > 0) {
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
duration= av_rescale(8*filesize, st->time_base.den, ic->bit_rate*(int64_t)st->time_base.num);
if (st->duration == AV_NOPTS_VALUE)
Fabrice Bellard
committed
st->duration = duration;
}
}
}
}
#define DURATION_MAX_READ_SIZE 250000
Michael Niedermayer
committed
#define DURATION_MAX_RETRY 3
Fabrice Bellard
committed
/* only usable for MPEG-PS streams */
static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
Fabrice Bellard
committed
{
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
int read_size, i, ret;
Fabrice Bellard
committed
int64_t filesize, offset, duration;
Michael Niedermayer
committed
int retry=0;
/* flush packet queue */
flush_packet_queue(ic);
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE && st->first_dts == AV_NOPTS_VALUE)
av_log(st->codec, AV_LOG_WARNING, "start time is not set in estimate_timings_from_pts\n");
if (st->parser) {
av_parser_close(st->parser);
st->parser= NULL;
}
}
Fabrice Bellard
committed
/* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->pb ? avio_size(ic->pb) : 0;
Michael Niedermayer
committed
end_time = AV_NOPTS_VALUE;
do{
offset = filesize - (DURATION_MAX_READ_SIZE<<retry);
if (offset < 0)
offset = 0;
Fabrice Bellard
committed
avio_seek(ic->pb, offset, SEEK_SET);
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE<<(FFMAX(retry-1,0)))
break;
} while(ret == AVERROR(EAGAIN));
if (ret != 0)
break;
read_size += pkt->size;
st = ic->streams[pkt->stream_index];
if (pkt->pts != AV_NOPTS_VALUE &&
(st->start_time != AV_NOPTS_VALUE ||
st->first_dts != AV_NOPTS_VALUE)) {
duration = end_time = pkt->pts;
if (st->start_time != AV_NOPTS_VALUE)
duration -= st->start_time;
else
duration -= st->first_dts;
if (duration < 0)
duration += 1LL<<st->pts_wrap_bits;
if (duration > 0) {
if (st->duration == AV_NOPTS_VALUE || st->duration < duration)
st->duration = duration;
}