首页
/ OBS Studio 模块(Module)API 参考:libobs 插件的声明、导出与加载全解

OBS Studio 模块(Module)API 参考:libobs 插件的声明、导出与加载全解

2026-09-06 10:37:27作者:宣海椒Queenly

OBS Studio 的可扩展性建立在 libobs 的模块(Module)机制之上:模块本质上是一个共享库(.so/.dylib/.dll),向 libobs 注入源(source)、编码器(encoder)、输出(output)和推流服务(service)等自定义功能。本文基于仓库官方 API 参考文档 reference-modules.rst 完整展开:先讲模块对象的定义与声明宏,再逐一解析插件必须导出/可选导出的函数,最后覆盖前端(frontend)用于发现、打开和批量加载模块的整套 API,并结合 libobs/obs-module.hlibobs/obs-module.c 的源码实现印证其底层行为。读完本文,你将掌握编写、本地化、注册一个 libobs 插件模块的全部 API 契约,以及前端如何安全地驱动插件生命周期。

1. 模块对象:obs_module_t

文档首先定义了核心类型:

#include <obs-module.h>

obs_module_t 是一个模块对象(不采用引用计数)。在 libobs 内部它由模块管理器统一持有,插件作者通常不需要手动创建或释放它——你通过 obs_current_module() 获取当前模块指针即可。

模块的定位在 reference-core.rst 所在 API 参考目录中与核心对象、图形、媒体 I/O 等参考并列,是整个插件体系(源、编码器、输出、服务)的宿主容器。

2. 模块宏(Module Macros)

2.1 OBS_DECLARE_MODULE():必需的模块声明

OBS_DECLARE_MODULE()每个 libobs 插件都必需的宏,用于声明一个 libobs 模块,并导出模块自身、OBS 版本等核心函数。

libobs/obs-module.h 中该宏的真实展开内容,它实际生成了三样东西:

#define OBS_DECLARE_MODULE()                                             \
    static obs_module_t *obs_module_pointer;                             \
    MODULE_EXPORT void obs_module_set_pointer(obs_module_t *module);    \
    void obs_module_set_pointer(obs_module_t *module)                    \
    { obs_module_pointer = module; }                                     \
    obs_module_t *obs_current_module(void)                               \
    { return obs_module_pointer; }                                       \
    MODULE_EXPORT uint32_t obs_module_ver(void);                         \
    uint32_t obs_module_ver(void)                                        \
    { return LIBOBS_API_VER; }

要点:

  • obs_module_set_pointer 被 libobs 在打开模块时调用,把模块指针注入插件;
  • obs_current_module() 因此成为插件内"当前模块"的获取途径(即 2.3 节外函数);
  • obs_module_ver() 返回 LIBOBS_API_VER这就是加载时版本兼容性检查的依据(见第 5 节源码分析)。

2.2 OBS_MODULE_USE_DEFAULT_LOCALE():标准 ini 本地化

OBS_MODULE_USE_DEFAULT_LOCALE(module_name, default_locale) 是一个辅助宏,使用标准 ini 文件格式做本地化。它自动初始化/销毁本地化数据,并自动提供 obs_module_text() 等模块外函数,让你以最小代价获取本地化字符串。

libobs/obs-module.h 的宏展开看,它一次性实现了四个函数:

生成函数 作用
obs_module_text(val) 查表返回翻译串;查不到时原样返回 val(fallback 到英文键名)
obs_module_get_string(val, &out) 查表,成功返回 true,失败 false
obs_module_set_locale(locale) obs_module_load_locale(obs_current_module(), default_locale, locale) 重建 lookup_t
obs_module_free_locale() 销毁 lookup_t 并置空

仓库中所有内置 C 插件都按此模式使用,例如 plugins/image-source/image-source.c

OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("image-source", "en-US")

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

第一个参数是插件名(用于定位数据目录下的 locale 子目录),第二个是回退语言。本地化数据以 ini 文件存放于插件数据目录,例如 plugins/image-source/data/locale/ 下按语言组织的翻译文件。

3. 模块导出函数(Module Exports)

以下函数是插件模块可以(或必须)导出、用于与 libobs 及前端通信的符号。

3.1 obs_module_load() —— 必需

bool obs_module_load(void);

