蘋果WebKit詳細分析WebRTC實現

作者:Youenn Fablet,Jon Lee

原文鏈接:https://webkit.org/blog/7763/a-closer-look-into-webrtc/

原標題:A Closer Look Into WebRTC

我們最近宣布了High Sierra系統和iOS 11系統中的Safari 11支持WebRTC。今天,我們想要更詳細地講講我們的實現,以及提供一些如何將WebRTC加入你的網站的建議。

使用WebRTC和媒體捕捉的網站能夠獲得并廣播非常隱私的個人信息。用戶必須非常的信任這個網站,認為網站會合規合理地使用他們的影像。WebKit要求網站必須達到指定的規定,以確保其用戶隱私的安全。另外,Safari會在使用攝像頭、麥克風等捕捉設備的時候提示用戶,用戶可以控制網站對這些捕捉設備權限。對于開發者來說,在他們的app中,RTCPeerConnection和RTCDataChannel在任何網頁視圖中都可以使用,但是Safari暫時還限制攝像頭和麥克風的權限。

開發菜單

Safari技術預覽版34版展示了各種flag可以讓測試WebRTC網站的工作變得更簡單,或者可以通過Develop > WebRTC子頁面中將Safari整合到你的連續集成系統:

Develop > WebRTC子頁面

我們會在下文中一個一個分析這些flag,并且解釋它們如何能在開發過程中對你起到幫助作用。

另外,WebKit會在系統日志中記錄WebRTC數據,其中包含了SDP請求和應答,ICE候選,WebRTC數據,以及流入和流出的視頻幀計數器。

媒體捕捉的安全來源政策

想要獲取捕捉設備權限的網站必須要滿足兩個要求。

首先,文件要求攝像頭和麥克風的請求必須是來于HTTPS域名的網站。因為在你進行本地開發和測試時,這項要求會成為一個負擔,所以你可以通過在Develop > WebRTC菜單中勾選“Allow Media Capture on Insecure Sites”這一項以跳過HTTPS限制。

第二,當一個子幀請求使用媒體捕捉設備時,領導主幀的幀鏈也必須來自于從同一個安全來源。用戶可能不會分辨出子幀的第三方來源與主幀的差別,所以這條要求可以避免用戶在沒弄清誰在請求的時候就授予權限。

模擬捕捉設備

Develop > WebRTC菜單中,你可以選擇“Use Mock Capture Devices”來使用一個模擬設備來替換實際的捕捉設備。像下圖一樣,模擬設備會循環一段bip-bop AV流。當用來做輸入流的時候,模擬設備的可預測數據使其評估流媒體回放的表現變得更加簡單。

bip-bop模擬

在連續集成系統中,模擬對運行自動測試也十分的有用。如果你正在使用一個模擬設備并且想要避開getUserMedia彈出的提示,你可以通過Safari瀏覽器中的Preferences… > Websites面板,將攝像頭和麥克風政策設定成“Allow”來實現。

ICE候選限制

在WebRTC連接的早期階段會進行ICE候選項來確認兩個對等端之間所有可能的網絡通道。為了實現這一點,WebKit必須將每一個對等端的ICE候選項展示給網站,這樣它們才能夠進行交換。ICE候選項包括IP地址,并且需需要注意的是這些是主機IP地址,可以被用來做跟蹤。

在多種網絡拓撲結構中,主機ICE候選項不需要被用來進行連接。服務器反向以及TURN的ICE候選項通常已經足夠用來保障連接,不管是用來交換視頻或者水機數據的。在沒有給捕捉設備授權的時候,WebKit只會展示服務器反向和TURN ICE候選項,展示網站已經獲得的IP地址。當權授予權限之后,WebKit會展示主機ICE候選,將連接成功率最大化。

一些測試頁面可能會假定主機ICE候選的可用性。為了測試這項,在Develop > WebRTC菜單中打開“Disable ICE Candidate Restrictions”選項,然后刷新網頁。

過時的WebRTC和媒體流API

隨著WebRTC標準的發展,RTCPeerConnection API也在各種方面都日益發展。最開始這個API是基于回調的,現在已經變成了完全基于承諾的。API最初聚焦于MediaStream,現在轉為專注于MediaStreamTrack。

在STP 34中,我們默認將傳統WebRTC API關掉,并且計劃將Safari 11推到 macOS High Sierra和iOS 11上的時候去掉這些API。保留這些過時的API值會限制我們在WebRTC方面的發展速度。任何想要支持Safari的網站也需要做一些其他的調整,所以現在是一個擺脫這些過時API的好時機。現有的一些網站可能還依賴于這些傳統API,你可以在Develop > WebRTC菜單中將“Enable Lagacy WebRTC API”勾選以打開這個功能。

更明確的說,下面這些API只在傳統API開關被打開的時候才能用,還附有如何更新的建議:

partial interface Navigator {

// Switch to navigator.mediaDevices.getUserMedia

void getUserMedia(MediaStreamConstraints constraints, NavigatorUserMediaSuccessCallback successCallback, NavigatorUserMediaErrorCallback errorCallback);

};

