一、vlcj
vlcj是一个强大的Java框架,专为VLC媒体播放器设计,让开发者能够轻松地在Java应用中集成专业的媒体播放功能。无论是构建简单的音频播放器还是复杂的视频处理应用,vlcj都提供了丰富的API和工具,帮助你快速实现各种媒体播放需求

二、为什么替换掉javacv+ffmpeg
1、起因
开发过程中的需求,需要调用海康isc平台,海康硬盘录像机、宇视平台、宇视硬盘录像机的接口获取其返回的rtsp流地址,然后在播放出来这个回放流。
2、难点
查阅资料后发现需要本地搭建一个流媒体服务器,然后给流媒体暴露出一个端口,通过websocket+rtsp播放地址实现画面播放,但是整理的各种流媒体播放类以及接口比较多需要安装ffmpeg环境依赖,服务搭建完成后发现本地确实可以运行,但是放到线上后因为现场环境和本地环境差异,效果很差,有很多流播放不了。而且还需要适配各种因播放流格式为h264,或h265转换,如果分辨率不同还是需要再次适配不同的分辨率,比较繁琐,现场也因为环境问题不能升级ffmpeg,所以还是只能用ffmpeg4.x的版本,bug还是比较多的。然后才想换一种方式
三、实现步骤
1、引入vlcj和jna依赖
java
<dependency>
<groupId>uk.co.caprica</groupId>
<artifactId>vlcj</artifactId>
<version>3.12.1</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.12.1</version>
</dependency>
注意:使用vlcj依赖为3.x的版本,对应服务器安装的vlc也要是3.x的版本
2、安装vlc
2.1 windows安装
官网地址,进入下载,如下页面

点击下载安装即可,像安装普通软件一样
2.2Linux下载
如果是有桌面操作系统那么就和windows无异,如果是无桌面版本需要执行命令,以Ubuntu为例
bash
sudo apt update
sudo apt install vlc vlc-plugin-base vlc-plugin-video-output libvlc-dev -y
安装完成输入命令查看版本
bash
vlc --version

如图就是安装好了
注意:如果是root用户因为vlc权限问题是执行不了的,建议不要用root用户
3、代码集成
3.1创建VlcjRtspService
java
package com.ruoyi.web.controller.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import uk.co.caprica.vlcj.discovery.NativeDiscovery;
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
import uk.co.caprica.vlcj.player.headless.HeadlessMediaPlayer;
import javax.annotation.PreDestroy;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class VlcjRtspService {
static {
// 自动发现 VLC 安装路径
//new NativeDiscovery().discover();
// 可选:手动指定 VLC 路径(如果自动发现失败)
System.setProperty("jna.library.path", "C:\\Program Files\\VideoLAN\\VLC");
//System.setProperty("jna.library.path", "/usr/lib64/vlc");
}
private final ConcurrentHashMap<String, HeadlessMediaPlayer> activePlayers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, MediaPlayerFactory> activeFactories = new ConcurrentHashMap<>();
/**
* 启动 RTSP 推流
*/
public boolean startStream(String streamId, String rtspUrl, int flvPort, String flvPath) {
if (activePlayers.containsKey(streamId)) {
log.warn("流 {} 已在运行", streamId);
return false;
}
try {
// 3.x 版本:直接 new MediaPlayerFactory()
MediaPlayerFactory factory = new MediaPlayerFactory();
activeFactories.put(streamId, factory);
// 3.x 版本:直接 newHeadlessMediaPlayer()
HeadlessMediaPlayer mediaPlayer = factory.newHeadlessMediaPlayer();
activePlayers.put(streamId, mediaPlayer);
// 转推选项:添加 keyint=25, bframes=0 强制 I 帧间隔和禁用 B 帧
String sout = String.format(
":sout=#transcode{vcodec=h264,acodec=none,noaudio,keyint=25,bframes=0}:http{mux=flv,dst=0.0.0.0:%d%s}",
flvPort, flvPath
);
// 播放选项
String[] options = {
"--live-caching=0",
"--network-caching=300",
"--clock-synchro=0",
"--no-audio",
"--vout", "dummy",
"--demux=ffmpeg",
"--codec=avcodec",
"--avcodec-threads=2",
"--avcodec-hw=none",
"--input-repeat=65535",
"--rtsp-tcp",
"--rtsp-timeout=30",
"--sout-keep",
"--sout-mux-caching=10000", // 增加缓存
"--fflags=+genpts", // 强制生成 PTS
"--use-wallclock-as-timestamps", // 使用系统时间作为时间戳
"--no-clock", // 禁用内部时钟同步
"--verbose=2",
sout
};
log.info("启动 VLCJ 推流: {} -> http://10.30.127.93:{}{}", rtspUrl, flvPort, flvPath);
// 3.x 版本:playMedia 方法签名
mediaPlayer.playMedia(rtspUrl, options);
// 等待启动
Thread.sleep(2000);
if (!mediaPlayer.isPlaying()) {
log.error("推流启动失败: {}", streamId);
stopStream(streamId);
return false;
}
log.info("推流启动成功: {}", streamId);
return true;
} catch (Exception e) {
log.error("启动推流失败: {}", e.getMessage(), e);
stopStream(streamId);
return false;
}
}
public void stopStream(String streamId) {
HeadlessMediaPlayer player = activePlayers.remove(streamId);
MediaPlayerFactory factory = activeFactories.remove(streamId);
if (player != null) {
player.stop();
player.release();
log.info("停止推流: {}", streamId);
}
if (factory != null) {
factory.release();
}
}
@PreDestroy
public void destroy() {
for (String streamId : activePlayers.keySet()) {
stopStream(streamId);
}
}
}
可以直接复制粘贴使用,注意下图:

这个是vlc安装路径所在,我的是国产统信息UOS操作系统所以和Ubuntu存在地址还是不太一样,输入命令查看安装所在地址
bash
whereis vlc

输入这个地址就行了
3.2调用方法
java
@GetMapping("getVlcFlv")
public AjaxResult getVlcFlv(String rtspUrl) {
// 生成唯一流ID
String streamId = UUID.randomUUID().toString().replace("-", "");
//String flvPath = "/" + streamId + ".flv";
String flvPath = "/1.flv";
int flvPort = 8088; // 从配置读取
//String rtspUrl = "rtsp://admin:abcd1234@192.168.10.xx:554/Streaming/Channels/101";
// 启动 VLCJ 拉流
boolean started = vlcjRtspService.startStream("1", rtspUrl, flvPort, flvPath);
if (!started) {
return AjaxResult.error("启动回放流失败");
}
// 返回 HTTP-FLV 地址
String flvUrl = "http://localhost:" + flvPort + flvPath;
return AjaxResult.success(flvUrl);
}
我写了一个测试方法,直接传入rtsp地址,然后可以推流就行了。使用uuid做流地址的一部分是因为摄像仪可能比较多,流也比较多,用来做区分,停止推流的时候制醋要传入已经推来流的uuid就行了,例如
java
@GetMapping("stopVlcFlv")
public AjaxResult stopVlcFlv(String streamId) {
// 停止 VLCJ 拉流
vlcjRtspService.stopStream(streamId);
return AjaxResult.success("停止回放流成功");
}
3.3前端页面
我这写了一个简单的html页面如果能打开视频就是播放成功了,可以直接复制测试
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>FLV测试</title>
<script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.6.2/flv.min.js"></script>
</head>
<body>
<video id="video" controls autoplay style="width: 800px;"></video>
<script>
const url = "http://192.168.10.x/flv/1.flv";
if (flvjs.isSupported()) {
const player = flvjs.createPlayer({ type: 'flv', url: url });
player.attachMediaElement(document.getElementById('video'));
player.load();
player.play();
} else {
alert("浏览器不支持flv.js");
}
</script>
</body>
</html>
四:注意项
1、跨域
如上所述,我后端开了一个端口为8088,然后用前端html页面测试发现有跨域问题,会播放失败例如错误提示:
html
3.html:1 Access to fetch at 'http://10.30.127.93:8088/1.flv' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
2、解决方案
使用nginx代理,修改nginx配置文件添加以下选项
bash
location /flv/ {
proxy_pass http://192.168.10.x:8088/;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
}

所以我在html测试页面才写url = "http://192.168.10.x/flv/1.flv"
五、效果
点击开始播放,后端开始推流

推流成功,看到画面,隐私问题,我故意打的马赛克

停止推流
