springboot+vue+ ffmpeg 实现视频的拉流播放

浏览器无法直接播放 RTSP/RTMP 等监控流协议,典型做法是:Spring Boot 调用服务器上安装的 FFmpeg,把拉到的流转成 HLS(.m3u8+.ts),Spring Boot 静态资源暴露地址,Vue 前端用 video.js 或 hls.js 播放。下面给你完整可落地的示例。

一、整体架构

bash 复制代码
摄像头RTSP → FFmpeg(转HLS) → 输出目录(/hls/xxx.m3u8 + .ts)
                ↑
         Spring Boot ProcessBuilder 启动/停止
                ↓
      Spring Boot 静态资源映射 → http://ip:port/hls/xxx.m3u8
                ↓
          Vue + video.js / hls.js 播放 HLS

⚠️ 建议生产环境用 Nginx 托管 HLS 目录并做跨域,Spring Boot 只负责启停 FFmpeg 进程和接口。

二、服务端 --- Spring Boot 调用 FFmpeg

1. FFmpeg 转 HLS 核心命令

perl 复制代码
ffmpeg -rtsp_transport tcp \
  -i "rtsp://admin:密码@192.168.1.100:554/stream1" \
  -c:v libx264 -preset ultrafast -tune zerolatency \
  -c:a aac \
  -f hls \
  -hls_time 2 \
  -hls_list_size 5 \
  -hls_flags delete_segments \
  -hls_segment_filename "/opt/hls/stream_%03d.ts" \
  /opt/hls/stream.m3u8
  • -rtsp_transport tcp:比 UDP 稳定,防花屏

  • hls_time 2:每片 2 秒,延迟约 4~8 秒属正常

  • delete_segments:自动删旧切片防磁盘爆满

2. FFmpeg 服务类(启停进程)

java 复制代码
@Service
public class FfmpegService {

    private Process ffmpegProcess;
    private static final String HLS_DIR = "/opt/hls/";
    // Windows 改成 "C:\\ffmpeg\\bin\\ffmpeg.exe"

    public synchronized void startStream(String rtspUrl) throws IOException {
        stopStream(); // 先停旧进程

        new File(HLS_DIR).mkdirs();

        List<String> cmd = new ArrayList<>();
        cmd.add("ffmpeg");
        cmd.addAll(List.of("-rtsp_transport", "tcp"));
        cmd.addAll(List.of("-i", rtspUrl));
        cmd.addAll(List.of("-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency"));
        cmd.addAll(List.of("-c:a", "aac"));
        cmd.addAll(List.of("-f", "hls", "-hls_time", "2", "-hls_list_size", "5"));
        cmd.addAll(List.of("-hls_flags", "delete_segments"));
        cmd.addAll(List.of("-hls_segment_filename", HLS_DIR + "stream_%03d.ts"));
        cmd.add(HLS_DIR + "stream.m3u8");

        ProcessBuilder pb = new ProcessBuilder(cmd);
        pb.redirectErrorStream(true);
        ffmpegProcess = pb.start();

        // 异步消费 FFmpeg 输出,防止阻塞
        new Thread(() -> {
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(ffmpegProcess.getInputStream()))) {
                while (br.readLine() != null) {}
            } catch (IOException ignored) {}
        }).start();
    }

    public synchronized void stopStream() {
        if (ffmpegProcess != null && ffmpegProcess.isAlive()) {
            ffmpegProcess.destroyForcibly();
        }
    }
}

3. Controller + 静态资源映射

typescript 复制代码
@RestController
@RequestMapping("/api/stream")
public class StreamController {
    @Autowired
    private FfmpegService ffmpegService;

    @PostMapping("/start")
    public String start(@RequestParam String rtsp) throws IOException {
        ffmpegService.startStream(rtsp);
        return "started";
    }

    @PostMapping("/stop")
    public String stop() {
        ffmpegService.stopStream();
        return "stopped";
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 访问 http://ip:8080/hls/stream.m3u8
        registry.addResourceHandler("/hls/**")
                .addResourceLocations("file:/opt/hls/");
    }
}

三、前端 --- Vue 播放 HLS

安装依赖

css 复制代码
npm install video.js hls.js

Vue 3 组件示例

xml 复制代码
<template>
  <div>
    <video ref="videoEl" id="my-video" class="video-js vjs-default-skin"
           controls width="800" height="450">
    </video>
    <button @click="play">开始播放</button>
  </div>
</template>

<script setup>
import { ref, onBeforeUnmount } from 'vue'
import videojs from 'video.js'
import 'video.js/dist/video-js.css'

const videoEl = ref(null)
let player = null

const play = () => {
  if (player) player.dispose()
  player = videojs(videoEl.value, {
    sources: [{
      src: 'http://localhost:8080/hls/stream.m3u8',
      type: 'application/x-mpegURL'
    }]
  })
}

onBeforeUnmount(() => {
  player?.dispose()
})
</script>

Safari 原生支持 HLS,Chrome/Firefox 靠 video.js 内部集成 hls.js 自动处理。

四、常见问题排查

问题

原因与解决

播放器 404

HLS 目录未被 Spring 静态资源映射或 Nginx 未配 alias

能下载 m3u8 但播不出

.ts 切片路径不对,确认 segment_filename 在同一目录

花屏/断流

RTSP 改用 -rtsp_transport tcp,检查网络

多路流 CPU 飙高

FFmpeg 转码耗 CPU,可用 -c:v copy(不重编码,需源流 H.264+AAC)或用 ZLMediaKit/SRS 替代

CORS 报错

Spring Boot 加 CORS 或 Nginx 加 Access-Control-Allow-Origin *

五、进阶建议

  • 轻量场景:上述 FFmpeg + Spring Boot 够用

  • 多路监控/低延迟 :推荐用 ZLMediaKit​ 或 **SRS(Simple Realtime Server)**​ 做流媒体服务器,Spring Boot 只做信令控制,Vue 播放 HTTP-FLV 或 WebRTC,延迟可压到 1 秒内

相关推荐
PedroQue992 小时前
uni-router v1.8.0新增冷启动守卫补执行
前端·uni-app
xiaok2 小时前
部署之后,本地浏览器还在读取旧缓存导致页面一直显示loading中
前端
用户059540174462 小时前
Redis缓存一致性踩坑实录:线上故障排查6小时,我用pytest+内存快照把它永久关进了笼子
前端·css
星栈2 小时前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:第一版先把列表和详情跑通
前端·rust·前端框架
用户1733598075372 小时前
Vue 3 SPA 首屏优化:从 3s 到 1.2s 的 5 个实践
前端·vue.js
咖啡无伴侣2 小时前
基础骨架:30 分钟搭好 pnpm workspace,完成双项目 Monorepo 迁入
前端
谷无姜2 小时前
Webpack5 进阶思考:那些官方文档没讲清楚的事
前端·webpack
weedsfly2 小时前
还在用 Axios?你可能需要重新理解 XHR 与 Fetch
前端·javascript·面试
CoderWeen2 小时前
从零实现一个 Vue3 流程图编辑器:节点拖拽、贝塞尔连线与框选
前端·javascript