Air 系列应用交流/轻剪辑(from Shotcut)|AirShot · 鸿蒙 PC 适配全记录

轻剪辑(from Shotcut)|AirShot · 鸿蒙 PC 适配全记录

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

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

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

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

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

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

▍一、为什么要适配 AirShot

视频剪辑是桌面生产力里对系统底座要求最苛刻的一类应用:GUI、多媒体解码、GPU 合成、音频通路、编码导出缺一不可。Shotcut 是这一领域最有代表性的开源跨平台编辑器,构建在 Qt6、MLT、FFmpeg、FFTW 全套框架之上。AirShot 把这套栈完整移植到 HarmonyOS(aarch64-linux-ohos,musl),应用内名"轻剪辑"(AppScope/app.json5 的 app_name),目标是让导入素材、时间线剪辑、工程保存、导出成片这条完整工作流跑在鸿蒙平板与 PC 上,而不是做一个只能播放的壳。

选择 Shotcut 而非轻量播放器作为移植对象,价值在于压力测试的完整性:Qt 6.11.1 全模块要在 musl 工具链下从源码交叉编译;MLT 7.40.0 与 FFmpeg 7.1.1 的几十个媒体库要以共享库形态随 HAP 分发并被动态加载;沙箱禁止 exec 子进程,意味着上游以命令行工具为骨架的渲染导出体系必须整个重构为进程内库调用。任何一环妥协,成片链路就断。

工程概况:包名 com.airshot.app,当前 versionName 1.1.0(versionCode 1100000),deviceTypes 为 tablet、2in1、default,入口 EntryAbility;compatibleSdkVersion 6.1.0(23)、targetSdkVersion 6.1.1(24);构建仅使用 default(debug)签名,release 配置禁用。README.md 记载截至 2026-08-16 M1 至 M6 全部门径 PASSED:启动、导入、剪辑、保存、打开、导出 mp4、出声全链在 HUAWEI MateBook Pro(2in1)真机跑通,界面全中文。module.json5 的 requestPermissions 为空——素材访问全部走系统 Picker 的无权限范式。

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

Shotcut 的桌面形态隐含着四个鸿蒙侧不成立的前提:有窗口系统可依赖、可以 fork 子进程跑 melt/ffmpeg 命令行、QtMultimedia 有系统媒体后端、SDL2 有系统音频驱动。逐一替换这些前提是本工程"适配完成"的真实定义。当前产物的边界由 README.md 关键架构决策与 PORTING.md 选型结论表共同框定:

层次 原项目实现(传统桌面方案) 鸿蒙侧当前方案
应用入口 main() 直接构造 QApplication EntryAbility → libshotbridge.so 按拓扑序 dlopen 88+ 库链
窗口承载 X11/Wayland 平台插件 自研 libqohos6.so QPA:单 EGLSurface + raster/rhiFlush 合成
媒体引擎 MLT + FFmpeg 库与 CLI 工具并用 全进程内:Mlt::Producer/Consumer,禁 exec
监视器渲染 QtMultimedia 系统后端 QT_MEDIA_BACKEND=minimal + 自研静态 MinimalMediaBackend,帧走 MLT → QVideoSink
音频输出 SDL2 系统音频驱动 SDL2 自研 OHAudio 后端,SDL_AUDIODRIVER=ohos
渲染导出 melt CLI 子进程 MeltRunnerOhos 进程内渲染,H.264/AAC mp4
菜单 原生 QMenuBar Shotcut_ExportMenuJson 动态导出 + ArkTS TopMenuBar/bindMenu
字体 fontconfig 遍历系统字体 rawfile/fonts 随包自带,首启幂等拷贝到 filesDir

这些替换全部落在可运行产物中并经真机验证,不是规划项。同样重要的是反向边界:真机手指触摸全链、软键盘 IME、frei0r 滤镜静态化仍登记在风险册中作为开放项,工程文档不把未验证的能力计入已交付。

▍三、鸿蒙版的整体架构

架构自顶向下五层。ArkTS 壳持有 XComponent(SURFACE 型)与顶栏菜单;薄 NAPI 壳 libshotbridge.so 只链系统库,按 RTLD_GLOBAL 拓扑序 dlopen 全部依赖后 dlsym 出 core 的 C API;重型核心 libshotcut_core.so 是 Shotcut 真身,携带 MLT、FFmpeg、FFTW、SDL2 与 CuteLogger;自研 QPA libqohos6.so 把 Qt 窗口合成到 OHOS NativeWindow;最底层是 Maleoon GLES2 EGL。

