鸟语天空
WebRTC RTCPeerConnection 接口
post by:追风剑情 2024-2-21 14:49

WebRTC 使用 RTCPeerConnection 接口来管理对等连接,该接口提供了建立、管理、监控、关闭对等连接的方法。

//RTCPeerConnection接口定义
interface RTCPeerConnection : EventTarget {
	constructor(optional RTCConfiguration configuration = {});
	static Promise<RTccertificate> generateCertificate(AlgorithmIdentifier keygenAlgorithm);
	Promise<RTCSessionDescriptionInit> createOffer(optional RicofferOptions options = {});
	Promise<RTCSessionDescriptionInit> createAnswer(optional RTCAnswerOptions options = {});
	Promise<void> setLocalDescription(optional RTcSessionDescriptionInit description = {});
	readonly attribute RTCSessionDescription? localDescription;
	readonly attribute RTcSessionDescription? currentLocalDescription;
	readonly attribute RTCSessionDescription? pendingLocalDescription;
	Promise<void> setRemoteDescription(optional RrcsessionDescriptionInit description = {});
	readonly attribute RTCSessionDescription? remoteDescription;
	readonly attribute RTCSessionDescription? currentRemoteDescription;
	readonly attribute RTCSessionDescription? pendingRemoteDescription;
	Promise<void> addIceCandidate(optional RTCIceCandidateInit candidate = {});
	readonly attribute RTCSignalingState signalingState;
	readonly attribute RTCIceGatheringState iceGatheringState;
	readonly attribute RTCIceConnectionState iceConnectionState;
	readonly attribute RTCPeerConnectionState connectionState;
	readonly attribute boolean? canTricklelceCandidates;
	void restartIce();
	RTCConfiguration getConfiguration();
	void setConfiguration(optional RTcConfiguration configuration = {});
	void close();
	attribute EventHandler onnegotiationneeded;
	attribute EventHandler onicecandidate;
	attribute EventHandler onicecandidateerror;
	attribute EventHandler onsignalingstatechange;
	attribute EventHandler oniceconnectionstatechange;
	attribute EventHandler onicegatheringstatechange;
	attribute EventHandler onconnectionstatechange;
}  

构造函数 RTCPeerConnection

该构造函数返回一个新创建的 RTCPeerConnection 对象,代表本地与对等端的一个连接。

pc = new RTcPeerConnection([configuration]);  

参数:confguration,可选参数,是一个类型为RTCConfiguration的字典对象,提供了创建新连接的配置选项。
返回值:RTCPeerConnection 对象。

下面的代码清单创建了一个 RTCPeerConnection对象,并在参数中指定了TURN和STUN的地址信息,其中TURN服务器地址提供了用户名和密码。

//RTCPeerConnection构造函数示例
const iceConfiguration = {
	iceServers: [
		{
			urls: 'turn:my-turn-server.mycompany.com:19403',
			username: 'optional-username',
			credentials: 'auth-token'
		},
		{
			urls: [
				"stun:stun.example.com",
				"stun:stun-l.example.com"
			]
		}
	]
}
const peerConnection = new RTcPeerConnection(iceConfiguration);  

出于网络安全性的考虑,通常为TURN服务器指定用户名和密码,这需要configuration参数中指定相应的username和credentials。

连接配置 RTCConfiguration

RTCConfiguration 的属性说明
属性 类型 说明
iceServers sequence<RTCIceServer> 可选参数,包含 STUN 和 TURN 服务器信息的数组
iceTransportPolicy RTCleeTransportPolicey 可选参数,指定传输策略,默认值为 all。RTCIceTransportPolicy为枚举类型,枚举值如下:
1) relay,流量全部通过 TURN 服务转发;
2) all,使用任意类型的网络候选,这是 WebRTC 的默认策略
bundlePolicy RTCBundlePolicy 可选参数,指定 ICE 协商过程中的媒体绑定策略,默认值为 balanced
rtepMuxPolicy RTCRtepMuxPolicy RTC 多路复用策略,当前唯一取值为 require,其含义是仅为 RTP 和 RTCP 收集 ICE 候选者,如果对等端不支持 rtcpmux,则协商失败
certificates sequence<RTCCertificate> 可选参数,包含证书的数组。如果该参数未指定 WebRTC 将自动为每一个连接创建-套证书
iceCandidatePoolSize octet 可选参数,指定了 ICE 预取池的大小,默认值为 0,表示关闭 ICE 预取。通常开启 ICE 预取功能,以加速建立连接过程

