ohos_Shotcut:基于 MLT 框架的跨平台视频编辑器项目

开源跨平台视频编辑器(原 mltframework/shotcut,基于 MLT 框架),因 OHOS 仅提供 Qt 5.15、无法承载其 Qt6 主程序,故以 SHELL 壳形态内置 FFmpeg(arm64)承载真实视频播放。针对运行时白屏补齐缺失依赖 libx264.so.164,最终在鸿蒙 PC 跑通"导入视频—时间轴轨道真实时长与缩略图显示—播放"主流程。

分支1Tags0
文件最后提交记录最后更新时间
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.json5nativeCompiler
目标架构 arm64-v8a entry/build-profile.json5abiFilters;自带的 .so 都是 arm64-v8a
目标设备 2in1 / tablet module.json5deviceTypes
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 构建(第一次上手建议走这条)

  1. 用 DevEco Studio 打开 harmony_pc/(注意是这个目录,不是仓库根)。
  2. 跑一次 ohpm install(或让 DevEco 自动 Sync),把 oh-package-lock.json5oh_modules/ 生成出来。
  3. 配签名。build-profile.json5 里的 signingConfigs 现在指向的是本机 ~/.ohos/config/ 下的调试证书,换成你自己的——File > Project Structure > Signing Configs 里点自动生成就行。
  4. 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 插件 dlopen libentry.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*),运行期那批 .solibavcodec.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.mdharmony_pc/third_party/README.md

项目介绍

开源跨平台视频编辑器(原 mltframework/shotcut,基于 MLT 框架),因 OHOS 仅提供 Qt 5.15、无法承载其 Qt6 主程序,故以 SHELL 壳形态内置 FFmpeg(arm64)承载真实视频播放。针对运行时白屏补齐缺失依赖 libx264.so.164,最终在鸿蒙 PC 跑通"导入视频—时间轴轨道真实时长与缩略图显示—播放"主流程。

定制我的领域