自从手里多了一块RISC-V开发板我就在反复折腾同一个问题怎么在板子上跑起FFmpeg来处理视频。板子本身的算力有限在上面直接编译源码属于典型的“豆腐盘成肉价钱”耗时几个小时不说动不动还能因为内存不够把编译进程给杀了。最靠谱的办法就是在PC上做交叉编译把RISC-V架构的二进制文件编好再丢到板子上跑。这篇文章就围绕“Fedora下为RISC-V交叉编译FFmpeg”这件事把我实操过程中的完整步骤、参数拆解、踩坑记录都写清楚适合刚接触RISC-V交叉编译、以及想在Fedora上快速产出可运行FFmpeg的读者参考。1. 为什么非要在Fedora上给RISC-V交叉编译FFmpeg1.1 交叉编译到底在解决什么问题很多朋友第一次听到“交叉编译”会觉得玄乎其实用一个不精确但容易理解的类比你在纸上画了一张桌子图纸但你并不想在画图纸的地方就把桌子敲出来而是要把图纸寄到另一个地方让别人按图施工。对应到技术上我们在x86_64的Fedora机器上写代码、跑编译器、生成二进制文件但生成出来的程序不是给x86_64跑的而是给RISC-V架构的处理器跑的。这个过程中最重要的就是工具链编译器、汇编器、链接器必须都认识RISC-V指令集才能生成正确的机器码。之所以不用开发板本体去编译FFmpeg原因非常现实。FFmpeg的完整编译非常吃资源尤其是开启大量外部库和优化参数时编译器的内存占用和CPU耗时都会暴涨。我在一块4核、2GB内存的RISC-V板子上试过一次configure过程还算安全进入make阶段后内存直接吃满系统卡到几乎没法操作。交叉编译相当于把“施工现场”从工地挪到了条件更好的工厂里机器码拿到现场直接安装就行。1.2 为什么选Fedora而不是Ubuntu或者直接上SDKRISC-V交叉编译的常见方案有很多。很多人习惯在Ubuntu上用apt安装gcc-riscv64-linux-gnu或者直接下载芯来、平头哥等厂商提供的定制工具链SDK。这些方案不是不行但我个人在实际使用后还是更喜欢在Fedora上做原因有几个。Fedora对较新的工具链版本跟进非常积极。无论是binutils、glibc还是GCCFedora仓库里的版本通常都比其他发行版更新。RISC-V指令集和相关ABI规范还在持续发展较新的编译器对指令生成、向量扩展、原子操作等特性的支持会更完善为RISC-V交叉编译FFmpeg时能少踩很多莫名其妙的坑。Fedora的包管理方式也比较干净。用dnf安装交叉编译工具链后系统会把独立的交叉环境放到/usr/riscv64-linux-gnu目录下与宿主机的x86_64环境彻底隔离不会出现头文件互相污染的问题。至于不用厂商SDK主要是可维护性考虑。厂商工具链往往体积巨大而且会自带一整套sysroot和构建脚本一旦升级版本脚本改动成本很高。用Fedora自带的工具链整个过程就是标准的GNU工具链工作流遇到问题时网上资料、邮件列表、GCC文档都能直接套用。1.3 FFmpeg与RISC-V生态的适配现状FFmpeg本身对RISC-V的支持已经相当成熟尤其是6.0之后的版本RISC-V的优化汇编代码覆盖率提升明显。早期版本在RISC-V平台上基本只能靠C语言兜底性能虽然可用但浪费了RISC-V的向量扩展潜力。如果你现在还在用5.x甚至更老的版本我建议直接用较新的主线版本或者至少选择6.0以上的release这样在configure时可以放心开启架构相关选项。还需要留意的是RISC-V本身有两种主流的ABI和指令集组合rv64gc和rv32ima等。大多数开发板用的是64位Linux环境对应riscv64-linux-gnu工具链类和目录里的交叉工具链前缀都是riscv64-linux-gnu-。如果你的目标是裸机或RTOS环境那编译FFmpeg基本没有意义毕竟FFmpeg依赖Linux系统调用和用户态库我们还是老老实实按linux用户态来编译。2. 环境准备先把手里的工具链弄齐整2.1 安装交叉编译工具链在Fedora里安装RISC-V交叉编译工具链非常直接用dnf搜索一下就能看到相关包sudo dnf search riscv64正常情况下会看到gcc-riscv64-linux-gnu、binutils-riscv64-linux-gnu、glibc-riscv64-linux-gnu等包。直接安装即可sudo dnf install gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu glibc-riscv64-linux-gnu如果你的Fedora版本较新可能还需要安装libc-devel相关的交叉包比如glibc-devel-riscv64-linux-gnu之类具体以dnf搜索出来的结果为准。装完以后先确认工具链可用riscv64-linux-gnu-gcc --version riscv64-linux-gnu-g --version riscv64-linux-gnu-ld --version这里需要强调一个很多人容易忽略的细节安装完工具链后系统里只多了交叉编译器本身并不会自动帮你把zlib、libssl、x264之类的第三方库也变成RISC-V版本。FFmpeg的编译过程会检测这些依赖库如果检测不到就会在configure阶段报错或者自动禁用相关功能。所以在编译之前你最好先想清楚最终需要哪些功能再决定是为这些库单独做一次交叉编译还是先用一个相对精简的配置把FFmpeg跑起来。2.2 准备sysroot与目录习惯sysroot是交叉编译中非常重要的一个概念。可以简单理解为“目标机器的根文件系统的浓缩版”编译器在交叉编译时默认去这个目录下找头文件、标准库和链接库而不是去找宿主机/x86_64下的那一套。Fedora安装交叉工具链后sysroot路径通常在/usr/riscv64-linux-gnu/sys-root这个目录下会有usr/include、usr/lib、lib等子目录结构和正常Linux根目录类似。在后续给FFmpeg传参数时最好通过--extra-cflags和--extra-ldflags显式指向这个sysroot避免编译器误用宿主机的头文件。我个人的习惯是建立一个专门的交叉编译工作目录把源码、产物、依赖库分开存放mkdir -p ~/riscv-work/{src,build,sysroot} cd ~/riscv-worksysroot目录不直接动系统默认的那个而是在自己的目录里再维护一份额外库的安装位置。这样做的原因后面会提到当你想给RISC-V平台额外编译x264时安装前缀就要指向这个自定义sysroot保证FFmpeg编译时能找到它们。2.3 版本检查清单很关键正式进入FFmpeg编译前花两分钟做一轮版本检查能省掉后面大量排查时间。我习惯于把下面几条命令都执行一遍并把版本记录下来检查项命令预期结果GCC版本riscv64-linux-gnu-gcc --version能看到较新的GCC版本号架构规格riscv64-linux-gnu-gcc -dumpmachine输出riscv64-linux-gnu之类系统库路径ls /usr/riscv64-linux-gnu/sys-root/usr/lib存在libgcc_s.so、libc.so等pkg-configpkg-config --versionpkg-config本身已安装如果gcc的dumpmachine输出里带了奇怪的vendor字段不要慌那只是厂商信息不影响使用。最需要确认的是sys-root下确实有libgcc_s.so和libc.so这样的文件否则后面链接阶段一定会报找不到库的错误。我碰到过一次比较意外的情况装完工具链后gcc可以用但ld始终报错“cannot find crt1.o”。排查下来是glibc-riscv64-linux-gnu没有安装完整部分启动文件缺失。重新执行dnf安装对应包之后问题就消失了。所以如果后续链接报错第一反应应该是检查sys-root下的文件完整性而不是怀疑FFmpeg配置写错了。3. FFmpeg源码归档与configure参数逐项拆解3.1 下载源码并且确认版本FFmpeg的源码获取方式很简单官网git仓库或者release tarball都行。我习惯直接拉release源码包因为git clone的仓库可能包含很多未发布的开发分支稳定性不如正式releasecd ~/riscv-work/src wget https://ffmpeg.org/releases/ffmpeg-6.1.2.tar.xz tar xf ffmpeg-6.1.2.tar.xz cd ffmpeg-6.1.2关于版本我建议选6.1.x或者7.0以上。太老的版本4.x、5.x在RISC-V汇编生成上确实有差距而且对新版GCC的兼容性也没有那么好。太新的git版本可能引入一些实验性特性对交叉编译的第三方库校验也更严格反而不容易一次通过。下载后先看看目录里的configure文件是否存在。FFmpeg在构建前不需要autogenrelease包中已经包含configure脚本直接使用。3.2 configure核心参数说明FFmpeg的configure脚本和大多数GNU项目一样本质是生成Makefile和config.h。交叉编译时关键参数如下./configure \ --prefix/usr \ --archriscv64 \ --target-oslinux \ --enable-cross-compile \ --cross-prefixriscv64-linux-gnu- \ --ccriscv64-linux-gnu-gcc \ --disable-x86asm \ --enable-static \ --disable-shared \ --disable-doc \ --disable-debug \ --disable-stripping \ --extra-cflags-I/usr/riscv64-linux-gnu/sys-root/usr/include \ --extra-ldflags-L/usr/riscv64-linux-gnu/sys-root/usr/lib逐个说明一下这些参数的实际作用。--archriscv64告诉FFmpeg目标架构是RISC-V 64位。这个参数会影响编译器的-march选择以及内部调度策略的默认值。这里要注意--arch和--cpu是两个不同维度如果你明确知道目标CPU支持什么扩展可以再用--cpugeneric-rv64之类的选项细化但普通场景下不设置--cpu反而更省心。--target-oslinux这个参数也很重要它控制的是目标平台的操作系统类型。因为FFmpeg内部会根据操作系统区分系统调用、多线程模型、内存分配方式等代码路径。写死为linux可以让它在configure阶段就选择正确的头文件和系统函数接口。--enable-cross-compile和--cross-prefix是交叉编译的开关。前者会让configure跳过一些只能在目标机上运行的测试后者把riscv64-linux-gnu-作为所有编译工具的前缀。理论上--cc指定了交叉gcc--cross-prefix可以让ar、strip、ranlib等工具也自动带上前缀这两条最好都写上。--disable-x86asm这个参数是很多人在RISC-V交叉编译时最容易漏掉的坑。FFmpeg默认会尝试调用nasm/yasm来编译x86汇编优化。RISC-V是截然不同的指令集根本不需要也不应该去编译x86汇编。如果不带这个参数configure一旦发现系统装过nasm就可能尝试去生成x86相关汇编文件最后在编译阶段直接报错。所以务必要加--disable-x86asm。这里顺便解释一下为什么不是--disable-asm。--disable-asm会把所有架构的汇编优化都关掉包括RISC-V自身的优化。而RISC-V架构的优化汇编是FFmpeg性能的重要来源特别是RVV向量优化已经能带来不小的提升。所以我们的目标是只关掉x86的汇编保留RISC-V的汇编这就是--disable-x86asm的精髓。--enable-static和--disable-shared的选择需要根据实际需求来。如果只是想快速验证交叉编译链路通不通静态链接会更简单因为产物只有一个ffmpeg二进制不需要额外拷贝一堆.so。但如果目标板上已经有一套RISC-V的动态库环境或者希望后续动态加载外部库那么--enable-shared可能更灵活。我这次选择静态链接主要是为了减少在开发板上部署的环节。--disable-doc、--disable-debug、--disable-stripping是打包常规操作分别关掉文档、调试信息和安装时的strip操作。stripping其实无损但放在最后交叉编译时有时因为工具链的strip与宿主机gcc版本不完全匹配出现“Failed to strip”之类的坑直接禁用最保险。--extra-cflags和--extra-ldflags这两个参数在交叉编译时非常重要。上面写的路径指向系统自带的RISC-V sysroot保证能正确引用目标架构的头文件和库。如果你之后给RISC-V平台额外编译了x264并装在自定义目录里记得要在这个基础上追加你自己的-I和-L路径。3.3 外部库的分歧要不要碰libx264很多朋友交叉编译FFmpeg是为了H.264编码所以会纠结是否集成libx264。我的建议是第一次做RISC-V交叉编译时先不要集成x264把FFmpeg本身跑通再说。原因倒不是x264很难编而是每增加一个外部依赖configure阶段就会多一组检测逻辑问题排查时多一个变量。如果你确实需要libx264流程是这样的先下载x264源码然后用riscv64-linux-gnu-gcc进行交叉编译安装到自定义目录cd ~/riscv-work/src git clone https://code.videolan.org/videolan/x264.git cd x264 ./configure --hostriscv64-linux-gnu --enable-static --disable-opencl --prefix$HOME/riscv-work/sysroot make -j$(nproc) make install之后重新回到FFmpeg的configure追加参数--enable-libx264 \ --extra-cflags-I$HOME/riscv-work/sysroot/include \ --extra-ldflags-L$HOME/riscv-work/sysroot/lib这样FFmpeg在configure阶段就能通过检查。需要注意libx264的configure有时会提示缺少asm此时可以加--disable-asm让x264用C语言实现虽然编码速度有损失但功能完整。对RISC-V平台来说先把功能跑通比追求性能更重要。4. 执行编译从autogen到make install的完整过程4.1 configure失败的常见三种报错与定位执行configure之后如果运气不好会出现三类常见的报错。第一种是“C compiler test failed”或类似的编译器检测失败。这种问题八成是工具链本身没装完整。建议用riscv64-linux-gnu-gcc写个最简单的hello world试试能否编译链接。如果hello world都不行先回第2节检查工具链安装情况。第二种是“nasm/yasm not found”或“Unknown option --disable-x86asm”之类。前者表示你没有加--disable-x86asmconfigure默认去找nasm后者通常出现在低版本FFmpeg上它不认识这个参数。解决办法是升级FFmpeg版本。第三种是configure通过了但提示找不到zlib、libssl等库。这种一般是因为交叉工具链的sysroot里没有对应库的头文件。FFmpeg对zlib是默认启用的找不到就会报错。如果你不需要压缩相关的输入输出可以加--disable-zlib但更好的做法是为RISC-V交叉编译一份zlib装到自定义sysroot里替换流程和前面x264一样。定位configure问题有个很好用的小技巧configure脚本会往ffbuild/config.log里写入大量检测细节。遇到报错后别急着改参数先打开config.log搜索最后一段“Failed”或者error相关的内容通常能直接看到是哪个头文件缺失、哪一行编译命令失败。比在终端里猜要高效得多。4.2 make并行编译与符号表问题configure成功之后进入编译阶段make -j$(nproc)我一般会先用-j4或者-j8不要盲目使用nproc所有核心。交叉编译本身比本机编译更吃内存如果本机内存不太宽裕并行度太高容易导致OOM。如果编译中途被系统杀掉最直接的解法就是把并行度调低重新make已经编译好的对象文件不会白费。这里还要提一个踩过的坑编译到某个位置时突然报“relocation truncated to fit: R_RISCV_HI20 against symbol”或者类似的重定位错误。这种现象在RISC-V交叉编译里不罕见通常与代码过大的静态链接有关。解决方案可以尝试给编译器增加-fPIC选项也就是在extra-cflags里加-fPIC让生成的代码更适合大型静态链接场景。如果你遇到“multiple definition of symbol”之类的重复定义错误大概率是FFmpeg版本与GCC版本之间的兼容性问题。可以尝试升级FFmpeg到更新版本或者降级GCC比如使用Fedora自带的旧工具链版本但升级FFmpeg通常更简单。4.3 安装到目标目录并处理依赖make完成后按照configure阶段设置的--prefix进行安装。由于我们在configure里写的是--prefix/usr直接make install会把文件安装到主机的/usr目录这显然不合适。交叉编译的安装目标应该是自定义目录或者你准备好的目标系统根目录。我习惯在make install时通过DESTDIR来指定安装位置避免直接写入主机系统目录make install DESTDIR$HOME/riscv-work/build/rootfs这样会生成一个rootfs形态的目录里面usr/bin下就有ffmpeg和ffprobe等产物。如果你后面想打包成镜像或者拷贝到开发板只需要把整个rootfs目录同步过去就行。安装完后检查一下产物是哪个架构file $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg如果输出里包含“UCB RISC-V”字样说明交叉编译已经成功。5. 产物验证在x86主机上跑RISC-V版FFmpeg5.1 file与readelf先检查二进制交叉编译完成不代表工作已经结束还要在宿主机上先验证产物可用性。第一步永远是file命令file $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg正常输出大致是ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, not stripped看到这些信息就说明架构对了。如果是静态链接最后不会出现interpreter那一段。接下来用readelf看它的动态依赖确认不会缺库readelf -d $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg | grep NEEDED如果输出为空或者只有libc和libm说明依赖很少部署会省事很多。如果出现一堆古怪的库路径你需要重新检查sysroot里的库是否都装了。5.2 qemu-user直接运行并试用命令在x86主机上直接执行RISC-V二进制肯定不行系统会报“Exec format error”。这时候就需要qemu-user来做用户态模拟。Fedora下安装qemu-user很简单sudo dnf install qemu-user然后可以直接qemu-riscv64 $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg -version如果看到ffmpeg version开头的输出说明程序能够在模拟环境中正常运行。这里有一个小坑需要注意qemu-riscv64执行动态链接二进制时默认会希望找到目标架构的动态链接器。如果你的FFmpeg是动态链接可能会报“Invalid ELF header”或者找不到/lib/ld-linux-riscv64-lp64d.so.1。解决办法有两个一是用静态编译二是给qemu加-L参数指定sysrootqemu-riscv64 -L /usr/riscv64-linux-gnu/sys-root $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg -version成功看到版本信息后再测试一个实际转换命令qemu-riscv64 -L /usr/riscv64-linux-gnu/sys-root $HOME/riscv-work/build/rootfs/usr/bin/ffmpeg -f lavfi -i testsrcduration5:size640x480:rate30 /tmp/test.mp4这条命令会用qemu模拟执行FFmpeg生成一个5秒的测试视频。如果顺利执行完说明FFmpeg的核心功能是正常的。5.3 传回开发板或镜像的注意事项验证完毕后把产物部署到真实的RISC-V开发板或者系统镜像里。需要注意几点。如果是把编译好的二进制直接拷贝到开发板的Linux系统务必保持动态库的一致性。你可以把在整个rootfs目录直接同步过去或者用qemu模拟时看到的NEEDED库列表从交叉sysroot里把对应库一起拷过去。静态链接的版本就无所谓了。权限和可执行位也容易被忽略。同步完成后在开发板上执行chmod x /usr/bin/ffmpeg然后运行ffmpeg -version一般就没有问题了。另外开发板上的Linux内核可能比较老但FFmpeg本身很少依赖新的内核特性。我见过有人拿一个很老的3.10内核跑新版FFmpeg只要glibc版本匹配基本没问题。如果运行时报错“FATAL: kernel too old”那是glibc的seterrno兼容性问题可以尝试在内核配置里打开CONFIG_COMPAT相关的选项或者换编译时使用的交叉glibc版本。6. 一些值得记录的进阶经验6.1 性能取舍RISC-V汇编与C语言的比重交叉编译完成后很多人会顺手去跑一个benchmark然后惊讶地发现FFmpeg在RISC-V板子上的编码速度并不理想。这其实很正常。RISC-V的向量扩展RVV虽然能提升性能但FFmpeg的rvv优化代码目前主要集中在特定的编码器、解码器和滤镜路径上很多复杂滤镜仍然走的是纯C路径。如果你最终追求的是编码性能而不是“能跑就行”那么建议在configure时确认当前FFmpeg版本是否默认启用RVV优化必要时可以尝试更新的git版本因为新版本对RVV的支持持续在完善。但也要做好心理准备RVV的收益在齐整数据类型上最明显视频编码这类不规则计算中未必能带来特别夸张的加速。6.2 交叉编译中的pkg-config陷阱FFmpeg的configure会大量使用pkg-config来检测外部库。在交叉编译时pkg-config如果找的是宿主机的.pc文件就会把头文件路径指到x86_64系统上最后编译报错一堆“找不到头文件”或“架构冲突”。这个问题非常隐蔽因为报错信息里往往看不出是pkg-config的问题。解决方案是设置环境变量或指定pkg-config路径。一个比较聪明的做法是让pkg-config只搜索交叉sysroot目录export PKG_CONFIG_PATH$HOME/riscv-work/sysroot/lib/pkgconfig:$HOME/riscv-work/sysroot/usr/lib/pkgconfig export PKG_CONFIG_LIBDIR$HOME/riscv-work/sysroot/usr/lib/pkgconfig然后确保configure时使用正确的pkg-config./configure ... --pkg-configpkg-config把pkg-config的搜索路径限制在自定义sysroot内能避免很多无厘头的头文件冲突。我建议每次切换外部库后都执行pkgs-config的输出检查确认搜到的库路径确实指向RISC-V环境。6.3 升级FFmpeg与持续集成思路RISC-V生态更新非常快FFmpeg版本也在迭代。交叉编译环境稳定后后续版本升级主要就是重复源码下载、configure、make三步唯一需要留意的是新增的库依赖项。建议写一个简单的shell脚本把整个流程固化下来包括工具链版本检查、sysroot路径配置、FFmpeg版本号参数这样从6.1升到7.0只会改动一行版本号。我自己的更新流程大概是修改版本号、重新下载、执行脚本、用qemu跑一遍smoke test然后在开发板上部署。整个过程半小时就能完成。对于需要长期维护RISC-V视频组件的团队这种方式比每次都手动敲configure命令要可靠得多。回到这次实践的收获上Fedora自带的RISC-V交叉工具链足够稳定FFmpeg的configure机制虽然参数多但把架构、系统类型、汇编选项三部曲记清楚后交叉编译就变成了一个流水线工作。即使后续遇到新报错先看config.log再查sysroot库基本上都能解决。如果你也是刚上手RISC-V希望这篇记录能帮你少走一点弯路。
