View Full Version : Passing values between runtime scripts
fvisagie
17th June 2014, 16:13
Hi All,
I'm having trouble reliably updating information between separate runtime scripts. The runtime scripts are used to track and display input clip frame number at frame serving time. As a rule the top ScriptClip only records the input clip frame number for the bottom one to display, the idea being that a Subtitle at the end of the script should better survive geometric transformation.
function TrackClip(clip c, bool "debug") {
ScriptClip(c, """ClipFrame = current_frame
debug = """" + String(debug) + """"
(debug == "true") ? Subtitle("Clip runtime:" + "\n" + \
"ClipFrame:" + String(ClipFrame), align=7, lsp=1) \
: last
""", after_frame=false)
}
global ClipFrame = 0
BlankClip(fps=25.0, color=$000000)
clip1 = TrackClip(debug=true)
BlankClip(fps=25.0, color=$FFFFFF)
clip2 = TrackClip(debug=true)
clip1.Trim(199, 201) ++ clip2.Trim(49, 51)
ScriptClip("""Subtitle("Script runtime:" + "\n" + \
"ClipFrame: " + String(ClipFrame) + "\n" + \
"script current_frame: " + String(current_frame), align=9, lsp=1)
""", after_frame=true)
This works correctly for monotonically increasing frame numbers. When the output is scrolled forwards and backwards the "Script runtime" starts displaying incorrect ClipFrame values. In some cases the ClipFrame values do not even change for a number of frames.
However, in all cases the "Script runtime" is still invoked every frame as shown by it correctly updating the "script current_frame" value, and the "Clip runtime" is also still invoked every frame as shown by correct updates to its ClipFrame display when enabled. It's just that somehow the "Script runtime" does not see these ClipFrame updates!
I've played with after_frame settings and the ones above seem to work best. Using FrameEvaluate() instead of ScriptClip() for the "Clip runtime" makes no difference.
What else can I try to get this to work for shuttled output? If for some fundamental reason this won't work, what other method can I use to track input clip frame numbers and display them at render time for shuttled output?
Many thanks,
Francois
StainlessS
17th June 2014, 17:01
I have not played with this, but this stood out a little
function TrackClip(clip c, bool "debug") {
ScriptClip(c, """Global ClipFrame = current_frame
debug = """" + String(debug) + """"
(debug == "true") ? Subtitle("Clip runtime:" + "\n" + \
"ClipFrame:" + String(ClipFrame), align=7, lsp=1) \
: last
""", after_frame=false)
}
global ClipFrame = 0
You have to use the word "Global" to assign to a global.
EDIT: I found it a little difficult to read, so changed as here
Global ClipFrame = 0
Function TrackClip(clip c, bool "debug") {
debug=Default(debug,false)
ES = """
Global ClipFrame = current_frame
Subtitle("Clip runtime:\nClipFrame: " + String(ClipFrame) + "\nscript current_frame: " + String(current_frame), align=7, lsp=1)
Global ClipFrame = current_frame # This dont make any difference
Return Last
"""
(debug) ? c.ScriptClip(ES, after_frame=false) : c
}
BlankClip(fps=25.0, color=$000000).ShowFrameNumber # Added ShowFrameNumber
clip1 = TrackClip(debug=true)
BlankClip(fps=25.0, color=$FFFFFF).ShowFrameNumber
clip2 = TrackClip(debug=true)
clip1.Trim(199, 201) ++ clip2.Trim(49, 51)
SCS="""
Subtitle("Script runtime:\nClipFrame: " + String(ClipFrame) + "\nscript current_frame: " + String(current_frame), align=4, lsp=1)
"""
ScriptClip(SCS, after_frame=true)
It still dont work :(, but now I can read it :). Perchance the chosen one will happen by.
EDIT: ShowFrameNumber works OK, but it dont explain the Scriptclip Global stuff.
Perhaps this will suffice:-
DEBUG=True
BlankClip(fps=25.0, color=$000000)
clip1 = (DEBUG) ? ShowFrameNumber : Last
BlankClip(fps=25.0, color=$FFFFFF)
clip2 = (DEBUG) ? ShowFrameNumber : Last
clip1.Trim(199, 201) ++ clip2.Trim(49, 51)
Gavino
17th June 2014, 23:17
What you are seeing is a side effect of the Avisynth cache.
The variable ClipFrame is changed only when the run-time script inside function TrackClip() is executed. When a given frame is requested for a second (or later) time, it is likely to be found first in a downstream cache, and then the run-time script will not be executed.
Incidentally, the variable does not need to be global - run-time scripts run at the same scope as the outer script.
foxyshadis
18th June 2014, 00:28
SetMemoryMax(0) will get you out of this jam, but it will seriously drop the speed of every temporal function. You can raise it up to just below where it will stop working as you need for each script/video, but it might be a better idea to use a can't-fail method like tacking a subtitled box to the bottom of the frame; that would generally survive filtering without impacting it much.
fvisagie
18th June 2014, 11:09
What you are seeing is a side effect of the Avisynth cache.
The variable ClipFrame is changed only when the run-time script inside function TrackClip() is executed. When a given frame is requested for a second (or later) time, it is likely to be found first in a downstream cache, and then the run-time script will not be executed.
I had assumed that TrackClip() was called even for re-requested frames because the frame numbers were still correctly subtitled, but I now suspect the previous frames had merely been cached and re-rendered along with previously rendered subtitles, right? Adding a Time()-stamp to subtitles seems to confirm that.
Incidentally, the variable does not need to be global - run-time scripts run at the same scope as the outer script.
Thanks, I'd forgotten about that!
SetMemoryMax(0) will get you out of this jam
Thanks. It seems from 2.5.8 a small value should be used to (almost) disable the cache; 0 returns the current value.
Groucho2004
18th June 2014, 11:52
It seems from 2.5.8 a small value should be used to (almost) disable the cache; 0 returns the current value.
Correct. Also, 4 MB cache is the minimum.
Gavino
18th June 2014, 16:11
Because there is a minimum value, you cannot disable the cache completely using SetMemoryMax().
However, in v2.60, you can effectively disable it by redefining the Cache() function (in your script) as follows:
function Cache(clip c) { return c }
StainlessS
18th June 2014, 16:24
I'm not sure, but I think I've seen a function to disable cache between two filters,
Don't remember which plugin it lives in. Anybody know?
PS,great tip G.
Edit, may have been a kassandro plug.
Cache, & internalcache seem to be the same & undocumented built-in func.
Guest
18th June 2014, 17:36
SetCacheHints(0,0) ?
StainlessS
18th June 2014, 18:22
Thank you n2
Much appreciated.
fvisagie
19th June 2014, 08:42
Thanks for the input and suggestions.
a function to disable cache between two filters
Why do you say "between two filters"? Are you perhaps implying that in cases like this one could selectively disable and re-enable caching at different points in the script? How do you suggest using SetCacheHints() in a script like this?
What values can the parameters of SetCacheHints() take and what do they mean? How does one re-enable caching? References on this forum are very few, along with a request for documentation!
StainlessS
19th June 2014, 19:54
I was thinking of disabling cache during plugin testing, and only at specific points in filter chain.
I have not as yet found which plugin that function lives in.
TheFluff
19th June 2014, 21:03
I was thinking of disabling cache during plugin testing, and only at specific points in filter chain.
I have not as yet found which plugin that function lives in.
it's an instance method on objects derived from IClip (see http://avisynth.nl/index.php/Cplusplus_API#SetCacheHints), but I doubt it's useful for what I think you think it's useful for
Why do you say "between two filters"? Are you perhaps implying that in cases like this one could selectively disable and re-enable caching at different points in the script? How do you suggest using SetCacheHints() in a script like this?
What values can the parameters of SetCacheHints() take and what do they mean? How does one re-enable caching? References on this forum are very few, along with a request for documentation!
It's not a script function, it's a C/C++ API function that plugin filters can use to tell their upstream filter which frames may be requested multiple times when GetFrame() is called. You can't use it from an Avisynth script.
Basically, what it does is tell Avisynth that when this filter requests frames from an upstream filter (the previous filter in the chain), it should either cache nothing (every frame requested from this filter results in one call to GetFrame() on the upstream filter per frame needed to produce an output frame), cache a fixed range of frames around the latest requested frame (useful for a temporal filter where, say, requesting frame n would require getting get n-1 and n+1; thus, if we cache those two frames, when you request one of them at least one of the new frames needed will be cached) or just use as much available memory as it finds suitable on a least-recently-used cache.
See also http://forum.doom9.org/showthread.php?p=1595750#post1595750
fvisagie
20th June 2014, 07:47
Thank you.
StainlessS
21st June 2014, 13:09
@TheFluff, thank you, but I was perhaps mistaken in that I thought I remembered a script function for removing cache from between
two filters (or not inserting one, ie add your own dummy filter that pretends to be Cache so that an additional following cache would not be added).
EDIT: I assumed that Neuron2 was giving me the name of a script function, I am aware of SetCacheHints in Avisynth.h.
From v2.6a5 source
/*********** C R E A T E ************/
AVSValue __cdecl Cache::Create_Cache(AVSValue args, void*, IScriptEnvironment* env)
{
PClip p=0;
if (args.IsClip())
p = args.AsClip();
else
p = args[0].AsClip();
if (p) {
int q = 0;
if (p->GetVersion() >= 5) // AVISYNTH_INTERFACE_VERSION which supports this
q = p->SetCacheHints(GetMyThis, 0); // Check if "p" is a cache instance
// Do not cache another cache!
if (q != (int)(void *)p) return new Cache(p, env);
}
return p;
}
This does seem to work OK, but not very convenient
Global ClipFrame = 0
Function Cache(clip c){return c} # Disable built-in Cache
Function TrackClip(clip c, bool "debug") {
debug=Default(debug,false)
ES = """
Global ClipFrame = current_frame
Subtitle("Clip runtime:\nClipFrame: " + String(ClipFrame) + "\nscript current_frame: " + String(current_frame), align=7, lsp=1)
Return Last
"""
(debug) ? c.ScriptClip(ES, after_frame=false) : c
}
# Cache output of ShowFrameNumber using built-in alternative name for Cache()
clip1 = BlankClip(fps=25.0, color=$000000).ShowFrameNumber.InternalCache().TrackClip(debug=true)
clip2 = BlankClip(fps=25.0, color=$FFFFFF).ShowFrameNumber.InternalCache().TrackClip(debug=true)
clip1.Trim(199, 201) ++ clip2.Trim(49, 51)
SCS="""
Subtitle("Script runtime:\nClipFrame: " + String(ClipFrame) + "\nscript current_frame: " + String(current_frame), align=4, lsp=1)
"""
ScriptClip(SCS, after_frame=true) # No Cache between TrackClip calls and ScriptClip()
InternalCache() # Can Cache after Scriptclip
StainlessS
21st June 2014, 14:20
Tried this but it dont work (after liberally scattering lots of NoCache()'s about.)
intended for v2.6
class NoCache : public GenericVideoFilter {
private:
enum {GetMyThis = 0x8686 }; // Stolen from Avisynth source
public:
NoCache(PClip _child,IScriptEnvironment* env) : GenericVideoFilter(_child) {}
~NoCache(){}
// void __stdcall SetCacheHints(int cachehints,int frame_range); SHOULD return void
int __stdcall SetCacheHints(int cachehints,int frame_range) { // Cheat and return int (as v2.5)
if(cachehints==GetMyThis) {
dprintf("SetCacheHints requested our 'this' pointer");
return (int)(void *)this;
// return (int)(void *)child; // Dont work either
}
return 0;
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
};
It does output the "SetCacheHints requested our 'this' pointer" to debugview, but does not suppress caching,
so we seem to be returning the wrong PClip.
EDIT: Returning 'child' rather than 'this' dont work either.
TheFluff
21st June 2014, 15:08
I think you've missed a rather significant point: SetCacheHints() only works in one direction, so even if your pretend-to-be-a-cache filter worked (and I don't think it does; I'd suggest using an interactive debugger instead of sprinkling printf's around) you'd still get the following scenario:
SomeFilter --(getframe with cache)--> NoCache --(getframe with no cache)--> SourceFilter
So you're basically in the exact same place as before, you've just added what amounts to a no-op to the filter chain.
Basically, you can't tell Avisynth to not cache your output. What SetCacheHints(0, 0) does is tell Avisynth to not cache your input, which is not really what you want. To do what you actually want you'd have to disable caching in the downstream direction as well, and there's no way to do that.
Have I mentioned lately that I think all these runtime hacks are a really bad idea?
StainlessS
21st June 2014, 15:28
Have I mentioned lately that I think all these runtime hacks are a really bad idea?
Yes, I do seem to recall you saying something like that, however,
if the situation as it stands is lacking then some improvisation
may be called for. Ideally it would not be necessary.
and thanks for your clarification.
fvisagie
25th June 2014, 16:08
I'm having some trouble dynamically disabling the cache (and wrapping that in a function for that matter).
When I go
condition ? Eval("function Cache(clip c) {return c}") : NOP()
# or simply Eval("function Cache(clip c) {return c}")
caching is only disabled from that point onward. Useful-looking, but in this case I'm trying to globally disable caching.
With
function DisableCache() {
function Cache(clip c) {return c}
}
condition ? DisableCache() : NOP()
caching is disabled even when DisableCache() isn't invoked!
function InternalCache(clip c) {return c}
seems to have no discernable effect.
SetMemoryMax(4)
doesn't sufficiently disable caching in some cases. SetMemoryMax() accepts values down to 1 but without noticeable improvement.
Any ideas?
Guest
25th June 2014, 16:38
Did you read what TheFluff wrote above?
Gavino
25th June 2014, 19:07
When I go
condition ? Eval("function Cache(clip c) {return c}") : NOP()
# or simply Eval("function Cache(clip c) {return c}")
caching is only disabled from that point onward. Useful-looking, but in this case I'm trying to globally disable caching.
With
function DisableCache() {
function Cache(clip c) {return c}
}
condition ? DisableCache() : NOP()
caching is disabled even when DisableCache() isn't invoked!
Although the parser accepts nested function definitions, they are all declared with global scope, so your function Cache() exists (causing caching to be disabled) whether DisableCache() is called or not.
Conversely, with Eval(), the function is created only when Eval() is called, so the cache is disabled from that point on.
function InternalCache(clip c) {return c}
seems to have no discernable effect.
Correct. When creating caches in the filter graph, Avisynth (from v2.60) invokes the function Cache() explicitly by name. The function InternalCache() is a synonym for the built in Cache() function, but is not used.
Did you read what TheFluff wrote above?
See my post #7 above.
TheFluff
25th June 2014, 20:15
I actually missed post #7 myself; my own posts were just talking about the limitations of SetCacheHints. But if you can override the Cache function from userspace then I don't see what's stopping you from reimplementing your own version of it and reading an IsCachable property on a per-clip basis (put it in the audio stream or whatever), plus providing a supporting function for changing said flag. It'd be a horrible abuse of the API, but well, it's not like that's stopped anyone before.
fvisagie
27th June 2014, 16:48
Conversely, with Eval(), the function is created only when Eval() is called, so the cache is disabled from that point on.
Thanks, I found a suitable point earlier in the script.
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.