swscale/graph: add plane copy metadata

This can be set opportunistically by passes which are effectively
memcopies. Information about end-to-end plane copies will also propagate
upwards to the SwsGraph, and can be used there by the caller.

The reason we need to solve it this way is deeply tied to the way
SwsGraph is designed; specifically the fact that it can be used with
arbitrary frames and also lives per-field (rather than per-frame),
meaning that we can't just directly mutate the output frame somehow from
the run() call to ref the output planes.

Signed-off-by: Niklas Haas <git@haasn.dev>
This commit is contained in:
Niklas Haas committed 2026-08-09 21:03:33 +02:00
1 parent 4af33ec418
commit 6b38a4035a
2 files changed
+61 -7

No files matched your search

+42 -7
View File
@@ -207,6 +207,7 @@ int ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
pass->output->height = height;
pass->output->width = width;
pass->output->width_align = 1;
memset(pass->output->plane_copy, -1, sizeof(pass->output->plane_copy));
if (!align) {
pass->slice_h = pass->lines;
@@ -242,6 +243,13 @@ void ff_sws_pass_link_output(SwsPass *dst, const SwsPass *src)
keep->width_align = FFMAX(keep->width_align, drop->width_align);
keep->width_pad = FFMAX(keep->width_pad, drop->width_pad);
for (int i = 0; i < FF_ARRAY_ELEMS(keep->plane_copy); i++) {
if (keep->plane_copy[i] < 0)
keep->plane_copy[i] = drop->plane_copy[i];
else if (drop->plane_copy[i] >= 0)
av_assert1(keep->plane_copy[i] == drop->plane_copy[i]);
}
av_refstruct_replace(&dst->output, src->output);
}
@@ -790,15 +798,41 @@ static int init_passes(SwsGraph *graph)
return ret;
}
if (pass)
return 0;
if (!pass) {
/* No passes were added, so no operations were necessary */
graph->noop = 1;
/* No passes were added, so no operations were necessary */
graph->noop = 1;
const int nb_planes = av_pix_fmt_count_planes(dst.format);
for (int i = 0; i < nb_planes; i++)
graph->plane_copy[i] = i;
/* Add threaded memcpy pass */
return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
pass, 0, 1, run_copy, NULL, NULL, NULL, &pass);
/* Add threaded memcpy pass */
return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height,
pass, 0, 1, run_copy, NULL, NULL, NULL, &pass);
}
/* Compute end-to-end plane copy map */
for (int n = 0; n < graph->num_passes; n++) {
const SwsPass *pass = graph->passes[n];
/* This pass writes to an output buffer other than the image
* output, or copies from the output of a different pass */
if (pass->output->avframe || pass->input)
continue;
for (int i = 0; i < FF_ARRAY_ELEMS(graph->plane_copy); i++) {
const int idx = pass->output->plane_copy[i];
if (idx < 0)
continue;
if (graph->plane_copy[i] < 0) {
graph->plane_copy[i] = idx;
av_log(graph->ctx, AV_LOG_DEBUG, "Plane %d passthrough from "
"plane %d\n", i, idx);
} else {
av_assert0(graph->plane_copy[i] == idx);
}
}
}
return 0;
}
static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
@@ -845,6 +879,7 @@ int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst,
graph->opts_copy = *ctx;
av_assert0(src->interlaced == dst->interlaced);
av_assert0(src->field == dst->field);
memset(graph->plane_copy, -1, sizeof(graph->plane_copy));
if (ctx->threads == 1) {
graph->num_threads = 1;
+19
View File
@@ -66,6 +66,15 @@ typedef struct SwsPassBuffer {
/* Optional allocation hints for optimal performance */
int width_align; /* Align width to multiple of this */
int width_pad; /* Extra padding pixels */
/**
* Map of planes which are directly copied from the pass input. These
* may be promoted from a memcpy to a refcopy.
*
* Each entry maps the output index to the corresponding input plane
* index, or -1 for no copythrough.
*/
int plane_copy[4];
} SwsPassBuffer;
/**
@@ -130,6 +139,16 @@ typedef struct SwsGraph {
AVBufferRef *hw_frames_ref;
/**
* Map of planes which directly copied from the input. These may be
* promoted from a memcpy to a refcopy. This requires special handling
* by the caller.
*
* Each entry maps the output index to the corresponding input plane
* index, or -1 for no copythrough.
*/
int plane_copy[4];
/** Sorted sequence of filter passes to apply */
SwsPass **passes;
int num_passes;