macOS 上用 lycium 交叉编译 FFmpeg 适配鸿蒙(OHOS):从构建到 HNP 打包
本文介绍了如何使用仓库现有脚本将OpenSSL 3.6.0和FFmpeg n6.1.4交叉编译为适用于OpenHarmony(OHOS)的库文件,并生成HNP包。主要内容包括: 一键构建流程:通过lycium/build.sh脚本实现OpenSSL和FFmpeg的交叉编译,需要设置OHOS_SDK环境变量。 构建前检查:包括验证OHOS SDK/NDK可用性和检查必要的构建依赖命令。 OpenSS
目标:用仓库现有脚本把 OpenSSL 3.6.0 + FFmpeg(n6.1.4)交叉编译出 OHOS 可用产物(headers + libs),并通过 hnpcli 产出 HNP 包。
涉及的关键文件(均在本仓):
- lycium 顶层编译入口:
lycium/build.sh - HPK 打包与 hnpcli 调用:
lycium/script/build_hpk.sh - 工具链/hnpcli 环境设置:
lycium/script/envset.sh - OpenSSL 配置:
thirdparty/openssl/HPKBUILD - FFmpeg 配置:
thirdparty/FFmpeg/HPKBUILD - HNP 元信息:
thirdparty/FFmpeg/hnp.json
1. 一键构建
在 macOS/Linux 上交叉编译 OHOS 目标时,lycium/build.sh 强依赖 OHOS_SDK 环境变量(脚本内部会自动设置 SYSROOT=${OHOS_SDK}/native/sysroot)。
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg
期望输出(关键行示例,具体版本号以你本机 SDK 为准):
Build OS Darwin
OHOS_SDK=/ABS/PATH/TO/ohos-sdk
CLANG_VERSION=...
Start building openssl 3.6.0!
Compileing OpenHarmony arm64-v8a openssl 3.6.0 libs...
Start building FFmpeg n6.1.4!
Compileing OpenHarmony arm64-v8a FFmpeg n6.1.4 libs...
ALL JOBS DONE!!!
产物位置:
lycium/usr/openssl/<ARCH>/{include,lib,...}
lycium/usr/FFmpeg/arm64-v8a/{include,lib,bin?}
lycium/output/<ARCH>/openssl_3.6.0.tar.gz
lycium/output/arm64-v8a/FFmpeg_n6.1.4.tar.gz
lycium/output/arm64-v8a/*.hnp (若 hnpcli 可用且存在 hnp.json)
2. 构建前检查(把环境信息记录下来)
2.1 检查 OHOS SDK/NDK 是否可用
test -n "${OHOS_SDK}" && echo "OHOS_SDK=${OHOS_SDK}"
test -d "${OHOS_SDK}/native/sysroot" && echo "SYSROOT=${OHOS_SDK}/native/sysroot"
"${OHOS_SDK}/native/llvm/bin/clang" --version | head -n 1
"${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang" --version | head -n 1
"${OHOS_SDK}/native/build-tools/cmake/bin/cmake" --version | head -n 1
if [ -x "${OHOS_SDK}/toolchains/hnpcli" ]; then
"${OHOS_SDK}/toolchains/hnpcli" --version || true
else
echo "hnpcli not found: ${OHOS_SDK}/toolchains/hnpcli"
fi
期望输出(示例):
OHOS_SDK=/ABS/PATH/TO/ohos-sdk
SYSROOT=/ABS/PATH/TO/ohos-sdk/native/sysroot
clang version ...
aarch64-linux-ohos-clang version ...
cmake version ...
hnpcli version ...
2.2 检查构建依赖命令是否齐全
lycium/build.sh 会检查并要求以下命令存在(非 HarmonyOS 主机):gcc g++ cmake make pkg-config autoconf autoreconf automake patch unzip tar git ninja curl sha512sum wget。
for c in gcc g++ cmake make pkg-config autoconf autoreconf automake patch unzip tar git ninja curl sha512sum wget; do
command -v "$c" >/dev/null 2>&1 || echo "missing: $c"
done
3. OpenSSL 3.6.0:确保源码离线可复现
本仓的 OpenSSL 构建被配置为“使用本地 tarball”,不会在构建时下载:
thirdparty/openssl/HPKBUILD:source="openssl-3.6.0.tar.gz"且downloadpackage=false
因此你需要确保文件存在:
ls -lh /Volumes/coder/开源/lycium_plusplus/thirdparty/openssl/openssl-3.6.0.tar.gz
期望输出:
-rw-r--r-- ... openssl-3.6.0.tar.gz
4. FFmpeg(n6.1.4):对齐 OpenSSL 3.6.0 并规避 librtmp 坑
4.1 关键事实(以本仓为准)
thirdparty/FFmpeg/HPKBUILD:pkgver=n6.1.4,depends=("openssl"),且目标架构当前只启用archs=("arm64-v8a")- 交叉编译时,FFmpeg 的
configure会显式注入 OpenSSL include/lib 路径:--extra-cflags="-I$LYCIUM_ROOT/usr/openssl/$ARCH/include"--extra-ldflags="... -L$LYCIUM_ROOT/usr/openssl/$ARCH/lib"
4.2 复制粘贴执行:只编译指定库(推荐)
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg
如果你倾向用外层编排脚本(不改 build.sh):
跳过即可,本仓核心入口就是 lycium/build.sh。
4.3 产物自检:确认是 AArch64 + sysroot 正确
SO="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/arm64-v8a/lib/libavcodec.so"
"${OHOS_SDK}/native/llvm/bin/llvm-readelf" -h "${SO}" | egrep "Class:|Machine:" || true
期望输出(示例):
Class: ELF64
Machine: AArch64
5. HNP 打包(hnpcli):自动触发 + 手动触发
5.1 自动触发(默认)
当你执行 ./build.sh ... 时,库的 archive() 阶段会被调用:
lycium/script/build_hpk.sh的default_archive()会在${LYCIUM_ROOT}/output/$ARCH/下生成tar.gz- 如果
HNP_TOOL非空且安装目录存在hnp.json,会执行:
${HNP_TOOL} pack -i "$packdir" -o "${LYCIUM_ROOT}/output/$ARCH/"
其中 HNP_TOOL 在 lycium/script/envset.sh 的 setarm64ENV() 中设置为:
${OHOS_SDK}/toolchains/hnpcli
thirdparty/FFmpeg/hnp.json 已在仓库内提供(包含 name 与 version),用于避免 name or version argv is miss。
5.2 手动触发(排障用)
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
HNP_TOOL="${OHOS_SDK}/toolchains/hnpcli"
PKG_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/arm64-v8a"
OUT_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/output/arm64-v8a"
test -f "${PKG_DIR}/hnp.json" && cat "${PKG_DIR}/hnp.json"
mkdir -p "${OUT_DIR}"
"${HNP_TOOL}" pack -i "${PKG_DIR}" -o "${OUT_DIR}"
ls -lh "${OUT_DIR}"
期望输出(示例):
.../lycium/output/arm64-v8a/FFmpeg_n6.1.4.tar.gz
.../lycium/output/arm64-v8a/ffmpeg-... .hnp
5.3 编译完成但没有输出产物:排查与解决方案
这里的“没有输出产物”通常分两类:
- A 类:
lycium/usr/<pkg>/<ARCH>/已有产物,但lycium/output/<ARCH>/没有对应的*.tar.gz - B 类:
*.tar.gz有,但*.hnp没有生成(或 hnpcli 打包失败)
5.3.1 快速定位:先看安装目录,再看输出目录
export ARCH="arm64-v8a"
export PKG="FFmpeg"
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}" || true
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}" || true
判断逻辑:
- 如果
lycium/usr/${PKG}/${ARCH}不存在或空:问题通常在package()(install)阶段没有把文件装进去 - 如果安装目录有
include/lib,但lycium/output/${ARCH}没有 tar:问题通常在archive()阶段没跑到或被覆盖/失败了
5.3.2 检查构建日志与失败原因
lycium 会把各库的构建日志落在库目录下,例如:
thirdparty/FFmpeg/FFmpeg-n6.1.4-arm64-v8a-lycium_build.log
thirdparty/openssl/openssl-3.6.0-arm64-v8a-lycium_build.log
另外,如果 build_hpk.sh 内部某条命令失败,会在当前库目录生成 last_error。
cd /Volumes/coder/开源/lycium_plusplus/thirdparty/FFmpeg
ls -la last_error || true
tail -n 80 "FFmpeg-n6.1.4-arm64-v8a-lycium_build.log" || true
5.3.3 A 类问题:没有 *.tar.gz(output 为空)
lycium/script/build_hpk.sh 的默认归档逻辑(default_archive())只有在安装目录存在时才会打包:
packdir=$LYCIUM_ROOT/usr/$pkgname/$ARCH
所以 A 类问题最常见的两个原因:
- 安装目录不完整或没生成:先回看该库的
package()是否执行成功(日志里通常能看到make install) - 该库的
HPKBUILD自己实现了archive(),但实现不正确导致没有写入lycium/output/$ARCH/
兜底手段(不改脚本,直接从安装目录手动打一个 tar.gz):
export ARCH="arm64-v8a"
export PKG="FFmpeg"
export VER="n6.1.4"
OUT="/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"
DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}"
mkdir -p "${OUT}"
cd "${DIR}"
tar -zvcf "${OUT}/${PKG}_${VER}.tar.gz" *
ls -lh "${OUT}/${PKG}_${VER}.tar.gz"
5.3.4 B 类问题:没有 *.hnp(hnpcli 没跑起来)
hnp 打包触发条件(以 build_hpk.sh 默认逻辑为例):
HNP_TOOL非空(通常来自lycium/script/envset.sh,arm64 下默认${OHOS_SDK}/toolchains/hnpcli)- 安装目录里存在
hnp.json
先确认 hnpcli 是否存在且可执行:
test -x "${OHOS_SDK}/toolchains/hnpcli" && "${OHOS_SDK}/toolchains/hnpcli" --version || true
再确认安装目录里是否有 hnp.json:
export ARCH="arm64-v8a"
export PKG="FFmpeg"
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/${PKG}/${ARCH}/hnp.json" || true
如果 hnp.json 不在安装目录但仓库里有(例如 thirdparty/FFmpeg/hnp.json),可以先拷贝再手动 pack:
export ARCH="arm64-v8a"
PKG_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/usr/FFmpeg/${ARCH}"
OUT_DIR="/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"
cp /Volumes/coder/开源/lycium_plusplus/thirdparty/FFmpeg/hnp.json "${PKG_DIR}/hnp.json"
mkdir -p "${OUT_DIR}"
"${OHOS_SDK}/toolchains/hnpcli" pack -i "${PKG_DIR}" -o "${OUT_DIR}"
ls -lh "${OUT_DIR}"
常见现象与对策:
name or version argv is miss:安装目录缺少hnp.json或其name/version为空hnpcli not found:${OHOS_SDK}/toolchains/hnpcli不存在,检查 SDK 安装与路径permission denied:hnpcli 无执行权限,或输出目录不可写
6. 交叉编译环境详解(以本仓为准)
6.1 OHOS_SDK 与 SYSROOT 的关系
在 macOS/Linux 主机上交叉编译时:
- 你需要设置
OHOS_SDK lycium/build.sh会自动设置SYSROOT=${OHOS_SDK}/native/sysroot
工具链环境变量主要在 lycium/script/envset.sh 中按架构设置(例如 setarm64ENV()):
- 编译器:
CC=${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang - 归档工具:
AR=${OHOS_SDK}/native/llvm/bin/llvm-ar - HNP 工具:
HNP_TOOL=${OHOS_SDK}/toolchains/hnpcli - sysroot:
CFLAGS/LDFLAGS里包含--sysroot=${SYSROOT}
如果遇到 sysroot/头文件找不到,优先确认这两点:
echo "OHOS_SDK=${OHOS_SDK}"
echo "SYSROOT=${SYSROOT}"
test -d "${SYSROOT}/usr/include" && echo "sysroot include ok"
6.2 ARCH 如何决定(为什么默认只编 arm64-v8a)
每个库的 HPKBUILD 里通过 archs=(...) 控制目标架构集合:
thirdparty/FFmpeg/HPKBUILD当前只启用archs=("arm64-v8a")thirdparty/openssl/HPKBUILD当前启用archs=("armeabi-v7a" "arm64-v8a" "x86_64")
lycium 会对 archs 逐一循环编译,并把产物分别安装到:
lycium/usr/<pkgname>/<ARCH>/
lycium/output/<ARCH>/
如果你希望 FFmpeg 也支持 armeabi-v7a 或 x86_64,第一步通常是扩展 thirdparty/FFmpeg/HPKBUILD 的 archs,并按 ARCH 分支补齐 setarm32ENV/setx86_64ENV 的配置、以及 --arch=/--extra-ldflags= 等参数。
6.3 pkg-config 路径与依赖发现机制
lycium/script/build_hpk.sh 会在配置阶段按依赖自动拼出 pkgconfigpath,并以 PKG_CONFIG_LIBDIR="${pkgconfigpath}" 的方式注入到构建流程。
这意味着:
- 依赖库必须先安装到
lycium/usr/<dep>/<ARCH>/,并且其lib/pkgconfig/*.pc需要存在(如果该库依赖 pkg-config 发现) - 依赖链必须写在
HPKBUILD的depends=(...)里,并且执行./build.sh时参数要包含依赖项(例如./build.sh openssl FFmpeg)
快速自检(以 OpenSSL 为例):
export ARCH="arm64-v8a"
ls -la "/Volumes/coder/开源/lycium_plusplus/lycium/usr/openssl/${ARCH}/lib/pkgconfig" || true
6.4 FFmpeg 的“host build”行为(为什么会先构建一次宿主)
thirdparty/FFmpeg/HPKBUILD 里存在一个 buildhost=true 的流程:先在宿主(macOS/Linux)上构建一套 host 工具与库,用于后续部分测试/生成步骤,再进入 OHOS 交叉编译阶段。
当你看到日志里先出现一次 host configure/make,再出现 Compileing OpenHarmony arm64-v8a FFmpeg ...,这是预期行为。
6.5 增量构建与强制重编(hpk_build.csv)
lycium 会在 lycium/usr/hpk_build.csv 记录“已完成构建”的库,后续再次执行 ./build.sh 会跳过这些库(除非你改了版本或清理了记录)。
定位是否跳过某库:
cat /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv | tail -n 50
grep -n ",FFmpeg," /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv || true
grep -n ",openssl," /Volumes/coder/开源/lycium_plusplus/lycium/usr/hpk_build.csv || true
强制重编的做法(原则):删除对应库在 hpk_build.csv 里的记录,再重新执行 ./build.sh ...。
6.6 常用交叉编译命令清单
只编一个库(例如只编 OpenSSL):
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl
编译多个库并显式包含依赖(推荐):
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
cd /Volumes/coder/开源/lycium_plusplus/lycium
./build.sh openssl FFmpeg
用外层编排脚本跑 OHOS 目标(不改 build.sh):
跳过即可,本仓核心入口就是 lycium/build.sh。
7. HNP 打包进阶(打包规则与可复用模板)
7.1 HNP 自动打包触发条件(再强调一次)
以 lycium/script/build_hpk.sh 的默认逻辑为准,HNP 打包需要同时满足:
HNP_TOOL非空(通常为${OHOS_SDK}/toolchains/hnpcli)- 安装目录存在
hnp.json(位于lycium/usr/<pkgname>/<ARCH>/hnp.json)
如果 hnp.json 不在安装目录,默认归档逻辑会尝试从库源码目录拷贝过去:
if [ ! -f "$packdir/hnp.json" ] && [ -f "${PWD}/hnp.json" ]
then
cp "${PWD}/hnp.json" "$packdir"
fi
所以更稳妥的放置策略是:把 hnp.json 放在库目录(与 HPKBUILD 同级),例如:
thirdparty/FFmpeg/hnp.json
7.2 hnp.json 的最小字段与错误对照
本仓的 FFmpeg 示例:
{
"type":"hnp-config",
"name":"ffmpeg",
"version":"n6.1.4",
"install":{}
}
常见错误与字段关系:
name or version argv is miss:hnp.json缺失或name/version为空- “能打 tar 但不出 hnp”:通常是
HNP_TOOL未设置或hnpcli不可执行
7.3 为某个库自定义 archive() 的推荐模板
如果某个库需要自定义 archive()(例如想改包名、改输出路径、或额外拷贝文件),建议遵循本仓约定:
- tar.gz 输出到:
${LYCIUM_ROOT}/output/$ARCH/ - HNP 输出到:
${LYCIUM_ROOT}/output/$ARCH/ - 打包输入目录为安装目录:
${LYCIUM_ROOT}/usr/$pkgname/$ARCH
示例模板:
archive() {
mkdir -p "${LYCIUM_ROOT}/output/${ARCH}"
packdir="${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}"
if [ -d "${packdir}" ]
then
if [ ! -f "${packdir}/hnp.json" ] && [ -f "${PWD}/hnp.json" ]
then
cp "${PWD}/hnp.json" "${packdir}"
fi
pushd "${packdir}" > /dev/null
tar -zvcf "${LYCIUM_ROOT}/output/${ARCH}/${pkgname}_${pkgver}.tar.gz" *
popd > /dev/null
if [ -n "${HNP_TOOL}" ] && [ -f "${packdir}/hnp.json" ]
then
"${HNP_TOOL}" pack -i "${packdir}" -o "${LYCIUM_ROOT}/output/${ARCH}/"
fi
fi
}
7.4 多架构 HNP 与产物命名建议
如果同一库启用了多个 archs,建议在产物命名上体现 ARCH,避免覆盖:
${pkgname}_${pkgver}_${ARCH}.tar.gz
对应的 HNP 输出建议也按 ARCH 分目录组织(本仓默认就是按 output/$ARCH/ 输出)。
7.5 hnpcli 参数与打包结果校验
本仓默认调用参数为:
hnpcli pack -i <install_dir> -o <output_dir>
你可以先查看本机 hnpcli 支持的参数:
export OHOS_SDK="/ABS/PATH/TO/ohos-sdk"
"${OHOS_SDK}/toolchains/hnpcli" --help || true
"${OHOS_SDK}/toolchains/hnpcli" pack --help || true
打包完成后,至少确认两件事:
export ARCH="arm64-v8a"
ls -lh "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}" || true
test -f "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}/FFmpeg_n6.1.4.tar.gz" && echo "tar.gz ok"
ls "/Volumes/coder/开源/lycium_plusplus/lycium/output/${ARCH}"/*.hnp 2>/dev/null || true
8. 常见报错速查(按症状直接定位)
8.1 hnpcli 报错:name or version argv is miss
- 确保安装目录存在
hnp.json(本仓已提供thirdparty/FFmpeg/hnp.json) - 手动 pack 一次验证参数:
hnpcli pack -i <pkgdir> -o <outdir>
8.2 OpenSSL 下载失败/不可复现
- 本仓 OpenSSL 默认不下载,必须提供
thirdparty/openssl/openssl-3.6.0.tar.gz
8.3 librtmp + OpenSSL 3.x 链接失败
- 典型根因:librtmp 依赖旧 OpenSSL API
- 优先策略:保证主链路稳定,禁用 librtmp(在 FFmpeg 的配置选项中不要启用它)
8.4 FFmpeg version 显示为 git hash(例如 82e1f01)
当 FFmpeg 在 git 源码树里构建时,版本信息可能携带 commit hash(更像“构建标识”而不是语义版本)。如果你需要稳定展示版本,优先用包元信息:
thirdparty/FFmpeg/HPKBUILD的pkgver=n6.1.4thirdparty/FFmpeg/hnp.json的version=n6.1.4
8.5 同时出现 host 与 OHOS 两段构建,且日志很长
这是 thirdparty/FFmpeg/HPKBUILD 的预期行为:会先构建一套宿主产物(hostbuild),再进入 OHOS 交叉编译阶段。排查交叉编译问题时,优先关注包含 Compileing OpenHarmony <ARCH> FFmpeg ... 的那段日志。
更多推荐




所有评论(0)