大家好,我是一名即将本科毕业的openharmony开发者,去年暑假利用了两个月时间移植了一个语音处理的三方库speexdsp到openharmony标准系统。主要为其编写了build.gn使其加入了openharmony编译体系(基于ninja和gn),speexdsp在linux下是使用构建工具configure、makefile构建的。移植的难点并不在于.c和.h以及cflags、idflags的分析,而在于重新熟悉一套编译构建体系,而且当时可参考的资料并不太多。笔者最近为speexdsp编写了cmakelists.txt,使用openharmony的ndk工具编译出来so动态库和可执行文件,并且成功在开发板上运行,现将经验分享如下:speexdsp移植完毕已提交至openhamrony sig仓库:https://gitee.com/openharmony-sig/contest/tree/master/2022_openharmony_thirdparty/speexdsp
(笔者也没想到会继续续写三方库方面的文章,学无止境,希望能够帮助更多人了解openharmony,加入openharmony生态)
ndk (原生开发套件) 是一套工具,使开发者能够在 openharmony hap应用中使用 c/c++ 代码。它提供了一系列的工具可以帮助开发者快速的开发c/c++的动态库、静态库和可执行文件。openharmony 应用开发的native c++开发方式就要依赖ndk。ndk被包含在openharmony sdk中。可以在deveco studio使用 ndk 将 c/c ++ 代码编译到so库中,然后使用 deveco studio 的构建插件hvigor-ohos-plugin将so库打包到 hap 中。arkts代码随后可以通过napi框架调用so库中的函数。深开鸿郭岳峰老师开发的ocrdemo就通过napi调用了c++的三方库tesseract的能力,而这个库本身还依赖leptonica、libjpeg、libpng、libtiff等c/c ++ 等四方库。如果重新编写build.gn移植到openharmony,工作量巨大。tesseract (apache 2.0 license)是一个可以进行图像ocr识别的c ++ 库openharmony集成ocr三方库实现文字提取1. 编写build.gn与编写cmakelists.txt移植到openharmony两者的区别1、编译环境不同,编译工具编写build.gn方式,编译环境是在openharmony源码中,编译时使用到的是源码中的编译工具。编写cmakelists.txt的移植方式实际上是native c++应用开发方式的一种,并且ndk是sdk的一部分,编译so时候实际上使用的是ndk的编译工具。2、so安装的地方不一样编写build.gn方式,三方库编译出来的so和测试用例可以打包进入openharmony固件中。编写cmakelists.txt方式,编译出来的实际上会被打包进入hap应用中,hap再安装到openharmony操作系统上完成三方库so能力的调用。3、编写cmakelists.txt比编写build.gn更容易build.gn总有各种各样的编译器标志要加入以消除编译报错,开发者学习成本比较高cmakelists.txt方式开发者则相对熟悉,对于原生库就是camke构建的三方库,只需要对原生库已有的cmakelists.txt做少量修改,比如删除与其他操作系统有关的部分(笔者说的就是aosp)。2. 使用openharmony的ndk工具移植speexdsp到speexdsp在windows端的ide上调用ndk创建native c++工程,但是先不写napi和arkts的部分,先为c/c ++的三方库编写cmakelists.txt(如果三方库本身就是cmake构建的,但也要对cmakelists.txt进行少量的修改,详细请参考该样例 https://gitee.com/openharmony-sig/knowledge_demo_temp/tree/master/fa/ocrdemo)。 然后编译hap应用来调用sdk中的ndk工具。3. 创建native c++工程使用sdk中的ndk工具创建native c++工程参考:三方库移植之napi开发[3]通过ide开发napi工程
1、打开ide deveco studio,创建一个native c++工程。
2、sdk选择api9,model选择stage。新建的native c++工程有一个默认的hello world教程
3.1 将speexdsp加入native c++工程,在库中编写顶层cmakelists.txt生成动态库1、将speexdsp源码移动到native c++工程entry\\src\\main\\cpp目录,cpp目录专门用于存放c/c ++代码。
2、删除speexdsp中无关的代码让代码结构简洁。speexdsp中有一些无关的代码,例如和win32、maco上运行的有关代码,甚至还有塞班系统symbian上的代码。(不管了先删除,不知道speexdsp的开源协议允不允许笔者这样做,但是看着乱乱的目录结构,笔者希望这样让自身的思路清晰一些。)
# 目录结构说明cpp├─include # .h文件├─libspeexdsp # .c文件│ └─cmakelists.txt # 笔者编写的用来生成可执行文件库的cmakelists.txt├─build.gn # 笔者之前写的build.gn,现在拿来参考写cmakelists.txt├─cmakelists.txt # 笔者编写的用来生成动态库的cmakelists.txt├─config.h # speexdsp原生库在linux下编译构建生成的配置文件├─speexdsp_api.txt # speexdsp的api列表└─speedsp_tested_api.txt3、编写顶层在cmakelists.txt生成动态库
# cmake的最小版本要求cmake_minimum_required(version 3.4.1)# 脚本中set是将普通变量、缓存变量或者环境变量设置为指定的值。# 从cmake v3.1开始,可以cmake_cxx_standard变量设置c++标准set(cmake_cxx_standard 11)# 项目名称project(speexdsp)# 添加cflags信息set(cmake_c_flags ${cmake_c_flags} -g)set(cmake_c_flags ${cmake_c_flags} -o2)set(cmake_c_flags ${cmake_c_flags} -fvisibility=hidden)set(cmake_c_flags ${cmake_c_flags} -wno-implicit-function-declaration)set(cmake_c_flags ${cmake_c_flags} -wno-pointer-sign)set(cmake_c_flags ${cmake_c_flags} -wno-c99-extensions)set(cmake_c_flags ${cmake_c_flags} -wno-unused-variable)# cflags信息# 这个命令是针对所有类型编译器的,也就是说这里添加的选项会在所有的编译器中运用,比如-std=c++11是针对c++的编译器参数,也会被运用在c语言编译器中# 通过在cmakelists.txt文件中添加add_compile_options命令可以起到添加参数的作用add_compile_options(-g -o2 -fvisibility=hidden -wno-implicit-function-declaration -wno-pointer-sign -wno-c99-extensions -wno-unused-variable)# 头文件set(include_dir ${cmake_current_source_dir})# 设定编译宏 -dadd_definitions(-dhave_config_h)############################################################# 创建so动态库# 源文件# cmake_current_source_dir指的cmakelists.txt当前所在的目录set(shared_lib_src ${cmake_current_source_dir}/libspeexdsp/preprocess.c ${cmake_current_source_dir}/libspeexdsp/jitter.c ${cmake_current_source_dir}/libspeexdsp/mdf.c ${cmake_current_source_dir}/libspeexdsp/fftwrap.c ${cmake_current_source_dir}/libspeexdsp/filterbank.c ${cmake_current_source_dir}/libspeexdsp/resample.c ${cmake_current_source_dir}/libspeexdsp/buffer.c ${cmake_current_source_dir}/libspeexdsp/scal.c ${cmake_current_source_dir}/libspeexdsp/smallft.c)add_library(speexdsp shared ${shared_lib_src})target_include_directories(speexdsp private ${include_dir})############################################################# 链接数学库-lm# 如果为所有target统一指定编译时要链接的库用link_libraries# 为每个target单独指定编译时要链接的库用target_link_librarieslink_libraries(-lm)target_link_libraries(speexdsp public m)# 使用add_subdirectory()将子目录添加到构建add_subdirectory(libspeexdsp)3.2 在库中编写底层cmakelists.txt生成可执行文件,用来验证so库是否运行正常在.c源文件目录添加cmakelists.txt用来编译出可执行文件,用来验证使用ndk移植三方库到openharmony标准系统是否成功。如下:
cmake_minimum_required(version 3.4.1)project(test)#生成执行二进制文件,生成testdenoise测试用例add_executable(testdenoise testdenoise.c)# 将二进制文件链接到生成的动态库target_link_libraries(testdenoise public speexdsp)# 将二进制文件链接的库文件link_libraries(-lm)# 添加编译器标志add_compile_options(-g -o2 -fvisibility=hidden)# 生成testecho测试用例add_executable(testecho testecho.c)target_link_libraries(testecho public speexdsp)link_libraries(-lm)add_compile_options(-g -o2 -fvisibility=hidden)# 生成testjitter测试用例add_executable(testjitter testjitter.c)target_link_libraries(testjitter public speexdsp)link_libraries(-lm)add_compile_options(-g -o2 -fvisibility=hidden)# 生成testresample测试用例add_executable(testresample testresample.c)target_link_libraries(testresample public speexdsp)link_libraries(-lm)add_compile_options(-g -o2 -fvisibility=hidden)# 生成testresample2测试用例add_executable(testresample2 testresample2.c)target_link_libraries(testresample2 public speexdsp)link_libraries(-lm)add_compile_options(-g -o2 -fvisibility=hidden)3.3 在库外的cmakelists.txt中添加代码使能speexdsp编译1、 新建的native c++工程是有一个默认的hello world模板的,在entry\\src\\main\\cpp目录下有一个cmakelists.txt,需要在其中添加代码使能speexdsp编译
在entry\\src\\main\\cpp\\cmakelists.txt中主要做两件事情# 添加子目录speexdspadd_subdirectory(speexdsp)# 添加链接libspeexdsp.so动态库# 把动态库libentry.so链接到动态库libspeexdsp.sotarget_link_libraries(entry public libace_napi.z.so speexdsp)2、如果不添加代码,则speexdsp的动态库和可执行用例编译不出来3.4 执行编译命令编译动态库和测试用例1、在ide上方工具栏选择编译hap进行so和测试用例的编译
2、编译结果在entry\\build\\default\\intermediates\\cmake\\default\\obj目录下
├─arm64-v8a │ libc++_shared.so│ libentry.so│ libspeexdsp.so│ testdenoise│ testecho│ testjitter│ testresample│ testresample2│└─armeabi-v7a libc++_shared.so libentry.so libspeexdsp.so testdenoise testecho testjitter testresample testresample23、为什么会ide中的ndk会编译出64位和32位的动态库和可执行文件呢?因为openharmony操作系统有32位和64位,这样是为了hap能在不同位数的openharmony版本上运行。3.5 根据32位和64位的openharmony版本推送相应的so和可执行文件到开发板上如何分辨开发板上openharmony版本是64位还是32位?和linux的方式是一样。用getconf word_bit和getconf long_bit获得word和long的位数。64位系统中分别得到32和64。32位系统中分别得到32和32。
1、笔者开发板上烧录的是32位的openharmony beta5版本
因此需要将native c++工程目录下的entry\\build\\default\\intermediates\\cmake\\default\\obj\\armeabi-v7a中的libspeexdsp.so和testdenoise、testecho、testjitter、testresample、testresample2推送到设备端的data目录
2、通过与ohos版本匹配的hdc_std工具,将编译生成的库以及测试用的可执行文件推送到开发板的data目录
hdc_std shell mount -o remount,rw / ## 重新加载系统为可读写hdc_std file send testdenoise /data ## 推送可执行文件testdenoise到data目录hdc_std file send libspeexdsp /data ## 推送libspeexsdp.so到data目录3、执行testdenoise可执行文件(其它测试用例的执行请参考 移植speexdsp到openharmony标准系统⑤)通过分析testdenoise.c源码,执行测试程序时需要指定一份输入的不为空的8000hz的input.pcm音频,并且需要指定一份空的输出的output.pcm音频。rk3568上运行,执行语句如下:./testdenoise output.pcm
4、测试结果:对比输入的input.pcm和输出的outpu.pcm的波形图和声谱图,噪声已经被消除。pc端和rk3568开发板运行testdenoise可执行程序效果一致。可执行文件运行成功,使用openharmonyndk移植三方库speexdsp成功
知识点附送1、aip8的应用如何更改为api9支持64位版本1.1 api8只支持32位,api9支持32位和64位。以该pr https://gitee.com/openharmony/applications_app_samples/pulls/759 学习将api8应用适配适配arm64
1、修改build-profile.json5 ,将compilesdkversion和compatiblesdkversion属性由8改为9
compilesdkversion指定openharmony应用/服务编译时的sdk版本
compatiblesdkversion指定openharmony应用/服务兼容的最低sdk版本
2、修改entry/build-profile.json5,abi添加64位arm64-v8a
abifilters用于设置本机的abi编译环境
3、修改entry/src/main/config.json,设备类型改为默认
4、这个pr改动了xcomponent/entry/src/main/cpp/common/plugin_common.h文件,plugin_common.h文件和hilog调试的功能有关。
2、编译构建子系统如何增加编译构建arm64选择以该issue https://gitee.com/openharmony/build/issues/i53e9i 来学习
分别在hb工具和build.sh脚本添加--target-cpu选项电源服务子系统支持64位
https://gitee.com/openharmony/powermgr_power_manager/issues/i55094
graphic子系统适配64位编译
https://gitee.com/openharmony/graphic_graphic_2d/issues/i53720
什么是RC振荡器?RC振荡器的工作原理 基本RC振荡器电路设计
5G将智能家居推入了一个怎样的发展水平
Shutterstock 发布了以道德 AI 为核心的最佳实践方法 TRUST
触控技术全方位PK
华为突然宣布大消息为中国制造鼓士气!
使用OpenHarmonyNDK移植三方库Speexdsp
苹果CEO库克公布财报 高端消费市场有利可图
关于高压串联蓄电池的问题方案的相关解答
保险丝、热敏电阻、整流桥设计电源元件计算
模拟电路网络课件 第十六节:多级放大电路
ROHS六项有害物质检测
一文读懂软包电池的外观缺陷检测应用
HiHope受邀参加2021南京智能芯片行业沙龙,与行业先锋共探“创「芯」突破”
Sunny King计划在欧洲安装一些区块链节点,决心打入欧洲区块链市场
什么是ISO 9001?
杜绝山寨!MIT无线芯片能核实身份 完成认证后才能充电
ELF 1技术贴|如何移植OpenCV
Honda e动力电池系统热管理与冷板设计解析
offs功能程序介绍
京东方AMOLED正式量产,打破韩企垄断神话绝非易事