Commit 40ed1cbf authored by Martin Storsjö's avatar Martin Storsjö

movenc: Allow writing a DASH sidx atom at the start of files

This is mapped to the faststart flag (which in this case
perhaps should be called "shift and write index at the
start of the file"), which for fragmented files will
write a sidx index at the start.

When segmenting DASH into files, there's usually one sidx
at the start of each segment (although it's not clear to me
whether that actually is necessary). When storing all of it
in one file, the MPD doesn't necessarily need to describe
the individual segments, but the offsets of the fragments can be
fetched from one large sidx atom at the start of the file. This
allows creating files for the DASH ISO BMFF on-demand profile.
Signed-off-by: 's avatarMartin Storsjö <martin@martin.st>
parent 3847f3ab
...@@ -2568,7 +2568,8 @@ static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov, ...@@ -2568,7 +2568,8 @@ static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov,
return 0; return 0;
} }
static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks) static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks,
int size)
{ {
int i; int i;
for (i = 0; i < mov->nb_streams; i++) { for (i = 0; i < mov->nb_streams; i++) {
...@@ -2587,6 +2588,7 @@ static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks) ...@@ -2587,6 +2588,7 @@ static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks)
} }
info = &track->frag_info[track->nb_frag_info - 1]; info = &track->frag_info[track->nb_frag_info - 1];
info->offset = avio_tell(pb); info->offset = avio_tell(pb);
info->size = size;
// Try to recreate the original pts for the first packet // Try to recreate the original pts for the first packet
// from the fields we have stored // from the fields we have stored
info->time = track->start_dts + track->frag_start + info->time = track->start_dts + track->frag_start +
...@@ -2676,12 +2678,20 @@ static int mov_write_sidx_tag(AVIOContext *pb, ...@@ -2676,12 +2678,20 @@ static int mov_write_sidx_tag(AVIOContext *pb,
MOVTrack *track, int ref_size, int total_sidx_size) MOVTrack *track, int ref_size, int total_sidx_size)
{ {
int64_t pos = avio_tell(pb), offset_pos, end_pos; int64_t pos = avio_tell(pb), offset_pos, end_pos;
int64_t presentation_time = track->start_dts + track->frag_start + int64_t presentation_time, duration, offset;
track->cluster[0].cts; int starts_with_SAP, i, entries;
int64_t duration = track->start_dts + track->track_duration -
track->cluster[0].dts; if (track->entry) {
int64_t offset; entries = 1;
int starts_with_SAP = track->cluster[0].flags & MOV_SYNC_SAMPLE; presentation_time = track->start_dts + track->frag_start +
track->cluster[0].cts;
duration = track->start_dts + track->track_duration -
track->cluster[0].dts;
starts_with_SAP = track->cluster[0].flags & MOV_SYNC_SAMPLE;
} else {
entries = track->nb_frag_info;
presentation_time = track->frag_info[0].time;
}
// pts<0 should be cut away using edts // pts<0 should be cut away using edts
if (presentation_time < 0) if (presentation_time < 0)
...@@ -2697,10 +2707,21 @@ static int mov_write_sidx_tag(AVIOContext *pb, ...@@ -2697,10 +2707,21 @@ static int mov_write_sidx_tag(AVIOContext *pb,
offset_pos = avio_tell(pb); offset_pos = avio_tell(pb);
avio_wb64(pb, 0); /* first_offset (offset to referenced moof) */ avio_wb64(pb, 0); /* first_offset (offset to referenced moof) */
avio_wb16(pb, 0); /* reserved */ 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_wb16(pb, entries); /* reference_count */
avio_wb32(pb, duration); /* subsegment_duration */ for (i = 0; i < entries; i++) {
avio_wb32(pb, (starts_with_SAP << 31) | (0 << 28) | 0); /* starts_with_SAP | SAP_type | SAP_delta_time */ if (!track->entry) {
if (i > 1 && track->frag_info[i].offset != track->frag_info[i - 1].offset + track->frag_info[i - 1].size) {
av_log(NULL, AV_LOG_ERROR, "Non-consecutive fragments, writing incorrect sidx\n");
}
duration = track->frag_info[i].duration;
ref_size = track->frag_info[i].size;
starts_with_SAP = 1;
}
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); end_pos = avio_tell(pb);
offset = pos + total_sidx_size - end_pos; offset = pos + total_sidx_size - end_pos;
...@@ -2731,7 +2752,10 @@ static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov, ...@@ -2731,7 +2752,10 @@ static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov,
MOVTrack *track = &mov->tracks[i]; MOVTrack *track = &mov->tracks[i];
if (tracks >= 0 && i != tracks) if (tracks >= 0 && i != tracks)
continue; continue;
if (!track->entry) // When writing a sidx for the full file, entry is 0, but
// we want to include all tracks. ref_size is 0 in this case,
// since we read it from frag_info instead.
if (!track->entry && ref_size > 0)
continue; continue;
total_size -= mov_write_sidx_tag(avio_buf, track, ref_size, total_size -= mov_write_sidx_tag(avio_buf, track, ref_size,
total_size); total_size);
...@@ -2753,10 +2777,10 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, ...@@ -2753,10 +2777,10 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks,
mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); mov_write_moof_tag_internal(avio_buf, mov, tracks, 0);
moof_size = ffio_close_null_buf(avio_buf); moof_size = ffio_close_null_buf(avio_buf);
if (mov->flags & FF_MOV_FLAG_DASH) if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_FASTSTART))
mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size);
if ((ret = mov_add_tfra_entries(pb, mov, tracks)) < 0) if ((ret = mov_add_tfra_entries(pb, mov, tracks, moof_size + 8 + mdat_size)) < 0)
return ret; return ret;
return mov_write_moof_tag_internal(pb, mov, tracks, moof_size); return mov_write_moof_tag_internal(pb, mov, tracks, moof_size);
...@@ -2777,7 +2801,7 @@ static int mov_write_tfra_tag(AVIOContext *pb, MOVTrack *track) ...@@ -2777,7 +2801,7 @@ static int mov_write_tfra_tag(AVIOContext *pb, MOVTrack *track)
avio_wb32(pb, track->nb_frag_info); avio_wb32(pb, track->nb_frag_info);
for (i = 0; i < track->nb_frag_info; i++) { for (i = 0; i < track->nb_frag_info; i++) {
avio_wb64(pb, track->frag_info[i].time); avio_wb64(pb, track->frag_info[i].time);
avio_wb64(pb, track->frag_info[i].offset); avio_wb64(pb, track->frag_info[i].offset + track->data_offset);
avio_w8(pb, 1); /* traf number */ avio_w8(pb, 1); /* traf number */
avio_w8(pb, 1); /* trun number */ avio_w8(pb, 1); /* trun number */
avio_w8(pb, 1); /* sample number */ avio_w8(pb, 1); /* sample number */
...@@ -2892,6 +2916,10 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) ...@@ -2892,6 +2916,10 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
ffio_wfourcc(pb, "MSNV"); ffio_wfourcc(pb, "MSNV");
else if (mov->mode == MODE_MP4) else if (mov->mode == MODE_MP4)
ffio_wfourcc(pb, "mp41"); ffio_wfourcc(pb, "mp41");
if (mov->flags & FF_MOV_FLAG_DASH && mov->flags & FF_MOV_FLAG_FASTSTART)
ffio_wfourcc(pb, "dash");
return update_size(pb, pos); return update_size(pb, pos);
} }
...@@ -3579,15 +3607,6 @@ static int mov_write_header(AVFormatContext *s) ...@@ -3579,15 +3607,6 @@ static int mov_write_header(AVFormatContext *s)
mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV |
FF_MOV_FLAG_DEFAULT_BASE_MOOF; 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) {
av_log(s, AV_LOG_WARNING, "The faststart flag is incompatible "
"with fragmentation, disabling faststart\n");
mov->flags &= ~FF_MOV_FLAG_FASTSTART;
}
}
if (mov->use_editlist < 0) { if (mov->use_editlist < 0) {
mov->use_editlist = 1; mov->use_editlist = 1;
if (mov->flags & FF_MOV_FLAG_FRAGMENT) { if (mov->flags & FF_MOV_FLAG_FRAGMENT) {
...@@ -3772,6 +3791,8 @@ static int mov_write_header(AVFormatContext *s) ...@@ -3772,6 +3791,8 @@ static int mov_write_header(AVFormatContext *s)
if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV) {
mov_write_moov_tag(pb, mov, s); mov_write_moov_tag(pb, mov, s);
mov->fragments++; mov->fragments++;
if (mov->flags & FF_MOV_FLAG_FASTSTART)
mov->reserved_moov_pos = avio_tell(pb);
} }
return 0; return 0;
...@@ -3792,6 +3813,18 @@ static int get_moov_size(AVFormatContext *s) ...@@ -3792,6 +3813,18 @@ static int get_moov_size(AVFormatContext *s)
return ffio_close_null_buf(moov_buf); return ffio_close_null_buf(moov_buf);
} }
static int get_sidx_size(AVFormatContext *s)
{
int ret;
AVIOContext *buf;
MOVMuxContext *mov = s->priv_data;
if ((ret = ffio_open_null_buf(&buf)) < 0)
return ret;
mov_write_sidx_tags(buf, mov, -1, 0);
return ffio_close_null_buf(buf);
}
/* /*
* This function gets the moov size if moved to the top of the file: the chunk * 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 * offset table can switch between stco (32-bit entries) to co64 (64-bit
...@@ -3823,6 +3856,21 @@ static int compute_moov_size(AVFormatContext *s) ...@@ -3823,6 +3856,21 @@ static int compute_moov_size(AVFormatContext *s)
return moov_size2; return moov_size2;
} }
static int compute_sidx_size(AVFormatContext *s)
{
int i, sidx_size;
MOVMuxContext *mov = s->priv_data;
sidx_size = get_sidx_size(s);
if (sidx_size < 0)
return sidx_size;
for (i = 0; i < mov->nb_streams; i++)
mov->tracks[i].data_offset += sidx_size;
return sidx_size;
}
static int shift_data(AVFormatContext *s) static int shift_data(AVFormatContext *s)
{ {
int ret = 0, moov_size; int ret = 0, moov_size;
...@@ -3833,7 +3881,10 @@ static int shift_data(AVFormatContext *s) ...@@ -3833,7 +3881,10 @@ static int shift_data(AVFormatContext *s)
int read_size[2]; int read_size[2];
AVIOContext *read_pb; AVIOContext *read_pb;
moov_size = compute_moov_size(s); if (mov->flags & FF_MOV_FLAG_FRAGMENT)
moov_size = compute_sidx_size(s);
else
moov_size = compute_moov_size(s);
if (moov_size < 0) if (moov_size < 0)
return moov_size; return moov_size;
...@@ -3935,7 +3986,21 @@ static int mov_write_trailer(AVFormatContext *s) ...@@ -3935,7 +3986,21 @@ static int mov_write_trailer(AVFormatContext *s)
} }
} else { } else {
mov_flush_fragment(s); mov_flush_fragment(s);
mov_write_mfra_tag(pb, mov); for (i = 0; i < mov->nb_streams; i++)
mov->tracks[i].data_offset = 0;
if (mov->flags & FF_MOV_FLAG_FASTSTART) {
av_log(s, AV_LOG_INFO, "Starting second pass: inserting sidx atoms\n");
res = shift_data(s);
if (res == 0) {
int64_t end = avio_tell(pb);
avio_seek(pb, mov->reserved_moov_pos, SEEK_SET);
mov_write_sidx_tags(pb, mov, -1, 0);
avio_seek(pb, end, SEEK_SET);
mov_write_mfra_tag(pb, mov);
}
} else {
mov_write_mfra_tag(pb, mov);
}
} }
for (i = 0; i < mov->nb_streams; i++) { for (i = 0; i < mov->nb_streams; i++) {
......
...@@ -73,6 +73,7 @@ typedef struct MOVFragmentInfo { ...@@ -73,6 +73,7 @@ typedef struct MOVFragmentInfo {
int64_t time; int64_t time;
int64_t duration; int64_t duration;
int64_t tfrf_offset; int64_t tfrf_offset;
int size;
} MOVFragmentInfo; } MOVFragmentInfo;
typedef struct MOVTrack { typedef struct MOVTrack {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment