diff mbox

[FFmpeg-devel,3/3] lavf/utils: add flag to fill unset timestamps from wallclock offset

Message ID 20171208042316.94655-3-rodger.combs@gmail.com
State Superseded
Headers show

Commit Message

Rodger Combs Dec. 8, 2017, 4:23 a.m. UTC
---
 libavformat/avformat.h      |  1 +
 libavformat/internal.h      |  5 +++++
 libavformat/options_table.h |  1 +
 libavformat/utils.c         | 12 ++++++++++++
 4 files changed, 19 insertions(+)

Comments

Nicolas George Dec. 10, 2017, 11:07 a.m. UTC | #1
Rodger Combs (2017-12-07):
> ---
>  libavformat/avformat.h      |  1 +
>  libavformat/internal.h      |  5 +++++
>  libavformat/options_table.h |  1 +
>  libavformat/utils.c         | 12 ++++++++++++
>  4 files changed, 19 insertions(+)

Can you give an example of scenario where this option would be useful?
I mean with some level of details about the problem and the solution.

Also, the timestamp would need to be filtered for scheduling
fluctuations.

Regards,
Rodger Combs Dec. 10, 2017, 11:14 a.m. UTC | #2
> On Dec 10, 2017, at 05:07, Nicolas George <george@nsup.org> wrote:
> 
> Rodger Combs (2017-12-07):
>> ---
>> libavformat/avformat.h      |  1 +
>> libavformat/internal.h      |  5 +++++
>> libavformat/options_table.h |  1 +
>> libavformat/utils.c         | 12 ++++++++++++
>> 4 files changed, 19 insertions(+)
> 
> Can you give an example of scenario where this option would be useful?
> I mean with some level of details about the problem and the solution.

The goal is to use this in conjunction with the previous patches in the set to deal with live broadcast streams coming off a tuner. The first patch sets the CORRUPT flag when the TEI bit is set in a TS packet (indicating that the tuner detected uncorrectable errors), the second clears the PTS and DTS in response to that flag (since a bit flip that causes a too-high timestamp can throw off the whole stream, since ffmpeg.c enforces monotonicity), and the third re-generates DTSs by interpolating from the previous packet's receipt time.

> 
> Also, the timestamp would need to be filtered for scheduling
> fluctuations.

In my target use-case, being slightly off due to scheduling fluctuations isn't particularly problematic, since this is expected to only occur in the case of corruption anyway, so the whole thing is best-effort.

> 
> Regards,
> 
> -- 
>  Nicolas George
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Nicolas George Dec. 10, 2017, 11:40 a.m. UTC | #3
Rodger Combs (2017-12-10):
> The goal is to use this in conjunction with the previous patches in
> the set to deal with live broadcast streams coming off a tuner. The
> first patch sets the CORRUPT flag when the TEI bit is set in a TS
> packet (indicating that the tuner detected uncorrectable errors), the
> second clears the PTS and DTS in response to that flag (since a bit
> flip that causes a too-high timestamp can throw off the whole stream,
> since ffmpeg.c enforces monotonicity), and the third re-generates DTSs
> by interpolating from the previous packet's receipt time.

You did not give enough details to judge, but I will try to extrapolate.

This change would be useful for an application that receives live
broadcast streams, from a source that may be corrupted, but that uses
wallclock timestamps, synchronized with the client application and with
no network delay, and that cannot fiddle with timestamps itself.

It seems awfully specific. Too specific for a change in the core
workings of the library.

> In my target use-case

Stop. We are not speaking of your target use case, we are speaking of a
change in the core components of the library. It should be designed to
be useful for more than your own use case.

Regards,
Rodger Combs Dec. 10, 2017, 11:56 a.m. UTC | #4
> On Dec 10, 2017, at 05:40, Nicolas George <george@nsup.org> wrote:
> 
> Rodger Combs (2017-12-10):
>> The goal is to use this in conjunction with the previous patches in
>> the set to deal with live broadcast streams coming off a tuner. The
>> first patch sets the CORRUPT flag when the TEI bit is set in a TS
>> packet (indicating that the tuner detected uncorrectable errors), the
>> second clears the PTS and DTS in response to that flag (since a bit
>> flip that causes a too-high timestamp can throw off the whole stream,
>> since ffmpeg.c enforces monotonicity), and the third re-generates DTSs
>> by interpolating from the previous packet's receipt time.
> 
> You did not give enough details to judge, but I will try to extrapolate.
> 
> This change would be useful for an application that receives live
> broadcast streams, from a source that may be corrupted, but that uses
> wallclock timestamps, synchronized with the client application and with
> no network delay, and that cannot fiddle with timestamps itself.