必需导出。模块被加载(初始化)时调用。在此实现中加载模块的所有 source/encoder/output/service,或任何需要在启动时完成的工作。返回 true 继续加载;返回 false 表示失败,libobs 会放弃该模块并记录告警日志(见 libobs/obs-module.cobs_init_module 对返回值 false 的处理:blog(LOG_WARNING, "Failed to initialize module ..."))。

典型实现见上文 image-source 示例:把模块持有的 struct obs_source_info 逐个 obs_register_source() 注册。其他模块类型对应 obs_register_encoder()obs_register_output()obs_register_service()

3.2 obs_module_unload() —— 可选但关键

void obs_module_unload(void);

可选导出。libobs 关闭、模块即将卸载前调用。此时所有 libobs 对象仍然有效。文档给出的三条纪律非常重要:

  1. 用此函数保存用户设置,并释放模块自身持有的对 libobs 对象(sources、canvases、outputs、encoders、services)的强引用
  2. 不要释放/销毁仍在使用中的模块所提供对象——返回后 libobs 可能还会调用模块的回调(如 destroy)来清理剩余实例;
  3. 函数返回后,除 libobs 回调之外,不得再发起任何 libobs API 调用

3.3 obs_module_post_load() —— 可选

void obs_module_post_load(void);

可选导出。所有模块都完成加载后调用,适合做依赖其他插件的初始化(如 A 插件要使用 B 插件提供的功能,B 可能尚未注册)。

3.4 本地化相关导出

void obs_module_set_locale(const char *locale);  // 设置 locale 语言并加载 locale 数据
void obs_module_free_locale(void);              // 模块销毁时释放 locale 数据

若使用了 OBS_MODULE_USE_DEFAULT_LOCALE,这两个函数已被宏自动生成,无需手写。

3.5 元信息导出(可选)

const char *obs_module_name(void);        // 模块全名
const char *obs_module_description(void); // 模块描述

均为可选。实际插件中常用于描述插件用途,例如 image-source 返回 "Image/color/slideshow sources"plugins/image-source/image-source.c)。libobs/obs-module.h 还额外提供了 OBS_MODULE_AUTHOR(name) 宏来导出作者信息,前端"插件管理器"类界面即靠这些元信息展示插件卡片。

4. 模块外函数(Module Externs)

以下函数在整个模块内部可用,无需显式传模块指针:

函数 说明
const char *obs_module_text(const char *lookup_string) 返回本地化字符串
bool obs_module_get_string(const char *lookup_string, const char **translated_string) 本地化查表助手;找到返回 true,否则 false
obs_module_t *obs_current_module(void) 返回当前模块指针(由 OBS_DECLARE_MODULE 提供)
char *obs_module_file(const char *file) 返回当前模块数据文件的绝对位置,用 bfree() 释放;等价于 obs_find_module_file(obs_current_module(), file)
char *obs_module_config_path(const char *file) 返回当前模块配置文件的绝对位置(无论文件是否存在),配置目录未设置时返回 NULL;用 bfree() 释放;等价于 obs_module_get_config_path(obs_current_module(), file)

后两个宏在 libobs/obs-module.h 中定义为:

#define obs_module_file(file)            obs_find_module_file(obs_current_module(), file)
#define obs_module_config_path(file)     obs_module_get_config_path(obs_current_module(), file)

它们是插件定位自身资源(effect 文件、locale 文件、配置文件等)的标准入口,替代了手工拼接路径的做法。

5. 前端模块函数(Frontend Module Functions)

这一组函数由前端(如 OBS Studio 主程序)用于加载插件并获取插件信息。下面逐一说明,并用 libobs/obs-module.c 的实现印证。

5.1 obs_open_module():打开单个模块

int obs_open_module(obs_module_t **module, const char *path, const char *data_path);

从指定路径直接打开插件模块。若模块已存在,则直接成功并返回已有模块的指针。注意:它只加载模块映像(加载符号表),并不初始化模块;要初始化需再调用 obs_init_module()

参数:

  • module:输出的模块指针;
  • path:模块库文件路径;省略扩展名时自动使用操作系统对应的扩展名(.so / .dylib / .dll);
  • data_path:模块数据文件目录(无则传 NULL)。

返回码(定义见 libobs/obs-defs.h):

