30 分钟搞定 SpringBoot 视频推拉流!实战避坑指南
off999 2025-09-19 01:09 2 浏览 0 评论
30 分钟搞定 SpringBoot 视频推拉流!实战避坑指南
在音视频开发领域,SpringBoot 凭借其快速开发特性,成为很多开发者实现视频推拉流功能的首选框架。但实际开发中,从环境搭建到流处理优化,处处都可能遇到 “坑”。本文将从实战角度出发,带大家一步步搭建 SpringBoot 视频推拉流系统,解决常见问题,让你快速上手音视频开发。
一、先搞懂:视频推拉流核心概念
在动手开发前,必须先理清几个关键概念,避免后续开发 “一头雾水”。视频推拉流简单来说,就是 “推流端” 将视频数据传输到 “流媒体服务器”,“拉流端” 从服务器获取视频数据并播放的过程。其中,流媒体服务器是核心枢纽,负责接收、转码、分发视频流;常用的协议有 RTMP、HLS、RTSP,不同协议适用场景不同 ——RTMP 延迟低(1-3 秒),适合直播互动;HLS 基于 HTTP,兼容性强(支持浏览器、手机),但延迟较高(10-30 秒);RTSP 多用于安防监控设备。
而 SpringBoot 在这套体系中,主要负责 “胶水工作”:整合流媒体服务器、处理业务逻辑(如用户认证、流权限控制)、提供 API 接口供前端调用。比如,我们可以用 SpringBoot 开发一个 “直播房间管理” 功能,用户创建房间后,系统自动分配推流地址,观众进入房间时自动获取拉流地址,整个过程通过 SpringBoot 的接口完成交互。
二、实战第一步:环境搭建与依赖配置
1. 选择合适的流媒体服务器
实战中,不建议重复造轮子开发流媒体服务器,推荐使用成熟的开源方案。这里我们选SRS(Simple RTMP Server) ,它轻量、高性能,支持 RTMP、HLS、HTTP-FLV 等协议,且有完善的中文文档,对国内开发者友好。
SRS 的安装很简单(以 Linux 为例):
bash
# 克隆源码
git clone https://github.com/ossrs/srs.git
# 进入目录编译
cd srs/trunk && ./configure && make
# 启动服务器(默认端口1935,RTMP协议)
./objs/srs -c conf/srs.conf
启动后,通过telnet 127.0.0.1 1935能连接成功,说明 SRS 已正常运行。
2. SpringBoot 项目依赖配置
创建一个 SpringBoot 项目(推荐 2.x 版本,兼容性更稳定),在pom.xml中引入核心依赖:
- Spring Web:提供 HTTP 接口,用于前后端交互;
- FFmpeg 相关依赖:处理视频转码(如 RTMP 转 HLS,适配浏览器播放);
- Lombok:简化代码,减少 getter/setter;
- Spring Security(可选):用于推流 / 拉流权限控制。
关键依赖代码如下:
xml
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- FFmpeg工具类 -->
<dependency>
<groupId>com.github.kokorin.jaffree</groupId>
<artifactId>jaffree</artifactId>
<version>1.5.11</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
三、核心功能开发:从推流到拉流全流程
1. 推流端实现:用 FFmpeg 模拟推流
实际项目中,推流端可能是手机 APP、PC 客户端(如 OBS),但开发阶段我们可以用 FFmpeg 命令模拟推流,快速验证流程。
首先,准备一个测试视频文件(如test.mp4),执行以下命令将视频推送到 SRS 服务器:
bash
# 格式:ffmpeg -re -i 视频文件 -c:v libx264 -c:a aac -f flv rtmp://SRS服务器IP:1935/live/房间号
ffmpeg -re -i test.mp4 -c:v libx264 -c:a aac -f flv rtmp://192.168.1.100:1935/live/room123
其中,-re表示按视频实际帧率推流(避免推流过快),live是 SRS 配置的 “应用名”(对应srs.conf中的vhost __defaultVhost__下的app live),room123是 “流名”(可理解为直播房间 ID,需唯一)。
如果想通过 SpringBoot 接口触发推流(比如自动推流监控视频),可以封装 FFmpeg 工具类,通过 Java 代码调用 FFmpeg 命令:
java
@Slf4j
@Component
public class FFmpegUtil {
// 推流方法:输入文件路径、SRS推流地址
public void pushStream(String inputPath, String rtmpUrl) {
try {
// 构建FFmpeg命令
FFmpeg.atPath()
.addArgument("-re")
.addArgument("-i")
.addArgument(inputPath)
.addArgument("-c:v")
.addArgument("libx264")
.addArgument("-c:a")
.addArgument("aac")
.addArgument("-f")
.addArgument("flv")
.addArgument(rtmpUrl)
.execute();
log.info("推流成功,推流地址:{}", rtmpUrl);
} catch (Exception e) {
log.error("推流失败:{}", e.getMessage());
throw new RuntimeException("推流异常", e);
}
}
}
2. 拉流端实现:多端适配(PC / 手机 / 浏览器)
拉流端需要适配不同设备,这里分两种场景实现:
(1)RTMP 拉流(适合 PC 客户端,如 VLC)
直接使用推流地址的 RTMP 链接,在 VLC 播放器中 “打开网络串流”,输入
rtmp://192.168.1.100:1935/live/room123,即可播放视频。
(2)HLS 拉流(适合浏览器 / 手机)
浏览器不支持 RTMP 协议,需要将 RTMP 流转成 HLS 流(生成.m3u8索引文件和.ts分片文件)。我们可以在 SRS 中配置自动转码,修改srs.conf:
conf
vhost __defaultVhost__ {
app live {
# 开启HLS转码
hls {
enabled on;
# HLS文件保存路径(需提前创建)
hls_path ./objs/nginx/html;
# 分片时长(默认10秒,越小延迟越低)
hls_fragment 5;
# 索引文件包含的分片数
hls_window 3;
}
}
}
重启 SRS 后,推流时会自动生成 HLS 文件,拉流地址为http://SRS服务器IP:8080/live/room123.m3u8(SRS 默认开启 8080 端口提供 HTTP 服务)。
在 SpringBoot 中,我们可以开发一个 “获取拉流地址” 的接口,根据设备类型返回不同协议的地址:
java
@RestController
@RequestMapping("/stream")
@RequiredArgsConstructor
public class StreamController {
private final String srsIp = "192.168.1.100";
// 获取拉流地址:deviceType=pc(RTMP)、mobile/browser(HLS)
@GetMapping("/pull")
public ResultVO getPullUrl(@RequestParam String roomId, @RequestParam String deviceType) {
String pullUrl;
if ("pc".equals(deviceType)) {
pullUrl = "rtmp://" + srsIp + ":1935/live/" + roomId;
} else {
pullUrl = "http://" + srsIp + ":8080/live/" + roomId + ".m3u8";
}
return ResultVO.success("拉流地址获取成功", pullUrl);
}
}
3. 权限控制:防止非法推流 / 拉流
没有权限控制的流很容易被 “盗用”,比如别人用你的推流地址推垃圾内容,或盗用你的直播流。我们可以用 Spring Security 实现简单的权限校验:
(1)推流权限校验
推流时,要求客户端携带 “认证令牌”,SpringBoot 接口验证令牌通过后,才返回有效的推流地址。
首先,生成推流令牌(可用 JWT):
java
@Component
public class JwtUtil {
private final String secret = "springboot-stream-secret"; // 密钥,实际项目需加密存储
// 生成推流令牌:包含房间号、过期时间(1小时)
public String generatePushToken(String roomId) {
return Jwts.builder()
.setSubject(roomId)
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
// 验证令牌
public boolean validateToken(String token, String roomId) {
try {
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return roomId.equals(claims.getSubject()) && new Date().before(claims.getExpiration());
} catch (Exception e) {
return false;
}
}
}
然后,开发 “获取推流地址” 的接口,验证令牌后返回地址:
java
@PostMapping("/push")
public ResultVO getPushUrl(@RequestParam String roomId, @RequestParam String token) {
// 验证令牌
if (!jwtUtil.validateToken(token, roomId)) {
return ResultVO.error("令牌无效,无推流权限");
}
String pushUrl = "rtmp://" + srsIp + ":1935/live/" + roomId;
return ResultVO.success("推流地址获取成功", pushUrl);
}
(2)拉流权限校验
拉流时,SRS 支持 “回调校验”,即每次拉流前,SRS 会调用 SpringBoot 的校验接口,通过后才允许拉流。配置srs.conf:
conf
vhost __defaultVhost__ {
app live {
# 拉流回调校验
http_hooks {
enabled on;
# 校验接口地址
on_hls_play http://SpringBoot服务器IP:8081/stream/checkPull;
}
}
}
SpringBoot 中开发校验接口,比如验证用户是否已登录:
java
@PostMapping("/checkPull")
public void checkPull(HttpServletRequest request, HttpServletResponse response) throws IOException {
// SRS回调时会携带roomId(流名)、userId(可从请求参数获取,需客户端传递)
String roomId = request.getParameter("stream");
String userId = request.getParameter("userId");
// 模拟校验:用户是否已登录(实际项目查数据库/Redis)
boolean hasPermission = "user123".equals(userId);
// 校验通过返回200,失败返回403
if (hasPermission) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
}
}
四、实战避坑:这些问题 90% 的人都会遇到
1. 推流失败:连接超时 / 拒绝连接
- 原因 1:SRS 服务器未启动,或端口被占用(1935 端口常用,可能被其他程序占用)。
解决:用netstat -tuln | grep 1935查看端口占用,杀死占用进程后重启 SRS。 - 原因 2:防火墙未开放 1935(RTMP)、8080(HLS HTTP)端口。
解决:Linux 执行firewall-cmd --add-port=1935/tcp --permanent和firewall-cmd --add-port=8080/tcp --permanent,然后firewall-cmd --reload。
2. 拉流延迟过高(超过 30 秒)
- 原因:HLS 分片时长设置过大,或 SRS 缓存配置不合理。
解决:在srs.conf中减小hls_fragment(如设为 3 秒),同时开启 “低延迟模式”:
conf
hls {
enabled on;
hls_fragment 3;
hls_window 2;
# 开启低延迟
hls_low_latency on;
}
3. 浏览器播放 HLS 流卡顿
- 原因:视频码率过高,浏览器加载分片慢;或分片文件未及时生成。
解决:推流时降低码率(FFmpeg 命令加-b:v 500k,表示视频码率 500kbps);同时确保 SRS 的hls_path目录有写入权限。
五、项目优化:从 “能用” 到 “好用”
1. 流状态监控
实时监控推流是否正常,避免 “推流断了但没人知道”。可以用 SRS 的 HTTP API 获取流状态,SpringBoot 定时调用接口:
java
@Component
@Scheduled(fixedRate = 5000) // 每5秒监控一次
public class StreamMonitor {
private final String srsApiUrl = "http://192.168.1.100:8080/api/v1/streams";
@Autowired
private RestTemplate restTemplate;
public void monitorStream() {
try {
String response = restTemplate.getForObject(srsApiUrl, String.class);
// 解析JSON,判断流是否存在(如room123是否在流列表中)
JSONObject json = new JSONObject(response);
JSONArray streams = json.getJSONArray("streams");
boolean isPushing = streams.stream()
.map(JSONObject.class::cast)
.anyMatch(s -> "live/room123".equals(s.getString("name")));
if (!isPushing) {
log.warn("房间room123推流已中断!");
// 发送告警(如短信、钉钉通知)
}
} catch (Exception e) {
log.error("流监控失败:{}", e.getMessage());
}
}
}
2. 集群部署(高并发场景)
当直播房间多、观众量大时,单台 SRS 服务器扛不住,需要集群部署。核心思路是:
- 用 Nginx 做负载均衡,分发推流 / 拉流请求;
- 多台 SRS 服务器通过 “集群同步”(如 SRS 的 RTMP Edge 模式)共享流数据;
- SpringBoot 服务集群部署,用 Redis 共享用户会话、流状态。
六、总结:快速上手的核心要点
- 先搭环境再开发:优先搞定 SRS 服务器,用 FFmpeg 模拟推流,验证基础流程;
- 协议选择看场景:低延迟用 RTMP,兼容性用 HLS;
- 权限控制不能少:用 JWT+SRS 回调防止非法访问;
- 遇到问题先查日志:SRS 日志(./objs/srs.log)和 SpringBoot 日志是排错关键。
按照本文的步骤,你可以在 1 小时内搭建一个能跑通的 SpringBoot 视频推拉流系统,后续再根据业务需求(如直播带货、在线教育)扩展功能(如弹幕、连麦)。如果在开发中遇到具体问题,欢迎在评论区交流!
感谢关注【AI码力】!
相关推荐
- Linux 网络协议栈_linux网络协议栈
-
前言;更多学习资料(包含视频、技术学习路线图谱、文档等)后台私信《资料》免费领取技术点包含了C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,Z...
- 揭秘 BPF map 前生今世_bpfdm
-
1.前言众所周知,map可用于内核BPF程序和用户应用程序之间实现双向的数据交换,为BPF技术中的重要基础数据结构。在BPF程序中可以通过声明structbpf_map_def...
- 教你简单 提取fmpeg 视频,音频,字幕 方法
-
ffmpeg提取视频,音频,字幕方法(HowtoExtractVideo,Audio,SubtitlefromOriginalVideo?)1.提取视频(ExtractVi...
- Linux内核原理到代码详解《内核视频教程》
-
Linux内核原理-进程入门进程进程不仅仅是一段可执行程序的代码,通常进程还包括其他资源,比如打开的文件,挂起的信号,内核内部的数据结构,处理器状态,内存地址空间,或多个执行线程,存放全局变量的数据段...
- Linux C Socket UDP编程详解及实例分享
-
1、UDP网络编程主要流程UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。...
- libevent源码分析之bufferevent使用详解
-
libevent的bufferevent在event的基础上自己维护了一个buffer,这样的话,就不需要再自己管理一个buffer了。先看看structbufferevent这个结构体struct...
- 一次解决Linux内核内存泄漏实战全过程
-
什么是内存泄漏:程序向系统申请内存,使用完不需要之后,不释放内存还给系统回收,造成申请的内存被浪费.发现系统中内存使用量随着时间的流逝,消耗的越来越多,例如下图所示:接下来的排查思路是:1.监控系统中...
- 彻底搞清楚内存泄漏的原因,如何避免内存泄漏,如何定位内存泄漏
-
作为C/C++开发人员,内存泄漏是最容易遇到的问题之一,这是由C/C++语言的特性引起的。C/C++语言与其他语言不同,需要开发者去申请和释放内存,即需要开发者去管理内存,如果内存使用不当,就容易造成...
- linux网络编程常见API详解_linux网络编程视频教程
-
Linux网络编程API函数初步剖析今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作。1、socket(family...
- Linux下C++访问web—使用libcurl库调用http接口发送解析json数据
-
一、背景这两天由于一些原因研究了研究如何在客户端C++代码中调用web服务端接口,需要访问url,并传入json数据,拿到返回值,并解析。 现在的情形是远程服务端的接口参数和返回类型都是json的字符...
- 平衡感知调节:“系统如人” 视角下的架构设计与业务稳定之道
-
在今天这个到处都是数字化的时代,系统可不是一堆冷冰冰的代码。它就像一个活生生的“数字人”,没了它,业务根本转不起来。总说“技术要为业务服务”,但实际操作起来问题不少:系统怎么才能快速响应业务需求?...
- 谈谈分布式文件系统下的本地缓存_什么是分布式文件存储
-
在分布式文件系统中,为了提高系统的性能,常常会引入不同类型的缓存存储系统(算法优化所带来的的效果可能远远不如缓存带来的优化效果)。在软件中缓存存储系统一般可分为了两类:一、分布式缓存,例如:Memca...
- 进程间通信之信号量semaphore--linux内核剖析
-
什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠...
- Qt编写推流程序/支持webrtc265/从此不用再转码/打开新世界的大门
-
一、前言在推流领域,尤其是监控行业,现在主流设备基本上都是265格式的视频流,想要在网页上直接显示监控流,之前的方案是,要么转成hls,要么魔改支持265格式的flv,要么265转成264,如果要追求...
- 30 分钟搞定 SpringBoot 视频推拉流!实战避坑指南
-
30分钟搞定SpringBoot视频推拉流!实战避坑指南在音视频开发领域,SpringBoot凭借其快速开发特性,成为很多开发者实现视频推拉流功能的首选框架。但实际开发中,从环境搭建到流处理优...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)