Not exactly. I'm not using actual wallclock timestamps anywhere; just _offsets_ of timestamps.
My application uses ffmpeg.c to record OTA and cable broadcasts (which may have corruption) to segments on-disk, which are later either concatenated, or streamed to client devices, or both. In either case, it's preferable to have timestamps be approximately correct in the event of corruption (as this patch produces), rather than entirely absent, or approximated from frame rate (which would produce incorrect values if frames were missing). I think the "remux a broadcast stream to disk" use-case is fairly common.

> 
> It seems awfully specific. Too specific for a change in the core
> workings of the library.
> 
>> In my target use-case
> 
> Stop. We are not speaking of your target use case, we are speaking of a
> change in the core components of the library. It should be designed to
> be useful for more than your own use case.

As described above, I think the use-case I'm referring to is fairly common and worth supporting.

> 
> Regards,
> 
> -- 
>  Nicolas George
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index d10d583dff..a039a2764d 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -1451,6 +1451,7 @@  typedef struct AVFormatContext {
 #define AVFMT_FLAG_SHORTEST   0x100000 ///< Stop muxing when the shortest stream stops.
 #define AVFMT_FLAG_AUTO_BSF   0x200000 ///< Add bitstream filters as requested by the muxer
 #define AVFMT_FLAG_DISCARD_CORRUPT_TS 0x400000 ///< Discard timestamps of frames marked corrupt
+#define AVFMT_FLAG_FILL_WALLCLOCK_DTS 0x800000 ///< Fill missing or discarded DTS values from wallclock (for live streams)
 
     /**
      * Maximum size of the data read from input for determining
diff --git a/libavformat/internal.h b/libavformat/internal.h
index 36a57214ce..3abf1dc720 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -192,6 +192,11 @@  struct AVStreamInternal {
     int need_context_update;
 
     FFFrac *priv_pts;
+
+    /**
+     * The wallclock timestamp of the most recent read packet (if AVFMT_FLAG_FILL_WALLCLOCK_DTS is set)
+     */
+    int64_t cur_wallclock_time;
 };
 
 #ifdef __GNUC__
diff --git a/libavformat/options_table.h b/libavformat/options_table.h
index 515574d3e0..64febc7a21 100644
--- a/libavformat/options_table.h
+++ b/libavformat/options_table.h
@@ -59,6 +59,7 @@  static const AVOption avformat_options[] = {
 {"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" },
 {"autobsf", "add needed bsfs automatically", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, "fflags" },
 {"discardcorruptts", "discard timestamps on corrupted frames", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_DISCARD_CORRUPT_TS }, 0, 0, E, "fflags" },
+{"fillwallclockdts", "fill missing or discarded DTS values from wallclock (for live streams)", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_FILL_WALLCLOCK_DTS }, 0, 0, E, "fflags" },
 {"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D},
 {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D},
 {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D},
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 98af941e9f..22cefb2975 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -881,6 +881,18 @@  int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
                    pkt->stream_index);
         }
 
+        if (s->flags & AVFMT_FLAG_FILL_WALLCLOCK_DTS) {
+            int64_t cur_wallclock_time = av_gettime_relative();
+            if (pkt->dts == AV_NOPTS_VALUE && st->cur_dts != AV_NOPTS_VALUE && st->internal->cur_wallclock_time) {
+                int64_t wallclock_offset = av_rescale_q(st->internal->cur_wallclock_time - cur_wallclock_time, AV_TIME_BASE_Q, st->time_base);
+                pkt->dts = st->cur_dts + FFMAX(wallclock_offset, 1);
+                av_log(s, AV_LOG_VERBOSE,
+                       "Filled timestamp from wallclock (stream = %d; last = %"PRId64"; val = %"PRId64")\n",
+                       pkt->stream_index, st->cur_dts, pkt->dts);
+            }
+            st->internal->cur_wallclock_time = cur_wallclock_time;
+        }
+
         pkt->dts = wrap_timestamp(st, pkt->dts);
         pkt->pts = wrap_timestamp(st, pkt->pts);