From: Martin Storsjö <mar...@martin.st>

This reduces the need for an edit list; streams that start with
e.g. dts=-1, pts=0 can be encoded as dts=0, pts=0 (which is valid
in mov/mp4) by shifting the dts values of all packets forward.
This avoids the need for edit lists for such streams (while they
still are needed for audio streams with encoder delay).

This eases conformance with the DASH-IF interoperability guidelines.

Signed-off-by: Martin Storsjö <mar...@martin.st>
---
 doc/muxers.texi      |  6 ++++++
 libavformat/movenc.c | 28 ++++++++++++++++++++++++----
 libavformat/movenc.h |  2 ++
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 36769b8c1a..8a1319c153 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -1055,6 +1055,12 @@ on the implicit end of the previous track fragment).
 @item -write_tmcd
 Specify @code{on} to force writing a timecode track, @code{off} to disable it
 and @code{auto} to write a timecode track only for mov and mp4 output 
(default).
+@item -movflags negative_cts_offsets
+Enables utilization of version 1 of the CTTS box, in which the CTS offsets can
+be negative. This enables the initial sample to have DTS/CTS of zero, and
+reduces the need for edit lists for some cases such as video tracks with
+B-frames. Additionally, eases conformance with the DASH-IF interoperability
+guidelines.
 @end table
 
 @subsection Example
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 10b959ad02..917486384a 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -77,6 +77,7 @@ static const AVOption options[] = {
     { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 
= FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 
"movflags" },
     { "use_metadata_tags", "Use mdta atom for metadata.", 0, 
AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, 
AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     { "skip_trailer", "Skip writing the mfra/tfra/mfro trailer for fragmented 
files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_TRAILER}, INT_MIN, 
INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+    { "negative_cts_offsets", "Use negative CTS offsets (reducing the need for 
edit lists)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS}, 
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_BOOL, {.i64 = 1}, 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},
@@ -2094,8 +2095,9 @@ static int mov_write_stsd_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContext
     return update_size(pb, pos);
 }
 
-static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_ctts_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack 
*track)
 {
+    MOVMuxContext *mov = s->priv_data;
     MOVStts *ctts_entries;
     uint32_t entries = 0;
     uint32_t atom_size;
@@ -2119,7 +2121,11 @@ static int mov_write_ctts_tag(AVIOContext *pb, MOVTrack 
*track)
     atom_size = 16 + (entries * 8);
     avio_wb32(pb, atom_size); /* size */
     ffio_wfourcc(pb, "ctts");
-    avio_wb32(pb, 0); /* version & flags */
+    if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+        avio_w8(pb, 1); /* version */
+    else
+        avio_w8(pb, 0); /* version */
+    avio_wb24(pb, 0); /* flags */
     avio_wb32(pb, entries); /* entry count */
     for (i = 0; i < entries; i++) {
         avio_wb32(pb, ctts_entries[i].count);
@@ -2303,7 +2309,7 @@ static int mov_write_stbl_tag(AVFormatContext *s, 
AVIOContext *pb, MOVMuxContext
     if (track->par->codec_type == AVMEDIA_TYPE_VIDEO &&
         track->flags & MOV_TRACK_CTTS && track->entry) {
 
-        if ((ret = mov_write_ctts_tag(pb, track)) < 0)
+        if ((ret = mov_write_ctts_tag(s, pb, track)) < 0)
             return ret;
     }
     mov_write_stsc_tag(pb, track);
@@ -4044,7 +4050,10 @@ static int mov_write_trun_tag(AVIOContext *pb, 
MOVMuxContext *mov,
 
     avio_wb32(pb, 0); /* size placeholder */
     ffio_wfourcc(pb, "trun");
-    avio_w8(pb, 0); /* version */
+    if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+        avio_w8(pb, 1); /* version */
+    else
+        avio_w8(pb, 0); /* version */
     avio_wb24(pb, flags);
 
     avio_wb32(pb, end - first); /* sample count */
@@ -4489,6 +4498,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, 
AVFormatContext *s)
         ffio_wfourcc(pb, "MSNV");
     else if (mov->mode == MODE_MP4 && mov->flags & 
FF_MOV_FLAG_DEFAULT_BASE_MOOF)
         ffio_wfourcc(pb, "iso5"); // Required when using default-base-is-moof
+    else if (mov->mode == MODE_MP4 && mov->flags & 
FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+        ffio_wfourcc(pb, "iso4");
     else if (mov->mode == MODE_MP4)
         ffio_wfourcc(pb, "isom");
     else if (mov->mode == MODE_IPOD)
@@ -4759,6 +4770,8 @@ static int mov_flush_fragment(AVFormatContext *s, int 
force)
         if (!track->end_reliable) {
             AVPacket pkt;
             if (!ff_interleaved_peek(s, i, &pkt, 1)) {
+                if (track->dts_shift != AV_NOPTS_VALUE)
+                    pkt.dts += track->dts_shift;
                 track->track_duration = pkt.dts - track->start_dts;
                 if (pkt.pts != AV_NOPTS_VALUE)
                     track->end_pts = pkt.pts;
@@ -5282,6 +5295,12 @@ static int mov_write_single_packet(AVFormatContext *s, 
AVPacket *pkt)
             mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
         }
 
+        if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) {
+            if (trk->dts_shift == AV_NOPTS_VALUE)
+                trk->dts_shift = pkt->pts - pkt->dts;
+            pkt->dts += trk->dts_shift;
+        }
+
         if (trk->par->codec_id == AV_CODEC_ID_MP4ALS ||
             trk->par->codec_id == AV_CODEC_ID_AAC ||
             trk->par->codec_id == AV_CODEC_ID_FLAC) {
@@ -5922,6 +5941,7 @@ static int mov_init(AVFormatContext *s)
         track->start_dts  = AV_NOPTS_VALUE;
         track->start_cts  = AV_NOPTS_VALUE;
         track->end_pts    = AV_NOPTS_VALUE;
+        track->dts_shift  = AV_NOPTS_VALUE;
         if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
             if (track->tag == MKTAG('m','x','3','p') || track->tag == 
MKTAG('m','x','3','n') ||
                 track->tag == MKTAG('m','x','4','p') || track->tag == 
MKTAG('m','x','4','n') ||
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 4c4f3cdfc3..cc2a155d79 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -116,6 +116,7 @@ typedef struct MOVTrack {
     int64_t     start_cts;
     int64_t     end_pts;
     int         end_reliable;
+    int64_t     dts_shift;
 
     int         hint_track;   ///< the track that hints this track, -1 if no 
hint track is set
     int         src_track;    ///< the track that this hint (or tmcd) track 
describes
@@ -241,6 +242,7 @@ typedef struct MOVMuxContext {
 #define FF_MOV_FLAG_WRITE_GAMA            (1 << 16)
 #define FF_MOV_FLAG_USE_MDTA              (1 << 17)
 #define FF_MOV_FLAG_SKIP_TRAILER          (1 << 18)
+#define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS  (1 << 19)
 
 int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);
 
-- 
2.13.5

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to