您的位置:首页 > 理论基础 > 计算机网络

WebRTC开发基础(WebRTC入门系列2:RTCPeerConnection)

2016-01-13 11:09 1181 查看
RTCPeerConnection的作用是在浏览器之间建立数据的“点对点”(peer to peer)通信.

/*
*  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';

var startButton = document.getElementById('startButton');
var callButton = document.getElementById('callButton');
var hangupButton = document.getElementById('hangupButton');
callButton.disabled = true;
hangupButton.disabled = true;
startButton.onclick = start;
callButton.onclick = call;
hangupButton.onclick = hangup;

var startTime;
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');

localVideo.addEventListener('loadedmetadata', function() {
trace('Local video videoWidth: ' + this.videoWidth +
'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.addEventListener('loadedmetadata', function() {
trace('Remote video videoWidth: ' + this.videoWidth +
'px,  videoHeight: ' + this.videoHeight + 'px');
});

remoteVideo.onresize = function() {
trace('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) {
var elapsedTime = window.performance.now() - startTime;
trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
};

var localStream;
var pc1;
var pc2;
var offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};

function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}

function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}

function gotStream(stream) {
trace('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
callButton.disabled = false;
}

function start() {
trace('Requesting local stream');
startButton.disabled = true;
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}

function call() {
callButton.disabled = true;
hangupButton.disabled = false;
trace('Starting call');
startTime = window.performance.now();
var videoTracks = localStream.getVideoTracks();
var audioTracks = localStream.getAudioTracks();
if (videoTracks.length > 0) {
trace('Using video device: ' + videoTracks[0].label);
}
if (audioTracks.length > 0) {
trace('Using audio device: ' + audioTracks[0].label);
}
var servers = null;
pc1 = new RTCPeerConnection(servers);
trace('Created local peer connection object pc1');
pc1.onicecandidate = function(e) {
onIceCandidate(pc1, e);
};
pc2 = new RTCPeerConnection(servers);
trace('Created remote peer connection object pc2');
pc2.onicecandidate = function(e) {
onIceCandidate(pc2, e);
};
pc1.oniceconnectionstatechange = function(e) {
onIceStateChange(pc1, e);
};
pc2.oniceconnectionstatechange = function(e) {
onIceStateChange(pc2, e);
};
pc2.onaddstream = gotRemoteStream;

pc1.addStream(localStream);
trace('Added local stream to pc1');

trace('pc1 createOffer start');
pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError,
offerOptions);
}

function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
trace('Offer from pc1\n' + desc.sdp);
trace('pc1 setLocalDescription start');
pc1.setLocalDescription(desc, function() {
onSetLocalSuccess(pc1);
}, onSetSessionDescriptionError);
trace('pc2 setRemoteDescription start');
pc2.setRemoteDescription(desc, function() {
onSetRemoteSuccess(pc2);
}, onSetSessionDescriptionError);
trace('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.
pc2.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);
}

function onSetLocalSuccess(pc) {
trace(getName(pc) + ' setLocalDescription complete');
}

function onSetRemoteSuccess(pc) {
trace(getName(pc) + ' setRemoteDescription complete');
}

function onSetSessionDescriptionError(error) {
trace('Failed to set session description: ' + error.toString());
}

function gotRemoteStream(e) {
remoteVideo.srcObject = e.stream;
trace('pc2 received remote stream');
}

function onCreateAnswerSuccess(desc) {
trace('Answer from pc2:\n' + desc.sdp);
trace('pc2 setLocalDescription start');
pc2.setLocalDescription(desc, function() {
onSetLocalSuccess(pc2);
}, onSetSessionDescriptionError);
trace('pc1 setRemoteDescription start');
pc1.setRemoteDescription(desc, function() {
onSetRemoteSuccess(pc1);
}, onSetSessionDescriptionError);
}

function onIceCandidate(pc, event) {
if (event.candidate) {
getOtherPc(pc).addIceCandidate(new RTCIceCandidate(event.candidate),
function() {
onAddIceCandidateSuccess(pc);
},
function(err) {
onAddIceCandidateError(pc, err);
}
);
trace(getName(pc) + ' ICE candidate: \n' + event.candidate.candidate);
}
}

function onAddIceCandidateSuccess(pc) {
trace(getName(pc) + ' addIceCandidate success');
}

function onAddIceCandidateError(pc, error) {
trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
}

function onIceStateChange(pc, event) {
if (pc) {
trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState);
console.log('ICE state change event: ', event);
}
}

function hangup() {
trace('Ending call');
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
hangupButton.disabled = true;
callButton.disabled = false;
}


View Code

本地,呼叫者

// 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后面找到它的公共地址和端口。



ICE顺序:

先UDP,

如果UDP失败 则TCP,

如果TCP失败 则HTTP,

最后HTTPS

具体实现:

ICE 先使用 STUN 通过UDP直连

如果UDP、TCP、http等失败 则使用TURN 中继服务(Relay server)



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)之间进行。

网址:

https://www.doubango.org/sipml5/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: