文章

探索 OBS 开发(3):使用 libobs 进行开发

系统介绍 OBS 开发相关的基础技术。

探索 OBS 开发(3):使用 libobs 进行开发

想要学习和提升音视频技术的朋友,快来加入我们的【音视频技术社群】,加入后你就能:

  • 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() 加载所有内容。

信号

核心以及场景和源都有一组标准信号,用于确定何时发生事件或更改。

通常最重要的信号是输出信号:尤其是 startstopstartingstoppingreconnectreconnect_success 信号。

如果只有你控制它们的状态,大多数其他场景 / 源的信号是可选的。不过,尽可能监视大多数信号以保持一致性通常是个好主意。有关更多信息,请参阅常见源信号和场景信号。

例如,假设要将回调连接到输出的 stop 信号。stop 信号有两个参数:outputcode。此信号的回调通常如下所示:

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 领域的最新技术和产品信息

微信公众号 微信扫码关注我们

本文由作者按照 CC BY-NC-ND 4.0 进行授权