日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

webRTC实战总结

發(fā)布時(shí)間:2023/12/18 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 webRTC实战总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

前言

前段時(shí)間一直在忙一個(gè)基于WebRTC的PC和移動(dòng)端雙向視頻的項(xiàng)目。第一次接觸webRTC,難免遇到了許多問題,比如:webRTC移動(dòng)端兼容性檢測(cè),如何配置MediaStreamConstraints, 信令(iceCandidate, sessionDescription)傳輸方式的選擇,iceCandidate和sessionDescription設(shè)置的先后順序,STUN和TURN的概念,如何實(shí)現(xiàn)截圖及錄制視頻及上傳圖片和視頻功能,如何高效跟蹤錯(cuò)誤等等。好記性不如爛筆頭,特寫此文以記之。

移動(dòng)端兼容性

對(duì)PC端來說,webRTC早已被各大瀏覽器支持了,Chrome 28,FF22,Edge…隨著不久之前發(fā)布的IOS11也宣布支持webRTC及getUserMedia,webRTC在移動(dòng)端的應(yīng)用前景也令人憧憬。

具體到實(shí)際項(xiàng)目中,經(jīng)過測(cè)試,各大國產(chǎn)安卓手機(jī)自帶的瀏覽器基本不支持webRTC,但這些安卓手機(jī)的微信內(nèi)置瀏覽器均能良好地支持webRTC,雖然Chrome及Firefox的移動(dòng)端版本也能良好的支持webRTC,但國情決定了微信內(nèi)置瀏覽器作為最佳切入點(diǎn)。另一方面。IOS11中微信內(nèi)置瀏覽器還不支持webRTC(我堅(jiān)信不久的將來就會(huì)支持),但在Safari中能夠完美支持。因此本項(xiàng)目選擇了微信公眾號(hào)為切入點(diǎn),通過檢測(cè)userAgent引導(dǎo)IOS11用戶在Safari中打開頁面。

檢測(cè)webRTC的可行性,主要從getUserMedia和webRTC本身來入手:

function detectWebRTC() { const WEBRTC_CONSTANTS = ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer']; const isWebRTCSupported = WEBRTC_CONSTANTS.find((item) => { return item in window; }); const isGetUserMediaSupported = navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia; if (!isWebRTCSupported || typeof isGetUserMediaSupported === 'undefined' ) { return false; } return true; }

如果返回false,再去檢測(cè)userAgent給予用戶不支持的具體提示。

配置MediaStreamConstraints

所謂MediaStreamConstraints,就是navigator.mediaDevices.getUserMedia(constraints)傳入的constraints,至于它的寫法及功能,參考MDN,本文不做贅述。我在這里想要強(qiáng)調(diào)的是,對(duì)于移動(dòng)端來說控制好視頻圖像的大小是很重要的,例如本項(xiàng)目中想要對(duì)方的圖像占據(jù)全屏,這不僅是改變video元素的樣式或者屬性能做到的,首先要做的是改變MediaStreamConstraints中的視頻分辨率(width, height),使其長寬比例大致與移動(dòng)端屏幕的類似,然后再將video元素的長和寬設(shè)置為容器的長和寬(例如100%)。

另外對(duì)于getUserMedia一定要捕獲可能出現(xiàn)的錯(cuò)誤,如果是老的API,設(shè)置onErr回調(diào),如果是新的(navigator.mediaDevices.getUserMedia),則catch異常。這樣做的原因:getUserMedia往往不會(huì)完全符合我們的預(yù)期,有時(shí)即使設(shè)置的是ideal的約束,仍然會(huì)報(bào)錯(cuò),如果不追蹤錯(cuò)誤,往往一臉懵逼。這也是后文要提到的高效追蹤錯(cuò)誤的方法之一。

搭建信令傳輸服務(wù)

要傳輸?shù)男帕畎▋蓚€(gè)部分:sessionDescription和iceCandidate。為了便于傳輸可將其處理成字符串,另一端接收時(shí)還原并用對(duì)應(yīng)的構(gòu)造函數(shù)構(gòu)造對(duì)應(yīng)的實(shí)例即可。

