Skip to content

Commit

Permalink
add example for zigapi
Browse files Browse the repository at this point in the history
  • Loading branch information
dnjulek committed Apr 13, 2024
1 parent 2f6a59a commit e302328
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 52 deletions.
21 changes: 0 additions & 21 deletions example/README.md

This file was deleted.

4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Examples

[example](/example/) shows how to use the C binding only.\
[example-zigapi](/example-zigapi/) shows how to use the C binding with extra Zig struct/methods.
21 changes: 21 additions & 0 deletions examples/example-zigapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# invert_example

To use this module in your project you will need:
1. A [build.zig.zon](/examples/example-zigapi/build.zig.zon) file like this.
2. These lines in your [build.zig](/examples/example-zigapi/build.zig):

```zig
const vapoursynth_dep = b.dependency("vapoursynth", .{
.target = target,
.optimize = optimize,
});
lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
```

The [invert_example.zig](/examples/example-zigapi/src/invert_example.zig) is based on [invert_example.c](https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c), from the VapourSynth SDK, I recommend checking it out first if you don't know the framework.

## Building
Zig version should be the master, 0.11.0 not supported.

``zig build -Doptimize=ReleaseFast``
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
.paths = .{""},
.dependencies = .{
.vapoursynth = .{
.url = "https://github.com/dnjulek/vapoursynth-zig/archive/6b5dfc82c74fc81de502b3c01b6fe9893f8359e6.tar.gz",
.hash = "12205f58b4ccddbbccea25cae0e801030e3959885e980454a607e0015707758f1398",
.url = "https://github.com/dnjulek/vapoursynth-zig/archive/2f6a59a6cccf23cec7ca8a3abb3f316593c5ed7b.tar.gz",
.hash = "12201cb4befda1224caa03a57a2188d5d5e985a7fdd7a8b3277d385dba4cb0e272cb",
},
},
}
107 changes: 107 additions & 0 deletions examples/example-zigapi/src/invert_example.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//! https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c

const std = @import("std");
const vapoursynth = @import("vapoursynth");

const math = std.math;
const vs = vapoursynth.vapoursynth4;
const vsh = vapoursynth.vshelper;
const zapi = vapoursynth.zigapi;

// https://ziglang.org/documentation/master/#Choosing-an-Allocator
const allocator = std.heap.c_allocator;

const InvertData = struct {
node: ?*vs.Node,
vi: *const vs.VideoInfo,
enabled: bool,
};

export fn invertGetFrame(n: c_int, activation_reason: vs.ActivationReason, instance_data: ?*anyopaque, frame_data: ?*?*anyopaque, frame_ctx: ?*vs.FrameContext, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) ?*const vs.Frame {
_ = frame_data;
const d: *InvertData = @ptrCast(@alignCast(instance_data));

if (activation_reason == .Initial) {
vsapi.?.requestFrameFilter.?(n, d.node, frame_ctx);
} else if (activation_reason == .AllFramesReady) {
var src = zapi.Frame.init(d.node, n, frame_ctx, core, vsapi);
defer src.deinit();
var dst = src.newVideoFrame();

var plane: u32 = 0;
while (plane < d.vi.format.numPlanes) : (plane += 1) {
var srcp = src.getReadPtr(plane);
var dstp = dst.getWritePtr(plane);

// getDimensions returns a tuple with [width, height, stride],
// use getDimensions2 if you want a struct.
const w, const h, const stride = src.getDimensions(plane);

var y: u32 = 0;
while (y < h) : (y += 1) {
var x: u32 = 0;
while (x < w) : (x += 1) {
dstp[x] = if (d.enabled) ~(srcp[x]) else srcp[x];
}

dstp = dstp[stride..];
srcp = srcp[stride..];
}
}

return dst.frame;
}

return null;
}

export fn invertFree(instance_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
_ = core;
const d: *InvertData = @ptrCast(@alignCast(instance_data));
vsapi.?.freeNode.?(d.node);
allocator.destroy(d);
}

export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
_ = user_data;
var d: InvertData = undefined;
var map = zapi.Map.init(in, out, vsapi);

// getNodeVi returns a tuple with [vs.Node, vs.VideoInfo],
// use getNodeVi2 if you want a struct.
d.node, d.vi = map.getNodeVi("clip");

if (!vsh.isConstantVideoFormat(d.vi) or (d.vi.format.sampleType != .Integer) or (d.vi.format.bitsPerSample != 8)) {
map.setError("Invert: only constant format 8bit integer input supported");
vsapi.?.freeNode.?(d.node);
return;
}

