简介本资源面向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.jsNginx 就能当分发服务器不需要额外流媒体服务。代价是延迟——标准 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:password192.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_segmentsappend_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?channel1subtype0这个要查设备手册。-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_segmentsappend_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 的标准 MIMEvideo/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 是摄像头 IDvalue 是 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; ListString 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_segmentsappend_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 跑 FFmpegNginx 用 nobody 跑那目录权限要设成 777 或者把两者用户统一。生产环境不建议 777统一用户更安全。3.3 SSM 项目部署与接口联调SSM 架构包是标准的 Maven 项目导入 IDE 后改jdbc.properties里的数据库连接执行schema.sql建表然后mvn tomcat7:run或部署到 Tomcat。核心接口就两个/stream/start?cameraIdxxx启动转码/stream/stop?cameraIdxxx停止。启动后访问http://localhost:8080/stream/start?cameraIdcam01然后去 HLS 目录看有没有生成cam01.m3u8和对应的 ts 文件。有文件生成说明 FFmpeg 转码正常没有的话看 SSM 日志里 FFmpeg 的输出。3.4 前端播放页hls.js 还是原生 videoplayerJQueryDemo 网页用的是 hls.js 方案。Safari 原生支持 HLS直接video srcxxx.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 文件没有读权限检查目录权限和 SELinuxLinux 下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软编本身吃 CPUultrafast已经是最低档了。解决方案有三个一是换硬件编码Intel 核显用h264_qsvNVIDIA 显卡用h264_nvenc把-c:v libx264换成对应编码器CPU 占用能降 70% 以上二是降低分辨率和帧率加-vf scale640: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 的代码。顺序反了的话出了问题根本分不清是转码、分发还是业务层的锅。希望帮到你。本文还有配套的精品资源点击获取