webRTC并沒有規(guī)定信令的傳輸方式,而是完全由開發(fā)者自定義。常見的方式有短輪詢、webSocket(socket.io等),短輪詢的優(yōu)點(diǎn)無非是簡單,兼容性強(qiáng),但在并發(fā)量較大時(shí),服務(wù)器負(fù)荷會(huì)很重。而webSocket就不存在這個(gè)問題,但webSocket搭建起來較為復(fù)雜,并不是所有的瀏覽器都支持websocket。綜合來說socket.io是個(gè)不錯(cuò)的解決方案,事件機(jī)制和自帶的房間概念對(duì)撮合視頻會(huì)話都是天然有利的,并且當(dāng)瀏覽器不支持websocket時(shí)可以切換為輪詢,也解決了兼容性的問題。

發(fā)起視頻會(huì)話的流程

可以看到無論是發(fā)起方還是接受方,第一步都是getUserMedia獲取本地媒體流,然后新建一個(gè)RTCPeerConnection實(shí)例,并指定好onicecandidate、onaddstream等回調(diào):

// 指定TURN及STUN const peerConnectionConfig = { 'iceServers': [ { 'urls': 'turn:numb.viagenie.ca', 'username': 'muazkh', 'credential': 'webrtc@live.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ], bundlePolicy: 'max-bundle', }; const pc = new RTCPeerConnection(peerConnectionConfig); pc.onicecandidate = ...; pc.onaddstream = ...;

然后addTrack指定要傳輸?shù)囊曨l流

stream.getTracks().forEach((track) => { pc.addTrack(track, stream); });

發(fā)起方通過createOffer生成localDescription并傳給pc.setLocalDescription(),pc獲取了本地的sdp后開始獲取candidate,這里的candidate指的是網(wǎng)絡(luò)信息(ip、端口、協(xié)議),根據(jù)優(yōu)先級(jí)從高到低分為三類:

  • host: 設(shè)備的ipv4或ipv6地址,即內(nèi)網(wǎng)地址,一般會(huì)有兩個(gè),分別對(duì)應(yīng)udp和tcp,ip相同,端口不同;
  • srflx(server reflexive): STUN返回的外網(wǎng)地址;
  • relay: 當(dāng)STUN不適用時(shí)(某些NAT會(huì)為每個(gè)連接分配不同的端口,導(dǎo)致獲取的端口和視頻連接端口并不一致),中繼服務(wù)器的地址;

三者之中只需要有一類連接成功即可,所以如果通信雙方在同一內(nèi)網(wǎng),不配置STUN和TURN也可以直接連接。其實(shí)這里隱藏著性能優(yōu)化的點(diǎn):如上圖所示,webRTC通信雙方在交換candidate時(shí),首先由發(fā)起方先收集所有的candidate,然后在icegatheringstatechange事件中檢測(cè)iceGatheringState是否為’complete’,再發(fā)送給接收方。接收方設(shè)置了發(fā)送方傳來的sdp和candidate后,同樣要收集完自己所有的candidate,再發(fā)送給對(duì)方。如果這些candidate中有一對(duì)可以連接成功,則P2P通信建立,否則連接失敗。

問題來了,接受端要等待發(fā)起方收集完所有的candidate之后才開始收集自己的candidate,這其實(shí)是可以同時(shí)進(jìn)行的;另外其實(shí)不一定需要所有的candidate才能建立連接,這也是可以省下時(shí)間的;最后如果網(wǎng)絡(luò),STUN或者TURN出現(xiàn)問題,在上述傳輸模式下是非常致命的,會(huì)讓連接的時(shí)間變得很長不可接受。

解決方案就是IETF提出的Trickle ICE。即發(fā)起方每獲取一個(gè)candidate便立即發(fā)送給接收方,這樣做的好處在于第一類candidate即host,會(huì)立即發(fā)送給接收方,這樣接收方收到后可以立刻開始收集candidate,也就是發(fā)起方和接收方同時(shí)進(jìn)行收集candidate的工作。另外,接收方每收到一個(gè)candidate會(huì)立即去檢查它的有效性,如果有效直接接通視頻,如果無效也不至于浪費(fèi)時(shí)間。詳情可以參見ICE always tastes better when it trickles.

