ljw 1 год назад
Родитель
Сommit
497a24ebd9
100 измененных файлов с 149068 добавлено и 6 удалено
  1. 2 2
      .gitignore
  2. 2 2
      build.bat
  3. 2 2
      build.sh
  4. 1 0
      resources/web/.gitignore
  5. 1 0
      resources/web/.last_build_id
  6. 1 0
      resources/web/assets/AssetManifest.json
  7. 1 0
      resources/web/assets/FontManifest.json
  8. 18580 0
      resources/web/assets/NOTICES
  9. BIN
      resources/web/assets/assets/android.png
  10. BIN
      resources/web/assets/assets/gestures.ttf
  11. BIN
      resources/web/assets/assets/insecure.png
  12. BIN
      resources/web/assets/assets/insecure_relay.png
  13. BIN
      resources/web/assets/assets/linux.png
  14. BIN
      resources/web/assets/assets/mac.png
  15. BIN
      resources/web/assets/assets/secure.png
  16. BIN
      resources/web/assets/assets/secure_relay.png
  17. BIN
      resources/web/assets/assets/win.png
  18. BIN
      resources/web/assets/fonts/MaterialIcons-Regular.otf
  19. BIN
      resources/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf
  20. 230 0
      resources/web/assets/packages/wakelock_web/assets/no_sleep.js
  21. 1 0
      resources/web/favicon.svg
  22. 1195 0
      resources/web/flutter_service_worker.js
  23. BIN
      resources/web/icons/Icon-192.png
  24. BIN
      resources/web/icons/Icon-512.png
  25. BIN
      resources/web/icons/Icon-maskable-192.png
  26. BIN
      resources/web/icons/Icon-maskable-512.png
  27. 208 0
      resources/web/index.html
  28. 1 0
      resources/web/js/.gitattributes
  29. 8 0
      resources/web/js/.gitignore
  30. 1 0
      resources/web/js/.yarnrc.yml
  31. 1 0
      resources/web/js/dist/index.css
  32. 15 0
      resources/web/js/dist/index.html
  33. 22 0
      resources/web/js/dist/index.js
  34. 1 0
      resources/web/js/dist/vendor.js
  35. 77 0
      resources/web/js/gen_js_from_hbb.py
  36. 15 0
      resources/web/js/index.html
  37. 1276 0
      resources/web/js/package-lock.json
  38. 22 0
      resources/web/js/package.json
  39. 43 0
      resources/web/js/src/codec.js
  40. 77 0
      resources/web/js/src/common.ts
  41. 774 0
      resources/web/js/src/connection.ts
  42. 4101 0
      resources/web/js/src/gen_js_from_hbb.ts
  43. 383 0
      resources/web/js/src/globals.js
  44. 2 0
      resources/web/js/src/main.ts
  45. 6073 0
      resources/web/js/src/message.ts
  46. 2025 0
      resources/web/js/src/rendezvous.ts
  47. 8 0
      resources/web/js/src/style.css
  48. 108 0
      resources/web/js/src/ui.js
  49. 1 0
      resources/web/js/src/vite-env.d.ts
  50. 183 0
      resources/web/js/src/websock.ts
  51. 20 0
      resources/web/js/ts_proto.py
  52. 24 0
      resources/web/js/tsconfig.json
  53. 14 0
      resources/web/js/vite.config.js
  54. 300 0
      resources/web/js/yarn.lock
  55. 5555 0
      resources/web/libopus.js
  56. BIN
      resources/web/libopus.wasm
  57. 2 0
      resources/web/libs/firebase-analytics.js
  58. 2 0
      resources/web/libs/firebase-app.js
  59. 106639 0
      resources/web/main.dart.js
  60. 35 0
      resources/web/manifest.json
  61. 21 0
      resources/web/ogvjs-1.8.6/COPYING
  62. 23 0
      resources/web/ogvjs-1.8.6/COPYING-dav1d.txt
  63. 28 0
      resources/web/ogvjs-1.8.6/COPYING-ogg.txt
  64. 44 0
      resources/web/ogvjs-1.8.6/COPYING-opus.txt
  65. 28 0
      resources/web/ogvjs-1.8.6/COPYING-theora.txt
  66. 28 0
      resources/web/ogvjs-1.8.6/COPYING-vorbis.txt
  67. 13 0
      resources/web/ogvjs-1.8.6/LICENSE-nestegg.txt
  68. 31 0
      resources/web/ogvjs-1.8.6/LICENSE-vpx.txt
  69. 23 0
      resources/web/ogvjs-1.8.6/PATENTS-vpx.txt
  70. 391 0
      resources/web/ogvjs-1.8.6/README.md
  71. 39 0
      resources/web/ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.js
  72. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.wasm
  73. 40 0
      resources/web/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js
  74. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.wasm
  75. 21 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js
  76. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.wasm
  77. 1 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js
  78. 21 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js
  79. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.wasm
  80. 1 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.worker.js
  81. 43 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.js
  82. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.wasm
  83. 43 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-wasm.js
  84. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-wasm.wasm
  85. 42 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-theora-wasm.js
  86. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-theora-wasm.wasm
  87. 21 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.js
  88. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.wasm
  89. 1 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.worker.js
  90. 44 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.js
  91. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.wasm
  92. 21 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.js
  93. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.wasm
  94. 1 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.worker.js
  95. 21 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.js
  96. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.wasm
  97. 1 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.worker.js
  98. 45 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.js
  99. BIN
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.wasm
  100. 0 0
      resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.js

+ 2 - 2
.gitignore

@@ -1,8 +1,8 @@
1 1
 .idea
2
-runtime
2
+runtime/*
3 3
 !runtime/cache/.gitignore
4 4
 go.sum
5
-resources
5
+resources/*
6 6
 !resources/public/upload/.gitignore
7 7
 !resources/web
8 8
 release

+ 2 - 2
build.bat

@@ -9,6 +9,6 @@ swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude h
9 9
 go build -o release/apimain.exe cmd/apimain.go
10 10
 xcopy resources release\resources /E /I /Y
11 11
 xcopy docs release\docs /E /I /Y
12
-xcopy data release\data /E /I /Y
13 12
 xcopy conf release\conf /E /I /Y
14
-xcopy runtime release\runtime /E /I /Y
13
+mkdir release\runtime
14
+mkdir release\data

+ 2 - 2
build.sh

@@ -10,6 +10,6 @@ swag init -g cmd/apimain.go --output docs/admin --instanceName admin --exclude h
10 10
 go build -o release/apimain cmd/apimain.go
11 11
 cp -ar resources release/
12 12
 cp -ar docs release/
13
-cp -ar data release/
14 13
 cp -ar conf release/
15
-cp -ar runtime release/
14
+mkdir release/data
15
+mkdir release/runtime

+ 1 - 0
resources/web/.gitignore

@@ -0,0 +1 @@
1
+node_modules

+ 1 - 0
resources/web/.last_build_id

@@ -0,0 +1 @@
1
+c72c6e6149056c379540743392607228

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/assets/AssetManifest.json


+ 1 - 0
resources/web/assets/FontManifest.json

@@ -0,0 +1 @@
1
+[{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"GestureIcons","fonts":[{"asset":"assets/gestures.ttf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}]

Разница между файлами не показана из-за своего большого размера
+ 18580 - 0
resources/web/assets/NOTICES


BIN
resources/web/assets/assets/android.png


BIN
resources/web/assets/assets/gestures.ttf


BIN
resources/web/assets/assets/insecure.png


BIN
resources/web/assets/assets/insecure_relay.png


BIN
resources/web/assets/assets/linux.png


BIN
resources/web/assets/assets/mac.png


BIN
resources/web/assets/assets/secure.png


BIN
resources/web/assets/assets/secure_relay.png


BIN
resources/web/assets/assets/win.png


BIN
resources/web/assets/fonts/MaterialIcons-Regular.otf


BIN
resources/web/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf


Разница между файлами не показана из-за своего большого размера
+ 230 - 0
resources/web/assets/packages/wakelock_web/assets/no_sleep.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/favicon.svg


Разница между файлами не показана из-за своего большого размера
+ 1195 - 0
resources/web/flutter_service_worker.js


BIN
resources/web/icons/Icon-192.png


BIN
resources/web/icons/Icon-512.png


BIN
resources/web/icons/Icon-maskable-192.png


BIN
resources/web/icons/Icon-maskable-512.png


Разница между файлами не показана из-за своего большого размера
+ 208 - 0
resources/web/index.html


+ 1 - 0
resources/web/js/.gitattributes

@@ -0,0 +1 @@
1
+* text=auto

+ 8 - 0
resources/web/js/.gitignore

@@ -0,0 +1,8 @@
1
+node_modules
2
+.DS_Store
3
+dist-ssr
4
+*.local
5
+*log
6
+ogvjs
7
+.vscode
8
+.yarn

+ 1 - 0
resources/web/js/.yarnrc.yml

@@ -0,0 +1 @@
1
+nodeLinker: node-modules

+ 1 - 0
resources/web/js/dist/index.css

@@ -0,0 +1 @@
1
+#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px}

+ 15 - 0
resources/web/js/dist/index.html

@@ -0,0 +1,15 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <link rel="icon" type="image/svg+xml" href="favicon.svg?v2" />
6
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+    <script src="ogvjs-1.8.6/ogv.js"></script>
8
+    <script src="./yuv-canvas-1.2.6.js"></script>
9
+    <title>Vite App</title>
10
+  </head>
11
+  <body>
12
+    <div id="app"></div>
13
+
14
+  </body>
15
+</html>

Разница между файлами не показана из-за своего большого размера
+ 22 - 0
resources/web/js/dist/index.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/js/dist/vendor.js


+ 77 - 0
resources/web/js/gen_js_from_hbb.py

@@ -0,0 +1,77 @@
1
+#!/usr/bin/env python3
2
+
3
+import re 
4
+import os
5
+import glob
6
+from tabnanny import check
7
+
8
+def pad_start(s, n, c = ' '):
9
+   if len(s) >= n:
10
+      return s
11
+   return c * (n - len(s)) + s
12
+
13
+def safe_unicode(s):
14
+   res = ""
15
+   for c in s:
16
+      res += r"\u{}".format(pad_start(hex(ord(c))[2:], 4, '0'))
17
+   return res
18
+
19
+def main():
20
+   print('export const LANGS = {')
21
+   for fn in glob.glob('../../../src/lang/*'):
22
+      lang = os.path.basename(fn)[:-3]
23
+      if lang == 'template': continue
24
+      print('  %s: {'%lang)
25
+      for ln in open(fn, encoding='utf-8'):
26
+         ln = ln.strip()
27
+         if ln.startswith('("'):
28
+            toks = ln.split('", "')
29
+            assert(len(toks) == 2)
30
+            a = toks[0][2:]
31
+            b = toks[1][:-3]
32
+            print('    "%s": "%s",'%(safe_unicode(a), safe_unicode(b)))
33
+      print('  },')
34
+   print('}')
35
+   check_if_retry = ['', False]
36
+   KEY_MAP = ['', False]
37
+   for ln in open('../../../src/client.rs', encoding='utf-8'):
38
+      ln = ln.strip()
39
+      if 'check_if_retry' in ln:
40
+         check_if_retry[1] = True
41
+         continue
42
+      if ln.startswith('}') and check_if_retry[1]:
43
+         check_if_retry[1] = False
44
+         continue
45
+      if check_if_retry[1]:
46
+         ln = removeComment(ln)
47
+         check_if_retry[0] += ln + '\n'
48
+      if 'KEY_MAP' in ln:
49
+         KEY_MAP[1] = True
50
+         continue
51
+      if '.collect' in ln and KEY_MAP[1]:
52
+         KEY_MAP[1] = False
53
+         continue
54
+      if KEY_MAP[1] and ln.startswith('('):
55
+         ln = removeComment(ln)
56
+         toks = ln.split('", Key::')
57
+         assert(len(toks) == 2)
58
+         a = toks[0][2:]
59
+         b = toks[1].replace('ControlKey(ControlKey::', '').replace("Chr('", '').replace("' as _)),", '').replace(')),', '')
60
+         KEY_MAP[0] += '  "%s": "%s",\n'%(a, b)
61
+   print()
62
+   print('export function checkIfRetry(msgtype: string, title: string, text: string) {')
63
+   print('  return %s'%check_if_retry[0].replace('to_lowercase', 'toLowerCase').replace('contains', 'indexOf').replace('!', '').replace('")', '") < 0'))
64
+   print(';}')
65
+   print()
66
+   print('export const KEY_MAP: any = {')
67
+   print(KEY_MAP[0])
68
+   print('}')
69
+   for ln in open('../../../Cargo.toml', encoding='utf-8'):
70
+      if ln.startswith('version ='):
71
+         print('export const ' + ln)
72
+      
73
+
74
+def removeComment(ln):
75
+   return re.sub('\s+\/\/.*$', '', ln)
76
+
77
+main()

+ 15 - 0
resources/web/js/index.html

@@ -0,0 +1,15 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <link rel="icon" type="image/svg+xml" href="favicon.svg?v2" />
6
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+    <script src="ogvjs-1.8.6/ogv.js"></script>
8
+    <script src="./yuv-canvas-1.2.6.js"></script>
9
+    <title>Vite App</title>
10
+  </head>
11
+  <body>
12
+    <div id="app"></div>
13
+    <script type="module" src="/src/main.ts"></script>
14
+  </body>
15
+</html>

Разница между файлами не показана из-за своего большого размера
+ 1276 - 0
resources/web/js/package-lock.json


+ 22 - 0
resources/web/js/package.json

@@ -0,0 +1,22 @@
1
+{
2
+  "name": "web_hbb",
3
+  "version": "1.0.0",
4
+  "scripts": {
5
+    "dev": "vite",
6
+    "build": "python ./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && python ./ts_proto.py && tsc && yarn vite build",
7
+    "preview": "vite preview"
8
+  },
9
+  "devDependencies": {
10
+    "typescript": "4.4.4",
11
+    "vite": "2.8"
12
+  },
13
+  "dependencies": {
14
+    "fast-sha256": "^1.3.0",
15
+    "libsodium": "^0.7.9",
16
+    "libsodium-wrappers": "^0.7.9",
17
+    "pcm-player": "^0.0.11",
18
+    "ts-proto": "^1.141.1",
19
+    "wasm-feature-detect": "^1.2.11",
20
+    "zstddec": "^0.0.2"
21
+  }
22
+}

+ 43 - 0
resources/web/js/src/codec.js

@@ -0,0 +1,43 @@
1
+// example: https://github.com/rgov/js-theora-decoder/blob/main/index.html
2
+// https://github.com/brion/ogv.js/releases, yarn add has no simd
3
+// dev: copy decoder files from node/ogv/dist/* to project dir
4
+// dist: .... to dist
5
+/*
6
+  OGVDemuxerOggW: 'ogv-demuxer-ogg-wasm.js',
7
+  OGVDemuxerWebMW: 'ogv-demuxer-webm-wasm.js',
8
+  OGVDecoderAudioOpusW: 'ogv-decoder-audio-opus-wasm.js',
9
+  OGVDecoderAudioVorbisW: 'ogv-decoder-audio-vorbis-wasm.js',
10
+  OGVDecoderVideoTheoraW: 'ogv-decoder-video-theora-wasm.js',
11
+  OGVDecoderVideoVP8W: 'ogv-decoder-video-vp8-wasm.js',
12
+  OGVDecoderVideoVP8MTW: 'ogv-decoder-video-vp8-mt-wasm.js',
13
+  OGVDecoderVideoVP9W: 'ogv-decoder-video-vp9-wasm.js',
14
+  OGVDecoderVideoVP9SIMDW: 'ogv-decoder-video-vp9-simd-wasm.js',
15
+  OGVDecoderVideoVP9MTW: 'ogv-decoder-video-vp9-mt-wasm.js',
16
+  OGVDecoderVideoVP9SIMDMTW: 'ogv-decoder-video-vp9-simd-mt-wasm.js',
17
+  OGVDecoderVideoAV1W: 'ogv-decoder-video-av1-wasm.js',
18
+  OGVDecoderVideoAV1SIMDW: 'ogv-decoder-video-av1-simd-wasm.js',
19
+  OGVDecoderVideoAV1MTW: 'ogv-decoder-video-av1-mt-wasm.js',
20
+  OGVDecoderVideoAV1SIMDMTW: 'ogv-decoder-video-av1-simd-mt-wasm.js',
21
+*/
22
+import { simd } from "wasm-feature-detect";
23
+
24
+export async function loadVp9(callback) {
25
+  // Multithreading is used only if `options.threading` is true. 
26
+  // This requires browser support for the new `SharedArrayBuffer` and `Atomics` APIs, 
27
+  // currently available in Firefox and Chrome with experimental flags enabled.
28
+  // 所有主流浏览器均默认于2018年1月5日禁用SharedArrayBuffer
29
+  const isSIMD = await simd();
30
+  console.log('isSIMD: ' + isSIMD);
31
+  window.OGVLoader.loadClass(
32
+    isSIMD ? "OGVDecoderVideoVP9SIMDW" : "OGVDecoderVideoVP9W",
33
+    (videoCodecClass) => {
34
+      window.videoCodecClass = videoCodecClass;
35
+      videoCodecClass({ videoFormat: {} }).then((decoder) => {
36
+        decoder.init(() => {
37
+          callback(decoder);
38
+        })
39
+      })
40
+    },
41
+    { worker: true, threading: true }
42
+  );
43
+}