含义
MODULE_SUCCESS 0 成功
MODULE_ERROR -1 通用错误
MODULE_FAILED_TO_OPEN -2 模块打开失败(未找到或符号缺失;旧名 MODULE_FILE_NOT_FOUND 已标记废弃)
MODULE_MISSING_EXPORTS -3 缺少必需的导出符号
MODULE_INCOMPATIBLE_VER -4 版本不兼容
MODULE_HARDCODED_SKIP -5 被硬编码规则跳过(例如 macOS 上已废弃的旧版 obs-browser 插件)

libobs/obs-module.cobs_open_module 实现可以看到完整的打开流程:

  1. 参数校验(module/path/全局 obs 任一为空直接 MODULE_ERROR);
  2. macOS 上有硬编码跳过逻辑:路径同时包含 Library/Application Support/obs-studioobs-browser 时返回 MODULE_HARDCODED_SKIP——这正是文档中 MODULE_HARDCODED_SKIP 示例的来源;
  3. os_dlopen(path) 打开库映像,失败返回 MODULE_FAILED_TO_OPEN
  4. load_module_exports(&mod, path) 解析并绑定导出函数,失败返回 MODULE_MISSING_EXPORTS
  5. 版本检查:取 obs_module_ver() 的高 32 位(忽略 patch 版本),若大于当前 LIBOBS_API_VER 则判定"用更新的 libobs 编译",返回 MODULE_INCOMPATIBLE_VER
  6. 填充 bin_path/文件名/模块名/data_path,初始化 sources/outputs/encoders/services 四个动态数组,并调用 obs_module_load_metadata()
  7. 元数据加载:若 data_path 下存在 manifest.json,解析其中的 display_nameidversionos_archdescriptionurls 等字段(libobs/obs-module.c),供插件管理器展示;
  8. 最后通过 mod.set_pointer(*module) 把模块指针注入插件,并立即用 obs->locale 调用插件的 set_locale

5.2 obs_init_module():初始化模块

bool obs_init_module(obs_module_t *module);

初始化模块,实际调用其 obs_module_load 导出。返回 true 表示加载成功。

实现(libobs/obs-module.c)中有两处细节值得注意:

  • 已加载的模块直接返回 true(幂等);
  • 调用 obs_module_load 期间设置全局 loadingModule 上下文,并用 profiler 记录每个模块的初始化耗时——这就是 obs_module_load 中注册资源时可安全使用注册 API 的原因。

5.3 模块信息查询函数

void obs_log_loaded_modules(void);                 // 在日志中打印已加载模块列表
const char *obs_get_module_file_name(obs_module_t *module);   // 模块文件名
const char *obs_get_module_name(obs_module_t *module);        // 模块全名(无则 NULL)
const char *obs_get_module_author(obs_module_t *module);      // 作者
const char *obs_get_module_description(obs_module_t *module); // 描述
const char *obs_get_module_binary_path(obs_module_t *module); // 二进制路径
const char *obs_get_module_data_path(obs_module_t *module);    // 数据路径
void *obs_get_module_lib(obs_module_t *module);              // 模块底层库句柄(dlopen 句柄)

其中 obs_get_module_name 的取值优先级从实现可见(libobs/obs-module.c):优先取 manifest.jsondisplay_name,否则回退到插件导出的 obs_module_name()

5.4 批量发现与加载

obs_add_module_path()

void obs_add_module_path(const char *bin, const char *data);

obs_find_modules 添加模块搜索路径。路径字符串中的 %module% 占位符会在实际使用时被替换为模块名。

obs_find_modules() / obs_find_modules2()

在已添加的搜索路径中查找所有模块,结果通过回调逐条返回:

struct obs_module_info {
    const char *bin_path;
    const char *data_path;
};
typedef void (*obs_find_module_callback_t)(void *param,
                const struct obs_module_info *info);

struct obs_module_info2 {
    const char *bin_path;
    const char *data_path;
    const char *name;
};
typedef void (*obs_find_module_callback2_t)(void *param,
                const struct obs_module_info2 *info);

obs_find_modules2 是 v2 版本,回调结构体额外携带模块 name 字段。两个类型定义同时可在 libobs/obs.h 中找到。

obs_load_all_modules() / obs_load_all_modules2()

void obs_load_all_modules(void);
void obs_load_all_modules2(struct obs_module_failure_info *mfi);

便利函数:自动从所有模块路径加载全部模块。v2 版本额外提供加载失败信息:

struct obs_module_failure_info {
    char **failed_modules;  // 加载失败的模块列表(字符串指针数组)
    size_t count;
};

