I have wrapped VapourSynth in Nim programming language. Nim is to C like CoffeScript is to JavaScript.
The main advantages I see are:
1. Writting a pipeline is very easy. It feels even easier than python. For instance, reading the first 100 frames, transposing and piping to stdout:
Code:
import vapoursynth
Source("video.mkv")[0..100].Transpose.Pipey4m
2. The code is compiled, so you end up with a binary file.
3. It should be as fast as C. (But I haven't performed any serious benchmark yet)
4. You can even perform filters like in C.
mycrop.nim is an implementation similar (not complete) to
vapoursynth's simplefilter.c version.
5. Nim has metaprogramming superpowers. I have created a macro to make easier the development of filters. This way, the creation of a filter becomes:
Code:
import ../vapoursynth
import mymacro
import options
import strformat
newFilter("MyCropRel"):
parameters:
inClip clip # Input video (mandatory)
optional:
left Natural
right Natural
top Natural
bottom Natural
validation:
# We assign the destination's frame size
data.width = data.vi.width.Natural - data.left - data.right
data.height = data.vi.height.Natural - data.top - data.bottom
# Avoid too much cropping
assert( (data.top + data.bottom) < data.vi.height, "vertical cropping too big" )
assert( (data.left + data.right) < data.vi.width, "horizontal cropping too big" )
# make sure the given values will work with subSampling
assert( (data.left mod divHorizontal) == 0, &"subSampling: \"left\"={data.left} not divisible by {divHorizontal}" )
assert( (data.top mod divVertical) == 0, &"subSampling: \"top\"={data.top} not divisible by {divVertical}" )
assert( (data.width mod divHorizontal) == 0, &"subSampling: new \"width\"={data.width} not divisible by {divHorizontal}" )
assert( (data.height mod divVertical) == 0, &"subSampling: new \"height\"={data.height} not divisible by {divVertical}")
if (data.left == 0 and data.right == 0 and data.top == 0 and data.bottom == 0):
passTrough()
processing:
let dst = src.newVideoFrame(data.width, data.height)
#echo "Frame: ", n
for i in 0..<srcNumPlanes:
var srcPlane = src.getPlane(i)
var dstPlane = dst.getPlane(i)
srcPlane.goto(row=data.top, col=data.left)
if data.height > 0:
srcPlane.copy(dstPlane, rows=data.height, cols=data.width)
As you can see it has three areas:
- parameters: The function parameters, where left, right, top, bottom has Natural type (>=0)
- validation: contains all the checks needed for the inputs
- processing: which is pretty human friendly code. Creates a new frame, iterates over the planes, goes to a position in the plane and copiesss a number of rows up to certain column. It does so bearing in mind the subsampling.
This is about 40 lines for a working filter. You can find it
here.
In order to use this filter you would do:
Code:
import ../vapoursynth
import options
import croprel
# Reads the file, applies the Simple filter and saves the result in a file
Source("../../test/2sec.mkv").MyCropRel(top=some(150.Natural),bottom=some(150.Natural)).Savey4m("original.y4m")
How much time does it take to compile above's code? Less than two seconds
Code:
$ time nim c modifyframe
real 0m1,284s
user 0m1,361s
sys 0m0,136s
How much time does it take to perform the cropping to the two seconds video?
Code:
$ time ./modifyframe
real 0m0,176s
user 0m0,141s
sys 0m0,038s
How much time does it take the original filter in pure C?
Code:
$ time ./modifyframe2
real 0m0,186s
user 0m0,148s
sys 0m0,037s
So pretty similar.
I hope you find it useful. There is lot of work to do ahead nonetheless. I am not a pro-developer, so my code is pretty ugly. Bear in mind that this has been my toy project to learn Nim.