news 2026/10/15 16:10:19

ARM平台交叉编译MJPEG-Streamer:从依赖库到摄像头推流实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM平台交叉编译MJPEG-Streamer:从依赖库到摄像头推流实战

1. 项目缘起与整体设计思路

1.1 为什么要在ARM平台上折腾MJPEG-Streamer

先说说这个项目到底在干什么。MJPEG-Streamer是一个把摄像头采集到的MJPEG格式视频流,通过HTTP服务推送给浏览器或其他客户端的小工具。它的核心价值在于轻量、依赖少、延迟低,特别适合嵌入式设备做本地视频监控或者远程画面查看。我第一次接触它是在一个ARM开发板上做图像采集项目,板子性能有限,跑不动复杂的流媒体服务器,MJPEG-Streamer就成了最合适的选择。

但问题来了,官方源码默认是给x86桌面环境编译的,直接拿到ARM平台上编译,十有八九会报错。原因也不复杂:输入插件依赖V4L2(Video for Linux 2)接口,输出插件依赖JPEG编解码库,而ARM平台上的库版本、头文件路径、交叉编译工具链都和桌面环境不一样。所以这个移植笔记的核心目标,就是让MJPEG-Streamer在ARM板上稳定跑起来,能正常采集摄像头画面并通过网络推流。

适合谁看?如果你手上有ARM开发板、树莓派类的单板计算机,或者任何嵌入式Linux设备,想快速搭一个低成本的视频流服务,这篇内容应该能帮你省下不少查资料和试错的时间。不需要你精通内核驱动,但基本的Linux操作和交叉编译概念得有一点。

1.2 整体方案选型与架构拆解

MJPEG-Streamer的架构其实很清晰,分成三个部分:输入插件、核心调度、输出插件。输入插件负责从摄像头拿数据,核心部分负责把数据从输入搬到输出,输出插件负责把数据以HTTP流的形式发出去。在ARM移植场景下,我们主要关注的是输入插件和输出插件的依赖问题。

输入插件方面,最常用的是input_uvc.so,它通过V4L2接口和USB摄像头通信。这个插件依赖libjpeg库来做MJPEG帧的解析和必要的格式转换。输出插件方面,output_http.so是最常用的,它启动一个轻量HTTP服务器,把MJPEG流以multipart/x-mixed-replace的方式推给客户端。这个插件本身不依赖太多外部库,但需要确保网络相关的基础库在ARM平台上可用。

我选择的是交叉编译方案,而不是在ARM板上直接编译。原因有两个:一是ARM板的存储和内存有限,直接编译容易因为资源不足中断;二是交叉编译工具链在PC上跑得快,调试效率高。当然,如果你的ARM板性能足够,直接在板上编译也不是不行,后面我会把两种方式的差异和注意事项都讲清楚。

注意:交叉编译工具链的版本要和目标板上的系统库版本匹配,否则编译出来的二进制文件可能在板上跑不起来,报“GLIBC版本不兼容”之类的错误。这一点我在第一次移植时就踩过坑,后面会详细说。

2. 核心细节解析与实操要点

2.1 交叉编译工具链的选择与验证

交叉编译工具链是整个移植工作的基础。你需要根据ARM板的CPU架构和系统环境来选。常见的ARM架构有armv7l、aarch64(也就是arm64),前者是32位,后者是64位。你可以通过在ARM板上执行uname -m来确认。比如输出armv7l就说明是32位ARM,输出aarch64就是64位。

工具链的来源一般有三种:芯片厂商提供的官方工具链、Linaro等社区维护的通用工具链、以及发行版自带的交叉编译包。我个人的经验是,如果芯片厂商有提供,优先用厂商的,因为兼容性最有保障。如果没有,Linaro的通用工具链也能应付大多数场景。

选好工具链之后,第一件事是验证它能不能正常工作。写一个最简单的C程序,比如就打印一行字,然后用交叉编译器编译,把生成的二进制文件拷到ARM板上运行。这一步看起来简单,但能帮你排除掉很多低级问题,比如工具链路径没配好、动态链接器路径不对等等。

# 假设工具链前缀是 arm-linux-gnueabihf- arm-linux-gnueabihf-gcc -o hello hello.c file hello # 输出应该显示 ARM 架构

file命令的输出很关键,它会告诉你这个二进制文件的目标架构和动态链接器路径。如果显示的是x86架构,说明你用的还是本机编译器,交叉编译前缀没生效。

2.2 依赖库的交叉编译顺序与要点

MJPEG-Streamer在ARM平台上需要两个核心依赖库:libjpeg和libv4l。libjpeg负责JPEG编解码,libv4l负责V4L2设备的封装访问。这两个库都需要先交叉编译好,再编译MJPEG-Streamer本身。

libjpeg的交叉编译相对简单,它的构建系统是经典的configure+make。关键是在configure阶段指定正确的host参数和安装路径。我一般会把所有交叉编译的库安装到一个独立的sysroot目录下,这样编译MJPEG-Streamer时只需要指定一个--sysroot参数就能找到所有依赖,管理起来清爽。

./configure --host=arm-linux-gnueabihf \ --prefix=/opt/arm-sysroot/usr \ --enable-shared \ --enable-static make -j4 make install

libv4l的编译稍微麻烦一点,它依赖内核头文件。你需要确保工具链的sysroot里有对应版本的Linux内核头文件。如果没有,可以从内核源码里把include/uapi目录下的头文件拷过去。libv4l的configure阶段需要指定--with-kernel-headers参数指向这些头文件的位置。

实操心得:libv4l在交叉编译时经常报找不到linux/videodev2.h的错误。最直接的解决办法是从内核源码的include/uapi/linux/目录下把videodev2.h和相关头文件拷到工具链的sysroot里。别去改源码里的include路径,那样会引入更多问题。

2.3 MJPEG-Streamer源码的配置与编译

依赖库准备好之后,就可以编译MJPEG-Streamer本身了。它的构建系统也是configure+make,但需要额外指定一些参数来告诉它依赖库的位置。

./configure --host=arm-linux-gnueabihf \ --prefix=/opt/arm-sysroot/usr \ --disable-v4l2 \ --enable-v4l2 \ --disable-input-file \ --enable-input-uvc \ --disable-output-file \ --enable-output-http

这里有几个关键点。--disable-v4l2和--enable-v4l2看起来矛盾,实际上第一个是禁用旧的V4L1接口,第二个是启用V4L2接口。--disable-input-file和--disable-output-file是为了减小最终二进制体积,如果你不需要从文件读输入或者输出到文件,就把它们关掉。--enable-input-uvc和--enable-output-http是我们真正需要的功能。

编译过程中最常见的错误是找不到jpeglib.h或者libjpeg.so。这时候需要检查CPPFLAGS和LDFLAGS环境变量是否指向了正确的sysroot路径。

export CPPFLAGS="-I/opt/arm-sysroot/usr/include" export LDFLAGS="-L/opt/arm-sysroot/usr/lib"

编译完成后,用file命令确认生成的mjpg_streamer二进制文件是ARM架构,然后用arm-linux-gnueabihf-readelf -d查看它依赖的动态库列表,确保所有依赖都能在ARM板上找到。

3. 实操过程与核心环节实现

3.1 从零开始的完整编译流程

我把整个编译流程整理成了一套可以直接照着做的步骤。假设你已经有了交叉编译工具链,并且工具链的前缀是arm-linux-gnueabihf-,目标板的sysroot放在/opt/arm-sysroot。

第一步,准备目录结构。我会在/opt下建三个目录:arm-sysroot放目标板的库和头文件,arm-build放编译过程中的中间文件,arm-output放最终要拷到板子上的文件。

mkdir -p /opt/arm-sysroot/usr/{include,lib} mkdir -p /opt/arm-build mkdir -p /opt/arm-output

第二步,交叉编译libjpeg。下载libjpeg的源码包,解压后进入目录。

cd /opt/arm-build tar xzf jpegsrc.v9d.tar.gz cd jpeg-9d ./configure --host=arm-linux-gnueabihf \ --prefix=/opt/arm-sysroot/usr \ --enable-shared \ --enable-static make -j$(nproc) make install

编译完成后,检查/opt/arm-sysroot/usr/lib下是否有libjpeg.so和libjpeg.a,以及/opt/arm-sysroot/usr/include下是否有jpeglib.h。

第三步,交叉编译libv4l。这一步需要内核头文件,我一般从内核源码的include/uapi目录拷贝。

cd /opt/arm-build tar xjf v4l-utils-1.20.0.tar.bz2 cd v4l-utils-1.20.0 ./configure --host=arm-linux-gnueabihf \ --prefix=/opt/arm-sysroot/usr \ --with-kernel-headers=/opt/arm-sysroot/usr/include \ --disable-doxygen-doc \ --disable-libdvbv5 \ --disable-v4l-utils make -j$(nproc) make install

这里--disable-v4l-utils是为了只编译libv4l库,不编译那些用不上的命令行工具,节省时间。

第四步,编译MJPEG-Streamer。

