首页
/ OBS Studio obs_data_t 数据设置 API 详解:源/编码器配置的 JSON 存储、默认值与安全读写机制

OBS Studio obs_data_t 数据设置 API 详解:源/编码器配置的 JSON 存储、默认值与安全读写机制

2026-09-06 19:13:59作者:农烁颖Land

本篇以 OBS Studio 官方 Sphinx 文档 reference-settings.rst 中“Data Settings API Reference (obs_data_t)”一节为核心,完整讲解 obs_data_t / obs_data_array_t 数据设置对象的设计目标、全部 API 分组(通用函数、Set/Get 函数、默认值函数、自动选择函数、数组函数),并结合 libobs/obs-data.clibobs/obs-data.h 的源码实现,说明其引用计数、JSON 序列化、备份回退等底层机制,帮助你为 OBS 插件开发(source、encoder、output、service)写出正确且可持久化的配置处理代码。

一、obs_data_t 是什么:比 JSON 对象更强的配置容器

官方文档对该 API 的定位是:

Data settings objects are reference-counted objects that store values in a string-table or array. They're similar to Json objects, but additionally allow additional functionality such as default or auto-selection values. Data is saved/loaded to/from Json text and Json text files.

即:数据设置对象是引用计数的对象,内部以“字符串表(string-table)”或数组形式存储值;类似 JSON 对象,但额外支持**默认值(default)自动选择值(autoselect)**等能力,并可保存到/加载自 JSON 文本或 JSON 文件。

从源码结构看,这三个核心类型定义在 libobs/obs-data.h 中:

struct obs_data;
struct obs_data_item;
struct obs_data_array;
typedef struct obs_data obs_data_t;
typedef struct obs_data_item obs_data_item_t;
typedef struct obs_data_array obs_data_array_t;

其内部结构在 libobs/obs-data.c 中实现:

struct obs_data_item {
	volatile long ref;
	const char *name;
	struct obs_data *parent;
	UT_hash_handle hh;      // uthash 哈希表句柄
	enum obs_data_type type;
	size_t name_len, data_len, data_size,
	       default_len, default_size, autoselect_size, capacity;
};

struct obs_data {
	volatile long ref;      // 引用计数
	char *json;            // 缓存的 JSON 字符串
	struct obs_data_item *items;
};

struct obs_data_array {
	volatile long ref;
	DARRAY(obs_data_t *) objects;   // 动态数组
};

几个值得注意的实现事实:

  • 值类型枚举enum obs_data_type 定义了 OBS_DATA_NULL / STRING / NUMBER / BOOLEAN / OBJECT / ARRAY 六种类型;数字类型进一步区分为 OBS_DATA_NUM_INTOBS_DATA_NUM_DOUBLE(见 libobs/obs-data.h)。
  • 单项只分配一次内存obs_data_item 的 name、user 值、default 值、autoselect 值按对齐连续排在同一块分配之后(源码注释 "designed to be one allocation only",见 libobs/obs-data.c),减少碎片化分配。
  • JSON 字符串缓存在对象内struct obs_data 中的 json 字段保存最近一次生成的 JSON 文本,对应 obs_data_get_last_json() 的语义。

头文件顶部注释明确了用途:"This is used for retrieving or setting the data settings for things such as sources, encoders, etc. This is designed for JSON serialization."(见 libobs/obs-data.h)——它是 source/encoder 等对象设置的标准存取层。

二、通用函数:创建、销毁与 JSON 互转

文档 “General Functions” 一节的 API 与源码对应关系如下(声明见 libobs/obs-data.h):

2.1 创建与引用计数

obs_data_t *obs_data_create();

返回一个新的数据对象引用,需以 obs_data_release() 释放。实现见 libobs/obs-data.cbzalloc 零初始化后 ref = 1

obs_data_t *obs_data_create_from_json(const char *json_string);
obs_data_t *obs_data_create_from_json_file(const char *json_file);
obs_data_t *obs_data_create_from_json_file_safe(const char *json_file, const char *backup_ext);
  • obs_data_create_from_json:从 JSON 字符串创建。源码中使用 jansson 解析并带 JSON_REJECT_DUPLICATES 标志(libobs/obs-data.c)——重复键的 JSON 会解析失败,失败时输出 LOG_ERROR 日志并返回 NULL
  • obs_data_create_from_json_file:读取 UTF-8 文件后调用上述字符串版本。
  • obs_data_create_from_json_file_safe:加载失败的备份回退机制。当主文件损坏或加载失败且提供了 backup_ext 时,会拼接备份文件路径(若非 . 开头会自动补 .)、检查其存在性,打印警告日志 “attempting backup file”,将备份文件 os_rename 回主文件再重新加载(libobs/obs-data.c)。这是场景文件等关键配置防损坏的第一道防线。

