X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=85df5d1374a6a07bfa5cb116b6b4b8349cf8c2f0;hb=9086af2a0a590c7f576b72379d1708392cd96d5c;hp=adaff4c5e95ccec75d0792aedd39571985b75ba0;hpb=d9a91d58a17ff485a02cd63581ba6f528315c767;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index adaff4c5e95..85df5d1374a 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -72,11 +72,13 @@ static const AVOption options[] = { { "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" }, + { "cmaf", "Write CMAF compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_CMAF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, + { "prefer_icc", "If writing colr atom prioritise usage of ICC profile if it exists in stream packet side data", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_PREFER_ICC}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "skip_trailer", "Skip writing the mfra/tfra/mfro trailer for fragmented files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_TRAILER}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -313,15 +315,19 @@ static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track) return 0x11; } -static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_ac3_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { GetBitContext gbc; PutBitContext pbc; uint8_t buf[3]; int fscod, bsid, bsmod, acmod, lfeon, frmsizecod; - if (track->vos_len < 7) - return -1; + if (track->vos_len < 7) { + av_log(s, AV_LOG_ERROR, + "Cannot write moov atom before AC3 packets." + " Set the delay_moov flag to fix this.\n"); + return AVERROR(EINVAL); + } avio_wb32(pb, 11); ffio_wfourcc(pb, "dac3"); @@ -531,22 +537,24 @@ end: } #endif -static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_eac3_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { PutBitContext pbc; uint8_t *buf; struct eac3_info *info; int size, i; - if (!track->eac3_priv) + if (!track->eac3_priv) { + av_log(s, AV_LOG_ERROR, + "Cannot write moov atom before EAC3 packets parsed.\n"); return AVERROR(EINVAL); + } info = track->eac3_priv; size = 2 + ((34 * (info->num_ind_sub + 1) + 7) >> 3); buf = av_malloc(size); if (!buf) { - size = AVERROR(ENOMEM); - goto end; + return AVERROR(ENOMEM); } init_put_bits(&pbc, buf, size); @@ -577,10 +585,6 @@ static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track) av_free(buf); -end: - av_packet_unref(&info->pkt); - av_freep(&track->eac3_priv); - return size; } @@ -746,14 +750,14 @@ static int mov_write_dfla_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } -static int mov_write_dops_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_dops_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); avio_wb32(pb, 0); ffio_wfourcc(pb, "dOps"); avio_w8(pb, 0); /* Version */ if (track->par->extradata_size < 19) { - av_log(pb, AV_LOG_ERROR, "invalid extradata size\n"); + av_log(s, AV_LOG_ERROR, "invalid extradata size\n"); return AVERROR_INVALIDDATA; } /* extradata contains an Ogg OpusHead, other than byte-ordering and @@ -769,6 +773,35 @@ static int mov_write_dops_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_dmlp_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int length; + avio_wb32(pb, 0); + ffio_wfourcc(pb, "dmlp"); + + if (track->vos_len < 20) { + av_log(s, AV_LOG_ERROR, + "Cannot write moov atom before TrueHD packets." + " Set the delay_moov flag to fix this.\n"); + return AVERROR(EINVAL); + } + + length = (AV_RB16(track->vos_data) & 0xFFF) * 2; + if (length < 20 || length > track->vos_len) + return AVERROR_INVALIDDATA; + + // Only TrueHD is supported + if (AV_RB32(track->vos_data + 4) != 0xF8726FBA) + return AVERROR_INVALIDDATA; + + avio_wb32(pb, AV_RB32(track->vos_data + 8)); /* format_info */ + avio_wb16(pb, AV_RB16(track->vos_data + 18) << 1); /* peak_data_rate */ + avio_wb32(pb, 0); /* reserved */ + + return update_size(pb, pos); +} + static int mov_write_chan_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { uint32_t layout_tag, bitmap; @@ -823,9 +856,9 @@ static int mov_write_wave_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra } else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) { mov_write_amr_tag(pb, track); } else if (track->par->codec_id == AV_CODEC_ID_AC3) { - mov_write_ac3_tag(pb, track); + mov_write_ac3_tag(s, pb, track); } else if (track->par->codec_id == AV_CODEC_ID_EAC3) { - mov_write_eac3_tag(pb, track); + mov_write_eac3_tag(s, pb, track); } else if (track->par->codec_id == AV_CODEC_ID_ALAC || track->par->codec_id == AV_CODEC_ID_QDM2) { mov_write_extradata_tag(pb, track); @@ -1022,6 +1055,7 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex int64_t pos = avio_tell(pb); int version = 0; uint32_t tag = track->tag; + int ret = 0; if (track->mode == MODE_MOV) { if (track->timescale > UINT16_MAX || !track->par->channels) { @@ -1097,10 +1131,14 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex avio_wb16(pb, 0); /* packet size (= 0) */ if (track->par->codec_id == AV_CODEC_ID_OPUS) avio_wb16(pb, 48000); + else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) + avio_wb32(pb, track->par->sample_rate); else avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ? track->par->sample_rate : 0); - avio_wb16(pb, 0); /* Reserved */ + + if (track->par->codec_id != AV_CODEC_ID_TRUEHD) + avio_wb16(pb, 0); /* Reserved */ } if (version == 1) { /* SoundDescription V1 extended info */ @@ -1125,34 +1163,43 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex track->par->codec_id == AV_CODEC_ID_QDM2 || (mov_pcm_le_gt16(track->par->codec_id) && version==1) || (mov_pcm_be_gt16(track->par->codec_id) && version==1))) - mov_write_wave_tag(s, pb, track); + ret = mov_write_wave_tag(s, pb, track); else if (track->tag == MKTAG('m','p','4','a')) - mov_write_esds_tag(pb, track); + ret = mov_write_esds_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) - mov_write_amr_tag(pb, track); + ret = mov_write_amr_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_AC3) - mov_write_ac3_tag(pb, track); + ret = mov_write_ac3_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_EAC3) - mov_write_eac3_tag(pb, track); + ret = mov_write_eac3_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_ALAC) - mov_write_extradata_tag(pb, track); + ret = mov_write_extradata_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_WMAPRO) - mov_write_wfex_tag(s, pb, track); + ret = mov_write_wfex_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_FLAC) - mov_write_dfla_tag(pb, track); + ret = mov_write_dfla_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_OPUS) - mov_write_dops_tag(pb, track); + ret = mov_write_dops_tag(s, pb, track); + else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) + ret = mov_write_dmlp_tag(s, pb, track); else if (track->vos_len > 0) - mov_write_glbl_tag(pb, track); + ret = mov_write_glbl_tag(pb, track); - if (track->mode == MODE_MOV && track->par->codec_type == AVMEDIA_TYPE_AUDIO) - mov_write_chan_tag(s, pb, track); + if (ret < 0) + return ret; - if (mov->encryption_scheme != MOV_ENC_NONE) { - ff_mov_cenc_write_sinf_tag(track, pb, mov->encryption_kid); + if (track->mode == MODE_MOV && track->par->codec_type == AVMEDIA_TYPE_AUDIO + && ((ret = mov_write_chan_tag(s, pb, track)) < 0)) { + return ret; } - return update_size(pb, pos); + if (mov->encryption_scheme != MOV_ENC_NONE + && ((ret = ff_mov_cenc_write_sinf_tag(track, pb, mov->encryption_kid)) < 0)) { + return ret; + } + + ret = update_size(pb, pos); + return ret; } static int mov_write_d263_tag(AVIOContext *pb) @@ -1681,12 +1728,12 @@ static int mov_write_subtitle_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } -static int mov_write_st3d_tag(AVIOContext *pb, AVStereo3D *stereo_3d) +static int mov_write_st3d_tag(AVFormatContext *s, AVIOContext *pb, AVStereo3D *stereo_3d) { int8_t stereo_mode; if (stereo_3d->flags != 0) { - av_log(pb, AV_LOG_WARNING, "Unsupported stereo_3d flags %x. st3d not written.\n", stereo_3d->flags); + av_log(s, AV_LOG_WARNING, "Unsupported stereo_3d flags %x. st3d not written.\n", stereo_3d->flags); return 0; } @@ -1701,7 +1748,7 @@ static int mov_write_st3d_tag(AVIOContext *pb, AVStereo3D *stereo_3d) stereo_mode = 2; break; default: - av_log(pb, AV_LOG_WARNING, "Unsupported stereo_3d type %s. st3d not written.\n", av_stereo3d_type_name(stereo_3d->type)); + av_log(s, AV_LOG_WARNING, "Unsupported stereo_3d type %s. st3d not written.\n", av_stereo3d_type_name(stereo_3d->type)); return 0; } avio_wb32(pb, 13); /* size */ @@ -1719,7 +1766,7 @@ static int mov_write_sv3d_tag(AVFormatContext *s, AVIOContext *pb, AVSphericalMa if (spherical_mapping->projection != AV_SPHERICAL_EQUIRECTANGULAR && spherical_mapping->projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE && spherical_mapping->projection != AV_SPHERICAL_CUBEMAP) { - av_log(pb, AV_LOG_WARNING, "Unsupported projection %d. sv3d not written.\n", spherical_mapping->projection); + av_log(s, AV_LOG_WARNING, "Unsupported projection %d. sv3d not written.\n", spherical_mapping->projection); return 0; } @@ -1797,36 +1844,54 @@ static int mov_write_pasp_tag(AVIOContext *pb, MOVTrack *track) return 16; } -static int mov_write_gama_tag(AVIOContext *pb, MOVTrack *track, double gamma) +static int mov_write_gama_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track, double gamma) { uint32_t gama = 0; - if (gamma <= 0.0) - { + if (gamma <= 0.0) { gamma = avpriv_get_gamma_from_trc(track->par->color_trc); } - av_log(pb, AV_LOG_DEBUG, "gamma value %g\n", gamma); + av_log(s, AV_LOG_DEBUG, "gamma value %g\n", gamma); if (gamma > 1e-6) { gama = (uint32_t)lrint((double)(1<<16) * gamma); - av_log(pb, AV_LOG_DEBUG, "writing gama value %"PRId32"\n", gama); + av_log(s, AV_LOG_DEBUG, "writing gama value %"PRId32"\n", gama); av_assert0(track->mode == MODE_MOV); avio_wb32(pb, 12); ffio_wfourcc(pb, "gama"); avio_wb32(pb, gama); return 12; - } - else { - av_log(pb, AV_LOG_WARNING, "gamma value unknown, unable to write gama atom\n"); + } else { + av_log(s, AV_LOG_WARNING, "gamma value unknown, unable to write gama atom\n"); } return 0; } -static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track) +static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) { + int64_t pos = avio_tell(pb); + // Ref (MOV): https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9 // Ref (MP4): ISO/IEC 14496-12:2012 + const uint8_t *icc_profile; + int icc_profile_size; + + if (prefer_icc) { + icc_profile = av_stream_get_side_data(track->st, AV_PKT_DATA_ICC_PROFILE, &icc_profile_size); + + if (icc_profile) { + avio_wb32(pb, 12 + icc_profile_size); + ffio_wfourcc(pb, "colr"); + ffio_wfourcc(pb, "prof"); + avio_write(pb, icc_profile, icc_profile_size); + return 12 + icc_profile_size; + } + else { + av_log(NULL, AV_LOG_INFO, "no ICC profile found, will write nclx/nclc colour info instead\n"); + } + } + if (track->par->color_primaries == AVCOL_PRI_UNSPECIFIED && track->par->color_trc == AVCOL_TRC_UNSPECIFIED && track->par->color_space == AVCOL_SPC_UNSPECIFIED) { @@ -1860,7 +1925,7 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track) /* We should only ever be called by MOV or MP4. */ av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4); - avio_wb32(pb, 18 + (track->mode == MODE_MP4)); + avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "colr"); if (track->mode == MODE_MP4) ffio_wfourcc(pb, "nclx"); @@ -1897,10 +1962,9 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track) if (track->mode == MODE_MP4) { int full_range = track->par->color_range == AVCOL_RANGE_JPEG; avio_w8(pb, full_range << 7); - return 19; - } else { - return 18; } + + return update_size(pb, pos); } static void find_compressor(char * compressor_name, int len, MOVTrack *track) @@ -1931,7 +1995,7 @@ static void find_compressor(char * compressor_name, int len, MOVTrack *track) } } -static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) +static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); char compressor_name[32] = { 0 }; @@ -2066,13 +2130,13 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr if (mov->flags & FF_MOV_FLAG_WRITE_GAMA) { if (track->mode == MODE_MOV) - mov_write_gama_tag(pb, track, mov->gamma); + mov_write_gama_tag(s, pb, track, mov->gamma); else av_log(mov->fc, AV_LOG_WARNING, "Not writing 'gama' atom. Format is not MOV.\n"); } if (mov->flags & FF_MOV_FLAG_WRITE_COLR) { if (track->mode == MODE_MOV || track->mode == MODE_MP4) - mov_write_colr_tag(pb, track); + mov_write_colr_tag(pb, track, mov->flags & FF_MOV_FLAG_PREFER_ICC); else av_log(mov->fc, AV_LOG_WARNING, "Not writing 'colr' atom. Format is not MOV or MP4.\n"); } @@ -2082,7 +2146,7 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr AVSphericalMapping* spherical_mapping = (AVSphericalMapping*)av_stream_get_side_data(track->st, AV_PKT_DATA_SPHERICAL, NULL); if (stereo_3d) - mov_write_st3d_tag(pb, stereo_3d); + mov_write_st3d_tag(s, pb, stereo_3d); if (spherical_mapping) mov_write_sv3d_tag(mov->fc, pb, spherical_mapping); } @@ -2217,22 +2281,27 @@ static int mov_write_gpmd_tag(AVIOContext *pb, const MOVTrack *track) static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); + int ret = 0; avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "stsd"); avio_wb32(pb, 0); /* version & flags */ avio_wb32(pb, 1); /* entry count */ if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) - mov_write_video_tag(pb, mov, track); + ret = mov_write_video_tag(s, pb, mov, track); else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) - mov_write_audio_tag(s, pb, mov, track); + ret = mov_write_audio_tag(s, pb, mov, track); else if (track->par->codec_type == AVMEDIA_TYPE_SUBTITLE) - mov_write_subtitle_tag(pb, track); + ret = mov_write_subtitle_tag(pb, track); else if (track->par->codec_tag == MKTAG('r','t','p',' ')) - mov_write_rtp_tag(pb, track); + ret = mov_write_rtp_tag(pb, track); else if (track->par->codec_tag == MKTAG('t','m','c','d')) - mov_write_tmcd_tag(pb, track); + ret = mov_write_tmcd_tag(pb, track); else if (track->par->codec_tag == MKTAG('g','p','m','d')) - mov_write_gpmd_tag(pb, track); + ret = mov_write_gpmd_tag(pb, track); + + if (ret < 0) + return ret; + return update_size(pb, pos); } @@ -2435,13 +2504,16 @@ static int mov_preroll_write_stbl_atoms(AVIOContext *pb, MOVTrack *track) static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); - int ret; + int ret = 0; avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "stbl"); - mov_write_stsd_tag(s, pb, mov, track); + if ((ret = mov_write_stsd_tag(s, pb, mov, track)) < 0) + return ret; mov_write_stts_tag(pb, track); if ((track->par->codec_type == AVMEDIA_TYPE_VIDEO || + track->par->codec_id == AV_CODEC_ID_TRUEHD || + track->par->codec_id == AV_CODEC_ID_MPEGH_3D_AUDIO || track->par->codec_tag == MKTAG('r','t','p',' ')) && track->has_keyframes && track->has_keyframes < track->entry) mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE); @@ -2709,10 +2781,28 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext return update_size(pb, pos); } +static int64_t calc_pts_duration(MOVMuxContext *mov, MOVTrack *track) +{ + if (track->tag == MKTAG('t','m','c','d')) { + // tmcd tracks gets track_duration set in mov_write_moov_tag from + // another track's duration, while the end_pts may be left at zero. + // Calculate the pts duration for that track instead. + return av_rescale(calc_pts_duration(mov, &mov->tracks[track->src_track]), + track->timescale, mov->tracks[track->src_track].timescale); + } + if (track->end_pts != AV_NOPTS_VALUE && + track->start_dts != AV_NOPTS_VALUE && + track->start_cts != AV_NOPTS_VALUE) { + return track->end_pts - (track->start_dts + track->start_cts); + } + return track->track_duration; +} + static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { - int version = track->track_duration < INT32_MAX ? 0 : 1; + int64_t duration = calc_pts_duration(mov, track); + int version = duration < INT32_MAX ? 0 : 1; if (track->mode == MODE_ISM) version = 1; @@ -2734,15 +2824,15 @@ static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, 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 */ + (version == 1) ? avio_wb64(pb, duration) : avio_wb32(pb, duration); /* duration */ avio_wb16(pb, track->language); /* language */ avio_wb16(pb, 0); /* reserved (quality) */ if (version != 0 && track->mode == MODE_MOV) { av_log(NULL, AV_LOG_ERROR, "FATAL error, file duration too long for timebase, this file will not be\n" - "playable with quicktime. Choose a different timebase or a different\n" - "container format\n"); + "playable with QuickTime. Choose a different timebase with " + "-video_track_timescale or a different container format\n"); } return 32; @@ -2784,8 +2874,9 @@ static void write_matrix(AVIOContext *pb, int16_t a, int16_t b, int16_t c, static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track, AVStream *st) { - int64_t duration = av_rescale_rnd(track->track_duration, MOV_TIMESCALE, - track->timescale, AV_ROUND_UP); + int64_t duration = av_rescale_rnd(calc_pts_duration(mov, track), + MOV_TIMESCALE, track->timescale, + AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; int flags = MOV_TKHD_FLAG_IN_MOVIE; int rotation = 0; @@ -2931,8 +3022,9 @@ static int mov_write_tapt_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); + int64_t duration = av_rescale_rnd(calc_pts_duration(mov, track), + MOV_TIMESCALE, track->timescale, + AV_ROUND_UP); int version = duration < INT32_MAX ? 0 : 1; int entry_size, entry_count, size; int64_t delay, start_ct = track->start_cts; @@ -3094,12 +3186,12 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (mov->mode & (MODE_MP4|MODE_MOV)) mov_write_track_metadata(pb_buf, st, "name", "title"); - if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { + if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) { avio_wb32(pb, size + 8); ffio_wfourcc(pb, "udta"); avio_write(pb, buf, size); } - av_free(buf); + ffio_free_dyn_buf(&pb_buf); return 0; } @@ -3218,7 +3310,8 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) for (i = 0; i < mov->nb_streams; i++) { if (mov->tracks[i].entry > 0 && mov->tracks[i].timescale) { - int64_t max_track_len_temp = av_rescale_rnd(mov->tracks[i].track_duration, + int64_t max_track_len_temp = av_rescale_rnd( + calc_pts_duration(mov, &mov->tracks[i]), MOV_TIMESCALE, mov->tracks[i].timescale, AV_ROUND_UP); @@ -3769,12 +3862,12 @@ static int mov_write_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (s->nb_chapters && !(mov->flags & FF_MOV_FLAG_DISABLE_CHPL)) mov_write_chpl_tag(pb_buf, s); - if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) { + if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) { avio_wb32(pb, size + 8); ffio_wfourcc(pb, "udta"); avio_write(pb, buf, size); } - av_free(buf); + ffio_free_dyn_buf(&pb_buf); return 0; } @@ -4158,6 +4251,9 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, flags &= ~MOV_TFHD_BASE_DATA_OFFSET; flags |= MOV_TFHD_DEFAULT_BASE_IS_MOOF; } + /* CMAF requires all values to be explicit in tfhd atoms */ + if (mov->flags & FF_MOV_FLAG_CMAF) + flags |= MOV_TFHD_STSD_ID; /* Don't set a default sample size, the silverlight player refuses * to play files with that set. Don't set a default sample duration, @@ -4165,7 +4261,7 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, * file format says it MUST NOT be set. */ if (track->mode == MODE_ISM) flags &= ~(MOV_TFHD_DEFAULT_SIZE | MOV_TFHD_DEFAULT_DURATION | - MOV_TFHD_BASE_DATA_OFFSET); + MOV_TFHD_BASE_DATA_OFFSET | MOV_TFHD_STSD_ID); avio_wb32(pb, 0); /* size placeholder */ ffio_wfourcc(pb, "tfhd"); @@ -4175,6 +4271,9 @@ static int mov_write_tfhd_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, track->track_id); /* track-id */ if (flags & MOV_TFHD_BASE_DATA_OFFSET) avio_wb64(pb, moof_offset); + if (flags & MOV_TFHD_STSD_ID) { + avio_wb32(pb, 1); + } if (flags & MOV_TFHD_DEFAULT_DURATION) { track->default_duration = get_cluster_duration(track, 0); avio_wb32(pb, track->default_duration); @@ -4461,7 +4560,8 @@ static int mov_write_sidx_tag(AVIOContext *pb, { int64_t pos = avio_tell(pb), offset_pos, end_pos; int64_t presentation_time, duration, offset; - int starts_with_SAP, i, entries; + unsigned starts_with_SAP; + int i, entries; if (track->entry) { entries = 1; @@ -4556,6 +4656,7 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) { int64_t pos = avio_tell(pb), pts_us, ntp_ts; MOVTrack *first_track; + int flags = 24; /* PRFT should be associated with at most one track. So, choosing only the * first track. */ @@ -4574,7 +4675,13 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) } if (mov->write_prft == MOV_PRFT_SRC_WALLCLOCK) { - ntp_ts = ff_get_formatted_ntp_time(ff_ntp_time()); + if (first_track->cluster[0].prft.wallclock) { + /* Round the NTP time to whole milliseconds. */ + ntp_ts = ff_get_formatted_ntp_time((first_track->cluster[0].prft.wallclock / 1000) * 1000 + + NTP_OFFSET_US); + flags = first_track->cluster[0].prft.flags; + } else + ntp_ts = ff_get_formatted_ntp_time(ff_ntp_time()); } else if (mov->write_prft == MOV_PRFT_SRC_PTS) { pts_us = av_rescale_q(first_track->cluster[0].pts, first_track->st->time_base, AV_TIME_BASE_Q); @@ -4588,7 +4695,7 @@ static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks) avio_wb32(pb, 0); // Size place holder ffio_wfourcc(pb, "prft"); // Type avio_w8(pb, 1); // Version - avio_wb24(pb, 0); // Flags + avio_wb24(pb, flags); // Flags avio_wb32(pb, first_track->track_id); // reference track ID avio_wb64(pb, ntp_ts); // NTP time stamp avio_wb64(pb, first_track->cluster[0].pts); //media time @@ -4688,27 +4795,11 @@ static int mov_write_mdat_tag(AVIOContext *pb, MOVMuxContext *mov) return 0; } -/* TODO: This needs to be more general */ -static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) +static void mov_write_ftyp_tag_internal(AVIOContext *pb, AVFormatContext *s, + int has_h264, int has_video, int write_minor) { MOVMuxContext *mov = s->priv_data; - int64_t pos = avio_tell(pb); - int has_h264 = 0, has_video = 0; int minor = 0x200; - int i; - - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (is_cover_image(st)) - continue; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - has_video = 1; - if (st->codecpar->codec_id == AV_CODEC_ID_H264) - has_h264 = 1; - } - - avio_wb32(pb, 0); /* size */ - ffio_wfourcc(pb, "ftyp"); if (mov->major_brand && strlen(mov->major_brand) >= 4) ffio_wfourcc(pb, mov->major_brand); @@ -4720,6 +4811,9 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) 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_FRAGMENT && + mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "iso6"); // Required when using signed CTS offsets in trun boxes 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 && mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) @@ -4735,31 +4829,68 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) else ffio_wfourcc(pb, "qt "); - avio_wb32(pb, minor); + if (write_minor) + avio_wb32(pb, minor); +} - if (mov->mode == MODE_MOV) - ffio_wfourcc(pb, "qt "); - else if (mov->mode == MODE_ISM) { - ffio_wfourcc(pb, "piff"); - } 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"); +static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + int64_t pos = avio_tell(pb); + int has_h264 = 0, has_video = 0; + int i; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (is_cover_image(st)) + continue; + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + has_video = 1; + if (st->codecpar->codec_id == AV_CODEC_ID_H264) + has_h264 = 1; } - // 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"); + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "ftyp"); - if (mov->mode == MODE_3GP) - ffio_wfourcc(pb, has_h264 ? "3gp6":"3gp4"); - else if (mov->mode & MODE_3G2) - ffio_wfourcc(pb, has_h264 ? "3g2b":"3g2a"); - else if (mov->mode == MODE_PSP) - ffio_wfourcc(pb, "MSNV"); - else if (mov->mode == MODE_MP4) + // Write major brand + mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 1); + // Write the major brand as the first compatible brand as well + mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 0); + + // Write compatible brands, ensuring that we don't write the major brand as a + // compatible brand a second time. + if (mov->mode == MODE_ISM) { + ffio_wfourcc(pb, "piff"); + } else if (mov->mode != MODE_MOV) { + // We add tfdt atoms when fragmenting, signal this with the iso6 compatible + // brand, if not already the major brand. This is compatible with users that + // don't understand tfdt. + if (mov->mode == MODE_MP4) { + if (mov->flags & FF_MOV_FLAG_CMAF) + ffio_wfourcc(pb, "cmfc"); + if (mov->flags & FF_MOV_FLAG_FRAGMENT && !(mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)) + ffio_wfourcc(pb, "iso6"); + } else { + if (mov->flags & FF_MOV_FLAG_FRAGMENT) + ffio_wfourcc(pb, "iso6"); + if (mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF) + ffio_wfourcc(pb, "iso5"); + else if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "iso4"); + } + // Brands prior to iso5 can't be signaled when using default-base-is-moof + if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) { + // write isom for mp4 only if it it's not the major brand already. + if (mov->mode != MODE_MP4 || mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) + ffio_wfourcc(pb, "isom"); + ffio_wfourcc(pb, "iso2"); + if (has_h264) + ffio_wfourcc(pb, "avc1"); + } + } + + if (mov->mode == MODE_MP4) ffio_wfourcc(pb, "mp41"); if (mov->flags & FF_MOV_FLAG_DASH && mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) @@ -4949,6 +5080,25 @@ static void mov_parse_vc1_frame(AVPacket *pkt, MOVTrack *trk) } } +static void mov_parse_truehd_frame(AVPacket *pkt, MOVTrack *trk) +{ + int length; + + if (pkt->size < 8) + return; + + length = (AV_RB16(pkt->data) & 0xFFF) * 2; + if (length < 8 || length > pkt->size) + return; + + if (AV_RB32(pkt->data + 4) == 0xF8726FBA) { + trk->cluster[trk->entry].flags |= MOV_SYNC_SAMPLE; + trk->has_keyframes++; + } + + return; +} + static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track) { MOVMuxContext *mov = s->priv_data; @@ -4962,12 +5112,11 @@ static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track) if ((ret = avio_open_dyn_buf(&mov->mdat_buf)) < 0) return ret; } - buf_size = avio_close_dyn_buf(track->mdat_buf, &buf); - track->mdat_buf = NULL; + buf_size = avio_get_dyn_buf(track->mdat_buf, &buf); offset = avio_tell(mov->mdat_buf); avio_write(mov->mdat_buf, buf, buf_size); - av_free(buf); + ffio_free_dyn_buf(&track->mdat_buf); for (i = track->entries_flushed; i < track->entry; i++) track->cluster[i].pos += offset; @@ -5059,17 +5208,16 @@ static int mov_flush_fragment(AVFormatContext *s, int force) if (mov->flags & FF_MOV_FLAG_DELAY_MOOV) { if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(s->pb); - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); mov->moov_written = 1; return 0; } - buf_size = avio_close_dyn_buf(mov->mdat_buf, &buf); - mov->mdat_buf = NULL; + buf_size = avio_get_dyn_buf(mov->mdat_buf, &buf); avio_wb32(s->pb, buf_size + 8); ffio_wfourcc(s->pb, "mdat"); avio_write(s->pb, buf, buf_size); - av_free(buf); + ffio_free_dyn_buf(&mov->mdat_buf); if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(s->pb); @@ -5084,7 +5232,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->tracks[i].entry = 0; mov->tracks[i].end_reliable = 0; } - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -5149,7 +5297,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) } if (write_moof) { - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); mov_write_moof_tag(s->pb, mov, moof_tracks, mdat_size); mov->fragments++; @@ -5181,7 +5329,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->mdat_size = 0; - avio_flush(s->pb); + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -5243,8 +5391,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; MOVTrack *trk = &mov->tracks[pkt->stream_index]; AVCodecParameters *par = trk->par; + AVProducerReferenceTime *prft; unsigned int samples_in_chunk = 0; - int size = pkt->size, ret = 0; + int size = pkt->size, ret = 0, offset = 0; + int prft_size; uint8_t *reformatted_data = NULL; ret = check_pkt(s, pkt); @@ -5307,12 +5457,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) !TAG_IS_AVCI(trk->tag) && (par->codec_id != AV_CODEC_ID_DNXHD)) { trk->vos_len = par->extradata_size; - trk->vos_data = av_malloc(trk->vos_len); + trk->vos_data = av_malloc(trk->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!trk->vos_data) { ret = AVERROR(ENOMEM); goto err; } memcpy(trk->vos_data, par->extradata, trk->vos_len); + memset(trk->vos_data + trk->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); } if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && @@ -5329,8 +5480,10 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) /* from x264 or from bytestream H.264 */ /* NAL reformatting needed */ if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { - ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, - &size); + ret = ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, + &size); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { if (trk->cenc.aes_ctr) { @@ -5347,14 +5500,20 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) (AV_RB24(trk->vos_data) == 1 || AV_RB32(trk->vos_data) == 1)) { /* extradata is Annex B, assume the bitstream is too and convert it */ if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { - ff_hevc_annexb2mp4_buf(pkt->data, &reformatted_data, &size, 0, NULL); + ret = ff_hevc_annexb2mp4_buf(pkt->data, &reformatted_data, + &size, 0, NULL); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL); } } else if (par->codec_id == AV_CODEC_ID_AV1) { if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { - ff_av1_filter_obus_buf(pkt->data, &reformatted_data, &size); + ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, + &size, &offset); + if (ret < 0) + return ret; avio_write(pb, reformatted_data, size); } else { size = ff_av1_filter_obus(pb, pkt->data, pkt->size); @@ -5386,15 +5545,17 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } if ((par->codec_id == AV_CODEC_ID_DNXHD || + par->codec_id == AV_CODEC_ID_TRUEHD || par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len) { /* copy frame to create needed atoms */ trk->vos_len = size; - trk->vos_data = av_malloc(size); + trk->vos_data = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE); if (!trk->vos_data) { ret = AVERROR(ENOMEM); goto err; } memcpy(trk->vos_data, pkt->data, size); + memset(trk->vos_data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); } if (trk->entry >= trk->cluster_capacity) { @@ -5495,6 +5656,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_VC1) { mov_parse_vc1_frame(pkt, trk); + } else if (par->codec_id == AV_CODEC_ID_TRUEHD) { + mov_parse_truehd_frame(pkt, trk); } else if (pkt->flags & AV_PKT_FLAG_KEY) { if (mov->mode == MODE_MOV && par->codec_id == AV_CODEC_ID_MPEG2VIDEO && trk->entry > 0) { // force sync sample for the first key frame @@ -5511,18 +5674,27 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) trk->cluster[trk->entry].flags |= MOV_DISPOSABLE_SAMPLE; trk->has_disposable++; } + + prft = (AVProducerReferenceTime *)av_packet_get_side_data(pkt, AV_PKT_DATA_PRFT, &prft_size); + if (prft && prft_size == sizeof(AVProducerReferenceTime)) + memcpy(&trk->cluster[trk->entry].prft, prft, prft_size); + else + memset(&trk->cluster[trk->entry].prft, 0, sizeof(AVProducerReferenceTime)); + trk->entry++; trk->sample_count += samples_in_chunk; mov->mdat_size += size; 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); + reformatted_data ? reformatted_data + offset + : NULL, size); end: err: - av_free(reformatted_data); + if (pkt->data != reformatted_data) + av_free(reformatted_data); return ret; } @@ -5952,6 +6124,11 @@ static void mov_free(AVFormatContext *s) av_freep(&mov->tracks[i].frag_info); av_packet_unref(&mov->tracks[i].cover_image); + if (mov->tracks[i].eac3_priv) { + struct eac3_info *info = mov->tracks[i].eac3_priv; + av_packet_unref(&info->pkt); + av_freep(&mov->tracks[i].eac3_priv); + } if (mov->tracks[i].vos_len) av_freep(&mov->tracks[i].vos_data); @@ -6012,12 +6189,13 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track, cur += strspn(cur, "\n\r"); } if (have_palette) { - track->vos_data = av_malloc(16*4); + track->vos_data = av_malloc(16*4 + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) return AVERROR(ENOMEM); for (i = 0; i < 16; i++) { AV_WB32(track->vos_data + i * 4, palette[i]); } + memset(track->vos_data + 16*4, 0, AV_INPUT_BUFFER_PADDING_SIZE); track->vos_len = 16 * 4; } st->codecpar->width = width; @@ -6066,6 +6244,9 @@ static int mov_init(AVFormatContext *s) if (mov->flags & FF_MOV_FLAG_DASH) mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_DEFAULT_BASE_MOOF; + if (mov->flags & FF_MOV_FLAG_CMAF) + mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | + FF_MOV_FLAG_DEFAULT_BASE_MOOF | FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && s->flags & AVFMT_FLAG_AUTO_BSF) { av_log(s, AV_LOG_VERBOSE, "Empty MOOV enabled; disabling automatic bitstream filtering\n"); @@ -6092,12 +6273,21 @@ static int mov_init(AVFormatContext *s) s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_MAKE_ZERO) mov->use_editlist = 0; } + if (mov->flags & FF_MOV_FLAG_CMAF) { + // CMAF Track requires negative cts offsets without edit lists + mov->use_editlist = 0; + } } if (mov->flags & FF_MOV_FLAG_EMPTY_MOOV && !(mov->flags & FF_MOV_FLAG_DELAY_MOOV) && mov->use_editlist) av_log(s, AV_LOG_WARNING, "No meaningful edit list will be written when using empty_moov without delay_moov\n"); - if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO) + if (mov->flags & FF_MOV_FLAG_CMAF && mov->use_editlist) { + av_log(s, AV_LOG_WARNING, "Edit list enabled; Assuming writing CMAF Track File\n"); + mov->flags &= ~FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; + } + if (!mov->use_editlist && s->avoid_negative_ts == AVFMT_AVOID_NEG_TS_AUTO && + !(mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)) s->avoid_negative_ts = AVFMT_AVOID_NEG_TS_MAKE_ZERO; /* Clear the omit_tfhd_offset flag if default_base_moof is set; @@ -6200,7 +6390,7 @@ static int mov_init(AVFormatContext *s) track->par = st->codecpar; track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV); if (track->language < 0) - track->language = 0; + track->language = 32767; // Unspecified Macintosh language code track->mode = mov->mode; track->tag = mov_find_codec_tag(s, track); if (!track->tag) { @@ -6228,6 +6418,8 @@ static int mov_init(AVFormatContext *s) } if (mov->video_track_timescale) { track->timescale = mov->video_track_timescale; + if (mov->mode == MODE_ISM && mov->video_track_timescale != 10000000) + av_log(s, AV_LOG_WARNING, "Warning: some tools, like mp4split, assume a timescale of 10000000 for ISMV.\n"); } else { track->timescale = st->time_base.den; while(track->timescale < 10000) @@ -6302,6 +6494,7 @@ static int mov_init(AVFormatContext *s) } } if (track->par->codec_id == AV_CODEC_ID_FLAC || + track->par->codec_id == AV_CODEC_ID_TRUEHD || track->par->codec_id == AV_CODEC_ID_OPUS) { if (track->mode != MODE_MP4) { av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id)); @@ -6324,10 +6517,14 @@ static int mov_init(AVFormatContext *s) } if (!track->height) track->height = st->codecpar->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; + /* The Protected Interoperable File Format (PIFF) standard, used by ISMV recommends but + doesn't mandate a track timescale of 10,000,000. The muxer allows a custom timescale + for video tracks, so if user-set, it isn't overwritten */ + if (mov->mode == MODE_ISM && + (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || + (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !mov->video_track_timescale))) { + track->timescale = 10000000; + } avpriv_set_pts_info(st, 64, 1, track->timescale); @@ -6360,7 +6557,7 @@ static int mov_write_header(AVFormatContext *s) nb_tracks++; } - if (mov->mode == MODE_MOV || mov->mode == MODE_MP4) + if (mov->nb_meta_tmcd) tmcd_track = nb_tracks; for (i = 0; i < s->nb_streams; i++) { @@ -6374,11 +6571,12 @@ static int mov_write_header(AVFormatContext *s) mov_create_dvd_sub_decoder_specific_info(track, st); else if (!TAG_IS_AVCI(track->tag) && st->codecpar->codec_id != AV_CODEC_ID_DNXHD) { track->vos_len = st->codecpar->extradata_size; - track->vos_data = av_malloc(track->vos_len); + track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) { return AVERROR(ENOMEM); } memcpy(track->vos_data, st->codecpar->extradata, track->vos_len); + memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); } } @@ -6474,7 +6672,6 @@ static int mov_write_header(AVFormatContext *s) !(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) { if ((ret = mov_write_moov_tag(pb, mov, s)) < 0) return ret; - avio_flush(pb); mov->moov_written = 1; if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) mov->reserved_header_pos = avio_tell(pb); @@ -6558,7 +6755,7 @@ static int shift_data(AVFormatContext *s) { int ret = 0, moov_size; MOVMuxContext *mov = s->priv_data; - int64_t pos, pos_end = avio_tell(s->pb); + int64_t pos, pos_end; uint8_t *buf, *read_buf[2]; int read_buf_id = 0; int read_size[2]; @@ -6634,10 +6831,11 @@ static int mov_write_trailer(AVFormatContext *s) AVCodecParameters *par = track->par; track->vos_len = par->extradata_size; - track->vos_data = av_malloc(track->vos_len); + track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE); if (!track->vos_data) return AVERROR(ENOMEM); memcpy(track->vos_data, par->extradata, track->vos_len); + memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); } mov->need_rewrite_extradata = 0; } @@ -6724,9 +6922,8 @@ static int mov_write_trailer(AVFormatContext *s) avio_seek(pb, mov->reserved_header_pos, SEEK_SET); mov_write_sidx_tags(pb, mov, -1, 0); avio_seek(pb, end, SEEK_SET); - avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); - mov_write_mfra_tag(pb, mov); - } else if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) { + } + if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) { avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); mov_write_mfra_tag(pb, mov); } @@ -6762,37 +6959,39 @@ static const AVCodecTag codec_3gp_tags[] = { }; const AVCodecTag codec_mp4_tags[] = { - { AV_CODEC_ID_MPEG4 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '1') }, - { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '3') }, - { AV_CODEC_ID_HEVC , MKTAG('h', 'e', 'v', '1') }, - { AV_CODEC_ID_HEVC , MKTAG('h', 'v', 'c', '1') }, - { AV_CODEC_ID_MPEG2VIDEO , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_MPEG1VIDEO , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_MJPEG , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_PNG , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_JPEG2000 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_VC1 , MKTAG('v', 'c', '-', '1') }, - { AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') }, - { AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') }, - { AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') }, - { AV_CODEC_ID_AV1 , MKTAG('a', 'v', '0', '1') }, - { AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_MP2 , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_AC3 , MKTAG('a', 'c', '-', '3') }, - { AV_CODEC_ID_EAC3 , MKTAG('e', 'c', '-', '3') }, - { AV_CODEC_ID_DTS , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_FLAC , MKTAG('f', 'L', 'a', 'C') }, - { AV_CODEC_ID_OPUS , MKTAG('O', 'p', 'u', 's') }, - { AV_CODEC_ID_VORBIS , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_QCELP , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_EVRC , MKTAG('m', 'p', '4', 'a') }, - { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') }, - { AV_CODEC_ID_MOV_TEXT , MKTAG('t', 'x', '3', 'g') }, - { AV_CODEC_ID_BIN_DATA , MKTAG('g', 'p', 'm', 'd') }, - { AV_CODEC_ID_NONE , 0 }, + { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, + { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') }, + { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') }, + { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_PNG, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_JPEG2000, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') }, + { AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') }, + { AV_CODEC_ID_TSCC2, MKTAG('m', 'p', '4', 'v') }, + { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, + { AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') }, + { AV_CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP4ALS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP3, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_MP2, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_AC3, MKTAG('a', 'c', '-', '3') }, + { AV_CODEC_ID_EAC3, MKTAG('e', 'c', '-', '3') }, + { AV_CODEC_ID_DTS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, + { AV_CODEC_ID_FLAC, MKTAG('f', 'L', 'a', 'C') }, + { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, + { AV_CODEC_ID_VORBIS, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_QCELP, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_EVRC, MKTAG('m', 'p', '4', 'a') }, + { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') }, + { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') }, + { AV_CODEC_ID_BIN_DATA, MKTAG('g', 'p', 'm', 'd') }, + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, + { AV_CODEC_ID_NONE, 0 }, }; const AVCodecTag codec_ism_tags[] = { @@ -6837,7 +7036,7 @@ AVOutputFormat ff_mov_muxer = { .deinit = mov_free, .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, .codec_tag = (const AVCodecTag* const []){ - ff_codec_movvideo_tags, ff_codec_movaudio_tags, 0 + ff_codec_movvideo_tags, ff_codec_movaudio_tags, ff_codec_movsubtitle_tags, 0 }, .check_bitstream = mov_check_bitstream, .priv_class = &mov_muxer_class,