text
ArkTS Shell(entry/src/main/ets)
├── EntryAbility.ets 生命周期 / 字体部署 / picker 桥 / want 指令测试通路
├── pages/Index.ets XComponent SURFACE 宿主 + 启动遮罩 + 键盘转发 + 遮罩自愈
├── components/TopMenuBar.ets ArkTS 触摸友好顶栏(菜单叠加层)
└── model/MenuModel.ets triggerAction 映射(3 秒刷新字节比对)
│ NAPI
▼
libshotbridge.so(薄 NAPI 壳:dlopen 链 / 输入转发 / rcc 资源注入)
└── libshotcut_core.so(Shotcut_Init:环境变量注入 → QApplication → MainWindow)
├── MLT 7.40.0(sdl2_audio consumer / avformat 模块 / MeltRunnerOhos)
├── FFmpeg 7.1.1(libav* / libsw*)+ FFTW3(示波器/频谱滤镜)+ SDL2(OHAudio 后端)
└── dlopen + dlsym:QOhos6_*
▼
libqohos6.so(自研 Qt6 QPA:QOhos6Integration/Screen/Window/BackingStore/GLContext)
└── 单共享 EGLSurface → raster QImage / RHI 纹理 → eglSwapBuffers
▼
OHOS NativeWindow / Maleoon GLES2 EGL


主要目录如下(与仓库实际结构一致):

text
AirShot/
├── README.md / PORTING.md / ROADMAP.md / AGENTS.md / THIRD_PARTY.md
├── AppScope/app.json5 # com.airshot.app / 1.1.0
├── entry/src/main/
│ ├── module.json5 # compressNativeLibs:false 铁律;requestPermissions 空
│ ├── ets/ # EntryAbility / Index / TopMenuBar / MenuModel / FileManager / ImmersiveManager
│ ├── cpp/
│ │ ├── napi_init.cpp # libshotbridge:dlopen 链 + 输入转发
│ │ └── qt_platform/ohos6/ # libqohos6 QPA 源码与构建脚本
│ ├── libs/arm64-v8a/ # 部署的 .so + mlt-7/ + qml/(deploy_modules.sh 产出)
│ └── resources/rawfile/ # fonts / shotcut 数据树(share、qml、profiles)
└── native/
├── sources/ # shotcut-master / qt*-6.11.1 / mlt / ffmpeg / fftw / sdl2 / x264 / lame
├── patches/
│ ├── shotcut-ohos/ # shotcut_ohos_entry + preset.cmake + 进程内 melt 说明
│ └── mlt-qt6-ohos/ # MLT Qt6 模块补丁
├── scripts/ # env.sh / build_qt6_* / build_ffmpeg / build_mlt /
│ # deploy_modules / elf_edit / fix_elf_align 等
├── cross/ # ohos-aarch64 toolchain / cross file / ohos-compat
└── install/ # 交叉编译 PREFIX(336M:Qt6.11.1/KF6/FFmpeg/MLT)


▍四、核心适配过程

### 1. Qt 6.11.1 全模块源码交叉编译与 install 前缀

工具链以 source native/scripts/env.sh 建立:AirChat-SDK24 的 clang 15 / API24 交叉链,musl sysroot,-D__OHOS__=1 -DOHOS_PLATFORM 全局定义,PATH 前置 /usr/bin 防止 macOS 工具污染 autoconf 探测。qtbase 及全部 GUI 相关模块从源码 configure+build 进 native/install 前缀,Shotcut 额外需要的 charts、websockets 用 build_qt6_submodule.sh 补编;musl 缺失的 iconv/gettext/线程扩展由 native/cross/ohos-compat/ 兼容层补齐。336M 的 install 前缀是后续全部组件的链接基线,x264、lame、fftw 等编码与分析库以独立脚本构建进同一前缀。

### 2. 自研 QPA:单 EGLSurface 上的窗口合成

entry/src/main/cpp/qt_platform/ohos6/ 是从 AirKrita(Qt5 版)到 AirLive(Qt6 版)一路演化而来的平台插件,在本工程继续增补。核心形态是全进程共享一个 EGLSurface:QBackingStore 的 raster 内容以 QImage 上传 GL 纹理 blit,含 QQuickWidget 的场景由自实现 rhiFlush 合成器叠加次级窗口,对话框、菜单等多实例以显式单调 zOrder 驱动绘制顺序与命中测试,两套顺序事实源不一致导致的弹窗错位由此消除。P11 轮重建了高 DPI 体系:logicalBaseDpi 固定 96、logicalDpi 按 96×dpr 计算,输入坐标除以 dpr、QWSI 事件乘以 dpr,UI 缩放由 QApplication 字体与 QProxyStyle 度量承接,平板按设备感知系数(约 1.65)注入。

### 3. 线程模型与进程内渲染导出

