OpenHarmony WebRTC 编译指南
一、环境准备
1. 编译环境
- Ubuntu 18.04
- WebRTC 版本:m80
- OpenHarmony SDK 版本:4.0.10.13(建议使用最新版)
2. OpenHarmony SDK 获取
参考 OpenHarmony 4.0 Release。 标准系统 Public SDK 包(Windows/Linux)。
OpenHarmony WebRTC 编译涉及环境准备、源码修改及 FFmpeg 集成。主要步骤包括配置 OHOS SDK、修改 GN 构建脚本以支持 ohos 平台、调整工具链路径与 sysroot,以及针对特定模块(如 libevent, zlib)添加 ohos 判断。此外还需处理 ffmpeg 解码器配置及常见编译错误,最终实现带 H264 解码功能的 WebRTC 构建。
参考 OpenHarmony 4.0 Release。 标准系统 Public SDK 包(Windows/Linux)。
使用 ohos_webrtc_compile.sh。一般编译不需要拷贝 libyuv 库,视项目需求而定。
#!/bin/bash
export OHOS_SDK=<YOUR_OHOS_SDK_PATH> # 根据实际 SDK 解压目录设置
export AS=${OHOS_SDK}/native/llvm/bin/llvm-as
export CC="${OHOS_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
export CXX="${OHOS_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
export LD=${OHOS_SDK}/native/llvm/bin/ld.lld
export STRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip
export RANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib
export OBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump
export OBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy
export NM=${OHOS_SDK}/native/llvm/bin/llvm-nm
export AR=${OHOS_SDK}/native/llvm/bin/llvm-ar
export CFLAGS="-fPIC -D__MUSL__=1"
export CXXFLAGS="-fPIC -D__MUSL__=1"
WORKSPACE=$(pwd)
echo "$WORKSPACE"
export PATH=$WORKSPACE/depot_tools:$WORKSPACE/depot_tools/python-bin:$WORKSPACE/src/buildtools/linux64:$PATH
cd $WORKSPACE/src/third_party
mv libyuv_m80 libyuv
cd $WORKSPACE/src
gn gen out/Release-ohos --args=' target_os="ohos" target_cpu="arm64" rtc_use_x11=true is_debug=false is_component_build=false rtc_include_tests=false rtc_use_h264=true use_rtti=true use_custom_libcxx=false treat_warnings_as_errors=false is_clang=false rtc_enable_protobuf=false symbol_level=0 rtc_build_examples=false proprietary_codecs=true rtc_use_vrv_disnoise = false rtc_use_pipewire=true rtc_build_ssl = false rtc_build_tools =false rtc_ssl_root = "//third_party/openssl" ffmpeg_branding="Chrome" rtc_include_pulse_audio=false rtc_use_dummy_audio_file_devices=true '
ninja -C out/Release-ohos
cd $WORKSPACE/src/third_party
mv libyuv libyuv_m80
cd $WORKSPACE/src
修改 src/build/config/BUILDCONFIG.gn。
--- a/src/build/config/BUILDCONFIG.gn +++ b/src/build/config/BUILDCONFIG.gn @@ -222,6+222,8 @@ if(target_os =="android"){assert(host_os =="linux"|| host_os =="mac","Android builds are only supported on Linux and Mac hosts.") _default_toolchain ="//build/toolchain/android:android_clang_$target_cpu"+}elseif(target_os =="ohos"){+ _default_toolchain ="//build/toolchain/ohos:ohos_clang_$target_cpu"}elseif(target_os =="chromeos"|| target_os =="linux"){#See comments in build/toolchain/cros/BUILD.gn about board compiles.if(is_clang){ @@ -283,6+285,7 @@ if(custom_toolchain !=""){#aixor one of the BSDs. If you need to check these, just check the#current_os value directly.+is_ohos = current_os =="ohos" is_android = current_os =="android" is_chromeos = current_os =="chromeos" is_fuchsia = current_os =="fuchsia" @@ -366,6+369,10 @@ if(is_android){["//build/config/android:default_orderfile_instrumentation"]}+if(is_ohos){+ default_compiler_configs +=["//build/config/ohos:compiler"]+}
修改 src/build/config/clang/clang.gni。
--- a/src/build/config/clang/clang.gni +++ b/src/build/config/clang/clang.gni @@ -11,13+11,27 @@ if(is_mac){if(is_win){ default_clang_base_path+="win"}++if(is_ohos){+declare_args(){+ ohos_sdk_native_root ="//buildtools/ohos-sdk/linux/native"+}+ default_clang_base_path ="${ohos_sdk_native_root}/llvm"+ clang_lib_path ="${default_clang_base_path}/lib"+}++declare_args(){#Indicates if the build should use the Chrome-specific plugins for enforcing#codingguidelines, etc. Only used when compiling with Chrome's Clang, not#Chrome OS's.++if(is_ohos){+ clang_use_chrome_plugins = false +}else{ clang_use_chrome_plugins = is_clang &&!is_nacl &&!use_xcode_clang && default_toolchain !="//build/toolchain/cros:target"-+} clang_base_path = default_clang_base_path }
备注: ohos_sdk_native_root 配置默认路径,用户需将 OHOS SDK 拷贝到 buildtools 目录,且目录需保持一致,或编译时手动配置。
修改 src/build/config/sysroot.gni。
--- a/src/build/config/sysroot.gni +++ b/src/build/config/sysroot.gni @@ -35,6 +35,9 @@ if (sysroot == "") { # Android uses unified headers, and thus a single compile time sysroot sysroot = "$android_toolchain_root/sysroot" + } else if (is_ohos) { + import("//build/config/clang/clang.gni") + sysroot = "${ohos_sdk_native_root}/sysroot" } else if ((is_linux || is_chromeos) && use_sysroot) { # By default build against a sysroot image downloaded from Cloud Storage # during gclient runhooks
修改 src/build/toolchain/toolchain.gni。
--- a/src/build/toolchain/toolchain.gni +++ b/src/build/toolchain/toolchain.gni @@ -38,7+38,11 @@ if(generate_linker_map){}declare_args(){- clang_version ="13.0.0"+if(is_ohos){+ clang_version ="15.0.4"+}else{+ clang_version ="13.0.0"+}}
在 src/build/toolchain/gcc_toolchain.gni 中添加参数识别。
--- a/src/build/toolchain/gcc_toolchain.gni +++ b/src/build/toolchain/gcc_toolchain.gni @@ -117,6+117,33 @@ template("gcc_toolchain"){ rebuild_string =""}++if(defined(invoker.libs_section_prefix)){+ libs_section_prefix = invoker.libs_section_prefix +}else{+ libs_section_prefix =""+}++if(defined(invoker.libs_section_postfix)){+ libs_section_postfix = invoker.libs_section_postfix +}else{+ libs_section_postfix =""+}++if(defined(invoker.solink_libs_section_prefix)){+ solink_libs_section_prefix = invoker.solink_libs_section_prefix +}else{+ solink_libs_section_prefix =""+}++if(defined(invoker.solink_libs_section_postfix)){+ solink_libs_section_postfix = invoker.solink_libs_section_postfix +}else{+ solink_libs_section_postfix =""+}+++#GN's syntax can't handle more than one scope dereference at once, like # "invoker.toolchain_args.foo", so make a temporary to hold the toolchain #argsso we can do"invoker_toolchain_args.foo". @@ -463,7+490,7 @@ template("gcc_toolchain"){if(target_cpu =="mipsel"&& is_component_build && is_android){ rspfile_content ="-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group"}else{- rspfile_content ="-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"+ rspfile_content ="-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix" description ="SOLINK $sofile" @@ -534,7+561,7 @@ template("gcc_toolchain"){ strip_command ="${invoker.strip} -o \"$sofile\" \"$unstripped_sofile\"" command +=" && "+ strip_command }- rspfile_content ="-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"+ rspfile_content ="-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix" description ="SOLINK_MODULE $sofile" @@ -589,7+616,7 @@ template("gcc_toolchain"){ start_group_flag ="-Wl,--start-group" end_group_flag ="-Wl,--end-group "}- link_command ="$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag {{libs}}"+ link_command ="$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $libs_section_prefix $start_group_flag @\"$rspfile\" {{solibs}} {{libs}} $end_group_flag $libs_section_postfix"
新建 build/toolchain/ohos/BUILD.gn。
import("//build/config/sysroot.gni")
import("//build/toolchain/gcc_toolchain.gni")
declare_args(){#Whether unstripped binaries, i.e. compiled with debug symbols, should be#consideredruntime_deps rather than stripped ones. ohos_unstripped_runtime_outputs =true ohos_extra_cflags ="" ohos_extra_cppflags ="" ohos_extra_cxxflags ="" ohos_extra_asmflags ="" ohos_extra_ldflags =""}
#The ohos clang toolchains share most of the same parameters, so we have this#wrapperaround gcc_toolchain to avoid duplication of logic. # #Parameters: # - toolchain_root #Path to cpu-specific toolchain within the ndk. # - sysroot #Sysroot forthis architecture. # - lib_dir #Subdirectory inside of sysroot where libs go. # - binary_prefix #Prefix of compiler executables.template("ohos_clang_toolchain"){gcc_toolchain(target_name){assert(defined(invoker.toolchain_args),"toolchain_args must be defined for ohos_clang_toolchain()") toolchain_args = invoker.toolchain_args toolchain_args.current_os ="ohos"#Output linker map files for binary size analysis. enable_linker_map =true ohos_libc_dir =rebase_path(invoker.sysroot +"/"+ invoker.lib_dir, root_build_dir)#libs_section_prefix ="${ohos_libc_dir}/Scrt1.o"#libs_section_prefix +=" ${ohos_libc_dir}/crti.o"#libs_section_postfix ="${ohos_libc_dir}/crtn.o"if(invoker.target_name =="ohos_clang_arm"){ abi_target ="arm-linux-ohos"}elseif(invoker.target_name =="ohos_clang_arm64"){ abi_target ="aarch64-linux-ohos"}elseif(invoker.target_name =="ohos_clang_x86_64"){ abi_target ="x86_64-linux-ohos"} clang_rt_dir =rebase_path("${clang_lib_path}/${abi_target}/nanlegacy", root_build_dir)print("ohos_libc_dir :", ohos_libc_dir)print("clang_rt_dir :", clang_rt_dir)#solink_libs_section_prefix ="${ohos_libc_dir}/crti.o"#solink_libs_section_prefix +=" ${clang_rt_dir}/clang_rt.crtbegin.o"#solink_libs_section_postfix ="${ohos_libc_dir}/crtn.o"#solink_libs_section_postfix +=" ${clang_rt_dir}/clang_rt.crtend.o" _prefix =rebase_path("${clang_base_path}/bin", root_build_dir) cc ="${_prefix}/clang" cxx ="${_prefix}/clang++" ar ="${_prefix}/llvm-ar" ld = cxx readelf ="${_prefix}/llvm-readobj" nm ="${_prefix}/llvm-nm"if(!is_debug){ strip =rebase_path("${clang_base_path}/bin/llvm-strip", root_build_dir) use_unstripped_as_runtime_outputs = ohos_unstripped_runtime_outputs } extra_cflags = ohos_extra_cflags extra_cppflags = ohos_extra_cppflags extra_cxxflags = ohos_extra_cxxflags extra_asmflags = ohos_extra_asmflags extra_ldflags = ohos_extra_ldflags }}ohos_clang_toolchain("ohos_clang_arm"){ sysroot ="${sysroot}" lib_dir ="usr/lib/arm-linux-ohos" toolchain_args ={ current_cpu ="arm"}}ohos_clang_toolchain("ohos_clang_arm64"){ sysroot ="${sysroot}" lib_dir ="usr/lib/aarch64-linux-ohos" toolchain_args ={ current_cpu ="arm64"}}ohos_clang_toolchain("ohos_clang_x86_64"){ sysroot ="${sysroot}" lib_dir ="usr/lib/x86_64-linux-ohos" toolchain_args ={ current_cpu ="x86_64"}}
新建 build/config/ohos/BUILD.gn。
import("//build/config/sysroot.gni")
import("//build/config/clang/clang.gni")
assert(is_ohos)
ohos_clang_base_path ="${ohos_sdk_native_root}/llvm"
ohos_clang_version ="15.0.4"
if(is_ohos){if(current_cpu =="arm"){ abi_target ="arm-linux-ohos"}elseif(current_cpu =="x86"){ abi_target =""}elseif(current_cpu =="arm64"){ abi_target ="aarch64-linux-ohos"}elseif(current_cpu =="x86_64"){ abi_target ="x86_64-linux-ohos"}else{assert(false,"Architecture not supported")}}
config("compiler"){ cflags =["-ffunction-sections","-fno-short-enums","-fno-addrsig",] cflags +=["-Wno-unknown-warning-option","-Wno-int-conversion","-Wno-unused-variable","-Wno-misleading-indentation","-Wno-missing-field-initializers","-Wno-unused-parameter","-Wno-c++11-narrowing","-Wno-unneeded-internal-declaration","-Wno-undefined-var-template","-Wno-implicit-int-float-conversion",] defines =[#The NDK has these things, but doesn't define the constants to say that it#does. Define them here instead."HAVE_SYS_UIO_H",] defines +=["OHOS","__MUSL__","_LIBCPP_HAS_MUSL_LIBC","__BUILD_LINUX_WITH_CLANG","__GNU_SOURCE","_GNU_SOURCE",] ldflags =["-Wl,--no-undefined","-Wl,--exclude-libs=libunwind_llvm.a","-Wl,--exclude-libs=libc++_static.a",#Don't allow visible symbols from libraries that contain#assemblycode with symbols that aren't hidden properly.#http://crbug.com/448386"-Wl,--exclude-libs=libvpx_assembly_arm.a",] cflags +=["--target=$abi_target"] include_dirs =["${sysroot}/usr/include/${abi_target}","${ohos_clang_base_path}/lib/clang/${ohos_clang_version}/include",] ldflags +=["--target=$abi_target"]#Assign any flags set for the C compiler to asmflags so that they are sent#tothe assembler. asmflags = cflags }
--- a/src/build/config/compiler/BUILD.gn +++ b/src/build/config/compiler/BUILD.gn @@ -1242,9+1242,15 @@ config("compiler_deterministic"){config("clang_revision"){if(is_clang && clang_base_path == default_clang_base_path){ update_args =[-"--print-revision",-"--verify-version=$clang_version",+# "--print-revision",+# "--verify-version=$clang_version",+"--print-revision"]++if(!is_ohos){+ update_args +=["--verify-version=$clang_version"]+}+if(llvm_force_head_revision){ update_args +=["--llvm-force-head-revision"]}
(1) src/base/third_party/libevent / src/third_party/libevent
--- a/src/base/third_party/libevent/BUILD.gn +++ b/src/base/third_party/libevent/BUILD.gn @@ -43,13+43,13 @@ static_library("libevent"){"mac/event-config.h",] include_dirs =["mac"]-}elseif(is_linux || is_chromeos){+}elseif(is_linux || is_chromeos || is_ohos){ sources +=["epoll.c","linux/config.h","linux/event-config.h",]- include_dirs =["linux"]+ include_dirs =["linux","compat"]}elseif(is_android){ sources +=["android/config.h",
(2) src/rtc_base
--- a/src/rtc_base/BUILD.gn +++ b/src/rtc_base/BUILD.gn @@ -30,6+30,9 @@ if(!rtc_build_ssl){if(is_mac){ lib_ext ="a"}+if(is_ohos){+ lib_ext ="so"+}if(is_linux){ lib_cpu_dir ="" lib_ext ="so"
(3) sys 下面找不到文件
将 $OHOS_SDK_PATH/native/sysroot/usr/include/ffrt 内容拷贝到 $OHOS_SDK_PATH/native/sysroot/usr/include/sys。
(4) AudioDeviceLinuxALSA 未定义错误
处理方式:增加编译参数 rtc_use_dummy_audio_file_devices=true,先不编译 audio。
(5) ffmpeg 编译
修改 third_party/ffmpeg/BUILD.gn。
platform_config_root ="chromium/config/$ffmpeg_branding/$os_config/$ffmpeg_arch" 修改为: if(is_ohos){ platform_config_root ="chromium/config/Chromium/linux/arm64"}else{ platform_config_root ="chromium/config/$ffmpeg_branding/$os_config/$ffmpeg_arch"}
(6) modules/video_capture
--- a/src/modules/video_capture/BUILD.gn +++ b/src/modules/video_capture/BUILD.gn @@ -90,7+90,7 @@ if(!build_with_chromium){"../../system_wrappers",]-if(is_linux || is_chromeos){+if(is_linux || is_chromeos || is_ohos){ sources =["linux/device_info_linux.cc","linux/device_info_linux.h",
(7) src/third_party/zlib
defines =["CRC32_ARMV8_CRC32"]if(is_android){ defines +=["ARMV8_OS_ANDROID"]-}elseif(is_linux || is_chromeos){+}elseif(is_linux || is_chromeos || is_ohos){ defines +=["ARMV8_OS_LINUX"]}elseif(is_mac){ defines +=["ARMV8_OS_MACOS"]
(8) 修改当前项目中不支持 ohos 的接口
rtc_base/platform_thread_types.cc 需要识别到 ohos 然后调用 gettid()。
首先在根目录的 BUILD.gn 中配置识别 ohos 系统的变量:diff -uprN src/BUILD.gn src_ohos/BUILD.gn --- src/BUILD.gn 2023-12-1519:42:56.000000000-0800+++ src_ohos/BUILD.gn 2023-12-1801:12:39.553298189-0800 @@ -214,6+214,9 @@ config("common_inherited_config"){"WEBRTC_IOS",]}+if(is_ohos){+ defines +=["WEBRTC_OHOS","WEBRTC_LINUX"]+}if(is_linux || is_chromeos){ defines +=["WEBRTC_LINUX"]}
修改 rtc_base/platform_thread_types.cc 业务代码:
--- src/rtc_base/platform_thread_types.cc 2023-12-1519:42:56.000000000-0800+++ src_ohos/rtc_base/platform_thread_types.cc 2023-12-1800:42:49.393883643-0800 @@ -40,7+40,7 @@ PlatformThreadId CurrentThreadId(){#elifdefined(WEBRTC_POSIX)#ifdefined(WEBRTC_MAC)||defined(WEBRTC_IOS)returnpthread_mach_thread_np(pthread_self());-#elif defined(WEBRTC_ANDROID)+#elif defined(WEBRTC_ANDROID)||defined(WEBRTC_OHOS)returngettid();#elifdefined(WEBRTC_FUCHSIA)returnzx_thread_self();
当前 ohos 支持的函数接口为 gettid(),因此可以将 WEBRTC_OHOS 判断和 WEBRTC_ANDROID 放在一起,也可以单独建一个分支,单独建分支时需要注意,其判断需要放在 WEBRTC_LINUX 之前。
third_party/dav1d 中不支持的接口 pthread_getaffinity_np。
通过分析可知,pthread_getaffinity_np 接口是在宏 HAVE_PTHREAD_GETAFFINITY_NP 生效时调用,故只需要将该宏在 ohos 平台时设置为不生效即可:--- src/third_party/dav1d/BUILD.gn 2023-12-1521:08:22.000000000-0800+++ src_ohos/third_party/dav1d/BUILD.gn 2023-12-1817:39:15.988668172-0800 @@ -56,7+56,7 @@ config("public_dav1d_config"){ defines ="CONFIG_LOG=1"]}-if(!is_android &&!is_win){+if(!is_android &&!is_win &&!is_ohos){ defines +=["HAVE_PTHREAD_GETAFFINITY_NP=1","HAVE_PTHREAD_SETAFFINITY_NP=1",
备注: 如果不修改下面配置,则 webrtc 无法解码 h264 数据。
修改 /src/third_party/ffmpeg/ffmpeg_generated.gni 第 15 行。
use_linux_config = is_linux || is_fuchsia || is_android 修改为: use_linux_config = is_linux || is_fuchsia || is_android || is_ohos
备注: (1)如果不做下面配置,解码时找不到解码器。 (2)因为鸿蒙的 cpu 架构是 arm64,所以改的是 arm64 目录。
在以下文件中增加配置:
src/third_party/ffmpeg/chromium/config/ChromeOS/linux/arm64/libavcodec/codec_list.c: 增加 &ff_h264_decodersrc/third_party/ffmpeg/chromium/config/ChromeOS/linux/arm64/libavcodec/parser_list.c: 增加 &ff_h264_parsersrc/third_party/ffmpeg/chromium/config/ChromeOS/linux/arm64/config.h (801 行): 修改 #define CONFIG_H264_DECODER 0 到 #define CONFIG_H264_DECODER 1
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online