- 通信
- 音视频
【免费下载链接】freeswitch
FreeSWITCH is a Software Defined Telecom Stack enabling the digital transformation from proprietary telecom switches to a versatile software implementation that runs on any commodity hardware. From a Raspberry PI to a multi-core server, FreeSWITCH can unlock the telecommunications potential of any device.
libyuv 是一个开源的 YUV 图像处理库,提供 YUV 缩放、格式转换(YUV/RGB 互转)、90/180/270 度旋转等核心能力,并为 x86/x64 的 SSSE3/AVX2、ARM 的 NEON、MIPS 的 MSA/MMI 提供高度优化的汇编实现。本指南基于 FreeSWITCH 仓库内随附的 libyuv 第三方库(位于libs/libyuv)及官方getting_started.md文档,完整讲解从获取代码、配置构建环境,到使用 GN+Ninja、Make、CMake 三种方式在 Windows / macOS / Linux / iOS / Android / ARM Linux / MIPS Linux 上构建库、单元测试与工具链的全过程,并深入解析单元测试的运行参数、基准测试方法以及 Sanitizer 等调试手段。
在 FreeSWITCH 项目中,libyuv 被作为第三方依赖整体随仓库分发(版本号见 include/libyuv/version.h,当前为LIBYUV_VERSION 1735),核心视频处理模块 src/switch_core_video.c 直接调用I420ToARGB、I420Rotate、I420Copy、RGBAToARGB等 libyuv API 完成视频帧的格式转换与旋转(均以SWITCH_HAVE_YUV编译开关保护)。因此,掌握 libyuv 的获取与构建方式,也是理解 FreeSWITCH 视频通路底层能力的基础。
一、前置条件:安装 depot tools
无论使用 GN+Ninja 还是 gclient 同步源码,首先都需要安装 Chromium 项目的depot tools(其中包含gclient、gn、ninja等核心工具)。官方文档要求参考 Chromium 的安装说明,并按照各平台说明准备其余编译依赖(编译器、SDK 等)。
提示:在 FreeSWITCH 仓库内,libyuv 已经以完整第三方源码形式内置在
libs/libyuv/目录中,如果你只是想在 FreeSWitch 构建体系内使用它,可以直接跳过"获取代码"一节,从构建部分开始;下面的获取与构建流程主要面向 libyuv 的独立开发与调试场景。
二、获取 libyuv 源码
2.1 通过 gclient 同步(推荐,包含完整依赖)
创建一个工作目录并进入,然后执行:
gclient config --name src https://chromium.googlesource.com/libyuv/libyuv gclient sync执行后会在工作目录生成一个.gclient文件,内容如下:
solutions = [ { "name" : "src", "url" : "https://chromium.googlesource.com/libyuv/libyuv", "deps_file" : "DEPS", "managed" : True, "custom_deps" : { }, "safesync_url": "", }, ];其中:
name指定源码检出到本地目录名(src);url为仓库地址;deps_file指向DEPS文件,gclient sync会按照它递归拉取全部构建依赖(如 gtest、gflags、libjpeg、Chromium 构建工具链等);managed: True表示依赖由 gclient 统一管理版本。
2.2 仅获取源码(不可直接构建)
如果只需要阅读或修改源码,而不需要构建环境,可以直接 clone:
git clone https://chromium.googlesource.com/libyuv/libyuv2.3 为 iOS / Android 增加 target_os
gclient默认只同步当前宿主平台的依赖。若需要跨平台构建,需在.gclient中追加target_os声明。
iOS:在 macOS 的.gclient末尾追加;target_os=['ios'];,然后重新执行gclient sync。
Android:在 Linux 的.gclient中追加target_os = ["android", "linux"];,完整示例:
solutions = [ { "name" : "src", "url" : "https://chromium.googlesource.com/libyuv/libyuv", "deps_file" : "DEPS", "managed" : True, "custom_deps" : { }, "safesync_url": "", }, ]; target_os = ["android", "linux"];修改后再次运行:
gclient sync三、使用 GN + Ninja 构建库与单元测试
libyuv 的主构建系统是 GN + Ninja。仓库根目录的 BUILD.gn 定义了全部构建目标,各目标的 SIMD 依赖(NEON / MSA / MMI)则由 libyuv.gni 中的构建参数控制:
libyuv_include_tests = !build_with_chromium libyuv_disable_jpeg = false libyuv_use_neon = current_cpu == "arm64" || (current_cpu == "arm" && (arm_use_neon || arm_optionally_use_neon)) libyuv_use_msa = (current_cpu == "mips64el" || current_cpu == "mipsel") && mips_use_msa libyuv_use_mmi = (current_cpu == "mips64el" || current_cpu == "mipsel") && mips_use_mmi从源码结构看,GN 会自动根据目标 CPU 架构决定是否编译libyuv_neon、libyuv_msa、libyuv_mmi这些独立的静态库,把对应平台的汇编优化代码(如source/row_neon.cc、source/row_msa.cc)编入最终产物。
3.1 Windows
在 Windows 上分别生成 Release 与 Debug 工程(x64 与 x86):
call gn gen out\Release "--args=is_debug=false target_cpu=\"x64\"" call gn gen out\Debug "--args=is_debug=true target_cpu=\"x64\"" ninja -v -C out\Release ninja -v -C out\Debug call gn gen out\Release "--args=is_debug=false target_cpu=\"x86\"" call gn gen out\Debug "--args=is_debug=true target_cpu=\"x86\"" ninja -v -C out\Release ninja -v -C out\Debug3.2 macOS 与 Linux
gn gen out/Release "--args=is_debug=false" gn gen out/Debug "--args=is_debug=true" ninja -v -C out/Release ninja -v -C out/Debug3.3 官方 Release 构建(GN)
gn gen out/Official "--args=is_debug=false is_official_build=true is_chrome_branded=true" ninja -C out/Official3.4 iOS
先在.gclient最后一行追加target_os=['ios'];。
arm64 真机:
gn gen out/Release "--args=is_debug=false target_os=\"ios\" ios_enable_code_signing=false target_cpu=\"arm64\"" gn gen out/Debug "--args=is_debug=true target_os=\"ios\" ios_enable_code_signing=false target_cpu=\"arm64\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittest模拟器:
gn gen out/Release "--args=is_debug=false target_os=\"ios\" ios_enable_code_signing=false use_xcode_clang=true target_cpu=\"x86\"" gn gen out/Debug "--args=is_debug=true target_os=\"ios\" ios_enable_code_signing=false use_xcode_clang=true target_cpu=\"x86\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestiOS 反汇编(使用otool输出 NEON64 行处理汇编):
otool -tV ./out/Release/obj/libyuv_neon/row_neon64.o >row_neon64.txt3.5 Android
在.gclient最后一行追加target_os=['android'];。
arm64:
gn gen out/Release "--args=is_debug=false target_os=\"android\" target_cpu=\"arm64\"" gn gen out/Debug "--args=is_debug=true target_os=\"android\" target_cpu=\"arm64\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestarmv7:
gn gen out/Release "--args=is_debug=false target_os=\"android\" target_cpu=\"arm\"" gn gen out/Debug "--args=is_debug=true target_os=\"android\" target_cpu=\"arm\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestia32:
gn gen out/Release "--args=is_debug=false target_os=\"android\" target_cpu=\"x86\"" gn gen out/Debug "--args=is_debug=true target_os=\"android\" target_cpu=\"x86\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestmips(MIPS64 r6 + MSA,组件构建):
gn gen out/Release "--args=is_debug=false target_os=\"android\" target_cpu=\"mips64el\" mips_arch_variant=\"r6\" mips_use_msa=true is_component_build=true" gn gen out/Debug "--args=is_debug=true target_os=\"android\" target_cpu=\"mips64el\" mips_arch_variant=\"r6\" mips_use_msa=true is_component_build=true" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestarm 反汇编(使用 NDK 自带的 aarch64 objdump):
third_party/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump -d ./out/Release/obj/libyuv/row_common.o >row_common.txt third_party/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump -d ./out/Release/obj/libyuv_neon/row_neon.o >row_neon.txt third_party/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump -d ./out/Release/obj/libyuv_neon/row_neon64.o >row_neon64.txtCaveat:反汇编可能需要在 BUILD.gn 中禁用
optimize_max优化(源码中 Release 构建默认使用//build/config/compiler:optimize_max,即-O2而非-Os),否则反汇编结果会因内联/优化而难以阅读。
3.6 ARM Linux 与 MIPS Linux
ARM Linux(arm64):
gn gen out/Release "--args=is_debug=false target_cpu=\"arm64\"" gn gen out/Debug "--args=is_debug=true target_cpu=\"arm64\"" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittestMIPS Linux(mips64el + loongson3 + MMI):
gn gen out/Release "--args=is_debug=false target_os=\"linux\" target_cpu=\"mips64el\" mips_arch_variant=\"loongson3\" mips_use_mmi=true is_component_build=false use_sysroot=false use_gold=false" gn gen out/Debug "--args=is_debug=true target_os=\"linux\" target_cpu=\"mips64el\" mips_arch_variant=\"loongson3\" mips_use_mmi=true is_component_build=false use_sysroot=false use_gold=false" ninja -v -C out/Debug libyuv_unittest ninja -v -C out/Release libyuv_unittest3.7 可用构建目标
常用 GN 构建目标如下(在 Debug 输出目录):
ninja -C out/Debug libyuv ninja -C out/Debug libyuv_unittest ninja -C out/Debug compare ninja -C out/Debug yuvconvert ninja -C out/Debug psnr ninja -C out/Debug cpuid各目标的含义与 BUILD.gn 中的定义一一对应:
| 目标 | 说明 | 源码位置 |
|---|---|---|
libyuv | 主库(内部细分为libyuv_internal静态库,以及按需编译的libyuv_neon/libyuv_msa/libyuv_mmi) | BUILD.gn 中group("libyuv") |
libyuv_unittest | 基于 gtest 的单元测试程序,覆盖 convert / scale / rotate / planar / compare / cpu 等测试用例 | unit_test/ |
compare | 图像比较工具 | util/compare.cc |
yuvconvert | YUV 格式转换命令行工具 | util/yuvconvert.cc |
psnr | PSNR/SSIM 质量评估工具 | util/psnr.cc、util/psnr_main.cc、util/ssim.cc |
cpuid | CPU 特性检测工具 | util/cpuid.c |
四、使用 Make 构建(Linux)
libyuv 同时提供一份不依赖 GN 的通用 Makefile,适用于纯 gcc/g++ 环境。仓库根目录的 linux.mk 会编译全部source/*.cc并生成静态库libyuv.a,同时构建yuvconvert、cpuid、psnr三个工具(对应其all目标)。
make V=1 -f linux.mk make V=1 -f linux.mk clean make V=1 -f linux.mk CXX=clang++说明:
V=1输出详细编译日志;- 第三条命令使用 clang++ 替代默认的 g++ 作为 C++ 编译器;
- linux.mk 中默认
CFLAGS=-O2 -fomit-frame-pointer -Iinclude/,CXXFLAGS同理,生成物为libyuv.a、yuvconvert、cpuid、psnr。
五、使用 CMake 构建
libyuv 也支持标准 CMake 构建(构建脚本见 CMakeLists.txt),它同时生成静态库yuv与共享库yuv_shared,并额外构建yuvconvert转换工具。使用前请确认已安装 CMake。
5.1 默认 Debug 构建
mkdir out cd out cmake .. cmake --build .5.2 Release 构建与安装
mkdir out cd out cmake -DCMAKE_INSTALL_PREFIX="/usr/lib" -DCMAKE_BUILD_TYPE="Release" .. cmake --build . --config Release sudo cmake --build . --target install --config Releaseinstall目标会安装yuvconvert到bin、静态库/共享库到lib、全部头文件到include(见 CMakeLists.txt 中的INSTALL指令)。
5.3 生成 RPM / DEB 软件包
mkdir out cd out cmake -DCMAKE_BUILD_TYPE=Release .. make -j4 make packageCMake 构建的可选开关:
-DTEST=ON:同时构建基于 gtest 的libyuv_unittest单元测试(CMake 会自动查找 gtest/gflags,若未安装则尝试从/usr/src/gtest源码编译 gtest,找不到会直接报错);- 检测到系统 libjpeg 时自动追加
-DHAVE_JPEG定义,为 JPEG/MJPEG 相关转换提供支持; - 打包逻辑位于 CM_linux_packages.cmake,其版本号通过正则从 include/libyuv/version.h 中的
LIBYUV_VERSION宏提取。
六、ARM 交叉编译环境准备
如需在 x86 主机上为 ARM 目标交叉编译,官方文档给出的依赖安装清单如下:
sudo apt-get install ssh dkms build-essential linux-headers-generic sudo apt-get install kdevelop cmake git subversion sudo apt-get install graphviz doxygen doxygen-gui sudo apt-get install manpages manpages-dev manpages-posix manpages-posix-dev sudo apt-get install libboost-all-dev libboost-dev libssl-dev sudo apt-get install rpm terminator fish sudo apt-get install g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf其中最后一行的g++-arm-linux-gnueabihf/gcc-arm-linux-gnueabihf是 ARM hard-float 交叉编译器,其余为文档生成、版本控制与构建辅助工具。
交叉编译 psnr 工具
安装好交叉工具链后,可以直接把 psnr 工具编译成 ARM 可执行文件:
cd util arm-linux-gnueabihf-g++ psnr_main.cc psnr.cc ssim.cc -o psnr arm-linux-gnueabihf-objdump -d psnr第二条命令用于反汇编生成的psnr,便于检查交叉编译产物是否包含预期的指令集。
七、运行单元测试
7.1 基本运行
Windows:
out\Release\libyuv_unittest.exe --gtest_catch_exceptions=0 --gtest_filter="*"macOS 与 Linux:
out/Release/libyuv_unittest --gtest_filter="*"--gtest_filter支持通配符,用于筛选特定用例,例如只运行I420ToARGB的优化路径测试:
out/Release/libyuv_unittest --gtest_filter=*I420ToARGB_Opt7.2 基准测试模式
单元测试默认使用较小的 128x72 图像以加速运行;当重复次数大于 1 时,会自动切换到 1280x720(720p)分辨率。基准测试通过命令行参数控制分辨率、迭代次数与 CPU 路径:
out/Release/bin/run_libyuv_unittest -vv --gtest_filter=* --libyuv_width=1280 --libyuv_height=720 --libyuv_repeat=999 --libyuv_flags=-1 --libyuv_cpu_info=-1强制使用 C 代码(关闭 SIMD):
out/Release/bin/run_libyuv_unittest -vv --gtest_filter=* --libyuv_width=1280 --libyuv_height=720 --libyuv_repeat=999 --libyuv_flags=1 --libyuv_cpu_info=1这些参数的读取逻辑可以直接在 unit_test/unit_test.cc 中看到:测试基类构造函数通过getenv("LIBYUV_REPEAT")、LIBYUV_WIDTH、LIBYUV_HEIGHT、LIBYUV_FLAGS、LIBYUV_CPU_INFO读取环境变量,并允许 gflags 命令行参数(--libyuv_width等)覆盖环境变量。参数语义如下:
| 参数 | 含义 |
|---|---|
--libyuv_width/--libyuv_height | 测试图像宽高,默认 128x72,基准测试建议 1280x720 |
--libyuv_repeat | 重复运行次数,>1 时自动切换 720p 分辨率,用于统计性能 |
--libyuv_flags | 参考实现(C 代码)的 CPU 标志,1表示纯 C,-1表示全部启用 SIMD |
--libyuv_cpu_info | 基准被测代码的 CPU 标志,1禁用 SIMD,-1启用全部 SIMD |
7.3 通过环境变量控制 CPU 特性(调试定位)
与单元测试配套的 docs/environment_variables.md 详细列出了测试专用的 CPU 禁用环境变量。将这些变量设为非零值即可禁用对应指令集,迫使代码回退到低级实现,便于定位 SIMD 路径的 bug 或对比性能:
- 全平台:
LIBYUV_DISABLE_ASM - Intel:
LIBYUV_DISABLE_X86、LIBYUV_DISABLE_SSE2、LIBYUV_DISABLE_SSSE3、LIBYUV_DISABLE_SSE41、LIBYUV_DISABLE_SSE42、LIBYUV_DISABLE_AVX、LIBYUV_DISABLE_AVX2、LIBYUV_DISABLE_ERMS、LIBYUV_DISABLE_FMA3、LIBYUV_DISABLE_F16C、LIBYUV_DISABLE_AVX512BW、LIBYUV_DISABLE_AVX512VL、LIBYUV_DISABLE_AVX512VBMI、LIBYUV_DISABLE_AVX512VBMI2、LIBYUV_DISABLE_AVX512VBITALG、LIBYUV_DISABLE_AVX512VPOPCNTDQ、LIBYUV_DISABLE_GFNI - ARM:
LIBYUV_DISABLE_NEON - MIPS:
LIBYUV_DISABLE_MSA、LIBYUV_DISABLE_MMI
环境变量方式与命令行方式等价:从源码看,unit_test/unit_test.cc 中的TestCpuEnv()会读取这些环境变量,把对应的kCpuHas*位从 CPU 标志中清除;最终通过libyuv::MaskCpuFlags()生效(该接口声明在 include/libyuv/cpu_id.h,MaskCpuFlags(-1)启用全部优化、MaskCpuFlags(1)全部禁用)。
八、CPU 仿真与静态分析工具
8.1 Intel SDE(Software Development Emulator)
在尚未支持最新指令集的机器上,可用 Intel SDE 仿真运行单元测试,验证 AVX2/AVX-512 等新指令路径的正确性:
c:\intelsde\sde -hsw -- out\Release\libyuv_unittest.exe --gtest_filter=* ~/intelsde/sde -skx -- out/Release/libyuv_unittest --gtest_filter=**I420ToARGB_Opt其中-hsw(Haswell)与-skx(Skylake-X)指定仿真目标微架构。
8.2 Intel Architecture Code Analyzer(IACA)
IACA 用于分析汇编代码的吞吐量与延迟。使用时在待分析的汇编代码中插入两个宏标记:
IACA_ASM_START IACA_ASM_END按常规方式编译后,对目标对象文件运行 IACA 分析:
~/iaca-lin64/bin/iaca.sh -reduceout -arch HSW out/Release/obj/libyuv_internal/compare_gcc.o九、Sanitizer 与内存检查
9.1 编译期 Sanitizer
GN 构建支持通过is_msan=true等参数开启内存/未定义行为检查,例如:
gn gen out/Release "--args=is_debug=false is_msan=true" ninja -v -C out/Release可用的 Sanitizer 包括:asan(地址)、msan(内存未初始化)、tsan(线程竞争)、ubsan(未定义行为)、lsan(泄漏)、ubsan_vptr(虚函数指针 UB)。
9.2 Windows 下的 Dr Memory
在 Windows 上可借助 Dr Memory 做内存错误检查,需要先安装 Dr Memory 并加入 PATH,然后:
drmemory out\Debug\libyuv_unittest.exe --gtest_catch_exceptions=0 --gtest_filter=*十、深入阅读与 FreeSWITCH 集成点
除本指南外,libyuv 在仓库中还有多份配套文档与实现,便于继续深入:
- libyuv 项目概述:YUV 缩放、转换、旋转与 SIMD 优化概览;
- docs/formats.md:libyuv 支持的 FOURCC 格式全表(I420/I422/I444/NV12/NV21/ARGB 系列等)及 OSX Core Media 像素格式映射;
- docs/rotation.md:旋转功能说明;
- docs/filtering.md:缩放滤波(point / bilinear / box)说明;
- docs/environment_variables.md:单元测试环境变量全集;
- docs/deprecated_builds.md:已废弃的 GYP 时代构建说明(仅作历史参考)。
在 FreeSWITCH 中,libyuv 的实际集成集中在 src/switch_core_video.c:例如switch_img_rotate()直接调用I420Rotate完成图像 90/180/270 度旋转;switch_I420ToARGB()封装I420ToARGB供上层模块(如mod_av的 avformat.c)进行 YUV→RGB 渲染转换。这些接口均以SWITCH_HAVE_YUV宏为编译开关,对应 FreeSWITCH 在配置 libyuv 时的启用逻辑。
掌握本指南中的构建流程后,无论是独立开发 libyuv 的 SIMD 优化,还是在 FreeSWITCH 中定位视频格式转换问题,都可以通过 GN、Make、CMake 三种方式快速产出可调试的库与测试程序,并借助单元测试参数、CPU 仿真器和 Sanitizer 精准复现与定位问题。
- 通信
- 音视频
【免费下载链接】freeswitch
FreeSWITCH is a Software Defined Telecom Stack enabling the digital transformation from proprietary telecom switches to a versatile software implementation that runs on any commodity hardware. From a Raspberry PI to a multi-core server, FreeSWITCH can unlock the telecommunications potential of any device.
相关推荐
Pyrite64节点图内置节点速查表:Flow/Math/Vector/Object八大类节点全清单
Pyrite64节点图内置节点速查表:Flow/Math/Vector/Object八大类节点全清单 Pyrite64 是基于 libdragon 与 tiny
OR-Tools .NET NuGet 多平台包构建指南:基于 CMake 与 dotnet CLI 的完整实战
OR Tools .NET NuGet 多平台包构建指南:基于 CMake 与 dotnet CLI 的完整实战 本篇技术指南聚焦 Google OR Tool
科学计算jonathandinu/face-parsing模型可视化工具:网络结构与数据流展示
jonathandinu/face parsing模型可视化工具:网络结构与数据流展示 jonathandinu/face parsing是一款强大的面部解析模
人工智能深度学习计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考