Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
minimal-webrtc/camera/templates/index.html

309 řádky
9.5 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Simple WebRTC</title>
<style>
body.justVideo {
margin: 0;
overflow: hidden;
}
#selfView {
position: fixed;
right: 0;
bottom: 0;
border: 5px solid blue;
}
#unmute, #start {
position: fixed;
top: 0;
width: 100%;
font-size: 4em;
}
form {
background: lightgray;
border: solid gold 5px;
display: inline-block;
display: table;
}
form label {
display: table-row;
}
#status {
clear: both;
}
</style>
</head>
<body>
<a id="qrcodelink" style="display:none;"><img id="qrcode" /></a>
<form name="settings">
<label>
Recieve remote video:
<select name="client-video">
<option value="none">none</option>
<option value="environment" selected>rear camera</option>
<option value="user">front camera</option>
<option value="true" selected>any camera</option>
<option value="screen">screen share</option>
</select>
</label>
<label>
Recieve remote audio:
<input name="client-audio" type="checkbox" value="true" />
</label>
<label>
Transmit video:
<select name="host-video">
<option value="none" selected>none</option>
<option value="true">any camera</option>
<option value="screen">screen share</option>
</select>
</label>
<label>
Transmit audio:
<input name="host-audio" type="checkbox" value="true" />
</label>
</form>
<div id="status"></div>
<div id="videos">
<button id="unmute" style="display:none;">Unmute</button>
<button id="start" style="display:none;">Start Streaming</button>
<video id="remoteView" width="100%" autoplay muted style="display:none;"></video>
<video id="selfView" width="200" height="150" autoplay muted style="display:none;"></video>
</div>
<script >
const create = (container, type) => container.appendChild(document.createElement(type));
const body = document.querySelector("body");
const out = document.getElementById("status");
const qrcode = document.getElementById("qrcode");
const qrcodelink = document.getElementById("qrcodelink");
const remoteView = document.getElementById("remoteView");
const selfView = document.getElementById("selfView");
const start = document.getElementById("start");
const unmute = document.getElementById("unmute");
const form = document.forms["settings"];
out.innerText += "Loading...\n";
unmute.addEventListener("click", _ => {
remoteView.muted = false;
unmute.style.display = 'none';
});
var settings = undefined;
function readSettingsForm() {
const obj = {};
for (const el of form.elements) {
obj[el.name] = el.type == 'checkbox' ? el.checked : el.value;
el.disabled = true;
}
return obj;
}
function getRoomName() {
JSON.parse(document.getElementById('room-name').textContent);
}
let roomName = window.location.hash;
let isHost = roomName === undefined || !roomName;
if (isHost) {
// From https://stackoverflow.com/a/1349426
function makeid(length) {
var result = '';
var characters = 'abcdefghijklmnopqrstuvwxyz';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
roomName = makeid(8);
qrcodelink.href = window.location.href.split('#')[0] + '#' + roomName;
qrcode.src = window.location.href.split('#')[0] + roomName + '/qr';
qrcodelink.style.display = '';
} else {
roomName = roomName.substring(1);
qrcodelink.style.display = 'none';
form.style.display = 'none';
}
out.innerText += "Room: " + roomName + "\n";
var webSocket = undefined;
function sendJson(data) {
const toSend = JSON.stringify(data);
out.innerText += "Sending message...\n";
create(out, 'pre').innerText = toSend.split('\\r\\n').join('\r\n');
create(out, 'br');
webSocket.send(toSend);
}
var pc = undefined;
function createRTCPeerConnection() {
const pc = new RTCPeerConnection();
out.innerText += "Created RTCPeerConnection.\n";
pc.onicecandidate = ({candidate}) => sendJson({candidate});
// let the "negotiationneeded" event trigger offer generation
pc.onnegotiationneeded = async function () {
out.innerText += "In pc.onnegotiationneeded...\n";
await pc.setLocalDescription(await pc.createOffer());
sendJson({
description: pc.localDescription
});
}
pc.ontrack = ({streams: [stream]}) => {
out.innerText += "In pc.ontrack...\n";
remoteView.srcObject = stream;
remoteView.style.display = '';
remoteView.play();
out.innerText += "Set srcObject\n";
out.style.display = 'none';
form.style.display = 'none';
videos.style.display = '';
body.classList.add('justVideo');
};
return pc;
}
// get a local stream, show it in a self-view and add it to be sent
async function startStreaming(fromButton) {
const otherAudioSettings = isHost
? settings['client-audio']
: settings['host-audio'];
if (otherAudioSettings) {
unmute.style.display = '';
}
const videoSettings = isHost
? settings['host-video']
: settings['client-video'];
out.innerText += "videoSettings=" + videoSettings + "\n";
const audioSettings = isHost
? settings['host-audio']
: settings['client-audio'];
out.innerText += "audioSettings=" + audioSettings + "\n";
if (videoSettings == 'screen' && !fromButton) {
start.style.display = '';
return;
}
start.style.display = 'none';
if (isHost) {
sendJson({
settings: settings
});
}
if (pc !== undefined) return;
pc = createRTCPeerConnection();
const videoConstraints = videoSettings == 'none'
? false
: videoSettings == 'true'
? true
: { advanced: [{facingMode: videoSettings}] };
out.innerText += "Created videoConstraints.\n";
if (!videoConstraints && !audioSettings) return;
const stream = videoSettings == 'screen'
? await navigator.mediaDevices.getDisplayMedia({
audio: audioSettings,
video: true
})
: await navigator.mediaDevices.getUserMedia({
audio: audioSettings,
video: videoConstraints
});
out.innerText += "Created stream.\n";
if (videoConstraints) {
selfView.srcObject = stream;
selfView.style.display = '';
}
for (const track of stream.getTracks()) {
out.innerText += "Added track.\n";
pc.addTrack(track, stream);
}
}
function startStartingWithErorrHandling(fromButton) {
startStreaming(fromButton)
.then(() => {
out.innerText += "startStreaming() finished.\n";
})
.catch(e => {
out.innerText += "startStreaming() errored: " + e.message + "\n";
});
}
start.addEventListener("click", _ => {
startStartingWithErorrHandling(true)
});
async function receiveMessage(e) {
qrcode.style.display = 'none';
out.innerText += "In webSocket.onmessage...\n";
create(out, 'pre').innerText = e.data.split('\\r\\n').join('\r\n');
create(out, 'br');
const data = JSON.parse(e.data);
if (data.requestSettings) {
settings = readSettingsForm();
startStartingWithErorrHandling(false);
} else if (data.settings) {
settings = data.settings;
startStartingWithErorrHandling(false);
} else if (data.description) {
await pc.setRemoteDescription(data.description);
if (data.description.type == "offer") {
out.innerText += "Got an offer...\n";
await pc.setLocalDescription(await pc.createAnswer());
sendJson({
description: pc.localDescription
});
}
} else if (data.candidate) {
out.innerText += "Adding ice candidate...\n";
await pc.addIceCandidate(data.candidate);
}
};
function createWebSocket() {
const webSocket = new WebSocket(
'ws' + (window.location.protocol == 'https:' ? 's' : '') + '://'
+ window.location.host
+ '/camera/ws/' + (isHost ? 'host' : 'client') + '/'
+ roomName
+ '/'
);
out.innerText += "Created WebSocket.\n";
webSocket.onclose = function(e) {
out.innerText += 'WebSocket closed unexpectedly: ' + e + '\n';
};
webSocket.onerror = function(e) {
out.innerText += 'WebSocket error: ' + e + '\n';
};
webSocket.onmessage = receiveMessage;
return webSocket;
}
webSocket = createWebSocket();
if (!isHost) {
webSocket.onopen = _ => sendJson({requestSettings: true});
}
out.innerText += "Finished <script> block.\n";
</script>
</body>
</html>