+ 77 - 0
resources/web/js/src/common.ts

@@ -0,0 +1,77 @@
1
+import * as zstd from "zstddec";
2
+import { KeyEvent, controlKeyFromJSON, ControlKey } from "./message";
3
+import { KEY_MAP, LANGS } from "./gen_js_from_hbb";
4
+
5
+let decompressor: zstd.ZSTDDecoder;
6
+
7
+export async function initZstd() {
8
+  const tmp = new zstd.ZSTDDecoder();
9
+  await tmp.init();
10
+  console.log("zstd ready");
11
+  decompressor = tmp;
12
+}
13
+
14
+export async function decompress(compressedArray: Uint8Array) {
15
+  const MAX = 1024 * 1024 * 64;
16
+  const MIN = 1024 * 1024;
17
+  let n = 30 * compressedArray.length;
18
+  if (n > MAX) {
19
+    n = MAX;
20
+  }
21
+  if (n < MIN) {
22
+    n = MIN;
23
+  }
24
+  try {
25
+    if (!decompressor) {
26
+      await initZstd();
27
+    }
28
+    return decompressor.decode(compressedArray, n);
29
+  } catch (e) {
30
+    console.error("decompress failed: " + e);
31
+    return undefined;
32
+  }
33
+}
34
+
35
+const LANG = getLang();
36
+
37
+export function translate(locale: string, text: string): string {
38
+  const lang = LANG || locale.substring(locale.length - 2).toLowerCase();
39
+  let en = LANGS.en as any;
40
+  let dict = (LANGS as any)[lang];
41
+  if (!dict) dict = en;
42
+  let res = dict[text];
43
+  if (!res && lang != "en") res = en[text];
44
+  return res || text;
45
+}
46
+
47
+const zCode = "z".charCodeAt(0);
48
+const aCode = "a".charCodeAt(0);
49
+
50
+export function mapKey(name: string, isDesktop: Boolean) {
51
+  const tmp = KEY_MAP[name] || name;
52
+  if (tmp.length == 1) {
53
+    const chr = tmp.charCodeAt(0);
54
+    if (!isDesktop && (chr > zCode || chr < aCode))
55
+      return KeyEvent.fromPartial({ unicode: chr });
56
+    else return KeyEvent.fromPartial({ chr });
57
+  }
58
+  const control_key = controlKeyFromJSON(tmp);
59
+  if (control_key == ControlKey.UNRECOGNIZED) {
60
+    console.error("Unknown control key " + tmp);
61
+  }
62
+  return KeyEvent.fromPartial({ control_key });
63
+}
64
+
65
+export async function sleep(ms: number) {
66
+  await new Promise((r) => setTimeout(r, ms));
67
+}
68
+
69
+function getLang(): string {
70
+  try {
71
+    const queryString = window.location.search;
72
+    const urlParams = new URLSearchParams(queryString);
73
+    return urlParams.get("lang") || "";
74
+  } catch (e) {
75
+    return "";
76
+  }
77
+}

+ 774 - 0
resources/web/js/src/connection.ts

