视频监控直播项目经验总结

项目说明

公司最近想要将智慧展厅中的海康摄像头布控系统功能 做成一套Web端的展示界面方便用于对外做展示。主要功能点:摄像视频监控播放,进出人员人脸识别报警,图表展示

  • 技术栈:React+Mobx+Echarts+Flvjs+Antd+Swiper

web端实现视频监控播放

关于海康摄像头输出

海康默认输出的是rtsp协议的视频流,而这在PC端是无法直接通过video的形式来播放的,所以这中间需要一层转码。

  • 常用的转码工具有FFmpeg,Live555
  • 转码方式: rstp–> rtmp,flv,hls

rtmp,flv,hls三者区别

由于rtmp需要flash支持,而chrome默认已经禁用了flash插件且到2020.12后停用flash,而flash本身也即将被淘汰,所以暂不考虑rtmp

- RTMP HLS HTTP-FLV
协议 TCP长连接 HTTP短连接 HTTP长链接
原理 每个时刻的数据收到后立刻转发 集合一段时间的数据,生成切片文件,并更新m3u8索引 将流媒体数据封装成 FLV 格式,然后通过 HTTP 协议传输给客户端【基于 HTTP/80 传输】。
延时 1-3秒 5-20秒(依切片情况) 1-3秒
Web支持 H5中需要用插件video.js 支持H5 H5中需要使用插件flv.js
其他 跨平台支持差,需要Flash技术支持 播放时需要多次请求,对网络质量要求高 需要flash技术支持(但可以通过flv.js来实现无flash播放),不支持多音频流,多视频流
缺陷 基于TCP,可能会被防火墙阻拦, 延迟高,网络要求高 会让流媒体资源缓存在本地客户端,在保密性方面不够好

项目应用方案

项目前后经过三次技术尝试:

  1. jsmpeg+canvas+websocket实现前端播放rtsp,参考html5播放rtsp方案
  2. hls+video.js实现H5播放【延迟高】
  3. flv+flv.js实现H5播放【最终方案】

canvas直接播放视频

这种方式前端用到jsmpeg插件,通过webSocket发送MPEG,前端通过js解析MPEG不断绘制canvas,包括音频。html5播放rtsp方案

  • ffmpeg解码:用于视频解码
  • node:搭建webSocket服务器,以及运行一个jsmpeg的js文件,
  • jsmpeg:运行主程序(绘制canvas播放视频及音频)

放弃原因:视频虽然能够正常播放,但容易花屏(搜索解决方案后说在ffmpeg解码时默认使用的是UDP协议,可以设置使用TCP进行解码可以解决),而且这套方案不适合放在生产环境上

hls视频流播放

起初用这套方案是因为后台只会转hls协议流视频,所以临时用这种方式实现播放,后端通过转码后给到的地址像这样http://xxxxxxx.m3u8,HLS是Apple公司推出的一种视频流协议,在IOS上兼容性可以说很好,而且视频做移动端项目,缺陷在于延迟较高,PC在播放容易卡顿

  • video.js:前端拿到HLS的视频地址后,只需要一个video.js用于视频播放即可(也可用hls.js包更小更方便,video.js在7.x版本后默认是引用了hls.js的)

flv直播

最终选择这套方案是因为flv相对hls延迟更低,可以无flash插件播放。最终确定后端实现rtsp到http-flv格式的转码,前端实现flv视频流的播放

  • 工具:flv.js
import React,{Component} from 'react'
import Flv from 'flv.js'

class VideoPlayer extends Component{
    videoNode = React.createRef()
    render(){
        setTimeout(()=>{
           this.reRenderVideo()
        },0)
        return(
            <div className="video-wrapper height-100">       
                <video style={{width:'100%',height:'100%'}} ref={this.videoNode} id="videoElement"></video>
            </div>
        )
    }
    componentDidMount(){
        if (this.flvPlayer) {
            this.flvPlayer.destroy();
        }
        var videoElement = this.videoNode.current;
        this.flvPlayer = Flv.createPlayer({
            type: 'flv',
            //后端最终给到的视频流地址
            url: 'http://192.168.0.253:9001/live?port=xxxx&app=xxxx&stream=bb646a3390', 
            muted:true,
        });
        this.flvPlayer.attachMediaElement(videoElement);
        this.flvPlayer.load();
        this.flvPlayer.play();
    }

    componentWillUnmount() {
        if (this.flvPlayer) {
            this.flvPlayer.destroy();
        }
    }
}
export default VideoPlayer;

Swiper引入问题记录

由于需要用到多视频画面切换以及进出人员展示的swiper所以想用引用这个库。这里只记录遇到的问题

问题一:在componentDidMount中设置new Swiper('.swiper-container')时,没有相应效果。【原因猜想:DOM挂载的异步延迟使得swiper在加载时没有拿到相应的DOM,导致渲染不成功】

  1. 引入swiper上
import Swiper from 'swiper'
import 'swiper/css/swiper.min.css'
  1. 解决:通过setTimeout(()=>{},0)的方式延后设置。估计是DOM挂载完成时间问题

Echarts使用问题记录

echarts算是用得很多的一款数据可视化工作库了,这次主要难点在于它的渐变色块的实现吧
解决方法:使用echarts内置的渐变色生成器来实现,通过封装实现

import echarts from "echarts";

/*
* @description 利用echarts渐变生成器来实现图表渐变色
* @param {string} color1  起始色
* @param {string} color2  终止色
* @return {object}  渐变对象
* */
export function generateGradient(color1,color2){
    return new echarts.graphic.LinearGradient(
        0, 0, 1, 0,
        [
            {offset: 0, color: color1},
            {offset: 1, color: color2},
        ]
    )
}

/*
* @description 将传入的渐变起止色二维数组转化为echarts可用的渐变值
* @param {array} colorList 渐变色数组
* @return {array} 渐变值数组
* */
export function generateColorList(colorList ){
    if(colorList.length<=0){return false}
    let res = colorList.map(item=>{
        return generateGradient(item[0],item[1])
    })
    return res
}

项目打包优化

由于使用的是create-react-app脚手架搭建的项目,在打包时生成的文件还是挺大的,最大的js达到了1.1MB,这导致首屏加载白屏时间很长差不多要10s了

1. 开启webpack打包分析

  • 借助webpack-bundle-analyzer插件可以实现对打包后文件的文件大小分析
  • npm install -D webpack-bundle-analyzer
  • 通过npm run eject可以不可逆的打开create-react-app的自定义webpack配置,从而实现自定义
// webpack.config.js
const BundleAnalyzerPlugin= require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
..省略千字..
plugins:[
//打包大小分析
      new BundleAnalyzerPlugin(
        {
          analyzerMode: 'server',
          // analyzerHost: '127.0.0.1',
          analyzerPort: 8889,
          reportFilename: 'report.html',
          //  应该是`stat`,`parsed`或者`gzip`中的一个。
          defaultSizes: 'parsed',
          //  在默认浏览器中自动打开报告
          openAnalyzer: true,
          //  如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
          generateStatsFile: false,
          statsFilename: 'stats.json',
          statsOptions: null,
          logLevel: 'info' /*日志级别。可以是'信息','警告','错误'或'沉默'。*/

        })
   ]
  • 效果如下图
    在这里插入图片描述

2. 服务端开启Gzip

  • 这一步让运维小哥做好对应服务的Gzip开启就好了,自己测试玩儿的服务器也可以用如下配置来开启某一服务的Gzip
location /cameraControl {
    # 映射服务器
    proxy_pass http://47.98.146.53:81/camera-control;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    index  index.html index.htm index.jsp;
    # 开启gzip压缩
    gzip  on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/javascript;
    gzip_disable "MSIE [1-6]\.";
    gzip_vary on;

}
  • 开启以后,通过chrome控制台可以查看是否开启成功,方法如下:打开Network–》在信息table头处右键–》Response Header–》勾选Content-Encoding
    在这里插入图片描述

3. 引入的antd V3.24.0 默认引入的icons资源过大

在通过webpack-bundle-plugin打包分析可以看出,整个打包出来最大的js文件中,包含antd的内容也是最大的,而antd中光icons资源都占有了483K左右(parsed),优化方案:通过将icons资源单独打包出来,并异步加载引用实现主js包大小的缩减。【目前版本的antd即使不引用Icon组件一样会有这样么大的包,所以只能通过优化打包的方式缩减,未来antd优化了这一部分issue就可以再去除这步】

  • npm install webpack-ant-icon-loader
// webpack.config.js
optimization:{
	splitChunk:{
		chunks:function(chunk){
          // 这里的name 可以参考在使用`webpack-ant-icon-loader`时指定的`chunkName`
          return chunk.name !== 'antd-icons';
        },
	}
}
  • 最终打包后会发现多了一个antd-icons.14c2af0f.chunk.js文件,这就是分离出来的antd-icon,

最终效果图

在这里插入图片描述

发布了114 篇原创文章 · 获赞 146 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/Sophie_U/article/details/102794245