Não pode escolher mais do que 25 tópicos
Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
309 linhas
9.5 KiB
HTML
309 linhas
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>
|