李丹清,韓利峰,李嘉曾,吳麗梅,張立園,陳永忠
(1.上海交通大學(xué) 軟件工程學(xué)院,上海 200240;2.中國(guó)科學(xué)院上海應(yīng)用物理研究所 系統(tǒng)工程部,上海 201800)
視頻監(jiān)控系統(tǒng)經(jīng)歷了由模擬到數(shù)字的轉(zhuǎn)變,目前發(fā)展到第三代,通過利用多媒體壓縮算法,網(wǎng)絡(luò)通訊以及人工智能,更加趨向于網(wǎng)絡(luò)化和智能化[1]。
伴隨著網(wǎng)絡(luò)通訊技術(shù)的進(jìn)步,大型SCADA(Supervisory Control And Data Acquisition)系統(tǒng)和視頻監(jiān)控系統(tǒng)的平臺(tái)融合、信號(hào)聯(lián)動(dòng)[2-4]逐漸成為可能。
在電力行業(yè)和軌道交通行業(yè)初步解決了系統(tǒng)間人為切換,但沒有互動(dòng)的情況[5,6]。解決方案是SCADA系統(tǒng)的操作處理,通過事件的方式觸發(fā)執(zhí)行機(jī)構(gòu)動(dòng)作,同時(shí)通過聯(lián)動(dòng)服務(wù)器彈出視頻畫面,觀察事件處理狀況。這種簡(jiǎn)單的互動(dòng)不具有視頻監(jiān)控系統(tǒng)縱深層次的優(yōu)化和橫向平臺(tái)擴(kuò)展的設(shè)計(jì)理念,況且越來越多的需求體現(xiàn)在視頻、環(huán)境、生產(chǎn)、運(yùn)行等全面信息的深度融合。
解決平臺(tái)間信息的深度融合需要解決以下問題:統(tǒng)一的平臺(tái)下能同時(shí)支持SCADA通訊協(xié)議和視頻傳輸通訊協(xié)議;擁有事件處理機(jī)制,能實(shí)現(xiàn)兩系統(tǒng)之間的事件傳遞;擁有可靠的數(shù)據(jù)傳輸通道,解決客戶端信號(hào)數(shù)據(jù)和視頻數(shù)據(jù)的傳輸;擁有前端的數(shù)據(jù)渲染能力,能同時(shí)渲染實(shí)時(shí)變量數(shù)據(jù)和視頻流數(shù)據(jù);具有深度融合所需要的數(shù)據(jù)處理,圖像處理、智能算法能力。
Nodejs平臺(tái)具有以上描述的系統(tǒng)集成和深度融合的能力。首先,它是一個(gè)讓JavaScript運(yùn)行在服務(wù)端的開發(fā)平臺(tái),采用Google-V8引擎作為編譯器,使得JavaScript的執(zhí)行速度遠(yuǎn)超Ruby、Python等腳本語言;另外,Nodejs具有開發(fā)實(shí)時(shí)監(jiān)控系統(tǒng)的能力,它采用事件驅(qū)動(dòng)、異步編程、非阻塞模式的IO處理工作機(jī)制,為其帶來在相對(duì)低系統(tǒng)資源耗用下的高性能與出眾的負(fù)載能力,非常適合用作依賴各種IO資源的中間層服務(wù),可完美解決數(shù)據(jù)IO密集型、分布式部署環(huán)境下的實(shí)時(shí)應(yīng)用系統(tǒng)[7-9]。
其次,Nodejs具有強(qiáng)大軟件包管理工具npm,它的在線代碼庫(kù)包含功能齊全的代碼模塊,如項(xiàng)目中使用的Web網(wǎng)站架設(shè)模塊Express,WebSocket通訊模塊socket.io,EPICS libCA接口模塊,node-epics、OpenCV算法模塊等都可以通過npm管理器安裝[10,11]。
再者,Nodejs可以通過FFI(Foreign Function Interface)調(diào)用本地C/C++鏈接庫(kù),把視頻采集、解碼、算法處理等集成到平臺(tái)之下[12,13]。
本系統(tǒng)的開發(fā)就是基于Nodejs平臺(tái),把Web網(wǎng)站服務(wù),EPICS監(jiān)控系統(tǒng),視頻采集融合在一起。結(jié)合中科院TMSR(Thermal Molten Salt Reactor)核能項(xiàng)目,完成一套綜合實(shí)驗(yàn)室安全監(jiān)控系統(tǒng)的開發(fā)。該系統(tǒng)實(shí)現(xiàn)了對(duì)項(xiàng)目?jī)?nèi)的放射化學(xué)、熔鹽化學(xué)、高溫材料、同步輻照、電解制氫、集熱傳熱等實(shí)驗(yàn)室內(nèi)環(huán)境、設(shè)備安全、火警、視頻等信息的一站式監(jiān)控[14]。
如圖1所示,本系統(tǒng)中使用Express框架建立Web網(wǎng)站。Express是由Nodejs提供的一款精簡(jiǎn)、靈活的Web開發(fā)框架。它提供了路由,模塊支持,模板引擎,以及中間件等相關(guān)功能[15]。
系統(tǒng)服務(wù)器通過兩種方式與客戶端進(jìn)行通信:一種是HTTP協(xié)議。客戶端發(fā)送Get請(qǐng)求或者Post請(qǐng)求,HTTP服務(wù)器在接受到請(qǐng)求時(shí),通過路由機(jī)制以及中間件處理,來傳送頁面靜態(tài)數(shù)據(jù);另一種是WebSocket協(xié)議。這種協(xié)議通過一次握手,建立瀏覽器和服務(wù)器之間一條可復(fù)用的通道,可以讓服務(wù)器主動(dòng)并且不斷推送信息,來傳送實(shí)時(shí)性動(dòng)態(tài)數(shù)據(jù)[16,17]。
圖1 實(shí)驗(yàn)室監(jiān)控系統(tǒng)軟件架構(gòu)Fig.1 Laboratory monitoring system software architecture
系統(tǒng)通過socket.io建立客戶端與服務(wù)端的TCP連接對(duì)象,通過IP地址和端口號(hào),規(guī)定目標(biāo)服務(wù)器。服務(wù)器在接受到來自客戶端的請(qǐng)求之后就會(huì)建立相應(yīng)的長(zhǎng)連接來發(fā)送實(shí)時(shí)數(shù)據(jù)。
系統(tǒng)服務(wù)器端與第三方實(shí)時(shí)系統(tǒng)通信來獲取實(shí)時(shí)的數(shù)據(jù),其中包括火災(zāi)警報(bào)系統(tǒng),EPICS實(shí)時(shí)信號(hào)監(jiān)控系統(tǒng)和視頻監(jiān)控系統(tǒng)。與火災(zāi)警報(bào)系統(tǒng)的通信依賴于Modbus接口,與EPICS實(shí)時(shí)信號(hào)監(jiān)控系統(tǒng)的通信依賴于libCA接口,而與視頻監(jiān)控系統(tǒng)的通信依賴于視頻軟件供應(yīng)商提供的SDK[18]。
node-ffi是一個(gè)Nodejs的第三方功能模塊,本質(zhì)上是一個(gè)已經(jīng)將Google-V8封裝好的專門用來調(diào)用動(dòng)態(tài)庫(kù)/靜態(tài)庫(kù)的功能插件。本系統(tǒng)所使用的node-modbus和node-epics模塊的內(nèi)部機(jī)制就是利用node-ffi對(duì)相應(yīng)接口的封裝,使其能在Nodejs環(huán)境下運(yùn)行。但是本系統(tǒng)的視頻軟件的SDK并沒有可以在Nodejs環(huán)境下運(yùn)行的API,所以必須手動(dòng)利用node-ffi對(duì)相應(yīng)的SDK進(jìn)行封裝并加以利用。
系統(tǒng)在客戶端通過JQuery、Angular、Echart、DataTable等基于JavaScript的框架進(jìn)行數(shù)據(jù)可視化,通過Canvas來進(jìn)行視頻圖像的顯示。
圖2 實(shí)時(shí)預(yù)覽流程圖Fig.2 Real-time preview flowchart
圖3 實(shí)時(shí)視頻監(jiān)測(cè)實(shí)現(xiàn)流程圖Fig.3 Real-time video monitoring implementation flowchart
本系統(tǒng)的視頻軟件SDK提供了視頻圖像采集和視頻播放控制的軟件開發(fā)包。它們提供了可以供C/C++、C#和Java調(diào)用的API,但并沒有提供JavaScript調(diào)用的API。因此,需要通過node-ffi封裝相應(yīng)的API以供JavaScript調(diào)用。封裝好的API就可以在Nodejs平臺(tái)上直接調(diào)用并且進(jìn)行功能模塊的開發(fā)。
2.1.1 程序?qū)崿F(xiàn)流程
SDK提供了設(shè)備網(wǎng)絡(luò)SDK和播放庫(kù)SDK。設(shè)備網(wǎng)絡(luò)SDK主要提供了實(shí)時(shí)碼流預(yù)覽、錄像文件下載、云臺(tái)控制、日志管理、格式化硬盤、參數(shù)配置等相關(guān)功能的開發(fā)模塊。播放庫(kù)SDK主要用于播放控制的相關(guān)功能的開發(fā)。
如圖2所示,本系統(tǒng)主要起到實(shí)時(shí)監(jiān)測(cè)的功能,故調(diào)用SDK中的實(shí)時(shí)預(yù)覽功能開發(fā)流程。根據(jù)實(shí)時(shí)預(yù)覽流程完成程序?qū)崿F(xiàn)流程,如圖3所示。
首先,需要通過NET_DVR_Init函數(shù)完成對(duì)設(shè)備的初始化,包括網(wǎng)絡(luò)初始化和內(nèi)存預(yù)分配等。之后設(shè)置設(shè)備連接時(shí)間(NET_DVR_SetConnectTime)和設(shè)置設(shè)備重連次數(shù)與時(shí)間(DVR_SetReconnect)。
運(yùn)行注冊(cè)函數(shù)(NET_DVR_Login_V30)需要傳入設(shè)備IP、端口號(hào)、用戶名、密碼等參數(shù)以供完成相應(yīng)的設(shè)備注冊(cè),從而獲取相應(yīng)設(shè)備的使用權(quán)限。
NET_DVR_SetExceptionCallBack_V30函數(shù)用來捕捉異常信號(hào)。此函數(shù)基于事件驅(qū)動(dòng),如果產(chǎn)生異常信號(hào)則觸發(fā)其回調(diào)函數(shù)fExceptionCallBack來進(jìn)一步處理異常信息。
NET_DVR_RealPlay_V40接口函數(shù)用于啟動(dòng)預(yù)覽,此函數(shù)通過傳入注冊(cè)設(shè)備時(shí)返回的句柄來獲取需要播放的視頻數(shù)據(jù)的內(nèi)存空間,也是基于事件驅(qū)動(dòng),每當(dāng)產(chǎn)生視頻流數(shù)據(jù)時(shí),觸發(fā)其內(nèi)部回調(diào)函數(shù)fRealDataCallBack。
為了便于視頻圖像的顯示與傳送,要求所有的視頻幀圖像數(shù)據(jù)以JPEG格式進(jìn)行編碼,同時(shí)JPEG格式數(shù)據(jù)必須存于內(nèi)存中,SDK提供了相應(yīng)的內(nèi)存抓圖接口函數(shù)PlayM4_GetJPEG。
為了調(diào)用此接口函數(shù),需調(diào)用播放庫(kù)接口函數(shù)PlayM4_SetDecCallBack,此函數(shù)將圖像幀數(shù)據(jù)轉(zhuǎn)交給內(nèi)部回調(diào)函數(shù)DecCBFUN來處理,在DecCBFUN內(nèi)部調(diào)用PlayM4_GetJPEG將視頻圖像數(shù)據(jù)編碼成JPEG格式。
2.1.2 數(shù)據(jù)類型封裝
Nodejs是基于JavaScript運(yùn)行的平臺(tái)。JavaScript的變量基于弱類型,變量在聲明時(shí)并不需要說明其類型。它的變量類型只有在初始化時(shí),才得以確定。同理,它的函數(shù)的返回類型以及參數(shù)類型在函數(shù)聲明時(shí)不需要確定,只有調(diào)用函數(shù),傳入實(shí)參時(shí),類型才能夠確定。而C/C++的變量是基于強(qiáng)類型,也就是變量在聲明時(shí)就必須指明具體的類型。對(duì)于函數(shù)聲明也是如此,函數(shù)的返回類型以及參數(shù)類型在函數(shù)聲明時(shí)也必須確定。因此,通過JavaScript調(diào)用基于C/C++編寫的接口函數(shù)時(shí),首先需要解決的是變量類型的封裝與預(yù)聲明的問題。
為了完成變量類型的封裝,首先必須引入3個(gè)第三方功能模塊,分別是ref、ref-array與ref-struct。
安裝完畢后,3個(gè)模塊的文件包置入Express框架的modules文件夾下,通過require函數(shù)引入即可使用。其中,ref模塊用于變量基本類型與指針類型的封裝,ref-array模塊用于數(shù)組類型的數(shù)據(jù)封裝與預(yù)聲明,而ref-struct模塊則用于結(jié)構(gòu)體的類型封裝與預(yù)聲明。
動(dòng)態(tài)庫(kù)調(diào)用:
通過ref、ref-array與ref-struct模塊完成數(shù)據(jù)類型的封裝與預(yù)聲明后,就可以調(diào)用node-ffi模塊封裝相應(yīng)的動(dòng)態(tài)庫(kù)的接口函數(shù)。
通用格式如下:
var NewFun=new ffi.Library(“動(dòng)態(tài)庫(kù)路徑”,{
‘調(diào)用函數(shù)名1’:[‘函數(shù)返回類型1’,[‘形參類型1’,‘形參類型2’,‘形參類型3’…]})
封裝動(dòng)態(tài)庫(kù)接口函數(shù)需要輸入以下參數(shù):
1)動(dòng)態(tài)庫(kù)路徑:SDK動(dòng)態(tài)庫(kù)所在路徑。
2)調(diào)用函數(shù)名:為SDK動(dòng)態(tài)庫(kù)對(duì)應(yīng)API的函數(shù)名。
3)函數(shù)返回類型:通過數(shù)據(jù)封裝后,預(yù)聲明的數(shù)據(jù)類型,用于定義函數(shù)返回類型。
4)形參類型:通過數(shù)據(jù)封裝后,預(yù)聲明的數(shù)據(jù)類型,用于定義形參類型。
封裝后的動(dòng)態(tài)庫(kù)利用之前聲明的JavaScript實(shí)例(NewFun)進(jìn)行調(diào)用。
通過這種通用格式,封裝實(shí)時(shí)預(yù)覽開發(fā)模塊中需要的API,通過這些API完成實(shí)時(shí)預(yù)覽的功能開發(fā),將功能模塊封裝成JavaScript庫(kù),對(duì)外生成可以直接調(diào)用的API(getVideo),通過向此函數(shù)中傳入設(shè)備IP、端口號(hào)、用戶名以及密碼,直接通過它的回調(diào)函數(shù)輸出JPEG格式的視頻圖像數(shù)據(jù)并加以處理。
如圖4所示,系統(tǒng)使用基于Nodejs的第三方功能模塊socket.io建立TCP/WebSocket連接。連接需要從客戶端和服務(wù)器兩方面進(jìn)行建立,在兩者之間建立一個(gè)可以雙向傳送數(shù)據(jù)的通道,該通道在一次連接之后便可以復(fù)用,完成數(shù)據(jù)實(shí)時(shí)推送,事件的監(jiān)聽或者事件的發(fā)射。
圖4 WebSocket連接建立流程Fig.4 WebSocket Connection establishment process
在服務(wù)器方面,先實(shí)例化socket.io,生成對(duì)象io。利用io提供的API(io.listen)監(jiān)聽HTTP服務(wù)器,等待客戶端的連接信號(hào)。在連接完成之后,根據(jù)客戶端的Get請(qǐng)求,調(diào)用JavaScript庫(kù)的API(getVideo),完成相應(yīng)設(shè)備的視頻圖像數(shù)據(jù)的獲取并編碼(成為JPEG格式的圖像)。然后通過io對(duì)象的API(emit)發(fā)射圖像數(shù)據(jù),將圖像數(shù)據(jù)發(fā)送到客戶端。最后在接收客戶端發(fā)來的停止信號(hào)后,調(diào)用io的監(jiān)聽函數(shù)進(jìn)行事件獲取和視頻推送停止處理。
在客戶端方面,通過在腳本中引入socket.io.js來初始化WebSocket協(xié)議,之后創(chuàng)建Connect類用于連接服務(wù)器,并且在實(shí)例化Connect對(duì)象時(shí)通過傳入IP和端口號(hào)的方式確定需要連接的目標(biāo)服務(wù)器,在Connect類內(nèi)部創(chuàng)建監(jiān)聽函數(shù)用于監(jiān)聽來自于服務(wù)器的連接成功事件和視頻圖像數(shù)據(jù)傳送成功事件,在播放確認(rèn)(submit)后,發(fā)送基于HTTP的Get請(qǐng)求至服務(wù)器,HTTP服務(wù)器在接收到Get請(qǐng)求后,調(diào)用相應(yīng)的庫(kù)函數(shù),并利用WebSocket協(xié)議發(fā)送視頻圖像數(shù)據(jù)到客戶端,客戶端接收到視頻圖像數(shù)據(jù)后,調(diào)用Socket.on(‘Canvas’,fun(data))所定義的事件回調(diào)函數(shù),對(duì)收到的視頻圖像數(shù)據(jù)進(jìn)行處理和顯示。如希望停止播放,發(fā)送停止信號(hào)至服務(wù)器,對(duì)應(yīng)的調(diào)用Socket.on(‘disconnect’,fun())來做停止播放后的后續(xù)處理。
圖5 信號(hào)聯(lián)動(dòng)交互圖Fig.5 Signal linkage interaction diagram
Canvas是HTML5的新引進(jìn)的一個(gè)標(biāo)簽,主要是提供了畫布的功能,開發(fā)者可以使用Canvas以及其提供的API,在畫布上繪制想要繪制的圖像,也可以將BMP圖像以及JPEG圖像,直接在Canvas畫布上直接顯示。當(dāng)然Canvas本身沒有畫圖的屬性,它是通過獲取繪制上下文(Rendering Context),并利用這種繪制上下文來進(jìn)行繪圖或圖像顯示。Canvas的繪制上下文,現(xiàn)今有兩種:Canvas2D所提供的API和WebGL所提供的API,前者往往用于繪制2D圖像和2D圖片顯示,后者主要應(yīng)用于繪制3D圖形[19]??紤]到實(shí)時(shí)視頻監(jiān)控系統(tǒng)本身特點(diǎn),這里選用Canvas2D來顯示圖像。
在服務(wù)器接收到來自攝像頭的視頻圖像數(shù)據(jù)后,為了方便Nodejs進(jìn)行調(diào)用和處理,系統(tǒng)利用Nodejs所提供緩沖區(qū)技術(shù)(buffer)來接收并封裝視頻圖像數(shù)據(jù)。通過buffer提供的編碼功能將視頻圖像數(shù)據(jù)轉(zhuǎn)化成base64格式的字符串,以方便客戶端顯示和數(shù)據(jù)推送。
在客戶端接收到base64格式的JPEG圖像數(shù)據(jù)后,利用Canvas提供的API進(jìn)行圖像實(shí)時(shí)顯示。
基本實(shí)現(xiàn)流程如下:
1)通過標(biāo)簽的ID獲取canvas對(duì)象
var canvas=document.getElementById(‘videostream’)
2)獲取Canvas2D的繪制上下文
var context=canvas.getContext(‘2d’)
3)創(chuàng)建圖像對(duì)象來接收來自服務(wù)端的視頻數(shù)據(jù)
var imageObj=new Image()
4)通過圖像源的方式,將base64編碼的JPEG格式的圖像數(shù)據(jù)嵌入到圖像對(duì)象中
imageObj.src=“data:image/jpeg;base64,”+data
5)將圖像數(shù)據(jù)通過Canvas2D的繪制上下文進(jìn)行顯示
context.drawImage(imageobj,0,0,context.width,context.height)
圖6 TMSR視頻監(jiān)測(cè)系統(tǒng)頁面圖Fig.6 TMSR Video monitoring system page diagram
如圖5所示,本系統(tǒng)基于Nodejs集成火災(zāi)警報(bào)系統(tǒng),EPICS信號(hào)監(jiān)控系統(tǒng)以及視頻監(jiān)控系統(tǒng),在客戶端登陸后,系統(tǒng)需獲取火災(zāi)警報(bào)信號(hào),EPICS實(shí)時(shí)數(shù)據(jù)以及實(shí)時(shí)視頻圖像數(shù)據(jù)。通過HTML5,CSS等渲染工具,以網(wǎng)頁的形式將數(shù)據(jù)顯示以供給用戶查看。在火災(zāi)警報(bào)系統(tǒng)或EPICS實(shí)時(shí)系統(tǒng)發(fā)出異常信號(hào)時(shí),系統(tǒng)通過Nodejs服務(wù)器將信號(hào)加工之后發(fā)送給客戶端,客戶端瀏覽器彈出新的瀏覽界面,之后新的瀏覽界面發(fā)送Get信號(hào)給Nodejs服務(wù)器,在服務(wù)器獲取到發(fā)送出異常信號(hào)的實(shí)驗(yàn)室視頻數(shù)據(jù)后,通過WebSocket協(xié)議將實(shí)時(shí)視頻圖像數(shù)據(jù)發(fā)送給客戶端。客戶端新界面接收到圖像數(shù)據(jù)后,通過Canvas顯示實(shí)時(shí)視頻,如圖6所示。
本文著重闡述Nodejs平臺(tái)下兩系統(tǒng)融合的方法,網(wǎng)絡(luò)攝像頭通訊接口的封裝,基于WebSokcet技術(shù)的實(shí)時(shí)視頻數(shù)據(jù)傳輸,以及基于Canvas前端視頻流的顯示等。在TMSR研究堆的實(shí)驗(yàn)室安全監(jiān)控系統(tǒng)中的應(yīng)用,為核能項(xiàng)目的實(shí)驗(yàn)開展提供了安全保障。