线程模型是架构裁决级的设计:Qt 主线程就是 RenderThread,Shotcut_Init 与 Shotcut_Step 在同一线程内执行,Step 循环以 processEvents(16ms) 有界阻塞真实泵动 Qt 主队列;JS(ArkTS)线程仅负责 dlopen 库链,绝不触碰 Qt 对象,跨线程调用一律经 core 内 runOnQtThread 的 BlockingQueuedConnection 封送。渲染导出同样进程内化:Shotcut_ExportProject C API 创建 MeltJob,MeltRunnerOhos 以 Mlt::Consumer("avformat") 在进程内完成 H.264/AAC mp4 编码,这是沙箱禁 exec 约束下对 melt CLI 的完整替代;音频通路由 SDL2 自研 OHAudio 后端承担,callback 加单槽 ringbuf 双信号量适配 SDL2 的 push 模型。

### 4. 三路分置打包与 ELF 纪律

沙箱禁 dlopen files 目录与插件扫描,打包因此三分:全部 .so 进 bundle 的 entry/libs/arm64-v8a/(dlopen 限位);qml 数据树进 filesDir 并以 QML2_IMPORT_PATH 指向;资源经 rcc 内嵌。deploy_modules.sh 按允许清单同步 install 前缀与构建产物,NEEDED/SONAME 去版本号(musl 严格匹配无版本文件名),fix_elf_align.py 修复 64K 页对齐。三条铁律写进 AGENTS.md:禁用 patchelf(段重排在 musl 重定位触发 SIGSEGV,ELF 编辑一律 elf_edit.py 原位改 .dynstr)、strip:false 与 compressNativeLibs:false(strip 破坏对齐布局)、rawfile 数据树部署用 manifest 版本戳幂等(避免每轮重拷 200MB)。

### 5. 输入、菜单与文件通道的桥接

输入侧处理了三类真实设备行为:2in1 设备同屏派发触摸序列与系统合成鼠标序列,鼠标事件距最近触摸小于 600ms 即丢弃,消除双 press 双笔迹;触摸 Press 前补一次无按钮 motion,规避 Qt 内部 hover 状态的 UAF 崩溃;P8 轮接通轴事件回调 OH_NativeXComponent_RegisterUIInputEventCallback(AXIS),把滚轮/触控板双指接进 QOhos6_InjectWheel,时间线缩放恢复可用。菜单以 Shotcut_ExportMenuJson 从真实 QAction 树动态导出(allowlist 过滤、已译文案、enabled/checked 快照),ArkTS 顶栏以 bindMenu 官方组件渲染并 3 秒字节比对刷新,杜绝手写静态菜单与真实应用结构脱节。文件经系统 Picker 授权后以沙箱中转:打开时 uri 拷贝到 tmp,保存时 core 写 staging 再经 TSFN 回调拷回 uri。

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

### 难点1:hilog 流控引发的 dlopen 链冻结

启动黑屏且 CPU 占用为零、进程"假活"。根因链在日志系统:NAPI 层 INFO 日志突发触发 hilogd 流控停止读取,OH_LOG 的 datagram 发送永久阻塞,把正在 dlopen 的加载线程一并冻结。解法是把策略倒置:常规日志一律写应用沙箱 files 目录下的 .txt 文件,hilog 只保留 ERROR 级别。这条纪律后来成为 Air 系列共享铁律——日志通道本身可以成为比被诊断问题更早的故障点。

### 难点2:Maleoon GPU 上的默认合成器崩溃

含 QQuickWidget 的窗口整树丢失乃至 SIGSEGV,定位到 Qt 自带 QBackingStoreDefaultCompositor 烘焙的 GLSL 在 Maleoon GPU 上编译失败。替换方案是 QPA 内自实现 rhiFlush:底图走 QImage,overlay 用 RHI 纹理 quad 复用自有 blit shader,并在合成前后做 GL 状态消毒(FBO0 重绑、关闭 SCISSOR_TEST)。这个自研合成器同时解决了 dpr 不一致时窗口几何乒乓互擦导致的整窗黑屏——dpr 先钉死 1.0,再在 P11 轮建立完整的双层缩放体系。

### 难点3:沙箱禁 exec 与媒体工具链进程内化

Shotcut 的导出体系构建在 melt 命令行工具之上,沙箱禁止 exec 使其整体失效。工程以 Mlt::Factory → Producer → Consumer 范式在进程内重建等价链路,并沉淀为 MeltRunnerOhos。同族问题还有 SDL2:主循环模式依赖自管事件循环,进程内化后仅作库链接,音频输出改道自研 OHAudio 后端。这一组改造的共同点是把"上游当作独立进程的能力"收编为库调用,同时保住 M4/M5 门径验收所要求的完整功能。

### 难点4:触摸与鼠标双通道的输入去重