RTCIceServer 的属性说明
属性 类型 说明
urls 字符串或者字符串数组 必选参数,为 STUN 或 TURN 的服务器地址
username 字符串 可选参数,为 TURN 服务器指定的用户名
credential 字符串 可选参数,为 TURN 服务器指定的密码
credentialType RTCIceCredentialType 可选参数,为 TURN 服务器指定的认证方式,默认是 password

注意   RTCPeerConnection 中的 urls 参数指定了STUN/TURN 服务器地址,WebRTC 会逐一 对这些地址尝试建立连接,如果指定的地址过多,将会显著增加 P2P 建立连接时间。

如果对等端不支持绑定(Bundle),绑定策略将影响协商哪些媒体轨道,以及收集哪些 ICE 候选者。如果对等端支持绑定,则所有媒体轨道和数据通道都绑定在同一传输通道上。

RTCBundlePolicy 枚举值说明
枚举值 说明
balanced 如果对等端不支持绑定,则为每个音视频轨道单独建立一个传输通道
max-compat 如果对等端不支持绑定,则所有媒体轨道使用同一个传输通道
max-bundle 如果对等端不支持绑定,则只选择一个媒体轨道进行协商,并且只发送一个

// RTCCertificate 的定义
interface RTCCertificate {
	readonly attribute DOMTimeStamp expires;
	sequence<RTCDtlsFingerprint> getFingerprints();
};
dictionary RTCDtlsFingerprint {
	DOMString algorithm;
	DOMString value;
};  

在 RTCCertificate 的属性中,expires 的单位是毫秒,表示证书过期时间,使用过期证书构造 RTCPeerConnection 将返回失败。

getFingerprints() 方法返回证书指纹数组,数组成员类型为 RTCDtlsFingerprint。在 RTCDtlsFingerprint 中,algorithm 是计算指纹数据用到的哈希算法,value 是以小写十六进制字符串表示的指纹数据,使用 generateCertificate() 方法可以创建证书,但我们通常不需要为 WebRTC 指定证书,WebRTC 将自动为每一个连接创建一套证书。

RTCPeerConnection 接囗的属性

1.canTrickleIceCandidates 只读

  该属性表示对等端是否支持ICETrickle,类型为布尔,值为true表示支持,值为false 表示不支持。

  目前大部分支持WebRTC的浏览器,也都支持ICE Trickle,如果不确定是否支持,可以检查该值进行判断。若值为false,则需要等待iceGatheringState值变为completed才能开始获取本地会话描述信息,此时已经完成了ICE候选者的收集,会话描述里包含所有的候选者。该属性由RTCPeerConnection.setRemoteDescription()方法设置,所以必须在此方法成功调用后才能获取。

  下面的代码清单演示了属性 canTrickleIceCandidates的使用方法。该示例首先创建了RTCPeerConnection对象pc,调用setRemoteDescription()方法设置从信令服务器收到的会话描述信息remoteOffer,创建并设置本地会话描述 answer,成功后获取属性 canTricklelceCandidates值并判断对等端是否支持ICETrickle,如果支持则获取本地会话描述并回复给对等端。注意,该会话描述只包含已收集的候选者,候选收集过程并没有结束,新的候选者仍然需要发送到对等端。如果不支持则等待icegatheringstatechange事件,待全部完成ICE候选者收集后再获取本地会话描述并回复给对等端。

//canTricklelceCandidates示例
const pc = new RTCPeerConnection();
pc.setRemoteDescription(remoteOffer)
.then(()=> pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(_ => {
	if (pc.canTrickleIceCandidates) {
		return pc.localDescription;
	}
	return new Promise(resolve => {
		pc.addEventListener('icegatheringstatechange', e => {
			if(e.target.iceGatheringState ==='complete') {
				resolve(pc.localDescription);
			}
		});
	});
})
.then(answer => sendAnswerToPeer(answer))
.catch(e => handleError(e));

pc.addEventListener('icecandidate', e => {
	if (pc.canTrickleIceCandidates) {
		sendCandidateToPeer(e.candidate);
	}
});  

  ICE Trickle 流程要求只要收集到新的ICE候选者,就马上发送给对等端,所以该示例监听了icecandidate事件,在该事件中将新的ICE候选者发送给对等端。

2.signalingState 只读

  该属性表示建立连接过程中ICE地址收集的状态,类型为RTCSignalingState。可以通过事件 signalingstatechange 探测该属性值的变化。RTCSignalingState 的定义如下面的代码清单所示。

//RTCSignalingState的定义
enum RTCsignalingState {
	"stable",
	"have-local-offer",
	"have-remote-offer",
	"have-local-pranswer",
	"have-remote-pranswer",
	"closed"
};  

  WebRTC中信号处理使用的是状态机,所以可以通过检查信号状态排查错误。如果收到了应答,但是signalingState值不是have-local-offer,这时候就知道是哪儿出错了,因为只有创建、设置(setLocalDescription)了本地提案并将提案发送给对等端,才有可能收到应答。这时候检查代码,就能找出导致状态错乱的问题。

RTCSignalingState 枚举常量说明
枚举值 说明
stable 没有进行中的SDP交换。这种情况出现在:1)RTCPcerConnection刚刚创建,还没有开始SDP交换;2)协商已经完成,连接成功建立
have-local-offer 已经创建了本地提案,并成功调用了setLocalDescription()方法
have-remote-offer 收到了对等端的提案,并成功调用了setRemoteDescription()方法
have-local-pranswer 已经创建了本地应答,并成功调用了setLocalDescription()方法
have-remote-pranswer 收到了对等端的应答,并成功调用了setRemoteDescription()方法
closed 连接已关闭

signalingState状态转换.png

3.iceGatheringState 只读

该属性表示建立连接过程中信号处理的状态,类型为RTCIceGatheringState。可以通过事件icegatheringstatechange探测该属性值的变化。RTCIceGatheringState的定义如下面的代码清单所示。

//RTCIceGatheringState的定义
enum RTCIceGatheringState {
	"new",
	"gathering",
	"complete"
}  

RTCIceGatheringState的定义
枚举值 说明
new RTCPeerConnection 中的 RTCIceTransport 至少有一个处于new状态,并且都没有处于 gathering状态
gathering RTCPeerConnection 中至少有一个 RTCIceTransport 处于 gathering 状态
complete RTCPeerConnection 中所有的 RTCIceTransport 都处于 complete 状态

4.iceConnectionState 只读

该属性表示与对等连接关联的ICE代理的状态,类型为RTCIceConnectionState。可以通过事件 iceconnectionstatechange探测该属性值的变化。RTCIceConnectionState 的定义如下面的代码清单所示。

//RTCIceConnectionState的定义
enum RTCIceConnectionState {
	"closed",
	"failed",
	"disconnected",
	"new",
	"checking",
	"completed",
	"connected"
}  

RTCIceConnectionState的定义
枚举值 说明
new 所有 RTCIceTransport 对象都处于 new 或 closed 状态
checking 任意一个 RTCIceTransport 对象处于 checking 或 new 状态
connected 所有 RTCIceTransport 对象都处于 connected、completed 或 closed 状态
completed 所有 RTCIceTransport 对象都处于 completed 或 closed 状态
disconnected 任意一个 RTCIceTransport 对象处于 disconnected 状态
failed 任意一个 RTCIceTransport 对象处于 failed 状态
closed RTCPeerConnection 对象处于关闭状态

RTCIceConnectionState 状态转换.png

5.connectionState 只读

该属性表示对等连接当前的状态,类型为RTCPeerConnectionState。通过事件connectionstatechange可以探测该属性值的变化。RTCPeerConnectionState的定义如下面的代码清单所示。

//RTCPeerConnectionState的定义
enum RTCPeerConnectionState {
	"closed",
	"failed",
	"disconnected",
	"new",
	"connecting",
	"connected"
}  

