资讯动态

FFmpeg 5路RTMP推流实战:基于MEDAI V2小盒子的完整方案

发布时间:2026/10/20 1:02:00 来源:尧图企业网站定制
FFmpeg 这个东西混流媒体的人没有一个绕得过。它既能读几十种封装格式也能转码、推流、录制所以大家习惯叫它音视频界的“瑞士军刀”。我这次想聊的是一个具体到能抄作业的案例用一台只有 118g 重的 MEDAI V2 小盒子把 5 路视频源通过 RTMP 协议同时推给远端服务器。这个需求在门店监控、展会直播、多机位教学里特别常见。本文既把 FFmpeg 背后最基本的原理讲清楚也会给出 5 路并发推流在 MEDAI V2 上的完整参数设计和排障过程。适合刚接触流媒体、想用低功耗设备搭推流服务的开发者参考。接下来我直接按实际项目推进的顺序写原理部分尽量口语化命令部分可以直接抄去改。1. FFmpeg 在流媒体链路里到底干了什么1.1 一次推流背后发生了什么从摄像头采集到画面呈现中间有好几道工序。摄像头或者采集卡把光信号变成原始视频帧这些帧通常尺寸很大。比如一帧1080p的YUV420数据算下来大约是1920乘1080乘1.5字节也就是差不多3MB。一秒钟30帧就是90MB这个数据量不压缩根本传不出去。于是第一道工序是编码把原始像素压成H.264或者HEVC这种带空间和时间冗余压缩的码流。第二道工序是封装把压缩后的视频帧、音频采样、时间戳等信息按一定规则装进容器常见的容器有MP4、FLV、MKV。第三道工序是传输也就是把封装好的数据用某种协议送到目的地RTMP就是直播推流场景里最常用的一种协议。FFmpeg在整个链路里相当于一个多功能的加工车间。输入文件或流先进解封装器拆出视频流、音频流编码器把它们从原始数据压成H.264/AAC封装器再按FLV或者MP4的规则装好最后通过协议层的推流模块发出去。所以一条FFmpeg命令看起来很长本质就是在配置这条生产线的每一段输入在哪、中间怎么加工、输出用什么格式、发给哪个地址。很多新手一上来就背命令遇到问题就懵。其实只要抓住了“解封装-解码-滤镜-编码-封装-传输”这条主线再看任何一条FFmpeg命令都不难。我习惯把它想成水管源是水库解码是净化编码是加压封装是桶RTMP是管道。水管哪一段堵了问题就出现在对应位置。实际排查时也是这样日志里报哪个模块的错就先查哪一段能省下很多瞎折腾的时间。1.2 解码、编码、封装、协议四件事要分清这四个概念是流媒体里最容易混淆的。编码解决“怎么压缩”解码解决“怎么还原”封装解决“怎么装”协议解决“怎么传”。四件事独立又耦合线上问题有百分之八十来自“以为是一个格式就能直接推流”。比如H.265编码的视频封装进MP4没问题但直接封装进FLV走RTMP推流很多平台和播放器就不认这是编码和封装之间的关系又比如音频是G.711RTMP的FLV封装基本不支持必须转成AAC这又是编码和协议之间的兼容问题。概念常见例子解决什么问题编码H.264、HEVC、AAC把原始音视频数据压缩成码流解码H.264解码、AAC解码把压缩码流还原成可显示、可播放的音视频封装MP4、FLV、MKV把视频、音频、字幕、时间戳组织成一个容器文件传输协议RTMP、SRT、HLS、WebRTC解决流数据从一端到另一端的传输问题FFmpeg 这个名字指整个项目项目里最常用的三个可执行文件是ffmpeg 负责转码和推流ffprobe 负责探测文件或流的详细信息ffplay 负责快速预览。生产环境里我用到最多的反而是 ffprobe因为它能告诉我一路流到底是什么编码、什么分辨率、什么帧率这些信息决定了后续命令里该不该用 -c copy、该不该转码。项目里有几十个库最重要的几个是 libavformat 处理封装和协议、libavcodec 处理编解码、libavfilter 处理滤镜、libavdevice 处理设备输入、libswscale 处理像素格式缩放。日常百分之九十的命令都只用了主程序加 libavformat 加 libavcodec但理解了这些库的分工你就能明白为什么有些编码器叫 libx264、有些叫 h264_v4l2m2m它们都挂在编码这一步上只是背后的实现不同。前者是纯软件编码后者调用硬件编码器名字不同、资源占用差异极大这也是后面 5 路并发的关键。1.3 FFmpeg 命令行的底层逻辑一条标准命令分为三段输入、中间处理、输出。中间处理尤其依赖滤镜图的概念。滤镜图相当于搭一条装配线我先截取输入视频的一段、缩放到 720p、叠一个字幕再把处理后的画面输出到编码器。你可以同时接多个输入也可以把一路输入分叉给多个输出这就是后面做多路推流和画面合成的基础。用做饭类比ffmpeg -i input.mp4 -vf scale1280:720 -c:v libx264 output.mp4相当于拿一块大肉切到合适大小用指定的锅炒熟装盘输出。滤镜图里的每个节点就像一个厨房工位这就是为什么 FFmpeg 被叫做瑞士军刀——什么活都能干但你需要知道每个刀具怎么拿。滤镜可以串联、并联、分叉比如把一路视频同时送去推流和本地录制只需要在输出端分两条分支。这里要提醒一个初学者常见的误区-c copy不是“复制文件”而是“不重新编码、只改变封装格式”。比如一个摄像头输出的是 H.264AAC我想把它从 RTSP 拉下来再走 RTMP 推出去用-c copy就能做到几乎零 CPU 占用的转封装。这一点在 5 路并发的场景里特别关键。很多人一听转码就觉得 CPU 一定吃紧实际上如果编码格式和封装容器匹配很多时候根本不需要转码。2. 5 路 RTMP 推流方案为什么这么设计2.1 为什么选 RTMP而不是 HLS 或 WebRTC做直播推流常见的选择有 RTMP、SRT、HLS、WebRTC。RTMP 是最老牌的协议之一基于 TCP 长连接推流端把一个连续的 FLV 流发给服务器。它最大的优势是生态成熟播放器、CDN、云服务几乎都支持接入成本最低延迟可以控制在 1 到 3 秒对门店监控、展会直播、多机位教学这种场景足够用。HLS 是切片式协议先把流切成一个个 TS 或 MP4 片段并生成索引播放端拉取最新片段播放。好处是能穿透普通 HTTP兼容性极好但延迟至少 5 秒起步做互动直播不舒服。WebRTC 延迟能到几百毫秒适合连麦和视频会议但服务端需要信令和媒体面基础设施不是一台 118g 盒子开个 ffmpeg 就能轻松搞定的。因此“5 路并发推流到远端平台统一收看”这个需求RTMP 是最稳妥的路线。协议延迟推流端复杂度服务端依赖适用场景RTMP1~3 秒低低成熟 CDN常规直播、监控上云HLS5 秒以上低但切片处理靠服务端需要切片能力点播、大并发观看SRT0.5~2 秒中需要 SRT 服务器弱网、跨地域传输WebRTC0.2~0.5 秒高需要信令和 SFU连麦、视频会议SRT 在弱网下表现很出色但多数远端平台不支持 SRT 推流所以一般不作首要方案。如果你的接收端服务器是自己的后期可以考虑用 SRT 替代 RTMP 做长途传输但那是另一个话题了。协议选型不要跟风先看接收端支持什么、播放端延迟接受度有多高再决定推流协议。2.2 MEDAI V2 的性能定位和选型逻辑MEDAI V2 是一台 118g 的小盒子体积不大无风扇低功耗适合放在摄像头旁边或弱电间里。这类盒子通常配多核处理器、板载硬件编解码器、千兆网口或者双频无线整体定位就是边缘侧的轻量计算设备。5 路 RTMP 推流真实的资源消耗主要在编码和网络。以最常见的 720p30fps 为例一路流畅的直播码率通常给 2Mbps 左右5 路就是 10Mbps 上行带宽加上音频每路 128kbps总需求约 10.64Mbps。带宽好解决很多企业宽带上行撑得住但编码就麻烦了。纯 CPU 用 libx264 做 720p 实时软编每一路大约要吃 1 个完整核心还得是主频说得过去的核。5 路软编差不多要 5 个核心盒子很可能会被压满此时必须考虑硬件编码或者转封装。所以选型逻辑很清楚盒子不能只看重量和体积要看三件事。第一有没有硬件编码器能不能在 ffmpeg 里以 h264_v4l2m2m、h264_omx 或者类似名称暴露出来。第二内存够不够至少 4GB因为每路解码缓冲、滤镜、协议栈都会吃内存。第三网络接口够不够稳推流是长连接无线容易抖动优先用网线接入。用 MEDAI V2 这种盒子而不是一台服务器最大的优势是就地部署摄像头在当地盒子也在当地拉流链路短即使公网抖动也能更快恢复。劣势也很明显计算资源有限所以方案设计里要预留足够的余量不能把每一路都当无敌转码器来用。我见过有人硬要在这种盒子上做 4K 转码结果推流直接卡死这不是设备不行而是方案本身就选错了。2.3 5 路输入的接入方式和链路规划先想清楚输入源是什么。最常见的是 RTSP 摄像头这类设备内部通常已经用 H.264 或 HEVC 编码所以推流时优先尝试-c copy直接转封装。第二种是 USB 摄像头或 HDMI 采集卡这类设备输出原始或未压缩格式必须经过编码。第三种是本地视频文件循环推流适合测试、演示或无人值守频道用-loop 1配合-re一直发。链路规划上我强烈建议 5 路分别用独立的 ffmpeg 进程而不是一条命令里塞 5 个输入。独立进程的好处是某一路网络抖动导致进程退出不影响其他四路可以用 systemd 单独拉起和查看日志资源占用可以通过进程 PID 精确统计。一条命令多输入看起来炫酷但耦合性太高排查问题的时候你会很痛苦。每路推流的目标地址可以是同一个服务器的不同流名也可以是不同平台的 5 个地址。这会影响并发连接数和鉴权配置但 ffmpeg 这边区别不大只要把地址、串流密钥写成配置就行。规划表里我会把每路的名称、输入地址、输出地址、编码方式、码率写清楚后面第 5 章给出一套可配置的管理脚本模板。实际项目中我习惯把每路流命名为 cam1 到 cam5方便后续统一维护。3. 在 MEDAI V2 上完成 FFmpeg 部署和 5 路推流3.1 环境准备直接安装 FFmpeg 还是编译在 MEDAI V2 上第一步是拿到一个能用的 FFmpeg。很多 Linux 发行版自带的软件源里就有 ffmpeg 包直接安装先跑通一路。不要一上来就编译因为编译过程长、依赖多万一系统缺某个头文件新手容易卡住。但自带包有时候版本旧缺 RTMP 或某些编码器尤其缺硬件编码模块那就需要自己编译。编译前先确认几件事系统架构、是否有硬件编码器、依赖库是否齐全。一般步骤是安装编译工具和开发库比如 build-essential、cmake、yasm、libx264-dev、libfdk-aac-dev、libmp3lame-dev、libssl-dev。随后 configure。我常用的配置项是./configure \ --prefix/usr/local \ --enable-gpl \ --enable-libx264 \ --enable-libfdk-aac \ --enable-libmp3lame \ --enable-openssl \ --enable-nonfree \ --disable-doc \ --enable-static \ --enable-hardcoded-tables make -j4 sudo make install为什么要这些项--enable-libx264是为了软编码 H.264--enable-libfdk-aac是为了高质量 AAC--enable-openssl是为了支持 rtmps 和某些加密传输--enable-nonfree是因为 libfdk-aac 的许可证和 GPL 不兼容。如果不需要 AAC可以把 libfdk-aac 换成--enable-libmp3lame许可证纠纷会少一些。很多人会漏掉--enable-hardcoded-tables它把一些查表数据固化进程序能减少运行时的初始化开销对小盒子很有用。编译完之后用ffmpeg -version验证版本用ffmpeg -encoders | grep 264查看可用的 H.264 编码器。如果列表里有 h264_v4l2m2m、h264_omx 这类名字说明盒子的硬件编码模块已经被 FFmpeg 识别到了。要注意硬件编码器在 ffmpeg 里的名字是特定前缀不是 libx264。如果看不到检查内核驱动是否加载、编译时是否加了对应模块。还有个细节静态编译出来的二进制可以直接拷贝到同架构的其他盒子上用这对批量部署很友好。3.2 单路推流命令每个参数都要懂先把一条最标准的 RTSP 转 RTMP 推流命令拆开。ffmpeg \ -rtsp_transport tcp \ -i rtsp://192.168.1.10/stream0 \ -c:v libx264 \ -preset veryfast \ -tune zerolatency \ -b:v 2000k \ -maxrate 2200k \ -bufsize 4000k \ -g 60 \ -bf 0 \ -c:a aac \ -b:a 128k \ -ar 44100 \ -f flv rtmp://your-server-ip/live/cam1?tokenxxx这里面的选项没有一个是多余的。-rtsp_transport tcp表示用 TCP 传 RTSP避免 UDP 丢包导致画面花屏。-c:v libx264表示视频用 CPU 软件编码。-preset veryfast是编码速度和画质之间的平衡速度越快 CPU 越少但同码率下画质会略差。-tune zerolatency是专门为低延迟直播设计的调优参数减少编码器内部缓冲。-g 60是关键帧间隔也就是每 60 帧强制出一个 IDR 帧如果帧率 30就是每 2 秒一个关键帧播放器中途进入或断线重连才能快速恢复画面。码率控制方面-b:v 2000k是目标码率-maxrate 2200k限制峰值-bufsize 4000k配合实现接近 CBR 的输出。直播场景需要平稳码率不能像点播一样起伏太大。-bf 0是关闭 B 帧。B 帧可以提升压缩率但会带来额外延迟而且很多低端播放器在 RTMP 里面对 B 帧兼容不好关掉之后延迟会更稳定。音频部分-c:a aac是 AAC 编码-b:a 128k是码率-ar 44100是采样率一般直播用 44100 或 48000 都行关键要和输入源保持一致减少重采样。最后-f flv是强制输出 FLV 格式因为 RTMP 传输的是 FLV 封装的数据流。如果不加这个FFmpeg 会根据输出地址的后缀猜测格式地址写成 rtmp:// 时它能猜出来是 FLV但显式指定更稳妥。这条命令如果输入源已经是 H.264AAC完全可以把-c:v libx264和-c:a aac改成-c copy让 CPU 占用直接降到几乎为零。第一次跑的时候建议先用本地文件测通整条命令再切到真实摄像头省得到处找网络问题。3.3 多路并发进程守护与 systemd 模板5 路并发我建议每个摄像头一个独立 systemd 服务。手动敲 5 条 nohup 命令也能跑但设备重启后你得手动拉起某一路崩了也不知道运维起来很累。写成一个 systemd 模板是几十行的事但长期稳定性提升非常明显。先建立每路的配置文件比如 /etc/stream/cam1.conf 里写INPUTrtsp://192.168.1.10/stream0 OUTPUTrtmp://your-server-ip/live/cam1?tokenxxx VCODEClibx264 PRESETveryfast VBR2000k ACODECaac ABR128k再写一个通用的启动脚本 /usr/local/bin/stream-runner.sh读取配置拼出 ffmpeg 命令行。随后创建 systemd 模板 stream.service[Unit] DescriptionRTMP Stream %i Afternetwork-online.target Wantsnetwork-online.target [Service] EnvironmentFile/etc/stream/%i.conf ExecStart/usr/local/bin/stream-runner.sh Restarton-failure RestartSec5 MemoryMax512M NoNewPrivilegesyes [Install] WantedBymulti-user.target然后执行systemctl enable --now streamcam1.service、streamcam2.service等就能统一管理 5 路。这里有个细节MemoryMax 给 512M 比较保守因为 FFmpeg 的内存占用和输入分辨率、滤镜数量有关如果盒子内存足够可以放到 1G一旦某个进程内存异常不会拖垮整个盒子。Restarton-failure 保证进程异常退出后被重新拉起RestartSec5 避免频繁重启打满 CPU。配置文件和脚本分开的好处是改某一路的分辨率、码率、推流地址只需要改配置文件然后systemctl restart streamcamX.service不用改代码。后面想扩展第 6 路也只需要复制一个配置文件再加一个 enable 命令。如果系统没有 systemd也可以用 supervisor 或者简单的 crontab 守护脚本但日志和依赖管理都不如 systemd 顺手。3.4 硬件编码和 copy 模式的取舍小盒子上跑 5 路并发最需要考虑的就是“能不能不转码”。如果摄像头输出已经是 H.264AAC而远端平台也接受 H.264AAC那么用-c copy是最省资源的方案。它只做解封装、重封装和协议转换不碰像素CPU 占用可以忽略不计。像安防摄像头里最常见的主码流就是 H.264 或 H.265完全符合这个条件。但-c copy有一类坑输入流的编码参数可能不适合 RTMP/FLV。比如 H.265/HEVC 的视频帧本身可以封装进 FLV但很多播放器和 CDN 不支持需要确认平台是否支持 HEVC over RTMP又比如音频如果是 G.711 或其他老格式FLV 并不支持这时必须转码音频为 AAC视频可以继续 copy。所以一半 copy 一半转码的命令很常见ffmpeg -rtsp_transport tcp -i rtsp://... \ -c:v copy -c:a aac -b:a 128k \ -f flv rtmp://...如果摄像头输出的是 H.265而远端只支持 H.264那就必须转码了。这时候优先用盒子上的硬件编码器比如 h264_v4l2m2m。硬件编码命令和软编相似只是编码器名称不同码率参数有些硬件会写成-b:v而不是全局参数。我实际测试下来硬编一路 720p 的 CPU 占用只有软编的十分之一左右对 5 路并发几乎是必备的。需要说明的是硬件编码器的画质在同等码率下通常不如 libx264 的中等 preset但直播场景追求低延迟和低负载画质够看就行。如果追求画质可以给最主要的一路用软编其余几路用硬编或 copy把资源留给最关键的镜头。这个思路比“所有路必须最高画质”要实际得多。另外硬编前要确认编码器支持的输入像素格式比如有的编码器不支持 1080p 或需要宽高是偶数提前用ffmpeg -h encoderh264_v4l2m2m查清楚。3.5 上线前压测与资源监控不要直接 5 路全开就当作上线。我建议的节奏是先单路跑半小时然后两路、三路递加每加一路都观察几个指标CPU 总占用、可用内存、上行实时带宽、进程的日志。命令行可以用top看 CPU 和内存用ifstat或者sar查看实时网卡流量用free看内存。小盒子的资源不像服务器那么富裕任何一路异常增长都能通过这组指标看出来。还有一个很实用的验证方法远端用 ffprobe 周期探测每路流是否正常。ffprobe -v error -show_entries streamcodec_name,width,height -of json rtmp://your-server-ip/live/cam1如果返回码流信息且分辨率正确说明推流成功。如果探测超时或报错说明进程可能在推但流没有被服务器接收问题通常出在鉴权或编码参数。我习惯写一个简单的健康检查脚本每 60 秒探测一次 5 个地址只要有一路失败就在日志里记录然后通过 webhook 通知运维群。这里的告警服务不绑定具体厂商自己搭一个简单的 HTTP 回调即可。压测时还要关注温度。无风扇小盒子持续满负载CPU 温度容易升到 90 度以上然后触发降频。降频会导致编码变慢推流出现卡顿。我的经验是给盒子加一块散热片或者放在通风良好的位置如果项目允许用一个带风扇的外壳是最省心的。另外尽量用网线而不是无线5 路 10Mbps 的稳定长连接对无线的重传和干扰非常敏感有线可以省掉 90% 的断流排查时间。4. 实战里的坑和排查记录4.1 推流握手失败问题到底出在哪一层“ffmpeg 一直在跑远端就是没有画面”是我遇到最多的第一类问题。排查时先分清楚阶段FFmpeg 日志会有[rtmp ]连接、握手、发布等字样。连接失败多半是网络不通或服务器端口没开放握手失败多半是地址里的 token 或串流密钥写错或者协议版本不匹配发布失败则是服务器拒绝了这条流最常见的是流名重复、权限不足。可以先在盒子上测端口通不通用nc -vz server-ip 1935。如果 1935 通了再直接拉取远端已有的测试流确认服务端正常。FFmpeg 日志加-loglevel debug看细节会有很多握手过程的信息帮你看是 TCP 层面失败还是 RTMP 应用层失败。千万不要只盯着最后一行 error前面的 context 才是定位的关键。另一个经验是很多平台给的推流地址是rtmp://domain/live/streamkey但域名解析出来的 IP 不一定能直接用。尤其某些平台用 HTTP-DNS 调度你直接填 IP 反而握手失败。这时候保持官方给的原始地址不要自作主张把域名换成 IP除非你已经确认 CDN 允许。同样串流密钥里的特殊字符要注意转义写进 shell 脚本时尤其容易踩坑。4.2 五路并发后的 CPU 危机软编 5 路跑满 CPU 是最常见的性能问题。我这里说一个具体现象盒子 4 核跑 3 路 libx264 veryfast 的时候 CPU 已经接近 90%再加第 4 路就开始推流报错或延迟飙升。原因是 FFmpeg 的 libx264 在一个进程里可以多线程但 5 个独立进程会争抢所有核心高优先级进程可能导致其他进程饥饿。解决办法按优先级排序第一对已经是 H.264 的输入源改-c copy零编码成本第二改用硬件编码器 h264_v4l2m2m第三降低编码规格比如从 720p 降到 540p帧率从 30 降到 25preset 从 veryfast 改成 ultrafast第四适当降低码率2000k 变成 1500k减少编码器输出压力。注意降码率会直接影响画质要跟业务方确认接受范围。如果用了硬编还卡检查是不是分辨率或格式转换造成的。硬件编码器通常有支持的输入像素格式和分辨率对齐要求比如某些编码器要求宽高是 16 的倍数FFmpeg 可能需要先 scale 再送编码器。此时滤镜会多一层内存拷贝和缩放计算反而推高 CPU。可以用ffmpeg -h encoderh264_v4l2m2m查看支持的像素格式和分辨率范围提前做适配。还有一个被忽略的点每路 FFmpeg 进程默认的线程数是按 CPU 核数分配的如果 5 个进程同时把线程数拉满反而会增加上下文切换开销。可以用-threads 2限制每个进程的线程数让调度更平滑。实测下来在 4 核盒子上每路最多给 2 个线程就够了多了不一定更快。4.3 音画不同步和关键帧问题多路摄像头质量参差不齐时间戳是重灾区。有些摄像头输出的 RTSP 流时间戳从设备开机开始算而推流到远端后播放器可能无法正确处理。症状是画面卡顿、快速跳动或音画对不上。解决方法之一是在输入侧加-use_wallclock_as_timestamps 1让 FFmpeg 用系统墙钟时间作为时间戳基准而不是完全信任设备的时间。这会重新校准视频和音频的同步关系。音频如果有偏移可以用 aresample 滤镜的 async 参数让音频自动对齐比如-af aresampleasync1:first_pts0。视频不能用简单的 fps 强制因为如果源本身掉帧强制 CFR 可能会插入重复帧或让进程缓冲积累。更稳的是保证关键帧间隔固定。直播播放器通常要等一个关键帧才开始解码如果关键帧间隔不固定有的播放器会一直黑屏。所以-g参数要和帧率匹配比如 30fps 下-g 60就是 2 秒一个关键帧。还有一个隐蔽问题转封装时把输入流的 B 帧原样传过去远端播放器花屏或者跳帧。关闭 B 帧不是必须的但如果播放端兼容性差可以考虑让摄像头子码流不开 B 帧或者在转码时用-bf 0。不过这会影响压缩率码率会略涨需要根据平台验证。另外如果盒子系统时间和实际时间差别较大也会导致推流后播放器显示的时间轴不对记得先同步系统时间。4.4 常见问题速查表现象可能原因排查和解决推流几秒后断开网络抖动、鉴权错误、带宽不足用 nc -vz 测端口加 -loglevel debug 查看断开阶段检查上行带宽画面长时间黑屏首帧不是关键帧、GOP 太大固定 -g确保关键帧间隔在 2 秒内播放端切换低延迟模式音画不同步时间戳不可信、音频偏移加 -use_wallclock_as_timestamps 1用 aresample async 对齐进程频繁退出内存不足、电源不稳、OOMjournalctl 看日志加 MemoryMax 限制加 Restarton-failureRTMP 握手失败地址或密钥错误、CDN 域名调度用官方完整地址保留域名检查流名是否重复硬编无法使用内核驱动或 FFmpeg 编译缺模块用 ffmpeg -encoders 查看编译时加 v4l2 相关模块无线网络下卡顿无线干扰或重传换有线调整天线位置降低码率推流后 CPU 居高不下软编过多、滤镜复杂改用 -c copy 或硬编降低分辨率、帧率限制线程数4.5 一份能落地的运维小模板日志管理容易被忽略。FFmpeg 默认把日志打到终端如果用 systemd会进 journald。时间一长journald 和 FFmpeg 日志都可能占用大量存储空间而在 118g 盒子里存储通常很紧凑。我习惯在 stream-runner.sh 里把每路日志输出到独立文件并控制级别为 warningffmpeg -v warning -i $INPUT ... -f flv $OUTPUT /var/log/stream/$STREAM.log 21然后配置 logrotate每天轮转保留 7 天。这样平时看不到刷屏信息出问题翻日志能找到关键错误。定期用 ffprobe 探测远端流状态把结果写成一个状态文件配合简单告警脚本。这一套不需要额外安装服务但对长期稳定运行非常重要。还有个小技巧给每个 ffmpeg 进程单独指定日志文件后排查时可以直接tail -f /var/log/stream/cam3.log不用在 journalctl 里过滤。如果系统日志权限不好搞也可以把日志写到内存目录比如 /dev/shm减少对存储卡的写入损耗。存储卡的寿命在持续写入场景下会明显缩短能少写就少写。5. 从 5 路推流延伸出去的小系统5.1 用配置文件管理一整套路任务前面提到 systemd 模板这里把它补完整。stream-runner.sh 读取几个环境变量拼出完整命令行#!/bin/bash STREAM_NAME${STREAM_NAME:-default} INPUT${INPUT:?INPUT not set} OUTPUT${OUTPUT:?OUTPUT not set} VCODEC${VCODEC:-libx264} ... exec ffmpeg -v warning -rtsp_transport tcp -i $INPUT \ -c:v $VCODEC ... -f flv $OUTPUT每个 cam*.conf 只存差异化的部分比如 STREAM_NAME、INPUT、OUTPUT、VCODEC、VBR。systemd 通过 EnvironmentFile 加载模板服务按实例名区分。这样管理 5 路的成本几乎等于管理 1 路。脚本里一定要用exec启动 ffmpeg这样 ffmpeg 会直接替换 shell 进程systemd 能准确拿到主进程 PID重启和停止才会可靠。这套做法的好处是肉眼可见要临时停掉某一路systemctl stop streamcam3.service要改某一路码率改配置文件然后 restart要加第 6 路复制一个 conf 再 enable。日志、重启、开机自启全部由 systemd 接管比手工脚本可靠得多。没有 systemd 的环境也可以用 supervisor 或 cron 加 flock 模拟但优先级和日志管理都不如 systemd 顺手。5.2 画面合成、水印和字幕另一种 5 路玩法如果需求不是“5 路各自推流”而是把 5 路画面合成一路FFmpeg 也能做。用-filter_complex把 5 个输入统一缩放再用 xstack 拼成 2 乘 3 或者 5 宫格画面最后编码推流。xstack 比老旧的 hstack 加 vstack 组合更灵活可以直接指定每路的位置。ffmpeg -i cam1 -i cam2 -i cam3 -i cam4 -i cam5 \ -filter_complex [0:v]scale960:540[a];[1:v]scale960:540[b];[2:v]scale960:540[c];[3:v]scale960:540[d];[4:v]scale960:540[e];[a][b][c][d][e]xstackinputs5:layout0_0|w0_0|0_h0|w0_h0|w0h0_0[v] \ -map [v] -map 0:a -c:v libx264 -preset veryfast \ -b:v 4000k -f flv rtmp://...layout 需要根据宽高细算这里只是一个示例。真正跑之前可以先输出到本地 mp4 验证布局不要一上来就推流。还要提醒xstack 会多占用内存和 CPU因为 5 路解码、5 路缩放、1 路合成编码同时进行对小盒子的压力比 5 路各自 copy 推流高得多。所以这种玩法通常要把输入分辨率降到 540p或者只在需要监看的场景使用。如果只是加个水印或字幕用 overlay 和 drawtext 滤镜就够。drawtext 需要 FFmpeg 编译时支持 freetype字体文件也要准备好。水印会导致多一层像素拷贝对 CPU 有一些额外消耗但比 xstack 轻得多。一般来说边缘盒子上不适合做太多滤镜操作能用上游设备配置完成的尽量放上游盒子只负责传输。5.3 自动恢复与告警让盒子自己管自己5 路推流跑通后真正考验人的是长期稳定性。网络闪断、远端服务器重启、电源毛刺都可能导致某一路进程退出。systemd 的 Restarton-failure 已经解决一部分但有时候进程并没有退出而是流卡住了表现为 FFmpeg 还在运行远端却接收不到新的关键帧。这时候需要应用层健康检查。我用的方案是在盒子上每 60 秒跑一次检查脚本循环 ffprobe 探测 5 个远端地址如果某个地址连续 3 次探测失败就systemctl restart streamcamX.service。如果连续 5 次失败说明网络或服务器有大问题直接触发告警。这个脚本不需要复杂依赖cron 守护就行。要注意的是探测本身会消耗一点带宽和时间5 路串行探测比并行更稳避免同时发起大量连接把服务器打挂。告警方面不必搞太重可以在日志里记录失败次数然后向自己的运维群发一条消息。关键是阈值要合理直播流出现一次失败就重启可能是在干扰正常业务。连续 3 次失败再动作既避免误判也不会让故障拖太久。我自己在这套体系下最怕的不是某一路断流而是所有路同时断那基本是上行链路出问题重启进程没有用要先去检查网络设备和运营商状态。5.4 扩展录制回放、转码分发和边缘预处理盒子已经在边缘位置接收 5 路线索顺手还能再做几件事。比如用 FFmpeg 同时把每路写成 MP4 片段做本地录制回放或者把其中一路转成 HLS 再推到内网播放器供没有 RTMP 插件的网页端观看又或者接一个 AI 检测模块发现画面异常才推流到远端平时只在本地录存储。不过我建议先把 5 路 RTMP 推流这个主链路打扎实再扩展。因为每加一个功能CPU、内存、带宽都会增加小盒子的余量会被快速吃掉。我见过一个项目为了省成本把 AI 检测和推流都放在同一台盒子里结果 CPU 常年 80% 以上最后连推流都变得不稳。功能扩展要分优先级核心链路永远是第一位的。结尾我个人实际操作中的体会是先把一条流完整跑通确认远端能拉到画面再往 MEDAI V2 上并发第 2 路、第 3 路。不要一上来就 5 路全开否则 CPU、带宽、电源任何一个短板爆发你很难判断问题出在哪。还有两个小细节把 FFmpeg 日志级别降到 warning别用 info不然日志文件半天就能顶爆存储存储卡或外接盘尽量选高耐久的系统日志轮转一定要配好。这台小盒子目前已经连续跑了几个月期间遇到过两次网络闪断导致某一路自动重连其他路完全不受影响。如果你也打算做类似的边缘推流盒子希望这篇记录能帮你少走点弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