movenc: Add a flag for omitting writing the tfdt atom

Message ID 1425550736-13937-1-git-send-email-martin@martin.st
State New
Headers show

Commit Message

Martin Storsjö March 5, 2015, 10:18 a.m.
In a truncated fragmented mp4 file (or a fragmented mp4 file where
fragments are accessed non-sequentially), the tfdt atom gives the
absolute timestamp of the fragment.  Without this atom, all streams
are assumed to start at the same timestamp (dts=0, unless edit lists
are present). For a truncated stream (where earlier fragments are
skipped), not all streams start at the exact same timestamp (since
frames in different streams have different durations, and the timestamps
at the fragment boundary don't line up exactly in all of them). Therefore,
the tfdt atom is essential for getting correct AV sync in such streams,
otherwise it will be off by less than one frame length.

Nevertheless, some players (Windows Phone 8.1) have problems playing
truncated fragmented mp4 live streams as long as the tfdt atom is
present (playback of the video stream may not start at all, or may
be very choppy). This isn't an issue with the tfdt atom itself, only
with the fact that tfdt atoms start from a non-zero timestamp, when the
stream is live (entering an already running stream). Thus add an option
for omitting it.

Some players (e.g. Firefox) refuse to play back a fragmented mp4 stream
unless it contains tfdt, while Chrome can play back such a stream just
fine though. So it is not universally recommended to omit it.
---
 libavformat/movenc.c  | 6 ++++--
 libavformat/movenc.h  | 1 +
 libavformat/version.h | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)

Comments

Luca Barbato March 5, 2015, 1:04 p.m. | #1
On 05/03/15 11:18, Martin Storsjö wrote:
> In a truncated fragmented mp4 file (or a fragmented mp4 file where
> fragments are accessed non-sequentially), the tfdt atom gives the
> absolute timestamp of the fragment.  Without this atom, all streams
> are assumed to start at the same timestamp (dts=0, unless edit lists
> are present). For a truncated stream (where earlier fragments are
> skipped), not all streams start at the exact same timestamp (since
> frames in different streams have different durations, and the timestamps
> at the fragment boundary don't line up exactly in all of them). Therefore,
> the tfdt atom is essential for getting correct AV sync in such streams,
> otherwise it will be off by less than one frame length.
>


Ok, but might be useful to have some documentation about it.

lu

Patch

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 585b080..d91b76a 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -62,6 +62,7 @@  static const AVOption options[] = {
     { "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+    { "omit_tfdt", "Omit the tfdt atoms in fragmented files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFDT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags),
     { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
     { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM},
@@ -2650,7 +2651,7 @@  static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov,
     ffio_wfourcc(pb, "traf");
 
     mov_write_tfhd_tag(pb, mov, track, moof_offset);
-    if (mov->mode != MODE_ISM)
+    if (mov->mode != MODE_ISM && !(mov->flags & FF_MOV_FLAG_OMIT_TFDT))
         mov_write_tfdt_tag(pb, track);
     mov_write_trun_tag(pb, mov, track, moof_size);
     if (mov->mode == MODE_ISM) {
@@ -2928,7 +2929,8 @@  static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
 
     // We add tfdt atoms when fragmenting, signal this with the iso6 compatible
     // brand. This is compatible with users that don't understand tfdt.
-    if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->mode != MODE_ISM)
+    if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->mode != MODE_ISM &&
+        !(mov->flags & FF_MOV_FLAG_OMIT_TFDT))
         ffio_wfourcc(pb, "iso6");
 
     if (mov->mode == MODE_3GP)
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 682820e..d81bb0f 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -185,6 +185,7 @@  typedef struct MOVMuxContext {
 #define FF_MOV_FLAG_DASH                  (1 << 11)
 #define FF_MOV_FLAG_FRAG_DISCONT          (1 << 12)
 #define FF_MOV_FLAG_DELAY_MOOV            (1 << 13)
+#define FF_MOV_FLAG_OMIT_TFDT             (1 << 14)
 
 int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);
 
diff --git a/libavformat/version.h b/libavformat/version.h
index 9ceb6ee..3ca6758 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@ 
 
 #define LIBAVFORMAT_VERSION_MAJOR 56
 #define LIBAVFORMAT_VERSION_MINOR 17
-#define LIBAVFORMAT_VERSION_MICRO  0
+#define LIBAVFORMAT_VERSION_MICRO  1
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \