Compare commits
1 Commits
bdeb886353
...
4f3ae7abd6
Author | SHA1 | Date | |
---|---|---|---|
4f3ae7abd6 |
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "external/LZMA-JS"]
|
||||||
|
path = external/LZMA-JS
|
||||||
|
url = https://github.com/LZMA-JS/LZMA-JS.git
|
||||||
|
[submodule "external/base64.js"]
|
||||||
|
path = external/base64.js
|
||||||
|
url = https://gist.github.com/72c423f727d395eeaa09697058238727.git
|
1
camera/static/js/base64.js
Symbolic link
1
camera/static/js/base64.js
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/base64.js/base64.js
|
1
camera/static/js/lzma_worker.js
Symbolic link
1
camera/static/js/lzma_worker.js
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/LZMA-JS/src/lzma_worker.js
|
175
camera/static/js/prefix
Normal file
175
camera/static/js/prefix
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
{"description":{"type":"answer","sdp":"v=0
|
||||||
|
o=- 0 2 IN IP4 127.0.0.1
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=msid-semantic: WMS
|
||||||
|
m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
a=rtcp:9 IN IP4 0.0.0.0
|
||||||
|
a=candidate:0 1 udp 0 a.local 0 typ host generation 0 network-cost 999
|
||||||
|
a=ice-ufrag:
|
||||||
|
a=ice-pwd:
|
||||||
|
a=ice-options:trickle
|
||||||
|
a=fingerprint:sha-256 000:01:12:23:34:45:56:67:78:89:9A:AB:BC:CD:DE:EF:F0:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F
|
||||||
|
a=setup:active
|
||||||
|
a=mid:0
|
||||||
|
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||||
|
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||||
|
a=recvonly
|
||||||
|
a=rtcp-mux
|
||||||
|
a=rtpmap:109 opus/48000/2
|
||||||
|
a=fmtp:109 minptime=10;useinbandfec=1
|
||||||
|
a=rtpmap:9 G722/8000
|
||||||
|
a=rtpmap:0 PCMU/8000
|
||||||
|
a=rtpmap:8 PCMA/8000
|
||||||
|
a=rtpmap:101 telephone-event/8000
|
||||||
|
"}}
|
||||||
|
{"description":{"type":"offer","sdp":"v=0
|
||||||
|
o=mozilla...THIS_IS_SDPARTA-68.8.1 0 0 IN IP4 0.0.0.0
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=sendrecv
|
||||||
|
a=fingerprint:sha-256 0:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20:21:22:23:24:25:26:27:28:29:2A:2B:2C:2D
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=ice-options:trickle
|
||||||
|
a=msid-semantic:WMS *
|
||||||
|
m=audio 40000 UDP/TLS/RTP/SAVPF 109 9 0 8 101
|
||||||
|
c=IN IP4 192.168.0.0
|
||||||
|
a=candidate:0 1 UDP 2100000000 192.168.0.0 40000 typ host
|
||||||
|
a=candidate:1 1 UDP 2100000000 ::1 40000 typ host
|
||||||
|
a=candidate:2 1 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
|
||||||
|
a=candidate:2 1 TCP 2100000000 192.168.0.0 9 typ host tcptype active
|
||||||
|
a=candidate:3 1 TCP 2100000000 ::1 40000 typ host tcptype passive
|
||||||
|
a=candidate:3 1 TCP 2100000000 ::1 9 typ host tcptype active
|
||||||
|
a=candidate:0 2 UDP 2100000000 192.168.0.0 40000 typ host
|
||||||
|
a=candidate:1 2 UDP 2100000000 ::1 40000 typ host
|
||||||
|
a=candidate:2 2 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
|
||||||
|
a=candidate:2 2 TCP 2100000000 192.168.0.0 9 typ host tcptype active
|
||||||
|
a=candidate:3 2 TCP 2100000000 ::1 40000 typ host tcptype passive
|
||||||
|
a=candidate:3 2 TCP 2100000000 ::1 9 typ host tcptype active
|
||||||
|
a=sendrecv
|
||||||
|
a=end-of-candidates
|
||||||
|
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||||
|
a=extmap:2/recvonly urn:ietf:params:rtp-hdrext:csrc-audio-level
|
||||||
|
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||||
|
a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1
|
||||||
|
a=fmtp:101 0-15
|
||||||
|
a=ice-pwd:
|
||||||
|
a=ice-ufrag:
|
||||||
|
a=mid:0
|
||||||
|
a=msid:{}
|
||||||
|
a=rtcp:40000 IN IP4 192.168.0.0
|
||||||
|
a=rtcp-mux
|
||||||
|
a=rtpmap:109 opus/48000/2
|
||||||
|
a=rtpmap:9 G722/8000/1
|
||||||
|
a=rtpmap:0 PCMU/8000
|
||||||
|
a=rtpmap:8 PCMA/8000
|
||||||
|
a=rtpmap:101 telephone-event/8000
|
||||||
|
a=setup:actpass
|
||||||
|
a=ssrc:0 cname:{}
|
||||||
|
"}}
|
||||||
|
{"description":{"type":"answer","sdp":"v=0
|
||||||
|
o=- 0 2 IN IP4 127.0.0.1
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=msid-semantic: WMS
|
||||||
|
m=video 9 UDP/TLS/RTP/SAVPF 120 121
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
a=rtcp:9 IN IP4 0.0.0.0
|
||||||
|
a=candidate:0 1 udp 2100000000 abc.local 40000 typ host generation 0 network-cost 999
|
||||||
|
a=ice-ufrag:
|
||||||
|
a=ice-pwd:
|
||||||
|
a=ice-options:trickle
|
||||||
|
a=fingerprint:sha-256 0:2E:2F:30:31:32:33:34:35:36:37:38:39:3A:3B:3C:3D:3E:3F:40:41:42:43:44:45:46:47:48:49:4A:4B
|
||||||
|
a=setup:active
|
||||||
|
a=mid:0
|
||||||
|
a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
|
||||||
|
a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||||
|
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||||
|
a=recvonly
|
||||||
|
a=rtcp-mux
|
||||||
|
a=rtpmap:120 VP8/90000
|
||||||
|
a=rtcp-fb:120 goog-remb
|
||||||
|
a=rtcp-fb:120 ccm fir
|
||||||
|
a=rtcp-fb:120 nack
|
||||||
|
a=rtcp-fb:120 nack pli
|
||||||
|
a=rtpmap:121 VP9/90000
|
||||||
|
a=rtcp-fb:121 goog-remb
|
||||||
|
a=rtcp-fb:121 ccm fir
|
||||||
|
a=rtcp-fb:121 nack
|
||||||
|
a=rtcp-fb:121 nack pli
|
||||||
|
a=fmtp:121 profile-id=0
|
||||||
|
"}}
|
||||||
|
{"description":{"type":"offer","sdp":"v=0
|
||||||
|
o=mozilla...THIS_IS_SDPARTA-68.8.1 0 0 IN IP4 0.0.0.0
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=sendrecv
|
||||||
|
a=fingerprint:sha-256 0:4C:4D:4E:4F:50:51:52:53:54:55:56:57:58:59:5A:5B:5C:5D:5E:5F:60:61:62:63:64:65:66:67:68:69
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=ice-options:trickle
|
||||||
|
a=msid-semantic:WMS *
|
||||||
|
m=video 40000 UDP/TLS/RTP/SAVPF 120 121
|
||||||
|
c=IN IP4 192.168.0.0
|
||||||
|
a=candidate:0 1 UDP 2100000000 192.168.0.0 40000 typ host
|
||||||
|
a=candidate:1 1 UDP 2100000000 ::1 40000 typ host
|
||||||
|
a=candidate:2 1 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
|
||||||
|
a=candidate:2 1 TCP 2100000000 192.168.0.0 9 typ host tcptype active
|
||||||
|
a=candidate:3 1 TCP 2100000000 ::1 40000 typ host tcptype passive
|
||||||
|
a=candidate:3 1 TCP 2100000000 ::1 9 typ host tcptype active
|
||||||
|
a=candidate:0 2 UDP 2100000000 192.168.0.0 40000 typ host
|
||||||
|
a=candidate:1 2 UDP 2100000000 ::1 40000 typ host
|
||||||
|
a=candidate:2 2 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
|
||||||
|
a=candidate:2 2 TCP 2100000000 192.168.0.0 9 typ host tcptype active
|
||||||
|
a=candidate:3 2 TCP 2100000000 ::1 40000 typ host tcptype passive
|
||||||
|
a=candidate:3 2 TCP 2100000000 ::1 9 typ host tcptype active
|
||||||
|
a=sendrecv
|
||||||
|
a=end-of-candidates
|
||||||
|
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||||
|
a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||||
|
a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
|
||||||
|
a=fmtp:120 max-fs=12288;max-fr=60
|
||||||
|
a=fmtp:121 max-fs=12288;max-fr=60
|
||||||
|
a=ice-pwd:
|
||||||
|
a=ice-ufrag:
|
||||||
|
a=mid:0
|
||||||
|
a=msid:{}
|
||||||
|
a=rtcp:40000 IN IP4 192.168.0.0
|
||||||
|
a=rtcp-fb:120 nack
|
||||||
|
a=rtcp-fb:120 nack pli
|
||||||
|
a=rtcp-fb:120 ccm fir
|
||||||
|
a=rtcp-fb:120 goog-remb
|
||||||
|
a=rtcp-fb:121 nack
|
||||||
|
a=rtcp-fb:121 nack pli
|
||||||
|
a=rtcp-fb:121 ccm fir
|
||||||
|
a=rtcp-fb:121 goog-remb
|
||||||
|
a=rtcp-mux
|
||||||
|
a=rtpmap:120 VP8/90000
|
||||||
|
a=rtpmap:121 VP9/90000
|
||||||
|
a=setup:actpass
|
||||||
|
a=ssrc:0 cname:{}
|
||||||
|
"}}
|
||||||
|
{"description":{"type":"offer","sdp":"v=0
|
||||||
|
o=mozilla...THIS_IS_SDPARTA-75.0 0 0 IN IP4 0.0.0.0
|
||||||
|
s=-
|
||||||
|
t=0 0
|
||||||
|
a=sendrecv
|
||||||
|
a=fingerprint:sha-256 6A:6B:6C:6D:6E:6F:70:71:72:73:74:75:76:77:78:79:7A:7B:7C:7D:7E:7F:80:81:82:83:84:85:86:87:88:89
|
||||||
|
a=group:BUNDLE 0
|
||||||
|
a=ice-options:trickle
|
||||||
|
a=msid-semantic:WMS *
|
||||||
|
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
a=candidate:0 1 UDP 2100000000 0.local 40000 typ host
|
||||||
|
a=candidate:1 1 TCP 2100000000 0.local 9 typ host tcptype active
|
||||||
|
a=sendrecv
|
||||||
|
a=end-of-candidates
|
||||||
|
a=ice-pwd:
|
||||||
|
a=ice-ufrag:
|
||||||
|
a=mid:0
|
||||||
|
a=setup:actpass
|
||||||
|
a=sctp-port:5000
|
||||||
|
a=max-message-size:
|
||||||
|
"}}
|
47
camera/static/js/prefixCompressor.js
Normal file
47
camera/static/js/prefixCompressor.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
async function loadLZMAPrefixCompressor(prefixFilename) {
|
||||||
|
class PrefixCompressor {
|
||||||
|
constructor(prefix, compress, decompress, bytesToIgnore) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.prefixCompressed = compress(prefix);
|
||||||
|
this.bytesToIgnore = bytesToIgnore;
|
||||||
|
this.baseCompress = compress;
|
||||||
|
this.baseDecompress = decompress;
|
||||||
|
}
|
||||||
|
|
||||||
|
compress(str) {
|
||||||
|
const c = this.baseCompress(this.prefix + str);
|
||||||
|
for (var i = this.bytesToIgnore;
|
||||||
|
i < this.prefixCompressed.length
|
||||||
|
&& this.prefixCompressed[i] == c[i];
|
||||||
|
i++) { }
|
||||||
|
const omittedBytes = this.prefixCompressed.length - i;
|
||||||
|
const res = c.subarray(i - 1);
|
||||||
|
res[0] = omittedBytes;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
decompress(arr) {
|
||||||
|
const omittedBytes = arr[0];
|
||||||
|
const prefixBytes = this.prefixCompressed.length-omittedBytes;
|
||||||
|
const c = new Uint8Array(prefixBytes + arr.length-1);
|
||||||
|
c.set(this.prefixCompressed.subarray(0, prefixBytes));
|
||||||
|
c.set(arr.subarray(1), prefixBytes);
|
||||||
|
const d = this.baseDecompress(c);
|
||||||
|
return d.substring(this.prefix.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function loadDictionary() {
|
||||||
|
const response = await fetch(prefixFilename);
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
const prefix = await loadDictionary();
|
||||||
|
|
||||||
|
return new PrefixCompressor(prefix, str => {
|
||||||
|
const res = new Uint8Array(LZMA.compress(str, 9));
|
||||||
|
// Clear uncompressed size as it will be invalid.
|
||||||
|
for (var i = 5; i < 13; i++) res[i] = 255;
|
||||||
|
return res;
|
||||||
|
}, arr => {
|
||||||
|
return LZMA.decompress(arr).toString();
|
||||||
|
}, 12);
|
||||||
|
}
|
|
@ -12,13 +12,13 @@ body.justVideo {
|
||||||
body.justVideo #status, body.justVideo form {
|
body.justVideo #status, body.justVideo form {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#qrcodelink {
|
.qrcontainer {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
#qrcodelink svg path {
|
.qrcontainer svg path {
|
||||||
min-width: 30vw;
|
min-width: 30vw;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
|
@ -57,15 +57,29 @@ form label {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript" src="static/js/qrcodegen.js"></script>
|
<script type="text/javascript" src="static/js/qrcodegen.js"></script>
|
||||||
|
<script type="text/javascript" src="static/js/lzma_worker.js"></script>
|
||||||
|
<script type="text/javascript" src="static/js/prefixCompressor.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a id="qrcodelink" style="display:none;">
|
<a class="qrcontainer" id="qrcodelink" style="display:none;">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" id="qrcode" style="width:20em; height:20em;" stroke="none">
|
<svg xmlns="http://www.w3.org/2000/svg" id="qrcode" style="width:20em; height:20em;" stroke="none">
|
||||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||||
<path d="" fill="#000000"/>
|
<path d="" fill="#000000"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<div class="qrcontainer" id="qrcodeObjContainer" style="display:none;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" id="qrcodeObj" style="width:20em; height:20em;" stroke="none">
|
||||||
|
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||||
|
<path d="" fill="#000000"/>
|
||||||
|
</svg> <br/>
|
||||||
|
<textarea id="localOffer" readonly></textarea>
|
||||||
|
<button id="copyLocalOffer">Copy</button>
|
||||||
|
</div>
|
||||||
<form name="settings">
|
<form name="settings">
|
||||||
|
<label>
|
||||||
|
Serverless mode:
|
||||||
|
<input name="serverless" type="checkbox" value="true" />
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Recieve remote video:
|
Recieve remote video:
|
||||||
<select name="client-video">
|
<select name="client-video">
|
||||||
|
@ -99,6 +113,12 @@ form label {
|
||||||
<input name="debug" type="checkbox" value="true" />
|
<input name="debug" type="checkbox" value="true" />
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
<form id="serverlessOffer" style="display: none;">
|
||||||
|
<label>
|
||||||
|
Paste offer here:
|
||||||
|
<textarea name="remoteOffer"></textarea>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
<div id="videos">
|
<div id="videos">
|
||||||
<button id="unmute" style="display:none;">Unmute</button>
|
<button id="unmute" style="display:none;">Unmute</button>
|
||||||
|
@ -106,19 +126,36 @@ form label {
|
||||||
<video id="remoteView" width="100%" autoplay muted style="display:none;"></video>
|
<video id="remoteView" width="100%" autoplay muted style="display:none;"></video>
|
||||||
<video id="selfView" width="200" height="150" autoplay muted style="display:none;"></video>
|
<video id="selfView" width="200" height="150" autoplay muted style="display:none;"></video>
|
||||||
</div>
|
</div>
|
||||||
<script >
|
<script type="module" >
|
||||||
|
import * as b64 from "./static/js/base64.js";
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
const create = (container, type) => container.appendChild(document.createElement(type));
|
const create = (container, type) => container.appendChild(document.createElement(type));
|
||||||
const QRGen = qrcodegen.QrCode;
|
const QRGen = qrcodegen.QrCode;
|
||||||
|
|
||||||
|
var compressor = null;
|
||||||
|
async function getCompressor() {
|
||||||
|
if (compressor == null) {
|
||||||
|
compressor = await loadLZMAPrefixCompressor('static/js/prefix');
|
||||||
|
}
|
||||||
|
return compressor;
|
||||||
|
}
|
||||||
|
|
||||||
const body = document.querySelector("body");
|
const body = document.querySelector("body");
|
||||||
const out = document.getElementById("status");
|
const out = document.getElementById("status");
|
||||||
const qrcode = document.getElementById("qrcode");
|
const qrcode = document.getElementById("qrcode");
|
||||||
const qrcodelink = document.getElementById("qrcodelink");
|
const qrcodelink = document.getElementById("qrcodelink");
|
||||||
|
const qrcodeObjContainer = document.getElementById("qrcodeObjContainer");
|
||||||
|
const qrcodeObj = document.getElementById("qrcodeObj");
|
||||||
const remoteView = document.getElementById("remoteView");
|
const remoteView = document.getElementById("remoteView");
|
||||||
const selfView = document.getElementById("selfView");
|
const selfView = document.getElementById("selfView");
|
||||||
const start = document.getElementById("start");
|
const start = document.getElementById("start");
|
||||||
const unmute = document.getElementById("unmute");
|
const unmute = document.getElementById("unmute");
|
||||||
const form = document.forms["settings"];
|
const form = document.forms["settings"];
|
||||||
|
const remoteOfferForm = document.forms["serverlessOffer"];
|
||||||
|
const remoteOffer = remoteOfferForm.elements["remoteOffer"];
|
||||||
|
const localOfferArea = document.getElementById("localOffer");
|
||||||
|
const localOfferCopyButton = document.getElementById("copyLocalOffer");
|
||||||
|
|
||||||
function _log(str, tag) {
|
function _log(str, tag) {
|
||||||
const logEntry = document.createElement(tag);
|
const logEntry = document.createElement(tag);
|
||||||
|
@ -157,19 +194,52 @@ form label {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isServerless() {
|
||||||
|
return settings && 'serverless' in settings && settings.serverless;
|
||||||
|
}
|
||||||
|
|
||||||
function withHash(hash) {
|
function withHash(hash) {
|
||||||
return window.location.href.split('#')[0] + '#' + hash;
|
return window.location.href.split('#')[0] + '#' + hash;
|
||||||
}
|
}
|
||||||
function displayQRUrl(url) {
|
function displayQR(qrcode, val) {
|
||||||
qrcodelink.href = url;
|
const encodeFunc = val instanceof Uint8Array
|
||||||
const qr = QRGen.encodeText(url, QRGen.Ecc.MEDIUM);
|
? QRGen.encodeBinary
|
||||||
|
: QRGen.encodeText;
|
||||||
|
const qr = encodeFunc(val, QRGen.Ecc.MEDIUM);
|
||||||
const code = qr.toSvgString(1);
|
const code = qr.toSvgString(1);
|
||||||
const viewBox = (/ viewBox="([^"]*)"/.exec(code))[1];
|
const viewBox = (/ viewBox="([^"]*)"/.exec(code))[1];
|
||||||
const pathD = (/ d="([^"]*)"/.exec(code))[1];
|
const pathD = (/ d="([^"]*)"/.exec(code))[1];
|
||||||
qrcode.setAttribute("viewBox", viewBox);
|
qrcode.setAttribute("viewBox", viewBox);
|
||||||
qrcode.querySelector("path").setAttribute("d", pathD);
|
qrcode.querySelector("path").setAttribute("d", pathD);
|
||||||
|
}
|
||||||
|
function displayQRUrl(url) {
|
||||||
|
qrcodelink.href = url;
|
||||||
|
displayQR(qrcode, url);
|
||||||
qrcodelink.style.display = '';
|
qrcodelink.style.display = '';
|
||||||
}
|
}
|
||||||
|
function displayQRUrlForObj(obj) {
|
||||||
|
const json = JSON.stringify(obj);
|
||||||
|
log("Encoding message in QR code/link:");
|
||||||
|
logPre(json);
|
||||||
|
const compressed = compressor.compress(json);
|
||||||
|
const encoded = b64.bytesToBase64(compressed);
|
||||||
|
displayQRUrl(withHash('^' + encoded));
|
||||||
|
}
|
||||||
|
function displayQRCodeForObj(obj) {
|
||||||
|
const json = JSON.stringify(obj);
|
||||||
|
log("Encoding message in QR code for copy/paste:");
|
||||||
|
logPre(json);
|
||||||
|
const compressed = compressor.compress(json);
|
||||||
|
const encoded = b64.bytesToBase64(compressed);
|
||||||
|
displayQR(qrcodeObj, compressed);
|
||||||
|
localOfferArea.value = encoded;
|
||||||
|
localOfferCopyButton.onclick = _ => {
|
||||||
|
localOfferArea.focus();
|
||||||
|
localOfferArea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
};
|
||||||
|
qrcodeObjContainer.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
let roomName = window.location.hash;
|
let roomName = window.location.hash;
|
||||||
let isHost = roomName === undefined || !roomName;
|
let isHost = roomName === undefined || !roomName;
|
||||||
|
@ -195,6 +265,25 @@ form label {
|
||||||
log("Failed to read settings from hash: " + error);
|
log("Failed to read settings from hash: " + error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var firstMessage = undefined;
|
||||||
|
if (roomName && roomName.startsWith("#^")) {
|
||||||
|
roomName = undefined;
|
||||||
|
isHost = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const hash = decodeURI(window.location.hash);
|
||||||
|
log("Reading first message from hash:");
|
||||||
|
logPre(hash);
|
||||||
|
log("Decoded base64:");
|
||||||
|
const decoded = (await getCompressor()).decompress(b64.base64ToBytes(hash.substring(2)));
|
||||||
|
logPre(decoded);
|
||||||
|
firstMessage = decoded;
|
||||||
|
settings = {serverless: true};
|
||||||
|
} catch (error) {
|
||||||
|
log("Failed to read message from hash: " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isHost) {
|
if (isHost) {
|
||||||
// From https://stackoverflow.com/a/1349426
|
// From https://stackoverflow.com/a/1349426
|
||||||
function makeid(length) {
|
function makeid(length) {
|
||||||
|
@ -207,15 +296,23 @@ form label {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isServerless()) {
|
||||||
roomName = makeid(8);
|
roomName = makeid(8);
|
||||||
displayQRUrl(withHash(roomName));
|
displayQRUrl(withHash(roomName));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (!isServerless()) {
|
||||||
roomName = roomName.substring(1);
|
roomName = roomName.substring(1);
|
||||||
|
}
|
||||||
qrcodelink.style.display = 'none';
|
qrcodelink.style.display = 'none';
|
||||||
form.style.display = 'none';
|
form.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isServerless()) {
|
||||||
|
log("In serverless mode.");
|
||||||
|
} else {
|
||||||
log("Room: " + roomName);
|
log("Room: " + roomName);
|
||||||
|
}
|
||||||
|
|
||||||
var webSocket = undefined;
|
var webSocket = undefined;
|
||||||
|
|
||||||
|
@ -225,6 +322,10 @@ form label {
|
||||||
const method = dcReady ? "dataConnection" : "webSocket";
|
const method = dcReady ? "dataConnection" : "webSocket";
|
||||||
log("Sending message via " + method + "...");
|
log("Sending message via " + method + "...");
|
||||||
logPre(toSend);
|
logPre(toSend);
|
||||||
|
if (isServerless() && !dcReady) {
|
||||||
|
log("ERROR: Attempted to use webSocket in serverless mode.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
(dcReady ? dc : webSocket).send(toSend);
|
(dcReady ? dc : webSocket).send(toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +334,12 @@ form label {
|
||||||
|
|
||||||
function sendOffer() {
|
function sendOffer() {
|
||||||
if (pc.iceGatheringState == "complete") {
|
if (pc.iceGatheringState == "complete") {
|
||||||
sendJson({
|
const sendFunc = !isServerless() || dc && dc.readyState == "open"
|
||||||
|
? sendJson
|
||||||
|
: isHost
|
||||||
|
? displayQRUrlForObj
|
||||||
|
: displayQRCodeForObj;
|
||||||
|
sendFunc({
|
||||||
description: pc.localDescription
|
description: pc.localDescription
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -284,6 +390,7 @@ form label {
|
||||||
receiveMessage({source: "dataChannel", data: e.data});
|
receiveMessage({source: "dataChannel", data: e.data});
|
||||||
}
|
}
|
||||||
log('Data channel initialized.');
|
log('Data channel initialized.');
|
||||||
|
qrcodeObjContainer.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHost) {
|
if (isHost) {
|
||||||
|
@ -292,6 +399,7 @@ form label {
|
||||||
receiveMessage({source: "dataChannel", data: e.data});
|
receiveMessage({source: "dataChannel", data: e.data});
|
||||||
}
|
}
|
||||||
dc.onopen = e => {
|
dc.onopen = e => {
|
||||||
|
remoteOffer.disabled = true;
|
||||||
log("Data channel open, sending settings...")
|
log("Data channel open, sending settings...")
|
||||||
settings = readSettingsForm(true);
|
settings = readSettingsForm(true);
|
||||||
sendJson({settings});
|
sendJson({settings});
|
||||||
|
@ -414,6 +522,7 @@ form label {
|
||||||
return webSocket;
|
return webSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isServerless()) {
|
||||||
webSocket = createWebSocket();
|
webSocket = createWebSocket();
|
||||||
|
|
||||||
if (!isHost) {
|
if (!isHost) {
|
||||||
|
@ -422,8 +531,23 @@ form label {
|
||||||
// so here just notify the host to start the process.
|
// so here just notify the host to start the process.
|
||||||
webSocket.onopen = _ => sendJson({ready: true});
|
webSocket.onopen = _ => sendJson({ready: true});
|
||||||
}
|
}
|
||||||
|
} else if (isHost) {
|
||||||
|
await getCompressor();
|
||||||
|
pc = createRTCPeerConnection();
|
||||||
|
remoteOffer.value = '';
|
||||||
|
remoteOffer.onchange = _ => {
|
||||||
|
const decoded = b64.base64ToBytes(remoteOffer.value);
|
||||||
|
const decompressed = compressor.decompress(decoded);
|
||||||
|
receiveMessage({source: 'textarea', data: decompressed});
|
||||||
|
}
|
||||||
|
remoteOfferForm.style.display = '';
|
||||||
|
} else {
|
||||||
|
receiveMessage({source: 'URL', data: firstMessage});
|
||||||
|
}
|
||||||
|
|
||||||
log("Finished <script> block.");
|
log("Finished <script> block.");
|
||||||
|
}
|
||||||
|
initialize();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
1
external/LZMA-JS
vendored
Submodule
1
external/LZMA-JS
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8f98fe85a1ef78ab6e9d26ab85ce338f50095a23
|
1
external/base64.js
vendored
Submodule
1
external/base64.js
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 74d3cbf82481545bc26c104de2419f4ee30c7dd7
|
Loading…
Reference in New Issue
Block a user