查看: 165|回复: 0

OBS鸿蒙迁移实践:ArkTS前端通道绑定、场景源不激活与熄屏假死排障

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在OBS Studio向HarmonyOS NEXT迁移的系列实践中,前端路线有一个关键取舍:是否把Qt也搬到鸿蒙。Qt for OpenHarmony确实存在,但生态较浅,而OBS的Qt前端与系统托盘、全局热键、多窗口管理、原生菜单等平台代码纠缠很深,逐处寻找鸿蒙等价物的工作量,反而大于重写一个精简前端。因此作者选择扔掉Qt,用ArkTS重写一个够用版前端,最终只有几百行。

这个选择还有两个支撑理由。其一,OBS前端的核心其实很小:选场景、加来源、看预览、点录制/推流、调音量,把这些做出来并不需要完整复刻桌面Qt界面。其二,ArkTS是鸿蒙原生UI范式,重写反而能利用ArkUI组件体系和状态管理V2,界面质量与性能更可控。后来做中文国际化时,纯ArkTS前端的资源化管理也比Qt的.qm翻译体系清爽。

【前端结构与ObsController中介层】

前端拆成五个组件加一个中介层:Index.ets主页面承载TopBar和布局,下挂SceneList场景面板、SourceList来源面板、PreviewCanvas预览(XComponent)、TransportBar推流/录制控制、AudioMixer混音器。这些组件统一通过ObsController中介层调用napi,再由obs_bridge.cpp进入libobs内核。

作者从系列第一篇就立下一条铁律:所有native调用只从ObsController这一个中介层走,UI组件从不直接碰napi函数,状态更新、错误处理、乐观UI都集中在ObsController一个文件里。这条纪律在修复“录制按钮不翻牌”时省了大麻烦——乐观置位只需要改中介层一处。

【核心问题:源永不activate与libobs数据流契约】

迁移中出现过一个典型现象:UI完整,能建场景、能加Display源,但源创建后obs_source_info.activate回调永远不触发,预览恒黑。排查链路是:源创建成功,加入场景成功,但activate不触发,说明源从未进入渲染树。

根因在NativeCreateScene只调用了obs_scene_create,从未调用obs_set_output_source(0, scene_source)。这需要理解OBS的渲染模型:libobs不是“创建了场景和源就该渲染”,渲染入口是通道(channel)。通道0对应当前节目,也就是场景;view再枚举场景内的源,逐个render进canvas。场景对象如果不挂到通道0,就是孤儿对象——它存在,但渲染循环根本不看它。

修复方式是在创建场景后补一行:
  1. obs_set_output_source(0, obs_scene_get_source(scene));
复制代码

修复后点击Display源,activate在图形线程正确触发。这个坑的教训是:OBS桌面Qt前端里“场景列表选中即挂通道”的逻辑,藏在setCurrentProgram往下几十层调用中,平时根本看不见。脱离Qt前端重写时,必须对着libobs的数据流契约理解渲染模型,而不是对着API列表挨个调用。API列表只告诉你“有obs_scene_create这个函数”,不会告诉你“光调它不够,还得挂通道”。这也是所有“重写前端接既有内核”任务的通用陷阱:内核的隐式约定,往往写在被扔掉的老前端里。

【napi桥的三线程模型】

前端跑在ArkTS UI线程,libobs图形循环跑在XComponent渲染线程,编码跑在“obs gpu encode”线程。三条线程靠napi桥连接。

UI到native的调用使用napi同步/异步函数,参数用JSON字符串过桥,省掉逐字段napi转换的样板。native到UI的状态回推分两条路:stats走installStatsCallback回调通道;音量表走200ms轮询pollLevels,因为音量峰值是高频小数据,轮询比每帧回调更省。跨线程GPU资源属于共享EGL context的战场,前端侧不直接碰,但中介层必须清楚“哪条线程能干什么”。

【调试环境坑王:锁屏/熄屏造成的假卡死】

设备端验证还遇到一个非代码问题。测试机MatePad Edge会快速自动熄屏/锁屏,而开发者模式无法远程解锁,整个移植过程里一晚能吞掉40多分钟的“锁屏拉锯”。

症状是多次出现“应用白屏卡死THREAD_BLOCK_6S”。真相却是平板自动熄屏造成的假象:snapshot_display拍到的是熄屏动画白帧,uitest点击无回执,因为点在了锁屏上,看起来像应用死了,实际是设备睡了。

鉴别真卡死与假卡死的方法后来被固化下来:
  1. pidof <bundle> # 进程还在吗
  2. cat /proc/<pid>/task/*/status | grep State # 全S=没死,在睡
  3. # 再看日志时间戳是否还在推进
复制代码

对策是power-shell wakeup唤醒,加双段上滑解锁手势:第一段关熄屏AOD页,第二段真解锁,这台设备没设PIN。脚本里每次点击前还要重新dumpLayout,因为窗口位置每次启动会变。这套流程全部固化进on-device-verify.sh。

原则可以总结为:行为没变先查进包,白屏先查熄屏,卡死先查线程状态。把“是不是我的代码错了”和“是不是设备在睡觉”分开,是设备黑盒验证省时间的第一原则。

【中文国际化:纯ArkTS前端的资源化红利】

UI中文化时,所有UI字符串收进resources/base/element/string.json,base放中文作默认;另建resources/en_US/element/string.json放英文覆盖。组件里的Text('Sources')统一换成Text($r('app.string.sources'))。

几个ArkTS资源化门道值得记录。带参数的文案用{0}占位,例如$r('app.string.notice_scene_fail', name),比字符串拼接更适合翻译,因为不同语言语序不同。状态提示这类动态字符串,把字段类型从string改成Resource | null,赋值用$r(...),判空用!== null,这样连错误提示都跟着系统语言走。来自C++的字符串,例如音频轨道名“Desktop Audio”,在ArkTS显示处按id映射资源,不动C++:track.id === 'desktop-audio' ? $r('app.string.track_desktop') : ...。默认场景名这种要存进内核的字符串,用resourceManager.getStringSync($r(...).id)在初始化时解析成当前语言的字符串再传下去。

CJK宽度也要注意:中文标签比英文宽,“服务器”“串流密钥”两个标签的固定.width()要调整,否则会换行。还有一个连锁反应:中文化后,on-device-verify.sh里找英文按钮(如“Start Recording”)的自动化全MISS了。修法是把按钮匹配改成中英双候选:
  1. wait_tap_any "开始录制" "Start Recording"
复制代码

也就是说,国际化不是改完UI就完事,连测试脚本都得跟着i18n-aware。

【收官与验证环境】

九篇下来,从“60个依赖库交叉编译”到“预览/录制/推流三大能力真机跑通”,OBS Studio在HarmonyOS NEXT上活了。这一程修掉的21个坑,每一个都是“桌面/移动/宽松驱动的隐含假设在鸿蒙EGL、沙箱、严格GLES下不成立”的标本。验证机为HUAWEI MatePad Edge(PC/2in1模式),HarmonyOS 7.0.0.109,API 26;OBS Studio 32.2.2,fork分支qingkouwei/obs-studio@harmonyos-port。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-5 09:41 , Processed in 0.019464 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部