RTSP转HLS播放方案:FFmpeg+Nginx+SSM实现浏览器视频监控
简介一套面向Java后端开发者与流媒体入门者的完整实践资源基于SSM架构整合Nginx与FFmpeg实现RTSP实时流到HLS流的转换并交付可在前端网页播放的工程解决摄像头或RTSP源无法直接在浏览器预览的常见问题。压缩包共52个文件、约70.26MB包含FFmpeg与Nginx安装包、SSM架构的Java源码及jsp/conf配置、基于jQuery的播放器页面html/css/js以及配置说明、调试源、部署文档等辅助材料覆盖从环境搭建到业务联调的主要环节。配套的转流命令、Nginx配置示例和部署必读笔记能帮助读者快速复现作者亲测可行的全流程尤其适合想动手实践但又缺少头绪的小白用户。资源已有262人学习浏览从中可直接获取可运行骨架和排错思路并进一步理解RTSP、HLS协议转换及前后端联调的关键细节便于迁移到监控、在线课堂等实时预览场景。1. 一套SSMNginxFFmpeg方案专治RTSP流在浏览器里放不出来监控摄像头几乎都走RTSP协议但浏览器偏偏不认它。想在内网Web系统里直接看监控画面就得先把RTSP拉成HLS切片再用Nginx吐给前端整个过程还得有个后端工程去编排转流进程。这套SSM架构NginxFFmpeg的资源包正好把三段链路全串好FFmpeg负责转流切片、Nginx负责静态分发、SSM工程负责业务控制和参数管理外加一个playerJQueryDemo网页打开即用。适合给园区做远程视频巡检、给车间接旧摄像头、给管理系统嵌入实时监控画面的开发者。链路走通了后续加摄像头只是改配置的事不用再动架构。2. FFmpeg把RTSP转成HLS切片先想清楚为什么选HLS2.1 三种转流协议对比HLS、RTMP、WebRTC各自的边界拿到一个RTSP流转发的需求第一反应往往是选协议。RTMP延迟低但2020年后浏览器对Flash彻底断供纯前端方案废掉一半WebRTC延迟最低但信令服务、STUN/TURN的部署成本对一个小工程来说太重HLS走的是HTTP拉流后端切成ts切片、写m3u8索引前端拿video标签或者hls.js直接播实现路径最顺。HLS的代价是延迟比不上RTMP和WebRTC切片2秒的情况下端到端通常有4到8秒延迟。但监控场景里这个延迟可接受安防画面的核心诉求是流畅和稳定不是实时对讲。另一个优势是HLS天然穿过HTTP端口公司内网防火墙很少拦80或8080跨部门协调成本低。这个资源包选HLS作为中间协议是合理的。FFmpeg对RTSP的解码兼容性比市面上的SDK封装要稳切片参数可以调后端可以随时重启转流进程这些都是商业播放器给不了的灵活性。2.2 FFmpeg转流命令拆解从RTSP到m3u8的关键参数先看最基本的转流命令。假设摄像头RTSP地址是rtsp://admin:password192.168.1.64:554/Streaming/Channels/101我要把它转成HLS切片输出到E:/hls/cam01目录。ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 \ -c:v copy -c:a aac -f hls \ -hls_time 2 -hls_list_size 0 -hls_flags delete_segments \ -hls_segment_filename E:/hls/cam01_%05d.ts \ E:/hls/cam01.m3u8说明几个关键参数。-rtsp_transport tcp强制走TCPRTSP默认用UDP传输摄像头到服务器跨交换机时UDP丢包会直接花屏或卡帧TCP虽然增加一点延迟但稳定得多。-c:v copy是视频流直接复制不重新编码CPU占用几乎为零前提是摄像头输出的编码格式是H.264绝大多数IPC默认就是这个。-c:a aac是音频转成AAC有些摄像头输出的是PCM或G711HLS的TS封装不认必须转一下。-hls_time 2表示每个ts切片时长为2秒-hls_list_size 0表示m3u8索引文件里保留所有切片如果做纯直播建议改成-hls_list_size 5这样播放器只看到最近5个切片延迟能压低一截。-hls_flags delete_segments是切完的旧切片自动删除防止转流跑一天后磁盘被几万个ts文件塞满。2.3 用Java的ProcessBuilder管理FFmpeg进程转流命令在命令行里跑通之后下一步是交给后端Java工程去拉起和管理。SSM工程里最常见的做法是封装一个转流服务内部用ProcessBuilder启动FFmpeg子进程。public class FfmpegStreamService { private Process ffmpegProcess; // 启动转流把RTSP转成HLS切片 public void startStream(String cameraName, String rtspUrl, String hlsDir) { // 目录不存在先创建否则FFmpeg会直接报错退出 Files.createDirectories(Paths.get(hlsDir)); String m3u8Path hlsDir / cameraName .m3u8; String tsPattern hlsDir / cameraName _%05d.ts; ProcessBuilder pb new ProcessBuilder( ffmpeg, -rtsp_transport, tcp, -i, rtspUrl, -c:v, copy, -c:a, aac, -f, hls, -hls_time, 2, -hls_list_size, 5, -hls_flags, delete_segments, -hls_segment_filename, tsPattern, m3u8Path ); // 合并错误输出流方便排查转流失败原因 pb.redirectErrorStream(true); ffmpegProcess pb.start(); // 用独立线程消费输出日志避免缓冲区阻塞 consumeOutput(ffmpegProcess); } public void stopStream() { if (ffmpegProcess ! null) { ffmpegProcess.destroy(); ffmpegProcess null; } } }这段代码有两点要特别注意。第一pb.redirectErrorStream(true)必须加FFmpeg日志量大不把子进程输出读掉缓冲区一满FFmpeg会阻塞假死。第二stopStream()直接 destroy 进程FFmpeg可能来不及把m3u8索引写完整稳妥做法是先发destroy()等500毫秒再destroyForcibly()兜底。转流进程的启动速度取决于RTSP握手耗时通常1到3秒前端的waiting动画要按这个心理预期来设计。3. Nginx托管HLS流切片目录、MIME类型与跨域配置3.1 HLS的访问链路m3u8主索引和ts分片各干什么FFmpeg切出来的HLS流在磁盘上是两种文件一个m3u8索引文件若干ts切片文件。播放器拿到m3u8后才知道ts切片叫什么名字、按什么顺序播。所以Nginx要做的不是转发什么特殊协议而是把这两个静态文件正确吐给浏览器。这条链路的坑藏在MIME类型上。ts切片如果被Nginx当成application/octet-stream返回浏览器会直接触发下载而不是播放m3u8如果没配application/vnd.apple.mpegurlSafari会显示一个黑屏播放器。这类问题不报错肉眼很难定位。3.2 nginx.conf关键配置段逐行说明资源包里的Nginx是Windows和Linux通用的解压后改配置文件即可。我通常维护一个专门托管流媒体的server块跟业务服务分开避免互相污染。server { listen 8080; server_name localhost; # 托管HLS切片目录FFmpeg输出到这里 location /hls/ { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } alias E:/hls/; add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, HEAD, OPTIONS; add_header Access-Control-Allow-Headers Range; } # 可选限制只允许内网访问 # allow 192.168.0.0/16; # deny all; }location /hls/末尾的斜杠和alias E:/hls/末尾的斜杠是配套关系。请求/hls/cam01.m3u8时Nginx把/hls/前缀剥掉拼上E:/hls/最终指向E:/hls/cam01.m3u8。如果alias漏了末尾斜杠文件路径会拼接错直接404。add_header Cache-Control no-cache给播放器一个明确信号每次都要重新拉m3u8索引不能缓存。直播场景下m3u8是动态更新的缓存机制会直接导致画面停留在几秒前甚至卡死。Access-Control-Allow-Origin *是给跨域播放用的如果你的管理页面和流媒体服务不同端口这一行缺了前端播放器连ts分片都拉不回来。改完配置后执行nginx -t校验语法再nginx -s reload热加载。Windows下注意Nginx目录不能放在路径带空格的目录里否则reload时经常报open() failed的错误。3.3 跨域细节和防盗链配置跨域是HLS播放最隐形的问题。页面在8080端口流媒体在8080端口看起来同源。但前端工程可能跑在http://192.168.1.100:8080Nginx流媒体跑在http://192.168.1.100:8080只要服务端口不同就算跨域。如果不加CORS头ts切片请求会被浏览器拦截报错信息则是No Access-Control-Allow-Origin header is present。防盗链按需配置。内网监控系统一般不设门槛但如果有公网暴露需求可以加上Referer判断location /hls/ { # 只允许指定来源的页面拉流 if ($http_referer !~* your-web-domain.com) { return 403; } }注意防盗链作用有限Referer可以伪造真正安全的做法是给m3u8加鉴权token这个超出资源包范围内网部署不必折腾。4. SSM后端工程怎么编排转流服务接口、状态与前端播放器4.1 SSM架构包里转流服务如何与Spring融合资源包里那个SSM架构包结构是标准的Spring SpringMVC MyBatis分层Controller管接口Service管业务Mapper管数据库。转流服务在这个架构里的定位是摄像头配置持久化到MySQLController接收到播放请求后查配置把rtsp地址传给FFmpeg服务去启动转流进程。摄像头配置表可以按这个结构设计字段类型说明idint primary key auto_increment主键camera_namevarchar(64)摄像头别名同时作为HLS文件名前缀rtsp_urlvarchar(255)RTSP完整地址含用户名密码hls_pathvarchar(255)切片输出目录statustinyint0未启动 1转流中 2失败这样设计的好处是摄像头信息不进代码新加摄像头只需INSERT一条记录不用改工程、不用重新部署。MyBatis的mapper里写标准的insert/selectController层直接复用。4.2 转流接口设计start、stop、status三个操作的返回约定工程里的核心Controller要暴露三个接口前端根据返回状态决定播放器怎么表现。RestController RequestMapping(/api/stream) public class StreamController { Autowired private FfmpegStreamService streamService; // 启动某摄像头的转流 PostMapping(/start) public Result start(RequestBody StreamRequest req) { CameraConfig config cameraMapper.selectByName(req.getCameraName()); if (config null) { return Result.error(摄像头不存在: req.getCameraName()); } boolean ok streamService.startStream( config.getCameraName(), config.getRtspUrl(), config.getHlsPath()); // 把状态回写到数据库便于下次查询 cameraMapper.updateStatus(config.getId(), ok ? 1 : 2); return ok ? Result.success(转流已启动) : Result.error(转流启动失败); } // 查询转流状态 GetMapping(/status) public Result status(String cameraName) { CameraConfig config cameraMapper.selectByName(cameraName); return Result.success(config null ? null : config.getStatus()); } }返回的Result对象是个统一包装结构里面带code、message、data三个字段。前端拿到code 0认为是成功。启动转流后建议轮询status接口看到状态从纯0变成1或2再让播放器去拉m3u8避免用户打开页面时转流还没就绪导致黑屏。一个容易忽略的细节是转流进程必须以cameraName为粒度做单例控制。同一个摄像头配了两次启动会拉起两个FFmpeg进程同时写同一个m3u8文件索引错乱、切片互相覆盖画面必然卡顿。这个校验必须放在Service层入口处。4.3 前端playerJQueryDemovideo.js接入和设备列表切换资源包里的playerJQueryDemo网页核心是利用video.js播放HLS流外层用jQuery控制设备列表切换和播放器状态。播放器初始化部分参照这个写法video idmyPlayer classvideo-js vjs-default-skin controls preloadauto width960 height540>ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 \ -an -c:v copy -f hls \ -hls_time 1 -hls_list_size 3 -hls_flags delete_segmentsindependent_segments \ -hls_segment_filename E:/hls/cam01_%05d.ts \ E:/hls/cam01.m3u8改动有三个。-hls_time 1切片降到1秒前提是摄像头的GOP关键帧间隔也要设为1秒否则FFmpeg想切1秒也切不出来切片边界必须落在IDR帧上。第二加了-an把音频流去掉监控场景经常不关心声音少了音频转码能省一点CPU和起播时间。第三independent_segments告诉播放器每个ts切片从独立关键帧开始拖拽和历史回放时不用依赖前一个切片的数据。还有一个容易被忽略的参数组合-hls_flags delete_segmentsindependent_segments中间用加号连接这是FFmpeg对多个flags的固定语法。分开写或者用逗号都会报Option hls_flags not found之类的解析错误。切片参数之外延迟还受摄像头编码侧影响。把摄像头的GOP设为1秒、码率上限设为4Mbps左右画面清晰度和流畅度都能兼顾。如果是海思方案的老摄像头GOP设1秒可能导致码率波动变大那就把切片时间改为2秒延迟在3到4秒之间也够用。判断延迟有没有达标的方法很直接打开手机秒表对准监控画面再用自己手机上打开播放页面对比两个数字的差值就是端到端延迟。我每次部署完都强制走一遍这个测试低于3秒才算验收通过。从那以后我每次接视频类需求第一件事不是写代码而是先跑一遍这条延迟验收流程确认输入端的编码参数没问题再动工程。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

