ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FFmpeg Linux推流器实战:命令拆解与延迟优化

FFmpeg Linux推流器实战:命令拆解与延迟优化 如果你也在Linux服务器上给自己搭过推流服务肯定绕不开FFmpeg。从RTSP拉流、摄像头采集到把画面推到SRS或者Nginx一套稳定干净的推流器是整套流媒体链路的地基。这篇是Linux实战系列的第27篇我准备把“在Linux上用FFmpeg做推流器”这件事从头到尾拆开讲一遍为什么要用Linux做、FFmpeg怎么装、推流命令的每个参数到底是什么意思、以及最头疼的延迟问题到底怎么压下去。正文开始1. 为什么在Linux上做推流器一种朴素但高效的选型1.1 推流器的本质是什么先给刚入门的朋友一个最直白的定义推流器就是“把一路视频源变成直播流并推给服务器的角色”。输入可以是一个视频文件、一块USB摄像头、一路RTSP网络流甚至是一块屏幕输出通常是RTMP流也可以是SRT、RTSP或者HLS。整个流程可以简化成一条管线数据源采集 → 解封装/解码 → 滤镜处理 → 编码 → 封装成直播容器 → 推送到流媒体服务器FFmpeg在这条管线里几乎每个环节都能干。它既可以做纯转封装-c copy也可以做软编码libx264和硬编码NVENC/VAAPI还能拉流、推流、转码、加滤镜。这也是为什么大家谈到推流器第一反应就是FFmpeg——可替代性的确不高。1.2 为什么不用Windows而选择Linux我在项目里曾经同时维护过Windows推流端和Linux推流端最后把Windows那台彻底淘汰了。原因不是Windows不能推流而是稳定性、资源占用和远程运维之间的差距太大。服务器场景大多是无头环境没有显示器Windows的图形层在这种场景下是累赘而Linux完全可以SSH进去改一条命令行就完成部署。FFmpeg在Linux上的发行版依赖更统一静态编译版本很多放到哪台机器都能跑。嵌入式硬件和边缘计算节点比如ARM盒子上跑推流器几乎只能选Linux。Windows杀毒软件、自动更新、桌面特效这些东西随时可能打断一条24小时无人值守的推流任务。我见过不止一次推流任务挂了Windows端弹个“某某程序停止工作”的窗口然后流在服务器侧就断了。Linux下你只需要配好systemd守护进程或者写个shell循环挂了自动拉起来完全不用人管。1.3 FFmpeg在直播生态里的位置为什么绕不开做直播的人都知道OBS StudioWindows和macOS上很好用但到了服务器和嵌入式场景就抓瞎了。FFmpeg是一个命令行工具也是一个功能完整的音视频处理库FFmpeg SDK。它和GStreamer比命令行使用更简单对推流协议的支持也更直接和OBS比没有界面但恰恰因为没界面它可以无人值守、可以脚本化、可以嵌入到C工程里。所以你会看到热词里有“ffmpeg c封装”“ffmpeg sdk下载”“嵌入式linux项目”说明很大一部分人并不是在Desktop上跑FFmpeg而是把FFmpeg作为代码库集成进自己的推流程序里。即便是集成开发底层跑的依然是FFmpeg的libavformat、libavcodec这些模块。搞清命令行推流器的原理对做SDK开发的人也同样适用。2. 环境准备与FFmpeg安装别在这里翻车2.1 系统基础Debian系还是Red Hat系我这边主力机是Debian系Ubuntu/Debian生产环境偶尔也用CentOS系。推流器本身并不挑发行版只要FFmpeg装得上就行。但这里有个坑不同发行版对FFmpeg的编译选项策略不同包版本新旧也不同。如果你用Debian/Ubuntu直接sudo apt update sudo apt install ffmpeg如果你用CentOS/RHEL系sudo yum install epel-release sudo yum install ffmpegCentOS的仓库里默认不带FFmpeg需要先启用EPEL源。如果你装的是国内发行版或者Debian系统apt源没走好大概率会遇到404、连接失败。热词搜索里有人问“debian gnu/linux 13 (trixie)换清华源”其实就是这类换源问题。建议直接把仓库镜像换成国内高校镜像源清华TUNA、阿里云尤其在生产环境和网络受限场景下能节省大量时间。2.2 两种安装方式二进制分发版和源码编译怎么选我实际推荐的方式是能用包管理装就用包管理装需要额外编码器时才编译。原因很简单包管理装出来的FFmpeg自带系统依赖升级方便编译装出来的最大优势是可以定制编译选项加上libx264、libfdk-aac这些非默认功能还能针对CPU架构优化。如果你从官网下载静态构建二进制注意区分GPL和LGPL版本。热词里就有“ffmpeg gpl和lgpl有什么区别”这个问题在选二进制时确实关键LGPL版本FFmpeg自身以LGPL发布但不包含libx264等GPL库适合商业闭源应用集成。GPL版本包含libx264、libx265这些GPL库代码一旦静态链接整个工程就必须开源。做直播推流几乎离不开libx264所以绝大多数场景你拿到的都是GPL版本。个人用无所谓商用集成就要认真看许可证了。源码编译的关键步骤大概是wget https://ffmpeg.org/releases/ffmpeg-6.1.1.tar.bz2 tar xjf ffmpeg-6.1.1.tar.bz2 cd ffmpeg-6.1.1 ./configure --enable-gpl --enable-libx264 --enable-libfdk-aac --enable-nonfree --enable-libmp3lame make -j$(nproc) sudo make install--enable-libfdk-aac加上--enable-nonfree是因为FDK-AAC不是BSD协议两者要同时开启。如果只是推流到RTMP用系统自带AAC编码器aac也完全够那个不用额外编译二进制里就有。2.3 验证安装与编码器可用性装完后先别急着推流运行下面三条命令自查ffmpeg -version ffmpeg -encoders | grep -E 264|aac ffmpeg -hide_banner -hwaccels第一行看版本和编译配置第二行看x264和AAC编码器是否存在第三行看硬件加速接口。如果-encoders里没有libx264后面用-c:v libx264会直接报Unknown encoder的错误。这一步提前确认比排错半天强得多。另外提一句热词里有人问“ffmpeg用d3d11va与dxva2有什么区别”这两个是Windows那边的硬件解码API。在Linux下你更该关注的是VAAPI、VDPAU、NVENC、NVDEC这套体系。别拿着Windows教程的命令往Linux上硬套会翻车。3. 推流命令拆解把每个参数都讲明白3.1 先理解FFmpeg的推流管线FFmpeg的命令行本质是描述一条处理管线核心结构是ffmpeg [输入参数] -i 输入 [滤镜参数] [输出参数] 输出地址推流器的输出地址不是本地文件而是一个网络协议地址比如rtmp://192.168.1.100:1935/live/stream。正是这个差异决定了输出端必须用-f flv这类直播格式而不是MP4。MP4在直播场景为什么不行因为它需要moov原子和编解码信息而且文件的时长、索引都是写在文件头的适合点播不适合流式传输。RTMP协议规定只能封装FLV所以FE推流到RTMP地址时必须把输出容器指定为flv-f flv rtmp://server:1935/live/stream如果你推的是SRT或者RTSP封装格式又有区别。我后面会给你一组可直接替换的模板。3.2 一个完整的RTMP推流命令模板假设我要把一个本地视频文件循环推送到SRS服务器命令长这样ffmpeg -re -stream_loop -1 -i input.mp4 \ -c:v libx264 -preset veryfast -tune zerolatency \ -b:v 2500k -maxrate 2500k -bufsize 5000k \ -g 50 -keyint_min 25 -sc_threshold 0 \ -c:a aac -b:a 128k -ar 44100 \ -f flv rtmp://127.0.0.1:1935/live/stream这条命令我用了好几年差不多是文件推流的标准模板。下面逐个拆-re以源文件原始帧率读取模拟实时推流。如果不加FFmpeg会以最快速度读文件并全部推给服务器瞬间把带宽打满服务器直接断开连接。-stream_loop -1无限循环输入文件。循环推流在测试环境和电视墙场景很常用。-c:v libx264用libx264做软件编码是兼容性最好的选择。-preset veryfast编码速度和压缩率的平衡点。越慢的画面质量越好但CPU开销越大。直播场景我一般用veryfast或faster不会用placebo。-tune zerolatency这个参数是低延迟直播的命门。它会禁用B帧让编码器以最小延迟模式工作。B帧虽然能提高压缩率但会引入额外的解码顺序延迟直播里能不用就不用。-b:v 2500k、-maxrate 2500k、-bufsize 5000k码率控制参数。直播推流通常用ABR平均码率模式maxrate限制最高码率bufsize是编码器码率控制缓冲大小。bufsize一般是目标码率的2倍。-g 50每50帧一个关键帧。在25fps下就是2秒一个I帧这是直播常见配置。-keyint_min 25最小关键帧间隔25帧。-sc_threshold 0关闭场景自动切换关键帧。不关的话画面剧烈变化时编码器可能会突然插入额外I帧导致GOP长度忽长忽短播放器的追帧和起播都会受影响。-c:a aac -b:a 128k -ar 44100音频用AAC编码采样率44100Hz。RTMP的FLV容器对AAC支持没毛病如果源文件音频不是这个采样率FFmpeg会自动重采样。-f flv输出封装格式必须指定为FLV。3.3 三种典型的源文件、摄像头、屏幕文件推流是入门真正到要用推流器的场景往往不是推文件而是推摄像头或者屏幕。USB摄像头在Linux下走的是V4L2接口ffmpeg -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/video0 \ -c:v libx264 -preset veryfast -tune zerolatency -b:v 2000k \ -f flv rtmp://server/live/cam注意-input_format mjpeg很多USB摄像头默认输出MJPEG不指定的话FFmpeg可能用YUYV处理器占用会明显高。屏幕录制推流X11环境用x11grabffmpeg -f x11grab -video_size 1920x1080 -framerate 30 -i :0.0 \ -c:v libx264 -preset veryfast -b:v 3000k \ -f flv rtmp://server/live/screen-i :0.0的意思是取X Server的0号屏幕。如果有多显示器可以写成:0.01280,0来偏移起点。还有一种最常被问到的场景RTSP摄像头拉流转推。海康、大华这类IP摄像头的原始流通常是RTSP一般不支持直接推RTMP这时候就用FFmpeg把RTSP拉过来再推RTMPffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.64:554/stream1 \ -c:v libx264 -preset veryfast -tune zerolatency \ -b:v 1500k -maxrate 1500k -bufsize 3000k \ -c:a aac -b:a 64k -ar 44100 \ -f flv rtmp://server/live/ipc这里-rtsp_transport tcp是重点。RTSP默认用UDP接收多路摄像头同时拉流时UDP丢包概率很高画面会马赛克或者卡帧。强制TCP传输虽然增加一点延迟但稳定性大幅提升。4. 为什么“FFmpeg推流到SRS存在延迟”成热搜延迟排查实录4.1 先搞清楚延迟到底出在哪一段热词里专门有一条“ffmpeg推流到srs存在延迟”说明这是很多人做完推流之后第一个撞上的墙。你推流推得很欢播放器一看画面比现实世界慢了5秒甚至10秒第一反应就是FFmpeg参数有问题。但实际延迟可能分布在四条链路里采集端延迟摄像头本身感光、ISP处理的延迟。推流端编码延迟B帧、preset、GOP设置不当造成的编码缓冲。服务器端缓冲SRS、Nginx-RTMP内部的缓存队列。播放器端缓冲播放器为了平滑播放而额外缓存的数据量。其中占比最大的往往不是FFmpeg编码而是播放器的缓冲策略。拿VLC默认参数播放RTMP它为了网络抖动会额外缓冲1到3秒这个延迟和推流器没关系。4.2 编码层面的三个隐形延迟源B帧编码器为了提高压缩率会引入B帧B帧需要等待前后帧都编码完才能解码直接增加延迟。所以低延迟推流一定要-tune zerolatency或者显式-bf 0。GOP长度GOP太长播放器切到某个频道后必须先等到下一个关键帧才能起播。假设GOP是50帧、帧率25fps那最坏情况要等2秒才能看到画面。直播里GOP建议控制在1到2秒。preset选慢慢速preset不是延迟高而是编码耗时高导致每一帧在送入网络前多等了几毫秒大量帧累积起来延迟就上去了。我在一次排查中把preset从medium改成veryfast加上-tune zerolatency端到端延迟从原先的4到5秒降到了1.5秒左右。这就是最直接的效果。4.3 用SRS做服务器时别忘了改服务端配置SRS默认配置其实已经适合低延迟直播但如果你用的是自己的vhost还是要注意几个参数。以下是一段常用的SRS vhost配置vhost __defaultVhost__ { gop_cache off; queue_length 10; min_latency on; tcp_nodelay on; }gop_cache off关闭GOP缓存。GOP缓存会让新播放者秒开画面但代价是新观看者拿到的第一帧可能不是关键帧导致花屏等待。低延迟场景我建议关闭让流严格按时间线走。queue_length 10缓冲队列只保留10个消息包消息包太大直接丢弃避免累积延迟。min_latency onSRS为低延迟专门优化过开启后立刻生效。tcp_nodelay禁用Nagle算法小包立即发送对码率不高的直播流有明显作用。你要是用的不是SRS而是Nginx-RTMP那就在rtmp配置块里给对应application加上play_time_fix off;相关的参数。说实话Nginx-RTMP模块我后期基本不碰了SRS在延迟控制和API监控上都好太多。4.4 用ffprobe找出延迟真相排查延迟不能全靠猜用ffprobe直接看输出流的信息是最快的办法。ffprobe rtmp://127.0.0.1:1935/live/stream重点看这几项codec_name、profile、avg_frame_rate、time_base。热词里有人搜“ffmpeg codec time base”这确实是新手容易绕晕的点。简单解释time_base是时间戳的计量单位比如1/1000表示每个时间戳单位是1毫秒而帧率是每秒多少帧。两者不能直接混为一谈。推流的时候如果输入文件和编码器的时间基准不一致FFmpeg会自动做转换但如果你手动拼接参数时把时间基准指定错了就会出现推流后音画不同步。真正判断延迟卡顿我建议推流端用ffmpeg的日志看有没有掉帧服务器端看SRS的vhost日志里有没有consume超时播放端用低缓冲的专业播放器去验证比如ffplay加参数ffplay -fflags nobuffer -flags low_delay -analyzeduration 0 -probesize 0 rtmp://127.0.0.1:1935/live/stream这一套排查下来基本能把“延迟是FFmpeg的锅还是SRS的锅还是播放器的锅”分清楚。5. 常见问题与排查技巧实录5.1 推流掉线、断连、连接被拒现象FFmpeg推流启动没几秒就报rtmp connect failed或者运行一段时间后服务器主动断开。排查思路先后是检查目标服务器端口通不通、防火墙有没有放行1935或其他RTMP端口、推流地址是不是正确。很多时候“连接被拒”不是因为服务器挂了而是你推流的application名不存在。比如SRS默认application是live你却推到了myapp下。还有一点容易被忽略服务器端的带宽。推流码率2Mbps但服务器上行只有1MbpsFFmpeg会因为发送超时被服务器踢掉。碰到这种情况先检查服务器带宽监控曲线再考虑降码率。5.2 音画不同步音画不同步发生在推流端绝大多数情况是输入源的时间戳不连续。用ffprobe看一下输入文件的start_time和duration如果音频和视频的时长差很多那就是源文件本身有问题。解决办法是在推流命令里强制校正时间戳-af aresampleasync1:min_hard_comp0.100000:first_pts0这一行强制音频重采样并把音频时间戳对齐到视频起始点实测对RTSP摄像头拉流、老旧视频文件推流都有用。5.3 硬件加速VAAPI和NVENC对比如果CPU吃紧Linux下要上硬编码优先看你的显卡支持什么。Intel集显和AMD用VAAPINVIDIA独显用NVENC。VAAPI转码命令模板ffmpeg -vaapi_device /dev/dri/renderD128 \ -i input.mp4 -vf formatnv12,hwupload \ -c:v h264_vaapi -b:v 2500k \ -f flv rtmp://server/live/streamNVENC是NVIDIA闭源驱动提供的能力命令更简单ffmpeg -i input.mp4 -c:v h264_nvenc -preset p4 -b:v 2500k \ -f flv rtmp://server/live/stream我这里提一句Windows上的d3d11va和dxva2它们本质是微软DirectX生态里的硬件解码API一个基于DX11一个基于旧版DXVA。我们Linux推流场景用不到这两个别在Linux教程里去抄Windows参数。Linux下硬件视频解码主要看VAAPI和NVDEC编码看VAAPI和NVENC。硬件加速虽然好用但要注意画质和编码参数可调范围都不如libx264灵活。5.4 常见问题速查表现象可能原因处理建议推流启动后立刻断开RTMP地址、application名错误检查SRS日志确认/live/stream路径画面花屏、马赛克RTSP over UDP丢包加-rtsp_transport tcp延迟越来越大播放器缓冲/GOP过长调-g到1~2秒播放器用低延迟模式音频静音或杂音输入源采样率不匹配加-ar 44100强制重采样CPU占用100%软编码preset太慢换veryfast或改用NVENC/VAAPI时间戳跳变、音画不同步输入流时间基准不连续ffprobe检查必要时重封装源文件这表里的问题我基本都在生产环境撞过一遍。有些问题看似是FFmpeg命令的问题其实是源流本身就不干净还有些是服务器配置的问题。排查的时候一定不要上来就改代码先把日志打开、把流拉下来本地播放分段定位。5.5 把推流器做成可自愈的服务最后分享一个我常用的生产化技巧把推流命令封装成systemd服务加个watchdog逻辑断线自动重启。写一个简单的shell脚本/usr/local/bin/push_rtmp.sh#!/bin/bash while true; do ffmpeg -rtsp_transport tcp \ -i rtsp://192.168.1.64:554/stream1 \ -c:v libx264 -preset veryfast -tune zerolatency \ -b:v 1500k -maxrate 1500k -bufsize 3000k \ -c:a aac -b:a 64k \ -f flv rtmp://127.0.0.1:1935/live/ipc echo ffmpeg exited, restarting in 3s... /var/log/push_rtmp.log sleep 3 done然后做一个systemd单元文件[Unit] DescriptionRTMP Pusher Afternetwork.target [Service] ExecStart/usr/local/bin/push_rtmp.sh Restartalways RestartSec3 [Install] WantedBymulti-user.target这样推流器即使因为网络抖动、上游摄像头断流退出也会在3秒内自动重启。注意服务器上如果同时跑多个推流器记得给不同脚本区分日志文件不然排查时全混在一起会疯。6. 我的个人经验和最后的建议在做过的几个推流项目里我最常被问的一句话是“到底用什么参数能保证不卡”。实话实说不存在万能参数因为推流器永远只是直播链路里的一段。你负责的那段要做到输入稳定、编码实时、输出不积压。输入稳定靠协议选择和源设备质量编码实时靠preset和硬件加速的组合输出不积压靠码率规划和服务器缓冲设置。我个人实际使用中最稳的软编码组合就是-preset veryfast -tune zerolatency -b:v 目标码率 -bufsize 两倍码率 -g 两秒帧数 -sc_threshold 0这个组合我在不同设备、不同网络上跑了很久兼容性没出过什么大问题。硬件加速的话服务器上有NVIDIA卡就优先NVENC普通Intel机器用VAAPI也能省下不少CPU。还有一个小技巧推流命令里那些看似可以省的参数比如-f flv、-rtsp_transport tcp该写就老老实实写全。少了-f flvFFmpeg有时候会根据输出后缀自动判断但一旦地址不带后缀或者协议特殊就会报错。命令行参数写得越明确出问题时越容易排查。把这套东西吃透之后你会发现推流器本质不是一个工具而是一套流程。把流程里的每段都弄明白就算哪天从RTMP换到SRT或者WebRTC思路依然是相通的。
返回列表