导言
目前flash在浏览器的支持越来越差,谷歌新版本已经抛弃flash支持,常规rtmp视频流播放基本没法满足技术要求。目前解决方案是将rtmp转为hls之后进行播放,常规情况下hls播放延迟比较大,平均在5-6s延迟左右,本章采用的是利用flv进行视频播放,谷歌浏览器,IE没有测试。测试延迟基本小于5s左右,延迟较大情况可在前端进行校验跳帧来保证延迟。这类传统的流媒体平台还是比较成熟的,如果想搭建GB28181监控对接平台可以参照:https://blog.youkuaiyun.com/mokeily99/article/details/115657823
正文
所需工具包含
ffmpeg
ffmpeg:https://download.youkuaiyun.com/download/mokeily99/16629192
nginx-rtmp-module编译后的:https://download.youkuaiyun.com/download/mokeily99/16629289
flv.min.js:https://download.youkuaiyun.com/download/mokeily99/16629251
vlc播放器:https://download.youkuaiyun.com/download/mokeily99/1662901
如果闲麻烦可可直接下载全量包,包含前端测试页面:https://download.youkuaiyun.com/download/mokeily99/16630141
步骤:
1、下载编译后的nginx-rtmp-module文件,配置nginx.conf如下
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#error_log logs/error.log debug;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
# 添加RTMP服务
rtmp {
server {
listen 1935; # 监听端口
chunk_size 4000;
application live {
live on;
gop_cache on;
}
}
}
# HTTP服务
http {
include mime.types;
default_type application/octet-stream;
#access_log logs/access.log main;
server {
listen 8088; # 监听端口
location /stat.xsl {
root html;
}
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location / {
root html;
}
location /rtmpLive {
flv_live on;
chunked_transfer_encoding on; #open 'Transfer-Encoding: chunked' response
add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header 'Cache-Control' 'no-cache';
}
}
}
如果是从上面地址下载的配置文件是已经修改完的,不需要动。注意配置文件中的几个监听端口,避免和其他程序冲突,1935是ffmpeg转码时用到的端口,需要和此处对应。8088是flv前端展示端口,后文会说明。
启动nginx,双击nginx.exe程序,cmd窗口一闪而过,启动任务管理器查看下nginx是否启动,出现nginx.exe就说明已经启动了。
2、启动ffmpeg
下载上面地址的ffmpeg,配置环境变量,path中加入ffmpeg的bin目录地址,我的是D:\workSpace\live\ffmpeg\ffmpeg-4.3.1-2021-01-01-essentials_build\bin
之后以管理员身份运行cmd,输入ffmpeg回车会出现一堆东西,说明配置完成
之后测试ffmpeg拉流解码,本次用的是大华监控,测试主码流,命令如下
ffmpeg -i "rtsp://admin:admin123@192.168.110.108:554/cam/realmonitor?channel=1&subtype=0" -vcodec copy -f flv -an rtmp://localhost:1935/live/1000000001
注释:
admin是监控登录页面的账号
admin123是监控登录页面密码
192.168.110.108:554为监控地址和端口,监控如果没做端口修改基本大华的都是554
subtype是码流类型,0是主码流1是副码流,如果使用副码流需要在监控管理页面启动副码流
channel是通道编号,都是默认配置,没有修改就写1
-vcodec copy -f flv -an:视频解码参数-vcodec copy为拷贝原视频,建议不要改动,我试过其他参数,也尝试修改视频尺寸都没有成功,不是特别卡就是不显示,只能通过监控页面修改码流的分辨率来实现视频尺寸。flv解码格式,对应前端的flv
rtmp://localhost:1935/live/1000000001:rtmp输出地址,本机测试就用localhost就可以,或替换本机IP
运行后会不停的输出拉流转码日志,如下图
3、测试rtmp视频流
打开vlc播放器,媒体-打开网络串流,输入上步中的rtmp://localhost:1935/live/1000000001地址,播放测试是否有图像,有图像说明拉流转码成功。
4、前端flv展示监控视频
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>播放页面</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.5.0/flv.min.js"></script>
<video style="height: 400px;width: 600px;" id="videoElement" muted autoplay controls></video>
<script>
var flvPlayer = null;
if (flvjs.isSupported()) {
var videoElement = document.getElementById('videoElement');
flvPlayer = flvjs.createPlayer({
type: 'flv',
// 8080 对应Nginx监听的端口
// rtmpLive 对应Nginx的路径
url: 'http://localhost:8088/rtmpLive?app=live&stream=1000000001',
enableWorker: true, //浏览器端开启flv.js的worker,多进程运行flv.js
isLive: true, //直播模式
hasAudio: false, //关闭音频
hasVideo: true,
stashInitialSize: 128,
enableStashBuffer: false
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
}
</script>
</body>
</html>
注意url地址,http://localhost:8088/rtmpLive?app=live&stream=1000000001,8088对应nginx配置文件中的监听端口,上文中配置的是8088。rtmpLive也是nginx配置文件末尾的配置标识符,app=live&stream=1000000001是ffmpeg拉流转码后的标识符,如果以上都是按照稳中内容写的话就不需要修改地址
前端播放成功,延迟基本在5s以内,如果想修改延迟,可进行前端时间校正,前端js加入代码
setInterval(function() {
console.log("时延校正判断");
if (!videoElement.buffered.length) {
return;
}
var end = videoElement.buffered.end(0);
var diff = end - videoElement.currentTime;
console.log(diff);
if (diff >= 2) {
console.log("进行时延校正");
videoElement.currentTime = (end - 1);
}
}, 3000);
延迟大于2s自动进行时间校正,跳转到当前时间,但是跳转时会出现视频加载现象,不太美观,建议判断时间在4s左右diff >= 4
扩展
java集成ffmpeg拉流转码,web启动自动进行拉流转码:springboot项目
PubListener.java,启动执行任务
package com.hs.job;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.hs.Rtsp.RtspFactory;
@Component
public class PubListener implements CommandLineRunner {
public void run(String... strings) throws Exception {
RtspFactory rtspFy = new RtspFactory();
rtspFy.initRtspInfo();
}
}
RtspFactory.java
package com.hs.Rtsp;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.hs.trans.bean.CameraInfo;
import com.hs.trans.service.CameraService;
import com.hs.util.FFmpegConfig;
import com.hs.util.PropertiesUtil;
import com.hs.util.SpringUtils;
public class RtspFactory {
private CameraService cameraService;
private static FFmpegConfig config = (FFmpegConfig) PropertiesUtil.load("defaultFFmpegConfig.properties", FFmpegConfig.class);//ffmpeg转码配置文件,配置了各类监控的转码方式,目前配置了大华、海康等设备
public void initRtspInfo(){
List<CameraInfo> cameraList = getCameraList();//getCameraList为获取监控列表方法,需要自己实现,测试可写死
for(CameraInfo camera : cameraList){
//String rtspUrl = "rtsp://admin:admin123@192.168.110.108:554/cam/realmonitor?channel=1&subtype=1";
//rtsp://[username]:[password]@[ip]:554/cam/realmonitor?channel=1&subtype=1
String rtspUrl = loadRtspUrl(camera);
// Nginx rtmp地址
// 1935 对应Nginx配置文件中rtmp所监听的端口
// live 对应Nginx配置文件中rtmp下application的值
// rtmpStream 对应播放页面中16行参数stream的值
if(rtspUrl != null){
String nginxRtmpUrl = "rtmp://localhost:1935/live/" + camera.getMaxaccept();
RtspThread rtsp;
try {
rtsp = new RtspThread(rtspUrl, nginxRtmpUrl);
rtsp.start();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
public String loadRtspUrl(CameraInfo camera){
String rtspUrl = null;
String comTypeLists = config.getCameraComType();
String[] comTypeArr = comTypeLists.split(",");
for(String comTypes : comTypeArr){
String[] comArr = comTypes.split("-");
if(comArr[0].equals(camera.getComType())){
rtspUrl = comArr[1];
break;
}
}
if(rtspUrl != null){
rtspUrl = rtspUrl.replace("[username]", camera.getUserName()).replace("[password]", camera.getPassWord()).replace("[ip]", camera.getIp());
}
return rtspUrl;
}
public List<CameraInfo> getCameraList(){
cameraService = SpringUtils.getBean(CameraService.class);
Map<String, Object> param = new HashMap<String, Object>();
param.put("deleteFlag", 0);
return cameraService.getCameraList(param);
}
}
RtspThread.java
package com.hs.Rtsp;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import com.hs.util.FFmpegConfig;
import com.hs.util.PropertiesUtil;
public class RtspThread extends Thread {
private static FFmpegConfig config = (FFmpegConfig) PropertiesUtil.load("defaultFFmpegConfig.properties", FFmpegConfig.class);
private String rtspUrl;
private String nginxRtmpUrl;
public RtspThread(String rtspUrl, String nginxRtmpUrl) throws UnsupportedEncodingException {
this.rtspUrl = rtspUrl;
this.nginxRtmpUrl = nginxRtmpUrl;
}
@Override
public void run() {
super.run();
try {
// ffmpeg 已经在系统环境变量中配置好了
String command = config.getFfpmegPath() + "ffmpeg.exe";
command += " -i \"" + rtspUrl + "\"";
command += " -vcodec copy -f flv -s 100x50 -an " + nginxRtmpUrl;
System.out.println("ffmpeg推流命令:" + command);
Process process = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
while ((line = br.readLine()) != null) {
}
} catch (Exception e) {
e.printStackTrace();
} finally {// 建立连接失败的话不会执行socket.close();
}
}
}
defaultFFmpegConfig.properties,放到resources目录下,10900对应大华、10901对应海康,“,”分割。ffpmegPath对应ffmpeg目录bin目录
#设备厂家映射
cameraComType=10900-rtsp\://[username]\:[password]@[ip]\:554/cam/realmonitor?channel\=1&subtype\=0,10901-rtsp\://[username]\:[password]@[ip]\:554/h264/ch1/main/av_stream,10902-rtsp\://[ip]\:3000
#ffpmeg路径
ffpmegPath=D\:\\workSpace\\live\\ffmpeg\\ffmpeg-4.3.1-2021-01-01-essentials_build\\bin\\
表结构
启动nginx后,启动springboot项目自动进行推拉流。集成写的比较粗略,有问题邮箱联系mokeily99@126.com,vx:18686530251