cd /opt/arm-build tar xzf mjpg-streamer.tar.gz cd mjpg-streamer/mjpg-streamer-experimental export CPPFLAGS="-I/opt/arm-sysroot/usr/include" export LDFLAGS="-L/opt/arm-sysroot/usr/lib" make -j$(nproc)

注意,MJPEG-Streamer的experimental分支用的是Makefile而不是configure脚本,所以不需要执行configure。编译完成后,在当前目录下会生成mjpg_streamer可执行文件和plugins目录。

3.2 部署到ARM板上的文件组织

编译完成后,需要把以下文件拷到ARM板上:

  • mjpg_streamer可执行文件
  • plugins/input_uvc.so
  • plugins/output_http.so
  • libjpeg.so.9(如果板子上没有的话)
  • libv4l2.so和libv4lconvert.so(如果板子上没有的话)

我一般会在ARM板上建一个/opt/mjpg-streamer目录,把可执行文件和插件放进去,库文件放到/usr/lib或者通过LD_LIBRARY_PATH指定。

# 在ARM板上执行 mkdir -p /opt/mjpg-streamer/plugins # 把文件拷进去之后 export LD_LIBRARY_PATH=/opt/mjpg-streamer:$LD_LIBRARY_PATH

3.3 启动参数配置与摄像头兼容性测试

MJPEG-Streamer的启动命令需要指定输入插件和输出插件的参数。一个典型的启动命令是这样的:

./mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 640x480 -f 30" \ -o "output_http.so -p 8080 -w ./www"

-d /dev/video0指定摄像头设备节点,-r 640x480指定分辨率,-f 30指定帧率。输出插件的-p 8080指定HTTP服务端口,-w ./www指定网页文件目录。

摄像头兼容性是移植过程中最头疼的问题。不是所有USB摄像头都能在ARM板上被V4L2正确识别。我遇到过几种典型情况:摄像头被识别了但不出图、出图但颜色不对、出图但帧率极低。排查方法是用v4l2-ctl工具查看摄像头支持的格式。

v4l2-ctl -d /dev/video0 --list-formats-ext

如果输出里没有MJPG格式,说明这个摄像头不支持MJPEG输出,MJPEG-Streamer的input_uvc插件可能无法正常工作。这时候可以尝试用YUYV格式,但CPU占用会高很多,因为需要软件编码成JPEG。

避坑技巧:如果摄像头只支持YUYV格式,可以在input_uvc插件的参数里加-y强制使用YUYV,然后让MJPEG-Streamer内部做JPEG编码。但ARM板的CPU性能有限,640x480@30fps的YUYV转JPEG可能会跑满一个核心。建议降到640x480@15fps或者320x240@30fps。

4. 常见问题与排查技巧实录

4.1 编译阶段的高频错误与解决

编译阶段最常见的问题我整理成了一个速查表,方便你对照排查。

错误现象可能原因解决方法
configure: error: C compiler cannot create executables工具链路径不对或权限不足检查--host参数和工具链可执行权限
fatal error: jpeglib.h: No such file or directoryCPPFLAGS未指向sysroot导出CPPFLAGS=-I/opt/arm-sysroot/usr/include
cannot find -ljpegLDFLAGS未指向sysroot导出LDFLAGS=-L/opt/arm-sysroot/usr/lib
undefined reference to 'v4l2_open'libv4l未正确链接确认libv4l已安装到sysroot,且LDFLAGS包含其路径
error: 'VIDIOC_S_FMT' undeclared内核头文件版本不匹配从内核源码拷贝最新的videodev2.h到sysroot

还有一个比较隐蔽的问题:编译出来的二进制文件在ARM板上运行时提示No such file or directory,但文件明明存在。这通常是因为动态链接器的路径不对。用readelf -l查看二进制文件的interpreter路径,确认它指向的链接器在ARM板上存在。

arm-linux-gnueabihf-readelf -l mjpg_streamer | grep interpreter

如果输出的路径是/lib/ld-linux-armhf.so.3,但ARM板上这个文件在/lib/arm-linux-gnueabihf/目录下,就需要在编译时通过-Wl,--dynamic-linker参数指定正确的路径,或者在ARM板上建一个软链接。

4.2 运行阶段的典型故障排查

运行阶段的问题往往更棘手,因为涉及到硬件和系统环境的交互。我挑几个最有代表性的场景来说。

场景一:启动后浏览器打不开画面。首先确认ARM板和客户端在同一网络下,然后用curl在ARM板上本地测试HTTP服务是否正常。

curl -I http://localhost:8080/?action=stream

如果返回200 OK,说明服务本身没问题,问题出在网络配置或防火墙。如果返回Connection refused,说明output_http插件没启动成功,检查端口是否被占用。

