在上一节中,我们把RTCPeerConnection 双方的sdp 和ice 互换以后,音视频的通道就建立起来了,但是上一节是在一个程序中的,这种程序在现实中,并没有用处。
通常的应用是这样的一个场景,一个端进行视频的推送,另一端进行视频的接收。
幸好的是双方的sdp 和ice 都是字符串形式的,我们可以通过websockets 把对方的sdp 和ice 先发到服务器上,然后进行互换。
关于websockets 的服务端,我们这里采用的是python ,关于python 的使用如果是新手,可以参考别的教程,这是使用的版本是3.8。
async def send_chat(websocket):
"""
Receive and process chat messages from a user.
"""
global connected
async for message in websocket:
# Parse a "talk" event from the UI.
for ws in connected:
if ws != websocket:
await ws.send(message)
这是一个最简单的服务端,在websockets 服务端上,就做了一件事情,把发送上来的websocket发来的消息,转发出去。只起到了一个中转的作用。
在发送端,当我们把视频流准备好以后,使用如下的步骤,把sdp发送到对方。
async function call() {
callButton.disabled = true;
const configuration = {};
//源连接,
pc1 = new RTCPeerConnection(configuration);
//当ice准备好后,加到目标源中
pc1.addEventListener('icecandidate', e => onIceCandidate(pc1, e));
//把localStream的音视频,放到源中
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
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);
//发送sdp
const req = {
type: "sdp",
content: desc_pc1,
};
websocket.send(JSON.stringify(req));
await pc1.setLocalDescription(desc_pc1);
} catch (e) {
onCreateSessionDescriptionError(e);
}
}
把准备好的ice也发送到另一方。
async function onIceCandidate(pc, event) {
try {
console.log(event.candidate.address);
//发送ice
const req = {
type: "candidate",
content: event.candidate,
};
websocket.send(JSON.stringify(req));
} catch (e) {
onAddIceCandidateError(pc, e);
}
//console.log(`${getName(pc)} ICE candidate:\n${event.candidate ? event.candidate.candidate : '(null)'}`);
}
在web的另外一端,准备接收发送端的sdp 和ice 。
async function messageHander(data){
const event = JSON.parse(data.data);
console.log("message:" + event['type']);
switch (event['type']) {
//接收sdp
case "sdp":
const desc_pc1 = event['content'];
await pc2.setRemoteDescription(desc_pc1);
const desc_pc2 = await pc2.createAnswer();
await pc2.setLocalDescription(desc_pc2);
console.log("answer desc_pc2 :");
console.log(desc_pc2);
//发送answer
const req = {
type: "answer_sdp",
content: desc_pc2,
};
websocket.send(JSON.stringify(req));
//接收ice
case "candidate":
pc2.addIceCandidate(event['content']);
}
}
同样的,我们也需要在发送方,接收接收方的sdp ,发送方的消息处理就比较简单了。
async function messageHander(data){
const event = JSON.parse(data.data);
console.log("message:" + event['type']);
switch (event['type']) {
case "answer_sdp":
const desc_pc2 = event['content'];
await pc1.setRemoteDescription(desc_pc2);
}
}
这样我们就把第四节的发送方和接收方,进行浏览器分离了。
看下这里的ice 信息:
端口号为:54642
这个接口就是流的端口。我们用iftop 命令查看下,流量的情况。
端口号 54642 ,把流推到 100.1:64388端口。这个时候,可以在另外一段,查看,应该会有64388的端口号是开着的。
这个是在局域网的情况下,可以通过p2p的方式进行视频流的传输,它并不需要第三方的服务器进行中转,这样极大的提高了传输速率。
后面我们会接着看看,如果不在局域网下,我们该如何建立起视频流的连接。
|