资讯动态

Windows下基于Nginx-RTMP实现RTSP转RTMP推流详解

发布时间:2026/10/15 3:28:51 来源:尧图企业网站定制
1. 内容整体设计与思路拆解先说说我为什么会在 Windows 上折腾这么一套东西。做流媒体的人基本都遇到过这种尴尬手里拿的摄像头、采集卡、或者老旧的监控设备推流协议清一色是 RTSP后端播放平台或者CDN却只认 RTMP。两边鸡同鸭讲直接对接根本推不上去。平时在 Linux 上干这活很顺手可一旦项目临时落在 Windows 环境或者现场只有一台 Windows 工控机你就得想办法在 Windows 上把这套链路搭起来。这篇文章就是我实际在 Windows 上搭建 nginx-rtmp 服务器把 RTSP 摄像头流拉下来再转成 RTMP 推流的完整记录包括原理、配置、踩坑和排查方法。先说清楚这套方案解决的核心问题它不是一个播放器也不是一个转码软件而是一个“协议中转站”。RTSP 源产生 H.264/H.265 编码的视频流通过 nginx-rtmp 模块接收后重新封装成 RTMP 协议再推给下游的流媒体服务或播放器。这里的重点在“转封装”不是“转码”——音视频编码格式基本保持不变只是把传送协议从 RTSP 换成 RTMP所以对 CPU 的消耗很小延迟也能控制在比较低的水平。适用人群很明确需要快速在 Windows 本机或服务器上搭一个临时的、轻量的 RTMP 分发节点的人包括嵌入式开发者、监控系统集成工程师、直播推流调试人员也包括刚刚接触流媒体协议栈的学生。整个方案只依赖开源软件不需要购买商业授权配置起来也不像某些重型流媒体服务那样动辄几百行配置。如果你已经对 RTSP、RTMP 这两个协议有一定印象但还没亲手搭过服务端这篇文章可以直接照着抄。为什么偏偏选 nginx-rtmp而不是其他方案我自己对比过几个主流选择用 FFmpeg 单独做转推虽然能实现 RTSP 拉流再推 RTMP但它只解决“一路转一路”的问题没有服务端的分发能力多个播放端同时拉取同一路流时每个端都要独立占用一次转推进程资源浪费明显。而 nginx-rtmp 具备服务端的流接收、回源、分发功能一个入口进来下游所有播放端都能从同一个流名拉流。另一方面Windows 上也有商业的流媒体服务但架设成本和授权限制往往比开源方案高出一截。综合对比下来nginx-rtmp 在轻量、可控、免费这三个维度上最适合作为中转节点。注意这个方案的核心是“协议转换”而非“转码”如果源流编码格式是 H.265而你下游的播放器不支持硬解 H.265那么即使 RTMP 链路通了画面也可能黑屏。这个问题在后面会专门讨论。2. 环境准备与工具选型解析2.1 Windows 下的 Nginx 与 RTMP 模块Nginx 本身是跨平台的官方提供 Windows 版本但官方 Windows 版本默认不包含 RTMP 模块。所以我们要用的是社区维护的、已经编译好了 RTMP 模块的 Windows 版本。这里需要注意不要跑到官网下载二进制包就完事必须找到带rtmp模块的编译版本。我用的是某开源社区维护的 Windows 编译包版本号跟随 Nginx 主线更新解压后目录里就能看到nginx.exe同时conf目录下带有完整的示例配置。判断一个 Nginx 编译包是否包含 RTMP 模块有一个特别简单的方法打开命令行进入解压后的目录执行nginx.exe -V输出的编译参数里面如果能看到--with-http_rtmp_module这一行就说明这个版本自带 RTMP 支持。我见过有人下载了官方原版 Nginx折腾半天写了 RTMP 配置启动后直接报unknown directive rtmp其实就是这个原因。这一步是基础中的基础一定先确认。2.2 RTSP 拉流工具的选择FFmpeg 还是专用工具把 RTSP 流转成 RTMP需要在服务器上有一个“拉流器”。最通用、最不容易出问题的方案就是 FFmpeg。它的 RTSP 解封装能力非常成熟支持 TCP/UDP 传输模式切换也支持各种 RTSP 鉴权方式。而且 FFmpeg 是纯命令行工具在 Windows 上不需要安装解压之后直接用ffmpeg.exe就行。也有人问能不能让 nginx-rtmp 自己直接拉 RTSP 流很遗憾nginx-rtmp 模块本身只做 RTMP 协议的接收和分发它没有内置 RTSP client 的能力。你可以在它的配置里写pull指令但pull的语法只支持从另一个 RTMP 服务器拉流不支持rtsp://这样的 URL。换句话说拉流这步必须借助外部工具。FFmpeg 是最顺手的选择因为我们可以用一条命令行完成“读 RTSP、输出 RTMP”两个动作中间还可以顺手做编码参数微调。如果你习惯用 OBS 或者 VLC 来做拉流也不是不行但这两个都偏向交互式操作不适合做无人值守的服务端。如果现场有多个摄像头也可以一次性在 FFmpeg 命令里用循环方式逐个处理但更稳的做法是每个摄像头单独启一个 FFmpeg 进程这样一路流出问题不会拖累其他路。具体的进程管理方式我后面会讲。2.3 目录结构与基础准备我建议把整个工具包放在一个固定的、无空格的路径下比如D:\StreamServer里面分子目录nginx、ffmpeg、conf、logs。虽然理论上配置文件和工具放哪里都能运行但 Windows 下路径有空格时在命令行里折腾转义特别容易出错尤其是脚本化启动的时候。无空格的路径能减少非常多不必要的麻烦。需要准备的软件清单如下组件用途获取方式nginx含 rtmp 模块RTMP 服务端社区预编译包FFmpegRTSP 拉流、转封装、推 RTMP官方编译版本VLC 或其他播放器验证 RTMP 流可播放常规安装命令行工具测试 RTSP 源连通性Windows 自带把 FFmpeg 解压后最好把它所在的bin目录加到系统环境变量 Path 里这样后续写脚本可以直接调用ffmpeg不用每次拼完整路径。不需要把 Nginx 加入系统服务保持手动启动就好方便调试。3. 核心配置与实操步骤拆解3.1 Nginx RTMP 配置文件的完整示例RTMP 模块的配置和 HTTP 配置是独立的两个块。Nginx 默认读取conf/nginx.conf我们在里面同时配置http块和rtmp块。下面是我实测可用的最小化配置worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location /stat { rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } location /stat.xsl { root html; } } } rtmp { server { listen 1935; chunk_size 4096; application live { live on; record off; } } }这里需要解释几个关键点。listen 1935是 RTMP 协议的默认端口下游播放器在拉流时如果不带端口默认就是 1935。chunk_size这个参数控制 RTMP 分块的大小设成 4096 是比较稳妥的值太大可能在某些播放器出现延迟抖动太小又会增加 CPU 开销。application live定义了一个挂载点名字可以随便起但客户端推流和拉流的地址都会包含这个名字。live on表示开启实时流模式record off很重要如果不关掉默认情况下 nginx-rtmp 会把收到的流录制成 flv 文件存到硬盘上一段时间下来磁盘会被写满。HTTP 块里的/stat页面是 nginx-rtmp 提供的在线状态监控通过浏览器访问http://localhost/stat可以看到当前有几路流、每路流的带宽、连接数等信息调试时非常有用。这个缺失不影响功能但强烈建议保留。3.2 FFmpeg 拉取 RTSP 并推送到 RTMP 的命令解析配置好 Nginx 后启动它cd D:\StreamServer\nginx nginx.exe然后在任务管理器里确认有nginx.exe进程在跑。接着调用 FFmpeg 拉取 RTSP 流。我的典型命令长这样ffmpeg -rtsp_transport tcp -i rtsp://your_camera_ip:554/stream -c copy -f flv rtmp://127.0.0.1:1935/live/stream1逐项说明一下参数的含义。-rtsp_transport tcp强制 RTSP 走 TCP 传输而不是默认的 UDP。这个选择很有讲究UDP 延迟更低但在网络质量不稳定的情况下丢包严重画面会出现马赛克、绿屏TCP 虽然有重传机制延迟稍微增加但稳定性好得多。在局域网环境里我基本都建议用 TCP。-i后面是 RTSP 源地址注意不同摄像头厂商的 RTSP 路径格式不同这个要按实际设备去查。-c copy的意思是直接复制编码流不做转码这是保证低 CPU 占用的关键。-f flv是输出封装格式因为 RTMP 协议承载的封装格式就是 FLV。最后的rtmp://127.0.0.1:1935/live/stream1是推流目标地址路径里的live对应 nginx 配置里的 application 名字stream1是自定义的流名后面拉流也需要用到这个名字。执行这条命令后如果一切正常FFmpeg 会持续输出日志显示帧率、码率、时间戳等信息。这时在另一台机器上用 VLC 打开rtmp://127.0.0.1:1935/live/stream1就能看到实时画面了。3.3 多种 RTSP 源适配思路并不是所有 RTSP 源都长一个样。有些源需要用户名密码认证那就在 URL 里带进去ffmpeg -rtsp_transport tcp -i rtsp://username:password192.168.1.100:554/h264/ch1/main/av_stream -c copy -f flv rtmp://127.0.0.1:1935/live/camera1有些源是 H.265 编码直接用-c copy推过去如果下游播放器能解码就没问题但部分 Flash 播放器或者老版本播放器往往不行。这时候就需要在服务器端转码把 H.265 解码再编码成 H.264。命令改成ffmpeg -rtsp_transport tcp -i rtsp://your_camera_ip:554/stream -c:v libx264 -preset veryfast -tune zerolatency -c:a aac -f flv rtmp://127.0.0.1:1935/live/stream2这里-c:v libx264表示用 x264 编码器重新编码视频-preset veryfast是编码速度和画质的取舍-tune zerolatency特别适合直播场景它会降低编码器的延迟。但这方案的代价是 CPU 占用明显上升。转码一个 1080p 的 H.265 流到 H.264即使veryfast预设也需要一个不错的 CPU 核心才能跑流畅。所以我的建议是能copy就copy只有下游明确不支持 H.265 时才转码。4. 实操过程与核心环节实现4.1 启动前的网络检查真正开始推流之前有几个网络层面的坑值得先排查。RTSP 源如果不是本机模拟的而是来自网络摄像头首先用 ping 命令确认能通。然后测试 RTSP 端口的可访问性telnet 192.168.1.100 554Windows 10 以上版本自带 telnet 客户端如果没有启用可以在“启用或关闭 Windows 功能”里打开。如果端口不通后面的 FFmpeg 拉流必然失败而且可能不是立刻报错而是反复重试后超时浪费时间。还有一种常见情况是摄像头默认关闭了 RTSP 鉴权或者设置了 IP 白名单需要在设备管理页调整。同样需要检查防火墙。Nginx 监听 1935 端口Windows 防火墙默认会拦截。第一次启动 Nginx 时系统会弹出防火墙提示记得勾选允许访问。如果没有弹出到“Windows Defender 防火墙-允许应用通过防火墙”里手动添加nginx.exe。如果是物理服务器注意别在公网敞开 1935 端口毕竟 Nginx-RTMP 没有内建鉴权任何人都能推流暴露到公网等于裸奔。如果确实需要公网访问建议加一层 Token 校验或者在前面套一个网关。4.2 FFmpeg 拉流推流全流程实录我拿一个局域网内的模拟 RTSP 源来演示完整过程。假设摄像头的 RTSP 地址是rtsp://192.168.1.64:554/ch01Nginx 服务在本机。第一步确认服务端监听起来。命令行执行netstat -an | findstr 1935看到TCP 0.0.0.0:1935处于LISTENING状态说明 Nginx 已经妥了。第二步执行 FFmpeg 推流。我推荐把日志级别调整一下否则默认输出太啰嗦ffmpeg -hide_banner -loglevel warning -rtsp_transport tcp -i rtsp://192.168.1.64:554/ch01 -c copy -f flv rtmp://127.0.0.1:1935/live/demo-hide_banner隐藏版本信息-loglevel warning只输出警告级别以上的日志。这样屏幕干净很多如果出现错误也仍然会显示。正常的情况下这条命令会一直挂在那里没有报错就是成功了。你可以在浏览器打开http://127.0.0.1/stat查看状态页面能看到demo这个流名点击后面的“观看”可以测试播放。第三步用 VLC 验证。打开 VLC按 CtrlN输入rtmp://127.0.0.1:1935/live/demo如果画面正常出来说明整条链路是通的。到这里核心功能已经完全实现了。4.3 进程守护与开机自动拉起FFmpeg 属于前台进程命令窗口关了流就断了。在实际项目中肯定不能要求人一直盯着窗口。我在 Windows 上做无人值守的经验是写一个批处理脚本用start加隐藏窗口的方式来运行再配合计划任务让它在开机时自动启动。批处理脚本示例echo off set RTSP_URLrtsp://192.168.1.64:554/ch01 set RTMP_URLrtmp://127.0.0.1:1935/live/demo :loop ffmpeg -hide_banner -loglevel warning -rtsp_transport tcp -i %RTSP_URL% -c copy -f flv %RTMP_URL% echo %date% %time% ffmpeg exited, restarting... restart.log timeout /t 5 /nobreak goto loop这个脚本的思路是FFmpeg 进程一旦异常退出脚本会在 5 秒后重新拉起它同时把重启记录写进日志方便排查。配合 Windows 计划任务在“系统启动时”触发这个批处理就能做到开机自启。注意批处理窗口会一直存在如果想要隐藏窗口可以把脚本存成.vbs或者用其他方式启动但对于调试期来说保留窗口反而方便观察。再说一个容易忽略的地方如果在一台机器上要推多路不同源绝对不要把所有源塞进同一个 FFmpeg 进程里用-map处理除非你精通 filter 图。更稳妥的做法是每路一个批处理子脚本或者写一个循环结构为每个摄像头启动独立进程。这样某一路源断线重启时不会影响其他路。5. 常见问题与排查技巧实录5.1 推流失败Nginx 报错 unknown directive “rtmp”这个问题我在前面提到过属于安装包不带模块。排查手段很简单执行nginx.exe -V看编译参数。如果确认没有--with-http_rtmp_module那就要重新找一个带 RTMP 的版本。社区常用的编译版本一般会明确标注“include rtmp module”下载前一定要看清楚。还有一种情况是 Nginx 正在运行你替换了 nginx.exe 但没重启进程这时候可以在任务管理器结束所有 Nginx 进程再重新启动避免新旧版本混乱。5.2 推流地址写对了但 VLC 拉不到流这种问题多半不是 Nginx 本身的问题而是 FFmpeg 推流进程其实已经退出了。先用nginx-rtmp的状态页确认有没有流在推。如果没有再回头看 FFmpeg 日志。最常见原因是 RTSP 源连接超时而且是 UDP 模式导致的。默认情况下 FFmpeg 的 RTSP 传输模式是 UDP但如果设备或者网络环境屏蔽了 UDP 端口FFmpeg 可能一直卡在Connection Timeout。解决办法就是显式加-rtsp_transport tcp。另一个原因是 RTSP URL 路径不对不同品牌摄像头的路径差异很大甚至同一个品牌不同型号都不同。拿到的地址最好先在 VLC 里验证一下能不能直接播放确认源没问题再往 Nginx 上推。5.3 画面上有花屏或者绿屏花屏一般发生在 UDP 传输、网络抖动的情况下。解决办法优先改成 TCP 传输。如果已经是 TCP 模式还有花屏那要考虑是不是源端本身输出就不稳定或者 FFmpeg 在-c copy模式下遇到时间戳断裂。可以尝试加-fflags genpts这个参数强制生成缺失的 PTS 时间戳能解决一部分时间戳异常导致的画面毛病。如果是音频出现爆音或者音画不同步可以试试在输出端加-flvflags no_duration_filesize这个参数用来消除 FLV 封装中某些错误的 duration 信息在一些特殊源上有效。5.4 延迟越来越高画面长时间卡住RTMP 本身是低延迟协议但 Nginx 默认配置可能因为缓冲因素导致延迟累积。检查chunk_size不要设置得过大另外如果 TTL 设置存在问题可以考虑调整 FFmpeg 输出端的-rw_timeout参数限制写超时时间避免进程假死。更彻底的办法是开启 Nginx 的实时模式在 application 配置中加上application live { live on; record off; gop_cache on; }gop_cache on的作用是缓存最新的一个关键帧这样当新播放器接入时可以立刻拿到关键帧开始解码而不是等到下一个关键帧到来这就大大缩短了播放器首屏等待时间。启用了这个参数后新播放器基本能在 1 秒内出画面代价是内存中会保留一个关键帧的数据内存占用很小可以忽略。5.5 RTSP 源是 H.265播放端黑屏这是最容易让人迷惑的问题FFmpeg 日志显示推流正常Nginx 状态页显示流存在VLC 可能也能播放但换到某些网页播放器就黑屏。原因就是 H.265 编码通过 RTMP 分发后FLV 封装中的 H.265 支持取决于播放器本身。FLV 规范最早只定义了 H.264后来各家才扩展支持 H.265。所以对于兼容性要求高的场景我建议在服务端直接用libx264转码。如果你的下游播放器确实不支持 H.265不要犹豫代价就是占用 CPU这是最省心的方案。5.6 端口被占用或者无法绑定 1935Windows 下比较容易出现端口被占用比如某些软件默认也监听 1935。排查命令netstat -ano | findstr 1935查到占用进程的 PID再到任务管理器里找到对应程序。如果确实不想换 Nginx 的端口可以先把占用端口进程停掉。如果希望改用其他端口比如 19350直接修改 Nginx 配置中listen 19350那么拉流地址也要对应修改为rtmp://127.0.0.1:19350/live/demo。6. 进阶玩法与实际项目经验6.1 加一层接入鉴权防止别人乱推流Nginx-rtmp 本身不带用户鉴权但可以用 on_publish 回调来做一个简单的 Token 校验。原理是当客户端推流时Nginx 会向配置的 HTTP 地址发一个 POST 请求带上传入的流名和参数如果接口返回 2xx才允许推流。配置如下application live { live on; record off; on_publish http://127.0.0.1:8080/auth; }这时候 FFmpeg 推流地址需要带上 token 参数ffmpeg -rtsp_transport tcp -i rtsp://... -c copy -f flv rtmp://127.0.0.1:1935/live/demo?token123456后端用任意语言写一个接口校验token123456则返回200否则返回非2xx状态码。我实际常用的做法是用一个极简的 Python HTTP 服务来做这个校验。这个方案虽然谈不上足够强但对于内部网络的项目来说已经能挡住绝大多数误推流的情况。6.2 多路输入统一入口到 RTMP实际项目中经常有这样的场景一个厂房里有十个摄像头希望全部接入同一台 Windows 机器再统一推到平台。这种需求可以每路一个 FFmpeg 进程推流名分别为camera1、camera2……这样可以做到独立管理某一路断线重启不影响其他路。Nginx 配置里不需要写死流名任意流名都会自动创建一路流。管理脚本可以用一个简单的循环for %%i in (1 2 3 4) do ( start /b ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.%%i:554/ch01 -c copy -f flv rtmp://127.0.0.1:1935/live/cam%%i )如果你想要在断线时自动重连每个进程都套上 4.3 中的循环结构即可。6.3 监控流状态从状态页到简单 APIhttp://127.0.0.1/stat这个页面能看到所有流的实时信息但它是一个 HTML 页面不适合程序读取。Nginx-rtmp 还提供了一个 XML 格式状态接口可以通过配置/stat时返回 XML 数据。如果只想快速写一个巡检脚本检查流是否存在可以用下面的命令行curl http://127.0.0.1/statWindows 10 自带 curl直接输出 HTML如果看到stream1表示流在。稍加解析就能做成简单的告警脚本如果状态页里找不到指定流名就自动重启 FFmpeg 进程并将告警信息发送到钉钉或者企业微信。这算是这个方案里性价比很高的小扩展。6.4 为什么我不建议用 VLC 做推流有人可能会想既然 VLC 既能拉 RTSP 又能推 RTMP为什么还专门用 FFmpegVLC 的图形界面操作简单但作为服务端组件有两个硬伤一是异常退出后无人值守重启不方便二是它对多路流的资源控制不如 FFmpeg 精细。FFmpeg 是纯命令行能非常方便地被批处理和计划任务调度日志也更利于排查。所以我劝所有打算长期跑这套服务的人老老实实用 FFmpeg 拉流。7. 性能开销与稳定性心得很多初学者会担心在 Windows 上跑这套方案会不会性能不行。以我的实测经验来说-c copy模式下FFmpeg 只做解封装和再封装CPU 占用极低。一路 1080p 的 H.264 流在普通 i5 处理器上 FFmpeg 进程 CPU 占比通常在 3%-5% 左右Nginx 就更低了毕竟它只做协议转发。真正吃 CPU 的是转码也就是 H.265 转 H.264 的操作这个场景下 CPU 占用会直接飙到 30% 以上。所以部署前先确认源编码格式能 copy 就不要转码。内存占用方面Nginx 默认配置跑几十路流都没什么压力每路流的缓冲、密钥帧缓存加在一起也就几十兆。只要你的 Windows 机器不是那种 2GB 内存的老古董完全不用担心。磁盘方面只要record offNginx 本身不写盘。FFmpeg 推流时默认没有落盘操作所以对磁盘的读写基本可以忽略。这意味着你可以把整套服务跑在一台小 SSD 的迷你主机上非常轻量。稳定性上我已经用这套方案跑过 7x24 小时的连续推流。最大的敌人不是服务器本身而是上游 RTSP 源不稳定。摄像头偶尔会重启、断流导致 FFmpeg 进程退出。因此守护脚本比什么都重要。建议在批处理里记录重启日志并把日志定期清理防止文件无限增长。还有一个细节批量处理时注意timeout命令的语法旧系统可能需要写成timeout /t 5 /nobreak nul避免把鼠标点击动作误传进窗口。8. 从单机到小集群的扩展思路如果你不止一台 Windows 机器或者前端有负载均衡的需求这套 Nginx-rtmp 方案也可以平滑扩展。最简单的方式是部署多台 Nginx-rtmp 服务器在前端用加权轮询的方式分发推流地址每台服务器的 Nginx 配置里再配置pull从另外的源拉取同一路流实现简单的冗余。比如服务器 A 和 B 都检测到主服务器 C 上有流A 和 B 各自通过pull rtmp://C/live/demo;来拉取这样即使 C 宕机A 和 B 上还存留着最近一段时间内拉取到的流下游播放端可以继续从 A 或 B 拉流完成一个基础的主备容灾。当然这只能缩短中断时间并不能做到零切换但对于内部监控项目来说已经完全够用。如果在更复杂的场景中希望动态管理流可以引入更重的流媒体服务来替换 Nginx-rtmp但那样整套系统的部署复杂度会高一个档次。我个人看法是技术选型要从实际规模出发。10 路以内的流Nginx-rtmp 加 FFmpeg 是性价比最高的组合超过 50 路就应该考虑专门的流媒体网关或者集群方案了。窗口从单机走到集群核心的思路是可替换、可观测、可恢复这正是这套轻量方案在真实项目里带给我的经验。最后再分享一个我在现场调试时经常用的小技巧在 RTSP 源不稳定、摄像头暂时不在线的时候可以先启动 Nginx再用 FFmpeg 测试本机生成的一路测试源也就是用 FFmpeg 的 lavi 虚拟设备推流验证下游链路是否通畅ffmpeg -re -f lavfi -i testsrc2size1280x720:rate30 -c:v libx264 -preset veryfast -f flv rtmp://127.0.0.1:1935/live/testVLC 能播放这路测试流说明服务器、端口、Nginx 配置都没有问题接着就可以安心排查摄像头那一侧。这个办法帮我省下了很多在机房里跟摄像头厂商扯皮的时间。这个内容后续还可以扩展的方向是接入 WebRTC 低延迟播放、把 RTMP 流转成 HLS 做回看、或者用 FFmpeg 对视频流叠加文字水印和动态时间戳。只要把“RTSP 进、RTMP 出”这套基础链路跑明白了后面这些都是在上面增加 filter 链的事情边界清晰改造成本很低。

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

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

免费获取报价 →
↑