引用计数操作:

void obs_data_addref(obs_data_t *data);
void obs_data_release(obs_data_t *data);

addref 对空指针安全;release 在计数归零时调用 obs_data_destroy,遍历哈希表逐个 obs_data_item_detach 并释放,且注释特别提示 JSON 文本不能用 libobs 的 bfree 释放,因为它由 jansson 分配(libobs/obs-data.c)。

另外,头文件提供了一个内联便捷函数 obs_data_newref:对非空指针加引用,对 NULL 则创建新对象(libobs/obs-data.h),常用于“取嵌套对象再回填”的赋值场景。

2.2 JSON 序列化:四种 get_json 变体

const char *obs_data_get_json(obs_data_t *data);
const char *obs_data_get_json_with_defaults(obs_data_t *data);
const char *obs_data_get_json_pretty(obs_data_t *data);
const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data);
const char *obs_data_get_last_json(obs_data_t *data);

文档说明:生成的字符串分配在数据对象内部,无需手动 free。源码 libobs/obs-data.c 显示它们共用 obs_data_get_json_internal(data, pretty, with_defaults)

  • pretty 时 jansson 使用 JSON_PRESERVE_ORDER | JSON_INDENT(4),否则用 JSON_PRESERVE_ORDER | JSON_COMPACT
  • 每次生成前先 free(data->json) 清掉旧缓存,再用 json_dumps 重新写入 data->json——因此对象中始终只缓存最近一次的 JSON 文本obs_data_get_last_json() 只是返回这个缓存(不生成新字符串);
  • 关键语义:默认序列化(with_defaults == false)时,没有 user value 的项会被跳过(源码行 578:if (!with_defaults && !obs_data_item_has_user_value(item)) continue;)。这正是 ..._with_defaults 变体的价值——把默认值一并落盘。

2.3 保存与合并

bool obs_data_save_json(obs_data_t *data, const char *file);
bool obs_data_save_json_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext);
  • obs_data_save_json:先生成 JSON,再用 os_quick_write_utf8_file 写入;JSON 为空返回 falselibobs/obs-data.c)。
  • obs_data_save_json_safe:走 os_quick_write_utf8_file_safe,覆盖旧文件前先按 backup_ext 备份,防止写坏配置文件;源码中还额外提供了文档未列出的 obs_data_save_json_pretty_safe(缩进美化 + 安全写,libobs/obs-data.c)。
void obs_data_apply(obs_data_t *target, obs_data_t *apply_data);

apply_data 合并进 target。实现(libobs/obs-data.c)遍历 apply_data 的哈希表逐项 copy_item;对同一对象(target == apply_data)直接返回以避免自引用死锁。copy_item 对 OBJECT/ARRAY 类型会深拷贝子树,即 apply 是深合并而非指针赋值。

void obs_data_erase(obs_data_t *data, const char *name);  // 删除名为 name 的 user 数据项
void obs_data_clear(obs_data_t *data);                    // 清空全部 user 数据

注意 obs_data_clear 的实现是 clear_itemlibobs/obs-data.c):它只重置每个 item 的 user 值部分(释放嵌套对象/数组的引用),而保留 default/autoselect 值与 item 本身——与 obs_data_erase(整个 item 从哈希表摘除)语义不同,插件中“恢复默认”应使用 clear 而不是 erase。

三、Set / Get 函数:六种标量与容器类型

文档 “Set Functions” 与 “Get Functions” 两组 API 构成配置读写的基本面:

// Set
void obs_data_set_string(obs_data_t *data, const char *name, const char *val);
void obs_data_set_int(obs_data_t *data, const char *name, long long val);
void obs_data_set_double(obs_data_t *data, const char *name, double val);
void obs_data_set_bool(obs_data_t *data, const char *name, bool val);
void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj);
void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array);

// Get
const char      *obs_data_get_string(obs_data_t *data, const char *name);
long long        obs_data_get_int(obs_data_t *data, const char *name);
double           obs_data_get_double(obs_data_t *data, const char *name);
bool             obs_data_get_bool(obs_data_t *data, const char *name);
obs_data_t      *obs_data_get_obj(obs_data_t *data, const char *name);   // 返回已加引用的引用
obs_data_array_t*obs_data_get_array(obs_data_t *data, const char *name); // 返回已加引用的引用

