开始使用WebRTC
Get Started with WebRTC??機(jī)翻
原文:Get Started with WebRTC - HTML5 Rocks
無(wú)需插件即可進(jìn)行實(shí)時(shí)通信
想象一下,在這樣一個(gè)世界中,您的手機(jī)、電視和計(jì)算機(jī)可以在一個(gè)通用平臺(tái)上進(jìn)行通信。想象一下,將視頻聊天和對(duì)等數(shù)據(jù)共享添加到 Web 應(yīng)用很容易。這就是WebRTC的愿景。
想試試嗎?WebRTC可在桌面和移動(dòng)設(shè)備上使用Google Chrome,Safari,Firefox和Opera。一個(gè)好的起點(diǎn)是?appr.tc?的簡(jiǎn)單視頻聊天應(yīng)用程序:
快速入門
沒有時(shí)間閱讀本文或只想要代碼?
要獲得WebRTC的概述,請(qǐng)觀看以下Google I / O視頻或查看這些幻燈片:
或者,直接跳轉(zhuǎn)到WebRTC代碼實(shí)驗(yàn)室,這是一個(gè)分步指南,解釋了如何構(gòu)建一個(gè)完整的視頻聊天應(yīng)用程序,包括一個(gè)簡(jiǎn)單的信令服務(wù)器。
WebRTC的非常短的歷史
網(wǎng)絡(luò)面臨的最后一個(gè)主要挑戰(zhàn)之一是通過(guò)語(yǔ)音和視頻實(shí)現(xiàn)人類通信:實(shí)時(shí)通信或簡(jiǎn)稱RTC。RTC 在 Web 應(yīng)用中應(yīng)與在文本輸入中輸入文本一樣自然。沒有它,你創(chuàng)新和開發(fā)新互動(dòng)方式的能力就會(huì)受到限制。
從歷史上看,RTC一直是企業(yè)和復(fù)雜的,需要昂貴的音頻和視頻技術(shù)在內(nèi)部許可或開發(fā)。將 RTC 技術(shù)與現(xiàn)有內(nèi)容、數(shù)據(jù)和服務(wù)集成既困難又耗時(shí),尤其是在 Web 上。
Gmail視頻聊天在2008年開始流行,2011年,谷歌推出了使用Talk的Hangouts(Gmail也是如此)。谷歌收購(gòu)了GISP,該公司開發(fā)了RTC所需的許多組件,例如編解碼器和回聲消除技術(shù)。Google 開源了 GIPS 開發(fā)的技術(shù),并與互聯(lián)網(wǎng)工程任務(wù)組 (IETF) 和萬(wàn)維網(wǎng)聯(lián)盟 (W3C) 的相關(guān)標(biāo)準(zhǔn)機(jī)構(gòu)合作,以確保行業(yè)共識(shí)。2011年5月,愛立信構(gòu)建了WebRTC的第一個(gè)實(shí)現(xiàn)。
WebRTC為實(shí)時(shí),無(wú)插件的視頻,音頻和數(shù)據(jù)通信實(shí)施了開放標(biāo)準(zhǔn)。需求是真實(shí)的:
- 許多 Web 服務(wù)使用 RTC,但需要下載、本機(jī)應(yīng)用或插件。其中包括Skype,Facebook和Hangouts。
- 下載,安裝和更新插件很復(fù)雜,容易出錯(cuò)并且很煩人。
- 插件難以部署、調(diào)試、故障排除、測(cè)試和維護(hù),并且可能需要許可和與復(fù)雜、昂貴的技術(shù)集成。首先,通常很難說(shuō)服人們安裝插件!
WebRTC項(xiàng)目的指導(dǎo)原則是,其API應(yīng)該是開源的,免費(fèi)的,標(biāo)準(zhǔn)化的,內(nèi)置于Web瀏覽器中,并且比現(xiàn)有技術(shù)更有效。
我們現(xiàn)在在哪里?
WebRTC用于各種應(yīng)用程序,例如Google Meet。WebRTC還與WebKitGTK+和Qt原生應(yīng)用程序集成。
WebRTC實(shí)現(xiàn)了以下三個(gè)API:
- MediaStream(也稱為getUserMedia)
- RTCPeerConnection
- RTCDataChannel
API 定義在以下兩個(gè)規(guī)范中:
- WebRTC
- getUserMedia
Chrome,Safari,Firefox,Edge和Opera在移動(dòng)設(shè)備和桌面上都支持這三個(gè)API。
getUserMedia:有關(guān)演示和代碼,請(qǐng)參閱?WebRTC 示例或嘗試 Chris Wilson 用作 Web 音頻輸入的驚人示例。getUserMedia
RTCPeerConnection:有關(guān)簡(jiǎn)單的演示和功能齊全的視頻聊天應(yīng)用程序,請(qǐng)參閱?WebRTC 示例分別對(duì)等連接和?appr.tc。這個(gè)應(yīng)用程序使用適配器.js,一個(gè)由谷歌在WebRTC社區(qū)的幫助下維護(hù)的JavaScript填充程序,以抽象出瀏覽器的差異和規(guī)格變化。
RTCDataChannel:若要查看實(shí)際效果,請(qǐng)參閱?WebRTC 示例以查看其中一個(gè)數(shù)據(jù)通道演示。
WebRTC codelab?展示了如何使用所有三個(gè) API 來(lái)構(gòu)建一個(gè)簡(jiǎn)單的應(yīng)用程序,用于視頻聊天和文件共享。
您的第一個(gè) WebRTC
WebRTC應(yīng)用程序需要做幾件事:
- 獲取流式音頻、視頻或其他數(shù)據(jù)。
- 獲取網(wǎng)絡(luò)信息,例如 IP 地址和端口,并將其與其他 WebRTC 客戶端(稱為對(duì)等方)交換以啟用連接,甚至通過(guò)?NAT?和防火墻。
- 協(xié)調(diào)信令通信以報(bào)告錯(cuò)誤并啟動(dòng)或關(guān)閉會(huì)話。
- 交換有關(guān)媒體和客戶端功能的信息,如分辨率和編解碼器。
- 傳輸流式音頻、視頻或數(shù)據(jù)。
為了獲取和通信流數(shù)據(jù),WebRTC 實(shí)現(xiàn)了以下 API:
- MediaStream?可以訪問(wèn)數(shù)據(jù)流,例如來(lái)自用戶的攝像頭和麥克風(fēng)。
- RTCPeerConnection?支持音頻或視頻通話,并提供加密和帶寬管理功能。
- RTCDataChannel?支持通用數(shù)據(jù)的對(duì)等通信。
(稍后將詳細(xì)討論WebRTC的網(wǎng)絡(luò)和信令方面。
MediaStream接口(也稱為接口)getUserMedia
MediaStream?API?表示同步的媒體流。例如,從攝像頭和麥克風(fēng)輸入中獲取的流具有同步的視頻和音頻軌道。(不要與<track>元素混淆,這是完全不同的東西。MediaStreamTrack
理解API的最簡(jiǎn)單方法可能是在野外查看它:MediaStream
每個(gè)都有一個(gè)輸入(可能是由 生成的),還有一個(gè)輸出(可能傳遞給視頻元素或 )。MediaStreamMediaStreamgetUserMedia()RTCPeerConnection
該方法采用?MediaStreamConstraints?對(duì)象參數(shù),并返回解析為對(duì)象的 參數(shù)。getUserMedia()PromiseMediaStream
每個(gè)都有一個(gè) ,例如 。由 and 方法返回 s 數(shù)組。MediaStreamlabel'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'MediaStreamTrackgetAudioTracks()getVideoTracks()
對(duì)于?getUserMedia?示例,返回一個(gè)空數(shù)組(因?yàn)闆]有音頻),并且假設(shè)連接了一個(gè)工作正常的網(wǎng)絡(luò)攝像頭,則返回一個(gè)數(shù)組,該數(shù)組表示來(lái)自網(wǎng)絡(luò)攝像頭的流。每個(gè)都有一個(gè)種類 ( 或 ),a(類似于 ),并表示音頻或視頻的一個(gè)或多個(gè)通道。在這種情況下,只有一個(gè)視頻軌道,沒有音頻,但很容易想象用例中還有更多,例如從前置攝像頭,后置攝像頭,麥克風(fēng)獲取流的聊天應(yīng)用程序以及共享其屏幕的應(yīng)用程序。stream.getAudioTracks()stream.getVideoTracks()MediaStreamTrackMediaStreamTrack'video''audio'label'FaceTime HD Camera (Built-in)'
可以通過(guò)設(shè)置?srcObject?屬性將 A?附加到視頻元素。以前,這是通過(guò)將屬性設(shè)置為使用 創(chuàng)建的對(duì)象 URL 來(lái)完成的,但這已被棄用。MediaStreamsrcURL.createObjectURL()
正在主動(dòng)使用相機(jī),這會(huì)占用資源,并使相機(jī)保持打開狀態(tài)并打開相機(jī)燈亮。當(dāng)您不再使用軌道時(shí),請(qǐng)確保呼叫以關(guān)閉攝像機(jī)。MediaStreamTracktrack.stop()
getUserMedia也可以用作 Web 音頻 API 的輸入節(jié)點(diǎn):
<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// Cope with browser differences.</em></span><span style="color:#ffffff"> let audioContext</span><span style="color:#ffffff">;</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#e28964">typeof</span> <span style="color:#89bdff">AudioContext</span> <span style="color:#ffffff">===</span> <span style="color:#65b042">'function'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audioContext </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">AudioContext</span><span style="color:#ffffff">();</span> <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#e28964">typeof</span><span style="color:#ffffff"> webkitAudioContext </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'function'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audioContext </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span><span style="color:#ffffff"> webkitAudioContext</span><span style="color:#ffffff">();</span> <span style="color:#aeaeae"><em>// eslint-disable-line new-cap</em></span> <span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Sorry! Web Audio not supported.'</span><span style="color:#ffffff">);</span> <span style="color:#ffffff">}</span><span style="color:#aeaeae"><em>// Create a filter node.</em></span> <span style="color:#e28964">var</span><span style="color:#ffffff"> filterNode </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createBiquadFilter</span><span style="color:#ffffff">();</span> <span style="color:#aeaeae"><em>// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section</em></span><span style="color:#ffffff"> filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">=</span> <span style="color:#65b042">'highpass'</span><span style="color:#ffffff">;</span> <span style="color:#aeaeae"><em>// Cutoff frequency. For highpass, audio is attenuated below this frequency.</em></span><span style="color:#ffffff"> filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">frequency</span><span style="color:#ffffff">.</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#3387cc">10000</span><span style="color:#ffffff">;</span><span style="color:#aeaeae"><em>// Create a gain node to change audio volume.</em></span> <span style="color:#e28964">var</span><span style="color:#ffffff"> gainNode </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createGain</span><span style="color:#ffffff">();</span> <span style="color:#aeaeae"><em>// Default is 1 (no change). Less than 1 means audio is attenuated</em></span> <span style="color:#aeaeae"><em>// and vice versa.</em></span><span style="color:#ffffff"> gainNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">gain</span><span style="color:#ffffff">.</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#3387cc">0.5</span><span style="color:#ffffff">;</span><span style="color:#ffffff">navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">({</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">},</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Create an AudioNode from the stream.</em></span><span style="color:#e28964">const</span><span style="color:#ffffff"> mediaStreamSource </span><span style="color:#ffffff">=</span><span style="color:#ffffff">audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createMediaStreamSource</span><span style="color:#ffffff">(</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">);</span><span style="color:#ffffff">mediaStreamSource</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">filterNode</span><span style="color:#ffffff">);</span><span style="color:#ffffff">filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">gainNode</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// Connect the gain node to the destination. For example, play the sound.</em></span><span style="color:#ffffff">gainNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">destination</span><span style="color:#ffffff">);</span> <span style="color:#ffffff">});</span></span></span>基于Chromium的應(yīng)用程序和擴(kuò)展程序也可以合并。通過(guò)向清單添加和/或權(quán)限,只能在安裝時(shí)請(qǐng)求和授予一次權(quán)限。此后,不會(huì)要求用戶獲得相機(jī)或麥克風(fēng)訪問(wèn)權(quán)限。getUserMediaaudioCapturevideoCapture
對(duì)于 , 只需授予一次權(quán)限。第一次,瀏覽器的信息欄中會(huì)顯示"允許"按鈕。Chrome 在 2015 年底棄用了 HTTP 訪問(wèn),因?yàn)樗粴w類為功能強(qiáng)大的功能。getUserMedia()getUserMedia()
其意圖可能是為任何流數(shù)據(jù)源啟用 a,而不僅僅是攝像頭或麥克風(fēng)。這將允許從存儲(chǔ)的數(shù)據(jù)或任意數(shù)據(jù)源(如傳感器或其他輸入)進(jìn)行流式傳輸。MediaStream
getUserMedia()真正與其他 JavaScript API 和庫(kù)結(jié)合使用:
- 網(wǎng)絡(luò)攝像頭玩具是一個(gè)照相亭應(yīng)用程序,它使用WebGL為可以在本地共享或保存的照片添加奇怪而美妙的效果。
- FaceKat是一款使用headtrackr.js構(gòu)建的面部跟蹤游戲。
- ASCII 相機(jī)使用 Canvas API 生成 ASCII 圖像。
正在上傳…重新上傳取消
咕嚕咕嚕的藝術(shù)!
約束
約束可用于設(shè)置 的視頻分辨率值。這也允許支持其他約束,例如寬高比;對(duì)置模式(前置或后置攝像頭);幀速率,高度和寬度;和?applyConstraints()?方法。getUserMedia()
有關(guān)示例,請(qǐng)參閱?WebRTC 示例?getUserMedia:選擇分辨率。
一個(gè)陷阱:約束可能會(huì)影響共享資源的可用配置。例如,如果相機(jī)在 640 x 480 模式下通過(guò)一個(gè)選項(xiàng)卡打開,則另一個(gè)選項(xiàng)卡將無(wú)法使用約束以更高分辨率模式打開它,因?yàn)樗荒茉谝环N模式下打開。請(qǐng)注意,這是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。可以讓第二個(gè)選項(xiàng)卡以更高分辨率的模式重新打開相機(jī),并使用視頻處理將第一個(gè)選項(xiàng)卡的視頻軌道縮小到640 x 480,但尚未實(shí)現(xiàn)。getUserMedia
設(shè)置不允許的約束值會(huì)給出 a 或 a,例如,如果請(qǐng)求的分辨率不可用。若要查看此操作的實(shí)際效果,請(qǐng)參閱?WebRTC 示例?getUserMedia:為演示選擇分辨率。DOMExceptionOverconstrainedError
屏幕和選項(xiàng)卡捕獲
Chrome 應(yīng)用還允許通過(guò)?chrome.tabCapture 和 chrome.desktopCapture?API 共享單個(gè)瀏覽器標(biāo)簽頁(yè)或整個(gè)桌面的實(shí)時(shí)視頻。(有關(guān)演示和更多信息,請(qǐng)參閱使用 WebRTC 進(jìn)行屏幕共享。這篇文章已經(jīng)有幾年的歷史了,但它仍然很有趣。
在 Chrome 中,也可以使用實(shí)驗(yàn)性約束將屏幕捕獲用作源。請(qǐng)注意,屏幕捕獲需要 HTTPS,并且只能用于開發(fā),因?yàn)樗峭ㄟ^(guò)命令行標(biāo)志啟用的,如本文所述。MediaStreamchromeMediaSource
信令:會(huì)話控制、網(wǎng)絡(luò)和媒體信息
WebRTC用于在瀏覽器(也稱為對(duì)等體)之間傳輸流數(shù)據(jù),但也需要一種機(jī)制來(lái)協(xié)調(diào)通信和發(fā)送控制消息,這一過(guò)程稱為信令。WebRTC未指定信令方法和協(xié)議。信令不是 API 的一部分。RTCPeerConnectionRTCPeerConnection
相反,WebRTC應(yīng)用程序開發(fā)人員可以選擇他們喜歡的任何消息傳遞協(xié)議,例如SIP或XMPP,以及任何適當(dāng)?shù)碾p工(雙向)通信通道。appr.tc?示例使用 XHR 和通道 API 作為信令機(jī)制。代碼實(shí)驗(yàn)室使用在?Node?服務(wù)器上運(yùn)行的 Socket.io。
信令用于交換三種類型的信息:
- 會(huì)話控制消息:初始化或關(guān)閉通信并報(bào)告錯(cuò)誤。
- 網(wǎng)絡(luò)配置:對(duì)外界來(lái)說(shuō),你電腦的IP地址和端口是什么?
- 媒體功能:您的瀏覽器和要與之通信的瀏覽器可以處理哪些編解碼器和分辨率?
通過(guò)信令進(jìn)行的信息交換必須已成功完成,然后才能開始對(duì)等流。
例如,假設(shè)愛麗絲想和鮑勃交流。下面是?W3C WebRTC 規(guī)范中的代碼示例,其中顯示了運(yùn)行中的信令過(guò)程。該代碼假定存在該方法中創(chuàng)建的某些信令機(jī)制。另請(qǐng)注意,在 Chrome 和 Opera 上,當(dāng)前帶有前綴。createSignalingChannel()RTCPeerConnection
<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// handles JSON.stringify/parse</em></span> <span style="color:#e28964">const</span><span style="color:#ffffff"> signaling </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">SignalingChannel</span><span style="color:#ffffff">();</span> <span style="color:#e28964">const</span><span style="color:#ffffff"> constraints </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> video</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">};</span> <span style="color:#e28964">const</span><span style="color:#ffffff"> configuration </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">iceServers</span><span style="color:#ffffff">:</span> <span style="color:#ffffff">[{</span><span style="color:#ffffff">urls</span><span style="color:#ffffff">:</span> <span style="color:#65b042">'stuns:stun.example.org'</span><span style="color:#ffffff">}]};</span> <span style="color:#e28964">const</span><span style="color:#ffffff"> pc </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">configuration</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// Send any ice candidates to the other peer.</em></span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onicecandidate </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span><span style="color:#ffffff"> signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">});</span><span style="color:#aeaeae"><em>// Let the "negotiationneeded" event trigger offer generation.</em></span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onnegotiationneeded </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createOffer</span><span style="color:#ffffff">());</span><span style="color:#aeaeae"><em>// Send the offer to the other peer.</em></span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span> <span style="color:#ffffff">};</span><span style="color:#aeaeae"><em>// Once remote track media arrives, show it in remote video element.</em></span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ontrack </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Don't set srcObject again if it is already set.</em></span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject</span><span style="color:#ffffff">)</span> <span style="color:#e28964">return</span><span style="color:#ffffff">;</span><span style="color:#ffffff">remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">streams</span><span style="color:#ffffff">[</span><span style="color:#3387cc">0</span><span style="color:#ffffff">];</span> <span style="color:#ffffff">};</span><span style="color:#aeaeae"><em>// Call start() to initiate.</em></span><span style="color:#ffffff"> async </span><span style="color:#e28964">function</span><span style="color:#ffffff"> start</span><span style="color:#ffffff">()</span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Get local stream, show it in self-view, and add it to be sent.</em></span><span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">selfView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">;</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span> <span style="color:#ffffff">}</span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onmessage </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// If you get an offer, you need to reply with an answer.</em></span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'offer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span><span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createAnswer</span><span style="color:#ffffff">());</span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'answer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Unsupported SDP type.'</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addIceCandidate</span><span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span> <span style="color:#ffffff">};</span></span></span>首先,Alice 和 Bob 交換網(wǎng)絡(luò)信息。(表達(dá)式查找候選項(xiàng)是指使用?ICE 框架查找網(wǎng)絡(luò)接口和端口的過(guò)程。
WebRTC 客戶端(也稱為對(duì)等體,在本例中稱為 Alice 和 Bob)還需要確定和交換本地和遠(yuǎn)程音頻和視頻媒體信息,例如解析和編解碼器功能。通過(guò)會(huì)話描述協(xié)議 (SDP) 交換產(chǎn)品/服務(wù)和答案來(lái)發(fā)出信號(hào)以交換媒體配置信息:
確保允許 在不再需要時(shí)通過(guò)調(diào)用來(lái)回收 垃圾回收。否則,線程和連接將保持活動(dòng)狀態(tài)。在WebRTC中可能會(huì)泄漏大量資源!RTCPeerConnectionclose()
RTCSessionDescription對(duì)象是符合會(huì)話描述協(xié)議?SDP 的 Blob。序列化后,SDP 對(duì)象如下所示:
<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">v</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span><span style="color:#ffffff"> o</span><span style="color:#ffffff">=-</span> <span style="color:#3387cc">3883943731</span> <span style="color:#3387cc">1</span><span style="color:#ffffff"> IN IP4 </span><span style="color:#3387cc">127.0</span><span style="color:#ffffff">.</span><span style="color:#3387cc">0.1</span><span style="color:#ffffff"> s</span><span style="color:#ffffff">=</span><span style="color:#ffffff"> t</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span> <span style="color:#3387cc">0</span><span style="color:#ffffff"> a</span><span style="color:#ffffff">=</span><span style="color:#e28964">group</span><span style="color:#ffffff">:</span><span style="color:#ffffff">BUNDLE audio video m</span><span style="color:#ffffff">=</span><span style="color:#ffffff">audio </span><span style="color:#3387cc">1</span><span style="color:#ffffff"> RTP</span><span style="color:#ffffff">/</span><span style="color:#ffffff">SAVPF </span><span style="color:#3387cc">103</span> <span style="color:#3387cc">104</span> <span style="color:#3387cc">0</span> <span style="color:#3387cc">8</span> <span style="color:#3387cc">106</span> <span style="color:#3387cc">105</span> <span style="color:#3387cc">13</span> <span style="color:#3387cc">126</span><span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff">a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">ssrc</span><span style="color:#ffffff">:</span><span style="color:#3387cc">2223794119</span><span style="color:#ffffff"> label</span><span style="color:#ffffff">:</span><span style="color:#ffffff">H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810</span></span></span>網(wǎng)絡(luò)和媒體信息的獲取和交換可以同時(shí)完成,但是在對(duì)等體之間的音頻和視頻流開始之前,這兩個(gè)過(guò)程必須已經(jīng)完成。
前面描述的要約/答案體系結(jié)構(gòu)稱為?JavaScript 會(huì)話建立協(xié)議或 JSEP。(在愛立信的第一個(gè)WebRTC實(shí)現(xiàn)的演示視頻中,有一個(gè)很棒的動(dòng)畫解釋了信令和流式傳輸?shù)倪^(guò)程。
JSEP 架構(gòu)
一旦信令過(guò)程成功完成,數(shù)據(jù)就可以在呼叫者和被叫方之間直接對(duì)等流式傳輸,或者,如果失敗,則通過(guò)中間中繼服務(wù)器(稍后將詳細(xì)介紹)。流式傳輸是 的工作。RTCPeerConnection
RTCPeerConnection
RTCPeerConnection是 WebRTC 組件,用于處理對(duì)等體之間流數(shù)據(jù)的穩(wěn)定和高效通信。
下面是一個(gè) WebRTC 體系結(jié)構(gòu)圖,顯示了 的作用。您會(huì)注意到,綠色部分很復(fù)雜!RTCPeerConnection
正在上傳…重新上傳取消
WebRTC架構(gòu)(來(lái)自?webrtc.org)
從JavaScript的角度來(lái)看,從這張圖中要了解的主要內(nèi)容是,它保護(hù)Web開發(fā)人員免受潛伏在背后的無(wú)數(shù)復(fù)雜性的影響。WebRTC使用的編解碼器和協(xié)議做了大量的工作,使實(shí)時(shí)通信成為可能,即使在不可靠的網(wǎng)絡(luò)上也是如此:RTCPeerConnection
- 丟包隱蔽
- 回聲消除
- 帶寬適應(yīng)性
- 動(dòng)態(tài)抖動(dòng)緩沖
- 自動(dòng)增益控制
- 降噪和抑制
- 圖像清理
前面的 W3C 代碼從信令角度展示了 WebRTC 的簡(jiǎn)化示例。以下是兩個(gè)工作 WebRTC 應(yīng)用程序的演練。第一個(gè)是要演示的簡(jiǎn)單示例,第二個(gè)是完全可操作的視頻聊天客戶端。RTCPeerConnection
沒有服務(wù)器的 RTCPeerConnection
以下代碼取自?WebRTC 示例對(duì)等連接,該連接在一個(gè)網(wǎng)頁(yè)上具有本地和遠(yuǎn)程(以及本地和遠(yuǎn)程視頻)。這并不構(gòu)成任何非常有用的東西 - 調(diào)用方和被調(diào)用方在同一頁(yè)面上 - 但它確實(shí)使API的工作原理更加清晰,因?yàn)轫?yè)面上的對(duì)象可以直接交換數(shù)據(jù)和消息,而無(wú)需使用中間信令機(jī)制。RTCPeerConnectionRTCPeerConnectionRTCPeerConnection
在此示例中,表示本地對(duì)等方(調(diào)用方)和遠(yuǎn)程對(duì)等方(被調(diào)用方)。pc1pc2
訪客
創(chuàng)建一個(gè)新流并從中添加流:RTCPeerConnectiongetUserMedia()
<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// Servers is an optional configuration file. (See TURN and STUN discussion later.)</em></span><span style="color:#ffffff"> pc1 </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span> <span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff"> localStream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">pc1</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> localStream</span><span style="color:#ffffff">);</span> <span style="color:#ffffff">});</span></span></span>創(chuàng)建產(chǎn)品/服務(wù)并將其設(shè)置為 的本地描述和 遠(yuǎn)程描述。這可以直接在代碼中完成,而無(wú)需使用信令,因?yàn)檎{(diào)用方和被調(diào)用方都在同一頁(yè)面上:pc1pc2
<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">pc1</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">).</span><span style="color:#e28964">then</span><span style="color:#ffffff">(()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">onSetLocalSuccess</span><span style="color:#ffffff">(</span><span style="color:#ffffff">pc1</span><span style="color:#ffffff">);</span><span style="color:#ffffff">},</span><span style="color:#ffffff">onSetSessionDescriptionError</span><span style="color:#ffffff">);</span><span style="color:#ffffff">trace</span><span style="color:#ffffff">(</span><span style="color:#65b042">'pc2 setRemoteDescription start'</span><span style="color:#ffffff">);</span><span style="color:#ffffff">pc2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">).</span><span style="color:#e28964">then</span><span style="color:#ffffff">(()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">onSetRemoteSuccess</span><span style="color:#ffffff">(</span><span style="color:#ffffff">pc2</span><span style="color:#ffffff">);</span><span style="color:#ffffff">},</span><span style="color:#ffffff">onSetSessionDescriptionError</span><span style="color:#ffffff">);</span></span></span>被叫方
創(chuàng)建并在添加來(lái)自 的流時(shí),將其顯示在視頻元素中:pc2pc1
<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">pc2 </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span><span style="color:#ffffff"> pc2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ontrack </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> gotRemoteStream</span><span style="color:#ffffff">;</span> <span style="color:#aeaeae"><em>//...</em></span> <span style="color:#e28964">function</span><span style="color:#ffffff"> gotRemoteStream</span><span style="color:#ffffff">(</span><span style="color:#ffffff">e</span><span style="color:#ffffff">){</span><span style="color:#ffffff">vid2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> e</span><span style="color:#ffffff">.</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">;</span> <span style="color:#ffffff">}</span></span></span>RTCPeerConnectionAPI 加服務(wù)器
在現(xiàn)實(shí)世界中,WebRTC需要服務(wù)器,無(wú)論多么簡(jiǎn)單,因此可能會(huì)發(fā)生以下情況:
- 用戶發(fā)現(xiàn)彼此并交換真實(shí)世界的詳細(xì)信息,例如名稱。
- WebRTC客戶端應(yīng)用程序(對(duì)等體)交換網(wǎng)絡(luò)信息。
- 對(duì)等方交換有關(guān)媒體的數(shù)據(jù),例如視頻格式和分辨率。
- WebRTC 客戶端應(yīng)用程序遍歷?NAT 網(wǎng)關(guān)和防火墻。
換句話說(shuō),WebRTC需要四種類型的服務(wù)器端功能:
- 用戶發(fā)現(xiàn)和通信
- 信號(hào)
- NAT/防火墻遍歷
- 對(duì)等通信失敗時(shí)的中繼服務(wù)器
NAT 遍歷、對(duì)等網(wǎng)絡(luò)以及構(gòu)建用于用戶發(fā)現(xiàn)和信令的服務(wù)器應(yīng)用的要求超出了本文的討論范圍。可以說(shuō),ICE框架使用STUN協(xié)議及其擴(kuò)展名TURN來(lái)應(yīng)對(duì)NAT遍歷和其他網(wǎng)絡(luò)變幻莫測(cè)。RTCPeerConnection
ICE 是用于連接對(duì)等方(如兩個(gè)視頻聊天客戶端)的框架。最初,ICE 嘗試通過(guò) UDP 以盡可能低的延遲直接連接對(duì)等方。在此過(guò)程中,STUN 服務(wù)器只有一個(gè)任務(wù):使 NAT 后面的對(duì)等方能夠查找其公共地址和端口。(有關(guān) STUN 和 TURN 的詳細(xì)信息,請(qǐng)參閱構(gòu)建 WebRTC 應(yīng)用所需的后端服務(wù)。
查找連接候選項(xiàng)
如果 UDP 失敗,ICE 將嘗試 TCP。如果直接連接失敗(特別是由于企業(yè) NAT 遍歷和防火墻),ICE 將使用中間(中繼)TURN 服務(wù)器。換句話說(shuō),ICE 首先將 STUN 與 UDP 結(jié)合使用來(lái)直接連接對(duì)等體,如果失敗,則回退到 TURN 中繼服務(wù)器。表達(dá)式查找候選項(xiàng)是指查找網(wǎng)絡(luò)接口和端口的過(guò)程。
WebRTC數(shù)據(jù)路徑
WebRTC工程師Justin Uberti在2013年Google I / O WebRTC演示中提供了有關(guān)ICE,STUN和TURN的更多信息。(演示幻燈片給出了 TURN 和 STUN 服務(wù)器實(shí)現(xiàn)的示例。
一個(gè)簡(jiǎn)單的視頻聊天客戶端
嘗試WebRTC的好地方,包括使用STUN服務(wù)器的信令和NAT /防火墻穿越,是?appr.tc?的視頻聊天演示。此應(yīng)用使用適配器.js、填充程序?qū)?yīng)用與規(guī)范更改和前綴差異隔離開來(lái)。
代碼在其日志記錄中故意冗長(zhǎng)。檢查控制臺(tái)以了解事件的順序。下面是代碼的詳細(xì)演練。
如果你覺得這有點(diǎn)令人困惑,你可能更喜歡 WebRTC codelab。本分步指南介紹了如何構(gòu)建完整的視頻聊天應(yīng)用程序,包括在 Node 服務(wù)器上運(yùn)行的簡(jiǎn)單信令 服務(wù)器。網(wǎng)絡(luò)拓?fù)?/span>
目前實(shí)現(xiàn)的WebRTC僅支持一對(duì)一通信,但可用于更復(fù)雜的網(wǎng)絡(luò)場(chǎng)景,例如多個(gè)對(duì)等體直接或通過(guò)多點(diǎn)控制單元(MCU)相互通信,多點(diǎn)控制單元(MCU)是可以處理大量參與者并進(jìn)行選擇性流轉(zhuǎn)發(fā)的服務(wù)器,以及混合或錄制音頻和視頻。
多點(diǎn)控制單元拓?fù)涫纠?/p>
許多現(xiàn)有的WebRTC應(yīng)用程序僅演示W(wǎng)eb瀏覽器之間的通信,但網(wǎng)關(guān)服務(wù)器可以使在瀏覽器上運(yùn)行的WebRTC應(yīng)用程序與設(shè)備(如電話(也稱為PSTN))和VOIP系統(tǒng)進(jìn)行交互。2012年5月,Doubango Telecom開源了使用WebRTC和WebSocket構(gòu)建的sipml5 SIP客戶端,該客戶端(以及其他潛在用途)支持在iOS和Android上運(yùn)行的瀏覽器和應(yīng)用程序之間的視頻通話。在Google I / O上,Tethr和Tropo展示了一個(gè)使用OpenBTS單元在公文包中進(jìn)行災(zāi)難通信的框架,以通過(guò)WebRTC實(shí)現(xiàn)功能手機(jī)和計(jì)算機(jī)之間的通信。電話通信沒有運(yùn)營(yíng)商!
Tethr/Tropo:公文包中的災(zāi)難通信
RTCDataChannel應(yīng)用程序接口
除了音頻和視頻,WebRTC還支持其他類型的數(shù)據(jù)的實(shí)時(shí)通信。
該 API 支持以低延遲和高吞吐量對(duì)等方式交換任意數(shù)據(jù)。有關(guān)單頁(yè)演示以及如何構(gòu)建簡(jiǎn)單的文件傳輸應(yīng)用程序,請(qǐng)分別參閱?WebRTC 示例和?WebRTC 代碼實(shí)驗(yàn)室。RTCDataChannel
該 API 有許多潛在的用例,包括:
- 賭博
- 遠(yuǎn)程桌面應(yīng)用
- 實(shí)時(shí)文本聊天
- 文件傳輸
- 去中心化網(wǎng)絡(luò)
該 API 具有多項(xiàng)功能,可充分利用并實(shí)現(xiàn)強(qiáng)大而靈活的對(duì)等通信:RTCPeerConnection
- 利用會(huì)話設(shè)置RTCPeerConnection
- 多個(gè)同時(shí)通道,優(yōu)先級(jí)
- 可靠和不可靠的交付語(yǔ)義
- 內(nèi)置安全性 (DTLS) 和擁塞控制
- 能夠與音頻或視頻一起使用或不使用音頻或視頻
語(yǔ)法有意類似于 WebSocket,具有方法和事件:send()message
<span style="color:white"><span style="background-color:#444444"><span style="color:#e28964">const</span><span style="color:#ffffff"> localConnection </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span> <span style="color:#e28964">const</span><span style="color:#ffffff"> remoteConnection </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span> <span style="color:#e28964">const</span><span style="color:#ffffff"> sendChannel </span><span style="color:#ffffff">=</span><span style="color:#ffffff">localConnection</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createDataChannel</span><span style="color:#ffffff">(</span><span style="color:#65b042">'sendDataChannel'</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff">remoteConnection</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ondatachannel </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">receiveChannel </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">channel</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onmessage </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveMessage</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onopen </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveChannelStateChange</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onclose </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveChannelStateChange</span><span style="color:#ffffff">;</span> <span style="color:#ffffff">};</span><span style="color:#e28964">function</span><span style="color:#ffffff"> onReceiveMessage</span><span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"textarea#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">data</span><span style="color:#ffffff">;</span> <span style="color:#ffffff">}</span><span style="color:#ffffff">document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"button#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">onclick </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">var</span><span style="color:#ffffff"> data </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"textarea#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">value</span><span style="color:#ffffff">;</span><span style="color:#ffffff">sendChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">(</span><span style="color:#ffffff">data</span><span style="color:#ffffff">);</span> <span style="color:#ffffff">};</span></span></span>通信直接發(fā)生在瀏覽器之間,因此即使當(dāng)打孔以應(yīng)對(duì)防火墻和 NAT 失敗時(shí)需要中繼 (TURN) 服務(wù)器,也可以比 WebSocket 快得多。RTCDataChannel
RTCDataChannel可在 Chrome、Safari、Firefox、Opera 和 Samsung Internet 中使用。Cube Slam?游戲使用 API 來(lái)傳達(dá)游戲狀態(tài)。玩朋友或玩熊!創(chuàng)新平臺(tái)Sharefest通過(guò)peerCDN實(shí)現(xiàn)了文件共享,并提供了WebRTC如何實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)內(nèi)容分發(fā)的一瞥。RTCDataChannel
有關(guān) 的更多信息,請(qǐng)查看 IETF?的協(xié)議規(guī)范草案。RTCDataChannel
安全
實(shí)時(shí)通信應(yīng)用程序或插件可能會(huì)通過(guò)多種方式危及安全性。例如:
- 未加密的媒體或數(shù)據(jù)可能會(huì)在瀏覽器之間或?yàn)g覽器與服務(wù)器之間被截獲。
- 應(yīng)用可能會(huì)在用戶不知情的情況下錄制和分發(fā)視頻或音頻。
- 惡意軟件或病毒可能與明顯無(wú)害的插件或應(yīng)用程序一起安裝。
WebRTC有幾個(gè)功能可以避免這些問(wèn)題:
- WebRTC實(shí)現(xiàn)使用安全協(xié)議,如DTLS和SRTP。
- 加密對(duì)于所有WebRTC組件都是強(qiáng)制性的,包括信令機(jī)制。
- WebRTC不是一個(gè)插件。它的組件在瀏覽器沙箱中運(yùn)行,而不是在單獨(dú)的進(jìn)程中運(yùn)行。組件不需要單獨(dú)安裝,并且每當(dāng)瀏覽器更新時(shí)都會(huì)更新。
- 必須顯式授予攝像頭和麥克風(fēng)訪問(wèn)權(quán)限,并且當(dāng)攝像頭或麥克風(fēng)運(yùn)行時(shí),用戶界面會(huì)清楚地顯示這一點(diǎn)。
關(guān)于流媒體安全性的完整討論超出了本文的范圍。有關(guān)更多信息,請(qǐng)參閱 IETF?提議的 WebRTC 安全架構(gòu)。
結(jié)語(yǔ)
WebRTC的API和標(biāo)準(zhǔn)可以使內(nèi)容創(chuàng)建和通信工具民主化和分散化,包括電話,游戲,視頻制作,音樂(lè)制作和新聞采集。
沒有比這更具顛覆性的技術(shù)了。
正如博主菲爾·埃德霍爾姆(Phil Edholm)所說(shuō),"WebRTC和HTML5可能實(shí)現(xiàn)與原始瀏覽器對(duì)信息相同的實(shí)時(shí)通信轉(zhuǎn)換。
開發(fā)人員工具
- 正在進(jìn)行的會(huì)話的WebRTC統(tǒng)計(jì)數(shù)據(jù)可以在以下位置找到:
chrome://webrtc-internals 屏幕截圖
- 在 Chrome 瀏覽器中 chrome://webrtc-internals
- 歌劇中的 opera://webrtc-internals
- 關(guān)于:火狐中的webrtc
- 跨瀏覽器互操作說(shuō)明
- adapter.js是由Google在WebRTC社區(qū)的幫助下維護(hù)的WebRTC的JavaScript填充程序,它抽象了供應(yīng)商前綴,瀏覽器差異和規(guī)范更改。
- 要了解有關(guān) WebRTC 信令過(guò)程的更多信息,請(qǐng)查看?appr.tc?日志輸出到控制臺(tái)。
- 如果太多了,你可能更喜歡使用WebRTC框架,甚至是完整的WebRTC服務(wù)。
- 錯(cuò)誤報(bào)告和功能請(qǐng)求始終受到贊賞:
- WebRTC錯(cuò)誤
- Chrome 錯(cuò)誤
- 歌劇錯(cuò)誤
- 火狐錯(cuò)誤
- WebRTC演示錯(cuò)誤
- 適配器.js錯(cuò)誤
了解更多信息
- Justin Uberti 在 Google I/O 2012 上的 WebRTC 會(huì)議
- 艾倫·B·約翰斯頓(Alan B. Johnston)和丹尼爾·C·伯內(nèi)特(Daniel C. Burnett)在 webrtcbook.com 上以印刷版和電子書格式出版了一本W(wǎng)ebRTC書籍,現(xiàn)已推出第三版。
- webrtc.org?是WebRTC所有內(nèi)容的所在地,包括演示,文檔和討論。
- discuss-webrtc是一個(gè)用于WebRTC技術(shù)討論的Google Group。
- @webrtc
- Google Developers?Talk 文檔提供了有關(guān) NAT 遍歷、STUN、中繼服務(wù)器和候選者收集的更多信息。
- WebRTC on GitHub
- Stack Overflow是尋找答案并提出有關(guān)WebRTC問(wèn)題的好地方。
標(biāo)準(zhǔn)和協(xié)議
- WebRTC W3C 編輯草稿
- W3C 編輯器草稿:媒體捕獲和流(也稱為getUserMedia)
- IETF工作組章程
- IETF WebRTC 數(shù)據(jù)通道協(xié)議草案
- IETF JSEP Draft
- IETF為ICE提出的標(biāo)準(zhǔn)
- IETF RTCWEB工作組互聯(lián)網(wǎng)草案:Web實(shí)時(shí)通信用例和要求
WebRTC 支持摘要
MediaStream和 APIgetUserMedia
- Chrome 桌面版 18.0.1008 及更高版本;適用于安卓 29 及更高版本的 Chrome 瀏覽器
- 歌劇18及以上;適用于安卓 20 及更高版本的 Opera
- Opera 12,Opera Mobile 12(基于Presto引擎)
- 火狐 17 及更高版本
- 微軟邊緣 16 及更高版本
- iOS 上的 Safari 11.2 及更高版本,以及 MacOS 上的 11.1 及更高版本
- 安卓版上的 UC 11.8 及更高版本
- 三星互聯(lián)網(wǎng)4及更高版本
RTCPeerConnection應(yīng)用程序接口
- Chrome 桌面 20 及更高版本;適用于 Android 29 及更高版本的 Chrome 瀏覽器(無(wú)標(biāo)志)
- Opera 18 及更高版本(默認(rèn)啟用);適用于 Android 20 及更高版本的 Opera(默認(rèn)情況下處于啟用狀態(tài))
- Firefox 22 及更高版本(默認(rèn)啟用)
- 微軟邊緣 16 及更高版本
- iOS 上的 Safari 11.2 及更高版本,以及 MacOS 上的 11.1 及更高版本
- 三星互聯(lián)網(wǎng)4及更高版本
RTCDataChannel應(yīng)用程序接口
- Chrome 25中的實(shí)驗(yàn)版本,但在Chrome 26及更高版本中更穩(wěn)定(并且具有Firefox互操作性);適用于安卓 29 及更高版本的 Chrome 瀏覽器
- Opera 18及更高版本中的穩(wěn)定版本(以及Firefox互操作性);適用于安卓 20 及更高版本的 Opera
- Firefox 22 及更高版本(默認(rèn)啟用)
有關(guān) API 的跨平臺(tái)支持(例如 和)的更多詳細(xì)信息,請(qǐng)參閱?caniuse.com?和?Chrome 平臺(tái)狀態(tài)。getUserMediaRTCPeerConnection
本機(jī) API 也可在有關(guān) webrtc.org 的文檔中找到。RTCPeerConnection
總結(jié)
以上是生活随笔為你收集整理的开始使用WebRTC的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: png怎么转换成jpg格式?如何转换照片
- 下一篇: 进神经网络的学习方式(译文)----中