▒ 目录 ▒
🛫 导读
概述
OBS Studio 后端由库
libobs
提供支持。Libobs 提供了主管线
( main pipeline)、视频/音频子系统
和为所有插件提供支持的通用框架
。
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2024-01-11 | |
操作系统 | Win10 - 22H2 | 22621.2715 |
OBS | 27.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),则它会对音频数据进行编码,并将编码的数据包发送到编码器连接到的输出(可以是多个)。如果输出同时采用编码的视频/音频,则会将数据包置于交错队列中,以确保以单调时间戳顺序发送编码的数据包。
然后,将编码的数据包或原始音频数据发送到输出。
📖 参考资料
- OBS源码分析(八)分析libobs几个主要的线程 https://blog.csdn.net/qq_33588386/article/details/112556804