Shotcut — HarmonyOS PC Adaptation
This repository remains the original Qt desktop project (Shotcut, a free, open source, cross-platform video editor built on Qt 6 and the MLT multimedia framework). The HarmonyOS PC wrapper lives under:
harmony_pc/
Open harmony_pc/ in DevEco Studio. The wrapper follows the standard
"Qt for HarmonyOS" shape: ArkTS creates a Stage UIAbility, places a
full-window XComponent, loads the Qt OpenHarmony QPA plugin, and starts the
native libentry.so. The QPA plugin dlopen()s libentry.so and calls its
qtmain entry point, which forwards to Shotcut's normal main(). Shotcut's C++
sources are shared with the desktop build; HarmonyOS-only behavior is guarded
with Q_OS_OPENHARMONY.
This wrapper uses a project-local Qt 5.15 for HarmonyOS SDK that lives inside this repository — it does not reference any other project's SDK:
harmony_pc/qtforharmony_sdk/
lib/cmake/Qt5/Qt5Config.cmake
lib/libQt5Core.so
lib/libQt5Widgets.so
lib/libQt5Svg.so
...
Application identity is Shotcut's own: the bundle is org.shotcut.shotcut, the
label is "Shotcut", and signing is intentionally left empty so DevEco Studio can
create/select this project's own signing config.
Layout
harmony_pc/build-profile.json5: app-level HarmonyOS build profile (empty signing config — set up your own in DevEco Studio).harmony_pc/AppScope/app.json5: Shotcut bundle metadata (org.shotcut.shotcut).harmony_pc/entry/: HarmonyOS entry module.harmony_pc/entry/src/main/ets:MyAbilityStage,EntryAbility,EntryBackupAbility, and theIndexpage hosting theXComponent.harmony_pc/entry/src/main/cpp/CMakeLists.txt: nativelibentry.sobuild.harmony_pc/entry/src/main/cpp/shotcut_ohos_shell.cpp: the self-contained Qt Widgets UI shell (SHELL build).harmony_pc/qtforharmony_sdk: this project's own bundled Qt 5.15 SDK.harmony_pc/entry/libs/arm64-v8a/: the Qt OpenHarmony QPA plugin plus the TLS (libssl/libcrypto).sos, copied from the bundled SDK and loaded at runtime.src/main.cpp: gains aQ_OS_OPENHARMONY-guardedextern "C" qtmainalias (default-visibility export for the QPA plugin). The desktop, macOS and Windows paths are unchanged.
Build modes: UI shell vs full app
The native build has two modes, selected by SHOTCUT_FULL_APP
(AUTO / ON / OFF, default AUTO in entry/src/main/cpp/CMakeLists.txt):
- SHELL (AUTO / OFF, the default) — compiles only
entry/src/main/cpp/shotcut_ohos_shell.cpp, a self-contained Qt Widgets approximation of Shotcut's editing workspace: the menu bar (File / Edit / View / Timeline / Player / Help), a main toolbar (Open, Save, Export, Append, Split, Ripple Delete, Lift, Zoom), a left Source / Project media bin, a central Player with a painted 16:9 video preview and a transport bar (skip-to-start/end, play/pause that actually advances a playhead, a scrub slider, and HH:MM:SS:FF timecode), a bottom multi-track Timeline (V2 / V1 / A1 / A2) you can really edit — append clips from the bin, click to select, drag the playhead on the ruler, Split at the playhead, Ripple Delete (later clips pull left) and Lift (leave a gap), with a live audio-style waveform on audio tracks — and a right Filters / Properties panel bound to the selected clip (name, track, in/out, duration, and an add-filter list). It has no dependency on Shotcut'ssrc/sources, MLT, FFmpeg or QML, so it always builds and launches. This proves the ArkTS → XComponent → Qt OpenHarmony QPA →libentry.so→ visible, interactive Qt window pipeline against the bundled SDK. - FULL (ON) — compiles Shotcut's real application. The CMake FULL branch is
now a real build recipe (it mirrors
src/CMakeLists.txt: globs Shotcut's C++/QML, excludes the platform-only files, builds CuteLogger, and links MLT++/FFTW + the Qt 6 stack into onelibentry.so). It is gated on prerequisites: if a Qt 6 OHOS SDK and the native libraries are not found, it stops with a precise list of what's missing instead of a generic error. Enable with-DSHOTCUT_FULL_APP=ONonce those are in place.
FULL-port status — done / blocked
Done (in this repo, no SDK required):
src/main.cppis OHOS-ported: the crash-watchdog child-process fork is skipped underQ_OS_OPENHARMONY(the app is a.soloaded by the QPA plugin and cannot relaunch itself), and the single-instanceQLocalServerIPC andQProcessself-restart are guarded out. Theextern "C" qtmainentry alias is exported.entry/src/main/cpp/CMakeLists.txtFULL branch is the real build recipe (prerequisite detection + precise missing-deps error + the mirrored build).
Blocked on external binaries (cannot be produced in-repo):
- A Qt 6 for HarmonyOS SDK with the QML/Quick/Multimedia/Charts stack. None
exists locally (only Qt 5.15). Without it the real UI (≈420
.qml) cannot compile. This is the hard blocker. - MLT++/MLT, FFmpeg, FFTW, frei0r, SDL2 cross-compiled for OHOS arm64.
(A partial FFmpeg arm64 OHOS build was found elsewhere on this machine —
…/video_compress_repo/ohos/src/main/cpp/thirdparty/FFmpeg/arm64-v8a/lib/,libavcodec.so.60+libavformat.so.60— usable as a starting point for MLT'savformatmodule, but the full set, FFTW, frei0r and SDL2 still need building.) Seeharmony_pc/third_party/README.md.
Remaining source-port work (needs the SDK to compile-verify):
- Finish the
Q_OS_OPENHARMONYguards in the ~40 platform-specific files enumerated below (screencapture/X11, native file dialogs, Docker/Flatpak, hardware-codec selection, melt-binary lookup, etc.). - Add
shotcut_static_plugins.cppregistering the needed Qt/QML/image plugins withQ_IMPORT_PLUGIN(HarmonyOS forbids loading.sofrom a writable dir). - Bundle
src/qml+filter-sets+voicesinto the HAP rawfile and extract to the sandbox on first launch (theEntryAbility.extractShareIfNeeded()hook is already wired); point MLT'sMLT_REPOSITORY/MLT_DATAat it.
Full-app port: prerequisites and roadmap
Shotcut targets Qt 6.4+ and the MLT video engine. Before FULL mode can
compile, these must be cross-compiled for OHOS arm64 and made discoverable via
CMAKE_PREFIX_PATH / CMAKE_FIND_ROOT_PATH:
- Qt 6 for HarmonyOS — the bundled SDK here is Qt 5.15 (enough for the
SHELL). Shotcut's desktop CMake requires Qt6 Charts, Multimedia, Network,
OpenGL, OpenGLWidgets, QuickControls2, QuickWidgets, Sql, WebSockets,
Widgets and Xml, plus the Qt 6 OpenHarmony QPA plugin and the QML runtime
(Shotcut's UI is ~420
.qmlfiles). A Qt 6 OHOS build is the first prerequisite; bundle it alongside (or replacing) the Qt 5 SDK and pointQT_PREFIXat it. - Required native libraries — the multimedia core:
- MLT++ / MLT (
mlt++-7≥ 7.36) — the multimedia authoring framework; Shotcut is essentially a Qt front end to MLT. MLT in turn loads its own runtime modules (avformat,qt,frei0r,sdl2,core, ...). - FFmpeg —
avformat,avcodec,avutil,swscale,swresample(format/codec support; pulls in x264/x265/vpx/opus/lame/… as configured). - FFTW (
fftw3) — used by audio spectrum/scopes. - frei0r — video effect plugins.
- SDL2 — cross-platform audio playback.
- MLT++ / MLT (
- MLT runtime data & module loading — MLT discovers its modules and
presets from an installed
share/mlttree andMLT_REPOSITORY/MLT_DATA/MLT_PROFILES_PATH. HarmonyOS restricts loading arbitrary.sofrom a writable dir, so either (a) compile the needed MLT modules statically intolibentry.soand register the factory entry points directly, or (b) ship them and point the env vars at the extracted sandbox path. Prefer (a) for the modules you keep. - CMake FULL branch — once (1)–(3) exist, replace the
FATAL_ERRORstub inentry/src/main/cpp/CMakeLists.txtwith a build that mirrorssrc/CMakeLists.txt, compiling Shotcut's C++/QML into onelibentry.so(the same shape as the SHELL target) and linking MLT++/FFmpeg/FFTW/SDL2. - Static Qt & QML plugins — Shotcut loads Qt platform/image/multimedia and
QML plugins at runtime. HarmonyOS restricts loading arbitrary
.sofrom a writable dir, so compile the needed ones in and register them withQ_IMPORT_PLUGIN+QPluginLoader::staticInstances(); bundle the QML modules (qmldir+.qml) in the HAP and add their import paths. - Runtime data layout — Shotcut resolves resources (QML, filter presets,
LUTs, fonts, MLT profiles, translations, the
share/shotcuttree) viaQStandardPaths/ its app-dir logic. On HarmonyOS these must be shipped inside the HAP (resources/rawfile) and extracted to the app sandbox on first launch —EntryAbility.extractShareIfNeeded()is already wired for this; guard the installed-prefix discovery withQ_OS_OPENHARMONY. - Desktop-only sources — guard out under
Q_OS_OPENHARMONY: theQT_MEDIA_BACKEND/QT_QPA_PLATFORMhints insrc/main.cpp, the single-instanceQLocalServer/QLocalSocketpath, the GPU watchdog child-process restart logic, the macOS (macos.mm) and Windows (windowstools, drmingw) backends, and the X11WindowPicker.
Native dependencies (already present)
The Qt SDK and QPA plugin are bundled in this project:
harmony_pc/qtforharmony_sdk/lib/cmake/Qt5/Qt5Config.cmake
harmony_pc/entry/libs/arm64-v8a/libplugins_platforms_qopenharmony.so
The native build profile passes the relative SDK path by default
(harmony_pc/entry/build-profile.json5):
"arguments": "-DQT_PREFIX=qtforharmony_sdk -DSHOTCUT_FULL_APP=AUTO"
If your Qt SDK lives elsewhere, edit harmony_pc/entry/build-profile.json5 and
pass an absolute path for QT_PREFIX. The default (relative) value resolves to
this project's own harmony_pc/qtforharmony_sdk.
Build
Desktop (unchanged) — the normal Shotcut CMake build.
HarmonyOS PC from the command line:
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
Unsigned output:
harmony_pc/entry/build/default/outputs/default/entry-default-unsigned.hap
DevEco Studio:
- Open
harmony_pc/as the project root. - Run
ohpm install(or let DevEco Studio sync) sooh-package-lock.json5andoh_modules/are generated. - Configure this project's own signing in DevEco Studio before Run on a real HarmonyOS PC/device (File > Project Structure > Signing Configs).
- Build the
entryHAP.
HarmonyOS behavior
- The SHELL is a Qt Widgets application. The QPA plugin renders its
top-level
QMainWindowinto the ArkTSXComponent; no QML scene is created. (The FULL app is QML-heavy and additionally needs the Qt 6 QML runtime — see the roadmap above.) - The native entry runs the Qt application exactly once per process; the QPA
plugin's later
onNewWantcalls are handled on the ArkTS side. - The desktop build path is unchanged.