Newer
Older
Nathan Kurz
committed
st->time_base.num * (int64_t)ref_st->time_base.den);
/**
* 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;
}
/* 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)) {
int64_t dts= av_rescale(pkt->dts, st->time_base.den, AV_TIME_BASE*(int64_t)st->time_base.num);
av_add_index_entry(st, st->parser->frame_offset, dts,
Michael Niedermayer
committed
0, AVINDEX_KEYFRAME);
}
av_free_packet(pkt);
}
}
/* return 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;
}
/**
* 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;
int64_t start_pos;
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
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
pos_max,pos_limit, ts_max);
#endif
}
}
if(ts_min == AV_NOPTS_VALUE){
pos_min = s->data_offset;
ts_min = avif->read_timestamp(s, stream_index, &pos_min, INT64_MAX);
if (ts_min == AV_NOPTS_VALUE)
return -1;
}
if(ts_max == AV_NOPTS_VALUE){
int step= 1024;
pos_max = url_filesize(url_fileno(&s->pb)) - 1;
do{
pos_max -= step;
ts_max = avif->read_timestamp(s, stream_index, &pos_max, pos_max + step);
step += step;
}while(ts_max == AV_NOPTS_VALUE && pos_max >= step);
if (ts_max == AV_NOPTS_VALUE)
return -1;
for(;;){
int64_t tmp_pos= pos_max + 1;
int64_t tmp_ts= avif->read_timestamp(s, stream_index, &tmp_pos, INT64_MAX);
if(tmp_ts == AV_NOPTS_VALUE)
break;
ts_max= tmp_ts;
pos_max= tmp_pos;
}
pos_limit= pos_max;
}
no_change=0;
while (pos_min < pos_limit) {
#ifdef DEBUG_SEEK
av_log(s, AV_LOG_DEBUG, "pos_min=0x%llx pos_max=0x%llx dts_min=%lld dts_max=%lld\n",
pos_min, pos_max,
ts_min, ts_max);
#endif
assert(pos_limit <= pos_max);
if(no_change==0){
int64_t approximate_keyframe_distance= pos_max - pos_limit;
// interpolate position (better than dichotomy)
pos = av_rescale(target_ts - ts_min, pos_max - pos_min, ts_max - ts_min)
+ pos_min - approximate_keyframe_distance;
Michael Niedermayer
committed
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
}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;
pos_max = url_filesize(url_fileno(&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
* @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
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
/* return TRUE if the stream has accurate timings for at least one component */
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;
}
/* estimate the stream timings from the one of each components. Also
compute the global bitrate if possible */
static void av_update_stream_timings(AVFormatContext *ic)
{
int64_t start_time, end_time, end_time1;
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) {
if (st->start_time < start_time)
start_time = st->start_time;
if (st->duration != AV_NOPTS_VALUE) {
end_time1 = st->start_time + st->duration;
if (end_time1 > end_time)
end_time = end_time1;
}
}
}
if (start_time != MAXINT64) {
ic->start_time = start_time;
if (end_time != MAXINT64) {
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) {
st->start_time = ic->start_time;
st->duration = ic->duration;
}
}
}
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];
bit_rate += st->codec.bit_rate;
}
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) {
duration = (int64_t)((8 * AV_TIME_BASE * (double)filesize) / (double)ic->bit_rate);
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
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;
int64_t start_time, end_time, end_time1;
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
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
/* 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)
st->start_time = av_rescale(pkt->pts, st->time_base.num * (int64_t)AV_TIME_BASE, st->time_base.den);
Michael Niedermayer
committed
}
av_free_packet(pkt);
}
Fabrice Bellard
committed
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
/* we compute the minimum start_time and use it as default */
start_time = MAXINT64;
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time != AV_NOPTS_VALUE &&
st->start_time < start_time)
start_time = st->start_time;
}
if (start_time != MAXINT64)
ic->start_time = start_time;
/* 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) {
end_time = av_rescale(pkt->pts, st->time_base.num * (int64_t)AV_TIME_BASE, st->time_base.den);
Fabrice Bellard
committed
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
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);
}
/* estimate total duration */
end_time = MININT64;
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->duration != AV_NOPTS_VALUE) {
end_time1 = st->start_time + st->duration;
if (end_time1 > end_time)
end_time = end_time1;
}
}
/* update start_time (new stream may have been created, so we do
it at the end */
if (ic->start_time != AV_NOPTS_VALUE) {
for(i = 0; i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->start_time == AV_NOPTS_VALUE)
st->start_time = ic->start_time;
}
}
if (end_time != MININT64) {
/* put dummy values for duration if needed */
for(i = 0;i < ic->nb_streams; i++) {
st = ic->streams[i];
if (st->duration == AV_NOPTS_VALUE &&
st->start_time != AV_NOPTS_VALUE)
st->duration = end_time - st->start_time;
}
ic->duration = end_time - ic->start_time;
}
url_fseek(&ic->pb, 0, SEEK_SET);
}
static void av_estimate_timings(AVFormatContext *ic)
{
URLContext *h;
int64_t file_size;
/* get the file size, if possible */
if (ic->iformat->flags & AVFMT_NOFILE) {
file_size = 0;
} else {
h = url_fileno(&ic->pb);
file_size = url_filesize(h);
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
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
/* 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;
break;
default:
val = 1;
break;
}
return (val != 0);
}
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
static int try_decode_frame(AVStream *st, const uint8_t *data, int size)
{
int16_t *samples;
AVCodec *codec;
int got_picture, ret;
AVFrame picture;
codec = avcodec_find_decoder(st->codec.codec_id);
if (!codec)
return -1;
ret = avcodec_open(&st->codec, codec);
if (ret < 0)
return ret;
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:
samples = av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
if (!samples)
goto fail;
ret = avcodec_decode_audio(&st->codec, samples,
&got_picture, (uint8_t *)data, size);
av_free(samples);
break;
default:
break;
}
fail:
avcodec_close(&st->codec);
return ret;
}
/* absolute maximum size we read until we abort */
#define MAX_READ_SIZE 5000000
/* maximum duration until we stop analysing the stream */
#define MAX_STREAM_DURATION ((int)(AV_TIME_BASE * 1.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.
*/
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 best_duration[MAX_STREAMS];
Fabrice Bellard
committed
for(i=0;i<MAX_STREAMS;i++){
last_dts[i]= AV_NOPTS_VALUE;
best_duration[i]= INT64_MAX;
}
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];
if (!has_codec_parameters(&st->codec))
Fabrice Bellard
committed
break;
/* variable fps and no guess at the real fps */
if( st->codec.frame_rate >= 1000LL*st->codec.frame_rate_base
&& best_duration[i]== INT64_MAX)
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];
if (!has_codec_parameters(&st->codec))
break;
}
if ((ic->ctx_flags & AVFMTCTX_NOHEADER) &&
i == ic->nb_streams)
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++;
if(st->codec.codec_type == CODEC_TYPE_VIDEO){
int64_t last= last_dts[pkt->stream_index];
if(pkt->dts != AV_NOPTS_VALUE && last != AV_NOPTS_VALUE && last < pkt->dts &&
best_duration[pkt->stream_index] > pkt->dts - last){
best_duration[pkt->stream_index] = pkt->dts - last;
}
last_dts[pkt->stream_index]= pkt->dts;
}
/* 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. */
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_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->codec_info_duration >= MAX_STREAM_DURATION) {
break;
Fabrice Bellard
committed
}
count++;
}
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
Michael Niedermayer
committed
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(best_duration[i] < INT64_MAX && st->codec.frame_rate_base*1000 <= st->codec.frame_rate){
st->r_frame_rate= st->codec.frame_rate;
st->r_frame_rate_base= av_rescale(best_duration[i], st->codec.frame_rate, AV_TIME_BASE);
av_reduce(&st->r_frame_rate, &st->r_frame_rate_base, st->r_frame_rate, st->r_frame_rate_base, 1<<15);
int_fps= av_rescale(st->r_frame_rate, 1, st->r_frame_rate_base);
if(int_fps>0 && av_rescale(st->r_frame_rate, 1, int_fps) == st->r_frame_rate_base){
st->r_frame_rate= int_fps;
st->r_frame_rate_base= 1;
}
/* compute the real frame rate for telecine */
if ((st->codec.codec_id == CODEC_ID_MPEG1VIDEO ||
st->codec.codec_id == CODEC_ID_MPEG2VIDEO) &&
st->codec.sub_id == 2) {
if (st->codec_info_nb_frames >= 20) {
float coded_frame_rate, est_frame_rate;
est_frame_rate = ((double)st->codec_info_nb_frames * AV_TIME_BASE) /
(double)st->codec_info_duration ;
coded_frame_rate = (double)st->codec.frame_rate /
(double)st->codec.frame_rate_base;
#if 0
printf("telecine: coded_frame_rate=%0.3f est_frame_rate=%0.3f\n",
coded_frame_rate, est_frame_rate);
#endif
/* if we detect that it could be a telecine, we
signal it. It would be better to do it at a
higher level as it can change in a film */
if (coded_frame_rate >= 24.97 &&
(est_frame_rate >= 23.5 && est_frame_rate < 24.5)) {
st->r_frame_rate_base = 1001;
}
}
}
/* if no real frame rate, use the codec one */
Michael Niedermayer
committed
if (!st->r_frame_rate){
st->r_frame_rate = st->codec.frame_rate;
st->r_frame_rate_base = st->codec.frame_rate_base;
}
Fabrice Bellard
committed
}
Fabrice Bellard
committed
Fabrice Bellard
committed
av_estimate_timings(ic);
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
#if 0
/* correct DTS for b frame streams with no timestamps */
for(i=0;i<ic->nb_streams;i++) {
st = ic->streams[i];
if (st->codec.codec_type == CODEC_TYPE_VIDEO) {
if(b-frames){
ppktl = &ic->packet_buffer;
while(ppkt1){
if(ppkt1->stream_index != i)
continue;
if(ppkt1->pkt->dts < 0)
break;
if(ppkt1->pkt->pts != AV_NOPTS_VALUE)
break;
ppkt1->pkt->dts -= delta;
ppkt1= ppkt1->next;
}
if(ppkt1)
continue;
st->cur_dts -= delta;
}
}
}
#endif
Fabrice Bellard
committed
return ret;
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
/*******************************************************/
/**
* start playing a network based stream (e.g. RTSP stream) at the
* current position
*/
int av_read_play(AVFormatContext *s)
{
if (!s->iformat->read_play)
return AVERROR_NOTSUPP;
return s->iformat->read_play(s);
}
/**
* pause a network based stream (e.g. RTSP stream). Use av_read_play()
* to resume it.
*/
int av_read_pause(AVFormatContext *s)
{
if (!s->iformat->read_pause)
return AVERROR_NOTSUPP;
return s->iformat->read_pause(s);
}
Fabrice Bellard
committed
/**
* Close a media file (but not its codecs)
*
* @param s media file handle
*/
AVStream *st;
/* free previous packet */
if (s->cur_st && s->cur_st->parser)
av_free_packet(&s->cur_pkt);
Fabrice Bellard
committed
if (s->iformat->read_close)
s->iformat->read_close(s);
/* free all data in a stream component */
st = s->streams[i];
if (st->parser) {
av_parser_close(st->parser);
av_free(st->index_entries);
av_free(st);
flush_packet_queue(s);
if (s->iformat->flags & AVFMT_NOFILE) {