movenc: Add support for writing sidx atoms for DASH segments

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ö <martin@martin.st>
This commit is contained in:
Martin Storsjö 2014-10-05 23:51:47 +03:00
parent 2d9d6afb8d
commit 2ded57371a
4 changed files with 83 additions and 3 deletions

View File

@ -6,6 +6,7 @@ version <next>:
- 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:

View File

@ -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) {

View File

@ -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);

View File

@ -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, \