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;
}