aboutsummaryrefslogtreecommitdiffstats
path: root/libavformat/utils.c
diff options
context:
space:
mode:
authorFabrice Bellard <fabrice@bellard.org>2003-08-08 18:02:23 +0000
committerFabrice Bellard <fabrice@bellard.org>2003-08-08 18:02:23 +0000
commit12f996edfab67b65af0ff1ee829f9eeabb025b0f (patch)
treed7f6a3dd8960d2bdcb55211dd6823ce32235fcff /libavformat/utils.c
parent5b685a7a76d184af8d4b9d213c9ba4a3c0b52542 (diff)
downloadffmpeg-12f996edfab67b65af0ff1ee829f9eeabb025b0f.tar.gz
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
Diffstat (limited to 'libavformat/utils.c')
-rw-r--r--libavformat/utils.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 1fa7fc34af..b9247cf803 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -321,6 +321,8 @@ int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
err = AVERROR_NOMEM;
goto fail;
}
+ ic->duration = AV_NOPTS_VALUE;
+ ic->start_time = AV_NOPTS_VALUE;
pstrcpy(ic->filename, sizeof(ic->filename), filename);
pd->filename = ic->filename;
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 */
#define CSTATE_NOTFOUND 0
#define CSTATE_DECODING 1
@@ -662,6 +953,8 @@ int av_find_stream_info(AVFormatContext *ic)
}
}
+
+ av_estimate_timings(ic);
return ret;
}
@@ -725,6 +1018,8 @@ AVStream *av_new_stream(AVFormatContext *s, int id)
st->index = s->nb_streams;
st->id = id;
+ st->start_time = AV_NOPTS_VALUE;
+ st->duration = AV_NOPTS_VALUE;
s->streams[s->nb_streams++] = st;
return st;
}
@@ -874,6 +1169,29 @@ void dump_format(AVFormatContext *ic,
index,
is_output ? ic->oformat->name : ic->iformat->name,
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++) {
AVStream *st = ic->streams[i];
avcodec_string(buf, sizeof(buf), &st->codec, is_output);