From 2ded57371abead879bcee56da5131e5fac0d17ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Sun, 5 Oct 2014 23:51:47 +0300 Subject: [PATCH] movenc: Add support for writing sidx atoms for DASH segments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A flag "dash" is added, which enables the necessary flags for creating DASH compatible fragments. When this is enabled, one sidx atom is written for each track before every moof atom. Signed-off-by: Martin Storsjö --- Changelog | 1 + libavformat/movenc.c | 82 +++++++++++++++++++++++++++++++++++++++++-- libavformat/movenc.h | 1 + libavformat/version.h | 2 +- 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/Changelog b/Changelog index c51fa8f78b..ecec401dbb 100644 --- a/Changelog +++ b/Changelog @@ -6,6 +6,7 @@ version : - HEVC/H.265 RTP payload format (draft v6) packetizer and depacketizer - avplay now exits by default at the end of playback - XCB-based screen-grabber +- creating DASH compatible fragmented MP4 version 11: diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 8d378c4357..76439678d1 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -59,6 +59,7 @@ static const AVOption options[] = { { "omit_tfhd_offset", "Omit the base data offset in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFHD_OFFSET}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "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" }, 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}, @@ -2675,7 +2676,78 @@ static int mov_write_moof_tag_internal(AVIOContext *pb, MOVMuxContext *mov, return update_size(pb, pos); } -static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) +static int mov_write_sidx_tag(AVIOContext *pb, + MOVTrack *track, int ref_size, int total_sidx_size) +{ + int64_t pos = avio_tell(pb), offset_pos, end_pos; + int64_t presentation_time = track->start_dts + track->frag_start + + track->cluster[0].cts; + int64_t duration = track->start_dts + track->track_duration - + track->cluster[0].dts; + int64_t offset; + int starts_with_SAP = track->cluster[0].flags & MOV_SYNC_SAMPLE; + + // pts<0 should be cut away using edts + if (presentation_time < 0) + presentation_time = 0; + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "sidx"); + avio_w8(pb, 1); /* version */ + avio_wb24(pb, 0); + avio_wb32(pb, track->track_id); /* reference_ID */ + avio_wb32(pb, track->timescale); /* timescale */ + avio_wb64(pb, presentation_time); /* earliest_presentation_time */ + offset_pos = avio_tell(pb); + avio_wb64(pb, 0); /* first_offset (offset to referenced moof) */ + avio_wb16(pb, 0); /* reserved */ + avio_wb16(pb, 1); /* reference_count */ + avio_wb32(pb, (0 << 31) | (ref_size & 0x7fffffff)); /* reference_type (0 = media) | referenced_size */ + avio_wb32(pb, duration); /* subsegment_duration */ + avio_wb32(pb, (starts_with_SAP << 31) | (0 << 28) | 0); /* starts_with_SAP | SAP_type | SAP_delta_time */ + + end_pos = avio_tell(pb); + offset = pos + total_sidx_size - end_pos; + avio_seek(pb, offset_pos, SEEK_SET); + avio_wb64(pb, offset); + avio_seek(pb, end_pos, SEEK_SET); + return update_size(pb, pos); +} + +static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov, + int tracks, int ref_size) +{ + int i, round, ret; + AVIOContext *avio_buf; + int total_size = 0; + for (round = 0; round < 2; round++) { + // First run one round to calculate the total size of all + // sidx atoms. + // This would be much simpler if we'd only write one sidx + // atom, for the first track in the moof. + if (round == 0) { + if ((ret = ffio_open_null_buf(&avio_buf)) < 0) + return ret; + } else { + avio_buf = pb; + } + for (i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + if (tracks >= 0 && i != tracks) + continue; + if (!track->entry) + continue; + total_size -= mov_write_sidx_tag(avio_buf, track, ref_size, + total_size); + } + if (round == 0) + total_size = ffio_close_null_buf(avio_buf); + } + return 0; +} + +static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, + int64_t mdat_size) { AVIOContext *avio_buf; int ret, moof_size; @@ -2685,6 +2757,9 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); moof_size = ffio_close_null_buf(avio_buf); + if (mov->flags & FF_MOV_FLAG_DASH) + mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); + if ((ret = mov_add_tfra_entries(pb, mov, tracks)) < 0) return ret; @@ -3054,7 +3129,7 @@ static int mov_flush_fragment(AVFormatContext *s) if (write_moof) { avio_flush(s->pb); - mov_write_moof_tag(s->pb, mov, moof_tracks); + mov_write_moof_tag(s->pb, mov, moof_tracks, mdat_size); mov->fragments++; avio_wb32(s->pb, mdat_size + 8); @@ -3504,6 +3579,9 @@ static int mov_write_header(AVFormatContext *s) if (mov->mode == MODE_ISM) mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF | FF_MOV_FLAG_FRAGMENT; + if (mov->flags & FF_MOV_FLAG_DASH) + mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | + FF_MOV_FLAG_DEFAULT_BASE_MOOF; /* faststart: moov at the beginning of the file, if supported */ if (mov->flags & FF_MOV_FLAG_FASTSTART) { diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 1df5a5cb96..2a40b2fb61 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -180,6 +180,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_OMIT_TFHD_OFFSET (1 << 8) #define FF_MOV_FLAG_DISABLE_CHPL (1 << 9) #define FF_MOV_FLAG_DEFAULT_BASE_MOOF (1 << 10) +#define FF_MOV_FLAG_DASH (1 << 11) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); diff --git a/libavformat/version.h b/libavformat/version.h index f8c5edbd9f..c10a6b87c2 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 56 #define LIBAVFORMAT_VERSION_MINOR 6 -#define LIBAVFORMAT_VERSION_MICRO 4 +#define LIBAVFORMAT_VERSION_MICRO 5 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \