Newer
Older
int64_t pos, int64_t timestamp, int size, int distance, int flags)
{
AVIndexEntry *entries, *ie;
int index;
if((unsigned)st->nb_index_entries + 1 >= UINT_MAX / sizeof(AVIndexEntry))
return -1;
entries = av_fast_realloc(st->index_entries,
&st->index_entries_allocated_size,
(st->nb_index_entries + 1) *
sizeof(AVIndexEntry));
if(!entries)
return -1;
st->index_entries= entries;
index= av_index_search_timestamp(st, timestamp, AVSEEK_FLAG_ANY);
Michael Niedermayer
committed
index= st->nb_index_entries++;
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)*(st->nb_index_entries - index));
st->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;
Michael Niedermayer
committed
int av_index_search_timestamp(AVStream *st, int64_t wanted_timestamp,
AVIndexEntry *entries= st->index_entries;
int nb_entries= st->nb_index_entries;
int a, b, m;
int64_t timestamp;
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;
}
}
Michael Niedermayer
committed
int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
Michael Niedermayer
committed
AVInputFormat *avif= s->iformat;
int64_t pos_min, pos_max, pos, pos_limit;
int64_t ts_min, ts_max, ts;
Michael Niedermayer
committed
int index;
Michael Niedermayer
committed
AVStream *st;
if (stream_index < 0)
return -1;
Michael Niedermayer
committed
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "read_seek: %d %"PRId64"\n", stream_index, target_ts);
Michael Niedermayer
committed
#endif
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;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "using cached pos_min=0x%"PRIx64" dts_min=%"PRId64"\n",
Michael Niedermayer
committed
pos_min,ts_min);
#endif
}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;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "using cached pos_max=0x%"PRIx64" pos_limit=0x%"PRIx64" dts_max=%"PRId64"\n",
Michael Niedermayer
committed
pos_max,pos_limit, ts_max);
#endif
}
}
Michael Niedermayer
committed
pos= av_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 */
url_fseek(s->pb, pos, SEEK_SET);
Michael Niedermayer
committed
av_update_cur_dts(s, st, ts);
return 0;
}
int64_t av_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 )){
int64_t pos, ts;
int64_t start_pos, filesize;
int no_change;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "gen_seek: %d %"PRId64"\n", stream_index, target_ts);
#endif
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;
filesize = url_fsize(s->pb);
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) {
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "pos_min=0x%"PRIx64" pos_max=0x%"PRIx64" dts_min=%"PRId64" dts_max=%"PRId64"\n",
Michael Niedermayer
committed
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 = 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 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;
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;
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "%"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);
Michael Niedermayer
committed
#endif
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
#ifdef DEBUG_SEEK
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_log(s, AV_LOG_DEBUG, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n",
Michael Niedermayer
committed
pos, ts_min, target_ts, ts_max);
#endif
Michael Niedermayer
committed
*ts_ret= ts;
return pos;
Michael Niedermayer
committed
}
static int av_seek_frame_byte(AVFormatContext *s, int stream_index, int64_t pos, int flags){
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_fsize(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);
#endif
return 0;
}
static int av_seek_frame_generic(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
AVStream *st;
AVIndexEntry *ie;
st = s->streams[stream_index];
index = av_index_search_timestamp(st, timestamp, flags);
if(index < 0 || index==st->nb_index_entries-1){
int i;
AVPacket pkt;
if(st->index_entries && st->nb_index_entries){
ie= &st->index_entries[st->nb_index_entries-1];
url_fseek(s->pb, ie->pos, SEEK_SET);
av_update_cur_dts(s, st, ie->timestamp);
}else
url_fseek(s->pb, 0, SEEK_SET);
for(i=0;; i++) {
int ret = av_read_frame(s, &pkt);
if(ret<0)
break;
av_free_packet(&pkt);
if(stream_index == pkt.stream_index){
if((pkt.flags & PKT_FLAG_KEY) && pkt.dts > timestamp)
break;
}
}
index = av_index_search_timestamp(st, timestamp, flags);
}
if (index < 0)
return -1;
av_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];
url_fseek(s->pb, ie->pos, SEEK_SET);
return 0;
}
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags)
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;
/* 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);
}
st= s->streams[stream_index];
/* first, we try the format specific seek */
if (s->iformat->read_seek)
ret = s->iformat->read_seek(s, stream_index, timestamp, flags);
else
ret = -1;
if (ret >= 0) {
return 0;
}
Michael Niedermayer
committed
if(s->iformat->read_timestamp)
return av_seek_frame_binary(s, stream_index, timestamp, flags);
Michael Niedermayer
committed
else
return av_seek_frame_generic(s, stream_index, timestamp, flags);
/*******************************************************/
Fabrice Bellard
committed
Daniel Kristjansson
committed
/**
* Returns 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 av_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;
}
return 0;
}
Daniel Kristjansson
committed
/**
* Estimate the stream timings from the one of each components.
*
* Also computes the global bitrate if possible.
*/
Fabrice Bellard
committed
static void av_update_stream_timings(AVFormatContext *ic)
{
int64_t start_time, start_time1, end_time, end_time1;
int64_t duration, duration1;
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) {
start_time1= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
if (start_time1 < start_time)
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);
Fabrice Bellard
committed
if (end_time1 > end_time)
end_time = end_time1;
}
}
if (st->duration != AV_NOPTS_VALUE) {
duration1 = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
if (duration1 > duration)
duration = duration1;
}
Fabrice Bellard
committed
}
if (start_time != INT64_MAX) {
Fabrice Bellard
committed
ic->start_time = start_time;
if (end_time != INT64_MIN) {
if (end_time - start_time > duration)
duration = end_time - start_time;
}
}
if (duration != INT64_MIN) {
ic->duration = duration;
if (ic->file_size > 0) {
/* compute the bit rate */
ic->bit_rate = (double)ic->file_size * 8.0 * AV_TIME_BASE /
(double)ic->duration;
Fabrice Bellard
committed
}
}
}
static void fill_all_stream_timings(AVFormatContext *ic)
{
int i;
AVStream *st;
av_update_stream_timings(ic);
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 av_estimate_timings_from_bit_rate(AVFormatContext *ic)
{
int64_t filesize, duration;
int bit_rate, i;
AVStream *st;
/* if bit_rate is already set, we believe it */
if (ic->bit_rate == 0) {
bit_rate = 0;
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
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 &&
Fabrice Bellard
committed
ic->file_size != 0) {
filesize = ic->file_size;
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
/* only usable for MPEG-PS streams */
static void av_estimate_timings_from_pts(AVFormatContext *ic, offset_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;
/* free previous packet */
if (ic->cur_st && ic->cur_st->parser)
av_free_packet(&ic->cur_pkt);
ic->cur_st = NULL;
/* flush packet queue */
flush_packet_queue(ic);
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (st->parser) {
av_parser_close(st->parser);
st->parser= NULL;
}
}
Fabrice Bellard
committed
/* we read the first packets to get the first PTS (not fully
accurate, but it is enough now) */
url_fseek(ic->pb, 0, SEEK_SET);
Fabrice Bellard
committed
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE)
break;
/* if all info is available, we can stop */
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE)
break;
}
if (i == ic->nb_streams)
break;
ret = av_read_packet(ic, pkt);
if (ret != 0)
break;
read_size += pkt->size;
st = ic->streams[pkt->stream_index];
if (pkt->pts != AV_NOPTS_VALUE) {
if (st->start_time == AV_NOPTS_VALUE)
Michael Niedermayer
committed
}
av_free_packet(pkt);
}
Fabrice Bellard
committed
/* estimate the end time (duration) */
/* XXX: may need to support wrapping */
filesize = ic->file_size;
offset = filesize - DURATION_MAX_READ_SIZE;
if (offset < 0)
offset = 0;
url_fseek(ic->pb, offset, SEEK_SET);
Fabrice Bellard
committed
read_size = 0;
for(;;) {
if (read_size >= DURATION_MAX_READ_SIZE)
break;
Fabrice Bellard
committed
ret = av_read_packet(ic, pkt);
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) {
Fabrice Bellard
committed
duration = end_time - st->start_time;
if (duration > 0) {
if (st->duration == AV_NOPTS_VALUE ||
st->duration < duration)
st->duration = duration;
}
}
av_free_packet(pkt);
}
Fabrice Bellard
committed
url_fseek(ic->pb, old_offset, SEEK_SET);
for(i=0; i<ic->nb_streams; i++){
st= ic->streams[i];
st->cur_dts= st->first_dts;
Fabrice Bellard
committed
}
static void av_estimate_timings(AVFormatContext *ic, offset_t old_offset)
Fabrice Bellard
committed
{
int64_t file_size;
/* get the file size, if possible */
if (ic->iformat->flags & AVFMT_NOFILE) {
file_size = 0;
} else {
file_size = url_fsize(ic->pb);
Fabrice Bellard
committed
if (file_size < 0)
file_size = 0;
}
ic->file_size = file_size;
if ((!strcmp(ic->iformat->name, "mpeg") ||
!strcmp(ic->iformat->name, "mpegts")) &&
file_size && !ic->pb->is_streamed) {
Fabrice Bellard
committed
/* get accurate estimate from the PTSes */
av_estimate_timings_from_pts(ic, old_offset);
} else if (av_has_duration(ic)) {
Fabrice Bellard
committed
/* at least one components has timings - we use them for all
the components */
fill_all_stream_timings(ic);
} else {
/* less precise: use bit rate info */
av_estimate_timings_from_bit_rate(ic);
}
av_update_stream_timings(ic);
#if 0
{
int i;
AVStream *st;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
printf("%d: start_time: %0.3f duration: %0.3f\n",
i, (double)st->start_time / AV_TIME_BASE,
Fabrice Bellard
committed
(double)st->duration / AV_TIME_BASE);
}
printf("stream: start_time: %0.3f duration: %0.3f bitrate=%d kb/s\n",
(double)ic->start_time / AV_TIME_BASE,
Fabrice Bellard
committed
(double)ic->duration / AV_TIME_BASE,
ic->bit_rate / 1000);
}
#endif
}
Fabrice Bellard
committed
static int has_codec_parameters(AVCodecContext *enc)
{
int val;
switch(enc->codec_type) {
case CODEC_TYPE_AUDIO:
val = enc->sample_rate;
break;
case CODEC_TYPE_VIDEO:
val = enc->width && enc->pix_fmt != PIX_FMT_NONE;
Fabrice Bellard
committed
break;
default:
val = 1;
break;
}
return (enc->codec_id != CODEC_ID_NONE && val != 0);
Fabrice Bellard
committed
}
static int try_decode_frame(AVStream *st, const uint8_t *data, int size)
{
int16_t *samples;
AVCodec *codec;
Michael Niedermayer
committed
if(!st->codec->codec){
codec = avcodec_find_decoder(st->codec->codec_id);
if (!codec)
return -1;
Michael Niedermayer
committed
ret = avcodec_open(st->codec, codec);
if (ret < 0)
return ret;
Michael Niedermayer
committed
}
Michael Niedermayer
committed
if(!has_codec_parameters(st->codec)){
switch(st->codec->codec_type) {
case CODEC_TYPE_VIDEO:
ret = avcodec_decode_video(st->codec, &picture,
&got_picture, (uint8_t *)data, size);
break;
case CODEC_TYPE_AUDIO:
data_size = FFMAX(size, AVCODEC_MAX_AUDIO_FRAME_SIZE);
samples = av_malloc(data_size);
if (!samples)
goto fail;
ret = avcodec_decode_audio2(st->codec, samples,
&data_size, (uint8_t *)data, size);
av_free(samples);
break;
default:
break;
}
fail:
return ret;
}
static int set_codec_from_probe_data(AVStream *st, AVProbeData *pd, int score)
{
AVInputFormat *fmt;
fmt = av_probe_input_format2(pd, 1, &score);
if (fmt) {
if (strncmp(fmt->name, "mp3", 3) == 0)
st->codec->codec_id = CODEC_ID_MP3;
else if (strncmp(fmt->name, "ac3", 3) == 0)
st->codec->codec_id = CODEC_ID_AC3;
}
unsigned int codec_get_tag(const AVCodecTag *tags, int id)
{
while (tags->id != CODEC_ID_NONE) {
if (tags->id == id)
return tags->tag;
tags++;
}
return 0;
}
enum CodecID codec_get_id(const AVCodecTag *tags, unsigned int tag)
{
int i;
for(i=0; tags[i].id != CODEC_ID_NONE;i++) {
if(tag == tags[i].tag)
return tags[i].id;
}
for(i=0; tags[i].id != CODEC_ID_NONE; i++) {
if( toupper((tag >> 0)&0xFF) == toupper((tags[i].tag >> 0)&0xFF)
&& toupper((tag >> 8)&0xFF) == toupper((tags[i].tag >> 8)&0xFF)
&& toupper((tag >>16)&0xFF) == toupper((tags[i].tag >>16)&0xFF)
&& toupper((tag >>24)&0xFF) == toupper((tags[i].tag >>24)&0xFF))
return tags[i].id;
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
}
return CODEC_ID_NONE;
}
unsigned int av_codec_get_tag(const AVCodecTag *tags[4], enum CodecID id)
{
int i;
for(i=0; tags && tags[i]; i++){
int tag= codec_get_tag(tags[i], id);
if(tag) return tag;
}
return 0;
}
enum CodecID av_codec_get_id(const AVCodecTag *tags[4], unsigned int tag)
{
int i;
for(i=0; tags && tags[i]; i++){
enum CodecID id= codec_get_id(tags[i], tag);
if(id!=CODEC_ID_NONE) return id;
}
return CODEC_ID_NONE;
}
/* absolute maximum size we read until we abort */
#define MAX_READ_SIZE 5000000
#define MAX_STD_TIMEBASES (60*12+5)
static int get_std_framerate(int i){
if(i<60*12) return i*1001;
else return ((int[]){24,30,60,12,15})[i-60*12]*1000*12;
Fabrice Bellard
committed
int av_find_stream_info(AVFormatContext *ic)
{
Fabrice Bellard
committed
AVStream *st;
AVPacket pkt1, *pkt;
int64_t last_dts[MAX_STREAMS];
int duration_count[MAX_STREAMS]={0};
double (*duration_error)[MAX_STD_TIMEBASES];
offset_t old_offset = url_ftell(ic->pb);
int64_t codec_info_duration[MAX_STREAMS]={0};
int codec_info_nb_frames[MAX_STREAMS]={0};
AVProbeData probe_data[MAX_STREAMS];
int codec_identified[MAX_STREAMS]={0};
Fabrice Bellard
committed
duration_error = av_mallocz(MAX_STREAMS * sizeof(*duration_error));
if (!duration_error) return AVERROR(ENOMEM);
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
Michael Niedermayer
committed
if(st->codec->codec_type == CODEC_TYPE_VIDEO){
/* if(!st->time_base.num)
st->time_base= */
Michael Niedermayer
committed
if(!st->codec->time_base.num)
st->codec->time_base= st->time_base;
Michael Niedermayer
committed
//only for the split stuff
if (!st->parser) {
Michael Niedermayer
committed
st->parser = av_parser_init(st->codec->codec_id);
if(st->need_parsing == AVSTREAM_PARSE_HEADERS && st->parser){
st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
}
Michael Niedermayer
committed
}
for(i=0;i<MAX_STREAMS;i++){
last_dts[i]= AV_NOPTS_VALUE;
}
Fabrice Bellard
committed
count = 0;
read_size = 0;
for(;;) {
/* check if one codec still needs to be handled */
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
Michael Niedermayer
committed
if (!has_codec_parameters(st->codec))
Fabrice Bellard
committed
break;
/* variable fps and no guess at the real fps */
if( (st->codec->time_base.den >= 101LL*st->codec->time_base.num || st->codec->codec_id == CODEC_ID_MPEG2VIDEO)
Michael Niedermayer
committed
&& duration_count[i]<20 && st->codec->codec_type == CODEC_TYPE_VIDEO)
Michael Niedermayer
committed
if(st->parser && st->parser->parser->split && !st->codec->extradata)
Michael Niedermayer
committed
break;
if(st->first_dts == AV_NOPTS_VALUE)
break;
Fabrice Bellard
committed
}
if (i == ic->nb_streams) {
/* NOTE: if the format has no header, then we need to read
some packets to get most of the streams, so we cannot
stop here */
if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
Fabrice Bellard
committed
/* if we found the info for all the codecs, we can stop */
ret = count;
break;
}
/* we did not get all the codec info, but we read too much data */
if (read_size >= MAX_READ_SIZE) {
ret = count;
break;
}
Fabrice Bellard
committed
/* NOTE: a new stream can be added there if no header in file
(AVFMTCTX_NOHEADER) */
ret = av_read_frame_internal(ic, &pkt1);
if (ret < 0) {
/* EOF or error */
ret = -1; /* we could not have all the codec parameters before EOF */
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (!has_codec_parameters(st->codec)){
char buf[256];
avcodec_string(buf, sizeof(buf), st->codec, 0);
av_log(ic, AV_LOG_INFO, "Could not find codec parameters (%s)\n", buf);
Måns Rullgård
committed
} else {
ret = 0;
}
break;
}
Michael Niedermayer
committed
pkt= add_to_pktbuf(ic, &pkt1);
if(av_dup_packet(pkt) < 0)
return AVERROR(ENOMEM);
Fabrice Bellard
committed
read_size += pkt->size;
Fabrice Bellard
committed
st = ic->streams[pkt->stream_index];
if(codec_info_nb_frames[st->index]>1)
codec_info_duration[st->index] += pkt->duration;
if (pkt->duration != 0)
codec_info_nb_frames[st->index]++;
int index= pkt->stream_index;
int64_t last= last_dts[index];
int64_t duration= pkt->dts - last;
if(pkt->dts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && duration>0){
double dur= duration * av_q2d(st->time_base);
// if(st->codec->codec_type == CODEC_TYPE_VIDEO)
// av_log(NULL, AV_LOG_ERROR, "%f\n", dur);
if(duration_count[index] < 2)
memset(duration_error, 0, MAX_STREAMS * sizeof(*duration_error));
for(i=1; i<MAX_STD_TIMEBASES; i++){
int framerate= get_std_framerate(i);
int ticks= lrintf(dur*framerate/(1001*12));
double error= dur - ticks*1001*12/(double)framerate;
duration_error[index][i] += error*error;
}
duration_count[index]++;
if(last == AV_NOPTS_VALUE || duration_count[index]<=1)
last_dts[pkt->stream_index]= pkt->dts;
if (st->codec->codec_id == CODEC_ID_NONE) {
AVProbeData *pd = &(probe_data[st->index]);
Wolfram Gloger
committed
pd->buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);
memcpy(pd->buf+pd->buf_size, pkt->data, pkt->size);
pd->buf_size += pkt->size;
Wolfram Gloger
committed
memset(pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE);
Michael Niedermayer
committed
if(st->parser && st->parser->parser->split && !st->codec->extradata){
int i= st->parser->parser->split(st->codec, pkt->data, pkt->size);
Michael Niedermayer
committed
if(i){
Michael Niedermayer
committed
st->codec->extradata_size= i;
st->codec->extradata= av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
Michael Niedermayer
committed
memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size);
memset(st->codec->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE);
Michael Niedermayer
committed
}
}
/* if still no information, we try to open the codec and to
decompress the frame. We try to avoid that in most cases as
it takes longer and uses more memory. For MPEG4, we need to
decompress for Quicktime. */
Michael Niedermayer
committed
if (!has_codec_parameters(st->codec) /*&&
(st->codec->codec_id == CODEC_ID_FLV1 ||
st->codec->codec_id == CODEC_ID_H264 ||
st->codec->codec_id == CODEC_ID_H263 ||
st->codec->codec_id == CODEC_ID_H261 ||
st->codec->codec_id == CODEC_ID_VORBIS ||
st->codec->codec_id == CODEC_ID_MJPEG ||
st->codec->codec_id == CODEC_ID_PNG ||
st->codec->codec_id == CODEC_ID_PAM ||
st->codec->codec_id == CODEC_ID_PGM ||
st->codec->codec_id == CODEC_ID_PGMYUV ||
st->codec->codec_id == CODEC_ID_PBM ||
st->codec->codec_id == CODEC_ID_PPM ||
st->codec->codec_id == CODEC_ID_SHORTEN ||
(st->codec->codec_id == CODEC_ID_MPEG4 && !st->need_parsing))*/)
try_decode_frame(st, pkt->data, pkt->size);
if (st->time_base.den > 0 && av_rescale_q(codec_info_duration[st->index], st->time_base, AV_TIME_BASE_Q) >= ic->max_analyze_duration) {
Fabrice Bellard
committed
}
count++;
}
Michael Niedermayer
committed
// close codecs which where opened in try_decode_frame()
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
Michael Niedermayer
committed
if(st->codec->codec)
avcodec_close(st->codec);
Michael Niedermayer
committed
}
Fabrice Bellard
committed
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
if(st->codec->codec_id == CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_sample)
st->codec->codec_tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
if(duration_count[i]
&& (st->codec->time_base.num*101LL <= st->codec->time_base.den || st->codec->codec_id == CODEC_ID_MPEG2VIDEO) /*&&
//FIXME we should not special case mpeg2, but this needs testing with non mpeg2 ...
st->time_base.num*duration_sum[i]/duration_count[i]*101LL > st->time_base.den*/){
double best_error= 2*av_q2d(st->time_base);
best_error= best_error*best_error*duration_count[i]*1000*12*30;
for(j=1; j<MAX_STD_TIMEBASES; j++){
double error= duration_error[i][j] * get_std_framerate(j);
// if(st->codec->codec_type == CODEC_TYPE_VIDEO)
// av_log(NULL, AV_LOG_ERROR, "%f %f\n", get_std_framerate(j) / 12.0/1001, error);
if(error < best_error){
best_error= error;
av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, get_std_framerate(j), 12*1001, INT_MAX);
if( st->codec->time_base.den * (int64_t)st->time_base.num
<= st->codec->time_base.num * (int64_t)st->time_base.den){
st->r_frame_rate.num = st->codec->time_base.den;
st->r_frame_rate.den = st->codec->time_base.num;
}else{
st->r_frame_rate.num = st->time_base.den;
st->r_frame_rate.den = st->time_base.num;
}
Michael Niedermayer
committed
}
}else if(st->codec->codec_type == CODEC_TYPE_AUDIO) {
if (st->codec->codec_id == CODEC_ID_NONE && probe_data[st->index].buf_size > 0) {
codec_identified[st->index] = set_codec_from_probe_data(st, &(probe_data[st->index]), 1);
if(!st->codec->bits_per_sample)
st->codec->bits_per_sample= av_get_bits_per_sample(st->codec->codec_id);
Fabrice Bellard
committed
}
Fabrice Bellard
committed
av_estimate_timings(ic, old_offset);
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (codec_identified[st->index])
break;
}
//FIXME this is a mess
if(i!=ic->nb_streams){
av_read_frame_flush(ic);
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (codec_identified[st->index]) {
av_seek_frame(ic, st->index, 0.0, 0);
}
st->cur_dts= st->first_dts;
url_fseek(ic->pb, ic->data_offset, SEEK_SET);
#if 0
/* correct DTS for b frame streams with no timestamps */
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];