鸿蒙Navigation实战:路由栈、参数传递与登录守卫

鸿蒙Navigation实战:路由栈、参数传递与登录守卫

搞定了前面三篇的组件基础之后,页面导航是鸿蒙开发里绕不开的一道坎。如果你之前像我一样习惯用 router 那套写页面跳转,项目一复杂就开始别扭:路由字符串满天飞、传参数只能序列化、页面之间根本不知道彼此在干什么。这一篇我不想再停在“能…

2026/10/11 7:56:38 阅读更多 →
K8s部署Sentinel Dashboard 1.8.9:Nacos持久化与踩坑全指南

K8s部署Sentinel Dashboard 1.8.9:Nacos持久化与踩坑全指南

最近在把微服务治理组件往 k8s 上迁移,Sentinel Dashboard 1.8.9 这块前后折腾了几天。标题看起来简单——写个 Deployment 拉起来一个 Java 服务就行,但真正落地的过程里,镜像选哪个、登录鉴权怎么配、Nacos 地址怎么传进去、重启之后规则还…

2026/10/10 3:33:19 阅读更多 →
Linux动态库兼容问题全解析:从SONAME到GLIBC符号版本

Linux动态库兼容问题全解析:从SONAME到GLIBC符号版本

前两天跟一个做运维的朋友聊天,他说最怕的不是程序崩溃,而是程序压根起不来。一个客户现场,部署包已经拷到服务器上,一执行就报cannot open shared object file: libxxx.so.6: No such file or directory。查了一圈,客…