实现要点(libobs/obs-data.c):

  • 内部统一的 set_item_t 函数指针机制让 user/default/autoselect 三层共享同一套写入逻辑;
  • obs_data_set_stringNULL 值写入空串 ""(不会存 NULL);
  • 整数与双精度共用 OBS_DATA_NUMBER 类型,用 struct obs_data_number{type, union{int_val, double_val}})区分存储;
  • obs_data_get_obj / obs_data_get_array 返回的是“增量引用”(incremented reference)——文档与源码都强调必须分别用 obs_data_release() / obs_data_array_release() 释放,这是插件开发中最常见的泄漏点。

文档还特别标注了一个前端行为细节:

Note: If the data object was generated from a OBS_COMBO_TYPE_EDITABLE property, the property's name will be returned instead of its val.

即由可编辑下拉框(OBS_COMBO_TYPE_EDITABLE)属性生成的设置,obs_data_get_string 返回的是选项显示名(name)而不是内部值(val),插件读取此类设置时要知晓这一点。

3.1 一个最小可用的插件配置读写示例

结合以上语义,一个典型的 source 插件 update 回调处理可以写成:

#include <obs.h>

static void my_source_video_tick(struct obs_source *source)
{
	obs_data_t *settings = obs_source_get_settings(source);

	long long width = obs_data_get_int(settings, "width");        // 未设置时返回 0
	bool   enabled = obs_data_get_bool(settings, "enabled");
	const char *path = obs_data_get_string(settings, "path");

	obs_data_release(settings);   // obs_source_get_settings 需显式释放
}

保存/加载时优先使用 _safe 变体:obs_data_create_from_json_file_safe(path, "bak") 读取、obs_data_save_json_safe(data, path, "temp", "bak") 写入,可获得“临时文件 + 备份”的双重保护。

四、默认值(Default Values):未设置时的取值依据

文档 “Default Value Functions” 一节的定义:Default values are used to determine what value will be given if a value is not set.(默认值用于决定某个值未被设置时应取什么值。)

4.1 API 一览

obs_data_t *obs_data_get_defaults(obs_data_t *data);          // 收集所有默认值(递归含嵌套对象)

void   obs_data_set_default_string(obs_data_t *data, const char *name, const char *val);
const char *obs_data_get_default_string(obs_data_t *data, const char *name);

void   obs_data_set_default_int(obs_data_t *data, const char *name, long long val);
long long obs_data_get_default_int(obs_data_t *data, const char *name);

void   obs_data_set_default_double(obs_data_t *data, const char *name, double val);
double obs_data_get_default_double(obs_data_t *data, const char *name);

void   obs_data_set_default_bool(obs_data_t *data, const char *name, bool val);
bool   obs_data_get_default_bool(obs_data_t *data, const char *name);

void   obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj);
obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name);   // 需 release

void   obs_data_set_default_array(obs_data_t *data, const char *name, obs_data_array_t *arr);
obs_data_array_t *obs_data_get_default_array(obs_data_t *data, const char *name);

4.2 源码中的递归收集逻辑

obs_data_get_defaults 的实现(libobs/obs-data.c)值得细读:它遍历所有 item,按类型分发——

  • STRING / NUMBER / BOOLEAN:读取 default 值写入新对象;
  • OBJECT:递归调用自身处理子对象(obs_data_get_defaults(val));
  • ARRAY:通过回调 get_defaults_array_cb 逐个递归收集数组元素的默认值并 push_back 到新数组。

这与文档“all default values (recursively for all objects as well)”的描述一致。

4.3 默认值在序列化中的体现

obs_data_get_json_with_defaults / obs_data_get_json_pretty_with_defaultsobs_data_to_json(data, with_defaults = true)不再跳过没有 user value 的项libobs/obs-data.c),因此导出的 JSON 会包含全部默认值——常用于“导出完整配置文件”或与用户当前值做差异比对。

另外,头文件中与默认值配套的状态检查/清除函数(文档未单列、但属于同一数据模型):

bool obs_data_has_user_value(obs_data_t *data, const char *name);
bool obs_data_has_default_value(obs_data_t *data, const char *name);
void obs_data_unset_user_value(obs_data_t *data, const char *name);
void obs_data_unset_default_value(obs_data_t *data, const char *name);

