WebRTC开发基础(WebRTC入门系列2:RTCPeerCo
版权声明:关于作者:王昕(QQ:475660) 十多年开发经验,在Delphi、ASP.net、Cobol、WPF、C#、SharePoint、Java、XMPP Server/Client、EPortal、BPM等领域较有经验,对开源技术有广泛兴趣,尤其关注门户平台和即时通讯技术,在广州工作生活30余年。 https://blog.csdn.net/starcrm/article/details/52576388
RTCPeerConnection的作用是在浏览器之间建立数据的“点对点”(peer to peer)通信.
使用WebRTC的编解码器和协议做了大量的工作,方便了开发者,使实时通信成为可能,甚至在不可靠的网络,
比如这些如果在voip体系下开发工作量将非常大,而用webRTC的js开发者则不用考虑这些,举几个例子:
- 丢包隐藏
- 回声抵消
- 带宽自适应
- 动态抖动缓冲
- 自动增益控制
- 噪声抑制与抑制
- 图像清洗
不同客户端之间的音频/视频传递,是不用通过服务器的。但是,两个客户端之间建立信令联系,需要通过服务器。这个和XMPP的Jingle会话很类似。
服务器主要转递两种数据:
- 通信内容的元数据:打开/关闭对话(session)的命令、媒体文件的元数据(编码格式、媒体类型和带宽)等。
- 网络通信的元数据:IP地址、NAT网络地址翻译和防火墙等。
WebRTC协议没有规定与服务器的信令通信方式,因此可以采用各种方式,比如WebSocket。通过服务器,两个客户端按照Session Description Protocol(SDP协议)交换双方的元数据。
本地和远端通讯的过程有些像电话,比如张三正在试着打电话给李四,详细机制:
- 张三创造了一个RTCPeerConnection 对象。
- 张三通过RTCPeerConnection createOffer()方法创造了一个offer(SDP会话描述) 。
- 张三通过他创建的offer调用setLocalDescription(),保存本地会话描述。
- 张三发送信令给李四。
- 李四接通带有李四offer的电话,调用setRemoteDescription() ,李四的RTCPeerConnection知道张三的设置(张三的本地描述到了李四这里,就成了李四的远程会话描述)。
- 李四调用createAnswer(),将李四的本地会话描述(local session description)成功回调。
- 李四调用setLocalDescription()设置他自己的本地局部描述。
- 李四发送应答信令answer给张三。
- 张三将李四的应答answer用setRemoteDescription()保存为远程会话描述(李四的remote session description)。
WebSocket简介(用于信令通信)
https://dzone.com/refcardz/html5-websocket
http://www.infoq.com/articles/Web-Sockets-Proxy-Servers
信令服务或开源webRTC框架
https://easyrtc.com/products/easyrtc-opensource
https://github.com/priologic/easyrtc
https://github.com/andyet/signalmaster
如果您一行代码都不想写,可以看看 vLine, OpenTok and Asterisk.
这里有一个单页应用程序。本地和远程的视频在一个网页,RTCPeerConnection objects 直接交换数据和消息。
https://webrtc.github.io/samples/src/content/peerconnection/pc1/
HTML代码:
<!DOCTYPE html>
<html>
<head>
......
</head>
<body>
<div id="container">
<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Peer connection</span></h1>
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
</div>
<script src="../../../js/adapter.js"></script>
<script src="../../../js/common.js"></script>
<script src="js/main.js"></script>
<script src="../../../js/lib/ga.js"></script>
</body>
</html>
这里是主要视图:
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
<div>
<button id="startButton">Start</button>
<button id="callButton">Call</button>
<button id="hangupButton">Hang Up</button>
</div>
适配器js代码:
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
const startButton = document.getElementById('startButton');
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.addEventListener('click', start);
callButton.addEventListener('click', call);
hangupButton.addEventListener('click', hangup);
let startTime;
const localVideo = document.getElementById('localVideo');
const remoteVideo = document.getElementById('remoteVideo');
localVideo.addEventListener('loadedmetadata', function() {
console.log(`Local video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
});
remoteVideo.addEventListener('loadedmetadata', function() {
console.log(`Remote video videoWidth: ${this.videoWidth}px, videoHeight: ${this.videoHeight}px`);
});
remoteVideo.addEventListener('resize', () => {
console.log(`Remote video size changed to ${remoteVideo.videoWidth}x${remoteVideo.videoHeight}`);
// We'll use the first onsize callback as an indication that video has started
// playing out.
if (startTime) {
const elapsedTime = window.performance.now() - startTime;
console.log('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
});
let localStream;
let pc1;
let pc2;
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
async function start() {
console.log('Requesting local stream');
startButton.disabled = true;
try {
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
console.log('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
callButton.disabled = false;
} catch (e) {
alert(`getUserMedia() error: ${e.name}`);
}
}
function getSelectedSdpSemantics() {
const sdpSemanticsSelect = document.querySelector('#sdpSemantics');
const option = sdpSemanticsSelect.options[sdpSemanticsSelect.selectedIndex];
return option.value === '' ? {} : {sdpSemantics: option.value};
}
async function call() {
callButton.disabled = true;
hangupButton.disabled = false;
console.log('Starting call');
startTime = window.performance.now();
const videoTracks = localStream.getVideoTracks();
const audioTracks = localStream.getAudioTracks();
if (videoTracks.length > 0) {
console.log(`Using video device: ${videoTracks[0].label}`);
}
if (audioTracks.length > 0) {
console.log(`Using audio device: ${audioTracks[0].label}`);
}
const configuration = getSelectedSdpSemantics();
console.log('RTCPeerConnection configuration:', configuration);
pc1 = new RTCPeerConnection(configuration);
console.log('Created local peer connection object pc1');
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
pc2 = new RTCPeerConnection(configuration);
console.log('Created remote peer connection object pc2');
pc2.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
pc1.addEventListener('iceconnectionstatechange', e => onIceStateChange(pc1, e));
pc2.addEventListener('iceconnectionstatechange', e => onIceStateChange(pc2, e));
pc2.addEventListener('track', gotRemoteStream);
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
console.log('Added local stream to pc1');
try {
console.log('pc1 createOffer start');
const offer = await pc1.createOffer(offerOptions);
await onCreateOfferSuccess(offer);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
function onCreateSessionDescriptionError(error) {
console.log(`Failed to create session description: ${error.toString()}`);
}
async function onCreateOfferSuccess(desc) {
console.log(`Offer from pc1\n${desc.sdp}`);
console.log('pc1 setLocalDescription start');
try {
await pc1.setLocalDescription(desc);
onSetLocalSuccess(pc1);
} catch (e) {
onSetSessionDescriptionError();
}
console.log('pc2 setRemoteDescription start');
try {
await pc2.setRemoteDescription(desc);
onSetRemoteSuccess(pc2);
} catch (e) {
onSetSessionDescriptionError();
}
console.log('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
try {
const answer = await pc2.createAnswer();
await onCreateAnswerSuccess(answer);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
function onSetLocalSuccess(pc) {
console.log(`${getName(pc)} setLocalDescription complete`);
}
function onSetRemoteSuccess(pc) {
console.log(`${getName(pc)} setRemoteDescription complete`);
}
function onSetSessionDescriptionError(error) {
console.log(`Failed to set session description: ${error.toString()}`);
}
function gotRemoteStream(e) {
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
console.log('pc2 received remote stream');
}
}
async function onCreateAnswerSuccess(desc) {
console.log(`Answer from pc2:\n${desc.sdp}`);
console.log('pc2 setLocalDescription start');
try {
await pc2.setLocalDescription(desc);
onSetLocalSuccess(pc2);
} catch (e) {
onSetSessionDescriptionError(e);
}
console.log('pc1 setRemoteDescription start');
try {
await pc1.setRemoteDescription(desc);
onSetRemoteSuccess(pc1);
} catch (e) {
onSetSessionDescriptionError(e);
}
}
async function onIceCandidate(pc, event) {
try {
await (getOtherPc(pc).addIceCandidate(event.candidate));
onAddIceCandidateSuccess(pc);
} catch (e) {
onAddIceCandidateError(pc, e);
}
console.log(`${getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : '(null)'}`);
}
function onAddIceCandidateSuccess(pc) {
console.log(`${getName(pc)} addIceCandidate success`);
}
function onAddIceCandidateError(pc, error) {
console.log(`${getName(pc)} failed to add ICE Candidate: ${error.toString()}`);
}
function onIceStateChange(pc, event) {
if (pc) {
console.log(`${getName(pc)} ICE state: ${pc.iceConnectionState}`);
console.log('ICE state change event: ', event);
}
}
function hangup() {
console.log('Ending call');
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
hangupButton.disabled = true;
callButton.disabled = false;
}
本地,呼叫者
// servers是配置文件(TURN and STUN配置)
pc1 = new webkitRTCPeerConnection(servers);
// ...
pc1.addStream(localStream);
远端,被叫着
创建一个offer ,将其设定为PC1的局部描述(local description),PC2远程描述(remote description)。
这样可以不使用信令通讯,因为主叫和被叫都在同一网页上。
pc1.createOffer(gotDescription1);
//...
function gotDescription1(desc){
pc1.setLocalDescription(desc);
trace("Offer from pc1 \n" + desc.sdp);
pc2.setRemoteDescription(desc);
pc2.createAnswer(gotDescription2);
}
创建pc2,当pc1产生视频流,则显示在remoteVideo视频控件(video element):
pc2 = new webkitRTCPeerConnection(servers);
pc2.onaddstream = gotRemoteStream;
//...
function gotRemoteStream(e){
remoteVideo.src = URL.createObjectURL(e.stream);
trace('pc2 received remote stream');
}
运行结果:
Navigated to https://webrtc.github.io/samples/src/content/peerconnection/pc1/
adapter.js:32 This appears to be Chrome
common.js:8 12.639: Requesting local stream
adapter.js:32 chrome: {"audio":true,"video":true}
common.js:8 12.653: Received local stream
common.js:8 14.038: Local video videoWidth: 640px, videoHeight: 480px
common.js:8 15.183: Starting call
common.js:8 15.183: Using video device: Integrated Camera (04f2:b39a)
common.js:8 15.183: Using audio device: 默认
common.js:8 15.185: Created local peer connection object pc1
common.js:8 15.186: Created remote peer connection object pc2
common.js:8 15.186: Added local stream to pc1
common.js:8 15.187: pc1 createOffer start
common.js:8 15.190: Offer from pc1
v=0
o=- 5740173043645401541 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:MOApAbo/PL8Jl3m9
a=ice-pwd:dxcuXVAFcyVqbgwi0QdQNh0S
a=fingerprint:sha-256 5F:CB:FF:EF:73:09:BC:0A:6F:18:0C:DB:11:A5:AE:AF:37:49:37:71:D0:FE:BA:39:EC:53:6B:10:8C:8A:95:9E
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10; useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
a=maxptime:60
a=ssrc:32244674 cname:anS0gTF+aWAKlwYj
a=ssrc:32244674 msid:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT 8ae8dd85-bd5c-49ff-a9bd-f4b88f2663c7
a=ssrc:32244674 mslabel:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT
a=ssrc:32244674 label:8ae8dd85-bd5c-49ff-a9bd-f4b88f2663c7
m=video 9 UDP/TLS/RTP/SAVPF 100 116 117 96
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:MOApAbo/PL8Jl3m9
a=ice-pwd:dxcuXVAFcyVqbgwi0QdQNh0S
a=fingerprint:sha-256 5F:CB:FF:EF:73:09:BC:0A:6F:18:0C:DB:11:A5:AE:AF:37:49:37:71:D0:FE:BA:39:EC:53:6B:10:8C:8A:95:9E
a=setup:actpass
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=sendrecv
a=rtcp-mux
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
a=rtpmap:96 rtx/90000
a=fmtp:96 apt=100
a=ssrc-group:FID 1099776253 671187929
a=ssrc:1099776253 cname:anS0gTF+aWAKlwYj
a=ssrc:1099776253 msid:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT eda73070-3562-4daf-ae0d-143694f294d5
a=ssrc:1099776253 mslabel:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT
a=ssrc:1099776253 label:eda73070-3562-4daf-ae0d-143694f294d5
a=ssrc:671187929 cname:anS0gTF+aWAKlwYj
a=ssrc:671187929 msid:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT eda73070-3562-4daf-ae0d-143694f294d5
a=ssrc:671187929 mslabel:ZWvBmXl2Dax58ugXR3BYDITTKIIV1TYPqViT
a=ssrc:671187929 label:eda73070-3562-4daf-ae0d-143694f294d5
common.js:8 15.190: pc1 setLocalDescription start
common.js:8 15.191: pc2 setRemoteDescription start
common.js:8 15.192: pc2 createAnswer start
common.js:8 15.202: pc1 setLocalDescription complete
common.js:8 15.203: pc1 ICE candidate:
candidate:2999745851 1 udp 2122260223 192.168.56.1 64106 typ host generation 0
common.js:8 15.204: pc1 ICE candidate:
candidate:1425577752 1 udp 2122194687 172.17.22.106 64107 typ host generation 0
common.js:8 15.204: pc1 ICE candidate:
candidate:2733511545 1 udp 2122129151 192.168.127.1 64108 typ host generation 0
common.js:8 15.204: pc1 ICE candidate:
candidate:1030387485 1 udp 2122063615 192.168.204.1 64109 typ host generation 0
common.js:8 15.205: pc1 ICE candidate:
candidate:3003979406 1 udp 2121998079 172.17.26.47 64110 typ host generation 0
common.js:8 15.206: pc2 setRemoteDescription complete
common.js:8 15.206: Answer from pc2:
v=0
o=- 3554329696104028001 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:raDEDz+tkFTWXMn8
a=ice-pwd:RQ8bf7mtOXHIDQ0/vF25IRMf
a=fingerprint:sha-256 5F:CB:FF:EF:73:09:BC:0A:6F:18:0C:DB:11:A5:AE:AF:37:49:37:71:D0:FE:BA:39:EC:53:6B:10:8C:8A:95:9E
a=setup:active
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=recvonly
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10; useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
a=maxptime:60
m=video 9 UDP/TLS/RTP/SAVPF 100 116 117 96
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:raDEDz+tkFTWXMn8
a=ice-pwd:RQ8bf7mtOXHIDQ0/vF25IRMf
a=fingerprint:sha-256 5F:CB:FF:EF:73:09:BC:0A:6F:18:0C:DB:11:A5:AE:AF:37:49:37:71:D0:FE:BA:39:EC:53:6B:10:8C:8A:95:9E
a=setup:active
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=recvonly
a=rtcp-mux
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
a=rtpmap:96 rtx/90000
a=fmtp:96 apt=100
common.js:8 15.207: pc2 setLocalDescription start
common.js:8 15.207: pc1 setRemoteDescription start
common.js:8 15.208: pc2 received remote stream
2common.js:8 15.209: pc1 addIceCandidate success
3common.js:8 15.210: pc1 addIceCandidate success
common.js:8 15.224: pc1 ICE candidate:
candidate:2999745851 2 udp 2122260222 192.168.56.1 64111 typ host generation 0
common.js:8 15.224: pc1 ICE candidate:
candidate:1425577752 2 udp 2122194686 172.17.22.106 64112 typ host generation 0
common.js:8 15.225: pc1 ICE candidate:
candidate:2733511545 2 udp 2122129150 192.168.127.1 64113 typ host generation 0
common.js:8 15.225: pc1 ICE candidate:
candidate:1030387485 2 udp 2122063614 192.168.204.1 64114 typ host generation 0
common.js:8 15.226: pc1 ICE candidate:
candidate:3003979406 2 udp 2121998078 172.17.26.47 64115 typ host generation 0
common.js:8 15.226: pc1 ICE candidate:
candidate:2999745851 1 udp 2122260223 192.168.56.1 64116 typ host generation 0
common.js:8 15.227: pc1 ICE candidate:
candidate:1425577752 1 udp 2122194687 172.17.22.106 64117 typ host generation 0
common.js:8 15.227: pc1 ICE candidate:
candidate:2733511545 1 udp 2122129151 192.168.127.1 64118 typ host generation 0
common.js:8 15.227: pc1 ICE candidate:
candidate:1030387485 1 udp 2122063615 192.168.204.1 64119 typ host generation 0
common.js:8 15.228: pc1 ICE candidate:
candidate:3003979406 1 udp 2121998079 172.17.26.47 64120 typ host generation 0
common.js:8 15.228: pc1 ICE candidate:
candidate:2999745851 2 udp 2122260222 192.168.56.1 64121 typ host generation 0
common.js:8 15.228: pc1 ICE candidate:
candidate:1425577752 2 udp 2122194686 172.17.22.106 64122 typ host generation 0
common.js:8 15.229: pc1 ICE candidate:
candidate:2733511545 2 udp 2122129150 192.168.127.1 64123 typ host generation 0
common.js:8 15.229: pc1 ICE candidate:
candidate:1030387485 2 udp 2122063614 192.168.204.1 64124 typ host generation 0
common.js:8 15.230: pc1 ICE candidate:
candidate:3003979406 2 udp 2121998078 172.17.26.47 64125 typ host generation 0
common.js:8 15.231: pc1 addIceCandidate success
common.js:8 15.231: pc2 setLocalDescription complete
common.js:8 15.231: pc1 setRemoteDescription complete
common.js:8 15.231: pc1 addIceCandidate success
8common.js:8 15.232: pc1 addIceCandidate success
5common.js:8 15.233: pc1 addIceCandidate success
common.js:8 15.233: pc2 ICE state: checking
main.js:197 ICE state change event: Event {isTrusted: true}
common.js:8 15.243: pc2 ICE candidate:
candidate:2999745851 1 udp 2122260223 192.168.56.1 64126 typ host generation 0
common.js:8 15.246: pc2 ICE candidate:
candidate:1425577752 1 udp 2122194687 172.17.22.106 64127 typ host generation 0
common.js:8 15.247: pc2 ICE candidate:
candidate:2733511545 1 udp 2122129151 192.168.127.1 64128 typ host generation 0
common.js:8 15.248: pc2 ICE candidate:
candidate:1030387485 1 udp 2122063615 192.168.204.1 64129 typ host generation 0
common.js:8 15.249: pc2 ICE candidate:
candidate:3003979406 1 udp 2121998079 172.17.26.47 64130 typ host generation 0
common.js:8 15.250: pc1 ICE state: checking
main.js:197 ICE state change event: Event {isTrusted: true}
5common.js:8 15.251: pc2 addIceCandidate success
common.js:8 16.271: pc1 ICE state: connected
main.js:197 ICE state change event: Event {isTrusted: true}
common.js:8 16.272: pc2 ICE state: connected
main.js:197 ICE state change event: Event {isTrusted: true}
common.js:8 16.326: Remote video size changed to 640x480
common.js:8 16.326: Setup time: 1142.795ms
common.js:8 16.326: Remote video videoWidth: 640px, videoHeight: 480px
common.js:8 16.326: Remote video size changed to 640x480
common.js:8 18.447: Ending call
但在真实世界,不可能不通过服务器传送信令,WebRTC 两端必须通过服务器交换信令。
- 用户相互发现对方和交换“真实世界”的信息,如姓名。
- WebRTC客户端应用程序交换网络信息。
- 视频格式和分辨率等交换数据。
- 客户端应用穿越NAT网关和防火墙。
所以,你的服务器端需要实现的功能:
- 用户发现和通信。
- 信令通信。
- NAT和防火墙的穿越。
- 在点对点通信失败后的中继服务(补救服务)。
STUN协议和它的扩展TURN使用ICE framework。
ICE先试图在节点直接连接,通过最低的延迟,通过UDP协议。在这个过程中:STUN服务器启用NAT后面找到它的公共地址和端口。
STUN, TURN and signaling 介绍:
http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/
许多现有的WebRTC应用只是Web浏览器之间的通信,但网关服务器可以使WebRTC应用在浏览器与设备如电话互动(PSTN和VoIP系统)。
2012五月,Doubango开源了sipml5 SIP客户端,sipml5是通过WebRTC和WebSocket,使视频和语音通话在浏览器和应用程序(iOS或Android)之间进行。