有了前面的准备,离视频会议的建设又进了一层了。我们现在准备好了音视频流的数据。双方的视频数据需要交给对方,对方就能看到相关的数据,达到了视频会议的效果。
假设我们是一个视频会议的发起人,我们当然先要知道,我们想跟谁进行视频通话,对方需要把相关的环境数据,比如我用的是什么视频编码啊,我们通信的协议是什么?我们把这些数据信息取了个名字叫 sdp
。互相交换了环境数据后,被叫端需要把数据的地址准备好,这些数据协议我们成为 ice
,当数据准备完成以后,被叫端把ice
发给发起端,发起端通过这个ice
就能够连上被叫端了。
简单的总结,互换两种信息,环境描述数据和数据地址。这两种叫为 sdp
和ice
。下面的例子为了简单起见,在同一个程序中,同时设置了两个角色,发起端和被叫端。这样我们比较容易看起 webrtc
中RTCPeerConnection
的原理。
- 1创建摄像头数据
这个在前面的章节中,都有介绍,也就是我们音视频数据的基础。
//开启音视频源
async function start() {
console.log('Requesting local stream');
try {
//捕获摄像头和麦克风的流,放到localVideo中
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}`);
}
}
2.创建RTCPeerConnection通道,让两端互换信息
RTCPeerConnection
就是webrtc
的核心,他创建了通道,互相设置彼此的信息,生成自己的直播地址。
pc2
拨打pc1
,pc1
把自己的sdp
给了pc2
,pc2
做了相应,在把自己的sdp
给了对方。双方的信息交互完成。这时候pc1
生成了自己的播放地址 也就是ice
,ice
当然也是需要给对方,对方用ice
就能够看见被叫端的数据了。
//拨打,建立连接
async function call() {
callButton.disabled = true;
console.log('Starting call');
startTime = window.performance.now();
const configuration = {};
console.log('RTCPeerConnection configuration:', configuration);
//源连接,
pc1 = new RTCPeerConnection(configuration);
//当ice准备好后,加到目标源中
pc1.addEventListener('icecandidate', e => onIceCandidate(pc2, e));
//把localStream的音视频,放到源中
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
//目标
pc2 = new RTCPeerConnection(configuration);
//等待源发来的流
pc2.addEventListener('track', gotRemoteStream);
try {
console.log('pc1 createOffer start');
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
//创建和设置连接描述
const desc_pc1 = await pc1.createOffer(offerOptions);
console.log("desc_pc1:");
console.log(desc_pc1);
await pc1.setLocalDescription(desc_pc1);
//目标 拿到源的连接描述后,给自己,并生成自己的连接描述
await pc2.setRemoteDescription(desc_pc1);
const desc_pc2 = await pc2.createAnswer();
console.log("answer desc_pc2 :");
console.log(desc_pc2);
await pc2.setLocalDescription(desc_pc2);
//源拿到目标的连接描述后,知道有人要来连接,开启 通道
await pc1.setRemoteDescription(desc_pc2);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
等待生成ice
,添加到发起方,这样视频就连通了。
async function onIceCandidate(pc, event) {
try {
console.log(event.candidate.address);
//源发来的ice,加入到目标中
console.log(event.candidate);
pc.addIceCandidate(event.candidate);
onAddIceCandidateSuccess(pc);
} catch (e) {
onAddIceCandidateError(pc, e);
}
//console.log(`${getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : '(null)'}`);
}
来看看sdp
和ice
张什么样子,这也是协议的主要部分。
sdp
"v=0
o=- 2176126363205996170 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:q14N
a=ice-pwd:abTQebhAIpOyGtpZpoyuzuyU
a=ice-options:trickle
a=fingerprint:sha-256 F7:16:89:90:4E:D7:7C:94:34:1C:10:5D:46:45:46:35:A3:5B:48:E4:B5:1E:E0:DE:64:F2:71:59:40:03:22:D4
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S f7248998-5512-4bf6-8611-14ef47dedc2d
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
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:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:2949455037 cname:WiqznckmjtPMfa13
a=ssrc:2949455037 msid:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S f7248998-5512-4bf6-8611-14ef47dedc2d
a=ssrc:2949455037 mslabel:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S
a=ssrc:2949455037 label:f7248998-5512-4bf6-8611-14ef47dedc2d
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 127 121 125 107 108 109 124 120 123 119 35 36 41 42 114 115 116
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:q14N
a=ice-pwd:abTQebhAIpOyGtpZpoyuzuyU
a=ice-options:trickle
a=fingerprint:sha-256 F7:16:89:90:4E:D7:7C:94:34:1C:10:5D:46:45:46:35:A3:5B:48:E4:B5:1E:E0:DE:64:F2:71:59:40:03:22:D4
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S 683b59d0-b640-4f8f-9f41-93cbfc557fb5
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=9
8a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:121 rtx/90000
a=fmtp:121 apt=127
a=rtpmap:125 H264/90000
a=rtcp-fb:125 goog-remb
a=rtcp-fb:125 transport-cc
a=rtcp-fb:125 ccm fir
a=rtcp-fb:125 nack
a=rtcp-fb:125 nack pli
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=125
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=10
8a=rtpmap:124 H264/90000
a=rtcp-fb:124 goog-remb
a=rtcp-fb:124 transport-cc
a=rtcp-fb:124 ccm fir
a=rtcp-fb:124 nack
a=rtcp-fb:124 nack pli
a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:120 rtx/90000
a=fmtp:120 apt=124
a=rtpmap:123 H264/90000
a=rtcp-fb:123 goog-remb
a=rtcp-fb:123 transport-cc
a=rtcp-fb:123 ccm fir
a=rtcp-fb:123 nack
a=rtcp-fb:123 nack pli
a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:119 rtx/90000
a=fmtp:119 apt=123
a=rtpmap:35 H264/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=fmtp:35 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:41 AV1/90000
a=rtcp-fb:41 goog-remb
a=rtcp-fb:41 transport-cc
a=rtcp-fb:41 ccm fir
a=rtcp-fb:41 nack
a=rtcp-fb:41 nack pli
a=rtpmap:42 rtx/90000
a=fmtp:42 apt=41
a=rtpmap:114 red/90000
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=114
a=rtpmap:116 ulpfec/90000
a=ssrc-group:FID 3205961662 1789602341
a=ssrc:3205961662 cname:WiqznckmjtPMfa13
a=ssrc:3205961662 msid:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S 683b59d0-b640-4f8f-9f41-93cbfc557fb5
a=ssrc:3205961662 mslabel:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S
a=ssrc:3205961662 label:683b59d0-b640-4f8f-9f41-93cbfc557fb5
a=ssrc:1789602341 cname:WiqznckmjtPMfa13
a=ssrc:1789602341 msid:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S 683b59d0-b640-4f8f-9f41-93cbfc557fb5
a=ssrc:1789602341 mslabel:FmdNsj66Elz3qRkmVom6A4WVF8VACLeDmy7S
a=ssrc:1789602341 label:683b59d0-b640-4f8f-9f41-93cbfc557fb5
"
ice
:
ice是不是包含着ip和端口号。
{
"candidate": "candidate:4142333030 1 udp 2122260223 192.168.40.34 56513 typ host generation 0 ufrag Ds60 network-id 1",
"sdpMid": "0",
"sdpMLineIndex": 0
}
用命令查看56513端口号:
netstat -aon|findstr "56513"
获得如下的信息:
UDP 192.168.40.34:56513 *:* 13900
端口号确实是开着的。
对于ice
的理解,同样的看下面的代码:文章来源:https://www.toymoban.com/news/detail-409220.html
//源连接,
pc1 = new RTCPeerConnection(configuration);
//把localStream的音视频,放到源中
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
//目标
pc2 = new RTCPeerConnection(configuration);
//等待源发来的流
pc2.addEventListener('track', gotRemoteStream);
//当ice准备好后,加到目标源中
pc2.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
这样也是可以进行连接显示的,我们就可以这样的理解ice
,RTCPeerConnection
中虽然是两方,当时只有一路流,双方都生成了ice
,最后ice
通过协商的方式进行选取。我们这里固定了一方的ice
,这样可能会减少ice
的协商时间。文章来源地址https://www.toymoban.com/news/detail-409220.html
到了这里,关于webrtc入门:4.RTCPeerConnection连接音视频流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!