探索 OBS 开发(3):使用 libobs 进行开发
系统介绍 OBS 开发相关的基础技术。
想要学习和提升音视频技术的朋友,快来加入我们的【音视频技术社群】,加入后你就能:
- 1)下载 30+ 个开箱即用的「音视频及渲染 Demo 源代码」
- 2)下载包含 500+ 知识条目的完整版「音视频知识图谱」
- 3)下载包含 200+ 题目的完整版「音视频面试题集锦」
- 4)技术和职业发展咨询 100% 得到回答
- 5)获得简历优化建议和大厂内推
现在加入,送你一张 20 元优惠券:点击领取优惠券
初始化与关闭
要初始化 libobs,必须调用 obs_startup()
、obs_reset_video()
,然后调用 obs_reset_audio()
。之后通常应该加载模块。
可以通过调用 obs_open_module()
手动加载各个模块。调用 obs_open_module()
函数后,还必须调用 obs_init_module()
来初始化该模块。
也可以通过两个函数自动加载模块:obs_add_module_path()
和 obs_load_all_modules()
。
在加载所有插件模块后,调用 obs_post_load_modules()
。
某些模块可能会使用配置存储目录,该目录作为 obs_startup()
的参数设置。
关闭前端时,确保释放所有对象引用,释放所有数据,然后调用 obs_shutdown()
。如果由于某种原因未释放任何 libobs 对象,它们将被自动销毁,并记录警告信息。
要检查是否有一般内存分配未释放,调用 bnum_allocs()
获取剩余分配次数。如果剩余次数大于 0,则存在内存泄漏。
有关更多信息,请参阅初始化、关闭和信息。
重新配置视频
初始化后,只要没有活动输出,随时可以通过调用 obs_reset_video()
重新配置视频设置。音频最初也打算具有此功能,但由于目前无法在初始化后重置音频;必须完全关闭 libobs 才能重新配置音频设置。
显示
顾名思义,显示用于显示 / 预览窗格。要使用显示,必须具有用于绘制的原生窗口句柄或标识符。
首先必须调用 obs_display_create()
初始化显示,然后必须使用 obs_display_add_draw_callback()
分配绘制回调。如果需要移除绘制回调,同样调用 obs_display_remove_draw_callback()
。
绘制时,要绘制主预览窗口(如果有),调用 obs_render_main_texture()
。如果需要在辅助显示上渲染特定源,可以在其在辅助显示上显示时使用 obs_source_inc_showing()
增加其 “显示” 状态,在绘制回调中使用 obs_source_video_render()
绘制它,然后当它不再在辅助显示上显示时,调用 obs_source_dec_showing()
。
如果需要调整显示大小,调用 obs_display_resize()
。
如果显示需要设置非黑色的自定义背景色,调用 obs_display_set_background_color()
。
如果需要临时禁用显示,调用 obs_display_set_enabled()
禁用,使用 obs_display_enabled()
获取其启用 / 禁用状态。
不再需要显示时调用 obs_display_destroy()
销毁显示。
(重要提示:在同一个基础窗口的层次结构中不要使用多个显示控件,这将导致在 macOS 上出现呈现停滞。)
有关如何使用 Qt 使用显示的示例,请参阅 UI/qt-display.hpp 和 UI/qt-display.cpp。
有关更多信息,请参阅显示。
保存 / 加载对象和对象管理
前端通常需要管理自己的对象,但对于源,有一些辅助函数可以更轻松地保存 / 加载所有源:obs_save_sources()
和 obs_load_sources()
。使用这些函数后,所有非私有源将自动保存和加载。也可以使用 obs_save_source()
和 obs_load_source()
手动保存 / 加载单个源。
(作者注:我不应该编写这些辅助函数;缺点是我不得不添加无法通过 obs_source_create_private()
函数保存的 “私有” 源。这只是长期开发过程中可能出现的众多小设计缺陷之一。)
对于输出、编码器和服务,没有辅助函数,因此通常需要分别获取它们的设置并以 json 格式保存。(参见 obs_output_get_settings()
)。不必将每个对象分别保存到不同文件;可以将多个对象保存到一个更大的 obs_data_t
对象中,然后通过 obs_data_save_json_safe()
保存,再通过 obs_data_create_from_json_file_safe()
加载所有内容。
信号
核心以及场景和源都有一组标准信号,用于确定何时发生事件或更改。
通常最重要的信号是输出信号:尤其是 start、stop、starting、stopping、reconnect、reconnect_success 信号。
如果只有你控制它们的状态,大多数其他场景 / 源的信号是可选的。不过,尽可能监视大多数信号以保持一致性通常是个好主意。有关更多信息,请参阅常见源信号和场景信号。
例如,假设要将回调连接到输出的 stop 信号。stop 信号有两个参数:output 和 code。此信号的回调通常如下所示:
1
2
3
4
5
6
7
static void output_stopped(void *my_data, calldata_t *cd)
{
obs_output_t *output = calldata_ptr(cd, "output");
int code = calldata_int(cd, "code");
[...]
}
(注意回调不是线程安全的。)
然后使用 signal_handler_connect()
将其连接到 stop 信号。例如:
1
2
signal_handler_t *handler = obs_output_get_signal_handler(output);
signal_handler_connect(handler, "stop", output_stopped);
显示源
源通过输出通道使用 obs_set_output_source()
函数在流 / 录制上显示。有 64 个通道可以分配给源,它们将按升序索引顺序绘制。通常,不应直接使用此函数分配普通源;应使用包含场景的过渡。
要以特定变换绘制一个或多个源,使用场景。要创建场景,调用 obs_scene_create()
。子源使用场景项引用,并对这些场景项应用特定变换。场景项不是源,而是源的容器;同一源可以在同一场景中被多个场景项引用,也可以被多个场景引用。要创建引用源的场景项,调用 obs_scene_add()
,它将返回对新场景项的引用。
要更改场景项的变换,通常调用 obs_sceneitem_set_pos()
更改其位置,obs_sceneitem_set_rot()
更改其旋转,或 obs_sceneitem_set_scale()
更改其缩放。场景项还可以强制缩放到自定义大小约束,称为 “边界框”;边界框将强制源在特定大小和缩放约束内绘制。要使用边界框,调用 obs_sceneitem_set_bounds_type()
、obs_sceneitem_set_bounds()
和 obs_sceneitem_set_bounds_alignment()
。不过,处理与变换相关的所有内容最简单的方法是使用 obs_sceneitem_set_info()
和 obs_sceneitem_get_info()
函数。有关与场景项相关的所有函数,请参阅场景项函数。
通常,需要在多个场景之间进行平滑过渡。为此,使用过渡。要创建过渡,使用 obs_source_create()
或 obs_source_create_private()
,就像创建其他源一样。然后,要激活过渡,调用 obs_transition_start()
。当过渡未激活且仅显示一个源时,它将直接传递到当前显示的源。有关使用过渡的更多函数,请参阅过渡。
推荐的结构设置方式是将过渡作为主输出源,场景作为过渡的子项,源作为场景中的子项。需要切换到新场景时,只需调用 obs_transition_start()
。
输出、编码器和服务
输出、编码器和服务一起使用,并且管理方式与源略有不同。目前没有全局函数来保存 / 加载它们,目前必须通过它们的设置手动完成。
编码器用于期望编码数据的输出(几乎所有典型输出),如标准文件录制或流媒体。
服务用于流媒体输出;RTMP 输出是此方面的典型示例。
以下是输出如何与编码器和服务一起使用的示例:
1
2
3
4
5
6
obs_encoder_set_video(my_h264_encoder, obs_get_video());
obs_encoder_set_audio(my_aac_encoder, obs_get_audio());
obs_output_set_video_encoder(my_output, my_h264_encoder);
obs_output_set_audio_encoder(my_output, my_aac_encoder);
obs_output_set_service(my_output, my_service); /* 如果是流媒体 */
obs_output_start(my_output);
一旦输出成功启动,它将自动从当前视频 / 音频输出(即分配给输出通道的任何源)捕获视频和 / 或音频。
如果输出启动失败,它将发送 stop 信号,其中 code 参数包含错误代码,可能还附带可通过 obs_output_get_last_error()
函数获取的翻译错误消息。
本文转自微信公众号
关键帧Keyframe
,推荐您关注来获取音视频、AI 领域的最新技术和产品信息: