View Full Version : GRunT - Easier run-time scripting


Gavino
9th July 2008, 02:00
Avisynth's run-time environment (http://avisynth.nl/index.php/Runtime_environment) is very powerful, supporting complex video processing that would be difficult or impossible to perform in a normal script. But it's 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 (http://avisynth.nl/index.php/The_script_execution_model/Scope_and_lifetime_of_variables#Runtime_scripts).

"Cond. filters are quite complex - and sometimes I am even surprised of the outcome" - sh0dan

GRunT (Gavino's Run-Time ;)) is a plugin which addresses these and other problems, making the run-time system much easier to use.

Features:
- Simple, natural and robust way to pass variables into a run-time script from 'outside'
- A run-time script can be evaluated in its own independent scope
- Run-time functions can be called from a user function
- Run-time functions can be applied to any frame (relative to the current one)
- Additional variant of ConditionalFilter with single boolean expression
- Fixes a fairly serious bug in the run-time system
- Lightweight plugin extending the standard run-time environment, minimal time and memory overhead
- 100% backwards compatible with existing scripts

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).

Details

The plugin provides extended versions of the following run-time filters:
- ScriptClip
- FrameEvaluate
- ConditionalFilter
- WriteFile
- WriteFileIf

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:
string args: the variables whose values are to be imported into the run-time script, written as a list of names separated by commas. The given variable names are evaluated in the current (compile-time) context, so can include function parameters or local variables.
Each value becomes the initial value of the corresponding variable at each invocation of the run-time script.
bool local: if true, the filter will evaluate its run-time script in a new variable scope, avoiding unintended sharing of variables between run-time scripts.
Default is true if args is also specified, otherwise false (to preserve backwards compatibility).
A short example (based on the original (http://avisynth.nl/index.php/The_script_execution_model/Scope_and_lifetime_of_variables#A_variables_scope_and_lifetime_example)) 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 is used to set the value of the named variable in the run-time script.
Example: args="x, y=n+1, c=c.Trim(2, 0)" will provide values for the variables x, y and c.
Here y need not even exist in the current environment (although x, n and c must).

The plugin also provides the following extensions to the run-time functions (eg AverageLuma):
these functions can now be called inside a user function, when the user function is called from a run-time script
each function has an new optional int argument, which can be used to get the value from another frame, relative to the current one. For example, AverageLuma(-1) returns the value for the previous frame. (No more assigning to current_frame ...)
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. For example,
ConditionalFilter(c, c1, c2, \
"AverageLuma(c1) > AverageLuma() && AverageLuma(c1) > AverageLuma(c2)")
where previously you would have to add (...,"=", "true")

Finally, 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.

More detailed documentation and examples are provided in the attached download.

EDIT 27-SEP-08: Updated to v1.0.1 to work with Avisynth 2.5.7

Comments and suggestions would still be very welcome.

martino
9th July 2008, 12:41
Oh my God! I love you!!! Finally I can do something on which I wasted 2 days trying to achieve (and countless headaches)... I think... xD

Gavino
9th July 2008, 13:54
Thanks, martino! I hope you find it solves your problem.

If it does, could you post your resulting script here as an example?
Alternatively, if you still have problems, explain what you're trying to do and I'll see if I can help.

backtohell
20th July 2008, 16:16
i am not an expert in avisynth but still will try your GRt

Thanks Gavino for sharing

Gavino
5th August 2008, 10:00
Previously, I wrote:
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.

I have now noticed that the standard ConditionalFilter, as well as forcing you to use 3 parameters, does not even support all the boolean operators - you can only use "=", "<" or ">", and not "<=", ">=" or "!=". Of course, you can express ">=" by using "<" and switching round the 'then' and the 'else' clips. But GRunT's version (as well as being slightly shorter) allows you to express the condition in the way you choose, whichever is more convenient/natural to you.

MOmonster
25th September 2008, 21:19
@Gavino
I donīt get ScriptClip running.
I get allways the error:
ScriptClip does not have the named argument "show"
I tested your sample scripts and some of my own, but allways the same error (no error without your plugin).
What is wrong?

Gavino
25th September 2008, 21:33
@MOmonster: I'm sorry about this - it is a problem with GRunT and Avisynth v2.5.7 that I am still looking into.

At the moment, the workaround is to use v2.5.8, where it works fine.

MOmonster
25th September 2008, 21:49
Thanks for the fast answer.
I will do some testing when I adapt srestore to avisynth 2.5.8.

Edit:
Another question. Is it possible to shift values without using global values?

Gavino
26th September 2008, 02:09
Is it possible to shift values without using global values?
What do you mean by 'shift values'?
Can you give me an example?

MOmonster
26th September 2008, 08:04
I use global vars for performance reasons.
Instead of something like this:
ScriptClip(last, """
prev = AverageLuma(last.loop(2,0,0))
curr = AverageLuma(last)
next = AverageLuma(last.trim(1,0))
...outputclip...
""")
I can write this:
ScriptClip(last, """
global prev = curr
global curr = next
global next = AverageLuma(last.trim(1,0))
...outputclip...
""")

Gavino
26th September 2008, 11:23
OK, I should have realised you meant that, having seen similar code in MRestore.

The variables in your example don't need to be global, as they are at the outer script-level scope (and hence persistent). But of course, if you need to see them in functions called from within the ScriptClip, they would have to be global (or passed as parameters).

If you are using GRunT, the example could also be written as
ScriptClip(last, """
prev = AverageLuma(-1)
curr = AverageLuma()
next = AverageLuma(1)
...outputclip...
""")

which would be slightly faster than the standard ScriptClip, though clearly still slower than your optimisation.
(As you know, the disadvantage of the optimised version is that it does not support seeking, since it relies on linear access.)

MOmonster
26th September 2008, 12:03
The variables donīt have to be global? I don`t know anymore why I think so.
The seeking problem can be solved with RequestLinear and some additional conditions, so speed is more important for me.

Thanks.

Gavino
26th September 2008, 13:13
The variables donīt have to be global? I don`t know anymore why I think so.
Perhaps you started out thinking (long ago) that each ScriptClip runs in a separate variable scope.

They don't - that's why you can get unexpected interference between different ScriptClip instances, and why GRunT provides the local=true option.

Didée
26th September 2008, 13:33
The variables donīt have to be global? I don`t know anymore why I think so.
Well, I can remember why ... iirc it was Restore24 which introduced this kind of variable-shifting in the conditional env. :)

In the 1st example with 3times AverageLuma, globals are not needed because all 3 variables are computed "now".
In the 2nd example, the globals are needed because only one variable is computed "now" (the "next" one), and the other two are derived from what the conditional environment had computed during processing the previous frame. If you need "now" the content of a variable from one frame before, then you're jumping out of ScriptClip's local context, and need a global variable to "save" that variable's content while stepping from one frame on to the next one.

The variant with independent calculation of all needed variables is of course slower (doing AverageLuma several times), and it's robust against seeking. The variant with globals is faster (only one time AverageLuma), but it's not robust against seeking and needs linear access.

When I did Restore24, the choice was easy:

a) RequstLinear() did not exist yet

b) GRunT did not exist yet

c) R24 uses not only the 3 variables 'previous', 'current', 'next'. Because of its kind of pattern reckognition, it uses six (or seven?) of such variables: previous(3), previous(2), previous(1), current, next(1), next(2). (Not sure anymore if the released versions used next(3), too.)

It's obvious why the variant with globals was chosen. The choices were to do only one time AverageLuma for every frame, or to do six (seven?) times AverageLuma for every single frame (with all except one of them being basically superfluous.)
So I went for the route that requires linear access, but does not do 500% (600%) of re-doing frame sampling operations that already had been done.


It's been quite some time since then. Knowledge/practice and available tools have noticeably developed in the meantime. I'm really looking forward to see Mrest..., erh, SRestore to do right what I had b0rked back then. :)

(Lord, make I don't ever have to touch Restore24 again!)

MOmonster
26th September 2008, 14:00
Perhaps you started out thinking (long ago) that each ScriptClip runs in a separate variable scope.

They don't - that's why you can get unexpected interference between different ScriptClip instances, and why GRunT provides the local=true option.

Yes, this and the need to initialise the variables are the reasons.
This script indeed works well:
ScriptClip (last, """ cfr=current_frame
now = cfr<2 ? 0 : next
next = AverageLuma(last)
last.subtitle(string(now)).subtitle(string(next),y=16)""")
I will test GRunT soon. Maybe next srestore release wonīt use global variables anymore.

@Didee
Yes I remember, first Cdeint releases use practical the same variables as restore24.:eek:

Gavino
26th September 2008, 14:06
In the 2nd example, the globals are needed because only one variable is computed "now" (the "next" one), and the other two are derived from what the conditional environment had computed during processing the previous frame. If you need "now" the content of a variable from one frame before, then you're jumping out of ScriptClip's local context, and need a global variable to "save" that variable's content while stepping from one frame on to the next one.
No, as I pointed out, and as MOmonster has demonstrated, there is no 'local' context in (the builtin) ScriptClip - it's all at outer script level.

Didée
26th September 2008, 14:41
Its probably related to the fact that R24 uses user defined functions within scriptclip -- Like

function ShiftBackVars() { do_the_shifting_stuff }
...
ScriptClip( c, "ShiftBackVars()" )

When referring to conditional variables in the do_the_shifting_stuff section, they have to be global, don't they?
(Sorry, I'm currently not in the mental "flow" of ScriptClip & Co. ... usually need some run-up when I have to.)

If so, well ... I would NEVER have done R24 without that style of using (lots of) defined functions, called from within the conditional envoronment. That way it was easily possible to get a reasonable level of "modularisation". When having to put all that stuff within one gigantic

ScriptClip(c, """
...
put several hundred lines of code here ...
...
""" )

then Restore24 never would have come into existance. (Which would be a pity ... think the butterfly effect.;) )

Howeveritmightbe. The important point is you guys now are heading to do it right, without me needing to get knots in the brain. :)

Gavino
26th September 2008, 15:18
When referring to conditional variables in the do_the_shifting_stuff section, they have to be global, don't they?
Yes, to be visible in a function, they do need to be global, as I mentioned above.
And if you're writing to them, you don't have the option of passing them as parameters.
I would NEVER have done R24 without that style of using (lots of) defined functions, called from within the conditional envoronment. That way it was easily possible to get a reasonable level of "modularisation".
GRunT helps in that respect because it allows calling of the run-time functions (such as AverageLuma) from within a user function.

Gavino
27th September 2008, 17:53
Version 1.0.1: Bug fix for Avisynth 2.5.7

As MOmonster reported above (#6), and also as reported here, there are problems using GRunT with Avisynth v2.5.7. In fact, I'm embarrassed to say that it just doesn't work on anything prior to v2.5.8. :(

I have produced a new version (GRunT 1.0.1) that solves the problem. The fix requires users running on 2.5.7 (and earlier) to use alternative names for the run-time filters (GScriptClip instead of ScriptClip and so on). The run-time functions, such as AverageLuma, are not affected.

Users already on 2.5.8 can continue to use the old names - the alternative names may also be used if preferred.

Go to the first post in this thread to download the new version.
Updated documentation, reflecting this change and minor editing, is also included.

MOmonster
28th September 2008, 11:58
Thanks for update, but I canīt download it.

Gavino
28th September 2008, 15:00
@MOmonster: I assume your problem was that the attachment was still awaiting approval. It should be OK now.

MOmonster
28th September 2008, 17:50
Yes, thanks.

canuckerfan
17th September 2009, 18:28
Currently I'm using AviSynth 2.5.8's built-in scriptclip and conditionalreader. anyway I can adapt this code so that I can use Gavino's versions?

Filtered = RemoveNoiseMC(rdlimit=18,rgrain=1,denoise=0,sharp=true)
ScriptClip("ABC1 ? Filtered : Last")
ConditionalReader("seriously.txt","ABC1",false)

Gavino
17th September 2009, 19:06
If you just load the GRunT plugin (or install it in the plugin folder), it will use my version automatically (if running on Avisynth 2.58).

For your example, where the run-time script is very simple, you wouldn't actually gain very much.
However, it would allow you to replace the ScriptClip call by
ConditionalFilter(Filtered, last, "ABC1")
which would be slightly faster.

Even without GRunT, it could also be changed, although with the standard ConditionalFilter you would have to write
ConditionalFilter(Filtered, last, "ABC1", "==", "true")

GRunT does not have its own version of ConditionalReader, so you would continue to use the standard version.

canuckerfan
17th September 2009, 20:03
^Thank you :)

Forensic
13th March 2016, 04:56
I have tried everything that I can think of. What I need is a single pass solution to step through a video and only retain frames where AverageLuma(a)>16 (IOW, retain only non-blank frames). Scriptclip can step through the video and conditionally test the AverageLuma value, but I can't get the new video out of the Scriptclip, even with GRunT. Any suggestions?

StainlessS
13th March 2016, 06:26
Yo dude, long time no see.

So far as I know, Scriptclip must produce same number of output frames as input. (+ same size, colorspace etc).
So 1 single pass solution is unlikely. Although could do it as realtime 1st pass, with auto second pass.
No idea what IOW means.

But see here:- http://forum.doom9.org/showthread.php?t=172904

You might want to use YPlaneMinMaxDifference (Threshold=eg 0.2) to establish 'blank frame', and YPlaneMin or AverageLuma to establish level.

StainlessS
13th March 2016, 07:05
Something like this (writing frames to reject in realtime pass, as likely fewer than writing all selected frames)


Avisource("D:\V\StarWars.avi").Trim(0,10000)

GSCript("""
Function Test(Clip c, int n,int v1,Float th,String File,Bool Show) {
c
mx = RT_YPlaneMax(n,Threshold=th)
if(mx < v1) {
RT_WriteFile(File,"%d",n,Append=True) # Write unwanted frames (fewer than wanted)
(Show) ? RT_SubTitle("%d] mx=%d - DELETING",n,mx) : NOP
} else {(Show) ? RT_SubTitle("%d] mx=%d",n,mx) : NOP}
Return Last
}
""")

OutFile = "Frames.txt"
RT_FileDelete(OutFile) # Delete existing
V1 = 18 # Was not deleteing anything for me at 16
th = 0.2 # Avoid 0.2% of noise above v1
Show=True # CHANGE to FALSE when happy with numbers
SSS = "Test(current_frame,V1,th,OutFile,Show)"
ARGS= "v1,th,OutFile,Show"
ScriptClip(SSS,args=ARGS,after_frame=True)

(!Show) ? ForceProcessAVI() : NOP # Force Pass 1 (From TWriteAVI v2.0) ie force writing of frames file
(!Show&&Exist(OutFile)) ? RejectRanges(Cmd=Outfile) : NOP # If Outfile does not exist, then none to Reject.

Return last

Function RejectRanges(clip c,String "SCmd",String "Cmd",Bool "TrimAudio",Float "FadeMS") {
# RejectRanges() by StainlessS. Required:- FrameSel, Prune, RT_Stats
# Wrapper to delete frames/ranges along with audio, can supply frames/ranges in SCmd string And/Or Cmd file.
# The wrapper makes for easier usage of Prune() which supports up to 256 input clips, but requires a clip index,
# eg '3, 100,200' would specify clip 3, range 100 to 200. The wrapper does away with the necessity for the clip index as we
# are only using a single clip here. Prune also does not have a 'reject' arg to delete specified frames rather than select them,
# this wrapper also converts a list of frames to delete into a list of frames to select so that we can use Prune and its audio
# capability.
#
# SCmd: Frames/Ranges specified in String (Frames/Ranges either Chr(10) or ';' separated, infix ',' specifies range, eg 'start,end').
# Cmd: Frames/Ranges specified in file (one frame/range per line, comments also allowed, see FrameSel for Further info).
# TrimAudio:
# True(default), deletes audio belonging to deleted frames
# False, returns original audio, probably out of sync.
# FadeMS: (default 1.0 millisec). Linear Audio Fade duration at splices when TrimAudio==true, 0 = dont fade (might result in audio 'clicks/cracks').
c
TrimAudio=Default(TrimAudio,True) # default true trims audio, false returns original audio (audiodubbed, as Framesel returns no audio)
FadeMS=Float(Default(FadeMS,1.0)) # 1 millisecond linear fadeout/fadein at splices
PruneCmd = (TrimAudio) ? "~Prune_"+RT_LocalTimeString+".txt" : ""
(!TrimAudio)
\ ? FrameSel(scmd=SCmd,cmd=Cmd,reject=true)
\ : FrameSel_CmdReWrite(PruneCmd,scmd=SCmd,cmd=Cmd,reject=true,Prune=True,range=true)
(TrimAudio) ? Prune(Cmd=PruneCmd,FadeIn=True,FadeSplice=True,FadeOut=True,Fade=FadeMS) : NOP
# If TrimAudio==true then delete Prune temp file, Else restore original Audio to the now audio-less clip
(TrimAudio)
\ ? RT_FileDelete(PruneCmd)
\ : (c.HasAudio) ? AudioDub(c) : NOP
Return Last
}


and to view rejected frames only

Avisource("D:\V\StarWars.avi").Trim(0,10000)

OutFile = "Frames.txt"
SelectRanges(Cmd=Outfile) # or change to RejectRanges to view kept after 1st pass script
Return last

# From Prune [req FrameSel]
Function SelectRanges(clip c,String "SCmd",String "Cmd",Bool "TrimAudio",Float "FadeMS",Bool "Ordered") {
# SelectRanges() by StainlessS. Required:- FrameSel, Prune, RT_Stats
# Wrapper to Select frames/ranges along with audio, can supply frames/ranges in SCmd string And/Or Cmd file.
# The wrapper makes for easier usage of Prune() which supports up to 256 input clips, but requires a clip index,
# eg '3, 100,200' would specify clip 3, range 100 to 200. The wrapper does away with the necessity for the clip index as we
# are only using a single clip here.
#
# SCmd: Frames/Ranges specified in String (Frames/Ranges either Chr(10) or ';' separated, infix ',' specifies range, eg 'start,end').
# Cmd: Frames/Ranges specified in file (one frame/range per line, comments allowed, see FrameSel for Further info).
# *** NOTE ***, If both Cmd and SCmd supplied AND Ordered == False, then will process Cmd file and then SCmd string afterwards, ie
# Will select ranges in Cmd file and in order specified (rather than auto ordering ranges) and then append ranges specified in
# SCmd string (and in order specified).
# TrimAudio:
# True(default), selects audio belonging to selected frames/ranges
# False, returns original audio, probably out of sync (maybe totally out of whack if Ordered == false and selected ranges out of order).
# FadeMS: (default 1.0 millisec). Linear Audio Fade duration at splices when TrimAudio==true, 0 = dont fade (might result in audio 'clicks/cracks').
# Ordered:
# True(default), all frames/ranges are returned in sequencial order. Any frame specified more than once will return only 1 instance.
# False, All frames/Ranges are returned in specified order, Cmd processed first and then SCmd. Frames/ranges specified more than once
# will return multiple instances. Allows out-of-order trimming of clip, eg re-sequencing of scenes in movie.
#
# Does not make much sense to select individual frames with audio, best used with ranges.
# Will coalesce individually selected adjacent frames/ranges before any Fade, ie only audio fade where sensible to do so.
# TrimAudio==false with non Ordered selection will result in completely out of sync audio.
c
TrimAudio=Default(TrimAudio,True) # default true trims audio, false returns original audio (audiodubbed, as Framesel returns no audio)
FadeMS=Float(Default(FadeMS,1.0)) # 1 millisecond linear fadeout/fadein at splices
Ordered=Default(Ordered,True) # True (default) frames/ranges will be Ordered and selected only once even if specified more than once.
# False, frames/ranges returned in specified order, Cmd processed 1st and then SCmd.
PruneCmd = (TrimAudio) ? "~Prune_"+RT_LocalTimeString+".txt" : ""
(!TrimAudio)
\ ? FrameSel(scmd=SCmd,cmd=Cmd,Ordered=Ordered)
\ : FrameSel_CmdReWrite(PruneCmd,scmd=SCmd,cmd=Cmd,Ordered=Ordered,Prune=True,range=true)
(TrimAudio) ? Prune(Cmd=PruneCmd,FadeIn=True,FadeSplice=True,FadeOut=True,Fade=FadeMS) : NOP
# If TrimAudio==true then delete Prune temp file, Else restore original Audio to the now audio-less clip
(TrimAudio)
\ ? RT_FileDelete(PruneCmd)
\ : (c.HasAudio) ? AudioDub(c) : NOP
Return Last
}


You can rearrange logic however you will, above was just a quick knock-up.

Requires RT_Stats, FrameSel, Prune, and TWriteAVI v2.0. http://forum.doom9.org/showthread.php?t=172837&highlight=twriteavi

EDIT:: Oops, and GSCript and Grunt too.

EDIT: This is more like what you asked for, set Show = False to do it for real, when true only shows metric
On my clip it was not deleting anything as all frames were above Ave luma 16.

Avisource("D:\V\StarWars.avi").Trim(0,10000)

GSCript("""
Function Test(Clip c, int n,Float th,String File,Bool Show) {
c
Ave = RT_AverageLuma(n)
if(Ave < th) {
RT_WriteFile(File,"%d",n,Append=True) # Write unwanted frames (fewer than wanted)
(Show) ? RT_SubTitle("%d] %f - DELETING",n,Ave) : NOP
} else {(Show) ? RT_SubTitle("%d] %f",n,Ave) : NOP}
Return Last
}
""")

OutFile = "Frames.txt"
RT_FileDelete(OutFile) # Delete existing
th = 18.0 # Keep >= 18.0
Show = True # CHANGE to FALSE when happy with numbers
SSS = """Test(current_frame,th,OutFile,Show)"""
ARGS="th,OutFile,Show"
ScriptClip(SSS,args=ARGS,after_frame=True)

(!Show) ? ForceProcessAVI() : NOP # Force Pass 1 (From TWriteAVI v2.0) ie force writing of frames file
(!Show&&Exist(Outfile)) ? RejectRanges(Cmd=Outfile) : NOP # If Outfile does not exist, then none to Reject.

Return last

Function RejectRanges(clip c,String "SCmd",String "Cmd",Bool "TrimAudio",Float "FadeMS") {
# RejectRanges() by StainlessS. Required:- FrameSel, Prune, RT_Stats
# Wrapper to delete frames/ranges along with audio, can supply frames/ranges in SCmd string And/Or Cmd file.
# The wrapper makes for easier usage of Prune() which supports up to 256 input clips, but requires a clip index,
# eg '3, 100,200' would specify clip 3, range 100 to 200. The wrapper does away with the necessity for the clip index as we
# are only using a single clip here. Prune also does not have a 'reject' arg to delete specified frames rather than select them,
# this wrapper also converts a list of frames to delete into a list of frames to select so that we can use Prune and its audio
# capability.
#
# SCmd: Frames/Ranges specified in String (Frames/Ranges either Chr(10) or ';' separated, infix ',' specifies range, eg 'start,end').
# Cmd: Frames/Ranges specified in file (one frame/range per line, comments also allowed, see FrameSel for Further info).
# TrimAudio:
# True(default), deletes audio belonging to deleted frames
# False, returns original audio, probably out of sync.
# FadeMS: (default 1.0 millisec). Linear Audio Fade duration at splices when TrimAudio==true, 0 = dont fade (might result in audio 'clicks/cracks').
c
TrimAudio=Default(TrimAudio,True) # default true trims audio, false returns original audio (audiodubbed, as Framesel returns no audio)
FadeMS=Float(Default(FadeMS,1.0)) # 1 millisecond linear fadeout/fadein at splices
PruneCmd = (TrimAudio) ? "~Prune_"+RT_LocalTimeString+".txt" : ""
(!TrimAudio)
\ ? FrameSel(scmd=SCmd,cmd=Cmd,reject=true)
\ : FrameSel_CmdReWrite(PruneCmd,scmd=SCmd,cmd=Cmd,reject=true,Prune=True,range=true)
(TrimAudio) ? Prune(Cmd=PruneCmd,FadeIn=True,FadeSplice=True,FadeOut=True,Fade=FadeMS) : NOP
# If TrimAudio==true then delete Prune temp file, Else restore original Audio to the now audio-less clip
(TrimAudio)
\ ? RT_FileDelete(PruneCmd)
\ : (c.HasAudio) ? AudioDub(c) : NOP
Return Last
}

Forensic
13th March 2016, 08:25
StainlessS. Thank you for this. I have been quite busy with my lab and creating FOSS (Free Open Source Software) that now has over 30,000 downloads and is relied upon by law enforcement worldwide. This would never have been possible without amazing programmers like you and Gavino. Your work has helped to solve crimes and has saved lives!!!! As for this script, it requires two functions I didn't have. I found "ForceProcessAVI" in your TWriteAVI_dll DLL but where do I find "RejectRanges"? Also IOW is a mostly American expression short for "In Other Words" which just means that something is being restated in another way.

StainlessS
13th March 2016, 08:40
Select/Reject Ranges is in the Prune AVS directory. (also requires FrameSel to do the clever Select to Reject conversion for Prune).

IOW, I thought it could not possibly stand for "Isle Of White" :)

Prune: http://forum.doom9.org/showthread.php?t=162446&highlight=Prune
FrameSel: http://forum.doom9.org/showthread.php?t=167971&highlight=FrameSel

EDIT: Or Select/Reject Ranges both posted in above code snippits, but still need the Prune and FrameSel plugins.

Gavino
13th March 2016, 09:33
I have tried everything that I can think of. What I need is a single pass solution to step through a video and only retain frames where AverageLuma(a)>16 (IOW, retain only non-blank frames). Scriptclip can step through the video and conditionally test the AverageLuma value, but I can't get the new video out of the Scriptclip, even with GRunT. Any suggestions?
As an alternative to StainlessS's solution, you could use my DeleteFrames() function, like this:
DeleteFrames("AverageLuma() <= 16")

(It works out at compile-time which frames to delete, so the script will take a noticeable time to load for a long video.)

BTW Nice to hear my code is being used in real practical applications!

StainlessS
13th March 2016, 09:37
Yes indeed big G.
Although, an advantage of the frames file method is that it is easily repeatable without a second compile time run.
Also, RT_AverageLuma is faster than built-in so would cut down overhead on longer clips.

Forensic
13th March 2016, 18:19
Thank you StainlessS & Gavino. DeleteFrames("RT_AverageLuma() <= 16") works like a charm. Simple single pass, and just what I wanted! Much appreciated.

yesmanitsbearman
16th May 2016, 21:26
Hey,

Does anyone have a x64 version of this? Need it for srestore and it's the only one I can't find a x64 build for.

yesmanitsbearman
17th May 2016, 20:02
I've taken the liberty to compile the .dll myself. Had to also change some stuff to make it compile with latest avs+ headers. Hopefully this comes useful to someone. srestore now tested working on a fully x64 setup.

here it is (http://rgho.st/7blM5TMJP)

real.finder
18th May 2016, 03:24
I've taken the liberty to compile the .dll myself. Had to also change some stuff to make it compile with latest avs+ headers. Hopefully this comes useful to someone. srestore now tested working on a fully x64 setup.

here it is (http://rgho.st/7blM5TMJP)

can we have one work with 2.5 x64?

Reel.Deel
18th May 2016, 03:38
can we have one work with 2.5 x64?

Why? 2.5 is outdated and there's no reason to be using it.

... Perhaps someone can add it to the wiki.

Thanks for the build, I'll add it to the wiki.

real.finder
18th May 2016, 03:49
Why? 2.5 is outdated and there's no reason to be using it.


yes, but I use it some times (for setmtmode) and 2.5 plugin work with avs+ too

yesmanitsbearman
20th May 2016, 10:36
I could never say no to you for all the times you helped me :)

here (http://rgho.st/6Zst4FPkx)

Not tested though. Let me know if it's fine.

real.finder
20th May 2016, 15:51
I could never say no to you for all the times you helped me :)

here (http://rgho.st/6Zst4FPkx)

Not tested though. Let me know if it's fine.

thank you

with srestore(frate=23.976) I get http://i.imgur.com/yHVtNvM.png in the top of video frame

but with same GrunT-x64-25 in avs+ it's fine!

yesmanitsbearman
20th May 2016, 22:21
I am guessing something to do with avisynth.h I used. Perhaps someone can point a stable 2.5 repo to use. There's loads of repos around with various forks and the include file is different all over.

real.finder
24th May 2016, 10:38
I am guessing something to do with avisynth.h I used. Perhaps someone can point a stable 2.5 repo to use. There's loads of repos around with various forks and the include file is different all over.

I think this is the reason


static const AVS_Linkage* AVS_linkage = nullptr;

extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, AVS_Linkage* vectors) {
AVS_linkage = vectors;

StainlessS
24th May 2016, 17:22
yesmanitsbearman,
v2.5 header has this at beginning (after copyright)

#ifndef __AVISYNTH_H__
#define __AVISYNTH_H__

enum { AVISYNTH_INTERFACE_VERSION = 3 };


The header snippet given by Real.Finder containing "AvisynthPluginInit3" is one of the v2.6 headers.

EDIT: Here link to ApparentFPS dll+source with both v2.58 header and STANDARD Avisynth v2.6 FINAL:- http://www.mediafire.com/download/x4e8ad215xd8z04/ApparentFPS_25%2626_dll_v1.03_20151217.zip

~148KB

EDIT: where AVISYNTH_INTERFACE_VERSION

=4, does not exist
=5, OLD Pre-final v2.6, will error/crash if dll compiled with this and used via v2.6Alpha3 and prior (I think, anyway, dont use).
=6, v2.6 FINAL.

real.finder
10th June 2016, 18:49
I am guessing something to do with avisynth.h I used. Perhaps someone can point a stable 2.5 repo to use. There's loads of repos around with various forks and the include file is different all over.

ok, it's avisynth64_8-29-10 fault, avisynth64_4-16-10 work fine

real.finder
19th January 2018, 01:12
hi Gavino

is there some way to avoid crash in theses cases with GRunT?


function SFrameBlendX(clip C, int "blendfactor", string "blend_mode", float "blend_opacity")
{
global SFrameBlendX_blend_mode=blend_mode
global SFrameBlendX_blend_opacity=blend_opacity
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
global SFrameBlendX_limit = limit
FrameCount!=1 ? ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
b = bb ? SFrameBlendX_limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(SFrameBlendX_limit)/FrameCount) : b.Loop(2,0,0) : nop()
b = bb ? b.Overlay(last, mode=SFrameBlendX_blend_mode, opacity=SFrameBlendX_blend_opacity) : last
return b
""") : last
}


SFrameBlendX(5,"lighten")

more information https://forum.doom9.org/showthread.php?p=1829149#post1829149

it use same method of https://forum.doom9.org/showthread.php?p=1188377#post1188377

but the problem seems that the frames from previous didn't ever clean up so it will crash after use all ram, the problem can't be seen in Srestore or any MOmonster function since they not do this with clips so seems ram will not be full easily

so, is there any method/idea/update to clean/limit theses data from previous frames process?

like


GScriptClip(last, """
global prev = curr
global curr = next
global next = AverageLuma(last.trim(1,0))
...outputclip...
""", limit=5)


or


GScriptClip(last, """
global prev = curr
global curr = next
global next = AverageLuma(last.trim(1,0)).runtimecleanup(5)
...outputclip...
""")


or anything suitable

StainlessS
19th January 2018, 04:23
Presumably, you are relying on 'b' being at top script level (never did get the hang of that), and so is sort of Global [or maybe 'common'].
I tried removing Globals and using Grunt Args instead, but it stopped working, seemed to interfere with setting non global b, and
was not seen as set on subsequent frames.

Nope, cant do it, cannot improve on yours. Tried a few things, no go. I'm quite mystified about how it works :confused:
Only suggestion is that blendfactor cannot be optional. [EDIT: Remove quotes]

I dont think you can 'clean up', even if you use blankclip and trim [ie create an additional new clip] while keeping a single previous frame, that frame is reliant on ones prior to itself, and they are reliant upon ones prior to them, etc.
With SRestore, etc, I would guess that output is reliant upon [and constructed from] only input frames [or frames synthesized from input frames] , here you are reliant upon all previous output frames.

EDIT: Working on Multi-Instance version function, that might work, but will reset if user jumps about.

Gavino
19th January 2018, 20:40
I tried removing Globals and using Grunt Args instead, but it stopped working, seemed to interfere with setting non global b, and
was not seen as set on subsequent frames.
I think that could be fixed by setting local=false (default is 'true' when args is used), but I don't think it would fix real.finder's original problem.

real.finder
20th January 2018, 02:41
I think that could be fixed by setting local=false (default is 'true' when args is used), but I don't think it would fix real.finder's original problem.


function SFrameBlendX(clip C, int "blendfactor", string "blend_mode", float "blend_opacity")
{
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
FrameCount!=1 ? ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
b = bb ? limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0) : nop()
b = bb ? b.Overlay(last, mode=blend_mode, opacity=blend_opacity) : last
return b
""",args="blend_mode, blend_opacity, limit", local=false) : last
}


yes it work like before and it not fix the ram problem

StainlessS
20th January 2018, 11:55
This works for a little bit longer (orig 825 frame, below 845 frames) before crash.


Function SBX_MI(clip c,int Factor, string "Mode", float "Opacity") {
c
myName = "SBX_MI: "
Mode = Default(Mode,"lighten")
Opacity = Default(Opacity,1.0)
Limit = (Factor<=0) ? 1 : Max(1, FrameCount/Factor)
FuncS="""
Function Fn@@@(clip c,Int factor,String mode,Float opacity,Int limit) {
n=current_frame
if(Prev@@@ != n) {
c
cf=c.Trim(n,-1) # current frame
if(Prev@@@ != n-1) {
Global B@@@ = cf # First Frame OR User Jumping, Init/Reset to current frame
} else {
# limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0)
if(limit > 1) {
Global B@@@ = Overlay(B@@@,cf, opacity=float(limit)/FrameCount)
} # Else, B@@@ = Previous B@@@
# b.Overlay(last, mode=mode, opacity=opacity)
Global B@@@=Overlay(B@@@,cf,mode=Mode,opacity=Opacity) # HOW do we cache only this frame & release all others ?
}
Global Prev@@@=n # REM for next time
} # Else, Cache failure, repeat request for current frame, just return same as last time
Return B@@@
}
#######################################
# Unique Global Variables Initialization
#######################################
Global Prev@@@=-666
#######################################
# Unique Runtime Call, GScriptClip must be a one-liner:
#######################################
ARGS = "Factor,Mode,Opacity,Limit"
c.GScriptClip("Fn@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
#######################################
# Unique Identifier Definition
#######################################
GIFunc="SBX_MI" # Function Name, Supply unique name for your multi-instance function.
GIName=GIFunc+"_InstanceNumber" # Name of the Instance number Global
RT_IncrGlobal(GIName) # Increment Instance Global (init to 1 if not already exists)
GID = GIFunc + "_" + String(Eval(GIName))
InstS = RT_StrReplace(FuncS,"@@@","_"+GID)
# RT_WriteFile("DEBUG_"+GID+".TXT","%s",InstS)
FrameCount!=1 ? GScript(InstS) : Last
Return Last
}

Client

aviSource("Parade.avi").trim(500,0)
SBX_MI(5)



EDIT: Made same mistake as in original, "Factor" was optional, removed quotes above.
EDIT: Perhaps Loop() added somewhere might delay crash ??? [EDIT: Nope, dont seem to work]
EDIT: Oops, added Int to Fn@@@(Int Limit).
EDIT: Small mods.

EDIT:
I think that could be fixed by setting local=false (default is 'true' when args is used), but I don't think it would fix real.finder's original problem.
I thought that I'de tried that, maybe I just tried without Local arg, perhaps Local is default true.
EDIT: Yep, default for Local=False, unless Args used, in which case default is True. (I should have read above quote more closely).
EDIT: I did above test with AVS Standard, without AVS+ Trim() issue ongoing in Devs forum, Avisynth+ Thread. https://forum.doom9.org/showthread.php?p=1830902#post1830902
EDIT: Above, we are using Global B@@@ as a single frame accumulator for all previous frames overlayed, we need some method to cache only that frame, and release all prevous ones.

pinterf
20th January 2018, 14:43
Maybe it's not the frames but the string resources that will never get freed up. Shorten everything in the runtime section and see if it crashes later.

StainlessS
20th January 2018, 15:15
Maybe it's not the frames but the string resources that will never get freed up. Shorten everything in the runtime section and see if it crashes later.

There are no strings used during Frame Serving stage, and the MI (Multi-Instance) template used above works flawlessly in all other MI functions that I've coded, I think is purely down to the single frame accumulator 'hanging on' to all previous frames used in the overlaying bit.
The MI stuff, usually poses no problem (no major problem) in functions using quite a few locally created strings during frame serving, but I usually make most of those (eg formatting strings for RT_Subtitle or similar), accessible via constant variables.

The MI interface created primarily by Martin53 & Gavino works extremely well.

EDIT: Actually, this MI function is way simpler than a lot of MI functions that I've done, using lots of string allocations (and they proved no problem less than maybe 500,000 or more frames).

EDIT:
On WXP32 4GB Ram, with Task Manager Open, opening script into VDub FilterMod,

where all strings already created and VDub displaying frame 0.

Physical Memory (K)
Total 3405284
Available 2944132
System Cache 1047929

Page File Usage 512 MB



Moving to frame 1,

Physical Memory (K)
Total 3405284
Available 2939632
System Cache 1048304

Page File Usage 515 MB

StainlessS
20th January 2018, 15:46
Test mod to script, highlited in BLUE, (removed Overlays, replaced with simple assignment to self), produces almost no change in ram usage,
when playing script.


Function SBX_MI(clip c,int Factor, string "Mode", float "Opacity") {
c
myName = "SBX_MI: "
Mode = Default(Mode,"lighten")
Opacity = Default(Opacity,1.0)
Limit = (Factor<=0) ? 1 : Max(1, FrameCount/Factor)
FuncS="""
Function Fn@@@(clip c,Int factor,String mode,Float opacity,Int limit) {
n=current_frame
if(Prev@@@ != n) {
c
cf=c.Trim(n,-1) # current frame
if(Prev@@@ != n-1) {
Global B@@@ = cf # First Frame OR User Jumping, Init/Reset to current frame
} else {
# limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0)
if(limit > 1) {
Global B@@@ = B@@@
} # Else, B@@@ = Previous B@@@
# b.Overlay(last, mode=mode, opacity=opacity)
Global B@@@=B@@@
}
Global Prev@@@=n # REM for next time
} # Else, Cache failure, repeat request for current frame, just return same as last time
Return B@@@
}
#######################################
# Unique Global Variables Initialization
#######################################
Global Prev@@@=-666
#######################################
# Unique Runtime Call, GScriptClip must be a one-liner:
#######################################
ARGS = "Factor,Mode,Opacity,Limit"
c.GScriptClip("Fn@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
#######################################
# Unique Identifier Definition
#######################################
GIFunc="SBX_MI" # Function Name, Supply unique name for your multi-instance function.
GIName=GIFunc+"_InstanceNumber" # Name of the Instance number Global
RT_IncrGlobal(GIName) # Increment Instance Global (init to 1 if not already exists)
GID = GIFunc + "_" + String(Eval(GIName))
InstS = RT_StrReplace(FuncS,"@@@","_"+GID)
# RT_WriteFile("DEBUG_"+GID+".TXT","%s",InstS)
FrameCount!=1 ? GScript(InstS) : Last
Return Last
}


But of course it not dont work as required, and only displays frame 0.

EDIT: Also tried append


Global B@@@=Overlay(B@@@,cf,mode=Mode,opacity=Opacity).changefps(c,true)


Due to this post (did not do anything to help, desparate measure)
I've heard tell ChangeFPS does some caching; maybe that's the reason it works here -
https://forum.doom9.org/showthread.php?p=1831109#post1831109

StainlessS
20th January 2018, 23:58
BELOW LINK UPDATE to TAKE_2 (RGB32 Alpha would contain rubbish, fixed, copies Alpha)
BELOW LINK UPDATE to TAKE_3 (Source fix in dprintf(), not used, No Change to Binary)

Here, FrameStore Plugin v0.0, fill your boots real.finder (Incl source, x86 only, ~12KB).
http://www.mediafire.com/file/4d74wly75ii8i1g/FrameStore_v0.0_avs26_20180120_TAKE_3.zip

FrameStore_ReadMe.txt

FrameStore(clip c) # by StainlessS

An Avisynth v2.6, Filter to both take and produce a single frame.
All standard AVS v2.6 Colorspaces.

The function creates a store for FRAME 0 of the input clip c.
The function is intended to sever the link between a frame and any clips that it is reliant upon.



A=Colorbars.trim(0,-1) # 1 frame
B=A.FlipVertical # B is reliant upon clip A
C=A.Overlay(B,Opacity=0.5) # C is reliant upon clips A and B

D=C.FrameStore # D is not reliant upon A, or B, or C.

return D


SBX_MI.avs [Pagefile usage Flatlines at about 550MB on my test clip (The John Meyer parade Clip)].
EDIT: Above machine was newly set up from scratch, on another machine with much more stuff (software/codecs etc), it flatlines at about 620MB.

Function SBX_MI(clip c,int Factor, string "Mode", float "Opacity") {
/*
Requires,
RT_Stats, FrameStore (c) StainlessS.
GSCript,Grunt, (c) Gavino.
*/
c
myName = "SBX_MI: "
Mode = Default(Mode,"lighten")
Opacity = Default(Opacity,1.0)
Limit = (Factor<=0) ? 1 : Max(1, FrameCount/Factor)
FuncS="""
Function Fn@@@(clip c,Int factor,String mode,Float opacity,Int limit) {
n=current_frame
if(Prev@@@ != n) {
c
cf=c.Trim(n,-1) # current frame
if(Prev@@@ != n-1) {
Global B@@@ = cf # First Frame OR User Jumping, Init/Reset to current frame
} else {
# limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0)
if(limit > 1) {
Global B@@@ = Overlay(B@@@,cf, opacity=float(limit)/FrameCount)
} # Else, B@@@ = Previous B@@@
# b.Overlay(last, mode=mode, opacity=opacity)

# REMOVE '.FrameStore' from end of next line to remove FrameStore and crash this function when out of memory.
Global B@@@=Overlay(B@@@,cf,mode=Mode,opacity=Opacity).FrameStore # Oops, removed .ChangeFps, prev attempt at fix

}
Global Prev@@@=n # REM for next time
} # Else, Cache failure, repeat request for current frame, just return same as last time
Return B@@@
}
#######################################
# Unique Global Variables Initialization
#######################################
Global Prev@@@=-666
#######################################
# Unique Runtime Call, GScriptClip must be a one-liner:
#######################################
ARGS = "Factor,Mode,Opacity,Limit"
c.GScriptClip("Fn@@@(last, "+ARGS+")", local=true, args=ARGS)
"""
#######################################
# Unique Identifier Definition
#######################################
GIFunc="SBX_MI" # Function Name, Supply unique name for your multi-instance function.
GIName=GIFunc+"_InstanceNumber" # Name of the Instance number Global
RT_IncrGlobal(GIName) # Increment Instance Global (init to 1 if not already exists)
GID = GIFunc + "_" + String(Eval(GIName))
InstS = RT_StrReplace(FuncS,"@@@","_"+GID)
# RT_WriteFile("DEBUG_"+GID+".TXT","%s",InstS)
FrameCount!=1 ? GScript(InstS) : Last
Return Last
}

AviSource("Parade.avi").trim(500,0) # Some clip

SBX_MI(100)


EDIT:
Source.

/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

//#define BUG // Uncomment to enable DPRINTF() output

// Version 0.0 FrameStore
/////////////////////////////////

#include "compiler.h"
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include "avisynth.h"

#ifdef BUG
int dprintf(char* fmt, ...) {
char printString[2048]="FrameStore: ";
char *p=printString;
for(;*p++;); // These lines added in TAKE3
--p; // @ null term
va_list argp;
va_start(argp, fmt);
vsprintf(p, fmt, argp);
va_end(argp);
for(;*p++;);
--p; // @ null term
if(printString == p || p[-1] != '\n') {
p[0]='\n'; // append n/l if not there already
p[1]='\0';
}
OutputDebugString(printString);
return p-printString; // strlen printString
}
#endif

class FrameStore : public IClip {
private:
const VideoInfo vi;
PVideoFrame frame;
bool parity;
//
public:
FrameStore(const VideoInfo& _vi,PVideoFrame _frame, bool _parity);
~FrameStore();
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) {}
const VideoInfo& __stdcall GetVideoInfo() { return vi; }
bool __stdcall GetParity(int n) { return (vi.IsFieldBased() ? (n&1) : false) ^ parity; }
int __stdcall SetCacheHints(int cachehints,int frame_range) {return 0;}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};

FrameStore::FrameStore(const VideoInfo& _vi,PVideoFrame _frame, bool _parity)
: vi(_vi), frame(_frame), parity(_parity) {
DPRINTF("Constructor: ENTER/EXIT")
}

FrameStore::~FrameStore() {
DPRINTF("Destructor: ENTER/EXIT")
}

PVideoFrame __stdcall FrameStore::GetFrame(int n, IScriptEnvironment* env) {
DPRINTF("GetFrame: ENTER on frame %d",n)
n = 0; // Ignore frame and get frame 0
DPRINTF("GetFrame: EXIT, returning Frame")
return frame;
}

static AVSValue __cdecl Create_FrameStore(AVSValue args, void*, IScriptEnvironment* env) {
DPRINTF("Create_FrameStore: ENTER")
PClip child = args[0].AsClip(); // clip compulsory arg
const VideoInfo &InVi = child->GetVideoInfo();

if(!(InVi.IsRGB24() || InVi.IsRGB32() || InVi.IsYUY2() || InVi.IsYV12() || InVi.IsYV16() || \
InVi.IsYV24() || InVi.IsY8() || InVi.IsYV411()))
env->ThrowError("FrameStore: Invalid Colorspace (standard AVS only)");
if(!InVi.HasVideo() || InVi.num_frames<=0)
env->ThrowError("FrameStore: No Video");
//
VideoInfo vi = InVi;
bool parity = child->GetParity(0);
//
vi.audio_samples_per_second =0; // 0 means no audio
vi.sample_type =0; // as of 2.5
vi.num_audio_samples =0; // changed as of 2.5
vi.nchannels =0; // as of 2.5
vi.num_frames =1;
//
PVideoFrame src = child->GetFrame(0, env);
const int rowsize = src->GetRowSize(PLANAR_Y); // PLANAR_Y no effect on RGB or YUY2
const int pitch = src->GetPitch(PLANAR_Y);
const int height = src->GetHeight(PLANAR_Y);
const BYTE * srcp = src->GetReadPtr(PLANAR_Y);
//
PVideoFrame dst = env->NewVideoFrame(vi); // Create initial frame
const int dpitch = dst->GetPitch(PLANAR_Y);
BYTE * dstp = dst->GetWritePtr(PLANAR_Y);
//
DPRINTF("Create_FrameStore: Making Frame")
int x,y;
if(vi.IsPlanar()) { // Planar
for(y=height;--y>=0;) {
for(x=rowsize;--x>=0;) {
dstp[x] = srcp[x];
}
srcp += pitch;
dstp += dpitch;
}
const int srowsizeUV = src->GetRowSize(PLANAR_U);
if(srowsizeUV) { // Not Y8
const int spitchUV = src->GetPitch(PLANAR_U);
const int sheightUV = src->GetHeight(PLANAR_U);
const BYTE * srcpU = src->GetReadPtr(PLANAR_U);
const BYTE * srcpV = src->GetReadPtr(PLANAR_V);
//
const int dpitchUV = dst->GetPitch(PLANAR_U);
BYTE * dstpU = dst->GetWritePtr(PLANAR_U);
BYTE * dstpV = dst->GetWritePtr(PLANAR_V);
//
for(y=sheightUV;--y>=0;) {
for(x=srowsizeUV;--x>=0;) {
dstpU[x] = srcpU[x];
dstpV[x] = srcpV[x];
}
srcpU += spitchUV;
srcpV += spitchUV;
dstpU += dpitchUV;
dstpV += dpitchUV;
}
}
} else if(vi.IsYUY2()) { // YUY2
for(y=height;--y>=0;) {
for(x=rowsize;(x-=4)>=0;) {
dstp[x + 0] = srcp[x + 0];
dstp[x + 1] = srcp[x + 1];
dstp[x + 2] = srcp[x + 2];
dstp[x + 3] = srcp[x + 3];
}
srcp += pitch;
dstp += dpitch;
}
} else { // RGB
if(vi.IsRGB24()) {
for(y=height;--y>=0;) {
for(x=rowsize;(x-=3)>=0;) {
dstp[x + 0] = srcp[x + 0];
dstp[x + 1] = srcp[x + 1];
dstp[x + 2] = srcp[x + 2];
}
srcp += pitch;
dstp += dpitch;
}
} else {
for(y=height;--y>=0;) {
for(x=rowsize;(x-=4)>=0;) {
dstp[x + 0] = srcp[x + 0];
dstp[x + 1] = srcp[x + 1];
dstp[x + 2] = srcp[x + 2];
dstp[x + 3] = srcp[x + 3]; // Added in TAKE_2, Copy Alpha
}
srcp += pitch;
dstp += dpitch;
}
}
}
DPRINTF("Create_FrameStore: Calling Constructor")
AVSValue ret = new FrameStore(vi,dst, parity);
DPRINTF("Create_FrameStore: returning Framestore filter. EXIT OK")
return ret;
}


/* New 2.6 requirement!!! */
// Declare and initialise server pointers static storage.
const AVS_Linkage *AVS_linkage = 0;
/* New 2.6 requirement!!! */
// DLL entry point called from LoadPlugin() to setup a user plugin.
extern "C" __declspec(dllexport) const char* __stdcall
AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
/* New 2.6 requirment!!! */
// Save the server pointers.
AVS_linkage = vectors;
DPRINTF("AvisynthPluginInit3: Adding FrameStore plugin")
env->AddFunction("FrameStore", "c", Create_FrameStore, 0);
DPRINTF("AvisynthPluginInit3: FrameStore plugin Added, Returning to Avisynth")
return "`FrameStore' FrameStore plugin";
// A freeform name of the plugin.
}

EDIT: FrameStore produces NO audio.
EDIT: Arh damn, spotted bug in RGB32, Alpha will hold rubbish, I'll up a another zip in a moment. (HAVE FIXED LINK)
EDIT: TAKE_3, Source fix in unused dprintf(), no change to binary. zip updated.
EDIT: Somebody wanna test it in AVS+ ? (standard v2.6 colorspaces)
EDIT: Real.Finder, any preference, Copy/Nullify Alpha of RGB32 ? (I used Copy, seemed more sensible for frame storage).

EDIT: Here a frame from my test script (Not the best choice of clip, it dont stay on scene for more than about 2 secs)
SBX_MI(10,"lighten",0.5)
https://s20.postimg.cc/akvsdtv4t/SBX_MI.jpg (https://postimages.cc/)

real.finder
21st January 2018, 05:51
that seems to work StainlessS, thanks

but the problem in runtime should have a Solution too, if someone want to do thing that used the previous frames

and that maybe related to crashes that happen sometimes when there are some function that has runtime part when reach some thousands frames (like 80% of 24 fps episode)

real.finder
21st January 2018, 05:53
EDIT: Here a frame from my test script (Not the best choice of clip, it dont stay on scene for more than about 2 secs)
SBX_MI(10,"lighten",0.5)
https://s20.postimg.org/akvsdtv4t/SBX_MI.jpg (https://postimages.org/)

I use "GlassPack262 Cars Moving in the Night" from youtube

https://s9.postimg.org/rontfhfmn/New_File_12_000758.png

SBX_MI(5,"lighten")

StainlessS
21st January 2018, 06:13
A frame cannot be released whilst something else holds a reference to it, things would start exploding.

When SRestore (or whatever) is doing its stuff, it references only input frames, and when it moves on in the clip, those frames are no longer referenced, because it is now references other later frames, and so those input frames can be released. (They would actually not be released until all frames that reference them, including any cached frames, are released).
When you instead reference previous OUTPUT frames, where E references D, and D refrences C, and C references B, and B references A, then A cannot be released until EVERY following frame which references it (directly or indirectly) is also released.
OverLay, Layer, Stackhorizontal, or any other filter which 'combines' multiple frames, or transforms single frames, will hold a reference to all source frames involved (where source in this case would include previous OUTPUT frames if that was what was used as source the the filter).

Any script which does as above, would crash Out Of Memory very quickly, just like your script did (and mine), way before thousands of frames were processed.

I dont think that there will likely be any other solution other than something like FrameStore.

EDIT: Oh, and nice pic too. :)

EDIT: Where my original script crashed at about 845 frames, thats how many OUTPUT frames were being held in memory, ALL AT ONCE.
(In actual fact, there would be additional intermediate frames also held in ram/pagefile).

real.finder
21st January 2018, 06:17
I dont think that there will likely be any other solution other than something like FrameStore.

if so, what about runtimestore() :) or anything that mean not only frames

and make it part for grunt, so I will added it to srestore and others to make it more clean

StainlessS
21st January 2018, 06:35
"not only frames"

Its only frames that are the problem.
FrameStore only makes a copy of a single frame, so that all frames that it depends upon can be released, I dont know if it would be at all useful in the likes of SRestore.
If an output frame cannot be found in cache, then that output frame has to be re-created using the source frames to it, that is why those frames upon which it depends, cannot be released.
Gavino is welcome to mod and incorporate the Framestore function/filter into Grunt, but I dont really think they are at all related.

EDIT: FrameStore is problably not much use outside of Scriptclip or other runtime functions.
EDIT: It is probably a fair candidate for inclusion in RT_Stats though.

StainlessS
21st January 2018, 07:21
Real.Finder,
I've just tried your last script (with only the non-optional blendfactor fixed, and SFrameBlendX_limit removed), and to my amazement, it goes for about 2,600 frames.
It was pretty much what I tried earlier but I only removed Local=true, not knowing that True was the default when args used, so I should have set Local=false and would have got the same result as you.

I tried with your script and also SBX_MI, with a subtract and amplify (via ClipDelta function), and we are getting identical results.

EDIT: Although, when trying clip again, it sort of stuttered at about 1,600 frames, and then continued, not sure what happened there (try/catch ?).