RTCPeerConnectionState 枚举值说明
枚举值 说明
closed RTCPcerConnection 对象处于关闭状态
failed 任意一个 RTCIceTransport 或 RTCDtlsTransport 处于 failed 状态
disconnected 任意一个 RTCIceTransport 处于 disconnected 状态
new 所有 RTCIceTransport 和 RTCDtlsTranspot 都处于 new 或 closed 状态,或者当前没有传输通道
connecting 任意一个 RTCIceTransport 处于 checking 状态,或者任意一个 RTCDtlsTransport 处于 connecting 状态
connected 所有 RTCIceTransport 都处于 connected、completed、closed 三种状态之一,并且所有 RTCDtlsTransport 都处于 connected 或 closed 状态

6.currentLocalDescription 只读

该属性表示上一次RTCPeerConnection成功建立连接时使用的本地会话描述,类型为RTCSessionDescription。

7.currentRemoteDescription 只读

该属性表示上一次RTCPeerConnection成功建立连接时使用的对等端会话描述,类型为RTCSessionDescription。

8.pendingLocalDescription 只读

该属性表示处于等待协商状态中的本地会话描述,类型为RTCSessionDescription。

9.pendingRemoteDescription 只读

该属性表示处于等待协商状态中的对等端会话描述,类型为RTCSessionDescription。

10.localDescription 只读

该属性表示当前本地会话描述,类型为RTCSessionDescription。该属性由setLocalDescription()方法设置,如果在未成功调用setLocalDescription()方法之前获取该属性,则返回空值。

11.remoteDescription 只读

该属性表示当前对等端会话描述,类型为RTCSessionDescription。该属性由setRemoteDescription()方法设置,如果在未成功调用方法setRemoteDescription()之前获取该属性,则返回空值。

RTCPeerConnection 接口的方法

1.generateCertificate() 静态方法

WebRTC要求两端的通信必须基于安全连接,在RTCConfiguration里为安全连接提供证书,如果当前没有证书,则可以调用该方法创建证书。

const cert = RTCPeerConnection.generateCertificate(keygenAlgorithm)  

参数:keygenAlgorithm,指定创建证书使用的算法。
返回值:RTCCertificate 对象。

下面的代码清单创建证书并在新建连接中使用证书。注意为generateCertificate()方法传入参数的格式和取值,目前大部分浏览器都支持RSASSA-PKCS1-v1_5算法。

//generateCertificate()方法示例
RTCPeerConnection.generateCertificate({
	name: "RSASSA-PKCS1-v1_5",
	hash: "SHA-256",
	modulusLength: 2048,
	publicExponent: new Uint8Array([1, 0, 1])
}).then((cert) => {
	const pc = new RTCPeerConnection({certificates: [cert]});
});  

2.addIceCandidate() 方法

WebRTC应用从信令服务器收到来自对等端的ICE候选者信息后,调用该方法将候选者信息通知给本地ICE代理层。

aPromise = pc.addIceCandidate(candidate);  

参数:candidate,可选参数,类型为RTCIceCandidateInit或者RTCIceCandidate。如果该参数为null,或者传入的对象参数中没有包含candidate属性,则意味着对等端没有更多ICE候选者信息。
返回值:无决议值的Promise对象。
异常:如果该方法调用出错,会抛出如下表所示的异常。

addIceCandidate 调用异常
异常名称 说明
TypeError 传人的参数中,属性 sdpMid 和 sdpMLinelndex 同时为空
InvalidStateError RTCPeerConnection 还没有收到对等端的SDP信息,remoteDescription 为空
OperationError 以下原因都可能导致该错误:
1)sdpMid 不为空,但是与 remoteDescription 不匹配;
2)sdpMLineIndex 索引值越界;
3)ufrag 不为空,但是与 remoteDescription 不匹配:
4)candidate 字符串值无效,或者解析失败

//addlceCandidate()方法示例
signaling.onmessage = async ({data: {description, candidate}}) => {
	try {
		if (description) {
			await pc.setRemoteDescription(description);
			// 如果收到的是提案,则开始应答
			if (description.type == 'offer') {
				await pc.setLocalDescription();
				signaling.send({description: pc.localDescription});
			}
		// 如果收到的是ICE候选者信息,则调用addIceCandidate()方法
		} else if (candidate) {
			await pc.addIceCandidate(candidate);
		}
	} catch (err) {
		console.error(err);
	}
};  

3.createOffer() 方法

该方法创建SDP提案信息,用于发起WebRTC连接,信息里包含已添加的媒体轨道编码格式、浏览器支持项,以及收集到的ICE候选者等信息。通过信令服务器将提案信息发送给对等端,开始进行建立连接协商。

调用该方法时,signalingState须处于stable或have-local-offer状态,否则将导致InvalidStateError错误。

aPromise = pc.createoffer([options]);  

参数:options,可选参数,类型为RTCOfferOptions,包含创建提案的选项。
返回值:Promise值,调用成功则获得类型为RTCSessionDescriptionInit的决议值。
异常:如果调用失败,会抛出如下表所示的异常。

createOffer 调用异常
异常名称 说明
InvalidStateError 当前 signalingState 状态为非 stable 或 have-local-offer,或当前连接已关闭
NotReadableError 没有提供安全证书,并且WebRTC也不能创建证书
OperationError 非上述原因导致的其他失败

//RTCOfferOptions的定义
dictionary RTCOfferOptions : RTCOfferAnswerOptions {
	boolean iceRestart = false:
	boolean offerToReceiveAudio;
	boolean offerToReceiveVideo;
};  

RTCOfferOptions 属性说明
属性 说明
iceRestart 默认为false,表示创建的SDP中ICE候选者与currentLocalDescription 相同;true表示创建的SDP中,ICE候选者与currentocalDescription不同
offerToReceiveAudio 默认为true,表示允许对等端发送音频; false表示不允许对等端发送音频
offerToReceiveVideo 默认为true,表示允许对等端发送视频; false表示不允许对等端发送视频

下面的代码清单创建了提案信息,如果成功则通过信令服务器发送给对等端,如果失败则进入错误处理流程。

//createOffer()方法示例
pc.createOffer().then((offer) => {
	return pc.setLocalDescription(offer);
}).then(() => {
	sendToServer({
		name: myUsername,
		target: targetUsername,
		type: "video-offer",
		sdp: pc.localDescription
	});
}).catch((reason) => {
	// 错误处理
});  

4.createAnswer() 方法

该方法创建SDP应答信息,信息里包含已添加的媒体轨道、编码格式、浏览器支持项,以及收集到的ICE候选者等信息。通过信令服务器将应答信息发送给提案方,继续进行协商过程。

aPromise = pc.createAnswer([options]);  

参数:options,可选参数,类型为RTCAnswerOptions,包含创建应答的选项。
返回值:Promise值,调用成功则获得类型为RTCSessionDescriptionInit的决议值。
异常:如果调用失败,会抛出如下表所示的异常。

createAnswer()异常
异常名称 说明
NotReadableError 没有提供安全证书,并且WebRTC也不能创建证书
OperationError 因为资源缺失导致创建失败

//RTCAnswerOptions的定义
dictionary RTCAnswerOptions : RTCOfferAnswerOptions {};
dictionary RTCOfferOptions : RTCOfferAnswerOptions {
	boolean iceRestart = false;
};  

下面的代码清单调用createAnswer()方法创建SDP应答,并通过信令服务器将应答发送给对等端,如果过程中出错,则进入错误处理流程。

//createAnswer()方法示例
try{
	answer = await pc.createAnswer();
	await pc.setLocalDescription(answer);
	sendToServer({
		name: myUsername,
		target: targetUsername,
		type: "video-answer",
		sdp: pc.localDescription
	});
} catch (err) {
	// 错误处理流程
}  

我们在 createAnswer() 及 createOffer() 方法的示例中分别使用了 await 和 promise 两种不同的 JavaScript 调用语法,所有返回 Promise 对象的方法都支持这两种调用方式,二者没有优劣之分,读者可以结合应用场景进行选择。

5.getConfiguration() 方法

该方法返回 RTCPeerConnection 的当前配置,返回对象类型为RTCConfiguration,配置里包含ICE 服务器列表、传输策略和标识信息。

const configuration = pc.getConfiguration();  

参数:无。
返回值:RTCConfiguration 对象。

下面的代码清单调用 getConfguration() 方法获取当前连接的配置,创建新的证书并添加到证书中,再调用 setConfguration() 方法设置新配置。

