X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=a315377ef64ff58a45e1196896cc0efa0e25cd7e;hb=ef6a9e5e311f09fa8032974fa4d0c1e166a959bb;hp=77943304b5a52b70aebf547d8351a3bd26d7caa9;hpb=b8eb0827f053230dd919bc73e25381b47b4fe1a7;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 77943304b5a..a315377ef64 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -51,6 +51,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "libavutil/timecode.h" +#include "libavutil/dovi_meta.h" #include "libavutil/color_utils.h" #include "hevc.h" #include "rtpenc.h" @@ -72,11 +73,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" }, + { "write_colr", "Write colr atom even if the color info is unspecified (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,7 +316,7 @@ 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; @@ -321,7 +324,7 @@ static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track) int fscod, bsid, bsmod, acmod, lfeon, frmsizecod; if (track->vos_len < 7) { - av_log(pb, AV_LOG_ERROR, + 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); @@ -362,7 +365,7 @@ static int mov_write_ac3_tag(AVIOContext *pb, MOVTrack *track) } struct eac3_info { - AVPacket pkt; + AVPacket *pkt; uint8_t ec3_done; uint8_t num_blocks; @@ -404,6 +407,9 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) return AVERROR(ENOMEM); info = track->eac3_priv; + if (!info->pkt && !(info->pkt = av_packet_alloc())) + return AVERROR(ENOMEM); + if (avpriv_ac3_parse_header(&hdr, pkt->data, pkt->size) < 0) { /* drop the packets until we see a good one */ if (!track->entry) { @@ -508,22 +514,20 @@ concatenate: } if (!info->num_blocks) { - ret = av_packet_ref(&info->pkt, pkt); + ret = av_packet_ref(info->pkt, pkt); if (!ret) info->num_blocks = num_blocks; goto end; } else { - if ((ret = av_grow_packet(&info->pkt, pkt->size)) < 0) + if ((ret = av_grow_packet(info->pkt, pkt->size)) < 0) goto end; - memcpy(info->pkt.data + info->pkt.size - pkt->size, pkt->data, pkt->size); + memcpy(info->pkt->data + info->pkt->size - pkt->size, pkt->data, pkt->size); info->num_blocks += num_blocks; - info->pkt.duration += pkt->duration; - if ((ret = av_copy_packet_side_data(&info->pkt, pkt)) < 0) - goto end; + info->pkt->duration += pkt->duration; if (info->num_blocks != 6) goto end; av_packet_unref(pkt); - av_packet_move_ref(pkt, &info->pkt); + av_packet_move_ref(pkt, info->pkt); info->num_blocks = 0; } ret = pkt->size; @@ -535,7 +539,7 @@ 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; @@ -543,7 +547,7 @@ static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track) int size, i; if (!track->eac3_priv) { - av_log(pb, AV_LOG_ERROR, + av_log(s, AV_LOG_ERROR, "Cannot write moov atom before EAC3 packets parsed.\n"); return AVERROR(EINVAL); } @@ -552,8 +556,7 @@ static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track) 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); @@ -576,7 +579,7 @@ static int mov_write_eac3_tag(AVIOContext *pb, MOVTrack *track) } } flush_put_bits(&pbc); - size = put_bits_count(&pbc) >> 3; + size = put_bytes_output(&pbc); avio_wb32(pb, size + 8); ffio_wfourcc(pb, "dec3"); @@ -584,10 +587,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; } @@ -637,12 +636,59 @@ static unsigned compute_avg_bitrate(MOVTrack *track) return size * 8 * track->timescale / track->track_duration; } +struct mpeg4_bit_rate_values { + uint32_t buffer_size; ///< Size of the decoding buffer for the elementary stream in bytes. + uint32_t max_bit_rate; ///< Maximum rate in bits/second over any window of one second. + uint32_t avg_bit_rate; ///< Average rate in bits/second over the entire presentation. +}; + +static struct mpeg4_bit_rate_values calculate_mpeg4_bit_rates(MOVTrack *track) +{ + AVCPBProperties *props = track->st ? + (AVCPBProperties*)av_stream_get_side_data(track->st, + AV_PKT_DATA_CPB_PROPERTIES, + NULL) : + NULL; + struct mpeg4_bit_rate_values bit_rates = { 0 }; + + bit_rates.avg_bit_rate = compute_avg_bitrate(track); + if (!bit_rates.avg_bit_rate) { + // if the average bit rate cannot be calculated at this point, such as + // in the case of fragmented MP4, utilize the following values as + // fall-back in priority order: + // + // 1. average bit rate property + // 2. bit rate (usually average over the whole clip) + // 3. maximum bit rate property + + if (props && props->avg_bitrate) { + bit_rates.avg_bit_rate = props->avg_bitrate; + } else if (track->par->bit_rate) { + bit_rates.avg_bit_rate = track->par->bit_rate; + } else if (props && props->max_bitrate) { + bit_rates.avg_bit_rate = props->max_bitrate; + } + } + + // (FIXME should be max rate in any 1 sec window) + bit_rates.max_bit_rate = FFMAX(track->par->bit_rate, + bit_rates.avg_bit_rate); + + // utilize values from properties if we have them available + if (props) { + bit_rates.max_bit_rate = FFMAX(bit_rates.max_bit_rate, + props->max_bitrate); + bit_rates.buffer_size = props->buffer_size / 8; + } + + return bit_rates; +} + static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic { - AVCPBProperties *props; + struct mpeg4_bit_rate_values bit_rates = calculate_mpeg4_bit_rates(track); int64_t pos = avio_tell(pb); int decoder_specific_info_len = track->vos_len ? 5 + track->vos_len : 0; - unsigned avg_bitrate; avio_wb32(pb, 0); // size ffio_wfourcc(pb, "esds"); @@ -673,14 +719,9 @@ static int mov_write_esds_tag(AVIOContext *pb, MOVTrack *track) // Basic else avio_w8(pb, 0x11); // flags (= Visualstream) - props = (AVCPBProperties*)av_stream_get_side_data(track->st, AV_PKT_DATA_CPB_PROPERTIES, - NULL); - - avio_wb24(pb, props ? props->buffer_size / 8 : 0); // Buffersize DB - - avg_bitrate = compute_avg_bitrate(track); - avio_wb32(pb, props ? FFMAX3(props->max_bitrate, props->avg_bitrate, avg_bitrate) : FFMAX(track->par->bit_rate, avg_bitrate)); // maxbitrate (FIXME should be max rate in any 1 sec window) - avio_wb32(pb, avg_bitrate); + avio_wb24(pb, bit_rates.buffer_size); // Buffersize DB + avio_wb32(pb, bit_rates.max_bit_rate); // maxbitrate + avio_wb32(pb, bit_rates.avg_bit_rate); if (track->vos_len) { // DecoderSpecific info descriptor @@ -753,25 +794,65 @@ 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); + int channels, channel_map; 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 OpusHead's preceeding magic/version, OpusSpecificBox is currently identical. */ - avio_w8(pb, AV_RB8(track->par->extradata + 9)); /* OuputChannelCount */ + channels = AV_RB8(track->par->extradata + 9); + channel_map = AV_RB8(track->par->extradata + 18); + + avio_w8(pb, channels); /* OuputChannelCount */ avio_wb16(pb, AV_RL16(track->par->extradata + 10)); /* PreSkip */ avio_wb32(pb, AV_RL32(track->par->extradata + 12)); /* InputSampleRate */ avio_wb16(pb, AV_RL16(track->par->extradata + 16)); /* OutputGain */ + avio_w8(pb, channel_map); /* ChannelMappingFamily */ /* Write the rest of the header out without byte-swapping. */ - avio_write(pb, track->par->extradata + 18, track->par->extradata_size - 18); + if (channel_map) { + if (track->par->extradata_size < 21 + channels) { + av_log(s, AV_LOG_ERROR, "invalid extradata size\n"); + return AVERROR_INVALIDDATA; + } + avio_write(pb, track->par->extradata + 19, 2 + channels); /* ChannelMappingTable */ + } + + 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); } @@ -830,9 +911,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); @@ -1024,6 +1105,25 @@ static int get_samples_per_packet(MOVTrack *track) return first_duration; } +static int mov_write_btrt_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + struct mpeg4_bit_rate_values bit_rates = calculate_mpeg4_bit_rates(track); + if (!bit_rates.max_bit_rate && !bit_rates.avg_bit_rate && + !bit_rates.buffer_size) + // no useful data to be written, skip + return 0; + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "btrt"); + + avio_wb32(pb, bit_rates.buffer_size); + avio_wb32(pb, bit_rates.max_bit_rate); + avio_wb32(pb, bit_rates.avg_bit_rate); + + return update_size(pb, pos); +} + static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1105,10 +1205,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 */ @@ -1139,9 +1243,9 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) ret = mov_write_amr_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_AC3) - ret = mov_write_ac3_tag(pb, track); + ret = mov_write_ac3_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_EAC3) - ret = mov_write_eac3_tag(pb, track); + ret = mov_write_eac3_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_ALAC) ret = mov_write_extradata_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_WMAPRO) @@ -1149,7 +1253,9 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex else if (track->par->codec_id == AV_CODEC_ID_FLAC) ret = mov_write_dfla_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_OPUS) - ret = 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) ret = mov_write_glbl_tag(pb, track); @@ -1166,6 +1272,10 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex return ret; } + if (track->mode == MODE_MP4 && + ((ret = mov_write_btrt_tag(pb, track)) < 0)) + return ret; + ret = update_size(pb, pos); return ret; } @@ -1557,6 +1667,10 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) { unsigned int tag = track->par->codec_tag; + // "rtp " is used to distinguish internally created RTP-hint tracks + // (with rtp_ctx) from other tracks. + if (tag == MKTAG('r','t','p',' ')) + tag = 0; if (!tag || (s->strict_std_compliance >= FF_COMPLIANCE_NORMAL && (track->par->codec_id == AV_CODEC_ID_DVVIDEO || track->par->codec_id == AV_CODEC_ID_RAWVIDEO || @@ -1681,6 +1795,7 @@ static int mov_write_fiel_tag(AVIOContext *pb, MOVTrack *track, int field_order) static int mov_write_subtitle_tag(AVIOContext *pb, MOVTrack *track) { + int ret = AVERROR_BUG; int64_t pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ avio_wl32(pb, track->tag); // store it byteswapped @@ -1693,15 +1808,19 @@ static int mov_write_subtitle_tag(AVIOContext *pb, MOVTrack *track) else if (track->par->extradata_size) avio_write(pb, track->par->extradata, track->par->extradata_size); + if (track->mode == MODE_MP4 && + ((ret = mov_write_btrt_tag(pb, track)) < 0)) + return ret; + 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; } @@ -1716,7 +1835,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 */ @@ -1734,7 +1853,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; } @@ -1784,6 +1903,36 @@ static int mov_write_sv3d_tag(AVFormatContext *s, AVIOContext *pb, AVSphericalMa return update_size(pb, sv3d_pos); } +static int mov_write_dvcc_dvvc_tag(AVFormatContext *s, AVIOContext *pb, AVDOVIDecoderConfigurationRecord *dovi) +{ + avio_wb32(pb, 32); /* size = 8 + 24 */ + if (dovi->dv_profile > 7) + ffio_wfourcc(pb, "dvvC"); + else + ffio_wfourcc(pb, "dvcC"); + avio_w8(pb, dovi->dv_version_major); + avio_w8(pb, dovi->dv_version_minor); + avio_wb16(pb, (dovi->dv_profile << 9) | (dovi->dv_level << 3) | + (dovi->rpu_present_flag << 2) | (dovi->el_present_flag << 1) | + dovi->bl_present_flag); + avio_wb32(pb, (dovi->dv_bl_signal_compatibility_id << 28) | 0); + + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + avio_wb32(pb, 0); /* reserved */ + av_log(s, AV_LOG_DEBUG, "DOVI in %s box, version: %d.%d, profile: %d, level: %d, " + "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n", + dovi->dv_profile > 7 ? "dvvC" : "dvcC", + dovi->dv_version_major, dovi->dv_version_minor, + dovi->dv_profile, dovi->dv_level, + dovi->rpu_present_flag, + dovi->el_present_flag, + dovi->bl_present_flag, + dovi->dv_bl_signal_compatibility_id); + return 32; /* 8 + 24 */ +} + static int mov_write_clap_tag(AVIOContext *pb, MOVTrack *track) { avio_wb32(pb, 40); @@ -1812,110 +1961,126 @@ 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 - if (track->par->color_primaries == AVCOL_PRI_UNSPECIFIED && - track->par->color_trc == AVCOL_TRC_UNSPECIFIED && - track->par->color_space == AVCOL_SPC_UNSPECIFIED) { - if ((track->par->width >= 1920 && track->par->height >= 1080) - || (track->par->width == 1280 && track->par->height == 720)) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt709\n"); - track->par->color_primaries = AVCOL_PRI_BT709; - } else if (track->par->width == 720 && track->height == 576) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming bt470bg\n"); - track->par->color_primaries = AVCOL_PRI_BT470BG; - } else if (track->par->width == 720 && - (track->height == 486 || track->height == 480)) { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, assuming smpte170\n"); - track->par->color_primaries = AVCOL_PRI_SMPTE170M; - } else { - av_log(NULL, AV_LOG_WARNING, "color primaries unspecified, unable to assume anything\n"); + const uint8_t *icc_profile; + size_t 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; } - switch (track->par->color_primaries) { - case AVCOL_PRI_BT709: - track->par->color_trc = AVCOL_TRC_BT709; - track->par->color_space = AVCOL_SPC_BT709; - break; - case AVCOL_PRI_SMPTE170M: - case AVCOL_PRI_BT470BG: - track->par->color_trc = AVCOL_TRC_BT709; - track->par->color_space = AVCOL_SPC_SMPTE170M; - break; + else { + av_log(NULL, AV_LOG_INFO, "no ICC profile found, will write nclx/nclc colour info instead\n"); } } /* 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"); else ffio_wfourcc(pb, "nclc"); - switch (track->par->color_primaries) { - case AVCOL_PRI_BT709: avio_wb16(pb, 1); break; - case AVCOL_PRI_BT470BG: avio_wb16(pb, 5); break; - case AVCOL_PRI_SMPTE170M: - case AVCOL_PRI_SMPTE240M: avio_wb16(pb, 6); break; - case AVCOL_PRI_BT2020: avio_wb16(pb, 9); break; - case AVCOL_PRI_SMPTE431: avio_wb16(pb, 11); break; - case AVCOL_PRI_SMPTE432: avio_wb16(pb, 12); break; - default: avio_wb16(pb, 2); - } - switch (track->par->color_trc) { - case AVCOL_TRC_BT709: avio_wb16(pb, 1); break; - case AVCOL_TRC_SMPTE170M: avio_wb16(pb, 1); break; // remapped - case AVCOL_TRC_SMPTE240M: avio_wb16(pb, 7); break; - case AVCOL_TRC_SMPTEST2084: avio_wb16(pb, 16); break; - case AVCOL_TRC_SMPTE428: avio_wb16(pb, 17); break; - case AVCOL_TRC_ARIB_STD_B67: avio_wb16(pb, 18); break; - default: avio_wb16(pb, 2); - } - switch (track->par->color_space) { - case AVCOL_SPC_BT709: avio_wb16(pb, 1); break; - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_SMPTE170M: avio_wb16(pb, 6); break; - case AVCOL_SPC_SMPTE240M: avio_wb16(pb, 7); break; - case AVCOL_SPC_BT2020_NCL: avio_wb16(pb, 9); break; - default: avio_wb16(pb, 2); - } - + // Do not try to guess the color info if it is AVCOL_PRI_UNSPECIFIED. + // e.g., Dolby Vision for Apple devices should be set to AVCOL_PRI_UNSPECIFIED. See + // https://developer.apple.com/av-foundation/High-Dynamic-Range-Metadata-for-Apple-Devices.pdf + avio_wb16(pb, track->par->color_primaries); + avio_wb16(pb, track->par->color_trc); + avio_wb16(pb, track->par->color_space); 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 int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track) +{ + const uint8_t *side_data; + const AVContentLightMetadata *content_light_metadata; + + side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL); + if (!side_data) { + return 0; + } + content_light_metadata = (const AVContentLightMetadata*)side_data; + + avio_wb32(pb, 12); // size + ffio_wfourcc(pb, "clli"); + avio_wb16(pb, content_light_metadata->MaxCLL); + avio_wb16(pb, content_light_metadata->MaxFALL); + return 12; +} + +static inline int64_t rescale_mdcv(AVRational q, int b) +{ + return av_rescale(q.num, b, q.den); +} + +static int mov_write_mdcv_tag(AVIOContext *pb, MOVTrack *track) +{ + const int chroma_den = 50000; + const int luma_den = 10000; + const uint8_t *side_data; + const AVMasteringDisplayMetadata *metadata; + + side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL); + metadata = (const AVMasteringDisplayMetadata*)side_data; + if (!metadata || !metadata->has_primaries || !metadata->has_luminance) { + return 0; + } + + avio_wb32(pb, 32); // size + ffio_wfourcc(pb, "mdcv"); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[1][0], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[1][1], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[2][0], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[2][1], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[0][0], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->display_primaries[0][1], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->white_point[0], chroma_den)); + avio_wb16(pb, rescale_mdcv(metadata->white_point[1], chroma_den)); + avio_wb32(pb, rescale_mdcv(metadata->max_luminance, luma_den)); + avio_wb32(pb, rescale_mdcv(metadata->min_luminance, luma_den)); + return 32; } static void find_compressor(char * compressor_name, int len, MOVTrack *track) @@ -1946,8 +2111,9 @@ 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) { + int ret = AVERROR_BUG; int64_t pos = avio_tell(pb); char compressor_name[32] = { 0 }; int avid = 0; @@ -2081,25 +2247,39 @@ 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); - else - av_log(mov->fc, AV_LOG_WARNING, "Not writing 'colr' atom. Format is not MOV or MP4.\n"); + if (track->mode == MODE_MOV || track->mode == MODE_MP4) { + int has_color_info = track->par->color_primaries != AVCOL_PRI_UNSPECIFIED && + track->par->color_trc != AVCOL_TRC_UNSPECIFIED && + track->par->color_space != AVCOL_SPC_UNSPECIFIED; + if (has_color_info || mov->flags & FF_MOV_FLAG_WRITE_COLR || + av_stream_get_side_data(track->st, AV_PKT_DATA_ICC_PROFILE, NULL)) { + int prefer_icc = mov->flags & FF_MOV_FLAG_PREFER_ICC || !has_color_info; + mov_write_colr_tag(pb, track, prefer_icc); + } else if (mov->flags & FF_MOV_FLAG_WRITE_COLR) { + av_log(mov->fc, AV_LOG_WARNING, "Not writing 'colr' atom. Format is not MOV or MP4.\n"); + } + } + if (track->mode == MODE_MOV || track->mode == MODE_MP4) { + mov_write_clli_tag(pb, track); + mov_write_mdcv_tag(pb, track); } if (track->mode == MODE_MP4 && mov->fc->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) { AVStereo3D* stereo_3d = (AVStereo3D*) av_stream_get_side_data(track->st, AV_PKT_DATA_STEREO3D, NULL); AVSphericalMapping* spherical_mapping = (AVSphericalMapping*)av_stream_get_side_data(track->st, AV_PKT_DATA_SPHERICAL, NULL); + AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *) + av_stream_get_side_data(track->st, AV_PKT_DATA_DOVI_CONF, 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); + if (dovi) + mov_write_dvcc_dvvc_tag(s, pb, dovi); } if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) { @@ -2114,6 +2294,10 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr ff_mov_cenc_write_sinf_tag(track, pb, mov->encryption_kid); } + if (track->mode == MODE_MP4 && + ((ret = mov_write_btrt_tag(pb, track)) < 0)) + return ret; + /* extra padding for avid stsd */ /* https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-61112 */ if (avid) @@ -2180,8 +2364,8 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) return AVERROR(EINVAL); #endif } else { - frame_duration = av_rescale(track->timescale, track->st->avg_frame_rate.num, track->st->avg_frame_rate.den); - nb_frames = ROUNDED_DIV(track->st->avg_frame_rate.den, track->st->avg_frame_rate.num); + frame_duration = av_rescale(track->timescale, track->st->avg_frame_rate.den, track->st->avg_frame_rate.num); + nb_frames = ROUNDED_DIV(track->st->avg_frame_rate.num, track->st->avg_frame_rate.den); } if (nb_frames > 255) { @@ -2238,7 +2422,7 @@ static int mov_write_stsd_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext avio_wb32(pb, 0); /* version & flags */ avio_wb32(pb, 1); /* entry count */ if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) - ret = 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) ret = mov_write_audio_tag(s, pb, mov, track); else if (track->par->codec_type == AVMEDIA_TYPE_SUBTITLE) @@ -2463,6 +2647,8 @@ static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext 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); @@ -2634,7 +2820,7 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra } else { hdlr_type = "text"; } - descr = "SubtitleHandler"; + descr = "SubtitleHandler"; } } else if (track->par->codec_tag == MKTAG('r','t','p',' ')) { hdlr_type = "hint"; @@ -2730,10 +2916,57 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext return update_size(pb, pos); } +static void get_pts_range(MOVMuxContext *mov, MOVTrack *track, + int64_t *start, int64_t *end) +{ + if (track->tag == MKTAG('t','m','c','d') && mov->nb_meta_tmcd) { + // 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. + get_pts_range(mov, &mov->tracks[track->src_track], start, end); + *start = av_rescale(*start, track->timescale, + mov->tracks[track->src_track].timescale); + *end = av_rescale(*end, track->timescale, + mov->tracks[track->src_track].timescale); + return; + } + if (track->end_pts != AV_NOPTS_VALUE && + track->start_dts != AV_NOPTS_VALUE && + track->start_cts != AV_NOPTS_VALUE) { + *start = track->start_dts + track->start_cts; + *end = track->end_pts; + return; + } + *start = 0; + *end = track->track_duration; +} + +static int64_t calc_samples_pts_duration(MOVMuxContext *mov, MOVTrack *track) +{ + int64_t start, end; + get_pts_range(mov, track, &start, &end); + return end - start; +} + +// Calculate the actual duration of the track, after edits. +// If it starts with a pts < 0, that is removed by the edit list. +// If it starts with a pts > 0, the edit list adds a delay before that. +// Thus, with edit lists enabled, the post-edit output of the file is +// starting with pts=0. +static int64_t calc_pts_duration(MOVMuxContext *mov, MOVTrack *track) +{ + int64_t start, end; + get_pts_range(mov, track, &start, &end); + if (mov->use_editlist != 0) + start = 0; + return end - start; +} + 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; @@ -2755,7 +2988,7 @@ 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) */ @@ -2805,15 +3038,16 @@ 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; int group = 0; uint32_t *display_matrix = NULL; - int display_matrix_size, i; + size_t display_matrix_size; + int i; if (st) { if (mov->per_stream_grouping) @@ -2865,23 +3099,9 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb16(pb, 0); /* reserved */ /* Matrix structure */ -#if FF_API_OLD_ROTATE_API - if (st && st->metadata) { - AVDictionaryEntry *rot = av_dict_get(st->metadata, "rotate", NULL, 0); - rotation = (rot && rot->value) ? atoi(rot->value) : 0; - } -#endif if (display_matrix) { for (i = 0; i < 9; i++) avio_wb32(pb, display_matrix[i]); -#if FF_API_OLD_ROTATE_API - } else if (rotation == 90) { - write_matrix(pb, 0, 1, -1, 0, track->par->height, 0); - } else if (rotation == 180) { - write_matrix(pb, -1, 0, 0, -1, track->par->width, track->par->height); - } else if (rotation == 270) { - write_matrix(pb, 0, -1, 1, 0, 0, track->par->width); -#endif } else { write_matrix(pb, 1, 0, 0, 1, 0, 0); } @@ -2952,8 +3172,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_samples_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; @@ -3115,12 +3336,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; } @@ -3239,7 +3460,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); @@ -3510,7 +3732,7 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) { MOVTrack *trk = &mov->tracks[i]; - if (!is_cover_image(trk->st) || trk->cover_image.size <= 0) + if (!is_cover_image(trk->st) || trk->cover_image->size <= 0) continue; if (!pos) { @@ -3518,11 +3740,11 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s) avio_wb32(pb, 0); ffio_wfourcc(pb, "covr"); } - avio_wb32(pb, 16 + trk->cover_image.size); + avio_wb32(pb, 16 + trk->cover_image->size); ffio_wfourcc(pb, "data"); avio_wb32(pb, trk->tag); avio_wb32(pb , 0); - avio_write(pb, trk->cover_image.data, trk->cover_image.size); + avio_write(pb, trk->cover_image->data, trk->cover_image->size); } return pos ? update_size(pb, pos) : 0; @@ -3790,12 +4012,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; } @@ -3951,7 +4173,8 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, track->tref_tag = MKTAG('h','i','n','t'); track->tref_id = mov->tracks[track->src_track].track_id; } else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) { - int * fallback, size; + size_t size; + int *fallback; fallback = (int*)av_stream_get_side_data(track->st, AV_PKT_DATA_FALLBACK_TRACK, &size); @@ -4019,8 +4242,6 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat { int64_t pos = avio_tell(pb); int i; - int64_t manifest_bit_rate = 0; - AVCPBProperties *props = NULL; static const uint8_t uuid[] = { 0xa5, 0xd4, 0x0b, 0x30, 0xe8, 0x14, 0x11, 0xdd, @@ -4046,6 +4267,8 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; + struct mpeg4_bit_rate_values bit_rates = + calculate_mpeg4_bit_rates(track); const char *type; int track_id = track->track_id; char track_name_buf[32] = { 0 }; @@ -4061,17 +4284,9 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat continue; } - props = (AVCPBProperties*)av_stream_get_side_data(track->st, AV_PKT_DATA_CPB_PROPERTIES, NULL); - - if (track->par->bit_rate) { - manifest_bit_rate = track->par->bit_rate; - } else if (props) { - manifest_bit_rate = props->max_bitrate; - } - - avio_printf(pb, "<%s systemBitrate=\"%"PRId64"\">\n", type, - manifest_bit_rate); - param_write_int(pb, "systemBitrate", manifest_bit_rate); + avio_printf(pb, "<%s systemBitrate=\"%"PRIu32"\">\n", type, + bit_rates.avg_bit_rate); + param_write_int(pb, "systemBitrate", bit_rates.avg_bit_rate); param_write_int(pb, "trackID", track_id); param_write_string(pb, "systemLanguage", lang ? lang->value : "und"); @@ -4179,6 +4394,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, @@ -4186,7 +4404,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"); @@ -4196,6 +4414,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); @@ -4482,7 +4703,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; @@ -4577,6 +4799,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. */ @@ -4595,7 +4818,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); @@ -4609,7 +4838,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 @@ -4674,28 +4903,40 @@ static int mov_write_tfra_tag(AVIOContext *pb, MOVTrack *track) static int mov_write_mfra_tag(AVIOContext *pb, MOVMuxContext *mov) { - int64_t pos = avio_tell(pb); - int i; + AVIOContext *mfra_pb; + int i, ret, sz; + uint8_t *buf; - avio_wb32(pb, 0); /* size placeholder */ - ffio_wfourcc(pb, "mfra"); + ret = avio_open_dyn_buf(&mfra_pb); + if (ret < 0) + return ret; + + avio_wb32(mfra_pb, 0); /* size placeholder */ + ffio_wfourcc(mfra_pb, "mfra"); /* An empty mfra atom is enough to indicate to the publishing point that * the stream has ended. */ if (mov->flags & FF_MOV_FLAG_ISML) - return update_size(pb, pos); + goto done_mfra; for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; if (track->nb_frag_info) - mov_write_tfra_tag(pb, track); + mov_write_tfra_tag(mfra_pb, track); } - avio_wb32(pb, 16); - ffio_wfourcc(pb, "mfro"); - avio_wb32(pb, 0); /* version + flags */ - avio_wb32(pb, avio_tell(pb) + 4 - pos); + avio_wb32(mfra_pb, 16); + ffio_wfourcc(mfra_pb, "mfro"); + avio_wb32(mfra_pb, 0); /* version + flags */ + avio_wb32(mfra_pb, avio_tell(mfra_pb) + 4); - return update_size(pb, pos); +done_mfra: + + sz = update_size(mfra_pb, 0); + ret = avio_get_dyn_buf(mfra_pb, &buf); + avio_write(pb, buf, ret); + ffio_free_dyn_buf(&mfra_pb); + + return sz; } static int mov_write_mdat_tag(AVIOContext *pb, MOVMuxContext *mov) @@ -4709,27 +4950,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); @@ -4741,6 +4966,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) @@ -4756,31 +4984,72 @@ 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_av1 = 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; + if (st->codecpar->codec_id == AV_CODEC_ID_AV1) + has_av1 = 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"); + if (has_av1) + ffio_wfourcc(pb, "av01"); + } 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) @@ -4970,6 +5239,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; @@ -4983,12 +5271,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; @@ -5015,15 +5302,19 @@ static int mov_flush_fragment(AVFormatContext *s, int force) for (i = 0; i < s->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; if (!track->end_reliable) { - AVPacket pkt; - if (!ff_interleaved_peek(s, i, &pkt, 1)) { + const AVPacket *pkt = ff_interleaved_peek(s, i); + if (pkt) { + int64_t offset, dts, pts; + ff_get_muxer_ts_offset(s, i, &offset); + pts = pkt->pts + offset; + dts = pkt->dts + offset; if (track->dts_shift != AV_NOPTS_VALUE) - pkt.dts += track->dts_shift; - track->track_duration = pkt.dts - track->start_dts; - if (pkt.pts != AV_NOPTS_VALUE) - track->end_pts = pkt.pts; + dts += track->dts_shift; + track->track_duration = dts - track->start_dts; + if (pts != AV_NOPTS_VALUE) + track->end_pts = pts; else - track->end_pts = pkt.dts; + track->end_pts = dts; } } } @@ -5080,17 +5371,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); @@ -5105,7 +5395,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; } @@ -5170,7 +5460,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++; @@ -5202,7 +5492,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; } @@ -5264,8 +5554,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; + size_t prft_size; uint8_t *reformatted_data = NULL; ret = check_pkt(s, pkt); @@ -5328,12 +5620,30 @@ 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_DNXHD || + par->codec_id == AV_CODEC_ID_H264 || + par->codec_id == AV_CODEC_ID_HEVC || + par->codec_id == AV_CODEC_ID_TRUEHD || + par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len && + !TAG_IS_AVCI(trk->tag)) { + /* copy frame to create needed atoms */ + trk->vos_len = 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 (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && @@ -5350,8 +5660,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) { @@ -5368,14 +5680,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); @@ -5389,6 +5707,22 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) goto end; avio_write(pb, pkt->data, size); #endif + } else if (par->codec_id == AV_CODEC_ID_EIA_608) { + size = 8; + + for (int i = 0; i < pkt->size; i += 3) { + if (pkt->data[i] == 0xFC) { + size += 2; + } + } + avio_wb32(pb, size); + ffio_wfourcc(pb, "cdat"); + for (int i = 0; i < pkt->size; i += 3) { + if (pkt->data[i] == 0xFC) { + avio_w8(pb, pkt->data[i + 1]); + avio_w8(pb, pkt->data[i + 2]); + } + } } else { if (trk->cenc.aes_ctr) { if (par->codec_id == AV_CODEC_ID_H264 && par->extradata_size > 4) { @@ -5406,25 +5740,14 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if ((par->codec_id == AV_CODEC_ID_DNXHD || - 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); - 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))) { + unsigned new_capacity = trk->entry + MOV_INDEX_CLUSTER_SIZE; + void *cluster = av_realloc_array(trk->cluster, new_capacity, sizeof(*trk->cluster)); + if (!cluster) { ret = AVERROR(ENOMEM); goto err; } + trk->cluster = cluster; trk->cluster_capacity = new_capacity; } @@ -5516,6 +5839,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 @@ -5532,123 +5857,132 @@ 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; } static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) { - MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; - AVCodecParameters *par = trk->par; - int64_t frag_duration = 0; - int size = pkt->size; + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk = &mov->tracks[pkt->stream_index]; + AVCodecParameters *par = trk->par; + int64_t frag_duration = 0; + int size = pkt->size; - int ret = check_pkt(s, pkt); - if (ret < 0) - return ret; + int ret = check_pkt(s, pkt); + if (ret < 0) + return ret; - if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) { - int i; - for (i = 0; i < s->nb_streams; i++) - mov->tracks[i].frag_discont = 1; - mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT; - } + if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) { + int i; + for (i = 0; i < s->nb_streams; i++) + mov->tracks[i].frag_discont = 1; + mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT; + } - if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) { - if (trk->dts_shift == AV_NOPTS_VALUE) - trk->dts_shift = pkt->pts - pkt->dts; - pkt->dts += trk->dts_shift; - } + if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS) { + if (trk->dts_shift == AV_NOPTS_VALUE) + trk->dts_shift = pkt->pts - pkt->dts; + pkt->dts += trk->dts_shift; + } - if (trk->par->codec_id == AV_CODEC_ID_MP4ALS || + if (trk->par->codec_id == AV_CODEC_ID_MP4ALS || trk->par->codec_id == AV_CODEC_ID_AAC || trk->par->codec_id == AV_CODEC_ID_AV1 || trk->par->codec_id == AV_CODEC_ID_FLAC) { - int side_size = 0; - uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); - if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { - void *newextra = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!newextra) - return AVERROR(ENOMEM); - av_free(par->extradata); - par->extradata = newextra; - memcpy(par->extradata, side, side_size); - par->extradata_size = side_size; - if (!pkt->size) // Flush packet - mov->need_rewrite_extradata = 1; - } + size_t side_size; + uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); + if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { + void *newextra = av_mallocz(side_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!newextra) + return AVERROR(ENOMEM); + av_free(par->extradata); + par->extradata = newextra; + memcpy(par->extradata, side, side_size); + par->extradata_size = side_size; + if (!pkt->size) // Flush packet + mov->need_rewrite_extradata = 1; } + } - if (!pkt->size) { - if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) { - trk->start_dts = pkt->dts; - if (pkt->pts != AV_NOPTS_VALUE) - trk->start_cts = pkt->pts - pkt->dts; - else - trk->start_cts = 0; - } - - return 0; /* Discard 0 sized packets */ + if (!pkt->size) { + if (trk->start_dts == AV_NOPTS_VALUE && trk->frag_discont) { + trk->start_dts = pkt->dts; + if (pkt->pts != AV_NOPTS_VALUE) + trk->start_cts = pkt->pts - pkt->dts; + else + trk->start_cts = 0; } - if (trk->entry && pkt->stream_index < s->nb_streams) - frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, - s->streams[pkt->stream_index]->time_base, - AV_TIME_BASE_Q); - if ((mov->max_fragment_duration && - frag_duration >= mov->max_fragment_duration) || - (mov->max_fragment_size && mov->mdat_size + size >= mov->max_fragment_size) || - (mov->flags & FF_MOV_FLAG_FRAG_KEYFRAME && - par->codec_type == AVMEDIA_TYPE_VIDEO && - trk->entry && pkt->flags & AV_PKT_FLAG_KEY) || - (mov->flags & FF_MOV_FLAG_FRAG_EVERY_FRAME)) { - if (frag_duration >= mov->min_fragment_duration) { - // Set the duration of this track to line up with the next - // sample in this track. This avoids relying on AVPacket - // duration, but only helps for this particular track, not - // for the other ones that are flushed at the same time. - trk->track_duration = pkt->dts - trk->start_dts; - if (pkt->pts != AV_NOPTS_VALUE) - trk->end_pts = pkt->pts; - else - trk->end_pts = pkt->dts; - trk->end_reliable = 1; - mov_auto_flush_fragment(s, 0); - } + return 0; /* Discard 0 sized packets */ + } + + if (trk->entry && pkt->stream_index < s->nb_streams) + frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, + s->streams[pkt->stream_index]->time_base, + AV_TIME_BASE_Q); + if ((mov->max_fragment_duration && + frag_duration >= mov->max_fragment_duration) || + (mov->max_fragment_size && mov->mdat_size + size >= mov->max_fragment_size) || + (mov->flags & FF_MOV_FLAG_FRAG_KEYFRAME && + par->codec_type == AVMEDIA_TYPE_VIDEO && + trk->entry && pkt->flags & AV_PKT_FLAG_KEY) || + (mov->flags & FF_MOV_FLAG_FRAG_EVERY_FRAME)) { + if (frag_duration >= mov->min_fragment_duration) { + // Set the duration of this track to line up with the next + // sample in this track. This avoids relying on AVPacket + // duration, but only helps for this particular track, not + // for the other ones that are flushed at the same time. + trk->track_duration = pkt->dts - trk->start_dts; + if (pkt->pts != AV_NOPTS_VALUE) + trk->end_pts = pkt->pts; + else + trk->end_pts = pkt->dts; + trk->end_reliable = 1; + mov_auto_flush_fragment(s, 0); } + } - return ff_mov_write_packet(s, pkt); + return ff_mov_write_packet(s, pkt); } static int mov_write_subtitle_end_packet(AVFormatContext *s, int stream_index, int64_t dts) { - AVPacket end; + MOVMuxContext *mov = s->priv_data; + AVPacket *end = mov->pkt; uint8_t data[2] = {0}; int ret; - av_init_packet(&end); - end.size = sizeof(data); - end.data = data; - end.pts = dts; - end.dts = dts; - end.duration = 0; - end.stream_index = stream_index; + end->size = sizeof(data); + end->data = data; + end->pts = dts; + end->dts = dts; + end->duration = 0; + end->stream_index = stream_index; - ret = mov_write_single_packet(s, &end); - av_packet_unref(&end); + ret = mov_write_single_packet(s, end); + av_packet_unref(end); return ret; } @@ -5675,7 +6009,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } - if ((ret = av_packet_ref(&trk->cover_image, pkt)) < 0) + if ((ret = av_packet_ref(trk->cover_image, pkt)) < 0) return ret; return 0; @@ -5761,7 +6095,7 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) MOVMuxContext *mov = s->priv_data; MOVTrack *track = &mov->tracks[tracknum]; - AVPacket pkt = { .stream_index = tracknum, .flags = AV_PKT_FLAG_KEY }; + AVPacket *pkt = mov->pkt; int i, len; track->mode = mov->mode; @@ -5823,13 +6157,16 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) } #endif + pkt->stream_index = tracknum; + pkt->flags = AV_PKT_FLAG_KEY; + for (i = 0; i < s->nb_chapters; i++) { AVChapter *c = s->chapters[i]; AVDictionaryEntry *t; int64_t end = av_rescale_q(c->end, c->time_base, (AVRational){1,MOV_TIMESCALE}); - pkt.pts = pkt.dts = av_rescale_q(c->start, c->time_base, (AVRational){1,MOV_TIMESCALE}); - pkt.duration = end - pkt.dts; + pkt->pts = pkt->dts = av_rescale_q(c->start, c->time_base, (AVRational){1,MOV_TIMESCALE}); + pkt->duration = end - pkt->dts; if ((t = av_dict_get(c->metadata, "title", NULL, 0))) { static const char encd[12] = { @@ -5837,18 +6174,22 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) 'e', 'n', 'c', 'd', 0x00, 0x00, 0x01, 0x00 }; len = strlen(t->value); - pkt.size = len + 2 + 12; - pkt.data = av_malloc(pkt.size); - if (!pkt.data) + pkt->size = len + 2 + 12; + pkt->data = av_malloc(pkt->size); + if (!pkt->data) { + av_packet_unref(pkt); return AVERROR(ENOMEM); - AV_WB16(pkt.data, len); - memcpy(pkt.data + 2, t->value, len); - memcpy(pkt.data + len + 2, encd, sizeof(encd)); - ff_mov_write_packet(s, &pkt); - av_freep(&pkt.data); + } + AV_WB16(pkt->data, len); + memcpy(pkt->data + 2, t->value, len); + memcpy(pkt->data + len + 2, encd, sizeof(encd)); + ff_mov_write_packet(s, pkt); + av_freep(&pkt->data); } } + av_packet_unref(mov->pkt); + return 0; } @@ -5864,12 +6205,13 @@ static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, AVTimecode tc) { - int ret; MOVMuxContext *mov = s->priv_data; MOVTrack *track = &mov->tracks[index]; AVStream *src_st = s->streams[src_index]; - AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4}; + uint8_t data[4]; + AVPacket *pkt = mov->pkt; AVRational rate = find_fps(s, src_st); + int ret; /* tmcd track based on video stream */ track->mode = mov->mode; @@ -5888,15 +6230,16 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde return AVERROR(ENOMEM); track->par->codec_type = AVMEDIA_TYPE_DATA; track->par->codec_tag = track->tag; - track->st->avg_frame_rate = av_inv_q(rate); + track->st->avg_frame_rate = rate; /* the tmcd track just contains one packet with the frame number */ - pkt.data = av_malloc(pkt.size); - if (!pkt.data) - return AVERROR(ENOMEM); - AV_WB32(pkt.data, tc.start); - ret = ff_mov_write_packet(s, &pkt); - av_free(pkt.data); + pkt->data = data; + pkt->stream_index = index; + pkt->flags = AV_PKT_FLAG_KEY; + pkt->size = 4; + AV_WB32(pkt->data, tc.start); + ret = ff_mov_write_packet(s, pkt); + av_packet_unref(pkt); return ret; } @@ -5958,10 +6301,13 @@ static void mov_free(AVFormatContext *s) MOVMuxContext *mov = s->priv_data; int i; + av_packet_free(&mov->pkt); + + if (!mov->tracks) + return; + if (mov->chapter_track) { - if (mov->tracks[mov->chapter_track].par) - av_freep(&mov->tracks[mov->chapter_track].par->extradata); - av_freep(&mov->tracks[mov->chapter_track].par); + avcodec_parameters_free(&mov->tracks[mov->chapter_track].par); } for (i = 0; i < mov->nb_streams; i++) { @@ -5971,15 +6317,22 @@ static void mov_free(AVFormatContext *s) av_freep(&mov->tracks[i].par); av_freep(&mov->tracks[i].cluster); av_freep(&mov->tracks[i].frag_info); - av_packet_unref(&mov->tracks[i].cover_image); + av_packet_free(&mov->tracks[i].cover_image); + if (mov->tracks[i].eac3_priv) { + struct eac3_info *info = mov->tracks[i].eac3_priv; + av_packet_free(&info->pkt); + av_freep(&mov->tracks[i].eac3_priv); + } if (mov->tracks[i].vos_len) av_freep(&mov->tracks[i].vos_data); ff_mov_cenc_free(&mov->tracks[i].cenc); + ffio_free_dyn_buf(&mov->tracks[i].mdat_buf); } av_freep(&mov->tracks); + ffio_free_dyn_buf(&mov->mdat_buf); } static uint32_t rgb_to_yuv(uint32_t rgb) @@ -6033,12 +6386,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; @@ -6050,7 +6404,6 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track, static int mov_init(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; - AVDictionaryEntry *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); int i, ret; mov->fc = s; @@ -6058,15 +6411,15 @@ static int mov_init(AVFormatContext *s) /* Default mode == MP4 */ mov->mode = MODE_MP4; - if (s->oformat) { - if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP; - else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3GP|MODE_3G2; - 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; - else if (!strcmp("f4v", s->oformat->name)) mov->mode = MODE_F4V; - } +#define IS_MODE(muxer, config) (CONFIG_ ## config ## _MUXER && !strcmp(#muxer, s->oformat->name)) + if (IS_MODE(3gp, TGP)) mov->mode = MODE_3GP; + else if (IS_MODE(3g2, TG2)) mov->mode = MODE_3GP|MODE_3G2; + else if (IS_MODE(mov, MOV)) mov->mode = MODE_MOV; + else if (IS_MODE(psp, PSP)) mov->mode = MODE_PSP; + else if (IS_MODE(ipod, IPOD)) mov->mode = MODE_IPOD; + else if (IS_MODE(ismv, ISMV)) mov->mode = MODE_ISM; + else if (IS_MODE(f4v, F4V)) mov->mode = MODE_F4V; +#undef IS_MODE if (mov->flags & FF_MOV_FLAG_DELAY_MOOV) mov->flags |= FF_MOV_FLAG_EMPTY_MOOV; @@ -6087,6 +6440,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"); @@ -6113,12 +6469,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; @@ -6156,6 +6521,9 @@ static int mov_init(AVFormatContext *s) if ( mov->write_tmcd == -1 && (mov->mode == MODE_MOV || mov->mode == MODE_MP4) || mov->write_tmcd == 1) { + AVDictionaryEntry *global_tcr = av_dict_get(s->metadata, "timecode", + NULL, 0); + /* +1 tmcd track for each video stream with a timecode */ for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -6184,6 +6552,10 @@ static int mov_init(AVFormatContext *s) mov->nb_streams += mov->nb_meta_tmcd; } + mov->pkt = av_packet_alloc(); + if (!mov->pkt) + return AVERROR(ENOMEM); + // Reserve an extra stream for chapters for the case where chapters // are written in the trailer mov->tracks = av_mallocz_array((mov->nb_streams + 1), sizeof(*mov->tracks)); @@ -6221,7 +6593,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) { @@ -6249,6 +6621,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) @@ -6289,6 +6663,11 @@ static int mov_init(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "VP8 muxing is currently not supported.\n"); return AVERROR_PATCHWELCOME; } + if (is_cover_image(st)) { + track->cover_image = av_packet_alloc(); + if (!track->cover_image) + return AVERROR(ENOMEM); + } } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { track->timescale = st->codecpar->sample_rate; if (!st->codecpar->frame_size && !av_get_bits_per_sample(st->codecpar->codec_id)) { @@ -6323,12 +6702,14 @@ 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)); return AVERROR(EINVAL); } - if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + if (track->par->codec_id != AV_CODEC_ID_OPUS && + s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(s, AV_LOG_ERROR, "%s in MP4 support is experimental, add " "'-strict %d' if you want to use it.\n", @@ -6345,10 +6726,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); @@ -6368,7 +6753,6 @@ static int mov_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; - AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams; if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) @@ -6381,7 +6765,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++) { @@ -6395,11 +6779,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); } } @@ -6466,6 +6851,8 @@ static int mov_write_header(AVFormatContext *s) } if (mov->nb_meta_tmcd) { + const AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, + "timecode", NULL, 0); /* Initialize the tmcd tracks */ for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; @@ -6495,7 +6882,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); @@ -6579,7 +6965,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]; @@ -6655,10 +7041,12 @@ 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); + av_freep(&track->vos_data); + 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; } @@ -6745,11 +7133,12 @@ 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); + } + 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); - } else 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); + res = mov_write_mfra_tag(pb, mov); + if (res < 0) + return res; } } @@ -6771,6 +7160,7 @@ static int mov_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) return ret; } +#if CONFIG_TGP_MUXER || CONFIG_TG2_MUXER static const AVCodecTag codec_3gp_tags[] = { { AV_CODEC_ID_H263, MKTAG('s','2','6','3') }, { AV_CODEC_ID_H264, MKTAG('a','v','c','1') }, @@ -6781,42 +7171,50 @@ static const AVCodecTag codec_3gp_tags[] = { { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') }, { AV_CODEC_ID_NONE, 0 }, }; +static const AVCodecTag *const codec_3gp_tags_list[] = { codec_3gp_tags, NULL }; +#endif -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 }, +static 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_ALAC, MKTAG('a', 'l', 'a', 'c') }, + { 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 }, }; +#if CONFIG_MP4_MUXER || CONFIG_PSP_MUXER +static const AVCodecTag *const mp4_codec_tags_list[] = { codec_mp4_tags, NULL }; +#endif -const AVCodecTag codec_ism_tags[] = { +static const AVCodecTag codec_ism_tags[] = { { AV_CODEC_ID_WMAPRO , MKTAG('w', 'm', 'a', ' ') }, { AV_CODEC_ID_NONE , 0 }, }; @@ -6858,7 +7256,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, @@ -6879,7 +7277,7 @@ AVOutputFormat ff_tgp_muxer = { .write_trailer = mov_write_trailer, .deinit = mov_free, .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 }, + .codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, .priv_class = &tgp_muxer_class, }; @@ -6901,7 +7299,7 @@ AVOutputFormat ff_mp4_muxer = { .write_trailer = mov_write_trailer, .deinit = mov_free, .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, 0 }, + .codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, .priv_class = &mp4_muxer_class, }; @@ -6922,7 +7320,7 @@ AVOutputFormat ff_psp_muxer = { .write_trailer = mov_write_trailer, .deinit = mov_free, .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, 0 }, + .codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, .priv_class = &psp_muxer_class, }; @@ -6942,7 +7340,7 @@ AVOutputFormat ff_tg2_muxer = { .write_trailer = mov_write_trailer, .deinit = mov_free, .flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, - .codec_tag = (const AVCodecTag* const []){ codec_3gp_tags, 0 }, + .codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, .priv_class = &tg2_muxer_class, };