2、开发环境搭建:安装交叉编译工具链、配置Android NDK或Yocto SDK,验证硬件编解码节点(/dev/video*)

好,咱们正式开始动手了。

这一章,说白了就是给你的电脑装上「翻译官」和「工具箱」。你想想看,我们写的代码是在x86电脑上,但最终要跑到ARM架构的Exynos芯片上。这中间差了十万八千里,没有交叉编译工具链,代码根本跑不起来。

2.1 交叉编译工具链——为什么非得用这个?

我刚开始接触嵌入式开发时,也犯过傻。直接在Ubuntu上用gcc编译了一个hello world,然后兴冲冲地拷贝到开发板上,结果执行时提示「无法执行二进制文件」。嗯,当时我还以为是板子坏了。

其实原因很简单:你的电脑是x86架构,Exynos是ARM架构。两种CPU的指令集完全不同。交叉编译工具链,就是让你在x86电脑上生成ARM可执行文件的桥梁。

核心概念:交叉编译工具链 = 编译器 + 链接器 + 库文件,全部针对目标架构(ARM)定制。

我个人习惯用Linaro提供的工具链,稳定且社区支持好。对于Exynos平台,我建议你下载gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf这个版本。为什么选这个?我在项目中踩过坑,太新的工具链有时会和内核驱动有兼容性问题,这个版本经过大量验证,稳得很。

安装步骤其实很简单:

  1. 下载工具链压缩包
  2. 解压到 /opt/toolchains/ 目录
  3. 配置环境变量 PATH
# 下载(以Linaro 7.5为例)
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 解压
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/toolchains/

# 配置环境变量(写入 ~/.bashrc)
export PATH=/opt/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH

# 验证
arm-linux-gnueabihf-gcc --version

小技巧:我个人习惯在 /opt/toolchains/ 下建一个 current 软链接,指向当前使用的工具链版本。这样切换版本时只需要改软链接,不用改环境变量。

2.2 配置Android NDK——如果你用安卓系统

如果你的Exynos平台跑的是Android系统,那事情就简单一些了。Android NDK本身就是一套完整的交叉编译环境,而且Google已经帮你把坑填得差不多了。

我曾经在一个项目里,直接用NDK编译了一个视频解码库,扔到Exynos 9820的板子上,一次就跑通了。说实话,当时我自己都有点意外。

配置步骤:

  1. 下载Android NDK(推荐r21e或更高版本)
  2. 解压到本地目录
  3. 使用 ndk-buildcmake 工具链文件
# 下载NDK
wget https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip
unzip android-ndk-r21e-linux-x86_64.zip -d ~/android-ndk

# 使用CMake工具链文件
cmake -DCMAKE_TOOLCHAIN_FILE=~/android-ndk/build/cmake/android.toolchain.cmake \
      -DANDROID_ABI=arm64-v8a \
      -DANDROID_PLATFORM=android-28 \
      ..

注意:Exynos芯片的MFC(Multi-Format Codec)硬件编解码器,在Android下是通过HAL层暴露的。如果你直接用NDK编译的代码去操作 /dev/video* 节点,可能会遇到权限问题。我曾经被这个坑过——明明节点存在,但open()返回-1。后来发现是SELinux策略在作怪。

2.3 配置Yocto SDK——如果你用嵌入式Linux

对于嵌入式Linux场景,Yocto是主流选择。Exynos的官方BSP(Board Support Package)通常也是基于Yocto构建的。

Yocto SDK的好处是:它不光给你编译器,还给你一套完整的头文件和库文件,版本和你的目标系统完全一致。说白了,就是「你编译出来的东西,放到板子上就能跑」,不用再担心库版本不匹配的问题。

配置步骤:

  1. 从Yocto构建环境生成SDK
  2. 安装SDK到指定目录
  3. 运行环境设置脚本
# 在Yocto构建目录下生成SDK
bitbake core-image-minimal -c populate_sdk

# 安装SDK(生成的文件在 tmp/deploy/sdk/ 下)
./poky-glibc-x86_64-core-image-minimal-armv8a-toolchain-*.sh

