頁(yè)播放監(jiān)控視頻:WEB播放器插件接入與排錯(cuò)指南)
剛過(guò)去的這個(gè)季度我連續(xù)被好幾個(gè)項(xiàng)目問(wèn)了差不多的事網(wǎng)頁(yè)上要播放攝像頭監(jiān)控視頻用哪個(gè)WEB播放器插件客戶裝的是某安防廠商的設(shè)備給了一堆接口文檔前端同事看著ActiveX、NPAPI、WebVideoCtrl一臉懵。這篇文章就把這套方案里從插件原理、環(huán)境準(zhǔn)備到代碼接入和排錯(cuò)的全過(guò)程整理出來(lái)希望對(duì)接了監(jiān)控需求的前端同學(xué)有點(diǎn)用。先說(shuō)明一下下文統(tǒng)一用“該廠商”代指做監(jiān)控設(shè)備的那家頭部企業(yè)不點(diǎn)名字。實(shí)際項(xiàng)目里它的設(shè)備市場(chǎng)占有率很高WEB播放器插件也是用得最多的方案之一。我會(huì)盡量把底層邏輯講透再給出能直接跑的代碼和排查路徑而不是只貼一段官方Demo讓你照抄。1. 為什么頁(yè)面放監(jiān)控視頻不能像放MP4那樣簡(jiǎn)單1.1 監(jiān)控?cái)z像頭輸出的不是瀏覽器認(rèn)識(shí)的視頻流很多前端同學(xué)第一次接到攝像頭預(yù)覽需求時(shí)第一反應(yīng)是給我一個(gè)RTSP地址我用video標(biāo)簽直接播放。這個(gè)想法很正常但現(xiàn)實(shí)很殘酷。視頻標(biāo)簽支持的是HTTP協(xié)議的漸進(jìn)式視頻文件以及HLS、DASH這類流媒體協(xié)議而絕大多數(shù)IPC網(wǎng)絡(luò)攝像頭和NVR網(wǎng)絡(luò)錄像機(jī)輸出的主碼流和子碼流都是RTSP協(xié)議走的是RTSP/RTP序列默認(rèn)端口是554。瀏覽器廠商根本沒(méi)有內(nèi)置RTSP解碼和傳輸能力你拿video去塞一個(gè)rtsp://192.168.1.64:554/Streaming/Channels/101瀏覽器只會(huì)甩給你一個(gè)“不支持的源”或者無(wú)限轉(zhuǎn)圈。這里面的原因是立體且復(fù)雜的RTSP涉及SDP會(huì)話協(xié)商、RTP分包、音視頻同步還要考慮TCP/UDP傳輸方式這些都不是Web標(biāo)準(zhǔn)協(xié)議棧的一部分。只要瀏覽器不支持前端寫(xiě)再多JavaScript也沒(méi)用。有人會(huì)問(wèn)那為什么YouTube、B站這些網(wǎng)站能直接在網(wǎng)頁(yè)上播直播因?yàn)樗鼈兒蠖艘呀?jīng)把流轉(zhuǎn)換成HLS或者DASH了服務(wù)器編碼和切片之后再拋給瀏覽器。這個(gè)轉(zhuǎn)碼過(guò)程是需要算力和延遲代價(jià)的。如果是監(jiān)控場(chǎng)景延遲超過(guò)一兩秒云臺(tái)操作和告警聯(lián)動(dòng)體驗(yàn)就會(huì)非常奇怪。1.2 插件方案是被“逼”出來(lái)的既然瀏覽器原生不支持RTSP那需要實(shí)現(xiàn)在網(wǎng)頁(yè)里看攝像頭的早期項(xiàng)目就只能靠插件。這個(gè)“插”字很形象瀏覽器沒(méi)有的能力通過(guò)在本地安裝一個(gè)組件來(lái)補(bǔ)充。這個(gè)組件能解RTSP協(xié)議、能調(diào)用系統(tǒng)的解碼器、能直接操作顯卡和窗口繪制畫(huà)面而頁(yè)面里的JavaScript只負(fù)責(zé)告訴它“你要播哪路流”“窗口在哪”畫(huà)面渲染實(shí)際發(fā)生在插件的窗口區(qū)域里。該廠商的WEB播放器插件就是這么來(lái)的。它并不是這幾年才出的新東西而是從IE時(shí)代一路演化過(guò)來(lái)的老將經(jīng)歷了ActiveX、OCX、NPAPI、Chrome擴(kuò)展、本地服務(wù)等好幾個(gè)階段。到今天它仍然是Windows環(huán)境下最穩(wěn)妥、延遲最低的監(jiān)控預(yù)覽方案之一前提是你能接受必須裝插件這件事。這樣做的好處非常明顯延遲低通常能控制在幾百毫秒功能全云臺(tái)控制、對(duì)講、抓圖、錄像、報(bào)警等都能用兼容性好對(duì)舊設(shè)備和舊系統(tǒng)支持完善。壞處也很突出只支持Windows和特定瀏覽器移動(dòng)端全部絕緣新版本的Chrome和Edge對(duì)舊插件方式越來(lái)越不友好信創(chuàng)和國(guó)產(chǎn)化環(huán)境幾乎用不了。如果你接觸過(guò)類似項(xiàng)目應(yīng)該能理解我這句話插件方案是權(quán)衡后的選擇不是最優(yōu)解但往往是當(dāng)前客戶系統(tǒng)里唯一“能用”的方案。2. WEB播放器插件的本質(zhì)與版本差異2.1 插件到底是什么ActiveX、NPAPI、本地服務(wù)要正確使用這套東西先別把它當(dāng)黑盒。該廠商的WEB播放器插件在不同階段有完全不同的形態(tài)理解這一點(diǎn)能省掉一大半排錯(cuò)時(shí)間。最早是ActiveX控件典型文件是一個(gè).ocx或.dll注冊(cè)到Windows系統(tǒng)里通過(guò)IE的ActiveX機(jī)制在頁(yè)面上渲染一個(gè)object標(biāo)簽區(qū)域。這個(gè)控件本質(zhì)上是一個(gè)可以嵌入到網(wǎng)頁(yè)的COM組件瀏覽器直接調(diào)用它播放視頻畫(huà)面甚至能嵌進(jìn)頁(yè)面布局。ActiveX的問(wèn)題大家都知道IE獨(dú)有、權(quán)限巨大、安全性差。但直到現(xiàn)在很多部隊(duì)、園區(qū)、能源行業(yè)的安防系統(tǒng)仍然跑在IE兼容模式下這一批存量項(xiàng)目基本都靠ActiveX撐著。后來(lái)Chrome快速崛起自己帶頭淘汰了NPAPI插件ActiveX在Chrome里徹底沒(méi)法用了。為了解決這個(gè)問(wèn)題該廠商推出了基于Chrome的插件變種有的是通過(guò)本地安裝一個(gè)獨(dú)立服務(wù)瀏覽器頁(yè)面通過(guò)WebSocket或者本地HTTP接口和這個(gè)服務(wù)通信服務(wù)負(fù)責(zé)拉流和解碼然后用某種方式把視頻數(shù)據(jù)推回頁(yè)面有的則做成了Chrome擴(kuò)展配合原生消息傳遞來(lái)工作。這兩種形態(tài)頁(yè)面上可能不再看到一個(gè)“控件”區(qū)域而是通過(guò)canvas或者video標(biāo)簽接收實(shí)時(shí)幀。所以你翻項(xiàng)目舊代碼時(shí)會(huì)看到兩種完全不同的調(diào)用方式一種是老式WebVideoCtrl通過(guò)object嵌入用classid指定控件另一種是后來(lái)基于WebSocket的JSSDK安裝插件服務(wù)后頁(yè)面通過(guò)地址和端口去連接本地服務(wù)。如果不先搞清楚項(xiàng)目里用的是哪一版拿新版SDK去對(duì)接老設(shè)備或者反過(guò)來(lái)都會(huì)遇到“插件已安裝但頁(yè)面不識(shí)別”的尷尬問(wèn)題。2.2 不同瀏覽器下插件形態(tài)和API差異在真實(shí)項(xiàng)目里同一個(gè)網(wǎng)頁(yè)可能要在IE、Chrome、EdgeChromium內(nèi)核、360安全瀏覽器這些不同瀏覽器里跑。不同瀏覽器的插件形態(tài)不同前端代碼也得跟著適配。先說(shuō)IE要使用ActiveX控件頁(yè)面里必須寫(xiě)一個(gè)object idwebVideoCtrl classidclsid:xxxx width... height.../object。然后JavaScript通過(guò)document.getElementById(webVideoCtrl)拿到控件對(duì)象再調(diào)用它的方法。這個(gè)方法也叫WebVideoCtrl但它是COM對(duì)象的方法不是純JS對(duì)象。需要注意IE的位數(shù)必須和ActiveX控件位數(shù)一致32位插件就要求用32位IE64位IE往往找不到控件。這一條在我實(shí)際項(xiàng)目里坑過(guò)很多人。再說(shuō)Chrome如果是新版插件頁(yè)面里一般不用寫(xiě)object而是引入一個(gè)官方JS文件里面封裝了WebVideoCtrl對(duì)象。這個(gè)對(duì)象內(nèi)置了和本地服務(wù)的通信邏輯比如通過(guò)HTTP或WebSocket連接本地的某個(gè)端口。安裝插件服務(wù)后本地服務(wù)會(huì)在127.0.0.1上監(jiān)聽(tīng)一個(gè)端口JS調(diào)用它的時(shí)候底層會(huì)去連接這個(gè)端口。如果端口被占用、服務(wù)沒(méi)啟動(dòng)、或者瀏覽器安全策略禁止了連接本地地址頁(yè)面就會(huì)出現(xiàn)“插件初始化失敗”。Edge和Chrome類似但要注意如果Edge版本過(guò)新可能對(duì)某些老的擴(kuò)展機(jī)制有更嚴(yán)格限制。360之類雙核瀏覽器極速模式基本和Chrome一致兼容模式可能走IE的邏輯。推薦做法是在項(xiàng)目里單獨(dú)維護(hù)一個(gè)“瀏覽器環(huán)境檢測(cè)”函數(shù)提前判斷當(dāng)前是IE還是Chrome然后加載對(duì)應(yīng)的JS和初始化邏輯。2.3 選型決策什么時(shí)候堅(jiān)持用插件我在做方案選型時(shí)會(huì)問(wèn)三個(gè)問(wèn)題第一客戶端是不是全部Windows第二有沒(méi)有必須用的瀏覽器版本要求第三移動(dòng)端要不要看視頻如果前兩條滿足、暫時(shí)不管移動(dòng)端那用插件方案是性價(jià)比最高的。如果有一條不滿足就要盡早考慮H5轉(zhuǎn)碼方案。插件方案還有一個(gè)隱形成本安裝和升級(jí)。就算你已經(jīng)接好了頁(yè)面客戶換了一臺(tái)新電腦首先要裝插件設(shè)置瀏覽器白名單否則頁(yè)面還是白的。很多商務(wù)人員給客戶演示往往就卡在這一步。所以在交付時(shí)最好做一個(gè)環(huán)境檢測(cè)頁(yè)面自動(dòng)提示“插件未安裝”“當(dāng)前瀏覽器不被支持”“請(qǐng)以管理員身份安裝”能極大減少電話支持。3. 接入前的環(huán)境準(zhǔn)備少一步后面全是坑3.1 安裝插件前的瀏覽器與系統(tǒng)檢查接入之前先花20分鐘把環(huán)境摸一遍。檢查系統(tǒng)是Windows幾檢查瀏覽器類型和版本號(hào)檢查瀏覽器是32位還是64位。特別是Windows Server系統(tǒng)IE增強(qiáng)安全配置默認(rèn)開(kāi)啟極其容易攔截插件必須先關(guān)閉或者把站點(diǎn)加入受信任區(qū)域。插件的安裝包一般放在設(shè)備附帶的光盤(pán)里或者從官網(wǎng)的下載中心按型號(hào)和SDK版本去下載。建議自己先下載一個(gè)測(cè)試環(huán)境裝好之后打開(kāi)IE進(jìn)入一個(gè)官方Demo頁(yè)面確認(rèn)能正常出畫(huà)面再拿這套環(huán)境去開(kāi)發(fā)。不要跳過(guò)這一步因?yàn)楹芏唷伴_(kāi)發(fā)半天發(fā)現(xiàn)插件沒(méi)生效”的坑其實(shí)都是環(huán)境沒(méi)搭好。安裝時(shí)如果殺毒軟件彈出警告不要盲目點(diǎn)攔截。ActiveX控件和本地服務(wù)很容易被誤報(bào)為風(fēng)險(xiǎn)程序。可以把安裝目錄加入白名單或者暫時(shí)關(guān)閉實(shí)時(shí)防護(hù)后再裝。裝完之后確認(rèn)兩個(gè)東西系統(tǒng)服務(wù)列表里有沒(méi)有對(duì)應(yīng)的服務(wù)進(jìn)程在運(yùn)行瀏覽器擴(kuò)展管理里有沒(méi)有對(duì)應(yīng)的插件擴(kuò)展被啟用。3.2 瀏覽器安全設(shè)置與本地控件權(quán)限如果你還在用IE這一步是必須的。打開(kāi)IE的“Internet選項(xiàng)”在“安全”選項(xiàng)卡里把站點(diǎn)加到“受信任的站點(diǎn)”域名最好用IP。然后把該區(qū)域的安全級(jí)別調(diào)到“低”或者自定義級(jí)別里把“ActiveX控件和插件”相關(guān)的幾個(gè)選項(xiàng)全部改成“啟用”或“提示”。還要勾選“允許運(yùn)行或安裝軟件即使簽名無(wú)效”否則控件簽名不在信任列表時(shí)會(huì)被攔下來(lái)。如果用的是Chrome和Edge的新版插件方式一般不涉及ActiveX但需要確認(rèn)瀏覽器沒(méi)有禁止“允許運(yùn)行不安全的內(nèi)容”。因?yàn)椴寮?wù)通常監(jiān)聽(tīng)在127.0.0.1而你的頁(yè)面可能是http://192.168.1.100:8080混合內(nèi)容策略可能會(huì)阻止瀏覽器訪問(wèn)本地HTTP服務(wù)。解決辦法是將站點(diǎn)設(shè)置為安全來(lái)源或者用HTTPS部署頁(yè)面又或者在Chrome標(biāo)記里臨時(shí)允許不安全內(nèi)容。這些細(xì)節(jié)在官方文檔里很少一次性寫(xiě)清楚實(shí)際接入時(shí)極常見(jiàn)。3.3 引入官方JS封裝理解初始化參數(shù)環(huán)境準(zhǔn)備好之后在頁(yè)面里引入官方提供的JS文件。老版本通常是webVideoCtrl.js新版本可能是一整個(gè)jessibuca風(fēng)格的對(duì)象。我下面以最主流的WebVideoCtrl命名來(lái)寫(xiě)。引入方式很簡(jiǎn)單script src/path/to/webVideoCtrl.js/script然后頁(yè)面上放一個(gè)用于承載視頻的容器div idplayWindow/div這個(gè)容器不必是video標(biāo)簽插件初始化時(shí)會(huì)自動(dòng)在里面創(chuàng)建視頻窗口或插件對(duì)象。你要做的是記錄這個(gè)容器的ID后面初始化時(shí)傳入。理解初始化參數(shù)比敲代碼更重要。初始化通常需要幾類參數(shù)窗口尺寸、是否啟用窗口拖拽、是否使用硬件加速、插件類型ActiveX還是本地服務(wù)。這些參數(shù)在不同版本中叫法不同但是大邏輯一致。示例var WebVideoCtrl; function initPlugin() { WebVideoCtrl new WebVideoCtrl(); WebVideoCtrl.I(playWindow, { bWndFull: true, iWndWidth: 640, iWndHeight: 480, oPlugin: { iType: 2, // 2表示視頻插件 bNoPlugin: false // 是否禁用插件 } }); }需要用I()方法去初始化容器這個(gè)命名很簡(jiǎn)略但它是官方API不是我們自己封裝的名字。初始化成功后可以調(diào)用CheckPluginVersion()之類的接口獲取插件版本號(hào)用于判斷是否安裝成功。如果這一步就報(bào)“對(duì)象不支持此操作”多半是插件環(huán)境沒(méi)生效后面所有調(diào)用都白搭。4. 手把手接入初始化、登錄設(shè)備、開(kāi)始播放4.1 構(gòu)建播放窗口容器容器大小會(huì)影響視頻拉伸效果推薦按顯示區(qū)的比例來(lái)設(shè)置。如果你的頁(yè)面是響應(yīng)式布局建議在window.resize事件里動(dòng)態(tài)調(diào)用插件窗口自適應(yīng)的方法避免切分辨率后畫(huà)面變形或出現(xiàn)黑邊。另一個(gè)容易忽略的點(diǎn)是在SPA框架Vue、React中容器元素可能在插件初始化后才掛載到DOM上導(dǎo)致插件找不到節(jié)點(diǎn)。所以最穩(wěn)妥的做法是在mounted或useEffect里等DOM渲染完成、nextTick之后再調(diào)用初始化。如果初始化太早容器寬高可能是0插件會(huì)認(rèn)為你不需要顯示畫(huà)面。4.2 初始化WebVideoCtrl假設(shè)你已經(jīng)引入了JS并且容器準(zhǔn)備好了下面這段就能完成初始化function initWebVideoCtrl() { if (!window.WebVideoCtrl) { console.error(WebVideoCtrl對(duì)象不存在請(qǐng)檢查JS引入或插件是否安裝); return false; } WebVideoCtrl new WebVideoCtrl(); WebVideoCtrl.I(playWindow, { bWndFull: true, iWndWidth: 960, iWndHeight: 540, iMaxSplit: 4, // 最大分屏數(shù) oPlugin: { iType: 2, bNoPlugin: false } }); // 可選注冊(cè)事件回調(diào) WebVideoCtrl.J({ onInit: function () { console.log(插件初始化成功); }, onError: function (errCode, errMsg) { console.error(插件錯(cuò)誤, errCode, errMsg); } }); return true; }J()方法類似于注冊(cè)事件監(jiān)聽(tīng)器。onInit在插件完全就緒后觸發(fā)之后你才可以去登錄設(shè)備。我建議把登錄和播放的邏輯全部放在onInit之后不要猜測(cè)“等個(gè)兩秒就調(diào)用”那樣容易在插件還沒(méi)準(zhǔn)備好時(shí)就拋出異常。4.3 登錄設(shè)備并獲取通道要讓插件去拉某臺(tái)攝像機(jī)的視頻必須先讓插件登錄這臺(tái)設(shè)備NVR或IPC。登錄需要設(shè)備的IP、HTTP端口、用戶名、密碼。這里的HTTP端口通常是80不是RTSP的554注意區(qū)分。代碼大致是var deviceInfo { szIp: 192.168.1.64, // 設(shè)備IP szPort: 80, // HTTP端口 szUserName: admin, szPassword: 12345 }; WebVideoCtrl.K(deviceInfo.szIp, deviceInfo.szPort, deviceInfo.szUserName, deviceInfo.szPassword, { success: function (data) { console.log(登錄成功, data); // 登錄成功后獲取通道列表 WebVideoCtrl.M(192.168.1.64, 80, { success: function (channels) { console.log(通道列表, channels); startPreview(0); // 播第一個(gè)通道 } }); }, error: function (err) { console.error(登錄失敗, err); } });這里K()是登錄M()是獲取通道列表。不同版本的方法名可能不同但流程一致。注意admin賬號(hào)密碼一定不要直接硬編碼在前端實(shí)際項(xiàng)目中應(yīng)該由用戶輸入或者后端下發(fā)一次性票據(jù)。我這個(gè)例子只是為了演示插件調(diào)用順序。如果設(shè)備有多個(gè)通道比如16路NVRchannels會(huì)是一個(gè)數(shù)組。你需要展示成下拉框或者按鈕組用戶選擇通道后再播放。沒(méi)有通道數(shù)據(jù)的設(shè)備一般是登錄失敗了或者賬號(hào)沒(méi)有視頻權(quán)限。4.4 啟動(dòng)實(shí)時(shí)預(yù)覽以及停止和清理拿到通道號(hào)后調(diào)用播放接口通常需要指定碼流類型主碼流高清、子碼流流暢。一個(gè)典型組合是大屏顯示用主碼流九宮格或者手機(jī)端用子碼流。切碼流的API一般是第四個(gè)參數(shù)傳0主碼流、1子碼流。function startPreview(channel) { WebVideoCtrl.N({ szIp: 192.168.1.64, szPort: 80, iChannelID: channel, iStreamType: 0, // 0主碼流1子碼流 iProtocolType: 0, // 0 TCP1 UDP success: function () { console.log(預(yù)覽啟動(dòng)); }, error: function (err) { console.error(預(yù)覽失敗, err); } }); }N()是開(kāi)始預(yù)覽O()是停止預(yù)覽具體方法名以你的SDK版本為準(zhǔn)。停止預(yù)覽后要釋放資源否則短時(shí)間頻繁切換通道會(huì)累積句柄最終導(dǎo)致插件崩潰。頁(yè)面卸載時(shí)一定記得清理function logoutAndStop() { WebVideoCtrl.O(); // 停止所有預(yù)覽 WebVideoCtrl.P(192.168.1.64, 80); // 登出設(shè)備 WebVideoCtrl.C(); // 釋放插件資源 }前后的調(diào)用順序不能亂先停止預(yù)覽再登出最后釋放插件。直接關(guān)頁(yè)面而不調(diào)停止預(yù)覽在部分版本里會(huì)導(dǎo)致后臺(tái)進(jìn)程殘留下次打開(kāi)頁(yè)面時(shí)端口被占用初始化失敗。5. 高頻API組合多畫(huà)面、云臺(tái)、抓圖、對(duì)講5.1 多畫(huà)面分屏切換監(jiān)控大屏場(chǎng)景中多畫(huà)面分屏是剛需。插件一般提供內(nèi)置的分屏能力你可以設(shè)置iMaxSplit最大分屏數(shù)為4、9、16等然后通過(guò)設(shè)置當(dāng)前窗口的分屏模式。我這里用偽API說(shuō)明思路// 切換為2x2四畫(huà)面 WebVideoCtrl.setSplit(4); // 在某個(gè)格子中播放指定通道 WebVideoCtrl.startPreviewByWindow(windowIndex, ip, port, channel, streamType);很多SDK內(nèi)部叫法不同但都有類似機(jī)制。分屏模式下要注意主碼流和子碼流的選擇要合理四個(gè)畫(huà)面同時(shí)走主碼流很容易讓NVR解碼能力吃緊導(dǎo)致畫(huà)面卡頓。建議分屏超過(guò)4路時(shí)優(yōu)先用子碼流單路放大時(shí)再切換主碼流。5.2 云臺(tái)控制與預(yù)置位云臺(tái)控制是監(jiān)控系統(tǒng)里高頻操作插件接口會(huì)提供方向控制、變倍、變焦、預(yù)置位設(shè)置、巡航等。方向控制一定要做“按下開(kāi)始、松開(kāi)停止”的邏輯如果只發(fā)一次轉(zhuǎn)動(dòng)指令設(shè)備會(huì)一直轉(zhuǎn)到限位才停。正確流程是// 按下開(kāi)始向上轉(zhuǎn)動(dòng) WebVideoCtrl.ptzControl(ip, port, channel, up, 0, function () {}); // 松開(kāi)停止 WebVideoCtrl.ptzControl(ip, port, channel, stop, 0, function () {});同時(shí)做防抖按下的時(shí)間間隔太短就忽略防止抖動(dòng)導(dǎo)致控制指令積壓。預(yù)置位調(diào)用一般先設(shè)置再巡航到目標(biāo)邏輯上先調(diào)用“設(shè)置預(yù)置位”接口再調(diào)用“轉(zhuǎn)到預(yù)置位”接口。需要注意不同設(shè)備支持的預(yù)置位號(hào)可能不同要先查設(shè)備能力。5.3 抓圖、本地錄像與倒帶回放業(yè)務(wù)側(cè)經(jīng)常要為“證據(jù)留存”做抓圖或錄像。插件提供了本地抓圖和本地錄像的API底層是通過(guò)插件直接抓取解碼后的畫(huà)面圖片格式一般是JPEG保存在本地磁盤(pán)。抓圖邏輯WebVideoCtrl.capturePicture(ip, port, channel, { success: function (data) { console.log(抓圖成功保存路徑:, data); }, error: function (err) { console.error(抓圖失敗, err); } });本地錄像則類似WebVideoCtrl.startRecord(ip, port, channel); WebVideoCtrl.stopRecord(ip, port, channel);要留意的是這里的“本地錄像”存的路徑在插件服務(wù)端機(jī)器上不是瀏覽器所在的機(jī)器。如果插件是本地服務(wù)模式保存路徑通常是服務(wù)配置里指定的目錄。實(shí)際項(xiàng)目中需要做服務(wù)端錄像時(shí)優(yōu)先通過(guò)后端調(diào)用設(shè)備SDK或ONVIF來(lái)做而不是依賴前端插件錄像因?yàn)榍岸虽浵窈苋菀妆磺謇砘蛑袛??;胤欧矫娌寮话阒С滞ㄟ^(guò)時(shí)間軸查詢錄像文件然后調(diào)用回放接口?;胤沤涌诤皖A(yù)覽接口最大的區(qū)別是要多傳一個(gè)時(shí)間段參數(shù)。實(shí)現(xiàn)回放時(shí)要注意時(shí)區(qū)問(wèn)題設(shè)備自身時(shí)區(qū)和瀏覽器時(shí)區(qū)不一致會(huì)導(dǎo)致查不到錄像。通常統(tǒng)一用設(shè)備本地時(shí)間查詢前端展示時(shí)再用瀏覽器時(shí)區(qū)處理。6. 真實(shí)排錯(cuò)經(jīng)歷插件裝好了就是不顯示畫(huà)面6.1 排查鏈路從頁(yè)面報(bào)錯(cuò)到系統(tǒng)服務(wù)我在處理這類問(wèn)題時(shí)習(xí)慣按鏈路排查頁(yè)面JS是否加載 - 初始化是否成功 - 插件服務(wù)是否運(yùn)行 - 網(wǎng)絡(luò)是否通 - 設(shè)備賬號(hào)是否有效 - 協(xié)議和碼流參數(shù)是否匹配。任何一環(huán)斷了現(xiàn)象都可能是同一個(gè)“畫(huà)面不顯示”。先打開(kāi)瀏覽器開(kāi)發(fā)者工具看Console有沒(méi)有報(bào)錯(cuò)。最常見(jiàn)的報(bào)錯(cuò)是WebVideoCtrl is not defined這說(shuō)明JS沒(méi)引入或引入順序有問(wèn)題。其次是對(duì)象不支持此操作這說(shuō)明JS引入了但插件對(duì)象創(chuàng)建失敗多半是ActiveX控件或擴(kuò)展沒(méi)生效。這時(shí)候不要急著看網(wǎng)絡(luò)先把本機(jī)環(huán)境和示例頁(yè)面對(duì)照一遍。如果頁(yè)面無(wú)報(bào)錯(cuò)但黑屏那么大概率是插件服務(wù)本身沒(méi)起來(lái)。打開(kāi)任務(wù)管理器搜索該廠商相關(guān)的進(jìn)程名比如xxxService.exe或xxxPlugin.exe如果不存在就去Windows服務(wù)里手動(dòng)啟動(dòng)。很多情況下插件安裝后服務(wù)是“手動(dòng)啟動(dòng)”類型只有頁(yè)面調(diào)用時(shí)才會(huì)拉起但如果啟動(dòng)條件不滿足就起不來(lái)。6.2 IE瀏覽器ActiveX被攔截的怪問(wèn)題有次客戶現(xiàn)場(chǎng)是Windows 7 IE11開(kāi)發(fā)機(jī)一切正??蛻魴C(jī)死活加載不出控件。我遠(yuǎn)程一看頁(yè)面底部彈了個(gè)黃色提示條“已阻止此網(wǎng)站使用ActiveX控件”。這是IE安全等級(jí)過(guò)高導(dǎo)致的。解決方案是把設(shè)備頁(yè)面地址加入受信任站點(diǎn)并把該站點(diǎn)區(qū)域的安全級(jí)別調(diào)低。但客戶是內(nèi)網(wǎng)管控不允許改IE設(shè)置最后只能讓管理員下發(fā)策略把域名加到白名單里。還有一個(gè)坑IE的“啟用內(nèi)存保護(hù)幫助減少聯(lián)機(jī)攻擊”選項(xiàng)會(huì)影響ActiveX運(yùn)行。如果勾選了“為增強(qiáng)保護(hù)模式啟用64位進(jìn)程”可能導(dǎo)致32位控件無(wú)法加載。解決辦法是在“Internet選項(xiàng)-高級(jí)”中取消勾選增強(qiáng)保護(hù)模式或者使用32位IE。6.3 Chrome下插件白屏本地服務(wù)與混合內(nèi)容排查新版Chrome下白屏先看瀏覽器右上角有沒(méi)有插件擴(kuò)展圖標(biāo)。如果沒(méi)有說(shuō)明擴(kuò)展未安裝或者被自動(dòng)停用。去chrome://extensions檢查擴(kuò)展是否存在重新啟用。如果擴(kuò)展在但頁(yè)面無(wú)法連接很可能是本地服務(wù)沒(méi)監(jiān)聽(tīng)在預(yù)期端口。用命令行驗(yàn)證netstat -ano | findstr 80808080換成實(shí)際端口。如果看不到本地監(jiān)聽(tīng)說(shuō)明服務(wù)沒(méi)起來(lái)。如果監(jiān)聽(tīng)正常但頁(yè)面還是白屏那就要看是不是混合內(nèi)容問(wèn)題。部署頁(yè)面是http://但是服務(wù)在https://或者反過(guò)來(lái)瀏覽器會(huì)攔截。最簡(jiǎn)單的方法是把服務(wù)地址改成http://127.0.0.1:端口并把頁(yè)面部署成http://協(xié)議保持一致。如果是純內(nèi)網(wǎng)環(huán)境一般不會(huì)強(qiáng)制上HTTPS。6.4 網(wǎng)絡(luò)與端口不通導(dǎo)致的預(yù)覽失敗插件初始化成功后調(diào)用播放接口提示“網(wǎng)絡(luò)錯(cuò)誤”或“連接超時(shí)”先別懷疑插件用命令行測(cè)一下設(shè)備IP和端口ping 192.168.1.64 telnet 192.168.1.64 8080是設(shè)備HTTP端口如果通了再測(cè)RTSP端口554。如果RTSP端口不通可能是網(wǎng)絡(luò)策略限制也可能是設(shè)備啟用了“流媒體加密”選項(xiàng)。有些設(shè)備默認(rèn)只開(kāi)放RTSP over TCP有些只開(kāi)放UDP你需要和網(wǎng)絡(luò)管理員確認(rèn)。前端這邊能做的就是嘗試把傳輸協(xié)議改成TCP或UDP來(lái)回測(cè)通常iProtocolType參數(shù)從0改成1能解決一部分UDP不通的問(wèn)題。如果多網(wǎng)卡環(huán)境插件拉流時(shí)可能走了錯(cuò)誤的網(wǎng)卡導(dǎo)致視頻斷斷續(xù)續(xù)或者連接失敗??梢栽诔跏蓟瘏?shù)里指定綁定的本機(jī)IP或者在設(shè)備網(wǎng)絡(luò)配置里設(shè)置多播/單播模式。這些都是從實(shí)際項(xiàng)目里試出來(lái)的官方文檔不一定寫(xiě)。7. 插件方案不是終點(diǎn)現(xiàn)代H5播放的替代路線7.1 插件的天花板移動(dòng)端和國(guó)產(chǎn)化環(huán)境做監(jiān)控平臺(tái)不可能只盯著Windows PC。很多用戶現(xiàn)在拿著一臺(tái)手機(jī)就想看實(shí)時(shí)畫(huà)面插件方案在這個(gè)場(chǎng)景下直接失效。還有信創(chuàng)環(huán)境UOS、麒麟系統(tǒng)瀏覽器不允許裝ActiveXChrome擴(kuò)展也要看兼容性。我在一個(gè)項(xiàng)目里遇到過(guò)客戶要求支持“國(guó)產(chǎn)瀏覽器環(huán)境且免插件”原來(lái)的ActiveX方案完全沒(méi)有可行性必須換一套思路。所以如果你現(xiàn)在剛著手設(shè)計(jì)一個(gè)新項(xiàng)目我的建議是明確一條“雙軌”思路。老設(shè)備、老系統(tǒng)保留插件通道作為兼容項(xiàng)新建設(shè)備和平臺(tái)盡量走H5流媒體方案用一個(gè)轉(zhuǎn)碼網(wǎng)關(guān)把RTSP源統(tǒng)一轉(zhuǎn)成瀏覽器能消費(fèi)的WebRTC或HTTP-FLV流頁(yè)面用支持WebRTC的播放器或者WASM解碼器播放。7.2 免插件方案轉(zhuǎn)碼網(wǎng)關(guān)與WebRTC市面上主流的免插件方案是“端側(cè)RTSP - 媒體網(wǎng)關(guān) - 瀏覽器播放”。媒體網(wǎng)關(guān)要做三件事拉取設(shè)備RTSP流轉(zhuǎn)碼或封裝成瀏覽器可播放的協(xié)議把流分發(fā)給瀏覽器端。WebRTC是延遲最低的基本能在500毫秒以內(nèi)接近插件體驗(yàn)但需要網(wǎng)關(guān)支持并做好信令交互。HTTP-FLV延遲稍高但勝在兼容性可以用mpegts.js或flv.js播放。另一種方案是GB28181國(guó)標(biāo)平臺(tái)設(shè)備通過(guò)國(guó)標(biāo)協(xié)議主動(dòng)注冊(cè)到平臺(tái)平臺(tái)輸出RTSP或WebRTC給前端。這種方式適合大規(guī)模視頻接入尤其是公安、園區(qū)類項(xiàng)目。缺點(diǎn)是需要維護(hù)一套國(guó)標(biāo)網(wǎng)關(guān)部署成本高。小項(xiàng)目如果只有十幾路攝像頭直接部署一個(gè)轉(zhuǎn)碼服務(wù)就夠了。前端部分我會(huì)用WebRTC播放器插件這里指純JS庫(kù)不是本地插件去接代碼邏輯和插件完全不同。初始化的是RTCPeerConnection收到流后塞進(jìn)video標(biāo)簽的srcObject。這要求后端信令服務(wù)打通前端才能拿到answer和候選地址。對(duì)不熟悉網(wǎng)絡(luò)協(xié)議的前端來(lái)說(shuō)這個(gè)學(xué)習(xí)曲線明顯更陡。7.3 如果必須兼容我建議這樣規(guī)劃我的經(jīng)驗(yàn)是做一個(gè)“環(huán)境檢測(cè)能力降級(jí)”的入口頁(yè)。頁(yè)面加載后先檢測(cè)當(dāng)前瀏覽器和操作系統(tǒng)如果滿足插件條件就加載插件播放器如果不滿足就加載H5播放器。兩種播放器的業(yè)務(wù)接口盡量封裝成一致抽象層對(duì)外暴露play(streamParams)和stop()內(nèi)部再根據(jù)環(huán)境路由。這樣既保護(hù)了存量項(xiàng)目又給新環(huán)境留了活路。要注意的是兩種播放器的事件體系不一樣比如“斷流重連”“碼流切換”一定要在抽象層里做統(tǒng)一。不要寫(xiě)一堆if (isIE) { ... } else { ... }散落在各處否則三個(gè)月后你自己都不想維護(hù)。我個(gè)人做了幾年安防平臺(tái)越來(lái)越覺(jué)得技術(shù)選型不是越新越好而是要看設(shè)備的可交付性。插件方案再老只要現(xiàn)場(chǎng)是WindowsChrome它依然是最省事、延遲最低的選擇。但如果你預(yù)見(jiàn)項(xiàng)目會(huì)擴(kuò)展移動(dòng)端或信創(chuàng)環(huán)境那從第一天起就要把H5播放器方案納入規(guī)劃哪怕先做成雙軌。最后分享一個(gè)細(xì)節(jié)無(wú)論用哪種方案都要在頁(yè)面里把“通道列表”和“設(shè)備登錄狀態(tài)”先拉出來(lái)用戶看不到畫(huà)面時(shí)優(yōu)先排查設(shè)備側(cè)不要一上來(lái)就懷疑播放器。前端只是管道源才是根本。