苹果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阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容