Newer
Older
/* duplicate the packet */
if (av_dup_packet(pkt) < 0)
return AVERROR_NOMEM;
while(*plast_pktl) plast_pktl= &(*plast_pktl)->next; //FIXME maybe maintain pointer to the last?
pktl = av_mallocz(sizeof(AVPacketList));
if (!pktl)
return AVERROR_NOMEM;
/* add the packet in the buffered packet list */
*plast_pktl = pktl;
pktl->pkt= *pkt;
}else{
assert(!s->packet_buffer);
return av_read_frame_internal(s, pkt);
}
}
}
/* XXX: suppress the packet queue */
static void flush_packet_queue(AVFormatContext *s)
{
AVPacketList *pktl;
for(;;) {
pktl = s->packet_buffer;
if (!pktl)
break;
s->packet_buffer = pktl->next;
av_free_packet(&pktl->pkt);
av_free(pktl);
Fabrice Bellard
committed
}
}
/*******************************************************/
/* seek support */
int av_find_default_stream_index(AVFormatContext *s)
{
int i;
AVStream *st;
if (s->nb_streams <= 0)
return -1;
for(i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
Michael Niedermayer
committed
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
return i;
}
}
return 0;
}
Daniel Kristjansson
committed
/**
* Flush the frame reader.
*/
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
static void av_read_frame_flush(AVFormatContext *s)
{
AVStream *st;
int i;
flush_packet_queue(s);
/* free previous packet */
if (s->cur_st) {
if (s->cur_st->parser)
av_free_packet(&s->cur_pkt);
s->cur_st = NULL;
}
/* fail safe */
s->cur_ptr = NULL;
s->cur_len = 0;
/* 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 = 0; /* we set the current DTS to an unspecified origin */
}
}
Daniel Kristjansson
committed
* Updates cur_dts of all streams based on given timestamp and AVStream.
*
* Stream ref_st unchanged, others set cur_dts in their native timebase
Nathan Kurz
committed
* only needed for timestamp wrapping or if (dts not set and pts!=dts)
* @param timestamp new dts expressed in time_base of param ref_st
* @param ref_st reference stream giving time_base of param timestamp
Nathan Kurz
committed
static void av_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];
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);
/**
Daniel Kristjansson
committed
* Add a index entry into a sorted list updateing if it is already there.
*
* @param timestamp timestamp in the timebase of the given stream
*/
Michael Niedermayer
committed
int av_add_index_entry(AVStream *st,
int64_t pos, int64_t timestamp, 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) //dont reduce the distance
distance= ie->min_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;
Daniel Kristjansson
committed
/**
* build an index for raw streams using a parser.
*/
static void av_build_index_raw(AVFormatContext *s)
{
AVPacket pkt1, *pkt = &pkt1;
int ret;
AVStream *st;
st = s->streams[0];
av_read_frame_flush(s);
url_fseek(&s->pb, s->data_offset, SEEK_SET);
for(;;) {
ret = av_read_frame(s, pkt);
if (ret < 0)
break;
if (pkt->stream_index == 0 && st->parser &&
(pkt->flags & PKT_FLAG_KEY)) {
av_add_index_entry(st, st->parser->frame_offset, pkt->dts,
Michael Niedermayer
committed
0, AVINDEX_KEYFRAME);
}
av_free_packet(pkt);
}
}
Daniel Kristjansson
committed
/**
* Returns TRUE if we deal with a raw stream.
*
* Raw codec data and parsing needed.
*/
static int is_raw_stream(AVFormatContext *s)
{
AVStream *st;
if (s->nb_streams != 1)
return 0;
st = s->streams[0];
if (!st->need_parsing)
return 0;
return 1;
}
Daniel Kristjansson
committed
* Gets the index for a specific timestamp.
* @param flags if AVSEEK_FLAG_BACKWARD then the returned index will correspond to
* the timestamp which is <= the requested one, if backward is 0
* then it will be >=
* if AVSEEK_FLAG_ANY seek to any frame, only keyframes otherwise
* @return < 0 if no such timestamp could be found
*/
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;
}
}
if(m == nb_entries)
return -1;
return m;
Michael Niedermayer
committed
#define DEBUG_SEEK
/**
* Does a binary search using av_index_search_timestamp() and AVCodec.read_timestamp().
* this isnt supposed to be called directly by a user application, but by demuxers
* @param target_ts target timestamp in the time base of the given stream
* @param stream_index stream number
*/
int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags){
Michael Niedermayer
committed
AVInputFormat *avif= s->iformat;
int64_t pos_min, pos_max, pos, pos_limit;
int64_t ts_min, ts_max, ts;
Kenneth Aafløy
committed
int64_t start_pos, filesize;
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 %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, 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%llx dts_min=%lld\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%llx pos_limit=0x%llx dts_max=%lld\n",
Michael Niedermayer
committed
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;
Kenneth Aafløy
committed
filesize = url_fsize(&s->pb);
pos_max = filesize - 1;
Michael Niedermayer
committed
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;
Kenneth Aafløy
committed
if(tmp_pos >= filesize)
break;
Michael Niedermayer
committed
}
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 = av_rescale(target_ts - ts_min, pos_max - pos_min, ts_max - ts_min)
+ pos_min - approximate_keyframe_distance;
Michael Niedermayer
committed
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
}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);
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;
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);
Michael Niedermayer
committed
return 0;
}
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;
Aurelien Jacobs
committed
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);
#if 0
#endif
return 0;
}
static int av_seek_frame_generic(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
AVStream *st;
AVIndexEntry *ie;
if (!s->index_built) {
if (is_raw_stream(s)) {
av_build_index_raw(s);
} else {
return -1;
}
s->index_built = 1;
}
st = s->streams[stream_index];
index = av_index_search_timestamp(st, timestamp, flags);
if (index < 0)
return -1;
/* now we have found the index, we can seek */
ie = &st->index_entries[index];
av_read_frame_flush(s);
url_fseek(&s->pb, ie->pos, SEEK_SET);
return 0;
}
/**
* Seek to the key frame at timestamp.
* 'timestamp' in 'stream_index'.
* @param stream_index If stream_index is (-1), a default
* stream is selected, and timestamp is automatically converted
* from AV_TIME_BASE units to the stream specific time_base.
* @param timestamp timestamp in AVStream.time_base units
* or if there is no stream specified then in AV_TIME_BASE units
* @param flags flags which select direction and seeking mode
* @return >= 0 on success
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int 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;
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);
}
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 timings in any stream.
*
* @return TRUE if the stream has accurate timings for at least one component.
*/
Fabrice Bellard
committed
static int av_has_timings(AVFormatContext *ic)
{
int i;
AVStream *st;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time != AV_NOPTS_VALUE &&
st->duration != AV_NOPTS_VALUE)
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;
Fabrice Bellard
committed
int i;
AVStream *st;
start_time = MAXINT64;
end_time = MININT64;
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 (start_time != MAXINT64) {
ic->start_time = start_time;
if (end_time != MININT64) {
Fabrice Bellard
committed
ic->duration = end_time - start_time;
if (ic->file_size > 0) {
/* compute the bit rate */
ic->bit_rate = (double)ic->file_size * 8.0 * AV_TIME_BASE /
(double)ic->duration;
}
}
}
}
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 &&
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);
Fabrice Bellard
committed
if (st->start_time == AV_NOPTS_VALUE ||
st->duration == AV_NOPTS_VALUE) {
st->start_time = 0;
st->duration = duration;
}
}
}
}
}
#define DURATION_MAX_READ_SIZE 250000
/* only usable for MPEG-PS streams */
static void av_estimate_timings_from_pts(AVFormatContext *ic)
{
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
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
/* 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);
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
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
/* 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);
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->duration == 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) {
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, 0, SEEK_SET);
}
static void av_estimate_timings(AVFormatContext *ic)
{
int64_t file_size;
/* get the file size, if possible */
if (ic->iformat->flags & AVFMT_NOFILE) {
file_size = 0;
} else {
Aurelien Jacobs
committed
file_size = url_fsize(&ic->pb);
Fabrice Bellard
committed
if (file_size < 0)
file_size = 0;
}
ic->file_size = file_size;
if ((ic->iformat == &mpegps_demux || ic->iformat == &mpegts_demux) && file_size && !ic->pb.is_streamed) {
Fabrice Bellard
committed
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
/* get accurate estimate from the PTSes */
av_estimate_timings_from_pts(ic);
} else if (av_has_timings(ic)) {
/* 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,
(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,
(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 (val != 0);
}
static int try_decode_frame(AVStream *st, const uint8_t *data, int size)
{
int16_t *samples;
AVCodec *codec;
Michael Niedermayer
committed
int got_picture, ret=0;
AVFrame picture;
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:
Michael Niedermayer
committed
ret = avcodec_decode_video(st->codec, &picture,
&got_picture, (uint8_t *)data, size);
break;
case CODEC_TYPE_AUDIO:
samples = av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
if (!samples)
goto fail;
Michael Niedermayer
committed
ret = avcodec_decode_audio(st->codec, samples,
&got_picture, (uint8_t *)data, size);
av_free(samples);
break;
default:
break;
}
fail:
return ret;
}
/* absolute maximum size we read until we abort */
#define MAX_READ_SIZE 5000000
/* maximum duration until we stop analysing the stream */
Daniel Kristjansson
committed
#define MAX_STREAM_DURATION ((int)(AV_TIME_BASE * 2.0))
Fabrice Bellard
committed
/**
* Read the beginning of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also compute the real frame rate in case of mpeg2 repeat
* frame mode.
*
* @param ic media file handle
* @return >=0 if OK. AVERROR_xxx if error.
* @todo let user decide somehow what information is needed so we dont waste time geting stuff the user doesnt need
Fabrice Bellard
committed
*/
int av_find_stream_info(AVFormatContext *ic)
{
int i, count, ret, read_size;
Fabrice Bellard
committed
AVStream *st;
AVPacket pkt1, *pkt;
Fabrice Bellard
committed
AVPacketList *pktl=NULL, **ppktl;
int64_t last_dts[MAX_STREAMS];
int64_t duration_sum[MAX_STREAMS];
int duration_count[MAX_STREAMS]={0};
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->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);
Michael Niedermayer
committed
}
for(i=0;i<MAX_STREAMS;i++){
last_dts[i]= AV_NOPTS_VALUE;
Fabrice Bellard
committed
count = 0;
read_size = 0;
ppktl = &ic->packet_buffer;
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 */
Michael Niedermayer
committed
if( st->codec->time_base.den >= 1000LL*st->codec->time_base.num
&& 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;
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;
}
} else {
/* we did not get all the codec info, but we read too much data */
if (read_size >= MAX_READ_SIZE) {
Fabrice Bellard
committed
ret = count;
break;
}
}
/* 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];
Michael Niedermayer
committed
if (!has_codec_parameters(st->codec))
break;
}
ret = 0;
break;
}
Fabrice Bellard
committed
pktl = av_mallocz(sizeof(AVPacketList));
if (!pktl) {
ret = AVERROR_NOMEM;
break;
}
/* add the packet in the buffered packet list */
*ppktl = pktl;
ppktl = &pktl->next;
pkt = &pktl->pkt;
*pkt = pkt1;
/* duplicate the packet */
if (av_dup_packet(pkt) < 0) {
ret = AVERROR_NOMEM;
break;
Fabrice Bellard
committed
}
read_size += pkt->size;
Fabrice Bellard
committed
st = ic->streams[pkt->stream_index];
st->codec_info_duration += pkt->duration;
if (pkt->duration != 0)
st->codec_info_nb_frames++;
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){
if(duration*duration_count[index]*10/9 < duration_sum[index]){
duration_sum[index]= duration;
duration_count[index]=1;
}else{
int factor= av_rescale(duration, duration_count[index], duration_sum[index]);
duration_sum[index] += duration;
duration_count[index]+= factor;
}
Michael Niedermayer
committed
if(st->codec_info_nb_frames == 0 && 0)
st->codec_info_duration += duration;
}
last_dts[pkt->stream_index]= pkt->dts;
}
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);
memcpy(st->codec->extradata, pkt->data, st->codec->extradata_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 (av_rescale_q(st->codec_info_duration, st->time_base, AV_TIME_BASE_Q) >= MAX_STREAM_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);
Michael Niedermayer
committed
if(duration_count[i] && st->codec->time_base.num*1000LL <= st->codec->time_base.den &&
st->time_base.num*duration_sum[i]/duration_count[i]*1000LL > st->time_base.den){