[FFmpeg-devel] [RFC] AVFilter Parser

Vitor Sessak vitor1001
Wed Mar 26 19:55:49 CET 2008


Hi

vmrsss wrote:
> Hello. I've tried to interpret all that was said in the past few days,  
> and would like to make a proposal to pull it all together. This is  
> essentially what Vitor said in his last email, somehow simplified,  

Well, I think here you really underestimating the differences between 
the two syntaxes. This is not good, because it can seriously confuse 
someone who is trying to follow the discussion.

> plus a hopefully cleaner treatment of feedback.

I don't agree. More on this later.

> 
> ==1==
> 
> I am using the following syntactic elements because I find them  
> intuitive, but there is nothing special about them, just a matter of  
> taste:
> 
>   - comma (,) for sequential composition --
> 	F,G implies #outstream(F) = #instream(G)

Well, this is a very big difference between my syntax and yours. In my 
syntax a comma compose two filters (not two streams) linking one or more 
streams between them.

> 
>   - star (*) for parallel composition --
> 	#instream(F*G) = #instreams(F) + #instream(G)
> 	#outstream(F*G) = #outstreams(F) + #outstream(G)

Same thing, in mine a star puts two filters (not streams) in parallel.

> 
>   - input naming ( ... )
>   notice this is a scoping operator, (x)F names x inaccessible outside F
> 
>   - output naming  < .... >
> 
>   - feedback naming [ ... ]
> 	notice this is a scoping operator, [x]F names x inaccessible outside F
> 
>   - a dummy name _
> 
> ==2==
> 
> In general a filter is described by a signature
> 
> 	filter{par_1,...,par_k} : n ---> k

using "k" twice was not a good idea...

> 
> that is, its parameters (note I am using curly brackets just not to  
> confuse parameters and streams), its number of input streams n, its  
> number of output streams k. (Typically n and k can be easily inferred  
> by looking at the code, but it's not a big pain to ask the user to  
> declare them when writing a user-defined filter.)
> 
> Examples:
> 
> 	crop{w,h,x,y} : 1 --->1
> 	picInPic{} : 2 ---> 1
> 	rotate{angle} : 1 ---> 1
> 	movie{file} : 0 ---> 1
> 
> 
> ==3==
> 
> Here are the four main syntactic categories.
> 
> OUTPUT_STREAM ::= < id_1,...,id_k >
> 	sequence can be empty, < x > * < y > be to read as < x y >
> 
> ATOMIC_STREAM ::= all the streams in avfilter, eg crop, vflip,  
> scale, ...

I'm not sure I understand what you call a stream. "crop" is a filter, 
with one input pad and one output pad. You can say that in a graph it 
gets an input stream and an output stream.