2026/10/10 3:33:19 阅读更多 →

最新新闻

司法拍卖土地数据爬虫:应对反爬、结构突变与双平台适配

司法拍卖土地数据爬虫:应对反爬、结构突变与双平台适配

简介:本资源是一套面向Python初学者与数据采集实践者的司法拍卖信息自动化采集工具,聚焦淘宝、京东两大平台的土地类司法拍卖每日数据抓取需求,适用于潜在竞买人、法律从业者及市场研究人员快速获取标的物名称、位置、起拍价、保证金、拍卖时…

2026/10/11 7:57:06 阅读更多 →
Copilot Code Review接入CI:Balanced模式实战与避坑指南

Copilot Code Review接入CI:Balanced模式实战与避坑指南

上个月我还在一遍一遍地点击同一个按钮:打开 PR、等 Copilot Code Review 在评论区里冒出一串建议、再人工判断哪些要改,哪些只是噪声。直到 GitHub 把这套 Code Review 能力开放成 API,我才终于把它从“网页上的一个功能”变成了“CI 里的一…

2026/10/11 7:57:06 阅读更多 →
Redis 为什么会卡住:fork、大 key、慢命令、AOF fsync 4 个阻塞源逐个排除

Redis 为什么会卡住:fork、大 key、慢命令、AOF fsync 4 个阻塞源逐个排除

我不会起名字322 后端 / 算法 / 数据库 📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL 文章目录Redis 为什么会卡住:fork、大 key、慢命令…

2026/10/11 7:57:06 阅读更多 →
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路

用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路

2026年用 Web VR 引擎搭建元宇宙 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路 2026年,3D 虚拟展厅与 VR 全景线上展会已从概念演示进入可规模化交付的工程阶段。本文面向开发者,围绕 Web VR 引擎这一技术底座,拆解一条完…

2026/10/11 7:57:06 阅读更多 →
MySQL 的脏页什么时候写回:Buffer Pool、redo log 与 4 个刷脏时机

MySQL 的脏页什么时候写回:Buffer Pool、redo log 与 4 个刷脏时机

我不会起名字322 后端 / 算法 / 数据库 📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL 文章目录MySQL 的脏页什么时候写回:Buffer Pool、…

2026/10/11 7:57:06 阅读更多 →
宁夏职业院校学工信息管理系统怎么选?本地需求特点要注意什么?

宁夏职业院校学工信息管理系统怎么选?本地需求特点要注意什么?

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

2026/10/11 7:56:05 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →