1 背景
webrtc的代码中有peerconnectionclient和peerconnectionserver的例子,但是没有对应的web端的例子,这里简单的写了一个测试例子,具体如下:
2 具体操作
2.1 操作流程
2.2 测试效果
使用webclient与peerconnectionclient的测试效果如下:
3 前端代码
<html>
<head>
<title>webclient p2p demo</title>
<meta charset="utf-8">
<style>
.left_part {
width: 50%;
float: left;
}
.right_part {
width: 50%;
float: right;
overflow-y: scroll;
}
</style>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="right_part" id="left_part_container">
<div>
SignalServer:
<input type="text" id="signal_url" value="127.0.0.1:8888">
<button id="btn_sign_in">登入</button>
<button id="btn_sign_out">登出</button><br>
<input type="text" id="selected_peerid_for_p2p" disabled value="不允许修改的文本">
<button id="btn_start_p2p" disabled>start_p2p</button>
</div>
<br>
<table id="PeersTable">
<thead>
<tr>
<th>Name</th>
<th>PeerId</th>
<th>Status</th>
</tr>
</thead>
</table>
</div>
<div class="left_part" id="left_part_container">
<video controls autoplay id="rtc_video_play" height="500" style="width:100%"></video>
</div>
<script type="text/javascript">
var myselfPeerId = -1;
var pc = new RTCPeerConnection({ "offerExtmapAllowMixed": false });
var datachannel = null;
var stream = new MediaStream();
async function initPC() {
pc.addTransceiver("audio", {direction: "recvonly"});
pc.addTransceiver("video", {direction: "recvonly"});
pc.onconnectionstatechange = function(event){
console.log("connection state change: ", pc.connectionState);
};
pc.onicecandidate = async (ev) => {
console.log('=======>' + JSON.stringify(ev.candidate));
};
pc.ontrack = function(event) {
stream.addTrack(event.track);
};
datachannel = pc.createDataChannel('chat');
datachannel.onopen = function(event) {
console.log("datachannel onopen: ", event.data);
}
datachannel.onmessage = function(event) {
console.log("receive message: ", event.data);
}
datachannel.onerror = function(event) {
console.log("datachannel error: ", event.data);
}
datachannel.onclose = function(event) {
console.log("datachannel close: ");
}
}
async function startP2P(signal_url) {
initPC();
var offer = await pc.createOffer();
await pc.setLocalDescription(offer);
let sendOfferPromise = await fetch(signal_url, {
"method": "POST",
"body": JSON.stringify(offer)
});
if (sendOfferPromise.ok) {
console.log("send offer: " + JSON.stringify(offer));
} else {
alert("HTTP-Error: " + sendOfferPromise.status);
}
}
async function nfyFromSignal(nfyPromise) {
let nfyString = await nfyPromise.text();
if (nfyString.includes("answer")) {
console.log("receive answer: " + nfyString);
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(nfyString)));
} else if (nfyString.includes("candidate")) {
console.log("receive candidate: " + nfyString);
pc.addIceCandidate(new RTCIceCandidate(JSON.parse(nfyString)));
} else {
let peersString = nfyString;
// console.log("rsp: ", peersString)
let peersChunk = peersString.split("\n");
for (let i=0; i<peersChunk.length && peersChunk[i].length>0; ++i) {
const peerColumns = peersChunk[i].split(",");
// console.log("index: " + i + ", data: " + peerColumns)
// for example: kevin@home-pc,36,1
// peerColumns[0]: peer_name
// peerColumns[1]: peer_id
// peerColumns[2]: peer_status
// table's column index starts from 1, not 0
const row_find_peer_id = $("#PeersTable tr:has(td:nth-child(2):contains('" + peerColumns[1] + "'))");
if (row_find_peer_id.length > 0) {
console.log("peerid:" + peerColumns[1] + ", status change from " + row_find_peer_id.find('td:nth-child(3)').text() + " to " + peerColumns[2]);
row_find_peer_id.find('td:nth-child(3)').text(peerColumns[2]);
continue;
}
const row_find_peer_name = $("#PeersTable tr:has(td:nth-child(1):contains('" + peerColumns[0] + "'))");
if (row_find_peer_name.length > 0) {
console.log("peerid:" + peerColumns[1] + ", status change from " + row_find_peer_name.find('td:nth-child(3)').text() + " to " + peerColumns[2]);
row_find_peer_name.find('td:nth-child(2)').text(peerColumns[1]);
row_find_peer_name.find('td:nth-child(3)').text(peerColumns[2]);
continue;
}
const row_peer = $("<tr>");
row_peer.append($("<td>").text(peerColumns[0]));
row_peer.append($("<td>").text(peerColumns[1]));
row_peer.append($("<td>").text(peerColumns[2]));
$("#PeersTable").append(row_peer);
}
}
}
async function pendingWait(myself_peerid) {
const signal_url = document.getElementById("signal_url").value;
while (true) {
let v = await fetch(`http://${signal_url}/wait?peer_id=${myself_peerid}`);
nfyFromSignal(v);
}
}
$('#btn_sign_in').on('click', async () => {
const signal_url = document.getElementById("signal_url").value;
let signinResultPromise = await fetch(`http://${signal_url}/sign_in`);
if (signinResultPromise.ok) {
myselfPeerId = parseInt(signinResultPromise.headers.get("Pragma"));
console.log("my peerid is: " + myselfPeerId);
nfyFromSignal(signinResultPromise);
pendingWait(myselfPeerId);
} else {
alert("HTTP-Error: " + signinResultPromise.status);
}
})
$('#btn_sign_out').on('click', async () => {
$("#PeersTable tbody").empty();
const signal_url = document.getElementById("signal_url").value;
let signoutResultPromise = await fetch(`http://${signal_url}/sign_out?peer_id=${myselfPeerId}`);
if (signoutResultPromise.ok) {
console.log("sign_out: " + myselfPeerId + " successful");
} else {
alert("HTTP-Error: " + signoutResultPromise.status);
}
})
$('#PeersTable').on('click', 'tr', function() {
const row_selected = $(this);
const row_selected_peer_id = parseInt(row_selected.find('td:nth-child(2)').text());
if (row_selected_peer_id === myselfPeerId) {
console.log("You should not choose yourself [" + myselfPeerId + "] to start p2p");
$("#btn_start_p2p").prop("disabled", true);
return;
}
const row_selected_peer_id_status = parseInt(row_selected.find('td:nth-child(3)').text());
if (row_selected_peer_id_status === 1) {
console.log("selected peerid: " + row_selected_peer_id);
$('#selected_peerid_for_p2p').val(row_selected_peer_id);
$("#btn_start_p2p").prop("disabled", false);
} else {
console.log("The peer [" + row_selected_peer_id + "] you choose is offline");
$("#btn_start_p2p").prop("disabled", true);
}
})
$('#btn_start_p2p').on('click', async () => {
const signal_url = document.getElementById("signal_url").value;
const remote_peer_id = $('#selected_peerid_for_p2p').val();
const sendmsg_url = `http://${signal_url}/message?peer_id=${myselfPeerId}&to=${remote_peer_id}`;
console.log("sendmsg url: " + sendmsg_url);
// $('#rtc_video_play').show();
// $('#rtc_video_play').prop('muted', false);
$('#rtc_video_play').prop('srcObject', stream);
console.log("Start P2P from [" + myselfPeerId + "] to [" + remote_peer_id + "]");
startP2P(sendmsg_url);
})
</script>
</body>
</html>