@@ -0,0 +1,774 @@
1
+import Websock from "./websock";
2
+import * as message from "./message.js";
3
+import * as rendezvous from "./rendezvous.js";
4
+import { loadVp9 } from "./codec";
5
+import * as sha256 from "fast-sha256";
6
+import * as globals from "./globals";
7
+import { decompress, mapKey, sleep } from "./common";
8
+
9
+const PORT = 21116;
10
+// only the first is used to init `HOST`
11
+const HOSTS = [
12
+  "rs-sg.rustdesk.com",
13
+  "rs-cn.rustdesk.com",
14
+  "rs-us.rustdesk.com",
15
+];
16
+let HOST = localStorage.getItem("rendezvous-server") || HOSTS[0];
17
+const SCHEMA = "ws://";
18
+
19
+type MsgboxCallback = (type: string, title: string, text: string) => void;
20
+type DrawCallback = (data: Uint8Array) => void;
21
+//const cursorCanvas = document.createElement("canvas");
22
+
23
+export default class Connection {
24
+  _msgs: any[];
25
+  _ws: Websock | undefined;
26
+  _interval: any;
27
+  _id: string;
28
+  _hash: message.Hash | undefined;
29
+  _msgbox: MsgboxCallback;
30
+  _draw: DrawCallback;
31
+  _peerInfo: message.PeerInfo | undefined;
32
+  _firstFrame: Boolean | undefined;
33
+  _videoDecoder: any;
34
+  _password: Uint8Array | undefined;
35
+  _options: any;
36
+  _videoTestSpeed: number[];
37
+  //_cursors: { [name: number]: any };
38
+
39
+  constructor() {
40
+    this._msgbox = globals.msgbox;
41
+    this._draw = globals.draw;
42
+    this._msgs = [];
43
+    this._id = "";
44
+    this._videoTestSpeed = [0, 0];
45
+    //this._cursors = {};
46
+  }
47
+
48
+  async start(id: string) {
49
+    try {
50
+      await this._start(id);
51
+    } catch (e: any) {
52
+      this.msgbox(
53
+        "error",
54
+        "Connection Error",
55
+        e.type == "close" ? "Reset by the peer" : String(e)
56
+      );
57
+    }
58
+  }
59
+
60
+  async _start(id: string) {
61
+    if (!this._options) {
62
+      this._options = globals.getPeers()[id] || {};
63
+    }
64
+    if (!this._password) {
65
+      const p = this.getOption("password");
66
+      if (p) {
67
+        try {
68
+          this._password = Uint8Array.from(JSON.parse("[" + p + "]"));
69
+        } catch (e) {
70
+          console.error(e);
71
+        }
72
+      }
73
+    }
74
+    this._interval = setInterval(() => {
75
+      while (this._msgs.length) {
76
+        this._ws?.sendMessage(this._msgs[0]);
77
+        this._msgs.splice(0, 1);
78
+      }
79
+    }, 1);
80
+    this.loadVideoDecoder();
81
+    const uri = getDefaultUri();
82
+    const ws = new Websock(uri, true);
83
+    this._ws = ws;
84
+    this._id = id;
85
+    console.log(
86
+      new Date() + ": Conntecting to rendezvoous server: " + uri + ", for " + id
87
+    );
88
+    await ws.open();
89
+    console.log(new Date() + ": Connected to rendezvoous server");
90
+    const conn_type = rendezvous.ConnType.DEFAULT_CONN;
91
+    const nat_type = rendezvous.NatType.SYMMETRIC;
92
+    const punch_hole_request = rendezvous.PunchHoleRequest.fromPartial({
93
+      id,
94
+      licence_key: localStorage.getItem("key") || undefined,
95
+      conn_type,
96
+      nat_type,
97
+      token: localStorage.getItem("access_token") || undefined,
98
+    });
99
+    ws.sendRendezvous({ punch_hole_request });
100
+    const msg = (await ws.next()) as rendezvous.RendezvousMessage;
101
+    ws.close();
102
+    console.log(new Date() + ": Got relay response");
103
+    const phr = msg.punch_hole_response;
104
+    const rr = msg.relay_response;
105
+    if (phr) {
106
+      if (phr?.other_failure) {
107
+        this.msgbox("error", "Error", phr?.other_failure);
108
+        return;
109
+      }
110
+      if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNRECOGNIZED) {
111
+        switch (phr?.failure) {
112
+          case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST:
113
+            this.msgbox("error", "Error", "ID does not exist");
114
+            break;
115
+          case rendezvous.PunchHoleResponse_Failure.OFFLINE:
116
+            this.msgbox("error", "Error", "Remote desktop is offline");
117
+            break;
118
+          case rendezvous.PunchHoleResponse_Failure.LICENSE_MISMATCH:
119
+            this.msgbox("error", "Error", "Key mismatch");
120
+            break;
121
+          case rendezvous.PunchHoleResponse_Failure.LICENSE_OVERUSE:
122
+            this.msgbox("error", "Error", "Key overuse");
123
+            break;
124
+        }
125
+      }
126
+    } else if (rr) {
127
+      if (!rr.version) {
128
+        this.msgbox("error", "Error", "Remote version is low, not support web");
129
+        return;
130
+      }
131
+      await this.connectRelay(rr);
132
+    }
133
+  }
134
+
135
+  async connectRelay(rr: rendezvous.RelayResponse) {
136
+    const pk = rr.pk;
137
+    let uri = rr.relay_server;
138
+    if (uri) {
139
+      uri = getrUriFromRs(uri, true, 2);
140
+    } else {
141
+      uri = getDefaultUri(true);
142
+    }
143
+    const uuid = rr.uuid;
144
+    console.log(new Date() + ": Connecting to relay server: " + uri);
145
+    const ws = new Websock(uri, false);
146
+    await ws.open();
147
+    console.log(new Date() + ": Connected to relay server");
148
+    this._ws = ws;
149
+    const request_relay = rendezvous.RequestRelay.fromPartial({
150
+      licence_key: localStorage.getItem("key") || undefined,
151
+      uuid,
152
+    });
153
+    ws.sendRendezvous({ request_relay });
154
+    const secure = (await this.secure(pk)) || false;
155
+    globals.pushEvent("connection_ready", { secure, direct: false });
156
+    await this.msgLoop();
157
+  }
158
+
159
+  async secure(pk: Uint8Array | undefined) {
160
+    if (pk) {
161
+      const RS_PK = "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=";
162
+      try {
163
+        pk = await globals.verify(pk, localStorage.getItem("key") || RS_PK);
164
+        if (pk) {
165
+          const idpk = message.IdPk.decode(pk);
166
+          if (idpk.id == this._id) {
167
+            pk = idpk.pk;
168
+          }
169
+        }
170
+        if (pk?.length != 32) {
171
+          pk = undefined;
172
+        }
173
+      } catch (e) {
174
+        console.error(e);
175
+        pk = undefined;
176
+      }
177
+      if (!pk)
178
+        console.error(
179
+          "Handshake failed: invalid public key from rendezvous server"
180
+        );
181
+    }
182
+    if (!pk) {
183
+      // send an empty message out in case server is setting up secure and waiting for first message
184
+      const public_key = message.PublicKey.fromPartial({});
185
+      this._ws?.sendMessage({ public_key });
186
+      return;
187
+    }
188
+    const msg = (await this._ws?.next()) as message.Message;
189
+    let signedId: any = msg?.signed_id;
190
+    if (!signedId) {
191
+      console.error("Handshake failed: invalid message type");
192
+      const public_key = message.PublicKey.fromPartial({});
193
+      this._ws?.sendMessage({ public_key });
194
+      return;
195
+    }
196
+    try {
197
+      signedId = await globals.verify(signedId.id, Uint8Array.from(pk!));
198
+    } catch (e) {
199
+      console.error(e);
200
+      // fall back to non-secure connection in case pk mismatch
201
+      console.error("pk mismatch, fall back to non-secure");
202
+      const public_key = message.PublicKey.fromPartial({});
203
+      this._ws?.sendMessage({ public_key });
204
+      return;
205
+    }
206
+    const idpk = message.IdPk.decode(signedId);
207
+    const id = idpk.id;
208
+    const theirPk = idpk.pk;
209
+    if (id != this._id!) {
210
+      console.error("Handshake failed: sign failure");
211
+      const public_key = message.PublicKey.fromPartial({});
212
+      this._ws?.sendMessage({ public_key });
213
+      return;
214
+    }
215
+    if (theirPk.length != 32) {
216
+      console.error(
217
+        "Handshake failed: invalid public box key length from peer"
218
+      );
219
+      const public_key = message.PublicKey.fromPartial({});
220
+      this._ws?.sendMessage({ public_key });
221
+      return;
222
+    }
223
+    const [mySk, asymmetric_value] = globals.genBoxKeyPair();
224
+    const secret_key = globals.genSecretKey();
225
+    const symmetric_value = globals.seal(secret_key, theirPk, mySk);
226
+    const public_key = message.PublicKey.fromPartial({
227
+      asymmetric_value,
228
+      symmetric_value,
229
+    });
230
+    this._ws?.sendMessage({ public_key });
231
+    this._ws?.setSecretKey(secret_key);
232
+    console.log("secured");
233
+    return true;
234
+  }
235
+
236
+  async msgLoop() {
237
+    while (true) {
238
+      const msg = (await this._ws?.next()) as message.Message;
239
+      if (msg?.hash) {
240
+        this._hash = msg?.hash;
241
+        if (!this._password)
242
+          this.msgbox("input-password", "Password Required", "");
243
+        this.login();
244
+      } else if (msg?.test_delay) {
245
+        const test_delay = msg?.test_delay;
246
+        console.log(test_delay);
247
+        if (!test_delay.from_client) {
248
+          this._ws?.sendMessage({ test_delay });
249
+        }
250
+      } else if (msg?.login_response) {
251
+        const r = msg?.login_response;
252
+        if (r.error) {
253
+          if (r.error == "Wrong Password") {
254
+            this._password = undefined;
255
+            this.msgbox(
256
+              "re-input-password",
257
+              r.error,
258
+              "Do you want to enter again?"
259
+            );
260
+          } else {
261
+            this.msgbox("error", "Login Error", r.error);
262
+          }
263
+        } else if (r.peer_info) {
264
+          this.handlePeerInfo(r.peer_info);
265
+        }
266
+      } else if (msg?.video_frame) {
267
+        this.handleVideoFrame(msg?.video_frame!);
268
+      } else if (msg?.clipboard) {
269
+        const cb = msg?.clipboard;
270
+        if (cb.compress) {
271
+          const c = await decompress(cb.content);
272
+          if (!c) continue;
273
+          cb.content = c;
274
+        }
275
+        try {
276
+          globals.copyToClipboard(new TextDecoder().decode(cb.content));
277
+        } catch (e) {
278
+          console.error(e);
279
+        }
280
+        // globals.pushEvent("clipboard", cb);
281
+      } else if (msg?.cursor_data) {
282
+        const cd = msg?.cursor_data;
283
+        const c = await decompress(cd.colors);
284
+        if (!c) continue;
285
+        cd.colors = c;
286
+        globals.pushEvent("cursor_data", cd);
287
+        /*
288
+        let ctx = cursorCanvas.getContext("2d");
289
+        cursorCanvas.width = cd.width;
290
+        cursorCanvas.height = cd.height;
291
+        let imgData = new ImageData(
292
+          new Uint8ClampedArray(c),
293
+          cd.width,
294
+          cd.height
295
+        );
296
+        ctx?.clearRect(0, 0, cd.width, cd.height);
297
+        ctx?.putImageData(imgData, 0, 0);
298
+        let url = cursorCanvas.toDataURL();
299
+        const img = document.createElement("img");
300
+        img.src = url;
301
+        this._cursors[cd.id] = img;
302
+        //cursorCanvas.width /= 2.;
303
+        //cursorCanvas.height /= 2.;
304
+        //ctx?.drawImage(img, cursorCanvas.width, cursorCanvas.height);
305
+        url = cursorCanvas.toDataURL();
306
+        document.body.style.cursor =
307
+          "url(" + url + ")" + cd.hotx + " " + cd.hoty + ", default";
308
+        console.log(document.body.style.cursor);
309
+        */
310
+      } else if (msg?.cursor_id) {
311
+        globals.pushEvent("cursor_id", { id: msg?.cursor_id });
312
+      } else if (msg?.cursor_position) {
313
+        globals.pushEvent("cursor_position", msg?.cursor_position);
314
+      } else if (msg?.misc) {
315
+        if (!this.handleMisc(msg?.misc)) break;
316
+      } else if (msg?.audio_frame) {
317
+        globals.playAudio(msg?.audio_frame.data);
318
+      }
319
+    }
320
+  }
321
+
322
+  msgbox(type_: string, title: string, text: string) {
323
+    this._msgbox?.(type_, title, text);
324
+  }
325
+
326
+  draw(frame: any) {
327
+    this._draw?.(frame);
328
+    globals.draw(frame);
329
+  }
330
+
331
+  close() {
332
+    this._msgs = [];
333
+    clearInterval(this._interval);
334
+    this._ws?.close();
335
+    this._videoDecoder?.close();
336
+  }
337
+
338
+  refresh() {
339
+    const misc = message.Misc.fromPartial({ refresh_video: true });
340
+    this._ws?.sendMessage({ misc });
341
+  }
342
+
343
+  setMsgbox(callback: MsgboxCallback) {
344
+    this._msgbox = callback;
345
+  }
346
+
347
+  setDraw(callback: DrawCallback) {
348
+    this._draw = callback;
349
+  }
350
+
351
+  login(password: string | undefined = undefined) {
352
+    if (password) {
353
+      const salt = this._hash?.salt;
354
+      let p = hash([password, salt!]);
355
+      this._password = p;
356
+      const challenge = this._hash?.challenge;
357
+      p = hash([p, challenge!]);
358
+      this.msgbox("connecting", "Connecting...", "Logging in...");
359
+      this._sendLoginMessage(p);
360
+    } else {
361
+      let p = this._password;
362
+      if (p) {
363
+        const challenge = this._hash?.challenge;
364
+        p = hash([p, challenge!]);
365
+      }
366
+      this._sendLoginMessage(p);
367
+    }
368
+  }
369
+
370
+  async reconnect() {
371
+    this.close();
372
+    await this.start(this._id);
373
+  }
374
+
375
+  _sendLoginMessage(password: Uint8Array | undefined = undefined) {
376
+    const login_request = message.LoginRequest.fromPartial({
377
+      username: this._id!,
378
+      my_id: "web", // to-do
379
+      my_name: "web", // to-do
380
+      password,
381
+      option: this.getOptionMessage(),
382
+      video_ack_required: true,
383
+    });
384
+    this._ws?.sendMessage({ login_request });
385
+  }
386
+
387
+  getOptionMessage(): message.OptionMessage | undefined {
388
+    let n = 0;
389
+    const msg = message.OptionMessage.fromPartial({});
390
+    const q = this.getImageQualityEnum(this.getImageQuality(), true);
391
+    const yes = message.OptionMessage_BoolOption.Yes;
392
+    if (q != undefined) {
393
+      msg.image_quality = q;
394
+      n += 1;
395
+    }
396
+    if (this._options["show-remote-cursor"]) {
397
+      msg.show_remote_cursor = yes;
398
+      n += 1;
399
+    }
400
+    if (this._options["lock-after-session-end"]) {
401
+      msg.lock_after_session_end = yes;
402
+      n += 1;
403
+    }
404
+    if (this._options["privacy-mode"]) {
405
+      msg.privacy_mode = yes;
406
+      n += 1;
407
+    }
408
+    if (this._options["disable-audio"]) {
409
+      msg.disable_audio = yes;
410
+      n += 1;
411
+    }
412
+    if (this._options["disable-clipboard"]) {
413
+      msg.disable_clipboard = yes;
414
+      n += 1;
415
+    }
416
+    return n > 0 ? msg : undefined;
417
+  }
418
+
419
+  sendVideoReceived() {
420
+    const misc = message.Misc.fromPartial({ video_received: true });
421
+    this._ws?.sendMessage({ misc });
422
+  }
423
+
424
+  handleVideoFrame(vf: message.VideoFrame) {
425
+    if (!this._firstFrame) {
426
+      this.msgbox("", "", "");
427
+      this._firstFrame = true;
428
+    }
429
+    if (vf.vp9s) {
430
+      const dec = this._videoDecoder;
431
+      var tm = new Date().getTime();
432
+      var i = 0;
433
+      const n = vf.vp9s?.frames.length;
434
+      vf.vp9s.frames.forEach((f) => {
435
+        dec.processFrame(f.data.slice(0).buffer, (ok: any) => {
436
+          i++;
437
+          if (i == n) this.sendVideoReceived();
438
+          if (ok && dec.frameBuffer && n == i) {
439
+            this.draw(dec.frameBuffer);
440
+            const now = new Date().getTime();
441
+            var elapsed = now - tm;
442
+            this._videoTestSpeed[1] += elapsed;
443
+            this._videoTestSpeed[0] += 1;
444
+            if (this._videoTestSpeed[0] >= 30) {
445
+              console.log(
446
+                "video decoder: " +
447
+                  parseInt(
448
+                    "" + this._videoTestSpeed[1] / this._videoTestSpeed[0]
449
+                  )
450
+              );
451
+              this._videoTestSpeed = [0, 0];
452
+            }
453
+          }
454
+        });
455
+      });
456
+    }
457
+  }
458
+
459
+  handlePeerInfo(pi: message.PeerInfo) {
460
+    this._peerInfo = pi;
461
+    if (pi.displays.length == 0) {
462
+      this.msgbox("error", "Remote Error", "No Display");
463
+      return;
464
+    }
465
+    this.msgbox("success", "Successful", "Connected, waiting for image...");
466
+    globals.pushEvent("peer_info", pi);
467
+    const p = this.shouldAutoLogin();
468
+    if (p) this.inputOsPassword(p);
469
+    const username = this.getOption("info")?.username;
470
+    if (username && !pi.username) pi.username = username;
471
+    this.setOption("info", pi);
472
+    if (this.getRemember()) {
473
+      if (this._password?.length) {
474
+        const p = this._password.toString();
475
+        if (p != this.getOption("password")) {
476
+          this.setOption("password", p);
477
+          console.log("remember password of " + this._id);
478
+        }
479
+      }
480
+    } else {
481
+      this.setOption("password", undefined);
482
+    }
483
+  }
484
+
485
+  shouldAutoLogin(): string {
486
+    const l = this.getOption("lock-after-session-end");
487
+    const a = !!this.getOption("auto-login");
488
+    const p = this.getOption("os-password");
489
+    if (p && l && a) {
490
+      return p;
491
+    }
492
+    return "";
493
+  }
494
+
495
+  handleMisc(misc: message.Misc) {
496
+    if (misc.audio_format) {
497
+      globals.initAudio(
498
+        misc.audio_format.channels,
499
+        misc.audio_format.sample_rate
500
+      );
501
+    } else if (misc.chat_message) {
502
+      globals.pushEvent("chat", { text: misc.chat_message.text });
503
+    } else if (misc.permission_info) {
504
+      const p = misc.permission_info;
505
+      console.info("Change permission " + p.permission + " -> " + p.enabled);
506
+      let name;
507
+      switch (p.permission) {
508
+        case message.PermissionInfo_Permission.Keyboard:
509
+          name = "keyboard";
510
+          break;
511
+        case message.PermissionInfo_Permission.Clipboard:
512
+          name = "clipboard";
513
+          break;
514
+        case message.PermissionInfo_Permission.Audio:
515
+          name = "audio";
516
+          break;
517
+        default:
518
+          return;
519
+      }
520
+      globals.pushEvent("permission", { [name]: p.enabled });
521
+    } else if (misc.switch_display) {
522
+      this.loadVideoDecoder();
523
+      globals.pushEvent("switch_display", misc.switch_display);
524
+    } else if (misc.close_reason) {
525
+      this.msgbox("error", "Connection Error", misc.close_reason);
526
+      this.close();
527
+      return false;
528
+    }
529
+    return true;
530
+  }
531
+
532
+  getRemember(): Boolean {
533
+    return this._options["remember"] || false;
534
+  }
535
+
536
+  setRemember(v: Boolean) {
537
+    this.setOption("remember", v);
538
+  }
539
+
540
+  getOption(name: string): any {
541
+    return this._options[name];
542
+  }
543
+
544
+  setOption(name: string, value: any) {
545
+    if (value == undefined) {
546
+      delete this._options[name];
547
+    } else {
548
+      this._options[name] = value;
549
+    }
550
+    this._options["tm"] = new Date().getTime();
551
+    const peers = globals.getPeers();
552
+    peers[this._id] = this._options;
553
+    localStorage.setItem("peers", JSON.stringify(peers));
554
+  }
555
+
556
+  inputKey(
557
+    name: string,
558
+    down: boolean,
559
+    press: boolean,
560
+    alt: Boolean,
561
+    ctrl: Boolean,
562
+    shift: Boolean,
563
+    command: Boolean
564
+  ) {
565
+    const key_event = mapKey(name, globals.isDesktop());
566
+    if (!key_event) return;
567
+    if (alt && (name == "VK_MENU" || name == "RAlt")) {
568
+      alt = false;
569
+    }
570
+    if (ctrl && (name == "VK_CONTROL" || name == "RControl")) {
571
+      ctrl = false;
572
+    }
573
+    if (shift && (name == "VK_SHIFT" || name == "RShift")) {
574
+      shift = false;
575
+    }
576
+    if (command && (name == "Meta" || name == "RWin")) {
577
+      command = false;
578
+    }
579
+    key_event.down = down;
580
+    key_event.press = press;
581
+    key_event.modifiers = this.getMod(alt, ctrl, shift, command);
582
+    this._ws?.sendMessage({ key_event });
583
+  }
584
+
585
+  ctrlAltDel() {
586
+    const key_event = message.KeyEvent.fromPartial({ down: true });
587
+    if (this._peerInfo?.platform == "Windows") {
588
+      key_event.control_key = message.ControlKey.CtrlAltDel;
589
+    } else {
590
+      key_event.control_key = message.ControlKey.Delete;
591
+      key_event.modifiers = this.getMod(true, true, false, false);
592
+    }
593
+    this._ws?.sendMessage({ key_event });
594
+  }
595
+
596
+  inputString(seq: string) {
597
+    const key_event = message.KeyEvent.fromPartial({ seq });
598
+    this._ws?.sendMessage({ key_event });
599
+  }
600
+
601
+  switchDisplay(display: number) {
602
+    const switch_display = message.SwitchDisplay.fromPartial({ display });
603
+    const misc = message.Misc.fromPartial({ switch_display });
604
+    this._ws?.sendMessage({ misc });
605
+  }
606
+
607
+  async inputOsPassword(seq: string) {
608
+    this.inputMouse();
609
+    await sleep(50);
610
+    this.inputMouse(0, 3, 3);
611
+    await sleep(50);
612
+    this.inputMouse(1 | (1 << 3));
613
+    this.inputMouse(2 | (1 << 3));
614
+    await sleep(1200);
615
+    const key_event = message.KeyEvent.fromPartial({ press: true, seq });
616
+    this._ws?.sendMessage({ key_event });
617
+  }
618
+
619
+  lockScreen() {
620
+    const key_event = message.KeyEvent.fromPartial({
621
+      down: true,
622
+      control_key: message.ControlKey.LockScreen,
623
+    });
624
+    this._ws?.sendMessage({ key_event });
625
+  }
626
+
627
+  getMod(alt: Boolean, ctrl: Boolean, shift: Boolean, command: Boolean) {
628
+    const mod: message.ControlKey[] = [];
629
+    if (alt) mod.push(message.ControlKey.Alt);
630
+    if (ctrl) mod.push(message.ControlKey.Control);
631
+    if (shift) mod.push(message.ControlKey.Shift);
632
+    if (command) mod.push(message.ControlKey.Meta);
633
+    return mod;
634
+  }
635
+
636
+  inputMouse(
637
+    mask: number = 0,
638
+    x: number = 0,
639
+    y: number = 0,
640
+    alt: Boolean = false,
641
+    ctrl: Boolean = false,
642
+    shift: Boolean = false,
643
+    command: Boolean = false
644
+  ) {
645
+    const mouse_event = message.MouseEvent.fromPartial({
646
+      mask,
647
+      x,
648
+      y,
649
+      modifiers: this.getMod(alt, ctrl, shift, command),
650
+    });
651
+    this._ws?.sendMessage({ mouse_event });
652
+  }
653
+
654
+  toggleOption(name: string) {
655
+    const v = !this._options[name];
656
+    const option = message.OptionMessage.fromPartial({});
657
+    const v2 = v
658
+      ? message.OptionMessage_BoolOption.Yes
659
+      : message.OptionMessage_BoolOption.No;
660
+    switch (name) {
661
+      case "show-remote-cursor":
662
+        option.show_remote_cursor = v2;
663
+        break;
664
+      case "disable-audio":
665
+        option.disable_audio = v2;
666
+        break;
667
+      case "disable-clipboard":
668
+        option.disable_clipboard = v2;
669
+        break;
670
+      case "lock-after-session-end":
671
+        option.lock_after_session_end = v2;
672
+        break;
673
+      case "privacy-mode":
674
+        option.privacy_mode = v2;
675
+        break;
676
+      case "block-input":
677
+        option.block_input = message.OptionMessage_BoolOption.Yes;
678
+        break;
679
+      case "unblock-input":
680
+        option.block_input = message.OptionMessage_BoolOption.No;
681
+        break;
682
+      default:
683
+        return;
684
+    }
685
+    if (name.indexOf("block-input") < 0) this.setOption(name, v);
686
+    const misc = message.Misc.fromPartial({ option });
687
+    this._ws?.sendMessage({ misc });
688
+  }
689
+
690
+  getImageQuality() {
691
+    return this.getOption("image-quality");
692
+  }
693
+
694
+  getImageQualityEnum(
695
+    value: string,
696
+    ignoreDefault: Boolean
697
+  ): message.ImageQuality | undefined {
698
+    switch (value) {
699
+      case "low":
700
+        return message.ImageQuality.Low;
701
+      case "best":
702
+        return message.ImageQuality.Best;
703
+      case "balanced":
704
+        return ignoreDefault ? undefined : message.ImageQuality.Balanced;
705
+      default:
706
+        return undefined;
707
+    }
708
+  }
709
+
710
+  setImageQuality(value: string) {
711
+    this.setOption("image-quality", value);
712
+    const image_quality = this.getImageQualityEnum(value, false);
713
+    if (image_quality == undefined) return;
714
+    const option = message.OptionMessage.fromPartial({ image_quality });
715
+    const misc = message.Misc.fromPartial({ option });
716
+    this._ws?.sendMessage({ misc });
717
+  }
718
+
719
+  loadVideoDecoder() {
720
+    this._videoDecoder?.close();
721
+    loadVp9((decoder: any) => {
722
+      this._videoDecoder = decoder;
723
+      console.log("vp9 loaded");
724
+      console.log(decoder);
725
+    });
726
+  }
727
+}
728
+
729
+function testDelay() {
730
+  var nearest = "";
731
+  HOSTS.forEach((host) => {
732
+    const now = new Date().getTime();
733
+    new Websock(getrUriFromRs(host), true).open().then(() => {
734
+      console.log("latency of " + host + ": " + (new Date().getTime() - now));
735
+      if (!nearest) {
736
+        HOST = host;
737
+        localStorage.setItem("rendezvous-server", host);
738
+      }
739
+    });
740
+  });
741
+}
742
+
743
+testDelay();
744
+
745
+function getDefaultUri(isRelay: Boolean = false): string {
746
+  const host = localStorage.getItem("custom-rendezvous-server");
747
+  return getrUriFromRs(host || HOST, isRelay);
748
+}
749
+
750
+function getrUriFromRs(
751
+  uri: string,
752
+  isRelay: Boolean = false,
753
+  roffset: number = 0
754
+): string {
755
+  if (uri.indexOf(":") > 0) {
756
+    const tmp = uri.split(":");
757
+    const port = parseInt(tmp[1]);
758
+    uri = tmp[0] + ":" + (port + (isRelay ? roffset || 3 : 2));
759
+  } else {
760
+    uri += ":" + (PORT + (isRelay ? 3 : 2));
761
+  }
762
+  return SCHEMA + uri;
763
+}
764
+
765
+function hash(datas: (string | Uint8Array)[]): Uint8Array {
766
+  const hasher = new sha256.Hash();
767
+  datas.forEach((data) => {
768
+    if (typeof data == "string") {
769
+      data = new TextEncoder().encode(data);
770
+    }
771
+    return hasher.update(data);
772
+  });
773
+  return hasher.digest();
774
+}

