diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 4c76332342..2f8539c42b 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -81,6 +81,7 @@ static const AVOption options[] = { { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "fragment_index", "Fragment number of the next fragment", offsetof(MOVMuxContext, fragments), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "mov_gamma", "gamma value for gama atom", offsetof(MOVMuxContext, gamma), AV_OPT_TYPE_FLOAT, {.dbl = 0.0 }, 0.0, 10, AV_OPT_FLAG_ENCODING_PARAM}, + { "frag_interleave", "Interleave samples within fragments (max number of consecutive samples, lower is tighter interleaving, but with more overhead)", offsetof(MOVMuxContext, frag_interleave), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; @@ -4056,6 +4057,32 @@ static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk) } } +static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track) +{ + MOVMuxContext *mov = s->priv_data; + int ret, buf_size; + uint8_t *buf; + int i, offset; + + if (!track->mdat_buf) + return 0; + if (!mov->mdat_buf) { + if ((ret = avio_open_dyn_buf(&mov->mdat_buf)) < 0) + return ret; + } + buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); + track->mdat_buf = NULL; + + offset = avio_tell(mov->mdat_buf); + avio_write(mov->mdat_buf, buf, buf_size); + av_free(buf); + + for (i = track->entries_flushed; i < track->entry; i++) + track->cluster[i].pos += offset; + track->entries_flushed = track->entry; + return 0; +} + static int mov_flush_fragment(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; @@ -4115,15 +4142,29 @@ static int mov_flush_fragment(AVFormatContext *s) return 0; } + if (mov->frag_interleave) { + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + int ret; + if ((ret = mov_flush_fragment_interleaving(s, track)) < 0) + return ret; + } + + if (!mov->mdat_buf) + return 0; + mdat_size = avio_tell(mov->mdat_buf); + } + for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; - if (mov->flags & FF_MOV_FLAG_SEPARATE_MOOF) + if (mov->flags & FF_MOV_FLAG_SEPARATE_MOOF || mov->frag_interleave) track->data_offset = 0; else track->data_offset = mdat_size; - if (!track->mdat_buf) + if (!track->entry) continue; - mdat_size += avio_tell(track->mdat_buf); + if (track->mdat_buf) + mdat_size += avio_tell(track->mdat_buf); if (first_track < 0) first_track = i; } @@ -4162,10 +4203,18 @@ static int mov_flush_fragment(AVFormatContext *s) if (track->entry) track->frag_start += duration; track->entry = 0; - if (!track->mdat_buf) - continue; - buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); - track->mdat_buf = NULL; + track->entries_flushed = 0; + if (!mov->frag_interleave) { + if (!track->mdat_buf) + continue; + buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); + track->mdat_buf = NULL; + } else { + if (!mov->mdat_buf) + continue; + buf_size = avio_close_dyn_buf(mov->mdat_buf, &buf); + mov->mdat_buf = NULL; + } avio_write(s->pb, buf, buf_size); av_free(buf); @@ -4219,6 +4268,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (mov->flags & FF_MOV_FLAG_FRAGMENT) { int ret; if (mov->moov_written || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { + if (mov->frag_interleave && mov->fragments > 0) { + if (trk->entry - trk->entries_flushed >= mov->frag_interleave) { + if ((ret = mov_flush_fragment_interleaving(s, trk)) < 0) + return ret; + } + } + if (!trk->mdat_buf) { if ((ret = avio_open_dyn_buf(&trk->mdat_buf)) < 0) return ret; @@ -4882,6 +4938,21 @@ static int mov_write_header(AVFormatContext *s) if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO; + /* Clear the omit_tfhd_offset flag if default_base_moof is set; + * if the latter is set that's enough and omit_tfhd_offset doesn't + * add anything extra on top of that. */ + if (mov->flags & FF_MOV_FLAG_OMIT_TFHD_OFFSET && + mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) + mov->flags &= ~FF_MOV_FLAG_OMIT_TFHD_OFFSET; + + if (mov->frag_interleave && + mov->flags & (FF_MOV_FLAG_OMIT_TFHD_OFFSET | FF_MOV_FLAG_SEPARATE_MOOF)) { + av_log(s, AV_LOG_ERROR, + "Sample interleaving in fragments is mutually exclusive with " + "omit_tfhd_offset and separate_moof\n"); + return AVERROR(EINVAL); + } + /* Non-seekable output is ok if using fragmentation. If ism_lookahead * is enabled, we don't support non-seekable output at all. */ if (!s->pb->seekable && diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 9605155cde..dfd4cc48cc 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -131,6 +131,7 @@ typedef struct MOVTrack { int64_t data_offset; int64_t frag_start; int frag_discont; + int entries_flushed; int nb_frag_info; MOVFragmentInfo *frag_info; @@ -189,6 +190,8 @@ typedef struct MOVMuxContext { int use_editlist; float gamma; + + int frag_interleave; } MOVMuxContext; #define FF_MOV_FLAG_RTP_HINT (1 << 0)