This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 90dd8673ce3a6ad50f0872e6606d578840e7b1c4 Author: James Almer <[email protected]> AuthorDate: Tue Mar 17 09:33:49 2026 -0300 Commit: James Almer <[email protected]> CommitDate: Sun May 17 11:16:56 2026 -0300 avformat/mov: handle all references in tref boxes tref types can have more than one value, as is the case of tmcd in fcp_export8-236.mov, where the single video track references all timecode tracks. Handle them in a generic and extensible way. Signed-off-by: James Almer <[email protected]> --- libavformat/isom.h | 13 ++- libavformat/mov.c | 168 +++++++++++++++++++++++++------ tests/ref/fate/mov-mp4-with-mov-in24-ver | 5 + 3 files changed, 150 insertions(+), 36 deletions(-) diff --git a/libavformat/isom.h b/libavformat/isom.h index d7e138585a..9b5437fb16 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -91,6 +91,12 @@ typedef struct MOVDref { int16_t nlvl_to, nlvl_from; } MOVDref; +typedef struct MovTref { + uint32_t name; + int nb_id; + uint32_t *id; ///< trackID of the referenced track +} MovTref; + typedef struct MOVAtom { uint32_t type; int64_t size; /* total size (excluding the size and type fields) */ @@ -222,9 +228,8 @@ typedef struct MOVStreamContext { unsigned drefs_count; MOVDref *drefs; int dref_id; - unsigned tref_flags; - int tref_id; - int timecode_track; + int nb_tref_tags; + MovTref *tref_tags; int width; ///< tkhd width int height; ///< tkhd height int h_spacing; ///< pasp hSpacing @@ -434,8 +439,6 @@ void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id); #define MOV_SAMPLE_DEPENDENCY_YES 0x1 #define MOV_SAMPLE_DEPENDENCY_NO 0x2 -#define MOV_TREF_FLAG_ENHANCEMENT 0x1 - #define TAG_IS_AVCI(tag) \ ((tag) == MKTAG('a', 'i', '5', 'p') || \ (tag) == MKTAG('a', 'i', '5', 'q') || \ diff --git a/libavformat/mov.c b/libavformat/mov.c index 0d982e5a79..93e419113d 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -2547,6 +2547,58 @@ static int mov_read_dvc1(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_find_tref_id(const MovTref *tag, uint32_t id) +{ + for (int i = 0; i < tag->nb_id; i++) { + if (tag->id[i] == id) + return 1; + } + return 0; +} + +static int mov_add_tref_id(MovTref *tag, uint32_t id) +{ + int ret = mov_find_tref_id(tag, id); + + if (!ret) { + uint32_t *tmp = av_realloc_array(tag->id, tag->nb_id + 1, sizeof(*tag->id)); + if (!tmp) + return AVERROR(ENOMEM); + tag->id = tmp; + tag->id[tag->nb_id++] = id; + } + + return 0; +} + +static MovTref *mov_find_tref_tag(const MOVStreamContext *sc, uint32_t name) +{ + for (int i = 0; i < sc->nb_tref_tags; i++) { + MovTref *entry = &sc->tref_tags[i]; + + if (entry->name == name) + return entry; + } + return NULL; +} + +static MovTref *mov_add_tref_tag(MOVStreamContext *sc, uint32_t name) +{ + MovTref *tag = mov_find_tref_tag(sc, name); + + if (!tag) { + MovTref *tmp = av_realloc_array(sc->tref_tags, sc->nb_tref_tags + 1, + sizeof(*sc->tref_tags)); + if (!tmp) + return NULL; + sc->tref_tags = tmp; + tag = &sc->tref_tags[sc->nb_tref_tags++]; + *tag = (MovTref){ .name = name }; + } + + return tag; +} + static int mov_read_sbas(MOVContext* c, AVIOContext* pb, MOVAtom atom) { AVStream* st; @@ -2562,11 +2614,19 @@ static int mov_read_sbas(MOVContext* c, AVIOContext* pb, MOVAtom atom) av_log(c->fc, AV_LOG_ERROR, "Only a single tref of type sbas is supported\n"); return AVERROR_PATCHWELCOME; } + if (atom.size < 4) + return AVERROR_INVALIDDATA; st = c->fc->streams[c->fc->nb_streams - 1]; sc = st->priv_data; - sc->tref_id = avio_rb32(pb); - sc->tref_flags |= MOV_TREF_FLAG_ENHANCEMENT; + + MovTref *tag = mov_add_tref_tag(sc, atom.type); + if (!tag) + return AVERROR(ENOMEM); + + int ret = mov_add_tref_id(tag, avio_rb32(pb)); + if (ret < 0) + return ret; return 0; } @@ -5210,8 +5270,6 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->codecpar->codec_type = AVMEDIA_TYPE_DATA; sc->ffindex = st->index; c->trak_index = st->index; - sc->tref_flags = 0; - sc->tref_id = -1; sc->refcount = 1; if ((ret = mov_read_default(c, pb, atom)) < 0) @@ -6403,11 +6461,28 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { MOVStreamContext *sc; + int err; if (c->fc->nb_streams < 1) return AVERROR_INVALIDDATA; sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; - sc->timecode_track = avio_rb32(pb); + + if (atom.size % sizeof(uint32_t)) + return AVERROR_INVALIDDATA; + + MovTref *tag = mov_add_tref_tag(sc, atom.type); + if (!tag) + return AVERROR(ENOMEM); + + int nb_timecode_track = atom.size >> 2; + for (int i = 0; i < nb_timecode_track; i++) { + if (avio_feof(pb)) + return AVERROR_INVALIDDATA; + err = mov_add_tref_id(tag, avio_rb32(pb)); + if (err < 0) + return err; + } + return 0; } @@ -10104,6 +10179,9 @@ static void mov_free_stream_context(AVFormatContext *s, AVStream *st) av_freep(&sc->open_key_samples); av_freep(&sc->display_matrix); av_freep(&sc->index_ranges); + for (int i = 0; i < sc->nb_tref_tags; i++) + av_freep(&sc->tref_tags[i].id); + av_freep(&sc->tref_tags); if (sc->extradata) for (int i = 0; i < sc->stsd_count; i++) @@ -10190,9 +10268,11 @@ static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id) for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; MOVStreamContext *sc = st->priv_data; + MovTref *tag = mov_find_tref_tag(sc, MKTAG('t','m','c','d')); - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - sc->timecode_track == tmcd_id) + if (!tag) + continue; + if (mov_find_tref_id(tag, tmcd_id)) return 1; } return 0; @@ -10207,7 +10287,7 @@ static void export_orphan_timecode(AVFormatContext *s) AVStream *st = s->streams[i]; if (st->codecpar->codec_tag == MKTAG('t','m','c','d') && - !tmcd_is_referenced(s, i + 1)) { + !tmcd_is_referenced(s, st->id)) { AVDictionaryEntry *tcr = av_dict_get(st->metadata, "timecode", NULL, 0); if (tcr) { av_dict_set(&s->metadata, "timecode", tcr->value, 0); @@ -10799,17 +10879,16 @@ static int mov_parse_heif_items(AVFormatContext *s) } static AVStream *mov_find_reference_track(AVFormatContext *s, AVStream *st, - int first_index) + uint32_t *tref_id, int nb_tref_id, int first_index) { - MOVStreamContext *sc = st->priv_data; - - if (sc->tref_id < 0) + if (!nb_tref_id) return NULL; for (int i = first_index; i < s->nb_streams; i++) - if (s->streams[i]->index != st->index && - s->streams[i]->id == sc->tref_id) - return s->streams[i]; + for (int j = 0; j < nb_tref_id; j++) + if (s->streams[i]->index != st->index && + s->streams[i]->id == tref_id[j]) + return s->streams[i]; return NULL; } @@ -10827,11 +10906,11 @@ static int mov_parse_lcevc_streams(AVFormatContext *s) AVStream *st = s->streams[i]; AVStream *st_base; MOVStreamContext *sc = st->priv_data; + MovTref *tag = mov_find_tref_tag(sc, MKTAG('s','b','a','s')); int j = 0; /* Find an enhancement stream. */ - if (st->codecpar->codec_id != AV_CODEC_ID_LCEVC || - !(sc->tref_flags & MOV_TREF_FLAG_ENHANCEMENT)) + if (st->codecpar->codec_id != AV_CODEC_ID_LCEVC || !tag) continue; stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_LCEVC, NULL); @@ -10842,7 +10921,7 @@ static int mov_parse_lcevc_streams(AVFormatContext *s) stg->params.lcevc->width = st->codecpar->width; stg->params.lcevc->height = st->codecpar->height; - while (st_base = mov_find_reference_track(s, st, j)) { + while (st_base = mov_find_reference_track(s, st, tag->id, tag->nb_id, j)) { err = avformat_stream_group_add_stream(stg, st_base); if (err < 0) return err; @@ -10963,24 +11042,51 @@ static int mov_read_header(AVFormatContext *s) /* copy timecode metadata from tmcd tracks to the related video streams */ for (i = 0; i < s->nb_streams; i++) { + AVStreamGroup *stg; + const AVDictionaryEntry *tcr; AVStream *st = s->streams[i]; - MOVStreamContext *sc = st->priv_data; - if (sc->timecode_track > 0) { - AVDictionaryEntry *tcr; - int tmcd_st_id = -1; - for (j = 0; j < s->nb_streams; j++) { - MOVStreamContext *sc2 = s->streams[j]->priv_data; - if (sc2->id == sc->timecode_track) - tmcd_st_id = j; - } + if (st->codecpar->codec_tag != MKTAG('t','m','c','d')) + continue; + + stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_TREF, NULL); + if (!stg) + return AVERROR(ENOMEM); + + stg->id = st->id; + tcr = av_dict_get(st->metadata, "timecode", NULL, 0); + + for (int j = 0; j < s->nb_streams; j++) { + AVStream *st2 = s->streams[j]; + MOVStreamContext *sc2 = st2->priv_data; + MovTref *tag = mov_find_tref_tag(sc2, MKTAG('t','m','c','d')); - if (tmcd_st_id < 0 || tmcd_st_id == i) + if (!tag) continue; - tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0); - if (tcr) - av_dict_set(&st->metadata, "timecode", tcr->value, 0); + + for (int k = 0; k < tag->nb_id; k++) { + if (tag->id[k] != st->id) + continue; + + err = avformat_stream_group_add_stream(stg, st2); + if (err < 0) + return err; + + if (tcr) + av_dict_set(&st2->metadata, "timecode", tcr->value, AV_DICT_DONT_OVERWRITE); + } } + + if (!stg->nb_streams) { + ff_remove_stream_group(s, stg); + continue; + } + + err = avformat_stream_group_add_stream(stg, st); + if (err < 0) + return err; + + stg->params.tref->metadata_index = stg->nb_streams - 1; } export_orphan_timecode(s); diff --git a/tests/ref/fate/mov-mp4-with-mov-in24-ver b/tests/ref/fate/mov-mp4-with-mov-in24-ver index b5522454df..450560b149 100644 --- a/tests/ref/fate/mov-mp4-with-mov-in24-ver +++ b/tests/ref/fate/mov-mp4-with-mov-in24-ver @@ -1,3 +1,8 @@ +[STREAM_GROUP] +[STREAM] +codec_name=pcm_s24le +[/STREAM] +[/STREAM_GROUP] [STREAM] codec_name=pcm_s24le [/STREAM] _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