Разница между файлами не показана из-за своего большого размера
+ 4101 - 0
resources/web/js/src/gen_js_from_hbb.ts


Разница между файлами не показана из-за своего большого размера
+ 383 - 0
resources/web/js/src/globals.js


+ 2 - 0
resources/web/js/src/main.ts

@@ -0,0 +1,2 @@
1
+import "./globals";
2
+import "./ui";

Разница между файлами не показана из-за своего большого размера
+ 6073 - 0
resources/web/js/src/message.ts


Разница между файлами не показана из-за своего большого размера
+ 2025 - 0
resources/web/js/src/rendezvous.ts


+ 8 - 0
resources/web/js/src/style.css

@@ -0,0 +1,8 @@
1
+#app {
2
+  font-family: Avenir, Helvetica, Arial, sans-serif;
3
+  -webkit-font-smoothing: antialiased;
4
+  -moz-osx-font-smoothing: grayscale;
5
+  text-align: center;
6
+  color: #2c3e50;
7
+  margin-top: 60px;
8
+}

+ 108 - 0
resources/web/js/src/ui.js

@@ -0,0 +1,108 @@
1
+import "./style.css";
2
+import "./connection";
3
+import * as globals from "./globals";
4
+
5
+const app = document.querySelector('#app');
6
+
7
+if (app) {
8
+  app.innerHTML = `
9
+  <div id="connect" style="text-align: center"><table style="display: inline-block">
10
+    <tr><td><span>Host: </span></td><td><input id="host" /></td></tr>
11
+    <tr><td><span>Key: </span></td><td><input id="key" /></td></tr>
12
+    <tr><td><span>Id: </span></td><td><input id="id" /></td></tr>
13
+    <tr><td></td><td><button onclick="connect();">Connect</button></td></tr>
14
+  </table></div>
15
+  <div id="password" style="display: none;">
16
+    <input type="password" id="password" />
17
+    <button id="confirm" onclick="confirm()">Confirm</button>
18
+    <button id="cancel" onclick="cancel();">Cancel</button>
19
+  </div>
20
+  <div id="status" style="display: none;">
21
+    <div id="text" style="line-height: 2em"></div>
22
+    <button id="cancel" onclick="cancel();">Cancel</button>
23
+  </div>
24
+  <div id="canvas" style="display: none;">
25
+    <button id="cancel" onclick="cancel();">Cancel</button>
26
+    <canvas id="player"></canvas>
27
+    <canvas id="test-yuv-decoder-canvas"></canvas>
28
+  </div>
29
+`;
30
+
31
+  let player;
32
+  window.init();
33
+
34
+  document.body.onload = () => {
35
+    const host = document.querySelector('#host');
36
+    host.value = localStorage.getItem('custom-rendezvous-server');
37
+    const id = document.querySelector('#id');
38
+    id.value = localStorage.getItem('id');
39
+    const key = document.querySelector('#key');
40
+    key.value = localStorage.getItem('key');
41
+    player = YUVCanvas.attach(document.getElementById('player'));
42
+    // globals.sendOffCanvas(document.getElementById('player'));
43
+  };
44
+
45
+  window.connect = () => {
46
+    const host = document.querySelector('#host');
47
+    localStorage.setItem('custom-rendezvous-server', host.value);
48
+    const id = document.querySelector('#id');
49
+    localStorage.setItem('id', id.value);
50
+    const key = document.querySelector('#key');
51
+    localStorage.setItem('key', key.value);
52
+    const func = async () => {
53
+      const conn = globals.newConn();
54
+      conn.setMsgbox(msgbox);
55
+      conn.setDraw((f) => {
56
+        /*
57
+        if (!(document.getElementById('player').width > 0)) {
58
+          document.getElementById('player').width = f.format.displayWidth;
59
+          document.getElementById('player').height = f.format.displayHeight;
60
+        }
61
+        */
62
+        globals.draw(f);
63
+        player.drawFrame(f);
64
+      });
65
+      document.querySelector('div#status').style.display = 'block';
66
+      document.querySelector('div#connect').style.display = 'none';
67
+      document.querySelector('div#text').innerHTML = 'Connecting ...';
68
+      await conn.start(id.value);
69
+    };
70
+    func();
71
+  }
72
+
73
+  function msgbox(type, title, text) {
74
+    if (!globals.getConn()) return;
75
+    if (type == 'input-password') {
76
+      document.querySelector('div#status').style.display = 'none';
77
+      document.querySelector('div#password').style.display = 'block';
78
+    } else if (!type) {
79
+      document.querySelector('div#canvas').style.display = 'block';
80
+      document.querySelector('div#password').style.display = 'none';
81
+      document.querySelector('div#status').style.display = 'none';
82
+    } else if (type == 'error') {
83
+      document.querySelector('div#status').style.display = 'block';
84
+      document.querySelector('div#canvas').style.display = 'none';
85
+      document.querySelector('div#text').innerHTML = '<div style="color: red; font-weight: bold;">' + text + '</div>';
86
+    } else {
87
+      document.querySelector('div#password').style.display = 'none';
88
+      document.querySelector('div#status').style.display = 'block';
89
+      document.querySelector('div#text').innerHTML = '<div style="font-weight: bold;">' + text + '</div>';
90
+    }
91
+  }
92
+
93
+  window.cancel = () => {
94
+    globals.close();
95
+    document.querySelector('div#connect').style.display = 'block';
96
+    document.querySelector('div#password').style.display = 'none';
97
+    document.querySelector('div#status').style.display = 'none';
98
+    document.querySelector('div#canvas').style.display = 'none';
99
+  }
100
+
101
+  window.confirm = () => {
102
+    const password = document.querySelector('input#password').value;
103
+    if (password) {
104
+      document.querySelector('div#password').style.display = 'none';
105
+      globals.getConn().login(password);
106
+    }
107
+  }
108
+}

+ 1 - 0
resources/web/js/src/vite-env.d.ts

@@ -0,0 +1 @@
1
+/// <reference types="vite/client" />

+ 183 - 0
resources/web/js/src/websock.ts

@@ -0,0 +1,183 @@
1
+import * as message from "./message.js";
2
+import * as rendezvous from "./rendezvous.js";
3
+import * as globals from "./globals";
4
+
5
+type Keys = "message" | "open" | "close" | "error";
6
+
7
+export default class Websock {
8
+  _websocket: WebSocket;
9
+  _eventHandlers: { [key in Keys]: Function };
10
+  _buf: (rendezvous.RendezvousMessage | message.Message)[];
11
+  _status: any;
12
+  _latency: number;
13
+  _secretKey: [Uint8Array, number, number] | undefined;
14
+  _uri: string;
15
+  _isRendezvous: boolean;
16
+
17
+  constructor(uri: string, isRendezvous: boolean = true) {
18
+    this._eventHandlers = {
19
+      message: (_: any) => {},
20
+      open: () => {},
21
+      close: () => {},
22
+      error: () => {},
23
+    };
24
+    this._uri = uri;
25
+    this._status = "";
26
+    this._buf = [];
27
+    this._websocket = new WebSocket(uri);
28
+    this._websocket.onmessage = this._recv_message.bind(this);
29
+    this._websocket.binaryType = "arraybuffer";
30
+    this._latency = new Date().getTime();
31
+    this._isRendezvous = isRendezvous;
32
+  }
33
+
34
+  latency(): number {
35
+    return this._latency;
36
+  }
37
+
38
+  setSecretKey(key: Uint8Array) {
39
+    this._secretKey = [key, 0, 0];
40
+  }
41
+
42
+  sendMessage(json: message.DeepPartial<message.Message>) {
43
+    let data = message.Message.encode(
44
+      message.Message.fromPartial(json)
45
+    ).finish();
46
+    let k = this._secretKey;
47
+    if (k) {
48
+      k[1] += 1;
49
+      data = globals.encrypt(data, k[1], k[0]);
50
+    }
51
+    this._websocket.send(data);
52
+  }
53
+
54
+  sendRendezvous(data: rendezvous.DeepPartial<rendezvous.RendezvousMessage>) {
55
+    this._websocket.send(
56
+      rendezvous.RendezvousMessage.encode(
57
+        rendezvous.RendezvousMessage.fromPartial(data)
58
+      ).finish()
59
+    );
60
+  }
61
+
62
+  parseMessage(data: Uint8Array) {
63
+    return message.Message.decode(data);
64
+  }
65
+
66
+  parseRendezvous(data: Uint8Array) {
67
+    return rendezvous.RendezvousMessage.decode(data);
68
+  }
69
+
70
+  // Event Handlers
71
+  off(evt: Keys) {
72
+    this._eventHandlers[evt] = () => {};
73
+  }
74
+
75
+  on(evt: Keys, handler: Function) {
76
+    this._eventHandlers[evt] = handler;
77
+  }
78
+
79
+  async open(timeout: number = 12000): Promise<Websock> {
80
+    return new Promise((resolve, reject) => {
81
+      setTimeout(() => {
82
+        if (this._status != "open") {
83
+          reject(this._status || "Timeout");
84
+        }
85
+      }, timeout);
86
+      this._websocket.onopen = () => {
87
+        this._latency = new Date().getTime() - this._latency;
88
+        this._status = "open";
89
+        console.debug(">> WebSock.onopen");
90
+        if (this._websocket?.protocol) {
91
+          console.info(
92
+            "Server choose sub-protocol: " + this._websocket.protocol
93
+          );
94
+        }
95
+
96
+        this._eventHandlers.open();
97
+        console.info("WebSock.onopen");
98
+        resolve(this);
99
+      };
100
+      this._websocket.onclose = (e) => {
101
+        if (this._status == "open") {
102
+          // e.code 1000 means that the connection was closed normally.
103
+          //
104
+        }
105
+        this._status = e;
106
+        console.error("WebSock.onclose: ");
107
+        console.error(e);
108
+        this._eventHandlers.close(e);
109
+        reject("Reset by the peer");
110
+      };
111
+      this._websocket.onerror = (e: any) => {
112
+        if (!this._status) {
113
+          reject("Failed to connect to " + (this._isRendezvous ? "rendezvous" : "relay") + " server");
114
+          return;
115
+        }
116
+        this._status = e;
117
+        console.error("WebSock.onerror: ")
118
+        console.error(e);
119
+        this._eventHandlers.error(e);
120
+      };
121
+    });
122
+  }
123
+
124
+  async next(
125
+    timeout = 12000
126
+  ): Promise<rendezvous.RendezvousMessage | message.Message> {
127
+    const func = (
128
+      resolve: (value: rendezvous.RendezvousMessage | message.Message) => void,
129
+      reject: (reason: any) => void,
130
+      tm0: number
131
+    ) => {
132
+      if (this._buf.length) {
133
+        resolve(this._buf[0]);
134
+        this._buf.splice(0, 1);
135
+      } else {
136
+        if (this._status != "open") {
137
+          reject(this._status);
138
+          return;
139
+        }
140
+        if (new Date().getTime() > tm0 + timeout) {
141
+          reject("Timeout");
142
+        } else {
143
+          setTimeout(() => func(resolve, reject, tm0), 1);
144
+        }
145
+      }
146
+    };
147
+    return new Promise((resolve, reject) => {
148
+      func(resolve, reject, new Date().getTime());
149
+    });
150
+  }
151
+
152
+  close() {
153
+    this._status = "";
154
+    if (this._websocket) {
155
+      if (
156
+        this._websocket.readyState === WebSocket.OPEN ||
157
+        this._websocket.readyState === WebSocket.CONNECTING
158
+      ) {
159
+        console.info("Closing WebSocket connection");
160
+        this._websocket.close();
161
+      }
162
+
163
+      this._websocket.onmessage = () => {};
164
+    }
165
+  }
166
+
167
+  _recv_message(e: any) {
168
+    if (e.data instanceof window.ArrayBuffer) {
169
+      let bytes = new Uint8Array(e.data);
170
+      const k = this._secretKey;
171
+      if (k) {
172
+        k[2] += 1;
173
+        bytes = globals.decrypt(bytes, k[2], k[0]);
174
+      }
175
+      this._buf.push(
176
+        this._isRendezvous
177
+          ? this.parseRendezvous(bytes)
178
+          : this.parseMessage(bytes)
179
+      );
180
+    }
181
+    this._eventHandlers.message(e.data);
182
+  }
183
+}

+ 20 - 0
resources/web/js/ts_proto.py

@@ -0,0 +1,20 @@
1
+#!/usr/bin/env python
2
+
3
+import os
4
+
5
+path = os.path.abspath(os.path.join(os.getcwd(), '..', '..', '..', 'libs', 'hbb_common', 'protos'))
6
+
7
+if os.name == 'nt':
8
+    cmd = r'protoc --ts_proto_opt=esModuleInterop=true --ts_proto_opt=snakeToCamel=false --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd  -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path
9
+    print(cmd)
10
+    os.system(cmd)
11
+    cmd = r'protoc --ts_proto_opt=esModuleInterop=true --ts_proto_opt=snakeToCamel=false --plugin=protoc-gen-ts_proto=.\node_modules\.bin\protoc-gen-ts_proto.cmd  -I "%s" --ts_proto_out=./src/ message.proto'%path
12
+    print(cmd)
13
+    os.system(cmd)
14
+else:
15
+    cmd = r'protoc --ts_proto_opt=esModuleInterop=true --ts_proto_opt=snakeToCamel=false --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ rendezvous.proto'%path
16
+    print(cmd)
17
+    os.system(cmd)
18
+    cmd = r'protoc --ts_proto_opt=esModuleInterop=true --ts_proto_opt=snakeToCamel=false --plugin=./node_modules/.bin/protoc-gen-ts_proto -I "%s" --ts_proto_out=./src/ message.proto'%path
19
+    print(cmd)
20
+    os.system(cmd)

+ 24 - 0
resources/web/js/tsconfig.json

@@ -0,0 +1,24 @@
1
+{
2
+  "compilerOptions": {
3
+    "target": "ESNext",
4
+    "useDefineForClassFields": true,
5
+    "module": "ESNext",
6
+    "allowJs": true,
7
+    "lib": [
8
+      "ESNext",
9
+      "DOM"
10
+    ],
11
+    "moduleResolution": "Node",
12
+    "strict": true,
13
+    "sourceMap": true,
14
+    "resolveJsonModule": true,
15
+    "esModuleInterop": true,
16
+    "noEmit": true,
17
+    "noUnusedLocals": true,
18
+    "noUnusedParameters": true,
19
+    "noImplicitReturns": true
20
+  },
21
+  "include": [
22
+    "./src"
23
+  ]
24
+}

+ 14 - 0
resources/web/js/vite.config.js

@@ -0,0 +1,14 @@
1
+import { defineConfig } from 'vite';
2
+
3
+export default defineConfig({
4
+    build: {
5
+        manifest: false,
6
+        rollupOptions: {
7
+            output: {
8
+                entryFileNames: `[name].js`,
9
+                chunkFileNames: `[name].js`,
10
+                assetFileNames: `[name].[ext]`,
11
+            }
12
+        }
13
+    },
14
+})

+ 300 - 0
resources/web/js/yarn.lock

