OBS Studio 前端 API 深度解析:obs-frontend-api.h 的事件回调、场景/配置控制与 Qt 主窗口实现
OBS Studio 的 libobs 核心负责采集、渲染与输出,而插件若需要操作界面本身——切换场景、启停推流、注册事件回调、添加 Dock——则必须依赖 Frontend API。本文以官方 API 参考 reference-frontend-api.rst 为主体,逐一覆盖其定义的 obs_frontend_event 事件枚举、obs_frontend_source_list 结构与全部 API 函数,并结合 frontend/api/ 目录的源码实现(分发器模式、OBSStudioAPI 对 Qt 主窗口的桥接),讲清楚该 API 的调用链路、内存管理与线程安全约定,帮助你写出可稳定嵌入 OBS Studio 的 C/C++ 插件。
一、Frontend API 的定位与整体架构
Frontend API 是 OBS Studio 自身(而非 libobs 核心)提供的 API,使用方式非常直接:
#include <obs-frontend-api.h>
从源码结构看,它的分层非常清晰,一次典型调用会经过四层:
- C 语言导出层:obs-frontend-api.h 声明全部
EXPORT函数,并以extern "C"暴露,供 C/C++ 插件链接; - 分发层:obs-frontend-api.cpp 持有一个
static unique_ptr<obs_frontend_callbacks> c,每个导出函数先经callbacks_valid()校验再转发给该指针; - 纯虚接口层:obs-frontend-internal.hpp 定义
obs_frontend_callbacks抽象类,把整个 API 面抽象为一组纯虚函数,实现端还包括on_load/on_preload/on_save/on_event四个内部钩子; - Qt 实现层:OBSStudioAPI.hpp 中的
OBSStudioAPI结构体实现该接口,内部持有OBSBasic *main(即 OBSBasic.hpp 定义的主窗口类),通过QMetaObject::invokeMethod把跨线程请求安全地投递回 UI 线程。
前端启动时由 OBSStudioAPI.cpp 的 InitializeAPIInterface 完成装配:
obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)
{
obs_frontend_callbacks *api = new OBSStudioAPI(main);
obs_frontend_set_callbacks_internal(api);
return api;
}
退出时 OBSBasic.cpp 会调用 obs_frontend_set_callbacks_internal(nullptr) 注销回调,此后任何前端调用都会在分发层被拦截并记录 "Tried to call %s with no callbacks!" 错误日志(见 obs-frontend-api.cpp)。
构建方面,frontend/api/CMakeLists.txt 将 obs-frontend-api 编译为 SHARED 库、链接 OBS::libobs,并以 obs-frontend-api.h 为 PUBLIC_HEADER 对外发布头文件。因此插件工程链接 OBS::frontend-api 即可使用整套 API。
需要说明的前提:Frontend API 只有在带 Qt 前端的 OBS Studio 中才有实现;返回值中的 void * 实际是 Qt 对象(QMainWindow *、QWidget *、QAction * 等),在 C 侧被当作不透明指针处理。
二、核心数据结构与类型
2.1 obs_frontend_source_list:场景/过渡源列表
文档给出的核心结构是一个基于 DARRAY 的源指针数组。在 obs-frontend-api.h 中的实际定义如下,obs_frontend_source_list_free 本身就是一个头文件内联函数:
struct obs_frontend_source_list {
DARRAY(obs_source_t *) sources;
};
static inline void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list)
{
size_t num = source_list->sources.num;
for (size_t i = 0; i < num; i++) {
obs_source_release(source_list->sources.array[i]);
}
da_free(source_list->sources);
}
文档中的标准用法示例(务必完整遵循其释放纪律):
struct obs_frontend_source_list scenes = {0};
obs_frontend_get_scenes(&scenes);
for (size_t i = 0; i < scenes.sources.num; i++) {
/* Do NOT call `obs_source_release` or `obs_scene_release`
* on these sources
*/
obs_source_t *source = scenes.sources.array[i];
/* Convert to obs_scene_t if needed */
obs_scene_t *scene = obs_scene_from_source(source);
[...]
}
obs_frontend_source_list_free(&scenes);
引用计数的规则在文档中写得很明确,源码也印证了这一点:
obs_frontend_get_scenes返回的是已增加引用的场景,与 Scenes dock 的显示顺序一致;- 不得在遍历中对列表内元素单独调用
obs_source_release/obs_scene_release,否则会造成双重释放、甚至导致场景被删除;统一用obs_frontend_source_list_free收尾; - 若需要长期持有某个场景,应先用
obs_source_get_ref/obs_scene_get_ref增引用,释放时只调用其中一种(两者释放的是同一对象)。
obs_frontend_get_transitions 也使用同一结构接收“引用已增的过渡源列表”,释放方式相同。
2.2 回调函数类型
文档定义了五类回调原型,均带 void *private_data 用户数据(翻译回调除外):
| 类型 | 原型 | 用途 |
|---|---|---|
obs_frontend_cb |
void (*)(void *private_data) |
前端“工具”菜单项点击回调 |
obs_frontend_event_cb |
void (*)(enum obs_frontend_event, void *private_data) |
前端事件回调 |
obs_frontend_save_cb |
void (*)(obs_data_t *save_data, bool saving, void *private_data) |
场景集保存/加载回调(saving 区分方向) |
obs_frontend_translate_ui_cb |
bool (*)(const char *text, const char **out) |
UI 翻译拦截回调 |
undo_redo_cb |
void (*)(const char *data) |
撤销/重做回调,携带数据字符串 |
2.3 obs_frontend_event:前端事件枚举
obs_frontend_event 枚举(定义见 obs-frontend-api.h)是插件与界面同步的核心。文档覆盖的完整事件集合如下,按功能分组:
直播(Streaming)
| 事件 | 触发时机 |
|---|---|
OBS_FRONTEND_EVENT_STREAMING_STARTING |
直播启动时 |
OBS_FRONTEND_EVENT_STREAMING_STARTED |
直播成功启动后 |
OBS_FRONTEND_EVENT_STREAMING_STOPPING |
直播停止时 |
OBS_FRONTEND_EVENT_STREAMING_STOPPED |
直播完全停止后 |
录像(Recording)
| 事件 | 触发时机 |
|---|---|
OBS_FRONTEND_EVENT_RECORDING_STARTING |
录像启动时 |
OBS_FRONTEND_EVENT_RECORDING_STARTED |
录像成功启动后 |
OBS_FRONTEND_EVENT_RECORDING_STOPPING |
录像停止时 |
OBS_FRONTEND_EVENT_RECORDING_STOPPED |
录像完全停止后 |
OBS_FRONTEND_EVENT_RECORDING_PAUSED |
录像被暂停时 |
OBS_FRONTEND_EVENT_RECORDING_UNPAUSED |
录像恢复(取消暂停)时 |
场景与过渡(Scene / Transition)
| 事件 | 触发时机 |
|---|---|
OBS_FRONTEND_EVENT_SCENE_CHANGED |
当前场景改变时 |
OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED |
用户添加/删除/重排场景时 |
OBS_FRONTEND_EVENT_TRANSITION_CHANGED |
用户更改当前过渡时 |
OBS_FRONTEND_EVENT_TRANSITION_STOPPED |
一次过渡执行完成时 |
OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED |
用户添加/删除过渡时 |
OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED |
用户更改过渡时长时 |
OBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED |
过渡 T 条被拖动时 |
场景集 / 配置档(Scene Collection / Profile)
| 事件 | 触发时机 |
|---|---|
OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING |
当前场景集即将切换时 |
OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED |
当前场景集已切换后 |
OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED |
场景集被添加或删除时 |
OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED |
场景集被重命名时 |
OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP |
场景集被完全卸载(即将加载新场景集或即将退出)时 |
OBS_FRONTEND_EVENT_PROFILE_CHANGING |
当前 profile 即将切换时 |
OBS_FRONTEND_EVENT_PROFILE_CHANGED |
当前 profile 已切换后 |
OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED |
profile 被添加或删除时 |
OBS_FRONTEND_EVENT_PROFILE_RENAMED |
profile 被重命名时 |
回放缓冲 / 虚拟摄像头 / Studio Mode
| 事件 | 触发时机 |
|---|---|
OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING |
回放缓冲启动时 |
OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED |
回放缓冲成功启动后 |
OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING |
回放缓冲停止时 |
OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED |
回放缓冲完全停止后 |
OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVING |
回放缓冲正在保存时 |
OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED |
回放缓冲已保存后 |
OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED |
虚拟摄像头启动时 |
OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED |
虚拟摄像头停止时 |
OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED |
用户开启 Studio Mode(预览/节目) |
OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED |
用户关闭 Studio Mode |
OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED |
Studio Mode 下预览场景改变时 |
程序生命周期与杂项
| 事件 | 触发时机 | 版本 |
|---|---|---|
OBS_FRONTEND_EVENT_FINISHED_LOADING |
程序完成加载时 | 基础 |
OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN |
脚本需要在 OBS 退出前得知时。通常 OBS_FRONTEND_EVENT_EXIT 在脚本被销毁之后才触发 |
基础 |
OBS_FRONTEND_EVENT_EXIT |
程序即将退出。这是最后调用任何前端 API 做保存/清理的机会,回调返回后不得再发起任何前端 API 调用 | 基础 |
OBS_FRONTEND_EVENT_THEME_CHANGED |
主题切换时 | 29.0.0 |
OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN |
截图完成时 | 29.0.0 |
补充一个源码层面的细节:头文件枚举中还额外定义了 OBS_FRONTEND_EVENT_CANVAS_ADDED 与 OBS_FRONTEND_EVENT_CANVAS_REMOVED(obs-frontend-api.h),配合多画布(canvas)能力使用,官方 RST 参考目前未将其列入正文。
三、API 函数参考(按功能分组)
以下函数签名与文档一致,全部声明于 obs-frontend-api.h。
3.1 场景列表与当前场景
| 函数 | 说明 |
|---|---|
void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list) |
释放列表内所有源并释放列表本身 |
void *obs_frontend_get_main_window(void) |
返回 OBS Studio 主窗口的 QMainWindow * |
void *obs_frontend_get_main_window_handle(void) |
返回主窗口的原生窗口句柄 |
char **obs_frontend_get_scene_names(void) |
返回以 NULL 结尾的场景名列表,顺序与 Scenes dock 显示一致。列表存于单一连续内存段,用 bfree() 释放基指针即可释放整个列表 |
void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) |
填充“引用已增”的场景列表(见 2.1 节的释放纪律) |
obs_source_t *obs_frontend_get_current_scene(void) |
返回当前激活场景的新引用,用 obs_source_release() 释放 |
void obs_frontend_set_current_scene(obs_source_t *scene) |
设置当前场景 |
源码印证:OBSStudioAPI.cpp 中 obs_frontend_get_scenes 正是遍历 Scenes dock 的列表项(main->ui->scenes)逐个 da_push_back,因此“顺序与 Scenes dock 一致”是有源码依据的;而 obs_frontend_set_current_scene 在 Studio Mode 下会走 TransitionToScene 触发过渡,否则直接调用 SetCurrentScene(OBSStudioAPI.cpp)。
3.2 过渡(Transition)与 T 条
| 函数 | 说明 |
|---|---|
void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) |
接收“引用已增”的过渡源列表,用 obs_frontend_source_list_free 释放 |
obs_source_t *obs_frontend_get_current_transition(void) |
返回当前过渡的新引用,用 obs_source_release() 释放 |
void obs_frontend_set_current_transition(obs_source_t *transition) |
设置当前过渡 |
int obs_frontend_get_transition_duration(void) |
返回 UI 中当前设置的过渡时长(毫秒) |
void obs_frontend_set_transition_duration(int duration) |
设置过渡时长(毫秒) |
void obs_frontend_release_tbar(void) |
模拟鼠标在 T 条上松开,确定过渡状态 |
void obs_frontend_set_tbar_position(int position) |
设置 T 条数值,取值范围 0–1023 |
int obs_frontend_get_tbar_position(void) |
获取 T 条数值,范围 0–1023 |
从实现看,set_current_transition、set_transition_duration、release_tbar、set_tbar_position 均通过 QMetaObject::invokeMethod 投递到 UI 线程(OBSStudioAPI.cpp),插件可在非 UI 线程安全调用这些“写”类接口。
3.3 场景集(Scene Collection)
| 函数 | 说明 |
|---|---|
char **obs_frontend_get_scene_collections(void) |
场景集名称列表,NULL 结尾、单段内存,bfree() 释放 |
char *obs_frontend_get_current_scene_collection(void) |
当前场景集名的新指针,bfree() 释放 |
void obs_frontend_set_current_scene_collection(const char *collection) |
激活指定名称的场景集 |
bool obs_frontend_add_scene_collection(const char *name) |
新建场景集并切换到它;返回是否成功 |
3.4 配置档(Profile)
| 函数 | 说明 |
|---|---|
char **obs_frontend_get_profiles(void) |
profile 名称列表,NULL 结尾、单段内存,bfree() 释放 |
char *obs_frontend_get_current_profile(void) |
当前 profile 名的新指针,bfree() 释放 |
char *obs_frontend_get_current_profile_path(void) |
当前 profile 的文件系统路径,bfree() 释放 |
void obs_frontend_set_current_profile(const char *profile) |
激活指定 profile |
bool obs_frontend_create_profile(const char *name) |
创建新 profile(名称必须唯一) |
bool obs_frontend_duplicate_profile(const char *name) |
复制当前 profile 为指定新名称(必须唯一) |
void obs_frontend_delete_profile(const char *profile) |
删除指定 profile |
3.5 UI 扩展:工具菜单与 Dock
| 函数 | 说明 |
|---|---|
void *obs_frontend_add_tools_menu_qaction(const char *name) |
向“工具”菜单添加 QAction 并返回其指针 |
void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data) |
添加菜单项并把 ::clicked 信号连接到回调 |
bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) |
以 QWidget 添加 Dock,并在“停靠窗口”菜单生成开关项。Dock 关闭时会向该控件发送类型为 QEvent::User + QEvent::Close 的自定义事件,便于其释放资源;显示时默认已有通用 QShowEvent。id 已占用时返回 false。30.0 加入 |
void obs_frontend_remove_dock(const char *id) |
从 UI 移除指定 id 的 Dock。30.0 加入 |
bool obs_frontend_add_custom_qdock(const char *id, void *dock) |
添加不带菜单开关的自定义 QDockWidget。30.0 加入 |
3.6 事件回调与保存/预加载回调
| 函数 | 说明 |
|---|---|
void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) |
注册前端事件回调 |
void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) |
移除事件回调 |
void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) |
注册场景集保存/加载回调 |
void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) |
移除保存/加载回调 |
void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) |
注册“场景集加载前”的回调 |
void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) |
移除预加载回调 |
保存/预加载/事件三组回调在 OBSStudioAPI.hpp 中分别以 saveCallbacks、preloadCallbacks、callbacks 三个向量管理,回调及其 private_data 被打包进 OBSStudioCallback<T>。事件分发的一个值得注意的细节在 OBSStudioAPI.cpp:当主窗口处于禁用保存状态(main->disableSaving)时,除 SCENE_COLLECTION_CLEANUP 与 EXIT 之外的所有事件都会被过滤,保证插件在清理阶段仍能收到收尾事件。
3.7 UI 翻译拦截
| 函数 | 说明 |
|---|---|
void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) |
压入 UI 翻译回调,允许前端插件拦截 Qt 自动生成翻译文本的过程,通常传入 obs_module_get_string |
void obs_frontend_pop_ui_translation(void) |
弹出当前 UI 翻译回调 |
obs-frontend-api.h 中的注释明确提醒:OBS UI 绕过了 Qt 的标准本地化机制,插件 UI 不应直接使用 Qt 的翻译方法,而应在文本即将被翻译时 push、翻译完成后 pop。
3.8 直播、录像与回放缓冲控制
| 函数 | 说明 |
|---|---|
void obs_frontend_streaming_start(void) |
启动直播 |
void obs_frontend_streaming_stop(void) |
停止直播 |
bool obs_frontend_streaming_active(void) |
直播是否激活 |
void obs_frontend_recording_start(void) |
启动录像 |
void obs_frontend_recording_stop(void) |
停止录像 |
bool obs_frontend_recording_active(void) |
录像是否激活 |
void obs_frontend_recording_pause(bool pause) |
true 暂停录像,false 取消暂停 |
bool obs_frontend_recording_paused(void) |
录像是否处于暂停 |
bool obs_frontend_recording_split_file(void) |
请求拆分当前录像文件。true 仅代表“请求成功”(不保证已完成或确实拆分),录像未激活/已暂停或拆分功能被禁用时返回 false |
bool obs_frontend_recording_add_chapter(const char *name) |
在当前输出时刻插入章节标记。name 可为 NULL(使用自动名 “Unnamed <章节号>” 或本地化等价名)。录像未激活、暂停或当前输出不支持章节时返回 false。30.2 加入 |
void obs_frontend_replay_buffer_start(void) |
启动回放缓冲 |
void obs_frontend_replay_buffer_stop(void) |
停止回放缓冲 |
void obs_frontend_replay_buffer_save(void) |
回放缓冲激活时保存一次回放 |
bool obs_frontend_replay_buffer_active(void) |
回放缓冲是否激活 |
从源码看,streaming_active/recording_active 等状态查询是原子地读取 volatile bool 标志位(如 OBSStudioAPI.cpp 使用 os_atomic_load_bool(&streaming_active)),因此这些“读”类接口可以安全地在任意线程调用;而启停操作则通过 invokeMethod 回到 UI 线程执行。
3.9 投影仪窗口(Projector)
| 函数 | 说明 |
|---|---|
void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name) |
打开投影仪。type 取值不区分大小写:"Preview"、"Source"、"Scene"、"StudioProgram"、"Multiview";monitor 为 -1 时打开为普通窗口,此时 geometry 生效(Base64 编码的 Qt 几何信息);type 为 "Source" 或 "Scene" 时 name 指定要显示的源或场景名 |
3.10 输出对象、配置与推流服务
| 函数 | 说明 |
|---|---|
void obs_frontend_save(void) |
保存当前场景集 |
obs_output_t *obs_frontend_get_streaming_output(void) |
当前直播输出的新引用,用 obs_output_release() 释放 |
obs_output_t *obs_frontend_get_recording_output(void) |
当前录像输出的新引用 |
obs_output_t *obs_frontend_get_replay_buffer_output(void) |
当前回放缓冲输出的新引用 |
config_t *obs_frontend_get_profile_config(void) |
当前 profile 对应的 config_t *(不拥有所有权) |
config_t *obs_frontend_get_global_config(void) |
31.0 起弃用:原返回全局配置(global.ini)。实现中会打印弃用警告并转发到 obs_frontend_get_app_config(obs-frontend-api.cpp) |
config_t *obs_frontend_get_app_config(void) |
系统级设置(global.ini)对应的 config_t *。31.0 加入 |
config_t *obs_frontend_get_user_config(void) |
用户设置(user.ini)对应的 config_t *。31.0 加入 |
void obs_frontend_set_streaming_service(obs_service_t *service) |
设置当前推流服务 |
obs_service_t *obs_frontend_get_streaming_service(void) |
当前推流服务对象(不增加引用) |
void obs_frontend_save_streaming_service(void) |
保存当前推流服务数据 |
3.11 Studio Mode(预览/节目)
| 函数 | 说明 |
|---|---|
bool obs_frontend_preview_program_mode_active(void) |
Studio Mode 是否激活 |
void obs_frontend_set_preview_program_mode(bool enable) |
开启/关闭 Studio Mode |
void obs_frontend_preview_program_trigger_transition(void) |
Studio Mode 激活时触发“预览到节目”过渡 |
obs_source_t *obs_frontend_get_current_preview_scene(void) |
Studio Mode 激活时返回当前预览场景的新引用(否则 NULL),用 obs_source_release() 释放 |
void obs_frontend_set_current_preview_scene(obs_source_t *scene) |
设置 Studio Mode 下的预览场景;非 Studio Mode 时不生效 |
void obs_frontend_set_preview_enabled(bool enable) |
设置预览画面开关状态,仅在 Studio Mode 关闭时相关 |
bool obs_frontend_preview_enabled(void) |
预览画面是否启用 |
源码印证:obs_frontend_get_current_scene 在 Studio Mode 下返回的是 programScene(节目场景)而非预览场景(OBSStudioAPI.cpp),这一点在写自动化脚本时需要特别注意。
3.12 截图、虚拟摄像头与视频重置
| 函数 | 说明 |
|---|---|
void *obs_frontend_take_screenshot(void) |
对 OBS 主输出截图,返回图像数据(QImage *) |
void *obs_frontend_take_source_screenshot(obs_source_t *source) |
对指定源截图 |
obs_output_t *obs_frontend_get_virtualcam_output(void) |
当前虚拟摄像头输出的新引用 |
void obs_frontend_start_virtualcam(void) |
启动虚拟摄像头 |
void obs_frontend_stop_virtualcam(void) |
停止虚拟摄像头 |
bool obs_frontend_virtualcam_active(void) |
虚拟摄像头是否激活 |
void obs_frontend_reset_video(void) |
依据当前 profile 的最新数据重载 UI 画布并重置 libobs 视频 |
3.13 源窗口、输出路径、本地化与主题
| 函数 | 说明 |
|---|---|
void *obs_frontend_open_source_properties(obs_source_t *source) |
打开指定源的属性窗口 |
void *obs_frontend_open_source_filters(obs_source_t *source) |
打开指定源的滤镜窗口 |
void *obs_frontend_open_source_interaction(obs_source_t *source) |
打开指定源的交互窗口;仅当源具有 OBS_SOURCE_INTERACTION 输出标志时才可调用 |
void *obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) |
打开指定 scene item 的变换编辑窗口。29.1 加入 |
char *obs_frontend_get_current_record_output_path(void) |
当前录像输出路径的新指针,bfree() 释放 |
const char *obs_frontend_get_locale_string(const char *string) |
获取给定字符串的前端翻译 |
bool obs_frontend_is_theme_dark(void) |
当前主题是否为深色。29.0.0 加入 |
char *obs_frontend_get_last_recording(void) |
最后一次录像的文件路径,bfree() 释放。29.0.0 加入 |
char *obs_frontend_get_last_screenshot(void) |
最后一次截图的文件路径,bfree() 释放。29.0.0 加入 |
char *obs_frontend_get_last_replay(void) |
最后一次回放缓冲保存的文件路径,bfree() 释放。29.0.0 加入 |
3.14 撤销/重做与场景项复制粘贴
| 函数 | 说明 |
|---|---|
void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo, const char *undo_data, const char *redo_data, bool repeatable) |
注册一个撤销/重做动作。repeatable 为 true 时,同名的多个动作可合并为一个撤销/重做动作(撤销取第一个、重做取最后一个)。29.1 加入 |
void obs_frontend_copy_sceneitem(obs_sceneitem_t *item) |
复制指定场景项。32.2 加入 |
bool obs_frontend_can_paste_sceneitem(bool duplicate) |
查询已复制的场景项能否粘贴(duplicate 用于检查所复制源是否允许复制方式粘贴)。32.2 加入 |
void obs_frontend_paste_sceneitem(obs_scene_t *scene, bool duplicate) |
粘贴到指定场景:true 为粘贴副本,false 为粘贴引用。32.2 加入 |
四、内存管理约定(务必遵守)
Frontend API 的字符串与对象返回遵循三套明确的释放规则,obs-frontend-api.h 中的注释与分发层实现共同确定了这套契约:
char **字符串列表是“单次分配”。以 obs-frontend-api.cpp 的convert_string_list为例:它先用bmalloc分配一段连续内存,前部存放char *指针数组(末尾置nullptr),后部紧跟各字符串数据(含终止符)。因此只需对基指针调用一次bfree(),整个列表(含所有字符串)即被释放——不要对单个元素再 free。char *单串返回值(如obs_frontend_get_current_profile、obs_frontend_get_current_profile_path、obs_frontend_get_current_record_output_path、obs_frontend_get_last_*系列)均为新分配指针,统一用bfree()释放。实现侧使用bstrdup复制字符串(OBSStudioAPI.cpp),确认了“返回新指针、调用方负责释放”的语义。- libobs 引用计数对象(
obs_source_t *、obs_output_t *等):get_current_scene、get_current_transition、get_*_output均返回新引用,用对应obs_*_release释放;而obs_frontend_get_streaming_service明确不增加引用,这是所有 get 函数中唯一的例外,文档已特别注明。 - 事件与退出顺序:
OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN在脚本体系销毁前触发,供脚本感知退出;OBS_FRONTEND_EVENT_EXIT是程序退出的最后通知,回调返回之后不允许再调用任何前端 API——这与分发层“退出时注销回调”的实现(OBSBasic.cpp)相互印证。
五、源码实现要点与线程安全
分发器模式。 obs-frontend-api.cpp 中所有导出函数都是薄封装:先 callbacks_valid() 校验(失败则 blog(LOG_ERROR, ...) 并返回空/false/0),再转发给 obs_frontend_callbacks。这使得前端 API 库可以独立于 Qt 实现构建,也解释了为什么插件在未装配前端(例如纯 libobs 环境)中调用这些函数不会崩溃,而是得到错误日志加空值。
跨线程 UI 操作。 写 UI 的操作在 OBSStudioAPI.cpp 中普遍采用 QMetaObject::invokeMethod(main, ..., WaitConnection(), ...) 形式,即阻塞式地把方法调用投递到主窗口的 UI 线程并等待完成。obs_frontend_add_scene_collection 甚至通过 lambda 捕获出参 success 的方式把 UI 线程的结果带回调用方(OBSStudioAPI.cpp)。可以推断:插件从非 UI 线程调用这些接口是受支持的,但会短暂阻塞等待 UI 线程。
回调注册/注销的匹配规则。 回调以“函数指针 + private_data”二元组识别(OBSStudioAPI.cpp 的 GetCallbackIdx),移除回调时两组参数都必须与注册时完全一致。
弃用演进。 obs_frontend_get_global_config 在 31.0 被 OBS_DEPRECATED 标记,实现层会输出弃用警告并建议显式使用 obs_frontend_get_app_config 或 obs_frontend_get_user_config(obs-frontend-api.cpp),新代码应直接采用后两者。
SWIG 边界。 头文件中 obs_frontend_source_list、obs_frontend_canvas_list 及其内联释放函数被 #ifndef SWIG 包裹(obs-frontend-api.h),说明脚本绑定(Python 等)走的是另一套适配路径,C/C++ 插件则直接使用这些结构。
六、版本演进小结与适用前提
综合文档中的 versionadded 标注,该 API 的主要演进节点为:
| 版本 | 新增能力 |
|---|---|
| 29.0.0 | OBS_FRONTEND_EVENT_THEME_CHANGED、OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN、obs_frontend_is_theme_dark、obs_frontend_get_last_recording/screenshot/replay |
| 29.1 | obs_frontend_open_sceneitem_edit_transform、obs_frontend_add_undo_redo_action |
| 30.0 | Dock 管理三件套:obs_frontend_add_dock_by_id、obs_frontend_remove_dock、obs_frontend_add_custom_qdock |
| 30.2 | obs_frontend_recording_add_chapter |
| 31.0 | obs_frontend_get_app_config、obs_frontend_get_user_config;obs_frontend_get_global_config 弃用 |
| 32.2 | 场景项复制粘贴:obs_frontend_copy_sceneitem、obs_frontend_can_paste_sceneitem、obs_frontend_paste_sceneitem |
适用前提与限制:
- 该 API 面向运行中的 OBS Studio(Qt 前端);返回值中的
void *是 Qt 对象指针,在纯 C 环境中只能当不透明句柄传递; - 头文件 obs-frontend-api.h 中还包含少量文档未覆盖的函数(如
obs_frontend_get_system_tray、obs_frontend_defer_save_begin/end及 canvas 管理函数),本文以官方 RST 参考文档列出的函数为准,额外函数请直接查头文件确认签名; - 插件工程侧只需链接构建产物中的
OBS::frontend-api目标(见 frontend/api/CMakeLists.txt),无需自行编译 OBS 前端代码。
参考文件:docs/sphinx/reference-frontend-api.rst、frontend/api/obs-frontend-api.h、frontend/api/obs-frontend-api.cpp、frontend/api/obs-frontend-internal.hpp、frontend/OBSStudioAPI.hpp、frontend/OBSStudioAPI.cpp、frontend/widgets/OBSBasic.hpp。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00