释放方式:调用 obs_module_failure_info_free(mfi),或直接对 failed_modules 成员 bfree() 即可。实现位于 libobs/obs-module.c

obs_add_safe_module() 与 Safe Mode

void obs_add_safe_module(const char *name);

name 加入安全模式(Safe Mode)允许加载的模块白名单;若白名单为空,则允许所有模块。name 是去掉扩展名的文件名。自 30.0 版本引入。

源码中可以看到其判定逻辑(libobs/obs-module.c):is_safe_module()obs->safe_modules 为空时对任意模块返回允许,否则做精确名字比较——这解释了文档"空列表即全放行"的语义。

其他生命周期函数

void obs_module_failure_info_free(struct obs_module_failure_info *mfi); // 释放失败信息(内部 bfree failed_modules)
void obs_post_load_modules(void);   // 通知所有模块"全部加载完成",即批量回调各插件的 obs_module_post_load

obs_enum_modules():枚举已加载模块

void obs_enum_modules(obs_enum_module_callback_t callback, void *param);

typedef void (*obs_enum_module_callback_t)(void *param, obs_module_t *module);

枚举当前所有已加载模块,前端遍历插件列表(如插件管理器界面)的基础设施。

5.5 模块文件/配置路径查询(前端视角)

char *obs_find_module_file(obs_module_t *module, const char *file);
char *obs_module_get_config_path(obs_module_t *module, const char *file);
  • obs_find_module_file:返回插件模块数据文件的位置;未找到返回 NULL;字符串用 bfree() 释放。
  • obs_module_get_config_path:返回插件模块配置文件的路径(无论文件是否存在);配置目录未设置返回 NULL

两者文档都特别注明:模块内部应使用 obs-module.h 中定义的 obs_module_file() / obs_module_config_path() 宏,它们省去显式传模块指针,是更优雅的用法。

6. 完整示例:一个最小插件的 API 契约

综合以上内容,一个注册单个源、启用标准本地化的最小模块,所需 API 面如下(示例取自 libobs/obs-module.h 的文档内嵌示例,与 plugins/image-source/image-source.c 的真实结构一致):

#include <obs-module.h>

OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("my-plugin", "en-US")

extern struct obs_source_info my_source;

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

各部分对应的 API 职责:

  1. OBS_DECLARE_MODULE():导出 obs_module_set_pointer / obs_current_module / obs_module_ver
  2. OBS_MODULE_USE_DEFAULT_LOCALE:自动生成 obs_module_textobs_module_get_stringobs_module_set_localeobs_module_free_locale
  3. obs_module_load():唯一的必需导出,返回 false 即加载失败;
  4. 其余(obs_module_unloadobs_module_post_loadobs_module_nameobs_module_description)均按需导出;
  5. 可选地在数据目录放置 manifest.json 提供 display_name/id/version/description 等元数据,供前端展示。

7. 小结:模块加载全链路

把前端函数按实际执行顺序串起来,即为 libobs 插件的完整生命周期:

obs_add_module_path(bin, data)          // 注册搜索路径(支持 %module% 占位)
  → obs_find_modules(2)(callback)      // 发现候选模块
  → obs_add_safe_module(name)          // (可选)安全模式白名单
  → obs_open_module(&mod, path, data) // dlopen + 导出检查 + 版本检查 + manifest 元数据
  → obs_init_module(mod)               // 调用插件 obs_module_load()
  → obs_post_load_modules()            // 触发各插件 obs_module_post_load()
  → obs_log_loaded_modules()           // 日志留痕

任何一步失败的诊断入口都有据可查:obs_open_module 的返回码区分"找不到/符号缺失/版本过新/被硬编码跳过"四类失败;obs_load_all_modules2 + struct obs_module_failure_info 批量给出失败模块清单;而 obs_log_loaded_modulesobs_find_modules2 则提供了成功侧的审计能力。

适用前提:本文所有行为均以当前仓库 master 代码为准,API 声明集中在 libobs/obs.hlibobs/obs-module.h,实现在 libobs/obs-module.cobs_add_safe_module 自 30.0 引入;MODULE_FILE_NOT_FOUND 已由 MODULE_FAILED_TO_OPEN 取代并标记废弃。

参考文档:docs/sphinx/reference-modules.rst,同级 API 参考还包括 Core核心对象GraphicsMedia I/O

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391