@@ -0,0 +1,300 @@
1
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+# yarn lockfile v1
3
+
4
+
5
+"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
6
+  version "1.1.2"
7
+  resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz"
8
+  integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
9
+
10
+"@protobufjs/base64@^1.1.2":
11
+  version "1.1.2"
12
+  resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz"
13
+  integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
14
+
15
+"@protobufjs/codegen@^2.0.4":
16
+  version "2.0.4"
17
+  resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz"
18
+  integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
19
+
20
+"@protobufjs/eventemitter@^1.1.0":
21
+  version "1.1.0"
22
+  resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz"
23
+  integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
24
+
25
+"@protobufjs/fetch@^1.1.0":
26
+  version "1.1.0"
27
+  resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz"
28
+  integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
29
+  dependencies:
30
+    "@protobufjs/aspromise" "^1.1.1"
31
+    "@protobufjs/inquire" "^1.1.0"
32
+
33
+"@protobufjs/float@^1.0.2":
34
+  version "1.0.2"
35
+  resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz"
36
+  integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
37
+
38
+"@protobufjs/inquire@^1.1.0":
39
+  version "1.1.0"
40
+  resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz"
41
+  integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
42
+
43
+"@protobufjs/path@^1.1.2":
44
+  version "1.1.2"
45
+  resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz"
46
+  integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
47
+
48
+"@protobufjs/pool@^1.1.0":
49
+  version "1.1.0"
50
+  resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz"
51
+  integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
52
+
53
+"@protobufjs/utf8@^1.1.0":
54
+  version "1.1.0"
55
+  resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz"
56
+  integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
57
+
58
+"@types/long@^4.0.1":
59
+  version "4.0.2"
60
+  resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz"
61
+  integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
62
+
63
+"@types/node@>=13.7.0":
64
+  version "18.7.6"
65
+  resolved "https://registry.npmjs.org/@types/node/-/node-18.7.6.tgz"
66
+  integrity sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A==
67
+
68
+"@types/object-hash@^1.3.0":
69
+  version "1.3.4"
70
+  resolved "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.3.4.tgz"
71
+  integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA==
72
+
73
+case-anything@^2.1.10:
74
+  version "2.1.10"
75
+  resolved "https://registry.npmjs.org/case-anything/-/case-anything-2.1.10.tgz"
76
+  integrity sha512-JczJwVrCP0jPKh05McyVsuOg6AYosrB9XWZKbQzXeDAm2ClE/PJE/BcrrQrVyGYH7Jg8V/LDupmyL4kFlVsVFQ==
77
+
78
+dataloader@^1.4.0:
79
+  version "1.4.0"
80
+  resolved "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz"
81
+  integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==
82
+
83
+detect-libc@^1.0.3:
84
+  version "1.0.3"
85
+  resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
86
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
87
+
88
+dprint-node@^1.0.7:
89
+  version "1.0.7"
90
+  resolved "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.7.tgz"
91
+  integrity sha512-NTZOW9A7ipb0n7z7nC3wftvsbceircwVHSgzobJsEQa+7RnOMbhrfX5IflA6CtC4GA63DSAiHYXa4JKEy9F7cA==
92
+  dependencies:
93
+    detect-libc "^1.0.3"
94
+
95
+esbuild-linux-64@0.14.54:
96
+  version "0.14.54"
97
+  resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz"
98
+  integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
99
+
100
+esbuild@^0.14.14:
101
+  version "0.14.54"
102
+  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz"
103
+  integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
104
+  optionalDependencies:
105
+    "@esbuild/linux-loong64" "0.14.54"
106
+    esbuild-android-64 "0.14.54"
107
+    esbuild-android-arm64 "0.14.54"
108
+    esbuild-darwin-64 "0.14.54"
109
+    esbuild-darwin-arm64 "0.14.54"
110
+    esbuild-freebsd-64 "0.14.54"
111
+    esbuild-freebsd-arm64 "0.14.54"
112
+    esbuild-linux-32 "0.14.54"
113
+    esbuild-linux-64 "0.14.54"
114
+    esbuild-linux-arm "0.14.54"
115
+    esbuild-linux-arm64 "0.14.54"
116
+    esbuild-linux-mips64le "0.14.54"
117
+    esbuild-linux-ppc64le "0.14.54"
118
+    esbuild-linux-riscv64 "0.14.54"
119
+    esbuild-linux-s390x "0.14.54"
120
+    esbuild-netbsd-64 "0.14.54"
121
+    esbuild-openbsd-64 "0.14.54"
122
+    esbuild-sunos-64 "0.14.54"
123
+    esbuild-windows-32 "0.14.54"
124
+    esbuild-windows-64 "0.14.54"
125
+    esbuild-windows-arm64 "0.14.54"
126
+
127
+fast-sha256@^1.3.0:
128
+  version "1.3.0"
129
+  resolved "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz"
130
+  integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==
131
+
132
+function-bind@^1.1.1:
133
+  version "1.1.1"
134
+  resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
135
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
136
+
137
+has@^1.0.3:
138
+  version "1.0.3"
139
+  resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
140
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
141
+  dependencies:
142
+    function-bind "^1.1.1"
143
+
144
+is-core-module@^2.9.0:
145
+  version "2.10.0"
146
+  resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz"
147
+  integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
148
+  dependencies:
149
+    has "^1.0.3"
150
+
151
+libsodium-wrappers@^0.7.9:
152
+  version "0.7.10"
153
+  resolved "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.10.tgz"
154
+  integrity sha512-pO3F1Q9NPLB/MWIhehim42b/Fwb30JNScCNh8TcQ/kIc+qGLQch8ag8wb0keK3EP5kbGakk1H8Wwo7v+36rNQg==
155
+  dependencies:
156
+    libsodium "^0.7.0"
157
+
158
+libsodium@^0.7.0, libsodium@^0.7.9:
159
+  version "0.7.10"
160
+  resolved "https://registry.npmjs.org/libsodium/-/libsodium-0.7.10.tgz"
161
+  integrity sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==
162
+
163
+long@^4.0.0:
164
+  version "4.0.0"
165
+  resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz"
166
+  integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
167
+
168
+nanoid@^3.3.4:
169
+  version "3.3.4"
170
+  resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz"
171
+  integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
172
+
173
+object-hash@^1.3.1:
174
+  version "1.3.1"
175
+  resolved "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz"
176
+  integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==
177
+
178
+path-parse@^1.0.7:
179
+  version "1.0.7"
180
+  resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
181
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
182
+
183
+pcm-player@^0.0.11:
184
+  version "0.0.11"
185
+  resolved "https://registry.npmjs.org/pcm-player/-/pcm-player-0.0.11.tgz"
186
+  integrity sha512-+FmX62jiqZa7wDCqSRQ1g3DuU6JNgpymgOLCWhmiE/Lj/M+rOUNqgNwVQX509LdA9dtBtVD3EQQUSp9JqU6upw==
187
+
188
+picocolors@^1.0.0:
189
+  version "1.0.0"
190
+  resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
191
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
192
+
193
+postcss@^8.4.6:
194
+  version "8.4.16"
195
+  resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz"
196
+  integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
197
+  dependencies:
198
+    nanoid "^3.3.4"
199
+    picocolors "^1.0.0"
200
+    source-map-js "^1.0.2"
201
+
202
+protobufjs@^6.11.3, protobufjs@^6.8.8:
203
+  version "6.11.3"
204
+  resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz"
205
+  integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==
206
+  dependencies:
207
+    "@protobufjs/aspromise" "^1.1.2"
208
+    "@protobufjs/base64" "^1.1.2"
209
+    "@protobufjs/codegen" "^2.0.4"
210
+    "@protobufjs/eventemitter" "^1.1.0"
211
+    "@protobufjs/fetch" "^1.1.0"
212
+    "@protobufjs/float" "^1.0.2"
213
+    "@protobufjs/inquire" "^1.1.0"
214
+    "@protobufjs/path" "^1.1.2"
215
+    "@protobufjs/pool" "^1.1.0"
216
+    "@protobufjs/utf8" "^1.1.0"
217
+    "@types/long" "^4.0.1"
218
+    "@types/node" ">=13.7.0"
219
+    long "^4.0.0"
220
+
221
+resolve@^1.22.0:
222
+  version "1.22.1"
223
+  resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz"
224
+  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
225
+  dependencies:
226
+    is-core-module "^2.9.0"
227
+    path-parse "^1.0.7"
228
+    supports-preserve-symlinks-flag "^1.0.0"
229
+
230
+rollup@^2.59.0:
231
+  version "2.77.3"
232
+  resolved "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz"
233
+  integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
234
+  optionalDependencies:
235
+    fsevents "~2.3.2"
236
+
237
+source-map-js@^1.0.2:
238
+  version "1.0.2"
239
+  resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
240
+  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
241
+
242
+supports-preserve-symlinks-flag@^1.0.0:
243
+  version "1.0.0"
244
+  resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
245
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
246
+
247
+ts-poet@^6.2.0:
248
+  version "6.4.1"
249
+  resolved "https://registry.npmjs.org/ts-poet/-/ts-poet-6.4.1.tgz"
250
+  integrity sha512-AjZEs4h2w4sDfwpHMxQKHrTlNh2wRbM5NRXmLz0RiH+yPGtSQFbe9hBpNocU8vqVNgfh0BIOiXR80xDz3kKxUQ==
251
+  dependencies:
252
+    dprint-node "^1.0.7"
253
+
254
+ts-proto-descriptors@1.7.1:
255
+  version "1.7.1"
256
+  resolved "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.7.1.tgz"
257
+  integrity sha512-oIKUh3K4Xts4v29USGLfUG+2mEk32MsqpgZAOUyUlkrcIdv34yE+k2oZ2Nzngm6cV/JgFdOxRCqeyvmWHuYAyw==
258
+  dependencies:
259
+    long "^4.0.0"
260
+    protobufjs "^6.8.8"
261
+
262
+ts-proto@^1.141.1:
263
+  version "1.141.1"
264
+  resolved "https://registry.npmjs.org/ts-proto/-/ts-proto-1.141.1.tgz"
265
+  integrity sha512-1b7Ka6R96FvFZldHnYPTFy4rzwOo+OTpIP1mBFW0dDwq4WWtSkIVlZ+SokOQSC1TiccNshOJwQC9soVyWfQ7Zg==
266
+  dependencies:
267
+    "@types/object-hash" "^1.3.0"
268
+    case-anything "^2.1.10"
269
+    dataloader "^1.4.0"
270
+    object-hash "^1.3.1"
271
+    protobufjs "^6.11.3"
272
+    ts-poet "^6.2.0"
273
+    ts-proto-descriptors "1.7.1"
274
+
275
+typescript@4.4.4:
276
+  version "4.4.4"
277
+  resolved "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz"
278
+  integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
279
+
280
+vite@2.8:
281
+  version "2.8.6"
282
+  resolved "https://registry.npmjs.org/vite/-/vite-2.8.6.tgz"
283
+  integrity sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug==
284
+  dependencies:
285
+    esbuild "^0.14.14"
286
+    postcss "^8.4.6"
287
+    resolve "^1.22.0"
288
+    rollup "^2.59.0"
289
+  optionalDependencies:
290
+    fsevents "~2.3.2"
291
+
292
+wasm-feature-detect@^1.2.11:
293
+  version "1.2.11"
294
+  resolved "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz"
295
+  integrity sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w==
296
+
297
+zstddec@^0.0.2:
298
+  version "0.0.2"
299
+  resolved "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz"
300
+  integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==

Разница между файлами не показана из-за своего большого размера
+ 5555 - 0
resources/web/libopus.js


BIN
resources/web/libopus.wasm


Разница между файлами не показана из-за своего большого размера
+ 2 - 0
resources/web/libs/firebase-analytics.js


Разница между файлами не показана из-за своего большого размера
+ 2 - 0
resources/web/libs/firebase-app.js


Разница между файлами не показана из-за своего большого размера
+ 106639 - 0
resources/web/main.dart.js


+ 35 - 0
resources/web/manifest.json

@@ -0,0 +1,35 @@
1
+{
2
+    "name": "rustdesk",
3
+    "short_name": "rustdesk",
4
+    "start_url": ".",
5
+    "display": "standalone",
6
+    "background_color": "#0175C2",
7
+    "theme_color": "#0175C2",
8
+    "description": "Remote Desktop.",
9
+    "orientation": "portrait-primary",
10
+    "prefer_related_applications": false,
11
+    "icons": [
12
+        {
13
+            "src": "icons/Icon-192.png",
14
+            "sizes": "192x192",
15
+            "type": "image/png"
16
+        },
17
+        {
18
+            "src": "icons/Icon-512.png",
19
+            "sizes": "512x512",
20
+            "type": "image/png"
21
+        },
22
+        {
23
+            "src": "icons/Icon-maskable-192.png",
24
+            "sizes": "192x192",
25
+            "type": "image/png",
26
+            "purpose": "maskable"
27
+        },
28
+        {
29
+            "src": "icons/Icon-maskable-512.png",
30
+            "sizes": "512x512",
31
+            "type": "image/png",
32
+            "purpose": "maskable"
33
+        }
34
+    ]
35
+}

+ 21 - 0
resources/web/ogvjs-1.8.6/COPYING

@@ -0,0 +1,21 @@
1
+ogv.js wrapper and player code
2
+
3
+Copyright (c) 2013-2019 Brion Vibber and other contributors
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE.

+ 23 - 0
resources/web/ogvjs-1.8.6/COPYING-dav1d.txt

@@ -0,0 +1,23 @@
1
+Copyright © 2018-2019, VideoLAN and dav1d authors
2
+All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are met:
6
+
7
+1. Redistributions of source code must retain the above copyright notice, this
8
+   list of conditions and the following disclaimer.
9
+
10
+2. Redistributions in binary form must reproduce the above copyright notice,
11
+   this list of conditions and the following disclaimer in the documentation
12
+   and/or other materials provided with the distribution.
13
+
14
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 28 - 0
resources/web/ogvjs-1.8.6/COPYING-ogg.txt

@@ -0,0 +1,28 @@
1
+Copyright (c) 2002, Xiph.org Foundation
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions
5
+are met:
6
+
7
+- Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+
10
+- Redistributions in binary form must reproduce the above copyright
11
+notice, this list of conditions and the following disclaimer in the
12
+documentation and/or other materials provided with the distribution.
13
+
14
+- Neither the name of the Xiph.org Foundation nor the names of its
15
+contributors may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
22
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 44 - 0
resources/web/ogvjs-1.8.6/COPYING-opus.txt

@@ -0,0 +1,44 @@
1
+Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
2
+                    Jean-Marc Valin, Timothy B. Terriberry,
3
+                    CSIRO, Gregory Maxwell, Mark Borgerding,
4
+                    Erik de Castro Lopo
5
+
6
+Redistribution and use in source and binary forms, with or without
7
+modification, are permitted provided that the following conditions
8
+are met:
9
+
10
+- Redistributions of source code must retain the above copyright
11
+notice, this list of conditions and the following disclaimer.
12
+
13
+- Redistributions in binary form must reproduce the above copyright
14
+notice, this list of conditions and the following disclaimer in the
15
+documentation and/or other materials provided with the distribution.
16
+
17
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
18
+names of specific contributors, may be used to endorse or promote
19
+products derived from this software without specific prior written
20
+permission.
21
+
22
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
26
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
+
34
+Opus is subject to the royalty-free patent licenses which are
35
+specified at:
36
+
37
+Xiph.Org Foundation:
38
+https://datatracker.ietf.org/ipr/1524/
39
+
40
+Microsoft Corporation:
41
+https://datatracker.ietf.org/ipr/1914/
42
+
43
+Broadcom Corporation:
44
+https://datatracker.ietf.org/ipr/1526/

+ 28 - 0
resources/web/ogvjs-1.8.6/COPYING-theora.txt

@@ -0,0 +1,28 @@
1
+Copyright (C) 2002-2009 Xiph.org Foundation
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions
5
+are met:
6
+
7
+- Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+
10
+- Redistributions in binary form must reproduce the above copyright
11
+notice, this list of conditions and the following disclaimer in the
12
+documentation and/or other materials provided with the distribution.
13
+
14
+- Neither the name of the Xiph.org Foundation nor the names of its
15
+contributors may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
22
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 28 - 0
resources/web/ogvjs-1.8.6/COPYING-vorbis.txt

@@ -0,0 +1,28 @@
1
+Copyright (c) 2002-2018 Xiph.org Foundation
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions
5
+are met:
6
+
7
+- Redistributions of source code must retain the above copyright
8
+notice, this list of conditions and the following disclaimer.
9
+
10
+- Redistributions in binary form must reproduce the above copyright
11
+notice, this list of conditions and the following disclaimer in the
12
+documentation and/or other materials provided with the distribution.
13
+
14
+- Neither the name of the Xiph.org Foundation nor the names of its
15
+contributors may be used to endorse or promote products derived from
16
+this software without specific prior written permission.
17
+
18
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION
22
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 13 - 0
resources/web/ogvjs-1.8.6/LICENSE-nestegg.txt

@@ -0,0 +1,13 @@
1
+Copyright © 2010 Mozilla Foundation
2
+
3
+Permission to use, copy, modify, and distribute this software for any
4
+purpose with or without fee is hereby granted, provided that the above
5
+copyright notice and this permission notice appear in all copies.
6
+
7
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 31 - 0
resources/web/ogvjs-1.8.6/LICENSE-vpx.txt

@@ -0,0 +1,31 @@
1
+Copyright (c) 2010, The WebM Project authors. All rights reserved.
2
+
3
+Redistribution and use in source and binary forms, with or without
4
+modification, are permitted provided that the following conditions are
5
+met:
6
+
7
+  * Redistributions of source code must retain the above copyright
8
+    notice, this list of conditions and the following disclaimer.
9
+
10
+  * Redistributions in binary form must reproduce the above copyright
11
+    notice, this list of conditions and the following disclaimer in
12
+    the documentation and/or other materials provided with the
13
+    distribution.
14
+
15
+  * Neither the name of Google, nor the WebM Project, nor the names
16
+    of its contributors may be used to endorse or promote products
17
+    derived from this software without specific prior written
18
+    permission.
19
+
20
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+

+ 23 - 0
resources/web/ogvjs-1.8.6/PATENTS-vpx.txt

@@ -0,0 +1,23 @@
1
+Additional IP Rights Grant (Patents)
2
+------------------------------------
3
+
4
+"These implementations" means the copyrightable works that implement the WebM
5
+codecs distributed by Google as part of the WebM Project.
6
+
7
+Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
8
+royalty-free, irrevocable (except as stated in this section) patent license to
9
+make, have made, use, offer to sell, sell, import, transfer, and otherwise
10
+run, modify and propagate the contents of these implementations of WebM, where
11
+such license applies only to those patent claims, both currently owned by
12
+Google and acquired in the future, licensable by Google that are necessarily
13
+infringed by these implementations of WebM. This grant does not include claims
14
+that would be infringed only as a consequence of further modification of these
15
+implementations. If you or your agent or exclusive licensee institute or order
16
+or agree to the institution of patent litigation or any other patent
17
+enforcement activity against any entity (including a cross-claim or
18
+counterclaim in a lawsuit) alleging that any of these implementations of WebM
19
+or any code incorporated within any of these implementations of WebM
20
+constitute direct or contributory patent infringement, or inducement of
21
+patent infringement, then any patent rights granted to you under this License
22
+for these implementations of WebM shall terminate as of the date such
23
+litigation is filed.

+ 391 - 0
resources/web/ogvjs-1.8.6/README.md

@@ -0,0 +1,391 @@
1
+ogv.js
2
+======
3
+
4
+Media decoder and player for Ogg Vorbis/Opus/Theora and WebM VP8/VP9/AV1 video.
5
+
6
+Based around libogg, libvorbis, libtheora, libopus, libvpx, libnestegg and dav1d compiled to JavaScript and WebAssembly with Emscripten.
7
+
8
+## Updates
9
+
10
+1.8.6 - 2022-01-12
11
+* Bump to yuv-canvas
12
+* Fix demo for removal of video-canvas mode
13
+
14
+1.8.5 - 2022-01-11
15
+* Remove unnecessary user-agent checks
16
+* Remove flaky, obsolete support for faking CSS `object-fit`
17
+* Remove experimental support for streaming `<canvas>` into `<video>`
18
+
19
+1.8.4 - 2021-07-02
20
+* Fix for fix for OGVLoader.base fix
21
+
22
+1.8.3 - 2021-07-02
23
+* Fixes for build with emscripten 2.0.25
24
+* Fix for nextTick/setImmediate-style polyfill in front-end
25
+* Provisional fix for OGVLoader.base not working with CDNs
26
+    * the fallback code for loading a non-local worker had been broken with WebAssembly for some time, sorry!
27
+
28
+1.8.2 - errored out
29
+
30
+1.8.1 - 2021-02-18
31
+* Fixed OGVCompat APIs to correctly return false without WebAssembly and Web Audio
32
+
33
+1.8.0 - 2021-02-09
34
+* Dropping IE support and Flash audio backend
35
+    * Updated to stream-file 0.3.0
36
+    * Updated to audio-feeder 0.5.0
37
+    * The old IE 10/11 support _no longer works_ due to the Flash plugin being disabled, and so is being removed
38
+* Drop es6-promise shim
39
+    * Now requires WebAssembly, which requires native Promise support
40
+* Build & fixes
41
+    * Demo fixed (removed test files that are now offline)
42
+    * Builds with emscripten 2.0.13
43
+    * Requires latest meson from git pending a fix hitting release
44
+
45
+1.7.0 - 2020-09-28
46
+* Builds with emscripten's LLVM upstream backend
47
+    * Updated to build with emscripten 2.0.4
48
+* Reduced amount of memory used between GC runs by reusing frame buffers
49
+* Removed `memoryLimit` option
50
+    * JS, Wasm, and threaded Wasm builds now all use dynamic memory growth
51
+* Updated dav1d
52
+* Updated libvpx to 1.8.1
53
+* Experimental SIMD builds of AV1 decoder optional, with `make SIMD=1`
54
+    * These work in Chrome with the "WebAssembly SIMD" flag enabled in chrome://flags/
55
+    * Significant speed boost when available.
56
+    * Available with and without multithreading.
57
+    * Must enable explicitly with `simd: true` in `options`.
58
+* Experimental SIMD work for VP9 as well, incomplete.
59
+
60
+1.6.1 - 2019-06-18
61
+* playbackSpeed attribute now supported
62
+* updated audio-feeder to 0.4.21;
63
+    * mono audio is now less loud, matching native playback better
64
+    * audio resampling now uses linear interpolation for upscaling
65
+    * fix for IE in bundling scenarios that use strict mode
66
+    * tempo change support thanks to a great patch from velochy!
67
+* updated yuv-canvas to 1.2.6;
68
+    * fixes for capturing WebGL canvas as MediaStream
69
+* fixes for seeks on low frame rate video
70
+* updated emscripten toolchain to 1.38.36
71
+    * drop OUTLINING_LIMIT from AV1 JS build; doesn't work in newer emscripten and not really needed
72
+
73
+1.6.0 - 2019-02-26
74
+* experimental support for AV1 video in WebM
75
+* update buildchain to emscripten 1.38.28
76
+* fix a stray global
77
+* starting to move to ES6 classes and modules
78
+* building with babel for ES5/IE11 compat
79
+* updated eslint
80
+* updated yuv-canvas to 1.2.4; fixes for software GL rendering
81
+* updated audio-feeder to 0.4.15; fixes for resampling and Flash perf
82
+* retooled buffer copies
83
+* sync fix for audio packets with discard padding
84
+* clients can pass a custom `StreamFile` instance as `{stream:foo}` in options. This can be useful for custom streaming until MSE interfaces are ready.
85
+* refactored WebM keyframe detection
86
+* prefill the frame pipeline as well as the audio pipeline before starting audio
87
+* removed BINARYEN_IGNORE_IMPLICIT_TRAPS=1 option which can cause intermittent breakages
88
+* changed download streaming method to avoid data corruption problem on certain files
89
+* fix for seek on very short WebM files
90
+* fix for replay-after-end-of-playback in WebM
91
+
92
+See more details and history in [CHANGES.md](https://github.com/brion/ogv.js/blob/master/CHANGES.md)
93
+
94
+## Current status
95
+
96
+Note that as of 2021 ogv.js works pretty nicely but may still have some packagine oddities with tools like webpack. It should work via CDNs again as of 1.8.2 if you can't or don't want to package locally, but this is not documented well yet. Improved documentation will come with the next major update & code cleanup!
97
+
98
+Since August 2015, ogv.js can be seen in action [on Wikipedia and Wikimedia Commons](https://commons.wikimedia.org/wiki/Commons:Video) in Safari and IE/Edge where native Ogg and WebM playback is not available. (See [technical details on MediaWiki integration](https://www.mediawiki.org/wiki/Extension:TimedMediaHandler/ogv.js).)
99
+
100
+See also a standalone demo with performance metrics at https://brionv.com/misc/ogv.js/demo/
101
+
102
+* streaming: yes (with Range header)
103
+* seeking: yes for Ogg and WebM (with Range header)
104
+* color: yes
105
+* audio: yes, with a/v sync (requires Web Audio or Flash)
106
+* background threading: yes (video, audio decoders in Workers)
107
+* [GPU accelerated drawing: yes (WebGL)](https://github.com/brion/ogv.js/wiki/GPU-acceleration)
108
+* GPU accelerated decoding: no
109
+* SIMD acceleration: no
110
+* Web Assembly: yes (with asm.js fallback)
111
+* multithreaded VP8, VP9, AV1: in development (set `options.threading` to `true`; requires flags to be enabled in Firefox 65 and Chrome 72, no support yet in Safari)
112
+* controls: no (currently provided by demo or other UI harness)
113
+
114
+Ogg and WebM files are fairly well supported.
115
+
116
+
117
+## Goals
118
+
119
+Long-form goal is to create a drop-in replacement for the HTML5 video and audio tags which can be used for basic playback of Ogg Theora and Vorbis or WebM media on browsers that don't support Ogg or WebM natively.
120
+
121
+The API isn't quite complete, but works pretty well.
122
+
123
+
124
+## Compatibility
125
+
126
+ogv.js requires a fast JS engine with typed arrays, and Web Audio for audio playback.
127
+
128
+The primary target browsers are (testing 360p/30fps and up):
129
+* Safari 6.1-12 on Mac OS X 10.7-10.14
130
+* Safari on iOS 10-11 64-bit
131
+
132
+Older versions of Safari have flaky JIT compilers. IE 9 and below lack typed arrays, and IE 10/11 no longer support an audio channel since the Flash plugin was sunset.
133
+
134
+(Note that Windows and Mac OS X can support Ogg and WebM by installing codecs or alternate browsers with built-in support, but this is not possible on iOS where all browsers are really Safari.)
135
+
136
+Testing browsers (these support .ogv and .webm natively):
137
+* Firefox 65
138
+* Chrome 73
139
+
140
+
141
+## Package installation
142
+
143
+Pre-built releases of ogv.js are available as [.zip downloads from the GitHub releases page](https://github.com/brion/ogv.js/releases) and through the npm package manager.
144
+
145
+You can load the `ogv.js` main entry point directly in a script tag, or bundle it through whatever build process you like. The other .js files must be made available for runtime loading, together in the same directory.
146
+
147
+ogv.js will try to auto-detect the path to its resources based on the script element that loads ogv.js or ogv-support.js. If you load ogv.js through another bundler (such as browserify or MediaWiki's ResourceLoader) you may need to override this manually before instantiating players:
148
+
149
+```
150
+  // Path to ogv-demuxer-ogg.js, ogv-worker-audio.js, etc
151
+  OGVLoader.base = '/path/to/resources';
152
+```
153
+
154
+To fetch from npm:
155
+
156
+```
157
+npm install ogv
158
+```
159
+
160
+The distribution-ready files will appear in 'node_modules/ogv/dist'.
161
+
162
+To load the player library into your browserify or webpack project:
163
+
164
+```
165
+var ogv = require('ogv');
166
+
167
+// Access public classes either as ogv.OGVPlayer or just OGVPlayer.
168
+// Your build/lint tools may be happier with ogv.OGVPlayer!
169
+ogv.OGVLoader.base = '/path/to/resources';
170
+var player = new ogv.OGVPlayer();
171
+```
172
+
173
+## Usage
174
+
175
+The `OGVPlayer` class implements a player, and supports a subset of the events, properties and methods from [HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) and [HTMLVideoElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement).
176
+
177
+```
178
+  // Create a new player with the constructor
179
+  var player = new OGVPlayer();
180
+
181
+  // Or with options
182
+  var player = new OGVPlayer({
183
+	debug: true,
184
+	debugFilter: /demuxer/
185
+  });
186
+
187
+  // Now treat it just like a video or audio element
188
+  containerElement.appendChild(player);
189
+  player.src = 'path/to/media.ogv';
190
+  player.play();
191
+  player.addEventListener('ended', function() {
192
+    // ta-da!
193
+  });
194
+```
195
+
196
+To check for compatibility before creating a player, include `ogv-support.js` and use the `OGVCompat` API:
197
+
198
+```
199
+  if (OGVCompat.supported('OGVPlayer')) {
200
+    // go load the full player from ogv.js and instantiate stuff
201
+  }
202
+```
203
+
204
+This will check for typed arrays, web audio, blacklisted iOS versions, and super-slow/broken JIT compilers.
205
+
206
+If you need a URL versioning/cache-buster parameter for dynamic loading of `ogv.js`, you can use the `OGVVersion` symbol provided by `ogv-support.js` or the even tinier `ogv-version.js`:
207
+
208
+```
209
+  var script = document.createElement('script');
210
+  script.src = 'ogv.js?version=' + encodeURIComponent(OGVVersion);
211
+  document.querySelector('head').appendChild(script);
212
+```
213
+
214
+## Distribution notes
215
+
216
+Entry points:
217
+* `ogv.js` contains the main runtime classes, including OGVPlayer, OGVLoader, and OGVCompat.
218
+* `ogv-support.js` contains the OGVCompat class and OGVVersion symbol, useful for checking for runtime support before loading the main `ogv.js`.
219
+* `ogv-version.js` contains only the OGVVersion symbol.
220
+
221
+These entry points may be loaded directly from a script element, or concatenated into a larger project, or otherwise loaded as you like.
222
+
223
+Further code modules are loaded at runtime, which must be available with their defined names together in a directory. If the files are not hosted same-origin to the web page that includes them, you will need to set up appropriate CORS headers to allow loading of the worker JS modules.
224
+
225
+Dynamically loaded assets:
226
+* `ogv-worker-audio.js`, `ogv-worker-video.js`, and `*.worker.js` are Worker entry points, used to run video and audio decoders in the background.
227
+* `ogv-demuxer-ogg-wasm.js/.wasm` are used in playing .ogg, .oga, and .ogv files.
228
+* `ogv-demuxer-webm-wasm.js/.wasm` are used in playing .webm files.
229
+* `ogv-decoder-audio-vorbis-wasm.js/.wasm` and `ogv-decoder-audio-opus-wasm.js/.wasm` are used in playing both Ogg and WebM files containing audio.
230
+* `ogv-decoder-video-theora-wasm.js/.wasm` are used in playing .ogg and .ogv video files.
231
+* `ogv-decoder-video-vp8-wasm.js/.wasm` and `ogv-decoder-video-vp9-wasm.js/.wasm` are used in playing .webm video files.
232
+* `*-mt.js/.wasm` are the multithreaded versions of some of the above modules. They have additional support files.
233
+
234
+If you know you will never use particular formats or codecs you can skip bundling them; for instance if you only need to play Ogg files you don't need `ogv-demuxer-webm-wasm.js` or `ogv-decoder-video-vp8-wasm.js` which are only used for WebM.
235
+
236
+
237
+## Performance
238
+
239
+(This section is somewhat out of date.)
240
+
241
+As of 2015, for SD-or-less resolution basic Ogg Theora decoding speed is reliable on desktop and newer high-end mobile devices; current high-end desktops and laptops can even reach HD resolutions. Older and low-end mobile devices may have difficulty on any but audio and the lowest-resolution video files.
242
+
243
+WebM VP8/VP9 is slower, but works pretty well at a resolution step below Theora.
244
+
245
+AV1 is slower still, and tops out around 360p for single-threaded decoding on a fast desktop or iOS device.
246
+
247
+*Low-res targets*
248
+
249
+I've gotten acceptable performance for Vorbis audio and 160p/15fps Theora files on 32-bit iOS devices: iPhone 4s, iPod Touch 5th-gen and iPad 3. These have difficulty at 240p and above, and just won't keep up with higher resolutions.
250
+
251
+Meanwhile, newer 64-bit iPhones and iPads are comparable to low-end laptops, and videos at 360p and often 480p play acceptably. Since 32-bit and 64-bit iOS devices have the same user-agent, a benchmark must be used to approximately test minimum CPU speed.
252
+
253
+(On iOS, Safari performs significantly better than some alternative browsers that are unable to enable the JIT due to use of the old UIWebView API. Chrome 49 and Firefox for iOS are known to work using the newer WKWebView API internally. Again, a benchmark must be used to detect slow performance, as the browser remains otherwise compatible.)
254
+
255
+
256
+Windows on 32-bit ARM platforms is similar... IE 11 on Windows RT 8.1 on a Surface tablet (NVidia Tegra 3) does not work (crashes IE), while Edge on Windows 10 Mobile works ok at low resolutions, having trouble starting around 240p.
257
+
258
+
259
+In both cases, a native application looms as a possibly better alternative. See [OGVKit](https://github.com/brion/OGVKit) and [OgvRt](https://github.com/brion/OgvRT) projects for experiments in those directions.
260
+
261
+
262
+Note that at these lower resolutions, Vorbis audio and Theora video decoding are about equally expensive operations -- dual-core phones and tablets should be able to eke out a little parallelism here thanks to audio and video being in separate Worker threads.
263
+
264
+
265
+*WebGL drawing acceleration*
266
+
267
+Accelerated YCbCr->RGB conversion and drawing is done using WebGL on supporting browsers, or through software CPU conversion if not. This is abstracted in the [yuv-canvas](https://github.com/brion/yuv-canvas) package, now separately installable.
268
+
269
+It may be possible to do further acceleration of actual decoding operations using WebGL shaders, but this could be ... tricky. WebGL is also only available on the main thread, and there are no compute shaders yet so would have to use fragment shaders.
270
+
271
+
272
+## Difficulties
273
+
274
+*Threading*
275
+
276
+Currently the video and audio codecs run in worker threads by default, while the demuxer and player logic run on the UI thread. This seems to work pretty well.
277
+
278
+There is some overhead in extracting data out of each emscripten module's heap and in the thread-to-thread communications, but the parallelism and smoother main thread makes up for it.
279
+
280
+*Streaming download*
281
+
282
+Streaming buffering is done by chunking the requests at up to a megabyte each, using the HTTP Range header. For cross-site playback, this requires CORS setup to whitelist the Range header! Chunks are downloaded as ArrayBuffers, so a chunk must be loaded in full before demuxing or playback can start.
283
+
284
+Old versions of [Safari have a bug with Range headers](https://bugs.webkit.org/show_bug.cgi?id=82672) which is worked around as necessary with a 'cache-busting' URL string parameter.
285
+
286
+
287
+*Seeking*
288
+
289
+Seeking is implemented via the HTTP Range: header.
290
+
291
+For Ogg files with keyframe indices in a skeleton index, seeking is very fast. Otherwise,  a bisection search is used to locate the target frame or audio position, which is very slow over the internet as it creates a lot of short-lived HTTP requests.
292
+
293
+For WebM files with cues, efficient seeking is supported as well as of 1.1.2. WebM files without cues can be seeked in 1.5.5, but inefficiently via linear seek from the beginning. This is fine for small audio-only files, but might be improved for large files with a bisection in future.
294
+
295
+As with chunked streaming, cross-site playback requires CORS support for the Range header.
296
+
297
+
298
+*Audio output*
299
+
300
+Audio output is handled through the [AudioFeeder](https://github.com/brion/audio-feeder) library, which encapsulates use of Web Audio API:
301
+
302
+Firefox, Safari, Chrome, and Edge support the W3C Web Audio API.
303
+
304
+IE is no longer supported; the workaround using Flash no longer works due to sunsetting of the Flash plugin.
305
+
306
+A/V synchronization is performed on files with both audio and video, and seems to actually work. Yay!
307
+
308
+Note that autoplay with audio doesn't work on iOS Safari due to limitations with starting audio playback from event handlers; if playback is started outside an event handler, the player will hang due to broken audio.
309
+
310
+As of 1.1.1, muting before script-triggered playback allows things to work:
311
+
312
+```
313
+  player = new OGVPlayer();
314
+  player.muted = true;
315
+  player.src = 'path/to/file-with-audio.ogv';
316
+  player.play();
317
+```
318
+
319
+You can then unmute the video in response to a touch or click handler. Alternately if audio is not required, do not include an audio track in the file.
320
+
321
+
322
+*WebM*
323
+
324
+WebM support was added in June 2015, with some major issues finally worked out in May 2016. Initial VP9 support was added in February 2017. It's pretty stable in production use at Wikipedia and is enabled by default as of October 2015.
325
+
326
+Beware that performance of WebM VP8 is much slower than Ogg Theora, and VP9 is slightly slower still.
327
+
328
+For best WebM decode speed, consider encoding VP8 with "profile 1" (simple deblocking filter) which will sacrifice quality modestly, mainly in high-motion scenes. When encoding with ffmpeg, this is the `-profile:v 1` option to the `libvpx` codec.
329
+
330
+It is also recommended to use the `-slices` option for VP8, or `-tile-columns` for VP9, to maximize ability to use multithreaded decoding when available in the future.
331
+
332
+*AV1*
333
+
334
+WebM files containing the AV1 codec are supported as of 1.6.0 (February 2019) using the [dav1d](https://code.videolan.org/videolan/dav1d) decoder.
335
+
336
+Currently this is experimental, and does not advertise support via `canPlayType`.
337
+
338
+Performance is about 2-3x slower than VP8 or VP9, and may require bumping down a resolution step or two to maintain frame rate. There may be further optimizations that can be done to improve this a bit, but the best improvements will come from future improvements to WebAssembly multithreading and SIMD.
339
+
340
+Currently AV1 in MP4 container is not supported.
341
+
342
+## Upstream library notes
343
+
344
+We've experimented with tremor (libivorbis), an integer-only variant of libvorbis. This actually does *not* decode faster, but does save about 200kb off our generated JavaScript, presumably thanks to not including an encoder in the library. However on slow devices like iPod Touch 5th-generation, it makes a significant negative impact on the decode time so we've gone back to libvorbis.
345
+
346
+The Ogg Skeleton library (libskeleton) is a bit ... unfinished and is slightly modified here.
347
+
348
+libvpx is slightly modified to work around emscripten threading limitations in the VP8 decoder.
349
+
350
+
351
+## WebAssembly
352
+
353
+WebAssembly (Wasm) builds are used exclusively as of 1.8.0, as Safari's Wasm support is pretty well established now and IE no longer works due to the Flash plugin deprecation.
354
+
355
+
356
+## Multithreading
357
+
358
+Experimental multithreaded VP8, VP9, and AV1 decoding up to 4 cores is in development, requiring emscripten 1.38.27 to build.
359
+
360
+Multithreading is used only if `options.threading` is true. This requires browser support for the new `SharedArrayBuffer` and `Atomics` APIs, currently available in Firefox and Chrome with experimental flags enabled.
361
+
362
+Threading currently requires WebAssembly; JavaScript builds are possible but perform poorly.
363
+
364
+Speedups will only be noticeable when using the "slices" or "token partitions" option for VP8 encoding, or the "tile columns" option for VP9 encoding.
365
+
366
+If you are making a slim build and will not use the `threading` option, you can leave out the `*-mt.*` files.
367
+
368
+
369
+## Building JS components
370
+
371
+Building ogv.js is known to work on Mac OS X and Linux (tested Fedora 29 and Ubuntu 18.10 with Meson manually updated).
372
+
373
+1. You will need autoconf, automake, libtool, pkg-config, meson, ninja, and node (nodejs). These can be installed through Homebrew on Mac OS X, or through distribution-specific methods on Linux. For meson, you may need a newer version than your distro packages -- install it manually with `pip3` or from source.
374
+2. Install [Emscripten](http://kripken.github.io/emscripten-site/docs/getting_started/Tutorial.html); currently building with 2.0.13.
375
+3. `git submodule update --init`
376
+4. Run `npm install` to install build utilities
377
+5. Run `make js` to configure and build the libraries and the C wrapper
378
+
379
+
380
+## Building the demo
381
+
382
+If you did all the setup above, just run `make demo` or `make`. Look in build/demo/ and enjoy!
383
+
384
+
385
+## License
386
+
387
+libogg, libvorbis, libtheora, libopus, nestegg, libvpx, and dav1d are available under their respective licenses, and the JavaScript and C wrapper code in this repo is licensed under MIT.
388
+
389
+Based on build scripts from https://github.com/devongovett/ogg.js
390
+
391
+See [AUTHORS.md](https://github.com/brion/ogv.js/blob/master/AUTHORS.md) and/or the git history for a list of contributors.

Разница между файлами не показана из-за своего большого размера
+ 39 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-audio-opus-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 40 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-audio-vorbis-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 21 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-mt-wasm.worker.js


Разница между файлами не показана из-за своего большого размера
+ 21 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-mt-wasm.worker.js


Разница между файлами не показана из-за своего большого размера
+ 43 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-simd-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 43 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-av1-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 42 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-theora-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-theora-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 21 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-mt-wasm.worker.js


Разница между файлами не показана из-за своего большого размера
+ 44 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp8-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 21 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-mt-wasm.worker.js


Разница между файлами не показана из-за своего большого размера
+ 21 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.wasm


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-mt-wasm.worker.js


Разница между файлами не показана из-за своего большого размера
+ 45 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.js


BIN
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-simd-wasm.wasm


+ 0 - 0
resources/web/ogvjs-1.8.6/ogv-decoder-video-vp9-wasm.js


Некоторые файлы не были показаны из-за большого количества измененных файлов