RTCPeerConnection
最后更新于:2022-04-02 03:30:10
[TOC]
## RTCPeerConnection
RTCPeerConnection 接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。
### sample
1. 添加流
```
var pc = new RTCPeerConnection();
// 设置添加流事件
pc.onaddstream = function(obj) {
var vid = document.createElement("video");
document.appendChild(vid);
vid.srcObject = obj.stream;
}
// 帮助函数
function endCall() {
var videos = document.getElementsByTagName("video");
for (var i = 0; i < videos.length; i++) {
videos[i].pause();
}
pc.close();
}
function error(err) { endCall(); }
```
2. 呼叫初始化
```
navigator.getUserMedia({video: true}, function(stream) {
pc.onaddstream({stream: stream});
// Adding a local stream won't trigger the onaddstream callback
pc.addStream(stream);
pc.createOffer(function(offer) {
pc.setLocalDescription(new RTCSessionDescription(offer), function() {
// send the offer to a server to be forwarded to the friend you're calling.
}, error);
}, error);
})
```
3. 呼叫回答
```
var offer = getOfferFromFriend();
navigator.getUserMedia({video: true}, function(stream) {
pc.onaddstream({stream: stream});
pc.addStream(stream);
pc.setRemoteDescription(new RTCSessionDescription(offer), function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(new RTCSessionDescription(answer), function() {
// send the answer to a server to be forwarded back to the caller (you)
}, error);
}, error);
}, error);
})
```
4. 处理应答
```
// pc was set up earlier when we made the original offer
var offer = getResponseFromFriend();
pc.setRemoteDescription(new RTCSessionDescription(offer), function() { }, error);
```
## 事件
### onaddstream
是收到`[addstream](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/addstream "/zh-CN/docs/Web/Reference/Events/addstream")`事件时调用的事件处理器。 Such an event is 当[`MediaStream`](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStream)被远端机器添加到这条连接时,该事件会被触发。 当调用[`RTCPeerConnection.setRemoteDescription()`](https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection/setRemoteDescription)方法时,这个事件就会被立即触发,它不会等待SDP协商的结果
### ondatachannel
是收到datachannel 事件时调用的事件处理器。 当一个 RTCDataChannel 被添加到连接时,这个事件被触发。
```
pc.ondatachannel = function(ev) {
console.log('Data channel is created!');
ev.channel.onopen = function() {
console.log('Data channel is open and ready to be used.');
};
};
```
### onicecandidate
只要本地代理ICE 需要通过信令服务器传递信息给其他对等端时就会触发。
```
pc.onicecandidate = function(event) {
if (event.candidate) {
// Send the candidate to the remote peer
} else {
// All ICE candidates have been sent
}
}
```
### oniceconnectionstatechange
是收到iceconnectionstatechange事件时调用的事件处理器 。 当iceConnectionState 改变时,这个事件被触发。
```
pc.oniceconnectionstatechange = function(event) {
if (pc.iceConnectionState === "failed" ||
pc.iceConnectionState === "disconnected" ||
pc.iceConnectionState === "closed") {
// Handle the failure
}
};
```
### onnegotiationneeded
发生需要会话协商的更改时,将触发此事件
## 方法
### createOffer()
生成一个offer,它是一个带有特定的配置信息寻找远端匹配机器(peer)的请求。
```
myPeerConnection.createOffer().then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
})
.then(function() {
// 自定义函数
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
})
.catch(function(reason) {
// An error occurred, so handle the failure to connect
});
```
### createAnswer()
在协调一条连接中的两端offer/answers时,根据从远端发来的offer生成一个answer
```
pc.createAnswer().then(function(answer) {
return pc.setLocalDescription(answer);
})
.then(function() {
// Send the answer to the remote peer through the signaling server.
})
.catch(handleGetUserMediaError);
```
### setLocalDescription()
改变与连接相关的本地描述。这个描述定义了连接的属性,例如:连接的编码方式。连接会受到它的改变的影响
实例1:
```
myPeerConnection.createOffer().then(function(offer) {
return myPeerConnection.setLocalDescription(offer);
});
// 等价于
myPeerConnection.createOffer().then(function(offer) {
return myPeerConnection.setLocalDescription(new RTCSessionDescription(offer));
});
```
实例2:
**隐式描述**
无参数形式的优点之一 setLocalDescription() 是,它使您可以大大简化协商代码
```
pc.addEventListener("negotiationneeded", async (event) => {
await pc.setLocalDescription();
signalRemotePeer({ description: pc.localDescription });
});
```
**显示**
```
async function handleNegotiationNeededEvent() {
try {
const offer = await pc.createOffer();
pc.setLocalDescription(offer);
signalRemotePeer({ description: pc.localDescription });
} catch(err) {
reportError(err);
}
}
```
### setRemoteDescription()
- 改变与连接相关的远端描述。这个描述定义了连接的属性,例如:连接的编码方式。连接会受到它的改变的影响
- 连接的offer通常来自于负责匹配的服务器所发送的数据。执行者应调用此方法设置远程描述,然后生成发送到对端计算机的answer
```
var pc = new PeerConnection();
pc.setRemoteDescription( new RTCSessionDescription( offer ), function() {
pc.createAnswer( function( answer ) {
pc.setLocalDescription( answer, function() {
// send the answer to the remote connection
})
})
});
```
### addIceCandidate()
- 这是一个实验中的功能
- 当本机当前页面的 RTCPeerConnection 接收到一个从远端页面通过信号通道发来的新的 ICE 候选地址信息,本机可以通过调用RTCPeerConnection.addIceCandidate() 来添加一个 ICE 代理
```
let candidate = new RTCIceCandidate(receivedSDP);
pc.addIceCandidate(candidate).then(_=>{
// Do stuff when the candidate is successfully passed to the ICE agent
}).catch(e=>{
console.log("Error: Failure during addIceCandidate()");
});
```
### getConfiguration()
RTCPeerConnection上调用该方法的当前配置
```
let configuration = myPeerConnection.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(function(cert) {
configuration.certificates = [cert];
myPeerConnection.setConfiguration(configuration);
});
}
```
### getSenders (原 getLocalStreams )
- 返回一个RTCRtpSender对象数组 ,每个对象代表负责传输一个轨道的数据的RTP发送方
实例:静音
```
function setMuting(pc, muting) {
let senderList = pc.getSenders();
senderList.forEach(sender) {
sender.track.enabled = !muting;
}
}
```
### getReceivers (原 getReceivers)
返回一个RTCRtpReceiver对象数组,每个对象代表一个RTP接收器
### removeStream
移除媒体流
```
var pc, videoStream;
navigator.getUserMedia({video: true}, function(stream) {
pc = new RTCPeerConnection();
videoStream = stream;
pc.addStream(stream);
}
document.getElementById("closeButton").addEventListener("click", function(event) {
pc.removeStream(videoStream);
pc.close();
}, false);
```
### close
关闭当前对等连接
```
var pc = new RTCPeerConnection();
var dc = pc.createDataChannel("my channel");
dc.onmessage = function (event) {
console.log("received: " + event.data);
pc.close(); // We decided to close after the first received message
};
dc.onopen = function () {
console.log("datachannel open");
};
dc.onclose = function () {
console.log("datachannel close");
};
```
### createDataChannel
创建一个可以发送任意数据的数据通道(data channel)。常用于后台传输内容, 例如: 图像, 文件传输, 聊天文字, 游戏数据更新包, 等等
实例1:
```
// 邀约方
var pc = new RTCPeerConnection(options);
var channel = pc.createDataChannel("chat");
channel.onopen = function(event) {
channel.send('Hi you!');
}
channel.onmessage = function(event) {
console.log(event.data);
}
// 应答方
var pc = new RTCPeerConnection(options);
pc.ondatachannel = function(event) {
var channel = event.channel;
channel.onopen = function(event) {
channel.send('Hi back!');
}
channel.onmessage = function(event) {
console.log(event.data);
}
}
```
实例2:使用约定的id
```
var pc = new RTCPeerConnection(options);
var channel = pc.createDataChannel("chat", {negotiated: true, id: 0});
channel.onopen = function(event) {
channel.send('Hi!');
}
channel.onmessage = function(event) {
console.log(event.data);
}
```
### getStats
关整体连接或指定的统计信息的数据进行解析
```
window.setInterval(function() {
myPeerConnection.getStats(null).then(stats => {
let statsOutput = "";
stats.forEach(report => {
statsOutput += `
\n` + `Timestamp: ${report.timestamp}
\n`; // Now the statistics for this report; we intentially drop the ones we // sorted to the top above Object.keys(report).forEach(statName => { if (statName !== "id" && statName !== "timestamp" && statName !== "type") { statsOutput += `${statName}: ${report[statName]}
\n`; } }); }); document.querySelector(".stats-box").innerHTML = statsOutput; }); }, 1000); ``` ### getStreamById (弃用) ``` var stream = pc.getStreamById(myTrackId); if (stream) { console.log("Found stream: " + stream.id); } ``` ### addStream (弃用,推荐用 addTrack) ``` navigator.mediaDevices.getUserMedia({video:true, audio:true}, function(stream) { var pc = new RTCPeerConnection(); pc.addStream(stream); }); ```
';
Report: ${report.type}
\nID: ${report.id}\n` + `Timestamp: ${report.timestamp}
\n`; // Now the statistics for this report; we intentially drop the ones we // sorted to the top above Object.keys(report).forEach(statName => { if (statName !== "id" && statName !== "timestamp" && statName !== "type") { statsOutput += `${statName}: ${report[statName]}
\n`; } }); }); document.querySelector(".stats-box").innerHTML = statsOutput; }); }, 1000); ``` ### getStreamById (弃用) ``` var stream = pc.getStreamById(myTrackId); if (stream) { console.log("Found stream: " + stream.id); } ``` ### addStream (弃用,推荐用 addTrack) ``` navigator.mediaDevices.getUserMedia({video:true, audio:true}, function(stream) { var pc = new RTCPeerConnection(); pc.addStream(stream); }); ```