【OBS】OBS Studio后端设计

🛫 导读

概述

OBS Studio 后端由库 libobs 提供支持。Libobs 提供了主管线( main pipeline)、视频/音频子系统和为所有插件提供支持的通用框架

开发环境

版本号描述
文章日期2024-01-11
操作系统Win10 - 22H222621.2715
OBS27.2.4生成的文档显示的是250.0的文档

1️⃣ 插件对象

Libobs 被设计为模块化的,其中添加模块将添加自定义功能。您可以为四个 libobs 对象创建插件:

Sources-源

源用于在流(stream)中渲染视频或音频。
捕获显示器/游戏/音频、播放视频、显示图像或播放音频等内容都是源。
源还可用于实现音频和视频过滤器filters

Outputs-输出

输出允许输出当前呈现的音频/视频。
直播录制是输出的两个常见示例,但不是唯一的输出类型。
输出可以接收原始数据或接收编码后的数据

Encoders-编码器

编码器是特定于 OBS 的视频/音频编码器实现,用于使用编码器的输出。
编码器实现包含:x264、NVENC、Quicksync等。

Services-服务

服务是流式处理服务(streaming services)的自定义实现,用于流式处理的输出。(可以理解是Outputs的后续操作)
例如,您可以有一个用于流式传输到 Twitch 的自定义实现,以及另一个用于 YouTube 的自定义实现,以允许登录并使用其 API 执行诸如获取 RTMP 服务器或控制频道之类的操作。

2️⃣ 线程

三个主要线程

libobs 在初始化时会生成三个主要线程:

  • libobs/obs-video.c 中专门用于渲染的 obs_graphics_thread 函数
  • libobs/media-io/video-io.c 中专门用于视频编码/输出的 video_thread 函数
  • libobs/media-io/audio-io.c 中用于所有音频处理/编码/输出的 audio_thread 函数

(作者注:obs_graphics_thread原名obs_video_thread;在撰写本文时已更名,以防止与video_thread混淆)

其它线程

  • 快捷键处理线程 obs_hotkey_thread
  • GPU编码线程 gpu_encode_thread
  • 推流掉线重连线程 reconnect_thread
  • 结束推流线程 end_data_capture_thread

3️⃣ 输出通道Channels

渲染视频或音频从输出通道开始。
您可以通过 obs_set_output_source(uint32_t channel, obs_source_t *source) 函数将源分配给输出通道。channel 参数可以是 0到MAX_CHANNELS-1 之间的任意数字。您最初可能认为这是一次显示多个源的方式;但是,源是分层的。场景或过渡等源可以有多个子源,而这些子源又可以有子源,依此类推(有关详细信息,请参阅显示源)。

通常,您将使用场景将多个源绘制为一个组,每个源都有特定的转换,因为场景只是另一种类型的源。“通道”设计允许高度复杂的视频演示设置。OBS Studio 前端甚至尚未充分利用这种后端设计进行渲染,目前一次仅使用一个输出通道来渲染一个场景。但是,它确实利用其他通道来执行在音频设置中设置的全局音频源等内容。

(作者注:“输出通道”不应与输出对象或音频通道混淆。输出通道用于设置要输出的源,输出对象用于实际流式传输/录制/等)

4️⃣ 视频管线(Video Pipeline)概述

来一张网图吧,算是比较好理解的了,部分数据结构可能跟版本会有所变化。
在这里插入图片描述

视频图形管道从两个线程运行:

  • 一个用于呈现预览显示和最终混音的专用图形线程(libobs/obs-video.c 中的 obs_graphics_thread 函数),
  • 一个特定于视频编码/输出的专用线程(libobs/media-io/video-io.c 中的 video_thread 函数)。

源将被绘制到编号为0到MAX_CHANNELS-1的输出通道。它们被绘制到最终纹理( final texture)上(纹理被用于输出)。
绘制完所有源后,最终纹理将转换为 libobs 设置为的任何格式(通常为 YUV 格式)。
转换为后端视频格式后,它将连同其时间戳一起发送到当前视频处理程序 obs_core_video::video

然后,它将该原始帧放入视频输出处理程序的MAX_CACHE_SIZE队列中。
发布信号量后,video_thread线程将尽可能地处理帧。如果视频帧队列已满,它将复制队列中的最后一帧,以尝试降低视频编码的复杂性(从而降低CPU使用率)。这就是为什么当编码器跟不上时,您可能会看到跳帧的原因。
当处于活动状态的时候(开始录制或直播的情况下),帧被发送到任何原始输出或视频编码器。

如果将其发送到视频编码器对象 (libobs/obs-encoder.c),则它会对帧进行编码,并将编码的数据包发送到编码器连接到的输出(可以是多个)。如果输出同时采用编码的视频/音频,则将数据包置于交错队列中,以确保编码的数据包以单调时间戳顺序发送。

最后,将编码的数据包或原始帧发送到输出。

5️⃣ 音频管线(Audio Pipeline)概述

音频管道从音频处理程序中的专用音频线程运行(libobs/media-io/audio-io.c 中的 audio_thread 函数);假设 AUDIO_OUTPUT_FRAMES 设置为 1024,则音频线程每 1024 个音频样本“滴答”(处理音频数据)一次(大约每 21 毫秒间隔一次),并调用 libobs/obs-audio.c 中的 audio_callback 函数,其中大部分音频处理都已完成。

带有音频的源将通过 obs_source_output_audio 函数输出其音频,并且该音频数据将被附加或插入到循环缓冲区 obs_source::audio_input_buf 中。
如果采样率或通道数与后端设置的频率不匹配,则通过 swresample自动重新混音/重新采样音频。在插入之前,音频数据还会通过附加到源的任何音频过滤器(audio filters)运行。

每次音频循环(tick)时,音频线程都会获取音频源树的引用快照(存储输出/处理音频的所有源的引用)。
在每个音频叶(音频源)上,它采用存储在循环缓冲区 obs_source::audio_input_buf 中的最接近的音频(相对于当前音频线程时间戳),并将其放入 obs_source::audio_output_buf 中。

然后,存储在叶子的obs_source::audio_output_buf中的音频样本通过源树快照中的父级发送,以便在层次结构中的每个源节点进行混合或处理。
具有多个子项的源(例如场景或过渡)将通过 obs_source_info::audio_render 回调自行混合/处理其子项的音频。例如,当转换在两个源之间转换时,这允许转换淡入一个源的音频,并在新源的音频中淡入。
然后,混音或处理后的音频数据以类似方式存储在该节点的 obs_source::audio_output_buf 中,并重复该过程,直到音频到达树的根节点。

最后,当音频到达快照树的底部时,每个输出通道中所有源的音频将混合在一起以进行最终混音。然后,最终混音将发送到当前处于活动状态的任何原始输出或音频编码器。

如果将其发送到音频编码器对象 (libobs/obs-encoder.c),则它会对音频数据进行编码,并将编码的数据包发送到编码器连接到的输出(可以是多个)。如果输出同时采用编码的视频/音频,则会将数据包置于交错队列中,以确保以单调时间戳顺序发送编码的数据包。

然后,将编码的数据包或原始音频数据发送到输出。

📖 参考资料

  • 22
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜猫逐梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值