I am trying to write a OpenFX 1.4 Plugin but I am having awful errors like that my Message of error "THE PARAMETER WAS NOT RETRIVED " being printed to stderr/stdout, and the frame not being printed, which would no happen anyway in this minimal reproducible example but in the project code I have some effect code that Is not being executed because is failing to retrive the parameter which is what indicates the error message that wrote. I have checked it multiple times against the examples in https://openfx.readthedocs.io but not idea of what is the mysterious error.
build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const openfxPlugin = b.addSharedLibrary(.{
.name = "OpenFXPlugin",
.root_source_file = .{ .path = "src/fxroot.zig" },
.target = target,
.optimize = optimize,
.version = .{ .major = 0, .minor = 0, .patch = 0 },
});
openfxPlugin.addIncludePath(.{
.path = "openfx/include",
});
openfxPlugin.addIncludePath(.{
.path = "openfx/Support/include",
});
openfxPlugin.linkLibC();
b.installArtifact(openfxPlugin);
const unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/fxroot.zig" },
.target = target,
.optimize = optimize,
});
unit_tests.addIncludePath(.{
.path = "openfx/include",
});
unit_tests.addIncludePath(.{
.path = "openfx/Support/include",
});
unit_tests.linkLibC();
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
}
src/fxroot.zig:
const std = @import("std");
const c = @cImport({
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("string.h");
});
const ofx = @cImport({
@cInclude("ofxImageEffect.h");
});
const InstanceData = struct {
testParameter: ofx.OfxParamHandle,
};
var gHost: ?*ofx.OfxHost = @ptrFromInt(0);
var gPropertySuite: ?*ofx.OfxPropertySuiteV1 = @ptrFromInt(0);
var gImageEffectSuite: ?*ofx.OfxImageEffectSuiteV1 = @ptrFromInt(0);
var gParameterSuite: ?*ofx.OfxParameterSuiteV1 = @ptrFromInt(0);
////////////////////////////////////////////////////////////////////////////////
// House keeper to make sure we are loaded and unloaded symetrically
var gInLoadedState: bool = false;
var gDescribeCalled: bool = false;
var gDescribeInContextCalled: bool = false;
var gNumInstancesLiving: i16 = 0;
fn error_abort(comptime msg: [*:0]const u8) void {
_ = c.fprintf(c.stderr, "FATAL ERROR: %s", msg);
c.abort();
}
fn LoadAction() ?ofx.OfxStatus {
if (gHost == null) error_abort("The OfxHost pointer has not been set, it should have been set in 'setHostFunc' before any action is called.");
if (gInLoadedState) error_abort("kOfxActionLoad called more than once without an intervening kOfxUnloadAction.");
if (gHost) |gHostInner| {
gPropertySuite = @constCast(@alignCast(@ptrCast(gHostInner.fetchSuite.?(gHostInner.host, ofx.kOfxPropertySuite, 1))));
if (gPropertySuite == null) {
_ = c.fprintf(c.stdout, "FATAL ERROR: Failed to fetch the %s verison 1 from the host.", ofx.kOfxPropertySuite);
c.abort();
}
gImageEffectSuite = @constCast(@alignCast(@ptrCast(gHostInner.fetchSuite.?(gHostInner.host, ofx.kOfxImageEffectSuite, 1))));
gParameterSuite = @constCast(@alignCast(@ptrCast(gHostInner.fetchSuite.?(gHostInner.host, ofx.kOfxParameterSuite, 1))));
gInLoadedState = true;
}
return ofx.kOfxStatOK;
}
////////////////////////////////////////////////////////////////////////////////
// last action called before a plugin binary is unloaded.
fn UnloadAction() ofx.OfxStatus {
// make sure no instances have been left alive
if (gNumInstancesLiving != 0) {
_ = c.fprintf(c.stdout, "kOfxActionUnload called while there are still %d instances of the plugin extant.", gNumInstancesLiving);
}
// check that we had a load called first
if (gInLoadedState) _ = c.fprintf(c.stdout, "kOfxActionUnload callewd without preceding kOfxAcrtionLoad%s", ".");
gInLoadedState = false;
return ofx.kOfxStatOK;
}
////////////////////////////////////////////////////////////////////////////////
// the plugin's basic description routine
fn DescribeAction(descriptor: ofx.OfxImageEffectHandle) ofx.OfxStatus {
// check stuff
if (!gInLoadedState) error_abort("kOfxActionLoad has not been called");
gDescribeCalled = true;
// get the property set handle for the plugin
var effectProps: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getPropertySet.?(descriptor, &effectProps);
// set some labels and the group it belongs to
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxPropLabel, 0, "Test");
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxImageEffectPluginPropGrouping, 0, "TV Tools");
// define the image effects contexts we can be used in, in this case a simple filter
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxImageEffectPropSupportedContexts, 0, ofx.kOfxImageEffectContextFilter);
// Set supported pixel depths
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxImageEffectPropSupportedPixelDepths, 0, ofx.kOfxBitDepthFloat);
// say that a single instance of this plugin can be rendered in multiple threads
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxImageEffectPluginRenderThreadSafety, 0, ofx.kOfxImageEffectRenderFullySafe);
// say that the host should manage SMP threading over a single frame
_ = gPropertySuite.?.propSetString.?(effectProps, ofx.kOfxImageEffectPluginPropHostFrameThreading, 0, 1);
return ofx.kOfxStatOK;
}
fn RenderAction(effect: ofx.OfxImageEffectHandle, inArgs: ofx.OfxPropertySetHandle, _: ofx.OfxPropertySetHandle) ofx.OfxStatus {
var time: ofx.OfxTime = 0;
_ = gPropertySuite.?.propGetDouble.?(inArgs, ofx.kOfxPropTime, 0, &time);
const instanceData: *InstanceData = blk: {
var effectProps: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getPropertySet.?(effect, &effectProps);
if (FetchInstanceData(effectProps)) |innerselben| {
break :blk innerselben;
} else {
return ofx.kOfxStatErrBadHandle;
}
};
_ = blk: {
var testParam: ?f64 = null;
_ = gParameterSuite.?.paramGetValueAtTime.?(instanceData.testParameter, time, &testParam);
if (testParam) |rts| {
break :blk rts;
}
std.debug.print("THE PARAMETER WAS NOT RETRIVED \n", .{});
return ofx.kOfxStatErrBadHandle;
};
return ofx.kOfxStatFailed;
}
fn CreateInstanceAction(instance: ofx.OfxImageEffectHandle) ofx.OfxStatus {
if (!gDescribeInContextCalled) error_abort("CreateInstanceAction called before DescribeInContext.");
gNumInstancesLiving += 1;
var effectProps: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getPropertySet.?(instance, &effectProps);
const instanceData: *InstanceData = blk: {
if (@as(?*InstanceData, @alignCast(@ptrCast(c.malloc(@sizeOf(InstanceData)))))) |v| {
break :blk v;
} else {
return ofx.kOfxStatErrMemory;
}
};
_ = gPropertySuite.?.propSetPointer.?(effectProps, ofx.kOfxPropInstanceData, 0, instanceData);
var paramSet: ofx.OfxParamSetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getParamSet.?(instance, ¶mSet);
_ = gParameterSuite.?.paramGetHandle.?(paramSet, "testParameter", &instanceData.testParameter, 0);
return ofx.kOfxStatOK;
}
fn FetchInstanceData(effectProps: ofx.OfxPropertySetHandle) ?*InstanceData {
var instanceData: ?*InstanceData = @ptrFromInt(0);
_ = gPropertySuite.?.propGetPointer.?(effectProps, ofx.kOfxPropInstanceData, 0, @ptrCast(&instanceData));
return instanceData;
}
fn DestroyInstanceAction(instance: ofx.OfxImageEffectHandle) ofx.OfxStatus {
gNumInstancesLiving -= 1;
var effectProps: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getPropertySet.?(instance, &effectProps);
if (FetchInstanceData(effectProps)) |innerselben| {
c.free(innerselben);
} else {
_ = c.fprintf(c.stdout, "Instance should not be null!", "");
}
return ofx.kOfxStatOK;
}
fn DescribeInContextAction(descriptor: ofx.OfxImageEffectHandle, inArgs: ofx.OfxPropertySetHandle) ofx.OfxStatus {
gDescribeInContextCalled = true;
var context: [*c]u8 = @ptrFromInt(0);
_ = gPropertySuite.?.propGetString.?(inArgs, ofx.kOfxImageEffectPropContext, 0, &context);
if (c.strcmp(context, ofx.kOfxImageEffectContextFilter) != 0) {
_ = c.fprintf(c.stdout, "FATAL ERROR: DescribeInContextAction called on unsupported contex %s", context);
c.abort();
}
var props: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.clipDefine.?(descriptor, "Output", &props);
_ = gPropertySuite.?.propSetString.?(props, ofx.kOfxImageEffectPropSupportedComponents, 0, ofx.kOfxImageComponentRGB);
_ = gImageEffectSuite.?.clipDefine.?(descriptor, "Source", &props);
_ = gPropertySuite.?.propSetString.?(props, ofx.kOfxImageEffectPropSupportedComponents, 0, ofx.kOfxImageComponentRGB);
var paramSet: ofx.OfxParamSetHandle = @ptrFromInt(0);
_ = gImageEffectSuite.?.getParamSet.?(descriptor, ¶mSet);
var paramProps: ofx.OfxPropertySetHandle = @ptrFromInt(0);
_ = gParameterSuite.?.paramDefine.?(paramSet, ofx.kOfxParamTypeDouble, "testParameter", ¶mProps);
_ = gPropertySuite.?.propSetString.?(paramProps, ofx.kOfxParamPropDoubleType, 0, ofx.kOfxParamDoubleTypeScale);
return ofx.kOfxStatOK;
}
fn SetHostFunc(hostStruct: [*c]ofx.OfxHost) callconv(.C) void {
gHost = hostStruct;
}
fn MainEntryPoint(
action: [*c]const u8,
handle: ?*const anyopaque,
inArgs: ofx.OfxPropertySetHandle,
outArgs: ofx.OfxPropertySetHandle,
) callconv(.C) ofx.OfxStatus {
_ = c.fprintf(c.stdout, ": START action is : %s \n", action);
const effect: ofx.OfxImageEffectHandle = @ptrCast(@constCast(handle));
var returnStatus: ofx.OfxStatus = ofx.kOfxStatReplyDefault;
std.debug.print("Entering Action Called: {s}\n", .{action});
if (c.strcmp(action, ofx.kOfxActionLoad) == 0) {
returnStatus = LoadAction().?;
} else if (c.strcmp(action, ofx.kOfxActionUnload) == 0) {
returnStatus = UnloadAction();
} else if (c.strcmp(action, ofx.kOfxActionDescribe) == 0) {
returnStatus = DescribeAction(effect);
} else if (c.strcmp(action, ofx.kOfxImageEffectActionDescribeInContext) == 0) {
returnStatus = DescribeInContextAction(effect, inArgs);
} else if (c.strcmp(action, ofx.kOfxImageEffectActionRender) == 0) {
returnStatus = RenderAction(effect, inArgs, outArgs);
} else if (c.strcmp(action, ofx.kOfxActionCreateInstance) == 0) {
returnStatus = CreateInstanceAction(effect);
} else if (c.strcmp(action, ofx.kOfxActionDestroyInstance) == 0) {
returnStatus = DestroyInstanceAction(effect);
}
_ = c.fprintf(c.stdout, ": END action is : %s \n", action);
return returnStatus;
}
var effectPluginStruct: ofx.OfxPlugin = .{
.pluginApi = ofx.kOfxImageEffectPluginApi,
.apiVersion = 1,
.pluginIdentifier = "minimal.reproducible.example",
.pluginVersionMajor = 1,
.pluginVersionMinor = 0,
.setHost = SetHostFunc,
.mainEntry = MainEntryPoint,
};
export fn OfxGetNumberOfPlugins() i16 {
return 1;
}
export fn OfxGetPlugin(nth: i16) ?*ofx.OfxPlugin {
_ = switch (nth) {
0 => return &effectPluginStruct,
else => null,
};
_ = c.fprintf(c.stdout, "Host tried to get more plugins from binary than were available, called with index %d", nth);
return @ptrFromInt(0);
}
openfx: https://github.com/AcademySoftwareFoundation/openfx.git git switch tags/OFX_Release_1_4_TAG