Filter graph config diagnostics
Mailing-list subject (draft):
[RFC] libavfilter: return graph-config error detail to callers, not only via av_log
The problem
Section titled “The problem”When avfilter_graph_config() fails, the reason is emitted with av_log() — e.g.
Output pad "output1" … of split not connected to any destination — while the function returns a
generic AVERROR(EINVAL). A caller only has the return code; the actionable detail has already
gone to stderr.
For a language binding this bites hard. We build a graph from the user’s code, and when they
make a recoverable mistake — a split whose second output is never consumed, an option a filter
rejects, an unlinked pad — all we can hand back is “Invalid argument.” The text that says what to
fix is on a log stream the binding doesn’t own and can’t attribute to a specific config() call.
You can see exactly this in the extension today: leave a split output unconsumed and the
EncodingException reads avfilter_graph_config: Invalid argument, while the useful line lands on
stderr where a PHP caller never sees it.
Why a binding can’t just capture the log
Section titled “Why a binding can’t just capture the log”av_log_set_callback()is process-global. A library that hijacks it steals logging from the host application and anything else linking FFmpeg — unacceptable in a shared runtime.- Even with a callback, messages aren’t attributable to one
config()call or scoped to oneAVFilterGraph, and aren’t structured — you’d be buffering free text and guessing. - Swapping the global callback around each call isn’t thread-safe.
So this extension does a partial workaround: before calling config() it walks the graph and
pre-detects the common case (an unconnected output pad) to throw a precise exception naming the
filter, the pad, and the fix. That covers one failure mode; it doesn’t generalise to every
config-time error.
Implementation candidates
Section titled “Implementation candidates”- Error-string out-param — a sibling entry point, e.g.
avfilter_graph_config2(AVFilterGraph *graph, void *log_ctx, char *errbuf, size_t errbuf_size), that writes the human-readable reason on failure. Smallest surface, no struct/ABI change, mirrorsav_strerror. Leaves the existing function untouched. - Per-graph last-error — store the last config diagnostic on the
AVFilterGraphand expose a getter (or anAV_OPT-readable field). Scoped to the graph and thread-reasonable, but adds state to the struct. - Scoped log capture — a per-
AVFilterGraphlog callback (not the global one), so a binding subscribes only to its own graph’s messages. Most general (captures all graph diagnostics), largest design.
Leaning: (1) for the narrow, low-risk win; (2) if a queryable error is preferred; (3) as the general direction.
To investigate before drafting
Section titled “To investigate before drafting”- The error/return plumbing in
libavfilter/avfiltergraph.c(graph_check_validity()and where the “pad not connected”av_logoriginates) — confirm those paths already carry a string we can route to the caller. - Whether maintainers prefer a new function or an
AVClass/AV_OPTfield. - ABI/versioning: any addition must be additive (new function, or a new field at struct end) behind
a
LIBAVFILTER_VERSIONbump — no change to existing signatures or layout.
Prior discussion
Section titled “Prior discussion”To check before posting: search the archives for earlier requests to surface filtergraph config errors through the return path. The 2013 “Scripting RFC” on binding lavfi to other languages is the relevant backdrop (same as the expression variables proposal).
Status
Section titled “Status”Seed/notes — not yet a list-ready patch. Filed so the workaround in the extension (the pre-config unconnected-pad scan) has a documented upstream end-state to aim at.
An Artisan Build project.
Built on FFmpeg — an independent binding, not affiliated with or endorsed by the FFmpeg project. Support FFmpeg.