写在前面:如果你不必担心连接状态、闪退问题(信号冲突)、角色(你处于哪一方),就可以在实时WebRTC连接中添加和删除媒体,你会怎么做呢?不受时间和地点限制,你可以很容易地调用pc.addTrack(track,stream),并且你的连接路径只会显示在另一侧,不会有终端连接失败的风险。这是个白日梦?还有很多问题亟待解决?实际上,现在Chrome已经基本完成了协商,上述操作几乎可以运行!但是这样的操作并没有广泛应用,可能是因为“几乎”这两个字。这代表该操作有5%的时间无法工作,并且若Chrome闪退,风险太高了(无法从该操作回滚,只能运用pc.close())。但API最初的承诺仍然有效。我们只需要一个除Firefox以外的浏览器来实现回滚,并用规范允许的方法修复一些明显的竞赛问题。
WebRTC API中的一些东西可能会导致像SDP和ICE的非对称交换一样多的操作失误。如果编程不正确,其SignalState和对时间敏感的ICE流可能会导致一些问题。如果这还不够有挑战性,那么保持两端同步并且不同方向的话,会使很多用户陷入死循环。
如果我告诉你我们可以解决这个棘手的问题呢?如果我说,如今大多数人在协商时用到的WebRTC人工是低效的,我们可以使其变得高效呢?
这里我们谈到的是negotiationneeded的(操作)和“rollback”。我们可以协同使用两种系统,完全不用协商。然而,没有人使用它们,因为它们分别都没有在Chrome中得到应用。下面我们会逐一介绍这两种系统。
negotiationneeded 处理当地变化
negotiationneeded是提供/回答交换ice candidate事件对ICE候选交换的内容。您可能已经知道如何使用后者。那么这样做就可以发送信息:
pc.onicecandidate = ({candidate}) => io.send({candidate});
这样做可以接收信息:
io.onmessage = async ({data: {description, candidate}}) => { if (candidate) await pc.addIceCandidate(candidate); }
然后浏览器会负责接下来的事情。 这简直太简单了! 我们不关心如何、何时或为何需要交换候选,我们只是连接网络,并用浏览器与其对接即可。 我们已经有效地抽离了ICE交易; 浏览器会处理它的。
有这么方便的操作,我们为什么还要多此一举用人工呢? 只需以同样的方式设置它。 这样做就可以发送信息:
pc.onnegotiationneeded = async () => { await pc.setLocalDescription(await pc.createOffer()); io.send({description: pc.localDescription}); }
这样做就可以接收信息:
io.onmessage = async ({data: {description, candidate}}) => { if (description) { await pc.setRemoteDescription(description); if (description.type == "offer") { await pc.setLocalDescription(await pc.createAnswer()); io.send({description: pc.localDescription}); } } else if (candidate) await pc.addIceCandidate(candidate); }
这样就完成了!
现在,每当我们在RTCPeerConnection上传输媒体流时,它就会自动发送以下内容:
const stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true}); for (const track of stream.getTracks()) { pc.addTrack(track, stream); }
到了另一端:
pc.ontrack = ({streams}) => video.srcObject = streams[0];
协商会按需自动进行。我们已经简化了协商操作,接下来就看浏览器的了。
原文地址:https://blog.mozilla.org/webrtc/perfect-negotiation-in-webrtc/
文章作者:Jan-Ivar Bruaroey