鸿蒙PC迁移:Shotcut 开源视频编辑器鸿蒙PC适配全记录 —— 从在鸿蒙跑起来到真能导入视频、轨道显示真实画面
一、写在前面
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态。鸿蒙PC开发者社区:https://harmonypc.csdn.net/
鸿蒙适配仓库:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_shotcut
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
这篇文章记录的是 Shotcut——一个 C++ / Qt 6 + MLT 的开源跨平台视频编辑器——在 HarmonyOS PC / OpenHarmony PC 上的一次完整适配过程。这次的适配走得比较深入:不仅把 Shotcut 的编辑器界面在鸿蒙上跑了起来,还接上了 FFmpeg,做到了“导入真实视频 → 预览里真播放 → 时间线轨道显示真实画面缩略图”,中间还踩了一个非常典型的坑——加完 FFmpeg 之后白屏,最后用 objdump 把依赖闭包查出来,发现是缺一个 libx264.so.164。
和 Web 套壳应用不同,把这样一个 Qt 桌面视频编辑器搬上鸿蒙 PC,难点在于:
- 怎样让一个 Qt 桌面应用进入鸿蒙 Stage 模型,由
libentry.so启动(Qt for HarmonyOS 的 QPA 思路)。 - Shotcut 全量是 Qt6 + 大量 QML + MLT/FFmpeg/FFTW/frei0r/SDL,短期内不可能全部交叉编译到 OHOS arm64,怎样先把 Shotcut 的编辑器界面在鸿蒙上跑起来、打通整条渲染/交互链路。
- “界面跑起来”不等于“能剪视频”——怎样在没有 Qt6 OHOS SDK、没有 MLT 的情况下,绕开 MLT、直接用 FFmpeg 把“真打开、真解码、真播放”做出来。
- 加完 FFmpeg 之后白屏,怎样定位是哪一步、哪一个库出了问题。
本次适配采用逐步验证的路线:保留 Shotcut 原有 C++ 主体不动,新建 harmony_pc/ 作为鸿蒙适配工程;ArkTS 侧只负责 Ability、窗口和 XComponent;界面由 Qt 运行时承载;鸿蒙特有改动全部用 Q_OS_OPENHARMONY 收敛,桌面构建完全不受影响。

二、项目背景:Shotcut 是 Qt6 + MLT 的大型视频编辑器
确认它是什么很简单:根目录有 CMakeLists.txt,里面是 find_package(Qt6 6.4 REQUIRED ...) 和 pkg_check_modules(mlt++ REQUIRED IMPORTED_TARGET mlt++-7>=7.36.0);README.md 第一行就写着「Shotcut - a free, open source, cross-platform video editor」。入口在 src/main.cpp,是一个继承自 QApplication 的 Application。
代码体量与依赖(节选):
- C++:约 191 个
.cpp/ 200 个.h;QML:约 422 个.qml(界面大量用 Qt Quick)。 - 直接依赖:MLT(多媒体框架,视频引擎核心)、Qt 6.4+、FFmpeg(编解码)、FFTW、frei0r(视频滤镜插件)、SDL(音频播放)。
- 构建系统:CMake;链接的 Qt6 模块有 Charts / Multimedia / Network / OpenGL / QuickControls2 / QuickWidgets / Sql / WebSockets / Widgets / Xml。
原始项目结构(节选):
ohso_shotcut/
├── CMakeLists.txt # 顶层 CMake(Qt6.4+、mlt++-7、FFTW)
├── src/ # 主源码(C++ + QML)
│ ├── main.cpp # 程序入口(Application : QApplication)
│ ├── mainwindow.cpp # 主窗口
│ ├── mltcontroller.cpp # 与 MLT 引擎交互
│ ├── player.cpp # 播放器
│ ├── qml/ # ~420 个 .qml(滤镜、示波器、视图…)
│ └── ...
└── harmony_pc/ # 本次新增的鸿蒙适配工程

三、鸿蒙适配工程:Ability + XComponent + Qt QPA
Qt for Harmony 的关键思路:ArkTS 侧创建鸿蒙窗口,页面里放一个 XComponent,再由 Qt OpenHarmony QPA 插件把 Qt 窗口挂上去。Index.ets 很薄,核心就是这个 XComponent:
XComponent({
id: this.windowId,
type: XComponentType.NODE,
libraryname: 'plugins_platforms_qopenharmony'
}).width('100%').height('100%');
EntryAbility.ets 在 onWindowStageCreate 里调用 qpa.startQtApplication(this),QPA 插件会去 dlopen 应用的 libentry.so 并调用它的 qtmain。所以在 Shotcut 的入口 src/main.cpp 末尾加一个仅鸿蒙生效的入口别名,转发到 Shotcut 原本的 main():
#if defined(Q_OS_OPENHARMONY)
// QPA 插件 dlopen libentry.so 后调用的就是这个 qtmain
extern "C" __attribute__((visibility("default"))) int qtmain(int argc, char *argv[])
{
return main(argc, argv);
}
#endif
Shotcut 桌面版有一套“看门狗父子进程”启动逻辑(崩溃后用 QProcess 重启自己)和单实例 QLocalServer IPC——这些在鸿蒙上行不通(应用是被 QPA 加载的 .so,不能 fork/重启自己)。所以在 main.cpp 里用 Q_OS_OPENHARMONY 把它们守掉,鸿蒙下直接运行应用、跳过看门狗与单实例 IPC:
#if defined(Q_OS_OPENHARMONY)
const bool runShotcutDirectly = true; // 鸿蒙:直接跑,不 fork 看门狗
#else
const bool runShotcutDirectly = ::qEnvironmentVariableIsSet(kWatchdogEnvVar);
#endif
这些都在 Q_OS_OPENHARMONY 守卫里,Linux/macOS/Windows 构建一个字节都不变。
鸿蒙适配工程集中放在 harmony_pc/:
harmony_pc/
├── AppScope/app.json5 # 包名 org.shotcut.shotcut
├── build-profile.json5 # 应用级(签名、SDK 版本)
├── entry/
│ ├── build-profile.json5 # 传 -DQT_PREFIX / -DSHOTCUT_FULL_APP
│ ├── libs/arm64-v8a/ # QPA 插件 + OpenSSL + 后面要加的 FFmpeg
│ └── src/main/
│ ├── cpp/CMakeLists.txt # 编成 libentry.so(SHELL/FULL 两模式)
│ ├── cpp/shotcut_ohos_shell.cpp # Shotcut 编辑器在鸿蒙的运行入口(Qt Widgets)
│ ├── cpp/ffdecoder.{h,cpp} # FFmpeg 视频解码器(后面加)
│ └── ets/ # AbilityStage / EntryAbility / Index
├── third_party/ffmpeg/ # 项目自带的 FFmpeg(OHOS arm64,后面加)
├── qtforharmony_sdk/ # 项目自带的 Qt 5.15 for Harmony SDK
└── HARMONYOS_PORT.md # 适配说明 + FULL 模式路线图

四、自带 Qt SDK + 先打通运行链路
适配有一条硬性要求:Qt for Harmony SDK 必须放进当前项目、引用项目内自己的 SDK。所以把整套 234MB 的 Qt 5.15 for Harmony SDK 复制到 harmony_pc/qtforharmony_sdk/,CMake 从 harmony_pc 反向定位项目根并强制用项目内置 SDK。
Shotcut 全量是 Qt6 + 一票还没有 OHOS arm64 版的原生库,直接上全量不现实。所以 CMakeLists.txt 做了两种构建模式(SHOTCUT_FULL_APP = AUTO | ON | OFF):
- SHELL 模式(默认 AUTO):先以 Qt Widgets 形式把 Shotcut 的编辑器界面在鸿蒙上跑起来,这一步不牵动 MLT 的全量原生依赖,保证能编、能起、能交互,先把“ArkTS → XComponent → QPA → libentry.so → 可见 Qt 窗口”这条链路打通。
- FULL 模式(
-DSHOTCUT_FULL_APP=ON):把 Shotcut 的全量源码(src/+ MLT 引擎)一起编进来。当前会主动FATAL_ERROR并列出前置条件——因为它需要 Qt6 OHOS 工具链 + MLT/FFmpeg/FFTW/frei0r/SDL 全套交叉编译到 OHOS arm64。
鸿蒙上的编辑器界面按 Shotcut 的布局实现:顶部 File / Edit / View / Settings / Help 菜单 + 工具栏;中间 Source / Project 标签的播放器 + 走带条;右侧 Properties / Filters / Export 选项卡式停靠面板;底部 多轨时间线(V2 / V1 / A1 / A2,带轨道头隐藏/静音/锁定开关、Append/Ripple Delete/Lift/Split/缩放工具条)。