//getConfiguration()方法示例
let configuration = pc.getConfiguration();
if ((configuration.certificates != undefined) && (!configuration.certificates.length)) {
	RTCPeerConnection.generateCertificate({
		name: 'RSASSA-PKCS1-v1_5',
		hash: 'SHA-256',
		modulusLength: 2048,
		publicExponent: new Uint8Array([1, 0, 1])
	}).then((cert) => {
		configuration.certificates = [cert];
		pc.setConfiguration(configuration);
	});
}  

6.setConfiguration() 方法

该方法为当前连接设置配置,通常在以下场景调用该方法。

在创建 RTCPeerConnection 时没有指定 STUN/TURN 服务器地址,随后在ICE协商开始前调用该方法设置 STUN/TURN 服务器地址。

替换原有的 STUN/TURN 服务器地址,然后发起 ICE restart,建立新的连接。

pc.setConfiguration(configuration);  

参数: configuration,RTCConfiguration 对象,新的配置将完全替换掉旧的配置。
返回值:无。
异常:如果调用失败,会抛出如下表所示的异常。

setConfiguration 调用异常
异常名称 说明
InvalidAccessError 在 iceServers 处指定 TURN 服务器地址,但是因为用户名(RTCIceServer.username) 或者密码(RTCIceServer.credentials)设置错误,导致认证失败
InvalidModificationError peerIdentity 或者 certificates 与当前使用的不一致
InvalidStateError 当前 RTCPeerConnection 连接已关闭
SyntaxError iceServers 地址配置出现语法错误

// 设置了 TURN 服务器地址,并重新发起协商。
const restartConfig = {
	iceServers: [{
		urls: "turn:asia.myturnserver.net",
		username: "allie@oopcode.com",
		credential: "topsecretpassword"
	}]
};
pc.setConfiguration(restartConfig);
pc.createOffer({"iceRestart": true}).then((offer) => {
	return pc.setLocalDescription(offer);
}).then() => {
	// 将offer发送给对等端
}).catch(error => {
	// 错误处理流程
});  

7.setLocalDescription() 方法

该方法为当前连接设置本地会话描述信息。

如果连接已经建立,则意味着需要重新进行协商,此时该调用不会马上生效,而是等 到新的协商完成后才会生效。

aPromise = pc.setLocalDescription(sessionDescription);  

参数:sessionDescription,可选参数,类型为RTCSessionDescriptionInit或者RTCSessionDescription,指定了应用到当前连接的本地会话描述信息。如果该参数未指定,则WebRTC会自动创建一个会话描述,并作为该调用的参数默认传入。
返回值:Promise 值,如果成功设置 RTCPeerConnection.localDescription,则返回成 功,否则返回失败。决议值为空值。

下面的代码清单演示了如何隐式设置本地会话描述,该例没有为setLocalDescription()方法指定参数,这会使WebRTC自动调用createOffer()方法生成一个本地会话描述信息这么做的好处是减少调用步骤,使代码更加简洁。

//setLocalDescription()方法的隐式调用
pc.addEventListener("negotiationneeded", async (event) => {
	await myPeerConnection.setLocalDescription();
	signalRemotePeer({ description: pc.localDescription });
});  

下面的代码清单演示了如何显式创建本地会话描述,调用createOffer()方法并将其决议值作为参数传给setLocalDescription()方法,注意与上述隐式调用的区别。

//setLocalDescription()方法的显式调用
pc.addEventListener("negotiationneeded", (event) => {
	pc.createOffer().then((offer) => {
		return pc.setLocalDescription(offer);
	}).then(() => {
		signalRemotePeer({ description: pc.localDescription });
	}).catch(error => {
		// 错误处理流程
	});
});  

8.setRemoteDescription() 方法

该方法为当前连接设置对等端会话描述信息,通常在收到对等端的提案或者应答后调用该方法。

如果连接已经建立,则意味着需要重新进行协商,此时该调用不会马上生效,而是等到新的协商完成后才会生效。

aPromise = pc.setRemoteDescription(sessionDescription);  

参数:sessionDescription,类型为RTCSessionDescriptionInit或者RTCSessionDescription,指定了应用到当前连接的对等端会话描述信息。
返回值:Promise值,如果成功设置remoteDescription,则返回成功,否则返回失败。决议值为空值。
异常:该方法调用失败时,会抛出如下表所示的异常。

