initial duration/start_time generic support - displays stream duration and average total bitrate when using an input file
Originally committed as revision 2115 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
5b685a7a76
commit
12f996edfa
@ -321,6 +321,8 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
|
|||||||
err = AVERROR_NOMEM;
|
err = AVERROR_NOMEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
ic->duration = AV_NOPTS_VALUE;
|
||||||
|
ic->start_time = AV_NOPTS_VALUE;
|
||||||
pstrcpy(ic->filename, sizeof(ic->filename), filename);
|
pstrcpy(ic->filename, sizeof(ic->filename), filename);
|
||||||
pd->filename = ic->filename;
|
pd->filename = ic->filename;
|
||||||
pd->buf = buf;
|
pd->buf = buf;
|
||||||
@ -439,6 +441,295 @@ int av_read_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_packet_queue(AVFormatContext *s)
|
||||||
|
{
|
||||||
|
AVPacketList *pktl;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
pktl = s->packet_buffer;
|
||||||
|
if (!pktl)
|
||||||
|
break;
|
||||||
|
s->packet_buffer = pktl->next;
|
||||||
|
av_free(pktl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
/* 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 = (int64_t)((double)pkt->pts * ic->pts_num * (double)AV_TIME_BASE / ic->pts_den);
|
||||||
|
}
|
||||||
|
av_free_packet(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
printf("start=%lld\n", 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;
|
||||||
|
|
||||||
|
/* flush packet queue */
|
||||||
|
flush_packet_queue(ic);
|
||||||
|
|
||||||
|
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 = (int64_t)((double)pkt->pts * ic->pts_num * (double)AV_TIME_BASE / ic->pts_den);
|
||||||
|
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) {
|
||||||
|
/* 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
|
||||||
|
}
|
||||||
|
|
||||||
/* state for codec information */
|
/* state for codec information */
|
||||||
#define CSTATE_NOTFOUND 0
|
#define CSTATE_NOTFOUND 0
|
||||||
#define CSTATE_DECODING 1
|
#define CSTATE_DECODING 1
|
||||||
@ -662,6 +953,8 @@ int av_find_stream_info(AVFormatContext *ic)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
av_estimate_timings(ic);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,6 +1018,8 @@ AVStream *av_new_stream(AVFormatContext *s, int id)
|
|||||||
|
|
||||||
st->index = s->nb_streams;
|
st->index = s->nb_streams;
|
||||||
st->id = id;
|
st->id = id;
|
||||||
|
st->start_time = AV_NOPTS_VALUE;
|
||||||
|
st->duration = AV_NOPTS_VALUE;
|
||||||
s->streams[s->nb_streams++] = st;
|
s->streams[s->nb_streams++] = st;
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
@ -874,6 +1169,29 @@ void dump_format(AVFormatContext *ic,
|
|||||||
index,
|
index,
|
||||||
is_output ? ic->oformat->name : ic->iformat->name,
|
is_output ? ic->oformat->name : ic->iformat->name,
|
||||||
is_output ? "to" : "from", url);
|
is_output ? "to" : "from", url);
|
||||||
|
if (!is_output) {
|
||||||
|
printf(" Duration: ");
|
||||||
|
if (ic->duration != AV_NOPTS_VALUE) {
|
||||||
|
int hours, mins, secs, us;
|
||||||
|
secs = ic->duration / AV_TIME_BASE;
|
||||||
|
us = ic->duration % AV_TIME_BASE;
|
||||||
|
mins = secs / 60;
|
||||||
|
secs %= 60;
|
||||||
|
hours = mins / 60;
|
||||||
|
mins %= 60;
|
||||||
|
printf("%02d:%02d:%02d.%01d", hours, mins, secs,
|
||||||
|
(10 * us) / AV_TIME_BASE);
|
||||||
|
} else {
|
||||||
|
printf("N/A");
|
||||||
|
}
|
||||||
|
printf(", bitrate: ");
|
||||||
|
if (ic->bit_rate) {
|
||||||
|
printf("%d kb/s", ic->bit_rate / 1000);
|
||||||
|
} else {
|
||||||
|
printf("N/A");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
for(i=0;i<ic->nb_streams;i++) {
|
for(i=0;i<ic->nb_streams;i++) {
|
||||||
AVStream *st = ic->streams[i];
|
AVStream *st = ic->streams[i];
|
||||||
avcodec_string(buf, sizeof(buf), &st->codec, is_output);
|
avcodec_string(buf, sizeof(buf), &st->codec, is_output);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user