abbshr.github.io
abbshr.github.io copied to clipboard
RealTime Web II - WebRTC tech (2)
编写Signaling服务
所谓signaling说白了就是外带的一个消息传递通道, 像polling, long-polling, long-connection, WebSocket等都可以做到. 至于signal服务具体要传递什么格式的消息, 下面有个signaling流程抽象, 来自HTML5ROCKs:
// 用WebSocket构建signalingChannel最为直观
var signalingChannel = new SignalingChannel();
// call start() to initiate
function start() {
pc = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer
pc.onicecandidate = function (evt) {
if (evt.candidate)
// 这里发送signal
signalingChannel.send(JSON.stringify({
'candidate': evt.candidate
}));
};
// let the 'negotiationneeded' event trigger offer generation
pc.onnegotiationneeded = function () {
pc.createOffer(localDescCreated, logError);
}
// once remote stream arrives, show it in the remote video element
pc.onaddstream = function (evt) {
remoteView.src = URL.createObjectURL(evt.stream);
};
// get a local stream, show it in a self-view and add it to be sent
navigator.getUserMedia({
'audio': true,
'video': true
}, function (stream) {
selfView.src = URL.createObjectURL(stream);
pc.addStream(stream);
}, logError);
}
function localDescCreated(desc) {
pc.setLocalDescription(desc, function () {
// 这里也要发送signal
signalingChannel.send(JSON.stringify({
'sdp': pc.localDescription
}));
}, logError);
}
// 收到对方发来的signal.
signalingChannel.onmessage = function (evt) {
if (!pc)
start();
var message = JSON.parse(evt.data);
if (message.sdp)
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
// if we received an offer, we need to answer
if (pc.remoteDescription.type == 'offer')
pc.createAnswer(localDescCreated, logError);
}, logError);
else
pc.addIceCandidate(new RTCIceCandidate(message.candidate));
};
function logError(error) {
log(error.name + ': ' + error.message);
}
STUN/TURN服务器
STUN是为了获取位于NAT背后节点的公开可访问IP和端口而存在的. 而且:
Most WebRTC calls successfully make a connection using STUN: 86%
TURN在之前也介绍过. WebRTC传输层默认使用UDP协议来建立peer, 如果失败的话换用TCP直连, 再不行就让TURN接手, 通过外部TURN服务器转发两个节点的流量了.
这幅图说明了signaling, STUN, TURN的关系:
在构建WebRTC应用时, 为了方便可以选择公共的STUN服务器. 当然, 最好是能部署在可信任的环境下.
多播
WebRTC允许通过浏览器建立多对peers. 如果一个网络中有数量很多的终端建立多方呼叫, 应该在链路上部署MCU设备.
多端控制器(Multipoint Control Unit (MCU))是一种用于多媒体视讯会议(Video Conference)的装置系统,主要功能是在控制多个终端间的视讯传输。
W3C WebRTC API & examples
终于到实际应用了, 来看下前面说的那些流程怎么用真实代码写出来. 下面是一个最简单的连接建立过程:
浏览器对开发者暴露了一个接口RTCPeerConnection
函数. 要使用WebRTC, 最先通过它:
var peer = new RTCPeerConnection(servers)
// servers是一个可选的配置对象, 表示要为peer使用的STUN/TURN服务器:
/*
{
'iceServers': [ { "urls": "stun:stun1.example.net" }, { "urls": "turn:turn.example.org", "username": "user", "credential": "myPassword" } ]
}
*/
Note: 由于目前各大浏览器厂商竞争不断, 导致这个API一直没有遵循W3C规范, 比如在Chrome下需要加webkit
前缀, FireFox需要moz
前缀
然后连接另一个对等方.
// peer需要首先发送一个offer报文
peer.createOffer(function (desc){
// 设置本地sessionDescription
peer.setLocalDescription(desc);
signalchannel.send({sdp: peer.localDescription})
});
peer.onicecandidate = function (e) {
signalchannel.send({candidate: e.candidate});
};
signalchannel.onMessage = function (e) {
peer.setRemoteDescription(new RTCSessionDescription(JSON.parse(e.data.sdp)));
}
对等方:
signalchannel.onMessage = function (e) {
// 对等方设置发送offer的远程sessionDescription
dstpeer.setRemoteDescription(new RTCSessionDescription(JSON.parse(e.data.sdp)), function () {
if (dstpeer.remoteDescription.type == 'offer')
dstpeer.createAnswer(cb);
dstpeer.addIceCandidate(new RTCIceCandidate(JSON.parse(e.data.candidate)));
});
};
sessionDescription设置完毕, offer/answer报文发送并且ICE探测成功, WebRTC通信就可以真正开始了. 至于MediaStream和DataChannel等多媒体Web的内容, 等到后面再讲.
详细的API参看W3C WebRTC API