Skip to content

How Plugins Interact With OBS Studio

Patrick Heyer edited this page Nov 1, 2024 · 1 revision

Contents

  1. OBS Studio Runtime Modules
  2. Plugin File Structure
  3. The Module API
  4. Capabilities Of The Module API
  5. ABI and API Compatibility Concerns

OBS Studio Runtime Modules

The basis on which all plugins rely on was actually a "happy accident": OBS Studio uses a modular approach to encapsulate different parts of its core functionality and to compose platform specific variants of all this functionality.

OBS Studio is made up of a core library (called libobs), companion libraries implementing a graphics API (e.g. Direct3D 11 or OpenGL), a varying number of runtime modules (e.g. obs-filters, obs-x264, etc.) and the main application and user interface (written in Qt).

Instead of composing a libobs variant for each platform using these modules, OBS Studio loads them at runtime. This requires setting up search paths to look for module files, iterating over all files found in those paths, loading them, checking if they have the binary symbols necessary for an OBS Studio runtime module, and finally initialize the module.

The "happy accident" is that OBS Studio makes no distinction between its own runtime modules and those created by 3rd parties - if it looks like a duck (it has a .dll extension on Windows), sounds like a duck (it has exported symbols such as obs_module_load), and walks like a duck (it successfully finished registered itself as either a source, filter, output, or another supported "source" type), it's probably an OBS Studio runtime module.

Plugin File Structure

The file structure of plugins differs between platforms. Windows and Linux place the plugin library, its localization files, and other support files in different locations. macOS uses plugin bundles, thus its format is self-contained but also has internal rules to follow.

Windows

The overall file structure for plugins on Windows looks as follows:

├ my-plugin
│  ├─ bin
│  │   ╰─ 64bit
│  │       ├─ my-plugin-for-obs.dll
│  │       ╰─ my-plugin-for-obs.pdb
│  ╰ data
│     ├─ locale
│     │   └─ en-US.ini
│     ╰─ my-plugin-example-file.png

Be mindful of the 64bit directory inside bin that needs to be used for historical reasons. Also ensure that the name of the sub-directory used in the data directory matches the name of your .dll file.

Caution

This plugin structure requires the plugin to be installed in %PROGRAMDATA%\obs-studio\plugins as the internal structure is different to the legacy structure used for plugins installed to the program directory.

This is different to the structure used by plugins in the past. Modern plugins built with this template cannot be copied into the OBS Studio program directory. They will NOT work.

Linux

The overall file structure for plugins on Linux follows the practices commonly used for system packages:

<Installation Prefix>
  ├ shared
  │  ╰ obs
  │     ╰─ obs-plugins
  │         ╰─ my-plugin-for-obs
  │             ├─ locale
  │             │   └─ en-US.ini
  │             ╰─ my-plugin-example-file.png
  ├ lib
  │  ╰ obs-plugins
  │     ╰─ 64bit
  │         ├─ my-plugin-for-obs.so.<SOVERSION>
  │         ├─ my-plugin-for-obs.so.<FULL-VERSION>
  │         ╰─ my-plugin-for-obs.so

The specifics of this structure (including the installation prefix) can change depending on the Linux distribution a plugin is provided for, but the general idea is that the shared library of a plugin is put into an obs-plugins sub-directory of the user library system directory, and the localization and support files are placed into the corresponding obs-plugins directory inside the shared system directory.

macOS

macOS uses plugin bundles, which allows all plugin files to be contained in a single directory (which is then interpreted as a package by macOS):

my-plugin.plugin
  ├ Contents
  │  ╰ MacOS
  │     ╰─ my-plugin
  ├ Resources
  │  ├ my-plugin-example-file.png
  │  ╰ locale
  │     ╰─ en-US.ini
  ╰ Info.plist

The name of the plugin bundle needs to match the name of the binary (which is found inside the MacOS directory of the Contents directory).

Note

While macOS does not use a case-sensitive file system by default, it is good practice to use the capitalized names for the default entries Contents, MacOS, Resources, and Info.plist.

The bundle format used by OBS Studio adheres to Apple's rules for Loadable Bundles, which plugin developers should follow as well.

The Module API

There is no bespoke "plugin API" in OBS Studio: 3rd party plugins make use of the same "internal" module API used by OBS Studio's first party modules (as defined in the obs-module.h header).

While the header provides some preprocessor macros for convenience, the most basic code for a module is always the same:

#include <obs-module.h>

OBS_DECLARE_MODULE()

