mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
210 lines
6.4 KiB
JavaScript
210 lines
6.4 KiB
JavaScript
/** @type {RTCPeerConnection} */
|
|
let rtc;
|
|
const iceConnectionLog = document.getElementById('ice-connection-state'),
|
|
iceGatheringLog = document.getElementById('ice-gathering-state'),
|
|
signalingLog = document.getElementById('signaling-state'),
|
|
dataChannelLog = document.getElementById('data-channel');
|
|
|
|
function randomString(len) {
|
|
const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
let randomString = '';
|
|
for (let i = 0; i < len; i++) {
|
|
const randomPoz = Math.floor(Math.random() * charSet.length);
|
|
randomString += charSet.substring(randomPoz, randomPoz + 1);
|
|
}
|
|
return randomString;
|
|
}
|
|
|
|
const receiveID = randomString(10);
|
|
const websocket = new WebSocket('ws://127.0.0.1:8000/' + receiveID);
|
|
websocket.onopen = function () {
|
|
document.getElementById('start').disabled = false;
|
|
}
|
|
|
|
// data channel
|
|
let dc = null, dcTimeout = null;
|
|
|
|
function createPeerConnection() {
|
|
const config = {
|
|
sdpSemantics: 'unified-plan',
|
|
bundlePolicy: "max-bundle",
|
|
};
|
|
|
|
if (document.getElementById('use-stun').checked) {
|
|
config.iceServers = [{urls: ['stun:stun.l.google.com:19302']}];
|
|
}
|
|
|
|
let pc = new RTCPeerConnection(config);
|
|
|
|
// register some listeners to help debugging
|
|
pc.addEventListener('icegatheringstatechange', function () {
|
|
iceGatheringLog.textContent += ' -> ' + pc.iceGatheringState;
|
|
}, false);
|
|
iceGatheringLog.textContent = pc.iceGatheringState;
|
|
|
|
pc.addEventListener('iceconnectionstatechange', function () {
|
|
iceConnectionLog.textContent += ' -> ' + pc.iceConnectionState;
|
|
}, false);
|
|
iceConnectionLog.textContent = pc.iceConnectionState;
|
|
|
|
pc.addEventListener('signalingstatechange', function () {
|
|
signalingLog.textContent += ' -> ' + pc.signalingState;
|
|
}, false);
|
|
signalingLog.textContent = pc.signalingState;
|
|
|
|
// connect audio / video
|
|
pc.addEventListener('track', function (evt) {
|
|
if (evt.track.kind == 'video') {
|
|
document.getElementById('media').style.display = 'block';
|
|
document.getElementById('video').srcObject = evt.streams[0];
|
|
} else {
|
|
document.getElementById('audio').srcObject = evt.streams[0];
|
|
}
|
|
});
|
|
|
|
let time_start = null;
|
|
|
|
function current_stamp() {
|
|
if (time_start === null) {
|
|
time_start = new Date().getTime();
|
|
return 0;
|
|
} else {
|
|
return new Date().getTime() - time_start;
|
|
}
|
|
}
|
|
|
|
pc.ondatachannel = function (event) {
|
|
dc = event.channel;
|
|
dc.onopen = function () {
|
|
dataChannelLog.textContent += '- open\n';
|
|
dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
|
|
};
|
|
dc.onmessage = function (evt) {
|
|
|
|
dataChannelLog.textContent += '< ' + evt.data + '\n';
|
|
dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
|
|
|
|
dcTimeout = setTimeout(function () {
|
|
if (dc == null && dcTimeout != null) {
|
|
dcTimeout = null;
|
|
return
|
|
}
|
|
const message = 'Pong ' + current_stamp();
|
|
dataChannelLog.textContent += '> ' + message + '\n';
|
|
dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
|
|
dc.send(message);
|
|
}, 1000);
|
|
}
|
|
dc.onclose = function () {
|
|
clearTimeout(dcTimeout);
|
|
dcTimeout = null;
|
|
dataChannelLog.textContent += '- close\n';
|
|
dataChannelLog.scrollTop = dataChannelLog.scrollHeight;
|
|
};
|
|
}
|
|
|
|
return pc;
|
|
}
|
|
|
|
function sendAnswer(pc) {
|
|
return pc.createAnswer()
|
|
.then((answer) => rtc.setLocalDescription(answer))
|
|
.then(function () {
|
|
// wait for ICE gathering to complete
|
|
return new Promise(function (resolve) {
|
|
if (pc.iceGatheringState === 'complete') {
|
|
resolve();
|
|
} else {
|
|
function checkState() {
|
|
if (pc.iceGatheringState === 'complete') {
|
|
pc.removeEventListener('icegatheringstatechange', checkState);
|
|
resolve();
|
|
}
|
|
}
|
|
|
|
pc.addEventListener('icegatheringstatechange', checkState);
|
|
}
|
|
});
|
|
}).then(function () {
|
|
const answer = pc.localDescription;
|
|
|
|
document.getElementById('answer-sdp').textContent = answer.sdp;
|
|
|
|
return websocket.send(JSON.stringify(
|
|
{
|
|
id: "server",
|
|
type: answer.type,
|
|
sdp: answer.sdp,
|
|
}));
|
|
}).catch(function (e) {
|
|
alert(e);
|
|
});
|
|
}
|
|
|
|
function handleOffer(offer) {
|
|
rtc = createPeerConnection();
|
|
return rtc.setRemoteDescription(offer)
|
|
.then(() => sendAnswer(rtc));
|
|
}
|
|
|
|
function sendStreamRequest() {
|
|
websocket.send(JSON.stringify(
|
|
{
|
|
id: "server",
|
|
type: "streamRequest",
|
|
receiver: receiveID,
|
|
}));
|
|
}
|
|
|
|
async function start() {
|
|
document.getElementById('start').style.display = 'none';
|
|
document.getElementById('stop').style.display = 'inline-block';
|
|
document.getElementById('media').style.display = 'block';
|
|
sendStreamRequest();
|
|
}
|
|
|
|
function stop() {
|
|
document.getElementById('stop').style.display = 'none';
|
|
document.getElementById('media').style.display = 'none';
|
|
document.getElementById('start').style.display = 'inline-block';
|
|
|
|
// close data channel
|
|
if (dc) {
|
|
dc.close();
|
|
dc = null;
|
|
}
|
|
|
|
// close transceivers
|
|
if (rtc.getTransceivers) {
|
|
rtc.getTransceivers().forEach(function (transceiver) {
|
|
if (transceiver.stop) {
|
|
transceiver.stop();
|
|
}
|
|
});
|
|
}
|
|
|
|
// close local audio / video
|
|
rtc.getSenders().forEach(function (sender) {
|
|
const track = sender.track;
|
|
if (track !== null) {
|
|
sender.track.stop();
|
|
}
|
|
});
|
|
|
|
// close peer connection
|
|
setTimeout(function () {
|
|
rtc.close();
|
|
rtc = null;
|
|
}, 500);
|
|
}
|
|
|
|
|
|
websocket.onmessage = async function (evt) {
|
|
const received_msg = evt.data;
|
|
const object = JSON.parse(received_msg);
|
|
if (object.type == "offer") {
|
|
document.getElementById('offer-sdp').textContent = object.sdp;
|
|
await handleOffer(object)
|
|
}
|
|
}
|