(见 libobs/obs-data.h

五、Autoselect 值:应用层的“强制修正”通道(已弃用)

文档 “Autoselect Functions” 的定义是:Autoselect values are optionally used to determine what values should be used to ensure functionality if the currently set values are inappropriate or invalid.(当当前设置的值不恰当或无效时,可选地用于确定应使用什么值以保证功能可用。)

对应 API 与 default 完全对称:obs_data_set/get_autoselect_string/int/double/bool/obj/array,其中 obs_data_set_autoselect_array 标注 versionadded:: 30.1

需要特别注意的事实:在 libobs/obs-data.h 中,全部 autoselect setter/getter 均带有 OBS_DEPRECATED 标记,头文件注释说明其用途是 "Application overrides — Use these to communicate the actual values of settings in case the user settings aren't appropriate"。即:旧版 OBS 曾用 autoselect 值在“用户设置无效时”向插件通报应用实际采用的值,而当前代码库已将其标记为弃用接口。新插件开发不应依赖该通道,可参考 obs_data_has_autoselect_value 同样被标记弃用(libobs/obs-data.h)这一事实确认其整体退出趋势。文档中保留了这一节,是因为序列化兼容性与旧插件仍在引用这些符号。

六、Array Functions:数据数组的引用计数操作

obs_data_array_t *obs_data_array_create();
void   obs_data_array_addref(obs_data_array_t *array);
void   obs_data_array_release(obs_data_array_t *array);

size_t obs_data_array_count(obs_data_array_t *array);
obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx);  // 返回已加引用的引用
size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj);
void   obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj);
void   obs_data_array_erase(obs_data_array_t *array, size_t idx);

源码(libobs/obs-data.c)验证了以下语义:

  • 数组持有元素的引用push_back / insert 会对 obj 执行 os_atomic_inc_long(&obj->ref)erase 会对被移除元素 obs_data_release;数组 destroy 时逐个释放所有元素。因此把同一个 obs_data_t 挂进数组后,原句柄仍可独立释放;
  • obs_data_array_item 越界安全:索引越界返回 NULL,有效项返回前加引用,调用方必须 obs_data_release
  • push_back 返回下标size_t),非法参数返回 0;
  • 头文件还额外提供 obs_data_array_push_back_array(批量拼接另一数组)与 obs_data_array_enum(回调遍历),后者被 obs_data_get_defaults 内部使用(libobs/obs-data.h)。

JSON 解析侧与之呼应:obs_data_add_json_arraylibobs/obs-data.c)在从 JSON 构建数组时只接受 object 类型的数组元素json_is_object 过滤),标量元素会被静默跳过。

七、典型工作流:从 JSON 文件到插件设置

把各节串起来,OBS 插件中处理设置的标准生命周期是:

  1. 加载obs_data_create_from_json_file_safe(file, "bak") —— 损坏时自动回退备份(libobs/obs-data.c);
  2. 读取obs_data_get_int/bool/string/obj 读取 user 值;需要兜底时用 obs_data_get_default_* 或自行判断 obs_data_has_user_value
  3. 修改obs_data_set_* 写 user 值;obs_data_unset_user_value 恢复为“未设置”;obs_data_apply 合并补丁数据;
  4. 序列化/保存obs_data_get_json_pretty() 得到缓存的 JSON 文本(无需 free);obs_data_save_json_safe(data, file, "temp", "bak") 原子落盘并备份旧文件;
  5. 释放:所有 get_obj/get_array/array_item 返回的增量引用、以及 create/create_from_json* 的根引用,最终都要配对 obs_data_release / obs_data_array_release

八、参考文件

内容 路径
API 参考文档(本文核心) reference-settings.rst
公共头文件(全部声明 + 弃用标记) libobs/obs-data.h
实现(结构、JSON 互转、apply/erase/clear、数组) libobs/obs-data.c
C++ 封装(obs_data 智能引用) libobs/obs.hpp
属性系统(settings 的 UI 来源) reference-properties.rstlibobs/obs-properties.h
配置场景/源对象的文档 reference-scenes.rstreference-sources.rst

以上 API 以当前仓库 libobs/obs-data.h 的实际声明为准;使用前提链接 libobs(CMake 构建中由 libobs/CMakeLists.txt 组织),且注意 autoselect 一族接口在当前代码库中已被标记弃用,新代码应避免使用。

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

项目优选

收起
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