On Mon, 19 Aug 2013, John Stebbins wrote:
From: Clément Bœsch <[email protected]>
Faststart moves moov atom to beginning of file and rewrites the reset of
the file after muxing is complete.
---
libavformat/movenc.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++--
libavformat/movenc.h | 3 +-
2 files changed, 137 insertions(+), 6 deletions(-)
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 6832746..c7536f5 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -51,6 +51,7 @@ static const AVOption options[] = {
{ "separate_moof", "Write separate moof/mdat atoms for each track", 0,
AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM,
"movflags" },
{ "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 =
FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "isml", "Create a live smooth streaming feed (for pushing to a publishing point)",
0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM,
"movflags" },
+ { "faststart", "Run a second pass to put the moov at the beginning of the file", 0,
AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM,
"movflags" },
{ "moov_size", "maximum moov size so it can be placed at the begin",
offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX,
AV_OPT_FLAG_ENCODING_PARAM, 0 },
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},
@@ -3017,6 +3018,14 @@ static int mov_write_header(AVFormatContext *s)
FF_MOV_FLAG_FRAG_CUSTOM))
mov->flags |= FF_MOV_FLAG_FRAGMENT;
+ /* faststart: moov at the beginning of the file, if supported */
+ if (mov->flags & FF_MOV_FLAG_FASTSTART) {
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT)
+ mov->flags &= ~FF_MOV_FLAG_FASTSTART;
+ else
+ mov->reserved_moov_size = -1;
+ }
+
/* 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 &&
@@ -3160,7 +3169,8 @@ static int mov_write_header(AVFormatContext *s)
if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) {
if(mov->reserved_moov_size) {
mov->reserved_moov_pos= avio_tell(pb);
- avio_skip(pb, mov->reserved_moov_size);
+ if(mov->reserved_moov_size > 0)
+ avio_skip(pb, mov->reserved_moov_size);
Add whitespace
}
mov_write_mdat_tag(pb, mov);
}
@@ -3201,6 +3211,115 @@ static int mov_write_header(AVFormatContext *s)
return -1;
}
+static int get_moov_size(AVFormatContext *s)
+{
+ int ret;
+ uint8_t *buf;
+ AVIOContext *moov_buf;
+ MOVMuxContext *mov = s->priv_data;
+
+ if ((ret = avio_open_dyn_buf(&moov_buf)) < 0)
+ return ret;
+ mov_write_moov_tag(moov_buf, mov, s);
+ ret = avio_close_dyn_buf(moov_buf, &buf);
+ av_free(buf);
+ return ret;
+}
+
+/**
+ * This function gets the moov size if moved to the top of the file: the chunk
+ * offset table can switch between stco (32-bit entries) to co64 (64-bit
+ * entries) when the moov is moved to the top, so the size of the moov would
+ * change. It also updates the chunk offset tables.
+ */
+static int compute_moov_size(AVFormatContext *s)
+{
+ int i, moov_size, moov_size2;
+ MOVMuxContext *mov = s->priv_data;
+
+ moov_size = get_moov_size(s);
+ if (moov_size < 0)
+ return moov_size;
+
+ for (i = 0; i < mov->nb_streams; i++)
+ mov->tracks[i].data_offset += moov_size;
+
+ moov_size2 = get_moov_size(s);
+ if (moov_size2 < 0)
+ return moov_size2;
+
+ /* if the size changed, we just switched from stco to co64 and needs to
+ * update the offsets */
+ if (moov_size2 != moov_size)
+ for (i = 0; i < mov->nb_streams; i++)
+ mov->tracks[i].data_offset += moov_size2 - moov_size;
+
+ return moov_size2;
+}
+
+static int shift_data(AVFormatContext *s)
+{
+ int ret = 0, moov_size;
+ MOVMuxContext *mov = s->priv_data;
+ int64_t pos, pos_end = avio_tell(s->pb);
+ uint8_t *buf, *read_buf[2];
+ int read_buf_id = 0;
+ int read_size[2];
+ AVIOContext *read_pb;
+
+ moov_size = compute_moov_size(s);
+ if (moov_size < 0)
+ return moov_size;
+
+ buf = av_malloc(moov_size * 2);
+ if (!buf)
+ return AVERROR(ENOMEM);
+ read_buf[0] = buf;
+ read_buf[1] = buf + moov_size;
+
+ /* Shift the data: the AVIO context of the output can only be used for
+ * writing, so we re-open the same output, but for reading. It also avoids
+ * a read/seek/write/seek back and forth. */
+ avio_flush(s->pb);
+ ret = avio_open(&read_pb, s->filename, AVIO_FLAG_READ);
This only works if s->filename actually is a normal file. Checking what
actual protocol is used might be overkill, but if s->flags contains
AVFMT_FLAG_CUSTOM_IO we should at least not try this.
Other than that, the patch looks useful even if it isn't generally doable
in all corner cases.
// Martin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel