ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Linux下用FFmpeg搭建稳定低延迟推流器实战指南

Linux下用FFmpeg搭建稳定低延迟推流器实战指南 我办公桌上那台常年跑着服务的内网Linux机器最近又多了一个活当一个稳如老狗的推流器。任务本身不复杂就是把一路视频信号实时搬到流媒体服务器上供会议室大屏和远程同事观看。一开始我还想找个成品推流软件装上后来发现Windows生态里这套东西很多到了Linux服务器上反倒不如直接用FFmpeg来得干净利落。于是就有了Linux_27FFMPEG推流器这个项目一台普通PC一个USB摄像头一段反复循环的测试视频再加上FFmpeg的命令行组成了一套能应付教学直播、会议转播、设备巡检录像回传的推流方案。这篇文章就是我在做这个推流器时做的完整备忘包括为什么选FFmpeg、Linux下环境怎么装、推流命令每个参数到底在干什么、延迟怎么压、断流怎么解决以及踩过的一堆坑。适合想在Linux上快速搭起推流的读者也适合想把FFmpeg命令行真正用明白的人。1. 方案选型为什么拿FFmpeg做推流器1.1 推流这件事本质上发生了什么先说清楚推流到底在做什么。推流不是简单地把一个文件扔到服务器上它是一条实时的生产流水线先采集一帧画面摄像头、屏幕、视频文件里的某一帧都算接着按某种编码标准把它压缩成二进制数据再封装进特定的容器格式最后通过RTMP、SRT、HLS这类流媒体协议按照实时的节奏把数据发送到服务器。这条链路里任何一个环节慢了、堵了观众那边表现出的就是卡顿、花屏、延迟越来越大。这也是为什么我觉得推流器这类工具核心不是说能不能推而是能不能稳定地、低延迟地推。FFmpeg在这件事上几乎是全能选手。视频流、音频流、字幕流它能处理H.264、H.265、AAC、Opus这些编码它能做文件、设备、网络流它都能当输入RTMP、RTSP、HLS、SRT、WebRTC这些协议它也都有对应的封装器。更关键的是它本身就是一套命令行工具在Linux服务器上可以直接进crontab、进shell脚本完全不需要图形界面这对服务器环境来说太重要了。1.2 对比其他方案后我为什么放弃了收费推流软件和自研程序之前我也考虑过用OBS但它对图形界面的依赖很强在纯命令行服务器上跑起来很别扭。也有些商业推流软体做得确实不错延迟控制很好可一旦要接入自己的业务系统要对推流状态做监控又涉及授权和二次开发成本。自研推流程序就更不划算了。视频采集、编码器封装、协议封装、时间戳控制每一项都是水很深的领域。我见过有人拿着GStreamer自己拼管线最后拼到音画不同步也见过团队花两个月写推流模块最后还是没搞定关键帧间隔的控制。相比之下FFmpeg的命令行方案几乎是为这个问题量身定制的。它像一个已经把各种零件打磨好的工具箱我要做的只是选对方案、调好参数。而且它留有完整的C接口库libavcodec、libavformat这些真到了要嵌入业务系统的时候还能在命令行之外做二次开发这个后面细说。1.3 项目整体架构采集、编码、推送、守护我在这个推流器项目里做的事本质上就是四段式结构采集端USB摄像头/dev/video0、屏幕录制、本地视频文件循环播放或者FFmpeg自带的各种测试源testsrc、smptebars这种。编码端视频用libx264软件编码或者Intel QSV、NVIDIA NVENC硬件编码音频用AAC。推送端封装成FLV格式通过RTMP协议推给SRS、Nginx-RTMP或者CDN。守护端写一个小shell脚本定时探测推流进程进程退出就自动重启推流异常能重启摄像头设备。这套架构的好处是每一段都可以独立替换。比如今天用摄像头推教育课程明天改成用屏幕录制推技术分享后天直接用loop滤镜循环推产品宣传片命令主体结构不用变只改输入源就行。2. 环境准备Linux下装一个能打硬仗的FFmpeg2.1 发行版自带源与官方静态编译版怎么选大多数Linux发行版都自带ffmpeg包Ubuntu/Debian一行命令就能装sudo apt update sudo apt install ffmpeg但这有个坑发行版源里的FFmpeg往往做了裁剪很多编码器和协议没编进去。我一开始在Ubuntu上直接apt装结果想用libx264得重新装想用libfdk_aac也没有想用SRT协议发现根本没有这个封装器。而且版本落后得厉害一些新的滤镜选项完全不存在。如果只是内网自用、推个简单的H.264流可以用发行版自带的。如果要追求编码器齐全、版本新我建议用FFmpeg官方发布的静态编译版本下载后放到/usr/local/bin就行不污染系统依赖升级也方便wget https://ffmpeg.org/download.html 对应的Linux静态包 tar -xf ffmpeg-release-amd64-static.tar.xz sudo cp ffmpeg /usr/local/bin/ sudo cp ffprobe /usr/local/bin/静态版的好处是它把x264、x265、libvpx这些常见编码器全编进去了拿到手就能用。但注意官方静态版默认是按GPL发布的而libfdk_aac因为许可证问题不在里面如果对AAC编码质量没有极端要求用内置的aac编码器就够了。2.2 源码编译什么时候需要自己动手官方静态版满足不了你的场景通常有两种情况。一种是要启用一些没编进去的库比如要推低延迟的SRT协议或者要用带专利保护但许可证更严格的编码器另一种是要针对特定硬件平台做优化比如硬编解码器版本特别新。源码编译FFmpeg的路子不复杂提醒一个点先用./configure --help看选项确认要开哪些功能再补依赖库。比如我要x264和硬件加速配置命令大致是sudo apt install yasm nasm pkg-config libx264-dev libx265-dev libvpx-dev \ libfdk-aac-dev libmp3lame-dev libopus-dev libsrt-openssl-dev ./configure --prefix/usr/local/ffmpeg \ --enable-gpl --enable-nonfree \ --enable-libx264 --enable-libx265 --enable-libvpx \ --enable-libfdk-aac --enable-libmp3lame --enable-libopus \ --enable-libsrt --enable-avcodec --enable-avformat \ --enable-pthreads --enable-hardcoded-tables make -j$(nproc) sudo make install编译耗时看机器性能8核机器跑全量大概十几分钟属于可以接受的等待。这里有个许可知识值得顺带说清楚FFmpeg本身是LGPL但一旦启用x264、x265这种GPL许可证的库整体就变成GPL发布如果启用fdk_aac又要加上非free的附加限制。这也是为什么官方静态版不带fdk_aac的原因。2.3 虚拟设备与测试源没有摄像头时怎么验证推流调试推流器最怕手上没有真实信号源。我建议准备两样东西第一个是FFmpeg自带的测试源。一条简单命令就能生成一路彩条测试音的视频ffmpeg -re -f lavfi -i testsrcsize1280x720:rate30 \ -f lavfi -i sinefrequency1000:sample_rate44100 \ -c:v libx264 -preset veryfast -b:v 2000k \ -c:a aac -b:a 128k -f flv rtmp://127.0.0.1:1935/live/test这路信号虽然是合成的但包含了完整的视频帧序列和音频数据流足够验证推流链路是否通、播放端是否正常出画。第二个是v4l2loopback这个内核模块它是Linux下的虚拟摄像头。可以先把视频文件映射成虚拟设备让FFmpeg像操作真实摄像头一样采集sudo apt install v4l2loopback-dkms sudo modprobe v4l2loopback video_nr10 card_labelVirtualCam ffmpeg -re -stream_loop -1 -i input.mp4 -f v4l2 /dev/video10这套方案的实战价值在于很多采集软件只认/dev/video*设备通过虚拟摄像头把它们串起来能模拟出真实的采集流程排障时特别好用。3. 核心实操推流命令逐参数拆解3.1 一条最小可用推流命令的完整解读我不喜欢一上来就上一堆长长的参数先用最简单的方式把一条推流命令跑通再逐步加内容。拿推一个本地文件到SRS服务器举例ffmpeg -re -i /data/videos/lesson.mp4 \ -c:v libx264 -preset veryfast -tune zerolatency \ -b:v 2500k -maxrate 2500k -bufsize 5000k \ -g 60 -keyint_min 60 -sc_threshold 0 \ -c:a aac -b:a 128k -ar 44100 \ -f flv rtmp://192.168.1.100:1935/live/lesson这里每个参数都在解决一个具体问题。-re这是文件推流和实时推流的分水岭。没有它FFmpeg会全速读取文件并立刻推完几十分钟的视频几秒钟就推没了观众看到的就是飞速跳动的画面。加上-re后FFmpeg按文件原始帧率读取模拟实时直播的节奏。-c:v libx264选用H.264软件编码器。兼容性最好几乎所有播放器和流媒体服务器都认识。-preset veryfastx264的预设档位影响编码速度和压缩率。veryfast偏重速度CPU占用低压缩率会差一点如果不计CPU成本slow或medium能压得更小、画质更高。直播场景我一般从veryfast起步。-tune zerolatency这个参数可能是我在直播推流里最喜欢的参数。它告诉x264不要为提升压缩率而引入额外的帧缓存优先保证低延迟。做直播一定要记得它否则端到端延迟可能会无谓地高出很多。-b:v / -maxrate / -bufsize目标码率、最大码率、缓冲大小。2500k对720p30来说是个画质和带宽都比较平衡的档位。bufsize一般设成码率的2倍给编码器一点呼吸空间避免画面快速变化时码率突然失控。-g 60关键帧间隔也就是GOPGroup of Pictures大小。设为60表示每60帧强制出一个关键帧按30fps算就是每2秒一个关键帧。这个参数直接影响两个东西观众拖进度条直播场景里通常叫切频道时要等多久才能看到画面以及视频流在网络上丢包后要多久才能自动恢复。直播我一般控制在2秒左右。-keyint_min 60最小关键帧间隔。防止编码器在场景切换时频繁插入额外关键帧虽然这会影响压缩率但直播场景下可控的关键帧间隔更重要。-sc_threshold 0关闭x264的场景切换检测。默认情况下画面剧烈变化时会自动插入关键帧这在直播里会打乱GOP节奏导致播放在不同客户端上的表现不一致所以直播场景里关掉它。-c:a aac -b:a 128k -ar 44100AAC音频编码128k码率、44.1kHz采样率对语音和人声完全够用。-f flv强制封装为FLV格式。RTMP协议底层要求的就是FLV封装所以推RTMP时记得带这个参数。3.2 硬件编码与软件编码的选择依据推流服务器的CPU是稀缺资源。如果编码一路720p就已经占满CPU那同时推多路流就完全不可能。所以机器有Intel核显或NVIDIA显卡我优先考虑硬件编码。Intel QSV的命令是ffmpeg -re -i input.mp4 -c:v h264_qsv -global_quality 26 \ -c:a aac -b:a 128k -f flv rtmp://server/live/hwNVIDIA NVENC是ffmpeg -re -i input.mp4 -c:v h264_nvenc -preset p4 -tune ll \ -b:v 2500k -maxrate 2500k -bufsize 5000k \ -c:a aac -b:a 128k -f flv rtmp://server/live/hw硬件编码器的特点是快、CPU占用低但画质在同码率下通常不如x264的medium以上档位好。我的经验是如果机器CPU有富余追求画质就选x264如果要同时推多路或者机器本身还要干别的事就上硬件编码。还有一个要注意的地方硬件编码器用之前一定先确认驱动和FFmpeg编译选项里带了对齐的组件不然会出现找不到编码器的报错。3.3 时间基准time base与音画同步的问题推流过程中最让人头疼的坑之一是视频流的时间基准不一致导致音画不同步或播放倍速异常。FFmpeg内部每一路流都有自己的time base时间基准比如视频是1/1000音频是1/44100。封装进FLV时如果两边时间戳换算不一致播放器就可能出现声音对不上口型或者画面忽快忽慢的诡异现象。处理办法是在推流命令里显式对齐时间基准ffmpeg -re -i input.mp4 -vf settbAVTB,setptsPTS \ -af aresampleasync1:first_pts0 \ -c:v libx264 ... -c:a aac ... -f flv rtmp://...settbAVTB把时间基准统一到AVContainer时间基准通常对应微秒级的毫秒表示setptsPTS保证视频PTS按这个基准输出aresampleasync1让音频在遇到采样率不匹配时自动补齐或丢弃帧避免音视频轨长度积累出偏差。这个组合是我做推流时的固定开场动作。3.4 循环推流loop滤镜与stream_loop的差异项目里有个常见需求是循环推一个视频文件比如开场动画、宣传片、等待画面。网上搜loop滤镜 ffmpeg能发现不少人把-stream_loop和loop滤镜搞混。-stream_loop -1 -i input.mp4是输入层面的循环在文件被读取时就无限循环适合整个文件反复推。loop滤镜是滤镜层面的循环可以直接插在滤镜链里对视频帧做循环处理灵活性更高。比如我要做一个视频上方循环滚动字幕的测试流就可以这么写ffmpeg -re -f lavfi -i testsrcsize1280x720:rate30 \ -vf drawtexttextLive Test:xmod(t*100\,w):y30:fontsize48:fontcolorwhite:box1:boxcolorblack \ -c:v libx264 -preset veryfast -tune zerolatency \ -t 600 -f flv rtmp://server/live/testdrawtext里的mod(t*100, w)让文字按时间横向滚动实测下来画面的文字滚动态能正常推出去。这种测试流比我原来看过的好多静态彩条要真实得多因为文字滚动了你就能直观看到延迟和卡顿的变化。4. 延迟调优与多路分发4.1 推流到SRS存在延迟到底是谁的问题热搜里ffmpeg推流到srs存在延迟这个问题我遇到过不止一次。很多人第一反应就是去调服务器配置但我觉得得先想明白延迟分为推流端延迟、服务器缓存延迟、播放端延迟三段每一段都可能贡献好几秒。先说播放端。FFmpeg推流后我用ffplay拉流测试时如果不加参数播放器会默认缓冲个两三秒来保证播放流畅ffplay -fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0 rtmp://.../live/test这里的-fflags nobuffer让播放器不做过多的预缓冲-flags low_delay配合低延迟模式-probesize和-analyzeduration调小可以减少探测时间。实测同一路流加上这些参数后显示延迟能少1秒多。再说服务器端。SRS的配置文件里play块内有些参数会影响延迟vhost __defaultVhost__ { play { gop_cache off; queue_length 10; mw_latency 0; } }gop_cache这个参数需要注意一下开启后新加入的播放端能立即从上一个关键帧开始播放体验上秒开代价是第一个关键帧到达前的历史GOP也会被缓存和发送造成延迟增大。对延迟敏感的场景我建议关掉它让播放端等到下一个关键帧再出画延迟会小很多。最后是推流端。ffmpeg推RTMP时默认可能会有一定缓冲可以在命令里加-flvflags no_duration_filesize或者干脆用-avioflags direct减少IO缓冲。实测下来对本地网络推流把三端同时调优延迟能控制在1到2秒而不调的时候在3到5秒甚至更高。4.2 GOP大小与延迟的关系很多人没仔细想过为什么我前面反复强调关键帧间隔。直播里播放端必须拿到关键帧才能开始解码所以关键帧间隔越大新进观众就可能等越久才看到画面。举例说如果GOP设为150帧即5秒一个关键帧某个观众在第4秒加入他要等到第5秒的关键帧才能看到画面再加上网络缓冲体感延迟直接奔着6秒去了。把GOP设为30到601到2秒一个关键帧观众最迟也就等一两秒。但这也不是越小越好关键帧越密同码率下画质会被稀释文件体积也更大。我一般取602秒这个平衡值。如果要做低延迟互动可以拉到30以下前提是带宽和编码器都撑得住。4.3 一路输入多路分发tee muxer的几种写法有时候一个信号源要同时推到直播平台、内网SRS、录制到本地做存档。逐个推要启动多个FFmpeg进程各自编码浪费CPU不说还会造成设备被多个进程争抢。FFmpeg的tee muxer能解决这个问题ffmpeg -re -i /dev/video0 \ -c:v libx264 -preset veryfast -tune zerolatency -b:v 2500k \ -c:a aac -b:a 128k \ -f tee [fflv]rtmp://server1/live/a|[fflv]rtmp://server2/live/a|[fflv]rtmp://server1/live/rec注意每个出口前用[fflv]指定该路输出的封装格式。tee的编码开销只算一份因为编码在进入tee前就完成了各出口共享同一份编码结果这个对CPU的节省非常明显。我实际跑过一路1080p同时推三处CPU占用比三路单独推低了一半多。如果你各路的码率要求不一样比如一路高清给内网、一路普清给外网tee就处理不了了这时候得用filter_complex和split滤镜分别做缩放和编码最后再分别推。这种写法更复杂但原理是一样的一次采集多份编码。4.4 长时间无人值守推流的稳定性设计推流任务经常一开就是几小时甚至几天谁都不可能一直守在终端前。我在这块做了两个保障第一个是FFmpeg自己的断线重连。向RTMP服务器推流时服务器重启或者网络闪断都会导致进程退出。可以写一个简单的重试循环在shell里完成#!/bin/bash while true; do ffmpeg -re -i /dev/video0 \ -c:v libx264 -preset veryfast -tune zerolatency -b:v 2500k \ -c:a aac -b:a 128k -f flv rtmp://server/live/main echo FFmpeg exited with code $?, restarting in 5s... sleep 5 done这个脚本可能是不太优雅但很实用的解决方案。加了while true以后进程哪怕异常退出5秒后也会自动重新拉起。第二个是探测推流状态的守护。在脚本里定期用ffprobe探测远端服务器上是否还能拉到流if ! ffprobe -v error -show_entries formatformat_name -of defaultnoprint_wrappers1:nokey1 rtmp://server/live/main 2/dev/null | grep -q flv; then echo Stream is down, restarting... pkill -f ffmpeg.*rtmp://server/live/main sleep 2 nohup /usr/local/bin/start_push.sh /var/log/push.log 21 fi这类探测的准确率依赖超时设置我一般给ffprobe加-timeout 3000000单位微秒相当于3秒避免网络抖动导致误判重启。正常运行状态下这套脚本大半个月没出过事。5. 常见问题与排查技巧实录5.1 推流延迟越拖越大的排查顺序这种现象我遇到时就一个感觉服务器还没乱我自己先乱了。后来总结了一套排查顺序顺着走基本能定位问题。第一步看推流端时间戳是否正常。用ffprobe拉取流信息观察各帧PTS是否呈递增且间隔稳定。PTS跳变严重常见原因是输入源帧率不稳定比如摄像头自动曝光导致帧间隔抖动或者源文件本身VFR可变帧率。解决办法是用-vf fps30把帧率强制成CFRffmpeg -re -i input.mp4 -vf fps30,settbAVTB,setptsPTS ...第二步看服务器端的缓存设置。SRS这类服务器有gop_cache、queue_length这些缓冲参数前面已经提到过延迟大时要优先检查这些配置。Nginx-RTMP相对简单些主要看play指令和底层的网络状况。第三步看播放端的buffer配置。VLC默认缓冲可能在2秒以上ffplay如果不加参数也会缓冲。用低延迟参数播放后再对比延迟如果明显改善问题多半在播放端。5.2 音画不同步与时间基报错的处理我踩过的典型场景是推流命令里没做时间基统一推到SRS后用浏览器播放画面和声音差了半秒还越拉越大。排查之后发现是源视频文件的音频采样率是48000而我推流命令里写死了44100声卡重采样没跟上导致的。处理时我做了两件事一是把推流命令里的-ar 44100改成跟随源文件直接去掉这个参数让FFmpeg用源文件原采样率二是加上aresampleasync1让音频在必要时做对齐。这两个操作配合settbAVTB音画同步问题基本解决。如果推流过程中日志出现Application provided invalid, non monotonically increasing dts这类提示说明PTS出现了非增。常见原因是滤镜修改帧率后没重置时钟处理办法还是那套settbAVTB,setptsPTS强制重排PTS。5.3 GPL和LGPL的坑以及编译期许可证问题网上搜ffmpeg gpl和lgpl有什么区别我看到好多人问。这个问题的现实意义在二进制分发时非常明显。如果你只是内部使用、不做商业再分发那无所谓如果你把FFmpeg编进自己的产品里再发布就必须考虑许可证。我的建议很简单内部自用怎么方便怎么来商业分发严格看组件许可证清单不把GPL组件和非free组件混在一起避免给自己公司的法务添麻烦。这个不是态度问题是真实会找上门的合规问题。5.4 摄像头和权限相关的Linux设备坑/dev/video0这个设备在Linux下有权限和占用两个坑。权限不足会出现Permission denied通常是把运行推流进程的用户加入video组sudo usermod -aG video $USER设备被占用的情况更隐蔽。系统里有别的进程占着摄像头比如某个监控软件FFmpeg打开设备时会报Device or resource busy。这个时候先用fuser -v /dev/video0看看谁占用了再决定是杀进程还是换一个设备节点。我遇到过系统里多出video0、video1两个节点实际摄像头在video1上的情况所以排查时不要只盯video0先ls /dev/video*全部看一遍。5.5 从网络热词里挖出的真实需求做完这个项目后回头看那些相关的搜索词其实能看出很多人的真实处境。ffmpeg 推流到 srs 存在延迟说明很多人第一步搭通了第二步被延迟卡住ffmpeg 多个视频合并一个视频ffmpeg 视频信息查询与逐帧导出说明有不少人把FFmpeg当全能视频工具在用ffmpeg 用 d3d11va 与 dxva2 有什么区别说明Windows用户也在关注同样的硬件解码问题。这篇文章里我至少覆盖了其中大部分问题剩下的像视频合并、视频信息查询这类话题只不过是在推流的基础上换了个参数组合而已原理是相通的。6. 实战记录从零搭建一路能跑一周的推流6.1 整个推流流程的一次完整走查用一台Ubuntu服务器、一个USB摄像头、一路测试视频我完整跑一遍推流流程。准备工作是确认FFmpeg版本和可用编码器ffmpeg -version ffmpeg -hide_banner -encoders | grep -E libx264|h264_qsv|h264_nvenc如果没有x264会走后面源码编译的流程。接下来起一路SRS服务作为推流接收端或者用Nginx-RTMP这里以SRS为例跑在1935端口。然后启动推流端nohup ffmpeg -re -i /dev/video0 \ -vf settbAVTB,setptsPTS,fps30 \ -c:v libx264 -preset veryfast -tune zerolatency -profile:v main \ -b:v 2000k -maxrate 2000k -bufsize 4000k \ -g 60 -keyint_min 60 -sc_threshold 0 \ -c:a aac -b:a 128k -ar 44100 -ac 1 \ -f flv rtmp://127.0.0.1:1935/live/demo \ /var/log/ffmpeg-demo.log 21 用播放端拉流验证ffplay -fflags nobuffer -flags low_delay \ -probesize 32 -analyzeduration 0 \ rtmp://127.0.0.1:1935/live/demo观察到画面流畅、音画同步后再在服务器侧用ffprobe验证流的实际情况ffprobe -show_entries streamcodec_name,width,height,pix_fmt,r_frame_rate,avg_frame_rate \ -of json rtmp://127.0.0.1:1935/live/demo看输出的编码格式、分辨率、帧率是否和预期一致。这套流程走完一个能跑的推流器就算落地了。6.2 推流日志里那些值得关注的输出FFmpeg的日志信息看着多但有几个字段值得每次都扫一眼Stream #0:0后面的视频编码参数是否启用了硬件Output #0, flv那一段确认封装格式没错speed这一项非常关键推流时正常应该接近1x如果看到speed2x甚至更大说明-re没有生效或者文件读取速度失控观众会看到快进的画面。frame、fps、q等数值是动态刷新的CPU够不够用、编码器是否在正常工作看这几个数就知道个大概。日志里出现Non-monotonous DTS或者timestamp discontinuity则基本可以收工进入音画同步排查流程了。6.3 断流重启与录制备份的组合项目里我还加了一道保险推流的同时本地录一份原始码流存档。FFmpeg的tee可以把同一份编码流同时推到RTMP和本地文件ffmpeg -re -i /dev/video0 \ -c:v libx264 -preset veryfast -tune zerolatency -b:v 2000k \ -c:a aac -b:a 128k \ -f tee \ [fflv]rtmp://server/live/demo|[fflv]filename/data/archive/$(date %Y%m%d-%H%M%S).flv这个做法对教学直播很管用线上出问题至少本地有一份完整的流文件回放和归档都有保障。实测每天定时切割文件、定期清理过期文件再配上面说的断线重拉脚本这路推流跑了一周多没人工干预过。7. 项目延展从命令行到业务系统7.1 C封装FFmpeg的边界问题ffmpeg c封装这个搜索词说明不少人有更深的诉求。命令行能覆盖90%的推流场景但到了业务系统里你要动态控制推流的启停、切换输入源、获取实时码率、上报错误日志命令行就有点力不从心了。这时要用FFmpeg的C接口库来封装。做法上常见的是基于libavformat的AVFormatContext做输出基于libavcodec的AVCodecContext做编码。核心流程大致是打开输入avformat_alloc_output_context2创建输出上下文avio_open打开网络地址avcodec_parameters_from_context复制编码参数avformat_write_header写FLV头然后循环av_read_frame、av_interleaved_write_frame写入帧。这里有个边界问题值得说清楚FFmpeg的C接口API经常变化版本升级可能改了函数名或参数。封装时要锁定FFmpeg版本不然代码维护会变成噩梦。我有段时间升级系统包结果编译好的库引用版本对不上所有程序都要重编这个教训印象很深。7.2 把推流能力嵌入运维监控系统在项目里我还把推流动作和运维监控系统做了一点联动。比如机房有告警时巡检机器人立刻把视频画面推到指定RTMP地址让值班人员第一时间看到现场。做法上一个shell脚本动一下或者通过C封装的FFmpeg库在业务代码里动态拉起一路推流都行。关键点在于推流命令要预先模板化把服务器地址、流密钥、视频源做成变量需要的时候直接替换进去。7.3 下一阶段的优化空间这个项目停在稳定可用这一步了但延展空间还是有的。比如HLS协议的延迟比RTMP更可控SRT协议对弱网环境更友好WebRTC能实现秒级甚至亚秒级互动但部署复杂度也更高。再比如音频侧如果只推语音AAC和Opus的取舍也值得做对比测试。我个人在实际操作中的体会是推流器这种东西关键在于把一条链路彻底搞懂而不是把工具换来换去。同一套FFmpeg命令换不同的硬件、不同的服务器、不同的网络环境都会冒出新的问题。把采集、编码、封装、推送这四段每一段的细节吃透以后遇到再复杂的流媒体任务也能顺着同样的思路拆开解决。最后再分享一个小技巧所有推流命令参数在正式上线前一定要先在本地用-t 30限时跑30秒验证一遍确认CPU占用、码率波动、音画同步都正常再放上生产跑。我见过太多人拿一条理论上正确的命令直接上线结果跑了几小时后延迟、花屏问题全出来了就是因为少做了这一步验证。稳定推流不靠玄学靠的是每一个参数都有据可依每一条链路上的环节都测过。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进