static const AVOption options[] = {
{ "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
- { "empty_moov", "Make the initial moov atom empty (not supported by QuickTime)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
+ { "empty_moov", "Make the initial moov atom empty", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" },
{ "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" },
{ "faststart", "Run a second pass to put the index (moov atom) 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" },
{ "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},
{ "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
{ "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
{ "brand", "Override major brand", offsetof(MOVMuxContext, major_brand), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM },
+ { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
{ NULL },
};
{
int tag = track->enc->codec_tag;
- if (!tag || (track->enc->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
+ if (!tag || (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL &&
(track->enc->codec_id == AV_CODEC_ID_DVVIDEO ||
track->enc->codec_id == AV_CODEC_ID_RAWVIDEO ||
track->enc->codec_id == AV_CODEC_ID_H263 ||
return update_size(pb, pos);
}
-static int mov_write_mdhd_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov,
+ MOVTrack *track)
{
int version = track->track_duration < INT32_MAX ? 0 : 1;
avio_wb32(pb, track->time); /* modification time */
}
avio_wb32(pb, track->timescale); /* time scale (sample rate for audio) */
- if (!track->entry)
+ if (!track->entry && mov->mode == MODE_ISM)
(version == 1) ? avio_wb64(pb, UINT64_C(0xffffffffffffffff)) : avio_wb32(pb, 0xffffffff);
+ else if (!track->entry)
+ (version == 1) ? avio_wb64(pb, 0) : avio_wb32(pb, 0);
else
(version == 1) ? avio_wb64(pb, track->track_duration) : avio_wb32(pb, track->track_duration); /* duration */
avio_wb16(pb, track->language); /* language */
return 32;
}
-static int mov_write_mdia_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_mdia_tag(AVIOContext *pb, MOVMuxContext *mov,
+ MOVTrack *track)
{
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "mdia");
- mov_write_mdhd_tag(pb, track);
+ mov_write_mdhd_tag(pb, mov, track);
mov_write_hdlr_tag(pb, track);
mov_write_minf_tag(pb, track);
return update_size(pb, pos);
}
avio_wb32(pb, track->track_id); /* track-id */
avio_wb32(pb, 0); /* reserved */
- if (!track->entry)
+ if (!track->entry && mov->mode == MODE_ISM)
(version == 1) ? avio_wb64(pb, UINT64_C(0xffffffffffffffff)) : avio_wb32(pb, 0xffffffff);
+ else if (!track->entry)
+ (version == 1) ? avio_wb64(pb, 0) : avio_wb32(pb, 0);
else
(version == 1) ? avio_wb64(pb, duration) : avio_wb32(pb, duration);
}
// This box seems important for the psp playback ... without it the movie seems to hang
-static int mov_write_edts_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov,
+ MOVTrack *track)
{
int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE,
track->timescale, AV_ROUND_UP);
avio_wb32(pb, entry_count);
if (delay > 0) { /* add an empty edit to delay presentation */
+ /* In the positive delay case, the delay includes the cts
+ * offset, and the second edit list entry below trims out
+ * the same amount from the actual content. This makes sure
+ * that the offsetted last sample is included in the edit
+ * list duration as well. */
if (version == 1) {
avio_wb64(pb, delay);
avio_wb64(pb, -1);
* here, but use FFMIN in case dts is a a small positive integer
* rounded to 0 when represented in MOV_TIMESCALE units. */
start_ct = -FFMIN(track->cluster[0].dts, 0);
+ /* Note, this delay is calculated from the pts of the first sample,
+ * ensuring that we don't reduce the duration for cases with
+ * dts<0 pts=0. */
duration += delay;
}
+ /* For fragmented files, we don't know the full length yet. Setting
+ * duration to 0 allows us to only specify the offset, including
+ * the rest of the content (from all future fragments) without specifying
+ * an explicit duration. */
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT)
+ duration = 0;
+
/* duration */
if (version == 1) {
avio_wb64(pb, duration);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "trak");
mov_write_tkhd_tag(pb, mov, track, st);
- if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS ||
- (track->entry && track->cluster[0].dts) ||
- is_clcp_track(track)) {
- if (!(mov->flags & FF_MOV_FLAG_FRAGMENT))
- mov_write_edts_tag(pb, track); // PSP Movies require edts box
+ if (track->entry &&
+ (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS ||
+ track->cluster[0].dts || is_clcp_track(track))) {
+ if (mov->use_editlist)
+ mov_write_edts_tag(pb, mov, track); // PSP Movies require edts box
+ else if ((track->entry && track->cluster[0].dts) || track->mode == MODE_PSP || is_clcp_track(track))
+ av_log(mov->fc, AV_LOG_WARNING,
+ "Not writing any edit list even though one would have been required\n");
}
if (track->tref_tag)
mov_write_tref_tag(pb, track);
- mov_write_mdia_tag(pb, track);
+ mov_write_mdia_tag(pb, mov, track);
if (track->mode == MODE_PSP)
mov_write_uuid_tag_psp(pb, track); // PSP Movies require this uuid box
if (track->tag == MKTAG('r','t','p',' '))
int audio_profile = mov->iods_audio_profile;
int video_profile = mov->iods_video_profile;
for (i = 0; i < mov->nb_streams; i++) {
- if (mov->tracks[i].entry > 0) {
+ if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
has_audio |= mov->tracks[i].enc->codec_type == AVMEDIA_TYPE_AUDIO;
has_video |= mov->tracks[i].enc->codec_type == AVMEDIA_TYPE_VIDEO;
}
}
if (mov->flags & FF_MOV_FLAG_OMIT_TFHD_OFFSET)
flags &= ~MOV_TFHD_BASE_DATA_OFFSET;
+ if (mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) {
+ flags &= ~MOV_TFHD_BASE_DATA_OFFSET;
+ flags |= MOV_TFHD_DEFAULT_BASE_IS_MOOF;
+ }
/* Don't set a default sample size, the silverlight player refuses
* to play files with that set. Don't set a default sample duration,
avio_wb32(pb, track->entry); /* sample count */
if (mov->flags & FF_MOV_FLAG_OMIT_TFHD_OFFSET &&
- !(mov->flags & FF_MOV_FLAG_SEPARATE_MOOF) &&
- track->track_id != 1)
+ !(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) &&
+ !mov->first_trun)
avio_wb32(pb, 0); /* Later tracks follow immediately after the previous one */
else
avio_wb32(pb, moof_size + 8 + track->data_offset +
avio_wb32(pb, track->cluster[i].cts);
}
+ mov->first_trun = 0;
return update_size(pb, pos);
}
return 0;
}
+static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks)
+{
+ int i;
+ for (i = 0; i < mov->nb_streams; i++) {
+ MOVTrack *track = &mov->tracks[i];
+ MOVFragmentInfo *info;
+ if ((tracks >= 0 && i != tracks) || !track->entry)
+ continue;
+ track->nb_frag_info++;
+ if (track->nb_frag_info >= track->frag_info_capacity) {
+ unsigned new_capacity = track->nb_frag_info + MOV_FRAG_INFO_ALLOC_INCREMENT;
+ if (av_reallocp_array(&track->frag_info,
+ new_capacity,
+ sizeof(*track->frag_info)))
+ return AVERROR(ENOMEM);
+ track->frag_info_capacity = new_capacity;
+ }
+ info = &track->frag_info[track->nb_frag_info - 1];
+ info->offset = avio_tell(pb);
+ // Try to recreate the original pts for the first packet
+ // from the fields we have stored
+ info->time = track->start_dts + track->frag_start +
+ track->cluster[0].cts;
+ // If the pts is less than zero, we will have trimmed
+ // away parts of the media track using an edit list,
+ // and the corresponding start presentation time is zero.
+ if (info->time < 0)
+ info->time = 0;
+ info->duration = track->start_dts + track->track_duration -
+ track->cluster[0].dts;
+ info->tfrf_offset = 0;
+ mov_write_tfrf_tags(pb, mov, track);
+ // If writing all tracks, we currently only add a tfra entry for
+ // the first track (that actually has data to be written).
+ if (tracks < 0)
+ break;
+ }
+ return 0;
+}
+
+static int mov_write_tfdt_tag(AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "tfdt");
+ avio_w8(pb, 1); /* version */
+ avio_wb24(pb, 0);
+ avio_wb64(pb, track->frag_start);
+ return update_size(pb, pos);
+}
+
static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov,
MOVTrack *track, int64_t moof_offset,
int moof_size)
ffio_wfourcc(pb, "traf");
mov_write_tfhd_tag(pb, mov, track, moof_offset);
+ if (mov->mode != MODE_ISM)
+ mov_write_tfdt_tag(pb, track);
mov_write_trun_tag(pb, mov, track, moof_size);
if (mov->mode == MODE_ISM) {
mov_write_tfxd_tag(pb, track);
if (mov->ism_lookahead) {
int i, size = 16 + 4 + 1 + 16 * mov->ism_lookahead;
- track->tfrf_offset = avio_tell(pb);
+ if (track->nb_frag_info > 0) {
+ MOVFragmentInfo *info = &track->frag_info[track->nb_frag_info - 1];
+ if (!info->tfrf_offset)
+ info->tfrf_offset = avio_tell(pb);
+ }
avio_wb32(pb, 8 + size);
ffio_wfourcc(pb, "free");
for (i = 0; i < size; i++)
avio_wb32(pb, 0); /* size placeholder */
ffio_wfourcc(pb, "moof");
+ mov->first_trun = 1;
mov_write_mfhd_tag(pb, mov);
for (i = 0; i < mov->nb_streams; i++) {
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;
return ret;
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;
+
return mov_write_moof_tag_internal(pb, mov, tracks, moof_size);
}
minor = has_h264 ? 0x20000 : 0x10000;
} else if (mov->mode == MODE_PSP)
ffio_wfourcc(pb, "MSNV");
+ else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)
+ ffio_wfourcc(pb, "iso5"); // Required when using default-base-is-moof
else if (mov->mode == MODE_MP4)
ffio_wfourcc(pb, "isom");
else if (mov->mode == MODE_IPOD)
ffio_wfourcc(pb, "qt ");
else if (mov->mode == MODE_ISM) {
ffio_wfourcc(pb, "piff");
- ffio_wfourcc(pb, "iso2");
- } else {
+ } else if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) {
ffio_wfourcc(pb, "isom");
ffio_wfourcc(pb, "iso2");
if (has_h264)
ffio_wfourcc(pb, "avc1");
}
+ // We add tfdt atoms when fragmenting, signal this with the iso6 compatible
+ // brand. This is compatible with users that don't understand tfdt.
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->mode != MODE_ISM)
+ ffio_wfourcc(pb, "iso6");
+
if (mov->mode == MODE_3GP)
ffio_wfourcc(pb, has_h264 ? "3gp6":"3gp4");
else if (mov->mode & MODE_3G2)
}
if (write_moof) {
- MOVFragmentInfo *info;
avio_flush(s->pb);
- track->nb_frag_info++;
- if (track->nb_frag_info >= track->frag_info_capacity) {
- unsigned new_capacity = track->nb_frag_info + MOV_FRAG_INFO_ALLOC_INCREMENT;
- if (av_reallocp_array(&track->frag_info,
- new_capacity,
- sizeof(*track->frag_info)))
- return AVERROR(ENOMEM);
- track->frag_info_capacity = new_capacity;
- }
- info = &track->frag_info[track->nb_frag_info - 1];
- info->offset = avio_tell(s->pb);
- info->time = mov->tracks[i].frag_start;
- info->duration = duration;
- mov_write_tfrf_tags(s->pb, mov, track);
-
- mov_write_moof_tag(s->pb, mov, moof_tracks);
- info->tfrf_offset = track->tfrf_offset;
+
+ mov_write_moof_tag(s->pb, mov, moof_tracks, mdat_size);
mov->fragments++;
avio_wb32(s->pb, mdat_size + 8);
MOVTrack *trk = &mov->tracks[pkt->stream_index];
AVCodecContext *enc = trk->enc;
unsigned int samples_in_chunk = 0;
- int size = pkt->size;
+ int size = pkt->size, ret = 0;
uint8_t *reformatted_data = NULL;
if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
/* copy frame to create needed atoms */
trk->vos_len = size;
trk->vos_data = av_malloc(size);
- if (!trk->vos_data)
- return AVERROR(ENOMEM);
+ if (!trk->vos_data) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
memcpy(trk->vos_data, pkt->data, size);
}
if (trk->entry >= trk->cluster_capacity) {
unsigned new_capacity = 2 * (trk->entry + MOV_INDEX_CLUSTER_SIZE);
if (av_reallocp_array(&trk->cluster, new_capacity,
- sizeof(*trk->cluster)))
- return AVERROR(ENOMEM);
+ sizeof(*trk->cluster))) {
+ ret = AVERROR(ENOMEM);
+ goto err;
+ }
trk->cluster_capacity = new_capacity;
}
* of this packet to be what the previous packets duration implies. */
trk->cluster[trk->entry].dts = trk->start_dts + trk->track_duration;
}
- if (trk->start_dts == AV_NOPTS_VALUE)
+ if (!trk->entry && trk->start_dts == AV_NOPTS_VALUE && !mov->use_editlist &&
+ s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) {
+ /* Not using edit lists and shifting the first track to start from zero.
+ * If the other streams start from a later timestamp, we won't be able
+ * to signal the difference in starting time without an edit list.
+ * Thus move the timestamp for this first sample to 0, increasing
+ * its duration instead. */
+ trk->cluster[trk->entry].dts = trk->start_dts = 0;
+ }
+ if (trk->start_dts == AV_NOPTS_VALUE) {
trk->start_dts = pkt->dts;
+ 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 "
+ "empty_moov.\n", pkt->stream_index, pkt->dts);
+ }
trk->track_duration = pkt->dts - trk->start_dts + pkt->duration;
if (pkt->pts == AV_NOPTS_VALUE) {
if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams)
ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry,
reformatted_data, size);
+
+err:
av_free(reformatted_data);
- return 0;
+ return ret;
}
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
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) {
- if ((mov->flags & FF_MOV_FLAG_FRAGMENT) ||
- (s->flags & AVFMT_FLAG_CUSTOM_IO)) {
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
av_log(s, AV_LOG_WARNING, "The faststart flag is incompatible "
- "with fragmentation and custom IO, disabling faststart\n");
+ "with fragmentation, disabling faststart\n");
mov->flags &= ~FF_MOV_FLAG_FASTSTART;
}
}
+ if (mov->use_editlist < 0) {
+ mov->use_editlist = 1;
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
+ // If we can avoid needing an edit list by shifting the
+ // tracks, prefer that over (trying to) write edit lists
+ // in fragmented output.
+ if (s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO ||
+ s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO)
+ mov->use_editlist = 0;
+ }
+ }
+ if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && mov->use_editlist)
+ av_log(s, AV_LOG_WARNING, "No meaningful edit list will be written when using empty_moov\n");
+
+ if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO)
+ s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO;
+
/* 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 &&
av_log(s, AV_LOG_INFO, "Starting second pass: moving the moov atom to the beginning of the file\n");
res = shift_data(s);
if (res == 0) {
- avio_seek(s->pb, mov->reserved_moov_pos, SEEK_SET);
+ avio_seek(pb, mov->reserved_moov_pos, SEEK_SET);
mov_write_moov_tag(pb, mov, s);
}
} else {