X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=ee28f0ed7b6eaa6a03fca08636a5c9b42cdc3663;hb=eb45a2188fb8d018bb5fbb0b54706c9e7b6a3749;hp=f9fc4d5c074f33b66b17054086cf022c3e48f31d;hpb=1aec1fbcc5952e1c2f9820e032ad2e5f9bf1a670;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index f9fc4d5c074..ee28f0ed7b6 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -78,7 +78,7 @@ static const AVOption options[] = { { "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" }, @@ -365,7 +365,7 @@ static int mov_write_ac3_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *trac } struct eac3_info { - AVPacket pkt; + AVPacket *pkt; uint8_t ec3_done; uint8_t num_blocks; @@ -407,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) { @@ -511,20 +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; + 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; @@ -576,7 +579,7 @@ static int mov_write_eac3_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra } } flush_put_bits(&pbc); - size = put_bits_count(&pbc) >> 3; + size = put_bytes_output(&pbc); avio_wb32(pb, size + 8); ffio_wfourcc(pb, "dec3"); @@ -633,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"); @@ -669,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 @@ -752,6 +797,7 @@ static int mov_write_dfla_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 */ @@ -762,12 +808,22 @@ static int mov_write_dops_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra /* 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); } @@ -1049,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); @@ -1197,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; } @@ -1380,27 +1459,9 @@ static int mov_get_dv_codec_tag(AVFormatContext *s, MOVTrack *track) return tag; } -static AVRational find_fps(AVFormatContext *s, AVStream *st) -{ - AVRational rate = st->avg_frame_rate; - -#if FF_API_LAVF_AVCTX - FF_DISABLE_DEPRECATION_WARNINGS - rate = av_inv_q(st->codec->time_base); - if (av_timecode_check_frame_rate(rate) < 0) { - av_log(s, AV_LOG_DEBUG, "timecode: tbc=%d/%d invalid, fallback on %d/%d\n", - rate.num, rate.den, st->avg_frame_rate.num, st->avg_frame_rate.den); - rate = st->avg_frame_rate; - } - FF_ENABLE_DEPRECATION_WARNINGS -#endif - - return rate; -} - static int defined_frame_rate(AVFormatContext *s, AVStream *st) { - AVRational rational_framerate = find_fps(s, st); + AVRational rational_framerate = st->avg_frame_rate; int rate = 0; if (rational_framerate.den != 0) rate = av_q2d(rational_framerate); @@ -1588,6 +1649,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 || @@ -1712,6 +1777,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 @@ -1724,6 +1790,10 @@ 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); } @@ -1904,7 +1974,7 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) // Ref (MP4): ISO/IEC 14496-12:2012 const uint8_t *icc_profile; - int icc_profile_size; + 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); @@ -1951,7 +2021,6 @@ static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track) side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL); if (!side_data) { - av_log(NULL, AV_LOG_VERBOSE, "Not writing 'clli' atom. No content light level info.\n"); return 0; } content_light_metadata = (const AVContentLightMetadata*)side_data; @@ -1978,7 +2047,6 @@ static int mov_write_mdcv_tag(AVIOContext *pb, MOVTrack *track) 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) { - av_log(NULL, AV_LOG_VERBOSE, "Not writing 'mdcv' atom. Missing mastering metadata.\n"); return 0; } @@ -2027,6 +2095,7 @@ static void find_compressor(char * compressor_name, int len, 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; @@ -2147,13 +2216,6 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex track->par->codec_id != AV_CODEC_ID_DNXHD) { int field_order = track->par->field_order; -#if FF_API_LAVF_AVCTX - FF_DISABLE_DEPRECATION_WARNINGS - if (field_order != track->st->codec->field_order && track->st->codec->field_order != AV_FIELD_UNKNOWN) - field_order = track->st->codec->field_order; - FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (field_order != AV_FIELD_UNKNOWN) mov_write_fiel_tag(pb, track, field_order); } @@ -2164,11 +2226,17 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex 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->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"); + 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); @@ -2179,7 +2247,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex 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);; + av_stream_get_side_data(track->st, AV_PKT_DATA_DOVI_CONF, NULL); if (stereo_3d) mov_write_st3d_tag(s, pb, stereo_3d); @@ -2201,6 +2269,10 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex 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) @@ -2257,18 +2329,11 @@ static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track) AVDictionaryEntry *t = NULL; if (!track->st->avg_frame_rate.num || !track->st->avg_frame_rate.den) { -#if FF_API_LAVF_AVCTX - FF_DISABLE_DEPRECATION_WARNINGS - frame_duration = av_rescale(track->timescale, track->st->codec->time_base.num, track->st->codec->time_base.den); - nb_frames = ROUNDED_DIV(track->st->codec->time_base.den, track->st->codec->time_base.num); - FF_ENABLE_DEPRECATION_WARNINGS -#else av_log(NULL, AV_LOG_ERROR, "avg_frame_rate not set for tmcd track.\n"); 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) { @@ -2819,21 +2884,50 @@ 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) +static void get_pts_range(MOVMuxContext *mov, MOVTrack *track, + int64_t *start, int64_t *end) { - if (track->tag == MKTAG('t','m','c','d')) { + 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. - return av_rescale(calc_pts_duration(mov, &mov->tracks[track->src_track]), - track->timescale, mov->tracks[track->src_track].timescale); + 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) { - return track->end_pts - (track->start_dts + track->start_cts); + *start = track->start_dts + track->start_cts; + *end = track->end_pts; + return; } - return track->track_duration; + *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, @@ -2917,11 +3011,11 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, 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) @@ -2973,23 +3067,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); } @@ -3060,7 +3140,7 @@ 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(calc_pts_duration(mov, track), + 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; @@ -3620,7 +3700,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) { @@ -3628,11 +3708,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; @@ -4061,7 +4141,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); @@ -4129,8 +4210,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, @@ -4156,6 +4235,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 }; @@ -4171,17 +4252,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"); @@ -4798,28 +4871,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) @@ -4875,7 +4960,7 @@ 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 has_h264 = 0, has_av1 = 0, has_video = 0; int i; for (i = 0; i < s->nb_streams; i++) { @@ -4886,6 +4971,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) 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; } avio_wb32(pb, 0); /* size */ @@ -4909,6 +4996,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) 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"); @@ -5181,15 +5270,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; } } } @@ -5432,7 +5525,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) AVProducerReferenceTime *prft; unsigned int samples_in_chunk = 0; int size = pkt->size, ret = 0, offset = 0; - int prft_size; + size_t prft_size; uint8_t *reformatted_data = NULL; ret = check_pkt(s, pkt); @@ -5508,9 +5601,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) 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 || - par->codec_id == AV_CODEC_ID_H264 || - par->codec_id == AV_CODEC_ID_HEVC) && !trk->vos_len && + 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; @@ -5584,6 +5675,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) { @@ -5603,11 +5710,12 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (trk->entry >= trk->cluster_capacity) { unsigned new_capacity = trk->entry + MOV_INDEX_CLUSTER_SIZE; - if (av_reallocp_array(&trk->cluster, new_capacity, - sizeof(*trk->cluster))) { + 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; } @@ -5770,7 +5878,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) 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; + 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); @@ -5829,20 +5937,20 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *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; } @@ -5869,7 +5977,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; @@ -5955,7 +6063,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; @@ -6017,13 +6125,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] = { @@ -6031,18 +6142,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; } @@ -6052,18 +6167,19 @@ static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_ int ret; /* compute the frame number */ - ret = av_timecode_init_from_string(tc, find_fps(s, s->streams[src_index]), tcstr, s); + ret = av_timecode_init_from_string(tc, s->streams[src_index]->avg_frame_rate, tcstr, s); return ret; } 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}; - AVRational rate = find_fps(s, src_st); + uint8_t data[4]; + AVPacket *pkt = mov->pkt; + AVRational rate = src_st->avg_frame_rate; + int ret; /* tmcd track based on video stream */ track->mode = mov->mode; @@ -6082,15 +6198,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; } @@ -6152,10 +6269,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++) { @@ -6165,20 +6285,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_unref(&info->pkt); + 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) @@ -6250,7 +6372,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; @@ -6258,15 +6379,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; @@ -6368,6 +6489,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]; @@ -6396,6 +6520,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)); @@ -6503,6 +6631,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)) { @@ -6588,7 +6721,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) @@ -6687,6 +6819,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]; @@ -6707,8 +6841,6 @@ static int mov_write_header(AVFormatContext *s) } } - avio_flush(pb); - if (mov->flags & FF_MOV_FLAG_ISML) mov_write_isml_manifest(pb, mov, s); @@ -6721,6 +6853,8 @@ static int mov_write_header(AVFormatContext *s) mov->reserved_header_pos = avio_tell(pb); } + avio_flush(pb); + return 0; } @@ -6875,6 +7009,7 @@ static int mov_write_trailer(AVFormatContext *s) AVCodecParameters *par = track->par; track->vos_len = par->extradata_size; + 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); @@ -6969,7 +7104,9 @@ static int mov_write_trailer(AVFormatContext *s) } 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; } } @@ -6991,6 +7128,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') }, @@ -7001,8 +7139,10 @@ 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[] = { +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') }, @@ -7019,6 +7159,7 @@ const AVCodecTag codec_mp4_tags[] = { { 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') }, @@ -7037,8 +7178,11 @@ const AVCodecTag codec_mp4_tags[] = { { 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 }, }; @@ -7065,7 +7209,7 @@ static const AVCodecTag codec_f4v_tags[] = { #if CONFIG_MOV_MUXER MOV_CLASS(mov) -AVOutputFormat ff_mov_muxer = { +const AVOutputFormat ff_mov_muxer = { .name = "mov", .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), .extensions = "mov", @@ -7088,7 +7232,7 @@ AVOutputFormat ff_mov_muxer = { #endif #if CONFIG_TGP_MUXER MOV_CLASS(tgp) -AVOutputFormat ff_tgp_muxer = { +const AVOutputFormat ff_tgp_muxer = { .name = "3gp", .long_name = NULL_IF_CONFIG_SMALL("3GP (3GPP file format)"), .extensions = "3gp", @@ -7101,14 +7245,14 @@ 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, }; #endif #if CONFIG_MP4_MUXER MOV_CLASS(mp4) -AVOutputFormat ff_mp4_muxer = { +const AVOutputFormat ff_mp4_muxer = { .name = "mp4", .long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"), .mime_type = "video/mp4", @@ -7123,14 +7267,14 @@ 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, }; #endif #if CONFIG_PSP_MUXER MOV_CLASS(psp) -AVOutputFormat ff_psp_muxer = { +const AVOutputFormat ff_psp_muxer = { .name = "psp", .long_name = NULL_IF_CONFIG_SMALL("PSP MP4 (MPEG-4 Part 14)"), .extensions = "mp4,psp", @@ -7144,14 +7288,14 @@ 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, }; #endif #if CONFIG_TG2_MUXER MOV_CLASS(tg2) -AVOutputFormat ff_tg2_muxer = { +const AVOutputFormat ff_tg2_muxer = { .name = "3g2", .long_name = NULL_IF_CONFIG_SMALL("3GP2 (3GPP2 file format)"), .extensions = "3g2", @@ -7164,14 +7308,14 @@ 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, }; #endif #if CONFIG_IPOD_MUXER MOV_CLASS(ipod) -AVOutputFormat ff_ipod_muxer = { +const AVOutputFormat ff_ipod_muxer = { .name = "ipod", .long_name = NULL_IF_CONFIG_SMALL("iPod H.264 MP4 (MPEG-4 Part 14)"), .mime_type = "video/mp4", @@ -7192,7 +7336,7 @@ AVOutputFormat ff_ipod_muxer = { #endif #if CONFIG_ISMV_MUXER MOV_CLASS(ismv) -AVOutputFormat ff_ismv_muxer = { +const AVOutputFormat ff_ismv_muxer = { .name = "ismv", .long_name = NULL_IF_CONFIG_SMALL("ISMV/ISMA (Smooth Streaming)"), .mime_type = "video/mp4", @@ -7214,7 +7358,7 @@ AVOutputFormat ff_ismv_muxer = { #endif #if CONFIG_F4V_MUXER MOV_CLASS(f4v) -AVOutputFormat ff_f4v_muxer = { +const AVOutputFormat ff_f4v_muxer = { .name = "f4v", .long_name = NULL_IF_CONFIG_SMALL("F4V Adobe Flash Video"), .mime_type = "application/f4v",