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 秒内

相关推荐
java1234_小锋26 分钟前
【技术专题】CSS3 网页技术 - CSS3 布局进阶
前端·css·css3
zhippt_10141 小时前
只剩截图没有源文件?图转PPT把图片还原成可编辑幻灯片
前端·人工智能·powerpoint
shmily麻瓜小菜鸡1 小时前
TypeScript 知识点补充(第三部分:工程化与高级类型)
前端·javascript·typescript
10岁的博客2 小时前
Chrome 扩展安全攻防实录:从权限模型到真实漏洞复盘
前端·chrome·安全
QianhangQianping2 小时前
WebSocket实时推送+ HTTP 定时轮询:一块大屏两套取数怎么不打架
前端·websocket·网络协议·http·低代码·架构·数据可视化
Eider19982 小时前
Python基础知识之并发相关-2
开发语言·前端·python
用户69371750013842 小时前
AI 能写代码,但工程架构还是应该由人来定
前端·后端
Hilaku2 小时前
前端的技术判断力是怎么练出来的?
前端·javascript·程序员
悠仁さん2 小时前
【C++】set map 使用
java·前端·c++
用户093671755412 小时前
用 4500 行 TypeScript 复刻了童年方格纸上的海战棋
前端·vue.js