[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