|
@@ -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) |
|
|
} |
|
|
} |
|
|
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, |
|
|
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 + |
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
int64_t presentation_time, duration, offset; |
|
|
|
|
|
int starts_with_SAP, i, entries; |
|
|
|
|
|
|
|
|
|
|
|
if (track->entry) { |
|
|
|
|
|
entries = 1; |
|
|
|
|
|
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, |
|
|
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_wb32(pb, duration); /* subsegment_duration */ |
|
|
|
|
|
avio_wb32(pb, (starts_with_SAP << 31) | (0 << 28) | 0); /* starts_with_SAP | SAP_type | SAP_delta_time */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
avio_wb16(pb, entries); /* reference_count */ |
|
|
|
|
|
for (i = 0; i < entries; i++) { |
|
|
|
|
|
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, |
|
|
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, |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
} |
|
|
} |
|
|
} 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++) { |
|
|