# 设置环境变量
source /opt/poky/2.7/environment-setup-aarch64-poky-linux

# 验证
$CC --version

我的经验:Yocto SDK安装后,一定要运行那个 environment-setup-* 脚本。它不光设置 CCCXX 这些变量,还会设置 PKG_CONFIG_PATH,这样你编译依赖库时,pkg-config能自动找到正确的头文件和库路径。我第一次用Yocto时没注意这个,结果编译ffmpeg时死活找不到libavcodec的头文件,折腾了一下午。

2.4 验证硬件编解码节点——/dev/video* 的秘密

好了,工具链装好了,接下来要确认你的Exynos板子上,硬件编解码器是不是活着的。

在Exynos平台上,硬件编解码器通常以V4L2(Video for Linux 2)设备节点的形式暴露。你会在 /dev/ 目录下看到 video0video1 这样的文件。但注意,不是所有的 /dev/video* 都是编解码器——有些是摄像头,有些是M2M(Memory to Memory)设备。

怎么区分?我教你一个方法:

# 查看所有video设备
ls -l /dev/video*

# 使用v4l2-ctl查看设备能力
v4l2-ctl -d /dev/video0 --all

# 或者用media-ctl查看拓扑
media-ctl -p -d /dev/media0

对于Exynos的MFC硬件编解码器,你通常会看到类似这样的输出:

Driver name      : s5p-mfc
Card name        : s5p-mfc-dec
Bus info         : platform:11000000.codec
Driver version   : 5.10.9
Capabilities     : 0x84204000
    Video Memory-to-Memory
    Streaming
    Device Capabilities
    Device Caps   : 0x04204000
    Video Memory-to-Memory
    Streaming

看到 s5p-mfcVideo Memory-to-Memory 了吗?这就是我们要找的硬件编解码器。

关键点:Exynos的MFC设备通常有两个节点:一个用于解码(dec),一个用于编码(enc)。比如 /dev/video10 是解码器,/dev/video11 是编码器。具体编号因内核配置而异,但规律是一样的。

2.5 写一个简单的测试程序——验证一切就绪

光说不练假把式。我们来写一个最简单的C程序,验证交叉编译环境和硬件节点是否都正常。

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

int main() {
    int fd = open("/dev/video10", O_RDWR);
    if (fd < 0) {
        perror("open /dev/video10 failed");
        return -1;
    }

    struct v4l2_capability cap;
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("VIDIOC_QUERYCAP failed");
        close(fd);
        return -1;
    }

    printf("Driver: %s\n", cap.driver);
    printf("Card: %s\n", cap.card);
    printf("Bus info: %s\n", cap.bus_info);

    close(fd);
    return 0;
}

用交叉编译器编译:

arm-linux-gnueabihf-gcc -o test_video test_video.c

然后把 test_video 拷贝到板子上,运行。如果看到类似上面的驱动信息,恭喜你——环境搭建成功了!

避坑指南:我曾经在Exynos 5422板子上遇到一个怪事——/dev/video10 明明存在,但open()总是返回权限错误。后来发现是内核配置里把MFC的节点权限设成了600(仅root可访问)。解决方案是修改udev规则,或者用 chmod 666 /dev/video10 临时解决。嗯,这个问题在量产时一定要处理好,不然你的App会崩溃得很惨。

2.6 本章小结

这一章我们做了三件事:

  • 安装了交叉编译工具链,让你的x86电脑能编译ARM代码
  • 配置了Android NDK或Yocto SDK,根据你的系统选择
  • 验证了 /dev/video* 节点,确认硬件编解码器在线

说实话,环境搭建是最枯燥的部分,但也是最重要的。我见过太多人一上来就写编解码代码,结果发现工具链不对、节点打不开,白白浪费几天时间。所以,请务必按照本章的步骤,一步一步验证通过。

下一章,我们就要真正开始操作MFC硬件编解码器了。我会带你写第一个V4L2的编解码程序,让你亲眼看到硬件加速的效果。准备好了吗?


公众号:蓝海资料掘金营,微信deep3321