五、关键转折:用 Qt5 + FFmpeg 直接做“真功能”
界面在鸿蒙上跑起来之后,下一步就是把真实的视频处理能力接上——真正打开、解码、播放视频文件。要做这件事,第一反应是上 Qt6 + MLT,但本地既没有 Qt6 OHOS SDK,也没有 MLT 的 OHOS 库。
转机来自两个事实(都查证过):
- 这套 Qt5 OHOS SDK 自带能用的多媒体后端:
plugins/mediaservice/libplugins_mediaservice_qtmedia_openharmony.so、plugins/audio/libplugins_audio_qtaudio_ohaudiodevice.so——也就是说 Qt 多媒体在鸿蒙上是能出声出画的。 - 本地已有一套 FFmpeg 的 OHOS arm64 产物(头文件 + 库齐全:avcodec / avformat / avfilter / avutil / swscale / swresample)。
关键认知:真剪辑的活其实是 FFmpeg 在干,MLT 只是它上面的封装层。既然 FFmpeg-OHOS 有了,就绕开 MLT、直接用 FFmpeg 做真功能;UI 继续用现成的 Qt5。于是把 FFmpeg 收进 harmony_pc/third_party/ffmpeg/,写了一个最小视频解码器 ffdecoder.cpp(libavformat + libavcodec + libswscale,open / seek / nextFrame → QImage),并在 CMake 里加 SHOTCUT_HAVE_FFMPEG 守卫:检测到 FFmpeg 就编入真解码、链 FFmpeg;没有 FFmpeg 时也能编过,方便单独验证界面。
set(FFMPEG_DIR "${HARMONY_PROJECT_ROOT}/third_party/ffmpeg")
if (SHOTCUT_ENABLE_FFMPEG AND EXISTS "${FFMPEG_DIR}/include/libavcodec/avcodec.h")
set(SHOTCUT_HAVE_FFMPEG ON)
list(APPEND SHELL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ffdecoder.cpp")
endif()
六、踩坑:加了 FFmpeg 之后白屏 —— 缺 libx264.so.164
把 FFmpeg 链进去、重新构建、装到真机——白屏。在此之前界面是正常显示的,所以问题一定出在 FFmpeg。
原理:libentry.so 在加载时必须先解析它依赖的所有 FFmpeg 库,只要有一个解析不了,整个 .so 加载失败 → QPA 拿不到窗口 → 白屏。于是用 objdump 把 FFmpeg 库的依赖闭包查出来:
objdump -p libavcodec.so.60 | grep NEEDED
# NEEDED libswresample.so.4
# NEEDED libavutil.so.58
# NEEDED libx264.so.164 ← 这个没打包!
# NEEDED libz.so (鸿蒙系统库)
# NEEDED libc.so (系统库)
根因找到了:libavcodec.so.60 依赖 libx264.so.164(H.264 编码器),而我们只打包了 FFmpeg 全家、漏了 x264。libz.so / libc.so 是鸿蒙系统库(设备自带)。把 libx264.so.164 也收进 third_party/ffmpeg/lib 和 entry/libs/arm64-v8a(打进 HAP),依赖闭包补齐,白屏解决。同时给 CMake 加了一个 -DSHOTCUT_ENABLE_FFMPEG=OFF 开关,万一还白屏可以一键回退到不链 FFmpeg 的构建来隔离问题。
经验:在鸿蒙上链接第三方
.so,一定要先objdump -p xxx.so | grep NEEDED把整条依赖链列清楚、逐个确认都打进了 HAP,否则就是“加载失败 → 白屏”。版本号后缀(.so.60、.so.164)的库在鸿蒙上是能正常加载的,不用强行改名。

七、真播放 + 时间线真实片段
把 FFmpeg 接进 UI 之后:
- 真打开 + 真播放:菜单 File → Open File… 用真实文件对话框选一个视频,
ffdecoder真解码,预览区画真实帧;播放/拖拽/逐帧/跳转都驱动真解码与 seek,时间码、总时长来自真实文件。 - 时间线显示真实片段:导入成功后,把这个视频作为真实片段加到时间线 V1 轨——用真实时长,并解一帧画成缩略图铺在轨道上,轨道就“显示出视频数据”了。第一次导入会清掉初始的演示片段,让轨道干净地只显示你导入的视频。
到这一步,“导入视频 → 预览真播放 → 轨道显示真实画面”这条最核心的体验就真的跑通了(仍在 Qt5 + FFmpeg 这条路上,不依赖 Qt6 / MLT)。

八、桌面构建零影响 + FULL 路线图
所有鸿蒙改动都收敛在 Q_OS_OPENHARMONY / SHOTCUT_HAVE_FFMPEG 守卫里,Shotcut 的 Linux/macOS/Windows 桌面构建完全不受影响。
需要说清楚的边界:当前跑通的是 Qt5 + FFmpeg 这条“真播放 + 轨道真实片段”的路;而 Shotcut 全量(FULL) —— 真正用 MLT 引擎做多轨合成、关键帧、几百个滤镜、时间线渲染导出 —— 还需要 Qt6 OHOS 工具链 + MLT/FFmpeg/FFTW/frei0r/SDL 全套交叉编译到 OHOS arm64,这部分作为后续路线图保留在 harmony_pc/HARMONYOS_PORT.md 与 third_party/README.md 里。
九、小结
- 用 Qt for Harmony 的 QPA 思路(ArkTS + XComponent +
qtmain)把 Qt 桌面应用搬上了鸿蒙 PC,鸿蒙改动全部Q_OS_OPENHARMONY收敛,桌面零影响。 - 先把 Shotcut 的编辑器界面在鸿蒙上跑通(打通“能编、能起、能交互”的链路),再在没有 Qt6 / MLT 的现实下,绕开 MLT、直接用 Qt5 + FFmpeg 做出了“真打开、真解码、真播放、轨道真实缩略图”。
- 踩了一个很典型的坑:第三方
.so的依赖闭包没补全(缺libx264.so.164)会直接白屏——objdump -p | grep NEEDED是定位这类问题的利器。
更多推荐




所有评论(0)