[FFmpeg-devel] [PATCH] MPEG-TS/-PS : better probing for duration and start-time for all streams in a container
Gaullier Nicolas
nicolas.gaullier at arkena.com
Tue May 13 10:00:21 CEST 2014
[Sorry, I think you prefer patch in plain text in the body rather than attached in (I suppose) binary form. Here below]
Hello,
In the MPEG2TS/PS containers, FFMPEG/FFPROBE does not report accurate duration and start time:
1- the duration does not take into account the duration of the last frame
2- the start time is taken from the first frame, but the lowest PTS is often found a bit later (commonly found on broadcast open GOP structures with the first frame not being a key frame)
3- depending on the file structure, one or several audio/video streams may have no information at all (the current implementation is focused on getting one single stream info; more stream info is 'welcome' but not required), The typical use case is where the video/audio delay is very high and the bitrate high too : the end of the file contains only audio frames and the current implementation does not try to 'look further' to get the video ptses.
Please review my patch - and be clement, I am not a developer and I only have a basic windows/mingw environment for testing with no FATE etc.
Nicolas Gaullier
diff --git a/d:/dev/utils-svn.c b/libavformat/utils.c
index 25736f9..65ab418 100644
--- a/d:/dev/utils-svn.c
+++ b/libavformat/utils.c
@@ -1013,6 +1013,15 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index,
int64_t shift;
int i, delay;
+ if (pts != AV_NOPTS_VALUE) {
+ if (st->start_time == AV_NOPTS_VALUE) {
+ st->start_time = pts;
+ }
+ else {
+ st->start_time = FFMIN(pts,st->start_time);
+ }
+ }
+
if (st->first_dts != AV_NOPTS_VALUE ||
dts == AV_NOPTS_VALUE ||
st->cur_dts == AV_NOPTS_VALUE ||
@@ -1050,9 +1059,6 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index,
pktl->pkt.dts = select_from_pts_buffer(st, pts_buffer, pktl->pkt.dts);
}
}
-
- if (st->start_time == AV_NOPTS_VALUE)
- st->start_time = pts;
}
static void update_initial_durations(AVFormatContext *s, AVStream *st,
@@ -2438,7 +2444,7 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
AVPacket pkt1, *pkt = &pkt1;
AVStream *st;
int read_size, i, ret;
- int64_t end_time;
+ int all_duration_valid;
int64_t filesize, offset, duration;
int retry = 0;
@@ -2457,12 +2463,12 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
av_parser_close(st->parser);
st->parser = NULL;
}
+ st->info->last_duration = AV_NOPTS_VALUE;
}
- /* estimate the end time (duration) */
+ /* estimate the end time (duration) for all streams */
/* XXX: may need to support wrapping */
filesize = ic->pb ? avio_size(ic->pb) : 0;
- end_time = AV_NOPTS_VALUE;
do {
offset = filesize - (DURATION_MAX_READ_SIZE << retry);
if (offset < 0)
@@ -2471,11 +2477,11 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
avio_seek(ic->pb, offset, SEEK_SET);
read_size = 0;
for (;;) {
- if (read_size >= DURATION_MAX_READ_SIZE << (FFMAX(retry - 1, 0)))
+ if (read_size >= DURATION_MAX_READ_SIZE << retry)
break;
do {
- ret = ff_read_packet(ic, pkt);
+ ret = read_frame_internal(ic,pkt);
} while (ret == AVERROR(EAGAIN));
if (ret != 0)
break;
@@ -2484,7 +2490,9 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
if (pkt->pts != AV_NOPTS_VALUE &&
(st->start_time != AV_NOPTS_VALUE ||
st->first_dts != AV_NOPTS_VALUE)) {
- duration = end_time = pkt->pts;
+
+ duration = pkt->pts + pkt->duration;
+
if (st->start_time != AV_NOPTS_VALUE)
duration -= st->start_time;
else
@@ -2498,10 +2506,35 @@ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
}
av_free_packet(pkt);
}
- } while (end_time == AV_NOPTS_VALUE &&
- filesize > (DURATION_MAX_READ_SIZE << retry) &&
+
+ /* check if all audio/video streams have valid duration */
+ all_duration_valid = 1;
+ for (i = 0; i < ic->nb_streams; i++) {
+ st = ic->streams[i];
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_AUDIO:
+ if (st->info->last_duration == AV_NOPTS_VALUE)
+ all_duration_valid = 0;
+ }
+ }
+ } while (all_duration_valid == 0 &&
+ filesize > (DURATION_MAX_READ_SIZE << (retry+1)) &&
++retry <= DURATION_MAX_RETRY);
+ /* warn about audio/video streams which duration could not be estimated */
+ for (i = 0; i < ic->nb_streams; i++) {
+ st = ic->streams[i];
+ if (st->duration == AV_NOPTS_VALUE)
+ {
+ switch (st->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_AUDIO:
+ av_log(NULL, AV_LOG_WARNING, "stream %d : no PTS found at end of file, duration not set\n", i);
+ }
+ }
+ }
+
fill_all_stream_timings(ic);
avio_seek(ic->pb, old_offset, SEEK_SET);
@@ -3145,6 +3178,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
/* NOTE: A new stream can be added there if no header in file
* (AVFMTCTX_NOHEADER). */
ret = read_frame_internal(ic, &pkt1);
+
if (ret == AVERROR(EAGAIN))
continue;
More information about the ffmpeg-devel
mailing list