Commit bd030344b7 for ffmpeg

commit bd030344b7ad54d602ceb30523ddd88329657d52
Author: James Almer <jamrial@gmail.com>
Date:   Tue Oct 20 10:40:22 2026 -0300

    fftools/ffmpeg_demux: automatically apply Alpha layer on Alpha Stream Group input

    Signed-off-by: James Almer <jamrial@gmail.com>

diff --git a/configure b/configure
index 007da1ee00..362e7b12f7 100755
--- a/configure
+++ b/configure
@@ -4355,7 +4355,7 @@ avutil_extralibs="d3d11va_extralibs d3d12va_extralibs mediacodec_extralibs nanos

 # programs
 ffmpeg_deps="avcodec avfilter avformat threads"
-ffmpeg_select="aformat_filter anull_filter atrim_filter crop_filter
+ffmpeg_select="aformat_filter alphamerge_filter anull_filter atrim_filter crop_filter
                format_filter hflip_filter null_filter rotate_filter
                transpose_filter trim_filter vflip_filter"
 ffmpeg_suggest="ole32 psapi shell32"
diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c
index f460d38b30..5409dcdc6c 100644
--- a/fftools/ffmpeg_demux.c
+++ b/fftools/ffmpeg_demux.c
@@ -1942,6 +1942,63 @@ static DemuxStreamGroup *demux_stream_group_alloc(Demuxer *d, AVStreamGroup *stg
     return dsg;
 }

+static int istg_parse_alpha(const OptionsContext *o, Demuxer *d, InputStreamGroup *istg)
+{
+    InputFile *f = &d->f;
+    AVStreamGroup *stg = istg->stg;
+    const AVStreamGroupLayeredVideo *layered_video = stg->params.layered_video;
+    OutputFilterOptions opts;
+    AVBPrint bp;
+    char *graph_str;
+    int ret;
+
+    if (stg->nb_entries != 2) {
+        av_log(istg, AV_LOG_FATAL, "A demuxer exported an invalid Alpha stream group. "
+                                   "This is a bug, please report it.\n");
+        return AVERROR_BUG;
+    }
+
+    memset(&opts, 0, sizeof(opts));
+
+    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+    if (stg->entries[!layered_video->el_index]->type == AV_STREAM_GROUP_ENTRY_STREAM)
+        av_bprintf(&bp, "[%d:g:%d:%d]", f->index, stg->index, !layered_video->el_index);
+    else
+        av_bprintf(&bp, "[%d:g:%d]", f->index,
+                   stg->entries[!layered_video->el_index]->stream_group->index);
+    if (stg->entries[layered_video->el_index]->type == AV_STREAM_GROUP_ENTRY_STREAM)
+        av_bprintf(&bp, "[%d:g:%d:%d]", f->index, stg->index, layered_video->el_index);
+    else
+        av_bprintf(&bp, "[%d:g:%d]", f->index,
+                   stg->entries[layered_video->el_index]->stream_group->index);
+    av_bprintf(&bp, "alphamerge[%d:g:%d]", f->index, stg->index);
+    ret = av_bprint_finalize(&bp, &graph_str);
+    if (ret < 0)
+        return ret;
+
+    for (int i = 0; i < stg->nb_coded_side_data; i++) {
+        const AVPacketSideData *sd = &stg->coded_side_data[i];
+
+        ret = av_packet_side_data_to_frame(&opts.side_data, &opts.nb_side_data, sd, 0);
+        if (ret < 0 && ret != AVERROR(EINVAL))
+            goto fail;
+    }
+
+    ret = fg_create(NULL, &graph_str, d->sch, &opts);
+    if (ret < 0)
+        goto fail;
+
+    istg->fg = filtergraphs[nb_filtergraphs-1];
+    istg->fg->is_internal = 1;
+
+    ret = 0;
+fail:
+    if (ret < 0)
+        av_freep(&graph_str);
+
+    return ret;
+}
+
 static int istg_parse_tile_grid(const OptionsContext *o, Demuxer *d, InputStreamGroup *istg)
 {
     InputFile *f = &d->f;
@@ -2114,6 +2171,11 @@ static int istg_add(const OptionsContext *o, Demuxer *d, AVStreamGroup *stg)
     }

     switch (stg->type) {
+    case AV_STREAM_GROUP_PARAMS_ALPHA:
+        ret = istg_parse_alpha(o, d, istg);
+        if (ret < 0)
+            return ret;
+        break;
     case AV_STREAM_GROUP_PARAMS_TILE_GRID:
         ret = istg_parse_tile_grid(o, d, istg);
         if (ret < 0)
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 27d6492388..011519f8d0 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -1663,6 +1663,25 @@ fail:
     return ret;
 }

+static int64_t score_new_packets_event_flag(const AVStreamGroup *stg)
+{
+    int64_t score = 0;
+
+    for (int j = 0; j < stg->nb_entries; j++) {
+        if (stg->entries[j]->type != AV_STREAM_GROUP_ENTRY_STREAM)
+            continue;
+
+        AVStream *st = stg->entries[j]->stream;
+
+        if (st->event_flags & AVSTREAM_EVENT_FLAG_NEW_PACKETS) {
+            score += 100000000;
+            break;
+        }
+    }
+
+    return score;
+}
+
 static int map_auto_video(Muxer *mux, const OptionsContext *o)
 {
     AVFormatContext *oc = mux->fc;
@@ -1683,22 +1702,12 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
         int64_t file_best_score = 0;
         for (int i = 0; i < ifile->nb_stream_groups; i++) {
             InputStreamGroup *istg = ifile->stream_groups[i];
-            int64_t score = 0;
+            int64_t score;

             if (!istg->fg)
                 continue;

-            for (int j = 0; j < istg->stg->nb_entries; j++) {
-                if (istg->stg->entries[j]->type != AV_STREAM_GROUP_ENTRY_STREAM)
-                    continue;
-
-                AVStream *st = istg->stg->entries[j]->stream;
-
-                if (st->event_flags & AVSTREAM_EVENT_FLAG_NEW_PACKETS) {
-                    score = 100000000;
-                    break;
-                }
-            }
+            score = score_new_packets_event_flag(istg->stg);

             switch (istg->stg->type) {
             case AV_STREAM_GROUP_PARAMS_TILE_GRID: {
@@ -1707,6 +1716,21 @@ static int map_auto_video(Muxer *mux, const OptionsContext *o)
                            + 5000000*!!(istg->stg->disposition & AV_DISPOSITION_DEFAULT);
                 break;
             }
+            case AV_STREAM_GROUP_PARAMS_ALPHA: {
+                const AVStreamGroupLayeredVideo *layered_video = istg->stg->params.layered_video;
+                for (int k = 0; k < istg->stg->nb_entries; k++) {
+                    const AVStreamGroupEntry *entry = istg->stg->entries[k];
+                    int disposition = entry->type == AV_STREAM_GROUP_ENTRY_STREAM
+                                       ? entry->stream->disposition
+                                       : entry->stream_group->disposition;
+                    if (istg->stg->entries[k]->type == AV_STREAM_GROUP_ENTRY_STREAM_GROUP)
+                        score += score_new_packets_event_flag(entry->stream_group);
+                    score += 5000000*!!(disposition & AV_DISPOSITION_DEFAULT);
+                }
+                score += layered_video->width * (int64_t)layered_video->height
+                           + 5000000*!!(istg->stg->disposition & AV_DISPOSITION_DEFAULT);
+                break;
+            }
             default:
                 continue;
             }