国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

webRTC實(shí)戰(zhàn)總結(jié)

Pandaaa / 1130人閱讀

摘要:中微信內(nèi)置瀏覽器還不支持我堅(jiān)信不久的將來就會(huì)支持,但在中能夠完美支持。因此本項(xiàng)目選擇了微信公眾號(hào)為切入點(diǎn),通過檢測(cè)引導(dǎo)用戶在中打開頁面。為了便于傳輸可將其處理成字符串,另一端接收時(shí)還原并用對(duì)應(yīng)的構(gòu)造函數(shù)構(gòu)造對(duì)應(yīng)的實(shí)例即可。

前言

前段時(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,F(xiàn)F22,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),使其長(zhǎng)寬比例大致與移動(dòng)端屏幕的類似,然后再將video元素的長(zhǎng)和寬設(shè)置為容器的長(zhǎng)和寬(例如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)無非是簡(jiǎ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í)間變得很長(zhǎng)不可接受。

解決方案就是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。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/92664.html

相關(guān)文章

  • 這是一篇RTC(Real-time Communications,實(shí)時(shí)通信)的普及帖

    摘要:隨著通信的發(fā)展,實(shí)時(shí)音視頻服務(wù)將進(jìn)一步覆蓋更多的生活場(chǎng)景。什么是實(shí)時(shí)通訊,我們很容易把和混淆。另外的延遲是毫秒級(jí),在正常的網(wǎng)絡(luò)情況下,延遲在之間,可以多方通話實(shí)時(shí)互動(dòng)。這篇文章主要是圍繞告訴大家什么是,能解決什么問題的普及貼。2020年初爆發(fā)的疫情,催生了在線教育、視頻會(huì)議、遠(yuǎn)程醫(yī)療等實(shí)時(shí)音視頻應(yīng)用的大規(guī)模增長(zhǎng),也使得服務(wù)于這些場(chǎng)景背后的底層框架RTC技術(shù)站上了風(fēng)口。早在 2010 年,Go...

    Tecode 評(píng)論0 收藏0
  • 參考 - 收藏集 - 掘金

    摘要:譯年你不能錯(cuò)過的類庫后端掘金各位讀者好,這篇文章是在我看過的一篇介紹文后,整理出來的。上線后平穩(wěn)運(yùn)行我的后端書架后端掘金我的后端書架月前本書架主要針對(duì)后端開發(fā)與架構(gòu)。 【譯】2017 年你不能錯(cuò)過的 Java 類庫 - 后端 - 掘金各位讀者好, 這篇文章是在我看過 Andres Almiray 的一篇介紹文后,整理出來的。 因?yàn)閮?nèi)容非常好,我便將它整理成參考列表分享給大家, 同時(shí)附上...

    feng409 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<