WebRTC开发基础(WebRTC入门系列2:RTCPeerConnection)
2016-01-13 11:09
1181 查看
RTCPeerConnection的作用是在浏览器之间建立数据的“点对点”(peer to peer)通信.
View Code
本地,呼叫者
远端,被叫着
创建一个offer ,将其设定为PC1的局部描述(local description),PC2远程描述(remote description)。
这样可以不使用信令通讯,因为主叫和被叫都在同一网页上。
创建pc2,当pc1产生视频流,则显示在remoteVideo视频控件(video element):
运行结果:
但在真实世界,不可能不通过服务器传送信令,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)
许多现有的WebRTC应用只是Web浏览器之间的通信,但网关服务器可以使WebRTC应用在浏览器与设备如电话互动(PSTN和VoIP系统)。
2012五月,Doubango开源了sipml5 SIP客户端,sipml5是通过WebRTC和WebSocket,使视频和语音通话在浏览器和应用程序(iOS或Android)之间进行。
网址:
https://www.doubango.org/sipml5/
/* * 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/
相关文章推荐
- HTTP Keep-Alive详解[转]
- App Transport Security has blocked a cleartext HTTP
- Android 广播监听网络状态
- TCP/IP, WebSocket 和 MQTT的了解----转载好文----阿冬专栏!!!
- 程序员之网络安全系列(六):动态密码
- Android 网络编程 Socket
- IOS网络访问之使用AFNetworking
- TCP连接的建立(二)
- TCP协议中的三次握手和四次挥手+利用wireshark分析包
- tcpdf生成中文pdf
- linux网络体检
- 快速Android开发系列网络篇之Retrofit
- Android网络编程之URLConnection&HttpURLConnection
- 常用的Linux网络配置命令
- 如何利用socket进行HTTP访问
- TCP/IP协议三次握手与四次握手流程解析
- 基于梯度下降的神经网络
- TCP和UDP的差别
- GRUB学习笔记(转自http://www.cnblogs.com/evilzy/archive/2008/03/30/1130173.html)
- qemu创建虚拟机,gdb调试及网络配置