The Generic Core API

This chapter describes how plug-ins are distributed and the core API for loading and identifying image effect plug-ins, and the methods of communications between plug-in and host.

OFX Include Files

The C include files that define an OFX API are all that are needed by a plug-in or host to implement the API. Most include files define a set of independent suites which are used by a plug-in to communicate with a host application.

There are two include files that are used with nearly every derived API. These are…

  • ofxCore.h is used to define the basic communication mechanisms between a host and a plug-in. This includes the way in which a plug-in is defined to a host and how to bootstrap the two way communications. It also has several other basic action and property definitions.

  • ofxProperty.h specifies the property suite, which is how a plug-in gets and sets values on various objects in a host application.

Identifying and Loading Plug-ins

Plug-ins must implement at least two, and normally three, exported functions for a host to identify the plug-ins and to initiate the bootstrapping of communication between the two.

OfxStatus OfxSetHost(const OfxHost *host)

First thing host should call.

This host call, added in 2020, is not specified in earlier implementation of the API. Therefore host must check if the plugin implemented it and not assume symbol exists. The order of calls is then: 1) OfxSetHost, 2) OfxGetNumberOfPlugins, 3) OfxGetPlugin The host pointer is only assumed valid until OfxGetPlugin where it might get reset. Plug-in can return kOfxStatFailed to indicate it has nothing to do here, it’s not for this Host and it should be skipped silently.

int OfxGetNumberOfPlugins(void)

Defines the number of plug-ins implemented inside a binary.

A host calls this to determine how many plug-ins there are inside a binary it has loaded. A function of this type must be implemented in and exported from each plug-in binary.

OfxPlugin *OfxGetPlugin(int nth)

Returns the ‘nth’ plug-in implemented inside a binary.

Returns a pointer to the ‘nth’ plug-in implemented in the binary. A function of this type must be implemented in and exported from each plug-in binary.

OfxSetHost is the very first function called by the host after the binary has been loaded, if it is implemented by the plugin. It passes an The OfxHost Struct struct to the plugin to enable the plugin to decide which effects to expose to the host. COMPAT: this call was introduced in 2020; some hosts and/or plugins may not implement it.

OfxGetNumberOfPlugins is the next function called by the host after the binary has been loaded and OFXSetHost has been called. The returned pointer to OfxGetPlugin and pointers in the struct do not need to be freed in any way by the host.

The Plug-in Main Entry Point And Actions

Actions are how a host communicates with a plug-in. They are in effect generic function calls. Actions are issued via a plug-in’s mainEntry function pointer found in its OfxPlugin struct. The function signature for the main entry point is

OfxStatus() OfxPluginEntryPoint (const char *action, const void *handle, OfxPropertySetHandle inArgs, OfxPropertySetHandle outArgs)

Entry point for plug-ins.

  • action ASCII c string indicating which action to take

  • instance object to which action should be applied, this will need to be cast to the appropriate blind data type depending on the action

  • inData handle that contains action specific properties

  • outData handle where the plug-in should set various action specific properties

This is how the host generally communicates with a plug-in. Entry points are used to pass messages to various objects used within OFX. The main use is within the OfxPlugin struct.

The exact set of actions is determined by the plug-in API that is being implemented, however all plug-ins can perform several actions. For the list of actions consult OFX Actions.

The OfxStatus value returned is dependent upon the action being called; however the value kOfxStatReplyDefault is returned if the plug-in does not trap the action.

The exact set of actions passed to a plug-in’s entry point are dependent upon the API the plug-in implements. However, there exists a core set of generic actions that most APIs would use.

Suites

Suites are how a plug-in communicates back to the host. A suite is simply a set of function pointers in a C struct. The set of suites a host needs to implement is defined by the API being implemented. A suite is fetched from a host via the OfxHost::fetchSuite() function. This returns a pointer (cast to void *) to the named and versioned set of functions. By using this suite fetching mechanism, there is no symbolic dependency from the plug-in to the host, and APIs can be easily expandable without causing backwards compatibility issues.

If the host does not implement a requested suite, or the requested version of that suite, then it should return NULL.

Sequences of Operations Required to Load a Plug-in

The following sequence of operations needs to be performed by a host before it can start telling a plug-in what to do via its mainEntry function.

  1. the binary containing the plug-in is loaded,

  2. (if implemented by plugin and host): the host calls the plug-in’s OfxSetHost function

  3. the number of plug-ins is determined via the OfxGetNumberOfPlugins function,

  4. for each plug-in defined in the binary

    1. OfxGetPlugin is called,

    2. the pluginApi and apiVersion of the returned OfxPlugin struct are examined,

    3. if the plug-in’s API or its version are not supported, the plug-in is ignored and we skip to the next one,

    4. the plug-in’s pointer is recorded in a plug-in cache,

    5. an appropriate OfxHost struct is passed to the plug-in via setHost in the returned OfxPlugin struct.

Who Owns The Data?

Objects are passed back and forth across the API, and in general, it is the thing that passes the data that is responsible for destroying it. For example the property set handle in the OfxHost struct is managed by the host.

There are a few explicit exceptions to this. For example, when an image effect asks for an image from a host it is passed back a property set handle which represents the image. That handle needs to later be disposed of by an effect, by an explicit function call back to the host. These few exceptions are documented with the suite functions that access the object.

Strings

A special case is made for strings. Strings are considered to be of two types, value strings and label strings. A label string is any string used by OFX to name a property or type. A value string is generally a string value of a property.

More specifically, a label string is a string passed across the API as one of the following…

  • a property label (i.e: the char* property argument in the property suites)

  • a string argument to a suite function which must be one of a set of predefined set of values e.g: paramType argument to OfxParameterSuiteV1::paramDefine , but not the name argument)

Label strings are considered to be static constant strings. When passed across the API the host/plug-in receiving the string neither needs to duplicate nor free the string, it can simply retain the original pointer passed over and use that in future, as it will not change. A host must be aware that when it unloads a plug-in all such pointers will be invalid, and be prepared to cope with such a situation.

A value string is a string passed across the API as one of the following…

  • all value arguments to any of the property suite calls

  • any other char* argument to any other function.

Value strings have no assumptions made about them. When one is passed across the API, the thing that passed the string retains ownership of it. The thing getting the string is not responsible for freeing that string. The scope of the string’s validity is until the next OFX API function is called. For example, within a plugin

// pointer to store the returned value of the host name
char *returnedHostName;

// get the host name
propSuite->propGetString(hostHandle, kOfxPropName, 0, &returnedHostName);

// now make a copy of that before the next API call, as it may not be valid after it
char *hostName = strdup(returnedHostName);

paramSuite->getParamValue(instance, "myParam", &value);