具体实现参考:
- 前端是无法直接播放rstp推流来的视频,所以需要用ffmpeg进行转码。
- ffmpeg只能推送TCP或者HTTP协议还不支持ws协议。
- 后端的实现暂时只试了ws模式,http没有试过
大致流程图。
ffmpeg.exe -i "rtsp地址" -q 0 -f mpegts -codec:v mpeg1video -s 800x600 推送地址
需要注意的点:
1.Runtime.getRuntime.exec的陷阱
参考:
解决方案:
a.定义StreamGobbler类来多开线程处理process.getErrorStream()的信息 ,只要循环输出即可
public class StreamGobbler extends Thread {
InputStream is;
String type;
String videoUrl;
public StreamGobbler(InputStream is, String type,String videoUrl) {
this.is = is;
this.type = type;
this.videoUrl = videoUrl;
}
@Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (type.equals("Error")) {
System.out.println(line);
} else {
// System.out.println("Debug:" + line);
}
}
//防止windows进程意外关闭
ShowWebSocket.conversionVideo.destroy(videoUrl);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
b.在调用ffmpeg时使用
/**
* 转换视频流
*/
@Component
@Slf4j
public class ConversionVideo{
public ConcurrentHashMap<String, Process> processMap = new ConcurrentHashMap<>();
public void destroy(String videoUrl){
Process process = processMap.get(videoUrl);
if (process !=null){
process.destroy();
processMap.remove(videoUrl);
log.info("------------------------推流结束-------------------------");
}
}
public Integer pushVideoAsRTSP(String videoUrl, String controllerUrl){
Process process = processMap.get(videoUrl);
// 已经在推送了不需要进行操作
if(process != null){
return 0;
}
// 获取ffmpeg位置
String ffmpegPath = System.getProperty("ffmpeg");
try {
// cmd命令拼接,注意命令中存在空格
String command = ffmpegPath;
// ffmpeg开头,-re代表按照帧率发送,在推流时必须有
// 指定要推送的视频
command += " -i \"" + videoUrl + "\"";
// 指定推送服务器,-f:指定格式
command += " -q 0 -f mpegts -codec:v mpeg1video -s 800x600 " + controllerUrl;
log.info("ffmpeg推流命令:" + command);
// 运行cmd命令,获取其进程
process = Runtime.getRuntime().exec(command);
//这里处理process的信息
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "Error",videoUrl);
errorGobbler.start();
log.info("开始推流");
processMap.put(videoUrl,process);
}catch (Exception e){
e.printStackTrace();
}
return 1;
}
}
2023-04-25
在对接IPCamera 摄像头的时候:
ffmpeg 报错 MPEG-1/2 does not support 15/1 fps
在命令行加上 -r 30 参数即可
原本命令:
ffmpeg -i “rtsp://admin:foa123456@192.168.1.135:554/h264/ch1/main/av_stream” -q 0 -f mpegts -codec:v mpeg1video -s 1366x768 http://127.0.0.1:8081/123456
改为:
ffmpeg -i "rtsp://admin:foa123456@192.168.1.135:554/h264/ch1/main/av_stream" -r 30 -q 0 -f mpegts -codec:v mpeg1video -s 1366x768 http://127.0.0.1:8081/foa123456
参考地址:
20230607 项目切换播放库mpegts(因为要使用video标签做3D视频融合)
前端播放库:
mpegts 和 jsmpeg
自行网上下载文件或者npm下载
mpegts (要求视频流是flv格式)
ffmpeg -i "视频地址" -q 0 -f flv -vcodec h264 -an -s 1674x942 http://127.0.0.1:8081/foa123456
var video = document.getElementById('video'+sxtIndex);
var url = this.app.wsUrl +'/live/' + UtilTools.getDataByObjectFields(sxtData, "unit","");
var player = mpegts.createPlayer({
type: 'flv',
isLive: true,
url
});
player.attachMediaElement(video);
player.load();
player.play();
jsmpeg (vue项目需要将jsmpeg.min.js 文件开头的var JSMpeg换成 window.JSMpeg)
// let canvas = document.getElementById('video')
let url = 'ws://127.0.0.1:8866/live?porthole=' + this.porthole
let player = new JSMpeg.Player(url, {canvas: this.$refs.video})
项目初始化ffmpeg
@PostConstruct
public void loadFFmpeg() {
log.info("正在初始化资源,请稍等...");
String path = System.getProperty("user.dir");
path = path + "\\ffmpeg\\bin\\ffmpeg.exe ";
System.setProperty("ffmpeg",path);
log.info(System.getProperty("ffmpeg"));
log.info("初始化成功");
}
2023-07-24 记录
发现使用websocket有一些新问题,比如第二个ws连接和第一个连接使用同一个视频时,第二个连接前端不会播放
使用postman测试
发现第一次请求的时候会返回一个头部video信息,postman看不到具体数据内容,在网页端查看
经过查阅资料,这是flv视频固定开头,使用播放库的时候需要保证第一个发送的信息是这个(不知道是不是所有播放库都需要,待查证,有知道的大神麻烦讲一下)
这个头部信息似乎是固定的(因为我这边要播放的视频流是经过统一转码).因此把这一段的信息直接copy下来,在建立ws连接后发送即可(取中间的16进制数据)