[FFmpeg-devel] [PATCH] [2/??] [3/3] Filter graphs - Parser for a graph description
vmrsss
vmrsss
Tue Mar 18 10:29:51 CET 2008
Hello.
I have some experience in my real life with representing graphs, and
believe I can help here (though I am not yet an expert on ffmpeg --
I'm interested in libavfilter, I'm learning from there).
There are essentially two ways to represent edges out of a graph's
node, the problem here stems from the fact that you're trying to use
both at a time: you can give them explicit names (as in (tmp), [tmp]
or [+tmp]), or implicitly consider them numbered (1st, 2nd, 3rd, ...).
As a filter's pads are already numbered in AVFilter, I suggest the
second option is the best here, more intuitive and much simpler. (The
problem with names is that you pay the greater syntax flexibility with
a lot of special cases and meaningless forms to handle.)
Vitor is right that you need two graph combinators (in fact you might
need a third one and few more symbols; more below), say , and * (the
choice is immaterial, of course -- traditionally ; would be chosen
instead of , and some tensor product symbol instead of *). The basic
rules are:
(A) filt1,filt2 means ---> filt1 ---> filt2 --->
and is only syntactically well formed when the number of filt1's
output streams equals the number of filt2's input streams; the result
is a filter with filt1's input streams and filt2's output streams;
filt1's output streams are fed to filt2's input stream as expected:
1st to 1st, 2nd to 2nd, etc. All as expected.
(B) filt1 * filt2 means
---> filt1 --->
---> filt2 --->
and is always well formed; the result is a filter with input/output
filters of both filt1 and filt2 numbered starting from filt1.
It is easy to make the entire filter system work smoothly based on
these rules only. I proceed with the details, just in case I managed
to interest you.
To make this work, we need a few elementary filters which don't do any
processing, just rearrange streams:
(C) split : 1 ---> 2 (by which I mean takes one input stream, produces
two output streams) to duplicate a stream;
(D) swap: 2 ---> 2 to swap the order of streams (1st in input becomes
second in output and vice versa);
(E) nop: 1 ---> 1 do nothing, kill: 1 --> 0 drop stream
(it's worth noticing that if one wishes to use split_n : 1 ---> n to
mean duplicate the input stream n times, then kill = split_0 and nop =
split_1)
For instance (assuming as usual that * binds tighter than , ):
split, nop*rotate, picInPic
would be a filter 1 ---> 1
nop * (movie_src=test.avi, vflip), overlay
would be a filter 1 ---> 1 (not that nop is fundamental in both
examples)
etc.
(F) If I understood your examples correctly (which I might have not)`,
there is still one missing ingredient: a feedback combinator, say !,
which takes a filter with at least one input and at least one output
stream and feeds back the first output to the first input. For
instance, let's look at the examples you have been discussing in this
thread:
(1)
> in --> scale --> crop --> picInPic --> rotate --> split --> out
> ^ |
> | |
> delay<-------------------/
becomes: scale,crop, !( picInPic, rotate, split, delay * nop ) -- in
fact the duplicated stream from split is fed to delay and delay's
output is then fed back to picInPic (if that was the sense of the
example, of course...)
(2) Similarly,
> in --> crop --> picInPic --> rotate --> split --> vflip --> out
> ^ |
> | |
> delay<---- hflip --------/
becomes: crop, !( picInPic, rotate, split, (hflip, delay) * vflip )
(3)
> filter1->(abc)
> (def)->filter2
>
becomes: filter1 * filter2
> filter1->filter2
> | ^
> v |
> (abc) (def)
becomes: filter1, split, (filter2 * nop) (note of course that you
can't add input streams, the number of a filter's required input/
output streams is determined by the filter you can't change it like
that, only duplicate, )
(4)
> [tmp]myfilter="crop=123:345,scale=333:555,[tmp]picInPic"
> -vf mirror,[anotherin]myfilter
becomes: myfilter="split, (crop,scale) * nop, picInPic" and -vf
mirror,myfilter
(5)
> movie_src=test.avi, vflip, (in)overlay(out)
becomes nop*(movie_src=test.avi, vflip), overlay
Finally, do you see for instance that with the (in) and (out) in the
syntax you can really express to which input between (in) and test.avi
is the first one? This may be irrelevant for overlay, but certainly it
is for other filters, eg picInPic. With the syntax I propose, the two
versions would be:
nop*(movie_src=test.avi, vflip), picInPic
which differs from
nop*(movie_src=test.avi, vflip), swap, picInPic
which is the same as
(movie_src=test.avi, vflip)*nop, picInPic
The latter refers to the fact that all this is mathematically very
solid and well defined, governed by a small set of equations (eg swap,
vflip*hflip = hflip * vflip, swap).
I find this syntax clearly superior (there are only two well-
formedness rules, no clumsy handling of names and aliases and
danglers). If there is interest for this, I will elaborate.
Regards.
More information about the ffmpeg-devel
mailing list