|
|
@@ -55,6 +55,7 @@ static const AVOption options[] = { |
|
|
|
{ "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.dbl = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, |
|
|
|
{ "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, |
|
|
|
{ "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.dbl = 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, {.dbl = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, |
|
|
|
{ NULL }, |
|
|
|
}; |
|
|
|
|
|
|
@@ -761,7 +762,7 @@ static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) |
|
|
|
{ |
|
|
|
int tag = track->enc->codec_tag; |
|
|
|
|
|
|
|
if (track->mode == MODE_MP4 || track->mode == MODE_PSP) |
|
|
|
if (track->mode == MODE_MP4 || track->mode == MODE_PSP || track->mode == MODE_ISM) |
|
|
|
tag = mp4_get_codec_tag(s, track); |
|
|
|
else if (track->mode == MODE_IPOD) |
|
|
|
tag = ipod_get_codec_tag(s, track); |
|
|
@@ -1940,6 +1941,11 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVTrack *track, |
|
|
|
flags |= 0x20; /* default-sample-flags-present */ |
|
|
|
} |
|
|
|
|
|
|
|
/* Don't set a default sample size when creating data for silverlight, |
|
|
|
* the player refuses to play files with that set. */ |
|
|
|
if (track->mode == MODE_ISM) |
|
|
|
flags &= ~0x10; |
|
|
|
|
|
|
|
avio_wb32(pb, 0); /* size placeholder */ |
|
|
|
ffio_wfourcc(pb, "tfhd"); |
|
|
|
avio_w8(pb, 0); /* version */ |
|
|
@@ -2023,7 +2029,78 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVTrack *track) |
|
|
|
return updateSize(pb, pos); |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_offset) |
|
|
|
static int mov_write_tfxd_tag(AVIOContext *pb, MOVTrack *track) |
|
|
|
{ |
|
|
|
int64_t pos = avio_tell(pb); |
|
|
|
const uint8_t uuid[] = { |
|
|
|
0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6, |
|
|
|
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2 |
|
|
|
}; |
|
|
|
|
|
|
|
avio_wb32(pb, 0); /* size placeholder */ |
|
|
|
ffio_wfourcc(pb, "uuid"); |
|
|
|
avio_write(pb, uuid, sizeof(uuid)); |
|
|
|
avio_w8(pb, 1); |
|
|
|
avio_wb24(pb, 0); |
|
|
|
avio_wb64(pb, track->frag_start); |
|
|
|
avio_wb64(pb, track->start_dts + track->trackDuration - |
|
|
|
track->cluster[0].dts); |
|
|
|
|
|
|
|
return updateSize(pb, pos); |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_tfrf_tag(AVIOContext *pb, MOVMuxContext *mov, |
|
|
|
MOVTrack *track, int entry) |
|
|
|
{ |
|
|
|
int n = track->nb_frag_info - 1 - entry, i; |
|
|
|
int size = 8 + 16 + 4 + 1 + 16*n; |
|
|
|
const uint8_t uuid[] = { |
|
|
|
0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95, |
|
|
|
0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f |
|
|
|
}; |
|
|
|
|
|
|
|
if (entry < 0) |
|
|
|
return 0; |
|
|
|
|
|
|
|
avio_seek(pb, track->frag_info[entry].tfrf_offset, SEEK_SET); |
|
|
|
avio_wb32(pb, size); |
|
|
|
ffio_wfourcc(pb, "uuid"); |
|
|
|
avio_write(pb, uuid, sizeof(uuid)); |
|
|
|
avio_w8(pb, 1); |
|
|
|
avio_wb24(pb, 0); |
|
|
|
avio_w8(pb, n); |
|
|
|
for (i = 0; i < n; i++) { |
|
|
|
int index = entry + 1 + i; |
|
|
|
avio_wb64(pb, track->frag_info[index].time); |
|
|
|
avio_wb64(pb, track->frag_info[index].duration); |
|
|
|
} |
|
|
|
if (n < mov->ism_lookahead) { |
|
|
|
int free_size = 16*(mov->ism_lookahead - n); |
|
|
|
avio_wb32(pb, free_size); |
|
|
|
ffio_wfourcc(pb, "free"); |
|
|
|
for (i = 0; i < free_size - 8; i++) |
|
|
|
avio_w8(pb, 0); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_tfrf_tags(AVIOContext *pb, MOVMuxContext *mov, |
|
|
|
MOVTrack *track) |
|
|
|
{ |
|
|
|
int64_t pos = avio_tell(pb); |
|
|
|
int i; |
|
|
|
for (i = 0; i < mov->ism_lookahead; i++) { |
|
|
|
/* Update the tfrf tag for the last ism_lookahead fragments, |
|
|
|
* nb_frag_info - 1 is the next fragment to be written. */ |
|
|
|
mov_write_tfrf_tag(pb, mov, track, track->nb_frag_info - 2 - i); |
|
|
|
} |
|
|
|
avio_seek(pb, pos, SEEK_SET); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_traf_tag(AVIOContext *pb, MOVMuxContext *mov, |
|
|
|
MOVTrack *track, int64_t moof_offset) |
|
|
|
{ |
|
|
|
int64_t pos = avio_tell(pb); |
|
|
|
avio_wb32(pb, 0); /* size placeholder */ |
|
|
@@ -2031,6 +2108,19 @@ static int mov_write_traf_tag(AVIOContext *pb, MOVTrack *track, int64_t moof_off |
|
|
|
|
|
|
|
mov_write_tfhd_tag(pb, track, moof_offset); |
|
|
|
mov_write_trun_tag(pb, track); |
|
|
|
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); |
|
|
|
avio_wb32(pb, 8 + size); |
|
|
|
ffio_wfourcc(pb, "free"); |
|
|
|
for (i = 0; i < size; i++) |
|
|
|
avio_w8(pb, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return updateSize(pb, pos); |
|
|
|
} |
|
|
@@ -2050,7 +2140,7 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) |
|
|
|
continue; |
|
|
|
if (!track->entry) |
|
|
|
continue; |
|
|
|
mov_write_traf_tag(pb, track, pos); |
|
|
|
mov_write_traf_tag(pb, mov, track, pos); |
|
|
|
} |
|
|
|
|
|
|
|
end = avio_tell(pb); |
|
|
@@ -2158,6 +2248,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) |
|
|
|
ffio_wfourcc(pb, "isom"); |
|
|
|
else if (mov->mode == MODE_IPOD) |
|
|
|
ffio_wfourcc(pb, has_video ? "M4V ":"M4A "); |
|
|
|
else if (mov->mode == MODE_ISM) |
|
|
|
ffio_wfourcc(pb, "isml"); |
|
|
|
else |
|
|
|
ffio_wfourcc(pb, "qt "); |
|
|
|
|
|
|
@@ -2165,7 +2257,10 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) |
|
|
|
|
|
|
|
if(mov->mode == MODE_MOV) |
|
|
|
ffio_wfourcc(pb, "qt "); |
|
|
|
else{ |
|
|
|
else if (mov->mode == MODE_ISM) { |
|
|
|
ffio_wfourcc(pb, "piff"); |
|
|
|
ffio_wfourcc(pb, "iso2"); |
|
|
|
} else { |
|
|
|
ffio_wfourcc(pb, "isom"); |
|
|
|
ffio_wfourcc(pb, "iso2"); |
|
|
|
if(has_h264) |
|
|
@@ -2342,8 +2437,11 @@ static int mov_flush_fragment(AVFormatContext *s) |
|
|
|
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->fragments++; |
|
|
|
|
|
|
|
avio_wb32(s->pb, mdat_size + 8); |
|
|
@@ -2571,6 +2669,7 @@ static int mov_write_header(AVFormatContext *s) |
|
|
|
else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV; |
|
|
|
else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP; |
|
|
|
else if (!strcmp("ipod",s->oformat->name)) mov->mode = MODE_IPOD; |
|
|
|
else if (!strcmp("ismv",s->oformat->name)) mov->mode = MODE_ISM; |
|
|
|
|
|
|
|
mov_write_ftyp_tag(pb,s); |
|
|
|
if (mov->mode == MODE_PSP) { |
|
|
@@ -2681,6 +2780,10 @@ static int mov_write_header(AVFormatContext *s) |
|
|
|
} |
|
|
|
if (!track->height) |
|
|
|
track->height = st->codec->height; |
|
|
|
/* The ism specific timescale isn't mandatory, but is assumed by |
|
|
|
* some tools, such as mp4split. */ |
|
|
|
if (mov->mode == MODE_ISM) |
|
|
|
track->timescale = 10000000; |
|
|
|
|
|
|
|
avpriv_set_pts_info(st, 64, 1, track->timescale); |
|
|
|
|
|
|
@@ -2692,6 +2795,15 @@ static int mov_write_header(AVFormatContext *s) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (mov->mode == MODE_ISM) { |
|
|
|
/* If no fragmentation options have been set, set a default. */ |
|
|
|
if (!(mov->flags & (FF_MOV_FLAG_FRAG_KEYFRAME | |
|
|
|
FF_MOV_FLAG_FRAG_CUSTOM)) && |
|
|
|
!mov->max_fragment_duration && !mov->max_fragment_size) |
|
|
|
mov->max_fragment_duration = 5000000; |
|
|
|
mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF; |
|
|
|
} |
|
|
|
|
|
|
|
/* Set the FRAGMENT flag if any of the fragmentation methods are |
|
|
|
* enabled. */ |
|
|
|
if (mov->max_fragment_duration || mov->max_fragment_size || |
|
|
@@ -2906,3 +3018,21 @@ AVOutputFormat ff_ipod_muxer = { |
|
|
|
.priv_class = &ipod_muxer_class, |
|
|
|
}; |
|
|
|
#endif |
|
|
|
#if CONFIG_ISMV_MUXER |
|
|
|
MOV_CLASS(ismv) |
|
|
|
AVOutputFormat ff_ismv_muxer = { |
|
|
|
.name = "ismv", |
|
|
|
.long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming) format"), |
|
|
|
.mime_type = "application/mp4", |
|
|
|
.extensions = "ismv,isma", |
|
|
|
.priv_data_size = sizeof(MOVMuxContext), |
|
|
|
.audio_codec = CODEC_ID_AAC, |
|
|
|
.video_codec = CODEC_ID_H264, |
|
|
|
.write_header = mov_write_header, |
|
|
|
.write_packet = ff_mov_write_packet, |
|
|
|
.write_trailer = mov_write_trailer, |
|
|
|
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, |
|
|
|
.codec_tag = (const AVCodecTag* const []){ff_mp4_obj_type, 0}, |
|
|
|
.priv_class = &ismv_muxer_class, |
|
|
|
}; |
|
|
|
#endif |