ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RTSP转HLS实战:FFmpeg+Nginx+SSM实现浏览器监控播放

RTSP转HLS实战:FFmpeg+Nginx+SSM实现浏览器监控播放 简介本资源面向Java后端初学者与流媒体实时预览需求者提供一套基于SSM架构、Nginx与FFmpeg将RTSP流转换为HLS流并在前端HTML播放的完整可运行方案适用于视频监控、在线教育、直播等场景的入门实践。压缩包共52个文件约70.26MB包含FFmpeg与Nginx的Windows安装包、SSM架构Java代码、nginx.conf配置、playerJQueryDemo播放器网页以及png截图、css样式、js脚本、html页面和说明文档覆盖从服务端转流到前端播放的完整链路。资源中附有转流命令、RTSP调试源与部署必读说明便于对照搭建环境、理解FFmpeg转码与Nginx分发HLS的协作方式并借助播放器插件快速验证效果。目前已有260人学习适合希望亲手跑通RTSP转HLS并实现网页播放的读者参考。1. 从 RTSP 到 HLS为什么监控画面在浏览器里总是一片空白很多做安防可视化或者物联网平台的朋友都遇到过这个场景手头有一台海康威视或者臻识科技的摄像头RTSP 拉流地址在 VLC 里跑得好好的但一到前端 HTML 页面上就抓瞎——浏览器原生根本不认rtsp://协议。这不是代码写错了而是浏览器厂商出于安全和插件生态的考虑早就把 RTSP 支持从内核里踢出去了。于是「RTSP 转 HLS」成了国内视频监控 Web 化最稳、最省成本的一条落地路径。这套方案的核心思路很直接用 FFmpeg 把摄像头的 RTSP 流实时切成 HLS 切片.m3u8.tsNginx 负责把这些静态切片以 HTTP 方式高效吐给浏览器SSMSpring SpringMVC MyBatis架构负责管理摄像头配置、通道信息和切片任务的生命周期前端用 playerJQueryDemo 这类基于 video.js 或 hls.js 的播放器加载播放。它适合谁适合那些不想引入 WebRTC 或 SRS 等重型流媒体服务器、只想在现有 Java Web 项目里快速把监控画面塞进页面的中小团队。整套东西不依赖云服务本地就能跑通硬件成本几乎为零。2. 拆解 RTSP 转 HLS 的链路FFmpeg 切片、Nginx 分发、SSM 调度各管什么2.1 为什么选 HLS 而不是 RTSP 转 FLV 或 WebRTC在动手之前先把选型逻辑理清楚。RTSP 转 FLV 确实延迟更低但依赖 flv.js 且对 H.265 支持很差RTSP 转 WebRTC 延迟能压到 500ms 以内但需要信令服务器和 STUN/TURN架构复杂度直接翻倍。HLS 的优势在于基于 HTTPNginx 天然就是干这个的切片是静态文件SSM 只需要管理文件目录和进程浏览器兼容性最好安卓端用 Media3 ExoPlayer 播放 HLS 也是原生支持。代价是延迟。HLS 的延迟由切片时长和播放列表长度决定默认配置下 6 到 10 秒是常态。如果你的场景是实时操控比如云台控制这个延迟不可接受但如果是常规监控预览、回放完全够用。我一般会跟团队说先想清楚业务能容忍多少延迟再决定要不要上 HLS。2.2 FFmpeg 切片命令的每个参数都要能解释清楚FFmpeg 是整个链路里最容易翻车的一环。下面这条命令是我在 Windows 和 Linux 上都验证过的稳定版本ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 \ -c:v libx264 -preset ultrafast -tune zerolatency \ -c:a aac -ar 44100 -ac 1 \ -f hls -hls_time 4 -hls_list_size 6 \ -hls_flags delete_segmentsappend_list \ -hls_segment_filename /usr/local/nginx/html/hls/cam01_%03d.ts \ /usr/local/nginx/html/hls/cam01.m3u8逐段说明-rtsp_transport tcp强制走 TCP避免 UDP 丢包导致的花屏这是海康、大华摄像头接入时的血泪经验-preset ultrafast -tune zerolatency牺牲压缩率换编码速度防止切片生成跟不上-hls_time 4表示每个 TS 切片 4 秒配合-hls_list_size 6意味着播放列表里保留 6 个切片理论延迟约 24 秒实际播放器会追到最新切片体感延迟 8 到 12 秒delete_segments自动清理旧切片否则磁盘几天就满。提示如果摄像头是 H.265 编码-c:v libx264会触发转码CPU 占用飙升。建议在摄像头后台把子码流改成 H.264或者用-c:v copy直接复制流但前提是浏览器能解 H.265——目前 Chrome 对 H.265 的 HLS 支持仍然不完整。2.3 Nginx 配置让 m3u8 和 ts 文件被正确识别与缓存Nginx 在这里的角色是静态文件服务器但有两个坑必须提前填。第一MIME 类型。默认 Nginx 的mime.types里没有.m3u8和.ts浏览器拿到application/octet-stream会拒绝播放。第二CORS。如果前端页面和 HLS 文件不同源必须加跨域头。server { listen 8080; server_name localhost; location /hls/ { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /usr/local/nginx/html; add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET, OPTIONS; } }Cache-Control no-cache对 m3u8 是必须的否则浏览器会缓存播放列表导致画面卡住不更新ts 切片可以设长缓存但为了简单统一 no-cache 也没问题。Access-Control-Allow-Origin *在生产环境建议改成具体域名。2.4 SSM 层要管的三件事通道注册、进程守护、切片清理SSM 架构在这里不是打酱油的。它至少承担三个职责第一摄像头通道的增删改查把 RTSP 地址、用户名密码、HLS 输出路径存到 MySQL第二FFmpeg 进程的启动与守护通过 Java 的ProcessBuilder拉起命令并定时检查进程是否存活第三切片目录的定期清理防止磁盘写满。// 启动 FFmpeg 进程的核心逻辑 public Process startFfmpeg(String rtspUrl, String outputPath) throws IOException { ListString command new ArrayList(); command.add(ffmpeg); command.add(-rtsp_transport); command.add(tcp); command.add(-i); command.add(rtspUrl); command.add(-c:v); command.add(libx264); command.add(-preset); command.add(ultrafast); command.add(-tune); command.add(zerolatency); command.add(-f); command.add(hls); command.add(-hls_time); command.add(4); command.add(-hls_list_size); command.add(6); command.add(-hls_flags); command.add(delete_segments); command.add(outputPath); ProcessBuilder pb new ProcessBuilder(command); pb.redirectErrorStream(true); pb.redirectOutput(ProcessBuilder.Redirect.appendTo(new File(outputPath .log))); return pb.start(); }这段代码的关键点redirectErrorStream(true)把 FFmpeg 的 stderr 合并到 stdout方便排查报错日志追加到独立文件出问题时能回溯。进程守护可以用 Spring 的Scheduled每 30 秒检查一次process.isAlive()不存活就重启。3. 从零搭一套可播放的环境安装包选择、目录规划与最小验证3.1 FFmpeg 安装包怎么选Windows 用 essentialsLinux 用包管理Windows 下直接去 FFmpeg 官网下载ffmpeg-master-latest-win64-gpl.zip解压后把bin目录加到系统 PATH。注意不要下essentials版本那个缺少某些编码器。Linux 下更简单Ubuntu/Debian 用apt install ffmpegAlmaLinux 9 需要先启用 EPEL 和 RPM Fusion# AlmaLinux 9 安装 FFmpeg sudo dnf install -y epel-release sudo dnf install -y --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-9.noarch.rpm sudo dnf install -y ffmpeg ffmpeg-devel安装完用ffmpeg -version验证能看到libx264和aac就说明编码器齐全。3.2 Nginx 安装Windows 直接解压Linux 编译或包管理Windows 版 Nginx 从官网下 zip 包解压到C:\nginx双击nginx.exe启动。Linux 下如果只是做静态文件服务apt install nginx或dnf install nginx足够需要额外模块才编译安装。安装后把上面 2.3 节的location /hls/配置加到nginx.conf的server块里nginx -s reload生效。3.3 目录规划与最小验证先让一个切片文件能被浏览器播出来在 Nginx 的 html 目录下建hls文件夹手动放一个测试用的test.m3u8和对应的test0.ts然后用浏览器访问http://localhost:8080/hls/test.m3u8。如果浏览器直接下载文件而不是播放说明 MIME 类型没配好如果 404检查root路径。这一步过了再跑 FFmpeg 命令生成真实切片。!-- playerJQueryDemo 最小播放页面 -- !DOCTYPE html html head meta charsetutf-8 titleHLS 播放测试/title link hrefhttps://vjs.zencdn.net/8.6.1/video-js.css relstylesheet /head body video idmy-video classvideo-js controls preloadauto width800 height450 source srchttp://localhost:8080/hls/cam01.m3u8 typeapplication/x-mpegURL /video script srchttps://vjs.zencdn.net/8.6.1/video.min.js/script script var player videojs(my-video); player.play(); /script /body /htmlvideo.js 会自动检测浏览器是否原生支持 HLS不支持则加载 hls.js。如果页面黑屏但控制台无报错大概率是切片还没生成完等 10 秒再刷新。4. 避坑与排查RTSP 转 HLS 最常见的 5 个翻车现场4.1 现象FFmpeg 启动后立刻退出日志显示 “Connection refused”原因RTSP 地址错误或摄像头未开启对应通道。海康的 RTSP 地址格式是rtsp://用户名:密码IP:554/Streaming/Channels/101其中101表示通道 1 主码流102是子码流。臻识科技的车牌相机地址格式不同通常是rtsp://IP:554/stream1。解决先用 VLC 的「打开网络串流」测试地址确认能播再交给 FFmpeg。4.2 现象切片文件生成了但浏览器播放几秒就卡住原因-hls_list_size设置过小播放器还没加载完旧切片就被删了。解决把-hls_list_size调到 6 以上或者加-hls_flags append_list让播放列表追加而不是覆盖。另外检查 Nginx 的Cache-Controlm3u8 必须 no-cache。4.3 现象CPU 占用 100%画面严重卡顿原因摄像头输出 H.265FFmpeg 在做软解转 H.264。解决登录摄像头后台把视频编码改成 H.264如果必须用 H.265考虑用支持硬件加速的 FFmpeg 编译版本加-hwaccel cuda或-hwaccel vaapi。4.4 现象多个摄像头同时转流时部分通道切片断流原因FFmpeg 进程数过多CPU 或网络带宽瓶颈。解决限制并发转流路数或者把-preset从ultrafast改成veryfast降低 CPU 占用网络方面RTSP 走 TCP 时每路流约占用 2 到 4 Mbps千兆网卡理论上限约 200 路但实际建议不超过 50 路。4.5 现象Linux 下 FFmpeg 进程变成僵尸进程SSM 无法重启原因Java 的ProcessBuilder启动的进程在父进程异常退出后没有被回收。解决在 SSM 里用process.destroyForcibly()强制销毁并注册Runtime.getRuntime().addShutdownHook()在应用关闭时清理所有 FFmpeg 进程。5. 进阶技巧用 SSM 定时任务做切片健康检查与自动恢复前面讲的都是「能跑」这一章讲「跑得稳」。生产环境里FFmpeg 进程可能因为网络抖动、摄像头重启、磁盘满等原因挂掉靠人工重启不现实。我的做法是在 SSM 里写一个定时任务每 30 秒扫描一次所有通道的 m3u8 文件最后修改时间如果超过 30 秒没更新就判定为断流自动杀掉旧进程并重启。Scheduled(fixedDelay 30000) public void checkHlsHealth() { ListCameraChannel channels channelMapper.selectAllActive(); for (CameraChannel ch : channels) { File m3u8 new File(ch.getOutputPath()); if (!m3u8.exists() || System.currentTimeMillis() - m3u8.lastModified() 30000) { log.warn(通道 {} 切片超时未更新准备重启, ch.getId()); ffmpegService.restart(ch); } } }这个逻辑的关键参数是 30000 毫秒的阈值。设太小会误判——比如摄像头关键帧间隔是 4 秒切片生成有波动设太大则恢复慢。我一般设成hls_time * 5即 4 秒切片对应 20 秒阈值留一点余量。另一个技巧是给每个通道加一个「心跳文件」。FFmpeg 命令里加-hls_flags delete_segmentstemp_file让切片先写临时文件再原子重命名避免播放器读到写了一半的文件。同时 SSM 在启动 FFmpeg 时记录进程 PID 到数据库重启时先根据 PID 杀旧进程防止端口和文件句柄泄漏。验证方法很简单手动kill掉一个 FFmpeg 进程观察 30 秒内 SSM 日志是否打印重启记录浏览器画面是否在 1 分钟内恢复。如果恢复不了检查restart方法里有没有先调用destroyForcibly再waitFor顺序错了会卡死。最后说个我自己的习惯每次上线新摄像头先用 FFmpeg 命令行手动跑 10 分钟盯着日志看有没有Non-monotonous DTS或Packet corrupt的警告。这些警告在测试时不起眼但上线后就是画面卡顿的元凶。希望帮到你。本文还有配套的精品资源点击获取
返回列表