开源跨平台视频编辑器(原 mltframework/shotcut,基于 MLT 框架),因 OHOS 仅提供 Qt 5.15、无法承载其 Qt6 主程序,故以 SHELL 壳形态内置 FFmpeg(arm64)承载真实视频播放。针对运行时白屏补齐缺失依赖 libx264.so.164,最终在鸿蒙 PC 跑通"导入视频—时间轴轨道真实时长与缩略图显示—播放"主流程。
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 14 天前 | ||
| 19 天前 | ||
| 19 天前 |
Shotcut 的 OpenHarmony 适配记录
这份文档记录我们把 Shotcut(一个免费、开源、跨平台的视频剪辑器)往 OpenHarmony / HarmonyOS PC 上搬的过程:鸿蒙壳工程长什么样、代码改在哪、HAP 怎么编出来,以及哪些能跑、哪些卡在外部依赖上还没法跑。先把话说在前面——这个项目的移植分两条腿走:一条是已经在真机上跑通、能编、能起、能播视频的 UI 壳(SHELL),另一条是把 Shotcut 真身整套搬过来的 完整应用(FULL),后者卡在「还没有 Qt 6 的鸿蒙 SDK 和一整套多媒体原生库」这个硬门槛上。下面会把这两条线分别讲清楚,免得看的人误以为「整个 Shotcut 已经在鸿蒙上跑起来了」。
Shotcut 原本是个标准的 Linux / Windows / macOS 桌面应用,基于 Qt 6(最低 6.4) 加 MLT 多媒体框架,UI 大量用 QML(光 .qml 文件就约 420 个),底层靠 FFmpeg / FFTW / frei0r / SDL2 做编解码、特效和音频播放。我们没有去分叉它的桌面逻辑,桌面构建那一套保持原样,鸿蒙相关的东西全部隔离在 harmony_pc/ 这个目录里,平台差异用 Q_OS_OPENHARMONY 宏分开。
原项目仓库:https://github.com/mltframework/shotcut
一、工程结构 / 鸿蒙工程在哪
鸿蒙工程目录就叫 harmony_pc/,在仓库根目录这一级,DevEco Studio 要打开的就是它(不是仓库根)。先扫一眼,知道每个东西管什么:
ohos_Shotcut/
├── src/ # Shotcut 桌面端 C++/QML 源码(鸿蒙端 FULL 模式复用这套)
│ └── main.cpp # 应用入口,新增了 Q_OS_OPENHARMONY 分支 + extern "C" qtmain
├── CMakeLists.txt # 原桌面 CMake 工程(保持不动)
├── CuteLogger/ # 日志库(FULL 模式里会一起编进去)
├── icons/ translations/ voices/ filter-sets/ # 运行期资源(FULL 模式要打进 HAP)
├── README.md # 原项目说明
└── harmony_pc/ # ★ 鸿蒙 PC 适配工程(DevEco Studio 打开这里)
├── HARMONYOS_PORT.md # 英文移植技术说明(移植细节的第一手资料)
├── build-profile.json5 # App 级构建配置(签名 / 产品 / SDK 版本)
├── AppScope/app.json5 # 应用信息:bundleName / 版本 / 显示名
├── oh-package.json5 # App 级依赖
├── hvigorfile.ts # 构建脚本
├── qtforharmony_sdk/ # 本项目自带的 Qt 5.15 for HarmonyOS SDK(SHELL 用)
├── third_party/ # FULL 依赖:ffmpeg/(头文件 + arm64 .so)+ README.md(交叉编译计划)
└── entry/ # 鸿蒙 entry 模块(HAP)
├── build-profile.json5 # 模块级配置(CMake 参数、QT_PREFIX、abi)
├── libs/arm64-v8a/ # 运行期 .so:QPA 插件 + TLS + 一整套 OHOS arm64 FFmpeg
└── src/main/
├── module.json5 # 模块配置(设备类型、权限、入口 Ability)
├── ets/ # ArkTS:MyAbilityStage / EntryAbility / Index 页面(XComponent 宿主)
└── cpp/
├── CMakeLists.txt # Native 构建入口(SHELL / FULL 两套分支)
├── shotcut_ohos_shell.cpp # 自包含的 Qt Widgets UI 壳(SHELL 产物)
└── ffdecoder.{cpp,h} # 基于 FFmpeg 的极简视频解码器(给壳做真实预览)
两个要点。一是鸿蒙工程不复制业务代码:FULL 模式下 harmony_pc/entry/src/main/cpp/CMakeLists.txt 会回溯到上级目录直接 glob src/ 里的源码来编,桌面和鸿蒙共用同一份。二是SHELL 那条线是独立的:它只编 shotcut_ohos_shell.cpp(外加可选的 ffdecoder.cpp),不碰 src/、MLT、QML,所以任何时候都能编出来、能起来。SHELL 是先做的,目的是用它把「ArkTS → XComponent → Qt OpenHarmony QPA 插件 → libentry.so → 一个能交互的 Qt 窗口」这条运行链路在鸿蒙上跑通,这条链路已在真机上实测打通,再谈把 Shotcut 真身搬进来。
二、环境要求
| 项目 | 要求 | 备注 |
|---|---|---|
| DevEco Studio | 支持 HarmonyOS PC(Stage 模型)的版本 | 命令行构建要用到自带的 hvigorw / ohpm |
| HarmonyOS SDK | targetSdkVersion / compatibleSdkVersion 均为 6.0.2(22) |
取自 harmony_pc/build-profile.json5 |
| Native 编译器 | BiSheng | 见 build-profile.json5 的 nativeCompiler |
| 目标架构 | arm64-v8a |
见 entry/build-profile.json5 的 abiFilters;自带的 .so 都是 arm64-v8a |
| 目标设备 | 2in1 / tablet |
见 module.json5 的 deviceTypes |
| Qt for HarmonyOS SDK(SHELL) | 仓库自带 Qt 5.15 | 在 harmony_pc/qtforharmony_sdk/,不引用别的项目的 SDK,开箱即用 |
| Qt 6 OHOS SDK(FULL) | 还没有 | FULL 模式的硬门槛,下面单独讲 |
| 签名 | 自己在 DevEco 里生成 | build-profile.json5 里的签名段是本机调试证书路径,换成你自己的 |
bundleName 是 Shotcut 自己的 org.shotcut.shotcut,显示名 Shotcut,版本 25.06.17(versionCode 250617)——这些都从 AppScope/app.json5 取,没改成别的。
三、编译与运行
桌面端不用管,原 Shotcut 的 CMake 构建照旧(参见根目录 README.md)。下面只讲鸿蒙这边。
默认就是 SHELL 模式(entry/build-profile.json5 里传的是 -DSHOTCUT_FULL_APP=AUTO,AUTO 在没满足 FULL 前置条件时落到 SHELL),所以下面的步骤直接就能编出一个能跑的 HAP。
命令行构建
cd harmony_pc
export DEVECO_SDK_HOME=<command-line-tools>/sdk
<command-line-tools>/bin/ohpm install
<command-line-tools>/bin/hvigorw --mode module -p module=entry@default -p product=default assembleHap --no-daemon
产物(未签名)在:
harmony_pc/entry/build/default/outputs/default/entry-default-unsigned.hap
DevEco Studio 构建(第一次上手建议走这条)
- 用 DevEco Studio 打开
harmony_pc/(注意是这个目录,不是仓库根)。 - 跑一次
ohpm install(或让 DevEco 自动 Sync),把oh-package-lock.json5和oh_modules/生成出来。 - 配签名。
build-profile.json5里的signingConfigs现在指向的是本机~/.ohos/config/下的调试证书,换成你自己的——File > Project Structure > Signing Configs里点自动生成就行。 - 选
entry模块,连真机 / 模拟器,点 Run,或者 Build 出 HAP 再装。
关于 QT_PREFIX
native 构建默认传 -DQT_PREFIX=qtforharmony_sdk,这是相对 harmony_pc/ 的路径,解析到本项目自带的 harmony_pc/qtforharmony_sdk。如果你的 Qt SDK 放在别处,去 harmony_pc/entry/build-profile.json5 把它改成绝对路径。
想关掉 FFmpeg 真实播放
SHELL 默认会把自带的 FFmpeg 链进去做真实视频解码(见下文),真机上已实测可放。如果调试时想把变量降到最少,可以传 -DSHOTCUT_ENABLE_FFMPEG=OFF,退回到纯画框的占位预览,UI 照样能起。
四、这个项目是怎么适配的
适配原则跟其它 Qt 移植一样:桌面逻辑不动,鸿蒙专属的东西用 Q_OS_OPENHARMONY 宏隔离,只由鸿蒙工具链编。 但 Shotcut 体量大、QML 又重,实际分成了 SHELL / FULL 两步走,下面逐块说。
1. 入口与崩溃看门狗的处理(src/main.cpp)
Shotcut 桌面端有个「看门狗父进程」机制:主进程会 fork 一个子进程,子进程真正跑 UI,父进程盯着子进程崩没崩、崩了就换个图形后端重启。这套在鸿蒙上根本走不通——鸿蒙端的 app 是被 QPA 插件 dlopen() 进来的 libentry.so,它没法 fork、也没法把自己当可执行文件重新拉起来。所以 main.cpp 里:
Q_OS_OPENHARMONY下把runShotcutDirectly直接置true,跳过看门狗父进程那一整套,直接跑真应用(main.cpp:429)。- 单实例用的
QLocalServer/QLocalSocket(接收从 Finder 之类打开文件的事件)在鸿蒙下用#if !defined(Q_OS_OPENHARMONY)关掉(main.cpp:524)。 - 退出码是
EXIT_RESTART/EXIT_RESET时那段QProcess自重启逻辑也关掉了(main.cpp:554)——鸿蒙端的「改主题后重启」之类要挪到 ArkTS 侧处理。 - 文件末尾导出了一个
extern "C" __attribute__((visibility("default"))) int qtmain(...),里面就一句return main(...)(main.cpp:632)。QPA 插件 dlopenlibentry.so之后找的就是这个符号,它再转回 Shotcut 正常的main()。
这部分是 FULL 移植里少数「在仓库里就能定下来、不依赖 SDK」的工作,已经做完了。
2. ArkTS 壳工程(harmony_pc/entry/src/main/ets)
Stage 模型,三个文件管事:MyAbilityStage(AbilityStage 入口)、EntryAbility(UIAbility)、Index(承载 XComponent 的页面)。EntryAbility.ets 里做了两件事:
import qpa from 'libplugins_platforms_qopenharmony.so',在onWindowStageCreate里调qpa.startQtApplication(this)把 Qt 应用拉起来;onNewWant转给qpa.handleOnNewWant。extractShareIfNeeded():Shotcut 的运行期数据(QML、滤镜预设、MLT profile、LUT、字体、翻译等)要打进 HAP 的 rawfile,但 rawfile 在 OHOS 上是不落盘的、只能通过 resourceManager 读,而 Shotcut / MLT 那套 C++ 代码要的是真实文件系统路径。所以这里写了个首启动时把 rawfile 里的share整个递归拷到沙箱(filesDir/share)的逻辑,拷完打个.extracted_v1标记避免重复拷。这个钩子是给 FULL 模式提前铺好的——SHELL 不读任何 factory 数据,rawfile 里没有share就安静跳过。
3. SHELL:自包含的 Qt Widgets 编辑器壳(shotcut_ohos_shell.cpp)
这是目前真正能在鸿蒙上跑起来的东西。它是一个不依赖 src/、MLT、QML 的纯 Qt 5 Widgets 程序,照着 Shotcut 的工作区布局仿了一套出来:真实的菜单结构(File / Edit / View / Settings / Help)、主工具栏、中间带 16:9 预览和完整走带条(跳到首/尾、播放/暂停、拖动滑块、HH:MM:SS:FF 时间码)的 Player、左右两侧的 Recent / Playlist / Properties / Filters / Export 停靠面板,底部还有一条真能编辑的多轨时间线——能从素材箱追加片段、点选、在播放头处 Split、Ripple Delete(后面的片段往左收)、Lift(留空挡),音频轨上还画了波形。它存在的意义就一句:把运行链路从头到尾验通,证明 ArkTS 到 Qt 窗口这条路在自带的 Qt 5 SDK 上是通的。
CMake 里 SHELL 分支链了 Qt5::Core/Gui/Widgets/Svg/Xml/Network/OpenGL/Multimedia。这里有个踩过的坑:链 Qt5::Gui 会让 hvigor 的 collectAllLibs 把 Qt 的图片格式插件(qgif/qico/qjpeg/qsvg)一起打进去,而 qsvg 插件要 libQt5Svg.so,所以必须把 Qt5::Svg 也链上,否则 QPA 插件加载 libentry.so 时就挂了,表现是一个空白窗口。
4. SHELL 里的真实视频解码(ffdecoder.cpp + 自带 FFmpeg)
只有一个画框的占位预览不够用,所以我们把一套 OHOS arm64 的 FFmpeg 放进了 harmony_pc/third_party/ffmpeg/(头文件 + lib*.so*),运行期那批 .so(libavcodec.so.60 / libavformat.so.60 / libavutil.so.58 / libswscale.so.7 / libswresample.so.4 / libavfilter.so.9 / libavdevice.so.60 / libx264.so.164 等)放在 entry/libs/arm64-v8a/ 跟着 HAP 走。ffdecoder.cpp 是个很薄的封装,用 libavformat/libavcodec/libswscale 把真实媒体文件解成 QImage 帧喂给预览,能 seek、能逐帧、能按秒取帧做拖动,这套解码已在真机上实测可用。
CMake 里这块是有就开、没有就退的:检测到 third_party/ffmpeg/include/libavcodec/avcodec.h 存在且 SHOTCUT_ENABLE_FFMPEG=ON(默认 ON)就编 ffdecoder.cpp、定义 SHOTCUT_HAVE_FFMPEG、链上那批 .so;检测不到就退回画框占位。这样 SHELL 永远能编出来,不会因为缺 FFmpeg 就构建失败。
5. FULL:Shotcut 真身(在建,被外部依赖卡住)
FULL 分支(-DSHOTCUT_FULL_APP=ON)的 CMake 已经是一份真实的构建配方,不是占位:它对着 src/CMakeLists.txt glob src/ 下的源码,排掉那些只在单一平台编的文件(macos.cpp / windowstools.cpp / linuxtools.cpp / DirectShow/D3D/Metal/AVFoundation 控件 / X11 的 windowpicker.cpp / Flatpak 生成器),把 CuteLogger 一起编,最后把 MLT++/FFTW 和 Qt 6 那一摞(Charts / Multimedia / Quick / QuickControls2 / QuickWidgets / Sql / WebSockets / Widgets / Xml 等)链成一个 libentry.so。
但它带前置条件检测:find_package(Qt6 ...) 加 pkg_check_modules(mlt++-7 / fftw3),缺哪个就用 FATAL_ERROR 精确报出缺什么,而不是甩一个泛泛的报错。现在的实情是这些前置条件都还不满足:
- Qt 6 for HarmonyOS SDK(带 QML/Quick/Multimedia/Charts 整套)——本机只有 Qt 5.15,没有 Qt 6 的 OHOS 版本。Shotcut 的 UI 是约 420 个
.qml,没有 Qt 6 的 QML 运行时根本编不动。这是最硬的那个卡点。 - MLT++/MLT、FFmpeg、FFTW、frei0r、SDL2 的 OHOS arm64 交叉编译版。FFmpeg 这块我们已经有一套 arm64(就是 SHELL 在用的那批),可以当 MLT
avformat模块的起点,但 MLT 本体、FFTW、frei0r、SDL2 还都得自己编。
harmony_pc/third_party/README.md 里写了这套依赖怎么交叉编译的构建计划(toolchain、各库的 configure 模板)。这些库(MLT 本体、FFTW、frei0r、SDL2)目前还没交叉编译出来,那里给的是配方模板,照着改版本和 flag 用。
6. FULL 还剩的源码移植活(要等 SDK 到位才能编译验证)
main.cpp 已经 OHOS 化了,但 CMake 注释和移植文档里列了还没收尾的部分,照实转述:
- 把剩下约 40 个平台相关文件里的
Q_OS_OPENHARMONY守卫补全(截屏/X11、原生文件对话框、Docker/Flatpak、硬件编解码选择、melt 二进制查找等)。 - 加一个
shotcut_static_plugins.cpp,用Q_IMPORT_PLUGIN把需要的 Qt / QML / 图片插件静态注册进去——HarmonyOS 不允许从可写目录加载任意.so,所以插件得静态编进来。 - 把
src/qml+filter-sets+voices打进 HAP rawfile,首启动时抽到沙箱(extractShareIfNeeded钩子已经接好了),再把 MLT 的MLT_REPOSITORY/MLT_DATA指过去。
五、功能支持情况(T0 / T1 / T2)
先把口径讲清楚,免得误读。下面这些状态,是基于 SHELL 模式的——也就是目前真正能编、能跑、已经在真机上实测过的那条线。完整的 Shotcut(FULL)还卡在 Qt 6 OHOS SDK 和多媒体原生库上,没法在真机上整体跑起来,所以凡是依赖 FULL 的剪辑能力,下面都标成「未适配」。SHELL 这条线已在真机上逐项验证过:编出 HAP、安装启动、界面渲染、走带与时间线交互、FFmpeg 真实解码预览都实测可用。
状态只有两个,含义如下:
- 能用:在鸿蒙 PC 真机上实际用过,正常。
- 未适配:桌面端特有、或平台没有对应概念的能力,按平台特点主动换了做法或没做,是取舍不是没做完。
T0 — 基础能力(编译、安装、启动、最低限度可用)
| 能力 | 状态 | 说明 |
|---|---|---|
| 编译打包成 HAP(SHELL) | 能用 | hvigorw assembleHap 或 DevEco 都能出 entry-default-unsigned.hap;SHELL 不依赖 src/MLT/QML,已实测稳定编出 |
| 安装 / 启动 / 不白屏 | 能用 | 链路是 ArkTS→XComponent→QPA→libentry.so→Qt 窗口,已在真机上实测正常启动、不白屏;早期 qsvg 缺 libQt5Svg.so 导致空白窗口的坑已修 |
| 主界面渲染(菜单 / 工具栏 / 面板 / 时间线) | 能用 | SHELL 用 Qt Widgets 仿了 Shotcut 工作区,经 XComponent 宿主渲染,真机实测正常显示 |
| 运行期数据抽取到沙箱 | 能用 | EntryAbility.extractShareIfNeeded() 已接好;SHELL 无 share 时安静跳过,FULL 复用 |
小结:T0 这层的目标是「壳能编、能装、能起、界面出得来」,SHELL 这条线已经在真机上把这件事全部跑通——编译、安装、启动、界面渲染都实测正常。
T1 — 主要能力(视频剪辑器的日常核心功能)
| 能力 | 状态 | 说明 |
|---|---|---|
| 视频预览播放(真实解码) | 能用 | SHELL 自带 OHOS arm64 FFmpeg,ffdecoder.cpp 把媒体解成 QImage 帧喂预览,能 seek / 逐帧 / 按秒取帧;真机实测可正常播放 |
| 走带控制(播放 / 暂停 / 跳首尾 / 拖动 / 时间码) | 能用 | SHELL 的 Player 走带条,播放头会真的走,HH:MM:SS:FF 时间码,真机实测正常 |
| 多轨时间线编辑(追加 / 选中 / Split / Ripple Delete / Lift) | 能用 | SHELL 时间线能真编辑,真机实测可用;当前实现是壳自带的近似剪辑,不是 Shotcut/MLT 真身的剪辑引擎 |
| Properties / Filters / Export 面板 | 能用 | SHELL 里是仿出来的面板(绑定选中片段显示名称/轨道/出入点等)并能正常显示;当前实现不接真实的 MLT 滤镜与导出 |
| MLT 驱动的真实剪辑 / 滤镜 / 转场 / 导出 | 未适配 | 依赖 FULL:需要 Qt 6 OHOS SDK + MLT++/MLT + frei0r + SDL2,目前外部依赖未就位 |
| 约 420 个 QML 构成的真实 UI | 未适配 | 依赖 Qt 6 的 QML 运行时,本机只有 Qt 5.15,编不动 |
小结:能在鸿蒙上跑的,是 SHELL 仿出来的编辑工作区,加一个用自带 FFmpeg 做真实解码的视频预览,这些都已在真机上实测正常,视频播放也是真解码、不是占位。Shotcut + MLT 那套真实剪辑还在 FULL 里,被 Qt 6 OHOS SDK 卡着,暂时用不了。
T2 — 增强 / 平台受限能力
| 能力 | 状态 | 说明 |
|---|---|---|
| FFmpeg 真实解码播放 | 能用 | SHELL 已链入自带 FFmpeg,真机实测可正常解码播放;可传 -DSHOTCUT_ENABLE_FFMPEG=OFF 退回占位预览 |
| 单实例 / 跨进程文件打开(QLocalServer) | 未适配 | 桌面看门狗那套,鸿蒙下 app 是被 dlopen 的 .so 没法 fork,已在 main.cpp 守卫关闭 |
| 崩溃看门狗 + 自动换图形后端重启 | 未适配 | 同上,鸿蒙端直接跑真应用、不走父子进程,重启逻辑挪到 ArkTS 侧 |
| 系统截屏 / X11 取窗 / 外部应用 / Flatpak·Docker 打包 | 未适配 | 桌面专属,FULL 移植时按平台特点用 Q_OS_OPENHARMONY 守卫排除(约 40 个文件待收尾) |
| 硬件编解码选择 | 未适配 | 依赖 FULL 的 MLT/FFmpeg 整合,待 SDK 到位 |
小结:T2 里真正落地、且已在真机实测可用的是 FFmpeg 真实解码这一项。其余标「未适配」的分两类:一类是桌面专属的进程/系统能力(看门狗、单实例、截屏、Flatpak 这些),鸿蒙平台没有对应概念,是按平台特点主动取舍掉的;另一类(硬件编解码等)要等 FULL 的依赖齐了才谈得上。
六、整体到什么程度
把三档合起来看:
- T0 基础能力:SHELL 这条线把「能编、能装、能起、界面出得来」全部做到了,运行链路已在真机上跑通并修过坑,编译、安装、启动、界面渲染都实测正常。
- T1 主要能力:跑起来的是 SHELL 仿出来的编辑工作区 + 一个用自带 FFmpeg 做真实解码的视频预览,这些都已在真机上实测可用。Shotcut/MLT 的真实剪辑、约 420 个 QML 的真 UI,都卡在 Qt 6 OHOS SDK 上,属于 FULL 范畴,暂时用不了。
- T2 增强能力:FFmpeg 真实播放真机实测可用;看门狗、单实例、截屏、Flatpak 这些桌面专属能力鸿蒙没有对应概念,按平台特点关掉了,硬件编解码等则要等 FULL 依赖到位。
一句话总结:现在能在鸿蒙真机上跑的,是一个能起、界面照着 Shotcut 仿、还能真播视频的 UI 壳,已实测可正常使用;另有一份已经写好、但卡在「没有 Qt 6 的鸿蒙 SDK 和整套多媒体原生库」的 完整应用构建配方。仓库里能定下来的几块——main.cpp 的入口与看门狗 OHOS 化、FFmpeg 解码、rawfile 抽取钩子——都已经做完;把 Shotcut 真身在鸿蒙上整体跑通,还差仓库外那批交叉编译的二进制依赖。更细的英文移植说明见 harmony_pc/HARMONYOS_PORT.md 和 harmony_pc/third_party/README.md。