partial interface RTCPeerConnection {

// Switch to getSenders, and look at RTCRtpSender.track

sequence getLocalStreams();

// Switch to getReceivers, and look at RTCRtpReceiver.track

sequence getRemoteStreams();

// Switch to getSenders/getReceivers

MediaStream getStreamById(DOMString streamId);

// Switch to addTrack

void addStream(MediaStream stream);

// Switch to removeTrack

void removeStream(MediaStream stream);

// Listen to ontrack event

attribute EventHandler onaddstream;

// Update to promise-only version of createOffer

Promise createOffer(RTCSessionDescriptionCallback successCallback, RTCPeerConnectionErrorCallback failureCallback, optional RTCOfferOptions options);

// Update to promise-only version of setLocalDescription

Promise setLocalDescription(RTCSessionDescriptionInit description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);

// Update to promise-only version of createAnswer

Promise createAnswer(RTCSessionDescriptionCallback successCallback, RTCPeerConnectionErrorCallback failureCallback);

// Update to promise-only version of setRemoteDescription

Promise setRemoteDescription(RTCSessionDescriptionInit description, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);

// Update to promise-only version of addIceCandidate

Promise addIceCandidate((RTCIceCandidateInit or RTCIceCandidate) candidate, VoidFunction successCallback, RTCPeerConnectionErrorCallback failureCallback);

};

很多網站通過開源adapter.js項目來填補API的支持。更新到最新的版本是一個填補API空缺的方法,但是我們還是建議換用在規范中明確列出來的API。

這里是一些關于如何使用最新API的例子。一個典型的只接收/視頻會議類型的WebRTC通話可以這樣做:

var pc = new RTCPeerConnection();

pc.addTransceiver('audio');

pc.addTransceiver('video');

var offer = await pc.createOffer();

await pc.setLocalDescription(offer);

// send offer to the other party

...

典型的音頻-視頻WebRTC通話可以這樣做:

var stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});

var pc = new RTCPeerConnection();

var audioSender = pc.addTrack(stream.getAudioTracks()[0], stream);

var videoSender = pc.addTrack(stream.getVideoTracks()[0], stream);

var offer = await pc.createOffer();

await pc.setLocalDescription(offer);

// send offer to the other party

...

基于MediaStreamTrack的API大部分處理工作在這個層面就已經完成了。舉個例子,默認640×480分辨率的捕捉視頻軌是并不好的。接著上一個例子,可以這樣進行動態更改:

videoSender.track.applyConstraints({width: 1280, height: 720});

或者我們可以將視頻靜音,但是要保持音頻一直運行:

videoSender.track.enabled = false;

獲取捕捉流

Safari允許用戶獲得對某個網站上你捕捉設備權限的完全控制。

首先,在getUserMedia第一次被調用時,用戶會被提示對網站使用捕捉設備進行授權。不像其他瀏覽器那樣,Safari不會要求用戶選擇特定的設備,取而代之的是是對特定類型的所有設備進行授權,比如說所有攝像頭或者麥克風。這可以減少需要多次授權時用戶的煩躁感,并且可以防止用戶養成看也不看就點“允許”的習慣。一個常見的例子是可以在iOS設備上切換前置和后置攝像頭。getUserMedia中會返回一個滿足要求的設備,隨后getUserMedia調用同類型的設備就可以避免給用戶再次彈出一個額外的提醒。如果你想要允許用戶切換到不同的設備,一定要確保你給用戶提供了一個UI來做這些。

第二,用戶可以通過設置來決定每次都允許或者拒絕攝像頭和麥克風的權限申請。

第三,一旦某個網站給一個設備創建了MediaStream,會在Safari UI和系統菜單欄上出現一個圖標,表示正在有捕捉設備正在使用。用戶可以點擊這個圖標來終止攝像頭和麥克風的工作。這里WebKit會發送靜默的音頻和全黑的視頻幀,而且你的網站可以通過檢查MediaStreamTrack中是mute還是unmute來展示合適的UI。

Safari UI和系統菜單欄

最終,為了避免發生意外捕捉的情況,WebKit只允許一次只有一個標簽頁能夠進行視頻或者音頻的捕捉。正在使用捕捉設備的標簽頁會看到他們的MediaStreamTrack被靜音并且在新標簽頁獲得權限之后,這個標簽頁會收到mute事件。

指紋

navigator.mediaDevices.enumerateDevices展示了可用的捕捉設備,甚至在權限沒有被許可的情況下也可以向網站發出請求。對于那些有自定義攝像頭和麥克風設置的用戶來說,這可以加到用戶指紋表面(fingerprinting surface)中。當權限沒有被允許或者被明確拒絕的時候,WebKit會通過返回一個默認設備清單(可能與真實的設備完全沒有關系)來避免暴露額外的信息。一旦權限被授予了,清單上的全部設備和他們的標簽都會變成可用狀態。

媒體捕捉和自動播放視頻

在上一篇文章中我們已經討論了macOS和iOS系統中視頻自動播放政策的改變。我們調整了兩個系統中的政策以適應WebRTC應用,WebRTC希望自動播放流入媒體流要包含音頻。需要進行下面這些改變:

基于MediaStream的媒體在網頁準備好捕捉的情況下會自動播放

基于MediaStream的媒體在網頁準備好播放音頻的時候回自動播放。依舊需要一個用戶收拾來開啟音頻播放

WebRTC是一項可以有多種應用的強大技術,我們都知道越大的能力伴隨著越大的責任。想要設計WebRTC應用就必須從一開始就有著清晰的思路。CPU,內存和網絡的限制都會嚴重影響用戶的體驗度。這個問題應該得到網頁引擎和網頁應用的同時處理。在網頁應用端,現在已經有很多機制可以用來解決這個問題:選用合適的視頻分辨率和幀速率,選擇正確的視頻codec,使用CVO,在源處靜音音軌,以及在客戶端處監控WebRTC數據。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容