什么是RTCPeerConnection
/** A WebRTC connection between the local computer and a remote peer. It provides methods to connect to a remote peer, maintain and monitor the connection, and close the connection once it's no longer needed. */
interface RTCPeerConnection extends EventTarget {
readonly canTrickleIceCandidates: boolean | null;
readonly connectionState: RTCPeerConnectionState;
readonly currentLocalDescription: RTCSessionDescription | null;
readonly currentRemoteDescription: RTCSessionDescription | null;
readonly iceConnectionState: RTCIceConnectionState;
readonly iceGatheringState: RTCIceGatheringState;
readonly idpErrorInfo: string | null;
readonly idpLoginUrl: string | null;
readonly localDescription: RTCSessionDescription | null;
onconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
ondatachannel: ((this: RTCPeerConnection, ev: RTCDataChannelEvent) => any) | null;
onicecandidate: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceEvent) => any) | null;
onicecandidateerror: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceErrorEvent) => any) | null;
oniceconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
onicegatheringstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
onnegotiationneeded: ((this: RTCPeerConnection, ev: Event) => any) | null;
onsignalingstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
onstatsended: ((this: RTCPeerConnection, ev: RTCStatsEvent) => any) | null;
ontrack: ((this: RTCPeerConnection, ev: RTCTrackEvent) => any) | null;
readonly peerIdentity: Promise<RTCIdentityAssertion>;
readonly pendingLocalDescription: RTCSessionDescription | null;
readonly pendingRemoteDescription: RTCSessionDescription | null;
readonly remoteDescription: RTCSessionDescription | null;
readonly sctp: RTCSctpTransport | null;
readonly signalingState: RTCSignalingState;
addIceCandidate(candidate: RTCIceCandidateInit | RTCIceCandidate): Promise<void>;
addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender;
addTransceiver(trackOrKind: MediaStreamTrack | string, init?: RTCRtpTransceiverInit): RTCRtpTransceiver;
close(): void;
createAnswer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit>;
createDataChannel(label: string, dataChannelDict?: RTCDataChannelInit): RTCDataChannel;
createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit>;
getConfiguration(): RTCConfiguration;
getIdentityAssertion(): Promise<string>;
getReceivers(): RTCRtpReceiver[];
getSenders(): RTCRtpSender[];
getStats(selector?: MediaStreamTrack | null): Promise<RTCStatsReport>;
getTransceivers(): RTCRtpTransceiver[];
removeTrack(sender: RTCRtpSender): void;
setConfiguration(configuration: RTCConfiguration): void;
setIdentityProvider(provider: string, options?: RTCIdentityProviderOptions): void;
setLocalDescription(description: RTCSessionDescriptionInit): Promise<void>;
setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void>;
addEventListener<K extends keyof RTCPeerConnectionEventMap>(type: K, listener: (this: RTCPeerConnection, ev: RTCPeerConnectionEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof RTCPeerConnectionEventMap>(type: K, listener: (this: RTCPeerConnection, ev: RTCPeerConnectionEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
Steps:
- localPC(PC,RTCPeerConnection,下同) 绑定 ice candidate事件(来确定连接方式:直连 or STUN or TURN)
- remotePC绑定ice candidate事件
- remotePC绑定media事件(onaddstream已经废弃,使用ontrack),这里是为了当本地发送的media到达remote端的时候,remote的video开始接受media并播放
- localPC添加media stream,这里要使用track
- 这里说明一下:连接两端local <==> remote, 在remote端看来 local端 是 remote peer, local 的 local description 就相当于是 remote 的 remote description(description,描述了支持的格式,编解码方式等,目的是为了协商出两端都支持的格式)
- localPC 来createOffer发起请求,生成的description设置为local的localDescription,根据上面的等价原则设置remote端的RemoteDescription
- remotePC生成回答,生成的description同样的设置方法
Code: API时间-2021年5月7日
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1">
<tr>
<th>Local</th>
<th>Remote</th>
</tr>
<tr>
<td><video id="local" autoplay></video> </td>
<td><video id="remote" autoplay></video> </td>
</tr>
<tr>
<td align="center">
<div>
<button id="StartBtn" onclick="start()">Start</button>
<button id="CallBtn" onclick="call()">Call</button>
<button id="StopBtn" onclick="hangup()">Stop</button>
</div>
</td>
</tr>
</table>
<script type="text/javascript">
var localStream, localPeerConnection, remotePeerConnection;
var localvideo = document.getElementById('local');
var remotevideo = document.getElementById('remote');
var startBtn = document.getElementById('StartBtn');
var callBtn = document.getElementById('CallBtn');
var stopBtn = document.getElementById('StopBtn');
startBtn.disabled = false;
callBtn.disabled = true;
stopBtn.disabled = true;
function log(text) {
console.log("At time: " + (performance.now()/1000).toFixed(3) + " --> " + text);
}
function successCallback(stream) {
console.log('Recv local stream');
localvideo.srcObject = stream;
localStream = stream; // URL.createObjectURL is no longer use in chrome
callBtn.disabled = false;
}
function start() {
log("Requesting local stream");
startBtn.disabled = false;
navigator.getUserMedia({audio:true, video:true}, successCallback, function (error) {
log("getUserMedia error:", error);
});
}
function call() {
callBtn.disabled = true;
stopBtn.disabled = false;
log('Starting call');
if (localStream.getVideoTracks().length > 0){
log('Using video device ...' + localStream.getVideoTracks()[0].label);
}
if (localStream.getAudioTracks().length > 0){
log('Using audio device ...' + localStream.getAudioTracks()[0].label);
}
RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //chrome
log('RTCPeerConnection object: ' + RTCPeerConnection);
var servers = null;
localPeerConnection = new RTCPeerConnection(servers);
log('Created local peer connection');
//gotLocalIceCandidate 是 onicecandidate 的 handler, 这里将candidate添加到 remote peer connection
localPeerConnection.onicecandidate = gotLocalIceCandidate;
remotePeerConnection = new RTCPeerConnection(servers);
log('Create remote peer connection');
remotePeerConnection.onicecandidate = gotRemoteIceCandidate;
//recv remote stream
// remotePeerConnection.onaddstream = gotRemoteStream; //Deprecated
remotePeerConnection.ontrack = gotRemoteStream;
//see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream
localStream.getTracks().forEach(function(track){
localPeerConnection.addTrack(track, localStream);
});
log('Added local stream to local peer connection');
// see https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection/createOffer
localPeerConnection.createOffer().then(function (desc) {
localPeerConnection.setLocalDescription(desc);
remotePeerConnection.setRemoteDescription(desc);
log('Offer from local peer connection: \n' + desc.sdp);
remotePeerConnection.createAnswer().then(function (desc) {
remotePeerConnection.setLocalDescription(desc);
localPeerConnection.setRemoteDescription(desc);
});
});
// 本地实验可以通过setDesc 简单实现sendToServer
// .then(function() {
// sendToServer({
// type: 'video-offer',
// sdp: localPeerConnection.localDescription
// });
// });
}
function hangup() {
log('Ending call');
localPeerConnection.close();
remotePeerConnection.close();
localPeerConnection = null;
remotePeerConnection = null;
stopBtn.disabled = true;
callBtn.disabled = false;
}
// 本地代理ICE需要通过信令服务器传递信息给其他对等端时触发此EventHandler
function gotLocalIceCandidate(event) {
if (event.candidate){
// add candidate to remote peer connection
remotePeerConnection.addIceCandidate(event.candidate);
log('local ICE candidate: \n' + event.candidate.candidate + '\n add to remote peer connection');
}
}
function gotRemoteIceCandidate(event) {
if (event.candidate){
// add candidate to local peer connection
localPeerConnection.addIceCandidate(event.candidate);
log('local ICE candidate: \n' + event.candidate.candidate+ '\n add to local peer connection');
}
}
function gotRemoteStream(event) {
// see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
remotevideo.srcObject = event.streams[0]; // chrome
log('Recv remote stream');
}
</script>
</body>
</html>
如果不工作,Chrome右击属性-目标-添加" --allow-file-access-from-files"
网友评论