2. GStreamer基础概念
嵌入式环境搭建,说白了就是给目标板子「定制」一套能跑的 GStreamer。跟 PC 上 apt install 完事不一样,嵌入式你得自己动手,从工具链到源码,再到裁剪,每一步都可能踩坑。我个人习惯先把整个流程拆成三块:工具链、编译、裁剪。咱们一块一块来。
2.1 交叉编译工具链配置
工具链选不对,后面全白干。我见过不少新手拿着 Linaro 的 GCC 去编译 glib,结果链接阶段报一堆莫名其妙的错。嗯,这里要注意:工具链必须跟目标系统的 libc 版本、内核头文件、CPU 架构 严格匹配。
2.1.1 工具链获取方式
- 官方 SDK: 厂家提供,最省心。比如 NXP 的 Yocto BSP、Rockchip 的 buildroot 输出。我建议优先用这个。
- Linaro 预编译工具链: 适合 ARM 通用平台。下载
gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz这类包。 - crosstool-NG 自编译: 适合特殊架构或定制 glibc。我曾经为了一个 MIPS 老平台,用 crosstool-NG 折腾了两天才搞定。
2.1.2 环境变量配置
解压工具链后,关键是把 bin 目录加入 PATH,并设置 CC、LD 等变量。我个人习惯写一个 setenv.sh 脚本,每次编译前 source 一下:
#!/bin/bash
export TOOLCHAIN_PATH=/opt/gcc-linaro-7.5.0-arm-linux-gnueabihf
export PATH=$TOOLCHAIN_PATH/bin:$PATH
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export LD=arm-linux-gnueabihf-ld
export AR=arm-linux-gnueabihf-ar
export STRIP=arm-linux-gnueabihf-strip
export SYSROOT=$TOOLCHAIN_PATH/arm-linux-gnueabihf/libc
export CFLAGS="--sysroot=$SYSROOT -march=armv7-a -mfpu=neon -mfloat-abi=hard"
export LDFLAGS="--sysroot=$SYSROOT"
--sysroot 告诉编译器去哪找头文件和库。如果漏了,编译时可能找到 PC 上的 /usr/include,链接时就会报错。
2.2 GStreamer 源码编译
GStreamer 依赖一堆库:glib、libffi、zlib、libxml2 等等。你想想看,如果一个个手动编译,光依赖就能让你崩溃。我建议用 Meson 构建系统,它比 autotools 快得多,而且依赖管理清晰。
2.2.1 依赖库交叉编译
先编译基础库,顺序很重要:
- zlib: 最简单的,
./configure --prefix=$SYSROOT/usr --host=arm-linux-gnueabihf就行。 - libffi: 注意要启用
--with-sysroot。 - glib: 这是大头。Meson 交叉编译需要写一个
cross-file.txt。
这是我的 cross-file.txt 示例:
[binaries]
c = 'arm-linux-gnueabihf-gcc'
cpp = 'arm-linux-gnueabihf-g++'
ar = 'arm-linux-gnueabihf-ar'
strip = 'arm-linux-gnueabihf-strip'
pkgconfig = 'arm-linux-gnueabihf-pkg-config'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7hl'
endian = 'little'
[properties]
sys_root = '/opt/gcc-linaro-7.5.0-arm-linux-gnueabihf/arm-linux-gnueabihf/libc'
pkg_config_libdir = '/opt/gcc-linaro-7.5.0-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib/pkgconfig'
然后编译 glib:
meson setup builddir --cross-file cross-file.txt \
-Dprefix=$SYSROOT/usr \
-Dlibdir=lib \
-Dbuildtype=release
ninja -C builddir
ninja -C builddir install
-mfloat-abi=soft 必须加上,否则 glib 的原子操作会崩溃。我曾经在一个 ARM926EJ-S 上吃过这个亏,排查了整整一下午。
2.2.2 GStreamer 核心编译
依赖搞定后,编译 GStreamer 本身反而简单。核心模块包括:
- gstreamer: 核心框架
- gst-plugins-base: 基础插件(appsrc、fakesink、typefind 等)
- gst-plugins-good: 常用编解码封装(wavparse、qtdemux 等)
- gst-plugins-bad: 实验性插件(需要时才加)
- gst-rtsp-server: 如果要做流媒体服务
编译命令类似,以 gstreamer 为例:
meson setup builddir --cross-file cross-file.txt \
-Dprefix=/usr \
-Dlibdir=lib \
-Dbuildtype=release \
-Dgst_debug=false \
-Dptp=disabled \
-Dnet=disabled
ninja -C builddir
DESTDIR=/path/to/target/rootfs ninja -C builddir install
DESTDIR? 这样安装到临时目录,方便后续打包成 .tar.gz 或 .ipk,不会污染 PC 的系统目录。
2.3 最小化裁剪策略
嵌入式设备存储空间金贵,Flash 可能只有 16MB 甚至 8MB。GStreamer 全编译出来动辄几十 MB,必须裁剪。我总结了一套「三步裁剪法」:
2.3.1 第一步:功能裁剪(Meson 选项)
编译时关掉不需要的功能:
| Meson 选项 | 作用 | 裁剪建议 |
|---|---|---|
-Dgst_debug=false |
关闭调试日志 | Release 版必关,省 30% 体积 |
-Dptp=disabled |
关闭 PTP 时钟同步 | 非音视频同步场景可关 |
-Dnet=disabled |
关闭网络功能 | 纯本地播放可关 |
-Dpython=disabled |
关闭 Python 绑定 | 嵌入式几乎不用 |
-Dexamples=disabled |
不编译示例 | 必关 |
-Dtests=disabled |
不编译测试 | 必关 |
2.3.2 第二步:插件裁剪(按需加载)
GStreamer 插件是动态库,每个 .so 文件都可以单独删除。我建议先编译所有插件,然后用 strip 和 rm 清理:
# 只保留必要插件
cd /path/to/target/rootfs/usr/lib/gstreamer-1.0
rm -f libgstalsa.so # 不用 ALSA 音频
rm -f libgstpulse.so # 不用 PulseAudio
rm -f libgstv4l2.so # 不用 V4L2 视频采集
rm -f libgstx264.so # 不用 H.264 编码
# ... 按需删除
# strip 所有保留的 .so
arm-linux-gnueabihf-strip --strip-unneeded *.so
一个典型的 IP Camera 场景,只需要保留:
libgstcoreelements.so(核心元素)libgstapp.so(appsrc/appsink)libgstrtp.so(RTP 打包)libgstvideoparsersbad.so(H.264 解析)libgstjpeg.so(JPEG 编码)
2.3.3 第三步:链接时裁剪(LTO + 符号表)
如果还嫌大,可以启用 LTO(链接时优化):
meson setup builddir --cross-file cross-file.txt \
-Db_lto=true \
-Dc_args="-ffunction-sections -fdata-sections" \
-Dc_link_args="-Wl,--gc-sections -Wl,-s"
这些选项会去掉未使用的函数和数据段。我曾在某个项目中,靠 LTO 把 GStreamer 核心库从 2.1MB 砍到了 1.3MB。
全编译:~18MB(含所有插件)
功能裁剪后:~8MB
插件裁剪后:~3.5MB
LTO 后:~2.8MB
2.4 整体流程框架
下面这张图概括了从工具链到裁剪的完整流程,我建议你把它打印出来贴在工位上:
2.5 验证与测试
编译部署后,别急着跑复杂 pipeline。先用最简单的命令验证:
# 在目标板子上执行
gst-launch-1.0 --version
gst-inspect-1.0 --plugin # 查看已安装插件列表
gst-launch-1.0 fakesrc ! fakesink # 测试核心框架是否正常
如果 fakesrc ! fakesink 能跑通,说明核心框架没问题。然后逐步加元素,比如 audiotestsrc ! fakesink 测试音频路径。我曾经遇到一个情况,fakesrc 正常,但 videotestsrc 报错,最后发现是 libgstvideo 库没拷全。嗯,这种问题只能靠耐心排查。
libgsttypefindfunctions.so,结果所有媒体文件都无法自动识别类型。这个插件虽然小,但必不可少。建议保留 coreelements、typefindfunctions、app 这三个基础插件,其他按需裁剪。
好了,嵌入式环境搭建就这些。工具链配好、依赖编好、裁剪到位,你的 GStreamer 就能在资源受限的板子上跑得飞起。记住:裁剪不是越狠越好,够用就行。