2in1 设备在手指触摸时会同时派发触摸序列与系统合成的鼠标序列,Qt 侧收到双份事件表现为双 press、双笔迹。处理分两步:以 GetTouchEventSourceType 把合成触摸分流进鼠标桶;鼠标事件距最近触摸不足 600ms 直接丢弃。配套约束还有 XComponent 刻意不加 focusable——ArkUI 焦点系统一旦介入会拦截全部触摸,键盘改由页面根容器 onKeyEvent 承接。

### 难点5:二级弹窗的几何契约

二级弹窗铺满全屏:核对 qtbase 6.11 源码确认 QWindow::geometry() 直接回读 platformWindow 几何,而 QPA 窗口构造时给非主窗预设了全屏矩形。修复为非主窗初始几何按基类同款契约经 QHighDpi::toNativePixels 换算,多实例叠加交由显式 zOrder 合成与命中测试支持。同族问题还包括沉浸式偶发失效(窗口获取竞态,挂接全部挪到 loadContent 成功回调后重试)与 DOWNLOAD picker 在 onCreate 静默挂起(挪到 loadContent 后并强制钉 savePath)。

▍六、编译、安装与启动

Native 侧构建顺序依据 README 与 native/BUILD.md:工具链环境就位后,Qt6 模块补齐(qtbase 已就位时跳过),再编 FFmpeg 与 MLT(SDL2/FFTW/x264/lame 有 markers 协调增量跳过),然后增量构建 Shotcut 本体并部署:

bash
cd AirShot
source native/scripts/env.sh
bash native/scripts/build_qt6_qtbase.sh install # 已就位时跳过
bash native/scripts/build_qt6_submodule.sh <module> # charts / websockets 补编
bash native/scripts/build_ffmpeg.sh
bash native/scripts/build_mlt.sh
ninja -C native/build/shotcut shotcut
bash native/scripts/deploy_modules.sh # 收集 .so + 去版本号 + fix_elf_align


HAP 打包使用 default(debug)签名,release 配置禁用:

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.airshot.app


PORTING.md 同时记载了 want 参数驱动的无输入法测试通路(aa start --ps openPath/triggerAction/savePath/addTimelineClip/exportPath,语义顺序固定 open → add → save/export),以及崩溃取证通道:core 初始化第 0 步安装 sigaction 处理器,段错误时写 airshot_crash.txt 后恢复默认重发,系统 faultlogger 记录照常保留。

▍七、当前功能边界

当前已覆盖的能力(M1-M6 门径与 P7/P8 阶段验收记录):

- Qt6+QPA demo 上屏到 Shotcut 主界面上屏(M1/M2 门,含 scissor 修复);
- 媒体导入与视频帧全屏监视(M3 门,MinimalMediaBackend + MLT 帧链路);
- 时间线剪辑与工程保存/打开(M4 门);
- 进程内渲染导出 H.264/AAC mp4(M5 门);
- SDL2 OHAudio 音频通路真机出声(M6 门);
- 界面全中文化(QTranslator + 沙箱预置 .qm,musl locale 缺失下的替代方案);
- 滚轮/触控板缩放时间线(P8 AXIS 接线)、崩溃取证 sigaction、权限表清零(素材全走 Picker);
- 高 DPI 双层缩放、沉浸式与横屏、动态菜单导出、字体随包部署。

尚未进入当前产物的能力(README 开放问题与风险登记册 R1-R7):真机手指触摸的全链验证(want 指令通路已全绿,手指菜单/拖拽待回归);IME 软键盘(QOhos6InputContext 为 stub,无物理键盘时 Qt 行编辑暂无法输入,P9+ 候选);frei0r 滤镜目录的静态注册;musl iconv/gettext 全量兼容层(当前经 QTranslator 绕过);诊断日志的功耗优化清理。

因此,当前版本更准确的定位是"Shotcut 鸿蒙原生移植的全链可用基础版":从导入到导出的完整成片链路已在 2in1 真机验证,输入法与滤镜生态是登记在册的下一阶段边界。

▍八、总结

AirShot 是 Air 系列里栈最深的一役:Qt 6.11.1 全模块、MLT、FFmpeg、FFTW、SDL2、x264、lame 全部从源码在 musl 工具链下交叉编译,再以 88+ 共享库的 dlopen 拓扑在鸿蒙沙箱内组装成完整的视频编辑器。它证明这条路径可复制的部分已经沉淀为资产——env.sh 工具链、ELF 脚本族、QPA6 工程、install 前缀基线都源自兄弟项目并在本工程继续演化,而沙箱禁 exec 的媒体工具链进程内化、OHAudio 音频后端、rhiFlush 合成器则是本工程交出的新范式。

后续工作重心按风险登记册推进:优先完成真机手指触摸全链回归,随后按 P9+ 计划为 QPA 补齐 InputContext 使软键盘可用,frei0r 滤镜静态化与 musl 兼容层按同一节奏排期。每一步验收延续既有门径风格:以可构建的 HAP 与真机上的实际成片产出为准。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。