JSVSynth is a plugin to AviSynth that allows the use of JavaScript within AviSynth scripts. It uses Google's V8 JavaScript engine (the same JavaScript engine that Chrome uses) to run JavaScript. This means that the pure JavaScript code will run with the same speed that Chrome's pure JavaScript does. (The scripting bridge provided by the plugin may not be quite as fast.)
JSVSynth is licensed under the GPLv3. Note that, just like AviSynth being under the GPL, this does not require scripts written using JSVSynth being released under the GPLv3.
You can download the current alpha version of JSVSynth (version 0.0.1-2013102300).
For complete documentation, check out the complete manual.
With a compiled version of the plugin (see Building the Plugin below), using it from within AviSynth is fairly simple:
LoadPlugin("jsvsynth.dll")
You can then use AviSynth's multi-line string blocks to write your actual JavaScript code:
JavaScript("""
avs.Subtitle(avs.BlankClip({color:0xFF}), "Hello from JavaScript!")
""")
The JavaScript object avs
offers access to any AviSynth variable or
function from within JavaScript:
avsvalue = "Set in AviSynth"
JavaScript("""
avs.Subtitle(avs.BlankClip({color:0xFF}), avs.avsvalue)
""")
You can almost do the reverse, but with one caveat:
avsvalue = 0
JavaScript("""
avs.avsvalue = "Hello " + "AviSynth!";
""")
Subtitle(BlankClip(color=$FF), avsvalue)
The value has to be set to something before it will work. Otherwise, even though the value will be set, AviSynth will give an "I don't know what avsvalue means" error for the variable.
I'm not entirely sure whether or not this is a problem with AviSynth or is instead something I'm unclear about within the AviSynth API.
For the most part, this works like you'd expect: invoke a function using the exact same values you would in AviSynth. However, things work slightly differently when using JavaScript than they would in AviSynth.
clip.Trim(50)
. A future
update will likely attempt to make code like that possible.avs.BlankClip({color:0xFF})
above. This value must always be the
last value given. Otherwise, it'll be translated into a string (likely
[object Object]
).last
is not a thing in JavaScript. Calling a filter without specifying a
clip won't automatically cause last
to be used.AviSynth is case insensitive, and the avs
object will reflect that:
avs.blankclip()
is the same as avs.BlankClip()
.
JavaScript is not: avs.blankclip()
is not the same as
AVS.blankclip()
!
This is one of the reasons why you can't use for(var i in avs)
to iterate
over variables in AviSynth. (The primary reason is that the AviSynth API offers
absolutely no way to pull the list of variables in any case.)
Another weird caveat is passing functions back to AviSynth. With most values, you can actually use the evaluation of the JavaScript block:
Subtitle(BlankClip(), String(JavaScript("12 + 30")))
However, you can't pass functions back in this fashion, as this doesn't make sense in AviSynth: functions aren't "variables" per se, they're instead in a separate (of sorts) namespace. (If you do write a bit of JavaScript that evaluates to a function, the result will be the function converted to a string.)
This doesn't mean you can't use JavaScript functions from AviSynth: you just
have to "export" them using the avs
object:
JavaScript("""
avs.fromjs = function(str) {
return "Hello " + str.substring(0,1).toUpperCase() + str.substring(1);
}
""")
Subtitle(BlankClip(), FromJS("world"))
Note that (at present) there's no way to specify a function's signature or to accept named arguments from AviSynth (as those rely on a signature).
Another caveat with this is that using functions in this way will (potentially) leak memory: these functions are bound to the AviSynth environment and can never be collected while the script runs. (I don't think this is any real practical concern, but it's worth noting somewhere.) Theoretically everything should be collected when AviSynth shuts down, but I haven't verified this.
It may be tempting to write a JavaScript function within an AviSynth function
and use that within Animate
. Don't do that. The JavaScript function will
be recompiled every single frame and you won't get any of the speed advantages
of V8's JIT.
Instead, use the above method of exporting functions and animate that way:
JavaScript("""
avs.animfun = function(clip, str, y) {
return avs.Subtitle(clip, str, {y:y});
}
""")
c = BlankClip()
Animate(0, 50, "animfun", c,"Scrolling",0, c,"Scrolling",50)
In order to build this plugin, you'll require V8, which is not included. (At one point I was hoping to include just the V8 binaries, but they're large enough and V8 is a fast enough moving target that it just isn't feasible.)
A third_party.bat
file is provided to help automate the process of downloading
the third party software. Since there are four separate repositories you need to
grab to get everything up and running under Windows, and since the
third_party.bat
file keeps everything to the same versions as the original
code, you should probably stick with using that. However, you can also manually
grab V8 into the "v8" directory by running the following command:
svn checkout http://v8.googlecode.com/svn/trunk v8
Note that while using git is "recommended," the git instructions are for accessing the "bleeding edge" branch, while all we want is the "stable" branch, which is the SVN trunk branch.
Once you have V8 downloaded, you can build it by following the V8 building with GYP instructions. Make sure you note the bit about downloading the Chromium-provided Cygwin, as the Visual Studio build will not work without it.
Note that if you're using a more recent version of Visual Studio (or is it just
the express versions?) you'll need to use MSBuild
instead of devenv
to build
the project from the command line. The command is similar:
MSBuild build\all.sln /p:Configuration=Release
If you're planning on building the debug version of the plugin, you'll want to
use /p:Configuration=Debug
instead.
Once V8 is built, you should be able to build everything else using the
jsvsynth.sln
.