X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=72a6e12d5280c6423cb2be06e263aa65151cb849;hb=2d62e06ff6a9f3dbd78136c1dc4a315a727c6f00;hp=5b1e66c89736a0e30712a2243a1fdfe451422aa5;hpb=f75ba4c2d632a613df9eb40e8ae7ac9fb162b67e;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 5b1e66c8973..72a6e12d528 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -30,6 +30,7 @@ #include "riff.h" #include "avio.h" #include "isom.h" +#include "av1.h" #include "avc.h" #include "libavcodec/ac3_parser_internal.h" #include "libavcodec/dnxhddata.h" @@ -98,6 +99,10 @@ static const AVOption options[] = { { "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM }, { "use_stream_ids_as_track_ids", "use stream ids as track ids", offsetof(MOVMuxContext, use_stream_ids_as_track_ids), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "write_tmcd", "force or disable writing tmcd", offsetof(MOVMuxContext, write_tmcd), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "write_prft", "Write producer reference time box with specified time source", offsetof(MOVMuxContext, write_prft), AV_OPT_TYPE_INT, {.i64 = MOV_PRFT_NONE}, 0, MOV_PRFT_NB-1, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, + { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_WALLCLOCK}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, + { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_PTS}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, + { "empty_hdlr_name", "write zero-length name string in hdlr atoms within mdia and minf atoms", offsetof(MOVMuxContext, empty_hdlr_name), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -140,6 +145,22 @@ static int co64_required(const MOVTrack *track) return 0; } +static int is_cover_image(const AVStream *st) +{ + /* Eg. AV_DISPOSITION_ATTACHED_PIC | AV_DISPOSITION_TIMED_THUMBNAILS + * is encoded as sparse video track */ + return st && st->disposition == AV_DISPOSITION_ATTACHED_PIC; +} + +static int rtp_hinting_needed(const AVStream *st) +{ + /* Add hint tracks for each real audio and video stream */ + if (is_cover_image(st)) + return 0; + return st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || + st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO; +} + /* Chunk offset atom */ static int mov_write_stco_tag(AVIOContext *pb, MOVTrack *track) { @@ -381,7 +402,7 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) if (avpriv_ac3_parse_header(&hdr, pkt->data, pkt->size) < 0) { /* drop the packets until we see a good one */ if (!track->entry) { - av_log(mov, AV_LOG_WARNING, "Dropping invalid packet from start of the stream\n"); + av_log(mov->fc, AV_LOG_WARNING, "Dropping invalid packet from start of the stream\n"); ret = 0; } else ret = AVERROR_INVALIDDATA; @@ -409,7 +430,7 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) if (hdr->substreamid == info->num_ind_sub + 1) { //info->num_ind_sub++; - avpriv_request_sample(track->par, "Multiple independent substreams"); + avpriv_request_sample(mov->fc, "Multiple independent substreams"); ret = AVERROR_PATCHWELCOME; goto end; } else if (hdr->substreamid < info->num_ind_sub || @@ -417,6 +438,12 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) info->ec3_done = 1; goto concatenate; } + } else { + if (hdr->substreamid != 0) { + avpriv_request_sample(mov->fc, "Multiple non EAC3 independent substreams"); + ret = AVERROR_PATCHWELCOME; + goto end; + } } /* fill the info needed for the "dec3" atom */ @@ -491,10 +518,7 @@ concatenate: if (info->num_blocks != 6) goto end; av_packet_unref(pkt); - ret = av_packet_ref(pkt, &info->pkt); - if (ret < 0) - goto end; - av_packet_unref(&info->pkt); + av_packet_move_ref(pkt, &info->pkt); info->num_blocks = 0; } ret = pkt->size; @@ -999,7 +1023,7 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex uint32_t tag = track->tag; if (track->mode == MODE_MOV) { - if (track->timescale > UINT16_MAX) { + if (track->timescale > UINT16_MAX || !track->par->channels) { if (mov_get_lpcm_flags(track->par->codec_id)) tag = AV_RL32("lpcm"); version = 2; @@ -1140,6 +1164,16 @@ static int mov_write_d263_tag(AVIOContext *pb) return 0xf; } +static int mov_write_av1c_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "av1C"); + ff_isom_write_av1c(pb, track->vos_data, track->vos_len); + return update_size(pb, pos); +} + static int mov_write_avcc_tag(AVIOContext *pb, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1548,10 +1582,20 @@ static int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) return tag; } +static const AVCodecTag codec_cover_image_tags[] = { + { AV_CODEC_ID_MJPEG, 0xD }, + { AV_CODEC_ID_PNG, 0xE }, + { AV_CODEC_ID_BMP, 0x1B }, + { AV_CODEC_ID_NONE, 0 }, +}; + static int mov_find_codec_tag(AVFormatContext *s, MOVTrack *track) { int tag; + if (is_cover_image(track->st)) + return ff_codec_get_tag(codec_cover_image_tags, track->par->codec_id); + if (track->mode == MODE_MP4 || track->mode == MODE_PSP) tag = track->par->codec_tag; else if (track->mode == MODE_ISM) @@ -1976,6 +2020,8 @@ static int mov_write_video_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *tr mov_write_uuid_tag_ipod(pb); } else if (track->par->codec_id == AV_CODEC_ID_VP9) { mov_write_vpcc_tag(mov->fc, pb, track); + } else if (track->par->codec_id == AV_CODEC_ID_AV1) { + mov_write_av1c_tag(pb, track); } else if (track->par->codec_id == AV_CODEC_ID_VC1 && track->vos_len > 0) mov_write_dvc1_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_VP6F || @@ -2319,9 +2365,9 @@ static int mov_preroll_write_stbl_atoms(AVIOContext *pb, MOVTrack *track) decoded. */ if (roll_samples_remaining > 0) distance = 0; - /* Verify distance is a minimum of 2 (60ms) packets and a maximum of - 32 (2.5ms) packets. */ - av_assert0(distance == 0 || (distance >= 2 && distance <= 32)); + /* Verify distance is a maximum of 32 (2.5ms) packets. */ + if (distance > 32) + return AVERROR_INVALIDDATA; if (i && distance == sgpd_entries[entries].roll_distance) { sgpd_entries[entries].count++; } else { @@ -2525,6 +2571,7 @@ static int is_clcp_track(MOVTrack *track) static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) { + MOVMuxContext *mov = s->priv_data; const char *hdlr, *descr = NULL, *hdlr_type = NULL; int64_t pos = avio_tell(pb); @@ -2573,12 +2620,15 @@ static int mov_write_hdlr_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra // of the track. So if an alternate handler description is // specified, use it. AVDictionaryEntry *t; - t = av_dict_get(track->st->metadata, "handler", NULL, 0); + t = av_dict_get(track->st->metadata, "handler_name", NULL, 0); if (t && utf8len(t->value)) descr = t->value; } } + if (mov->empty_hdlr_name) /* expressly allowed by QTFF and not prohibited in ISO 14496-12 8.4.3.3 */ + descr = ""; + avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "hdlr"); avio_wb32(pb, 0); /* Version & flags */ @@ -2637,7 +2687,7 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext } else if (track->tag == MKTAG('g','p','m','d')) { mov_write_gmhd_tag(pb, track); } - if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ + if (track->mode == MODE_MOV) /* ISO 14496-12 8.4.3.1 specifies hdlr only within mdia or meta boxes */ mov_write_hdlr_tag(s, pb, NULL); mov_write_dinf_tag(pb); if ((ret = mov_write_stbl_tag(s, pb, mov, track)) < 0) @@ -3027,7 +3077,7 @@ static int mov_write_track_udta_tag(AVIOContext *pb, MOVMuxContext *mov, if (ret < 0) return ret; - if (mov->mode & MODE_MP4) + 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) { @@ -3416,6 +3466,33 @@ static int mov_write_int8_metadata(AVFormatContext *s, AVIOContext *pb, return size; } +static int mov_write_covr(AVIOContext *pb, AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + int64_t pos = 0; + int i; + + for (i = 0; i < s->nb_streams; i++) { + MOVTrack *trk = &mov->tracks[i]; + + if (!is_cover_image(trk->st) || trk->cover_image.size <= 0) + continue; + + if (!pos) { + pos = avio_tell(pb); + avio_wb32(pb, 0); + ffio_wfourcc(pb, "covr"); + } + 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); + } + + return pos ? update_size(pb, pos) : 0; +} + /* iTunes meta data list */ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) @@ -3435,7 +3512,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, } mov_write_string_metadata(s, pb, "\251cmt", "comment" , 1); mov_write_string_metadata(s, pb, "\251gen", "genre" , 1); - mov_write_string_metadata(s, pb, "\251cpy", "copyright", 1); + mov_write_string_metadata(s, pb, "cprt", "copyright", 1); mov_write_string_metadata(s, pb, "\251grp", "grouping" , 1); mov_write_string_metadata(s, pb, "\251lyr", "lyrics" , 1); mov_write_string_metadata(s, pb, "desc", "description",1); @@ -3450,6 +3527,7 @@ static int mov_write_ilst_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_int8_metadata (s, pb, "hdvd", "hd_video", 1); mov_write_int8_metadata (s, pb, "pgap", "gapless_playback",1); mov_write_int8_metadata (s, pb, "cpil", "compilation", 1); + mov_write_covr(pb, s); mov_write_trkn_tag(pb, mov, s, 0); // track number mov_write_trkn_tag(pb, mov, s, 1); // disc number mov_write_tmpo_tag(pb, s); @@ -3940,7 +4018,7 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat AVStream *st = track->st; AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0); - if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { + if (track->par->codec_type == AVMEDIA_TYPE_VIDEO && !is_cover_image(st)) { type = "video"; } else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) { type = "audio"; @@ -4460,6 +4538,49 @@ static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov, return 0; } +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; + + /* PRFT should be associated with at most one track. So, choosing only the + * first track. */ + if (tracks > 0) + return 0; + first_track = &(mov->tracks[0]); + + if (!first_track->entry) { + av_log(mov->fc, AV_LOG_WARNING, "Unable to write PRFT, no entries in the track\n"); + return 0; + } + + if (first_track->cluster[0].pts == AV_NOPTS_VALUE) { + av_log(mov->fc, AV_LOG_WARNING, "Unable to write PRFT, first PTS is invalid\n"); + return 0; + } + + if (mov->write_prft == MOV_PRFT_SRC_WALLCLOCK) { + 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); + ntp_ts = ff_get_formatted_ntp_time(pts_us + NTP_OFFSET_US); + } else { + av_log(mov->fc, AV_LOG_WARNING, "Unsupported PRFT box configuration: %d\n", + mov->write_prft); + return 0; + } + + avio_wb32(pb, 0); // Size place holder + ffio_wfourcc(pb, "prft"); // Type + avio_w8(pb, 1); // Version + avio_wb24(pb, 0); // 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 + return update_size(pb, pos); +} + static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, int64_t mdat_size) { @@ -4474,6 +4595,9 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); + if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB) + mov_write_prft_tag(pb, mov, tracks); + if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX || !(mov->flags & FF_MOV_FLAG_SKIP_TRAILER) || mov->ism_lookahead) { @@ -4560,6 +4684,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) 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) @@ -4708,6 +4834,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s) int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0; 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) video_streams_nb++; else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) @@ -4897,7 +5025,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) int buf_size, moov_size; for (i = 0; i < mov->nb_streams; i++) - if (!mov->tracks[i].entry) + if (!mov->tracks[i].entry && !is_cover_image(mov->tracks[i].st)) break; /* Don't write the initial moov unless all tracks have data */ if (i < mov->nb_streams && !force) @@ -5154,6 +5282,11 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) else samples_in_chunk = 1; + if (samples_in_chunk < 1) { + av_log(s, AV_LOG_ERROR, "fatal error, input packet contains no samples\n"); + return AVERROR_PATCHWELCOME; + } + /* copy extradata if it exists */ if (trk->vos_len == 0 && par->extradata_size > 0 && !TAG_IS_AVCI(trk->tag) && @@ -5204,6 +5337,13 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } 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); + avio_write(pb, reformatted_data, size); + } else { + size = ff_av1_filter_obus(pb, pkt->data, pkt->size); + } #if CONFIG_AC3_PARSER } else if (par->codec_id == AV_CODEC_ID_EAC3) { size = handle_eac3(mov, pkt, trk); @@ -5258,6 +5398,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) trk->cluster[trk->entry].size = size; trk->cluster[trk->entry].entries = samples_in_chunk; trk->cluster[trk->entry].dts = pkt->dts; + trk->cluster[trk->entry].pts = pkt->pts; if (!trk->entry && trk->start_dts != AV_NOPTS_VALUE) { if (!trk->frag_discont) { /* First packet of a new fragment. We already wrote the duration @@ -5396,6 +5537,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) 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); @@ -5476,13 +5618,32 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s, static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) { + MOVMuxContext *mov = s->priv_data; + MOVTrack *trk; + if (!pkt) { mov_flush_fragment(s, 1); return 1; + } + + trk = &mov->tracks[pkt->stream_index]; + + if (is_cover_image(trk->st)) { + int ret; + + if (trk->st->nb_frames >= 1) { + if (trk->st->nb_frames == 1) + av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," + " ignoring.\n", pkt->stream_index); + return 0; + } + + if ((ret = av_packet_ref(&trk->cover_image, pkt)) < 0) + return ret; + + return 0; } else { int i; - MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; if (!pkt->size) return mov_write_single_packet(s, pkt); /* Passthrough. */ @@ -5729,7 +5890,8 @@ static void enable_tracks(AVFormatContext *s) AVStream *st = s->streams[i]; if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN || - st->codecpar->codec_type >= AVMEDIA_TYPE_NB) + st->codecpar->codec_type >= AVMEDIA_TYPE_NB || + is_cover_image(st)) continue; if (first[st->codecpar->codec_type] < 0) @@ -5772,6 +5934,7 @@ 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); if (mov->tracks[i].vos_len) av_freep(&mov->tracks[i].vos_data); @@ -5883,7 +6046,7 @@ static int mov_init(AVFormatContext *s) /* Set other implicit flags immediately */ if (mov->mode == MODE_ISM) mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF | - FF_MOV_FLAG_FRAGMENT; + FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; if (mov->flags & FF_MOV_FLAG_DASH) mov->flags |= FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_DEFAULT_BASE_MOOF; @@ -5944,14 +6107,9 @@ static int mov_init(AVFormatContext *s) mov->chapter_track = mov->nb_streams++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Add hint tracks for each audio and video stream */ - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + for (i = 0; i < s->nb_streams; i++) + if (rtp_hinting_needed(s->streams[i])) mov->nb_streams++; - } - } } if ( mov->write_tmcd == -1 && (mov->mode == MODE_MOV || mov->mode == MODE_MP4) @@ -6077,11 +6235,20 @@ static int mov_init(AVFormatContext *s) pix_fmt == AV_PIX_FMT_MONOWHITE || pix_fmt == AV_PIX_FMT_MONOBLACK; } - if (track->par->codec_id == AV_CODEC_ID_VP9) { + if (track->par->codec_id == AV_CODEC_ID_VP9 || + track->par->codec_id == AV_CODEC_ID_AV1) { if (track->mode != MODE_MP4) { - av_log(s, AV_LOG_ERROR, "VP9 only supported in MP4.\n"); + av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id)); return AVERROR(EINVAL); } + if (track->par->codec_id == AV_CODEC_ID_AV1 && + s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + av_log(s, AV_LOG_ERROR, + "av1 in MP4 support is experimental, add " + "'-strict %d' if you want to use it.\n", + FF_COMPLIANCE_EXPERIMENTAL); + return AVERROR_EXPERIMENTAL; + } } else if (track->par->codec_id == AV_CODEC_ID_VP8) { /* altref frames handling is not defined in the spec as of version v1.0, * so just forbid muxing VP8 streams altogether until a new version does */ @@ -6174,15 +6341,10 @@ static int mov_write_header(AVFormatContext *s) nb_tracks++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Add hint tracks for each audio and video stream */ hint_track = nb_tracks; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + for (i = 0; i < s->nb_streams; i++) + if (rtp_hinting_needed(s->streams[i])) nb_tracks++; - } - } } if (mov->mode == MODE_MOV || mov->mode == MODE_MP4) @@ -6260,11 +6422,8 @@ static int mov_write_header(AVFormatContext *s) return ret; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - /* Initialize the hint tracks for each audio and video stream */ for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + if (rtp_hinting_needed(s->streams[i])) { if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0) return ret; hint_track++; @@ -6604,6 +6763,7 @@ const AVCodecTag codec_mp4_tags[] = { { 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') },