场景二:画面卡顿或延迟高。这通常是带宽或CPU性能瓶颈。先用top看CPU占用,如果某个核心跑满,说明JPEG编码或网络发送是瓶颈。可以尝试降低分辨率或帧率,或者换用支持硬件JPEG编码的摄像头。

场景三:画面颜色异常,偏绿或偏紫。这是典型的像素格式不匹配问题。V4L2支持多种像素格式,如果摄像头输出的是YUYV但程序按RGB解析,颜色就会错乱。解决方法是在input_uvc插件参数里明确指定格式,或者用v4l2-ctl把摄像头格式设成程序期望的格式。

v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480,pixelformat=MJPG

4.3 性能调优与长期运行稳定性

MJPEG-Streamer在ARM板上长期运行,有几个调优点值得注意。

第一,内存缓冲区的设置。input_uvc插件默认使用mmap方式采集,缓冲区数量可以通过-n参数调整。缓冲区太少容易丢帧,太多会占内存。我一般设成4,兼顾流畅度和内存占用。

第二,网络发送的优化。output_http插件默认是阻塞发送,如果客户端网络慢,会拖累整个采集流程。可以在启动参数里加-c开启客户端连接数限制,避免一个慢客户端影响其他客户端。

第三,看门狗机制。长期运行最怕程序卡死,我一般会写一个简单的shell脚本,每隔几分钟检查一次HTTP服务是否响应,不响应就重启进程。

#!/bin/bash while true; do if ! curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/?action=stream | grep -q 200; then killall mjpg_streamer sleep 1 /opt/mjpg-streamer/mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 640x480 -f 15" -o "output_http.so -p 8080" & fi sleep 60 done

这个脚本虽然简单,但在实际部署中非常管用。我有一块板子连续跑了三个月,中间因为USB摄像头偶发掉线重启过几次,但整体服务可用性很高。

最后分享一个小技巧:如果你的ARM板支持硬件JPEG编码(比如某些带有VPU的芯片),可以尝试用input_uvc.so的-softfps参数配合硬件编码器,能大幅降低CPU占用。不过这个需要针对具体芯片做适配,通用性不强,这里就不展开说了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:09:31

串口短信猫开发实战:AT指令、PDU编码与避坑指南

简介:串口短信猫.rar 面向工业自动化、远程监控与报警系统方向的开发者,以及需要将短信收发能力集成进项目的编程人员,提供从串口配置到功能验证的一整套参考资料。压缩包约17.96MB,内含配置说明、测试源码、调试方法与测试程序等…

作者头像 李华
网站建设 2026/10/15 16:09:30

SAP寄售业务配置与故障排查全指南

1. 寄售不是“先发货后收款”,而是重构整条供应链的信任机制在SAP后勤实施中,寄售(Consignment)业务常被新人误读为“客户先收货、我司后开票”的简单延迟结算模式。这种理解偏差直接导致后续配置跑不通、库存对不上、财务凭证错乱…

作者头像 李华
网站建设 2026/10/15 16:09:15

用Python+公众号模板消息实现每日天气自动推送

简介:微信公众号每日自动推送天气与纪念日通知的制作教程,专门面向想用技术为女朋友制造惊喜、有一点编程基础的学习者,目标是实现无需手动干预的每日定时消息提醒。包体很精简,共5个文件、大小仅5KB,涵盖Python主程序…

作者头像 李华
网站建设 2026/10/15 16:08:42

STM32F103 DMA+DAC正弦波输出:高精度低负载波形生成方案

简介:本资源是一份基于STM32F103微控制器的嵌入式信号发生器实战项目,面向嵌入式初学者、高校电子类课程设计学生及STM32进阶开发者,解决可调频正弦波模拟信号高效生成这一典型工程问题,适用于教学演示、音频实验、简易函数发生器…

作者头像 李华
网站建设 2026/10/15 16:08:27

核线影像纠正原理与C++高精度实现:从基础矩阵到亚像素重采样

简介:本资源是一套完整的核线影像纠正C实现程序,面向遥感图像处理方向的开发者、GIS工程师及高校测绘/地信专业高年级学生,用于解决卫星或航空影像因地球曲率与传感器姿态导致的几何失真问题,支撑立体匹配、数字高程模型生成等关键…

作者头像 李华
网站建设 2026/10/15 16:07:47

32-基于单片机的工地扬尘与噪音实时监测系统设计

单片机型号(stm32)目录一、摘要二、设计要求三、原理图四、说明书预览五、QA作者简介:指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企业讲师以及同行交流合作主要内容:人工智能与大数据、单片机/PLC开…

作者头像 李华