extern struct obs_source_info my_source;

bool obs_module_load(void)
{
    obs_register_source(&my_source);
    return true;
}

OBS_DECLARE_MODULE is a preprocessor macro (the preprocessor is explained in more detail in the C++ reference documentation for the C language) which will automatically add boilerplate code which is used by libobs to interact with your plugin and contains the bare minimum for your final binary to be recognized as a OBS Studio runtime module.

extern struct obs_source_info my_source; declares that some other piece of source code will have implemented an obs_source_info data structure with the name my_source (for more on this, read up on translation units in C and C++ programming).

obs_source_info itself is a big structure type defined in an "internal" OBS Studio header (obs-source.h) and not part of the module API. It is a "one size fits all" structure that tries to accommodate any and all current source types that OBS Studio supports, but luckily a module does neither need to concern itself with all of its member variables, nor does it have to implement them.

Important

While the word "source" has a very specific meaning in the user interface of OBS Studio, it is the name for the most basic data type in the application binary interface (ABI) of it - everything is a "source".

obs_module_load is a function that will be automatically called by libobs (if it has been implemented). This is the entry point for a plugin's setup code and where a plugin would tell OBS Studio about the things it's capable of (i.e. the "sources" it provides).

In the given example it calls obs_register_source with the address(!) of that my_source struct that was implemented in another translation unit.

A very simple example for such a structure looks like this:

    struct obs_source_info av_capture_info = {
        .id = "my-plugin",
        .type = OBS_SOURCE_TYPE_INPUT,
        .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE,
        .create = my_create_function,
        .update = my_update_function,
        .destroy = my_destroy_function,
        .icon_type = OBS_ICON_TYPE_CAMERA,
    };

In this example the new plugin has the ID (not name) my-plugin, it declares that it provides an input source, that the input will use asynchronous video (common for webcam-based sources), will also provide audio, and should not be duplicated.

It also provides function references to create, update, and destroy, the plugin. OBS Studio will retain those at runtime and call the functions when necessary. Finally the icon type is defined, which will be used in the OBS Studio user interface in appropriate places (mainly the menu to add a new source type to a scene).

The full details of each value or function call are available in the module API reference.

Capabilities Of The Module API

The full capabilities of the API are available in the official documentation for plugins. Because the API was primarily written for first-party modules, it can offer a vast amount of functionality to third party plugins as well, but comes with high amount of risk as there is no encapsulation of plugins from the main OBS Studio process.

Caution

Your plugin code will run unrestricted as a part of OBS Studio. From an end-user point of view there is no distinction between your code and OBS Studio's own code. Thus, if an unrecoverable error occurs, crashes can happen and your plugin might be the main culprit for it.

Not all functionality of libobs or other parts of OBS Studio are made available to first party modules however, which means that those elements are not available to third party plugins either. Starting with OBS Studio 30.0.0 the headers generated (and provided) by libobs contain only functionality that is made publicly available by the library. Using any other "hidden" header to gain access to internal functionality might work (for some time) but should be considered a bug.

Caution

Relying on private APIs is dangerous as there is zero accommodation for their use. Private APIs can and will change or might even be removed entirely at any point in time. As they are not part of the module API, consider them to not even exist in the first place.

This includes developing a plugin within the OBS Studio source tree, which would allow a developer to pull in and make use of a header somewhere within the OBS Studio source code. These headers are not for modules to use and might disappear at any time.

ABI and API Compatibility Concerns

Note

The following applies to Windows and macOS only for the most part - compatibility of plugins and the OBS Studio version used as a host depends on distributions and their packaging, so no general advice can be given here.

Because plugins use the same internal API that OBS Studio uses for its first-party runtime modules, compatibility concerns can arise when changes need to be made to these internal APIs due to bug fixing, feature development, or other developments.

In general OBS Studio tries to keep its module API and ABI stable (or at the very least changes its ABI in such a way that it doesn't negatively impact existing plugins). Still, a set of good practices should be followed:

Important

  • Plugins developed using the most recent version of libobs might not work with older versions of OBS Studio if they make full use of updated or new APIs
  • Plugins developed using an older version of libobs can reasonably expect their plugins continue to work. If there is a risk of this not being the case, OBS Studio will announce such a breaking change early during the beta cycle of a new version.
  • Users are encouraged to always update to the most recent version of OBS Studio available within a reasonable time after release - plugin authors are encouraged to update their plugins in the same fashion to ensure the best possible experience for their users.