A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】08 / 56
■ 应用:AirCast(from OBS Studio)(AirCast)
■ 定位:OBS Studio 移植,录屏与直播推流
■ 上游:OBS Studio | 路线:Qt6 6.11.1+QPA | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-OBS-Studio
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614378127773638
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-OBS-Studio
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirCast
OBS Studio 是开源直播与录制领域的事实标准,围绕 libobs 核心引擎、插件体系和 Qt 前端构建,覆盖屏幕采集、窗口采集、编码推流、场景合成与混音等完整工作流。它的价值在于把"采集—合成—编码—输出"这条最重的桌面媒体管线做成了可扩展的框架,从个人录课到专业直播都依赖它。将这样一条管线带到鸿蒙 PC,意味着系统首次拥有原生形态的录播工具链,而不只是一个个孤立的单点功能。
对鸿蒙 PC 生态而言,OBS Studio 的适配还是一次高强度的工程检验。它同时压上了 Qt 6 Widgets 前端、OpenGL 3.3 渲染、音视频采集、多线程编码、RTMP 网络输出等几乎所有桌面应用的难点,任何一环的处理经验都能反哺后续同类移植。工程选择 obs-studio master(31.x frontend 架构,Qt6 Widgets)为上游,遵循 GPL-2.0-or-later 随上游传染。
当前鸿蒙工程的应用名为 AirCast,包名 com.aircast.app,版本 1.2.0,支持 phone、tablet 与 2in1 三种设备形态,目标 Native ABI 为 arm64-v8a,声明了 INTERNET、MICROPHONE 与 KEEP_BACKGROUND_RUNNING 权限,并以 audioRecording 后台模式支撑后台录制。测试与验证设备为 HUAWEI MateBook Pro(2in1),SDK 基于 HarmonyOS 6.1.0(API 23)+,实际使用 SDK 24 头文件与库构建。
▍二、先划清边界:不把编译通过当成适配完成
OBS Studio 的桌面形态是一个独立的 Qt 可执行进程:obs 主程序链接 libobs、libobs-opengl 与插件集,通过 X11/Windows QPA 创建窗口,通过 DXGI/X11 采集屏幕,经 WASAPI/PulseAudio 取音频。这些平台层在 Linux 构建中深度绑定 X11、PulseAudio 与桌面 DBus 生态,直接交叉编译只能得到一堆无法初始化的符号。
AirCast 的路线是把前端整体改成共享库,再由鸿蒙侧的宿主拉起:frontend 以 SHARED 方式编为 libobs_frontend.so,main 改名为 obscast_main,由 native 宿主在 dlopen 拓扑中加载。窗口交给 ArkTS XComponent 与 ohos QPA 插件,渲染走 libGLv4 桌面 GL 兼容层直连,采集与音频以新的 ohos-capture/ohos-audio 插件接入系统能力。两层之间的边界在工程文档中登记为明确的补丁清单与风险登记册。
层次 原项目实现(或传统桌面方案) 鸿蒙侧当前方案
应用入口 main 可执行进程(obs 二进制) Stage 模型 EntryAbility + obscast_main 库入口
窗口承载 X11/Windows QPA 顶层窗口 XComponent OHNativeWindow + libqohos QPA
前端形态 obs 前端静态链入可执行文件 libobs_frontend.so(GUI→SHARED 库范式)
渲染 桌面 GL 3.3(GLX/WGL) libGLv4.so 直连 + GLES3 化 effect + pbuffer 离屏
屏幕采集 DXGI/X11 Composite ohos-capture 插件(AVScreenCapture 系统授权)
音频输入 WASAPI / PulseAudio ohos-audio 插件(OHAudio capturer)
菜单系统 QMenuBar 原生渲染 OHOSMenuBar 快照 → ArkTS 顶栏接管与回注
数据文件 安装目录 data/ rawfile obs-data.tar 首启解包至沙箱
这条边界意味着:当前产物是可安装、可启动、可完成屏幕录制与 RTMP 推流的 OBS 工程化版本,而不是对桌面 OBS 每一个角落的原样复刻;多视图、投影仪等依赖独立顶层窗口的能力按平台裁剪,Simple 录制模式因依赖 helper 进程暂不可用。
▍三、鸿蒙版的整体架构
工程采用 Air 双层架构:ArkTS 壳负责 Ability 生命周期、权限与系统交互;NAPI 薄桥 libcastbridge.so 只链接系统库,负责输入注入、TSFN 文件选择器、IME 与光标桥接;宿主 libobscst_core.so 建立环境前置与 dlopen 拓扑,按 Qt 栈、依赖、QPA、libobs、libobs-opengl、frontend-api、frontend 的顺序拉起整个 OBS 世界。
text
EntryAbility(权限 / 窗口 / 生命周期)
├── Index.ets:XComponent SURFACE + 顶栏(菜单快照渲染、bgkeep 后台任务)
├── FileManager:DocumentViewPicker ⇄ QPA filedialoghelper 事件桥
└── DialogHost / DialogPage + SubWindowManager:对话框浮层与真子窗双模式
libcastbridge.so(NAPI 薄桥:输入注入 / TSFN 选择器 / IME / 光标 / 数据部署门控)
└── libobscst_core.so(宿主:环境前置 + gl4es 防护 env + dlopen 拓扑
Qt → deps → QPA → libobs → libobs-opengl → frontend-api → frontend
+ tar 解包 + 崩溃取证 + HTTP 触发通道 :8099)
└── libobscast-frontend.so(obscast_main → run_program → OBSApp(OBSBasic))
├── libobs 核心 + 11 插件(obs-plugins/ 目录 dlopen)
├── libobs-opengl:GLES3 pbuffer 离屏(gl-ohos-egl)
└── libqohos.so QPA:Qt UI raster blit
主要目录如下:
text
AirCast/
├── AppScope/app.json5 # 包名、版本与应用配置
├── PORTING.md # 依赖矩阵、补丁清单 P1-P54、架构、风险登记册
├── README.md # 状态总览、构建部署、权限声明
├── entry/src/main/
│ ├── ets/
│ │ ├── entryability/EntryAbility.ets # 权限申请、窗口与后台任务
│ │ ├── pages/Index.ets # 主表面 XComponent、菜单快照顶栏
│ │ ├── pages/DialogPage.ets # 自由多窗形态的真子窗对话框页
│ │ ├── components/DialogHost.ets # 非自由多窗形态的浮层卡片
│ │ └── utils/FileManager.ets # 系统文件选择器中转
│ └── cpp/
│ ├── napi_init.cpp # 录制/推流/场景/IME NAPI 导出
│ ├── obscst_core.cpp # 宿主:dlopen 拓扑 + HTTP 通道
│ └── qt_platform/ohos_qt6/ # libqohos QPA 插件(AirCad 同源)
└── native/
├── env.sh # 交叉编译环境
├── sources/obs-studio/ # 鸿蒙化工作树(PATCH-xx(AirCast) 就地标记)
├── sources/ # curl/jansson/mbedtls/nlohmann/speexdsp/simde 源码
├── scripts/ # build_deps.sh / build_obs.sh / deploy.sh
├── seed/ # 首启种子配置
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. GUI→共享库范式与 dlopen 拓扑
frontend 在 OHOS 下改为 SHARED 库 libobs_frontend.so,入口函数改名为 obscast_main,这是 Air 系列已验证的 GUI→库范式。宿主按依赖顺序 dlopen 整个拓扑:Qt 栈先于依赖,依赖先于 libobs,最后才是 frontend。musl 环境对已加载库不做 RTLD_GLOBAL 提升,frontend 一度无法经 RTLD_DEFAULT 找到 core 符号,工程改为反转注册——由 core 把函数指针注入 frontend,绕开符号解析的缺陷。所有对外 NAPI 通道(录制、推流、场景切换、IME 提交)都以 extern "C" 显式导出,避免 C++ 修饰名让 dlsym 静默失败。
### 2. 渲染层:libobs-opengl 的 GLES3 化
libobs-opengl 原本面向桌面 GL 3.3。工程新增 gl-ohos-egl.c EGL 平台 vtable,以 EGL_DEFAULT_DISPLAY 绑定 libGLv4,交换链使用 pbuffer 离屏,device_present 改为 glReadPixels 读回后经预览回调上屏。effect 着色器全部完成 ES3 化:跳过 ES3 不存在的 GL_TEXTURE_CUBE_MAP_SEAMLESS,sRGB 帧缓冲能力位改为设备侧状态记录,避免桌面枚举每帧触发 INVALID_ENUM 连坐打断纹理与视口链。编码帧冻结的根因最终定位在阶段 surf 的读回方式——每帧 create/delete FBO 会让 GPU 的 PBO 读回数秒后静默停写,改为持久读 FBO 后录制画面恢复稳定。
### 3. 采集与音频:两个新插件
plugins/ohos-capture 基于 AVScreenCapture 实现,屏幕录制走系统授权弹窗,不需要受限权限;buffer 回调中 NV12/RGBA 双路径按设备能力自动回退,回调末尾必须归还 buffer,否则画面冻结在首帧。窗口采集源 ohos_window_capture 把"选窗"从会话启动自动弹窗改为属性面板显式动作,选前零采集零弹窗;动态换窗必须 Release→Create→Init 整体重建,同一对象 Stop→Start 复用会遭遇数据回调不再投递的帧饿死。plugins/ohos-audio 用 OHAudio capturer 把麦克风输入送进 obs_source_output_audio,麦克风权限按需经插件到 ArkTS 的 UI 请求桥弹出,空场景启动零弹窗。
### 4. 菜单快照与对话框子窗
QMenuBar 被隐藏,OHOSMenuBar 以 QTimer 每秒导出菜单快照 JSON(serial 仅随内容变化前进),ArkTS 顶栏据此渲染并经 QueuedConnection 把 trigger 回注 GUI 线程。对话框则按设备能力双模式分流:自由多窗形态用 createSubWindowWithOptions 创建真子窗(DialogPage),否则在 Index 内以 DialogHost 浮层卡片呈现;标题栏统一由 ArkTS 自绘。分离呈现的对话框拥有独立 XComponent surfaceId,QPA 为其建立独立 EGLSurface;输入按 XComponent id 定向注入。2in1 上"对话框定格首帧"的终案是一行根修——swap 前未 makeCurrent 对话框表面,OHOS EGL 对未绑定 current context 的 window surface 静默丢弃每一帧提交。
### 5. 数据部署与打包
打包遵循五件套:nativeLib.strip 关闭保护预编译 so、免 compressNativeLibs、NEEDED 去版本号、.so 经 entry/libs 通道随 HAP 分发、运行期按拓扑 dlopen。OBS 的 effect 着色器、zh-CN locale 与 Yami 主题打包为 rawfile obs-data.tar,首启解包到 files/obs-data 并以 stamp 协议防重复;配置侧设置 HOME=files/home,OBS 按桌面习惯写入 ~/.config/obs-studio。签名产物约 99MB,README 状态总览记载 11 个插件在真机全部加载。
▍五、适配中遇到的几个难点
### 难点一:musl 的动态链接语义与桌面 Linux 不同
三类问题同源:musl 不做 RTLD_GLOBAL 提升,插件深处经 libobs dlopen 链无法用 RTLD_DEFAULT 找到桥符号,工程以 /proc/self/maps 定位 libcastbridge.so 后 NOLOAD dlopen 再 dlsym;预编译依赖带版本化 NEEDED,鸿蒙环境禁用,部署期统一 elf_edit 去版本;NAPI 桥函数漏写 extern "C" 导致导出为 C++ 修饰名,插件按 C 名查找恒 NULL 且不报错,权限请求桥静默失效后才被 nm -D 实锤。这类问题的共同特征是"不崩溃、只失效",必须靠符号表取证。
### 难点二:ES3 与桌面 GL 3.3 的能力位差异
libobs 的渲染逻辑假设桌面 GL 语义:sRGB 能力位可枚举、贴图无缝可用、读回路径廉价。在设备 GLES3 上,每帧枚举不存在的枚举值会让错误队列积压,连坐打断后续纹理创建;预览回调不清理错误队列时,每帧泄漏的 0x500 错误会在开录期打断着色器创建,最终表现为"渲染停摆、采集饿死"。工程的解法是把所有能力差异改成设备侧状态记录,并在每个读回入口加错误屏障。
### 难点三:平板对话框的坐标系失配
设置对话框 981×730 逻辑像素超出平板视口宽度,ArkTS 卡片把表面钳到可用区的 94%×88%,而 QPA 把 Qt 帧拉伸铺满较小表面、注入却按 1:1 直喂——两套坐标系失配后,点击偏移随深度放大,表面高度以下的 Qt 坐标区完全不可达。工程在 QPA 源头把 Qt 窗口钳制到同限额,无法缩窗的 Fixed 宽框改按窗口/表面千分比换算注入,并为"触摸按几何双派发导致弹层 1ms 闪关"补了主链拦截。
### 难点四:触屏输入与 IME 的长链路
Qt 主表面在部分平板输入栈上触摸不可达,自动化只能驱动 ArkTS 层;滚动交互按控件族分家——QScrollArea 用 QScroller 手势,QMenu 与条目视图因自消费按下事件必须改拖动转滚轮且过滤器要 view+viewport 双装。IME 链经六轮断点定位才闭环:分离表面对话框内控件从未获得 Qt 焦点,工程改用"最后按压控件"的触摸语义直连定位提交目标;spinbox 行编辑带 "6000 Kbps" 后缀,光标落在后缀之后会触发 QSpinBox 同步回滚,解法是会话首笔对行编辑 selectAll 复刻桌面整体替换语义。
### 难点五:后台保活与系统约束
纯采集态切后台约 2 秒被系统以 ILLEGAL_AUDIO_CAPTURER_BY_SUSPEND 击杀,录制态则依赖持续任务。工程让采集与音频插件经 UI 桥上报会话计数,ArkTS 侧把 bgkeep(录制)与 bgcap(采集)聚合,任一为真即申请 AUDIO_RECORDING 持续任务;module.json5 补 backgroundModes 与 KEEP_BACKGROUND_RUNNING。另一类约束来自权限模型:缺 INTERNET 权限时 socket 全败,HTTP 触发通道与 RTMP 推流同时瘫痪,这一前置是真机实证后才补上的。
▍六、编译、安装与启动
构建分四步前置再加标准 hvigor 流程,脚本均位于 native/scripts/,按 README 记载顺序执行:
bash
bash native/scripts/build_deps.sh
bash native/scripts/build_obs.sh
(cd entry/src/main/cpp/qt_platform/ohos_qt6 && bash build_libqohos_qt6.sh)
bash native/scripts/deploy.sh
随后进行 ArkTS 与 HAP 构建:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
连接设备后安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.aircast.app
PORTING.md 里程碑记载了这条链路的真机验证结果:屏幕录制全链(AVScreenCapture→ES3 渲染→x264/AAC→MP4 落盘可播放)、RTMP 推流(外部 ffmpeg 收流约 31.6 秒、h264 1080p@30+AAC)、8 个顶级菜单 118 个叶子全枚举与 58 项跨菜单触发零崩溃,以及后台录制两分钟存活与回前台计时续走。
▍七、当前功能边界
已进入当前可运行产物的能力,依据 README 状态总览与里程碑 M5-M12:
- libobs + libobs-opengl(原生 GLES3,全 effect ES3 化)与 11 个插件真机全部加载;
- 编码器 obs_x264、ffmpeg_aac、ffmpeg_opus、pcm_s16le 就绪;
- Qt 前端 libobscast-frontend.so(Yami 主题,zh-CN 汉化);
- 屏幕采集源(AVScreenCapture 原始流 + 系统授权弹窗)与窗口采集源(显式选窗、持续跟随、NV12/RGBA 回退);
- 麦克风源(OHAudio,normal 级权限,按需申请);
- 录制与 RTMP 推流全链,HTTP :8099 触发通道支撑外部自动化直驱 frontend API;
- 菜单 ArkTS 快照接管、系统文件选择器(TSFN 桥 + 沙箱中转)、IME/光标/主窗 ID 桥;
- 后台录制持续任务聚合(bgkeep + bgcap)与真子窗/浮层对话框双模式。
尚未覆盖或按平台裁剪的能力:Simple 录制模式(依赖 helper 进程)、多视图与投影仪(单表面 QPA 无独立顶层窗)、NV12 硬路径(本机拒绝 SURFACE_YUV,已自动回退 RGBA)、系统选窗结果的编程恢复(场景切换或重启后需在属性面板重选)、拖拽排序的触摸转换(有意保留给物理鼠标)、部分平板上 combo 下拉列表的视觉投影(交互链已通,XComponent 消费时序待查)。
因此,当前版本更准确的定位是"OBS Studio 在鸿蒙 PC 上的可工作工程化版本":录制、推流、采集、菜单与输入的主干全部真实可用,剩余边界集中在依赖独立顶层窗口的桌面特性与仍在收敛的平板渲染细节。
▍八、总结
AirCast 的价值不在于"OBS 能启动",而在于它把一条完整的桌面媒体管线逐层拆开、逐层重接:前端改库解决了进程形态,QPA 与 XComponent 解决了窗口承载,libGLv4 直连与 GLES3 化解决了渲染语义,两个新插件解决了采集与音频,快照菜单与定向注入解决了输入与交互。PORTING.md 中 54 项补丁与八项里程碑记录了每一层的真实依据,也积累了 musl 动态链接、EGL 表面纪律、触屏手势分家这类可复用的平台结论。
后续工作的重心已经明确:完成 M6 汉化与截图回归的收尾,跟踪平板对话框弹层的 XComponent 消费时序,把窗口采集在更多设备上的画布跟随策略打磨稳定,同时保持 HTTP 自动化通道作为回归基建。对希望把其他重型 Qt 应用带到鸿蒙 PC 的团队而言,AirCast 的补丁清单与风险登记册本身就是一份可对照的施工图。
■ 应用:AirCast(from OBS Studio)(AirCast)
■ 定位:OBS Studio 移植,录屏与直播推流
■ 上游:OBS Studio | 路线:Qt6 6.11.1+QPA | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-OBS-Studio
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917614378127773638
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-OBS-Studio
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirCast
OBS Studio 是开源直播与录制领域的事实标准,围绕 libobs 核心引擎、插件体系和 Qt 前端构建,覆盖屏幕采集、窗口采集、编码推流、场景合成与混音等完整工作流。它的价值在于把"采集—合成—编码—输出"这条最重的桌面媒体管线做成了可扩展的框架,从个人录课到专业直播都依赖它。将这样一条管线带到鸿蒙 PC,意味着系统首次拥有原生形态的录播工具链,而不只是一个个孤立的单点功能。
对鸿蒙 PC 生态而言,OBS Studio 的适配还是一次高强度的工程检验。它同时压上了 Qt 6 Widgets 前端、OpenGL 3.3 渲染、音视频采集、多线程编码、RTMP 网络输出等几乎所有桌面应用的难点,任何一环的处理经验都能反哺后续同类移植。工程选择 obs-studio master(31.x frontend 架构,Qt6 Widgets)为上游,遵循 GPL-2.0-or-later 随上游传染。
当前鸿蒙工程的应用名为 AirCast,包名 com.aircast.app,版本 1.2.0,支持 phone、tablet 与 2in1 三种设备形态,目标 Native ABI 为 arm64-v8a,声明了 INTERNET、MICROPHONE 与 KEEP_BACKGROUND_RUNNING 权限,并以 audioRecording 后台模式支撑后台录制。测试与验证设备为 HUAWEI MateBook Pro(2in1),SDK 基于 HarmonyOS 6.1.0(API 23)+,实际使用 SDK 24 头文件与库构建。
▍二、先划清边界:不把编译通过当成适配完成
OBS Studio 的桌面形态是一个独立的 Qt 可执行进程:obs 主程序链接 libobs、libobs-opengl 与插件集,通过 X11/Windows QPA 创建窗口,通过 DXGI/X11 采集屏幕,经 WASAPI/PulseAudio 取音频。这些平台层在 Linux 构建中深度绑定 X11、PulseAudio 与桌面 DBus 生态,直接交叉编译只能得到一堆无法初始化的符号。
AirCast 的路线是把前端整体改成共享库,再由鸿蒙侧的宿主拉起:frontend 以 SHARED 方式编为 libobs_frontend.so,main 改名为 obscast_main,由 native 宿主在 dlopen 拓扑中加载。窗口交给 ArkTS XComponent 与 ohos QPA 插件,渲染走 libGLv4 桌面 GL 兼容层直连,采集与音频以新的 ohos-capture/ohos-audio 插件接入系统能力。两层之间的边界在工程文档中登记为明确的补丁清单与风险登记册。
层次 原项目实现(或传统桌面方案) 鸿蒙侧当前方案
应用入口 main 可执行进程(obs 二进制) Stage 模型 EntryAbility + obscast_main 库入口
窗口承载 X11/Windows QPA 顶层窗口 XComponent OHNativeWindow + libqohos QPA
前端形态 obs 前端静态链入可执行文件 libobs_frontend.so(GUI→SHARED 库范式)
渲染 桌面 GL 3.3(GLX/WGL) libGLv4.so 直连 + GLES3 化 effect + pbuffer 离屏
屏幕采集 DXGI/X11 Composite ohos-capture 插件(AVScreenCapture 系统授权)
音频输入 WASAPI / PulseAudio ohos-audio 插件(OHAudio capturer)
菜单系统 QMenuBar 原生渲染 OHOSMenuBar 快照 → ArkTS 顶栏接管与回注
数据文件 安装目录 data/ rawfile obs-data.tar 首启解包至沙箱
这条边界意味着:当前产物是可安装、可启动、可完成屏幕录制与 RTMP 推流的 OBS 工程化版本,而不是对桌面 OBS 每一个角落的原样复刻;多视图、投影仪等依赖独立顶层窗口的能力按平台裁剪,Simple 录制模式因依赖 helper 进程暂不可用。
▍三、鸿蒙版的整体架构
工程采用 Air 双层架构:ArkTS 壳负责 Ability 生命周期、权限与系统交互;NAPI 薄桥 libcastbridge.so 只链接系统库,负责输入注入、TSFN 文件选择器、IME 与光标桥接;宿主 libobscst_core.so 建立环境前置与 dlopen 拓扑,按 Qt 栈、依赖、QPA、libobs、libobs-opengl、frontend-api、frontend 的顺序拉起整个 OBS 世界。
text
EntryAbility(权限 / 窗口 / 生命周期)
├── Index.ets:XComponent SURFACE + 顶栏(菜单快照渲染、bgkeep 后台任务)
├── FileManager:DocumentViewPicker ⇄ QPA filedialoghelper 事件桥
└── DialogHost / DialogPage + SubWindowManager:对话框浮层与真子窗双模式
libcastbridge.so(NAPI 薄桥:输入注入 / TSFN 选择器 / IME / 光标 / 数据部署门控)
└── libobscst_core.so(宿主:环境前置 + gl4es 防护 env + dlopen 拓扑
Qt → deps → QPA → libobs → libobs-opengl → frontend-api → frontend
+ tar 解包 + 崩溃取证 + HTTP 触发通道 :8099)
└── libobscast-frontend.so(obscast_main → run_program → OBSApp(OBSBasic))
├── libobs 核心 + 11 插件(obs-plugins/ 目录 dlopen)
├── libobs-opengl:GLES3 pbuffer 离屏(gl-ohos-egl)
└── libqohos.so QPA:Qt UI raster blit
主要目录如下:
text
AirCast/
├── AppScope/app.json5 # 包名、版本与应用配置
├── PORTING.md # 依赖矩阵、补丁清单 P1-P54、架构、风险登记册
├── README.md # 状态总览、构建部署、权限声明
├── entry/src/main/
│ ├── ets/
│ │ ├── entryability/EntryAbility.ets # 权限申请、窗口与后台任务
│ │ ├── pages/Index.ets # 主表面 XComponent、菜单快照顶栏
│ │ ├── pages/DialogPage.ets # 自由多窗形态的真子窗对话框页
│ │ ├── components/DialogHost.ets # 非自由多窗形态的浮层卡片
│ │ └── utils/FileManager.ets # 系统文件选择器中转
│ └── cpp/
│ ├── napi_init.cpp # 录制/推流/场景/IME NAPI 导出
│ ├── obscst_core.cpp # 宿主:dlopen 拓扑 + HTTP 通道
│ └── qt_platform/ohos_qt6/ # libqohos QPA 插件(AirCad 同源)
└── native/
├── env.sh # 交叉编译环境
├── sources/obs-studio/ # 鸿蒙化工作树(PATCH-xx(AirCast) 就地标记)
├── sources/ # curl/jansson/mbedtls/nlohmann/speexdsp/simde 源码
├── scripts/ # build_deps.sh / build_obs.sh / deploy.sh
├── seed/ # 首启种子配置
└── install/ # 交叉编译安装前缀
▍四、核心适配过程
### 1. GUI→共享库范式与 dlopen 拓扑
frontend 在 OHOS 下改为 SHARED 库 libobs_frontend.so,入口函数改名为 obscast_main,这是 Air 系列已验证的 GUI→库范式。宿主按依赖顺序 dlopen 整个拓扑:Qt 栈先于依赖,依赖先于 libobs,最后才是 frontend。musl 环境对已加载库不做 RTLD_GLOBAL 提升,frontend 一度无法经 RTLD_DEFAULT 找到 core 符号,工程改为反转注册——由 core 把函数指针注入 frontend,绕开符号解析的缺陷。所有对外 NAPI 通道(录制、推流、场景切换、IME 提交)都以 extern "C" 显式导出,避免 C++ 修饰名让 dlsym 静默失败。
### 2. 渲染层:libobs-opengl 的 GLES3 化
libobs-opengl 原本面向桌面 GL 3.3。工程新增 gl-ohos-egl.c EGL 平台 vtable,以 EGL_DEFAULT_DISPLAY 绑定 libGLv4,交换链使用 pbuffer 离屏,device_present 改为 glReadPixels 读回后经预览回调上屏。effect 着色器全部完成 ES3 化:跳过 ES3 不存在的 GL_TEXTURE_CUBE_MAP_SEAMLESS,sRGB 帧缓冲能力位改为设备侧状态记录,避免桌面枚举每帧触发 INVALID_ENUM 连坐打断纹理与视口链。编码帧冻结的根因最终定位在阶段 surf 的读回方式——每帧 create/delete FBO 会让 GPU 的 PBO 读回数秒后静默停写,改为持久读 FBO 后录制画面恢复稳定。
### 3. 采集与音频:两个新插件
plugins/ohos-capture 基于 AVScreenCapture 实现,屏幕录制走系统授权弹窗,不需要受限权限;buffer 回调中 NV12/RGBA 双路径按设备能力自动回退,回调末尾必须归还 buffer,否则画面冻结在首帧。窗口采集源 ohos_window_capture 把"选窗"从会话启动自动弹窗改为属性面板显式动作,选前零采集零弹窗;动态换窗必须 Release→Create→Init 整体重建,同一对象 Stop→Start 复用会遭遇数据回调不再投递的帧饿死。plugins/ohos-audio 用 OHAudio capturer 把麦克风输入送进 obs_source_output_audio,麦克风权限按需经插件到 ArkTS 的 UI 请求桥弹出,空场景启动零弹窗。
### 4. 菜单快照与对话框子窗
QMenuBar 被隐藏,OHOSMenuBar 以 QTimer 每秒导出菜单快照 JSON(serial 仅随内容变化前进),ArkTS 顶栏据此渲染并经 QueuedConnection 把 trigger 回注 GUI 线程。对话框则按设备能力双模式分流:自由多窗形态用 createSubWindowWithOptions 创建真子窗(DialogPage),否则在 Index 内以 DialogHost 浮层卡片呈现;标题栏统一由 ArkTS 自绘。分离呈现的对话框拥有独立 XComponent surfaceId,QPA 为其建立独立 EGLSurface;输入按 XComponent id 定向注入。2in1 上"对话框定格首帧"的终案是一行根修——swap 前未 makeCurrent 对话框表面,OHOS EGL 对未绑定 current context 的 window surface 静默丢弃每一帧提交。
### 5. 数据部署与打包
打包遵循五件套:nativeLib.strip 关闭保护预编译 so、免 compressNativeLibs、NEEDED 去版本号、.so 经 entry/libs 通道随 HAP 分发、运行期按拓扑 dlopen。OBS 的 effect 着色器、zh-CN locale 与 Yami 主题打包为 rawfile obs-data.tar,首启解包到 files/obs-data 并以 stamp 协议防重复;配置侧设置 HOME=files/home,OBS 按桌面习惯写入 ~/.config/obs-studio。签名产物约 99MB,README 状态总览记载 11 个插件在真机全部加载。
▍五、适配中遇到的几个难点
### 难点一:musl 的动态链接语义与桌面 Linux 不同
三类问题同源:musl 不做 RTLD_GLOBAL 提升,插件深处经 libobs dlopen 链无法用 RTLD_DEFAULT 找到桥符号,工程以 /proc/self/maps 定位 libcastbridge.so 后 NOLOAD dlopen 再 dlsym;预编译依赖带版本化 NEEDED,鸿蒙环境禁用,部署期统一 elf_edit 去版本;NAPI 桥函数漏写 extern "C" 导致导出为 C++ 修饰名,插件按 C 名查找恒 NULL 且不报错,权限请求桥静默失效后才被 nm -D 实锤。这类问题的共同特征是"不崩溃、只失效",必须靠符号表取证。
### 难点二:ES3 与桌面 GL 3.3 的能力位差异
libobs 的渲染逻辑假设桌面 GL 语义:sRGB 能力位可枚举、贴图无缝可用、读回路径廉价。在设备 GLES3 上,每帧枚举不存在的枚举值会让错误队列积压,连坐打断后续纹理创建;预览回调不清理错误队列时,每帧泄漏的 0x500 错误会在开录期打断着色器创建,最终表现为"渲染停摆、采集饿死"。工程的解法是把所有能力差异改成设备侧状态记录,并在每个读回入口加错误屏障。
### 难点三:平板对话框的坐标系失配
设置对话框 981×730 逻辑像素超出平板视口宽度,ArkTS 卡片把表面钳到可用区的 94%×88%,而 QPA 把 Qt 帧拉伸铺满较小表面、注入却按 1:1 直喂——两套坐标系失配后,点击偏移随深度放大,表面高度以下的 Qt 坐标区完全不可达。工程在 QPA 源头把 Qt 窗口钳制到同限额,无法缩窗的 Fixed 宽框改按窗口/表面千分比换算注入,并为"触摸按几何双派发导致弹层 1ms 闪关"补了主链拦截。
### 难点四:触屏输入与 IME 的长链路
Qt 主表面在部分平板输入栈上触摸不可达,自动化只能驱动 ArkTS 层;滚动交互按控件族分家——QScrollArea 用 QScroller 手势,QMenu 与条目视图因自消费按下事件必须改拖动转滚轮且过滤器要 view+viewport 双装。IME 链经六轮断点定位才闭环:分离表面对话框内控件从未获得 Qt 焦点,工程改用"最后按压控件"的触摸语义直连定位提交目标;spinbox 行编辑带 "6000 Kbps" 后缀,光标落在后缀之后会触发 QSpinBox 同步回滚,解法是会话首笔对行编辑 selectAll 复刻桌面整体替换语义。
### 难点五:后台保活与系统约束
纯采集态切后台约 2 秒被系统以 ILLEGAL_AUDIO_CAPTURER_BY_SUSPEND 击杀,录制态则依赖持续任务。工程让采集与音频插件经 UI 桥上报会话计数,ArkTS 侧把 bgkeep(录制)与 bgcap(采集)聚合,任一为真即申请 AUDIO_RECORDING 持续任务;module.json5 补 backgroundModes 与 KEEP_BACKGROUND_RUNNING。另一类约束来自权限模型:缺 INTERNET 权限时 socket 全败,HTTP 触发通道与 RTMP 推流同时瘫痪,这一前置是真机实证后才补上的。
▍六、编译、安装与启动
构建分四步前置再加标准 hvigor 流程,脚本均位于 native/scripts/,按 README 记载顺序执行:
bash
bash native/scripts/build_deps.sh
bash native/scripts/build_obs.sh
(cd entry/src/main/cpp/qt_platform/ohos_qt6 && bash build_libqohos_qt6.sh)
bash native/scripts/deploy.sh
随后进行 ArkTS 与 HAP 构建:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
连接设备后安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.aircast.app
PORTING.md 里程碑记载了这条链路的真机验证结果:屏幕录制全链(AVScreenCapture→ES3 渲染→x264/AAC→MP4 落盘可播放)、RTMP 推流(外部 ffmpeg 收流约 31.6 秒、h264 1080p@30+AAC)、8 个顶级菜单 118 个叶子全枚举与 58 项跨菜单触发零崩溃,以及后台录制两分钟存活与回前台计时续走。
▍七、当前功能边界
已进入当前可运行产物的能力,依据 README 状态总览与里程碑 M5-M12:
- libobs + libobs-opengl(原生 GLES3,全 effect ES3 化)与 11 个插件真机全部加载;
- 编码器 obs_x264、ffmpeg_aac、ffmpeg_opus、pcm_s16le 就绪;
- Qt 前端 libobscast-frontend.so(Yami 主题,zh-CN 汉化);
- 屏幕采集源(AVScreenCapture 原始流 + 系统授权弹窗)与窗口采集源(显式选窗、持续跟随、NV12/RGBA 回退);
- 麦克风源(OHAudio,normal 级权限,按需申请);
- 录制与 RTMP 推流全链,HTTP :8099 触发通道支撑外部自动化直驱 frontend API;
- 菜单 ArkTS 快照接管、系统文件选择器(TSFN 桥 + 沙箱中转)、IME/光标/主窗 ID 桥;
- 后台录制持续任务聚合(bgkeep + bgcap)与真子窗/浮层对话框双模式。
尚未覆盖或按平台裁剪的能力:Simple 录制模式(依赖 helper 进程)、多视图与投影仪(单表面 QPA 无独立顶层窗)、NV12 硬路径(本机拒绝 SURFACE_YUV,已自动回退 RGBA)、系统选窗结果的编程恢复(场景切换或重启后需在属性面板重选)、拖拽排序的触摸转换(有意保留给物理鼠标)、部分平板上 combo 下拉列表的视觉投影(交互链已通,XComponent 消费时序待查)。
因此,当前版本更准确的定位是"OBS Studio 在鸿蒙 PC 上的可工作工程化版本":录制、推流、采集、菜单与输入的主干全部真实可用,剩余边界集中在依赖独立顶层窗口的桌面特性与仍在收敛的平板渲染细节。
▍八、总结
AirCast 的价值不在于"OBS 能启动",而在于它把一条完整的桌面媒体管线逐层拆开、逐层重接:前端改库解决了进程形态,QPA 与 XComponent 解决了窗口承载,libGLv4 直连与 GLES3 化解决了渲染语义,两个新插件解决了采集与音频,快照菜单与定向注入解决了输入与交互。PORTING.md 中 54 项补丁与八项里程碑记录了每一层的真实依据,也积累了 musl 动态链接、EGL 表面纪律、触屏手势分家这类可复用的平台结论。
后续工作的重心已经明确:完成 M6 汉化与截图回归的收尾,跟踪平板对话框弹层的 XComponent 消费时序,把窗口采集在更多设备上的画布跟随策略打磨稳定,同时保持 HTTP 自动化通道作为回归基建。对希望把其他重型 Qt 应用带到鸿蒙 PC 的团队而言,AirCast 的补丁清单与风险登记册本身就是一份可对照的施工图。
楼主
8 天前