导言

目前flash在浏览器的支持越来越差,谷歌新版本已经抛弃flash支持,常规rtmp视频流播放基本没法满足技术要求。目前解决方案是将rtmp转为hls之后进行播放,常规情况下hls播放延迟比较大,平均在5-6s延迟左右,本章采用的是利用flv进行视频播放,谷歌浏览器,IE没有测试。测试延迟基本小于5s左右,延迟较大情况可在前端进行校验跳帧来保证延迟。这类传统的流媒体平台还是比较成熟的,如果想搭建GB28181监控对接平台可以参照:

正文

所需工具包含

ffmpeg

ffmpeg:

nginx-rtmp-module编译后的:

flv.min.js:

vlc播放器:

如果闲麻烦可可直接下载全量包,包含前端测试页面:

步骤:

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就说明已经启动了。

nginx 加速mp4 播放 nginx rtmp延迟_nginx 加速mp4 播放

2、启动ffmpeg

下载上面地址的ffmpeg,配置环境变量,path中加入ffmpeg的bin目录地址,我的是D:\workSpace\live\ffmpeg\ffmpeg-4.3.1-2021-01-01-essentials_build\bin

nginx 加速mp4 播放 nginx rtmp延迟_视频处理_02

之后以管理员身份运行cmd,输入ffmpeg回车会出现一堆东西,说明配置完成

nginx 加速mp4 播放 nginx rtmp延迟_视频处理_03

之后测试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

运行后会不停的输出拉流转码日志,如下图

nginx 加速mp4 播放 nginx rtmp延迟_ffmpeg_04

3、测试rtmp视频流

打开vlc播放器,媒体-打开网络串流,输入上步中的rtmp://localhost:1935/live/1000000001地址,播放测试是否有图像,有图像说明拉流转码成功。

nginx 加速mp4 播放 nginx rtmp延迟_nginx 加速mp4 播放_05

nginx 加速mp4 播放 nginx rtmp延迟_ffmpeg_06

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拉流转码后的标识符,如果以上都是按照稳中内容写的话就不需要修改地址

nginx 加速mp4 播放 nginx rtmp延迟_视频处理_07

前端播放成功,延迟基本在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 加速mp4 播放 nginx rtmp延迟_nginx 加速mp4 播放_08