// https://ziglang.org/documentation/master/#Optionals
const enabled = map.getInt(i32, "enabled") orelse 1;

if ((enabled < 0) or (enabled > 1)) {
map.setError("Invert: enabled must be 0 or 1");
vsapi.?.freeNode.?(d.node);
return;
}

d.enabled = enabled == 1;

const data: *InvertData = allocator.create(InvertData) catch unreachable;
data.* = d;

var deps = [_]vs.FilterDependency{
vs.FilterDependency{
.source = d.node,
.requestPattern = .StrictSpatial,
},
};

vsapi.?.createVideoFilter.?(out, "Invert", d.vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
}

export fn VapourSynthPluginInit2(plugin: *vs.Plugin, vspapi: *const vs.PLUGINAPI) void {
_ = vspapi.configPlugin.?("com.example.zinvert", "zinvert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
_ = vspapi.registerFunction.?("Filter", "clip:vnode;enabled:int:opt;", "clip:vnode;", invertCreate, null, plugin);
}
21 changes: 21 additions & 0 deletions examples/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# invert_example

To use this module in your project you will need:
1. A [build.zig.zon](/examples/example/build.zig.zon) file like this.
2. These lines in your [build.zig](/examples/example/build.zig):

```zig
const vapoursynth_dep = b.dependency("vapoursynth", .{
.target = target,
.optimize = optimize,
});
lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
```

The [invert_example.zig](/examples/example/src/invert_example.zig) is based on [invert_example.c](https://github.com/vapoursynth/vapoursynth/blob/master/sdk/invert_example.c), from the VapourSynth SDK, I recommend checking it out first if you don't know the framework.

## Building
Zig version should be the master.

``zig build -Doptimize=ReleaseFast``
53 changes: 53 additions & 0 deletions examples/example/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const std = @import("std");

pub const min_zig_version = std.SemanticVersion{ .major = 0, .minor = 12, .patch = 0, .pre = "dev.2158" };

pub fn build(b: *std.Build) void {
ensureZigVersion() catch return;
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const lib = b.addSharedLibrary(.{
.name = "invert_example",
.root_source_file = .{ .path = "src/invert_example.zig" },
.target = target,
.optimize = optimize,
});

const vapoursynth_dep = b.dependency("vapoursynth", .{
.target = target,
.optimize = optimize,
});

lib.root_module.addImport("vapoursynth", vapoursynth_dep.module("vapoursynth"));
lib.linkLibC();

if (lib.root_module.optimize == .ReleaseFast) {
lib.root_module.strip = true;
}

b.installArtifact(lib);
}

fn ensureZigVersion() !void {
var installed_ver = @import("builtin").zig_version;
installed_ver.build = null;

if (installed_ver.order(min_zig_version) == .lt) {
std.log.err("\n" ++
\\---------------------------------------------------------------------------
\\
\\Installed Zig compiler version is too old.
\\
\\Min. required version: {any}
\\Installed version: {any}
\\
\\Please install newer version and try again.
\\Latest version can be found here: https://ziglang.org/download/
\\
\\---------------------------------------------------------------------------
\\
, .{ min_zig_version, installed_ver });
return error.ZigIsTooOld;
}
}
11 changes: 11 additions & 0 deletions examples/example/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.{
.name = "invert_example",
.version = "1.0.0",
.paths = .{""},
.dependencies = .{
.vapoursynth = .{
.url = "https://github.com/dnjulek/vapoursynth-zig/archive/2f6a59a6cccf23cec7ca8a3abb3f316593c5ed7b.tar.gz",
.hash = "12201cb4befda1224caa03a57a2188d5d5e985a7fdd7a8b3277d385dba4cb0e272cb",
},
},
}
File renamed without changes.
56 changes: 27 additions & 29 deletions test/src/invert_example.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const vapoursynth = @import("vapoursynth");
const math = std.math;
const vs = vapoursynth.vapoursynth4;
const vsh = vapoursynth.vshelper;
const zapi = vapoursynth.zigapi;

// https://ziglang.org/documentation/master/#Choosing-an-Allocator
const allocator = std.heap.c_allocator;

const InvertData = struct {
node: ?*vs.Node,
vi: *const vs.VideoInfo,
enabled: bool,
};

Expand All @@ -22,38 +24,32 @@ export fn invertGetFrame(n: c_int, activation_reason: vs.ActivationReason, insta
if (activation_reason == .Initial) {
vsapi.?.requestFrameFilter.?(n, d.node, frame_ctx);
} else if (activation_reason == .AllFramesReady) {
const src = vsapi.?.getFrameFilter.?(n, d.node, frame_ctx);
var src = zapi.Frame.init(d.node, n, frame_ctx, core, vsapi);
defer src.deinit();
var dst = src.newVideoFrame();

// https://ziglang.org/documentation/master/#defer
defer vsapi.?.freeFrame.?(src);
var plane: u32 = 0;
while (plane < d.vi.format.numPlanes) : (plane += 1) {
var srcp = src.getReadPtr(plane);
var dstp = dst.getWritePtr(plane);

const fi = vsapi.?.getVideoFrameFormat.?(src);
// getDimensions returns a tuple with [width, height, stride],
// use getDimensions2 if you want a struct.
const w, const h, const stride = src.getDimensions(plane);

const height = vsapi.?.getFrameHeight.?(src, 0);
const width = vsapi.?.getFrameWidth.?(src, 0);
const dst = vsapi.?.newVideoFrame.?(fi, width, height, src, core);

var plane: c_int = 0;
while (plane < fi.numPlanes) : (plane += 1) {
var srcp: [*]const u8 = vsapi.?.getReadPtr.?(src, plane);
var dstp: [*]u8 = vsapi.?.getWritePtr.?(dst, plane);
const stride: usize = @intCast(vsapi.?.getStride.?(src, plane));
const h: usize = @intCast(vsapi.?.getFrameHeight.?(src, plane));
const w: usize = @intCast(vsapi.?.getFrameWidth.?(src, plane));

var y: usize = 0;
var y: u32 = 0;
while (y < h) : (y += 1) {
var x: usize = 0;
var x: u32 = 0;
while (x < w) : (x += 1) {
dstp[x] = if (d.enabled) ~(srcp[x]) else srcp[x];
}

dstp += stride;
srcp += stride;
dstp = dstp[stride..];
srcp = srcp[stride..];
}
}

return dst;
return dst.frame;
}

return null;
Expand All @@ -69,21 +65,23 @@ export fn invertFree(instance_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const
export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque, core: ?*vs.Core, vsapi: ?*const vs.API) callconv(.C) void {
_ = user_data;
var d: InvertData = undefined;
var map = zapi.Map.init(in, out, vsapi);

d.node = vsapi.?.mapGetNode.?(in, "clip", 0, null).?;
const vi: *const vs.VideoInfo = vsapi.?.getVideoInfo.?(d.node);
// getNodeVi returns a tuple with [vs.Node, vs.VideoInfo],
// use getNodeVi2 if you want a struct.
d.node, d.vi = map.getNodeVi("clip");

if (!vsh.isConstantVideoFormat(vi) or (vi.format.sampleType != .Integer) or (vi.format.bitsPerSample != @as(c_int, 8))) {
vsapi.?.mapSetError.?(out, "Invert: only constant format 8bit integer input supported");
if (!vsh.isConstantVideoFormat(d.vi) or (d.vi.format.sampleType != .Integer) or (d.vi.format.bitsPerSample != 8)) {
map.setError("Invert: only constant format 8bit integer input supported");
vsapi.?.freeNode.?(d.node);
return;
}

// https://ziglang.org/documentation/master/#Optionals
const enabled = vsh.mapGetN(i32, in, "enabled", 0, vsapi) orelse 1;
const enabled = map.getInt(i32, "enabled") orelse 1;

if ((enabled < 0) or (enabled > 1)) {
vsapi.?.mapSetError.?(out, "Invert: enabled must be 0 or 1");
map.setError("Invert: enabled must be 0 or 1");
vsapi.?.freeNode.?(d.node);
return;
}
Expand All @@ -100,10 +98,10 @@ export fn invertCreate(in: ?*const vs.Map, out: ?*vs.Map, user_data: ?*anyopaque
},
};

vsapi.?.createVideoFilter.?(out, "Invert", vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
vsapi.?.createVideoFilter.?(out, "Invert", d.vi, invertGetFrame, invertFree, .Parallel, &deps, deps.len, data, core);
}

export fn VapourSynthPluginInit2(plugin: *vs.Plugin, vspapi: *const vs.PLUGINAPI) void {
_ = vspapi.configPlugin.?("com.example.invert", "invert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
_ = vspapi.configPlugin.?("com.example.zinvert", "zinvert", "VapourSynth Invert Example", vs.makeVersion(1, 0), vs.VAPOURSYNTH_API_VERSION, 0, plugin);
_ = vspapi.registerFunction.?("Filter", "clip:vnode;enabled:int:opt;", "clip:vnode;", invertCreate, null, plugin);
}

0 comments on commit e302328

Please sign in to comment.