This repository has been archived on 2023-11-14. You can view files and clone it, but cannot push or open issues/pull-requests.
viscord/rtc-test/index.html

334 lines
9.8 KiB
HTML

<html>
<head>
<script src="https://unpkg.com/peerjs@1.4.5/dist/peerjs.min.js"></script>
<style>
html {
font-family: sans-serif;
}
#tracks {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-flow: row;
}
#tracks fieldset {
width: '50%'
}
</style>
</head>
<body>
<div id="tracks"></div>
<fieldset>
<legend>Call</legend>
<div id="call_status"></div>
<label>Audio</label>
<select id="in_audio">
</select>
<br />
<label>Video</label>
<select id="in_video">
</select>
<br />
</fieldset>
<fieldset>
<legend>Peer Connection</legend>
<div>Peer ID: <span id="pid"></span></div>
<div id="status"></div>
</fieldset>
<fieldset>
<legend>Dial Out</legend>
<label>Peer ID:</label>
<input type="text" id="out_pid" />
<br />
<button disabled type="submit" id="out_btn">CALL</button>
<span id="out_status">Inactive</span>
</fieldset>
<fieldset>
<legend>Dial In</legend>
<div id="in_status"></div>
<button disabled type="submit" id="answer_btn">ANSWER</button>
</fieldset>
<script>
const eStatus = document.getElementById('status');
const ePid = document.getElementById('pid');
const eCallPid = document.getElementById('out_pid');
const eCallBtn = document.getElementById('out_btn');
const eIncommingData = document.getElementById('incomming_data');
const eIncommingStatus = document.getElementById('in_status');
const eAnswerBtn = document.getElementById('answer_btn');
const eOutgoingStatus = document.getElementById('out_status');
const eTracks = document.getElementById('tracks');
const eAudioOptions = document.getElementById('in_audio');
const eVideoOptions = document.getElementById('in_video');
const eEndBtn = document.getElementById('end');
let connected = false;
let localMediaStream = null;
let remoteMediaStream = null;
let incommingCall = null;
let currentVideoTrackId = null;
let currentAudioTrackId = null;
let selfBoxRemove = null;
let ctx = null;
setInterval(() => {
if(ctx === null) return;
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, 160, 90);
setTimeout(() => {
if(ctx === null) return;
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 160, 90);
}, 100);
}, 200);
function closeLocalMediaStream() {
for(const track of localMediaStream.getTracks()) {
track.stop();
}
}
async function createLocalMediaStream() {
console.log('creating stream');
if(localMediaStream !== null) {
closeLocalMediaStream();
}
localMediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId: {
exact: eAudioOptions.value
}
},
noiseSuppression: false,
video: eVideoOptions.value === 'none' ? false : {
deviceId: {
exact: eVideoOptions.value
}
}
});
if(eVideoOptions.value === 'none') {
const canvas = Object.assign(document.createElement("canvas"), {
width: 160,
height: 90
});
ctx = canvas.getContext("2d");
const blankStream = canvas.captureStream();
const videoTrack = blankStream.getVideoTracks()[0];
localMediaStream.addTrack(videoTrack);
}
updateOutgoing();
};
async function updateOutgoing() {
for(const peerId in connections) {
const { call, completed } = connections[peerId];
if(!completed) continue;
if(call.peerConnection === undefined) debugger;
const audioTrack = localMediaStream.getTracks()
.filter(track => track.kind === 'audio')[0];
const videoTrack = localMediaStream.getTracks()
.filter(track => track.kind === 'video')[0];
call.peerConnection.getSenders()
.filter(sender => sender.track.kind === 'audio')[0]
.replaceTrack(audioTrack);
call.peerConnection.getSenders()
.filter(sender => sender.track.kind === 'video')[0]
.replaceTrack(videoTrack)
}
selfBoxRemove?.();
if(Object.keys(connections.filter(c => c.completed)).length === 0) {
selfBoxRemove = addVideoBox(localMediaStream, 'Self', () => {
alert('DISCO ALL CONNS');
}).remove;
} else {
selfBoxRemove = null;
}
}
eAudioOptions.onchange = createLocalMediaStream;
eVideoOptions.onchange = createLocalMediaStream;
// get permissions and enumerate devices
(async function() {
const scanDevices = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
for(const track of scanDevices.getTracks()) {
track.stop();
}
const devices = await navigator.mediaDevices.enumerateDevices();
for(const device of devices.filter(v => v.kind === 'audioinput')) {
const elem = document.createElement('option');
elem.innerText = device.label;
elem.value = device.deviceId;
eAudioOptions.appendChild(elem);
}
eVideoOptions.appendChild((function () {
const elem = document.createElement('option');
elem.innerText = "None";
elem.value = "none";
return elem;
})());
for(const device of devices.filter(v => v.kind === 'videoinput')) {
const elem = document.createElement('option');
elem.innerText = device.label;
elem.value = device.deviceId;
eVideoOptions.appendChild(elem);
}
// const
})();
eCallBtn.disabled = false;
eStatus.innerText = 'Connecting...'
const peer = new Peer();
peer.on('open', (id) => {
eStatus.innerText = 'Connected'
ePid.innerText = '' + id;
connected = true;
});
peer.on('close', () => {
eStatus.innerText = 'Closed'
ePid.innerText = '';
connected = false;
});
eCallBtn.addEventListener('click', async () => {
await createLocalMediaStream();
const conn = peer.connect(eCallPid.value);
addConnection(conn);
eCallBtn.disabled = true;
const call = peer.call(eCallPid.value, localMediaStream);
call.on('stream', (mediaStream) => addCall(call, mediaStream));
eOutgoingStatus.innerText = 'Calling...';
});
peer.on('connection', (connection) => {
addConnection(connection);
});
eAnswerBtn.addEventListener('click', async () => {
await createLocalMediaStream();
const tempCall = incommingCall;
incommingCall = null;
tempCall.on('stream', (mediaStream) => addCall(tempCall, mediaStream));
tempCall.answer(localMediaStream);
eIncommingStatus.innerText = '';
eAnswerBtn.disabled = true;
});
peer.on('call', (call) => {
if(incommingCall !== null) {
return; // reject the call request.
}
eIncommingStatus.innerText = 'Incomming call from ' + call.peer;
eAnswerBtn.disabled = false;
incommingCall = call;
});
const connections = {}
function addConnection(conn) {
connections[conn.peer] ??= {};
connections[conn.peer].conn = conn;
onCall(conn.peer);
}
function addCall(call, mediaStream) {
connections[call.peer] ??= {};
connections[call.peer].call = call;
connections[call.peer].mediaStream = mediaStream;
onCall(call.peer);
}
function addVideoBox(stream, name, end) {
const root = document.createElement('fieldset');
const elem = document.createElement('video');
const legend = document.createElement('legend');
const endBtn = document.createElement('button');
elem.autoplay = true;
elem.controls = true;
elem.style.width = '100%';
elem.srcObject = stream;
legend.innerText = name;
endBtn.innerText = "END";
endBtn.addEventListener('click', end);
// root.appendChild(document.createElement('br'));
root.appendChild(legend);
root.appendChild(endBtn);
root.appendChild(document.createElement('br'));
root.appendChild(elem);
eTracks.appendChild(root);
return {
remove() {
while(root.firstChild) {
root.removeChild(root.firstChild);
}
root.remove();
}
}
}
function onCall(peerId) {
const { call, conn, mediaStream, completed } = connections[peerId];
if(!call || !conn || !mediaStream) return;
if(completed) return;
connections[peerId].completed = true;
eOutgoingStatus.innerText = 'Inactive';
eCallBtn.disabled = false;
eCallPid.value = '';
eIncommingStatus.innerText = '';
eAnswerBtn.disabled = true;
}
function updateRemoteBoxes() {
for(const peerId in connections.filter(c => c.completed)) {
const { mediaStream, call, conn } = connections[peerId];
const { remove } = addVideoBox(mediaStream, peerId.split('-')[0], () => {
conn.close();
call.close();
});
conn.on('close', () => {
remove();
delete connections[peerId];
if(Object.keys(connections).length === 0) closeLocalMediaStream();
});
}
}
// eEndBtn.addEventListener('click', () => {
// call.close();
// })
</script>
</body>
</html>