Expression variables & constants
Mailing-list subject (draft):
[PATCH RFC] libavfilter: expose expression variable metadata via AVFilter struct
Why we need this
Section titled “Why we need this”Many filters take expression parameters — drawtext’s x/y, geq, overlay’s position —
that reference variables like w, h, t, n, text_w, and named constants (the
AV_OPT_TYPE_CONST values grouped under an option’s unit). These are documented and used in
filter strings worldwide, but they are not programmatically discoverable: the variable names
live in internal var_names[] arrays inside each filter’s .c.
For this binding it’s the blocker for a specific feature we want: letting an expression parameter be a PHP closure evaluated per frame. To safely parse and evaluate an expression in the host language — and to inject the right values — we have to know which variables and constants that filter’s expression accepts. Today the only options are to hard-code FFmpeg’s internal arrays and keep them in sync by hand (fragile, and drifts every release) or to fall back to opaque string-only parameters. Every GUI, video editor, and language binding that builds graphs programmatically hits the same wall.
These variables and constants are already stable, user-facing API — they can’t change without breaking existing filter strings. The stability contract exists; this proposal just extends programmatic access to match what string users already have. Nothing here is a behaviour change.
Proposed solution
Section titled “Proposed solution”Add an expression_vars field to AVFilter, mirroring how AVOption exposes options.
typedef struct AVFilterExprVar { const char *name; /* e.g. "text_w" */ const char *help; /* e.g. "Width of rendered text" */} AVFilterExprVar;
typedef struct AVFilter { // ... existing fields ...
/** NULL-terminated array of expression variables this filter accepts, * or NULL if it does no expression evaluation. */ const AVFilterExprVar *expression_vars;} AVFilter;Each filter keeps its existing internal var_names[] (used by av_expr_parse() et al.) unchanged,
and adds an adjacent public, documented array:
static const AVFilterExprVar drawtext_expr_vars[] = { { "w", "Video width" }, { "h", "Video height" }, { "t", "Timestamp in seconds" }, { "n", "Frame number (from 0)" }, { "text_w", "Width of rendered text" }, // … { NULL }};Consumers then discover them from any AVFilter *:
const AVFilter *f = avfilter_get_by_name("drawtext");for (const AVFilterExprVar *v = f->expression_vars; v && v->name; v++) printf("%s — %s\n", v->name, v->help);Constants, too
Section titled “Constants, too”The same gap applies to an option’s named constants (AV_OPT_TYPE_CONST grouped by unit).
Those are already reachable by walking the AVOption table, so a binding can generate enums for
them — but expression-referenced constants (and which unit an expression may use) want the same
discoverability as the variables. The proposal can carry constant metadata in the same additive
spirit if maintainers prefer one mechanism for both.
Why this shape
Section titled “Why this shape”- Precedent — mirrors
AVOption; a pattern developers already know. - Discoverable & self-documenting — help text (already in the
.texidocs) becomes machine-readable; no per-filter symbol knowledge needed. - Purely additive — internal functions keep using
var_names[]unchanged; no signature changes, no behaviour change. Minor-release compatible. The struct can later gain type hints/flags without breaking users.
On the two arrays
Section titled “On the two arrays”Yes, a filter would carry two adjacent arrays with overlapping content — intentional: they sit together so drift is obvious in review, and the existing array feeds several internal functions that a major release could later consolidate. If duplication is a concern, an X-macro generates both from one definition:
#define DRAWTEXT_EXPR_VARS \ X("w", "Video width") X("h", "Video height") X("t", "Timestamp in seconds")
#define X(name, help) name,static const char *const var_names[] = { DRAWTEXT_EXPR_VARS NULL };#undef X#define X(name, help) { name, help },static const AVFilterExprVar drawtext_expr_vars[] = { DRAWTEXT_EXPR_VARS { NULL } };#undef XSimpler alternatives
Section titled “Simpler alternatives”If extending AVFilter is unwelcome, exporting the existing symbols
(const char *const ff_drawtext_var_names[]) or a per-filter accessor also solve the immediate
problem — but without discoverability or metadata. If we’re committing to API stability for these
variables, the struct approach has the better long-term value.
Prior discussion
Section titled “Prior discussion”A search of the archives found no prior proposal for this specific feature; the closest is the 2013 Scripting RFC on binding lavfi to other languages. This addresses one narrow, concrete part of that.
Status
Section titled “Status”RFC drafted; ready to offer a patch series (the AVFilterExprVar struct + field, drawtext as a
proof of concept, then other expression-enabled filters and docs). Seeking feedback on the shape
before submitting.
An Artisan Build project.
Built on FFmpeg — an independent binding, not affiliated with or endorsed by the FFmpeg project. Support FFmpeg.