> 
> USER_DEFINED ::= filter_id{par_1,...,par_k} : n ---> k = FILTER
> 
> FILTER ::=
> 	OUTPUT_STREAM
> 	ATOMIC_STREAM=val_1,...,val_k	(k = # of parameters of filter)
> 	USER_DEFINED=val_1,...,val_k 	(k = # of parameters of filter)
> 	FILTER * FILTER					(as discussed above)
> 	FILTER, FILTER					(as discussed above)
> 	(id_1 ... id_k) FILTER
> 	FILTER [bind_1 ... bind_k]	(bind is either the dummy id _ or a proper  
> id))
> 
> 
> ==4==
> 
> Let me now give a few examples to explain the elementary constructs;  
> (in fact, the only thing which is slightly different is feedback).
> 
> 	copy{} = (x)< x >
> 	split{} = (x)< x x >

This is not so easy. There are some memory permission problems. That's 
why there is a vf_split.c

> 	swap{} = (x y)< y x >
> 	drop{} = (x) < >
> 
> 	myfilter(angle): 1 ---> 1 =	
> 		case angle of 90 --> transpose,vflip
> 					180 --> hflip,vflip
> 					270 --> vflip,transpose
> 					else --> rotate_slow=angle
> Something slightly more complex:
> 
> 	filter(): 3 ---> 1 = (a b c) < a b >, picInPic, (t) < t c >, picInPic


Well, user-defined filters should be given as examples after the 
examples of the common usage are clear. Would something like

vitor at vitor:~$ ffmpeg -i file1.avi -vfilters "(a b c) < a b > , 
picInPic, (t) < t c >, picInPic" file2.avi

be the common usage?

> 
> where:
> 	(a b c) names the three inputs of the filter
> 	< a b > outputs to of them to go straight to picInPit
> 	(t) names to output of picInPic
> 	< t c > feeds the final picInPic stage
> 
> Note that fully parenthesised this would be:
> 
> 	(a b c)  ( < a b >, picInPic , (t) (< t c >, picInPic) )
> 
> (comma is associative, so parenthesis don't matter there -- I find  
> using a comma after < > clumsy though)
> 
> Notice that if you don't need to name streams for special treatment,  
> you're not required to do it: all simple filter chains can be written  
> just as "...,crop=...,scale=...," and so can also a number of more  
> complex ones -- as long as they don't require feedback.
> 
> 
> ==5==
> 
> Time for well-formedness rules relative to naming:
> 
> (1) all ids in a well-formed filter are bound by either ( ) or [ ]
> 	this guarantees that a filter has got no visible names, which
> 	may interfere with other names unintentionally; it is however
> 	possible to use input/output/feedback naming to use the filter
> 	not trivially even if one only knows its n ---> k type.

Unless I am missing something, it is also true in my syntax.

> 
> (2) ids appear at most once in a single (...)
> 	this avoids non-sense like < a b >, (x x)F
> 
> (3) the number of names in input (resp output) naming determines
> 	n (resp k) in the type n ---> k of a filter.
> 
> There might be still something missing here, but these are the  
> important ones.
> 
> 
> ==6==
> 
> Finally, we come to feedback. The treatment is similar to the one  
> proposed by Michael and Vitor, the only difference is that the  
> [ .... ] is a scoping operator that at the same time feedbacks output  
> to inputs and the same time puts the names out of scope, while keeping  
> input/output and feedback mechanisms entirely separated. The basic  
> mechanism is as follows.
> 
> Let F be a filter 2 ---> 2 and suppose I want to feed its outputs back  
> to its inputs. Firstly, I can name F's inputs use the form:
> 
> 	G = < a b >, F  	(which is 0 --> 2)
> 
> Then G [ a b ] says: name a and b the 1st (resp 2nd) output of G to  
> the a input named a (resp b). To swap the outputs (ie crop the wires)  
> I would write G [ b a ]. Now G [a b] is crap (eg because it's got no  
> input nor output), one would like to feedback only some of the outputs  
> to some of the inputs. That is where one might use a dummy variable _,  
> which is a passthrough sort of mechanism.
> 
>   	< a b > F [  _ a ] : 2nd output to 1st input, 1st output pass  
> through as filter's output
>   	< a b > F [  _ b ] : 2nd output to 2nd input, 1st output pass  
> through as filter's output
> 	< a b > F [  a _ ] : 1st output to 1st input, 2nd output pass through  
> as filter's output
> 	... etc
> 
> Importantly, [  _ a ] makes a vanish from output and input (it becomes  
> a loop, with no specific name) so that it is no more accessible from  
> outside. Note that is very important, as any further access to the  
> loop would be wrong. So, the final term that takes F and feeds back  
> its 2nd output to its 1st input would be
> 
>    	(x) (< A x > F [  _ A ] ) : 1 ---> 1
> 
> which says: name x the only input, feed it to F's 2nd input,  
> temporarily name A its 1st input, pass through the 1st output and  
> feedback the 2nd to A as indicated by the feedback naming [  _ A ].
> 
> To conclude, we need to constrain the occurrence of names in F  
> [ ... ]. This requires some thinking as to the best way to express it,  
> but essentially F must be of the form < ... > F' and: there must be as  
> many names in [ ... ] as in < ... >, they must all be distinct, and  
> actually appear exactly once in < ... >. The type of < ... >  
> F' [ ... ] is n ---> k, where n is
> 
> ==7==
> 
> A few examples with feedback:
> 
> These were proposed my Michael:
> 
>> 2<X 0<L Filter0 L<0 2>Y ; Y>0 2<L Filter1 L<2 X<0
>>
>>   _________
>>  /         \
>>  \          |
>>>       /
>> ---> Filter0 --->
>>>       \
>>  /          |
>>  \____  ___/
>>       \/
>>   ____/\___
>>  /         \
>>  \          |
>>>       /
>> ---> Filter1 --->
>>>       \
>>  /          |
>>  \_________/

I really can't see what is so special about feedback. It could be 
written like

(tmp  in1 tmp4) filter0 (tmp  out1 tmp2);
(tmp2 in2 tmp5) filter1 (tmp4 out2 tmp5);

or using your syntax

(a b), (<tmp  a tmp2>, filter0, (tmp  out1 tmp2)) *
        (<tmp2 b tmp5>, filter1, (tmp4 out2 tmp5)), <out1 out2>

Why is the extra complexity of the '[]' needed?

Other than that, if I understand correctly, the main difference between 
the syntax and mine is that yours is stream based (and you link streams) 
and mine is filter based (and I define links between the filters). In my 
syntax "(in1),(out1)" or "(in1) (out1)" is a syntax error, filter missing.

-Vitor




More information about the ffmpeg-devel mailing list