开源鸿蒙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 虚拟机

  1. 下载并安装 VMware Workstation Pro(或免费的 VMware Player)。
  2. 下载 Ubuntu 20.04 / 22.04 的 ISO 镜像(本记录环境为 Ubuntu 20.04)。
  3. 新建虚拟机:
    • 客户机系统选 Linux / Ubuntu 64 位
    • 内存 ≥ 4 GB,处理器 ≥ 2 核,磁盘 ≥ 40 GB(选"将虚拟磁盘拆分成多个文件"便于共享)
    • 网络适配器设为 NAT(最易通外网;桥接需手动配 IP,新手不建议)
    • 勾选"安装 VMware Tools"(或装完系统后用 1.2 的 open-vm-tools 代替)
  4. 开机,按引导安装 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)

  1. 在 VMware 菜单:虚拟机 → 设置 → 选项 → 共享文件夹,启用"总是启用",添加一条:
    • 名称填 vmshare
    • 路径指向 Windows 上一个空目录(本记录用 E:\OpenHarmony\vmshare
  2. 在 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/vmsharevmhgfs-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-v7aarm64-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.soIMPORTED_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.so
  • turbojpeg\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_librariesturbojpeg 链接进 libentry.so,运行时按 SONAME libturbojpeg.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() 是三方库基本功能的核心:调 tjInitDecompresstjDecompressHeader3 取尺寸 → 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 打开 MyApplicationBuild Hap → 手机/真机运行。
  • ABI 一致:手机是 arm64-v8a,与交叉编译产物一致。
  • Buildlibturbojpeg.so 找不到:确认 .soentry/libs/arm64-v8a/、头文件在 entry/src/main/cpp/include/turbojpeg/turbojpeg.h,且 SONAME 已修成 libturbojpeg.so(第 3 节)。
  • 若要在开发板本地跑(非手机 TCP 遥控):按 7.6 末尾把三个驱动 .c 加回并改造 CMakeListsnapi_init

9. 常见问题 / 踩坑记录

现象 原因 解决
cd ~/lycium 报无此目录 框架在 /home/lycium_plusplus cd /home/lycium_plusplusfind / -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*`
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