視頻監控直播項目經驗總結

項目說明

公司最近想要將智慧展廳中的海康攝像頭布控系統功能 做成一套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,

最終效果圖

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章