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码力】!