Your script with slight mods.

Function SFrameBlendX(clip C, int blendfactor, string "blend_mode", float "blend_opacity") {
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
FrameCount!=1 ? ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
b = bb ? limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0) : nop()
b = bb ? b.Overlay(last, mode=blend_mode, opacity=blend_opacity) : last
return b
""",args="blend_mode, blend_opacity, limit", local=false) : last
}


EDIT: This still both stutters (~1400) and crashes at about 2,600 frames.

Function SFrameBlendX(clip C, int blendfactor, string "blend_mode", float "blend_opacity") {
C
limit = (blendfactor<=0) ? 1 : Max(1, FrameCount/blendfactor)
FrameCount!=1 ? ScriptClip("""
try{bb=isclip(b)} catch(error_msg) {bb=false}
try {
b = bb ? limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0) : nop
b = bb ? b.Overlay(last, mode=blend_mode, opacity=blend_opacity) : last
} catch(error_msg) {
b=Last # attempt at reset, dont work.
}
return b
""",args="blend_mode, blend_opacity, limit", local=false) : last
}


EDIT: By the way, the logic in the SBX_MI() script is the same as your original logic, pretty much, but being multi-instance you can run
two copies together, with Local=false in your script, the b variables (and perhaps others) would interfere with each other.

EDIT: Suggest that you study the SBX_MI thing, its really not a very complex script and does pretty much exactly what your script does,
the Martin53/Gavino Multi-instance interface makes for a very powerful little gizmo, with GScript and Grunt making things easy
to acheive, it would be worth your time having a play with it. All you have to remember is to add @@@ to global names and when
assigning to them always use Global x@@@=y, and copy the other stuff near the bottom of the SBX_MI script.

EDIT: Heres a little MI test script to help you figure out how it works:- https://forum.doom9.org/showthread.php?p=1805249#post1805249
Use DebugView (google) to see the messages.
Uncomment the RT_WriteFile line near the end to write the resultant script as a text file, it will be different for each
instance of the function.

Here, is instance 1 of SBX_MI

Function Fn_SBX_MI_1(clip c,Int factor,String mode,Float opacity,Int limit) {
n=current_frame
if(Prev_SBX_MI_1 != n) {
c
cf=c.Trim(n,-1) # current frame
if(Prev_SBX_MI_1 != n-1) {
Global B_SBX_MI_1 = cf # First Frame OR User Jumping, Init/Reset to current frame
} else {
# limit>1 ? Overlay(b.Loop(2,0,0),last,opacity=float(limit)/FrameCount) : b.Loop(2,0,0)
if(limit > 1) {
Global B_SBX_MI_1 = Overlay(B_SBX_MI_1,cf, opacity=float(limit)/FrameCount)
} # Else, B_SBX_MI_1 = Previous B_SBX_MI_1

# b.Overlay(last, mode=mode, opacity=opacity)
# REMOVE '.FrameStore' from end of next line to crash this function.
Global B_SBX_MI_1=Overlay(B_SBX_MI_1,cf,mode=Mode,opacity=Opacity).FrameStore

}
Global Prev_SBX_MI_1=n # REM for next time
} # Else, Cache failure, repeat request for current frame, just return same as last time
Return B_SBX_MI_1
}
#######################################
# Unique Global Variables Initialization
#######################################
Global Prev_SBX_MI_1=-666
#######################################
# Unique Runtime Call, GScriptClip must be a one-liner:
#######################################
ARGS = "Factor,Mode,Opacity,Limit"
c.GScriptClip("Fn_SBX_MI_1(last, "+ARGS+")", local=true, args=ARGS)



EDIT: Maybe of interest:-
https://forum.doom9.org/showthread.php?t=169624

EDIT: Whatever that stutter is, we get different results when it happens.

EDIT: Found reason for different results when stutter, I originally used A, and B, instead of AC, and BC below, B was interfering with B in your script.

AviSource("Parade.avi") # Some clip
AC=SFrameBlendX(10,"lighten",0.5)
BC=SBX_MI(10,"lighten",0.5)
ClipDelta(AC,BC,true)

real.finder
21st January 2018, 17:28
yes, that line
global SFrameBlendX_limit = limit
I forget to remove