开源鸿蒙PC三方库交叉编译
开源鸿蒙PC三方库交叉编译:turbojpeg(libjpeg-turbo)交叉编译与验证记录
适用目标:鸿蒙小车(OpenHarmony,arm64-v8a 开发板)
复用环境:实验「鸿蒙 PC 三方库交叉编译」搭建的 lycium++ 框架与 OHOS_SDK
用途:既可作为实验的延续(环境已搭好,直接看第2节起),也可作为从零搭建教程(第1节为完整环境配置,适用于全新电脑)
记录日期:2026-07-27
1. 环境配置(从零搭建,适用于全新电脑)
本章假设读者拿到的是一台只装了 Windows、什么都没配的电脑,目标是最终能在 Ubuntu 虚拟机里用 lycium++ 交叉编译 OpenHarmony 三方库。除特别说明外,所有命令都在 Ubuntu 终端执行。
如果你已经按实验搭好环境,本章可略读,直接跳到第 2 节;但建议对照 1.7 速查表确认路径一致。
1.0 准备清单
- 一台 Windows 主机(CPU 需支持虚拟化 VT-x / AMD-V,进 BIOS 开启)
- 能联网(下载 VMware、Ubuntu、SDK 用)
- 磁盘预留 ≥ 60 GB(虚拟机系统 + OHOS SDK + 编译中间产物)
1.1 第一步:安装 VMware + 创建 Ubuntu 虚拟机
- 下载并安装 VMware Workstation Pro(或免费的 VMware Player)。
- 下载 Ubuntu 20.04 / 22.04 的 ISO 镜像(本记录环境为 Ubuntu 20.04)。
- 新建虚拟机:
- 客户机系统选 Linux / Ubuntu 64 位
- 内存 ≥ 4 GB,处理器 ≥ 2 核,磁盘 ≥ 40 GB(选"将虚拟磁盘拆分成多个文件"便于共享)
- 网络适配器设为 NAT(最易通外网;桥接需手动配 IP,新手不建议)
- 勾选"安装 VMware Tools"(或装完系统后用 1.2 的
open-vm-tools代替)
- 开机,按引导安装 Ubuntu,创建普通用户(示例后续用
/home/ohpkg思路,实际登录用户随意;框架位置/home/lycium_plusplus与登录用户无关)。
联网排错:若 NAT 下
ping 8.8.8.8不通或apt超时,多半是 DNS / 镜像源问题。先确认网卡已 up:ip link set ens33 up && dhclient ens33;再把/etc/apt/sources.list换成国内源(见 1.2)。
1.2 第二步:Ubuntu 基础依赖安装
# 换国内源(以阿里云、Ubuntu 20.04 为例)
sudo sed -i 's|http://archive.ubuntu.com|https://mirrors.aliyun.com|g' /etc/apt/sources.list
sudo sed -i 's|http://security.ubuntu.com|https://mirrors.aliyun.com|g' /etc/apt/sources.list
sudo apt update
# 安装交叉编译与框架所需基础工具
sudo apt install -y git curl wget unzip tar \
build-essential cmake ninja-build \
python3 python3-pip \
open-vm-tools open-vm-tools-desktop \
net-tools libncurses5
验证安装成功:
git --version
cmake --version | head -1
ninja --version
python3 --version
1.3 第三步:下载并配置 OpenHarmony SDK(OHOS_SDK)
lycium++ 编译时靠 OHOS_SDK 环境变量定位 Clang 交叉工具链(即 native/llvm/bin/...)。
# 固定目录(可自定义,记得同步改下方环境变量路径)
sudo mkdir -p /home/ohpkg
cd /home/ohpkg
# 下载 Public SDK 包。版本号以镜像站当前为准;
# 若 404,回到 https://repo.huaweicloud.com/openharmony/os/ 复制最新地址
wget https://repo.huaweicloud.com/openharmony/os/6.0-Release/ohos-sdk-windows_linux-public.tar.gz
# 解压主包
tar -zxf ohos-sdk-windows_linux-public.tar.gz
cd /home/ohpkg/linux
# 解压 native 工具链(包内文件名含版本号,用通配符匹配)
unzip -o -q native-linux-x64-*.zip
# 配置环境变量,写入 ~/.bashrc 永久生效
echo 'export OHOS_SDK=/home/ohpkg/linux' >> ~/.bashrc
source ~/.bashrc
验证工具链存在(Clang 版本应与本记录 15.0.4 一致):
ls $OHOS_SDK/native/llvm/bin/aarch64-linux-ohos-clang
$OHOS_SDK/native/llvm/bin/clang --version
关键点:
OHOS_SDK指向包含native目录的那一层(即linux目录本身),不是native里面。lycium 的build.sh会自动读这个变量,无需每次手设。
1.4 第四步:克隆 lycium_plusplus 框架
cd /home
git clone https://gitcode.com/OpenHarmonyPCDeveloper/lycium_plusplus.git
cd lycium_plusplus
ls # 应看到 community/ lycium/ thirdparty/ README.md 等
ls lycium/build.sh # 构建入口脚本,必须在 lycium/ 子目录下执行
框架根目录即
/home/lycium_plusplus——注意不是~/lycium,也不是/root/lycium。
1.5 第五步:配置 VMware 共享文件夹(用于把产物拷回 Windows)
- 在 VMware 菜单:虚拟机 → 设置 → 选项 → 共享文件夹,启用"总是启用",添加一条:
- 名称填
vmshare - 路径指向 Windows 上一个空目录(本记录用
E:\OpenHarmony\vmshare)
- 名称填
- 在 Ubuntu 挂载:
sudo mkdir -p /mnt/vmshare
# 多数情况系统已自动挂载;手动挂载用:
sudo vmhgfs-fuse .host:/vmshare /mnt/vmshare -o allow_other
# 若报 No such device / not found,先查真实共享名再替换:
vmware-hgfsclient
# 然后用查到的名字: sudo vmhgfs-fuse .host:/<查到的名> /mnt/vmshare -o allow_other
Windows 端对应目录即 E:\OpenHarmony\vmshare\,两边可互拷文件。
1.6 第六步:验证整套环境(用 cups 跑一次)
cd /home/lycium_plusplus/lycium
./build.sh cups
若能顺利下载、编译并出现 Build cups ... end! / ALL JOBS DONE!!! 之类输出,说明环境已打通,可继续编译任意三方库。
1.7 落定后的环境速查表
| 项 | 值 | 说明 |
|---|---|---|
| 编译机 | VMware Ubuntu 20.04(NAT 联网) | 与 Windows 通过 VMware 共享文件夹互通 |
| 交叉编译框架 | lycium++,/home/lycium_plusplus |
入口 lycium/build.sh |
| OHOS_SDK | /home/ohpkg/linux |
含 native/llvm 交叉工具链 |
| 编译器 | OHOS Clang 15.0.4 | native/llvm/bin/aarch64-linux-ohos-clang |
| 目标架构 | arm64-v8a | 小车板架构,与实验3 libplacebo 的 arm64 产物一致 |
| 共享文件夹(VM) | /mnt/vmshare(vmhgfs-fuse .host:/vmshare) |
用于把产物拷回 Windows |
| 共享文件夹(Win) | E:\OpenHarmony\vmshare\ |
Windows 端对应路径 |
关键环境变量(已写入 ~/.bashrc):
OHOS_SDK=/home/ohpkg/linux
2. 交叉编译过程
2.1 定位构建入口
一开始 cd ~/lycium 失败,框架实际在 /home/lycium_plusplus;且根目录没有 build.sh,入口在 lycium/ 子目录:
cd /home/lycium_plusplus
ls -la # 确认根目录结构
find /home/lycium_plusplus -maxdepth 3 -name 'build.sh'
# 输出:/home/lycium_plusplus/lycium/build.sh
2.2 确认配方存在
lycium++ 自带 libjpeg-turbo 配方,无需手动写 HPKBUILD:
ls thirdparty/ | grep -i jpeg
# 输出:jpeg libjpeg-turbo openjpeg
2.3 执行编译
cd /home/lycium_plusplus/lycium
./build.sh libjpeg-turbo
成功输出(节选):
Build OS linux
OHOS_SDK=/home/ohpkg/linux
CLANG_VERSION=15.0.4
...
modulename: libjpeg-turbo, ...
Start building libjpeg-turbo 2.1.91!
Downloading libjpeg-turbo-2.1.91.zip
libjpeg-turbo-2.1.91.zip: 成功
Compileing OpenHarmony armeabi-v7a libjpeg-turbo 2.1.91 libs...
Compileing OpenHarmony arm64-v8a libjpeg-turbo 2.1.91 libs...
Build libjpeg-turbo 2.1.91 end!
ALL JOBS DONE!!!
同时编出 armeabi-v7a 与 arm64-v8a 两套;小车板用 arm64-v8a。
2.4 定位编译产物
find /home/lycium_plusplus/thirdparty/libjpeg-turbo -name '*.so*'
find /home/lycium_plusplus/thirdparty/libjpeg-turbo -name 'turbojpeg.h' -o -name 'jpeglib.h'
arm64-v8a 关键产物(真实文件,另两个是符号链接):
.../libjpeg-turbo-2.1.91/arm64-v8a-build/libturbojpeg.so.0.3.0 ← 真实文件(我们要的)
.../libjpeg-turbo-2.1.91/arm64-v8a-build/libturbojpeg.so ← 符号链接
.../libjpeg-turbo-2.1.91/arm64-v8a-build/libturbojpeg.so.0 ← 符号链接
头文件:.../libjpeg-turbo-2.1.91/turbojpeg.h
3. SONAME 修复(必做)
3.1 为什么要修
lycium++ 编出的 libturbojpeg.so.0.3.0 内部 SONAME 是 libturbojpeg.so.0(带版本尾巴)。
DevEco 工程 CMake 里链接的是 libturbojpeg.so(IMPORTED_LOCATION .../libturbojpeg.so),运行时动态链接器按 SONAME 找 libturbojpeg.so.0,会找不到 → dlopen 失败。
这与实验3里 libplacebo 的 libplacebo.so.371 是同一个坑。
3.2 修复命令(在 VM 上,操作真实文件)
cd /home/lycium_plusplus/thirdparty/libjpeg-turbo/libjpeg-turbo-2.1.91/arm64-v8a-build
python3 - <<'PY'
import re
path="libturbojpeg.so.0.3.0" # 真实文件
data=open(path,'rb').read()
# 负向预查 (?![.\d]):仅匹配真正的 SONAME "libturbojpeg.so.0",
# 避免误伤文件名串 "libturbojpeg.so.0.3.0" 里嵌套的 ".0"
m=re.search(rb'(lib[A-Za-z0-9_+\-]+\.so)\.\d+(?![.\d])', data)
if not m:
print("没有带版本尾巴的 SONAME,无需修复"); raise SystemExit
old=m.group(0); target=m.group(1); pad=len(old)-len(target)
new=target+b'\x00'*pad # 等长 null 补齐,保持二进制结构不变
open(path+'.bak','wb').write(data) # 自动备份
open(path,'wb').write(data[:m.start()]+new+data[m.start()+len(new):])
print("fixed:", old.decode(), "->", target.decode())
PY
输出: fixed: libturbojpeg.so.0 -> libturbojpeg.so
说明:Windows 端的
D:\trae\WORK\harmony-car\fix_soname.py已同步加上(?![.\d])负向预查,
以后可直接在 Windows 跑:python fix_soname.py <so路径>。
4. 最终检验(验证 SONAME)
readelf -d libturbojpeg.so.0.3.0 | grep -i soname
# 期望输出:
# 0x000000000000000e (SONAME) Library soname: [libturbojpeg.so]
注:OHOS 自带
llvm-readelf路径可能与手册不同(/home/ohpkg/linux/llvm/bin/llvm-readelf
在本机不存在)。直接用系统readelf即可(只读不执行,跨架构没问题)。
看到 [libturbojpeg.so] 即说明修复成功。
5. 产物拷贝到 Windows(共享文件夹方式)
沿用实验3的 VMware 共享文件夹(vmhgfs-fuse):
# 共享文件夹已挂载在 /mnt/vmshare(若未挂:vmhgfs-fuse .host:/vmshare /mnt/vmshare -o allow_other)
mkdir -p /mnt/vmshare/turbojpeg
# ① .so 重命名为 libturbojpeg.so(CMake 找的就是这个名字)
cp libturbojpeg.so.0.3.0 /mnt/vmshare/libturbojpeg.so
# ② 头文件放进 turbojpeg/ 子目录(对应 CMake 的 include/turbojpeg)
cp /home/lycium_plusplus/thirdparty/libjpeg-turbo/libjpeg-turbo-2.1.91/turbojpeg.h /mnt/vmshare/turbojpeg/
ls -l /mnt/vmshare/ /mnt/vmshare/turbojpeg/
Windows 端 E:\OpenHarmony\vmshare\ 出现:
libturbojpeg.soturbojpeg\turbojpeg.h
6. 落位到 DevEco 工程
# 在 Windows 上把共享文件夹里的文件放进工程
mkdir -p D:\trae\WORK\harmony-car\entry\libs\arm64-v8a
mkdir -p D:\trae\WORK\harmony-car\entry\src\main\cpp\include\turbojpeg
cp E:\OpenHarmony\vmshare\libturbojpeg.so D:\trae\WORK\harmony-car\entry\libs\arm64-v8a\libturbojpeg.so
cp E:\OpenHarmony\vmshare\turbojpeg\turbojpeg.h D:\trae\WORK\harmony-car\entry\src\main\cpp\include\turbojpeg\turbojpeg.h
最终工程目录(已核对一致):
harmony-car/entry/
├─ libs/arm64-v8a/libturbojpeg.so ← 交叉编译产物(SONAME 已修)
└─ src/main/cpp/
├─ include/turbojpeg/turbojpeg.h ← 头文件(CMake 的 include/turbojpeg)
├─ line_follower.{h,cpp} ← MJPEG解码→灰度→二值化→中线→PD
├─ napi_init.cpp ← NAPI 桥接
├─ camera_driver.h / chassis_driver.h / wit_c_sdk.h
├─ CMakeLists.txt ← 链接 IMPORTED turbojpeg + GLOB drivers/*.c
└─ types/libentry/index.d.ts
CMake 里:
add_library(turbojpeg SHARED IMPORTED)+IMPORTED_LOCATION ${LIBS_DIR}/libturbojpeg.so+target_link_libraries(entry ... turbojpeg),
头文件搜索路径include/turbojpeg。
7. DevEco 工程桥接代码(MyApplication 中的三方库适配)
MyApplication 是课程给定的 DevEco 工程,里面已经写好"把三方库 turbojpeg 适配进 ArkTS"的全部桥接代码。本节只保留三方库把 JPEG 转灰度图这一基本功能的桥接:编译链接配置(CMake)、NAPI 桥接入口(decodeGray)、ArkTS 类型声明。巡线/避障算法、板载驱动桩、TCP 遥控等不在本基本功能范围内,故从本节删除。
7.1 工程定位与部署形态
该工程的形态是 「手机端(HarmonyOS)运行 turbojpeg 图像处理核心,通过 NAPI 暴露纯图像函数给 ArkTS」。本基本功能只用到 decodeGray:把一段 JPEG/MJPEG 内存数据用 turbojpeg 解码成单通道灰度图(Uint8Array)返回给上层。
7.2 CMake 配置(entry/src/main/cpp/CMakeLists.txt)
turbojpeg 以 IMPORTED 方式链接(交叉编译好的 .so 已落在 entry/libs/${OHOS_ARCH}/,SONAME 已修正为 libturbojpeg.so)。
# Copyright (c) 2025 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
cmake_minimum_required(VERSION 3.5.0)
project(MyNativeCar)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
# libjpeg-turbo prebuilt library (cross-compiled, SONAME fixed to libturbojpeg.so)
set(LIBS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH})
add_library(turbojpeg SHARED IMPORTED)
set_target_properties(turbojpeg PROPERTIES IMPORTED_LOCATION ${LIBS_DIR}/libturbojpeg.so)
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include
${NATIVERENDER_ROOT_PATH}/include/turbojpeg)
add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so turbojpeg)
关键点:
LIBS_DIR用${OHOS_ARCH}(编译时展开为arm64-v8a等),确保.so按 ABI 落位;target_link_libraries把turbojpeg链接进libentry.so,运行时按 SONAMElibturbojpeg.so找库(见第 3 节)。
桥接文件清单(本基本功能):
| 文件 | 作用 |
|---|---|
entry/src/main/cpp/CMakeLists.txt |
链接 turbojpeg 预编译库(见上) |
entry/src/main/cpp/napi_init.cpp |
NAPI 桥接入口:注册 decodeGray(见 7.3) |
entry/src/main/cpp/types/libentry/index.d.ts |
ArkTS 类型声明(见 7.4) |
7.3 NAPI 桥接入口(entry/src/main/cpp/napi_init.cpp)
本基本功能只注册 decodeGray 一个函数:把 JPEG/MJPEG 帧(Uint8Array/ArrayBuffer)用 turbojpeg 解成单通道灰度图并返回。入参读取用 read_jpeg_arg(),它用 napi_get_typedarray_info + napi_get_arraybuffer_info 双重探测(本 SDK 的 NAPI 没有 napi_typedarray 类型,不能直接 napi_typeof)。
#include "napi/native_api.h"
#include <cstring>
#include <cstdlib>
#include <cmath>
#include "turbojpeg.h"
/* Decode a JPEG/MJPEG memory block into a grayscale buffer using turbojpeg.
* Returns 0 on success, -1 on failure. Caller owns *out_gray (malloc). */
static int decode_to_gray(const unsigned char *jpeg, int jpeg_len,
int *out_w, int *out_h, unsigned char **out_gray)
{
if (!jpeg || jpeg_len <= 0 || !out_w || !out_h || !out_gray) {
return -1;
}
tjhandle tj = tjInitDecompress();
if (!tj) {
return -1;
}
int w = 0, h = 0, subsamp = 0, colorspace = 0;
if (tjDecompressHeader3(tj, jpeg, jpeg_len, &w, &h, &subsamp, &colorspace) != 0) {
tjDestroy(tj);
return -1;
}
unsigned char *gray = (unsigned char *)malloc((size_t)w * h);
if (!gray) {
tjDestroy(tj);
return -1;
}
if (tjDecompress2(tj, jpeg, jpeg_len, gray, w, 0, h, TJPF_GRAY, 0) != 0) {
free(gray);
tjDestroy(tj);
return -1;
}
tjDestroy(tj);
*out_w = w;
*out_h = h;
*out_gray = gray;
return 0;
}
/* Helper: read a Uint8Array / ArrayBuffer argument into (ptr, len).
* Note: this SDK's NAPI has no napi_typedarray value type, so we do not
* rely on napi_typeof(); instead we probe napi_get_typedarray_info first
* and fall back to napi_get_arraybuffer_info. */
static bool read_jpeg_arg(napi_env env, napi_value arg,
unsigned char **out_ptr, size_t *out_len)
{
/* Try TypedArray (e.g. Uint8Array) first. */
napi_typedarray_type atype;
napi_value buf;
size_t length = 0;
size_t offset = 0;
unsigned char *data = nullptr;
if (napi_get_typedarray_info(env, arg, &atype, &length,
(void **)&data, &buf, &offset) == napi_ok) {
*out_ptr = data + offset;
*out_len = length; /* Uint8Array element size is 1 byte */
return true;
}
/* Fall back to ArrayBuffer. */
void *abData = nullptr;
size_t abLen = 0;
if (napi_get_arraybuffer_info(env, arg, &abData, &abLen) == napi_ok) {
*out_ptr = (unsigned char *)abData;
*out_len = abLen;
return true;
}
return false;
}
/* decodeGray(jpeg: Uint8Array): { ok, width, height, data: Uint8Array } */
static napi_value DecodeGray(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
unsigned char *jpeg = nullptr;
size_t jpeg_len = 0;
if (!read_jpeg_arg(env, args[0], &jpeg, &jpeg_len)) {
napi_throw_error(env, nullptr, "decodeGray: arg must be Uint8Array/ArrayBuffer");
return nullptr;
}
int w = 0, h = 0;
unsigned char *gray = nullptr;
if (decode_to_gray(jpeg, (int)jpeg_len, &w, &h, &gray) != 0) {
napi_value result;
napi_create_object(env, &result);
napi_value ok;
napi_get_boolean(env, false, &ok);
napi_set_named_property(env, result, "ok", ok);
return result;
}
napi_value result;
napi_create_object(env, &result);
napi_value ok;
napi_get_boolean(env, true, &ok);
napi_set_named_property(env, result, "ok", ok);
napi_value jsW, jsH, arrbuf, jsData;
napi_create_int32(env, w, &jsW);
napi_create_int32(env, h, &jsH);
void *grayOut = nullptr;
napi_create_arraybuffer(env, (size_t)w * h, &grayOut, &arrbuf);
if (grayOut) {
memcpy(grayOut, gray, (size_t)w * h);
}
napi_create_typedarray(env, napi_uint8_array, (size_t)w * h, arrbuf, 0, &jsData);
napi_set_named_property(env, result, "width", jsW);
napi_set_named_property(env, result, "height", jsH);
napi_set_named_property(env, result, "data", jsData);
free(gray);
return result;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ "decodeGray", nullptr, DecodeGray, nullptr, nullptr, nullptr, napi_default, nullptr },
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
napi_module_register(&demoModule);
}
decode_to_gray()是三方库基本功能的核心:调tjInitDecompress→tjDecompressHeader3取尺寸 →tjDecompress2(..., TJPF_GRAY, 0)直接解成灰度图(巡线/预览只需亮度,省内存省算力),最后tjDestroy释放句柄。DecodeGray把灰度ArrayBuffer包成Uint8Array返回给 ArkTS。
7.4 ArkTS 类型声明(entry/src/main/cpp/types/libentry/index.d.ts)
export interface GrayResult {
ok: boolean;
width: number;
height: number;
data: Uint8Array;
}
/* Decode a JPEG/MJPEG frame into a grayscale buffer (third-party lib: turbojpeg). */
export const decodeGray: (jpeg: Uint8Array) => GrayResult;
8. 后续落地步骤
把交叉编译产物对接进 MyApplication:
mkdir -p MyApplication/entry/libs/arm64-v8a
mkdir -p MyApplication/entry/src/main/cpp/include/turbojpeg
cp E:\OpenHarmony\vmshare\libturbojpeg.so MyApplication/entry/libs/arm64-v8a/libturbojpeg.so
cp E:\OpenHarmony\vmshare\turbojpeg\turbojpeg.h MyApplication/entry/src/main/cpp/include/turbojpeg\turbojpeg.h
- 用 DevEco Studio 打开
MyApplication→Build Hap→ 手机/真机运行。 - ABI 一致:手机是
arm64-v8a,与交叉编译产物一致。 - 若
Build报libturbojpeg.so找不到:确认.so在entry/libs/arm64-v8a/、头文件在entry/src/main/cpp/include/turbojpeg/turbojpeg.h,且 SONAME 已修成libturbojpeg.so(第 3 节)。 - 若要在开发板本地跑(非手机 TCP 遥控):按 7.6 末尾把三个驱动
.c加回并改造CMakeLists与napi_init。
9. 常见问题 / 踩坑记录
| 现象 | 原因 | 解决 |
|---|---|---|
cd ~/lycium 报无此目录 |
框架在 /home/lycium_plusplus |
用 cd /home/lycium_plusplus 或 find / -maxdepth 4 -type d -name 'lycium*' |
./build.sh 报无此文件 |
入口在 lycium/ 子目录,非根 |
cd lycium && ./build.sh libjpeg-turbo |
| 运行时 dlopen 找不到库 | SONAME 带尾巴 libturbojpeg.so.0 |
用第 3 节脚本修成 libturbojpeg.so |
llvm-readelf 路径不存在 |
OHOS 自带路径与本机不符 | 直接用系统 readelf -d 验证 SONAME |
共享文件夹挂载 No such device |
共享名不对 | 先 vmware-hgfsclient 查真实共享名,再 vmhgfs-fuse .host:/<名> /mnt/vmshare |
没有像 libplacebo 那样的 .hnp/.tar.gz |
libjpeg-turbo 配方无 archive 步骤 |
不影响;DevEco 直接链接 .so 即可,无需 hnp 包 |
Build 报 libturbojpeg.so missing |
落位路径/架构错(如放到 x86_64 目录) | 确认放进 entry/libs/arm64-v8a/(手机/小车板均 arm64) |
ImageRecognition 没用到 processFrame |
该页当前走 componentSnapshot,非 MJPEG 直采 |
直接拉摄像头 MJPEG 帧时改调 processFrame;避障现由 ObstacleAvoidance 工具算 |
10. 补充说明
- 为什么没有
.hnp/.tar.gz包? libplacebo 之前出包是因为它的 HPKBUILD 里写了archive步骤(HNP_TOOL pack);
libjpeg-turbo 的 lycium 自带配方只编译+安装,不带 archive,故只留下 build 目录里的.so。
对"DevEco 直接链接 .so"的用法无任何影响。 - 架构一致性: 小车板 / 手机均为
arm64-v8a,与本次交叉编译产物同架构,不用重编x86_64版。
或在D:\trae\asd\MyApplication\entry\build-profile.json5中
将
"apiType": "stageMode",
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "-DOHOS_ARCH=arm64-v8a",
"cppFlags": "",
"abiFilters": [
"arm64-v8a",
"x86_64"
]
}
、、、
其中的"x86_64"删除。
- **手机 ≠ PC ≠ 开发板:** 带 native `.so` 的程序必须按目标 ABI 重编;纯 ArkTS 才与架构无关。
小车(arm64 / OpenHarmony)、鸿蒙 PC(x86_64)、手机(arm64 / 商业 HarmonyOS)三者 `.so` 不能混用。
- **两种部署形态:** 本文 `MyApplication` 是「手机运行 turbojpeg 图像核心 + TCP 遥控小车」;
早期 `harmony-car` 参考实现是「开发板本地一体化巡线(含 camera/chassis/imu 驱动线程)」。
选哪种取决于小车是否跑 OpenHarmony 应用 + 是否能本地访问 `/dev/video*`、`/dev/tty*`。
更多推荐



所有评论(0)