On Sun, 23 Nov 2014, Martin Storsjö wrote:

On Sat, 22 Nov 2014, Bryan Huh wrote:

On Sat, Nov 22, 2014 at 1:55 PM, Martin Storsjö <mar...@martin.st> wrote:

This allows creating a later mp4 fragment without sequentially
writing the earlier ones before (when called from a segmenter).

Normally when writing a fragmented mp4 file sequentially, the
first timestamps of a fragment are adjusted to match the
end of the previous fragment, to make sure the timestamp is the
same, even if it is calculated as the sum of previous fragment
durations. (And for the first packet in a file, the offset of
the first packet is written using an edit list.)

When writing an individual mp4 fragment discontinuously like this
(with potentially writing the earlier fragments separately later),
there's a risk of getting a gap in the timeline if the duration
field of the last packet in the previous fragment doesn't match up
with the start time of the next fragment.

Using this requires setting -avoid_negative_ts make_non_negative
(or -avoid_negative_ts 0).

Why is that? So make_zero won't work?

Because if you set make_zero, it will shift all the timestamps to start from 0. If we've e.g. 10 second segments and we want to start writing the third segment (seconds 20-30), we'd start off sending a packet with timestamp 20 to the muxer. If we've got avoid_negative_ts = make_zero then, the packet will end up with timestamp 0 when it reaches the muxer, which gives the wrong baseDecodeTime in tfdt.

This does however mean that you would need to take some extra care with timestamps in general - if you want the effect of make_zero, you'd need to do the same offsetting outside of the mp4 muxer, to make the first segment (which isn't written) start from 0.

Also, maybe this is for another commit, but would it maybe be useful to
expose this as an option in dashenc.c as well?

Yes, I might add it as a feature on that level later as well.

---
 libavformat/movenc.c | 34 ++++++++++++++++++++++++++++------
 libavformat/movenc.h |  2 ++
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 3ba1cc8..80531d0 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -60,6 +60,7 @@ static const AVOption options[] = {
     { "disable_chpl", "Disable Nero chapter atom", 0, AV_OPT_TYPE_CONST,
{.i64 = FF_MOV_FLAG_DISABLE_CHPL}, INT_MIN, INT_MAX,
AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     { "default_base_moof", "Set the default-base-is-moof flag in tfhd
atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DEFAULT_BASE_MOOF},
INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
     { "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" },
     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},
@@ -3282,11 +3283,19 @@ int ff_mov_write_packet(AVFormatContext *s,
AVPacket *pkt)
     trk->cluster[trk->entry].entries          = samples_in_chunk;
     trk->cluster[trk->entry].dts              = pkt->dts;
     if (!trk->entry && trk->start_dts != AV_NOPTS_VALUE) {
-        /* First packet of a new fragment. We already wrote the duration
-         * of the last packet of the previous fragment based on
track_duration,
- * which might not exactly match our dts. Therefore adjust the
dts
-         * of this packet to be what the previous packets duration
implies. */
-        trk->cluster[trk->entry].dts = trk->start_dts +
trk->track_duration;
+        if (!trk->frag_discont) {
+            /* First packet of a new fragment. We already wrote the
duration
+             * of the last packet of the previous fragment based on
track_duration,
+             * which might not exactly match our dts. Therefore adjust
the dts
+             * of this packet to be what the previous packets duration
implies. */
+            trk->cluster[trk->entry].dts = trk->start_dts +
trk->track_duration;
+        } else {
+            /* New fragment, but discontinuous from previous fragments.
+             * Pretend the duration sum of the earlier fragments is
+             * pkt->dts - trk->start_dts. */
+            trk->frag_start = pkt->dts - trk->start_dts;
+            trk->frag_discont = 0;
+        }
     }
     if (!trk->entry && trk->start_dts == AV_NOPTS_VALUE &&
!mov->use_editlist &&
         s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) {
@@ -3299,7 +3308,13 @@ int ff_mov_write_packet(AVFormatContext *s,
AVPacket *pkt)
     }
     if (trk->start_dts == AV_NOPTS_VALUE) {
         trk->start_dts = pkt->dts;
-        if (pkt->dts && mov->flags & FF_MOV_FLAG_EMPTY_MOOV)
+        if (trk->frag_discont) {
+            /* Pretend the whole stream started at dts=0, with earlier
framgents
+ * already written, with a duration summing up to pkt->dts.
*/
+            trk->frag_start   = pkt->dts;
+            trk->start_dts    = 0;
+            trk->frag_discont = 0;
+        } else if (pkt->dts && mov->flags & FF_MOV_FLAG_EMPTY_MOOV)
             av_log(s, AV_LOG_WARNING,
                    "Track %d starts with a nonzero dts %"PRId64". This "
                    "currently isn't handled correctly in combination with
"
@@ -3357,6 +3372,13 @@ static int mov_write_packet(AVFormatContext *s,
AVPacket *pkt)
         if (!pkt->size)
             return 0;             /* Discard 0 sized packets */

+        if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) {
+            int i;
+            for (i = 0; i < s->nb_streams; i++)
+                mov->tracks[i].frag_discont = 1;
+            mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
+        }
+

Maybe I'm missing something, but are you not able to do this initialization
in mov_write_header? Then you wouldn't have to unset the flag either.

I could, if I would treat it as a flag only set at startup. However, the idea is that you can open the mp4 muxer once, write the initialization segment (moov) at startup - then you can write segments in any order, just set the frag_discont flag before starting a new fragment which doesn't start off where the previous fragment ended. (If not, you would have to close and reopen the muxer for each segment written. That obviously wouldn't any big performance issue, but I think being able to use one single muxer instance for it all is a nice convenience.)

Any further comments on this one? If not I'll push it tomorrow or so.

// Martin
_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to