作者: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整合到你的連續集成系統:
我們會在下文中一個一個分析這些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流。當用來做輸入流的時候,模擬設備的可預測數據使其評估流媒體回放的表現變得更加簡單。
在連續集成系統中,模擬對運行自動測試也十分的有用。如果你正在使用一個模擬設備并且想要避開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。
最終,為了避免發生意外捕捉的情況,WebKit只允許一次只有一個標簽頁能夠進行視頻或者音頻的捕捉。正在使用捕捉設備的標簽頁會看到他們的MediaStreamTrack被靜音并且在新標簽頁獲得權限之后,這個標簽頁會收到mute事件。
指紋
navigator.mediaDevices.enumerateDevices展示了可用的捕捉設備,甚至在權限沒有被許可的情況下也可以向網站發出請求。對于那些有自定義攝像頭和麥克風設置的用戶來說,這可以加到用戶指紋表面(fingerprinting surface)中。當權限沒有被允許或者被明確拒絕的時候,WebKit會通過返回一個默認設備清單(可能與真實的設備完全沒有關系)來避免暴露額外的信息。一旦權限被授予了,清單上的全部設備和他們的標簽都會變成可用狀態。
媒體捕捉和自動播放視頻
在上一篇文章中我們已經討論了macOS和iOS系統中視頻自動播放政策的改變。我們調整了兩個系統中的政策以適應WebRTC應用,WebRTC希望自動播放流入媒體流要包含音頻。需要進行下面這些改變:
基于MediaStream的媒體在網頁準備好捕捉的情況下會自動播放
基于MediaStream的媒體在網頁準備好播放音頻的時候回自動播放。依舊需要一個用戶收拾來開啟音頻播放
WebRTC是一項可以有多種應用的強大技術,我們都知道越大的能力伴隨著越大的責任。想要設計WebRTC應用就必須從一開始就有著清晰的思路。CPU,內存和網絡的限制都會嚴重影響用戶的體驗度。這個問題應該得到網頁引擎和網頁應用的同時處理。在網頁應用端,現在已經有很多機制可以用來解決這個問題:選用合適的視頻分辨率和幀速率,選擇正確的視頻codec,使用CVO,在源處靜音音軌,以及在客戶端處監控WebRTC數據。