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). --- 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; + } + if (trk->entry) frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, s->streams[pkt->stream_index]->time_base, diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 4483b69..c13a834 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -121,6 +121,7 @@ typedef struct MOVTrack { AVIOContext *mdat_buf; int64_t data_offset; int64_t frag_start; + int frag_discont; int nb_frag_info; MOVFragmentInfo *frag_info; @@ -182,6 +183,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_DISABLE_CHPL (1 << 9) #define FF_MOV_FLAG_DEFAULT_BASE_MOOF (1 << 10) #define FF_MOV_FLAG_DASH (1 << 11) +#define FF_MOV_FLAG_FRAG_DISCONT (1 << 12) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); -- 1.9.3 (Apple Git-50) _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel