Air 系列应用交流/AirLive(from kdenlive)|AirLive · 鸿蒙 PC 适配全记录

AirLive(from kdenlive)|AirLive · 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】19 / 56

■ 应用:AirLive(from kdenlive)(AirLive)
■ 定位:Kdenlive 移植,视频剪辑
■ 上游:kdenlive 26.11.70 (master) | 路线:Qt6 6.11+QPA6 | 版本:1.5.0
■ 开源: https://gitcode.com/Andy88/Harmony-kdenlive
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917612383718868455
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:https://gitcode.com/Andy88/Harmony-kdenlive

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配 AirLive

kdenlive 是 KDE 生态的主力视频编辑器,约 21 万行 C++,建立在 Qt6、KDE Frameworks 6、MLT 多媒体框架与 FFmpeg 之上。多轨时间线、项目素材仓、双监视器、特效与转场体系,使它成为桌面视频创作场景里结构最复杂的一类应用。把这样的软件带到鸿蒙 PC,考验的不是单个控件能否绘制,而是整套桌面技术栈在 musl 沙箱与全新图形栈上的生存能力。

AirLive 采取的是路线 A:Qt6.11 全模块(qtbase、declarative、svg、multimedia、networkauth、shadertools、5compat 共七个源码包)、KF6 6.28.0 全 31 组件、KDDockWidgets 2.4.1、MLT 7.40.0 与 FFmpeg 7.1.1 全部交叉编译到 aarch64-linux-ohos,kdenlive master(版本 26.11.70)全量复用,配一套自研的 Qt6 QPA 插件 libqohos6.so 承接窗口与输入。当前工程包名 com.airlive.app,版本 1.2.0,deviceTypes 为 tablet 与 2in1,compatibleSdkVersion 6.1.0(23),HAP 内随包 .so 达 129 个。

工程档案以"门"与"阶段"记录进度:M0(Qt6 与 QPA 上屏)、M1(Widgets 与 QML 同框上屏)、M2(进程内 MLT 渲染出 mp4)三道验证门全部通过,P7 到 P17 的功能轮次把真 kdenlive 内核从首次启动推进到"导入、剪辑、工程保存与打开"全流程,验证设备覆盖 MateBook Pro、MatePad Mini 与 MatePad 11.5。

▍二、先划清边界:不把编译通过当成适配完成

21 万行代码链接成一个 .so 并不等于视频编辑器可用了。kdenlive 的启动依赖 KIO 文件对话框、KXmlGui 菜单、QML 模块目录、MLT 环境变量与渲染子进程,每一项在鸿蒙沙箱里都有不同的失效方式。AirLive 的做法是把"当前可运行产物真实覆盖的链路"与"原桌面能力"逐层对照,缺什么就明说。

层次 原项目实现 鸿蒙侧当前方案
应用入口 main() + QApplication 桌面主循环 EntryAbility + XComponent SURFACE 宿主
渲染执行 QProcess::execute(kdenlive_render) 独立进程 沙箱禁 exec;M2 已验证进程内 Mlt::Consumer 范式
菜单系统 QMenuBar / KXmlGui 原生菜单栏 Kdenlive_GetMenuBarJson 桥 + ArkTS 顶部菜单栏
文件对话框 KIO 桌面对话框 DocumentViewPicker + uri 与沙箱双向中转
停靠布局 KDDockWidgets QMainWindow 前端 原样交叉编译 libkddockwidgets-qt6.so
工程文件 .kdenlive XML 读写 同格式实证:保存 68,595 字节、5 轨打开恢复
监视器音频 MLT sdl2/rtaudio consumer 构建期裁剪,OHOS 音频后端选型中

需要强调的是渲染一行:GUI 的 File 到 Render 链当前走不通,原因是沙箱禁止 exec,而不是移植缺陷;工程用 M2 门证明了进程内编程式渲染的可行性(37,878 字节 mp4 经 ffprobe 复核),把替代路径提前钉牢。菜单、对话框两行则说明交互层完全按鸿蒙习惯重建,Qt 侧只保留数据与逻辑。

▍三、鸿蒙版的整体架构

自顶向下分四层:ArkTS 壳持有 XComponent 与菜单栏;liblivebridge.so 做 NAPI 薄桥;libairlive_core.so 是 kdenlive 真身;libqohos6.so 是自研 Qt6 QPA 插件,直接对接 OHOS NativeWindow 与 Maleoon GLES2 EGL。

text
EntryAbility / Index.ets(XComponent SURFACE + ArkTS 菜单栏 + surface kick)
│ NAPI
▼
liblivebridge.so(napi_init.cpp)
├── 21 个 Kdenlive_* C API dlsym 解析(14 必需 + 7 可选)
├── DispatchTouchEvent → QOhos6_InjectPointer 瘦转发(QueuedConnection)
├── rcc 字节码注入 kdenliveui.rc(qRegisterResourceData)
└── TSFN 双通道(首帧撤闪屏 / picker 与 saveDone 回执)
│ dlopen
▼
libairlive_core.so(kdenlive 真身,src/ohos/kdenlive_ohos_entry)
├── Kdenlive_Init:环境变量注入 → QApplication → Core::build → 主窗 show
├── 全部 Init+Step 归 RenderThread(即 Qt 主线程),JS 线程零 Qt 对象接触
└── Kdenlive_M2RenderTest:Mlt::Factory → Producer → Consumer("avformat")
│ dlopen + dlsym QOhos6_*
▼
libqohos6.so(自研 Qt6 QPA)
└── 单全局 EGL context + 单 EGL surface;raster QImage → GL 纹理 blit;
rhiFlush overlay 合成次级 QWindow;输入模态优先 + 逆序命中 + Press grab


仓库主要目录如下:

text
AirLive/
├── AppScope/ # 应用级配置(com.airlive.app / 1.2.0)
├── README.md / ROADMAP.md / THIRD_PARTY.md # 状态总账、路线图、第三方清单
├── build-profile.json5 # 签名仅 default(debug),release 禁用
├── entry/
│ ├── libs/arm64-v8a/ # 129 个 .so(kdenlive/Qt6/KF6/MLT/FFmpeg/libqohos6)
│ └── src/main/
│ ├── ets/pages/Index.ets # XComponent 宿主 + 顶部菜单栏 + IME 屏外输入
│ ├── ets/pages/DialogPage.ets # 弹窗真子窗宿主页
│ ├── ets/components/ # DialogTitleBar、DialogHost(自绘标题条族)
│ ├── ets/utils/SubWindowManager.ets # 弹窗双模式实例管理
│ ├── cpp/napi_init.cpp # liblivebridge 薄桥
│ ├── cpp/qt_platform/ohos6/ # QPA 插件源码与构建脚本
│ └── resources/rawfile/ # kdenlive-data 数据树 + HarmonyOS Sans 字体
├── native/
│ ├── sources/ # kdenlive-src、Qt6.11.1 七包、ffmpeg、mlt、kf6
│ ├── patches/kdenlive-ohos/ # 0003~0006 补丁 + 入口 + exports + cmake 预设
│ ├── scripts/ # build_*.sh 全链 + env.sh + deploy_m1_demo.sh
│ ├── cross/ # OHOS aarch64 工具链配置
│ ├── install/ # 交叉编译产物 PREFIX
│ └── build/ # 各组件构建目录(独占不混用)
└── ffbuild/ # ffmpeg 构建辅助


▍四、核心适配过程

### 1. Qt6.11 交叉编译与自研 QPA6

M0 阶段把 Qt6 七个源码包全部交叉编译到 native/install,关键配置包括 QT_FEATURE_freetype=1 并入 libQt6Gui、仅 GLES2(QPA 侧先试 ES3 回退 ES2)。QPA 插件从 Qt5 版本移植,处理了 moc revision 69、QSG_RENDER_LOOP=basic 等差异。M1 门暴露的呈现路径缺陷很有代表性:eglMakeCurrent 之后从未 glBindFramebuffer(GL_FRAMEBUFFER,0),QSG 离屏 pass 遗留的 FBO 绑定让 rhiFlush 的 glClear 抹掉 QML 纹理,整窗恒黑;三处补上绑定归零后,Widgets 光栅内容与 QML 场景才在同一条 EGL surface 上同框。

### 2. kdenlive 真身的链接与补丁

21 万行代码先编成 kdenliveLib.a,再以独立 clang++ 命令加 --whole-archive 加 --start-group 链接为 libairlive_core.so,并用 airlive_core.exports 版本脚本只导出 14 个 Kdenlive_* C API——不加 whole-archive 时链接器会把无人引用的符号全部剔除,产物仅 11KB。源码补丁共四组:OTIO 与 SharedMimeInfo 依赖整体裁剪、QML 插件改 QT_NO_PLUGIN、Qt 6.11.1 兼容修正,外加 Purpose、Solid、KNotifications 等桌面运行时能力的 Q_OS_OHOS 守卫跳过。OHOS 下不产出 kdenlive 可执行文件与 kdenlive_render,这为渲染链的进程内化预留了结构。

### 3. QML 三路分置与静态注册

鸿蒙沙箱禁 dlopen Qt 插件目录,QML 的加载被拆成三路:.so 走 entry/libs 由 QML_PLUGIN_PATH 指向;qmldir、.qml、.qmltypes 数据文件走 rawfile 拷贝到 filesDir,由 QML2_IMPORT_PATH 指向(hvigor 打包 entry/libs 时只收 .so,数据文件必须走 rawfile 通道);qrc 资源内嵌在 backing 库里。kdenlive 自身 QML 模块不生成插件,改为进程内手动调用 qml_register_types_org_kde_kdenlive();QPA 平台插件同样通过 dlsym("QOhos6_RegisterStaticPlugin") 静态注册。P7 启动迭代的 run1 到 run7 逐项解决了缺符号、静态插件未注册、qrc 截胡、57 个 QML 目录注入与数据树搬运。

### 4. 输入、HiDPI 与全流程贯通

输入链的关键根因是坐标体系:Qt6 的 QWSI handleMouseEvent 收的是原生像素,QPA 却误投逻辑坐标,Qt 内部再除一次 dpr,命中点被缩到上半区。修正后按钮、列表全部点中。HiDPI 经 Kdenlive_SetDisplayMetrics 注入 dpr,QPA 内 logicalDpi=96×dpr 抵消系统缩放的双重计费,实测 1.9 取整为 2.0。run25 确立了线程模型裁决:Init 与 Step 全部归 RenderThread(即 Qt 主线程),ArkTS JS 线程零 Qt 对象接触,触摸注入走 QueuedConnection 瘦转发加 QPointer 保护。此后素材导入(系统 picker、MLT 探测 720x576@25、缩略图)、时间线剪辑、工程保存与 5 轨打开恢复逐一贯通。

### 5. 菜单桥、弹窗标题条与黑屏自愈

P9 把 QMenuBar 从 Qt 侧摘除,菜单结构经 JSON 桥交给 ArkTS 顶部菜单栏渲染,支持级联、置灰、勾选与快捷键显示。P17 按移植手册的弹窗终态实现四层链:QPA 对弹窗分类识别,为每个对话框子窗建专属 EGLSurface 帧直投;ArkTS 侧 DialogTitleBar(40vp 自绘标题条加关闭钮,拖拽双通道)、DialogPage 真子窗宿主页、DialogHost 沉浸浮层与 SubWindowManager 双模式管理协同工作。稳定性方面,P15 查明 ArkUI 窗口重配置期约 1 秒内建出的 EGLSurface 是僵尸(swap 成功却零像素上屏),引入 1.2 秒宽限与自调度重试;窗口 INACTIVE 到 ACTIVE 的 surface kick 提供黑屏自愈,P16 补上 rhiFlush 计时后消除了静置期的误 kick 循环,并按 NDK 权威键表全表重写了键映射。

▍五、适配中遇到的几个难点

### 难点一:QSG 渲染线程独占 EGL context

主界面首次启动整窗黑屏,排查确认 QSG THREADED 渲染线程独占了全局 EGL context,主线程 eglMakeCurrent 恒返回 0x3002,旧路径销毁 surface 后同一 NativeWindow 永久 0x3003。工程把渲染循环改为 basic 模式,配合 0x3002 的非破坏式重试与 QT_WIDGETS_NO_CHILD_RHI 补丁,主窗的素材仓、双监视器、时间线才完整上屏。后续 P12 又发现时间线与监视器的 QQuickWidget 离屏纹理不进顶层合成,改用 QQuickWindow::setGraphicsApi(Software) 软件后端解决,避免再动 qtbase。

### 难点二:字体豆腐块与 QPA 前缀错绑

主界面文字全部渲染为豆腐块,根因是 build_libqohos6.sh 的 ARLIVE_PREFIX 误指向另一个工程的安装前缀,QT_FEATURE_freetype 为 -1,Qt6 平台字体库别名退化为基类,字体数归零。修复默认值并在 fontDatabase() 里显式返回 FreeType 实现。这个案例说明了交叉编译环境里"路径即语义":一个前缀变量指向错误目录,就能让整个文字系统静默失效。

### 难点三:沙箱禁 exec 与渲染子进程

kdenlive 的 GUI 渲染入口 QProcess::execute(kdenlive_render) 在鸿蒙沙箱必然失败,工程在 CMake 层把 renderer 子目录整体排除并在注释里写明改道方向。替代范式由 M2 门先行验证:进程内从 Mlt::Factory 出发构造 Producer 与 avformat Consumer,纯编程渲染出 50 帧、37,878 字节的有效 mp4,ffprobe 复核编码与封装合法。期间还暴露了 <dataPath>/tmp 目录从不创建导致 muxer 打不开目标的问题,渲染前补建父目录。把"进程模型差异"转成"已验证的替代范式",是这个难点的处理思路。

### 难点四:僵尸 surface 与黑屏自愈

窗口移动后黑屏的根因链较长:ArkUI 在窗口事件后约 1 秒内不回调重配置 buffer 队列,宽限期内建出的 EGLSurface 交换成功却零像素上屏;kick 换句柄后的新 surface 若仍落在湍流期就循环黑屏。工程引入建面 1.2 秒宽限加自调度重试吸收这段窗口期,再以 XComponent 重建发新 NativeWindow 作为唯一可靠的治愈手段固化下来(Fix2 surface kick),5 次真实移动全部保持内容、静置 60 秒以上零黑屏。配套发现主窗口恒走 rhiFlush 而看门狗只统计 flush 计时,造成静置期每约 3 秒误 kick 一次,补上计时后归零。

### 难点五:ELF 交付纪律

musl 的 reloc_all 对段布局重排敏感,patchelf 一律禁用,ELF 编辑用仓库内 elf_edit.py 原位改 .dynstr;新库的 NEEDED 与 SONAME 必须去版本号,64K 页对齐用 fix_elf_align.py 修复。增量重建按改动范围三选一:只改 QPA 走直出链;改 Qt6 源码则必须重跑 deploy_m1_demo.sh,否则 patch_libs 的 install 到 libs 同步方向会把新库回退成旧版。run38 事故(Qt6Gui 增量重编漏去版本号)之后,这条纪律连同 llvm-readelf -d 的验证命令一起写进了构建文档。

▍六、编译、安装与启动

全量重建链从环境脚本开始:source native/scripts/env.sh 设置交叉工具链;kdenlive 改动后执行 build_kdenlive.sh build / link / audit 三步;随后把本轮重建的 core 与现役 QPA 都拷入 native/install/lib/,再跑 deploy_m1_demo.sh 完成 patch_libs 同步、去版本号与对齐修复。只改 ArkTS 或薄桥时直接用标准 hvigor 构建:

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


工程约定只用 default(debug)签名配置,release 配置仅占位禁用;签名材料不入库。产物位于 entry/build/default/outputs/default/entry-default-signed.hap,打包后建议用 unzip -l 核对 libairlive_core.so 的新鲜度。安装与启动:

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airlive.app


单轮取证有既定命令序列:先 aa force-stop 清进程,删除 kdenlive/kdenlive_ohos.txt 与 airlive_trace.txt 两个日志文件再启动,25 秒后经 /data/app/el2/100/base/com.airlive.app/haps/entry/files/ 镜像路径拉回日志。日志文件一律 .txt 后缀——设备安全服务实时扫描沙箱 *.log 文件,扫描期写盘挂起已有实证。

▍七、当前功能边界

已覆盖的能力,以工程三件套的验证记录为准:

- Qt6.11 七模块与 KF6 全 31 组件交叉编译,129 个 .so 随 HAP 打包运行;
- kdenlive 主窗完整呈现:项目仓、双监视器、工具栏、时间线与状态栏,界面中文化;
- 素材导入全链:系统 picker 选片、MLT 探测与 profile 切换、Project Bin 缩略图、seek;
- 时间线插入剪辑、工程保存(68,595 字节 XML)与打开恢复(5 轨);
- 进程内编程式 MLT 渲染出有效 mp4(M2 门,ffprobe 复核);
- ArkTS 菜单栏(级联、置灰、勾选)、弹窗自绘标题条与拖拽钳制、键盘权威键表;
- HiDPI dpr 2.0、黑屏自愈 surface kick、移动黑屏根治、中文字体链。

尚未覆盖或处于开放状态的能力:File 到 Render 的 GUI 渲染链受 exec 缺口阻塞,待按 M2 范式进程内化;监视器视频帧流(null consumer 帧事件链已入包但待装机实测);音频后端缺失(MLT 的 sdl2/rtaudio 构建期裁剪,OHOS 侧选型未定);fit-zoom 触发的心跳冻结型黑屏以规避为主;IME 弹键盘最后一环在注入测试下未进输入框,待真实手指复测;titler、scopes、mixer 已编译入包但完整验证清单化仍在进行。渲染预设 profiles.xml 与中文化资源已随包就位。

因此,当前版本更准确的定位是"kdenlive 的鸿蒙原生可运行底座加剪辑主流程骨架":从启动、导入、剪辑到工程持久化的骨架已经真实跑通,渲染执行、音频与视频帧呈现按清单缺省,边界在文档里逐项可查。

▍八、总结

AirLive 证明了 Qt6 时代的重型 KDE 应用同样能在鸿蒙侧获得完整的原生栈支撑:七个 Qt6 模块、31 个 KF6 组件、MLT 与 FFmpeg 全部交叉编译,21 万行 kdenlive 代码以受控导出的单一 .so 运行。三道里程碑门与 P 系列功能轮次把"能不能起来"的问题拆解成 EGL 呈现、QML 加载、输入坐标、字体、弹窗、生命周期等一组可独立验证的子问题,每个子问题都留下了根因与回归记录。

后续工作的重心按工程路线图排列:渲染链进程内化复用 M2 范式取代 exec,监视器视频帧流装机实测收尾,音频后端在 OHOS AudioStream 与 OpenSL ES 之间定界,fit-zoom 死锁与 IME 最后一环随整机回归一并处理。每一项都以"门"的方式验收——产物可构建、行为在真机可复现、证据可回溯。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。