Log in

View Full Version : OnDemandFilter v0.1 - reduces loading times and memory usage


wonkey_monkey
27th July 2025, 13:58
OnDemandFilter v0.2 (http://horman.net/avisynth/)
Direct link (http://horman.net/avisynth/download/OnDemandFilter_v0.2.zip)
Documentation (http://horman.net/avisynth/OnDemandFilter.html)


0.2: Possibly safer unloadng
0.1: First release


OnDemandFilter is a wrapper for calls to other filters which defers invoking the filter until it's truly required, reducing initial script loading time. It also only allows a specified number of such filters to be active at any one tme, reducing memory usage.

In testing, a script with peak memory usage of over 10Gb was reduced to using less than 450Mb, while initial loadng time was reduced from 10s to 1s.

As an example, suppose you have (as I do) a script which opens dozens of HD sources using BestSource (https://github.com/vapoursynth/bestsource/) (or FFMpegSource, DirectShowSource, etc). Even with caching turned off, each source will require a few hundred megabytes to keep a GOP's worth of uncompressed frames in RAM for decoding purposes.

Instead of doing this:

a = BSSource("file1.mkv", atrack = 1, cachemode = 4, vcachesize = 0)
b = BSSource("file2.mkv", atrack = 1, cachemode = 4, vcachesize = 0)
c = BSSource("file3.mkv", atrack = 1, cachemode = 4, vcachesize = 0)
[...]

a + b + c + d + e + f + g + h + ...

...you can now do this, passing a filter name, plus its parameters as an array:

a = OnDemandFilter("BSSource", [ "file1.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ])
b = OnDemandFilter("BSSource", [ "file2.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ])
c = OnDemandFilter("BSSource", [ "file3.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ])
[...]

OnDemandFilterLimit(1) # only 1 clip will be loaded at any time

a + b + c + d + e + f + g + h + ...

In the latter case, only one instance of BSSource will be invoked at any time, depending on what frames are being requested. For example, if clip "a" is 100 frames, once playback reaches frame 100, clip "a"'s instance of BSSource will be unloaded to make way for clip "b", releasing any RAM it was using.

OnDemandFilterLimit sets the number of clips allowed to be active at any one time. It defaults to 4 to prevent transitions (dissolves, for example) from thrashing as multiple clips would be repeatedly loaded and unloaded.

vi_clip parameter:

In the above example, the specified filter must be opened on load to get the parameters of the resulting clip (resolution, pixel type, audio parameters etc). To avoid this, and to increase initial loading time, a placeholder clip can be specified which should match the result of the deferred filter, e.g.:

vi_clip = blankclip(length = 1000000, pixel_type = "yv12", width = 1920, height = 1080, fps = 25, audio_rate = 48000, channels = 2)


a = OnDemandFilter("BSSource", [ "file1.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ], vi_clip.trim(0, [length of clip a - 1]))
b = OnDemandFilter("BSSource", [ "file2.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ], vi_clip.trim(0, [length of clip b - 1]))
c = OnDemandFilter("BSSource", [ "file3.mkv", [ "atrack", 1 ], [ "cachemode", 4 ], [ "vcachesize", 0 ] ], vi_clip.trim(0, [length of clip c - 1]))
[...]

OnDemandFilterLimit(1) # only 1 clip will be loaded at any time

a + b + c + d + e + f + g + h + ...

Hopefully this all makes sense. If not, ask away.

Jamaika
27th July 2025, 15:37
OnDemandFilter is a wrapper for calls to other filters which defers invoking the filter until it's truly required, reducing initial script loading time. It also only allows a specified number of such filters to be active at any one tme, reducing memory usage.
Short test
function LibavSource2(string path, int "atrack",
\ int "fpsnum", int "fpsden",
\ string "format", bool "cache")
{
atrack = Default(atrack, -1)
fpsnum = Default(fpsnum, 0)
fpsden = Default(fpsden, 1)
cache = Default(cache, true)

format = Default(format, "")

video = LWLibavVideoSource(path,
\ fpsnum=fpsnum, fpsden=fpsden, format=format,
\ cache=cache)
return (atrack==-2) ? video: AudioDub(video,
\ LWLibavAudioSource(path, stream_index=atrack, cache=cache))
}
OnDemandFilter("LibavSource2", [ "input_rgba.avi", [ "fpsnum", 30000 ], [ "fpsden", 1001 ] ])
OnDemandFilterLimit(1)
Assertion failed: IsClip(), file interface.cpp, line 872

wonkey_monkey
27th July 2025, 15:43
Try switching the last two lines around. Otherwise the result of the clip is the value being returned from OnDemandFilterLimit, which is an int.

Jamaika
27th July 2025, 15:47
It didn't work. GCC 15.1.0 with avisynth 91fc408 doesn't work. Same message.

wonkey_monkey
27th July 2025, 16:01
Copied-and-pasted your script, switched the last two lines around, changed the filename for one on my drive... and it worked :confused:

OnDemandFilterLimit(1) # or you could remove this completely
OnDemandFilter("LibavSource2", [ "my_file.avi", [ "fpsnum", 30000 ], [ "fpsden", 1001 ] ])


Assuming your file works if you call LibavSource2 directly... I have no idea. I'm using the 3.7.5 compiled release.

Jamaika
27th July 2025, 16:26
You can test it yourself
https://www.sendspace.com/file/46bh9i

wonkey_monkey
27th July 2025, 16:38
With that script and file (uvg266_yuv420p08le.vvc) I just get

LWLibavVideoSource: failed to get the video track.

either way.

I don't think we're going to get very far here.

Jamaika
27th July 2025, 16:42
I gave a bad example. Add another file and check.
ffplay_avx2.exe OnDemandFilter.avs

wonkey_monkey
27th July 2025, 16:54
Works in VirtualDub. Works in AvsMeter64.exe. Doesn't work with ffplay_avx2.exe (direct call to LibavSource2 does work).

I have no idea what ffplay_avx2.exe is doing differently.

Jamaika
27th July 2025, 16:58
Other add-ons work. That was a rhetorical question on my part.

LigH
27th July 2025, 18:50
:thanks:

Great job, interesting attempt. I will surely test it with a huge chain of cross-faded clips when I find enough time.

real.finder
28th July 2025, 04:20
Looks interesting! thanks!

Out of curiosity, is the memory consumption with this will be less than in video editing programs like kdenlive?

LigH
6th August 2025, 15:54
I made some attempts to rewrite my scripts to use this filter. Unfortunately, it doesn't work well with variable length parameter lists, I need to name them all explicitly, which makes the calls incredibly complex. So it won't be a very convenient solution for my case.

wonkey_monkey
6th August 2025, 21:18
Actually you shouldn't have to name the parameters if you're supplying them in the order the function expects. Sorry that this wasn't obvious; I did rather throw it together!

This should work, for example:

OnDemandFilter("LibavSource2", [ "my_file.avi", -1, 30000, 1001 ]) # filename, audio track, fpsnum, fpsden

If you skip one by providing a named one instead, then it seems to fill in the skipped one with the next unnamed one. Not 100% sure on that but probably best to stick with AviSynth's general scripting requirement - if you name any parameter, you should name all subsequent parameters.

LigH
7th August 2025, 20:41
OK, I put it in the wrong place. Should not try to load custom functions on demand, only the sources inside the function.

Because I use a lot of Trim's and Dissolve's, and Video and Audio source separately, I set OnDemandFilterLimit(4) to be safe, and so far the audio conversion finished, probably successfully, and the video conversion started ... will report about the final mux when done.

wonkey_monkey
7th August 2025, 21:02
Because I use a lot of Trim's and Dissolve's, and Video and Audio source separately, I set OnDemandFilterLimit(4) to be safe

For the same reason, 4 is already the default :cool:

You probably don't need to on-demand the audio sources though. I doubt they have large memory requirements.

LigH
7th August 2025, 23:17
Success! Processing was fast and reliable, moderate RAM demands (only 5 GB instead of 15+), result was in sync.

:thanks:

StainlessS
8th August 2025, 07:31
Kudos Wonkey!, you're always bursting with surprises. :)

wonkey_monkey
8th August 2025, 10:55
Yay! I'm useful!

LigH
18th August 2025, 15:06
I noticed that I had no problems with LSMASH*Source or LwLibav*Source, but when I switched to FFMS2 which is more stable in handling WMV, rarely I get a "System Exception - Access Violation" in VirtualDub2 when editing and reloading (F2; F5) an AVS+ script using OnDemandFilter("FFVideoSource") in a user function. Might be a hint that not every source filter enjoys deferred loading? After closing and reopening the script I can usually continue editing.

PS: I noticed I forgot a few edits to use remuxed MKV instead of original WMV files; anyway, probably worth knowing that where may be rare unstable cases, but reloading / restarting usually helped reliably.

wonkey_monkey
18th August 2025, 16:58
It might be OnDemandFilter not being as safe as it could be when unloading. I always seem to run into these sorts of problems with my filters.

v0.2 (http://horman.net/avisynth/download/OnDemandFilter_v0.2.zip) might work better. If not, then I don't think there's any more it can do.