Since v2.52, Avisynth has included a very powerful run-time environment (also known as the 'conditional' environment). This was a great step forward, supporting complex video processing that would be difficult or impossible to perform in a normal script. However, it is not easy to use - the behaviour of run-time scripts (especially in combination) can be hard to understand and there are usability problems concerning scope and lifetime of variables.
"Cond. filters are quite complex - and sometimes I am even surprised of the outcome" - sh0dan
Some of the difficulty is inherent in the concept itself, requiring a clear understanding of the difference between the compiling and frame-serving phases of Avisynth. However, many of the problems stem from specific limitations of the interface presented by the run-time filters/functions and the details of their implementation which, with the benefit of hindsight, we can see could have been done differently.
GRunT (Gavino's Run-Time) is a plugin which addresses these and other problems, making the run-time system much easier to use.
GRunT provides the following features:
Backwards compatibility is an important design objective of GRunT. It means that you can put the plugin in your plugins folder and all correctly functioning scripts will continue to operate exactly as before.
GRunT should be useful to anyone who uses the run-time filters, from those who make occasional use of ScriptClip to those who write complex functions based on run-time features (such as Restore24 or MRestore).
The plugin provides extended versions of the following run-time filters:
Some users may prefer not to use the same names as the original filters, so the alternative names GScriptClip, GFrameEvaluate, GConditionalFilter, GWriteFile and GWriteFileIf may also be used. However, if running on version 2.57 or earlier of Avisynth, then only the alternative names may be used. (This restriction is necessary for technical reasons - sorry about that.)
Each filter is 100% backwards compatible with its standard equivalent, but has two additional optional arguments:
A short example (based on the original) shows how this greatly simplifies passing function parameters into a run-time script.
function bracket_luma(clip c, float th1, float th2) { Assert(0 <= th1 && th1 < th2 && th2 <= 255, "Invalid thresholds!") ScriptClip(c, """ avl = AverageLuma() avl <= th1 ? last.BlankClip() : avl >= th2 ? last.BlankClip(color=color_white) : last """, args="th1,th2", local=true) }
This is much easier than the standard approach of dynamically building the runtime script using string concatenation, or passing the values via global variables.
"I really do not like this global variable business with ScriptClip ..." - stickboy
And because the run-time script is evaluated in its own scope, there is now no problem in calling bracket_luma more than once in the same script (previously the variables th1 and th2 of different instances could interfere with each other).
Elements of the args string can also take the form 'name=expression' - the expression is evaluated in the current context and, as in the simple case, is used to set the initial value of the named variable in the run-time script. For example,
args="x, y=n+1, c=c.Trim(2, 0)"will provide values for the variables x, y and c which are used in the run-time script.
Evidently, the simple form (without the expression) is just a shorthand for the more general form, with the variable name
itself being used as the expression.
So, for example,
args="x, y, z"is equivalent to
args="x=x, y=y, z=z"passing the values of the compile-time variables x, y and z (from outside the run-time script) into the variables of the same name inside the script.
Spaces may be freely used (or not) inside the args string, just as in Avisynth function calls.
The plugin also provides the following extensions to the run-time functions (such as AverageLuma):
AverageLuma(-1)
returns the value for the previous frame.Note that to support the first feature, current_frame is now a global variable (see discussion). However, the second feature means that it is no longer necessary to change current_frame to access other frames. In fact, for most purposes, you can forget that current_frame exists as an explicit variable.
Here is an example of a weighted second order luma interpolation function:
function InterpLuma2(clip c) { lm_k = AverageLuma(c) lm_km1 = AverageLuma(c, -1) lm_kp1 = AverageLuma(c, 1) dvg = (lm_km1 - 2 * lm_k + lm_kp1) / 2 return lm_k + Sign(dvg) * Sqrt(Abs(dvg)) }This function can be called from any run-time script. Previously, something like this this had to be done by assigning to current_frame (or using additional filters, eg Trim(1,0)), and the code had to be written directly in the run-time script rather than in a function.
In effect, we are now able to write run-time functions of our own, derived from the standard ones. Here is another example:
function AverageRed(clip c) { return RGBDifference(ShowRed(c), BlankClip(c)) }
Note too that, while useful in its own right, the ability to call run-time functions in a user function gives you more than just that. For the first time, it allows an entire run-time script to be put inside a function body, called for example like this:
ScriptClip("MyFunc()")
As the function body will be evaluated in a separate scope, this is another way of eliminating the problem of unintended sharing of variables between run-time scripts. However, you may prefer to write the run-time script 'in-line' and invoke the run-time filter with local=true.
For added convenience, there is a new variant of ConditionalFilter which takes a single boolean expression instead
of three separate parameters as at present. This is useful when the condition to be tested is a compound one or is already
available in boolean form (like IsCombed()
). For example,
ConditionalFilter(c, c1, c2, "AverageLuma(c1) > AverageLuma() && AverageLuma(c1) > AverageLuma(c2)")
where previously you would have to say
ConditionalFilter(c, c1, c2, "AverageLuma(c1) > AverageLuma() && AverageLuma(c1) > AverageLuma(c2)", "=", "true")
Note that this form of ConditionalFilter also makes it easier use the relational operators <=
,
>=
and !=
, which are not supported as the operator in the standard ConditionalFilter.
The plugin fixes a fairly serious bug I discovered in the run-time system. This fix is needed if you are running a version of Avisynth prior to build 080620 of 2.58. (GRunT actually originated as a means to demonstrate this bug-fix, but I have now extended it to do a lot more.)
Since things like ScriptClip are often hidden deep inside huge functions like Restore24, you may be affected by this bug without even realising it.
For the extended run-time filters, the default for local is false unless args is also specified (for compatibility reasons). However, I recommend always using local=true unless you really need to communicate values of variables from one run-time script to another (note that you can always use global variables for this).
To this end, the plugin provides a function GRTConfig which allows the default behaviour to be changed.
So, GRTConfig(local=true)
sets the default to local=true for all subsequent filters.
For reference, the complete interface to the extended run-time filters is as follows (see above for a description of the args and local parameters):
ScriptClip
(clip, string function, bool "show", bool
"after_frame",
string "args", bool "local")
FrameEvaluate
(clip, string function, bool "show", bool
"after_frame",
string "args", bool "local")
ConditionalFilter
(clip testclip, clip source1, clip source2,
string filter, string operator, string value, bool "show",
string "args", bool "local")
ConditionalFilter
(clip testclip, clip source1, clip source2,
string condition, bool "show",
string "args", bool "local")
WriteFile
(clip, string filename, string expression1,
... , string expression16, bool "append", bool "flush",
string "args", bool "local")
WriteFileIf
(clip, string filename, string expression1,
... , string expression16, bool "append", bool "flush",
string "args", bool "local")
The alternative names GScriptClip
, GFrameEvaluate
, GConditionalFilter
,
GWriteFile
and GWriteFileIf
may also be used (and with Avisynth version 2.57 or earlier,
must be used).
Here is the complete list of run-time functions, with their extended interface.
AverageLuma
(clip, int offset)AverageChromaU
(clip, int offset)AverageChromaV
(clip, int offset)RGBDifference
(clip1, clip2, int offset)LumaDifference
(clip1, clip2, int offset)ChromaUDifference
(clip1, clip2, int offset)ChromaVDifference
(clip1, clip2, int offset)RGBDifferenceFromPrevious
(clip, int offset)YDifferenceFromPrevious
(clip, int offset)UDifferenceFromPrevious
(clip, int offset)VDifferenceFromPrevious
(clip, int offset)RGBDifferenceToNext
(clip, int offset)YDifferenceToNext
(clip, int offset)UDifferenceToNext
(clip, int offset)VDifferenceToNext
(clip, int offset)YPlaneMax
(clip, float threshold, int offset)UPlaneMax
(clip, float threshold, int offset)VPlaneMax
(clip, float threshold, int offset)YPlaneMin
(clip, float threshold, int offset)UPlaneMin
(clip, float threshold, int offset)VPlaneMin
(clip, float threshold, int offset)YPlaneMedian
(clip, int offset)UPlaneMedian
(clip, int offset)VPlaneMedian
(clip, int offset)YPlaneMinMaxDifference
(clip, float threshold, int offset)UPlaneMinMaxDifference
(clip, float threshold, int offset)VPlaneMinMaxDifference
(clip, float threshold, int offset)
The optional offset parameter specifies the offset (which may be negative) from the current frame; default is zero.
v1.0.1 (Gavino, 27th September 2008): fix for Avisynth 2.5.7 and earlier (have to use alternative names for filters, eg GScriptClip)
v1.0.0 (Gavino, 9th July 2008): first full version
v0.1 (Gavino, 18th June 2008): initial version to fix run-time system bug