setRemoteDescription 调用异常
异常名称 说明
InvalidAccessError 传入的会话描述信息包含了无效数据
InvalidStateError 当前 RTCPeerConnection 连接处于关闭状态
TyрeError 传入的会话描述信息没有包含type或者sdp属性
RTCError 传入的会话描述信息包含了错误的语法
OperationError 非以上错误导致的调用失败

下面的代码清单演示了 setRemoteDescription() 方法的用法。该示例收到对等端传输过来的 offer 信息,将其作为参数传递给 setRemoteDeseription() 方法,如果调用成功,则调用 getUserMedia() 方法获取本地媒体流,将获取到的媒体流添加到本地连接,然后生成 answer,通过信令服务器传输给对等端。

//setRemoteDescription()方法示例
function handleOffer(msg) {
	pc.setRemoteDescription(msg.description).then(()=> {
		return navigator.mediaDevices.getUserMedia(mediaConstraints);
	}).then((stream) => {
		document.getElementById("local_video").srcObject= stream;
		return pc.addStream(stream);
	}).then(() => {
		return pc.createAnswer();
	}).then((answer) => {
		return pc.setLocalDescription(answer);
	}).then(() => {
		// 使用信令服务器将answer发送到对等端
	}).catch(error => {
		// 错误处理流程
	});
}  

9.restartIce() 方法

调用该方法重新发起 ICE 协商,在该方法之后调用 createOffer() 方法会自动将 iceRestart 设置为 true。

因为该方法将触发 negotiationneeded 事件,所以应该在该事件处理函数中进行ICE协商。在ICE 重新协商的过程中,原有的连接继续生效,媒体流可以正常传输。

pc.restartIce();  

参数:无。
返回值:无。

下面的代码清单演示了 restartIce() 方法的用法。当网络连接状态 connectionState 处理 failed 时,调用 restartIce() 方法重新进行ICE协商。

pc.onconnectionstatechange = ev => {
	if (pc.connectionState === "failed") {
		//网络连接中断,重新进行协商
		pc.restartIce();
	}
};  

10.close() 方法

调用该方法关闭当前连接,终止正在进行的ICE协商,将 signalingState 值改为 closed。

pc.close();  

参数:无。
返回值:无。

下面的代码清单演示了 close() 方法的用法。该示例建立网络连接,并创建了数据通道当从数据通道收到第一条消息后主动关闭连接。

const pc = new RTCPeerConnection();
const dc = pc.createDataChannel("my channel");
dc.onmessage = (event) => {
	console.log("received:" + event.data);
	//收到第一条消息后关闭连接
	pc.close();
};
dc.onopen = () => {
	console.log("datachannel open");
};
dc.onclose = () => {
	console.log("datachannel close");
};  

RTCPeerConnection 接囗的事件

1.connectionstatechange 事件

当WebRTC连接状态变化时触发该事件,此时 connectionState 值变为新的状态值。该事件对应事件句柄 onconnectionstatechange。

下面的代码清单演示了 onconnectionstatechange 事件句柄的用法。当触发 connectionstatechange 事件时,打印提示信息。

//onconnectionstatechange事件句柄示例
pc.onconnectionstatechange = ev => {
	switch (pc.connectionState)
	{
		case "new":
		case "checking":
			console.log("Connecting...");
			break;
		case "connected":
			console.log("Online");
			break;
		case "disconnected":
			console.log("Disconnecting...");
			break;
		case "closed":
			console.log("offline");
			break;
		case "failed":
			console.log("Error");
			break;
		default:
			console.log("Unknown");
			break;
	}
};  

也可以使用 addEventListener() 方法监听事件 connectionstatechange

2.iceconnectionstatechange 事件

在ICE协商过程中,当ICE连接状态发生变化时触发该事件,新的连接状态可以通过iceConnectionState 属性获取。该事件对应事件句柄 oniceconnectionstatechange。

//oniceconnectionstatechange 事件句柄示例
pc.oniceconnectionstatechange = ev => {
	if(pc.iceConnectionState === "disconnected") {
		//当ICE连接状态变为disconnected时,关闭网络连接
		closevideoCall(pc);
	}
}  

也可以使用 addEventListener() 方法监听事件 iceconnectionstatechange。

