[v2,1/3] movenc: Add an option for enabling negative CTS offsets

Message ID 20170224201812.78531-1-martin@martin.st
State Committed
Commit c380a0d7f7a2c7411aae60463e25d916541f0388
Headers show

Commit Message

Martin Storsjö Feb. 24, 2017, 8:18 p.m.
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).

---
Write iso4 as major brand, properly apply the dts shift when
peeking into the interleaving queue.
---
 libavformat/movenc.c | 28 ++++++++++++++++++++++++----
 libavformat/movenc.h |  2 ++
 2 files changed, 26 insertions(+), 4 deletions(-)

Comments

Martin Storsjö May 15, 2017, 7:42 a.m. | #1
On Fri, 24 Feb 2017, Martin Storsjö wrote:

> 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).
>
> ---
> Write iso4 as major brand, properly apply the dts shift when
> peeking into the interleaving queue.
> ---
> libavformat/movenc.c | 28 ++++++++++++++++++++++++----
> libavformat/movenc.h |  2 ++
> 2 files changed, 26 insertions(+), 4 deletions(-)

FYI, I intend to push this soon. It has been tested a bit by Jan Ekström.

I'll amend the commit message further with this:
"This eases conformance with the DASH-IF interoperability guidelines."

// Martin
Luca Barbato May 15, 2017, 9:09 a.m. | #2
On 5/15/17 9:42 AM, Martin Storsjö wrote:
> On Fri, 24 Feb 2017, Martin Storsjö wrote:
> 
>> 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).
>>
>> ---
>> Write iso4 as major brand, properly apply the dts shift when
>> peeking into the interleaving queue.
>> ---
>> libavformat/movenc.c | 28 ++++++++++++++++++++++++----
>> libavformat/movenc.h |  2 ++
>> 2 files changed, 26 insertions(+), 4 deletions(-)
> 
> FYI, I intend to push this soon. It has been tested a bit by Jan Ekström.
> 
> I'll amend the commit message further with this:
> "This eases conformance with the DASH-IF interoperability guidelines."

Looks fine.

Patch

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 840190d..356d1d6 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -62,6 +62,7 @@  static const AVOption options[] = {
     { "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" },
     { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, 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_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},
@@ -1163,8 +1164,9 @@  static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
     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;
@@ -1188,7 +1190,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);
@@ -1273,7 +1279,7 @@  static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
         mov_write_stss_tag(pb, track, MOV_PARTIAL_SYNC_SAMPLE);
     if (track->par->codec_type == AVMEDIA_TYPE_VIDEO &&
         track->flags & MOV_TRACK_CTTS && track->entry)
-        mov_write_ctts_tag(pb, track);
+        mov_write_ctts_tag(s, pb, track);
     mov_write_stsc_tag(pb, track);
     mov_write_stsz_tag(pb, track);
     mov_write_stco_tag(pb, track);
@@ -2594,7 +2600,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 */
@@ -3040,6 +3049,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)
@@ -3288,6 +3299,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;
@@ -3729,6 +3742,12 @@  static int mov_write_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 (!pkt->size) {
             if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) {
                 trk->start_dts = pkt->dts;
@@ -4095,6 +4114,7 @@  static int mov_write_header(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 f4ed188..008f467 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -107,6 +107,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 track describes
@@ -195,6 +196,7 @@  typedef struct MOVMuxContext {
 #define FF_MOV_FLAG_DELAY_MOOV            (1 << 13)
 #define FF_MOV_FLAG_GLOBAL_SIDX           (1 << 14)
 #define FF_MOV_FLAG_SKIP_TRAILER          (1 << 15)
+#define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS  (1 << 16)
 
 int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt);