Nginx+ffmpeg+java+hls+videojs实现RTSP转HLS

场景: 在页面上选择摄像头,弹出窗口展示摄像头的监控视频

思路:在点选摄像头时根据选中的摄像头id,去数据库中读取rtsp地址,调用ffmpeg命令转换成hls格式,返回hls地址给前端,通过videojs进行播放


首先来搭建环境
环境说明:
1. 视频服务器(linux,java8,nginx 1.16.1)
2. 开发机 win10 ,影响不大


服务搭建部分参考
搭建视频服务需要用到nginx的nginx-rtmp-module

安装nginx

  1. 安装nginx

wget和unzip通过yum安装
yum install wget
yum install unzip

# mkdir module     // 创建下载module的文件夹
# cd module
# wget https://github.com/arut/nginx-rtmp-module/archive/master.zip        //下载模块
# unzip master.zip      // 解压

  1. 编译安装nginx
# yum -y install pcre-devel openssl openssl-devel //安装依赖
# wget http://nginx.org/download/nginx-1.12.2.tar.gz  //下载nginx包
# tar -zxvf nginx-1.12.2.tar.gz
# ./configure --prefix=/opt/nginx-1.9.5 --add-module=/root/module/nginx-rtmp-module-master --with-http_ssl_module       // 编译安装nginx到/opt/nginx-1.9.5
# make
# make install
  1. 修改nginx配置文件,添加如下内容并重新载入配置文件
rtmp{
   server {
     listen 1935;
     application myapp{
        live on;
        record off;
        allow play all;
     }
     application hls{
        live on;
        hls on;
        hls_path /tmp/hls;
        hls_fragment 10s;
        hls_playlist_length 3s;
        record off;
     }
   }
}

注意:rtmp协议和http协议不同,需要写在http外面
由于使用hls播放,需要在http中添加支持, 因为服务在8002端口 所以在nginx监听的8002端口下添加配置 /hls 部分,并添加跨域处理

 server {
        listen       8002;
        server_name  server_name;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location /nginx_status {
               stub_status on;
               access_log off;
               allow 127.0.0.1;
               allow 172.30.17.243;
               deny all;
        }
        location / {
           root /opt/vue/well/dist;
           index index.html;
           add_header Access-Control-Allow-Origin *;
           add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
           add_header Access-Control-Methods "GET, POST, OPTIONS";
        }
        
        location /hls/ {
                types {
                   application/vnd.apple.mpegusr m3u8;
                   video/mp2t ts;
                }
                root html;
                add_header Cache-Control no-cache;
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Allow-Headers "Origin, X-Requested-With, 	Content-Type, Accept";
                add_header Access-Control-Methods "GET, POST, OPTIONS";

        }
        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

  1. 重新载入配置文件

nginx安装完后如果想直接运行,还需要将nginx配置到环境变量中,此处不多介绍

# nginx -s reload

安装ffmpeg

  1. 安装依赖
# yum install yasm -y
  1. 下载ffmpeg并安装
# git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg  //下载ffmpeg
# cd ffmpeg
# ./configure --prefix=/usr/local/ffmpeg
# make
# make install
  1. 拷贝命令到/usr/bin
# ls /usr/local/ffmpeg/
bin    include    lib    share
# cp /usr/local/ffmpeg/bin/*   /usr/bin/

测试

测试通过vlc进行测试

  1. rtmp转rtsp
    rtsp后面为你的rtsp地址,
    rtmp后面为
    你的视频服务器的ip+上面配置的1935端口/application名/视频名
ffmpeg -rtsp_transport tcp  -i rtsp://admin:[email protected]/8000 -acodec aac -strict experimental -ar 44100 -ac 2 -b:a 96k -r 25 -b:v 500k -s 640*480 -f flv rtmp://xxx.xx.xx.xxx:1935/myapp/23

成功之后下面会出来一堆东西 一跳一跳的,
之后打开VLC->右键打开媒体->打开网络->输入你的rtmp地址进行测试

  1. rtsp转hls
    HLS (HTTP Live Streaming)是Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。包括一个m3u(8)的索引文件,TS媒体分片文件和key加密串文件。
    在上文nginx中/hls的root部分配置了html路径,对应nginx目录下的html文件夹,用于存储m3u8文件及ts文件

此时ffmpeg的命令使用,将在html文件夹生成test.m3u8文件

ffmpeg -f rtsp -rtsp_transport tcp -i rtsp://admin:[email protected]/8000 -c copy -f hls -hls_time 2.0 -hls_list_size 1 -hls_wrap 15 /usr/local/nginx/html/hls/test.m3u8

在VLC中通过 http://视频服务ip:8002/hls/test.m3u8进行访问

:8002是 nginx中 /hls 所在监听端口

java 视频服务

在java调用ffmpeg命令时愁了好久,后来找到了git上的一个项目,帮助很大
http://github.com/eguid/FFCH4J,将其引入到项目ffmpeg目录中
Nginx+ffmpeg+java+hls+videojs实现RTSP转HLS_第1张图片
遇到的坑:

  1. 在通过start启动之后无法通过stop停止
    解决方案,将存放进程id的TashDao注入到Spring管理中,还有CommandManager也注入到Spring管理中(@Service,@Autowired)这样保证实例为单例,存放进程的map唯一
  2. 在页面刷新之后无法主动停止,新写了一个线程,在40s之后关闭转换

部分代码如下
controller部分

	@Autowired
    CommandManager manager;
	/**
     * start
     * @param id
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "toHls2", method = RequestMethod.GET)
    @CrossOrigin
    public void toHls(@RequestParam String id) throws Exception
    {
    				// 省略查询路径部分    实体-> result
       			    manager.stop(id);                             // 先停止视频
                    manager.start(id, CommandBuidlerFactory.createBuidler()
                            .add("ffmpeg").add("-f","rtsp")
                            .add("-rtsp_transport","tcp")
                            .add("-i", result.getVideoUrl())              // 取videoUrl
                            .add("-c", "copy")
                            .add("-f", "hls")
                            .add("-hls_time", "2.0")
                            .add("-hls_list_size", "1")
                            .add("-hls_wrap","15")
                            .add("/usr/local/nginx/html/hls/" + id + ".m3u8"));
                     return "视频路径"
    }

停止转换部分如果不删除m3u8文件,videojs会直接调用之前停止时的m3u8文件,里面有终止符,导致视频无法继续播放

	@Autowired
    CommandManager manager;
    /**
     * stop
     * @param id
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "stop", method = RequestMethod.GET)
    @CrossOrigin
    public ObjectRestResponse stop(@RequestParam String id) throws Exception
    {
        ObjectRestResponse objectRestResponse = new ObjectRestResponse();
        manager.stop(id);
        manager.start(id, CommandBuidlerFactory.createBuidler()
                .add("rm -rf","/usr/local/nginx/html/hls/"+ id + ".m3u8"));
        objectRestResponse.setData("");
        return objectRestResponse;
    }

videojs

思路:调用接口返回地址成功后给video的src重新赋值

		
			var player = videojs('my-video');
              player.src([
                {type:"application/x-mpegURL", src: res.data}
              ])
              this.player.load();
              this.player.play();
            }, 3000);

你可能感兴趣的:(java)