3.icegatheringstatechange 事件

在ICE协商建立连接的过程中,如果ICE候选者的收集过程发生状态改变,则触发该事件,新的状态值可以通过iceGatheringState 属性获取。该事件对应事件句柄onicegatheringstatechange。

//onicegatheringstatechange 事件句柄示例
pc.onicegatheringstatechange = ev => {
	let connection = ev.target;
	switch (connection.iceGatheringState) {
		case "gathering":
			//开始收集ICE候选者信息
			break;
		case "complete":
			//完成ICE候选者信息的收集
			break;
	}
}  

也可以使用 addEventListener() 方法监听事件 icegatheringstatechange。

4.signalingstatechange 事件

当信令状态发生改变时触发该事件,新的状态值可以通过属性signalingState获取,该事件对应事件句柄 onsignalingstatechange。

//onsignalingstatechange 事件句柄示例
pc.onsignalingstatechange = ev => {
	switch (pc.signalingstate) {
		case "stable":
			updateStatus("ICE negotiation complete");
			break;
	}
}  

也可以使用 addEventListener() 方法监听事件 signalingstatechange。

5.negotiationneeded 事件

当需要进行ICE协商时触发该事件,对应事件句柄 onnegotiationneeded。以下两种情况需要进行 ICE 协商。

下面的代码清单演示了 onnegotiationneeded 事件句柄的用法。当触发 negotiationneeded事件时,意味着需要进行ICE协商,此时创建并设置本地提案,通过信令服务器将提案发送给对等端。

//onnegotiationneeded 事件句柄示例
pc.onnegotiationneeded = ev => {
	pc.createOffer()
	.then(offer => return pc.setLocalDescription(offer))
	.then(() => sendSignalingMessage({
		type: "video-offer",
		sdp: pc.localDescription
	}))
	.catch(err => {
		// 异常处理流程
	});
};  

也可以使用 addEventListener() 方法监听事件 negotiationneeded。

6.icecandidate 事件

当有新的ICE候选者加入或者完成了ICE候选者收集时触发该事件,此时需要将新的ICE 候选者发送到对等端,对等端收到ICE候选者信息后,调用addIceCandidate()方法将候选者信息通知给ICE代理层。此事件对应事件句柄onicecandidate。此事件有传入参数,参数类型为RTCPeerlceCandidateEvent,其定义如下面的代码清单所示。

//RTCPeerlceCandidateEvent的定义
interface RTCPeerConnectionIceEvent : Event {
	constructor(DOMString type, optional RTCPeerconnectionIceEventInit eventInitDict={});
	readonly attribute RTCIceCandidate? candidate;
	readonly attribute DOMString? url;
};  
//onicecandidate事件句柄示例
pc.onicecandidate = ev => {
	if (ev.candidate) {
		sendMessage({
			type: "new-ice-candidate",
			candidate: ev.candidate
		});
	}
};  

也可以使用 addEventListener() 方法监听事件 icecandidate。

7.icecandidateerror 事件

在ICE候选者收集失败时触发该事件,对应事件句柄onicecandidateerror。该事件有传入参数,参数类型为RTCPeerConnectionlceErrorEvent,其定义如下面的代码清单所示。

//RTCPeerConnectionlceErrorEvent的定义
interface RTCPeerConnectionIceErrorEvent : Event {
	constructor(DOMString type, RTCPeerConnectionIceErrorEventInit eventInitDict);
	readonly attribute DOMString? address;
	readonly attribute unsigned short? port;
	readonly attribute DOMString url;
	readonly attribute unsigned short errorCode;
	readonly attribute USVString errorText;
};  

RTCPeerConnectionlceErrorEvent 的属性说明
属性 说明
address 与 STUN/TURN 通信的本地 IP 地址
port 与 STUN/TURN 通信的端口
url STUN/TURN 服务器的URL地址
errorCode STUN/TURN 服务器返回的错误代码
errorText STUN/TURN 服务器返回的错误信息

//onicecandidateerror事件句柄示例
pc.onicecandidateerror = (event) => {
	if (event.errorCode >= 300 && event.errorCode <= 699) {
		// STUN返回的错误代码范围为300-699
	}
}  

也可以使用 addEventListener() 方法监听事件 icecandidateerror。

评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容