webrtc入门:4.RTCPeerConnection连接音视频流

有了前面的准备,离视频会议的建设又进了一层了。我们现在准备好了音视频流的数据。双方的视频数据需要交给对方,对方就能看到相关的数据,达到了视频会议的效果。

假设我们是一个视频会议的发起人,我们当然先要知道,我们想跟谁进行视频通话,对方需要把相关的环境数据,比如我用的是什么视频编码啊,我们通信的协议是什么?我们把这些数据信息取了个名字叫 sdp。互相交换了环境数据后,被叫端需要把数据的地址准备好,这些数据协议我们成为 ice,当数据准备完成以后,被叫端把ice发给发起端,发起端通过这个ice就能够连上被叫端了。

简单的总结,互换两种信息,环境描述数据和数据地址。这两种叫为 sdpice。下面的例子为了简单起见,在同一个程序中,同时设置了两个角色,发起端和被叫端。这样我们比较容易看起 webrtcRTCPeerConnection的原理。

  • 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拨打pc1pc1把自己的sdp给了pc2pc2做了相应,在把自己的sdp给了对方。双方的信息交互完成。这时候pc1生成了自己的播放地址 也就是iceice当然也是需要给对方,对方用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)'}`);
}

在这里插入图片描述

来看看sdpice张什么样子,这也是协议的主要部分。

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的理解,同样的看下面的代码:

  //源连接,
  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));

这样也是可以进行连接显示的,我们就可以这样的理解iceRTCPeerConnection 中虽然是两方,当时只有一路流,双方都生成了ice,最后ice通过协商的方式进行选取。我们这里固定了一方的ice,这样可能会减少ice的协商时间。

猜你喜欢

转载自blog.csdn.net/weixin_40425640/article/details/124341142