简介:本资源面向Java后端初学者与流媒体实时预览爱好者,提供一套基于SSM架构整合Nginx与FFmpeg、将RTSP流转换为HLS流并在前端HTML页面播放的完整可运行方案,覆盖视频监控、在线教育、直播等实时预览场景。压缩包共52个文件,约70.26MB,包含FFmpeg与Nginx安装包、SSM架构Java代码、playerJQueryDemo播放器网页,以及png截图、css样式、js脚本、html页面、jsp登录页、nginx.conf配置和hosts文件等,便于按模块对照搭建环境。资源内附转流命令、调试源与部署必读说明,可帮助读者快速跑通从RTSP拉流、FFmpeg转码到Nginx分发HLS、前端播放的完整链路,并理解各组件配置要点与常见排错思路。目前已有262人学习下载,适合希望亲手实践流媒体服务搭建的小白参考。
1. 从 RTSP 到 HLS:一套能跑起来的视频流转码与播放方案
监控摄像头、工业相机、无人机图传,这些设备吐出来的大多是 RTSP 流。RTSP 本身是给实时传输设计的,延迟低,但浏览器原生不支持——你没法在<video>标签里直接塞一个rtsp://地址。想让它出现在网页上,中间必须有人做协议转换。这套资源包干的就是这件事:用 FFmpeg 把 RTSP 拉过来切成 HLS 分片,Nginx 负责把分片和播放列表吐给前端,SSM 架构做业务层的流管理和接口暴露,最后用一个 jQuery 写的播放页把流播出来。包里带了 FFmpeg 安装包、Nginx 安装包、SSM 架构包和一个 playerJQueryDemo 网页,基本是开箱即用的组合。适合谁?做安防监控平台、做设备远程查看、做工业视觉上位机的后端和全栈同学。如果你正在被「摄像头画面怎么上网页」这个问题卡住,这套东西能帮你把链路先跑通,再谈优化。
2. 链路拆解:FFmpeg 转码、Nginx 分发、SSM 调度各管什么
2.1 为什么是 HLS 而不是 RTMP 或 WebRTC
浏览器能播的视频流方案就那么几种。RTMP 需要 Flash,已经死透了;WebRTC 延迟最低,但服务端要处理 ICE、STUN、DTLS 一堆信令,部署复杂度高,而且对多路并发的资源消耗不小;HLS 基于 HTTP,切片传输,浏览器原生支持(Safari 直接播,Chrome 靠 hls.js),Nginx 就能当分发服务器,不需要额外流媒体服务。代价是延迟——标准 HLS 切片 10 秒一个,延迟通常在 15 到 30 秒。如果你的场景是「看实时画面做精确操作」,HLS 不合适;如果是「远程查看、录像回放、多路轮巡」,HLS 的延迟完全可以接受。这套资源选 HLS,走的就是兼容性和部署成本优先的路线。
FFmpeg 在这里的角色是转码器。它从 RTSP 源拉流,解码后重新编码成 H.264,再封装成 MPEG-TS 分片,同时生成.m3u8索引文件。Nginx 不需要任何流媒体模块,只要把 FFmpeg 输出的目录挂到 HTTP 路径下,就是一个 HLS 分发服务。SSM 架构负责的是业务层——管理哪些摄像头要转码、转码进程的启停、流地址的生成和鉴权。三层各司其职,耦合度低,任何一层出问题都好排查。
2.2 FFmpeg 转码命令的参数逻辑
先看核心命令。这是整个链路里最关键的一行,参数不对后面全白搭:
ffmpeg -rtsp_transport tcp \ -i "rtsp://admin:password@192.168.1.100:554/stream1" \ -c:v libx264 \ -preset ultrafast \ -tune zerolatency \ -c:a aac \ -ar 44100 \ -b:a 64k \ -f hls \ -hls_time 4 \ -hls_list_size 6 \ -hls_flags delete_segments+append_list \ -hls_segment_filename "/usr/local/nginx/html/hls/stream_%03d.ts" \ /usr/local/nginx/html/hls/stream.m3u8逐段说。-rtsp_transport tcp强制用 TCP 拉流,UDP 在丢包网络下会花屏,TCP 稳但延迟略高,内网环境建议 TCP。-i后面是 RTSP 地址,格式通常是rtsp://用户名:密码@IP:端口/路径,不同厂商的路径不一样,海康是/Streaming/Channels/101,大华是/cam/realmonitor?channel=1&subtype=0,这个要查设备手册。-c:v libx264指定视频编码器,-preset ultrafast是编码速度优先,画质会降但 CPU 占用低,适合多路并发;如果路数少、CPU 富裕,可以换成veryfast或faster。-tune zerolatency关掉编码器的帧缓冲,降低延迟。音频部分-c:a aac转成 AAC,-ar 44100采样率,-b:a 64k码率——如果摄像头没有音频,这两行可以去掉,否则 FFmpeg 会因为找不到音频流报错。
HLS 参数是重点。-hls_time 4表示每个 TS 分片 4 秒,比默认的 10 秒延迟低,但分片多了 HTTP 请求也多。-hls_list_size 6表示 m3u8 里保留 6 个分片,配合 4 秒切片就是 24 秒的窗口。-hls_flags delete_segments+append_list让 FFmpeg 自动删除旧分片并追加列表,不加这个磁盘会被写满。-hls_segment_filename指定分片命名规则,%03d是三位序号。最后是 m3u8 的输出路径。
注意:输出目录必须提前创建,FFmpeg 不会自动建目录。另外 Nginx 的 worker 进程用户要对这个目录有写权限,否则分片写不进去,前端一直转圈。
2.3 Nginx 配置:让 m3u8 和 ts 能被正确访问
Nginx 这边配置不复杂,但有两个坑:MIME 类型和跨域。默认 Nginx 的 mime.types 里没有 m3u8 和 ts 的映射,浏览器拿到application/octet-stream可能不认。在http块或server块里加上:
types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } server { listen 80; server_name localhost; location /hls/ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, OPTIONS'; add_header Cache-Control no-cache; alias /usr/local/nginx/html/hls/; } }application/vnd.apple.mpegurl是 m3u8 的标准 MIME,video/mp2t是 TS 分片的。Access-Control-Allow-Origin *解决跨域——如果你的播放页和 HLS 不在同一个域名或端口下,不加这行浏览器会拦。Cache-Control no-cache让浏览器每次都拉最新的 m3u8,不然直播流会卡在旧列表上。alias指向 FFmpeg 的输出目录,注意 alias 和 root 的区别:alias 是替换 location 匹配的部分,root 是拼接,这里用 alias 更直观。
2.4 SSM 层怎么管流转码
SSM 架构在这套方案里不是必须的,但如果你要做多路摄像头管理,它就有价值了。核心思路是用 Java 的ProcessBuilder启动 FFmpeg 进程,把进程对象存到一个 Map 里,key 是摄像头 ID,value 是 Process。启动转码的 Service 方法大概长这样:
public boolean startStream(String cameraId, String rtspUrl) { String outputPath = "/usr/local/nginx/html/hls/" + cameraId + ".m3u8"; String segmentPath = "/usr/local/nginx/html/hls/" + cameraId + "_%03d.ts"; List<String> cmd = Arrays.asList( "ffmpeg", "-rtsp_transport", "tcp", "-i", rtspUrl, "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", "-f", "hls", "-hls_time", "4", "-hls_list_size", "6", "-hls_flags", "delete_segments+append_list", "-hls_segment_filename", segmentPath, outputPath ); try { ProcessBuilder pb = new ProcessBuilder(cmd); pb.redirectErrorStream(true); Process process = pb.start(); processMap.put(cameraId, process); return true; } catch (IOException e) { log.error("启动转码失败: {}", cameraId, e); return false; } }ProcessBuilder把命令和参数拆成 List,避免拼接字符串带来的空格转义问题。redirectErrorStream(true)把 stderr 合并到 stdout,方便统一读日志。processMap用ConcurrentHashMap存,因为可能有多线程并发调用。停止转码就是process.destroy(),但要注意 FFmpeg 收到 SIGTERM 后会先写完当前分片再退出,不会立刻死。如果你要强制杀,用destroyForcibly(),但可能留下不完整的 TS 文件。
3. 从零跑通:安装、配置、启动的完整步骤
3.1 FFmpeg 安装与验证
资源包里带了 FFmpeg 安装包,Windows 下是解压即用的绿色版,Linux 下是编译好的二进制。Windows 把bin目录加到 PATH 环境变量,Linux 把二进制拷到/usr/local/bin/并chmod +x。验证:
ffmpeg -version能输出版本号和编译配置就说明装好了。重点看编译配置里有没有--enable-libx264,没有的话 H.264 编码用不了,只能软编或换编码器。如果缺,Linux 下用包管理器装libx264-dev后重新编译,或者直接下静态编译版。
3.2 Nginx 安装与 HLS 目录准备
Nginx 安装包同样分 Windows 和 Linux 版本。Linux 下解压后./configure --prefix=/usr/local/nginx,然后make && make install。Windows 下解压到任意目录,双击nginx.exe启动。启动前先把 HLS 输出目录建好:
mkdir -p /usr/local/nginx/html/hls chmod 755 /usr/local/nginx/html/hls权限给 755,确保 Nginx 的 worker 用户能读、FFmpeg 能写。如果你用 root 跑 FFmpeg,Nginx 用 nobody 跑,那目录权限要设成 777 或者把两者用户统一。生产环境不建议 777,统一用户更安全。
3.3 SSM 项目部署与接口联调
SSM 架构包是标准的 Maven 项目,导入 IDE 后改jdbc.properties里的数据库连接,执行schema.sql建表,然后mvn tomcat7:run或部署到 Tomcat。核心接口就两个:/stream/start?cameraId=xxx启动转码,/stream/stop?cameraId=xxx停止。启动后访问http://localhost:8080/stream/start?cameraId=cam01,然后去 HLS 目录看有没有生成cam01.m3u8和对应的 ts 文件。有文件生成说明 FFmpeg 转码正常,没有的话看 SSM 日志里 FFmpeg 的输出。
3.4 前端播放页:hls.js 还是原生 video
playerJQueryDemo 网页用的是 hls.js 方案。Safari 原生支持 HLS,直接<video src="xxx.m3u8">就行;Chrome 和 Firefox 需要 hls.js 做 MSE 封装。核心代码:
var video = document.getElementById('video'); var videoSrc = 'http://localhost/hls/cam01.m3u8'; if (Hls.isSupported()) { var hls = new Hls({ liveSyncDurationCount: 3, liveMaxLatencyDurationCount: 10 }); hls.loadSource(videoSrc); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, function() { video.play(); }); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = videoSrc; video.addEventListener('loadedmetadata', function() { video.play(); }); }liveSyncDurationCount: 3表示播放器保持落后直播边缘 3 个分片的距离,配合 4 秒切片就是 12 秒延迟。liveMaxLatencyDurationCount: 10是最大延迟容忍,超过就跳帧追上去。这两个参数是延迟和流畅度的平衡点,调小延迟低但容易卡,调大流畅但延迟高。MANIFEST_PARSED事件里调play()是因为浏览器自动播放策略——没有用户交互时play()可能被拦,实际部署时加个「点击播放」按钮更稳。
4. 避坑排查:转码失败、播放卡顿、延迟飙高的常见原因
4.1 FFmpeg 启动就退出,日志报「Connection refused」
现象是 SSM 调启动接口后进程秒退,日志里 FFmpeg 输出Connection refused或401 Unauthorized。原因通常是 RTSP 地址不对或鉴权失败。先单独在命令行跑一遍 FFmpeg 命令,确认地址能拉流。如果命令行能拉、Java 里不行,检查ProcessBuilder的参数有没有被 shell 转义——Java 不走 shell,&、?这些字符不需要转义,但空格分隔的参数必须严格按 List 传。另外有些摄像头限制并发连接数,同一路流被拉两次会拒绝,确认没有残留的 FFmpeg 进程。
4.2 前端一直转圈,m3u8 请求 404
现象是播放页加载后 video 元素一直缓冲,F12 看网络请求 m3u8 返回 404。原因通常是 Nginx 的 alias 路径和 FFmpeg 输出路径不一致。检查 Nginx 配置里location /hls/的 alias 是否指向 FFmpeg 的-hls_segment_filename所在目录。另一个可能是 Nginx 没配 m3u8 的 MIME 类型,浏览器拿到application/octet-stream不解析。在nginx.conf的http块里确认 types 配置生效,改完nginx -s reload。
4.3 播放几秒就卡住,ts 分片加载失败
现象是画面播几秒后卡死,控制台报 ts 文件 404 或 403。404 通常是delete_segments把旧分片删了但 m3u8 还没更新,播放器请求了已删除的分片。把-hls_list_size调大,比如从 6 调到 10,给播放器更多缓冲窗口。403 是权限问题,Nginx 的 worker 用户对 ts 文件没有读权限,检查目录权限和 SELinux(Linux 下getenforce看是否 Enforcing,是的话setenforce 0临时关闭测试)。
4.4 延迟越来越大,画面越播越落后
现象是刚开始延迟十几秒,播了半小时后延迟涨到几分钟。原因是播放器没有追帧机制,或者liveMaxLatencyDurationCount设太大。hls.js 默认会在延迟超过阈值时跳帧,但如果 m3u8 的EXT-X-MEDIA-SEQUENCE没更新,播放器不知道直播边缘在哪。检查 FFmpeg 的-hls_flags有没有加append_list,没有的话每次重写 m3u8 会导致序列号重置。另外-hls_time别设太小,2 秒以下分片过多,HTTP 开销大反而容易堆积。
4.5 多路并发时 CPU 跑满,画面掉帧
现象是同时转 4 路以上时 CPU 100%,所有画面都开始卡顿掉帧。原因是libx264软编本身吃 CPU,ultrafast已经是最低档了。解决方案有三个:一是换硬件编码,Intel 核显用h264_qsv,NVIDIA 显卡用h264_nvenc,把-c:v libx264换成对应编码器,CPU 占用能降 70% 以上;二是降低分辨率和帧率,加-vf scale=640:360 -r 15;三是用摄像头自带的子码流,子码流通常是 640x480 或 704x576,码率低,转码压力小。
5. 进阶技巧:用硬件编码和分片预热把延迟压到 5 秒以内
标准 HLS 的延迟瓶颈在切片时长和播放器缓冲策略。把-hls_time从 4 秒降到 2 秒,liveSyncDurationCount从 3 降到 2,理论延迟能到 4 到 6 秒。但切片太小会导致 HTTP 请求数翻倍,Nginx 的keepalive要开起来,不然每次请求都新建 TCP 连接,开销比省下的延迟还大。在 Nginx 的location /hls/里加:
keepalive_timeout 65; keepalive_requests 1000;硬件编码是另一个关键优化。以 Intel 核显为例,把 FFmpeg 命令里的-c:v libx264 -preset ultrafast -tune zerolatency换成:
-c:v h264_qsv -preset veryfast -look_ahead 0h264_qsv是 Intel Quick Sync Video 的编码器,-look_ahead 0关掉前瞻缓冲降延迟。NVIDIA 平台换成-c:v h264_nvenc -preset llhq -tune ll。硬件编码的画质在低码率下不如软编,但监控场景对画质要求不高,流畅度和 CPU 占用更重要。实测同一台机器,软编 4 路 1080p 就满载,硬编能跑到 12 路以上。
还有一个容易被忽略的点:FFmpeg 启动后的前几秒是没有分片可播的,播放器会一直等第一个 m3u8 生成。如果摄像头出流慢,这个等待可能到 10 秒以上。可以在 SSM 层做个预热——启动转码后轮询 HLS 目录,等 m3u8 文件出现且包含至少 2 个分片再返回前端播放地址。这样用户点播放时立刻有画面,体验上感知的延迟就低了很多。
从那以后我每次部署这套链路,都会先在命令行单独跑一遍 FFmpeg 确认拉流正常,再配 Nginx,最后才动 SSM 的代码。顺序反了的话,出了问题根本分不清是转码、分发还是业务层的锅。希望帮到你。
本文还有配套的精品资源,点击获取