至于sessionDescription及iceCandidate的傳輸,因?yàn)镴avaScript沒有處理sdp格式數(shù)據(jù)的方法,所以直接將其當(dāng)做字符串處理,這樣做的壞處是難以改變sdp中的信息(如果非要改,通過正則匹配還是能改的)。

在掛斷視頻時(shí),不僅要關(guān)閉peerConnection,也要停止本地及遠(yuǎn)程的媒體流:

const tracks = localStream.getTracks().concat(remoteStream.getTracks()); tracks.forEach((track) => { track.stop(); }); peerConnection.close();

截圖&錄制視頻

截圖其實(shí)并不算什么新鮮的東西,無非是利用canvas的drawImage函數(shù)獲取video元素在某一幀的圖像,得到的是圖片的base64格式字符串,但要注意的是這樣得到的base64碼之前有這樣一串文本:

data:image/png;base64,

這是對(duì)數(shù)據(jù)協(xié)議,格式,編碼方式的聲明,是給瀏覽器看的。所以在將drawImage得到的字符串上傳給服務(wù)器時(shí),最好將這串文本去掉,防止后端在轉(zhuǎn)換圖片時(shí)出現(xiàn)錯(cuò)誤。

錄制視頻使用的是MediaRecorder API 詳情參考MDN MediaRecorder,目前僅支持錄制webm格式的視頻。可以在新建MediaRecorder實(shí)例的時(shí)候,設(shè)置mimeType、videoBitsPerSecond、audioBitsPerSecond:

const options = { mimeType: 'video/webm;codecs=vp8', // 視頻格式及編碼格式 videoBitsPerSecond: 2500000, // 視頻比特率,影響文件大小和質(zhì)量 audioBitsPerSecond: 128000 // 音頻比特率,影響文件大小和質(zhì)量 }; const recorder = new MediaRecorder(options);

在recorder的ondataavailable事件中拿到數(shù)據(jù),將其轉(zhuǎn)換為Blob對(duì)象,再通過Formdata異步上傳至服務(wù)器。

錯(cuò)誤追蹤

整個(gè)雙向視頻涉及到的步驟較多,做好錯(cuò)誤追蹤是非常重要的。像getUserMedia時(shí),一定要catch可能出現(xiàn)的異常。因?yàn)椴煌脑O(shè)備,不同的瀏覽器或者說不同的用戶往往不能完全滿足我們?cè)O(shè)置的constraints。還有在實(shí)例化RTCPeerConnection時(shí),往往會(huì)出現(xiàn)不可預(yù)期的錯(cuò)誤,常見的有STUN、TURN格式不對(duì),還有createOffer時(shí)傳遞的offerOptions格式不對(duì),正確的應(yīng)該為:

const offerOptions = { 'offerToReceiveAudio': true, 'offerToReceiveVideo': true };

CAVEAT

因?yàn)閣ebRTC標(biāo)準(zhǔn)還在不斷地更新中,所以相關(guān)的API經(jīng)常會(huì)有改動(dòng)。

  • navigator.getUserMeida(已廢棄),現(xiàn)在改為navigator.mediaDevices.getUserMedia;
  • RTCPeerConnection.addStream被RTCPeerConnection.addTrack取代;
  • STUN,TURN配置里的url現(xiàn)被urls取代;

另外,對(duì)video元素也要特殊處理。設(shè)置autoPlay屬性,對(duì)播放本地視頻源的video還要設(shè)置muted屬性以去除回音。針對(duì)IOS播放視頻自動(dòng)全屏的特性,還要設(shè)置playsinline屬性的值為true。

轉(zhuǎn)載于:https://www.cnblogs.com/liuhao-web/p/8706528.html

總結(jié)

以上是生活随笔為你收集整理的webRTC实战总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。