diff --git a/.codacy/cli.sh b/.codacy/cli.sh index 7057e3b..8a3e679 100755 --- a/.codacy/cli.sh +++ b/.codacy/cli.sh @@ -59,6 +59,20 @@ get_latest_version() { echo "$version" } +get_latest_cached_version() { + if [ -d "$CODACY_CLI_V2_TMP_FOLDER" ]; then + # Pick the highest version folder already present on disk. + # This is used as a fallback when the GitHub API is unavailable and the cache is populated. + local cached + cached=$(ls -1 "$CODACY_CLI_V2_TMP_FOLDER" 2>/dev/null | grep -v '^version\.yaml$' | sort -V | tail -n 1) + if [ -n "$cached" ]; then + echo "$cached" + return 0 + fi + fi + return 1 +} + handle_rate_limit() { local response="$1" if echo "$response" | grep -q "API rate limit exceeded"; then @@ -123,7 +137,18 @@ fi if [ -n "$CODACY_CLI_V2_VERSION" ]; then version="$CODACY_CLI_V2_VERSION" else - version=$(get_version_from_yaml) + version=$(get_version_from_yaml || true) + if [ -z "$version" ]; then + # version.yaml can be left with an empty version when the GitHub API call fails. + # Fall back to any already cached version first; if none exists, fetch latest. + version=$(get_latest_cached_version || true) + if [ -z "$version" ]; then + echo "ℹ️ Fetching latest version..." + version=$(get_latest_version) + fi + mkdir -p "$CODACY_CLI_V2_TMP_FOLDER" + echo "version: \"$version\"" > "$version_file" + fi fi diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml index 15365c7..0208f94 100644 --- a/.codacy/codacy.yaml +++ b/.codacy/codacy.yaml @@ -1,15 +1,4 @@ -runtimes: - - dart@3.7.2 - - go@1.22.3 - - java@17.0.10 - - node@22.2.0 - - python@3.11.11 tools: - - dartanalyzer@3.7.2 - - eslint@8.57.0 - - lizard@1.17.31 - - pmd@7.11.0 - - pylint@3.3.6 - - revive@1.7.0 - - semgrep@1.78.0 - trivy@0.66.0 + - semgrep@1.78.0 +7.2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5929935 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.sh text eol=lf +*.bash text eol=lf \ No newline at end of file diff --git a/.github/github_copilot_instructions.md b/.github/github_copilot_instructions.md new file mode 100644 index 0000000..c130e72 --- /dev/null +++ b/.github/github_copilot_instructions.md @@ -0,0 +1,182 @@ +# GitHub Copilot Instructions + +> Datei: `.github/copilot-instructions.md` + +## Zweck +Diese Datei gibt GitHub Copilot (inkl. Copilot Chat) repository‑weite Hinweise, wie Vorschläge, Code‑Snippets und Antworten formuliert werden sollen. Sie ist auf ein Multi‑Language‑Projekt ausgelegt: **C/C++**, **Rust**, **Dart/Flutter** (plus Hinweise zu `uv`‑basierten I/O‑Bindings), sowie generelle Tools wie **ruff**. Ziel ist konsistente Code‑Qualität, starke Typisierung und sichere Vorschläge. + +Hinweis: In diesem Dokument meint „**uv**“ in zwei Kontexten unterschiedliche Dinge: +- **Python `uv`** = Package Manager/Runner. +- **libuv** = native I/O‑Library (siehe Abschnitt zu libuv‑Bindings). + +--- + +## Kurze Zusammenfassung / About +Dieses Repository enthält native Komponenten (C/C++), Rust‑Bibliotheken, und eine Flutter‑UI (Dart). Der Fokus liegt auf Performance, Cross‑Language Interop, deterministischem Verhalten und starker Typisierung. + +--- + +## Repository‑Scope +- **Behandeln mit Vorrang:** `src/`, `lib/`, `rust/`, `flutter/`, `native/`, `include/`, `tests/`. +- **Ignorieren / nur mit Vorsicht ändern:** `third_party/`, `vendor/`, `build/`, `artifacts/`. +- **CI/Infra:** Vorschläge für CI (z. B. GitHub Actions) nur wenn klarer Nutzen erkennbar; keine ungeprüften Änderungen an Workflows ohne Review. + +--- + +## Personality / Ton +- Sprache: Deutsch (bei technischen Kommentaren kurze englische Code‑Begriffe ok). +- Ton: Präzise, technisch, knapp. 1–3 Sätze Erklärung plus minimaler Beispielcode, wenn nötig. +- Umfang: Bei trivialen Änderungen kurz; bei Architektur/Interop ausführlicher (aber nicht ausschweifend). + +--- + +## Starke Typisierung (allgemein) +- Priorisiere klar typisierte Lösungen: + - **C/C++:** explizite Typen, keine zu weiten `auto`‑Verwendungen an API‑Borders. + - **Rust:** Typinferenz nutzen, aber Signaturen und `Result`/`Option`-Fehlerpfade explizit behandeln. + - **Dart:** Null‑Safety strikt beachten, Typannotationen für öffentliche API‑Signaturen. +- Copilot soll Typannotation vorschlagen, falls weggelassen. + +### Type‑Safety‑Policy (maximal) +- Öffentliche APIs (inkl. FFI‑Grenzen) müssen vollständig typisiert sein; keine „untyped“ Escape‑Hatches (`void*`, `dynamic`, „Any“) ohne Begründung. +- Fehlerpfade müssen typisiert sein (z. B. `Result`/`Option`, `std::optional`/Fehlercode, Dart Exceptions nur wenn idiomatisch). +- Narrowing/implizite Konvertierungen vermeiden; bevorzugt explizite Casts und Wrapper‑Typen. +- Wenn Tooling/Compiler das hergeben: Warnings werden in CI als Errors behandelt. + +--- + +## Code Style & Tooling +**C/C++** +- Target standard: **C++20**. +- Build‑System‑Policy (falls CMake): `CMAKE_CXX_STANDARD=20`, `CMAKE_CXX_STANDARD_REQUIRED=ON`, `CMAKE_CXX_EXTENSIONS=OFF`. +- Format: `clang-format` (Repo‑konfiguration verwenden). Vorschläge müssen `clang-format`-konform sein. +- Lints: `clang-tidy` Empfehlungen sind willkommen; vermeide invasive API‑Änderungen. +- Type‑Safety bevorzugen: + - Keine Ownership über rohe Pointer: RAII (`std::unique_ptr`, `std::shared_ptr`) und klare Lifetimes. + - Für Größen/Indizes: `std::size_t`/`ptrdiff_t` statt `int`; für ABI/FFI: feste Breiten (`std::uint32_t` etc.). + - `enum class` statt unscoped enums; `[[nodiscard]]` für Rückgaben, die nicht ignoriert werden sollen. + - Für Views: `std::span`, `std::string_view` (keine rohen Buffer+Len‑Paare ohne Wrapper). + - Keine `#define` für Konstanten/Typen; `constexpr`/`consteval`/`using`. +- Compiler‑Strenge (als Vorschlag, projektabhängig): + - Clang/GCC: `-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow -Wnull-dereference`. + - In CI nach Möglichkeit: `-Werror` (oder selektiv `-Werror=`). +- Windows/MSVC (falls genutzt): `/W4 /WX /permissive-` und nach Möglichkeit SDL‑Hardening (`/sdl`). +- Runtime‑Checks (Debug/CI): Sanitizer‑Builds (`ASan`, `UBSan`, optional `TSan`) für native Komponenten. +- Tests: Google Test (gtest). Vorschläge für Tests sollen kleine, deterministische Cases enthalten. + +**Rust** +- Format: `rustfmt`. Linter: `clippy` — Copilot soll `clippy`-freundliche Patterns bevorzugen. +- Error Handling: Verwende `Result`/`?`-Operatoren idiomatisch; keine `unwrap()` in Produktionscode. +- Type‑Safety: + - `unsafe` nur wenn notwendig; klein halten, kapseln, und Safety‑Invarianten in einem kurzen Kommentar festhalten. + - In CI bevorzugt: `cargo clippy -- -D warnings` und (falls passend) `#![deny(warnings)]`/`#![deny(clippy::all)]` auf Crate‑Level. + - Optional (wenn Crate es zulässt): `#![forbid(unsafe_code)]` in rein „safe“ Crates. +- Tests: `cargo test` mit klaren Unit‑Tests. + +**Dart / Flutter** +- Format: `dart format`. Null‑safety verpflichtend. +- Architektur: Trenne UI/Business/Platform (z. B. Provider/Bloc/riverpod‑Pattern) — Copilot soll keine monolithischen Widgets vorschlagen. +- Type‑Safety: + - `analysis_options.yaml` soll strikte Analyse aktivieren (z. B. `strict-casts`, `strict-inference`, `strict-raw-types`). + - Keine `dynamic`‑Leaks in Public APIs; generische Typen überall vollständig ausfüllen. + - Prefer `flutter_lints`/`lints` (projektabhängig) und behandle Analyzer‑Warnings in CI als Fehler. +- Tests: `flutter_test` für Widgets + Unit‑Tests. + +**Python (uv + ruff + ty)** +- Package Manager/Runner: **`uv`** (Dependencies gehören in `pyproject.toml`; Lockfile/Sync über `uv`). +- Tools sollen über `uv run …` ausgeführt werden (kein „global pip“, kein ungepinnter Tool‑Mix). +- Lint/Style: `ruff` (und optional Formatter nach Projektpolicy). +- Typisierung: **`ty`** als Type Checker; Copilot soll Typannotationen vollständig ergänzen und `Any` vermeiden. +- Type‑Safety Regeln: + - `from __future__ import annotations` bevorzugen (falls Projektpolicy), und öffentliche APIs vollständig annotieren. + - Keine stillen `Any`‑Leaks: `Any`/`cast()`/`# type: ignore` nur mit Begründung und so lokal wie möglich. + - Collections/Generics immer parametrisieren (`list[str]` statt `list`). + +--- + +## CI / Checks (nur Kommandos, keine Workflow‑Änderungen) +Copilot soll bei „How to validate“ bevorzugt konkrete, reproduzierbare Kommandos vorschlagen (an Repo‑Tooling anpassen): +- C/C++: configure/build via CMake Presets oder `cmake -S . -B build` + `cmake --build build` + Tests (`ctest`). +- clang-format: `clang-format -i` (nur auf geänderten Dateien) oder ein Check‑Target. +- clang-tidy: nur wenn `compile_commands.json` vorhanden ist; keine massiven Auto‑Fixes ohne Review. +- Rust: `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`. +- Flutter: `dart format --output=none --set-exit-if-changed .`, `dart analyze`, `flutter test`. +- Python (uv): `uv sync` (ggf. „frozen“/locked nach Projektpolicy), dann `uv run ruff check .` und `uv run ty` (bzw. `uv run ty check`, je nach Tool‑Konfiguration). + +**I/O / libuv‑basierte Bindings** +- Für libuv/uvloop/uvicorn‑artige APIs: keine blockierenden Aufrufe in Event‑Loop‑Callbacks vorschlagen. +- Prefer async patterns and explicit threading/futures when interacting with native I/O. + +--- + +## Testing & Qualität +- Neue Features sollten Unit‑Tests enthalten. Copilot soll Test‑Skeletons vorschlagen, die vorhandene Fixtures nutzen. +- Vermeide flakige Tests; prefer deterministic seeds and small inputs. +- Coverage: Mindestens smoke tests für kritische native bindings. + +--- + +## Interop (C/C++ <-> Rust <-> Dart) +- Copilot soll sicherheitsbewusst bei FFI/Bindings handeln: null/ownership/ABI‑stability prüfen. +- Bei Vorschlägen für Bindings: automatische Free/Fallocate Regeln, `unsafe`-Blocks in Rust nur mit Kommentar. +- Keine Code‑Generierung für Bindings ohne Hinweis (z. B. `cbindgen`, `flutter_rust_bridge`) und vorgeschlagenen Tests. + +### FFI‑Typregeln (konkret) +- C ABI: `extern "C"`, stabile, explizite Layouts; keine C++‑Typen über ABI (z. B. `std::string`, Exceptions) exportieren. +- Pointer/Nullability: Nullability im Typ ausdrücken (z. B. `Option>`/`*mut T` + Dokumentation); nie „silent null“. +- Ownership: Erzeuger/Zerstörer‑Paare oder klare „borrowed vs owned“ Konventionen; keine impliziten Frees. +- Fehler: über ABI entweder Fehlercodes + `out`‑Parameter oder klar definierte Ergebnis‑Structs; keine Exceptions über FFI. + +--- + +## Sicherheit & Geheimnisse +- Niemals API‑Keys, Passwörter, private Zertifikate oder sonstige Secrets einchecken oder vorschlagen. +- Wenn Copilot möglichen Secret‑Leak erkennt, soll es auf Vault/Secret‑Manager oder `.gitignore` hinweisen. + +--- + +## Lizenz & rechtliche Hinweise +- Repository‑Lizenz: bitte hier eintragen (z. B. MIT / Apache‑2.0). Copilot soll Lizenz‑Header nur vorschlagen, wenn klar passend. + +--- + +## Commit‑ und PR‑Konventionen +- Commit‑Format: **Conventional Commits** (`feat:`, `fix:`, `chore:`, `docs:` etc.). +- PRs: kurze Zusammenfassung + „Changes“ + „Testing/How to validate“ und CI‑Status. +- Keine squash‑commits für sicherheitsrelevante Änderungen ohne Review. + +--- + +## Do / Don't (kurz) +**Do** +- Kleine, überprüfbare Änderungen vorschlagen. +- Typannotationen an öffentliche APIs ergänzen. +- Tests + CI‑Checks vorschlagen. + +**Don't** +- Große, invasive Refactorings ohne PR‑Diskussion vorschlagen. +- Secrets oder unsichere Defaults einfügen. +- Ungetestete native Änderungen direkt vorschlagen (z. B. ABI‑Änderungen ohne Tests). + +--- + +## Beispiele / Snippets +> *Hinweis: konkrete Codebeispiele und Style‑Configs (z. B. `.clang-format`, `rustfmt.toml`, `analysis_options.yaml`) sollten projekt‑spezifisch ergänzt werden.* + +--- + +## Verhalten bei Unsicherheit +- Wenn Anforderungen unklar sind, soll Copilot Rückfragen vorschlagen (z. B. "Welcher C++ Standard wird verwendet?") anstatt zu raten. +- Für kritische Bereiche (Memory/FFI/Concurrency) präferiere conservative, safe Vorschläge und verweise auf Tests. + +--- + +## Anpassung & Pflege +- Passe die Datei an, wenn der C++ Standard wechselt oder neue CI‑Checks/Tools (z. B. OSS security scanners) hinzukommen. + +--- + +*Ende der projekt‑spezifischen Copilot‑Anleitung.* + +*Wenn du möchtest: Ich kann noch spezifische Beispiele für `.clang-format`, `rustfmt.toml` und `analysis_options.yaml` (für Dart) hinzufügen — oder die Datei auf Englisch übersetzen.* + diff --git a/.github/workflows/dart_build_android_app.yml b/.github/workflows/dart_build_android_app.yml new file mode 100644 index 0000000..58f1bed --- /dev/null +++ b/.github/workflows/dart_build_android_app.yml @@ -0,0 +1,171 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Build + test + run android app + +on: + push: + branches: ["main", "develop"] + pull_request: + branches: ["main", "develop"] + +env: + APP_NAME: kataglyphis-inference-engine-apk + FLUTTER_VERSION: 3.38.9 # change here to update version for the whole workflow + FLUTTER_DIR: /workspace/flutter # Flutter SDK installation directory + +jobs: + build: + strategy: + matrix: + include: + - runs_on: ubuntu-24.04 + arch: x64 + platform: linux/amd64 + + runs-on: ${{ matrix.runs_on }} + + steps: + - name: Free Disk Space on Host + uses: jlumbroso/free-disk-space@main + with: + tool-cache: true + android: true + dotnet: true + haskell: true + large-packages: true + docker-images: false + swap-storage: true + + - uses: actions/checkout@v6.0.0 + with: + fetch-depth: 0 + submodules: recursive + + - name: Create swapfile on runner (helps avoid OOM during linking) + run: | + # create 6 GiB swapfile (falls back to dd if fallocate fails) + sudo swapoff -a || true + if sudo test -f /swapfile; then + echo "/swapfile already exists" + else + if sudo fallocate -l 6G /swapfile; then + echo "fallocate succeeded" + else + echo "fallocate failed, using dd" + sudo dd if=/dev/zero of=/swapfile bs=1M count=6144 + fi + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile + echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab + fi + swapon --show || true + free -h || true + + - name: Login to GitHub Container Registry + run: echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Pull container image + run: | + for i in 1 2 3; do + echo "Attempt $i to pull container..." + if timeout 900 docker pull ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest; then + echo "Successfully pulled container" + exit 0 + fi + echo "Pull failed, waiting before retry..." + sleep 30 + done + echo "Failed to pull container after 3 attempts" + exit 1 + + - name: Setup Flutter in container + run: | + docker run --rm \ + --platform ${{ matrix.platform }} \ + -v ${{ github.workspace }}:/workspace \ + -w /workspace \ + -e FLUTTER_VERSION=${{ env.FLUTTER_VERSION }} \ + -e FLUTTER_DIR=${{ env.FLUTTER_DIR }} \ + -e MATRIX_ARCH=${{ matrix.arch }} \ + ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ + bash -lc ' + set -e + git config --global --add safe.directory /workspace || true + git config --global --add safe.directory ${FLUTTER_DIR} || true + + chmod +x scripts/linux/setup-flutter-x86-64.sh + ./scripts/linux/setup-flutter-x86-64.sh $FLUTTER_VERSION $(dirname ${FLUTTER_DIR}) + ' + + - name: Run Flutter checks and tests + run: | + docker run --rm \ + --platform ${{ matrix.platform }} \ + -v ${{ github.workspace }}:/workspace \ + -w /workspace \ + ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ + bash -lc ' + set -e + export PATH="$PWD/flutter/bin:$PATH" + git config --global --add safe.directory /workspace || true + git config --global --add safe.directory ${FLUTTER_DIR} || true + + source ~/.bashrc + export PATH="${FLUTTER_DIR}/bin:$PATH" + + flutter pub get + dart format --output=none --set-exit-if-changed . || true + dart analyze || true + flutter test || true + flutter config --enable-android + ' + + - name: Build Flutter Android app + run: | + docker run --rm \ + --platform ${{ matrix.platform }} \ + -v ${{ github.workspace }}:/workspace \ + -w /workspace \ + -e APP_NAME=${{ env.APP_NAME }} \ + -e MATRIX_ARCH=${{ matrix.arch }} \ + ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ + bash -lc ' + set -e + export PATH="$PWD/flutter/bin:$PATH" + git config --global --add safe.directory /workspace || true + git config --global --add safe.directory ${FLUTTER_DIR} || true + + source ~/.bashrc + export PATH="${FLUTTER_DIR}/bin:$PATH" + + flutter build apk --release + ' + + - name: Package build artifacts + run: | + docker run --rm \ + --platform ${{ matrix.platform }} \ + -v ${{ github.workspace }}:/workspace \ + -w /workspace \ + -e APP_NAME=${{ env.APP_NAME }} \ + -e MATRIX_ARCH=${{ matrix.arch }} \ + ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ + bash -lc ' + set -e + rm -rf build/linux/$MATRIX_ARCH/release/obj || true + rm -rf ~/.pub-cache/hosted || true + mkdir -p out + cp -r build/app/outputs/flutter-apk out/${APP_NAME}-bundle + tar -C out -czf ${APP_NAME}-linux-$MATRIX_ARCH.tar.gz ${APP_NAME}-bundle + ' + + - name: Upload artifact + uses: actions/upload-artifact@v6.0.0 + with: + name: ${{ env.APP_NAME }}-linux-${{ matrix.arch }}-tar + path: ${{ env.APP_NAME }}-linux-${{ matrix.arch }}.tar.gz + diff --git a/.github/workflows/dart_on_linux.yml b/.github/workflows/dart_on_native_linux.yml similarity index 71% rename from .github/workflows/dart_on_linux.yml rename to .github/workflows/dart_on_native_linux.yml index 216be2b..e862d02 100644 --- a/.github/workflows/dart_on_linux.yml +++ b/.github/workflows/dart_on_native_linux.yml @@ -1,9 +1,12 @@ # This workflow uses actions that are not certified by GitHub. + # They are provided by a third-party and are governed by + # separate terms of service, privacy policy, and support + # documentation. -name: Build + run + test on Linux +name: Build + test + run on Linux natively - x86-64/arm64 on: push: @@ -13,7 +16,8 @@ on: env: APP_NAME: kataglyphis-inference-engine - FLUTTER_VERSION: 3.38.3 # change here to update version for the whole workflow + FLUTTER_VERSION: 3.38.9 # change here to update version for the whole workflow + FLUTTER_DIR: /workspace/flutter # Flutter SDK installation directory jobs: build: @@ -38,12 +42,19 @@ jobs: dotnet: true haskell: true large-packages: true - docker-images: false + docker-images: true swap-storage: true - - uses: actions/checkout@v6.0.0 + - name: Prune Docker + show disk usage + run: | + set -eux + docker system prune -af --volumes || true + docker system df || true + df -h + + - uses: actions/checkout@v6.0.2 with: - fetch-depth: 0 + fetch-depth: 1 submodules: recursive - name: Login to GitHub Container Registry @@ -55,6 +66,7 @@ jobs: echo "Attempt $i to pull container..." if timeout 900 docker pull ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest; then echo "Successfully pulled container" + docker system df || true exit 0 fi echo "Pull failed, waiting before retry..." @@ -70,20 +82,23 @@ jobs: -v ${{ github.workspace }}:/workspace \ -w /workspace \ -e FLUTTER_VERSION=${{ env.FLUTTER_VERSION }} \ + -e FLUTTER_DIR=${{ env.FLUTTER_DIR }} \ -e MATRIX_ARCH=${{ matrix.arch }} \ ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ bash -lc ' set -e git config --global --add safe.directory /workspace || true - git config --global --add safe.directory /workspace/flutter || true + git config --global --add safe.directory ${FLUTTER_DIR} || true if [ "$MATRIX_ARCH" = "x64" ]; then chmod +x scripts/linux/setup-flutter-x86-64.sh - ./scripts/linux/setup-flutter-x86-64.sh $FLUTTER_VERSION + ./scripts/linux/setup-flutter-x86-64.sh $FLUTTER_VERSION $(dirname ${FLUTTER_DIR}) else chmod +x scripts/linux/setup-flutter-arm64.sh - ./scripts/linux/setup-flutter-arm64.sh $FLUTTER_VERSION + ./scripts/linux/setup-flutter-arm64.sh $FLUTTER_VERSION $(dirname ${FLUTTER_DIR}) fi + chmod -R u+rwX ${FLUTTER_DIR}/bin/cache 2>/dev/null || true + chmod -R u+rwX ${FLUTTER_DIR} 2>/dev/null || true ' - name: Run Flutter checks and tests @@ -92,12 +107,17 @@ jobs: --platform ${{ matrix.platform }} \ -v ${{ github.workspace }}:/workspace \ -w /workspace \ + -e FLUTTER_DIR=${{ env.FLUTTER_DIR }} \ ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ bash -lc ' set -e - export PATH="$PWD/flutter/bin:$PATH" + git config --global --add safe.directory /workspace || true - git config --global --add safe.directory /workspace/flutter || true + git config --global --add safe.directory ${FLUTTER_DIR} || true + + source ~/.bashrc + export PATH="${FLUTTER_DIR}/bin:$PATH" + flutter pub get dart format --output=none --set-exit-if-changed . || true dart analyze || true @@ -112,15 +132,35 @@ jobs: -v ${{ github.workspace }}:/workspace \ -w /workspace \ -e APP_NAME=${{ env.APP_NAME }} \ + -e FLUTTER_DIR=${{ env.FLUTTER_DIR }} \ -e MATRIX_ARCH=${{ matrix.arch }} \ ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ bash -lc ' set -e - export PATH="$PWD/flutter/bin:$PATH" + git config --global --add safe.directory /workspace || true - git config --global --add safe.directory /workspace/flutter || true - + git config --global --add safe.directory ${FLUTTER_DIR} || true + + export PATH="${FLUTTER_DIR}/bin:$PATH" + source ~/.bashrc + + # --- START FIX --- + # 1. Force use of Clang/Clang++ + export CC=clang + export CXX=clang++ + + # 2. Point Clang to the custom GCC headers and library + # This fixes the "type_traits not found" error + export CXXFLAGS="--gcc-toolchain=/opt/gcc-15.2.0 $CXXFLAGS" + + # 3. Ensure the linker finds the newer libstdc++ + # We add rpath so the binary finds these libs at runtime/test time locally + export LDFLAGS="-L/opt/gcc-15.2.0/lib64 -Wl,-rpath,/opt/gcc-15.2.0/lib64 --gcc-toolchain=/opt/gcc-15.2.0 $LDFLAGS" + # --- END FIX --- + + # run the build (use the absolute path to be robust) flutter build linux --release + ' - name: Package build artifacts @@ -148,23 +188,28 @@ jobs: --platform ${{ matrix.platform }} \ -v ${{ github.workspace }}:/workspace \ -w /workspace \ + -e FLUTTER_DIR=${{ env.FLUTTER_DIR }} \ ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest \ bash -lc ' set -e - export PATH="$PWD/flutter/bin:$PATH" + git config --global --add safe.directory /workspace || true - git config --global --add safe.directory /workspace/flutter || true + git config --global --add safe.directory ${FLUTTER_DIR} || true + + source ~/.bashrc + export PATH="${FLUTTER_DIR}/bin:$PATH" + flutter clean dart doc - + OWNER_UID=$(stat -c "%u" /workspace) OWNER_GID=$(stat -c "%g" /workspace) echo "Fixing ownership of doc/api to ${OWNER_UID}:${OWNER_GID}" chown -R ${OWNER_UID}:${OWNER_GID} /workspace/doc || true ' - + - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6.0.0 with: name: ${{ env.APP_NAME }}-linux-${{ matrix.arch }}-tar path: ${{ env.APP_NAME }}-linux-${{ matrix.arch }}.tar.gz @@ -174,7 +219,7 @@ jobs: run: | chmod -R 755 ./doc/api/ ls -la ./doc/api/ - + - name: 📂 Sync files to domain if: ${{ matrix.arch == 'x64' }} uses: SamKirkland/FTP-Deploy-Action@v4.3.6 @@ -182,4 +227,4 @@ jobs: server: ${{ secrets.SERVER }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PW }} - local-dir: "./doc/api/" + local-dir: "./doc/api/" \ No newline at end of file diff --git a/.github/workflows/dart_on_windows.yml b/.github/workflows/dart_on_native_windows.yml similarity index 89% rename from .github/workflows/dart_on_windows.yml rename to .github/workflows/dart_on_native_windows.yml index 99a0968..b5b26d0 100644 --- a/.github/workflows/dart_on_windows.yml +++ b/.github/workflows/dart_on_native_windows.yml @@ -1,4 +1,4 @@ -name: Windows CMake (clang-cl) +name: Build + test + run on Windows with (clang-cl) on: [ push, pull_request ] @@ -21,7 +21,7 @@ jobs: shell: pwsh - name: Checkout - uses: actions/checkout@v6.0.0 + uses: actions/checkout@v6.0.2 with: fetch-depth: 0 submodules: recursive @@ -32,7 +32,7 @@ jobs: uses: ./.github/actions/cleanup-disk-space - name: Login to GitHub - uses: docker/login-action@v3.6.0 + uses: docker/login-action@v3.7.0 with: registry: ghcr.io username: Kataglyphis @@ -52,7 +52,7 @@ jobs: # see also the install commands in the bottom of the CMakeLists.txt - name: Upload files & Zip - uses: actions/upload-artifact@v5.0.0 + uses: actions/upload-artifact@v6.0.0 with: name: windows-files path: | diff --git a/.github/workflows/dart_on_web_linux.yml b/.github/workflows/dart_on_web_linux.yml new file mode 100644 index 0000000..d4125ff --- /dev/null +++ b/.github/workflows/dart_on_web_linux.yml @@ -0,0 +1,79 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Build + test + run for web + +env: + LOCAL_ASSETS_FOLDER: "assets" + BUILD_DIR_RELEASE: "build/web" + +on: + push: + branches: ["main", "develop"] + +jobs: + build: + name: 🚀 Deploy website on push + #runs-on: windows-latest + runs-on: ubuntu-24.04 + steps: + - name: 🚚 Get latest code + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + submodules: recursive + + - name: Setup Flutter SDK + uses: flutter-actions/setup-flutter@v4.1 + with: + channel: stable + version: 3.38.9 + + # git submodule update --init --recursive + - name: Install dependencies + run: | + flutter pub get + cd ExternalLib/jotrockenmitlockenrepo + flutter pub get + + # Uncomment this step to verify the use of 'dart format' on each commit. + - name: Verify formatting + continue-on-error: true + run: dart format --output=none --set-exit-if-changed . + + # Consider passing '--fatal-infos' for slightly stricter analysis. + - name: Analyze project source + continue-on-error: true + run: dart analyze + + # Your project will need to have tests in test/ and a dependency on + # package:test for this step to succeed. Note that Flutter projects will + # want to change this to 'flutter test'. + - name: Run tests + continue-on-error: true + run: | + flutter test + + - name: Enable flutter web + run: | + rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu + rustup target add wasm32-unknown-unknown --toolchain nightly + cargo install flutter_rust_bridge_codegen + flutter config --enable-web + + - name: "Build Web App" + run: | + flutter_rust_bridge_codegen build-web \ + --wasm-pack-rustflags "-Ctarget-feature=+atomics -Clink-args=--shared-memory -Clink-args=--max-memory=1073741824 -Clink-args=--import-memory -Clink-args=--export=__wasm_init_tls -Clink-args=--export=__tls_size -Clink-args=--export=__tls_align -Clink-args=--export=__tls_base" \ + --release \ + --rust-root ExternalLib/Kataglyphis-RustProjectTemplate + flutter build web --release --wasm + + - name: Upload Web App Files for Deployment + uses: actions/upload-artifact@v6.0.0 + with: + name: gstreamer-ai-web-frontend + path: ${{ env.BUILD_DIR_RELEASE }}/** + if-no-files-found: error diff --git a/.gitignore b/.gitignore index 3545eb9..d410d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,11 +41,15 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release - - -#Ignore vscode AI rules -.github/instructions/codacy.instructions.md - - -#Ignore vscode AI rules -.github\instructions\codacy.instructions.md + + +#Ignore vscode AI rules +.github/instructions/codacy.instructions.md + + +#Ignore vscode AI rules +.github\instructions\codacy.instructions.md + +ExternalLib/Kataglyphis_NativeInferencePlugin/.dart_tool/* + +ExternalLib/jotrockenmitlockenrepo/.dart_tool/* diff --git a/.gitmodules b/.gitmodules index 8094f92..742d155 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,9 @@ path = ExternalLib/Kataglyphis_NativeInferencePlugin url = git@github.com:Kataglyphis/Kataglyphis-NativeInferencePlugin.git branch = main +[submodule "ExternalLib/Kataglyphis-ContainerHub"] + path = ExternalLib/Kataglyphis-ContainerHub + url = git@github.com:Kataglyphis/Kataglyphis-ContainerHub.git +[submodule "ExternalLib/Kataglyphis-RustProjectTemplate"] + path = ExternalLib/Kataglyphis-RustProjectTemplate + url = git@github.com:Kataglyphis/Kataglyphis-RustProjectTemplate.git diff --git a/.metadata b/.metadata index 1b79560..62391df 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "b25305a8832cfc6ba632a7f87ad455e319dccce8" + revision: "66dd93f9a27ffe2a9bfc8297506ce066ff51265f" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8 - base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8 - - platform: windows - create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8 - base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8 + create_revision: 66dd93f9a27ffe2a9bfc8297506ce066ff51265f + base_revision: 66dd93f9a27ffe2a9bfc8297506ce066ff51265f + - platform: android + create_revision: 66dd93f9a27ffe2a9bfc8297506ce066ff51265f + base_revision: 66dd93f9a27ffe2a9bfc8297506ce066ff51265f # User provided section diff --git a/.vscode/settings.json b/.vscode/settings.json index 1ad1dee..6489904 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "idf.pythonInstallPath": "/usr/bin/python3" + "idf.pythonInstallPath": "/usr/bin/python3", + "java.configuration.updateBuildConfiguration": "interactive" } \ No newline at end of file diff --git a/ExternalLib/Kataglyphis-ContainerHub b/ExternalLib/Kataglyphis-ContainerHub new file mode 160000 index 0000000..eac2cc8 --- /dev/null +++ b/ExternalLib/Kataglyphis-ContainerHub @@ -0,0 +1 @@ +Subproject commit eac2cc80e0fc053a33d5676a2497cd812116aedf diff --git a/ExternalLib/Kataglyphis-RustProjectTemplate b/ExternalLib/Kataglyphis-RustProjectTemplate new file mode 160000 index 0000000..16d97f1 --- /dev/null +++ b/ExternalLib/Kataglyphis-RustProjectTemplate @@ -0,0 +1 @@ +Subproject commit 16d97f1bd45723268817a7f109597830206b03aa diff --git a/ExternalLib/Kataglyphis_NativeInferencePlugin b/ExternalLib/Kataglyphis_NativeInferencePlugin index 193b33b..63a0cdf 160000 --- a/ExternalLib/Kataglyphis_NativeInferencePlugin +++ b/ExternalLib/Kataglyphis_NativeInferencePlugin @@ -1 +1 @@ -Subproject commit 193b33b159ed81c915fc7f3a1172bfc21b5bf86c +Subproject commit 63a0cdf8c3603e7b05e430cfc6b8c250a3704833 diff --git a/ExternalLib/jotrockenmitlockenrepo b/ExternalLib/jotrockenmitlockenrepo index 96d8890..5408dab 160000 --- a/ExternalLib/jotrockenmitlockenrepo +++ b/ExternalLib/jotrockenmitlockenrepo @@ -1 +1 @@ -Subproject commit 96d88904c3d1db32b3837b4e6e6e84ebf0ad7c28 +Subproject commit 5408dab392127e3bca1c27d7e6796163ce7456b2 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9f6aaf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Jonas Heinle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 4076b7d..941ab64 100644 --- a/README.md +++ b/README.md @@ -1,202 +1,201 @@ -

-
- logo -
- Kataglyphis-Inference-Engine -
-

- - - -

An inference engine with flutter/dart frontend an rust backend. .

- -[![Build + run + test on Linux](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_linux.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_linux.yml) -[![Automatic Dependency Submission](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependency-graph/auto-submission/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependency-graph/auto-submission) -[![CodeQL](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/github-code-scanning/codeql) +
+ + logo + + +

Kataglyphis-Inference-Engine

+ +

An inference engine with Flutter/Dart frontend and Rust/C++ backend, showcasing Gstreamer capabilities enhanced with AI. Read further if you are interested in cross platform AI inference.

+
+ + + +[![Build + run + test on Linux natively](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_native_linux.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_native_linux.yml) [![Windows CMake (clang-cl) natively](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_native_windows.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_native_windows.yml) [![Build + test + run for web](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_web_linux.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_web_linux.yml) + [![Build + test + run android app](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_build_android_app.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_build_android_app.yml)[![Automatic Dependency Submission](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependency-graph/auto-submission/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependency-graph/auto-submission) +[![CodeQL](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/github-code-scanning/codeql) [![Dependabot Updates](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dependabot/dependabot-updates) -[![Windows CMake (clang-cl)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_windows.yml/badge.svg)](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine/actions/workflows/dart_on_windows.yml) [![TopLang](https://img.shields.io/github/languages/top/Kataglyphis/Kataglyphis-Inference-Engine)]() [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?hosted_button_id=BX9AVVES2P9LN) [![Twitter](https://img.shields.io/twitter/follow/Cataglyphis_?style=social)](https://twitter.com/Cataglyphis_) [![YouTube](https://img.shields.io/youtube/channel/subscribers/UC3LZiH4sZzzaVBCUV8knYeg?style=social)](https://www.youtube.com/channel/UC3LZiH4sZzzaVBCUV8knYeg) -[**__Official homepage__**](https://kataglyphisinferenceengine.jonasheinle.de) - -

- About The Project • - Getting Started • - License • - Literature -

- - -
- Table of Contents -
    -
  1. - About The Project - - - -
  2. -
  3. - Getting Started - -
  4. -
  5. Tests
  6. -
  7. Roadmap
  8. -
  9. Contributing
  10. -
  11. License
  12. -
  13. Contact
  14. -
  15. Acknowledgements
  16. -
  17. Literature
  18. -
-
- - -## About The Project -Building a high performance native inference engine with a frontend is quite challenging. This project discovers possibilities in doing it using Flutter/Dart and Rust. - - - - -This project is a template. - -### Key Features - - - - -### Dependencies -This enumeration also includes submodules. - - -### Useful tools - - - - -## Getting Started - -```bash -git submodule update --init --recursive -``` - -### Prerequisites - -### Installation - -1. Clone the repo - ```sh - git clone --recurse-submodules git@github.com:Kataglyphis/Kataglyphis-Inference-Engine.git +[**Official homepage**](https://kataglyphisinferenceengine.jonasheinle.de) + +## Overview + +Kataglyphis-Inference-Engine bundles a Flutter/Dart frontend, a Rust/C++ inference core, and a rich set of camera streaming pipelines powered by GStreamer. The repository acts as an end-to-end reference for building cross-platform inference products that target desktop, web, and embedded devices. + +## Highlights & Key Features – Kataglyphis-Inference-Engine + +### 🌟 Highlights + +- 🎨 **GStreamer native GTK integration** – Leveraging users to write beautiful Linux AI inference apps. +- 📹 **GStreamer WebRTC livestreaming** with ready-to-use pipelines for USB, Raspberry Pi, and Orange Pi cameras. +- 🌉 **flutter_rust_bridge integration** – Ensures a seamless API boundary between Dart UI and Rust logic. +- 🐳 **Containerized development flow** plus native instructions for Windows, Linux, web. For details in my build environment look into [Kataglyphis-ContainerHub](https://github.com/Kataglyphis/Kataglyphis-ContainerHub) +- 🐍 **Python inference demos** for rapid experimentation alongside the Rust core. + +### 📊 Feature Status Matrix + +#### Core Features + +| Category | Feature | Win x64 | Linux x64 | Linux ARM64 | Linux RISC-V | Android | +|----------|---------|:-------:|:---------:|:-----------:|:------------:|:-------:| +| **Camera Streaming** | 📹 GStreamer WebRTC Livestream | ✔️ | ✔️ | ✔️ | ✔️ | N/A | +| **Supported Cameras** | 🔌 USB Devices | ✔️ | ✔️ | ✔️ | ✔️ | N/A | +| | 🍓 Raspberry Pi Camera | N/A | ✔️ | ✔️ | ✔️ | N/A | +| | 🟠 Orange Pi Camera | N/A | ❌ | ❌ | ❌ | N/A | +| | 📱 Native Camera API | N/A | N/A | N/A | N/A | ✔️ | + +#### Infrastructure & Build + +| Category | Feature | Win x64 | Linux x64 | Linux ARM64 | Linux RISC-V | Android | +|----------|---------|:-------:|:---------:|:-----------:|:------------:|:-------:| +| **Containerization** | 🐳 Dockerfile | ✔️ | ✔️ | ✔️ | ✔️ | N/A | +| | 🐳 Docker Compose | N/A | ✔️ | ✔️ | ✔️ | N/A | +| **Native Integration** | 🎨 GTK Integration | N/A | ✔️ | ✔️ | ✔️ | N/A | +| | 🪟 Win32 API | ✔️ | N/A | N/A | N/A | N/A | +| | 🤖 Android NDK | N/A | N/A | N/A | N/A | ✔️ | +| **Bridge Layer** | 🌉 flutter_rust_bridge | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| **Compiler** | 🔧 Clang-CL | ✔️ | N/A | N/A | N/A | N/A | +| | 🔧 GCC/Clang | N/A | ✔️ | ✔️ | ✔️ | ✔️ | + +#### Testing & Quality Assurance + +| Category | Feature | Win x64 | Linux x64 | Linux ARM64 | Linux RISC-V | Android | +|----------|---------|:-------:|:---------:|:-----------:|:------------:|:-------:| +| **Unit Testing** | 🧪 Advanced unit testing | 🔶 | 🔶 | 🔶 | 🔶 | 🔶 | +| **Performance** | ⚡ Advanced performance testing | 🔶 | 🔶 | 🔶 | 🔶 | 🔶 | +| **Security** | 🔍 Advanced fuzz testing | 🔶 | 🔶 | 🔶 | 🔶 | 🔶 | + +#### Frontend Platforms + +| Category | Feature | Win x64 | Linux x64 | Linux ARM64 | Linux RISC-V | Android | +|----------|---------|:-------:|:---------:|:-----------:|:------------:|:-------:| +| **Flutter UI** | 🦋 Flutter Web Support | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| | 💻 Flutter Desktop | ✔️ | ✔️ | ✔️ | ✔️ | N/A | +| | 📱 Flutter Mobile | N/A | N/A | N/A | N/A | ✔️ | + +--- + +#### Platform Summary + +| Platform | Architecture | Status | Notes | +|----------|-------------|:------:|-------| +| 🪟 **Windows** | x86-64 | ✔️ | Built with clang-cl, Win32 integration | +| 🐧 **Linux** | x86-64 | ✔️ | Full GTK support, Docker ready | +| 🐧 **Linux** | ARM64 | ✔️ | SBC optimized (RPi, OPi support) | +| 🐧 **Linux** | RISC-V | ✔️ | Emerging architecture support | +| 🤖 **Android** | ARM64/x86-64 | ✔️ | Native camera, NDK integration | + +--- + +**Legend:** +- ✔️ **Completed** - Feature fully implemented and tested +- 🔶 **In Progress** - Active development underway +- ❌ **Not Started** - Planned but not yet begun +- **N/A** - Not applicable for this platform + +## Quick Start + +1. Clone the repository with submodules: + > **__NOTE:__** + > On Windows I use [Git Bash](https://git-scm.com/install/windows) instead of + > Powershell or cmd + ```bash + git clone --recurse-submodules --branch develop git@github.com:Kataglyphis/Kataglyphis-Inference-Engine.git + cd Kataglyphis-Inference-Engine ``` -### Upgrades -Upgrading the flutter/dart bridge dependencies is as simple as this command: -[see source](https://cjycode.com/flutter_rust_bridge/guides/miscellaneous/upgrade/regular) -```bash -cargo install flutter_rust_bridge_codegen && flutter_rust_bridge_codegen generate -``` - -### Windows -For windows we absolutely do not want to be dependent on MSVC compiler. -Therefore I use [clang-cl](https://clang.llvm.org/docs/MSVCCompatibility.html). -Using clang-cl instead of MSVC needed adjustment. Therefore i give some instructions here. - -#### Flutter generated cmake project -Adjust the CXX-Flags in the auto-generated Cmake project. Find the folloeing line -and adjust accordingly: - -```cmake -# comment this line -# target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") -# add the following: -# target_compile_options(${TARGET} PRIVATE /W3 /WX /wd4100 -Wno-cast-function-type-mismatch -Wno-unused-function) -``` - -Now you can build the project by running following commands: -**__Attention:__** Adjust paths accordingly. - -```powershell -cd rust -cargo build --release -cp rust\target\release\rust_lib_kataglyphis_inference_engine.dll build\windows\x64\plugins\rust_lib_kataglyphis_inference_engine -cmake C:\GitHub\Kataglyphis-Inference-Engine\windows -B C:\GitHub\Kataglyphis-Inference-Engine\build\windows\x64 -G "Ninja" -DFLUTTER_TARGET_PLATFORM=windows-x64 -DCMAKE_CXX_COMPILER="C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\bin\clang-cl.exe" -DCMAKE_CXX_COMPILER_TARGET=x86_64-pc-windows-msvc -cmake --build C:\GitHub\Kataglyphis-Inference-Engine\build\windows\x64 --config Release --target install --verbose -``` +2. Initialize submodules if needed. + If u used `--recurse-submodules` while cloning you are already good. + Otherwise you can use this :smile: + ```bash + git submodule update --init --recursive + ``` + +Refer to the detailed docs below for platform-specific requirements, camera streaming pipelines, and deployment workflows. + + +## Documentation + +| Topic | Location | Description | +|-------|----------|-------------| +| Getting Started | [docs/source/getting-started.md](docs/source/getting-started.md) | Environment prerequisites, installation, and run commands. | +| Platform Guides | [docs/source/platforms.md](docs/source/platforms.md) | Container, Windows, Raspberry Pi, and web build instructions. | +| Camera Streaming | [docs/source/camera-streaming.md](docs/source/camera-streaming.md) | GStreamer WebRTC pipelines and Python inference demos. | +| Upgrade guide | [docs/source/upgrade-guide.md](docs/source/upgrade-guide.md) | How to keep things up-to-date. | + +Build the full Sphinx documentation from the `docs/` directory when you need a browsable site. ## Tests - -## Roadmap -Upcoming :) - +Testing infrastructure is under active development. Track progress on the roadmap or contribute test plans via pull requests. +## Roadmap +Upcoming features and improvements will be documented in this repository. +Please have a look [docs/source/roadmap.md](docs/source/roadmap.md) for more deetails. - ## Contributing -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. +Contributions are what make the open-source community amazing. Any contributions are **greatly appreciated**. -1. Fork the Project -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request +1. Fork the project. +2. Create your feature branch (`git checkout -b feature/AmazingFeature`). +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`). +4. Push to the branch (`git push origin feature/AmazingFeature`). +5. Open a Pull Request. - - ## License - -## Contact +MIT (see [here](LICENSE)) + +## Acknowledgements -Jonas Heinle - [@Cataglyphis_](https://twitter.com/Cataglyphis_) - jonasheinle@googlemail.com +Thanks to the open-source community and all contributors! -Project Link: [https://github.com/Kataglyphis/...](https://github.com/Kataglyphis/...) +## Literature +Helpful tutorials, documentation, and resources: - -## Acknowledgements +### Multimedia +- [GStreamer](https://gstreamer.freedesktop.org/) + +### Rust +- [GStreamer-rs tutorial](https://gstreamer.freedesktop.org/documentation/rswebrtc/index.html?gi-language=c) +- [gst-plugins-rs](https://github.com/GStreamer/gst-plugins-rs) +- [GStreamer WebRTC](https://github.com/GStreamer/gst-plugins-rs/tree/main/net/webrtc) - +### Raspberry Pi +- [GStreamer on Raspberry Pi](https://www.raspberrypi.com/documentation/computers/camera_software.html) +- [libcamera](https://libcamera.org/) +- [libcamera on Raspberry Pi](https://github.com/raspberrypi/libcamera) -## Literature +### CMake/C++ +- [clang-cl](https://clang.llvm.org/docs/MSVCCompatibility.html) -Some very helpful literature, tutorials, etc. +### Flutter/Dart +- [Linux Native Textures](https://github.com/flutter/flutter/blob/master/examples/texture/lib/main.dart) +- [flutter_rust_bridge](https://cjycode.com/flutter_rust_bridge/) +- [Flutter on RISCV](https://github.com/ardera/flutter-ci/) + +### Protocols +- [WebRTC](https://webrtc.org/?hl=de) + +### Tooling +- [tmux](https://github.com/tmux/tmux/wiki) +- [zellij](https://zellij.dev/) +- [psmux](https://github.com/marlocarlo/psmux) + +### Android +- [Gstreamer+flutter+android](https://github.com/hpdragon1618/flutter_gstreamer_player) + +## Contact -CMake/C++ -* [clang-cl](https://clang.llvm.org/docs/MSVCCompatibility.html) +**Jonas Heinle** +Twitter: [@Cataglyphis_](https://twitter.com/Cataglyphis_) +Email: cataglyphis@jonasheinle.de -Flutter/Dart -* [Linux Native Textures](https://github.com/flutter/flutter/blob/master/examples/texture/lib/main.dart) +**Project Links:** +- GitHub: [Kataglyphis-Inference-Engine](https://github.com/Kataglyphis/Kataglyphis-Inference-Engine) +- Homepage: [Official Site](https://kataglyphisinferenceengine.jonasheinle.de) diff --git a/android/.gitignore b/android/.gitignore index fc83c26..be3943c 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,13 +1,14 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/to/reference-keystore -key.properties -**/*.keystore -**/*.jks +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle.kts similarity index 57% rename from android/app/build.gradle rename to android/app/build.gradle.kts index 2d88cd1..b328bc8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle.kts @@ -1,44 +1,57 @@ -plugins { - id "com.android.application" - id "kotlin-android" - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" -} - -android { - namespace = "com.example.kataglyphis_inference_engine" - compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.kataglyphis_inference_engine" - // You can update the following values to match your application needs. - // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = flutter.minSdkVersion - targetSdk = flutter.targetSdkVersion - versionCode = flutter.versionCode - versionName = flutter.versionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug - } - } -} - -flutter { - source = "../.." -} +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.kataglyphis_inference_engine" + // compileSdk = flutter.compileSdkVersion + // ndkVersion = flutter.ndkVersion + compileSdk = 36 + buildToolsVersion = "35.0.0" + ndkVersion = "29.0.14206865" + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + isCoreLibraryDesugaringEnabled = true + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_21.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.kataglyphis_inference_engine" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + // my native_code_plugin requires minSdkVersion 26. + minSdk = 26 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} + +kotlin { + jvmToolchain(21) +} + +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.3") +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 8ffe024..399f698 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cd5acf4..8ea5a33 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,45 +1,46 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/kataglyphis_inference_engine/MainActivity.kt b/android/app/src/main/kotlin/com/example/kataglyphis_inference_engine/MainActivity.kt index 81b1070..f73149b 100644 --- a/android/app/src/main/kotlin/com/example/kataglyphis_inference_engine/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/kataglyphis_inference_engine/MainActivity.kt @@ -1,5 +1,5 @@ -package com.example.kataglyphis_inference_engine - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() +package com.example.kataglyphis_inference_engine + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml index 1cb7aa2..f74085f 100644 --- a/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,12 @@ - - - - - - - - + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 8403758..304732f 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,12 @@ - - - - - - - - + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4..5657785 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b7..a828c9f 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d4391..ca02ea0 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..55f0369 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372e..f33a7ef 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml index 360a160..06952be 100644 --- a/android/app/src/main/res/values-night/styles.xml +++ b/android/app/src/main/res/values-night/styles.xml @@ -1,18 +1,18 @@ - - - - - - - + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 5fac679..cb1ef88 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,18 +1,18 @@ - - - - - - - + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index 8ffe024..399f698 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 0066644..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html new file mode 100644 index 0000000..defc283 --- /dev/null +++ b/android/build/reports/problems/problems-report.html @@ -0,0 +1,663 @@ + + + + + + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/android/gradle.properties b/android/gradle.properties index 5ddc559..a9f5d10 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,7 @@ -org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError -android.useAndroidX=true -android.enableJetifier=true +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +# Use full JDK 21 distribution so Gradle toolchains can find javac +org.gradle.java.home=/usr/lib/jvm/java-1.21.0-openjdk-amd64 +# Help Gradle toolchains locate the installed JDK 21 explicitly +org.gradle.java.installations.fromEnv=JAVA_HOME +org.gradle.java.installations.paths=/usr/lib/jvm/java-1.21.0-openjdk-amd64,/usr/lib/jvm/java-21-openjdk-amd64 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 653f2e8..e4ef43f 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index beb05b2..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.1.0" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false -} - -include ":app" diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..ca7fe06 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git "a/assets/documents/books/Einf\303\274hrung in die Philosophie - Arno Anzenbacher.md" "b/assets/documents/books/Einf\303\274hrung in die Philosophie - Arno Anzenbacher.md" deleted file mode 100644 index b6326d9..0000000 --- "a/assets/documents/books/Einf\303\274hrung in die Philosophie - Arno Anzenbacher.md" +++ /dev/null @@ -1,2 +0,0 @@ -Arno Anzenbacher - diff --git a/assets/documents/books/fuenfGeheimnisse.md b/assets/documents/books/fuenfGeheimnisse.md deleted file mode 100644 index 7410319..0000000 --- a/assets/documents/books/fuenfGeheimnisse.md +++ /dev/null @@ -1,262 +0,0 @@ -> **_NOTE_** : For now the following content has no deeper meaning. -> Right now this is my playground for integrating markdown into flutter. -> - -# AI Blog -![Clouds](ScreenshotWorleyNoise.png) -*image_caption*d - - -This is inline latex: $f(x) = \sum_{i=0}^{n} \frac{a_i}{1+x}$ -This is block level latex: - -This is inline latex with displayMode: $$f(x) = \sum_{i=0}^{n} \frac{a_i}{1+x}$$ - -**The Cauchy-Schwarz Inequality** - -```math - \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) -``` - - -```c -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __CGROUP_INTERNAL_H -#define __CGROUP_INTERNAL_H - -#include -#include - -#define TRACE_CGROUP_PATH_LEN 1024 - - -/* - * cgroup_path() takes a spin lock. It is good practice not to take - * spin locks within trace point handlers, as they are mostly hidden - * from normal view. As cgroup_path() can take the kernfs_rename_lock - * spin lock, it is best to not call that function from the trace event - * handler. - * - * Note: trace_cgroup_##type##_enabled() is a static branch that will only - * be set when the trace event is enabled. - */ -#define TRACE_CGROUP_PATH(type, cgrp, ...) \ - do { \ - if (trace_cgroup_##type##_enabled()) { \ - unsigned long flags; \ - spin_lock_irqsave(&trace_cgroup_path_lock, \ - flags); \ - cgroup_path(cgrp, trace_cgroup_path, \ - TRACE_CGROUP_PATH_LEN); \ - trace_cgroup_##type(cgrp, trace_cgroup_path, \ - ##__VA_ARGS__); \ - spin_unlock_irqrestore(&trace_cgroup_path_lock, \ - flags); \ - } \ - } while (0) - -/* - * The cgroup filesystem superblock creation/mount context. - */ -struct cgroup_fs_context { - struct kernfs_fs_context kfc; - struct cgroup_root *root; - struct cgroup_namespace *ns; - unsigned int flags; /* CGRP_ROOT_* flags */ - - /* cgroup1 bits */ - bool cpuset_clone_children; - bool none; /* User explicitly requested empty subsystem */ - bool all_ss; /* Seen 'all' option */ - u16 subsys_mask; /* Selected subsystems */ - char *name; /* Hierarchy name */ - char *release_agent; /* Path for release notifications */ -}; - -static inline struct cgroup_fs_context *cgroup_fc2context(struct fs_context *fc) -{ - struct kernfs_fs_context *kfc = fc->fs_private; - - return container_of(kfc, struct cgroup_fs_context, kfc); -} - -struct cgroup_pidlist; - -struct cgroup_file_ctx { - struct cgroup_namespace *ns; - - struct { - void *trigger; - } psi; - - struct { - bool started; - struct css_task_iter iter; - } procs; - - struct { - struct cgroup_pidlist *pidlist; - } procs1; -}; - -/* - * A cgroup can be associated with multiple css_sets as different tasks may - * belong to different cgroups on different hierarchies. In the other - * direction, a css_set is naturally associated with multiple cgroups. - * This M:N relationship is represented by the following link structure - * which exists for each association and allows traversing the associations - * from both sides. - */ -struct cgrp_cset_link { - /* the cgroup and css_set this link associates */ - struct cgroup *cgrp; - struct css_set *cset; - - /* list of cgrp_cset_links anchored at cgrp->cset_links */ - struct list_head cset_link; - - /* list of cgrp_cset_links anchored at css_set->cgrp_links */ - struct list_head cgrp_link; -}; - -/* used to track tasks and csets during migration */ -struct cgroup_taskset { - /* the src and dst cset list running through cset->mg_node */ - struct list_head src_csets; - struct list_head dst_csets; - - /* the number of tasks in the set */ - int nr_tasks; - - /* the subsys currently being processed */ - int ssid; - - /* - * Fields for cgroup_taskset_*() iteration. - * - * Before migration is committed, the target migration tasks are on - * ->mg_tasks of the csets on ->src_csets. After, on ->mg_tasks of - * the csets on ->dst_csets. ->csets point to either ->src_csets - * or ->dst_csets depending on whether migration is committed. - * - * ->cur_csets and ->cur_task point to the current task position - * during iteration. - */ - struct list_head *csets; - struct css_set *cur_cset; - struct task_struct *cur_task; -}; - -/* migration context also tracks preloading */ -struct cgroup_mgctx { - /* - * Preloaded source and destination csets. Used to guarantee - * atomic success or failure on actual migration. - */ - struct list_head preloaded_src_csets; - struct list_head preloaded_dst_csets; - - /* tasks and csets to migrate */ - struct cgroup_taskset tset; - - /* subsystems affected by migration */ - u16 ss_mask; -}; - -#define CGROUP_TASKSET_INIT(tset) \ -{ \ - .src_csets = LIST_HEAD_INIT(tset.src_csets), \ - .dst_csets = LIST_HEAD_INIT(tset.dst_csets), \ - .csets = &tset.src_csets, \ -} - -#define CGROUP_MGCTX_INIT(name) \ -{ \ - LIST_HEAD_INIT(name.preloaded_src_csets), \ - LIST_HEAD_INIT(name.preloaded_dst_csets), \ - CGROUP_TASKSET_INIT(name.tset), \ -} - -#define DEFINE_CGROUP_MGCTX(name) \ - struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name) - -extern struct cgroup_subsys *cgroup_subsys[]; -extern struct list_head cgroup_roots; - -/* iterate across the hierarchies */ -#define for_each_root(root) \ - list_for_each_entry_rcu((root), &cgroup_roots, root_list, \ - lockdep_is_held(&cgroup_mutex)) - -/** - * for_each_subsys - iterate all enabled cgroup subsystems - * @ss: the iteration cursor - * @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end - */ -#define for_each_subsys(ss, ssid) \ - for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ - (((ss) = cgroup_subsys[ssid]) || true); (ssid)++) - -static inline bool cgroup_is_dead(const struct cgroup *cgrp) -{ - return !(cgrp->self.flags & CSS_ONLINE); -} - -static inline bool notify_on_release(const struct cgroup *cgrp) -{ - return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); -} - -void put_css_set_locked(struct css_set *cset); - -static inline void put_css_set(struct css_set *cset) -{ - unsigned long flags; - - /* - * Ensure that the refcount doesn't hit zero while any readers - * can see it. Similar to atomic_dec_and_lock(), but for an - * rwlock - */ - if (refcount_dec_not_one(&cset->refcount)) - return; - - spin_lock_irqsave(&css_set_lock, flags); - put_css_set_locked(cset); - spin_unlock_irqrestore(&css_set_lock, flags); -} - -/* - * refcounted get/put for css_set objects - */ -static inline void get_css_set(struct css_set *cset) -{ - refcount_inc(&cset->refcount); -} -#endif /* __CGROUP_INTERNAL_H */ -``` -| | Command | Description | -| ----- | -------- | --------------------------------- | -| | `git status` | List all new or modified files | -| | `git-diff` | Show file differences that haven't been staged| - -> **_NOTE:_** Das Impressum befindet sich in einer experimentellen Phase und -beinhaltet derzeit unvollständige/dummy Daten. - -## Anbieter -Jonas Heinle -Elchwinkel 42 -12345 Bärstadt - - -## Links - -### E-Mail -- für allgemeine Anfragen: [Kontakt zu mir](#) - -## Externe Links -- [philoclopedia.de](#) -- [johannes-heinle.de](#) - - -Stand: 13.02.2024 diff --git a/assets/documents/cv/CV_Jonas_Heinle_english.pdf b/assets/documents/cv/CV_Jonas_Heinle_english.pdf deleted file mode 100644 index 6edc5aa..0000000 Binary files a/assets/documents/cv/CV_Jonas_Heinle_english.pdf and /dev/null differ diff --git a/assets/documents/cv/CV_Jonas_Heinle_german.pdf b/assets/documents/cv/CV_Jonas_Heinle_german.pdf deleted file mode 100644 index 09dd607..0000000 Binary files a/assets/documents/cv/CV_Jonas_Heinle_german.pdf and /dev/null differ diff --git a/assets/documents/games/Gothic1.md b/assets/documents/games/Gothic1.md deleted file mode 100644 index 253b325..0000000 --- a/assets/documents/games/Gothic1.md +++ /dev/null @@ -1,263 +0,0 @@ -> ***NOTE*** : For now the following content has no deeper meaning. -> Right now this is my playground for integrating markdown into flutter. - -# AI Blog - -![Clouds](ScreenshotWorleyNoise.png) -*image_caption*d - -This is inline latex: $f(x) = \\sum\_{i=0}^{n} \\frac{a_i}{1+x}$ -This is block level latex: - -This is inline latex with displayMode: $$f(x) = \\sum\_{i=0}^{n} \\frac{a_i}{1+x}$$ - -**The Cauchy-Schwarz Inequality** - -```math - \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) -``` - -```c -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __CGROUP_INTERNAL_H -#define __CGROUP_INTERNAL_H - -#include -#include - -#define TRACE_CGROUP_PATH_LEN 1024 - - -/* - * cgroup_path() takes a spin lock. It is good practice not to take - * spin locks within trace point handlers, as they are mostly hidden - * from normal view. As cgroup_path() can take the kernfs_rename_lock - * spin lock, it is best to not call that function from the trace event - * handler. - * - * Note: trace_cgroup_##type##_enabled() is a static branch that will only - * be set when the trace event is enabled. - */ -#define TRACE_CGROUP_PATH(type, cgrp, ...) \ - do { \ - if (trace_cgroup_##type##_enabled()) { \ - unsigned long flags; \ - spin_lock_irqsave(&trace_cgroup_path_lock, \ - flags); \ - cgroup_path(cgrp, trace_cgroup_path, \ - TRACE_CGROUP_PATH_LEN); \ - trace_cgroup_##type(cgrp, trace_cgroup_path, \ - ##__VA_ARGS__); \ - spin_unlock_irqrestore(&trace_cgroup_path_lock, \ - flags); \ - } \ - } while (0) - -/* - * The cgroup filesystem superblock creation/mount context. - */ -struct cgroup_fs_context { - struct kernfs_fs_context kfc; - struct cgroup_root *root; - struct cgroup_namespace *ns; - unsigned int flags; /* CGRP_ROOT_* flags */ - - /* cgroup1 bits */ - bool cpuset_clone_children; - bool none; /* User explicitly requested empty subsystem */ - bool all_ss; /* Seen 'all' option */ - u16 subsys_mask; /* Selected subsystems */ - char *name; /* Hierarchy name */ - char *release_agent; /* Path for release notifications */ -}; - -static inline struct cgroup_fs_context *cgroup_fc2context(struct fs_context *fc) -{ - struct kernfs_fs_context *kfc = fc->fs_private; - - return container_of(kfc, struct cgroup_fs_context, kfc); -} - -struct cgroup_pidlist; - -struct cgroup_file_ctx { - struct cgroup_namespace *ns; - - struct { - void *trigger; - } psi; - - struct { - bool started; - struct css_task_iter iter; - } procs; - - struct { - struct cgroup_pidlist *pidlist; - } procs1; -}; - -/* - * A cgroup can be associated with multiple css_sets as different tasks may - * belong to different cgroups on different hierarchies. In the other - * direction, a css_set is naturally associated with multiple cgroups. - * This M:N relationship is represented by the following link structure - * which exists for each association and allows traversing the associations - * from both sides. - */ -struct cgrp_cset_link { - /* the cgroup and css_set this link associates */ - struct cgroup *cgrp; - struct css_set *cset; - - /* list of cgrp_cset_links anchored at cgrp->cset_links */ - struct list_head cset_link; - - /* list of cgrp_cset_links anchored at css_set->cgrp_links */ - struct list_head cgrp_link; -}; - -/* used to track tasks and csets during migration */ -struct cgroup_taskset { - /* the src and dst cset list running through cset->mg_node */ - struct list_head src_csets; - struct list_head dst_csets; - - /* the number of tasks in the set */ - int nr_tasks; - - /* the subsys currently being processed */ - int ssid; - - /* - * Fields for cgroup_taskset_*() iteration. - * - * Before migration is committed, the target migration tasks are on - * ->mg_tasks of the csets on ->src_csets. After, on ->mg_tasks of - * the csets on ->dst_csets. ->csets point to either ->src_csets - * or ->dst_csets depending on whether migration is committed. - * - * ->cur_csets and ->cur_task point to the current task position - * during iteration. - */ - struct list_head *csets; - struct css_set *cur_cset; - struct task_struct *cur_task; -}; - -/* migration context also tracks preloading */ -struct cgroup_mgctx { - /* - * Preloaded source and destination csets. Used to guarantee - * atomic success or failure on actual migration. - */ - struct list_head preloaded_src_csets; - struct list_head preloaded_dst_csets; - - /* tasks and csets to migrate */ - struct cgroup_taskset tset; - - /* subsystems affected by migration */ - u16 ss_mask; -}; - -#define CGROUP_TASKSET_INIT(tset) \ -{ \ - .src_csets = LIST_HEAD_INIT(tset.src_csets), \ - .dst_csets = LIST_HEAD_INIT(tset.dst_csets), \ - .csets = &tset.src_csets, \ -} - -#define CGROUP_MGCTX_INIT(name) \ -{ \ - LIST_HEAD_INIT(name.preloaded_src_csets), \ - LIST_HEAD_INIT(name.preloaded_dst_csets), \ - CGROUP_TASKSET_INIT(name.tset), \ -} - -#define DEFINE_CGROUP_MGCTX(name) \ - struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name) - -extern struct cgroup_subsys *cgroup_subsys[]; -extern struct list_head cgroup_roots; - -/* iterate across the hierarchies */ -#define for_each_root(root) \ - list_for_each_entry_rcu((root), &cgroup_roots, root_list, \ - lockdep_is_held(&cgroup_mutex)) - -/** - * for_each_subsys - iterate all enabled cgroup subsystems - * @ss: the iteration cursor - * @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end - */ -#define for_each_subsys(ss, ssid) \ - for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ - (((ss) = cgroup_subsys[ssid]) || true); (ssid)++) - -static inline bool cgroup_is_dead(const struct cgroup *cgrp) -{ - return !(cgrp->self.flags & CSS_ONLINE); -} - -static inline bool notify_on_release(const struct cgroup *cgrp) -{ - return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); -} - -void put_css_set_locked(struct css_set *cset); - -static inline void put_css_set(struct css_set *cset) -{ - unsigned long flags; - - /* - * Ensure that the refcount doesn't hit zero while any readers - * can see it. Similar to atomic_dec_and_lock(), but for an - * rwlock - */ - if (refcount_dec_not_one(&cset->refcount)) - return; - - spin_lock_irqsave(&css_set_lock, flags); - put_css_set_locked(cset); - spin_unlock_irqrestore(&css_set_lock, flags); -} - -/* - * refcounted get/put for css_set objects - */ -static inline void get_css_set(struct css_set *cset) -{ - refcount_inc(&cset->refcount); -} -#endif /* __CGROUP_INTERNAL_H */ -``` - -| | Command | Description | -|--|---------|-------------| -| | `git status` | List all new or modified files | -| | `git-diff` | Show file differences that haven't been staged | - -> ***NOTE:*** Das Impressum befindet sich in einer experimentellen Phase und -> beinhaltet derzeit unvollständige/dummy Daten. - -## Anbieter - -Jonas Heinle -Elchwinkel 42 -12345 Bärstadt - - -## Links - -### E-Mail - -- für allgemeine Anfragen: [Kontakt zu mir](#) - -## Externe Links - -- [philoclopedia.de](#) -- [johannes-heinle.de](#) - -Stand: 13.02.2024 \ No newline at end of file diff --git a/assets/documents/thesis/Bachelor_Thesis.pdf b/assets/documents/thesis/Bachelor_Thesis.pdf deleted file mode 100644 index 3d616bb..0000000 Binary files a/assets/documents/thesis/Bachelor_Thesis.pdf and /dev/null differ diff --git a/assets/documents/thesis/Master_Thesis.pdf b/assets/documents/thesis/Master_Thesis.pdf deleted file mode 100644 index fdefc3b..0000000 Binary files a/assets/documents/thesis/Master_Thesis.pdf and /dev/null differ diff --git a/assets/fonts/Noto_Sans/NotoSans-Italic-VariableFont_wdth,wght.ttf b/assets/fonts/Noto_Sans/NotoSans-Italic-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..6245ba0 Binary files /dev/null and b/assets/fonts/Noto_Sans/NotoSans-Italic-VariableFont_wdth,wght.ttf differ diff --git a/assets/fonts/Noto_Sans/NotoSans-VariableFont_wdth,wght.ttf b/assets/fonts/Noto_Sans/NotoSans-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..9530d84 Binary files /dev/null and b/assets/fonts/Noto_Sans/NotoSans-VariableFont_wdth,wght.ttf differ diff --git a/assets/fonts/Noto_Sans/OFL.txt b/assets/fonts/Noto_Sans/OFL.txt new file mode 100644 index 0000000..09f020b --- /dev/null +++ b/assets/fonts/Noto_Sans/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Noto_Sans/README.txt b/assets/fonts/Noto_Sans/README.txt new file mode 100644 index 0000000..4958bee --- /dev/null +++ b/assets/fonts/Noto_Sans/README.txt @@ -0,0 +1,136 @@ +Noto Sans Variable Font +======================= + +This download contains Noto Sans as both variable fonts and static fonts. + +Noto Sans is a variable font with these axes: + wdth + wght + +This means all the styles are contained in these files: + NotoSans-VariableFont_wdth,wght.ttf + NotoSans-Italic-VariableFont_wdth,wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that aren’t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for Noto Sans: + static/NotoSans_ExtraCondensed-Thin.ttf + static/NotoSans_ExtraCondensed-ExtraLight.ttf + static/NotoSans_ExtraCondensed-Light.ttf + static/NotoSans_ExtraCondensed-Regular.ttf + static/NotoSans_ExtraCondensed-Medium.ttf + static/NotoSans_ExtraCondensed-SemiBold.ttf + static/NotoSans_ExtraCondensed-Bold.ttf + static/NotoSans_ExtraCondensed-ExtraBold.ttf + static/NotoSans_ExtraCondensed-Black.ttf + static/NotoSans_Condensed-Thin.ttf + static/NotoSans_Condensed-ExtraLight.ttf + static/NotoSans_Condensed-Light.ttf + static/NotoSans_Condensed-Regular.ttf + static/NotoSans_Condensed-Medium.ttf + static/NotoSans_Condensed-SemiBold.ttf + static/NotoSans_Condensed-Bold.ttf + static/NotoSans_Condensed-ExtraBold.ttf + static/NotoSans_Condensed-Black.ttf + static/NotoSans_SemiCondensed-Thin.ttf + static/NotoSans_SemiCondensed-ExtraLight.ttf + static/NotoSans_SemiCondensed-Light.ttf + static/NotoSans_SemiCondensed-Regular.ttf + static/NotoSans_SemiCondensed-Medium.ttf + static/NotoSans_SemiCondensed-SemiBold.ttf + static/NotoSans_SemiCondensed-Bold.ttf + static/NotoSans_SemiCondensed-ExtraBold.ttf + static/NotoSans_SemiCondensed-Black.ttf + static/NotoSans-Thin.ttf + static/NotoSans-ExtraLight.ttf + static/NotoSans-Light.ttf + static/NotoSans-Regular.ttf + static/NotoSans-Medium.ttf + static/NotoSans-SemiBold.ttf + static/NotoSans-Bold.ttf + static/NotoSans-ExtraBold.ttf + static/NotoSans-Black.ttf + static/NotoSans_ExtraCondensed-ThinItalic.ttf + static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf + static/NotoSans_ExtraCondensed-LightItalic.ttf + static/NotoSans_ExtraCondensed-Italic.ttf + static/NotoSans_ExtraCondensed-MediumItalic.ttf + static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf + static/NotoSans_ExtraCondensed-BoldItalic.ttf + static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf + static/NotoSans_ExtraCondensed-BlackItalic.ttf + static/NotoSans_Condensed-ThinItalic.ttf + static/NotoSans_Condensed-ExtraLightItalic.ttf + static/NotoSans_Condensed-LightItalic.ttf + static/NotoSans_Condensed-Italic.ttf + static/NotoSans_Condensed-MediumItalic.ttf + static/NotoSans_Condensed-SemiBoldItalic.ttf + static/NotoSans_Condensed-BoldItalic.ttf + static/NotoSans_Condensed-ExtraBoldItalic.ttf + static/NotoSans_Condensed-BlackItalic.ttf + static/NotoSans_SemiCondensed-ThinItalic.ttf + static/NotoSans_SemiCondensed-ExtraLightItalic.ttf + static/NotoSans_SemiCondensed-LightItalic.ttf + static/NotoSans_SemiCondensed-Italic.ttf + static/NotoSans_SemiCondensed-MediumItalic.ttf + static/NotoSans_SemiCondensed-SemiBoldItalic.ttf + static/NotoSans_SemiCondensed-BoldItalic.ttf + static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf + static/NotoSans_SemiCondensed-BlackItalic.ttf + static/NotoSans-ThinItalic.ttf + static/NotoSans-ExtraLightItalic.ttf + static/NotoSans-LightItalic.ttf + static/NotoSans-Italic.ttf + static/NotoSans-MediumItalic.ttf + static/NotoSans-SemiBoldItalic.ttf + static/NotoSans-BoldItalic.ttf + static/NotoSans-ExtraBoldItalic.ttf + static/NotoSans-BlackItalic.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects – print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Black.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Black.ttf new file mode 100644 index 0000000..d5a6e0d Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Black.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-BlackItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-BlackItalic.ttf new file mode 100644 index 0000000..dfc640c Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-BlackItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Bold.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Bold.ttf new file mode 100644 index 0000000..07f0d25 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Bold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-BoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-BoldItalic.ttf new file mode 100644 index 0000000..e538eae Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-BoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-ExtraBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans-ExtraBold.ttf new file mode 100644 index 0000000..5868446 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-ExtraBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-ExtraBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-ExtraBoldItalic.ttf new file mode 100644 index 0000000..68abd4c Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-ExtraLight.ttf b/assets/fonts/Noto_Sans/static/NotoSans-ExtraLight.ttf new file mode 100644 index 0000000..078f8dc Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-ExtraLight.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-ExtraLightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-ExtraLightItalic.ttf new file mode 100644 index 0000000..acaa466 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Italic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Italic.ttf new file mode 100644 index 0000000..d9b9e14 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Italic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Light.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Light.ttf new file mode 100644 index 0000000..8d8a678 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Light.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-LightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-LightItalic.ttf new file mode 100644 index 0000000..0ab65c0 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-LightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Medium.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Medium.ttf new file mode 100644 index 0000000..a44124b Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Medium.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-MediumItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-MediumItalic.ttf new file mode 100644 index 0000000..467af1b Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-MediumItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Regular.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Regular.ttf new file mode 100644 index 0000000..4bac02f Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Regular.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-SemiBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans-SemiBold.ttf new file mode 100644 index 0000000..e846749 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-SemiBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-SemiBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-SemiBoldItalic.ttf new file mode 100644 index 0000000..cacc7ec Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-SemiBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-Thin.ttf b/assets/fonts/Noto_Sans/static/NotoSans-Thin.ttf new file mode 100644 index 0000000..04335a5 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-Thin.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans-ThinItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans-ThinItalic.ttf new file mode 100644 index 0000000..910dfc7 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans-ThinItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Black.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Black.ttf new file mode 100644 index 0000000..3186699 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Black.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BlackItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BlackItalic.ttf new file mode 100644 index 0000000..d4b19bc Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BlackItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Bold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Bold.ttf new file mode 100644 index 0000000..bb82d6b Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Bold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BoldItalic.ttf new file mode 100644 index 0000000..c31898f Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-BoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBold.ttf new file mode 100644 index 0000000..cb36919 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBoldItalic.ttf new file mode 100644 index 0000000..7bbea17 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLight.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLight.ttf new file mode 100644 index 0000000..29a7751 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLight.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLightItalic.ttf new file mode 100644 index 0000000..983b81a Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Italic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Italic.ttf new file mode 100644 index 0000000..8e2d1f8 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Italic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Light.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Light.ttf new file mode 100644 index 0000000..32c58a5 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Light.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-LightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-LightItalic.ttf new file mode 100644 index 0000000..c5d1b45 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-LightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Medium.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Medium.ttf new file mode 100644 index 0000000..45f8ea4 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Medium.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-MediumItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-MediumItalic.ttf new file mode 100644 index 0000000..92cd88a Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-MediumItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Regular.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Regular.ttf new file mode 100644 index 0000000..3ad9a1b Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Regular.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBold.ttf new file mode 100644 index 0000000..2f20a21 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBoldItalic.ttf new file mode 100644 index 0000000..b28147d Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-SemiBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Thin.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Thin.ttf new file mode 100644 index 0000000..d5b50b5 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-Thin.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ThinItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ThinItalic.ttf new file mode 100644 index 0000000..00d9315 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_Condensed-ThinItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Black.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Black.ttf new file mode 100644 index 0000000..619c4f8 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Black.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BlackItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BlackItalic.ttf new file mode 100644 index 0000000..f124627 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BlackItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Bold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Bold.ttf new file mode 100644 index 0000000..2854b42 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Bold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BoldItalic.ttf new file mode 100644 index 0000000..3f6f8ac Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-BoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBold.ttf new file mode 100644 index 0000000..2ce3cb3 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf new file mode 100644 index 0000000..9892967 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLight.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLight.ttf new file mode 100644 index 0000000..ce67cb1 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLight.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf new file mode 100644 index 0000000..45726c3 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Italic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Italic.ttf new file mode 100644 index 0000000..e6b1a73 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Italic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Light.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Light.ttf new file mode 100644 index 0000000..5e9fef8 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Light.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-LightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-LightItalic.ttf new file mode 100644 index 0000000..500c919 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-LightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Medium.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Medium.ttf new file mode 100644 index 0000000..c78465e Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Medium.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-MediumItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-MediumItalic.ttf new file mode 100644 index 0000000..527291a Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-MediumItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Regular.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Regular.ttf new file mode 100644 index 0000000..8921daa Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Regular.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBold.ttf new file mode 100644 index 0000000..83b98b2 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf new file mode 100644 index 0000000..9dedf3e Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-SemiBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Thin.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Thin.ttf new file mode 100644 index 0000000..81e2bf9 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-Thin.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ThinItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ThinItalic.ttf new file mode 100644 index 0000000..17b43b1 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_ExtraCondensed-ThinItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Black.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Black.ttf new file mode 100644 index 0000000..5a141da Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Black.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BlackItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BlackItalic.ttf new file mode 100644 index 0000000..538888d Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BlackItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Bold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Bold.ttf new file mode 100644 index 0000000..e0cd54f Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Bold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BoldItalic.ttf new file mode 100644 index 0000000..e7b743a Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-BoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBold.ttf new file mode 100644 index 0000000..c50c081 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf new file mode 100644 index 0000000..b8b053e Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLight.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLight.ttf new file mode 100644 index 0000000..6450a04 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLight.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf new file mode 100644 index 0000000..a655d1e Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ExtraLightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Italic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Italic.ttf new file mode 100644 index 0000000..67c7a2f Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Italic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Light.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Light.ttf new file mode 100644 index 0000000..f9221c3 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Light.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-LightItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-LightItalic.ttf new file mode 100644 index 0000000..9a72200 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-LightItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Medium.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Medium.ttf new file mode 100644 index 0000000..e2c825c Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Medium.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-MediumItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-MediumItalic.ttf new file mode 100644 index 0000000..6be577a Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-MediumItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Regular.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Regular.ttf new file mode 100644 index 0000000..06a2982 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Regular.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBold.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBold.ttf new file mode 100644 index 0000000..8c8f313 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBold.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf new file mode 100644 index 0000000..59093a9 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-SemiBoldItalic.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Thin.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Thin.ttf new file mode 100644 index 0000000..7d7ef33 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-Thin.ttf differ diff --git a/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ThinItalic.ttf b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ThinItalic.ttf new file mode 100644 index 0000000..44084d9 Binary files /dev/null and b/assets/fonts/Noto_Sans/static/NotoSans_SemiCondensed-ThinItalic.ttf differ diff --git a/assets/icons/kataglyphis_app_icon.png b/assets/icons/kataglyphis_app_icon.png new file mode 100644 index 0000000..800d5d1 Binary files /dev/null and b/assets/icons/kataglyphis_app_icon.png differ diff --git a/assets/icons/kataglyphis_app_icon.svg b/assets/icons/kataglyphis_app_icon.svg new file mode 100644 index 0000000..43e4f34 --- /dev/null +++ b/assets/icons/kataglyphis_app_icon.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/default.profraw b/default.profraw index 086e567..8bcc55b 100644 Binary files a/default.profraw and b/default.profraw differ diff --git a/docs/source/README.md b/docs/source/README.md index ded6d49..9a8c350 100644 --- a/docs/source/README.md +++ b/docs/source/README.md @@ -1,191 +1,29 @@

-
- logo -
- Kataglyphis-Inference-Engine -
+ Kataglyphis-Inference-Engine Docs

- - -

A template .

- - -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/paypalme/JonasHeinle) -[![Twitter](https://img.shields.io/twitter/follow/Cataglyphis_?style=social)](https://twitter.com/Cataglyphis_) -

- About The Project • - Getting Started • - License • - Literature + Reference documentation for the Kataglyphis inference stack that blends Flutter, Rust, and GStreamer.

- -
- Table of Contents -
    -
  1. - About The Project - - - -
  2. -
  3. - Getting Started - -
  4. -
  5. Tests
  6. -
  7. Roadmap
  8. -
  9. Contributing
  10. -
  11. License
  12. -
  13. Contact
  14. -
  15. Acknowledgements
  16. -
  17. Literature
  18. -
-
- - -## About The Project - - - - - -This project is a template. - -### Key Features - - - - -### Dependencies -This enumeration also includes submodules. - - -### Useful tools - - - - -## Getting Started - -### Prerequisites - -### Installation - -1. Clone the repo - ```sh - git clone --recurse-submodules git@github.com:Kataglyphis/Kataglyphis-Inference-Engine.git - ``` - -## Tests - - -## Roadmap -Upcoming :) - - - - - -## Contributing - -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. - -1. Fork the Project -2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) -3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) -4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request - - - -## License - - -## Contact - -Jonas Heinle - [@Cataglyphis_](https://twitter.com/Cataglyphis_) - jonasheinle@googlemail.com - -Project Link: [https://github.com/Kataglyphis/...](https://github.com/Kataglyphis/...) - - - -## Acknowledgements - - - -## Literature - -Some very helpful literature, tutorials, etc. - - +| Topic | Description | +|-------|-------------| +| [Overview](overview.md) | High-level summary, key features, dependencies, and toolchain. | +| [Getting Started](getting-started.md) | Environment prerequisites, installation steps, and run commands. | +| [Platforms](platforms.md) | Container, Windows, Raspberry Pi, and Web build guides. | +| [Camera Streaming](camera-streaming.md) | GStreamer WebRTC pipelines and Python inference demos. | +| [Project Operations](project-operations.md) | Tests, roadmap, contribution process, literature, and upgrade notes. | - - -[contributors-shield]: https://img.shields.io/github/contributors/othneildrew/Best-README-Template.svg?style=for-the-badge -[contributors-url]: https://github.com/othneildrew/Best-README-Template/graphs/contributors -[forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=for-the-badge -[forks-url]: https://github.com/othneildrew/Best-README-Template/network/members -[stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=for-the-badge -[stars-url]: https://github.com/othneildrew/Best-README-Template/stargazers -[issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=for-the-badge -[issues-url]: https://github.com/othneildrew/Best-README-Template/issues -[license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=for-the-badge -[license-url]: https://github.com/othneildrew/Best-README-Template/blob/master/LICENSE.txt -[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 -[linkedin-url]: https://www.linkedin.com/in/jonas-heinle-0b2a301a0/ +Use the navigation above to jump directly to the section you need. Contributions to the docs are welcome—keep the structure modular so sections stay focused and easy to maintain. diff --git a/docs/source/camera-streaming.md b/docs/source/camera-streaming.md new file mode 100644 index 0000000..b0942f9 --- /dev/null +++ b/docs/source/camera-streaming.md @@ -0,0 +1,109 @@ +# Camera Streaming + +Guides for configuring WebRTC streaming pipelines and inference demos. + +## GStreamer WebRTC Setup + +Follow the official [GStreamer WebRTC tutorial](https://gstreamer.freedesktop.org/documentation/rswebrtc/index.html?gi-language=c) for deeper background. + +### 1. Start the Signalling Server + +```bash +cd /opt/gst-plugins-rs/net/webrtc/signalling +WEBRTCSINK_SIGNALLING_SERVER_LOG=debug cargo run --bin gst-webrtc-signalling-server -- --port 8444 --host 127.0.0.1 +``` + +### 2. Stream From a Webcam + +Set the plugin path before launching pipelines: + +```bash +export GST_PLUGIN_PATH=/home/user/gst-plugins-rs/target/release:$GST_PLUGIN_PATH +``` + +**USB webcam (inside Docker):** + +```bash +gst-launch-1.0 -e webrtcsink signaller::uri="ws://ubuntu:8444" name=ws \ + meta="meta,name=kataglyphis-webfrontend-stream" \ + v4l2src device=/dev/video0 ! image/jpeg,width=640,height=360,framerate=30/1 ! \ + jpegdec ! videoconvert ! ws. +``` + +**Pylon camera:** + +```bash +gst-launch-1.0 -e webrtcsink signaller::uri="ws://ubuntu:8444" name=ws \ + meta="meta,name=kataglyphis-webfrontend-stream" \ + pylonsrc ! videoconvert ! ws. audiotestsrc ! ws. +``` + +**Orange Pi RV2 RISC-V board:** + +```bash +GST_DEBUG=4 gst-launch-1.0 -v -e v4l2src device=/dev/video20 ! \ + video/x-raw,format=YUY2,width=1280,height=720,framerate=5/1 ! \ + videoconvert ! video/x-raw,format=I420 ! kyh264enc ! h264parse config-interval=1 ! \ + capsfilter caps="video/x-h264,stream-format=(string)byte-stream,alignment=(string)au,profile=(string)main,level=(string)3.1,coded-picture-structure=(string)frame,chroma-format=(string)4:2:0,bit-depth-luma=(uint)8,bit-depth-chroma=(uint)8,parsed=(boolean)true" ! \ + queue ! webrtcsink congestion-control=disabled signaller::uri="ws://0.0.0.0:8443" name=ws \ + meta="meta,name=kataglyphis-webfrontend-stream" +``` + +**Rotating camera stream:** + +```bash +gst-launch-1.0 ... \ + video/x-raw,width=1280,height=720,format=NV12,interlace-mode=progressive ! \ + videoflip method=rotate-180 ! \ + x264enc speed-preset=1 threads=1 byte-stream=true ! \ + ... +``` + +### 3. Launch the Web App + +Once the stream is live, run the Flutter web frontend as described in the [web build guide](platforms.md#web-build). + +## Python Inference Pipelines + +Install system dependencies first: + +```bash +sudo apt install libgirepository1.0-dev gir1.2-glib-2.0 \ + build-essential pkg-config python3-dev libgirepository-2.0-dev \ + gobject-introspection libcairo2-dev python3-gi python3-gi-cairo gir1.2-gtk-4.0 +``` + +> **NOTE for Raspberry Pi:** Share system packages into your virtual environment: +> +> ```bash +> python3 -m venv --system-site-packages .venv +> ``` + +### demo_ai.py + +```bash +uv venv +uv pip install loguru pygobject numpy opencv-python +GST_DEBUG=3 python3 demo_ai.py +``` + +### demo_yolov5.py + +```bash +uv venv +uv pip install loguru pygobject numpy opencv-python +uv pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu121 +uv pip install seaborn ultralytics +GST_DEBUG=3 python3 demo_yolov5.py +``` + +### Example Pipeline + +```bash +gst-launch-1.0 -e webrtcsink signaller::uri="ws://ubuntu:8443" name=ws \ + meta="meta,name=kataglyphis-webfrontend-stream" \ + v4l2src device=/dev/video0 ! video/x-raw,width=320,height=240,framerate=10/1 ! \ + videoconvert ! ws. +``` + +Increase verbosity with `GST_DEBUG=2` to see FPS data in the logs. diff --git a/docs/source/getting-started.md b/docs/source/getting-started.md new file mode 100644 index 0000000..73deef7 --- /dev/null +++ b/docs/source/getting-started.md @@ -0,0 +1,66 @@ +# Getting Started + +Follow these steps to set up the Kataglyphis Inference Engine locally. + +## Prerequisites + +### GStreamer + +Find all available video devices on Linux: + +```bash +for dev in /dev/video*; do + echo "Testing $dev" + gst-launch-1.0 -v v4l2src device=$dev ! fakesink +done +``` + +Check available resolutions and framerates: + +```bash +apt update +apt install v4l-utils +v4l2-ctl --device=/dev/video0 --list-formats-ext +``` + +### WSL2 USB Passthrough + +If running Docker on WSL2, share USB devices before use: + +```bash +# List USB devices +usbipd list +# Attach device to WSL +usbipd attach --wsl --busid 1-1.2 +# Verify in WSL +lsusb +# Example output for Basler USB camera: /dev/bus/usb/002/002 +``` + +## Installation + +3. Install the GStreamer WebRTC JavaScript libraries. The `public/lib` folder contains two JavaScript libraries generated from [gstwebrtc-api](https://github.com/GStreamer/gst-plugins-rs/tree/main/net/webrtc/gstwebrtc-api). + +> **NOTE:** If publishing to the internet, replace `127.0.0.1` with your domain: +> +> ```bash +> sed -i 's@ws://127.0.0.1:8443@ws://customdomain:8443@g' ./public/lib/gstwebrtc-api-1.0.1.min.js +> ``` +> +> To find all IP addresses and ports: +> +> ```bash +> grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\b' yourfile.txt +> ``` + +## Running the Application + +### Development Mode + +```bash +flutter run -d web-server --profile --web-port 8080 --web-hostname 0.0.0.0 +``` + +### Production Build + +Production build automation is under construction. Use your preferred CI/CD setup or follow the platform-specific guides to create release artifacts. diff --git a/docs/source/index.rst b/docs/source/index.rst index cb14b94..4765919 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,4 +16,7 @@ documentation for details. :caption: Contents: README.md + getting-started.md + platforms.md + camera-streaming.md diff --git a/docs/source/platforms.md b/docs/source/platforms.md new file mode 100644 index 0000000..b86c28b --- /dev/null +++ b/docs/source/platforms.md @@ -0,0 +1,174 @@ +# Platform Guides + +Detailed instructions for building and running on different targets. + +3. Launch the Flutter web target for rapid iteration: + ```bash + flutter run -d web-server --profile --web-port 8080 --web-hostname 0.0.0.0 + ``` + +## Container Setup + +Containerization keeps dependencies reproducible. A pre-built container image is available on [ContainerHub](https://github.com/Kataglyphis/Kataglyphis-ContainerHub/pkgs/container/kataglyphis_beschleuniger). + +```bash +docker run -it --rm \ + -v "$(pwd)":/workspace \ + -p 9090:9090 \ + -p 8443:8443 \ + -p 8444:8444 \ + -p 5173:5173 \ + --device=/dev/video0 \ + docker pull ghcr.io/kataglyphis/kataglyphis_beschleuniger:latest +``` + +> **NOTE for WSL2:** The `--device=/dev/video0` flag is required to pass USB cameras through to Docker. + +## Windows Development + +Windows builds rely on [clang-cl](https://clang.llvm.org/docs/MSVCCompatibility.html) to avoid MSVC dependencies. + +### Run local script + +#### Standard-Build + +powershell -ExecutionPolicy Bypass -File .\add-gstreamer-to-path.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\windows\build-windows.ps1 + +#### Mit benutzerdefiniertem Workspace + +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\windows\build-windows.ps1 -WorkspaceDir "C:\GitHub\Kataglyphis-Inference-Engine" + +#### Debug-Build ohne Tests + +.\build.ps1 -BuildType Debug -SkipTests + +#### Vollständig konfiguriert + +.\build.ps1 ` + -WorkspaceRoot "E:\flutter-project" ` + -BuildType Release ` + -Architecture x64 ` + -CMakeGenerator "Ninja" ` + -SkipFormatCheck ` + -CleanBuild $true + +### CMake Adjustments + +Update the Flutter-generated CMake project to relax warnings and silence unused helper code: + +```cmake +# Comment out +# target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + +# Add +target_compile_options(${TARGET} PRIVATE /W3 /WX /wd4100 -Wno-cast-function-type-mismatch -Wno-unused-function) +``` + +### Build Commands + +Adjust the paths to match your environment: + +```powershell +cd rust +cargo build --release +cp rust\target\release\rust_lib_kataglyphis_inference_engine.dll build\windows\x64\plugins\rust_lib_kataglyphis_inference_engine + +cmake C:\GitHub\Kataglyphis-Inference-Engine\windows ` + -B C:\GitHub\Kataglyphis-Inference-Engine\build\windows\x64 ` + -G "Ninja" ` + -DFLUTTER_TARGET_PLATFORM=windows-x64 ` + -DCMAKE_CXX_COMPILER="C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\bin\clang-cl.exe" ` + -DCMAKE_CXX_COMPILER_TARGET=x86_64-pc-windows-msvc + +cmake --build C:\GitHub\Kataglyphis-Inference-Engine\build\windows\x64 ` + --config Release ` + --target install ` + --verbose +``` + +## Anroid + +> **__NOTE__:**: +> You might encounter weired Java SDK Stuff ... in this case stop gradlew daemon +> ./gradlew --stop + +```bash +cd android && ./gradlew assembleRelease +``` + +Recreate android project. +```bash +flutter create --platforms=android . +``` + +Recreate plugins. +flutter create --template=plugin --platforms=android . + +## Raspberry Pi + +Run GStreamer commands **outside** Docker for Pi devices. + +```bash +gst-launch-1.0 \ + libcamerasrc ! video/x-raw,width=640,height=360,format=NV12,interlace-mode=progressive ! \ + x264enc speed-preset=1 threads=1 byte-stream=true ! \ + h264parse ! \ + webrtcsink signaller::uri="ws://0.0.0.0:8444" name=ws meta="meta,name=gst-stream" +``` + +Rotate the stream if the camera is mounted upside-down: + +```bash +gst-launch-1.0 \ + libcamerasrc ! video/x-raw,width=640,height=360,format=NV12,interlace-mode=progressive ! \ + videoflip method=rotate-180 ! \ + x264enc speed-preset=1 threads=1 byte-stream=true ! \ + h264parse ! \ + webrtcsink signaller::uri="ws://0.0.0.0:8444" name=ws meta="meta,name=gst-stream" +``` + +Use VP8/VP9 when hardware encoders are limited: + +```bash +GST_DEBUG=3 gst-launch-1.0 \ + libcamerasrc ! video/x-raw,format=RGB,width=640,height=360,framerate=30/1 ! \ + videoflip method=rotate-180 ! \ + videoconvert ! video/x-raw,format=I420 ! queue ! \ + vp8enc deadline=1 threads=2 ! queue ! \ + webrtcsink signaller::uri="ws://0.0.0.0:8443" name=ws meta="meta,name=gst-stream" +``` + +`deadline=0` prioritizes speed, `deadline=1` maximizes quality. + +## Web Build + +Enable Rust features required for the WebAssembly build: + +```bash +# Windows +rustup component add rust-src --toolchain nightly-x86_64-pc-windows-msvc +rustup target add wasm32-unknown-unknown +``` + +Build the WASM binding with shared memory support: + +```bash +flutter_rust_bridge_codegen build-web \ + --wasm-pack-rustflags "-Ctarget-feature=+atomics -Clink-args=--shared-memory -Clink-args=--max-memory=1073741824 -Clink-args=--import-memory -Clink-args=--export=__wasm_init_tls -Clink-args=--export=__tls_size -Clink-args=--export=__tls_align -Clink-args=--export=__tls_base" \ + --release +``` + +Serve the web build with the required COOP/COEP headers: + +```bash +flutter run \ + --web-header=Cross-Origin-Opener-Policy=same-origin \ + --web-header=Cross-Origin-Embedder-Policy=require-corp +``` + +Verify the WASM headers: + +```bash +curl -I http://localhost:8080/pkg/rust_lib_kataglyphis_inference_engine_bg.wasm 2>/dev/null | grep -i "cross-origin\|content-type" +``` diff --git a/assets/documents/books/.gitkeep b/docs/source/project-operations.md similarity index 100% rename from assets/documents/books/.gitkeep rename to docs/source/project-operations.md diff --git a/docs/source/roadmap.md b/docs/source/roadmap.md new file mode 100644 index 0000000..6a69712 --- /dev/null +++ b/docs/source/roadmap.md @@ -0,0 +1,48 @@ +# Roadmap – Kataglyphis-Inference-Engine + +Basierend auf dem Projekt und den dokumentierten Entwicklungsständen hier eine strukturierte Roadmap: + +## Phase 1: Stabilität & Testing (Q1–Q2 2024) + +| Feature | Beschreibung | Status | +|---------|-------------|--------| +| Unit Tests (Rust) | Kernfunktionen der Inference-Engine testen | 🟡 In Arbeit | +| Integration Tests | Frontend-Backend-Kommunikation via flutter_rust_bridge | 🔴 Geplant | +| E2E Tests (Web) | WebRTC-Streaming und UI-Flüsse testen | 🔴 Geplant | +| CI/CD Optimierung | GStreamer-Builds beschleunigen | 🟡 In Arbeit | + +## Phase 2: Plattformerweiterung (Q2–Q3 2024) + +| Feature | Beschreibung | Status | +|---------|-------------|--------| +| Android-Support | Flutter auf Android mit C++ FFI | 🔴 Geplant | +| iOS-Support | Cross-compile für iOS (Metal statt OpenGL) | 🔴 Geplant | +| Orange Pi Optimierung | Performance-Tuning für Orange Pi Zero | 🟡 In Arbeit | +| Docker-Compose Setup | Multi-Container für Dev/Production | 🟡 In Arbeit | + +## Phase 3: KI-Modelle & Performance (Q3–Q4 2024) + +| Feature | Beschreibung | Status | +|---------|-------------|--------| +| TensorFlow Lite Integration | Leichtgewichtige Modelle | 🔴 Geplant | +| ONNX Runtime Support | Modellportabilität | 🔴 Geplant | +| GPU Acceleration (Vulkan) | Desktop-Performance | 🔴 Geplant | +| Edge TPU Support | Google Coral für Embedded | 🔴 Geplant | + +## Phase 4: Developer Experience (Q4 2024–Q1 2025) + +| Feature | Beschreibung | Status | +|---------|-------------|--------| +| Python Bindings | Schnelle Prototypisierung | 🔴 Geplant | +| CLI Tool | Standalone Inference | 🔴 Geplant | +| Beispielmodelle | Pre-packaged YOLOv5, MobileNet | 🟡 In Arbeit | +| Dokumentation erweitern | Video-Tutorials, Best Practices | 🟡 In Arbeit | + +--- + +**Status-Legende:** +- 🟢 Abgeschlossen +- 🟡 In Arbeit +- 🔴 Geplant + +Möchtest du eine Phase spezifizieren oder weitere Details zu einer bestimmten Funktion? \ No newline at end of file diff --git a/docs/source/upgrade-guide.md b/docs/source/upgrade-guide.md new file mode 100644 index 0000000..f1d229d --- /dev/null +++ b/docs/source/upgrade-guide.md @@ -0,0 +1,8 @@ +## Upgrade Notes + +Upgrade the Flutter/Dart bridge dependencies when the Rust API changes: + +```bash +cargo install flutter_rust_bridge_codegen +flutter_rust_bridge_codegen generate +``` \ No newline at end of file diff --git a/flutter_rust_bridge.yaml b/flutter_rust_bridge.yaml index e15ed91..032b626 100644 --- a/flutter_rust_bridge.yaml +++ b/flutter_rust_bridge.yaml @@ -1,3 +1,4 @@ rust_input: crate::api -rust_root: rust/ +rust_root: ExternalLib/Kataglyphis-RustProjectTemplate +# rust_root: rust/ dart_output: lib/src/rust \ No newline at end of file diff --git a/images/Engine_logo.png b/images/Engine_logo.png new file mode 100644 index 0000000..5b01a41 Binary files /dev/null and b/images/Engine_logo.png differ diff --git a/images/overview.gif b/images/overview.gif new file mode 100644 index 0000000..202f715 Binary files /dev/null and b/images/overview.gif differ diff --git a/integration_test/simple_test.dart b/integration_test/simple_test.dart index 370c27c..38154b5 100644 --- a/integration_test/simple_test.dart +++ b/integration_test/simple_test.dart @@ -1,8 +1,3 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:kataglyphis_inference_engine/main.dart'; -import 'package:kataglyphis_inference_engine/src/rust/frb_generated.dart'; -import 'package:integration_test/integration_test.dart'; - void main() { // IntegrationTestWidgetsFlutterBinding.ensureInitialized(); // setUpAll(() async => await RustLib.init()); diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index ad322bc..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index e041d38..0000000 --- a/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 12.0 - - diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 0b2d479..0000000 --- a/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig new file mode 100644 index 0000000..25ca358 --- /dev/null +++ b/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\flutter +FLUTTER_APPLICATION_PATH=C:\GitHub\Kataglyphis-Inference-Engine +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib\main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig deleted file mode 100644 index 0b2d479..0000000 --- a/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000..a88caf9 --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000..e3ba6fb --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 0000000..7483153 --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\flutter" +export "FLUTTER_APPLICATION_PATH=C:\GitHub\Kataglyphis-Inference-Engine" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index b462c2a..0000000 --- a/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,616 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C8082294A63A400263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 331C8082294A63A400263BE5 /* RunnerTests */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 331C8081294A63A400263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C8080294A63A400263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C807D294A63A400263BE5 /* Sources */, - 331C807F294A63A400263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C8086294A63A400263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C8080294A63A400263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 331C8080294A63A400263BE5 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C807F294A63A400263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C807D294A63A400263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 331C8088294A63A400263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Debug; - }; - 331C8089294A63A400263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Release; - }; - 331C808A294A63A400263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C8088294A63A400263BE5 /* Debug */, - 331C8089294A63A400263BE5 /* Release */, - 331C808A294A63A400263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index c4b79bd..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index fc6bf80..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index af0309c..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 2324e91..0000000 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 59c6d39..0000000 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index fc6bf80..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index af0309c..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift deleted file mode 100644 index 8be1cec..0000000 --- a/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Flutter -import UIKit - -@main -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 1950fd8..0000000 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 7353c41..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 797d452..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6ed2d93..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cd7b00..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index fe73094..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index 321773c..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 797d452..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 502f463..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index 0ec3034..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 0ec3034..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index e9f5fea..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index 84ac32a..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 8953cba..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0467bf1..0000000 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index d08a4de..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19ea..0000000 Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 65a94b5..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 497371e..0000000 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index bbb83ca..0000000 --- a/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/GeneratedPluginRegistrant.h b/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 0000000..7a89092 --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,19 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 0000000..e76e0ed --- /dev/null +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,35 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#import "GeneratedPluginRegistrant.h" + +#if __has_include() +#import +#else +@import integration_test; +#endif + +#if __has_include() +#import +#else +@import permission_handler_apple; +#endif + +#if __has_include() +#import +#else +@import url_launcher_ios; +#endif + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { + [IntegrationTestPlugin registerWithRegistrar:[registry registrarForPlugin:@"IntegrationTestPlugin"]]; + [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; + [URLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"URLLauncherPlugin"]]; +} + +@end diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist deleted file mode 100644 index 8fb0e27..0000000 --- a/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Kataglyphis Inference Engine - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - kataglyphis_inference_engine - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index fae207f..0000000 --- a/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index 4d206de..0000000 --- a/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Flutter -import UIKit -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/lib/Pages/AboutMePage/Widgets/skill_table.dart b/lib/Pages/AboutMePage/Widgets/skill_table.dart index b1a2dd1..81a7ed3 100644 --- a/lib/Pages/AboutMePage/Widgets/skill_table.dart +++ b/lib/Pages/AboutMePage/Widgets/skill_table.dart @@ -8,8 +8,11 @@ import 'package:jotrockenmitlockenrepo/user_settings.dart'; import 'package:jotrockenmitlockenrepo/Decoration/row_divider.dart'; class SkillTable extends StatefulWidget { - const SkillTable( - {super.key, required this.userSettings, required this.aboutMeFile}); + const SkillTable({ + super.key, + required this.userSettings, + required this.aboutMeFile, + }); final UserSettings userSettings; final String aboutMeFile; //= widget.userSettings.aboutMeFileEn!; @@ -59,82 +62,88 @@ class _SkillTableState extends State { Text( key.substring(key.toString().indexOf("(")).trim(), style: Theme.of(context).textTheme.titleMedium, - ) - ]; - } else { - return [ - Text( - key, - style: Theme.of(context).textTheme.titleLarge, ), ]; + } else { + return [Text(key, style: Theme.of(context).textTheme.titleLarge)]; } } @override Widget build(BuildContext context) { final double currentWith = MediaQuery.of(context).size.width; - double betweenColumnPadding = - (currentWith <= narrowScreenWidthThreshold) ? 40.0 : 80.0; + double betweenColumnPadding = (currentWith <= narrowScreenWidthThreshold) + ? 40.0 + : 80.0; return FutureBuilder( - future: _skillTableJson, - builder: (context, data) { - if (data.hasData) { - List keys = data.requireData.$1; - List> values = data.requireData.$2; - List skills = []; - for (int i = 0; i < keys.length; i++) { - String entryVal = ""; - var entryList = values[i]; - for (int j = 0; j < entryList.length; j++) { - entryVal += "• ${entryList[j]}${"\n"}"; - } - skills.add(TableRow(children: [ - TableCell( + future: _skillTableJson, + builder: (context, data) { + if (data.hasData) { + List keys = data.requireData.$1; + List> values = data.requireData.$2; + List skills = []; + for (int i = 0; i < keys.length; i++) { + String entryVal = ""; + var entryList = values[i]; + for (int j = 0; j < entryList.length; j++) { + entryVal += "• ${entryList[j]}${"\n"}"; + } + skills.add( + TableRow( + children: [ + TableCell( child: Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 0, 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: getSkillTableKeys(keys[i]), + padding: const EdgeInsets.fromLTRB(8, 8, 0, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: getSkillTableKeys(keys[i]), + ), + ), ), - )), - TableCell( + TableCell( child: Padding( - padding: EdgeInsets.fromLTRB(betweenColumnPadding, 8, 0, 0), - child: Text(entryVal, - style: Theme.of(context).textTheme.titleMedium), - )) - ])); - skills.add(TableRow(children: [ - rowDivider, - rowDivider, - ])); - } - final double currentWidth = MediaQuery.of(context).size.width; - double skillTableWidth = currentWidth; - if (currentWidth >= mediumWidthBreakpoint) { - skillTableWidth = skillTableWidth * 0.4; - } else { - skillTableWidth = skillTableWidth * 0.9; - } - return CenteredBoxDecoration( - color: Theme.of(context).colorScheme.primary, - child: SizedBox( - width: skillTableWidth, - child: Table( - defaultVerticalAlignment: TableCellVerticalAlignment.top, - children: skills), + padding: EdgeInsets.fromLTRB( + betweenColumnPadding, + 8, + 0, + 0, + ), + child: Text( + entryVal, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + ], ), ); - } else if (data.hasError) { - return Center(child: Text("${data.error}")); + skills.add(TableRow(children: [rowDivider, rowDivider])); + } + final double currentWidth = MediaQuery.of(context).size.width; + double skillTableWidth = currentWidth; + if (currentWidth >= mediumWidthBreakpoint) { + skillTableWidth = skillTableWidth * 0.4; } else { - return const Center( - child: CircularProgressIndicator(), - ); + skillTableWidth = skillTableWidth * 0.9; } - }); + return CenteredBoxDecoration( + color: Theme.of(context).colorScheme.primary, + child: SizedBox( + width: skillTableWidth, + child: Table( + defaultVerticalAlignment: TableCellVerticalAlignment.top, + children: skills, + ), + ), + ); + } else if (data.hasError) { + return Center(child: Text("${data.error}")); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); } } diff --git a/lib/Pages/AboutMePage/about_me_page.dart b/lib/Pages/AboutMePage/about_me_page.dart index 0d40973..396ae0e 100644 --- a/lib/Pages/AboutMePage/about_me_page.dart +++ b/lib/Pages/AboutMePage/about_me_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:kataglyphis_inference_engine/Pages/AboutMePage/Widgets/about_me_table.dart'; -import 'package:kataglyphis_inference_engine/Pages/AboutMePage/Widgets/perfect_day_chart.dart'; import 'package:kataglyphis_inference_engine/Pages/AboutMePage/Widgets/skill_table.dart'; import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/one_two_transition_widget.dart'; import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; @@ -8,8 +7,9 @@ import 'package:jotrockenmitlockenrepo/app_attributes.dart'; import 'package:jotrockenmitlockenrepo/constants.dart'; import 'package:jotrockenmitlockenrepo/user_settings.dart'; +import 'package:flutter/foundation.dart'; + import 'package:kataglyphis_inference_engine/src/rust/api/simple.dart'; -import 'package:kataglyphis_inference_engine/src/rust/frb_generated.dart'; import 'package:kataglyphis_native_inference/kataglyphis_native_inference.dart'; @@ -44,21 +44,22 @@ class AboutMePageState extends State { 'Action: Call Rust `greet("Tom")`\nResult: `${greet(name: "Tom")}`', ), ), - Center( - child: FutureBuilder( - future: KataglyphisNativeInference.add(3, 4), // call native method - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - if (snapshot.hasError) { - return Text('Error: ${snapshot.error}'); - } - final value = snapshot.data ?? 0; - return Text('Native result: $value'); - }, + if (!kIsWeb) + Center( + child: FutureBuilder( + future: KataglyphisNativeInference.add(3, 4), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const CircularProgressIndicator(); + } + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } + final value = snapshot.data ?? 0; + return Text('Native result: $value'); + }, + ), ), - ) ]; List childWidgetsRightPage = [ // const PerfectDay(), diff --git a/lib/Pages/Blog/blog_page.dart b/lib/Pages/Blog/blog_page.dart index 8c7da9b..3d4c0c0 100644 --- a/lib/Pages/Blog/blog_page.dart +++ b/lib/Pages/Blog/blog_page.dart @@ -11,11 +11,12 @@ class BlogPage extends StatefulWidget { final AppAttributes appAttributes; final Footer footer; final BlogPageConfig blogPageConfig; - const BlogPage( - {super.key, - required this.appAttributes, - required this.footer, - required this.blogPageConfig}); + const BlogPage({ + super.key, + required this.appAttributes, + required this.footer, + required this.blogPageConfig, + }); @override State createState() => BlogPageState(); @@ -46,10 +47,7 @@ class BlogPageState extends State { imageDirectory: widget.blogPageConfig.imageDir, useLightMode: widget.appAttributes.useLightMode, ), - FileTable( - title: 'Appendix', - docs: docs, - ) + FileTable(title: 'Appendix', docs: docs), ], ); } diff --git a/lib/Pages/DataPage/BlockOverviewPage/block_entry.dart b/lib/Pages/DataPage/BlockOverviewPage/block_entry.dart index 46a75c9..87464d4 100644 --- a/lib/Pages/DataPage/BlockOverviewPage/block_entry.dart +++ b/lib/Pages/DataPage/BlockOverviewPage/block_entry.dart @@ -5,11 +5,7 @@ class BlockEntry extends TableData { String date = "placeholder"; String comment = "placeholder"; - BlockEntry({ - required this.title, - required this.date, - required this.comment, - }); + BlockEntry({required this.title, required this.date, required this.comment}); @override List getCells() { diff --git a/lib/Pages/DataPage/BlockOverviewPage/block_entry_list.dart b/lib/Pages/DataPage/BlockOverviewPage/block_entry_list.dart index 9e896dd..6c59e14 100644 --- a/lib/Pages/DataPage/BlockOverviewPage/block_entry_list.dart +++ b/lib/Pages/DataPage/BlockOverviewPage/block_entry_list.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/BooksPage/book.dart'; +import 'package:kataglyphis_inference_engine/Pages/DataPage/BlockOverviewPage/block_entry.dart'; import 'package:jotrockenmitlockenrepo/Media/DataTable/data_list.dart'; import 'package:jotrockenmitlockenrepo/Media/DataTable/datacell_content_strategies.dart'; import 'package:jotrockenmitlockenrepo/app_attributes.dart'; @@ -24,7 +24,7 @@ class BlockEntryList extends DataList { final AppAttributes appAttributes; } -class _BlockEntryListState extends DataListState { +class _BlockEntryListState extends DataListState { @override List getSpacing(bool isMobileDevice) { if (isMobileDevice) { @@ -39,7 +39,7 @@ class _BlockEntryListState extends DataListState { return [ DataCellContentStrategies.text, DataCellContentStrategies.text, - DataCellContentStrategies.textButton + DataCellContentStrategies.textButton, ]; } } diff --git a/lib/Pages/DataPage/BooksPage/book.dart b/lib/Pages/DataPage/BooksPage/book.dart deleted file mode 100644 index 8400571..0000000 --- a/lib/Pages/DataPage/BooksPage/book.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Media/DataTable/table_data.dart'; - -class Book extends TableData { - String title = "placeholder"; - String author = "placeholder"; - String isbn = "placeholder"; - String comment = "placeholder"; - - Book({ - required this.title, - required this.author, - required this.isbn, - required this.comment, - }); - - @override - List getCells() { - return [title, author, isbn, comment]; - } -} diff --git a/lib/Pages/DataPage/BooksPage/books_list.dart b/lib/Pages/DataPage/BooksPage/books_list.dart deleted file mode 100644 index a2ccf2c..0000000 --- a/lib/Pages/DataPage/BooksPage/books_list.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/BooksPage/book.dart'; -import 'package:kataglyphis_inference_engine/blog_dependent_app_attributes.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/csv_data_list.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/datacell_content_strategies.dart'; -import 'package:kataglyphis_inference_engine/my_two_cents_config.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; - -class BooksList extends CsvDataList { - const BooksList( - {super.key, - required super.dataFilePath, - required super.title, - required super.entryRedirectText, - required super.description, - // all entries with a critic should be displayed in the very beginning :) - super.sortColumnIndex = 3, - super.sortOnLoaded = true, - // super.isAscending = true, - required this.appAttributes, - required this.blogDependentAppAttributes}); - //"Books worth reading" - @override - State createState() => _BooksListState(); - - final AppAttributes appAttributes; - final BlogDependentAppAttributes blogDependentAppAttributes; -} - -class _BooksListState extends CsvDataListState { - @override - Future<(List, List)> convertRawCSVDataToFinalLayout( - List> csvListData) async { - List configs = - widget.blogDependentAppAttributes.twoCentsConfigs; - List dataCategories = List.from(csvListData.first); - List convertedCsvListData = - csvListData.getRange(1, csvListData.length).toList().map((List e) { - int index = configs - .indexWhere((item) => item.mediaTitle == e.elementAt(0).toString()); - return Book( - title: e.elementAt(0).toString(), - author: e.elementAt(1).toString(), - isbn: e.elementAt(2).toString(), - comment: index != -1 ? configs[index].routingName : "", - ); - }).toList(); - return (convertedCsvListData, dataCategories); - } - - @override - List getSpacing(bool isMobileDevice) { - if (isMobileDevice) { - return [0.4, 0.3, 0.2, 0.3]; - } else { - return [0.2, 0.2, 0.2, 0.2]; - } - } - - @override - List getDataCellContentStrategies() { - return [ - DataCellContentStrategies.text, - DataCellContentStrategies.text, - DataCellContentStrategies.text, - DataCellContentStrategies.textButton - ]; - } -} diff --git a/lib/Pages/DataPage/BooksPage/books_page.dart b/lib/Pages/DataPage/BooksPage/books_page.dart deleted file mode 100644 index 731afcf..0000000 --- a/lib/Pages/DataPage/BooksPage/books_page.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/BooksPage/books_list.dart'; -import 'package:kataglyphis_inference_engine/blog_dependent_app_attributes.dart'; -import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/single_page.dart'; - -class BooksPage extends StatefulWidget { - final AppAttributes appAttributes; - final BlogDependentAppAttributes blogDependentAppAttributes; - final Footer footer; - const BooksPage({ - super.key, - required this.appAttributes, - required this.footer, - required this.blogDependentAppAttributes, - }); - - @override - State createState() => BooksPageState(); -} - -class BooksPageState extends State { - @override - Widget build(BuildContext context) { - return SinglePage( - footer: widget.footer, - appAttributes: widget.appAttributes, - showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, - showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - children: [ - BooksList( - blogDependentAppAttributes: widget.blogDependentAppAttributes, - entryRedirectText: AppLocalizations.of(context)!.entryRedirectText, - appAttributes: widget.appAttributes, - title: AppLocalizations.of(context)!.books, - description: - "${AppLocalizations.of(context)!.booksDescription}\u{1F63A}", - dataFilePath: "assets/data/Buecherliste_gelesen.csv", - ), - ], - ); - } -} diff --git a/lib/Pages/DataPage/BooksPage/books_page_config.dart b/lib/Pages/DataPage/BooksPage/books_page_config.dart deleted file mode 100644 index 7975c30..0000000 --- a/lib/Pages/DataPage/BooksPage/books_page_config.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Pages/stateful_branch_info_provider.dart'; - -class BooksPageConfig extends StatefulBranchInfoProvider { - @override - String getRoutingName() { - return '/books'; - } -} diff --git a/lib/Pages/DataPage/FilmsPage/film.dart b/lib/Pages/DataPage/FilmsPage/film.dart deleted file mode 100644 index 6d9b4ec..0000000 --- a/lib/Pages/DataPage/FilmsPage/film.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Media/DataTable/table_data.dart'; - -class Film extends TableData { - String title = "placeholder"; - String genre = "placeholder"; - String actor = "placeholder"; - String sonstiges = "placeholder"; - Film( - {required this.title, - required this.genre, - required this.actor, - required this.sonstiges}); - - @override - List getCells() { - return [title, genre, actor, sonstiges]; - } -} diff --git a/lib/Pages/DataPage/FilmsPage/films_list.dart b/lib/Pages/DataPage/FilmsPage/films_list.dart deleted file mode 100644 index 8848cb4..0000000 --- a/lib/Pages/DataPage/FilmsPage/films_list.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/csv_data_list.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/FilmsPage/film.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/datacell_content_strategies.dart'; - -class FilmsList extends CsvDataList { - const FilmsList( - {super.key, - required super.dataFilePath, - required super.title, - required super.entryRedirectText, - required super.description}); - // "Films/Series worth watching" - @override - State createState() => _FilmsListState(); -} - -class _FilmsListState extends CsvDataListState { - @override - Future<(List, List)> convertRawCSVDataToFinalLayout( - List> csvListData) async { - List dataCategories = List.from(csvListData.first); - List convertedCsvListData = csvListData - .getRange(1, csvListData.length) - .toList() - .map((List e) => Film( - title: e.elementAt(0).toString(), - genre: e.elementAt(1).toString(), - actor: e.elementAt(2).toString(), - sonstiges: e.elementAt(3).toString(), - )) - .toList(); - return (convertedCsvListData, dataCategories); - } - - @override - List getSpacing(bool isMobileDevice) { - if (isMobileDevice) { - return [0.35, 0.3, 0.3, 0.2]; - } else { - return [0.2, 0.2, 0.2, 0.2]; - } - } - - @override - List getDataCellContentStrategies() { - return [ - DataCellContentStrategies.text, - DataCellContentStrategies.text, - DataCellContentStrategies.text, - DataCellContentStrategies.text - ]; - } -} diff --git a/lib/Pages/DataPage/FilmsPage/films_page.dart b/lib/Pages/DataPage/FilmsPage/films_page.dart deleted file mode 100644 index b392f11..0000000 --- a/lib/Pages/DataPage/FilmsPage/films_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/FilmsPage/films_list.dart'; -import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/single_page.dart'; - -class FilmsPage extends StatefulWidget { - final AppAttributes appAttributes; - final Footer footer; - const FilmsPage({ - super.key, - required this.appAttributes, - required this.footer, - }); - - @override - State createState() => FilmsPageState(); -} - -class FilmsPageState extends State { - @override - Widget build(BuildContext context) { - return SinglePage( - footer: widget.footer, - appAttributes: widget.appAttributes, - showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, - showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - children: [ - FilmsList( - entryRedirectText: AppLocalizations.of(context)!.entryRedirectText, - title: AppLocalizations.of(context)!.films, - description: - "${AppLocalizations.of(context)!.filmsDescription}\u{1F63A}", - dataFilePath: "assets/data/Filmliste_gesehen.csv", - ), - ], - ); - } -} diff --git a/lib/Pages/DataPage/FilmsPage/films_page_config.dart b/lib/Pages/DataPage/FilmsPage/films_page_config.dart deleted file mode 100644 index db0115a..0000000 --- a/lib/Pages/DataPage/FilmsPage/films_page_config.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Pages/stateful_branch_info_provider.dart'; - -class FilmsPageConfig extends StatefulBranchInfoProvider { - @override - String getRoutingName() { - return '/films'; - } -} diff --git a/lib/Pages/DataPage/GamesPage/game.dart b/lib/Pages/DataPage/GamesPage/game.dart deleted file mode 100644 index 14ddf68..0000000 --- a/lib/Pages/DataPage/GamesPage/game.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Media/DataTable/table_data.dart'; - -class Game extends TableData { - String title = "placeholder"; - String developer = "placeholder"; - String comment = "placeholder"; - - Game({ - required this.title, - required this.developer, - required this.comment, - }); - - @override - List getCells() { - return [title, developer, comment]; - } -} diff --git a/lib/Pages/DataPage/GamesPage/games_list.dart b/lib/Pages/DataPage/GamesPage/games_list.dart deleted file mode 100644 index de122b0..0000000 --- a/lib/Pages/DataPage/GamesPage/games_list.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/GamesPage/game.dart'; -import 'package:kataglyphis_inference_engine/blog_dependent_app_attributes.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/csv_data_list.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/datacell_content_strategies.dart'; -import 'package:kataglyphis_inference_engine/my_two_cents_config.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; - -class GamesList extends CsvDataList { - const GamesList( - {super.key, - required super.dataFilePath, - required super.title, - required super.entryRedirectText, - required super.description, - required this.blogDependentAppAttributes, - // all entries with a critic should be displayed in the very beginning :) - super.sortColumnIndex = 2, - super.sortOnLoaded = true, - // super.isAscending = true, - required this.appAttributes}); - //"Books worth reading" - @override - State createState() => _GamesListState(); - - final AppAttributes appAttributes; - final BlogDependentAppAttributes blogDependentAppAttributes; -} - -class _GamesListState extends CsvDataListState { - @override - Future<(List, List)> convertRawCSVDataToFinalLayout( - List> csvListData) async { - List configs = - widget.blogDependentAppAttributes.twoCentsConfigs; - List dataCategories = List.from(csvListData.first); - List convertedCsvListData = - csvListData.getRange(1, csvListData.length).toList().map((List e) { - int index = configs - .indexWhere((item) => item.mediaTitle == e.elementAt(0).toString()); - return Game( - title: e.elementAt(0).toString(), - developer: e.elementAt(1).toString(), - comment: index != -1 ? configs[index].routingName : "", - ); - }).toList(); - return (convertedCsvListData, dataCategories); - } - - @override - List getSpacing(bool isMobileDevice) { - if (isMobileDevice) { - return [0.3, 0.3, 0.3]; - } else { - return [0.3, 0.3, 0.3]; - } - } - - @override - List getDataCellContentStrategies() { - return [ - DataCellContentStrategies.text, - DataCellContentStrategies.text, - DataCellContentStrategies.textButton - ]; - } -} diff --git a/lib/Pages/DataPage/GamesPage/games_page.dart b/lib/Pages/DataPage/GamesPage/games_page.dart deleted file mode 100644 index 535a50d..0000000 --- a/lib/Pages/DataPage/GamesPage/games_page.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/GamesPage/games_list.dart'; -import 'package:kataglyphis_inference_engine/blog_dependent_app_attributes.dart'; -import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/single_page.dart'; - -class GamesPage extends StatefulWidget { - final AppAttributes appAttributes; - final Footer footer; - final BlogDependentAppAttributes blogDependentAppAttributes; - const GamesPage({ - super.key, - required this.appAttributes, - required this.footer, - required this.blogDependentAppAttributes, - }); - - @override - State createState() => GamesPageState(); -} - -class GamesPageState extends State { - @override - Widget build(BuildContext context) { - return SinglePage( - footer: widget.footer, - appAttributes: widget.appAttributes, - showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, - showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - children: [ - GamesList( - blogDependentAppAttributes: widget.blogDependentAppAttributes, - entryRedirectText: AppLocalizations.of(context)!.entryRedirectText, - appAttributes: widget.appAttributes, - title: AppLocalizations.of(context)!.games, - description: - "${AppLocalizations.of(context)!.gamesDescription}\u{1F63A}", - dataFilePath: "assets/data/Spiele.csv", - ), - ], - ); - } -} diff --git a/lib/Pages/DataPage/GamesPage/games_page_config.dart b/lib/Pages/DataPage/GamesPage/games_page_config.dart deleted file mode 100644 index e0f345c..0000000 --- a/lib/Pages/DataPage/GamesPage/games_page_config.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Pages/stateful_branch_info_provider.dart'; - -class GamesPageConfig extends StatefulBranchInfoProvider { - @override - String getRoutingName() { - return '/games'; - } -} diff --git a/lib/Pages/DataPage/QuotesPage/quotations_page_config.dart b/lib/Pages/DataPage/QuotesPage/quotations_page_config.dart deleted file mode 100644 index 9e36e3b..0000000 --- a/lib/Pages/DataPage/QuotesPage/quotations_page_config.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Pages/stateful_branch_info_provider.dart'; - -class QuotationsPageConfig extends StatefulBranchInfoProvider { - @override - String getRoutingName() { - return '/quotations'; - } -} diff --git a/lib/Pages/DataPage/QuotesPage/quote.dart b/lib/Pages/DataPage/QuotesPage/quote.dart deleted file mode 100644 index 3b1851a..0000000 --- a/lib/Pages/DataPage/QuotesPage/quote.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:jotrockenmitlockenrepo/Media/DataTable/table_data.dart'; - -class Quote extends TableData { - String author = "placeholder"; - String content = "placeholder"; - Quote({required this.author, required this.content}); - - @override - List getCells() { - return [author, content]; - } -} diff --git a/lib/Pages/DataPage/QuotesPage/quotes_list.dart b/lib/Pages/DataPage/QuotesPage/quotes_list.dart deleted file mode 100644 index a879cad..0000000 --- a/lib/Pages/DataPage/QuotesPage/quotes_list.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/csv_data_list.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/QuotesPage/quote.dart'; -import 'package:jotrockenmitlockenrepo/Media/DataTable/datacell_content_strategies.dart'; - -class QuotesList extends CsvDataList { - const QuotesList({ - super.key, - required super.entryRedirectText, - required super.dataFilePath, - required super.title, - required super.description, - }); - - @override - State createState() => _QuotesListState(); -} - -class _QuotesListState extends CsvDataListState { - String _formatQuote(String unformattedQuote) { - return "»$unformattedQuote«"; - } - - @override - Future<(List, List)> convertRawCSVDataToFinalLayout( - List> csvListData) async { - List dataCategories = List.from(csvListData.first); - List convertedCsvListData = csvListData - .getRange(1, csvListData.length) - .toList() - .map((List e) => Quote( - author: e.elementAt(0).toString(), - content: _formatQuote(e.elementAt(1).toString()), - )) - .toList(); - return (convertedCsvListData, dataCategories); - } - - @override - List getSpacing(bool isMobileDevice) { - if (isMobileDevice) { - return [0.2, 0.65]; - } else { - return [0.2, 0.65]; - } - } - - @override - List getDataCellContentStrategies() { - return [ - DataCellContentStrategies.text, - DataCellContentStrategies.text, - ]; - } -} diff --git a/lib/Pages/DataPage/QuotesPage/quotes_page.dart b/lib/Pages/DataPage/QuotesPage/quotes_page.dart deleted file mode 100644 index 058435a..0000000 --- a/lib/Pages/DataPage/QuotesPage/quotes_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/QuotesPage/quotes_list.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/single_page.dart'; - -class QuotesPage extends StatefulWidget { - final AppAttributes appAttributes; - final Footer footer; - const QuotesPage({ - super.key, - required this.appAttributes, - required this.footer, - }); - - @override - State createState() => QuotesPageState(); -} - -class QuotesPageState extends State { - @override - Widget build(BuildContext context) { - return SinglePage( - footer: widget.footer, - appAttributes: widget.appAttributes, - showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, - showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - children: [ - QuotesList( - entryRedirectText: AppLocalizations.of(context)!.entryRedirectText, - title: AppLocalizations.of(context)!.quotations, - description: - "${AppLocalizations.of(context)!.quotationsDescription}\u{1F63A}", - dataFilePath: "assets/data/Zitate.csv", - ), - ], - ); - } -} diff --git a/lib/Pages/DataPage/data_page.dart b/lib/Pages/DataPage/data_page.dart deleted file mode 100644 index 16cdcbf..0000000 --- a/lib/Pages/DataPage/data_page.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/data_page_entry.dart'; -import 'package:jotrockenmitlockenrepo/Decoration/row_divider.dart'; - -import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; -import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/one_two_transition_widget.dart'; -import 'package:jotrockenmitlockenrepo/app_attributes.dart'; - -class DataPage extends StatefulWidget { - final AppAttributes appAttributes; - final Footer footer; - const DataPage({ - super.key, - required this.appAttributes, - required this.footer, - }); - - @override - State createState() => DataPageState(); -} - -class DataPageState extends State { - List> _createLandingPageChildWidgets(BuildContext context) { - List childWidgetsLeftPage = [ - DataPageEntry( - label: AppLocalizations.of(context)!.quotations, - routerPath: '/quotations', - imagePath: 'assets/images/Pages/Data/Quotes_cover.jpg', - description: AppLocalizations.of(context)!.quotationsDescription, - lastModified: 'babbeln', - ), - rowDivider, - DataPageEntry( - label: AppLocalizations.of(context)!.films, - routerPath: '/films', - imagePath: 'assets/images/Pages/Data/Film_cover.jpg', - description: AppLocalizations.of(context)!.filmsDescription, - lastModified: 'glotze', - ), - rowDivider, - ]; - List childWidgetsRightPage = [ - DataPageEntry( - label: AppLocalizations.of(context)!.books, - routerPath: '/books', - imagePath: 'assets/images/Pages/Data/Book_cover.jpg', - description: AppLocalizations.of(context)!.booksDescription, - lastModified: '.._..', - ), - rowDivider, - DataPageEntry( - label: AppLocalizations.of(context)!.games, - routerPath: '/games', - imagePath: 'assets/images/Pages/Data/Spiele_cover.jpg', - description: AppLocalizations.of(context)!.gamesDescription, - lastModified: 'Go rust', - ), - ]; - - return [childWidgetsLeftPage, childWidgetsRightPage]; - } - - @override - Widget build(BuildContext context) { - var homePagesLeftRight = _createLandingPageChildWidgets(context); - - return OneTwoTransitionPage( - childWidgetsLeftPage: homePagesLeftRight[0], - childWidgetsRightPage: homePagesLeftRight[1], - appAttributes: widget.appAttributes, - footer: widget.footer, - showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, - showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - railAnimation: widget.appAttributes.railAnimation, - ); - } -} diff --git a/lib/Pages/DataPage/data_page_entry.dart b/lib/Pages/DataPage/data_page_entry.dart deleted file mode 100644 index 0ab3f60..0000000 --- a/lib/Pages/DataPage/data_page_entry.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:go_router/go_router.dart'; -import 'package:jotrockenmitlockenrepo/Decoration/row_divider.dart'; -import 'package:jotrockenmitlockenrepo/Decoration/component_group_decoration.dart'; -import 'package:jotrockenmitlockenrepo/Media/Image/openable_image.dart'; -import 'package:flutter/material.dart'; - -class DataPageEntry extends StatefulWidget { - const DataPageEntry({ - super.key, - required this.label, - required this.routerPath, - required this.imagePath, - required this.description, - required this.lastModified, - this.imageCaptioning, - }); - final String label; - final String routerPath; - final String imagePath; - final String description; - final String? imageCaptioning; - final String lastModified; - - @override - State createState() => DataPageEntryState(); -} - -class DataPageEntryState extends State { - bool isDisabled = false; - - @override - Widget build(BuildContext context) { - List undecoratedChilds = [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Text( - widget.description, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - ], - ), - rowDivider, - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FilledButton.tonal( - onPressed: - isDisabled - ? null - : () { - context.go(widget.routerPath); - }, - child: Text( - AppLocalizations.of(context)!.follow, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - ], - ), - rowDivider, - OpenableImage( - displayedImage: widget.imagePath, - disableOpen: true, - imageCaptioning: widget.imageCaptioning, - ), - rowDivider, - Padding( - padding: const EdgeInsets.only(right: 34.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - widget.lastModified, - style: Theme.of(context).textTheme.titleSmall, - ), - ], - ), - ), - ]; - return Container( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ComponentGroupDecoration( - label: widget.label, - children: [...undecoratedChilds], - ), - ], - ), - ); - } -} diff --git a/lib/Pages/DataPage/data_pages_navbar_page_config.dart b/lib/Pages/DataPage/data_pages_navbar_page_config.dart deleted file mode 100644 index c2fb1d8..0000000 --- a/lib/Pages/DataPage/data_pages_navbar_page_config.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; -import 'package:jotrockenmitlockenrepo/Pages/navbar_page_config.dart'; - -class DataPageNavBarConfig extends NavBarPageConfig { - @override - NavigationDestination getNavigationDestination(BuildContext context) { - return NavigationDestination( - tooltip: '', - icon: const Icon(Icons.folder_open_outlined), - label: AppLocalizations.of(context)!.data, - selectedIcon: const Icon(Icons.folder_open), - ); - } - - @override - String getRoutingName() { - return '/data'; - } -} diff --git a/lib/Pages/DataPage/media_critics_page.dart b/lib/Pages/DataPage/media_critics_page.dart index 1e11f53..feb4483 100644 --- a/lib/Pages/DataPage/media_critics_page.dart +++ b/lib/Pages/DataPage/media_critics_page.dart @@ -11,11 +11,12 @@ class MediaCriticsPage extends StatefulWidget { final AppAttributes appAttributes; final Footer footer; final MyTwoCentsConfig mediaCriticsPageConfig; - const MediaCriticsPage( - {super.key, - required this.appAttributes, - required this.footer, - required this.mediaCriticsPageConfig}); + const MediaCriticsPage({ + super.key, + required this.appAttributes, + required this.footer, + required this.mediaCriticsPageConfig, + }); @override State createState() => MediaCriticsPageState(); @@ -46,10 +47,7 @@ class MediaCriticsPageState extends State { imageDirectory: widget.mediaCriticsPageConfig.imageDir, useLightMode: widget.appAttributes.useLightMode, ), - FileTable( - title: 'Appendix', - docs: docs, - ) + FileTable(title: 'Appendix', docs: docs), ], ); } diff --git a/lib/Pages/ErrorPage/error_page.dart b/lib/Pages/ErrorPage/error_page.dart index de14663..6eb8df3 100644 --- a/lib/Pages/ErrorPage/error_page.dart +++ b/lib/Pages/ErrorPage/error_page.dart @@ -7,8 +7,11 @@ import 'package:jotrockenmitlockenrepo/app_attributes.dart'; class ErrorPage extends StatefulWidget { final AppAttributes appAttributes; final Footer footer; - const ErrorPage( - {super.key, required this.appAttributes, required this.footer}); + const ErrorPage({ + super.key, + required this.appAttributes, + required this.footer, + }); @override State createState() => ErrorPageState(); @@ -22,9 +25,7 @@ class ErrorPageState extends State { appAttributes: widget.appAttributes, showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, - children: const [ - ErrorPageWidget(), - ], + children: const [ErrorPageWidget()], ); } } diff --git a/lib/Pages/ErrorPage/error_page_widget.dart b/lib/Pages/ErrorPage/error_page_widget.dart index 4d9dd22..25eff84 100644 --- a/lib/Pages/ErrorPage/error_page_widget.dart +++ b/lib/Pages/ErrorPage/error_page_widget.dart @@ -3,9 +3,7 @@ import 'package:jotrockenmitlockenrepo/Decoration/component_group_decoration.dar import 'package:flutter/material.dart'; class ErrorPageWidget extends StatefulWidget { - const ErrorPageWidget({ - super.key, - }); + const ErrorPageWidget({super.key}); @override State createState() => _ErrorPageWidget(); @@ -15,18 +13,21 @@ class _ErrorPageWidget extends State { @override Widget build(BuildContext context) { const colDivider = SizedBox(height: 10); - return ComponentGroupDecoration(label: 'Error 404', children: [ - colDivider, - CenteredBoxDecoration( - borderRadius: 0, - borderWidth: 5, - color: Theme.of(context).colorScheme.primary, - child: ClipRRect( - borderRadius: BorderRadius.circular(0), - child: Image.asset("assets/images/Pages/Error/error404.gif"), + return ComponentGroupDecoration( + label: 'Error 404', + children: [ + colDivider, + CenteredBoxDecoration( + borderRadius: 0, + borderWidth: 5, + color: Theme.of(context).colorScheme.primary, + child: ClipRRect( + borderRadius: BorderRadius.circular(0), + child: Image.asset("assets/images/Pages/Error/error404.gif"), + ), ), - ), - colDivider - ]); + colDivider, + ], + ); } } diff --git a/lib/Pages/StreamPage/stream_page.dart b/lib/Pages/StreamPage/stream_page.dart index dcdd9b9..087d80b 100644 --- a/lib/Pages/StreamPage/stream_page.dart +++ b/lib/Pages/StreamPage/stream_page.dart @@ -1,14 +1,21 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:kataglyphis_inference_engine/l10n/app_localizations.dart'; import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; import 'package:jotrockenmitlockenrepo/Layout/ResponsiveDesign/single_page.dart'; import 'package:jotrockenmitlockenrepo/app_attributes.dart'; -import 'package:jotrockenmitlockenrepo/constants.dart'; +import 'package:permission_handler/permission_handler.dart'; + +// Web imports (only loaded on web) +// conditional import: stub for non-web, web impl for web +import 'package:kataglyphis_inference_engine/Pages/StreamPage/webrtc_view_stub.dart' + if (dart.library.js_interop) 'package:kataglyphis_inference_engine/Pages/StreamPage/webrtc_view.dart' + as webrtc_import; class StreamPage extends StatefulWidget { final AppAttributes appAttributes; final Footer footer; + const StreamPage({ super.key, required this.appAttributes, @@ -20,6 +27,7 @@ class StreamPage extends StatefulWidget { } class StreamPageState extends State { + // Native GStreamer setup static const MethodChannel channel = MethodChannel( 'kataglyphis_native_inference', ); @@ -29,60 +37,190 @@ class StreamPageState extends State { bool _isPlaying = false; String? _errorMessage; - String _currentPipeline = 'v4l2src'; + bool _nativeInitFailed = false; + late final String _defaultNativeSource; + static const int _androidWidth = 320; + static const int _androidHeight = 240; + static const int _androidFps = 15; + // Order: try modern Camera2 NDK (ahcsrc), then generic autodetect, then test pattern + // ahc2src and androidvideosource are not available in current GStreamer builds + final List _androidSourceCandidates = [ + 'ahcsrc', + 'autovideosrc', + 'videotestsrc', + ]; + + int get _targetTextureWidth => _isAndroid ? _androidWidth : textureWidth; + int get _targetTextureHeight => _isAndroid ? _androidHeight : textureHeight; + + bool get _isWindows => defaultTargetPlatform == TargetPlatform.windows; + bool get _isLinux => defaultTargetPlatform == TargetPlatform.linux; + bool get _isMacOS => defaultTargetPlatform == TargetPlatform.macOS; + bool get _isAndroid => defaultTargetPlatform == TargetPlatform.android; @override void initState() { super.initState(); - // Create the texture once. - textureId = channel - .invokeMethod('create', [textureWidth, textureHeight]) - .then((id) { - // Nach erfolgreicher Textur-Erstellung eine Standard-Pipeline setzen - _setPipeline(_buildPipelineString('v4l2src')); + + _defaultNativeSource = _pickDefaultSource(); + + // Native Integration für Desktop + Android initialisieren (nach Android-Permission) + textureId = _initNativeIfNeeded(); + } + + String _pickDefaultSource() { + if (_isWindows) return 'ksvideosrc'; + if (_isLinux) return 'v4l2src'; + if (_isMacOS) return 'avfvideosrc'; + // On Android, prefer ahcsrc (Camera2 NDK) which is more stable than older Camera1 API + if (_isAndroid) return 'ahcsrc'; + return 'videotestsrc'; + } + + Future _initNativeIfNeeded() async { + if (kIsWeb || !(_isWindows || _isLinux || _isMacOS || _isAndroid)) { + return null; + } + + if (_isAndroid) { + final bool granted = await _ensureCameraPermission(); + if (!granted) return null; + } + + return _initializeNative(); + } + + Future _ensureCameraPermission() async { + final PermissionStatus status = await Permission.camera.request(); + + if (status.isGranted) return true; + + if (mounted) { + setState(() { + _errorMessage = 'Camera permission is required to start the stream.'; + }); + } + + if (status.isPermanentlyDenied) { + await openAppSettings(); + } + + return false; + } + + Future _initializeNative() { + return channel + .invokeMethod('create', [ + _targetTextureWidth, + _targetTextureHeight, + ]) + .then((id) async { + await _setPipeline( + _buildPipelineString(_defaultNativeSource), + source: _defaultNativeSource, + ); return id; }) .catchError((e) { debugPrint('create texture error: $e'); - setState(() { - _errorMessage = 'Texture creation failed: $e'; - }); + if (mounted) { + setState(() { + _errorMessage = 'Texture creation failed: $e'; + _nativeInitFailed = true; + }); + } throw e; }); } String _buildPipelineString(String source) { + final bool useOverlaySink = _isAndroid; + // Force RGBA on Android to avoid NV12/AHardwareBuffer format negotiation errors on Adreno. + const String pixelFormat = 'RGBA'; + final int w = _isAndroid ? _androidWidth : textureWidth; + final int h = _isAndroid ? _androidHeight : textureHeight; + final int fps = _isAndroid ? _androidFps : 30; + + // Use glimagesink on Android for hardware rendering, appsink for other platforms + final String sink = useOverlaySink + ? 'glimagesink name=overlay qos=true sync=false max-lateness=20000000' + : 'appsink name=sink emit-signals=true sync=false'; + + // Android conversion chain for camera-like sources. + // Do NOT force AHardwareBuffer/NV12 here: different sources/devices negotiate different + // memory types and formats. We keep caps to size/fps and convert to RGBA for the sink. + // NOTE: DO NOT add caps constraints after glcolorconvert - let it auto-negotiate + final String androidGlConvertCamera = + 'video/x-raw,width=$w,height=$h,framerate=$fps/1 ' + // Some Android camera sources output formats (e.g. NV21) that glupload can't always + // negotiate directly; videoconvert makes the pipeline far more robust. + '! videoconvert ! video/x-raw,format=RGBA,width=$w,height=$h ' + '! glupload ! glcolorconvert'; + + // Android conversion chain for videotestsrc. + // videotestsrc produces system-memory frames; forcing AHardwareBuffer caps breaks preroll. + final String androidGlConvertTest = + 'video/x-raw,width=$w,height=$h,framerate=$fps/1 ' + '! glupload ! glcolorconvert'; + switch (source) { case 'videotestsrc': - return 'videotestsrc pattern=ball ! videoconvert ! video/x-raw,format=RGBA,width=$textureWidth,height=$textureHeight,framerate=30/1 ! appsink name=sink emit-signals=true sync=false'; + // Test pattern: safe baseline to verify GStreamer/glimagesink works + if (_isAndroid) { + return 'videotestsrc pattern=ball ! $androidGlConvertTest ! $sink'; + } + return 'videotestsrc pattern=ball ! video/x-raw,width=$w,height=$h,framerate=$fps/1 ! $sink'; + case 'ahcsrc': + // Android Camera2 NDK-based source - most stable camera source + if (_isAndroid) { + return 'ahcsrc ! $androidGlConvertCamera ! $sink'; + } + return 'ahcsrc ! $sink'; + case 'autovideosrc': + // Generic autodetect: lets GStreamer pick the best available platform source + if (_isAndroid) { + return 'autovideosrc ! $androidGlConvertCamera ! $sink'; + } + return 'autovideosrc ! $sink'; case 'v4l2src': - return 'v4l2src device=/dev/video0 ! image/jpeg,width=$textureWidth,height=$textureHeight,framerate=30/1 ! jpegdec ! videoconvert ! video/x-raw,format=RGBA,width=$textureWidth,height=$textureHeight ! appsink name=sink emit-signals=true sync=false'; + return 'v4l2src device=/dev/video0 ! image/jpeg,width=$w,height=$h,framerate=$fps/1 ! jpegdec ! videoconvert ! video/x-raw,format=$pixelFormat,width=$w,height=$h ! $sink'; + case 'ksvideosrc': + return 'ksvideosrc device-index=0 ! videoconvert ! video/x-raw,format=$pixelFormat,width=$w,height=$h,framerate=$fps/1 ! $sink'; + case 'avfvideosrc': + return 'avfvideosrc capture-raw-data=true ! videoconvert ! video/x-raw,format=$pixelFormat,width=$w,height=$h,framerate=$fps/1 ! $sink'; case 'pattern-smpte': - return 'videotestsrc pattern=smpte ! videoconvert ! video/x-raw,format=RGBA,width=$textureWidth,height=$textureHeight,framerate=30/1 ! appsink name=sink emit-signals=true sync=false'; + if (_isAndroid) { + return 'videotestsrc pattern=smpte ! $androidGlConvertTest ! $sink'; + } + return 'videotestsrc pattern=smpte ! video/x-raw,width=$w,height=$h,framerate=$fps/1 ! $sink'; case 'pattern-snow': - return 'videotestsrc pattern=snow ! videoconvert ! video/x-raw,format=RGBA,width=$textureWidth,height=$textureHeight,framerate=30/1 ! appsink name=sink emit-signals=true sync=false'; + if (_isAndroid) { + return 'videotestsrc pattern=snow ! $androidGlConvertTest ! $sink'; + } + return 'videotestsrc pattern=snow ! video/x-raw,width=$w,height=$h,framerate=$fps/1 ! $sink'; default: - return 'videotestsrc pattern=ball ! videoconvert ! video/x-raw,format=RGBA,width=$textureWidth,height=$textureHeight,framerate=30/1 ! appsink name=sink emit-signals=true sync=false'; + if (_isAndroid) { + return 'videotestsrc pattern=ball ! $androidGlConvertTest ! $sink'; + } + return 'videotestsrc pattern=ball ! video/x-raw,width=$w,height=$h,framerate=$fps/1 ! $sink'; } } - Future _setPipeline(String pipelineString) async { + Future _setPipeline(String pipelineString, {String? source}) async { try { + if (!mounted) return; setState(() { _errorMessage = null; }); - // Stoppe zuerst die alte Pipeline if (_isPlaying) { await channel.invokeMethod('stop'); } - // Setze neue Pipeline await channel.invokeMethod('setPipeline', pipelineString); - - // Starte die Pipeline await channel.invokeMethod('play'); + if (!mounted) return; setState(() { _isPlaying = true; }); @@ -90,28 +228,76 @@ class StreamPageState extends State { debugPrint('Pipeline set and playing: $pipelineString'); } on PlatformException catch (e) { debugPrint('setPipeline failed: $e'); + final message = e.message ?? ''; + final bool missingElement = + message.contains('no element') || message.contains('not found'); + final bool shouldRetryAndroid = + _isAndroid && + source != null && + (missingElement || e.code == 'command_failed'); + + if (shouldRetryAndroid) { + // Print native-side diagnostics once per failure to avoid guesswork. + // This does not change UX, it only improves logs. + final String? diag = await channel + .invokeMethod('diagnose') + .catchError((_) => null); + if (diag != null && diag.isNotEmpty) { + debugPrint('GStreamer diagnose:\n$diag'); + } + + final String? nextSource = _nextAndroidSource(source); + if (nextSource != null) { + debugPrint( + missingElement + ? 'Android pipeline "$source" missing; trying "$nextSource"' + : 'Android pipeline "$source" failed; trying "$nextSource"', + ); + await _setPipeline( + _buildPipelineString(nextSource), + source: nextSource, + ); + return; + } + } + + if (!mounted) return; setState(() { - _errorMessage = 'Pipeline error: ${e.message}'; + _errorMessage = _isAndroid && shouldRetryAndroid + ? 'GStreamer pipeline failed on Android (tried: ${_androidSourceCandidates.join(', ')}). See logs for diagnose output.' + : 'Pipeline error: ${e.message}'; _isPlaying = false; }); } } + String? _nextAndroidSource(String failedSource) { + final int idx = _androidSourceCandidates.indexOf(failedSource); + if (idx == -1) return null; + if (idx + 1 < _androidSourceCandidates.length) { + return _androidSourceCandidates[idx + 1]; + } + return null; + } + Future _togglePlayPause() async { try { if (_isPlaying) { await channel.invokeMethod('pause'); + if (!mounted) return; setState(() { _isPlaying = false; }); } else { await channel.invokeMethod('play'); + if (!mounted) return; setState(() { _isPlaying = true; }); } } on PlatformException catch (e) { debugPrint('togglePlayPause failed: $e'); + if (!mounted) return; setState(() { _errorMessage = 'Play/Pause error: ${e.message}'; }); @@ -121,6 +307,7 @@ class StreamPageState extends State { Future _stopPipeline() async { try { await channel.invokeMethod('stop'); + if (!mounted) return; setState(() { _isPlaying = false; }); @@ -129,6 +316,15 @@ class StreamPageState extends State { } } + @override + void dispose() { + channel + .invokeMethod('stop') + .catchError((e) => debugPrint('stop on dispose failed: $e')); + _isPlaying = false; + super.dispose(); + } + Future setColor(int r, int g, int b) async { try { await channel.invokeMethod('setColor', [r, g, b]); @@ -139,10 +335,60 @@ class StreamPageState extends State { @override Widget build(BuildContext context) { - bool isMobileDevice = - MediaQuery.of(context).size.width <= narrowScreenWidthThreshold; - Locale currentLocale = Localizations.localeOf(context); + // Web: vollwertige WebRTC-Ansicht + if (kIsWeb) { + return _buildWebView(); + } + + // Desktop + Android: Native GStreamer-/Texture-Ansicht + if (_isWindows || _isLinux || _isMacOS || _isAndroid) { + return _buildNativeView(); + } + // Fallback für andere Plattformen: + // Zeige die WebRTC-Stub-Ansicht (reine Flutter-UI, kein Native-Code). + return _buildWebView(); + } + + Widget _buildWebView() { + return SinglePage( + footer: widget.footer, + appAttributes: widget.appAttributes, + showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, + showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, + children: [ + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 10, + children: [ + Container( + constraints: const BoxConstraints( + maxWidth: 800, + maxHeight: 600, + ), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey, width: 2), + borderRadius: BorderRadius.circular(8), + ), + child: webrtc_import.WebRTCView( + signalingUrl: 'ws://127.0.0.1:8443', + producerIdToConsume: null, + ), + ), + const SizedBox(height: 20), + const Text( + 'WebRTC Stream', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ], + ), + ), + ], + ); + } + + Widget _buildNativeView() { return SinglePage( footer: widget.footer, appAttributes: widget.appAttributes, @@ -154,29 +400,44 @@ class StreamPageState extends State { mainAxisAlignment: MainAxisAlignment.center, spacing: 10, children: [ - // Fehlermeldung anzeigen + // Error message if (_errorMessage != null) Container( padding: const EdgeInsets.all(8), - color: Colors.red.withOpacity(0.1), + color: Colors.red.withValues(alpha: 0.1), child: Text( _errorMessage!, style: const TextStyle(color: Colors.red), ), ), - // Textur-Widget + // Texture widget FutureBuilder( future: textureId, builder: (BuildContext context, AsyncSnapshot snapshot) { + final bool rotateAndroidRight = _isAndroid; + final double displayWidth = + (rotateAndroidRight + ? _targetTextureHeight + : _targetTextureWidth) + .toDouble(); + final double displayHeight = + (rotateAndroidRight + ? _targetTextureWidth + : _targetTextureHeight) + .toDouble(); + if (snapshot.connectionState == ConnectionState.waiting) { return SizedBox( - width: textureWidth.toDouble(), - height: textureHeight.toDouble(), + width: displayWidth, + height: displayHeight, child: const Center(child: CircularProgressIndicator()), ); } if (snapshot.hasError) { + if (_isAndroid) { + return _buildAndroidFallback(snapshot.error?.toString()); + } return SizedBox( width: textureWidth.toDouble(), height: textureHeight.toDouble(), @@ -184,23 +445,37 @@ class StreamPageState extends State { ); } if (!snapshot.hasData || snapshot.data == null) { + if (_isAndroid) { + return _buildAndroidFallback( + 'Error creating texture (null id)', + ); + } return const Text('Error creating texture (null id)'); } + if (_isAndroid && _nativeInitFailed) { + return _buildAndroidFallback('Native init failed'); + } + return Container( decoration: BoxDecoration( border: Border.all(color: Colors.grey, width: 2), ), child: SizedBox( - width: textureWidth.toDouble(), - height: textureHeight.toDouble(), - child: Texture(textureId: snapshot.data!), + width: displayWidth, + height: displayHeight, + child: rotateAndroidRight + ? RotatedBox( + quarterTurns: 1, + child: Texture(textureId: snapshot.data!), + ) + : Texture(textureId: snapshot.data!), ), ); }, ), - // Status-Anzeige + // Status display Text( _isPlaying ? 'Playing' : 'Paused', style: TextStyle( @@ -215,7 +490,7 @@ class StreamPageState extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), - // Play/Pause Button + // Play/Pause buttons Row( mainAxisAlignment: MainAxisAlignment.center, spacing: 10, @@ -239,177 +514,177 @@ class StreamPageState extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), - // GStreamer Pipeline Buttons + // Pipeline buttons Wrap( spacing: 10, runSpacing: 10, alignment: WrapAlignment.center, children: [ - OutlinedButton.icon( - icon: const Icon(Icons.sports_soccer), - label: const Text('Test Pattern (Ball)'), - onPressed: () { - _currentPipeline = 'videotestsrc'; - _setPipeline(_buildPipelineString('videotestsrc')); - }, - ), - OutlinedButton.icon( - icon: const Icon(Icons.grid_on), - label: const Text('SMPTE Pattern'), - onPressed: () { - _currentPipeline = 'pattern-smpte'; - _setPipeline(_buildPipelineString('pattern-smpte')); - }, - ), - OutlinedButton.icon( - icon: const Icon(Icons.grain), - label: const Text('Snow Pattern'), - onPressed: () { - _currentPipeline = 'pattern-snow'; - _setPipeline(_buildPipelineString('pattern-snow')); - }, - ), - OutlinedButton.icon( - icon: const Icon(Icons.videocam), - label: const Text('Webcam (/dev/video0)'), - onPressed: () { - _currentPipeline = 'v4l2src'; - _setPipeline(_buildPipelineString('v4l2src')); - }, - ), + if (_isAndroid) ...[ + // Test pattern - safe baseline + OutlinedButton.icon( + icon: const Icon(Icons.sports_soccer), + label: const Text('Test Pattern'), + onPressed: () { + _setPipeline( + _buildPipelineString('videotestsrc'), + source: 'videotestsrc', + ); + }, + ), + // Camera2 (Modern Pixel 4) + OutlinedButton.icon( + icon: const Icon(Icons.videocam), + label: const Text('Camera (ahc2src)'), + onPressed: () { + _setPipeline( + _buildPipelineString('ahc2src'), + source: 'ahc2src', + ); + }, + ), + // Autodetect - best available + OutlinedButton.icon( + icon: const Icon(Icons.auto_awesome), + label: const Text('Auto Camera'), + onPressed: () { + _setPipeline( + _buildPipelineString('autovideosrc'), + source: 'autovideosrc', + ); + }, + ), + ], + if (!_isAndroid) + OutlinedButton.icon( + icon: const Icon(Icons.sports_soccer), + label: const Text('Test Pattern (Ball)'), + onPressed: () { + _setPipeline(_buildPipelineString('videotestsrc')); + }, + ), + if (!_isAndroid) + OutlinedButton.icon( + icon: const Icon(Icons.grid_on), + label: const Text('SMPTE Pattern'), + onPressed: () { + _setPipeline(_buildPipelineString('pattern-smpte')); + }, + ), + if (!_isAndroid) + OutlinedButton.icon( + icon: const Icon(Icons.grain), + label: const Text('Snow Pattern'), + onPressed: () { + _setPipeline(_buildPipelineString('pattern-snow')); + }, + ), + if (_isWindows) + OutlinedButton.icon( + icon: const Icon(Icons.videocam), + label: const Text('Windows Camera (ksvideosrc)'), + onPressed: () { + _setPipeline(_buildPipelineString('ksvideosrc')); + }, + ), + if (_isLinux) + OutlinedButton.icon( + icon: const Icon(Icons.videocam), + label: const Text('Webcam (/dev/video0)'), + onPressed: () { + _setPipeline(_buildPipelineString('v4l2src')); + }, + ), + if (_isMacOS) + OutlinedButton.icon( + icon: const Icon(Icons.videocam), + label: const Text('Mac Camera (avfvideosrc)'), + onPressed: () { + _setPipeline(_buildPipelineString('avfvideosrc')); + }, + ), ], ), - const Divider(), - const Text( - 'Static Colors (Legacy)', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), + if (!_isAndroid) ...[ + const Divider(), + const Text( + 'Static Colors (Legacy)', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), - // Original Color Buttons - Wrap( - spacing: 10, - runSpacing: 10, - alignment: WrapAlignment.center, - children: [ - OutlinedButton( - child: const Text('Flutter Navy'), - onPressed: () => setColor(0x04, 0x2b, 0x59), - ), - OutlinedButton( - child: const Text('Flutter Blue'), - onPressed: () => setColor(0x05, 0x53, 0xb1), - ), - OutlinedButton( - child: const Text('Flutter Sky'), - onPressed: () => setColor(0x02, 0x7d, 0xfd), - ), - OutlinedButton( - child: const Text('Red'), - onPressed: () => setColor(0xf2, 0x5d, 0x50), - ), - OutlinedButton( - child: const Text('Yellow'), - onPressed: () => setColor(0xff, 0xf2, 0x75), - ), - OutlinedButton( - child: const Text('Purple'), - onPressed: () => setColor(0x62, 0x00, 0xee), - ), - OutlinedButton( - child: const Text('Green'), - onPressed: () => setColor(0x1c, 0xda, 0xc5), - ), - ], - ), + // Color buttons (only relevant for videotestsrc) + Wrap( + spacing: 10, + runSpacing: 10, + alignment: WrapAlignment.center, + children: [ + OutlinedButton( + child: const Text('Flutter Navy'), + onPressed: () => setColor(0x04, 0x2b, 0x59), + ), + OutlinedButton( + child: const Text('Flutter Blue'), + onPressed: () => setColor(0x05, 0x53, 0xb1), + ), + OutlinedButton( + child: const Text('Flutter Sky'), + onPressed: () => setColor(0x02, 0x7d, 0xfd), + ), + OutlinedButton( + child: const Text('Red'), + onPressed: () => setColor(0xf2, 0x5d, 0x50), + ), + OutlinedButton( + child: const Text('Yellow'), + onPressed: () => setColor(0xff, 0xf2, 0x75), + ), + OutlinedButton( + child: const Text('Purple'), + onPressed: () => setColor(0x62, 0x00, 0xee), + ), + OutlinedButton( + child: const Text('Green'), + onPressed: () => setColor(0x1c, 0xda, 0xc5), + ), + ], + ), + ], ], ), ), ], ); } + + Widget _buildAndroidFallback(String? reason) { + final String message = + reason ?? _errorMessage ?? 'Native texture unavailable'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(8), + color: Colors.orange.withValues(alpha: 0.1), + child: Text( + 'Falling back to WebRTC preview on Android. Reason: $message', + style: const TextStyle(color: Colors.orange), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 12), + Container( + constraints: const BoxConstraints(maxWidth: 800, maxHeight: 600), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey, width: 2), + borderRadius: BorderRadius.circular(8), + ), + child: webrtc_import.WebRTCView( + signalingUrl: 'ws://127.0.0.1:8443', + producerIdToConsume: null, + ), + ), + ], + ); + } } -// @override -// Widget build(BuildContext context) { -// bool isMobileDevice = -// MediaQuery.of(context).size.width <= narrowScreenWidthThreshold; -// Locale currentLocale = Localizations.localeOf(context); -// // const int textureWidth = 300; -// // const int textureHeight = 300; -// // const MethodChannel channel = MethodChannel('kataglyphis_native_inference'); -// // final Future textureId = channel.invokeMethod('create', [ -// // textureWidth, -// // textureHeight, -// // ]); - -// // // Set the color of the texture. -// // Future setColor(int r, int g, int b) async { -// // await channel.invokeMethod('setColor', [r, g, b]); -// // } - -// return SinglePage( -// footer: widget.footer, -// appAttributes: widget.appAttributes, -// showMediumSizeLayout: widget.appAttributes.showMediumSizeLayout, -// showLargeSizeLayout: widget.appAttributes.showLargeSizeLayout, -// children: [ -// Center( -// child: Column( -// mainAxisAlignment: MainAxisAlignment.center, -// spacing: 10, -// children: [ -// FutureBuilder( -// future: textureId, -// builder: (BuildContext context, AsyncSnapshot snapshot) { -// if (snapshot.connectionState == ConnectionState.waiting) { -// return const Text('Creating texture...'); -// } -// if (snapshot.hasError) { -// return Text('Error creating texture: ${snapshot.error}'); -// } -// if (!snapshot.hasData || snapshot.data == null) { -// return const Text('Error creating texture (null id)'); -// } - -// return SizedBox( -// width: textureWidth.toDouble(), -// height: textureHeight.toDouble(), -// child: Texture(textureId: snapshot.data!), -// ); -// }, -// ), -// OutlinedButton( -// child: const Text('Flutter Navy'), -// onPressed: () => setColor(0x04, 0x2b, 0x59), -// ), -// OutlinedButton( -// child: const Text('Flutter Blue'), -// onPressed: () => setColor(0x05, 0x53, 0xb1), -// ), -// OutlinedButton( -// child: const Text('Flutter Sky'), -// onPressed: () => setColor(0x02, 0x7d, 0xfd), -// ), -// OutlinedButton( -// child: const Text('Red'), -// onPressed: () => setColor(0xf2, 0x5d, 0x50), -// ), -// OutlinedButton( -// child: const Text('Yellow'), -// onPressed: () => setColor(0xff, 0xf2, 0x75), -// ), -// OutlinedButton( -// child: const Text('Purple'), -// onPressed: () => setColor(0x62, 0x00, 0xee), -// ), -// OutlinedButton( -// child: const Text('Green'), -// onPressed: () => setColor(0x1c, 0xda, 0xc5), -// ), -// ], -// ), -// ), -// ], -// ); -// } -// } diff --git a/lib/Pages/StreamPage/webrtc_view.dart b/lib/Pages/StreamPage/webrtc_view.dart new file mode 100644 index 0000000..97f0dba --- /dev/null +++ b/lib/Pages/StreamPage/webrtc_view.dart @@ -0,0 +1,158 @@ +// lib/webrtc_view.dart +import 'dart:js_interop'; +import 'dart:ui_web' as ui_web; +import 'package:web/web.dart' as web; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:kataglyphis_inference_engine/js/gstwebrtc_api_interop.dart'; + +// Add this extension to access the JS property directly. +extension HTMLVideoElementSrcObject on web.HTMLVideoElement { + external JSAny? get srcObject; + external set srcObject(JSAny? value); +} + +class WebRTCView extends StatefulWidget { + final String signalingUrl; + final String? producerIdToConsume; + + const WebRTCView({ + super.key, + required this.signalingUrl, + this.producerIdToConsume, + }); + + @override + State createState() => _WebRTCViewState(); +} + +class _WebRTCViewState extends State { + late final web.HTMLVideoElement _video; + late final String _viewType; + late final GstWebRTCAPI _api; + ConsumerSession? _consumer; + + @override + void initState() { + super.initState(); + + _video = web.HTMLVideoElement() + ..autoplay = true + ..muted = + true // helps autoplay + ..controls = true + ..style.width = '100%' + ..style.height = '100%' + ..setAttribute('playsinline', 'true'); + + _viewType = 'webrtc-video-${DateTime.now().microsecondsSinceEpoch}'; + ui_web.platformViewRegistry.registerViewFactory(_viewType, (_) => _video); + + final cfg = GstWebRTCConfig(signalingServerUrl: widget.signalingUrl); + _api = GstWebRTCAPI(cfg); + + _api.registerConnectionListener( + ConnectionListener( + connected: ((JSAny clientId) { + debugPrint('Connected. ClientId: ${clientId.dartify()}'); + final wanted = widget.producerIdToConsume; + if (wanted != null && wanted.isNotEmpty) { + _startConsuming(wanted); + } + }).toJS, + disconnected: (() { + debugPrint('Disconnected'); + }).toJS, + ), + ); + + _api.registerPeerListener( + PeerListener( + producerAdded: ((JSAny peerAny) { + final peer = peerAny as Peer; + debugPrint('Producer added: ${peer.id}'); + if (_consumer == null && + (widget.producerIdToConsume == null || + widget.producerIdToConsume == peer.id)) { + _startConsuming(peer.id); + } + }).toJS, + producerRemoved: ((JSAny peerAny) { + final peer = peerAny as Peer; + debugPrint('Producer removed: ${peer.id}'); + }).toJS, + ), + ); + } + + @override + void dispose() { + _consumer?.close(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return HtmlElementView(viewType: _viewType); + } + + void _startConsuming(String producerId) { + if (_consumer != null) return; + + final consumer = _api.createConsumerSession(producerId); + if (consumer == null) { + debugPrint('createConsumerSession returned null (not connected yet?)'); + return; + } + _consumer = consumer; + + consumer.addEventListener( + 'streamsChanged'.toJS, + ((JSAny _) { + final streams = consumer.streams.toDart; // List + if (streams.isNotEmpty) { + final mediaStream = streams.first as web.MediaStream; // cast + _video.srcObject = mediaStream; // MediaProvider? OK + _video.play(); + } + }).toJS, + ); + + consumer.addEventListener( + 'remoteControllerChanged'.toJS, + ((JSAny _) { + final rcAny = consumer.remoteController; + if (rcAny != null) { + final rc = rcAny as RemoteController; + rc.attachVideoElement(_video as JSAny); + } + }).toJS, + ); + + consumer.addEventListener( + 'stateChanged'.toJS, + ((JSAny _) { + debugPrint('Consumer state: ${consumer.state}'); + }).toJS, + ); + consumer.addEventListener( + 'error'.toJS, + ((JSAny e) { + debugPrint('Consumer error event'); + }).toJS, + ); + + final ok = consumer.connect(); + if (!ok) debugPrint('consumer.connect() returned false'); + } +} + +extension on JSAny { + Object? dartify() => switch (this) { + JSString s => s.toDart, + JSNumber n => n.toDartInt, + JSBoolean b => b.toDart, + _ => toString(), + }; +} diff --git a/lib/Pages/StreamPage/webrtc_view_stub.dart b/lib/Pages/StreamPage/webrtc_view_stub.dart new file mode 100644 index 0000000..515e9f5 --- /dev/null +++ b/lib/Pages/StreamPage/webrtc_view_stub.dart @@ -0,0 +1,28 @@ +// lib/Pages/StreamPage/webrtc_view_stub.dart +import 'package:flutter/material.dart'; + +class WebRTCView extends StatelessWidget { + final String signalingUrl; + final String? producerIdToConsume; + + const WebRTCView({ + super.key, + required this.signalingUrl, + this.producerIdToConsume, + }); + + @override + Widget build(BuildContext context) { + // Simple native fallback / placeholder + return Container( + constraints: const BoxConstraints(minHeight: 200), + alignment: Alignment.center, + padding: const EdgeInsets.all(12), + child: const Text( + 'WebRTC view is only available in the web build.\n' + 'This is a native fallback placeholder.', + textAlign: TextAlign.center, + ), + ); + } +} diff --git a/lib/Pages/jotrockenmitlocken_screen_configurations.dart b/lib/Pages/jotrockenmitlocken_screen_configurations.dart index 00efb46..b57e787 100644 --- a/lib/Pages/jotrockenmitlocken_screen_configurations.dart +++ b/lib/Pages/jotrockenmitlocken_screen_configurations.dart @@ -1,9 +1,5 @@ import 'package:kataglyphis_inference_engine/Pages/AboutMePage/about_me_page_navbar_config.dart'; import 'package:kataglyphis_inference_engine/Pages/DataPage/BlockOverviewPage/block_overview_page_config.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/BooksPage/books_page_config.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/FilmsPage/films_page_config.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/GamesPage/games_page_config.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/data_pages_navbar_page_config.dart'; import 'package:kataglyphis_inference_engine/Pages/StreamPage/stream_page_navbar_config.dart'; import 'package:kataglyphis_inference_engine/Pages/ErrorPage/error_page_stateful_branch_info_provider.dart'; import 'package:kataglyphis_inference_engine/Pages/Footer/Pages/configs/contact_footer_config.dart'; @@ -13,7 +9,6 @@ import 'package:kataglyphis_inference_engine/Pages/Footer/Pages/configs/declarat import 'package:kataglyphis_inference_engine/Pages/Footer/Pages/configs/imprint_footer_config.dart'; import 'package:kataglyphis_inference_engine/Pages/Footer/Pages/configs/privacy_policy_config.dart'; import 'package:kataglyphis_inference_engine/Pages/LandingPage/landing_page_navbar_page_config.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/QuotesPage/quotations_page_config.dart'; import 'package:kataglyphis_inference_engine/Pages/blog_dependent_screen_configurations.dart'; import 'package:kataglyphis_inference_engine/blog_page_config.dart'; import 'package:jotrockenmitlockenrepo/Pages/Footer/footer_page_config.dart'; @@ -61,10 +56,9 @@ class JotrockenmitLockenScreenConfigurations extends ScreenConfigurations @override List getNavRailPagesConfig() { return [ + StreamPageNavBarConfig(), LandingPageNavBarConfig(), AboutMePageNavBarConfig(), - DataPageNavBarConfig(), - StreamPageNavBarConfig(), ]; } @@ -98,12 +92,6 @@ class JotrockenmitLockenScreenConfigurations extends ScreenConfigurations @override List getDataPagesConfig() { - return [ - QuotationsPageConfig(), - BooksPageConfig(), - FilmsPageConfig(), - GamesPageConfig(), - BlockOverviewPageConfig(), - ]; + return [BlockOverviewPageConfig()]; } } diff --git a/lib/Routing/jotrockenmitlocken_router.dart b/lib/Routing/jotrockenmitlocken_router.dart index 85ba1e8..e8edbf5 100644 --- a/lib/Routing/jotrockenmitlocken_router.dart +++ b/lib/Routing/jotrockenmitlocken_router.dart @@ -2,10 +2,6 @@ import 'package:flutter/material.dart'; import 'package:kataglyphis_inference_engine/Pages/AboutMePage/about_me_page.dart'; import 'package:kataglyphis_inference_engine/Pages/Blog/blog_page.dart'; import 'package:kataglyphis_inference_engine/Pages/DataPage/BlockOverviewPage/block_overview_page.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/BooksPage/books_page.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/FilmsPage/films_page.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/GamesPage/games_page.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/data_page.dart'; import 'package:kataglyphis_inference_engine/Pages/DataPage/media_critics_page.dart'; import 'package:kataglyphis_inference_engine/Pages/StreamPage/stream_page.dart'; import 'package:kataglyphis_inference_engine/Pages/ErrorPage/error_page.dart'; @@ -13,7 +9,6 @@ import 'package:kataglyphis_inference_engine/blog_dependent_app_attributes.dart' import 'package:jotrockenmitlockenrepo/Pages/Footer/footer_page.dart'; import 'package:kataglyphis_inference_engine/Pages/LandingPage/landing_page.dart'; -import 'package:kataglyphis_inference_engine/Pages/DataPage/QuotesPage/quotes_page.dart'; import 'package:jotrockenmitlockenrepo/Pages/Footer/footer.dart'; import 'package:jotrockenmitlockenrepo/Pages/Footer/footer_page_config.dart'; import 'package:kataglyphis_inference_engine/blog_page_config.dart'; @@ -66,21 +61,6 @@ class JotrockenMitLockenRoutes extends RoutesCreator { blogDependentAppAttributes.blogDependentScreenConfigurations .getDataPagesConfig(); List dataPages = [ - QuotesPage( - footer: getFooter(appAttributes), - appAttributes: appAttributes, - ), - BooksPage( - footer: getFooter(appAttributes), - appAttributes: appAttributes, - blogDependentAppAttributes: blogDependentAppAttributes, - ), - FilmsPage(footer: getFooter(appAttributes), appAttributes: appAttributes), - GamesPage( - footer: getFooter(appAttributes), - appAttributes: appAttributes, - blogDependentAppAttributes: blogDependentAppAttributes, - ), BlockOverviewPage( footer: getFooter(appAttributes), appAttributes: appAttributes, @@ -101,17 +81,16 @@ class JotrockenMitLockenRoutes extends RoutesCreator { .screenConfigurations .getNavRailPagesConfig(); List navBarPages = [ - LandingPage( + StreamPage( footer: getFooter(appAttributes), appAttributes: appAttributes, - blogDependentAppAttributes: blogDependentAppAttributes, ), - AboutMePage( + LandingPage( footer: getFooter(appAttributes), appAttributes: appAttributes, + blogDependentAppAttributes: blogDependentAppAttributes, ), - DataPage(footer: getFooter(appAttributes), appAttributes: appAttributes), - StreamPage( + AboutMePage( footer: getFooter(appAttributes), appAttributes: appAttributes, ), diff --git a/lib/Widgets/Navigation/old/Navbar/mobile/drawer_item.dart b/lib/Widgets/Navigation/old/Navbar/mobile/drawer_item.dart index df973aa..c525c80 100644 --- a/lib/Widgets/Navigation/old/Navbar/mobile/drawer_item.dart +++ b/lib/Widgets/Navigation/old/Navbar/mobile/drawer_item.dart @@ -4,8 +4,5 @@ class DrawerItem { final String title; final IconData icon; - const DrawerItem({ - required this.title, - required this.icon, - }); + const DrawerItem({required this.title, required this.icon}); } diff --git a/lib/Widgets/Navigation/old/Navbar/mobile/navigation_drawer_widget.dart b/lib/Widgets/Navigation/old/Navbar/mobile/navigation_drawer_widget.dart index f971ce9..7479a65 100644 --- a/lib/Widgets/Navigation/old/Navbar/mobile/navigation_drawer_widget.dart +++ b/lib/Widgets/Navigation/old/Navbar/mobile/navigation_drawer_widget.dart @@ -66,7 +66,7 @@ class _NavigationDrawerWidgetState extends State { } } - buildMenuItem({ + Material buildMenuItem({ required bool isCollapsed, required String text, required IconData icon, @@ -90,7 +90,10 @@ class _NavigationDrawerWidgetState extends State { ); } - buildList({required BuildContext context, required bool isCollapsed}) { + ListView buildList({ + required BuildContext context, + required bool isCollapsed, + }) { List items = [ DrawerItem( title: AppLocalizations.of(context)!.homepage, diff --git a/lib/blog_dependent_app_attributes.dart b/lib/blog_dependent_app_attributes.dart index c9ffff9..b60fd63 100644 --- a/lib/blog_dependent_app_attributes.dart +++ b/lib/blog_dependent_app_attributes.dart @@ -8,8 +8,9 @@ class BlogDependentAppAttributes { BlogDependentScreenConfigurations blogDependentScreenConfigurations; - BlogDependentAppAttributes( - {required this.blogDependentScreenConfigurations, - required this.twoCentsConfigs, - required this.blockSettings}); + BlogDependentAppAttributes({ + required this.blogDependentScreenConfigurations, + required this.twoCentsConfigs, + required this.blockSettings, + }); } diff --git a/lib/blog_page_config.dart b/lib/blog_page_config.dart index ecfeb7b..959294f 100644 --- a/lib/blog_page_config.dart +++ b/lib/blog_page_config.dart @@ -4,21 +4,21 @@ enum LandingPageAlignment { left, right } class BlogPageConfig extends StatefulBranchInfoProvider { BlogPageConfig.fromJsonFile(Map jsonFile) - : routingName = jsonFile["routingName"] as String, - shortDescriptionEN = jsonFile["shortDescriptionEN"] as String, - shortDescriptionDE = jsonFile["shortDescriptionDE"] as String, - filePath = jsonFile["filePath"] as String, - imageDir = jsonFile["imageDir"] as String, - githubRepo = jsonFile["githubRepo"] as String, - landingPageAlignment = jsonFile["landingPageAlignment"] as String, - landingPageEntryImagePath = - jsonFile["landingPageEntryImagePath"] as String, - landingPageEntryImageCaptioning = - jsonFile["landingPageEntryImageCaptioning"] as String, - lastModified = jsonFile["lastModified"] as String, - fileTitle = jsonFile["fileTitle"] as String, - fileAdditionalInfo = jsonFile["fileAdditionalInfo"] as String, - fileBaseDir = jsonFile["fileBaseDir"] as String { + : routingName = jsonFile["routingName"] as String, + shortDescriptionEN = jsonFile["shortDescriptionEN"] as String, + shortDescriptionDE = jsonFile["shortDescriptionDE"] as String, + filePath = jsonFile["filePath"] as String, + imageDir = jsonFile["imageDir"] as String, + githubRepo = jsonFile["githubRepo"] as String, + landingPageAlignment = jsonFile["landingPageAlignment"] as String, + landingPageEntryImagePath = + jsonFile["landingPageEntryImagePath"] as String, + landingPageEntryImageCaptioning = + jsonFile["landingPageEntryImageCaptioning"] as String, + lastModified = jsonFile["lastModified"] as String, + fileTitle = jsonFile["fileTitle"] as String, + fileAdditionalInfo = jsonFile["fileAdditionalInfo"] as String, + fileBaseDir = jsonFile["fileBaseDir"] as String { var docsDescJsonFile = jsonFile["docsDesc"] as List; for (var element in docsDescJsonFile) { docsDesc.add({ diff --git a/lib/js/gstwebrtc_api_interop.dart b/lib/js/gstwebrtc_api_interop.dart new file mode 100644 index 0000000..76ce104 --- /dev/null +++ b/lib/js/gstwebrtc_api_interop.dart @@ -0,0 +1,98 @@ +// lib/js/gstwebrtc_api_interop.dart +@JS() +library gstwebrtc_api_interop; + +import 'dart:js_interop'; + +@JS('GstWebRTCAPI') +@staticInterop +class GstWebRTCAPI { + external factory GstWebRTCAPI([GstWebRTCConfig? userConfig]); +} + +extension GstWebRTCAPIMembers on GstWebRTCAPI { + external bool registerConnectionListener(ConnectionListener listener); + external bool registerPeerListener(PeerListener listener); + + external ConsumerSession? createConsumerSession(String producerId); + external ConsumerSession? createConsumerSessionWithOfferOptions( + String producerId, + JSAny offerOptions, + ); + + external JSArray getAvailableProducers(); + external JSArray getAvailableConsumers(); + + external void unregisterAllConnectionListeners(); + external void unregisterAllPeerListeners(); +} + +@JS() +@staticInterop +@anonymous +class GstWebRTCConfig { + external factory GstWebRTCConfig({ + JSAny? meta, + String? signalingServerUrl, + int? reconnectionTimeout, + JSAny? webrtcConfig, + }); +} + +@JS() +@staticInterop +@anonymous +class ConnectionListener { + external factory ConnectionListener({ + JSFunction? connected, + JSFunction? disconnected, + }); +} + +@JS() +@staticInterop +@anonymous +class PeerListener { + external factory PeerListener({ + JSFunction? producerAdded, + JSFunction? producerRemoved, + JSFunction? consumerAdded, + JSFunction? consumerRemoved, + }); +} + +@JS() +@staticInterop +@anonymous +class Peer { + external factory Peer({String id, JSAny meta}); +} + +extension PeerMembers on Peer { + external String get id; + external JSAny get meta; +} + +@JS() +@staticInterop +class ConsumerSession {} + +extension ConsumerSessionMembers on ConsumerSession { + external bool connect(); + external void close(); + external void addEventListener(JSString type, JSFunction callback); + external JSArray get streams; + external JSObject? get remoteController; + external set mungeStereoHack(bool enable); + external int get state; +} + +@JS() +@staticInterop +class RemoteController {} + +extension RemoteControllerMembers on RemoteController { + external void addEventListener(JSString type, JSFunction callback); + external void attachVideoElement(JSAny? element); + external void close(); +} diff --git a/lib/main.dart b/lib/main.dart index c586513..fa82773 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -280,20 +280,3 @@ class _AppState extends State with SingleTickerProviderStateMixin { ); } } - -// class MyApp extends StatelessWidget { -// const MyApp({super.key}); - -// @override -// Widget build(BuildContext context) { -// return MaterialApp( -// home: Scaffold( -// appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')), -// body: Center( -// child: Text( -// 'Action: Call Rust `greet("Tom")`\nResult: `${greet(name: "Tom")}`'), -// ), -// ), -// ); -// } -// } diff --git a/lib/src/rust/api/simple.dart b/lib/src/rust/api/simple.dart index b632d8b..9218afd 100644 --- a/lib/src/rust/api/simple.dart +++ b/lib/src/rust/api/simple.dart @@ -8,3 +8,9 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; String greet({required String name}) => RustLib.instance.api.crateApiSimpleGreet(name: name); + +int heavyComputation({required int input}) => + RustLib.instance.api.crateApiSimpleHeavyComputation(input: input); + +Future asyncHeavyWork({required int input}) => + RustLib.instance.api.crateApiSimpleAsyncHeavyWork(input: input); diff --git a/lib/src/rust/frb_generated.dart b/lib/src/rust/frb_generated.dart index 3629ac0..4c04eae 100644 --- a/lib/src/rust/frb_generated.dart +++ b/lib/src/rust/frb_generated.dart @@ -66,19 +66,24 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.11.1'; @override - int get rustContentHash => -1918914929; + int get rustContentHash => 1844710464; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( - stem: 'rust_lib_kataglyphis_inference_engine', - ioDirectory: 'rust/target/release/', + stem: 'kataglyphis_rustprojecttemplate', + ioDirectory: + 'ExternalLib/Kataglyphis-RustProjectTemplate/target/debug/', webPrefix: 'pkg/', ); } abstract class RustLibApi extends BaseApi { + Future crateApiSimpleAsyncHeavyWork({required int input}); + String crateApiSimpleGreet({required String name}); + int crateApiSimpleHeavyComputation({required int input}); + Future crateApiSimpleInitApp(); } @@ -90,6 +95,34 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { required super.portManager, }); + @override + Future crateApiSimpleAsyncHeavyWork({required int input}) { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_i_32(input, serializer); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 1, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_i_32, + decodeErrorData: null, + ), + constMeta: kCrateApiSimpleAsyncHeavyWorkConstMeta, + argValues: [input], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiSimpleAsyncHeavyWorkConstMeta => + const TaskConstMeta(debugName: "async_heavy_work", argNames: ["input"]); + @override String crateApiSimpleGreet({required String name}) { return handler.executeSync( @@ -97,7 +130,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(name, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -113,6 +146,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta(debugName: "greet", argNames: ["name"]); + @override + int crateApiSimpleHeavyComputation({required int input}) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_i_32(input, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_i_32, + decodeErrorData: null, + ), + constMeta: kCrateApiSimpleHeavyComputationConstMeta, + argValues: [input], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiSimpleHeavyComputationConstMeta => + const TaskConstMeta(debugName: "heavy_computation", argNames: ["input"]); + @override Future crateApiSimpleInitApp() { return handler.executeNormal( @@ -122,7 +178,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { pdeCallFfi( generalizedFrbRustBinding, serializer, - funcId: 2, + funcId: 4, port: port_, ); }, @@ -146,6 +202,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + int dco_decode_i_32(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw as int; + } + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -171,6 +233,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + int sse_decode_i_32(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return deserializer.buffer.getInt32(); + } + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -189,12 +257,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } - @protected - int sse_decode_i_32(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getInt32(); - } - @protected bool sse_decode_bool(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -207,6 +269,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_i_32(int self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + serializer.buffer.putInt32(self); + } + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, @@ -228,12 +296,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } - @protected - void sse_encode_i_32(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putInt32(self); - } - @protected void sse_encode_bool(bool self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/lib/src/rust/frb_generated.io.dart b/lib/src/rust/frb_generated.io.dart index d797351..ed1dd5c 100644 --- a/lib/src/rust/frb_generated.io.dart +++ b/lib/src/rust/frb_generated.io.dart @@ -21,6 +21,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + int dco_decode_i_32(dynamic raw); + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); @@ -33,6 +36,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String sse_decode_String(SseDeserializer deserializer); + @protected + int sse_decode_i_32(SseDeserializer deserializer); + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); @@ -42,15 +48,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); - @protected - int sse_decode_i_32(SseDeserializer deserializer); - @protected bool sse_decode_bool(SseDeserializer deserializer); @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, @@ -63,9 +69,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); - @protected - void sse_encode_i_32(int self, SseSerializer serializer); - @protected void sse_encode_bool(bool self, SseSerializer serializer); } diff --git a/lib/src/rust/frb_generated.web.dart b/lib/src/rust/frb_generated.web.dart index 431eaa9..43db348 100644 --- a/lib/src/rust/frb_generated.web.dart +++ b/lib/src/rust/frb_generated.web.dart @@ -23,6 +23,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String dco_decode_String(dynamic raw); + @protected + int dco_decode_i_32(dynamic raw); + @protected Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); @@ -35,6 +38,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected String sse_decode_String(SseDeserializer deserializer); + @protected + int sse_decode_i_32(SseDeserializer deserializer); + @protected Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); @@ -44,15 +50,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); - @protected - int sse_decode_i_32(SseDeserializer deserializer); - @protected bool sse_decode_bool(SseDeserializer deserializer); @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_i_32(int self, SseSerializer serializer); + @protected void sse_encode_list_prim_u_8_strict( Uint8List self, @@ -65,9 +71,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); - @protected - void sse_encode_i_32(int self, SseSerializer serializer); - @protected void sse_encode_bool(bool self, SseSerializer serializer); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index c13dc20..3640201 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -8,7 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - rust_lib_kataglyphis_inference_engine + kataglyphis_rustprojecttemplate ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/.gitignore b/macos/.gitignore deleted file mode 100644 index d4e0569..0000000 --- a/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index f022c34..0000000 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index f022c34..0000000 --- a/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig new file mode 100644 index 0000000..009ab3a --- /dev/null +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -0,0 +1,11 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=C:\flutter +FLUTTER_APPLICATION_PATH=C:\GitHub\Kataglyphis-Inference-Engine +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=1.0.0 +FLUTTER_BUILD_NUMBER=1 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh new file mode 100755 index 0000000..519e074 --- /dev/null +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=C:\flutter" +export "FLUTTER_APPLICATION_PATH=C:\GitHub\Kataglyphis-Inference-Engine" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=1.0.0" +export "FLUTTER_BUILD_NUMBER=1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 3280c1e..0000000 --- a/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,705 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC10EC2044A3C60003C045; - remoteInfo = Runner; - }; - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* kataglyphis_inference_engine.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "kataglyphis_inference_engine.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 331C80D2294CF70F00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C80D6294CF71000263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C80D7294CF71000263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 331C80D6294CF71000263BE5 /* RunnerTests */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* kataglyphis_inference_engine.app */, - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C80D4294CF70F00263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C80D1294CF70F00263BE5 /* Sources */, - 331C80D2294CF70F00263BE5 /* Frameworks */, - 331C80D3294CF70F00263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C80DA294CF71000263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* kataglyphis_inference_engine.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C80D4294CF70F00263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 33CC10EC2044A3C60003C045; - }; - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 331C80D4294CF70F00263BE5 /* RunnerTests */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C80D3294CF70F00263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C80D1294CF70F00263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC10EC2044A3C60003C045 /* Runner */; - targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; - }; - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 331C80DB294CF71000263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/kataglyphis_inference_engine.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/kataglyphis_inference_engine"; - }; - name = Debug; - }; - 331C80DC294CF71000263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/kataglyphis_inference_engine.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/kataglyphis_inference_engine"; - }; - name = Release; - }; - 331C80DD294CF71000263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/kataglyphis_inference_engine.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/kataglyphis_inference_engine"; - }; - name = Profile; - }; - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C80DB294CF71000263BE5 /* Debug */, - 331C80DC294CF71000263BE5 /* Release */, - 331C80DD294CF71000263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index fc6bf80..0000000 --- a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a31f811..0000000 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 59c6d39..0000000 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index fc6bf80..0000000 --- a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift deleted file mode 100644 index 1a8d37d..0000000 --- a/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@main -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 8d4e7cb..0000000 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eb..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bdb5722..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index f083318..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index 326c0e7..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632c..0000000 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 4632c69..0000000 --- a/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 3e66af4..0000000 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = kataglyphis_inference_engine - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.kataglyphisInferenceEngine - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index b398823..0000000 --- a/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index d93e5dc..0000000 --- a/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index fb4d7d3..0000000 --- a/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index 51d0967..0000000 --- a/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist deleted file mode 100644 index 3733c1a..0000000 --- a/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index ab30cba..0000000 --- a/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements deleted file mode 100644 index 04336df..0000000 --- a/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index 21fe1ab..0000000 --- a/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Cocoa -import FlutterMacOS -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/pubspec.lock b/pubspec.lock index aad0f78..ac58db1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" + sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" url: "https://pub.dev" source: hosted - version: "92.0.0" + version: "93.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" + sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "10.0.1" archive: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: build - sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 + sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "4.0.4" build_cli_annotations: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: built_value - sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" + sha256: "7931c90b84bc573fef103548e354258ae4c9d28d140e41961df6843c5d60d4d8" url: "https://pub.dev" source: hosted - version: "8.12.1" + version: "8.12.3" characters: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" url: "https://pub.dev" source: hosted - version: "4.11.0" + version: "4.11.1" collection: dependency: transitive description: @@ -165,18 +165,18 @@ packages: dependency: transitive description: name: dart_style - sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + sha256: "15a7db352c8fc6a4d2bc475ba901c25b39fe7157541da4c16eacce6f8be83e49" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.5" equatable: dependency: transitive description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.8" fake_async: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" file: dependency: transitive description: @@ -264,6 +264,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_markdown_plus: + dependency: transitive + description: + name: flutter_markdown_plus + sha256: "039177906850278e8fb1cd364115ee0a46281135932fa8ecea8455522166d2de" + url: "https://pub.dev" + source: hosted + version: "1.0.7" flutter_rust_bridge: dependency: "direct main" description: @@ -307,10 +315,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: c92d18e1fe994cb06d48aa786c46b142a5633067e8297cff6b5a3ac742620104 + sha256: eff94d2a6fc79fa8b811dde79c7549808c2346037ee107a1121b4a644c745f2a url: "https://pub.dev" source: hosted - version: "17.0.0" + version: "17.0.1" highlight: dependency: transitive description: @@ -331,10 +339,10 @@ packages: dependency: transitive description: name: image - sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" url: "https://pub.dev" source: hosted - version: "4.5.4" + version: "4.7.2" integration_test: dependency: "direct dev" description: flutter @@ -359,10 +367,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + sha256: "805fa86df56383000f640384b282ce0cb8431f1a7a2396de92fb66186d8c57df" url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.10.0" kataglyphis_native_inference: dependency: "direct main" description: @@ -370,6 +378,13 @@ packages: relative: true source: path version: "0.0.1" + kataglyphis_rustprojecttemplate: + dependency: "direct main" + description: + path: rust_builder + relative: true + source: path + version: "0.0.1" leak_tracker: dependency: transitive description: @@ -462,10 +477,18 @@ packages: dependency: "direct dev" description: name: mockito - sha256: dac24d461418d363778d53198d9ac0510b9d073869f078450f195766ec48d05e + sha256: a45d1aa065b796922db7b9e7e7e45f921aed17adf3a8318a1f47097e7e695566 url: "https://pub.dev" source: hosted - version: "5.6.1" + version: "5.6.3" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -482,6 +505,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + url: "https://pub.dev" + source: hosted + version: "12.0.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: @@ -522,6 +593,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.5" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -530,13 +609,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - rust_lib_kataglyphis_inference_engine: - dependency: "direct main" - description: - path: rust_builder - relative: true - source: path - version: "0.0.1" scroll_to_index: dependency: transitive description: @@ -554,10 +626,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" + sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17" url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.2.0" source_span: dependency: transitive description: @@ -674,10 +746,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" url_launcher_windows: dependency: transitive description: @@ -714,10 +786,10 @@ packages: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.1" web: dependency: transitive description: @@ -751,5 +823,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.1 <4.0.0" - flutter: ">=3.38.3" + dart: ">=3.10.8 <4.0.0" + flutter: ">=3.38.9" diff --git a/pubspec.yaml b/pubspec.yaml index 602abd2..a0c56a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,8 +7,8 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.10.1 <4.0.0" - flutter: ">=3.38.3" + sdk: ">=3.10.8 <4.0.0" + flutter: ">=3.38.9" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -26,17 +26,19 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - rust_lib_kataglyphis_inference_engine: - path: rust_builder flutter_rust_bridge: ^2.11.1 - go_router: ^17.0.0 + go_router: ^17.0.1 font_awesome_flutter: ^10.12.0 flutter_localizations: sdk: flutter intl: ^0.20.2 + kataglyphis_rustprojecttemplate: + path: rust_builder + permission_handler: ^12.0.1 + provider: any dev_dependencies: - mockito: ^5.5.1 + mockito: ^5.6.3 flutter_test: sdk: flutter integration_test: @@ -65,98 +67,87 @@ flutter: - assets/documents/ - assets/documents/footer/ - assets/documents/blog/ - - assets/documents/books/ - - assets/documents/games/ - - assets/documents/thesis/ - - assets/documents/cv/ - assets/data/ - assets/settings/ - assets/settings/user_settings/ + - assets/fonts/ fonts: - family: Montserrat fonts: - - asset: static/Montserrat-Thin.ttf - - asset: static/Montserrat-ExtraLight.ttf - - asset: static/Montserrat-Light.ttf - - asset: static/Montserrat-Regular.ttf - - asset: static/Montserrat-Medium.ttf - - asset: static/Montserrat-SemiBold.ttf - - asset: static/Montserrat-Bold.ttf - - asset: static/Montserrat-ExtraBold.ttf - - asset: static/Montserrat-Black.ttf - - asset: static/Montserrat-ThinItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-Thin.ttf + - asset: assets/fonts/Montserrat/Montserrat-ExtraLight.ttf + - asset: assets/fonts/Montserrat/Montserrat-Light.ttf + - asset: assets/fonts/Montserrat/Montserrat-Regular.ttf + - asset: assets/fonts/Montserrat/Montserrat-Medium.ttf + - asset: assets/fonts/Montserrat/Montserrat-SemiBold.ttf + - asset: assets/fonts/Montserrat/Montserrat-Bold.ttf + - asset: assets/fonts/Montserrat/Montserrat-ExtraBold.ttf + - asset: assets/fonts/Montserrat/Montserrat-Black.ttf + - asset: assets/fonts/Montserrat/Montserrat-ThinItalic.ttf style: italic - - asset: static/Montserrat-ExtraLightItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf style: italic - - asset: static/Montserrat-LightItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-LightItalic.ttf style: italic - - asset: static/Montserrat-Italic.ttf + - asset: assets/fonts/Montserrat/Montserrat-Italic.ttf style: italic - - asset: static/Montserrat-MediumItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-MediumItalic.ttf style: italic - - asset: static/Montserrat-SemiBoldItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf style: italic - - asset: static/Montserrat-BoldItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-BoldItalic.ttf style: italic - - asset: static/Montserrat-ExtraBoldItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf style: italic - - asset: static/Montserrat-BlackItalic.ttf + - asset: assets/fonts/Montserrat/Montserrat-BlackItalic.ttf style: italic - family: Roboto fonts: - - asset: Roboto-Black.ttf - - asset: Roboto-BlackItalic.ttf + - asset: assets/fonts/Roboto/Roboto-Black.ttf + - asset: assets/fonts/Roboto/Roboto-BlackItalic.ttf style: italic - - asset: Roboto-Bold.ttf + - asset: assets/fonts/Roboto/Roboto-Bold.ttf weight: 700 - - asset: Roboto-BoldItalic.ttf + - asset: assets/fonts/Roboto/Roboto-BoldItalic.ttf style: italic weight: 700 - - asset: Roboto-Italic.ttf + - asset: assets/fonts/Roboto/Roboto-Italic.ttf style: italic - - asset: Roboto-Light.ttf - - asset: Roboto-LightItalic.ttf + - asset: assets/fonts/Roboto/Roboto-Light.ttf + - asset: assets/fonts/Roboto/Roboto-LightItalic.ttf style: italic - - asset: Roboto-Medium.ttf + - asset: assets/fonts/Roboto/Roboto-Medium.ttf weight: 400 - - asset: Roboto-MediumItalic.ttf + - asset: assets/fonts/Roboto/Roboto-MediumItalic.ttf weight: 400 style: italic - - asset: Roboto-Regular.ttf - - asset: Roboto-Thin.ttf + - asset: assets/fonts/Roboto/Roboto-Regular.ttf + - asset: assets/fonts/Roboto/Roboto-Thin.ttf weight: 200 - - asset: Roboto-ThinItalic.ttf + - asset: assets/fonts/Roboto/Roboto-ThinItalic.ttf weight: 200 style: italic + - family: Noto Sans + fonts: + - asset: assets/fonts/Noto_Sans/static/NotoSans-Regular.ttf + - asset: assets/fonts/Noto_Sans/static/NotoSans-Bold.ttf + weight: 700 + - asset: assets/fonts/Noto_Sans/static/NotoSans-Italic.ttf + style: italic + - asset: assets/fonts/Noto_Sans/static/NotoSans-BoldItalic.ttf + style: italic + weight: 700 - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package +flutter_launcher_icons: + android: true + image_path: assets/icons/kataglyphis_app_icon.png + web: + generate: true + image_path: assets/icons/kataglyphis_app_icon.png + background_color: "#0B1220" + theme_color: "#0B1220" + windows: + generate: true + image_path: assets/icons/kataglyphis_app_icon.png + icon_size: 256 \ No newline at end of file diff --git a/rust/.gitignore b/rust/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/rust/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/rust/Cargo.lock b/rust/Cargo.lock deleted file mode 100644 index adcb5cc..0000000 --- a/rust/Cargo.lock +++ /dev/null @@ -1,730 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449e356a4864c017286dbbec0e12767ea07efba29e3b7d984194c2a7ff3c4550" -dependencies = [ - "anyhow", - "atomic", - "backtrace", -] - -[[package]] -name = "android_log-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" - -[[package]] -name = "android_logger" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" -dependencies = [ - "android_log-sys", - "env_filter", - "log", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "build-target" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cc" -version = "1.2.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "dart-sys" -version = "4.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57967e4b200d767d091b961d6ab42cc7d0cc14fe9e052e75d0d3cf9eb732d895" -dependencies = [ - "cc", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "delegate-attr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51aac4c99b2e6775164b412ea33ae8441b2fde2dbf05a20bc0052a63d08c475b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "env_filter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" - -[[package]] -name = "flutter_rust_bridge" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde126295b2acc5f0a712e265e91b6fdc0ed38767496483e592ae7134db83725" -dependencies = [ - "allo-isolate", - "android_logger", - "anyhow", - "build-target", - "bytemuck", - "byteorder", - "console_error_panic_hook", - "dart-sys", - "delegate-attr", - "flutter_rust_bridge_macros", - "futures", - "js-sys", - "lazy_static", - "log", - "oslog", - "portable-atomic", - "threadpool", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "flutter_rust_bridge_macros" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f0420326b13675321b194928bb7830043b68cf8b810e1c651285c747abb080" -dependencies = [ - "hex", - "md-5", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "oslog" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" -dependencies = [ - "cc", - "dashmap", - "log", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "rust_lib_kataglyphis_inference_engine" -version = "0.1.0" -dependencies = [ - "flutter_rust_bridge", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "syn" -version = "2.0.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "pin-project-lite", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasm-bindgen" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/rust/Cargo.toml b/rust/Cargo.toml deleted file mode 100644 index 4b2f4d5..0000000 --- a/rust/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "rust_lib_kataglyphis_inference_engine" -version = "0.1.0" -edition = "2024" - -[lib] -crate-type = ["cdylib", "staticlib"] - -[dependencies] -flutter_rust_bridge = "=2.11.1" diff --git a/rust/src/api/mod.rs b/rust/src/api/mod.rs deleted file mode 100644 index b252f36..0000000 --- a/rust/src/api/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod simple; diff --git a/rust/src/api/simple.rs b/rust/src/api/simple.rs deleted file mode 100644 index 4360c82..0000000 --- a/rust/src/api/simple.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo -pub fn greet(name: String) -> String { - format!("Hello, {name}!") -} - -#[flutter_rust_bridge::frb(init)] -pub fn init_app() { - // Default utilities - feel free to customize - flutter_rust_bridge::setup_default_user_utils(); -} diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs deleted file mode 100644 index 4f41c47..0000000 --- a/rust/src/frb_generated.rs +++ /dev/null @@ -1,276 +0,0 @@ -// This file is automatically generated, so please do not edit it. -// @generated by `flutter_rust_bridge`@ 2.11.1. - -#![allow( - non_camel_case_types, - unused, - non_snake_case, - clippy::needless_return, - clippy::redundant_closure_call, - clippy::redundant_closure, - clippy::useless_conversion, - clippy::unit_arg, - clippy::unused_unit, - clippy::double_parens, - clippy::let_and_return, - clippy::too_many_arguments, - clippy::match_single_binding, - clippy::clone_on_copy, - clippy::let_unit_value, - clippy::deref_addrof, - clippy::explicit_auto_deref, - clippy::borrow_deref_ref, - clippy::needless_borrow -)] - -// Section: imports - -use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; -use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; -use flutter_rust_bridge::{Handler, IntoIntoDart}; - -// Section: boilerplate - -flutter_rust_bridge::frb_generated_boilerplate!( - default_stream_sink_codec = SseCodec, - default_rust_opaque = RustOpaqueMoi, - default_rust_auto_opaque = RustAutoOpaqueMoi, -); -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1918914929; - -// Section: executor - -flutter_rust_bridge::frb_generated_default_handler!(); - -// Section: wire_funcs - -fn wire__crate__api__simple__greet_impl( - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "greet", - port: None, - mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_name = ::sse_decode(&mut deserializer); - deserializer.end(); - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?; - Ok(output_ok) - })()) - }, - ) -} -fn wire__crate__api__simple__init_app_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "init_app", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - deserializer.end(); - move |context| { - transform_result_sse::<_, ()>((move || { - let output_ok = Result::<_, ()>::Ok({ - crate::api::simple::init_app(); - })?; - Ok(output_ok) - })()) - } - }, - ) -} - -// Section: dart2rust - -impl SseDecode for String { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = >::sse_decode(deserializer); - return String::from_utf8(inner).unwrap(); - } -} - -impl SseDecode for Vec { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut len_ = ::sse_decode(deserializer); - let mut ans_ = vec![]; - for idx_ in 0..len_ { - ans_.push(::sse_decode(deserializer)); - } - return ans_; - } -} - -impl SseDecode for u8 { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_u8().unwrap() - } -} - -impl SseDecode for () { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} -} - -impl SseDecode for i32 { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_i32::().unwrap() - } -} - -impl SseDecode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_u8().unwrap() != 0 - } -} - -fn pde_ffi_dispatcher_primary_impl( - func_id: i32, - port: flutter_rust_bridge::for_generated::MessagePort, - ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len: i32, - data_len: i32, -) { - // Codec=Pde (Serialization + dispatch), see doc to use other codecs - match func_id { - 2 => wire__crate__api__simple__init_app_impl(port, ptr, rust_vec_len, data_len), - _ => unreachable!(), - } -} - -fn pde_ffi_dispatcher_sync_impl( - func_id: i32, - ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len: i32, - data_len: i32, -) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { - // Codec=Pde (Serialization + dispatch), see doc to use other codecs - match func_id { - 1 => wire__crate__api__simple__greet_impl(ptr, rust_vec_len, data_len), - _ => unreachable!(), - } -} - -// Section: rust2dart - -impl SseEncode for String { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >::sse_encode(self.into_bytes(), serializer); - } -} - -impl SseEncode for Vec { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - ::sse_encode(self.len() as _, serializer); - for item in self { - ::sse_encode(item, serializer); - } - } -} - -impl SseEncode for u8 { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer.cursor.write_u8(self).unwrap(); - } -} - -impl SseEncode for () { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} -} - -impl SseEncode for i32 { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer.cursor.write_i32::(self).unwrap(); - } -} - -impl SseEncode for bool { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer.cursor.write_u8(self as _).unwrap(); - } -} - -#[cfg(not(target_family = "wasm"))] -mod io { - // This file is automatically generated, so please do not edit it. - // @generated by `flutter_rust_bridge`@ 2.11.1. - - // Section: imports - - use super::*; - use flutter_rust_bridge::for_generated::byteorder::{ - NativeEndian, ReadBytesExt, WriteBytesExt, - }; - use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; - use flutter_rust_bridge::{Handler, IntoIntoDart}; - - // Section: boilerplate - - flutter_rust_bridge::frb_generated_boilerplate_io!(); -} -#[cfg(not(target_family = "wasm"))] -pub use io::*; - -/// cbindgen:ignore -#[cfg(target_family = "wasm")] -mod web { - // This file is automatically generated, so please do not edit it. - // @generated by `flutter_rust_bridge`@ 2.11.1. - - // Section: imports - - use super::*; - use flutter_rust_bridge::for_generated::byteorder::{ - NativeEndian, ReadBytesExt, WriteBytesExt, - }; - use flutter_rust_bridge::for_generated::wasm_bindgen; - use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*; - use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable}; - use flutter_rust_bridge::{Handler, IntoIntoDart}; - - // Section: boilerplate - - flutter_rust_bridge::frb_generated_boilerplate_web!(); -} -#[cfg(target_family = "wasm")] -pub use web::*; diff --git a/rust/src/lib.rs b/rust/src/lib.rs deleted file mode 100644 index cbb071f..0000000 --- a/rust/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod api; -mod frb_generated; diff --git a/rust_builder/android/build.gradle b/rust_builder/android/build.gradle index 802493f..68d374d 100644 --- a/rust_builder/android/build.gradle +++ b/rust_builder/android/build.gradle @@ -11,7 +11,8 @@ buildscript { dependencies { // The Android Gradle Plugin knows how to build native code with the NDK. - classpath 'com.android.tools.build:gradle:7.3.0' + // Keep this aligned with the root Android project to avoid Gradle/AGP/JDK mismatches. + classpath 'com.android.tools.build:gradle:8.11.1' } } @@ -31,7 +32,7 @@ android { // Bumping the plugin compileSdkVersion requires all clients of this plugin // to bump the version in their app. - compileSdkVersion 33 + compileSdkVersion 36 // Use the NDK version // declared in /android/app/build.gradle file of the Flutter project. @@ -40,17 +41,19 @@ android { ndkVersion android.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + // Flutter's Android toolchain and most plugins target Java 17. + // Using Java 21 here can trigger Gradle toolchain resolution issues depending on the installed JDK. + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 } defaultConfig { - minSdkVersion 19 + minSdkVersion 21 } } apply from: "../cargokit/gradle/plugin.gradle" cargokit { - manifestDir = "../../rust" - libname = "rust_lib_kataglyphis_inference_engine" + manifestDir = "../../ExternalLib/Kataglyphis-RustProjectTemplate" + libname = "kataglyphis_rustprojecttemplate" } diff --git a/rust_builder/android/gradle.properties b/rust_builder/android/gradle.properties new file mode 100644 index 0000000..e60721a --- /dev/null +++ b/rust_builder/android/gradle.properties @@ -0,0 +1,5 @@ +org.gradle.jvmargs=-Xmx2G +org.gradle.java.home=/usr/lib/jvm/java-21-openjdk-amd64 +org.gradle.java.installations.auto-detect=false +org.gradle.java.installations.fromEnv=JAVA_HOME +org.gradle.java.installations.paths=/usr/lib/jvm/java-21-openjdk-amd64 diff --git a/rust_builder/cargokit/build_tool/lib/src/builder.dart b/rust_builder/cargokit/build_tool/lib/src/builder.dart index 84c46e4..63edec4 100644 --- a/rust_builder/cargokit/build_tool/lib/src/builder.dart +++ b/rust_builder/cargokit/build_tool/lib/src/builder.dart @@ -15,19 +15,15 @@ import 'util.dart'; final _log = Logger('builder'); -enum BuildConfiguration { - debug, - release, - profile, -} +enum BuildConfiguration { debug, release, profile } extension on BuildConfiguration { bool get isDebug => this == BuildConfiguration.debug; String get rustName => switch (this) { - BuildConfiguration.debug => 'debug', - BuildConfiguration.release => 'release', - BuildConfiguration.profile => 'release', - }; + BuildConfiguration.debug => 'debug', + BuildConfiguration.release => 'release', + BuildConfiguration.profile => 'release', + }; } class BuildException implements Exception { @@ -80,15 +76,12 @@ class BuildEnvironment { return buildConfiguration; } - static BuildEnvironment fromEnvironment({ - required bool isAndroid, - }) { - final buildConfiguration = - parseBuildConfiguration(Environment.configuration); - final manifestDir = Environment.manifestDir; - final crateOptions = CargokitCrateOptions.load( - manifestDir: manifestDir, + static BuildEnvironment fromEnvironment({required bool isAndroid}) { + final buildConfiguration = parseBuildConfiguration( + Environment.configuration, ); + final manifestDir = Environment.manifestDir; + final crateOptions = CargokitCrateOptions.load(manifestDir: manifestDir); final crateInfo = CrateInfo.load(manifestDir); return BuildEnvironment( configuration: buildConfiguration, @@ -99,8 +92,9 @@ class BuildEnvironment { isAndroid: isAndroid, androidSdkPath: isAndroid ? Environment.sdkPath : null, androidNdkVersion: isAndroid ? Environment.ndkVersion : null, - androidMinSdkVersion: - isAndroid ? int.parse(Environment.minSdkVersion) : null, + androidMinSdkVersion: isAndroid + ? int.parse(Environment.minSdkVersion) + : null, javaHome: isAndroid ? Environment.javaHome : null, ); } @@ -110,14 +104,9 @@ class RustBuilder { final Target target; final BuildEnvironment environment; - RustBuilder({ - required this.target, - required this.environment, - }); + RustBuilder({required this.target, required this.environment}); - void prepare( - Rustup rustup, - ) { + void prepare(Rustup rustup) { final toolchain = _toolchain; if (rustup.installedTargets(toolchain) == null) { rustup.installToolchain(toolchain); @@ -133,32 +122,28 @@ class RustBuilder { CargoBuildOptions? get _buildOptions => environment.crateOptions.cargo[environment.configuration]; - String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; + String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; // /// Returns the path of directory containing build artifacts. Future build() async { final extraArgs = _buildOptions?.flags ?? []; final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); - runCommand( - 'rustup', - [ - 'run', - _toolchain, - 'cargo', - 'build', - ...extraArgs, - '--manifest-path', - manifestPath, - '-p', - environment.crateInfo.packageName, - if (!environment.configuration.isDebug) '--release', - '--target', - target.rust, - '--target-dir', - environment.targetTempDir, - ], - environment: await _buildEnvironment(), - ); + runCommand('rustup', [ + 'run', + _toolchain, + 'cargo', + 'build', + ...extraArgs, + '--manifest-path', + manifestPath, + '-p', + environment.crateInfo.packageName, + if (!environment.configuration.isDebug) '--release', + '--target', + target.rust, + '--target-dir', + environment.targetTempDir, + ], environment: await _buildEnvironment()); return path.join( environment.targetTempDir, target.rust, diff --git a/rust_builder/cargokit/build_tool/pubspec.lock b/rust_builder/cargokit/build_tool/pubspec.lock index 5841cfe..343bdd3 100644 --- a/rust_builder/cargokit/build_tool/pubspec.lock +++ b/rust_builder/cargokit/build_tool/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "64.0.0" adaptive_number: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.2.0" args: dependency: "direct main" description: @@ -37,26 +37,18 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" - cli_config: - dependency: transitive - description: - name: cli_config - sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.dev" - source: hosted - version: "0.2.0" + version: "2.1.1" collection: dependency: "direct main" description: @@ -77,10 +69,10 @@ packages: dependency: transitive description: name: coverage - sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d" + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" url: "https://pub.dev" source: hosted - version: "1.13.1" + version: "1.6.3" crypto: dependency: "direct main" description: @@ -109,18 +101,18 @@ packages: dependency: transitive description: name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "3.2.0" github: dependency: "direct main" description: @@ -133,10 +125,10 @@ packages: dependency: transitive description: name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" hex: dependency: "direct main" description: @@ -157,10 +149,10 @@ packages: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" http_parser: dependency: transitive description: @@ -173,26 +165,26 @@ packages: dependency: transitive description: name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.6.7" json_annotation: dependency: transitive description: name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.8.1" lints: dependency: "direct dev" description: @@ -213,26 +205,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16" meta: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.9.1" mime: dependency: transitive description: name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.4" node_preamble: dependency: transitive description: @@ -245,10 +237,10 @@ packages: dependency: transitive description: name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.0" path: dependency: "direct main" description: @@ -277,10 +269,10 @@ packages: dependency: transitive description: name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" shelf: dependency: transitive description: @@ -301,34 +293,34 @@ packages: dependency: transitive description: name: shelf_static - sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "1.0.4" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.13" + version: "0.10.12" source_span: dependency: "direct main" description: @@ -341,58 +333,58 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" + sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" url: "https://pub.dev" source: hosted - version: "1.26.3" + version: "1.24.6" test_api: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" + sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" url: "https://pub.dev" source: hosted - version: "0.6.12" + version: "0.5.6" toml: dependency: "direct main" description: @@ -405,10 +397,10 @@ packages: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" version: dependency: "direct main" description: @@ -421,50 +413,34 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "11.9.0" watcher: dependency: transitive description: name: watcher - sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c" - url: "https://pub.dev" - source: hosted - version: "1.1.3" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.0" yaml: dependency: "direct main" description: @@ -474,4 +450,4 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.0.0 <4.0.0" diff --git a/rust_builder/cargokit/cmake/resolve_symlinks.ps1 b/rust_builder/cargokit/cmake/resolve_symlinks.ps1 index 3d10d28..2ac593a 100644 --- a/rust_builder/cargokit/cmake/resolve_symlinks.ps1 +++ b/rust_builder/cargokit/cmake/resolve_symlinks.ps1 @@ -14,14 +14,21 @@ function Resolve-Symlinks { if ($realPath -and !$realPath.EndsWith($separator)) { $realPath += $separator } - $realPath += $part + + $realPath += $part.Replace('\', '/') + + # The slash is important when using Get-Item on Drive letters in pwsh. + if (-not($realPath.Contains($separator)) -and $realPath.EndsWith(':')) { + $realPath += '/' + } + $item = Get-Item $realPath - if ($item.Target) { - $realPath = $item.Target.Replace('\', '/') + if ($item.LinkTarget) { + $realPath = $item.LinkTarget.Replace('\', '/') } } $realPath } -$path=Resolve-Symlinks -Path $args[0] +$path = Resolve-Symlinks -Path $args[0] Write-Host $path diff --git a/rust_builder/cargokit/gradle/plugin.gradle b/rust_builder/cargokit/gradle/plugin.gradle index 1aead89..4af35ee 100644 --- a/rust_builder/cargokit/gradle/plugin.gradle +++ b/rust_builder/cargokit/gradle/plugin.gradle @@ -58,13 +58,13 @@ abstract class CargoKitBuildTask extends DefaultTask { def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) def rootProjectDir = project.rootProject.projectDir - + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { project.exec { commandLine 'chmod', '+x', path } } - + project.exec { executable path args "build-gradle" @@ -95,7 +95,7 @@ class CargoKitPlugin implements Plugin { private Plugin _findFlutterPlugin(Map projects) { for (project in projects) { for (plugin in project.value.getPlugins()) { - if (plugin.class.name == "FlutterPlugin") { + if (plugin.class.name == "com.flutter.gradle.FlutterPlugin") { return plugin; } } @@ -119,7 +119,7 @@ class CargoKitPlugin implements Plugin { } def cargoBuildDir = "${project.buildDir}/build" - + // Determine if the project is an application or library def isApplication = plugin.project.plugins.hasPlugin('com.android.application') def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants @@ -132,7 +132,7 @@ class CargoKitPlugin implements Plugin { def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; jniLibs.srcDir(new File(cargoOutputDir)) - def platforms = plugin.getTargetPlatforms().collect() + def platforms = com.flutter.gradle.FlutterPluginUtils.getTargetPlatforms(project).collect() // Same thing addFlutterDependencies does in flutter.gradle if (buildType == "debug") { diff --git a/rust_builder/cargokit/run_build_tool.sh b/rust_builder/cargokit/run_build_tool.sh index 6e594a2..24b0ed8 100755 --- a/rust_builder/cargokit/run_build_tool.sh +++ b/rust_builder/cargokit/run_build_tool.sh @@ -77,6 +77,11 @@ if [ ! -f "$PACKAGE_HASH_FILE" ]; then echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" fi +# Rebuild the tool if it was deleted by Android Studio +if [ ! -f "bin/build_tool_runner.dill" ]; then + "$DART" compile kernel bin/build_tool_runner.dart +fi + set +e "$DART" bin/build_tool_runner.dill "$@" diff --git a/rust_builder/linux/CMakeLists.txt b/rust_builder/linux/CMakeLists.txt index a69447a..b0657c3 100644 --- a/rust_builder/linux/CMakeLists.txt +++ b/rust_builder/linux/CMakeLists.txt @@ -8,7 +8,7 @@ set(PROJECT_NAME "rust_lib_kataglyphis_inference_engine") project(${PROJECT_NAME} LANGUAGES CXX) include("../cargokit/cmake/cargokit.cmake") -apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_kataglyphis_inference_engine "") +apply_cargokit(${PROJECT_NAME} ../../ExternalLib/Kataglyphis-RustProjectTemplate rust_lib_kataglyphis_inference_engine "") # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an diff --git a/rust_builder/pubspec.yaml b/rust_builder/pubspec.yaml index e74b1c6..b8c0873 100644 --- a/rust_builder/pubspec.yaml +++ b/rust_builder/pubspec.yaml @@ -1,23 +1,23 @@ -name: rust_lib_kataglyphis_inference_engine +name: kataglyphis_rustprojecttemplate description: "Utility to build Rust code" version: 0.0.1 publish_to: none environment: - sdk: ">=3.3.0 <4.0.0" - flutter: ">=3.3.0" + sdk: '>=3.10.8 <4.0.0' + flutter: '>=3.38.9' dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 + plugin_platform_interface: ^2.1.8 dev_dependencies: - ffi: ^2.0.2 - ffigen: ^11.0.0 + ffi: ^2.1.4 + ffigen: ^20.1.1 flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^6.0.0 flutter: plugin: diff --git a/rust_builder/windows/CMakeLists.txt b/rust_builder/windows/CMakeLists.txt index 7e807b6..7c45708 100644 --- a/rust_builder/windows/CMakeLists.txt +++ b/rust_builder/windows/CMakeLists.txt @@ -9,7 +9,7 @@ set(PROJECT_NAME "rust_lib_kataglyphis_inference_engine") project(${PROJECT_NAME} LANGUAGES CXX) include("../cargokit/cmake/cargokit.cmake") -apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_kataglyphis_inference_engine "") +apply_cargokit(${PROJECT_NAME} ../../../../../../ExternalLib/Kataglyphis-RustProjectTemplate rust_lib_kataglyphis_inference_engine "") # List of absolute paths to libraries that should be bundled with the plugin. # This list could contain prebuilt libraries, or libraries created by an diff --git a/assets/documents/games/.gitkeep b/scripts/ai/README.md similarity index 100% rename from assets/documents/games/.gitkeep rename to scripts/ai/README.md diff --git a/scripts/ai/demo.py b/scripts/ai/demo.py new file mode 100644 index 0000000..a3011fc --- /dev/null +++ b/scripts/ai/demo.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +Python script to run a GStreamer pipeline equivalent to: + + gst-launch-1.0 -e \ + webrtcsink name=ws meta="meta,name=kataglyphiswebfrontend-webfrontend-stream" \ + pylonsrc ! videoconvert ! ws. \ + audiotestsrc ! ws. + +This script initializes GStreamer, sets up the pipeline, and manages the GLib MainLoop +""" + +import sys +import gi + +gi.require_version("Gst", "1.0") +gi.require_version("GstWebRTC", "1.0") +from gi.repository import GObject, Gst + + +def on_message(bus, message, loop): + msg_type = message.type + if msg_type == Gst.MessageType.EOS: + print("End-Of-Stream reached.") + loop.quit() + elif msg_type == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error: {err}, {debug}") + loop.quit() + return True + + +def main(): + # Initialize GObject and GStreamer + GObject.threads_init() + Gst.init(None) + + # Define the pipeline using gst-launch syntax + launch_description = ( + "webrtcsink name=ws " + 'meta="meta,name=kataglyphiswebfrontend-webfrontend-stream" ' + "pylonsrc ! videoconvert ! ws. " + "audiotestsrc ! ws." + ) + + # Parse the launch description into a Gst.Pipeline + try: + pipeline = Gst.parse_launch(launch_description) + except Exception as e: + print(f"Failed to create pipeline: {e}") + sys.exit(1) + + # Create a GLib MainLoop + loop = GObject.MainLoop() + + # Watch the pipeline's bus for messages + bus = pipeline.get_bus() + bus.add_signal_watch() + bus.connect("message", on_message, loop) + + # Start playing the pipeline + ret = pipeline.set_state(Gst.State.PLAYING) + if ret == Gst.StateChangeReturn.FAILURE: + print("Unable to set the pipeline to the playing state.") + sys.exit(1) + + # Run the loop until an EOS or ERROR message is received + try: + print("Running pipeline... Press Ctrl+C to stop.") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipeline...") + finally: + # Clean up + pipeline.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_ai.py b/scripts/ai/demo_ai.py new file mode 100644 index 0000000..29f3046 --- /dev/null +++ b/scripts/ai/demo_ai.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +import gi +import sys + +import numpy as np +import cv2 + +gi.require_version("Gst", "1.0") +gi.require_version("GstWebRTC", "1.0") +from gi.repository import Gst, GObject, GLib + + +def on_new_sample(sink, appsrc): + sample = sink.emit("pull-sample") + if not sample: + return Gst.FlowReturn.ERROR + + buffer = sample.get_buffer() + caps = sample.get_caps() + structure = caps.get_structure(0) + width = structure.get_value("width") + height = structure.get_value("height") + + # Buffer in numpy-Array umwandeln + success, map_info = buffer.map(Gst.MapFlags.READ) + if not success: + return Gst.FlowReturn.ERROR + + try: + # Annahme: RGB, 3 Kanäle + frame = np.ndarray( + (height, width, 3), buffer=map_info.data, dtype=np.uint8 + ).copy() # copy, da map_info.data readonly ist + + # Einfachen roten Kreis in die Mitte zeichnen + center = (width // 2, height // 2) + cv2.circle(frame, center, 50, (255, 0, 0), thickness=5) # Rot, Dicke 5 + + # Neues Gst.Buffer aus dem bearbeiteten Frame erstellen + out_buffer = Gst.Buffer.new_allocate(None, frame.nbytes, None) + out_buffer.fill(0, frame.tobytes()) + out_buffer.pts = buffer.pts + out_buffer.dts = buffer.dts + out_buffer.duration = buffer.duration + + finally: + buffer.unmap(map_info) + + # Buffer an appsrc weitergeben + appsrc.emit("push-buffer", out_buffer) + return Gst.FlowReturn.OK + + +def on_message(bus, message, loop): + msg_type = message.type + + if msg_type == Gst.MessageType.EOS: + print("End-Of-Stream reached.") + loop.quit() + + elif msg_type == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error: {err}, {debug}") + loop.quit() + + +def main(): + Gst.init(None) + + # Pipeline 1: Kamera -> JPEG -> RGB -> appsink + pipeline1_str = ( + "v4l2src device=/dev/video0 ! " + "image/jpeg,width=640,height=360,framerate=30/1 ! " + "jpegdec ! " + "videoconvert ! " + "video/x-raw,format=RGB,width=640,height=360,framerate=30/1 ! " + "appsink name=ai_sink emit-signals=true max-buffers=1 drop=true" + ) + + # Pipeline 2: appsrc -> webrtcsink + pipeline2_str = ( + "appsrc name=ai_src is-live=true block=true format=time caps=video/x-raw,format=RGB,width=640,height=360,framerate=30/1 ! " + "videoconvert ! " + 'webrtcsink name=ws meta="meta,name=kataglyphiswebfrontend-webfrontend-stream"' + ) + + pipeline1 = Gst.parse_launch(pipeline1_str) + pipeline2 = Gst.parse_launch(pipeline2_str) + if not pipeline1 or not pipeline2: + print("Failed to create pipelines.") + return + + ai_sink = pipeline1.get_by_name("ai_sink") + ai_src = pipeline2.get_by_name("ai_src") + ai_sink.connect("new-sample", on_new_sample, ai_src) + + # Message handling für beide Pipelines + loop = GLib.MainLoop() + bus1 = pipeline1.get_bus() + bus2 = pipeline2.get_bus() + bus1.add_signal_watch() + bus2.add_signal_watch() + bus1.connect("message", on_message, loop) + bus2.connect("message", on_message, loop) + + # Pipelines starten + ret1 = pipeline1.set_state(Gst.State.PLAYING) + ret2 = pipeline2.set_state(Gst.State.PLAYING) + if ret1 == Gst.StateChangeReturn.FAILURE or ret2 == Gst.StateChangeReturn.FAILURE: + print("Unable to set pipelines to playing state") + return + + try: + print("Running pipelines...") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipelines.") + finally: + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_overlay.py b/scripts/ai/demo_overlay.py new file mode 100644 index 0000000..0c0619f --- /dev/null +++ b/scripts/ai/demo_overlay.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +""" +Python script to run a GStreamer pipeline with a properly configured cairooverlay. +Ensures the video feed retains color and adds a simple overlay. +""" + +import sys +import gi + +gi.require_version("Gst", "1.0") +gi.require_version("GstWebRTC", "1.0") +from gi.repository import GObject, Gst + + +def draw_overlay(overlay, context, timestamp, duration): + """ + Callback function for cairooverlay to draw on video frames. + """ + # Set a red color (RGBA) and draw sample text + context.set_source_rgba(1.0, 0.0, 0.0, 1.0) # Red color with full opacity + context.select_font_face("Sans", 0, 0) + context.set_font_size(36) + context.move_to(50, 50) + context.show_text("Sample Overlay") + context.stroke() + + +def on_message(bus, message, loop): + """ + Handles GStreamer bus messages like EOS and ERROR. + """ + msg_type = message.type + if msg_type == Gst.MessageType.EOS: + print("End-Of-Stream reached.") + loop.quit() + elif msg_type == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error: {err}, {debug}") + loop.quit() + return True + + +def main(): + # Initialize GObject and GStreamer + GObject.threads_init() + Gst.init(None) + + # Define the pipeline using gst-launch syntax + launch_description = ( + 'webrtcsink name=ws meta="meta,name=kataglyphiswebfrontend-webfrontend-stream" ' + "pylonsrc ! video/x-raw,format=RGB ! videoconvert ! video/x-raw,format=BGRA ! " + "cairooverlay name=overlay ! videoconvert ! video/x-raw,format=I420 ! ws. " + "audiotestsrc ! ws." + ) + + # Parse the launch description into a Gst.Pipeline + try: + pipeline = Gst.parse_launch(launch_description) + except Exception as e: + print(f"Failed to create pipeline: {e}") + sys.exit(1) + + # Get the cairooverlay element and attach the drawing callback + overlay = pipeline.get_by_name("overlay") + if not overlay: + print("Failed to find cairooverlay element in the pipeline.") + sys.exit(1) + overlay.connect("draw", draw_overlay) + + # Create a GLib MainLoop + loop = GObject.MainLoop() + + # Watch the pipeline's bus for messages + bus = pipeline.get_bus() + bus.add_signal_watch() + bus.connect("message", on_message, loop) + + # Start playing the pipeline + ret = pipeline.set_state(Gst.State.PLAYING) + if ret == Gst.StateChangeReturn.FAILURE: + print("Unable to set the pipeline to the playing state.") + sys.exit(1) + + # Run the loop until an EOS or ERROR message is received + try: + print("Running pipeline... Press Ctrl+C to stop.") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipeline...") + finally: + # Clean up + pipeline.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_yolov11_cocksorange.py b/scripts/ai/demo_yolov11_cocksorange.py new file mode 100644 index 0000000..43550d4 --- /dev/null +++ b/scripts/ai/demo_yolov11_cocksorange.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +import gi +import sys +import torch +import numpy as np +import cv2 + +# Ultralytics YOLO11 +from ultralytics import YOLO + +gi.require_version('Gst', '1.0') +gi.require_version('GstWebRTC', '1.0') +from gi.repository import Gst, GObject, GLib + +# ---- Config ---- +VIDEO_DEVICE = "/dev/video20" +WIDTH = 1280 +HEIGHT = 720 +FPS_NUM = 5 +FPS_DEN = 1 +YOLO_SIZE = 640 # inference resize (tweak for perf/accuracy) +MODEL_NAME = 'yolo11n' # yolo11 variants: yolo11n, yolo11s, yolo11m, yolo11l, yolo11x + +# Load model once before pipeline +model_path = MODEL_NAME if MODEL_NAME.endswith('.pt') else MODEL_NAME + '.pt' +print(f"Loading model {model_path} ...") +model = YOLO(model_path) # downloads official weights automatically if needed + +# Inference params (we'll pass them per-call) +MODEL_CONF = 0.25 +MODEL_IOU = 0.45 + + +def on_new_sample(sink, appsrc): + """ + appsink -> pull sample, convert to numpy BGR, run YOLO11, draw, + then push to appsrc (which will be converted to I420 and encoded). + """ + sample = sink.emit("pull-sample") + if not sample: + return Gst.FlowReturn.ERROR + + buffer = sample.get_buffer() + caps = sample.get_caps() + struct = caps.get_structure(0) + width = struct.get_value('width') + height = struct.get_value('height') + + # Map incoming buffer for read + success, map_info = buffer.map(Gst.MapFlags.READ) + if not success: + return Gst.FlowReturn.ERROR + + try: + # Build HxWx3 uint8 numpy frame (BGR) — ensure appsink sends BGR + frame = np.ndarray((height, width, 3), buffer=map_info.data, dtype=np.uint8).copy() + + # ---- YOLO11 Inference ---- + # Ultralytics expects RGB images for best compatibility + img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # choose device automatically + device = 'cuda' if torch.cuda.is_available() else 'cpu' + + # run prediction (returns a list-like Results object) + results = model.predict(source=img_rgb, imgsz=YOLO_SIZE, conf=MODEL_CONF, iou=MODEL_IOU, device=device) + + if len(results) == 0: + detections = [] + else: + res = results[0] + # res.boxes.xyxy, res.boxes.conf, res.boxes.cls are accessible + if hasattr(res, 'boxes') and len(res.boxes) > 0: + boxes = res.boxes.xyxy.cpu().numpy() + confs = res.boxes.conf.cpu().numpy() + cls_ids = res.boxes.cls.cpu().numpy().astype(int) + else: + boxes = np.empty((0, 4)) + confs = np.empty((0,)) + cls_ids = np.empty((0,), dtype=int) + + # draw detections if any + if len(results) and len(boxes): + for (x1, y1, x2, y2), conf, cid in zip(boxes, confs, cls_ids): + x1, y1, x2, y2 = map(int, (x1, y1, x2, y2)) + label = model.names[int(cid)] if hasattr(model, 'names') else str(int(cid)) + cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) + text = f"{label} {conf:.2f}" + (w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + cv2.rectangle(frame, (x1, y1 - h - 4), (x1 + w, y1), (0, 255, 0), -1) + cv2.putText(frame, text, (x1, y1 - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) + + # (Optional) red circle as before + center = (width // 2, height // 2) + cv2.circle(frame, center, 50, (255, 0, 0), thickness=5) + + # Pack into Gst.Buffer for appsrc — keep same caps as appsrc (BGR) + data = frame.tobytes() + out_buffer = Gst.Buffer.new_allocate(None, len(data), None) + out_buffer.fill(0, data) + + # Preserve timestamps to keep webrtc timing reasonable + out_buffer.pts = buffer.pts + out_buffer.dts = buffer.dts + out_buffer.duration = buffer.duration + + finally: + buffer.unmap(map_info) + + # Push into appsrc pipeline + appsrc.emit("push-buffer", out_buffer) + return Gst.FlowReturn.OK + + +def on_message(bus, message, loop): + t = message.type + if t == Gst.MessageType.EOS: + print("End-Of-Stream") + loop.quit() + elif t == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error from {message.src.get_name()}: {err} - {debug}") + loop.quit() + + +def main(): + Gst.init(None) + + # Source pipeline: v4l2src (YUY2) -> videoconvert -> BGR -> appsink + pipeline1_str = ( + f'v4l2src device={VIDEO_DEVICE} ! ' + f'video/x-raw,format=YUY2,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoconvert ! ' + f'video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'appsink name=ai_sink emit-signals=true max-buffers=1 drop=true' + ) + + # Sender pipeline: appsrc (BGR) -> videoconvert -> I420 -> kyh264enc -> h264parse -> capsfilter -> queue -> webrtcsink + pipeline2_str = ( + f'appsrc name=ai_src is-live=true block=true format=time caps=video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoconvert ! ' + 'video/x-raw,format=I420 ! ' + 'kyh264enc ! ' + 'h264parse config-interval=1 ! ' + 'capsfilter caps="video/x-h264, stream-format=(string)byte-stream,alignment=(string)au, profile=(string)main,level=(string)3.1, coded-picture-structure=(string)frame, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true" ! ' + 'queue ! ' + 'webrtcsink name=ws congestion-control=disabled signaller::uri="ws://0.0.0.0:8443" meta="meta,name=kataglyphis-webfrontend-stream"' + ) + + pipeline1 = Gst.parse_launch(pipeline1_str) + pipeline2 = Gst.parse_launch(pipeline2_str) + + if not pipeline1 or not pipeline2: + print("Failed to create pipelines.") + return + + ai_sink = pipeline1.get_by_name("ai_sink") + ai_src = pipeline2.get_by_name("ai_src") + if not ai_sink or not ai_src: + print("Failed to get app elements by name.") + return + + # Connect appsink new-sample -> on_new_sample, pass ai_src as user_data + ai_sink.connect("new-sample", on_new_sample, ai_src) + + # Message handling + loop = GLib.MainLoop() + bus1 = pipeline1.get_bus() + bus2 = pipeline2.get_bus() + bus1.add_signal_watch() + bus2.add_signal_watch() + bus1.connect("message", on_message, loop) + bus2.connect("message", on_message, loop) + + # Start pipelines + ret1 = pipeline1.set_state(Gst.State.PLAYING) + ret2 = pipeline2.set_state(Gst.State.PLAYING) + if ret1 == Gst.StateChangeReturn.FAILURE or ret2 == Gst.StateChangeReturn.FAILURE: + print("Unable to set pipelines to playing state") + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + return + + try: + print("Running pipelines... (Ctrl+C to stop)") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipelines.") + finally: + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_yolov11_rasp.py b/scripts/ai/demo_yolov11_rasp.py new file mode 100644 index 0000000..2902719 --- /dev/null +++ b/scripts/ai/demo_yolov11_rasp.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +import gi +import sys +import torch +import numpy as np +import cv2 + +# Ultralytics YOLO11 +from ultralytics import YOLO + +gi.require_version('Gst', '1.0') +gi.require_version('GstWebRTC', '1.0') +from gi.repository import Gst, GObject, GLib + +# ---- Config ---- +# Matches the gst-launch pipeline the user provided: +WIDTH = 1280 +HEIGHT = 720 +FPS_NUM = 30 +FPS_DEN = 1 +YOLO_SIZE = 640 # inference resize (tweak for perf/accuracy) +MODEL_NAME = 'yolo11n' # yolo11 variants: yolo11n, yolo11s, yolo11m, yolo11l, yolo11x + +# Inference params +MODEL_CONF = 0.25 +MODEL_IOU = 0.45 + +# Load model once before pipeline +model_path = MODEL_NAME if MODEL_NAME.endswith('.pt') else MODEL_NAME + '.pt' +print(f"Loading model {model_path} ...") +model = YOLO(model_path) # downloads official weights automatically if needed + +def on_new_sample(sink, appsrc): + """ + appsink -> pull sample, convert to numpy BGR, run YOLO11, draw, + then push to appsrc (which will be converted to I420 and encoded). + """ + sample = sink.emit("pull-sample") + if not sample: + return Gst.FlowReturn.ERROR + + buffer = sample.get_buffer() + caps = sample.get_caps() + if not caps: + return Gst.FlowReturn.ERROR + + struct = caps.get_structure(0) + # Try to read width/height from caps (fallback to constants) + try: + width = struct.get_value('width') or WIDTH + height = struct.get_value('height') or HEIGHT + except Exception: + width = WIDTH + height = HEIGHT + + # Map incoming buffer for read + success, map_info = buffer.map(Gst.MapFlags.READ) + if not success: + return Gst.FlowReturn.ERROR + + try: + # Build HxWx3 uint8 numpy frame (BGR) — ensure appsink sends BGR + frame = np.ndarray((height, width, 3), buffer=map_info.data, dtype=np.uint8).copy() + + # ---- YOLO11 Inference ---- + # Ultralytics expects RGB images for best compatibility + img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # choose device automatically + device = 'cuda' if torch.cuda.is_available() else 'cpu' + + # run prediction (returns a list-like Results object) + results = model.predict(source=img_rgb, imgsz=YOLO_SIZE, conf=MODEL_CONF, iou=MODEL_IOU, device=device) + + if len(results) == 0: + boxes = np.empty((0, 4)) + confs = np.empty((0,)) + cls_ids = np.empty((0,), dtype=int) + else: + res = results[0] + if hasattr(res, 'boxes') and len(res.boxes) > 0: + # move to cpu and numpy + boxes = res.boxes.xyxy.cpu().numpy() + confs = res.boxes.conf.cpu().numpy() + cls_ids = res.boxes.cls.cpu().numpy().astype(int) + else: + boxes = np.empty((0, 4)) + confs = np.empty((0,)) + cls_ids = np.empty((0,), dtype=int) + + # draw detections if any + if len(boxes): + for (x1, y1, x2, y2), conf, cid in zip(boxes, confs, cls_ids): + x1, y1, x2, y2 = map(int, (x1, y1, x2, y2)) + label = model.names[int(cid)] if hasattr(model, 'names') else str(int(cid)) + cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) + text = f"{label} {conf:.2f}" + (w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + cv2.rectangle(frame, (x1, y1 - h - 4), (x1 + w, y1), (0, 255, 0), -1) + cv2.putText(frame, text, (x1, y1 - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) + + # (Optional) red circle as before + center = (width // 2, height // 2) + cv2.circle(frame, center, 30, (255, 0, 0), thickness=3) + + # Pack into Gst.Buffer for appsrc — keep same caps as appsrc (BGR) + data = frame.tobytes() + out_buffer = Gst.Buffer.new_allocate(None, len(data), None) + out_buffer.fill(0, data) + + # Preserve timestamps to keep webrtc timing reasonable + out_buffer.pts = buffer.pts + out_buffer.dts = buffer.dts + out_buffer.duration = buffer.duration + + finally: + buffer.unmap(map_info) + + # Push into appsrc pipeline + ret = appsrc.emit("push-buffer", out_buffer) + if ret != Gst.FlowReturn.OK: + print("Warning: push-buffer returned", ret) + return ret + return Gst.FlowReturn.OK + +def on_message(bus, message, loop): + t = message.type + if t == Gst.MessageType.EOS: + print("End-Of-Stream") + loop.quit() + elif t == Gst.MessageType.ERROR: + err, debug = message.parse_error() + src_name = message.src.get_name() if message.src else "unknown" + print(f"Error from {src_name}: {err} - {debug}") + loop.quit() + +def main(): + Gst.init(None) + + # Source pipeline: libcamerasrc -> videoflip rotate-180 -> videoconvert -> BGR -> appsink + pipeline1_str = ( + f'libcamerasrc ! ' + f'video/x-raw,format=RGB,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoflip method=rotate-180 ! ' + 'videoconvert ! ' + f'video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'appsink name=ai_sink emit-signals=true max-buffers=1 drop=true' + ) + + # Sender pipeline: appsrc (BGR) -> videoconvert -> I420 -> vp8enc -> webrtcsink + pipeline2_str = ( + f'appsrc name=ai_src is-live=true block=true format=time caps=video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoconvert ! ' + 'video/x-raw,format=I420 ! ' + 'queue ! ' + 'vp8enc deadline=1 threads=2 ! ' + 'queue ! ' + 'webrtcsink name=ws signaller::uri="ws://0.0.0.0:8444" meta="meta,name=gst-stream"' + ) + + pipeline1 = Gst.parse_launch(pipeline1_str) + pipeline2 = Gst.parse_launch(pipeline2_str) + + if not pipeline1 or not pipeline2: + print("Failed to create pipelines.") + return + + ai_sink = pipeline1.get_by_name("ai_sink") + ai_src = pipeline2.get_by_name("ai_src") + if not ai_sink or not ai_src: + print("Failed to get app elements by name.") + return + + # Connect appsink new-sample -> on_new_sample, pass ai_src as user_data + ai_sink.connect("new-sample", on_new_sample, ai_src) + + # Message handling + loop = GLib.MainLoop() + bus1 = pipeline1.get_bus() + bus2 = pipeline2.get_bus() + bus1.add_signal_watch() + bus2.add_signal_watch() + bus1.connect("message", on_message, loop) + bus2.connect("message", on_message, loop) + + # Start pipelines + ret1 = pipeline1.set_state(Gst.State.PLAYING) + ret2 = pipeline2.set_state(Gst.State.PLAYING) + if ret1 == Gst.StateChangeReturn.FAILURE or ret2 == Gst.StateChangeReturn.FAILURE: + print("Unable to set pipelines to playing state") + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + return + + try: + print("Running pipelines... (Ctrl+C to stop)") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipelines.") + finally: + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_yolov5.py b/scripts/ai/demo_yolov5.py new file mode 100644 index 0000000..25728e7 --- /dev/null +++ b/scripts/ai/demo_yolov5.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +import gi +import sys + +import torch +import numpy as np +import cv2 + +gi.require_version("Gst", "1.0") +gi.require_version("GstWebRTC", "1.0") +from gi.repository import Gst, GObject, GLib + +# Load the YOLOv5 model once, ideally before you start the pipeline: +# (you can choose 'yolov5s', 'yolov5m', etc. depending on your needs) +model = torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True) +model.conf = 0.25 # confidence threshold (optional) +model.iou = 0.45 # NMS IoU threshold (optional) + + +def on_new_sample(sink, appsrc): + sample = sink.emit("pull-sample") + if not sample: + return Gst.FlowReturn.ERROR + + buffer = sample.get_buffer() + caps = sample.get_caps() + struct = caps.get_structure(0) + width = struct.get_value("width") + height = struct.get_value("height") + + success, map_info = buffer.map(Gst.MapFlags.READ) + if not success: + return Gst.FlowReturn.ERROR + + try: + # build a HxWx3 uint8 numpy frame (BGR) + frame = np.ndarray( + (height, width, 3), buffer=map_info.data, dtype=np.uint8 + ).copy() + + # ---- YOLOv5 Inference ---- + # convert BGR→RGB + img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + # run inference (will return a "Results" object) + results = model(img, size=640) # you can tweak size for speed/accuracy + + # parse detections + # results.xyxy[0] is a tensor of [x1, y1, x2, y2, conf, cls] + for *box, conf, cls in results.xyxy[0].cpu().numpy(): + x1, y1, x2, y2 = map(int, box) + label = results.names[int(cls)] + # draw rectangle (green) and label (white on black bg) + cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) + text = f"{label} {conf:.2f}" + # get text size + (w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + # background + cv2.rectangle(frame, (x1, y1 - h - 4), (x1 + w, y1), (0, 255, 0), -1) + # text + cv2.putText( + frame, text, (x1, y1 - 2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1 + ) + # --------------------------- + + # (Optional) also draw your red circle + center = (width // 2, height // 2) + cv2.circle(frame, center, 50, (255, 0, 0), thickness=5) + + # pack back into a Gst.Buffer + out_buffer = Gst.Buffer.new_allocate(None, frame.nbytes, None) + out_buffer.fill(0, frame.tobytes()) + out_buffer.pts = buffer.pts + out_buffer.dts = buffer.dts + out_buffer.duration = buffer.duration + + finally: + buffer.unmap(map_info) + + appsrc.emit("push-buffer", out_buffer) + return Gst.FlowReturn.OK + + +def on_message(bus, message, loop): + msg_type = message.type + + if msg_type == Gst.MessageType.EOS: + print("End-Of-Stream reached.") + loop.quit() + + elif msg_type == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error: {err}, {debug}") + loop.quit() + + +def main(): + Gst.init(None) + + # Pipeline 1: Kamera -> JPEG -> RGB -> appsink + pipeline1_str = ( + "v4l2src device=/dev/video0 ! " + "image/jpeg,width=640,height=360,framerate=30/1 ! " + "jpegdec ! " + "videoconvert ! " + "video/x-raw,format=RGB,width=640,height=360,framerate=30/1 ! " + "appsink name=ai_sink emit-signals=true max-buffers=1 drop=true" + ) + + # Pipeline 2: appsrc -> webrtcsink + pipeline2_str = ( + "appsrc name=ai_src is-live=true block=true format=time caps=video/x-raw,format=RGB,width=640,height=360,framerate=30/1 ! " + "videoconvert ! " + 'webrtcsink name=ws meta="meta,name=kataglyphiswebfrontend-webfrontend-stream"' + ) + + pipeline1 = Gst.parse_launch(pipeline1_str) + pipeline2 = Gst.parse_launch(pipeline2_str) + if not pipeline1 or not pipeline2: + print("Failed to create pipelines.") + return + + ai_sink = pipeline1.get_by_name("ai_sink") + ai_src = pipeline2.get_by_name("ai_src") + ai_sink.connect("new-sample", on_new_sample, ai_src) + + # Message handling für beide Pipelines + loop = GLib.MainLoop() + bus1 = pipeline1.get_bus() + bus2 = pipeline2.get_bus() + bus1.add_signal_watch() + bus2.add_signal_watch() + bus1.connect("message", on_message, loop) + bus2.connect("message", on_message, loop) + + # Pipelines starten + ret1 = pipeline1.set_state(Gst.State.PLAYING) + ret2 = pipeline2.set_state(Gst.State.PLAYING) + if ret1 == Gst.StateChangeReturn.FAILURE or ret2 == Gst.StateChangeReturn.FAILURE: + print("Unable to set pipelines to playing state") + return + + try: + print("Running pipelines...") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipelines.") + finally: + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/ai/demo_yolov5_cocksorange.py b/scripts/ai/demo_yolov5_cocksorange.py new file mode 100644 index 0000000..9508a1c --- /dev/null +++ b/scripts/ai/demo_yolov5_cocksorange.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +import gi +import sys +import torch +import numpy as np +import cv2 + +gi.require_version('Gst', '1.0') +gi.require_version('GstWebRTC', '1.0') +from gi.repository import Gst, GObject, GLib + +# ---- Config ---- +VIDEO_DEVICE = "/dev/video20" +WIDTH = 1280 +HEIGHT = 720 +FPS_NUM = 5 +FPS_DEN = 1 +YOLO_SIZE = 640 # inference resize (tweak for perf/accuracy) +MODEL_NAME = 'yolov5n' # or yolov5m, etc. + +# Load model once before pipeline +model = torch.hub.load('ultralytics/yolov5', MODEL_NAME, pretrained=True) +model.conf = 0.25 +model.iou = 0.45 + + +def on_new_sample(sink, appsrc): + """ + appsink -> pull sample, convert to numpy BGR, run YOLO, draw, + then push to appsrc (which will be converted to I420 and encoded). + """ + sample = sink.emit("pull-sample") + if not sample: + return Gst.FlowReturn.ERROR + + buffer = sample.get_buffer() + caps = sample.get_caps() + struct = caps.get_structure(0) + width = struct.get_value('width') + height = struct.get_value('height') + + # Map incoming buffer for read + success, map_info = buffer.map(Gst.MapFlags.READ) + if not success: + return Gst.FlowReturn.ERROR + + try: + # Build HxWx3 uint8 numpy frame (BGR) — ensure appsink sends BGR + frame = np.ndarray( + (height, width, 3), + buffer=map_info.data, + dtype=np.uint8 + ).copy() + + # ---- YOLOv5 Inference ---- + img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + results = model(img, size=YOLO_SIZE) + + # draw detections + if len(results.xyxy) and len(results.xyxy[0]): + for *box, conf, cls in results.xyxy[0].cpu().numpy(): + x1, y1, x2, y2 = map(int, box) + label = results.names[int(cls)] + cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) + text = f"{label} {conf:.2f}" + (w, h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + cv2.rectangle(frame, (x1, y1 - h - 4), (x1 + w, y1), (0, 255, 0), -1) + cv2.putText(frame, text, (x1, y1 - 2), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) + + # (Optional) red circle as before + center = (width // 2, height // 2) + cv2.circle(frame, center, 50, (255, 0, 0), thickness=5) + + # Pack into Gst.Buffer for appsrc — keep same caps as appsrc (BGR) + data = frame.tobytes() + out_buffer = Gst.Buffer.new_allocate(None, len(data), None) + out_buffer.fill(0, data) + + # Preserve timestamps to keep webrtc timing reasonable + out_buffer.pts = buffer.pts + out_buffer.dts = buffer.dts + out_buffer.duration = buffer.duration + + finally: + buffer.unmap(map_info) + + # Push into appsrc pipeline + appsrc.emit("push-buffer", out_buffer) + return Gst.FlowReturn.OK + + +def on_message(bus, message, loop): + t = message.type + if t == Gst.MessageType.EOS: + print("End-Of-Stream") + loop.quit() + elif t == Gst.MessageType.ERROR: + err, debug = message.parse_error() + print(f"Error from {message.src.get_name()}: {err} - {debug}") + loop.quit() + + +def main(): + Gst.init(None) + + # Source pipeline: v4l2src (YUY2) -> videoconvert -> BGR -> appsink + pipeline1_str = ( + f'v4l2src device={VIDEO_DEVICE} ! ' + f'video/x-raw,format=YUY2,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoconvert ! ' + f'video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'appsink name=ai_sink emit-signals=true max-buffers=1 drop=true' + ) + + # Sender pipeline: appsrc (BGR) -> videoconvert -> I420 -> kyh264enc -> h264parse -> capsfilter -> queue -> webrtcsink + pipeline2_str = ( + f'appsrc name=ai_src is-live=true block=true format=time caps=video/x-raw,format=BGR,width={WIDTH},height={HEIGHT},framerate={FPS_NUM}/{FPS_DEN} ! ' + 'videoconvert ! ' + 'video/x-raw,format=I420 ! ' + 'kyh264enc ! ' + 'h264parse config-interval=1 ! ' + 'capsfilter caps="video/x-h264, stream-format=(string)byte-stream,alignment=(string)au, profile=(string)main,level=(string)3.1, coded-picture-structure=(string)frame, chroma-format=(string)4:2:0, bit-depth-luma=(uint)8, bit-depth-chroma=(uint)8, parsed=(boolean)true" ! ' + 'queue ! ' + 'webrtcsink name=ws congestion-control=disabled signaller::uri="ws://0.0.0.0:8443" meta="meta,name=kataglyphis-webfrontend-stream"' + ) + + pipeline1 = Gst.parse_launch(pipeline1_str) + pipeline2 = Gst.parse_launch(pipeline2_str) + + if not pipeline1 or not pipeline2: + print("Failed to create pipelines.") + return + + ai_sink = pipeline1.get_by_name("ai_sink") + ai_src = pipeline2.get_by_name("ai_src") + if not ai_sink or not ai_src: + print("Failed to get app elements by name.") + return + + # Connect appsink new-sample -> on_new_sample, pass ai_src as user_data + ai_sink.connect("new-sample", on_new_sample, ai_src) + + # Message handling + loop = GLib.MainLoop() + bus1 = pipeline1.get_bus() + bus2 = pipeline2.get_bus() + bus1.add_signal_watch() + bus2.add_signal_watch() + bus1.connect("message", on_message, loop) + bus2.connect("message", on_message, loop) + + # Start pipelines + ret1 = pipeline1.set_state(Gst.State.PLAYING) + ret2 = pipeline2.set_state(Gst.State.PLAYING) + if ret1 == Gst.StateChangeReturn.FAILURE or ret2 == Gst.StateChangeReturn.FAILURE: + print("Unable to set pipelines to playing state") + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + return + + try: + print("Running pipelines... (Ctrl+C to stop)") + loop.run() + except KeyboardInterrupt: + print("Interrupted by user, stopping pipelines.") + finally: + pipeline1.set_state(Gst.State.NULL) + pipeline2.set_state(Gst.State.NULL) + + +if __name__ == "__main__": + main() diff --git a/scripts/linux/setup-flutter-arm64.sh b/scripts/linux/setup-flutter-arm64.sh index 50abb85..50a4ef1 100644 --- a/scripts/linux/setup-flutter-arm64.sh +++ b/scripts/linux/setup-flutter-arm64.sh @@ -1,29 +1,21 @@ #!/usr/bin/env bash set -e # exit on any error +# NOTE: Keep LF line endings for Linux shells. + # ------------------------------------------ # Setup Flutter SDK on ARM64 (Linux) # ------------------------------------------ -# Allow passing version as an argument -FLUTTER_VERSION="${1:-3.35.6}" # default if not provided - -echo "📦 Setting up Flutter $FLUTTER_VERSION for ARM64..." - -# Download Flutter SDK -wget -q "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Extract -tar xf "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" +# shellcheck source=./setup-flutter-common.sh -# Add Flutter to PATH for subsequent GitHub Action steps -# echo "$PWD/flutter/bin" >> "$GITHUB_PATH" +source "${SCRIPT_DIR}/setup-flutter-common.sh" -# Add Flutter to PATH for current shell session -export PATH="$PWD/flutter/bin:$PATH" +# Allow passing version and install directory as arguments -# Clean up unnecessary cache -rm -rf "$PWD/flutter/bin/cache" +FLUTTER_VERSION="${1:-3.38.9}" # default if not provided +FLUTTER_DIR="${2:-/opt}" # default if not provided -# Verify installation -flutter --version +setup_flutter "ARM64" "${FLUTTER_VERSION}" "${FLUTTER_DIR}" \ No newline at end of file diff --git a/scripts/linux/setup-flutter-common.sh b/scripts/linux/setup-flutter-common.sh new file mode 100644 index 0000000..ab6c595 --- /dev/null +++ b/scripts/linux/setup-flutter-common.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e # exit on any error + +# NOTE: Keep LF line endings for Linux shells. + +setup_flutter() { + local arch_label="$1" + local flutter_version="$2" + local install_dir="${3:-/opt}" # Default: /opt, override with 3rd argument + local flutter_path="${install_dir}/flutter" + local archive="flutter_linux_${flutter_version}-stable.tar.xz" + + echo "📦 Setting up Flutter ${flutter_version} for ${arch_label}..." + echo "📁 Installation directory: ${flutter_path}" + + # Download Flutter SDK + wget -q "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/${archive}" + + # Extract to specified directory + sudo mkdir -p "${install_dir}" + sudo tar xf "${archive}" -C "${install_dir}" + + # Remove downloaded archive to keep things clean + rm -f "${archive}" + + # Add Flutter to PATH permanently for the current user + local path_export="export PATH=\"${flutter_path}/bin:\$PATH\"" + if [ -f "$HOME/.bashrc" ]; then + grep -q "${flutter_path}/bin" "$HOME/.bashrc" || echo "${path_export}" >> "$HOME/.bashrc" + elif [ -f "$HOME/.profile" ]; then + grep -q "${flutter_path}/bin" "$HOME/.profile" || echo "${path_export}" >> "$HOME/.profile" + else + echo "${path_export}" >> "$HOME/.profile" + fi + + # Add Flutter to PATH for subsequent GitHub Action steps + # echo "${flutter_path}/bin" >> "$GITHUB_PATH" + + # Add Flutter to PATH for current shell session + export PATH="${flutter_path}/bin:$PATH" + + # Clean up unnecessary cache + sudo rm -rf "${flutter_path}/bin/cache" + + # Verify installation + flutter --version +} \ No newline at end of file diff --git a/scripts/linux/setup-flutter-x86-64.sh b/scripts/linux/setup-flutter-x86-64.sh old mode 100644 new mode 100755 index 632f98a..d8edba7 --- a/scripts/linux/setup-flutter-x86-64.sh +++ b/scripts/linux/setup-flutter-x86-64.sh @@ -1,26 +1,21 @@ #!/usr/bin/env bash set -e # exit on any error +# NOTE: Keep LF line endings for Linux shells. + # ------------------------------------------ -# Setup Flutter SDK on ARM64 (Linux) +# Setup Flutter SDK on AMDx86 (Linux) # ------------------------------------------ -# Allow passing version as an argument -FLUTTER_VERSION="${1:-3.35.6}" # default if not provided - -echo "📦 Setting up Flutter $FLUTTER_VERSION for x86-64..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Download Flutter SDK -wget -q "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" +# shellcheck source=./setup-flutter-common.sh -# Extract -tar xf "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" +source "${SCRIPT_DIR}/setup-flutter-common.sh" -# Add Flutter to PATH for subsequent GitHub Action steps -# echo "$PWD/flutter/bin" >> "$GITHUB_PATH" +# Allow passing version and install directory as arguments -# Add Flutter to PATH for current shell session -export PATH="$PWD/flutter/bin:$PATH" +FLUTTER_VERSION="${1:-3.38.9}" # default if not provided +FLUTTER_DIR="${2:-/opt}" # default if not provided -# Verify installation -flutter --version +setup_flutter "x86-64" "${FLUTTER_VERSION}" "${FLUTTER_DIR}" \ No newline at end of file diff --git a/scripts/linux/start_on_rasp.sh b/scripts/linux/start_on_rasp.sh new file mode 100644 index 0000000..81e16bf --- /dev/null +++ b/scripts/linux/start_on_rasp.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Create a new tmux session called 'webrtc-setup' +tmux new-session -d -s webrtc-setup + +# Rename the first window +tmux rename-window -t webrtc-setup:0 'webrtc' + +# In the first pane, source bashrc, navigate to the signalling directory and run the server +tmux send-keys -t webrtc-setup:0.0 'source ~/.bashrc' Enter +tmux send-keys -t webrtc-setup:0.0 'cd /home/bigjuicyjones/gst-plugins-rs/net/webrtc/signalling' Enter +tmux send-keys -t webrtc-setup:0.0 'WEBRTCSINK_SIGNALLING_SERVER_LOG=debug cargo run --bin gst-webrtc-signalling-server' Enter + +# Split the window horizontally to create a second pane +tmux split-window -h -t webrtc-setup:0 + +# Wait a moment for the server to start, then source bashrc and run gst-launch in the second pane +tmux send-keys -t webrtc-setup:0.1 'sleep 5' Enter +tmux send-keys -t webrtc-setup:0.1 'source ~/.bashrc' Enter +tmux send-keys -t webrtc-setup:0.1 'gst-launch-1.0 \ + libcamerasrc ! video/x-raw,width=1280,height=720,format=NV12,interlace-mode=progressive ! \ + x264enc speed-preset=1 threads=1 byte-stream=true ! \ + h264parse ! \ + webrtcsink signaller::uri="ws://0.0.0.0:8443" name=ws meta="meta,name=gst-stream"' Enter + +# Split the second pane vertically to create a third pane +tmux split-window -v -t webrtc-setup:0.1 + +# Wait a moment, then source bashrc and run the serve command in the third pane +tmux send-keys -t webrtc-setup:0.2 'sleep 6' Enter +tmux send-keys -t webrtc-setup:0.2 'source ~/.bashrc' Enter +tmux send-keys -t webrtc-setup:0.2 'serve -s dist -l 5173' Enter + +# Attach to the session +tmux attach-session -t webrtc-setup diff --git a/scripts/windows/add-gstreamer-to-path.ps1 b/scripts/windows/add-gstreamer-to-path.ps1 new file mode 100644 index 0000000..86629e5 --- /dev/null +++ b/scripts/windows/add-gstreamer-to-path.ps1 @@ -0,0 +1,190 @@ +<# +.SYNOPSIS + Configure GStreamer environment variables and PATH on Windows. + +.DESCRIPTION + - Updates current session PATH to include GStreamer bin. + - Optionally persists environment variables (User or Machine). + - Sets GSTREAMER_ROOT_X86 or GSTREAMER_ROOT_X86_64 depending on Architecture param. + - Optional plugin path settings. + - Backs up existing PATH before persisting. + - Removes old GStreamer bin entries to avoid duplicates. +#> + +param( + [string]$GStreamerRoot = $env:GSTREAMER_ROOT, + [ValidateSet("x86","x86_64","both")] + [string]$Architecture = "x86_64", + [ValidateSet("None","User","Machine")] + [string]$PersistScope = "Machine", # "None", "User", or "Machine" + [string[]]$PluginPaths = @(), # Additional plugin folders to add to GST_PLUGIN_PATH + [string]$PluginSystemPath = $null, # If set, sets GST_PLUGIN_SYSTEM_PATH to this value + [switch]$RemoveOldGStreamerBins = $false, # Remove older GStreamer bin entries from PATH before adding + [switch]$Force # Force overwrite without extra prompts (not used interactively here) +) + +function Test-IsAdmin { + $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($currentUser) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +# Default GStreamer root if not provided (keep your CMake default) +if (-not $GStreamerRoot) { + $GStreamerRoot = "C:\Program Files\gstreamer\1.0\msvc_x86_64" +} + +# Resolve full path (suppress error output) +try { + $resolved = Resolve-Path -Path $GStreamerRoot -ErrorAction Stop + $GStreamerRoot = $resolved.ProviderPath +} catch { + Write-Error "GStreamer root not found or not resolvable. Check the -GStreamerRoot parameter. Error: $($_.Exception.Message)" + exit 1 +} + +# Determine which roots to set +$rootsToSet = @() +switch ($Architecture) { + "x86" { $rootsToSet += @{Name="GSTREAMER_ROOT_X86"; Path=Join-Path $GStreamerRoot ""} } + "x86_64" { $rootsToSet += @{Name="GSTREAMER_ROOT_X86_64"; Path=Join-Path $GStreamerRoot ""} } + "both" { + $rootsToSet += @{Name="GSTREAMER_ROOT_X86_64"; Path=Join-Path $GStreamerRoot ""} + $x86Candidate = $GStreamerRoot -replace 'msvc_x86_64','msvc_x86' + if (Test-Path $x86Candidate) { $rootsToSet += @{Name="GSTREAMER_ROOT_X86"; Path=$x86Candidate} } + } +} + +# Pick primary bin to add (prefer x86_64 bin) +$primaryBin = $null +if ($rootsToSet) { + $preferred = $rootsToSet | Where-Object { $_.Path -like '*msvc_x86_64*' } | Select-Object -First 1 + if (-not $preferred) { $preferred = $rootsToSet[0] } + $primaryBin = Join-Path $preferred.Path "bin" +} + +if (-not (Test-Path $primaryBin)) { + Write-Error "GStreamer bin folder not found: $primaryBin" + exit 1 +} + +function Remove-GStreamerBinsFromPath([string]$path) { + if (-not $path) { return $path } + $parts = $path -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } + # Match typical gstreamer install bin folders + $filtered = $parts | Where-Object { + -not ($_ -match '(?i)gstreamer.*\\msvc.*\\bin') -and -not ($_ -match '(?i)gstreamer.*\\1\.0.*\\bin') + } + return ($filtered -join ';') +} + +# 1) Update current session PATH (no duplicates) +$sessionPathParts = $env:PATH -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } +if ($RemoveOldGStreamerBins) { + $sessionPath = Remove-GStreamerBinsFromPath($env:PATH) + $sessionPathParts = $sessionPath -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } +} +if ($sessionPathParts -notcontains $primaryBin) { + $env:PATH = "$primaryBin;$($sessionPathParts -join ';')" + Write-Host "Updated PATH for this session with: $primaryBin" +} else { + Write-Host "Session PATH already contains: $primaryBin" +} + +# 2) Persist environment variables (User or Machine) if requested +if ($PersistScope -ne "None") { + if ($PersistScope -eq "Machine" -and -not (Test-IsAdmin)) { + Write-Warning "Persisting to Machine scope requires elevation. Attempting to continue may fail." + } + + # Backup existing PATH for chosen scope + $timestamp = (Get-Date).ToString('yyyyMMdd-HHmmss') + $backupFile = Join-Path $env:TEMP "path-backup-$PersistScope-$timestamp.txt" + try { + $oldPath = [Environment]::GetEnvironmentVariable("PATH", $PersistScope) + $oldPath | Out-File -FilePath $backupFile -Encoding UTF8 + Write-Host "Backed up existing $PersistScope PATH to: $backupFile" + } catch { + # <-- FIXED: wrap PersistScope in subexpression so colon after var doesn't confuse parser + Write-Warning "Failed to back up existing PATH for scope $($PersistScope): $($_.Exception.Message)" + } + + # Prepare new persistent PATH: remove old gstreamer bins and prepend primaryBin + $cleanPath = Remove-GStreamerBinsFromPath($oldPath) + $newPath = $primaryBin + ";" + $cleanPath.Trim(';') + $newPathParts = $newPath -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } | Select-Object -Unique + $newPathFinal = ($newPathParts -join ';') + + try { + [Environment]::SetEnvironmentVariable("PATH", $newPathFinal, $PersistScope) + Write-Host "Persisted GStreamer bin in $PersistScope PATH: $primaryBin" + } catch { + Write-Error "Failed to set PATH in $PersistScope scope: $($_.Exception.Message)" + } + + # Persist the root variables + foreach ($r in $rootsToSet) { + try { + [Environment]::SetEnvironmentVariable($r.Name, $r.Path, $PersistScope) + Write-Host "Set $($r.Name) = $($r.Path) in $PersistScope scope" + } catch { + Write-Error "Failed to set $($r.Name) in $PersistScope scope: $($_.Exception.Message)" + } + } + + # Persist plugin variables if requested + if ($PluginPaths -and $PluginPaths.Count -gt 0) { + try { + $existingPluginPath = [Environment]::GetEnvironmentVariable("GST_PLUGIN_PATH", $PersistScope) + } catch { + $existingPluginPath = $null + } + $merged = @() + if ($existingPluginPath) { $merged += ($existingPluginPath -split ';') | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' } } + $merged += $PluginPaths + $merged = $merged | Select-Object -Unique + $mergedFinal = ($merged -join ';') + try { + [Environment]::SetEnvironmentVariable("GST_PLUGIN_PATH", $mergedFinal, $PersistScope) + Write-Host "Set GST_PLUGIN_PATH in $PersistScope scope to: $mergedFinal" + } catch { + Write-Error "Failed to set GST_PLUGIN_PATH in $PersistScope scope: $($_.Exception.Message)" + } + } + + if ($PluginSystemPath) { + try { + [Environment]::SetEnvironmentVariable("GST_PLUGIN_SYSTEM_PATH", $PluginSystemPath, $PersistScope) + Write-Host "Set GST_PLUGIN_SYSTEM_PATH in $PersistScope scope to: $PluginSystemPath" + } catch { + Write-Error "Failed to set GST_PLUGIN_SYSTEM_PATH in $PersistScope scope: $($_.Exception.Message)" + } + } + + Write-Host "Note: Applications and new shells will pick up new environment variables after restart or logoff/login." +} + +# Final summary +Write-Host "---- Summary ----" +Write-Host "Session PATH starts with: $primaryBin" +if ($PersistScope -ne "None") { + Write-Host "Persisted to: $PersistScope (backup at $backupFile)" +} else { + Write-Host "No persistence requested (session-only change)." +} +if ($PluginPaths -and $PluginPaths.Count -gt 0) { Write-Host "GST_PLUGIN_PATH updated with: $($PluginPaths -join ', ')" } +if ($PluginSystemPath) { Write-Host "GST_PLUGIN_SYSTEM_PATH set to: $PluginSystemPath" } +# build a readable string for printing +$rootsStr = ($rootsToSet | ForEach-Object { "{0}={1}" -f $_.Name, $_.Path }) -join '; ' +Write-Host "GStreamer roots set: $rootsStr" +Write-Host "-----------------" + +# Return object for programmatic use +[PSCustomObject]@{ + PrimaryBin = $primaryBin + PersistScope = $PersistScope + Roots = $rootsToSet + PluginPaths = $PluginPaths + PluginSystemPath = $PluginSystemPath + BackupFile = $backupFile +} diff --git a/scripts/windows/build-windows.ps1 b/scripts/windows/build-windows.ps1 index 7ca8e19..a4995b0 100644 --- a/scripts/windows/build-windows.ps1 +++ b/scripts/windows/build-windows.ps1 @@ -1,117 +1,541 @@ -$ErrorActionPreference = "Stop" - -# Environment variables matching the workflow -$env:BUILD_DIR_RELEASE = "build/windows/x64/runner" -$env:CLANG_VERSION = "21.1.6" -$env:GSTREAMER_VERSION = "1.26.6" -$env:LLVM_BIN = 'C:\Program Files\LLVM\bin' - -Write-Host "=== 1. Environment Check ===" -cmake --version -clang-cl --version -flutter --version -cargo --version - -# --- GIT CONFIGURATION --- -Write-Host "=== 2. Enabling Git Long Paths ===" -git config --global core.longpaths true - -# --- FLUTTER & DART SETUP --- -Write-Host "=== 3. Installing Flutter Dependencies ===" -flutter pub get -flutter config --enable-windows-desktop - -# --- CODE QUALITY & TESTS (matching workflow's continue-on-error behavior) --- -Write-Host "=== 4. Verifying Formatting (dart format) ===" -try { - dart format --output=none --set-exit-if-changed . - Write-Host "Formatting verification passed." -} catch { - Write-Warning "Formatting verification failed, but continuing (continue-on-error: true)." +<# +.SYNOPSIS + Configurable Windows build script for the Kataglyphis project with logging. + +.DESCRIPTION + Run with defaults or pass parameters to override workspace path and other options. + Example: + .\build-windows.ps1 -WorkspaceDir "D:\dev\kataglyphis" -SkipFlutterBuild + .\build-windows.ps1 -LogDir "build_logs" -StopOnError +#> + +[CmdletBinding()] +param( + [string] $WorkspaceDir = $PWD.Path, + [string] $BuildDirRelease = "build/windows/x64/runner", + [string] $RustCrateDir = "ExternalLib\Kataglyphis-RustProjectTemplate", + [string] $RustDllName = "kataglyphis_rustprojecttemplate.dll", + [string] $CMakeGenerator = "Ninja", + [string] $CMakeBuildType = "Release", + [string] $LogDir = "logs", + [switch] $SkipTests, + [switch] $SkipFlutterBuild, + [switch] $ContinueOnError, + [switch] $StopOnError +) + +#region ==================== LOGGING INFRASTRUCTURE ==================== + +$script:LogWriter = $null +$script:LogPath = $null + +# Results tracking + +$script:Results = @{ + Succeeded = New-Object System.Collections.Generic.List[string] + Failed = New-Object System.Collections.Generic.List[string] + Errors = @{} } -Write-Host "=== 5. Analyzing Project Source (dart analyze) ===" -try { - dart analyze - Write-Host "Analysis passed." -} catch { - Write-Warning "Analysis failed, but continuing (continue-on-error: true)." +function Open-Log { + param( + [Parameter(Mandatory)] + [string]$Path + ) + + $parentDir = Split-Path -Parent $Path + if ($parentDir -and -not (Test-Path $parentDir)) { + New-Item -ItemType Directory -Force -Path $parentDir | Out-Null + } + + $fileStream = New-Object System.IO.FileStream( + $Path, + [System.IO.FileMode]::Append, + [System.IO.FileAccess]::Write, + [System.IO.FileShare]::ReadWrite + ) + $script:LogWriter = New-Object System.IO.StreamWriter($fileStream, [System.Text.Encoding]::UTF8) + $script:LogWriter.AutoFlush = $true + $script:LogPath = $Path } -Write-Host "=== 6. Running Flutter Tests ===" -try { - flutter test - Write-Host "Tests passed." -} catch { - Write-Warning "Tests failed, but continuing (continue-on-error: true)." +function Close-Log { + if ($script:LogWriter) { + try { + $script:LogWriter.Flush() + $script:LogWriter.Dispose() + } catch { + # ignore + } finally { + $script:LogWriter = $null + } + } } -# --- FLUTTER EPHEMERAL BUILD (Header Generation) --- -Write-Host "=== 7. Generating C++ Bindings (Ephemeral Build) ===" -try { - flutter build windows --release -} catch { - Write-Warning "Flutter build threw an error, but proceeding if headers were generated." +function Write-Log { + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string]$Message + ) + + Write-Host $Message + if ($script:LogWriter) { + $timestamp = Get-Date -Format "HH:mm:ss" + $script:LogWriter.WriteLine("[$timestamp] $Message") + } +} + +function Write-LogWarning { + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string]$Message + ) + + if ($Message) { + Write-Warning $Message + if ($script:LogWriter) { + $timestamp = Get-Date -Format "HH:mm:ss" + $script:LogWriter.WriteLine("[$timestamp] WARNING: $Message") + } + } else { + Write-Host "" + if ($script:LogWriter) { + $script:LogWriter.WriteLine("") + } + } +} + +function Write-LogError { + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string]$Message + ) + + if ($Message) { + Write-Host $Message -ForegroundColor Red + if ($script:LogWriter) { + $timestamp = Get-Date -Format "HH:mm:ss" + $script:LogWriter.WriteLine("[$timestamp] ERROR: $Message") + } + } else { + Write-Host "" + if ($script:LogWriter) { + $script:LogWriter.WriteLine("") + } + } } -Write-Host "Cleaning build directory for CMake..." -if (Test-Path "C:\workspace\build") { - Remove-Item -Recurse -Force "C:\workspace\build" +function Write-LogSuccess { + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string]$Message + ) + + if ($Message) { + Write-Host $Message -ForegroundColor Green + if ($script:LogWriter) { + $timestamp = Get-Date -Format "HH:mm:ss" + $script:LogWriter.WriteLine("[$timestamp] SUCCESS: $Message") + } + } else { + Write-Host "" + if ($script:LogWriter) { + $script:LogWriter.WriteLine("") + } + } } -# --- CMAKE CONFIGURE --- -Write-Host "=== 8. CMake Configure ===" -cmake ` - "C:\workspace\windows" ` - -B "C:\workspace\build\windows\x64" ` - -G "Ninja" ` - -DCMAKE_BUILD_TYPE=Release ` - -DCMAKE_INSTALL_PREFIX="C:\workspace\build\windows\x64\runner" ` - -DFLUTTER_TARGET_PLATFORM=windows-x64 ` - -DCMAKE_CXX_COMPILER='clang-cl' ` - -DCMAKE_C_COMPILER='clang-cl' ` - -DCMAKE_CXX_COMPILER_TARGET=x86_64-pc-windows-msvc - -# --- RUST BUILD --- -Write-Host "=== 9. Building Rust Crate ===" -Set-Location "C:\workspace\rust" -cargo install flutter_rust_bridge_codegen -cargo build --release -Set-Location "C:\workspace" - -# --- MOVE RUST DLL --- -Write-Host "=== 10. Copying Rust DLL ===" -$dllSource = "C:\workspace\rust\target\release\rust_lib_kataglyphis_inference_engine.dll" -$dllDestDir = "C:\workspace\build\windows\x64\plugins\rust_lib_kataglyphis_inference_engine" - -# --- NATIVE ASSETS FIX --- -Write-Host "=== 11. Applying Native Assets Dir Fix ===" -$p = "C:\workspace\build\native_assets\windows" -if (Test-Path $p) { - $it = Get-Item -LiteralPath $p -Force - if ($it.PSIsContainer) { - Write-Host "Path is already a directory: $p" +function Invoke-External { + param( + [Parameter(Mandatory)] + [string]$File, + [string[]]$Args = @(), + [switch]$IgnoreExitCode + ) + + $cmdLine = if ($Args -and $Args.Count) { "$File $($Args -join ' ')" } else { $File } + Write-Log "CMD: $cmdLine" + + $previousErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + $global:LASTEXITCODE = 0 + + try { + & $File @Args 2>&1 | ForEach-Object { + $line = $_ + if ($null -eq $line) { return } + Write-Log ([string]$line) + } + $exitCode = $LASTEXITCODE + if ($exitCode -ne 0 -and -not $IgnoreExitCode) { + throw "Command failed with exit code ${exitCode}: $cmdLine" + } + return $exitCode + } finally { + $ErrorActionPreference = $previousErrorActionPreference + } +} + +function Invoke-Step { + param( + [Parameter(Mandatory)] + [string]$StepName, + [Parameter(Mandatory)] + [scriptblock]$Script, + [switch]$Critical + ) + + Write-Log "" + Write-Log ">>> Starting: $StepName" + Write-Log ("=" * 60) + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + try { + & $Script + $stopwatch.Stop() + $script:Results.Succeeded.Add($StepName) | Out-Null + Write-LogSuccess "<<< Completed: $StepName (Duration: $($stopwatch.Elapsed.ToString('mm\:ss\.fff')))" + return $true + } catch { + $stopwatch.Stop() + $errorMessage = $_.Exception.Message + $script:Results.Failed.Add($StepName) | Out-Null + $script:Results.Errors[$StepName] = $errorMessage + Write-LogError "<<< FAILED: $StepName (Duration: $($stopwatch.Elapsed.ToString('mm\:ss\.fff')))" + Write-LogError " Error: $errorMessage" + + if ($_.ScriptStackTrace) { + Write-Log " Stack: $($_.ScriptStackTrace)" + } + + if ($StopOnError -and $Critical) { + throw "Critical step '$StepName' failed: $errorMessage" + } + + return $false + } +} + +function Invoke-Optional { + param( + [scriptblock]$Script, + [string]$Name + ) + + try { + & $Script + } catch { + Write-LogWarning "$Name failed, continuing. Details: $($_.Exception.Message)" + } +} + +function Write-Summary { + Write-Log "" + Write-Log ("=" * 60) + Write-Log "=== BUILD PIPELINE SUMMARY ===" + Write-Log ("=" * 60) + Write-Log "" + + if ($script:Results.Succeeded.Count -gt 0) { + Write-LogSuccess "SUCCEEDED ($($script:Results.Succeeded.Count)):" + foreach ($step in $script:Results.Succeeded) { + Write-LogSuccess " [OK] $step" + } + } + + Write-Log "" + + if ($script:Results.Failed.Count -gt 0) { + Write-LogError "FAILED ($($script:Results.Failed.Count)):" + foreach ($step in $script:Results.Failed) { + Write-LogError " [X] $step" + Write-LogError " Error: $($script:Results.Errors[$step])" + } + } + + Write-Log "" + $total = $script:Results.Succeeded.Count + $script:Results.Failed.Count + $successRate = if ($total -gt 0) { [math]::Round(($script:Results.Succeeded.Count / $total) * 100, 1) } else { 0 } + Write-Log "Total: $total steps, $($script:Results.Succeeded.Count) succeeded, $($script:Results.Failed.Count) failed ($($successRate)% success rate)" + Write-Log "" + + if ($script:LogPath) { + Write-Log "Full log available at: $script:LogPath" + } + + if ($script:Results.Failed.Count -gt 0) { + Write-LogWarning "Pipeline completed with errors!" } else { - Write-Host "Path exists but is NOT a directory. Removing and creating directory: $p" - Remove-Item -LiteralPath $p -Force - New-Item -ItemType Directory -Path $p | Out-Null + Write-LogSuccess "Pipeline completed successfully!" + } +} + +#endregion + +#region ==================== HELPER FUNCTIONS ==================== + +function Remove-BuildRoot { + param([Parameter(Mandatory=$true)][string]$Path) + + if (-not (Test-Path $Path)) { + Write-Log "Build root does not exist: $Path" + return $true } + + Write-Log "Terminating potentially locking processes..." + @("flutter", "dart", "msbuild", "devenv", "ninja", "cmake") | ForEach-Object { + Get-Process $_ -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue + } + + Start-Sleep -Seconds 3 + + for ($i = 1; $i -le 3; $i++) { + try { + Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop + Write-Log "Build directory removed: $Path" + return $true + } catch { + Write-LogWarning "Attempt $i/3 failed: $($_.Exception.Message)" + if ($i -lt 3) { Start-Sleep -Seconds 2 } + } + } + return $false +} + +#endregion + +#region ==================== MAIN SCRIPT ==================== + +# Error handling preference + +if ($ContinueOnError) { + $ErrorActionPreference = "Continue" } else { - Write-Host "Creating directory: $p" - New-Item -ItemType Directory -Path $p | Out-Null + $ErrorActionPreference = "Stop" } -if (-not (Test-Path $dllSource)) { - Write-Error "Rust DLL not found at $dllSource" +# Resolve workspace + +try { + $Workspace = (Resolve-Path -Path $WorkspaceDir -ErrorAction Stop).Path +} catch { + Write-Host "Workspace path doesn't exist, creating: $WorkspaceDir" + New-Item -ItemType Directory -Force -Path $WorkspaceDir | Out-Null + $Workspace = (Resolve-Path -Path $WorkspaceDir).Path } -New-Item -ItemType Directory -Force -Path $dllDestDir | Out-Null -Copy-Item -Path $dllSource -Destination $dllDestDir -Force -Write-Host "Rust DLL copied successfully." +# Initialize logging + +$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" +$logDirPath = Join-Path $Workspace $LogDir +New-Item -ItemType Directory -Force $logDirPath | Out-Null +$logPath = Join-Path $logDirPath "build-windows-$timestamp.log" + +Open-Log -Path $logPath + +# Derived paths + +$BuildRoot = Join-Path $Workspace "build" +$BuildDirFull = Join-Path $Workspace ($BuildDirRelease -replace '/','\') +$WindowsSrc = Join-Path $Workspace "windows" +$CMakeBuildDir = Join-Path $Workspace "build\windows\x64" +$RustDir = Join-Path $Workspace $RustCrateDir +$DllSource = Join-Path $RustDir "target\release\$RustDllName" +$DllDestDir = Join-Path $Workspace "build\windows\x64\plugins\$($RustDllName -replace '\.dll$','')" +$NativeAssetsDir = Join-Path $Workspace "build\native_assets\windows" + +$env:BUILD_DIR_RELEASE = $BuildDirRelease + +try { + Write-Log "=== Kataglyphis Windows Build Script ===" + Write-Log "Started at: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" + Write-Log "Logging to: $logPath" + Write-Log "" + Write-Log "=== Configuration ===" + Write-Log "Workspace: $Workspace" + Write-Log "BuildDirRelease: $BuildDirRelease" + Write-Log "BuildDirFull: $BuildDirFull" + Write-Log "CMakeBuildDir: $CMakeBuildDir" + Write-Log "CMakeGenerator: $CMakeGenerator" + Write-Log "CMakeBuildType: $CMakeBuildType" + Write-Log "RustDir: $RustDir" + Write-Log "Rust DLL source: $DllSource" + Write-Log "Rust DLL dest: $DllDestDir" + Write-Log "SkipTests: $SkipTests" + Write-Log "SkipFlutterBuild: $SkipFlutterBuild" + Write-Log "ContinueOnError: $ContinueOnError" + Write-Log "StopOnError: $StopOnError" + Write-Log ("=" * 60) + + # --- Step 1: Environment Check --- + Invoke-Step -StepName "Environment Check" -Script { + Invoke-Optional -Name "cmake" -Script { + Invoke-External -File "cmake" -Args @("--version") + } + Invoke-Optional -Name "clang-cl" -Script { + Invoke-External -File "clang-cl" -Args @("--version") + } + Invoke-Optional -Name "flutter" -Script { + Invoke-External -File "flutter" -Args @("--version") + } + Invoke-Optional -Name "cargo" -Script { + Invoke-External -File "cargo" -Args @("--version") + } + Invoke-Optional -Name "ninja" -Script { + Invoke-External -File "ninja" -Args @("--version") + } + } + + # --- Step 2: Git Configuration --- + Invoke-Step -StepName "Git Configuration" -Script { + Invoke-External -File "git" -Args @("config", "--global", "core.longpaths", "true") -IgnoreExitCode + } + + # --- Step 3: Flutter Setup --- + if (-not $SkipFlutterBuild) { + Invoke-Step -StepName "Flutter Dependencies" -Critical -Script { + Invoke-External -File "flutter" -Args @("pub", "get") + Invoke-External -File "flutter" -Args @("config", "--enable-windows-desktop") + } + } else { + Write-Log "Skipping Flutter dependency steps (SkipFlutterBuild set)." + } + + # --- Step 4-6: Code Quality & Tests --- + if (-not $SkipTests) { + Invoke-Step -StepName "Dart Format Verification" -Script { + # Nur Dart-relevante Verzeichnisse formatieren (ExternalLib ausschließen) + $dartDirs = @("lib", "test", "bin", "integration_test") | + Where-Object { Test-Path (Join-Path $Workspace $_) } + + if ($dartDirs.Count -eq 0) { + Write-Log "No Dart directories found to format." + return + } + + Write-Log "Formatting directories: $($dartDirs -join ', ')" + foreach ($dir in $dartDirs) { + Invoke-External -File "dart" -Args @("format", "--output=none", "--set-exit-if-changed", $dir) + } + } -# --- CMAKE BUILD & INSTALL --- -Write-Host "=== 12. CMake Build & Install ===" -cmake --build "C:\workspace\build\windows\x64" --config Release --target install --verbose + Invoke-Step -StepName "Dart Analysis" -Script { + Invoke-External -File "dart" -Args @("analyze") + } + + Invoke-Step -StepName "Flutter Tests" -Script { + Invoke-External -File "flutter" -Args @("test") + } + } else { + Write-Log "Skipping format/analyze/tests (SkipTests set)." + } + + # --- Step 7: Flutter Ephemeral Build --- + if (-not $SkipFlutterBuild) { + Invoke-Step -StepName "Flutter Ephemeral Build (C++ Headers)" -Script { + Invoke-External -File "flutter" -Args @("build", "windows", "--release") -IgnoreExitCode + } + } + + # --- Step 8: Clean Build Directory --- + Invoke-Step -StepName "Clean Build Directory" -Script { + $removed = Remove-BuildRoot -Path $BuildRoot + if (-not $removed -and -not $ContinueOnError) { + throw "Failed to remove build root: $BuildRoot" + } + } + + # --- Step 9: CMake Configure --- + Invoke-Step -StepName "CMake Configure" -Critical -Script { + $cmakeArgs = @( + $WindowsSrc + "-B", $CMakeBuildDir + "-G", $CMakeGenerator + "-DCMAKE_BUILD_TYPE=$CMakeBuildType" + "-DCMAKE_INSTALL_PREFIX=$BuildDirFull" + "-DFLUTTER_TARGET_PLATFORM=windows-x64" + "-DCMAKE_CXX_COMPILER=clang-cl" + "-DCMAKE_C_COMPILER=clang-cl" + "-DCMAKE_CXX_COMPILER_TARGET=x86_64-pc-windows-msvc" + ) + Invoke-External -File "cmake" -Args $cmakeArgs + } + + # --- Step 10: Rust Build --- + Invoke-Step -StepName "Rust Crate Build" -Script { + if (-not (Test-Path $RustDir)) { + throw "Rust crate directory not found: $RustDir" + } + + Push-Location $RustDir + try { + Invoke-Optional -Name "flutter_rust_bridge_codegen install" -Script { + Invoke-External -File "cargo" -Args @("install", "flutter_rust_bridge_codegen") + } + Invoke-External -File "cargo" -Args @("build", "--release") + } finally { + Pop-Location + } + } + + # --- Step 11: Copy Rust DLL --- + Invoke-Step -StepName "Copy Rust DLL" -Script { + if (-not (Test-Path $DllSource)) { + throw "Rust DLL not found at $DllSource" + } + New-Item -ItemType Directory -Force -Path $DllDestDir | Out-Null + Copy-Item -Path $DllSource -Destination $DllDestDir -Force + Write-Log "Rust DLL copied to $DllDestDir" + } + + # --- Step 12: Native Assets Fix --- + Invoke-Step -StepName "Native Assets Directory Fix" -Script { + if (Test-Path $NativeAssetsDir) { + $item = Get-Item -LiteralPath $NativeAssetsDir -Force + if (-not $item.PSIsContainer) { + Write-Log "Path exists but is NOT a directory. Replacing: $NativeAssetsDir" + Remove-Item -LiteralPath $NativeAssetsDir -Force + New-Item -ItemType Directory -Path $NativeAssetsDir | Out-Null + } else { + Write-Log "Path is already a directory: $NativeAssetsDir" + } + } else { + Write-Log "Creating directory: $NativeAssetsDir" + New-Item -ItemType Directory -Path $NativeAssetsDir | Out-Null + } + } + + # --- Step 13: CMake Build & Install --- + Invoke-Step -StepName "CMake Build & Install" -Critical -Script { + Invoke-External -File "cmake" -Args @( + "--build", $CMakeBuildDir, + "--config", $CMakeBuildType, + "--target", "install", + "--verbose" + ) + } + + Write-Log "" + Write-LogSuccess "=== Build Complete ===" + Write-Log "Build artifacts located at: $(Join-Path $Workspace $env:BUILD_DIR_RELEASE)" + +} catch { + Write-LogError "Unhandled critical error: $($_.Exception.Message)" + if ($_.ScriptStackTrace) { + Write-LogError "Stack trace: $($_.ScriptStackTrace)" + } +} finally { + Write-Summary + Close-Log + + if ($script:Results.Failed.Count -gt 0) { + exit 1 + } +} -Write-Host "=== Build Complete ===" -Write-Host "Build artifacts located at: C:\workspace\$env:BUILD_DIR_RELEASE" +#endregion \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..bcb0dcc --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:kataglyphis_inference_engine/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/test_driver/main_test.dart b/test_driver/main_test.dart new file mode 100644 index 0000000..e69de29 diff --git a/web/.htaccess b/web/.htaccess new file mode 100644 index 0000000..7ce922f --- /dev/null +++ b/web/.htaccess @@ -0,0 +1,18 @@ +# Begin .htaccess + +# Ensure mod_headers is enabled + + # Add security headers + Header set X-Content-Type-Options "nosniff" + Header set X-Frame-Options "DENY" + Header set X-XSS-Protection "1; mode=block" + Header set Strict-Transport-Security "max-age=31536000; includeSubDomains" + + # Add Cross-Origin headers + Header set Cross-Origin-Embedder-Policy "credentialless" + Header set Cross-Origin-Opener-Policy "same-origin" + + +# Other configurations... + +# End .htaccess diff --git a/web/css/cookie.css b/web/css/cookie.css new file mode 100644 index 0000000..a4cff27 --- /dev/null +++ b/web/css/cookie.css @@ -0,0 +1,90 @@ +#cookie-notice { + background-color: #fff; + bottom: 0; + box-shadow: 0px 8px 24px rgba(0, 0, 0, .25); + -webkit-box-direction: normal; + -webkit-font-smoothing: antialiased; + display: none; + left: 0; + opacity: 0; + padding: 60px 0; /*40px*/ + position: fixed; + width: 100%; + z-index: 11; +} + +.cookie-consent { + align-items: right; + display: flex; + justify-content: right; + align-items: center; + position: relative; + white-space: pre-line; +} + +#cookie-notice.show { + animation-delay: .2s; + animation-duration: .5s; + animation-fill-mode: forwards; + animation-iteration-count: 1; + animation-name: fadein; + animation-timing-function: cubic-bezier(.27, .89, .39, .95); + display: block; +} + +@keyframes fadein { + 0% { + opacity: 0 + } + + to { + opacity: 1 + } +} + +.container { + align-items: center; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + top: 1px; + margin: 0 auto; + max-width: calc(1280px - 100px); + width: calc(100% - 24px*2); + white-space: pre-line; +} + +.container p { + max-width: 1200px; + margin: 0; + margin-right: 50px; + color: #4a4a4a; + font-size: 16px; + font-family: "Roboto", sans-serif; + line-height: 1.5; /* */ + +} + +p a { + color: #0468d7; + text-decoration: none; +} + +button { + background-color: var(--kPrimaryColor); + color: #fff; + align-items: center; + justify-content: center; + border: 0 solid; + border-radius: 24px; + cursor: pointer; + display: inline-flex; + font-family: "Google Sans", sans-serif; + font-size: 14px; + font-weight: 700; + height: 40px; + padding: 0 32px; + text-align: center; + line-height: 1.15; +} \ No newline at end of file diff --git a/web/css/loading_animation.css b/web/css/loading_animation.css new file mode 100644 index 0000000..72f190e --- /dev/null +++ b/web/css/loading_animation.css @@ -0,0 +1,81 @@ +.loading { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 0; + position: absolute; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +#loading-message { + color: #000000; + font-size: 24px; + margin-left: 15px; +} + +/* #69f0ae */ +#background-animation { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + background: linear-gradient(45deg, #ffffff, #69f0ae); + background-size: 400% 400%; + animation: gradientAnimation 5s ease infinite; +} + +/* #ffffff, #69f0ae */ +.loader { + border: 16px solid #f3f3f3; + border-radius: 50%; + border: 15px solid; + border-top: 16px solid var(--kPrimaryColor); + border-right: 16px solid white; + border-bottom: 16px solid var(--kPrimaryColor); + border-left: 16px solid white; + width: 120px; + height: 120px; + -webkit-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + margin: 10px 0; +} + +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +@keyframes gradientAnimation { + 0% { + background-position: 0% 50%; + } + + 50% { + background-position: 100% 50%; + } + + 100% { + background-position: 0% 50%; + } +} \ No newline at end of file diff --git a/web/favicon.png b/web/favicon.png index 8aaa46a..1ebf26b 100644 Binary files a/web/favicon.png and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index b749bfe..f33a7ef 100644 Binary files a/web/icons/Icon-192.png and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index 88cfd48..c29c531 100644 Binary files a/web/icons/Icon-512.png and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png index eb9b4d7..f33a7ef 100644 Binary files a/web/icons/Icon-maskable-192.png and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png index d69c566..c29c531 100644 Binary files a/web/icons/Icon-maskable-512.png and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html index 1867905..a62133e 100644 --- a/web/index.html +++ b/web/index.html @@ -1,38 +1,110 @@ - - - - - - - - - - - - - - - - - - - - kataglyphis_inference_engine - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + kataglyphis-webfrontendproject + + + + + + + + + + + + +
+ + +
+ + + +
+ +
+
+
+ +
+ + + + + + + \ No newline at end of file diff --git a/web/javascript/cookies.js b/web/javascript/cookies.js new file mode 100644 index 0000000..e099435 --- /dev/null +++ b/web/javascript/cookies.js @@ -0,0 +1,34 @@ +function _setCookie(name, value) { + const expires = "; expires=2147483647"; // ~2038 i.e. until user clears cookies + document.cookie = name + "=" + (value || "") + expires + "; SameSite=Strict"; +} + +function _getCookie(name) { + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; +} + +function initCookieNotice() { + const notice = document.getElementById('cookie-notice'); + const consentBtn = document.getElementById('cookie-consent'); + const cookieKey = 'cookie-consent'; + const cookieConsentValue = 'true' + const activeClass = 'show'; + if (_getCookie(cookieKey) === cookieConsentValue) { + return; + } + notice.classList.add(activeClass); + consentBtn.classList.add(activeClass); + consentBtn.addEventListener('click', (e) => { + e.preventDefault(); + _setCookie(cookieKey, cookieConsentValue); + notice.classList.remove(activeClass); + consentBtn.classList.add(activeClass); + }); +} \ No newline at end of file diff --git a/web/javascript/utils.js b/web/javascript/utils.js new file mode 100644 index 0000000..727c66c --- /dev/null +++ b/web/javascript/utils.js @@ -0,0 +1,26 @@ +// Function to set message based on language +function setMessage(language, messageDivContainerName, messageDe, messageEn) { + + var messageElement = document.getElementById(messageDivContainerName); + + // Check if the element exists + if (!messageElement) { + return; + } + var message = ""; + + switch (language) { + case "de": + message=messageDe + break; + case "en": + message = messageEn; + break; + // Add more cases for other languages as needed + default: + message = messageEn; + } + + // Set the message content + messageElement.textContent = message; +} \ No newline at end of file diff --git a/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js b/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js new file mode 100644 index 0000000..d8388ed --- /dev/null +++ b/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js @@ -0,0 +1,5 @@ +/*! gstwebrtc-api (https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/gstwebrtc-api), MPL-2.0 License, Copyright (C) 2022 Igalia S.L. , Author: Loïc Le Page */ +/*! Contains embedded adapter from webrtc-adapter (https://github.com/webrtcHacks/adapter), BSD 3-Clause License, Copyright (c) 2014, The WebRTC project authors. All rights reserved. Copyright (c) 2018, The adapter.js project authors. All rights reserved. */ + +var e={539:e=>{const t={generateIdentifier:function(){return Math.random().toString(36).substring(2,12)}};t.localCName=t.generateIdentifier(),t.splitLines=function(e){return e.trim().split("\n").map(e=>e.trim())},t.splitSections=function(e){return e.split("\nm=").map((e,t)=>(t>0?"m="+e:e).trim()+"\r\n")},t.getDescription=function(e){const n=t.splitSections(e);return n&&n[0]},t.getMediaSections=function(e){const n=t.splitSections(e);return n.shift(),n},t.matchPrefix=function(e,n){return t.splitLines(e).filter(e=>0===e.indexOf(n))},t.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1],attributes:t.slice(2).join(" ")}},t.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+(e.attributes?" "+e.attributes:"")+"\r\n"},t.parseFmtp=function(e){const t={};let n;const r=e.substring(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?r.push(t+"="+e.parameters[t]):r.push(t)}),t+="a=fmtp:"+n+" "+r.join(";")+"\r\n"}return t},t.parseRtcpFb=function(e){const t=e.substring(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},t.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach(e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"}),t},t.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substring(7,t),10)},r=e.indexOf(":",t);return r>-1?(n.attribute=e.substring(t+1,r),n.value=e.substring(r+1)):n.attribute=e.substring(t+1),n},t.parseSsrcGroup=function(e){const t=e.substring(13).split(" ");return{semantics:t.shift(),ssrcs:t.map(e=>parseInt(e,10))}},t.getMid=function(e){const n=t.matchPrefix(e,"a=mid:")[0];if(n)return n.substring(6)},t.parseFingerprint=function(e){const t=e.substring(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},t.getDtlsParameters=function(e,n){return{role:"auto",fingerprints:t.matchPrefix(e+n,"a=fingerprint:").map(t.parseFingerprint)}},t.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},t.parseCryptoLine=function(e){const t=e.substring(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},t.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?t.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},t.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substring(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},t.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},t.getCryptoParameters=function(e,n){return t.matchPrefix(e+n,"a=crypto:").map(t.parseCryptoLine)},t.getIceParameters=function(e,n){const r=t.matchPrefix(e+n,"a=ice-ufrag:")[0],i=t.matchPrefix(e+n,"a=ice-pwd:")[0];return r&&i?{usernameFragment:r.substring(12),password:i.substring(10)}:null},t.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},t.parseRtpParameters=function(e){const n={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=t.splitLines(e)[0].split(" ");n.profile=r[2];for(let i=3;i{n.headerExtensions.push(t.parseExtmap(e))});const i=t.matchPrefix(e,"a=rtcp-fb:* ").map(t.parseRtcpFb);return n.codecs.forEach(e=>{i.forEach(t=>{e.rtcpFeedback.find(e=>e.type===t.type&&e.parameter===t.parameter)||e.rtcpFeedback.push(t)})}),n},t.writeRtpDescription=function(e,n){let r="";r+="m="+e+" ",r+=n.codecs.length>0?"9":"0",r+=" "+(n.profile||"UDP/TLS/RTP/SAVPF")+" ",r+=n.codecs.map(e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",n.codecs.forEach(e=>{r+=t.writeRtpMap(e),r+=t.writeFmtp(e),r+=t.writeRtcpFb(e)});let i=0;return n.codecs.forEach(e=>{e.maxptime>i&&(i=e.maxptime)}),i>0&&(r+="a=maxptime:"+i+"\r\n"),n.headerExtensions&&n.headerExtensions.forEach(e=>{r+=t.writeExtmap(e)}),r},t.parseRtpEncodingParameters=function(e){const n=[],r=t.parseRtpParameters(e),i=-1!==r.fecMechanisms.indexOf("RED"),o=-1!==r.fecMechanisms.indexOf("ULPFEC"),s=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute),a=s.length>0&&s[0].ssrc;let c;const d=t.matchPrefix(e,"a=ssrc-group:FID").map(e=>e.substring(17).split(" ").map(e=>parseInt(e,10)));d.length>0&&d[0].length>1&&d[0][0]===a&&(c=d[0][1]),r.codecs.forEach(e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let t={ssrc:a,codecPayloadType:parseInt(e.parameters.apt,10)};a&&c&&(t.rtx={ssrc:c}),n.push(t),i&&(t=JSON.parse(JSON.stringify(t)),t.fec={ssrc:a,mechanism:o?"red+ulpfec":"red"},n.push(t))}}),0===n.length&&a&&n.push({ssrc:a});let l=t.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substring(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substring(5),10)*.95-16e3:void 0,n.forEach(e=>{e.maxBitrate=l})),n},t.parseRtcpParameters=function(e){const n={},r=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute)[0];r&&(n.cname=r.value,n.ssrc=r.ssrc);const i=t.matchPrefix(e,"a=rtcp-rsize");n.reducedSize=i.length>0,n.compound=0===i.length;const o=t.matchPrefix(e,"a=rtcp-mux");return n.mux=o.length>0,n},t.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},t.parseMsid=function(e){let n;const r=t.matchPrefix(e,"a=msid:");if(1===r.length)return n=r[0].substring(7).split(" "),{stream:n[0],track:n[1]};const i=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"msid"===e.attribute);return i.length>0?(n=i[0].value.split(" "),{stream:n[0],track:n[1]}):void 0},t.parseSctpDescription=function(e){const n=t.parseMLine(e),r=t.matchPrefix(e,"a=max-message-size:");let i;r.length>0&&(i=parseInt(r[0].substring(19),10)),isNaN(i)&&(i=65536);const o=t.matchPrefix(e,"a=sctp-port:");if(o.length>0)return{port:parseInt(o[0].substring(12),10),protocol:n.fmt,maxMessageSize:i};const s=t.matchPrefix(e,"a=sctpmap:");if(s.length>0){const e=s[0].substring(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:i}}},t.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},t.generateSessionId=function(){return Math.random().toString().substr(2,22)},t.writeSessionBoilerplate=function(e,n,r){let i;const o=void 0!==n?n:2;i=e||t.generateSessionId();return"v=0\r\no="+(r||"thisisadapterortc")+" "+i+" "+o+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},t.getDirection=function(e,n){const r=t.splitLines(e);for(let e=0;e{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};(()=>{n.d(r,{Z:()=>De});var e={};n.r(e),n.d(e,{fixNegotiationNeeded:()=>A,shimAddTrackRemoveTrack:()=>R,shimAddTrackRemoveTrackWithNative:()=>P,shimGetDisplayMedia:()=>v,shimGetSendersWithDtmf:()=>k,shimGetStats:()=>T,shimGetUserMedia:()=>y,shimMediaStream:()=>b,shimOnTrack:()=>S,shimPeerConnection:()=>w,shimSenderReceiverGetStats:()=>E});var t={};n.r(t),n.d(t,{shimAddTransceiver:()=>j,shimCreateAnswer:()=>F,shimCreateOffer:()=>U,shimGetDisplayMedia:()=>x,shimGetParameters:()=>N,shimGetUserMedia:()=>I,shimOnTrack:()=>O,shimPeerConnection:()=>D,shimRTCDataChannel:()=>G,shimReceiverGetStats:()=>L,shimRemoveStream:()=>H,shimSenderGetStats:()=>M});var i={};n.r(i),n.d(i,{shimAudioContext:()=>Z,shimCallbacksAPI:()=>q,shimConstraints:()=>W,shimCreateOfferLegacy:()=>B,shimGetUserMedia:()=>K,shimLocalStreamsAPI:()=>z,shimRTCIceServerUrls:()=>Y,shimRemoteStreamsAPI:()=>J,shimTrackEventTransceiver:()=>V});var o={};n.r(o),n.d(o,{removeExtmapAllowMixed:()=>ie,shimAddIceCandidateNullOrEmpty:()=>oe,shimConnectionState:()=>re,shimMaxMessageSize:()=>te,shimParameterlessSetLocalDescription:()=>se,shimRTCIceCandidate:()=>Q,shimRTCIceCandidateRelayProtocol:()=>ee,shimSendThrowTypeError:()=>ne});let s=!0,a=!0;function c(e,t,n){const r=e.match(t);return r&&r.length>=n&&parseInt(r[n],10)}function d(e,t,n){if(!e.RTCPeerConnection)return;const r=e.RTCPeerConnection.prototype,i=r.addEventListener;r.addEventListener=function(e,r){if(e!==t)return i.apply(this,arguments);const o=e=>{const t=n(e);t&&(r.handleEvent?r.handleEvent(t):r(t))};return this._eventMap=this._eventMap||{},this._eventMap[t]||(this._eventMap[t]=new Map),this._eventMap[t].set(r,o),i.apply(this,[e,o])};const o=r.removeEventListener;r.removeEventListener=function(e,n){if(e!==t||!this._eventMap||!this._eventMap[t])return o.apply(this,arguments);if(!this._eventMap[t].has(n))return o.apply(this,arguments);const r=this._eventMap[t].get(n);return this._eventMap[t].delete(n),0===this._eventMap[t].size&&delete this._eventMap[t],0===Object.keys(this._eventMap).length&&delete this._eventMap,o.apply(this,[e,r])},Object.defineProperty(r,"on"+t,{get(){return this["_on"+t]},set(e){this["_on"+t]&&(this.removeEventListener(t,this["_on"+t]),delete this["_on"+t]),e&&this.addEventListener(t,this["_on"+t]=e)},enumerable:!0,configurable:!0})}function l(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(s=e,e?"adapter.js logging disabled":"adapter.js logging enabled")}function h(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(a=!e,"adapter.js deprecation warnings "+(e?"disabled":"enabled"))}function p(){if("object"==typeof window){if(s)return;"undefined"!=typeof console&&"function"==typeof console.log&&console.log.apply(console,arguments)}}function u(e,t){a&&console.warn(e+" is deprecated, please use "+t+" instead.")}function m(e){return"[object Object]"===Object.prototype.toString.call(e)}function _(e){return m(e)?Object.keys(e).reduce(function(t,n){const r=m(e[n]),i=r?_(e[n]):e[n],o=r&&!Object.keys(i).length;return void 0===i||o?t:Object.assign(t,{[n]:i})},{}):e}function f(e,t,n){t&&!n.has(t.id)&&(n.set(t.id,t),Object.keys(t).forEach(r=>{r.endsWith("Id")?f(e,e.get(t[r]),n):r.endsWith("Ids")&&t[r].forEach(t=>{f(e,e.get(t),n)})}))}function g(e,t,n){const r=n?"outbound-rtp":"inbound-rtp",i=new Map;if(null===t)return i;const o=[];return e.forEach(e=>{"track"===e.type&&e.trackIdentifier===t.id&&o.push(e)}),o.forEach(t=>{e.forEach(n=>{n.type===r&&n.trackId===t.id&&f(e,n,i)})}),i}const C=p;function y(e,t){const n=e&&e.navigator;if(!n.mediaDevices)return;const r=function(e){if("object"!=typeof e||e.mandatory||e.optional)return e;const t={};return Object.keys(e).forEach(n=>{if("require"===n||"advanced"===n||"mediaSource"===n)return;const r="object"==typeof e[n]?e[n]:{ideal:e[n]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);const i=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==r.ideal){t.optional=t.optional||[];let e={};"number"==typeof r.ideal?(e[i("min",n)]=r.ideal,t.optional.push(e),e={},e[i("max",n)]=r.ideal,t.optional.push(e)):(e[i("",n)]=r.ideal,t.optional.push(e))}void 0!==r.exact&&"number"!=typeof r.exact?(t.mandatory=t.mandatory||{},t.mandatory[i("",n)]=r.exact):["min","max"].forEach(e=>{void 0!==r[e]&&(t.mandatory=t.mandatory||{},t.mandatory[i(e,n)]=r[e])})}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},i=function(e,i){if(t.version>=61)return i(e);if((e=JSON.parse(JSON.stringify(e)))&&"object"==typeof e.audio){const t=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])};t((e=JSON.parse(JSON.stringify(e))).audio,"autoGainControl","googAutoGainControl"),t(e.audio,"noiseSuppression","googNoiseSuppression"),e.audio=r(e.audio)}if(e&&"object"==typeof e.video){let o=e.video.facingMode;o=o&&("object"==typeof o?o:{ideal:o});const s=t.version<66;if(o&&("user"===o.exact||"environment"===o.exact||"user"===o.ideal||"environment"===o.ideal)&&(!n.mediaDevices.getSupportedConstraints||!n.mediaDevices.getSupportedConstraints().facingMode||s)){let t;if(delete e.video.facingMode,"environment"===o.exact||"environment"===o.ideal?t=["back","rear"]:"user"!==o.exact&&"user"!==o.ideal||(t=["front"]),t)return n.mediaDevices.enumerateDevices().then(n=>{let s=(n=n.filter(e=>"videoinput"===e.kind)).find(e=>t.some(t=>e.label.toLowerCase().includes(t)));return!s&&n.length&&t.includes("back")&&(s=n[n.length-1]),s&&(e.video.deviceId=o.exact?{exact:s.deviceId}:{ideal:s.deviceId}),e.video=r(e.video),C("chrome: "+JSON.stringify(e)),i(e)})}e.video=r(e.video)}return C("chrome: "+JSON.stringify(e)),i(e)},o=function(e){return t.version>=64?e:{name:{PermissionDeniedError:"NotAllowedError",PermissionDismissedError:"NotAllowedError",InvalidStateError:"NotAllowedError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotAllowedError",MediaDeviceKillSwitchOn:"NotAllowedError",TabCaptureError:"AbortError",ScreenCaptureError:"AbortError",DeviceCaptureError:"AbortError"}[e.name]||e.name,message:e.message,constraint:e.constraint||e.constraintName,toString(){return this.name+(this.message&&": ")+this.message}}};if(n.getUserMedia=function(e,t,r){i(e,e=>{n.webkitGetUserMedia(e,t,e=>{r&&r(o(e))})})}.bind(n),n.mediaDevices.getUserMedia){const e=n.mediaDevices.getUserMedia.bind(n.mediaDevices);n.mediaDevices.getUserMedia=function(t){return i(t,t=>e(t).then(e=>{if(t.audio&&!e.getAudioTracks().length||t.video&&!e.getVideoTracks().length)throw e.getTracks().forEach(e=>{e.stop()}),new DOMException("","NotFoundError");return e},e=>Promise.reject(o(e))))}}}function v(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&("function"==typeof t?e.navigator.mediaDevices.getDisplayMedia=function(n){return t(n).then(t=>{const r=n.video&&n.video.width,i=n.video&&n.video.height,o=n.video&&n.video.frameRate;return n.video={mandatory:{chromeMediaSource:"desktop",chromeMediaSourceId:t,maxFrameRate:o||3}},r&&(n.video.mandatory.maxWidth=r),i&&(n.video.mandatory.maxHeight=i),e.navigator.mediaDevices.getUserMedia(n)})}:console.error("shimGetDisplayMedia: getSourceId argument is not a function"))}function b(e){e.MediaStream=e.MediaStream||e.webkitMediaStream}function S(e){if("object"==typeof e&&e.RTCPeerConnection&&!("ontrack"in e.RTCPeerConnection.prototype)){Object.defineProperty(e.RTCPeerConnection.prototype,"ontrack",{get(){return this._ontrack},set(e){this._ontrack&&this.removeEventListener("track",this._ontrack),this.addEventListener("track",this._ontrack=e)},enumerable:!0,configurable:!0});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){return this._ontrackpoly||(this._ontrackpoly=t=>{t.stream.addEventListener("addtrack",n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find(e=>e.track&&e.track.id===n.track.id):{track:n.track};const i=new Event("track");i.track=n.track,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)}),t.stream.getTracks().forEach(n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find(e=>e.track&&e.track.id===n.id):{track:n};const i=new Event("track");i.track=n,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)})},this.addEventListener("addstream",this._ontrackpoly)),t.apply(this,arguments)}}else d(e,"track",e=>(e.transceiver||Object.defineProperty(e,"transceiver",{value:{receiver:e.receiver}}),e))}function k(e){if("object"==typeof e&&e.RTCPeerConnection&&!("getSenders"in e.RTCPeerConnection.prototype)&&"createDTMFSender"in e.RTCPeerConnection.prototype){const t=function(e,t){return{track:t,get dtmf(){return void 0===this._dtmf&&("audio"===t.kind?this._dtmf=e.createDTMFSender(t):this._dtmf=null),this._dtmf},_pc:e}};if(!e.RTCPeerConnection.prototype.getSenders){e.RTCPeerConnection.prototype.getSenders=function(){return this._senders=this._senders||[],this._senders.slice()};const n=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,r){let i=n.apply(this,arguments);return i||(i=t(this,e),this._senders.push(i)),i};const r=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){r.apply(this,arguments);const t=this._senders.indexOf(e);-1!==t&&this._senders.splice(t,1)}}const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._senders=this._senders||[],n.apply(this,[e]),e.getTracks().forEach(e=>{this._senders.push(t(this,e))})};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){this._senders=this._senders||[],r.apply(this,[e]),e.getTracks().forEach(e=>{const t=this._senders.find(t=>t.track===e);t&&this._senders.splice(this._senders.indexOf(t),1)})}}else if("object"==typeof e&&e.RTCPeerConnection&&"getSenders"in e.RTCPeerConnection.prototype&&"createDTMFSender"in e.RTCPeerConnection.prototype&&e.RTCRtpSender&&!("dtmf"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e},Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=this._pc.createDTMFSender(this.track):this._dtmf=null),this._dtmf}})}}function T(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,n,r]=arguments;if(arguments.length>0&&"function"==typeof e)return t.apply(this,arguments);if(0===t.length&&(0===arguments.length||"function"!=typeof e))return t.apply(this,[]);const i=function(e){const t={};return e.result().forEach(e=>{const n={id:e.id,timestamp:e.timestamp,type:{localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[e.type]||e.type};e.names().forEach(t=>{n[t]=e.stat(t)}),t[n.id]=n}),t},o=function(e){return new Map(Object.keys(e).map(t=>[t,e[t]]))};if(arguments.length>=2){const r=function(e){n(o(i(e)))};return t.apply(this,[r,e])}return new Promise((e,n)=>{t.apply(this,[function(t){e(o(i(t)))},n])}).then(n,r)}}function E(e){if(!("object"==typeof e&&e.RTCPeerConnection&&e.RTCRtpSender&&e.RTCRtpReceiver))return;if(!("getStats"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){const e=this;return this._pc.getStats().then(t=>g(t,e.track,!0))}}if(!("getStats"in e.RTCRtpReceiver.prototype)){const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e}),d(e,"track",e=>(e.receiver._pc=e.srcElement,e)),e.RTCRtpReceiver.prototype.getStats=function(){const e=this;return this._pc.getStats().then(t=>g(t,e.track,!1))}}if(!("getStats"in e.RTCRtpSender.prototype)||!("getStats"in e.RTCRtpReceiver.prototype))return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){if(arguments.length>0&&arguments[0]instanceof e.MediaStreamTrack){const e=arguments[0];let t,n,r;return this.getSenders().forEach(n=>{n.track===e&&(t?r=!0:t=n)}),this.getReceivers().forEach(t=>(t.track===e&&(n?r=!0:n=t),t.track===e)),r||t&&n?Promise.reject(new DOMException("There are more than one sender or receiver for the track.","InvalidAccessError")):t?t.getStats():n?n.getStats():Promise.reject(new DOMException("There is no sender or receiver for the track.","InvalidAccessError"))}return t.apply(this,arguments)}}function P(e){e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},Object.keys(this._shimmedLocalStreams).map(e=>this._shimmedLocalStreams[e][0])};const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,n){if(!n)return t.apply(this,arguments);this._shimmedLocalStreams=this._shimmedLocalStreams||{};const r=t.apply(this,arguments);return this._shimmedLocalStreams[n.id]?-1===this._shimmedLocalStreams[n.id].indexOf(r)&&this._shimmedLocalStreams[n.id].push(r):this._shimmedLocalStreams[n.id]=[n,r],r};const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._shimmedLocalStreams=this._shimmedLocalStreams||{},e.getTracks().forEach(e=>{if(this.getSenders().find(t=>t.track===e))throw new DOMException("Track already exists.","InvalidAccessError")});const t=this.getSenders();n.apply(this,arguments);const r=this.getSenders().filter(e=>-1===t.indexOf(e));this._shimmedLocalStreams[e.id]=[e].concat(r)};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},delete this._shimmedLocalStreams[e.id],r.apply(this,arguments)};const i=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},e&&Object.keys(this._shimmedLocalStreams).forEach(t=>{const n=this._shimmedLocalStreams[t].indexOf(e);-1!==n&&this._shimmedLocalStreams[t].splice(n,1),1===this._shimmedLocalStreams[t].length&&delete this._shimmedLocalStreams[t]}),i.apply(this,arguments)}}function R(e,t){if(!e.RTCPeerConnection)return;if(e.RTCPeerConnection.prototype.addTrack&&t.version>=65)return P(e);const n=e.RTCPeerConnection.prototype.getLocalStreams;e.RTCPeerConnection.prototype.getLocalStreams=function(){const e=n.apply(this);return this._reverseStreams=this._reverseStreams||{},e.map(e=>this._reverseStreams[e.id])};const r=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(t){if(this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},t.getTracks().forEach(e=>{if(this.getSenders().find(t=>t.track===e))throw new DOMException("Track already exists.","InvalidAccessError")}),!this._reverseStreams[t.id]){const n=new e.MediaStream(t.getTracks());this._streams[t.id]=n,this._reverseStreams[n.id]=t,t=n}r.apply(this,[t])};const i=e.RTCPeerConnection.prototype.removeStream;function o(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(i.id,"g"),r.id)}),new RTCSessionDescription({type:t.type,sdp:n})}e.RTCPeerConnection.prototype.removeStream=function(e){this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},i.apply(this,[this._streams[e.id]||e]),delete this._reverseStreams[this._streams[e.id]?this._streams[e.id].id:e.id],delete this._streams[e.id]},e.RTCPeerConnection.prototype.addTrack=function(t,n){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");const r=[].slice.call(arguments,1);if(1!==r.length||!r[0].getTracks().find(e=>e===t))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(this.getSenders().find(e=>e.track===t))throw new DOMException("Track already exists.","InvalidAccessError");this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{};const i=this._streams[n.id];if(i)i.addTrack(t),Promise.resolve().then(()=>{this.dispatchEvent(new Event("negotiationneeded"))});else{const r=new e.MediaStream([t]);this._streams[n.id]=r,this._reverseStreams[r.id]=n,this.addStream(r)}return this.getSenders().find(e=>e.track===t)},["createOffer","createAnswer"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){const e=arguments;return arguments.length&&"function"==typeof arguments[0]?n.apply(this,[t=>{const n=o(this,t);e[0].apply(null,[n])},t=>{e[1]&&e[1].apply(null,t)},arguments[2]]):n.apply(this,arguments).then(e=>o(this,e))}};e.RTCPeerConnection.prototype[t]=r[t]});const s=e.RTCPeerConnection.prototype.setLocalDescription;e.RTCPeerConnection.prototype.setLocalDescription=function(){return arguments.length&&arguments[0].type?(arguments[0]=function(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(r.id,"g"),i.id)}),new RTCSessionDescription({type:t.type,sdp:n})}(this,arguments[0]),s.apply(this,arguments)):s.apply(this,arguments)};const a=Object.getOwnPropertyDescriptor(e.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(e.RTCPeerConnection.prototype,"localDescription",{get(){const e=a.get.apply(this);return""===e.type?e:o(this,e)}}),e.RTCPeerConnection.prototype.removeTrack=function(e){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!e._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(!(e._pc===this))throw new DOMException("Sender was not created by this connection.","InvalidAccessError");let t;this._streams=this._streams||{},Object.keys(this._streams).forEach(n=>{this._streams[n].getTracks().find(t=>e.track===t)&&(t=this._streams[n])}),t&&(1===t.getTracks().length?this.removeStream(this._reverseStreams[t.id]):t.removeTrack(e.track),this.dispatchEvent(new Event("negotiationneeded")))}}function w(e,t){!e.RTCPeerConnection&&e.webkitRTCPeerConnection&&(e.RTCPeerConnection=e.webkitRTCPeerConnection),e.RTCPeerConnection&&t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]})}function A(e,t){d(e,"negotiationneeded",e=>{const n=e.target;if(!(t.version<72||n.getConfiguration&&"plan-b"===n.getConfiguration().sdpSemantics)||"stable"===n.signalingState)return e})}function I(e,t){const n=e&&e.navigator,r=e&&e.MediaStreamTrack;if(n.getUserMedia=function(e,t,r){u("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(t,r)},!(t.version>55&&"autoGainControl"in n.mediaDevices.getSupportedConstraints())){const e=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])},t=n.mediaDevices.getUserMedia.bind(n.mediaDevices);if(n.mediaDevices.getUserMedia=function(n){return"object"==typeof n&&"object"==typeof n.audio&&(n=JSON.parse(JSON.stringify(n)),e(n.audio,"autoGainControl","mozAutoGainControl"),e(n.audio,"noiseSuppression","mozNoiseSuppression")),t(n)},r&&r.prototype.getSettings){const t=r.prototype.getSettings;r.prototype.getSettings=function(){const n=t.apply(this,arguments);return e(n,"mozAutoGainControl","autoGainControl"),e(n,"mozNoiseSuppression","noiseSuppression"),n}}if(r&&r.prototype.applyConstraints){const t=r.prototype.applyConstraints;r.prototype.applyConstraints=function(n){return"audio"===this.kind&&"object"==typeof n&&(n=JSON.parse(JSON.stringify(n)),e(n,"autoGainControl","mozAutoGainControl"),e(n,"noiseSuppression","mozNoiseSuppression")),t.apply(this,[n])}}}}function x(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&(e.navigator.mediaDevices.getDisplayMedia=function(n){if(!n||!n.video){const e=new DOMException("getDisplayMedia without video constraints is undefined");return e.name="NotFoundError",e.code=8,Promise.reject(e)}return!0===n.video?n.video={mediaSource:t}:n.video.mediaSource=t,e.navigator.mediaDevices.getUserMedia(n)})}function O(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function D(e,t){if("object"!=typeof e||!e.RTCPeerConnection&&!e.mozRTCPeerConnection)return;!e.RTCPeerConnection&&e.mozRTCPeerConnection&&(e.RTCPeerConnection=e.mozRTCPeerConnection),t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]});const n={inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"},r=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,i,o]=arguments;return r.apply(this,[e||null]).then(e=>{if(t.version<53&&!i)try{e.forEach(e=>{e.type=n[e.type]||e.type})}catch(t){if("TypeError"!==t.name)throw t;e.forEach((t,r)=>{e.set(r,Object.assign({},t,{type:n[t.type]||t.type}))})}return e}).then(i,o)}}function M(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpSender.prototype)return;const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){return this.track?this._pc.getStats(this.track):Promise.resolve(new Map)}}function L(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpReceiver.prototype)return;const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e}),d(e,"track",e=>(e.receiver._pc=e.srcElement,e)),e.RTCRtpReceiver.prototype.getStats=function(){return this._pc.getStats(this.track)}}function H(e){e.RTCPeerConnection&&!("removeStream"in e.RTCPeerConnection.prototype)&&(e.RTCPeerConnection.prototype.removeStream=function(e){u("removeStream","removeTrack"),this.getSenders().forEach(t=>{t.track&&e.getTracks().includes(t.track)&&this.removeTrack(t)})})}function G(e){e.DataChannel&&!e.RTCDataChannel&&(e.RTCDataChannel=e.DataChannel)}function j(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.addTransceiver;t&&(e.RTCPeerConnection.prototype.addTransceiver=function(){this.setParametersPromises=[];let e=arguments[1]&&arguments[1].sendEncodings;void 0===e&&(e=[]),e=[...e];const n=e.length>0;n&&e.forEach(e=>{if("rid"in e){if(!/^[a-z0-9]{0,16}$/i.test(e.rid))throw new TypeError("Invalid RID value provided.")}if("scaleResolutionDownBy"in e&&!(parseFloat(e.scaleResolutionDownBy)>=1))throw new RangeError("scale_resolution_down_by must be >= 1.0");if("maxFramerate"in e&&!(parseFloat(e.maxFramerate)>=0))throw new RangeError("max_framerate must be >= 0.0")});const r=t.apply(this,arguments);if(n){const{sender:t}=r,n=t.getParameters();(!("encodings"in n)||1===n.encodings.length&&0===Object.keys(n.encodings[0]).length)&&(n.encodings=e,t.sendEncodings=e,this.setParametersPromises.push(t.setParameters(n).then(()=>{delete t.sendEncodings}).catch(()=>{delete t.sendEncodings})))}return r})}function N(e){if("object"!=typeof e||!e.RTCRtpSender)return;const t=e.RTCRtpSender.prototype.getParameters;t&&(e.RTCRtpSender.prototype.getParameters=function(){const e=t.apply(this,arguments);return"encodings"in e||(e.encodings=[].concat(this.sendEncodings||[{}])),e})}function U(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}function F(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createAnswer;e.RTCPeerConnection.prototype.createAnswer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}function z(e){if("object"==typeof e&&e.RTCPeerConnection){if("getLocalStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),!("addStream"in e.RTCPeerConnection.prototype)){const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addStream=function(e){this._localStreams||(this._localStreams=[]),this._localStreams.includes(e)||this._localStreams.push(e),e.getAudioTracks().forEach(n=>t.call(this,n,e)),e.getVideoTracks().forEach(n=>t.call(this,n,e))},e.RTCPeerConnection.prototype.addTrack=function(e,...n){return n&&n.forEach(e=>{this._localStreams?this._localStreams.includes(e)||this._localStreams.push(e):this._localStreams=[e]}),t.apply(this,arguments)}}"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(e){this._localStreams||(this._localStreams=[]);const t=this._localStreams.indexOf(e);if(-1===t)return;this._localStreams.splice(t,1);const n=e.getTracks();this.getSenders().forEach(e=>{n.includes(e.track)&&this.removeTrack(e)})})}}function J(e){if("object"==typeof e&&e.RTCPeerConnection&&("getRemoteStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),!("onaddstream"in e.RTCPeerConnection.prototype))){Object.defineProperty(e.RTCPeerConnection.prototype,"onaddstream",{get(){return this._onaddstream},set(e){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=e),this.addEventListener("track",this._onaddstreampoly=e=>{e.streams.forEach(e=>{if(this._remoteStreams||(this._remoteStreams=[]),this._remoteStreams.includes(e))return;this._remoteStreams.push(e);const t=new Event("addstream");t.stream=e,this.dispatchEvent(t)})})}});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){const e=this;return this._onaddstreampoly||this.addEventListener("track",this._onaddstreampoly=function(t){t.streams.forEach(t=>{if(e._remoteStreams||(e._remoteStreams=[]),e._remoteStreams.indexOf(t)>=0)return;e._remoteStreams.push(t);const n=new Event("addstream");n.stream=t,e.dispatchEvent(n)})}),t.apply(e,arguments)}}}function q(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype,n=t.createOffer,r=t.createAnswer,i=t.setLocalDescription,o=t.setRemoteDescription,s=t.addIceCandidate;t.createOffer=function(e,t){const r=arguments.length>=2?arguments[2]:arguments[0],i=n.apply(this,[r]);return t?(i.then(e,t),Promise.resolve()):i},t.createAnswer=function(e,t){const n=arguments.length>=2?arguments[2]:arguments[0],i=r.apply(this,[n]);return t?(i.then(e,t),Promise.resolve()):i};let a=function(e,t,n){const r=i.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r};t.setLocalDescription=a,a=function(e,t,n){const r=o.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.setRemoteDescription=a,a=function(e,t,n){const r=s.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.addIceCandidate=a}function K(e){const t=e&&e.navigator;if(t.mediaDevices&&t.mediaDevices.getUserMedia){const e=t.mediaDevices,n=e.getUserMedia.bind(e);t.mediaDevices.getUserMedia=e=>n(W(e))}!t.getUserMedia&&t.mediaDevices&&t.mediaDevices.getUserMedia&&(t.getUserMedia=function(e,n,r){t.mediaDevices.getUserMedia(e).then(n,r)}.bind(t))}function W(e){return e&&void 0!==e.video?Object.assign({},e,{video:_(e.video)}):e}function Y(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection;e.RTCPeerConnection=function(e,n){if(e&&e.iceServers){const t=[];for(let n=0;nt.generateCertificate})}function V(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function B(e){const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(e){if(e){void 0!==e.offerToReceiveAudio&&(e.offerToReceiveAudio=!!e.offerToReceiveAudio);const t=this.getTransceivers().find(e=>"audio"===e.receiver.track.kind);!1===e.offerToReceiveAudio&&t?"sendrecv"===t.direction?t.setDirection?t.setDirection("sendonly"):t.direction="sendonly":"recvonly"===t.direction&&(t.setDirection?t.setDirection("inactive"):t.direction="inactive"):!0!==e.offerToReceiveAudio||t||this.addTransceiver("audio",{direction:"recvonly"}),void 0!==e.offerToReceiveVideo&&(e.offerToReceiveVideo=!!e.offerToReceiveVideo);const n=this.getTransceivers().find(e=>"video"===e.receiver.track.kind);!1===e.offerToReceiveVideo&&n?"sendrecv"===n.direction?n.setDirection?n.setDirection("sendonly"):n.direction="sendonly":"recvonly"===n.direction&&(n.setDirection?n.setDirection("inactive"):n.direction="inactive"):!0!==e.offerToReceiveVideo||n||this.addTransceiver("video",{direction:"recvonly"})}return t.apply(this,arguments)}}function Z(e){"object"!=typeof e||e.AudioContext||(e.AudioContext=e.webkitAudioContext)}var X=n(539),$=n.n(X);function Q(e){if(!e.RTCIceCandidate||e.RTCIceCandidate&&"foundation"in e.RTCIceCandidate.prototype)return;const t=e.RTCIceCandidate;e.RTCIceCandidate=function(e){if("object"==typeof e&&e.candidate&&0===e.candidate.indexOf("a=")&&((e=JSON.parse(JSON.stringify(e))).candidate=e.candidate.substring(2)),e.candidate&&e.candidate.length){const n=new t(e),r=$().parseCandidate(e.candidate);for(const e in r)e in n||Object.defineProperty(n,e,{value:r[e]});return n.toJSON=function(){return{candidate:n.candidate,sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex,usernameFragment:n.usernameFragment}},n}return new t(e)},e.RTCIceCandidate.prototype=t.prototype,d(e,"icecandidate",t=>(t.candidate&&Object.defineProperty(t,"candidate",{value:new e.RTCIceCandidate(t.candidate),writable:"false"}),t))}function ee(e){!e.RTCIceCandidate||e.RTCIceCandidate&&"relayProtocol"in e.RTCIceCandidate.prototype||d(e,"icecandidate",e=>{if(e.candidate){const t=$().parseCandidate(e.candidate.candidate);"relay"===t.type&&(e.candidate.relayProtocol={0:"tls",1:"tcp",2:"udp"}[t.priority>>24])}return e})}function te(e,t){if(!e.RTCPeerConnection)return;"sctp"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"sctp",{get(){return void 0===this._sctp?null:this._sctp}});const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){if(this._sctp=null,"chrome"===t.browser&&t.version>=76){const{sdpSemantics:e}=this.getConfiguration();"plan-b"===e&&Object.defineProperty(this,"sctp",{get(){return void 0===this._sctp?null:this._sctp},enumerable:!0,configurable:!0})}if(function(e){if(!e||!e.sdp)return!1;const t=$().splitSections(e.sdp);return t.shift(),t.some(e=>{const t=$().parseMLine(e);return t&&"application"===t.kind&&-1!==t.protocol.indexOf("SCTP")})}(arguments[0])){const e=function(e){const t=e.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);if(null===t||t.length<2)return-1;const n=parseInt(t[1],10);return n!=n?-1:n}(arguments[0]),n=function(e){let n=65536;return"firefox"===t.browser&&(n=t.version<57?-1===e?16384:2147483637:t.version<60?57===t.version?65535:65536:2147483637),n}(e),r=function(e,n){let r=65536;"firefox"===t.browser&&57===t.version&&(r=65535);const i=$().matchPrefix(e.sdp,"a=max-message-size:");return i.length>0?r=parseInt(i[0].substring(19),10):"firefox"===t.browser&&-1!==n&&(r=2147483637),r}(arguments[0],e);let i;i=0===n&&0===r?Number.POSITIVE_INFINITY:0===n||0===r?Math.max(n,r):Math.min(n,r);const o={};Object.defineProperty(o,"maxMessageSize",{get:()=>i}),this._sctp=o}return n.apply(this,arguments)}}function ne(e){if(!e.RTCPeerConnection||!("createDataChannel"in e.RTCPeerConnection.prototype))return;function t(e,t){const n=e.send;e.send=function(){const r=arguments[0],i=r.length||r.size||r.byteLength;if("open"===e.readyState&&t.sctp&&i>t.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+t.sctp.maxMessageSize+" bytes)");return n.apply(e,arguments)}}const n=e.RTCPeerConnection.prototype.createDataChannel;e.RTCPeerConnection.prototype.createDataChannel=function(){const e=n.apply(this,arguments);return t(e,this),e},d(e,"datachannel",e=>(t(e.channel,e.target),e))}function re(e){if(!e.RTCPeerConnection||"connectionState"in e.RTCPeerConnection.prototype)return;const t=e.RTCPeerConnection.prototype;Object.defineProperty(t,"connectionState",{get(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(t,"onconnectionstatechange",{get(){return this._onconnectionstatechange||null},set(e){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),e&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=e)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach(e=>{const n=t[e];t[e]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=e=>{const t=e.target;if(t._lastConnectionState!==t.connectionState){t._lastConnectionState=t.connectionState;const n=new Event("connectionstatechange",e);t.dispatchEvent(n)}return e},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),n.apply(this,arguments)}})}function ie(e,t){if(!e.RTCPeerConnection)return;if("chrome"===t.browser&&t.version>=71)return;if("safari"===t.browser&&t.version>=605)return;const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(t){if(t&&t.sdp&&-1!==t.sdp.indexOf("\na=extmap-allow-mixed")){const n=t.sdp.split("\n").filter(e=>"a=extmap-allow-mixed"!==e.trim()).join("\n");e.RTCSessionDescription&&t instanceof e.RTCSessionDescription?arguments[0]=new e.RTCSessionDescription({type:t.type,sdp:n}):t.sdp=n}return n.apply(this,arguments)}}function oe(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.addIceCandidate;n&&0!==n.length&&(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?("chrome"===t.browser&&t.version<78||"firefox"===t.browser&&t.version<68||"safari"===t.browser)&&arguments[0]&&""===arguments[0].candidate?Promise.resolve():n.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function se(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.setLocalDescription;n&&0!==n.length&&(e.RTCPeerConnection.prototype.setLocalDescription=function(){let e=arguments[0]||{};if("object"!=typeof e||e.type&&e.sdp)return n.apply(this,arguments);if(e={type:e.type,sdp:e.sdp},!e.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":e.type="offer";break;default:e.type="answer"}if(e.sdp||"offer"!==e.type&&"answer"!==e.type)return n.apply(this,[e]);return("offer"===e.type?this.createOffer:this.createAnswer).apply(this).then(e=>n.apply(this,[e]))})}!function({window:n}={},r={shimChrome:!0,shimFirefox:!0,shimSafari:!0}){const s=p,a=function(e){const t={browser:null,version:null};if(void 0===e||!e.navigator||!e.navigator.userAgent)return t.browser="Not a browser.",t;const{navigator:n}=e;if(n.mozGetUserMedia)t.browser="firefox",t.version=c(n.userAgent,/Firefox\/(\d+)\./,1);else if(n.webkitGetUserMedia||!1===e.isSecureContext&&e.webkitRTCPeerConnection)t.browser="chrome",t.version=c(n.userAgent,/Chrom(e|ium)\/(\d+)\./,2);else{if(!e.RTCPeerConnection||!n.userAgent.match(/AppleWebKit\/(\d+)\./))return t.browser="Not a supported browser.",t;t.browser="safari",t.version=c(n.userAgent,/AppleWebKit\/(\d+)\./,1),t.supportsUnifiedPlan=e.RTCRtpTransceiver&&"currentDirection"in e.RTCRtpTransceiver.prototype}return t}(n),d={browserDetails:a,commonShim:o,extractVersion:c,disableLog:l,disableWarnings:h,sdp:X};switch(a.browser){case"chrome":if(!e||!w||!r.shimChrome)return s("Chrome shim is not included in this adapter release."),d;if(null===a.version)return s("Chrome shim can not determine version, not shimming."),d;s("adapter.js shimming chrome."),d.browserShim=e,oe(n,a),se(n),y(n,a),b(n),w(n,a),S(n),R(n,a),k(n),T(n),E(n),A(n,a),Q(n),ee(n),re(n),te(n,a),ne(n),ie(n,a);break;case"firefox":if(!t||!D||!r.shimFirefox)return s("Firefox shim is not included in this adapter release."),d;s("adapter.js shimming firefox."),d.browserShim=t,oe(n,a),se(n),I(n,a),D(n,a),O(n),H(n),M(n),L(n),G(n),j(n),N(n),U(n),F(n),Q(n),re(n),te(n,a),ne(n);break;case"safari":if(!i||!r.shimSafari)return s("Safari shim is not included in this adapter release."),d;s("adapter.js shimming safari."),d.browserShim=i,oe(n,a),se(n),Y(n),B(n),q(n),z(n),J(n),V(n),K(n),Z(n),Q(n),ee(n),te(n,a),ne(n),ie(n,a);break;default:s("Unsupported browser!")}}({window:"undefined"==typeof window?void 0:window});const ae=Object.freeze({meta:null,signalingServerUrl:"ws://127.0.0.1:8443",reconnectionTimeout:2500,webrtcConfig:{iceServers:[{urls:["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302"]}],bundlePolicy:"max-bundle"}}),ce={idle:0,connecting:1,streaming:2,closed:3};Object.freeze(ce);const de=ce;class le extends EventTarget{constructor(e,t){super(),this._peerId=e,this._sessionId="",this._comChannel=t,this._state=de.idle,this._rtcPeerConnection=null}get peerId(){return this._peerId}get sessionId(){return this._sessionId}get state(){return this._state}get rtcPeerConnection(){return this._rtcPeerConnection}close(){this._state!==de.closed&&(this._state!==de.idle&&this._comChannel&&this._sessionId&&this._comChannel.send({type:"endSession",sessionId:this._sessionId}),this._state=de.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._rtcPeerConnection&&(this._rtcPeerConnection.close(),this._rtcPeerConnection=null,this.dispatchEvent(new Event("rtcPeerConnectionChanged"))),this.dispatchEvent(new Event("closed")))}}const he=le,pe=Object.freeze({32:"space",33:"exclam",34:"quotedbl",35:"numbersign",36:"dollar",37:"percent",38:"ampersand",39:"apostrophe",40:"parenleft",41:"parenright",42:"asterisk",43:"plus",44:"comma",45:"minus",46:"period",47:"slash",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",58:"colon",59:"semicolon",60:"less",61:"equal",62:"greater",63:"question",64:"at",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",91:"bracketleft",92:"backslash",93:"bracketright",94:"asciicircum",95:"underscore",96:"grave",97:"a",98:"b",99:"c",100:"d",101:"e",102:"f",103:"g",104:"h",105:"i",106:"j",107:"k",108:"l",109:"m",110:"n",111:"o",112:"p",113:"q",114:"r",115:"s",116:"t",117:"u",118:"v",119:"w",120:"x",121:"y",122:"z",123:"braceleft",124:"bar",125:"braceright",126:"asciitilde",160:"nobreakspace",161:"exclamdown",162:"cent",163:"sterling",164:"currency",165:"yen",166:"brokenbar",167:"section",168:"diaeresis",169:"copyright",170:"ordfeminine",171:"guillemotleft",172:"notsign",173:"hyphen",174:"registered",175:"macron",176:"degree",177:"plusminus",178:"twosuperior",179:"threesuperior",180:"acute",181:"mu",182:"paragraph",183:"periodcentered",184:"cedilla",185:"onesuperior",186:"masculine",187:"guillemotright",188:"onequarter",189:"onehalf",190:"threequarters",191:"questiondown",192:"Agrave",193:"Aacute",194:"Acircumflex",195:"Atilde",196:"Adiaeresis",197:"Aring",198:"AE",199:"Ccedilla",200:"Egrave",201:"Eacute",202:"Ecircumflex",203:"Ediaeresis",204:"Igrave",205:"Iacute",206:"Icircumflex",207:"Idiaeresis",208:"ETH",209:"Ntilde",210:"Ograve",211:"Oacute",212:"Ocircumflex",213:"Otilde",214:"Odiaeresis",215:"multiply",216:"Ooblique",217:"Ugrave",218:"Uacute",219:"Ucircumflex",220:"Udiaeresis",221:"Yacute",222:"THORN",223:"ssharp",224:"agrave",225:"aacute",226:"acircumflex",227:"atilde",228:"adiaeresis",229:"aring",230:"ae",231:"ccedilla",232:"egrave",233:"eacute",234:"ecircumflex",235:"ediaeresis",236:"igrave",237:"iacute",238:"icircumflex",239:"idiaeresis",240:"eth",241:"ntilde",242:"ograve",243:"oacute",244:"ocircumflex",245:"otilde",246:"odiaeresis",247:"division",248:"oslash",249:"ugrave",250:"uacute",251:"ucircumflex",252:"udiaeresis",253:"yacute",254:"thorn",255:"ydiaeresis",260:"Aogonek",728:"breve",321:"Lstroke",317:"Lcaron",346:"Sacute",352:"Scaron",350:"Scedilla",356:"Tcaron",377:"Zacute",381:"Zcaron",379:"Zabovedot",261:"aogonek",731:"ogonek",322:"lstroke",318:"lcaron",347:"sacute",711:"caron",353:"scaron",351:"scedilla",357:"tcaron",378:"zacute",733:"doubleacute",382:"zcaron",380:"zabovedot",340:"Racute",258:"Abreve",313:"Lacute",262:"Cacute",268:"Ccaron",280:"Eogonek",282:"Ecaron",270:"Dcaron",272:"Dstroke",323:"Nacute",327:"Ncaron",336:"Odoubleacute",344:"Rcaron",366:"Uring",368:"Udoubleacute",354:"Tcedilla",341:"racute",259:"abreve",314:"lacute",263:"cacute",269:"ccaron",281:"eogonek",283:"ecaron",271:"dcaron",273:"dstroke",324:"nacute",328:"ncaron",337:"odoubleacute",345:"rcaron",367:"uring",369:"udoubleacute",355:"tcedilla",729:"abovedot",294:"Hstroke",292:"Hcircumflex",304:"Iabovedot",286:"Gbreve",308:"Jcircumflex",295:"hstroke",293:"hcircumflex",305:"idotless",287:"gbreve",309:"jcircumflex",266:"Cabovedot",264:"Ccircumflex",288:"Gabovedot",284:"Gcircumflex",364:"Ubreve",348:"Scircumflex",267:"cabovedot",265:"ccircumflex",289:"gabovedot",285:"gcircumflex",365:"ubreve",349:"scircumflex",312:"kra",342:"Rcedilla",296:"Itilde",315:"Lcedilla",274:"Emacron",290:"Gcedilla",358:"Tslash",343:"rcedilla",297:"itilde",316:"lcedilla",275:"emacron",291:"gcedilla",359:"tslash",330:"ENG",331:"eng",256:"Amacron",302:"Iogonek",278:"Eabovedot",298:"Imacron",325:"Ncedilla",332:"Omacron",310:"Kcedilla",370:"Uogonek",360:"Utilde",362:"Umacron",257:"amacron",303:"iogonek",279:"eabovedot",299:"imacron",326:"ncedilla",333:"omacron",311:"kcedilla",371:"uogonek",361:"utilde",363:"umacron",8254:"overline",12290:"kana_fullstop",12300:"kana_openingbracket",12301:"kana_closingbracket",12289:"kana_comma",12539:"kana_conjunctive",12530:"kana_WO",12449:"kana_a",12451:"kana_i",12453:"kana_u",12455:"kana_e",12457:"kana_o",12515:"kana_ya",12517:"kana_yu",12519:"kana_yo",12483:"kana_tsu",12540:"prolongedsound",12450:"kana_A",12452:"kana_I",12454:"kana_U",12456:"kana_E",12458:"kana_O",12459:"kana_KA",12461:"kana_KI",12463:"kana_KU",12465:"kana_KE",12467:"kana_KO",12469:"kana_SA",12471:"kana_SHI",12473:"kana_SU",12475:"kana_SE",12477:"kana_SO",12479:"kana_TA",12481:"kana_CHI",12484:"kana_TSU",12486:"kana_TE",12488:"kana_TO",12490:"kana_NA",12491:"kana_NI",12492:"kana_NU",12493:"kana_NE",12494:"kana_NO",12495:"kana_HA",12498:"kana_HI",12501:"kana_FU",12504:"kana_HE",12507:"kana_HO",12510:"kana_MA",12511:"kana_MI",12512:"kana_MU",12513:"kana_ME",12514:"kana_MO",12516:"kana_YA",12518:"kana_YU",12520:"kana_YO",12521:"kana_RA",12522:"kana_RI",12523:"kana_RU",12524:"kana_RE",12525:"kana_RO",12527:"kana_WA",12531:"kana_N",12443:"voicedsound",12444:"semivoicedsound",1548:"Arabic_comma",1563:"Arabic_semicolon",1567:"Arabic_question_mark",1569:"Arabic_hamza",1570:"Arabic_maddaonalef",1571:"Arabic_hamzaonalef",1572:"Arabic_hamzaonwaw",1573:"Arabic_hamzaunderalef",1574:"Arabic_hamzaonyeh",1575:"Arabic_alef",1576:"Arabic_beh",1577:"Arabic_tehmarbuta",1578:"Arabic_teh",1579:"Arabic_theh",1580:"Arabic_jeem",1581:"Arabic_hah",1582:"Arabic_khah",1583:"Arabic_dal",1584:"Arabic_thal",1585:"Arabic_ra",1586:"Arabic_zain",1587:"Arabic_seen",1588:"Arabic_sheen",1589:"Arabic_sad",1590:"Arabic_dad",1591:"Arabic_tah",1592:"Arabic_zah",1593:"Arabic_ain",1594:"Arabic_ghain",1600:"Arabic_tatweel",1601:"Arabic_feh",1602:"Arabic_qaf",1603:"Arabic_kaf",1604:"Arabic_lam",1605:"Arabic_meem",1606:"Arabic_noon",1607:"Arabic_ha",1608:"Arabic_waw",1609:"Arabic_alefmaksura",1610:"Arabic_yeh",1611:"Arabic_fathatan",1612:"Arabic_dammatan",1613:"Arabic_kasratan",1614:"Arabic_fatha",1615:"Arabic_damma",1616:"Arabic_kasra",1617:"Arabic_shadda",1618:"Arabic_sukun",1106:"Serbian_dje",1107:"Macedonia_gje",1105:"Cyrillic_io",1108:"Ukrainian_ie",1109:"Macedonia_dse",1110:"Ukrainian_i",1111:"Ukrainian_yi",1112:"Cyrillic_je",1113:"Cyrillic_lje",1114:"Cyrillic_nje",1115:"Serbian_tshe",1116:"Macedonia_kje",1118:"Byelorussian_shortu",1119:"Cyrillic_dzhe",8470:"numerosign",1026:"Serbian_DJE",1027:"Macedonia_GJE",1025:"Cyrillic_IO",1028:"Ukrainian_IE",1029:"Macedonia_DSE",1030:"Ukrainian_I",1031:"Ukrainian_YI",1032:"Cyrillic_JE",1033:"Cyrillic_LJE",1034:"Cyrillic_NJE",1035:"Serbian_TSHE",1036:"Macedonia_KJE",1038:"Byelorussian_SHORTU",1039:"Cyrillic_DZHE",1102:"Cyrillic_yu",1072:"Cyrillic_a",1073:"Cyrillic_be",1094:"Cyrillic_tse",1076:"Cyrillic_de",1077:"Cyrillic_ie",1092:"Cyrillic_ef",1075:"Cyrillic_ghe",1093:"Cyrillic_ha",1080:"Cyrillic_i",1081:"Cyrillic_shorti",1082:"Cyrillic_ka",1083:"Cyrillic_el",1084:"Cyrillic_em",1085:"Cyrillic_en",1086:"Cyrillic_o",1087:"Cyrillic_pe",1103:"Cyrillic_ya",1088:"Cyrillic_er",1089:"Cyrillic_es",1090:"Cyrillic_te",1091:"Cyrillic_u",1078:"Cyrillic_zhe",1074:"Cyrillic_ve",1100:"Cyrillic_softsign",1099:"Cyrillic_yeru",1079:"Cyrillic_ze",1096:"Cyrillic_sha",1101:"Cyrillic_e",1097:"Cyrillic_shcha",1095:"Cyrillic_che",1098:"Cyrillic_hardsign",1070:"Cyrillic_YU",1040:"Cyrillic_A",1041:"Cyrillic_BE",1062:"Cyrillic_TSE",1044:"Cyrillic_DE",1045:"Cyrillic_IE",1060:"Cyrillic_EF",1043:"Cyrillic_GHE",1061:"Cyrillic_HA",1048:"Cyrillic_I",1049:"Cyrillic_SHORTI",1050:"Cyrillic_KA",1051:"Cyrillic_EL",1052:"Cyrillic_EM",1053:"Cyrillic_EN",1054:"Cyrillic_O",1055:"Cyrillic_PE",1071:"Cyrillic_YA",1056:"Cyrillic_ER",1057:"Cyrillic_ES",1058:"Cyrillic_TE",1059:"Cyrillic_U",1046:"Cyrillic_ZHE",1042:"Cyrillic_VE",1068:"Cyrillic_SOFTSIGN",1067:"Cyrillic_YERU",1047:"Cyrillic_ZE",1064:"Cyrillic_SHA",1069:"Cyrillic_E",1065:"Cyrillic_SHCHA",1063:"Cyrillic_CHE",1066:"Cyrillic_HARDSIGN",902:"Greek_ALPHAaccent",904:"Greek_EPSILONaccent",905:"Greek_ETAaccent",906:"Greek_IOTAaccent",938:"Greek_IOTAdiaeresis",908:"Greek_OMICRONaccent",910:"Greek_UPSILONaccent",939:"Greek_UPSILONdieresis",911:"Greek_OMEGAaccent",901:"Greek_accentdieresis",8213:"Greek_horizbar",940:"Greek_alphaaccent",941:"Greek_epsilonaccent",942:"Greek_etaaccent",943:"Greek_iotaaccent",970:"Greek_iotadieresis",912:"Greek_iotaaccentdieresis",972:"Greek_omicronaccent",973:"Greek_upsilonaccent",971:"Greek_upsilondieresis",944:"Greek_upsilonaccentdieresis",974:"Greek_omegaaccent",913:"Greek_ALPHA",914:"Greek_BETA",915:"Greek_GAMMA",916:"Greek_DELTA",917:"Greek_EPSILON",918:"Greek_ZETA",919:"Greek_ETA",920:"Greek_THETA",921:"Greek_IOTA",922:"Greek_KAPPA",923:"Greek_LAMBDA",924:"Greek_MU",925:"Greek_NU",926:"Greek_XI",927:"Greek_OMICRON",928:"Greek_PI",929:"Greek_RHO",931:"Greek_SIGMA",932:"Greek_TAU",933:"Greek_UPSILON",934:"Greek_PHI",935:"Greek_CHI",936:"Greek_PSI",937:"Greek_OMEGA",945:"Greek_alpha",946:"Greek_beta",947:"Greek_gamma",948:"Greek_delta",949:"Greek_epsilon",950:"Greek_zeta",951:"Greek_eta",952:"Greek_theta",953:"Greek_iota",954:"Greek_kappa",955:"Greek_lambda",956:"Greek_mu",957:"Greek_nu",958:"Greek_xi",959:"Greek_omicron",960:"Greek_pi",961:"Greek_rho",963:"Greek_sigma",962:"Greek_finalsmallsigma",964:"Greek_tau",965:"Greek_upsilon",966:"Greek_phi",967:"Greek_chi",968:"Greek_psi",969:"Greek_omega",9143:"leftradical",8992:"topintegral",8993:"botintegral",9121:"topleftsqbracket",9123:"botleftsqbracket",9124:"toprightsqbracket",9126:"botrightsqbracket",9115:"topleftparens",9117:"botleftparens",9118:"toprightparens",9120:"botrightparens",9128:"leftmiddlecurlybrace",9132:"rightmiddlecurlybrace",8804:"lessthanequal",8800:"notequal",8805:"greaterthanequal",8747:"integral",8756:"therefore",8733:"variation",8734:"infinity",8711:"nabla",8764:"approximate",8771:"similarequal",8660:"ifonlyif",8658:"implies",8801:"identical",8730:"radical",8834:"includedin",8835:"includes",8745:"intersection",8746:"union",8743:"logicaland",8744:"logicalor",8706:"partialderivative",402:"function",8592:"leftarrow",8593:"uparrow",8594:"rightarrow",8595:"downarrow",9670:"soliddiamond",9618:"checkerboard",9225:"ht",9228:"ff",9229:"cr",9226:"lf",9252:"nl",9227:"vt",9496:"lowrightcorner",9488:"uprightcorner",9484:"upleftcorner",9492:"lowleftcorner",9532:"crossinglines",9146:"horizlinescan1",9147:"horizlinescan3",9472:"horizlinescan5",9148:"horizlinescan7",9149:"horizlinescan9",9500:"leftt",9508:"rightt",9524:"bott",9516:"topt",9474:"vertbar",8195:"emspace",8194:"enspace",8196:"em3space",8197:"em4space",8199:"digitspace",8200:"punctspace",8201:"thinspace",8202:"hairspace",8212:"emdash",8211:"endash",9251:"signifblank",8230:"ellipsis",8229:"doubbaselinedot",8531:"onethird",8532:"twothirds",8533:"onefifth",8534:"twofifths",8535:"threefifths",8536:"fourfifths",8537:"onesixth",8538:"fivesixths",8453:"careof",8210:"figdash",10216:"leftanglebracket",10217:"rightanglebracket",8539:"oneeighth",8540:"threeeighths",8541:"fiveeighths",8542:"seveneighths",8482:"trademark",9747:"signaturemark",9665:"leftopentriangle",9655:"rightopentriangle",9647:"emopenrectangle",8216:"leftsinglequotemark",8217:"rightsinglequotemark",8220:"leftdoublequotemark",8221:"rightdoublequotemark",8478:"prescription",8242:"minutes",8243:"seconds",10013:"latincross",9644:"filledrectbullet",9664:"filledlefttribullet",9654:"filledrighttribullet",9679:"emfilledcircle",9646:"emfilledrect",9702:"enopencircbullet",9643:"enopensquarebullet",9645:"openrectbullet",9651:"opentribulletup",9661:"opentribulletdown",9734:"openstar",8226:"enfilledcircbullet",9642:"enfilledsqbullet",9650:"filledtribulletup",9660:"filledtribulletdown",9756:"leftpointer",9758:"rightpointer",9827:"club",9830:"diamond",9829:"heart",10016:"maltesecross",8224:"dagger",8225:"doubledagger",10003:"checkmark",10007:"ballotcross",9839:"musicalsharp",9837:"musicalflat",9794:"malesymbol",9792:"femalesymbol",9742:"telephone",8981:"telephonerecorder",8471:"phonographcopyright",8248:"caret",8218:"singlelowquotemark",8222:"doublelowquotemark",8869:"downtack",8970:"downstile",8728:"jot",9109:"quad",8868:"uptack",9675:"circle",8968:"upstile",8866:"lefttack",8867:"righttack",8215:"hebrew_doublelowline",1488:"hebrew_aleph",1489:"hebrew_beth",1490:"hebrew_gimmel",1491:"hebrew_daleth",1492:"hebrew_he",1493:"hebrew_waw",1494:"hebrew_zayin",1495:"hebrew_het",1496:"hebrew_teth",1497:"hebrew_yod",1498:"hebrew_finalkaph",1499:"hebrew_kaph",1500:"hebrew_lamed",1501:"hebrew_finalmem",1502:"hebrew_mem",1503:"hebrew_finalnun",1504:"hebrew_nun",1505:"hebrew_samekh",1506:"hebrew_ayin",1507:"hebrew_finalpe",1508:"hebrew_pe",1509:"hebrew_finalzadi",1510:"hebrew_zadi",1511:"hebrew_qoph",1512:"hebrew_resh",1513:"hebrew_shin",1514:"hebrew_taw",3585:"Thai_kokai",3586:"Thai_khokhai",3587:"Thai_khokhuat",3588:"Thai_khokhwai",3589:"Thai_khokhon",3590:"Thai_khorakhang",3591:"Thai_ngongu",3592:"Thai_chochan",3593:"Thai_choching",3594:"Thai_chochang",3595:"Thai_soso",3596:"Thai_chochoe",3597:"Thai_yoying",3598:"Thai_dochada",3599:"Thai_topatak",3600:"Thai_thothan",3601:"Thai_thonangmontho",3602:"Thai_thophuthao",3603:"Thai_nonen",3604:"Thai_dodek",3605:"Thai_totao",3606:"Thai_thothung",3607:"Thai_thothahan",3608:"Thai_thothong",3609:"Thai_nonu",3610:"Thai_bobaimai",3611:"Thai_popla",3612:"Thai_phophung",3613:"Thai_fofa",3614:"Thai_phophan",3615:"Thai_fofan",3616:"Thai_phosamphao",3617:"Thai_moma",3618:"Thai_yoyak",3619:"Thai_rorua",3620:"Thai_ru",3621:"Thai_loling",3622:"Thai_lu",3623:"Thai_wowaen",3624:"Thai_sosala",3625:"Thai_sorusi",3626:"Thai_sosua",3627:"Thai_hohip",3628:"Thai_lochula",3629:"Thai_oang",3630:"Thai_honokhuk",3631:"Thai_paiyannoi",3632:"Thai_saraa",3633:"Thai_maihanakat",3634:"Thai_saraaa",3635:"Thai_saraam",3636:"Thai_sarai",3637:"Thai_saraii",3638:"Thai_saraue",3639:"Thai_sarauee",3640:"Thai_sarau",3641:"Thai_sarauu",3642:"Thai_phinthu",3647:"Thai_baht",3648:"Thai_sarae",3649:"Thai_saraae",3650:"Thai_sarao",3651:"Thai_saraaimaimuan",3652:"Thai_saraaimaimalai",3653:"Thai_lakkhangyao",3654:"Thai_maiyamok",3655:"Thai_maitaikhu",3656:"Thai_maiek",3657:"Thai_maitho",3658:"Thai_maitri",3659:"Thai_maichattawa",3660:"Thai_thanthakhat",3661:"Thai_nikhahit",3664:"Thai_leksun",3665:"Thai_leknung",3666:"Thai_leksong",3667:"Thai_leksam",3668:"Thai_leksi",3669:"Thai_lekha",3670:"Thai_lekhok",3671:"Thai_lekchet",3672:"Thai_lekpaet",3673:"Thai_lekkao",12593:"Hangul_Kiyeog",12594:"Hangul_SsangKiyeog",12595:"Hangul_KiyeogSios",12596:"Hangul_Nieun",12597:"Hangul_NieunJieuj",12598:"Hangul_NieunHieuh",12599:"Hangul_Dikeud",12600:"Hangul_SsangDikeud",12601:"Hangul_Rieul",12602:"Hangul_RieulKiyeog",12603:"Hangul_RieulMieum",12604:"Hangul_RieulPieub",12605:"Hangul_RieulSios",12606:"Hangul_RieulTieut",12607:"Hangul_RieulPhieuf",12608:"Hangul_RieulHieuh",12609:"Hangul_Mieum",12610:"Hangul_Pieub",12611:"Hangul_SsangPieub",12612:"Hangul_PieubSios",12613:"Hangul_Sios",12614:"Hangul_SsangSios",12615:"Hangul_Ieung",12616:"Hangul_Jieuj",12617:"Hangul_SsangJieuj",12618:"Hangul_Cieuc",12619:"Hangul_Khieuq",12620:"Hangul_Tieut",12621:"Hangul_Phieuf",12622:"Hangul_Hieuh",12623:"Hangul_A",12624:"Hangul_AE",12625:"Hangul_YA",12626:"Hangul_YAE",12627:"Hangul_EO",12628:"Hangul_E",12629:"Hangul_YEO",12630:"Hangul_YE",12631:"Hangul_O",12632:"Hangul_WA",12633:"Hangul_WAE",12634:"Hangul_OE",12635:"Hangul_YO",12636:"Hangul_U",12637:"Hangul_WEO",12638:"Hangul_WE",12639:"Hangul_WI",12640:"Hangul_YU",12641:"Hangul_EU",12642:"Hangul_YI",12643:"Hangul_I",4520:"Hangul_J_Kiyeog",4521:"Hangul_J_SsangKiyeog",4522:"Hangul_J_KiyeogSios",4523:"Hangul_J_Nieun",4524:"Hangul_J_NieunJieuj",4525:"Hangul_J_NieunHieuh",4526:"Hangul_J_Dikeud",4527:"Hangul_J_Rieul",4528:"Hangul_J_RieulKiyeog",4529:"Hangul_J_RieulMieum",4530:"Hangul_J_RieulPieub",4531:"Hangul_J_RieulSios",4532:"Hangul_J_RieulTieut",4533:"Hangul_J_RieulPhieuf",4534:"Hangul_J_RieulHieuh",4535:"Hangul_J_Mieum",4536:"Hangul_J_Pieub",4537:"Hangul_J_PieubSios",4538:"Hangul_J_Sios",4539:"Hangul_J_SsangSios",4540:"Hangul_J_Ieung",4541:"Hangul_J_Jieuj",4542:"Hangul_J_Cieuc",4543:"Hangul_J_Khieuq",4544:"Hangul_J_Tieut",4545:"Hangul_J_Phieuf",4546:"Hangul_J_Hieuh",12653:"Hangul_RieulYeorinHieuh",12657:"Hangul_SunkyeongeumMieum",12664:"Hangul_SunkyeongeumPieub",12671:"Hangul_PanSios",12673:"Hangul_KkogjiDalrinIeung",12676:"Hangul_SunkyeongeumPhieuf",12678:"Hangul_YeorinHieuh",12685:"Hangul_AraeA",12686:"Hangul_AraeAE",4587:"Hangul_J_PanSios",4592:"Hangul_J_KkogjiDalrinIeung",4601:"Hangul_J_YeorinHieuh",338:"OE",339:"oe",376:"Ydiaeresis",8352:"EcuSign",8353:"ColonSign",8354:"CruzeiroSign",8355:"FFrancSign",8356:"LiraSign",8357:"MillSign",8358:"NairaSign",8359:"PesetaSign",8360:"RupeeSign",8361:"WonSign",8362:"NewSheqelSign",8363:"DongSign",8364:"EuroSign",768:"dead_grave",769:"dead_acute",770:"dead_circumflex",771:"dead_tilde",772:"dead_macron",774:"dead_breve",775:"dead_abovedot",776:"dead_diaeresis",778:"dead_abovering",779:"dead_doubleacute",780:"dead_caron",807:"dead_cedilla",808:"dead_ogonek",837:"dead_iota",12441:"dead_voiced_sound",12442:"dead_semivoiced_sound",8:"BackSpace",9:"Tab",10:"Linefeed",11:"Clear",13:"Return",19:"Pause",20:"Scroll_Lock",21:"Sys_Req",27:"Escape",1169:"Ukrainian_ghe_with_upturn",1168:"Ukrainian_GHE_WITH_UPTURN",1415:"Armenian_ligature_ew",1417:"Armenian_verjaket",1373:"Armenian_but",1418:"Armenian_yentamna",1372:"Armenian_amanak",1371:"Armenian_shesht",1374:"Armenian_paruyk",1329:"Armenian_AYB",1377:"Armenian_ayb",1330:"Armenian_BEN",1378:"Armenian_ben",1331:"Armenian_GIM",1379:"Armenian_gim",1332:"Armenian_DA",1380:"Armenian_da",1333:"Armenian_YECH",1381:"Armenian_yech",1334:"Armenian_ZA",1382:"Armenian_za",1335:"Armenian_E",1383:"Armenian_e",1336:"Armenian_AT",1384:"Armenian_at",1337:"Armenian_TO",1385:"Armenian_to",1338:"Armenian_ZHE",1386:"Armenian_zhe",1339:"Armenian_INI",1387:"Armenian_ini",1340:"Armenian_LYUN",1388:"Armenian_lyun",1341:"Armenian_KHE",1389:"Armenian_khe",1342:"Armenian_TSA",1390:"Armenian_tsa",1343:"Armenian_KEN",1391:"Armenian_ken",1344:"Armenian_HO",1392:"Armenian_ho",1345:"Armenian_DZA",1393:"Armenian_dza",1346:"Armenian_GHAT",1394:"Armenian_ghat",1347:"Armenian_TCHE",1395:"Armenian_tche",1348:"Armenian_MEN",1396:"Armenian_men",1349:"Armenian_HI",1397:"Armenian_hi",1350:"Armenian_NU",1398:"Armenian_nu",1351:"Armenian_SHA",1399:"Armenian_sha",1352:"Armenian_VO",1400:"Armenian_vo",1353:"Armenian_CHA",1401:"Armenian_cha",1354:"Armenian_PE",1402:"Armenian_pe",1355:"Armenian_JE",1403:"Armenian_je",1356:"Armenian_RA",1404:"Armenian_ra",1357:"Armenian_SE",1405:"Armenian_se",1358:"Armenian_VEV",1406:"Armenian_vev",1359:"Armenian_TYUN",1407:"Armenian_tyun",1360:"Armenian_RE",1408:"Armenian_re",1361:"Armenian_TSO",1409:"Armenian_tso",1362:"Armenian_VYUN",1410:"Armenian_vyun",1363:"Armenian_PYUR",1411:"Armenian_pyur",1364:"Armenian_KE",1412:"Armenian_ke",1365:"Armenian_O",1413:"Armenian_o",1366:"Armenian_FE",1414:"Armenian_fe",1370:"Armenian_apostrophe",4304:"Georgian_an",4305:"Georgian_ban",4306:"Georgian_gan",4307:"Georgian_don",4308:"Georgian_en",4309:"Georgian_vin",4310:"Georgian_zen",4311:"Georgian_tan",4312:"Georgian_in",4313:"Georgian_kan",4314:"Georgian_las",4315:"Georgian_man",4316:"Georgian_nar",4317:"Georgian_on",4318:"Georgian_par",4319:"Georgian_zhar",4320:"Georgian_rae",4321:"Georgian_san",4322:"Georgian_tar",4323:"Georgian_un",4324:"Georgian_phar",4325:"Georgian_khar",4326:"Georgian_ghan",4327:"Georgian_qar",4328:"Georgian_shin",4329:"Georgian_chin",4330:"Georgian_can",4331:"Georgian_jil",4332:"Georgian_cil",4333:"Georgian_char",4334:"Georgian_xan",4335:"Georgian_jhan",4336:"Georgian_hae",4337:"Georgian_he",4338:"Georgian_hie",4339:"Georgian_we",4340:"Georgian_har",4341:"Georgian_hoe",4342:"Georgian_fi",7682:"Babovedot",7683:"babovedot",7690:"Dabovedot",7808:"Wgrave",7810:"Wacute",7691:"dabovedot",7922:"Ygrave",7710:"Fabovedot",7711:"fabovedot",7744:"Mabovedot",7745:"mabovedot",7766:"Pabovedot",7809:"wgrave",7767:"pabovedot",7811:"wacute",7776:"Sabovedot",7923:"ygrave",7812:"Wdiaeresis",7813:"wdiaeresis",7777:"sabovedot",372:"Wcircumflex",7786:"Tabovedot",374:"Ycircumflex",373:"wcircumflex",7787:"tabovedot",375:"ycircumflex",1776:"Farsi_0",1777:"Farsi_1",1778:"Farsi_2",1779:"Farsi_3",1780:"Farsi_4",1781:"Farsi_5",1782:"Farsi_6",1783:"Farsi_7",1784:"Farsi_8",1785:"Farsi_9",1642:"Arabic_percent",1648:"Arabic_superscript_alef",1657:"Arabic_tteh",1662:"Arabic_peh",1670:"Arabic_tcheh",1672:"Arabic_ddal",1681:"Arabic_rreh",1748:"Arabic_fullstop",1632:"Arabic_0",1633:"Arabic_1",1634:"Arabic_2",1635:"Arabic_3",1636:"Arabic_4",1637:"Arabic_5",1638:"Arabic_6",1639:"Arabic_7",1640:"Arabic_8",1641:"Arabic_9",1619:"Arabic_madda_above",1620:"Arabic_hamza_above",1621:"Arabic_hamza_below",1688:"Arabic_jeh",1700:"Arabic_veh",1705:"Arabic_keheh",1711:"Arabic_gaf",1722:"Arabic_noon_ghunna",1726:"Arabic_heh_doachashmee",1740:"Farsi_yeh",1746:"Arabic_yeh_baree",1729:"Arabic_heh_goal",1170:"Cyrillic_GHE_bar",1174:"Cyrillic_ZHE_descender",1178:"Cyrillic_KA_descender",1180:"Cyrillic_KA_vertstroke",1186:"Cyrillic_EN_descender",1198:"Cyrillic_U_straight",1200:"Cyrillic_U_straight_bar",1202:"Cyrillic_HA_descender",1206:"Cyrillic_CHE_descender",1208:"Cyrillic_CHE_vertstroke",1210:"Cyrillic_SHHA",1240:"Cyrillic_SCHWA",1250:"Cyrillic_I_macron",1256:"Cyrillic_O_bar",1262:"Cyrillic_U_macron",1171:"Cyrillic_ghe_bar",1175:"Cyrillic_zhe_descender",1179:"Cyrillic_ka_descender",1181:"Cyrillic_ka_vertstroke",1187:"Cyrillic_en_descender",1199:"Cyrillic_u_straight",1201:"Cyrillic_u_straight_bar",1203:"Cyrillic_ha_descender",1207:"Cyrillic_che_descender",1209:"Cyrillic_che_vertstroke",1211:"Cyrillic_shha",1241:"Cyrillic_schwa",1251:"Cyrillic_i_macron",1257:"Cyrillic_o_bar",1263:"Cyrillic_u_macron",7818:"Xabovedot",300:"Ibreve",437:"Zstroke",486:"Gcaron",415:"Obarred",7819:"xabovedot",301:"ibreve",438:"zstroke",487:"gcaron",466:"ocaron",629:"obarred",399:"SCHWA",601:"schwa",7734:"Lbelowdot",7735:"lbelowdot",7840:"Abelowdot",7841:"abelowdot",7842:"Ahook",7843:"ahook",7844:"Acircumflexacute",7845:"acircumflexacute",7846:"Acircumflexgrave",7847:"acircumflexgrave",7848:"Acircumflexhook",7849:"acircumflexhook",7850:"Acircumflextilde",7851:"acircumflextilde",7852:"Acircumflexbelowdot",7853:"acircumflexbelowdot",7854:"Abreveacute",7855:"abreveacute",7856:"Abrevegrave",7857:"abrevegrave",7858:"Abrevehook",7859:"abrevehook",7860:"Abrevetilde",7861:"abrevetilde",7862:"Abrevebelowdot",7863:"abrevebelowdot",7864:"Ebelowdot",7865:"ebelowdot",7866:"Ehook",7867:"ehook",7868:"Etilde",7869:"etilde",7870:"Ecircumflexacute",7871:"ecircumflexacute",7872:"Ecircumflexgrave",7873:"ecircumflexgrave",7874:"Ecircumflexhook",7875:"ecircumflexhook",7876:"Ecircumflextilde",7877:"ecircumflextilde",7878:"Ecircumflexbelowdot",7879:"ecircumflexbelowdot",7880:"Ihook",7881:"ihook",7882:"Ibelowdot",7883:"ibelowdot",7884:"Obelowdot",7885:"obelowdot",7886:"Ohook",7887:"ohook",7888:"Ocircumflexacute",7889:"ocircumflexacute",7890:"Ocircumflexgrave",7891:"ocircumflexgrave",7892:"Ocircumflexhook",7893:"ocircumflexhook",7894:"Ocircumflextilde",7895:"ocircumflextilde",7896:"Ocircumflexbelowdot",7897:"ocircumflexbelowdot",7898:"Ohornacute",7899:"ohornacute",7900:"Ohorngrave",7901:"ohorngrave",7902:"Ohornhook",7903:"ohornhook",7904:"Ohorntilde",7905:"ohorntilde",7906:"Ohornbelowdot",7907:"ohornbelowdot",7908:"Ubelowdot",7909:"ubelowdot",7910:"Uhook",7911:"uhook",7912:"Uhornacute",7913:"uhornacute",7914:"Uhorngrave",7915:"uhorngrave",7916:"Uhornhook",7917:"uhornhook",7918:"Uhorntilde",7919:"uhorntilde",7920:"Uhornbelowdot",7921:"uhornbelowdot",7924:"Ybelowdot",7925:"ybelowdot",7926:"Yhook",7927:"yhook",7928:"Ytilde",7929:"ytilde",416:"Ohorn",417:"ohorn",431:"Uhorn",432:"uhorn",803:"dead_belowdot",777:"dead_hook",795:"dead_horn"}),ue=Object.freeze({AltLeft:"Alt_L",AltRight:"Alt_R",ArrowDown:"Down",ArrowLeft:"Left",ArrowRight:"Right",ArrowUp:"Up",Backspace:"BackSpace",CapsLock:"Caps_Lock",ControlLeft:"Control_L",ControlRight:"Control_R",Enter:"Return",HyperLeft:"Hyper_L",HyperRight:"Hyper_R",NumLock:"Num_Lock",NumpadEnter:"Return",MetaLeft:"Meta_L",MetaRight:"Meta_R",PageDown:"Page_Down",PageUp:"Page_Up",ScrollLock:"Scroll_Lock",ShiftLeft:"Shift_L",ShiftRight:"Shift_R",SuperLeft:"Super_L",SuperRight:"Super_R"}),me=new Set(["Clear","Copy","Cut","Delete","End","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","Home","Insert","Paste","Redo","Tab","Undo"]);function _e(e,t){var n="Unidentified";if(1===e.length){const t=e.charCodeAt(0);t in pe&&(n=pe[t])}else t in ue?n=ue[t]:me.has(t)&&(n=t);return n}const fe=Object.freeze(["wheel","contextmenu","mousemove","mousedown","mouseup","touchstart","touchend","touchmove","touchcancel","keyup","keydown"]),ge=Object.freeze({mousemove:"MouseMove",mousedown:"MouseButtonPress",mouseup:"MouseButtonRelease"}),Ce=Object.freeze({touchstart:"TouchDown",touchend:"TouchUp",touchmove:"TouchMotion",touchcancel:"TouchUp"}),ye=Object.freeze({keydown:"KeyPress",keyup:"KeyRelease"});function ve(e){const t=[];return e.altKey&&t.push("mod1-mask"),e.ctrlKey&&t.push("control-mask"),e.metaKey&&t.push("meta-mask"),e.shiftKey&&t.push("shift-mask"),t.join("+")}class be extends EventTarget{constructor(e,t){super(),this._rtcDataChannel=e,this._consumerSession=t,this._videoElement=null,this._videoElementComputedStyle=null,this._videoElementKeyboard=null,this._lastTouchEventTimestamp=0,this._requestCounter=0,e.addEventListener("close",()=>{this._rtcDataChannel===e&&this.close()}),e.addEventListener("error",t=>{if(this._rtcDataChannel===e){const e=t.error;this.dispatchEvent(new ErrorEvent("error",{message:e&&e.message||"Remote controller error",error:e||new Error("unknown error on the remote controller data channel")}))}}),e.addEventListener("message",e=>{try{const t=JSON.parse(e.data);"ControlResponseMessage"===t.type?this.dispatchEvent(new CustomEvent("controlResponse",{detail:t})):"InfoMessage"===t.type&&this.dispatchEvent(new CustomEvent("info",{detail:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse control message from signaling server",error:e}))}})}get rtcDataChannel(){return this._rtcDataChannel}get consumerSession(){return this._consumerSession}get videoElement(){return this._videoElement}attachVideoElement(e){if(e instanceof HTMLVideoElement&&e!==this._videoElement){this._videoElement&&this.attachVideoElement(null),this._videoElement=e,this._videoElementComputedStyle=window.getComputedStyle(e);for(const t of fe)e.addEventListener(t,this);e.setAttribute("tabindex","0")}else if(null===e&&this._videoElement){const e=this._videoElement;e.removeAttribute("tabindex"),this._videoElement=null,this._videoElementComputedStyle=null,this._lastTouchEventTimestamp=0;for(const t of fe)e.removeEventListener(t,this)}}sendControlRequest(e){try{if(!e||"object"!=typeof e&&"string"!=typeof e)throw new Error("invalid request");if(!this._rtcDataChannel)throw new Error("remote controller data channel is closed");let t={id:this._requestCounter++,request:e};return this._rtcDataChannel.send(JSON.stringify(t)),t.id}catch(e){return this.dispatchEvent(new ErrorEvent("error",{message:`cannot send control message over session ${this._consumerSession.sessionId} remote controller`,error:e})),-1}}close(){this.attachVideoElement(null);const e=this._rtcDataChannel;this._rtcDataChannel=null,e&&(e.close(),this.dispatchEvent(new Event("closed")))}_sendGstNavigationEvent(e){let t={type:"navigationEvent",event:e};this.sendControlRequest(t)}_computeVideoMousePosition(e){const t={x:0,y:0};if(!this._videoElement||this._videoElement.videoWidth<=0||this._videoElement.videoHeight<=0)return t;const n=parseFloat(this._videoElementComputedStyle.paddingLeft),r=parseFloat(this._videoElementComputedStyle.paddingRight),i=parseFloat(this._videoElementComputedStyle.paddingTop),o=parseFloat(this._videoElementComputedStyle.paddingBottom);if("offsetX"in e&&"offsetY"in e)t.x=e.offsetX-n,t.y=e.offsetY-i;else{const r=this._videoElement.getBoundingClientRect(),o={left:parseFloat(this._videoElementComputedStyle.borderLeftWidth),top:parseFloat(this._videoElementComputedStyle.borderTopWidth)};t.x=e.clientX-r.left-o.left-n,t.y=e.clientY-r.top-o.top-i}const s={x:this._videoElement.clientWidth-(n+r),y:this._videoElement.clientHeight-(i+o)},a=Math.min(s.x/this._videoElement.videoWidth,s.y/this._videoElement.videoHeight);s.x=Math.max(.5*(s.x-this._videoElement.videoWidth*a),0),s.y=Math.max(.5*(s.y-this._videoElement.videoHeight*a),0);const c=0!==a?1/a:0;return t.x=(t.x-s.x)*c,t.y=(t.y-s.y)*c,t.x=Math.min(Math.max(t.x,0),this._videoElement.videoWidth),t.y=Math.min(Math.max(t.y,0),this._videoElement.videoHeight),t}handleEvent(e){if(this._videoElement)switch(e.type){case"wheel":e.preventDefault();{const t=this._computeVideoMousePosition(e);this._sendGstNavigationEvent({event:"MouseScroll",x:t.x,y:t.y,delta_x:-e.deltaX,delta_y:-e.deltaY,modifier_state:ve(e)})}break;case"contextmenu":e.preventDefault();break;case"mousemove":case"mousedown":case"mouseup":e.preventDefault();{const t=this._computeVideoMousePosition(e),n={event:ge[e.type],x:t.x,y:t.y,modifier_state:ve(e)};"mousemove"!==e.type&&(n.button=e.button+1,"mousedown"===e.type&&0===e.button&&this._videoElement.focus()),this._sendGstNavigationEvent(n)}break;case"touchstart":case"touchend":case"touchmove":case"touchcancel":for(const t of e.changedTouches){const n=this._computeVideoMousePosition(t),r={event:Ce[e.type],identifier:t.identifier,x:n.x,y:n.y,modifier_state:ve(e)};!("force"in t)||"touchstart"!==e.type&&"touchmove"!==e.type||(r.pressure=t.force),this._sendGstNavigationEvent(r)}e.timeStamp>this._lastTouchEventTimestamp&&(this._lastTouchEventTimestamp=e.timeStamp,this._sendGstNavigationEvent({event:"TouchFrame",modifier_state:ve(e)}));break;case"keyup":case"keydown":e.preventDefault();{const t={event:ye[e.type],key:_e(e.key,e.code),modifier_state:ve(e)};this._sendGstNavigationEvent(t)}}}}const Se=be;const ke=class extends he{constructor(e,t,n){super(e,t),this._streams=[],this._remoteController=null,this._pendingCandidates=[],this._mungeStereoHack=!1,this._offerOptions=n,this.addEventListener("closed",()=>{this._streams=[],this._pendingCandidates=[],this._remoteController&&this._remoteController.close()})}set mungeStereoHack(e){"boolean"==typeof e&&(this._mungeStereoHack=e)}get streams(){return this._streams}get remoteController(){return this._remoteController}connect(){if(!this._comChannel||this._state===de.closed)return!1;if(this._state!==de.idle)return!0;if(this._offerOptions)this.ensurePeerConnection(),this._rtcPeerConnection.createDataChannel("control"),this._rtcPeerConnection.createOffer(this._offerOptions).then(e=>{if(this._rtcPeerConnection&&e)return this._rtcPeerConnection.setLocalDescription(e);throw new Error("cannot send local offer to WebRTC peer")}).then(()=>{if(this._rtcPeerConnection&&this._comChannel){const e={type:"startSession",peerId:this._peerId,offer:this._rtcPeerConnection.localDescription.toJSON().sdp};if(!this._comChannel.send(e))throw new Error("cannot send startSession message to signaling server");this._state=de.connecting,this.dispatchEvent(new Event("stateChanged"))}}).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{const e={type:"startSession",peerId:this._peerId};if(!this._comChannel.send(e))return this.dispatchEvent(new ErrorEvent("error",{message:"cannot connect consumer session",error:new Error("cannot send startSession message to signaling server")})),this.close(),!1;this._state=de.connecting,this.dispatchEvent(new Event("stateChanged"))}return!0}onSessionStarted(e,t){if(this._peerId===e&&this._state===de.connecting&&!this._sessionId){console.log("Session started",this._sessionId),this._sessionId=t;for(const e of this._pendingCandidates)console.log("Sending delayed ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.toJSON()});this._pendingCandidates=[]}}ensurePeerConnection(){if(!this._rtcPeerConnection){const e=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=e,e.ontrack=t=>{if(this._rtcPeerConnection===e&&t.streams&&t.streams.length>0){this._state===de.connecting&&(this._state=de.streaming,this.dispatchEvent(new Event("stateChanged")));let e=!1;for(const n of t.streams)this._streams.includes(n)||(this._streams.push(n),e=!0);e&&this.dispatchEvent(new Event("streamsChanged"))}},e.ondatachannel=e=>{const t=e.channel;if(t&&"control"===t.label){if(this._remoteController){const e=this._remoteController;this._remoteController=null,e.close()}const e=new Se(t,this);this._remoteController=e,this.dispatchEvent(new Event("remoteControllerChanged")),e.addEventListener("closed",()=>{this._remoteController===e&&(this._remoteController=null,this.dispatchEvent(new Event("remoteControllerChanged")))})}},e.onicecandidate=t=>{this._rtcPeerConnection===e&&t.candidate&&this._comChannel&&(this._sessionId?(console.log("Sending ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:t.candidate.toJSON()})):this._pendingCandidates.push(t.candidate))},this.dispatchEvent(new Event("rtcPeerConnectionChanged"))}}mungeStereo(e,t){const n=/a=fmtp:.* sprop-stereo/g;let r=new Set;for(const t of e.matchAll(n)){const e=t[0].match(/a=fmtp:(\d+) .*/);e&&r.add(e[1])}for(const e of r){const n=new RegExp("a=fmtp:"+e+".*stereo");t.match(n)||(t=t.replaceAll("a=fmtp:"+e,"a=fmtp:"+e+" stereo=1;"))}return t}onSessionPeerMessage(e){if(this._state!==de.closed&&this._comChannel&&this._sessionId)if(this.ensurePeerConnection(),e.sdp)this._offerOptions?this._rtcPeerConnection.setRemoteDescription(e.sdp).then(()=>{console.log("done")}).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}):this._rtcPeerConnection.setRemoteDescription(e.sdp).then(()=>this._rtcPeerConnection?this._rtcPeerConnection.createAnswer():null).then(t=>this._rtcPeerConnection&&t?(this._mungeStereoHack&&(t.sdp=this.mungeStereo(e.sdp.sdp,t.sdp)),this._rtcPeerConnection.setLocalDescription(t)):null).then(()=>{if(this._rtcPeerConnection&&this._comChannel){console.log("Sending SDP with session id",this._sessionId);const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}}).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{if(!e.ice)throw new Error(`invalid empty peer message received from consumer session ${this._sessionId}`);{const t=e.ice.candidate?new RTCIceCandidate(e.ice):null;this._rtcPeerConnection.addIceCandidate(t).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())})}}}};class Te extends he{constructor(e,t,n,r){super(e,n),this._sessionId=t,this._state=de.streaming;const i=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=i;for(const e of r.getTracks())i.addTrack(e,r);i.onicecandidate=e=>{this._rtcPeerConnection===i&&e.candidate&&this._comChannel&&this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.candidate.toJSON()})},this.dispatchEvent(new Event("rtcPeerConnectionChanged")),i.setLocalDescription().then(()=>{if(this._rtcPeerConnection===i&&this._comChannel){const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}}).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())})}onSessionPeerMessage(e){if(this._state!==de.closed&&this._rtcPeerConnection)if(e.sdp)this._rtcPeerConnection.setRemoteDescription(e.sdp).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{if(!e.ice)throw new Error(`invalid empty peer message received from producer's client session ${this._peerId}`);{const t=new RTCIceCandidate(e.ice);this._rtcPeerConnection.addIceCandidate(t).catch(e=>{this._state!==de.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())})}}}}class Ee extends EventTarget{constructor(e,t,n){super(),this._comChannel=e,this._stream=t,this._state=de.idle,this._clientSessions={},this._consumerId=n}get stream(){return this._stream}get state(){return this._state}start(){if(!this._comChannel||this._state===de.closed)return!1;if(this._state!==de.idle)return!0;const e={type:"setPeerStatus",roles:["listener","producer"],meta:this._comChannel.meta};return this._comChannel.send(e)?(this._state=de.connecting,this.dispatchEvent(new Event("stateChanged")),!0):(this.dispatchEvent(new ErrorEvent("error",{message:"cannot start producer session",error:new Error("cannot register producer to signaling server")})),this.close(),!1)}close(){if(this._state!==de.closed){for(const e of this._stream.getTracks())e.stop();this._state!==de.idle&&this._comChannel&&this._comChannel.send({type:"setPeerStatus",roles:["listener"],meta:this._comChannel.meta}),this._state=de.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._stream=null;for(const e of Object.values(this._clientSessions))e.close();this._clientSessions={},this.dispatchEvent(new Event("closed"))}}onProducerRegistered(){if(this._state===de.connecting&&(this._state=de.streaming,this.dispatchEvent(new Event("stateChanged"))),this._consumerId){const e={type:"startSession",peerId:this._consumerId};this._comChannel.send(e)||(this.dispatchEvent(new ErrorEvent("error",{message:"cannot send session request to specified consumer",error:new Error("cannot send startSession message to signaling server")})),this.close())}}onStartSessionMessage(e){if(this._comChannel&&this._stream&&!(e.sessionId in this._clientSessions)){const t=new Te(e.peerId,e.sessionId,this._comChannel,this._stream);this._clientSessions[e.sessionId]=t,t.addEventListener("closed",e=>{const n=e.target.sessionId;n in this._clientSessions&&this._clientSessions[n]===t&&(delete this._clientSessions[n],this.dispatchEvent(new CustomEvent("clientConsumerRemoved",{detail:t})))}),t.addEventListener("error",e=>{this.dispatchEvent(new ErrorEvent("error",{message:`error from client consumer ${e.target.peerId}: ${e.message}`,error:e.error}))}),this.dispatchEvent(new CustomEvent("clientConsumerAdded",{detail:t}))}}onEndSessionMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].close()}onSessionPeerMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].onSessionPeerMessage(e)}}const Pe=Ee,Re=Object.freeze({welcome:"welcome",peerStatusChanged:"peerStatusChanged",list:"list",listConsumers:"listConsumers",sessionStarted:"sessionStarted",peer:"peer",startSession:"startSession",endSession:"endSession",error:"error"});function we(e,t){if(!e||"object"!=typeof e)return null;const n={id:"",meta:{}};if(e.id&&"string"==typeof e.id)n.id=e.id;else{if(!e.peerId||"string"!=typeof e.peerId)return null;n.id=e.peerId}return n.id===t?null:(e.meta&&"object"==typeof e.meta&&(n.meta=e.meta),Object.freeze(n.meta),Object.freeze(n))}class Ae extends EventTarget{constructor(e,t,n){super(),this._meta=t,this._webrtcConfig=n,this._ws=new WebSocket(e),this._ready=!1,this._channelId="",this._producerSession=null,this._consumerSessions={},this._peers={},this._ws.onerror=e=>{this.dispatchEvent(new ErrorEvent("error",{message:e.message||"WebSocket error",error:e.error||new Error(this._ready?"transportation error":"cannot connect to signaling server")})),this.close()},this._ws.onclose=()=>{this._ready=!1,this._channelId="",this._ws=null,this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null),this.dispatchEvent(new Event("closed"))},this._ws.onmessage=e=>{try{const n=JSON.parse(e.data);if(n&&"object"==typeof n)switch(n.type){case Re.welcome:this._channelId=n.peerId;try{this._ws.send(JSON.stringify({type:"setPeerStatus",roles:["listener"],meta:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot initialize connection to signaling server",error:e})),this.close()}break;case Re.peerStatusChanged:{if(n.peerId===this._channelId){!this._ready&&n.roles.includes("listener")&&(this._ready=!0,this.dispatchEvent(new Event("ready")),this.send({type:"list"}),this.send({type:"listConsumers"})),this._producerSession&&n.roles.includes("producer")&&this._producerSession.onProducerRegistered();break}const e=we(n,this._channelId);if(!e)break;const t=this._peers[n.peerId]||[];this._peers[n.peerId]=n.roles;for(const r of["producer","consumer"])!t.includes(r)&&n.roles.includes(r)?this.dispatchEvent(new CustomEvent("peerAdded",{detail:{peer:e,role:r}})):t.includes(r)&&!n.roles.includes(r)&&this.dispatchEvent(new CustomEvent("peerRemoved",{detail:{peerId:n.peerId,role:r}}));break}case Re.list:this.clearPeers("producer"),this.addPeers(n.producers,"producer");break;case Re.listConsumers:this.clearPeers("consumer"),this.addPeers(n.consumers,"consumer");break;case Re.sessionStarted:{const e=this.getConsumerSession(n.peerId);e&&(delete this._consumerSessions[n.peerId],e.onSessionStarted(n.peerId,n.sessionId),e.sessionId&&!(e.sessionId in this._consumerSessions)?this._consumerSessions[e.sessionId]=e:e.close())}break;case Re.peer:{const e=this.getConsumerSession(n.sessionId);e?e.onSessionPeerMessage(n):this._producerSession&&this._producerSession.onSessionPeerMessage(n)}break;case Re.startSession:this._producerSession&&this._producerSession.onStartSessionMessage(n);break;case Re.endSession:{const e=this.getConsumerSession(n.sessionId);e?e.close():this._producerSession&&this._producerSession.onEndSessionMessage(n)}break;case Re.error:this.dispatchEvent(new ErrorEvent("error",{message:"error received from signaling server",error:new Error(n.details)}));break;default:throw new Error(`unknown message type: "${n.type}"`)}}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse incoming message from signaling server",error:e}))}}}get meta(){return this._meta}get webrtcConfig(){return this._webrtcConfig}get ready(){return this._ready}get channelId(){return this._channelId}get producerSession(){return this._producerSession}createProducerSession(e,t){if(!(this._ready&&e instanceof MediaStream))return null;if(this._producerSession)return this._producerSession.stream===e?this._producerSession:null;const n=new Pe(this,e,t);return this._producerSession=n,n.addEventListener("closed",()=>{this._producerSession===n&&(this._producerSession=null)}),n}createConsumerSession(e,t){if(!this._ready||!e||"string"!=typeof e)return null;if(t&&"object"!=typeof t&&(t=void 0),e in this._consumerSessions)return this._consumerSessions[e];for(const t of Object.values(this._consumerSessions))if(t.peerId===e)return t;const n=new ke(e,this,t);return this._consumerSessions[e]=n,n.addEventListener("closed",e=>{let t=e.target.sessionId;t||(t=e.target.peerId),t in this._consumerSessions&&this._consumerSessions[t]===n&&delete this._consumerSessions[t]}),n}getConsumerSession(e){return e in this._consumerSessions?this._consumerSessions[e]:null}closeAllConsumerSessions(){for(const e of Object.values(this._consumerSessions))e.close();this._consumerSessions={}}send(e){if(this._ready&&e&&"object"==typeof e)try{return this._ws.send(JSON.stringify(e)),!0}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot send message to signaling server",error:e}))}return!1}close(){this._ws&&(this._ready=!1,this._channelId="",this._ws.close(),this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null))}clearPeers(e){for(const t in this._peers)this._peers[t].includes(e)&&(delete this._peers[t],this.dispatchEvent(new CustomEvent("peerRemoved",{detail:{peerId:t,role:e}})))}addPeers(e,t){e.forEach(e=>{const n=we(e,this._channelId);n&&(this._peers[n.id]=[t],this.dispatchEvent(new CustomEvent("peerAdded",{detail:{peer:n,role:t}})))})}}const Ie=Ae;class xe{constructor(e){this._channel=null,this._producers={},this._consumers={},this._connectionListeners=[],this._peerListeners=[];const t=Object.assign({},ae);e&&"object"==typeof e&&Object.assign(t,e),"object"!=typeof t.meta&&(t.meta=null),this._config=t,this.connectChannel()}registerConnectionListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.connected||"function"!=typeof e.disconnected)&&(this._connectionListeners.includes(e)||this._connectionListeners.push(e),!0)}unregisterConnectionListener(e){const t=this._connectionListeners.indexOf(e);return t>=0&&(this._connectionListeners.splice(t,1),!0)}unregisterAllConnectionListeners(){this._connectionListeners=[]}createProducerSession(e){return this._channel?this._channel.createProducerSession(e):null}createProducerSessionForConsumer(e,t){return this._channel?this._channel.createProducerSession(e,t):null}getAvailableProducers(){return Object.values(this._producers)}getAvailableConsumers(){return Object.values(this._consumers)}registerPeerListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.producerAdded&&"function"!=typeof e.producerRemoved&&"function"!=typeof e.consumerAdded&&"function"!=typeof e.consumerRemoved)&&(this._peerListeners.includes(e)||this._peerListeners.push(e),!0)}unregisterPeerListener(e){const t=this._peerListeners.indexOf(e);return t>=0&&(this._peerListeners.splice(t,1),!0)}unregisterAllPeerListeners(){this._peerListeners=[]}createConsumerSession(e){return this._channel?this._channel.createConsumerSession(e):null}createConsumerSessionWithOfferOptions(e,t){return this._channel?this._channel.createConsumerSession(e,t):null}connectChannel(){if(this._channel){const e=this._channel;this._channel=null,e.close();for(const e in this._producers)this.triggerProducerRemoved(e);for(const e in this._consumers)this.triggerConsumerRemoved(e);this._producers={},this._consumers={},this.triggerDisconnected()}this._channel=new Ie(this._config.signalingServerUrl,this._config.meta,this._config.webrtcConfig),this._channel.addEventListener("error",e=>{e.target===this._channel&&console.error(e.message,e.error)}),this._channel.addEventListener("closed",e=>{if(e.target===this._channel){this._channel=null;for(const e in this._producers)this.triggerProducerRemoved(e);for(const e in this._consumers)this.triggerConsumerRemoved(e);this._producers={},this._consumers={},this.triggerDisconnected(),this._config.reconnectionTimeout>0&&window.setTimeout(()=>{this.connectChannel()},this._config.reconnectionTimeout)}}),this._channel.addEventListener("ready",e=>{e.target===this._channel&&this.triggerConnected(this._channel.channelId)}),this._channel.addEventListener("peerAdded",e=>{e.target===this._channel&&("producer"===e.detail.role?this.triggerProducerAdded(e.detail.peer):this.triggerConsumerAdded(e.detail.peer))}),this._channel.addEventListener("peerRemoved",e=>{e.target===this._channel&&("producer"===e.detail.role?this.triggerProducerRemoved(e.detail.peerId):this.triggerConsumerRemoved(e.detail.peerId))})}triggerConnected(e){for(const t of this._connectionListeners)try{t.connected(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerDisconnected(){for(const e of this._connectionListeners)try{e.disconnected()}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerProducerAdded(e){if(!(e.id in this._producers)){this._producers[e.id]=e;for(const t of this._peerListeners)if(t.producerAdded)try{t.producerAdded(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}}triggerProducerRemoved(e){if(!(e in this._producers))return;const t=this._producers[e];delete this._producers[e];for(const e of this._peerListeners)if(e.producerRemoved)try{e.producerRemoved(t)}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerConsumerAdded(e){if(!(e.id in this._consumers)){this._consumers[e.id]=e;for(const t of this._peerListeners)if(t.consumerAdded)try{t.consumerAdded(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}}triggerConsumerRemoved(e){if(!(e in this._consumers))return;const t=this._consumers[e];delete this._consumers[e];for(const e of this._peerListeners)if(e.consumerRemoved)try{e.consumerRemoved(t)}catch(e){console.error("a listener callback should not throw any exception",e)}}}xe.SessionState=de;const Oe=xe;window.GstWebRTCAPI||(window.GstWebRTCAPI=Oe);const De=Oe})();var i=r.Z;export{i as default}; +//# sourceMappingURL=gstwebrtc-api-3.0.0.esm.js.map \ No newline at end of file diff --git a/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js.map b/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js.map new file mode 100644 index 0000000..15337b8 --- /dev/null +++ b/web/javascript/webrtc/gstwebrtc-api-3.0.0.esm.js.map @@ -0,0 +1 @@ +{"version":3,"file":"gstwebrtc-api-3.0.0.esm.js","mappings":"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","sources":["webpack://gstwebrtc-api/./node_modules/sdp/sdp.js","webpack://gstwebrtc-api/webpack/bootstrap","webpack://gstwebrtc-api/webpack/runtime/compat get default export","webpack://gstwebrtc-api/webpack/runtime/define property getters","webpack://gstwebrtc-api/webpack/runtime/hasOwnProperty shorthand","webpack://gstwebrtc-api/webpack/runtime/make namespace object","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/utils.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/safari/safari_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/common_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/adapter_factory.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/adapter_core.js","webpack://gstwebrtc-api/./src/config.js","webpack://gstwebrtc-api/./src/session-state.js","webpack://gstwebrtc-api/./src/webrtc-session.js","webpack://gstwebrtc-api/./src/keysyms.js","webpack://gstwebrtc-api/./src/remote-controller.js","webpack://gstwebrtc-api/./src/consumer-session.js","webpack://gstwebrtc-api/./src/producer-session.js","webpack://gstwebrtc-api/./src/com-channel.js","webpack://gstwebrtc-api/./src/gstwebrtc-api.js","webpack://gstwebrtc-api/./src/index.js"],"sourcesContent":["/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substring(2, 12);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substring(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substring(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n attributes: parts.slice(2).join(' '),\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri +\n (headerExtension.attributes ? ' ' + headerExtension.attributes : '') +\n '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\n// Non-key-value such as telephone-events `0-15` get parsed as\n// {`0-15`:undefined}\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substring(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substring(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substring(7, sp), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substring(sp + 1, colon);\n parts.value = line.substring(colon + 1);\n } else {\n parts.attribute = line.substring(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substring(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substring(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substring(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substring(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substring(12),\n password: pwd.substring(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n description.profile = mline[2];\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.forEach(codec => {\n wildcardRtcpFb.forEach(fb=> {\n const duplicate = codec.rtcpFeedback.find(existingFeedback => {\n return existingFeedback.type === fb.type &&\n existingFeedback.parameter === fb.parameter;\n });\n if (!duplicate) {\n codec.rtcpFeedback.push(fb);\n }\n });\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substring(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substring(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substring(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substring(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substring(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 22);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substring(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substring(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substring(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substring(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n const match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n const nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n const wrappedCallback = (e) => {\n const modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n if (cb.handleEvent) {\n cb.handleEvent(modifiedEvent);\n } else {\n cb(modifiedEvent);\n }\n }\n };\n this._eventMap = this._eventMap || {};\n if (!this._eventMap[eventNameToWrap]) {\n this._eventMap[eventNameToWrap] = new Map();\n }\n this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n const nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[eventNameToWrap]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n if (!this._eventMap[eventNameToWrap].has(cb)) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n this._eventMap[eventNameToWrap].delete(cb);\n if (this._eventMap[eventNameToWrap].size === 0) {\n delete this._eventMap[eventNameToWrap];\n }\n if (Object.keys(this._eventMap).length === 0) {\n delete this._eventMap;\n }\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get() {\n return this['_on' + eventNameToWrap];\n },\n set(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\nexport function disableLog(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\nexport function detectBrowser(window) {\n // Returned result object.\n const result = {browser: null, version: null};\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator ||\n !window.navigator.userAgent) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n const {navigator} = window;\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia ||\n (window.isSecureContext === false && window.webkitRTCPeerConnection)) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n // more complicated fallback to webkitRTCPeerConnection.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n 'currentDirection' in window.RTCRtpTransceiver.prototype;\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n if (!isObject(data)) {\n return data;\n }\n\n return Object.keys(data).reduce(function(accumulator, key) {\n const isObj = isObject(data[key]);\n const value = isObj ? compactObject(data[key]) : data[key];\n const isEmptyObject = isObj && !Object.keys(value).length;\n if (value === undefined || isEmptyObject) {\n return accumulator;\n }\n return Object.assign(accumulator, {[key]: value});\n }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n if (!base || resultSet.has(base.id)) {\n return;\n }\n resultSet.set(base.id, base);\n Object.keys(base).forEach(name => {\n if (name.endsWith('Id')) {\n walkStats(stats, stats.get(base[name]), resultSet);\n } else if (name.endsWith('Ids')) {\n base[name].forEach(id => {\n walkStats(stats, stats.get(id), resultSet);\n });\n }\n });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n const filteredResult = new Map();\n if (track === null) {\n return filteredResult;\n }\n const trackStats = [];\n result.forEach(value => {\n if (value.type === 'track' &&\n value.trackIdentifier === track.id) {\n trackStats.push(value);\n }\n });\n trackStats.forEach(trackStat => {\n result.forEach(stats => {\n if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n walkStats(result, stats, filteredResult);\n }\n });\n });\n return filteredResult;\n}\n\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n\n if (!navigator.mediaDevices) {\n return;\n }\n\n const constraintsToChrome_ = function(c) {\n if (typeof c !== 'object' || c.mandatory || c.optional) {\n return c;\n }\n const cc = {};\n Object.keys(c).forEach(key => {\n if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n return;\n }\n const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n if (r.exact !== undefined && typeof r.exact === 'number') {\n r.min = r.max = r.exact;\n }\n const oldname_ = function(prefix, name) {\n if (prefix) {\n return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n }\n return (name === 'deviceId') ? 'sourceId' : name;\n };\n if (r.ideal !== undefined) {\n cc.optional = cc.optional || [];\n let oc = {};\n if (typeof r.ideal === 'number') {\n oc[oldname_('min', key)] = r.ideal;\n cc.optional.push(oc);\n oc = {};\n oc[oldname_('max', key)] = r.ideal;\n cc.optional.push(oc);\n } else {\n oc[oldname_('', key)] = r.ideal;\n cc.optional.push(oc);\n }\n }\n if (r.exact !== undefined && typeof r.exact !== 'number') {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_('', key)] = r.exact;\n } else {\n ['min', 'max'].forEach(mix => {\n if (r[mix] !== undefined) {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_(mix, key)] = r[mix];\n }\n });\n }\n });\n if (c.advanced) {\n cc.optional = (cc.optional || []).concat(c.advanced);\n }\n return cc;\n };\n\n const shimConstraints_ = function(constraints, func) {\n if (browserDetails.version >= 61) {\n return func(constraints);\n }\n constraints = JSON.parse(JSON.stringify(constraints));\n if (constraints && typeof constraints.audio === 'object') {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n constraints = JSON.parse(JSON.stringify(constraints));\n remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n constraints.audio = constraintsToChrome_(constraints.audio);\n }\n if (constraints && typeof constraints.video === 'object') {\n // Shim facingMode for mobile & surface pro.\n let face = constraints.video.facingMode;\n face = face && ((typeof face === 'object') ? face : {ideal: face});\n const getSupportedFacingModeLies = browserDetails.version < 66;\n\n if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n face.ideal === 'user' || face.ideal === 'environment')) &&\n !(navigator.mediaDevices.getSupportedConstraints &&\n navigator.mediaDevices.getSupportedConstraints().facingMode &&\n !getSupportedFacingModeLies)) {\n delete constraints.video.facingMode;\n let matches;\n if (face.exact === 'environment' || face.ideal === 'environment') {\n matches = ['back', 'rear'];\n } else if (face.exact === 'user' || face.ideal === 'user') {\n matches = ['front'];\n }\n if (matches) {\n // Look for matches in label, or use last cam for back (typical).\n return navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n devices = devices.filter(d => d.kind === 'videoinput');\n let dev = devices.find(d => matches.some(match =>\n d.label.toLowerCase().includes(match)));\n if (!dev && devices.length && matches.includes('back')) {\n dev = devices[devices.length - 1]; // more likely the back cam\n }\n if (dev) {\n constraints.video.deviceId = face.exact\n ? {exact: dev.deviceId}\n : {ideal: dev.deviceId};\n }\n constraints.video = constraintsToChrome_(constraints.video);\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n });\n }\n }\n constraints.video = constraintsToChrome_(constraints.video);\n }\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n };\n\n const shimError_ = function(e) {\n if (browserDetails.version >= 64) {\n return e;\n }\n return {\n name: {\n PermissionDeniedError: 'NotAllowedError',\n PermissionDismissedError: 'NotAllowedError',\n InvalidStateError: 'NotAllowedError',\n DevicesNotFoundError: 'NotFoundError',\n ConstraintNotSatisfiedError: 'OverconstrainedError',\n TrackStartError: 'NotReadableError',\n MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n MediaDeviceKillSwitchOn: 'NotAllowedError',\n TabCaptureError: 'AbortError',\n ScreenCaptureError: 'AbortError',\n DeviceCaptureError: 'AbortError'\n }[e.name] || e.name,\n message: e.message,\n constraint: e.constraint || e.constraintName,\n toString() {\n return this.name + (this.message && ': ') + this.message;\n }\n };\n };\n\n const getUserMedia_ = function(constraints, onSuccess, onError) {\n shimConstraints_(constraints, c => {\n navigator.webkitGetUserMedia(c, onSuccess, e => {\n if (onError) {\n onError(shimError_(e));\n }\n });\n });\n };\n navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n // function which returns a Promise, it does not accept spec-style\n // constraints.\n if (navigator.mediaDevices.getUserMedia) {\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(cs) {\n return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n if (c.audio && !stream.getAudioTracks().length ||\n c.video && !stream.getVideoTracks().length) {\n stream.getTracks().forEach(track => {\n track.stop();\n });\n throw new DOMException('', 'NotFoundError');\n }\n return stream;\n }, e => Promise.reject(shimError_(e))));\n };\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n // getSourceId is a function that returns a promise resolving with\n // the sourceId of the screen/window/tab to be shared.\n if (typeof getSourceId !== 'function') {\n console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n 'a function');\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n return getSourceId(constraints)\n .then(sourceId => {\n const widthSpecified = constraints.video && constraints.video.width;\n const heightSpecified = constraints.video &&\n constraints.video.height;\n const frameRateSpecified = constraints.video &&\n constraints.video.frameRate;\n constraints.video = {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: sourceId,\n maxFrameRate: frameRateSpecified || 3\n }\n };\n if (widthSpecified) {\n constraints.video.mandatory.maxWidth = widthSpecified;\n }\n if (heightSpecified) {\n constraints.video.mandatory.maxHeight = heightSpecified;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n });\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n get() {\n return this._ontrack;\n },\n set(f) {\n if (this._ontrack) {\n this.removeEventListener('track', this._ontrack);\n }\n this.addEventListener('track', this._ontrack = f);\n },\n enumerable: true,\n configurable: true\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n if (!this._ontrackpoly) {\n this._ontrackpoly = (e) => {\n // onaddstream does not fire when a track is added to an existing\n // stream. But stream.onaddtrack is implemented so we use that.\n e.stream.addEventListener('addtrack', te => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === te.track.id);\n } else {\n receiver = {track: te.track};\n }\n\n const event = new Event('track');\n event.track = te.track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n e.stream.getTracks().forEach(track => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === track.id);\n } else {\n receiver = {track};\n }\n const event = new Event('track');\n event.track = track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n };\n this.addEventListener('addstream', this._ontrackpoly);\n }\n return origSetRemoteDescription.apply(this, arguments);\n };\n } else {\n // even if RTCRtpTransceiver is in window, it is only used and\n // emitted in unified-plan. Unfortunately this means we need\n // to unconditionally wrap the event.\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n if (!e.transceiver) {\n Object.defineProperty(e, 'transceiver',\n {value: {receiver: e.receiver}});\n }\n return e;\n });\n }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n if (typeof window === 'object' && window.RTCPeerConnection &&\n !('getSenders' in window.RTCPeerConnection.prototype) &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype) {\n const shimSenderWithDtmf = function(pc, track) {\n return {\n track,\n get dtmf() {\n if (this._dtmf === undefined) {\n if (track.kind === 'audio') {\n this._dtmf = pc.createDTMFSender(track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n },\n _pc: pc\n };\n };\n\n // augment addTrack when getSenders is not available.\n if (!window.RTCPeerConnection.prototype.getSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n this._senders = this._senders || [];\n return this._senders.slice(); // return a copy of the internal state.\n };\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n let sender = origAddTrack.apply(this, arguments);\n if (!sender) {\n sender = shimSenderWithDtmf(this, track);\n this._senders.push(sender);\n }\n return sender;\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n origRemoveTrack.apply(this, arguments);\n const idx = this._senders.indexOf(sender);\n if (idx !== -1) {\n this._senders.splice(idx, 1);\n }\n };\n }\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._senders = this._senders || [];\n origAddStream.apply(this, [stream]);\n stream.getTracks().forEach(track => {\n this._senders.push(shimSenderWithDtmf(this, track));\n });\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._senders = this._senders || [];\n origRemoveStream.apply(this, [stream]);\n\n stream.getTracks().forEach(track => {\n const sender = this._senders.find(s => s.track === track);\n if (sender) { // remove sender\n this._senders.splice(this._senders.indexOf(sender), 1);\n }\n });\n };\n } else if (typeof window === 'object' && window.RTCPeerConnection &&\n 'getSenders' in window.RTCPeerConnection.prototype &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype &&\n window.RTCRtpSender &&\n !('dtmf' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = this._pc.createDTMFSender(this.track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n}\n\nexport function shimGetStats(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n\n // If selector is a function then we are in the old style stats so just\n // pass back the original getStats format to avoid breaking old users.\n if (arguments.length > 0 && typeof selector === 'function') {\n return origGetStats.apply(this, arguments);\n }\n\n // When spec-style getStats is supported, return those when called with\n // either no arguments or the selector argument is null.\n if (origGetStats.length === 0 && (arguments.length === 0 ||\n typeof selector !== 'function')) {\n return origGetStats.apply(this, []);\n }\n\n const fixChromeStats_ = function(response) {\n const standardReport = {};\n const reports = response.result();\n reports.forEach(report => {\n const standardStats = {\n id: report.id,\n timestamp: report.timestamp,\n type: {\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[report.type] || report.type\n };\n report.names().forEach(name => {\n standardStats[name] = report.stat(name);\n });\n standardReport[standardStats.id] = standardStats;\n });\n\n return standardReport;\n };\n\n // shim getStats with maplike support\n const makeMapStats = function(stats) {\n return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n };\n\n if (arguments.length >= 2) {\n const successCallbackWrapper_ = function(response) {\n onSucc(makeMapStats(fixChromeStats_(response)));\n };\n\n return origGetStats.apply(this, [successCallbackWrapper_,\n selector]);\n }\n\n // promise-support\n return new Promise((resolve, reject) => {\n origGetStats.apply(this, [\n function(response) {\n resolve(makeMapStats(fixChromeStats_(response)));\n }, reject]);\n }).then(onSucc, onErr);\n };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender && window.RTCRtpReceiver)) {\n return;\n }\n\n // shim sender stats.\n if (!('getStats' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n const sender = this;\n return this._pc.getStats().then(result =>\n /* Note: this will include stats of all senders that\n * send a track with the same id as sender.track as\n * it is not possible to identify the RTCRtpSender.\n */\n utils.filterStats(result, sender.track, true));\n };\n }\n\n // shim receiver stats.\n if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers =\n function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n const receiver = this;\n return this._pc.getStats().then(result =>\n utils.filterStats(result, receiver.track, false));\n };\n }\n\n if (!('getStats' in window.RTCRtpSender.prototype &&\n 'getStats' in window.RTCRtpReceiver.prototype)) {\n return;\n }\n\n // shim RTCPeerConnection.getStats(track).\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n if (arguments.length > 0 &&\n arguments[0] instanceof window.MediaStreamTrack) {\n const track = arguments[0];\n let sender;\n let receiver;\n let err;\n this.getSenders().forEach(s => {\n if (s.track === track) {\n if (sender) {\n err = true;\n } else {\n sender = s;\n }\n }\n });\n this.getReceivers().forEach(r => {\n if (r.track === track) {\n if (receiver) {\n err = true;\n } else {\n receiver = r;\n }\n }\n return r.track === track;\n });\n if (err || (sender && receiver)) {\n return Promise.reject(new DOMException(\n 'There are more than one sender or receiver for the track.',\n 'InvalidAccessError'));\n } else if (sender) {\n return sender.getStats();\n } else if (receiver) {\n return receiver.getStats();\n }\n return Promise.reject(new DOMException(\n 'There is no sender or receiver for the track.',\n 'InvalidAccessError'));\n }\n return origGetStats.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n // shim addTrack/removeTrack with native variants in order to make\n // the interactions with legacy getLocalStreams behave as in other browsers.\n // Keeps a mapping stream.id => [stream, rtpsenders...]\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n return Object.keys(this._shimmedLocalStreams)\n .map(streamId => this._shimmedLocalStreams[streamId][0]);\n };\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (!stream) {\n return origAddTrack.apply(this, arguments);\n }\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n const sender = origAddTrack.apply(this, arguments);\n if (!this._shimmedLocalStreams[stream.id]) {\n this._shimmedLocalStreams[stream.id] = [stream, sender];\n } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n this._shimmedLocalStreams[stream.id].push(sender);\n }\n return sender;\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n const existingSenders = this.getSenders();\n origAddStream.apply(this, arguments);\n const newSenders = this.getSenders()\n .filter(newSender => existingSenders.indexOf(newSender) === -1);\n this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n delete this._shimmedLocalStreams[stream.id];\n return origRemoveStream.apply(this, arguments);\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n if (sender) {\n Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n if (idx !== -1) {\n this._shimmedLocalStreams[streamId].splice(idx, 1);\n }\n if (this._shimmedLocalStreams[streamId].length === 1) {\n delete this._shimmedLocalStreams[streamId];\n }\n });\n }\n return origRemoveTrack.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // shim addTrack and removeTrack.\n if (window.RTCPeerConnection.prototype.addTrack &&\n browserDetails.version >= 65) {\n return shimAddTrackRemoveTrackWithNative(window);\n }\n\n // also shim pc.getLocalStreams when addTrack is shimmed\n // to return the original streams.\n const origGetLocalStreams = window.RTCPeerConnection.prototype\n .getLocalStreams;\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n const nativeStreams = origGetLocalStreams.apply(this);\n this._reverseStreams = this._reverseStreams || {};\n return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n // Add identity mapping for consistency with addTrack.\n // Unless this is being used with a stream from addTrack.\n if (!this._reverseStreams[stream.id]) {\n const newStream = new window.MediaStream(stream.getTracks());\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n stream = newStream;\n }\n origAddStream.apply(this, [stream]);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n delete this._reverseStreams[(this._streams[stream.id] ?\n this._streams[stream.id].id : stream.id)];\n delete this._streams[stream.id];\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n const streams = [].slice.call(arguments, 1);\n if (streams.length !== 1 ||\n !streams[0].getTracks().find(t => t === track)) {\n // this is not fully correct but all we can manage without\n // [[associated MediaStreams]] internal slot.\n throw new DOMException(\n 'The adapter.js addTrack polyfill only supports a single ' +\n ' stream which is associated with the specified track.',\n 'NotSupportedError');\n }\n\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n const oldStream = this._streams[stream.id];\n if (oldStream) {\n // this is using odd Chrome behaviour, use with caution:\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n // Note: we rely on the high-level addTrack/dtmf shim to\n // create the sender with a dtmf sender.\n oldStream.addTrack(track);\n\n // Trigger ONN async.\n Promise.resolve().then(() => {\n this.dispatchEvent(new Event('negotiationneeded'));\n });\n } else {\n const newStream = new window.MediaStream([track]);\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n this.addStream(newStream);\n }\n return this.getSenders().find(s => s.track === track);\n };\n\n // replace the internal stream id with the external one and\n // vice versa.\n function replaceInternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n externalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n function replaceExternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n internalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n ['createOffer', 'createAnswer'].forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n const args = arguments;\n const isLegacyCall = arguments.length &&\n typeof arguments[0] === 'function';\n if (isLegacyCall) {\n return nativeMethod.apply(this, [\n (description) => {\n const desc = replaceInternalStreamId(this, description);\n args[0].apply(null, [desc]);\n },\n (err) => {\n if (args[1]) {\n args[1].apply(null, err);\n }\n }, arguments[2]\n ]);\n }\n return nativeMethod.apply(this, arguments)\n .then(description => replaceInternalStreamId(this, description));\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n\n const origSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n if (!arguments.length || !arguments[0].type) {\n return origSetLocalDescription.apply(this, arguments);\n }\n arguments[0] = replaceExternalStreamId(this, arguments[0]);\n return origSetLocalDescription.apply(this, arguments);\n };\n\n // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n const origLocalDescription = Object.getOwnPropertyDescriptor(\n window.RTCPeerConnection.prototype, 'localDescription');\n Object.defineProperty(window.RTCPeerConnection.prototype,\n 'localDescription', {\n get() {\n const description = origLocalDescription.get.apply(this);\n if (description.type === '') {\n return description;\n }\n return replaceInternalStreamId(this, description);\n }\n });\n\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n // We can not yet check for sender instanceof RTCRtpSender\n // since we shim RTPSender. So we check if sender._pc is set.\n if (!sender._pc) {\n throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.', 'TypeError');\n }\n const isLocal = sender._pc === this;\n if (!isLocal) {\n throw new DOMException('Sender was not created by this connection.',\n 'InvalidAccessError');\n }\n\n // Search for the native stream the senders track belongs to.\n this._streams = this._streams || {};\n let stream;\n Object.keys(this._streams).forEach(streamid => {\n const hasTrack = this._streams[streamid].getTracks()\n .find(track => sender.track === track);\n if (hasTrack) {\n stream = this._streams[streamid];\n }\n });\n\n if (stream) {\n if (stream.getTracks().length === 1) {\n // if this is the last track of the stream, remove the stream. This\n // takes care of any shimmed _senders.\n this.removeStream(this._reverseStreams[stream.id]);\n } else {\n // relying on the same odd chrome behaviour as above.\n stream.removeTrack(sender.track);\n }\n this.dispatchEvent(new Event('negotiationneeded'));\n }\n };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.webkitRTCPeerConnection;\n }\n if (!window.RTCPeerConnection) {\n return;\n }\n\n // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n if (browserDetails.version < 53) {\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n const pc = e.target;\n if (browserDetails.version < 72 || (pc.getConfiguration &&\n pc.getConfiguration().sdpSemantics === 'plan-b')) {\n if (pc.signalingState !== 'stable') {\n return;\n }\n }\n return e;\n });\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n const MediaStreamTrack = window && window.MediaStreamTrack;\n\n navigator.getUserMedia = function(constraints, onSuccess, onError) {\n // Replace Firefox 44+'s deprecation warning with unprefixed version.\n utils.deprecated('navigator.getUserMedia',\n 'navigator.mediaDevices.getUserMedia');\n navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n };\n\n if (!(browserDetails.version > 55 &&\n 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n\n const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n if (typeof c === 'object' && typeof c.audio === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeGetUserMedia(c);\n };\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n MediaStreamTrack.prototype.getSettings = function() {\n const obj = nativeGetSettings.apply(this, arguments);\n remap(obj, 'mozAutoGainControl', 'autoGainControl');\n remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n return obj;\n };\n }\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n const nativeApplyConstraints =\n MediaStreamTrack.prototype.applyConstraints;\n MediaStreamTrack.prototype.applyConstraints = function(c) {\n if (this.kind === 'audio' && typeof c === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c, 'autoGainControl', 'mozAutoGainControl');\n remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeApplyConstraints.apply(this, [c]);\n };\n }\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n if (!(constraints && constraints.video)) {\n const err = new DOMException('getDisplayMedia without video ' +\n 'constraints is undefined');\n err.name = 'NotFoundError';\n // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n err.code = 8;\n return Promise.reject(err);\n }\n if (constraints.video === true) {\n constraints.video = {mediaSource: preferredMediaSource};\n } else {\n constraints.video.mediaSource = preferredMediaSource;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCTrackEvent &&\n ('receiver' in window.RTCTrackEvent.prototype) &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (typeof window !== 'object' ||\n !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n return; // probably media.peerconnection.enabled=false in about:config\n }\n if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.mozRTCPeerConnection;\n }\n\n if (browserDetails.version < 53) {\n // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n\n const modernStatsTypes = {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n };\n\n const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n return nativeGetStats.apply(this, [selector || null])\n .then(stats => {\n if (browserDetails.version < 53 && !onSucc) {\n // Shim only promise getStats with spec-hyphens in type names\n // Leave callback version alone; misc old uses of forEach before Map\n try {\n stats.forEach(stat => {\n stat.type = modernStatsTypes[stat.type] || stat.type;\n });\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n stats.forEach((stat, i) => {\n stats.set(i, Object.assign({}, stat, {\n type: modernStatsTypes[stat.type] || stat.type\n }));\n });\n }\n }\n return stats;\n })\n .then(onSucc, onErr);\n };\n}\n\nexport function shimSenderGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n return;\n }\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n return this.track ? this._pc.getStats(this.track) :\n Promise.resolve(new Map());\n };\n}\n\nexport function shimReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n return;\n }\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n return this._pc.getStats(this.track);\n };\n}\n\nexport function shimRemoveStream(window) {\n if (!window.RTCPeerConnection ||\n 'removeStream' in window.RTCPeerConnection.prototype) {\n return;\n }\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n utils.deprecated('removeStream', 'removeTrack');\n this.getSenders().forEach(sender => {\n if (sender.track && stream.getTracks().includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n}\n\nexport function shimRTCDataChannel(window) {\n // rename DataChannel to RTCDataChannel (native fix in FF60):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n if (window.DataChannel && !window.RTCDataChannel) {\n window.RTCDataChannel = window.DataChannel;\n }\n}\n\nexport function shimAddTransceiver(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n if (origAddTransceiver) {\n window.RTCPeerConnection.prototype.addTransceiver =\n function addTransceiver() {\n this.setParametersPromises = [];\n // WebIDL input coercion and validation\n let sendEncodings = arguments[1] && arguments[1].sendEncodings;\n if (sendEncodings === undefined) {\n sendEncodings = [];\n }\n sendEncodings = [...sendEncodings];\n const shouldPerformCheck = sendEncodings.length > 0;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n sendEncodings.forEach((encodingParam) => {\n if ('rid' in encodingParam) {\n const ridRegex = /^[a-z0-9]{0,16}$/i;\n if (!ridRegex.test(encodingParam.rid)) {\n throw new TypeError('Invalid RID value provided.');\n }\n }\n if ('scaleResolutionDownBy' in encodingParam) {\n if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n throw new RangeError('scale_resolution_down_by must be >= 1.0');\n }\n }\n if ('maxFramerate' in encodingParam) {\n if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n throw new RangeError('max_framerate must be >= 0.0');\n }\n }\n });\n }\n const transceiver = origAddTransceiver.apply(this, arguments);\n if (shouldPerformCheck) {\n // Check if the init options were applied. If not we do this in an\n // asynchronous way and save the promise reference in a global object.\n // This is an ugly hack, but at the same time is way more robust than\n // checking the sender parameters before and after the createOffer\n // Also note that after the createoffer we are not 100% sure that\n // the params were asynchronously applied so we might miss the\n // opportunity to recreate offer.\n const {sender} = transceiver;\n const params = sender.getParameters();\n if (!('encodings' in params) ||\n // Avoid being fooled by patched getParameters() below.\n (params.encodings.length === 1 &&\n Object.keys(params.encodings[0]).length === 0)) {\n params.encodings = sendEncodings;\n sender.sendEncodings = sendEncodings;\n this.setParametersPromises.push(sender.setParameters(params)\n .then(() => {\n delete sender.sendEncodings;\n }).catch(() => {\n delete sender.sendEncodings;\n })\n );\n }\n }\n return transceiver;\n };\n }\n}\n\nexport function shimGetParameters(window) {\n if (!(typeof window === 'object' && window.RTCRtpSender)) {\n return;\n }\n const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n if (origGetParameters) {\n window.RTCRtpSender.prototype.getParameters =\n function getParameters() {\n const params = origGetParameters.apply(this, arguments);\n if (!('encodings' in params)) {\n params.encodings = [].concat(this.sendEncodings || [{}]);\n }\n return params;\n };\n }\n}\n\nexport function shimCreateOffer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateOffer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimCreateAnswer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateAnswer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateAnswer.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n return this._localStreams;\n };\n }\n if (!('addStream' in window.RTCPeerConnection.prototype)) {\n const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n // Try to emulate Chrome's behaviour of adding in audio-video order.\n // Safari orders by track id.\n stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n stream));\n stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n stream));\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, ...streams) {\n if (streams) {\n streams.forEach((stream) => {\n if (!this._localStreams) {\n this._localStreams = [stream];\n } else if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n });\n }\n return _addTrack.apply(this, arguments);\n };\n }\n if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n const index = this._localStreams.indexOf(stream);\n if (index === -1) {\n return;\n }\n this._localStreams.splice(index, 1);\n const tracks = stream.getTracks();\n this.getSenders().forEach(sender => {\n if (tracks.includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getRemoteStreams =\n function getRemoteStreams() {\n return this._remoteStreams ? this._remoteStreams : [];\n };\n }\n if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n get() {\n return this._onaddstream;\n },\n set(f) {\n if (this._onaddstream) {\n this.removeEventListener('addstream', this._onaddstream);\n this.removeEventListener('track', this._onaddstreampoly);\n }\n this.addEventListener('addstream', this._onaddstream = f);\n this.addEventListener('track', this._onaddstreampoly = (e) => {\n e.streams.forEach(stream => {\n if (!this._remoteStreams) {\n this._remoteStreams = [];\n }\n if (this._remoteStreams.includes(stream)) {\n return;\n }\n this._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n this.dispatchEvent(event);\n });\n });\n }\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n const pc = this;\n if (!this._onaddstreampoly) {\n this.addEventListener('track', this._onaddstreampoly = function(e) {\n e.streams.forEach(stream => {\n if (!pc._remoteStreams) {\n pc._remoteStreams = [];\n }\n if (pc._remoteStreams.indexOf(stream) >= 0) {\n return;\n }\n pc._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n pc.dispatchEvent(event);\n });\n });\n }\n return origSetRemoteDescription.apply(pc, arguments);\n };\n }\n}\n\nexport function shimCallbacksAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n const prototype = window.RTCPeerConnection.prototype;\n const origCreateOffer = prototype.createOffer;\n const origCreateAnswer = prototype.createAnswer;\n const setLocalDescription = prototype.setLocalDescription;\n const setRemoteDescription = prototype.setRemoteDescription;\n const addIceCandidate = prototype.addIceCandidate;\n\n prototype.createOffer =\n function createOffer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateOffer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n prototype.createAnswer =\n function createAnswer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateAnswer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n let withCallback = function(description, successCallback, failureCallback) {\n const promise = setLocalDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setLocalDescription = withCallback;\n\n withCallback = function(description, successCallback, failureCallback) {\n const promise = setRemoteDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setRemoteDescription = withCallback;\n\n withCallback = function(candidate, successCallback, failureCallback) {\n const promise = addIceCandidate.apply(this, [candidate]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n // shim not needed in Safari 12.1\n const mediaDevices = navigator.mediaDevices;\n const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n navigator.mediaDevices.getUserMedia = (constraints) => {\n return _getUserMedia(shimConstraints(constraints));\n };\n }\n\n if (!navigator.getUserMedia && navigator.mediaDevices &&\n navigator.mediaDevices.getUserMedia) {\n navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(cb, errcb);\n }.bind(navigator);\n }\n}\n\nexport function shimConstraints(constraints) {\n if (constraints && constraints.video !== undefined) {\n return Object.assign({},\n constraints,\n {video: utils.compactObject(constraints.video)}\n );\n }\n\n return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n const OrigPeerConnection = window.RTCPeerConnection;\n window.RTCPeerConnection =\n function RTCPeerConnection(pcConfig, pcConstraints) {\n if (pcConfig && pcConfig.iceServers) {\n const newIceServers = [];\n for (let i = 0; i < pcConfig.iceServers.length; i++) {\n let server = pcConfig.iceServers[i];\n if (server.urls === undefined && server.url) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n server = JSON.parse(JSON.stringify(server));\n server.urls = server.url;\n delete server.url;\n newIceServers.push(server);\n } else {\n newIceServers.push(pcConfig.iceServers[i]);\n }\n }\n pcConfig.iceServers = newIceServers;\n }\n return new OrigPeerConnection(pcConfig, pcConstraints);\n };\n window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n // wrap static methods. Currently just generateCertificate.\n if ('generateCertificate' in OrigPeerConnection) {\n Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n get() {\n return OrigPeerConnection.generateCertificate;\n }\n });\n }\n}\n\nexport function shimTrackEventTransceiver(window) {\n // Add event.transceiver member over deprecated event.receiver\n if (typeof window === 'object' && window.RTCTrackEvent &&\n 'receiver' in window.RTCTrackEvent.prototype &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimCreateOfferLegacy(window) {\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer =\n function createOffer(offerOptions) {\n if (offerOptions) {\n if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveAudio =\n !!offerOptions.offerToReceiveAudio;\n }\n const audioTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'audio');\n if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n if (audioTransceiver.direction === 'sendrecv') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('sendonly');\n } else {\n audioTransceiver.direction = 'sendonly';\n }\n } else if (audioTransceiver.direction === 'recvonly') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('inactive');\n } else {\n audioTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveAudio === true &&\n !audioTransceiver) {\n this.addTransceiver('audio', {direction: 'recvonly'});\n }\n\n if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveVideo =\n !!offerOptions.offerToReceiveVideo;\n }\n const videoTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'video');\n if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n if (videoTransceiver.direction === 'sendrecv') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('sendonly');\n } else {\n videoTransceiver.direction = 'sendonly';\n }\n } else if (videoTransceiver.direction === 'recvonly') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('inactive');\n } else {\n videoTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveVideo === true &&\n !videoTransceiver) {\n this.addTransceiver('video', {direction: 'recvonly'});\n }\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimAudioContext(window) {\n if (typeof window !== 'object' || window.AudioContext) {\n return;\n }\n window.AudioContext = window.webkitAudioContext;\n}\n\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n // foundation is arbitrarily chosen as an indicator for full support for\n // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n const NativeRTCIceCandidate = window.RTCIceCandidate;\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n // Remove the a= which shouldn't be part of the candidate string.\n if (typeof args === 'object' && args.candidate &&\n args.candidate.indexOf('a=') === 0) {\n args = JSON.parse(JSON.stringify(args));\n args.candidate = args.candidate.substring(2);\n }\n\n if (args.candidate && args.candidate.length) {\n // Augment the native candidate with the parsed fields.\n const nativeCandidate = new NativeRTCIceCandidate(args);\n const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n for (const key in parsedCandidate) {\n if (!(key in nativeCandidate)) {\n Object.defineProperty(nativeCandidate, key,\n {value: parsedCandidate[key]});\n }\n }\n\n // Override serializer to not serialize the extra attributes.\n nativeCandidate.toJSON = function toJSON() {\n return {\n candidate: nativeCandidate.candidate,\n sdpMid: nativeCandidate.sdpMid,\n sdpMLineIndex: nativeCandidate.sdpMLineIndex,\n usernameFragment: nativeCandidate.usernameFragment,\n };\n };\n return nativeCandidate;\n }\n return new NativeRTCIceCandidate(args);\n };\n window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n Object.defineProperty(e, 'candidate', {\n value: new window.RTCIceCandidate(e.candidate),\n writable: 'false'\n });\n }\n return e;\n });\n}\n\nexport function shimRTCIceCandidateRelayProtocol(window) {\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'relayProtocol' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n const parsedCandidate = SDPUtils.parseCandidate(e.candidate.candidate);\n if (parsedCandidate.type === 'relay') {\n // This is a libwebrtc-specific mapping of local type preference\n // to relayProtocol.\n e.candidate.relayProtocol = {\n 0: 'tls',\n 1: 'tcp',\n 2: 'udp',\n }[parsedCandidate.priority >> 24];\n }\n }\n return e;\n });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n if (!('sctp' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n }\n });\n }\n\n const sctpInDescription = function(description) {\n if (!description || !description.sdp) {\n return false;\n }\n const sections = SDPUtils.splitSections(description.sdp);\n sections.shift();\n return sections.some(mediaSection => {\n const mLine = SDPUtils.parseMLine(mediaSection);\n return mLine && mLine.kind === 'application'\n && mLine.protocol.indexOf('SCTP') !== -1;\n });\n };\n\n const getRemoteFirefoxVersion = function(description) {\n // TODO: Is there a better solution for detecting Firefox?\n const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n if (match === null || match.length < 2) {\n return -1;\n }\n const version = parseInt(match[1], 10);\n // Test for NaN (yes, this is ugly)\n return version !== version ? -1 : version;\n };\n\n const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n // Every implementation we know can send at least 64 KiB.\n // Note: Although Chrome is technically able to send up to 256 KiB, the\n // data does not reach the other peer reliably.\n // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n let canSendMaxMessageSize = 65536;\n if (browserDetails.browser === 'firefox') {\n if (browserDetails.version < 57) {\n if (remoteIsFirefox === -1) {\n // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n // fragmentation.\n canSendMaxMessageSize = 16384;\n } else {\n // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n // messages. Thus, supporting ~2 GiB when sending.\n canSendMaxMessageSize = 2147483637;\n }\n } else if (browserDetails.version < 60) {\n // Currently, all FF >= 57 will reset the remote maximum message size\n // to the default value when a data channel is created at a later\n // stage. :(\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n canSendMaxMessageSize =\n browserDetails.version === 57 ? 65535 : 65536;\n } else {\n // FF >= 60 supports sending ~2 GiB\n canSendMaxMessageSize = 2147483637;\n }\n }\n return canSendMaxMessageSize;\n };\n\n const getMaxMessageSize = function(description, remoteIsFirefox) {\n // Note: 65536 bytes is the default value from the SDP spec. Also,\n // every implementation we know supports receiving 65536 bytes.\n let maxMessageSize = 65536;\n\n // FF 57 has a slightly incorrect default remote max message size, so\n // we need to adjust it here to avoid a failure when sending.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n if (browserDetails.browser === 'firefox'\n && browserDetails.version === 57) {\n maxMessageSize = 65535;\n }\n\n const match = SDPUtils.matchPrefix(description.sdp,\n 'a=max-message-size:');\n if (match.length > 0) {\n maxMessageSize = parseInt(match[0].substring(19), 10);\n } else if (browserDetails.browser === 'firefox' &&\n remoteIsFirefox !== -1) {\n // If the maximum message size is not present in the remote SDP and\n // both local and remote are Firefox, the remote peer can receive\n // ~2 GiB.\n maxMessageSize = 2147483637;\n }\n return maxMessageSize;\n };\n\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n this._sctp = null;\n // Chrome decided to not expose .sctp in plan-b mode.\n // As usual, adapter.js has to do an 'ugly worakaround'\n // to cover up the mess.\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n const {sdpSemantics} = this.getConfiguration();\n if (sdpSemantics === 'plan-b') {\n Object.defineProperty(this, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n if (sctpInDescription(arguments[0])) {\n // Check if the remote is FF.\n const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n // Get the maximum message size the local peer is capable of sending\n const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n // Get the maximum message size of the remote peer.\n const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n // Determine final maximum message size\n let maxMessageSize;\n if (canSendMMS === 0 && remoteMMS === 0) {\n maxMessageSize = Number.POSITIVE_INFINITY;\n } else if (canSendMMS === 0 || remoteMMS === 0) {\n maxMessageSize = Math.max(canSendMMS, remoteMMS);\n } else {\n maxMessageSize = Math.min(canSendMMS, remoteMMS);\n }\n\n // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n // attribute.\n const sctp = {};\n Object.defineProperty(sctp, 'maxMessageSize', {\n get() {\n return maxMessageSize;\n }\n });\n this._sctp = sctp;\n }\n\n return origSetRemoteDescription.apply(this, arguments);\n };\n}\n\nexport function shimSendThrowTypeError(window) {\n if (!(window.RTCPeerConnection &&\n 'createDataChannel' in window.RTCPeerConnection.prototype)) {\n return;\n }\n\n // Note: Although Firefox >= 57 has a native implementation, the maximum\n // message size can be reset for all data channels at a later stage.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n function wrapDcSend(dc, pc) {\n const origDataChannelSend = dc.send;\n dc.send = function send() {\n const data = arguments[0];\n const length = data.length || data.size || data.byteLength;\n if (dc.readyState === 'open' &&\n pc.sctp && length > pc.sctp.maxMessageSize) {\n throw new TypeError('Message too large (can send a maximum of ' +\n pc.sctp.maxMessageSize + ' bytes)');\n }\n return origDataChannelSend.apply(dc, arguments);\n };\n }\n const origCreateDataChannel =\n window.RTCPeerConnection.prototype.createDataChannel;\n window.RTCPeerConnection.prototype.createDataChannel =\n function createDataChannel() {\n const dataChannel = origCreateDataChannel.apply(this, arguments);\n wrapDcSend(dataChannel, this);\n return dataChannel;\n };\n utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n wrapDcSend(e.channel, e.target);\n return e;\n });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n if (!window.RTCPeerConnection ||\n 'connectionState' in window.RTCPeerConnection.prototype) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n Object.defineProperty(proto, 'connectionState', {\n get() {\n return {\n completed: 'connected',\n checking: 'connecting'\n }[this.iceConnectionState] || this.iceConnectionState;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(proto, 'onconnectionstatechange', {\n get() {\n return this._onconnectionstatechange || null;\n },\n set(cb) {\n if (this._onconnectionstatechange) {\n this.removeEventListener('connectionstatechange',\n this._onconnectionstatechange);\n delete this._onconnectionstatechange;\n }\n if (cb) {\n this.addEventListener('connectionstatechange',\n this._onconnectionstatechange = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n\n ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n const origMethod = proto[method];\n proto[method] = function() {\n if (!this._connectionstatechangepoly) {\n this._connectionstatechangepoly = e => {\n const pc = e.target;\n if (pc._lastConnectionState !== pc.connectionState) {\n pc._lastConnectionState = pc.connectionState;\n const newEvent = new Event('connectionstatechange', e);\n pc.dispatchEvent(newEvent);\n }\n return e;\n };\n this.addEventListener('iceconnectionstatechange',\n this._connectionstatechangepoly);\n }\n return origMethod.apply(this, arguments);\n };\n });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n if (!window.RTCPeerConnection) {\n return;\n }\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n return;\n }\n if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n return;\n }\n const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription(desc) {\n if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n const sdp = desc.sdp.split('\\n').filter((line) => {\n return line.trim() !== 'a=extmap-allow-mixed';\n }).join('\\n');\n // Safari enforces read-only-ness of RTCSessionDescription fields.\n if (window.RTCSessionDescription &&\n desc instanceof window.RTCSessionDescription) {\n arguments[0] = new window.RTCSessionDescription({\n type: desc.type,\n sdp,\n });\n } else {\n desc.sdp = sdp;\n }\n }\n return nativeSRD.apply(this, arguments);\n };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n // Support for addIceCandidate(null or undefined)\n // as well as addIceCandidate({candidate: \"\", ...})\n // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n // Note: must be called before other polyfills which change the signature.\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeAddIceCandidate =\n window.RTCPeerConnection.prototype.addIceCandidate;\n if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.addIceCandidate =\n function addIceCandidate() {\n if (!arguments[0]) {\n if (arguments[1]) {\n arguments[1].apply(null);\n }\n return Promise.resolve();\n }\n // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n // in older versions.\n // Native support for ignoring exists for Chrome M77+.\n // Safari ignores as well, exact version unknown but works in the same\n // version that also ignores addIceCandidate(null).\n if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n || (browserDetails.browser === 'firefox'\n && browserDetails.version < 68)\n || (browserDetails.browser === 'safari'))\n && arguments[0] && arguments[0].candidate === '') {\n return Promise.resolve();\n }\n return nativeAddIceCandidate.apply(this, arguments);\n };\n}\n\n// Note: Make sure to call this ahead of APIs that modify\n// setLocalDescription.length\nexport function shimParameterlessSetLocalDescription(window, browserDetails) {\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n let desc = arguments[0] || {};\n if (typeof desc !== 'object' || (desc.type && desc.sdp)) {\n return nativeSetLocalDescription.apply(this, arguments);\n }\n // The remaining steps should technically happen when SLD comes off the\n // RTCPeerConnection's operations chain (not ahead of going on it), but\n // this is too difficult to shim. Instead, this shim only covers the\n // common case where the operations chain is empty. This is imperfect, but\n // should cover many cases. Rationale: Even if we can't reduce the glare\n // window to zero on imperfect implementations, there's value in tapping\n // into the perfect negotiation pattern that several browsers support.\n desc = {type: desc.type, sdp: desc.sdp};\n if (!desc.type) {\n switch (this.signalingState) {\n case 'stable':\n case 'have-local-offer':\n case 'have-remote-pranswer':\n desc.type = 'offer';\n break;\n default:\n desc.type = 'answer';\n break;\n }\n }\n if (desc.sdp || (desc.type !== 'offer' && desc.type !== 'answer')) {\n return nativeSetLocalDescription.apply(this, [desc]);\n }\n const func = desc.type === 'offer' ? this.createOffer : this.createAnswer;\n return func.apply(this)\n .then(d => nativeSetLocalDescription.apply(this, [d]));\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\nimport * as utils from './utils';\n\n// Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\nimport * as sdp from 'sdp';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n shimChrome: true,\n shimFirefox: true,\n shimSafari: true,\n}) {\n // Utils.\n const logging = utils.log;\n const browserDetails = utils.detectBrowser(window);\n\n const adapter = {\n browserDetails,\n commonShim,\n extractVersion: utils.extractVersion,\n disableLog: utils.disableLog,\n disableWarnings: utils.disableWarnings,\n // Expose sdp as a convenience. For production apps include directly.\n sdp,\n };\n\n // Shim browser if found.\n switch (browserDetails.browser) {\n case 'chrome':\n if (!chromeShim || !chromeShim.shimPeerConnection ||\n !options.shimChrome) {\n logging('Chrome shim is not included in this adapter release.');\n return adapter;\n }\n if (browserDetails.version === null) {\n logging('Chrome shim can not determine version, not shimming.');\n return adapter;\n }\n logging('adapter.js shimming chrome.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = chromeShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n chromeShim.shimGetUserMedia(window, browserDetails);\n chromeShim.shimMediaStream(window, browserDetails);\n chromeShim.shimPeerConnection(window, browserDetails);\n chromeShim.shimOnTrack(window, browserDetails);\n chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n chromeShim.shimGetStats(window, browserDetails);\n chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n case 'firefox':\n if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n !options.shimFirefox) {\n logging('Firefox shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming firefox.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = firefoxShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n firefoxShim.shimGetUserMedia(window, browserDetails);\n firefoxShim.shimPeerConnection(window, browserDetails);\n firefoxShim.shimOnTrack(window, browserDetails);\n firefoxShim.shimRemoveStream(window, browserDetails);\n firefoxShim.shimSenderGetStats(window, browserDetails);\n firefoxShim.shimReceiverGetStats(window, browserDetails);\n firefoxShim.shimRTCDataChannel(window, browserDetails);\n firefoxShim.shimAddTransceiver(window, browserDetails);\n firefoxShim.shimGetParameters(window, browserDetails);\n firefoxShim.shimCreateOffer(window, browserDetails);\n firefoxShim.shimCreateAnswer(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'safari':\n if (!safariShim || !options.shimSafari) {\n logging('Safari shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming safari.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = safariShim;\n\n // Must be called before shimCallbackAPI.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n safariShim.shimRTCIceServerUrls(window, browserDetails);\n safariShim.shimCreateOfferLegacy(window, browserDetails);\n safariShim.shimCallbacksAPI(window, browserDetails);\n safariShim.shimLocalStreamsAPI(window, browserDetails);\n safariShim.shimRemoteStreamsAPI(window, browserDetails);\n safariShim.shimTrackEventTransceiver(window, browserDetails);\n safariShim.shimGetUserMedia(window, browserDetails);\n safariShim.shimAudioContext(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n default:\n logging('Unsupported browser!');\n break;\n }\n\n return adapter;\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n/**\n * GStreamer WebRTC configuration.\n *

The config can be passed on creation of the GstWebRTCAPI class.

\n *

For example:\n *

\n *     const signalingProtocol = window.location.protocol.startsWith(\"https\") ? \"wss\" : \"ws\";\n *     const api = new GstWebRTCAPI({\n *         meta: { name: `WebClient-${Date.now()}` },\n *         signalingServerUrl: `${signalingProtocol}://${window.location.host}/webrtc`\n *     });\n * 

\n * @typedef {object} GstWebRTCConfig\n * @property {object} meta=null - Client free-form information that will be exchanged with all peers through the\n * signaling meta property, its content depends on your application.\n * @property {string} signalingServerUrl=ws://127.0.0.1:8443 - The WebRTC signaling server URL.\n * @property {number} reconnectionTimeout=2500 - Timeout in milliseconds to reconnect to the signaling server in\n * case of an unexpected disconnection.\n * @property {object} webrtcConfig={iceServers...} - The WebRTC peer connection configuration passed to\n * {@link RTCPeerConnection}. Default configuration only includes a list of free STUN servers\n * (stun[0-4].l.google.com:19302).\n */\n\n/**\n * Default GstWebRTCAPI configuration.\n * @type {GstWebRTCConfig}\n */\nconst defaultConfig = Object.freeze({\n meta: null,\n signalingServerUrl: \"ws://127.0.0.1:8443\",\n reconnectionTimeout: 2500,\n webrtcConfig: {\n iceServers: [\n {\n urls: [\n \"stun:stun.l.google.com:19302\",\n \"stun:stun1.l.google.com:19302\"\n ]\n }\n ],\n bundlePolicy: \"max-bundle\"\n }\n});\n\nexport default defaultConfig;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n/**\n * Session states enumeration.
\n * Session state always increases from idle to closed and never switches backwards.\n * @enum {number}\n * @readonly\n */\nconst SessionState = /** @type {const} */ {\n /**\n * (0) Default state when creating a new session, goes to connecting when starting the session.\n */\n idle: 0,\n /**\n * (1) Session is trying to connect to remote peers, goes to streaming in case of\n * success or closed in case of error.\n */\n connecting: 1,\n /**\n * (2) Session is correctly connected to remote peers and currently streaming audio/video, goes\n * to closed when any peer closes the session.\n */\n streaming: 2,\n /**\n * (3) Session is closed and can be garbage collected, state will not change anymore.\n */\n closed: 3\n};\nObject.freeze(SessionState);\n\nexport default SessionState;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport SessionState from \"./session-state.js\";\n\n/** @import ProducerSession from \"./producer-session.js\"; */\n/** @import ClientSession from \"./producer-session.js\"; */\n\n/**\n * Event name: \"error\".
\n * Triggered when any kind of error occurs.\n *

When emitted by a session, it is in general an unrecoverable error. Normally, the session is automatically closed\n * but in the specific case of a {@link ProducerSession}, when the error occurs on an underlying\n * {@link ClientSession} between the producer session and a remote client consuming the streamed media,\n * then only the failing {@link ClientSession} is closed. The producer session can keep on serving the\n * other consumer peers.

\n * @event GstWebRTCAPI#ErrorEvent\n * @type {ErrorEvent}\n * @property {string} message - The error message.\n * @property {Error} error - The error exception.\n * @see WebRTCSession\n * @see RemoteController\n */\n/**\n * Event name: \"stateChanged\".
\n * Triggered each time a session state changes.\n * @event GstWebRTCAPI#StateChangedEvent\n * @type {Event}\n * @see WebRTCSession#state\n */\n/**\n * Event name: \"rtcPeerConnectionChanged\".
\n * Triggered each time a session internal {@link RTCPeerConnection} changes. This can occur during the session\n * connecting state when the peer-to-peer WebRTC connection is established, and when closing the\n * {@link WebRTCSession}.\n * @event GstWebRTCAPI#RTCPeerConnectionChangedEvent\n * @type {Event}\n * @see WebRTCSession#rtcPeerConnection\n */\n/**\n * Event name: \"closed\".
\n * Triggered when a session is definitively closed (then it can be garbage collected as session instances are not\n * reusable).\n * @event GstWebRTCAPI#ClosedEvent\n * @type {Event}\n */\n\n/**\n * @class WebRTCSession\n * @hideconstructor\n * @classdesc Manages a WebRTC session between a producer and a consumer (peer-to-peer channel).\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:StateChangedEvent}\n * @fires {@link GstWebRTCAPI#event:RTCPeerConnectionChangedEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @see ConsumerSession\n * @see ProducerSession\n * @see ClientSession\n */\nclass WebRTCSession extends EventTarget {\n constructor(peerId, comChannel) {\n super();\n\n this._peerId = peerId;\n this._sessionId = \"\";\n this._comChannel = comChannel;\n this._state = SessionState.idle;\n this._rtcPeerConnection = null;\n }\n\n /**\n * Unique identifier of the remote peer to which this session is connected.\n * @type {string}\n * @readonly\n */\n get peerId() {\n return this._peerId;\n }\n\n /**\n * Unique identifier of this session (defined by the signaling server).
\n * The local session ID equals \"\" until it is created on server side. This is done during the connection handshake.\n * The local session ID is guaranteed to be valid and to correctly reflect the signaling server value once\n * session state has switched to {@link SessionState#streaming}.\n * @type {string}\n * @readonly\n */\n get sessionId() {\n return this._sessionId;\n }\n\n /**\n * The current WebRTC session state.\n * @type {SessionState}\n * @readonly\n */\n get state() {\n return this._state;\n }\n\n /**\n * The internal {@link RTCPeerConnection} used to manage the underlying WebRTC connection with session\n * peer. Value may be null if session has no active WebRTC connection. You can listen to the\n * {@link GstWebRTCAPI#event:RTCPeerConnectionChangedEvent} event to be informed when the connection is established\n * or destroyed.\n * @type {RTCPeerConnection}\n * @readonly\n */\n get rtcPeerConnection() {\n return this._rtcPeerConnection;\n }\n\n /**\n * Terminates the WebRTC session.
\n * It immediately disconnects the remote peer attached to this session and unregisters the session from the\n * signaling server.\n */\n close() {\n if (this._state !== SessionState.closed) {\n if ((this._state !== SessionState.idle) && this._comChannel && this._sessionId) {\n this._comChannel.send({\n type: \"endSession\",\n sessionId: this._sessionId\n });\n }\n\n this._state = SessionState.closed;\n this.dispatchEvent(new Event(\"stateChanged\"));\n\n this._comChannel = null;\n\n if (this._rtcPeerConnection) {\n this._rtcPeerConnection.close();\n this._rtcPeerConnection = null;\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n }\n\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n}\n\nexport default WebRTCSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n// mapping is based on https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt\nconst uniToKeySyms = Object.freeze({\n 0x0020: \"space\",\n 0x0021: \"exclam\",\n 0x0022: \"quotedbl\",\n 0x0023: \"numbersign\",\n 0x0024: \"dollar\",\n 0x0025: \"percent\",\n 0x0026: \"ampersand\",\n 0x0027: \"apostrophe\",\n 0x0028: \"parenleft\",\n 0x0029: \"parenright\",\n 0x002a: \"asterisk\",\n 0x002b: \"plus\",\n 0x002c: \"comma\",\n 0x002d: \"minus\",\n 0x002e: \"period\",\n 0x002f: \"slash\",\n 0x0030: \"0\",\n 0x0031: \"1\",\n 0x0032: \"2\",\n 0x0033: \"3\",\n 0x0034: \"4\",\n 0x0035: \"5\",\n 0x0036: \"6\",\n 0x0037: \"7\",\n 0x0038: \"8\",\n 0x0039: \"9\",\n 0x003a: \"colon\",\n 0x003b: \"semicolon\",\n 0x003c: \"less\",\n 0x003d: \"equal\",\n 0x003e: \"greater\",\n 0x003f: \"question\",\n 0x0040: \"at\",\n 0x0041: \"A\",\n 0x0042: \"B\",\n 0x0043: \"C\",\n 0x0044: \"D\",\n 0x0045: \"E\",\n 0x0046: \"F\",\n 0x0047: \"G\",\n 0x0048: \"H\",\n 0x0049: \"I\",\n 0x004a: \"J\",\n 0x004b: \"K\",\n 0x004c: \"L\",\n 0x004d: \"M\",\n 0x004e: \"N\",\n 0x004f: \"O\",\n 0x0050: \"P\",\n 0x0051: \"Q\",\n 0x0052: \"R\",\n 0x0053: \"S\",\n 0x0054: \"T\",\n 0x0055: \"U\",\n 0x0056: \"V\",\n 0x0057: \"W\",\n 0x0058: \"X\",\n 0x0059: \"Y\",\n 0x005a: \"Z\",\n 0x005b: \"bracketleft\",\n 0x005c: \"backslash\",\n 0x005d: \"bracketright\",\n 0x005e: \"asciicircum\",\n 0x005f: \"underscore\",\n 0x0060: \"grave\",\n 0x0061: \"a\",\n 0x0062: \"b\",\n 0x0063: \"c\",\n 0x0064: \"d\",\n 0x0065: \"e\",\n 0x0066: \"f\",\n 0x0067: \"g\",\n 0x0068: \"h\",\n 0x0069: \"i\",\n 0x006a: \"j\",\n 0x006b: \"k\",\n 0x006c: \"l\",\n 0x006d: \"m\",\n 0x006e: \"n\",\n 0x006f: \"o\",\n 0x0070: \"p\",\n 0x0071: \"q\",\n 0x0072: \"r\",\n 0x0073: \"s\",\n 0x0074: \"t\",\n 0x0075: \"u\",\n 0x0076: \"v\",\n 0x0077: \"w\",\n 0x0078: \"x\",\n 0x0079: \"y\",\n 0x007a: \"z\",\n 0x007b: \"braceleft\",\n 0x007c: \"bar\",\n 0x007d: \"braceright\",\n 0x007e: \"asciitilde\",\n 0x00a0: \"nobreakspace\",\n 0x00a1: \"exclamdown\",\n 0x00a2: \"cent\",\n 0x00a3: \"sterling\",\n 0x00a4: \"currency\",\n 0x00a5: \"yen\",\n 0x00a6: \"brokenbar\",\n 0x00a7: \"section\",\n 0x00a8: \"diaeresis\",\n 0x00a9: \"copyright\",\n 0x00aa: \"ordfeminine\",\n 0x00ab: \"guillemotleft\",\n 0x00ac: \"notsign\",\n 0x00ad: \"hyphen\",\n 0x00ae: \"registered\",\n 0x00af: \"macron\",\n 0x00b0: \"degree\",\n 0x00b1: \"plusminus\",\n 0x00b2: \"twosuperior\",\n 0x00b3: \"threesuperior\",\n 0x00b4: \"acute\",\n 0x00b5: \"mu\",\n 0x00b6: \"paragraph\",\n 0x00b7: \"periodcentered\",\n 0x00b8: \"cedilla\",\n 0x00b9: \"onesuperior\",\n 0x00ba: \"masculine\",\n 0x00bb: \"guillemotright\",\n 0x00bc: \"onequarter\",\n 0x00bd: \"onehalf\",\n 0x00be: \"threequarters\",\n 0x00bf: \"questiondown\",\n 0x00c0: \"Agrave\",\n 0x00c1: \"Aacute\",\n 0x00c2: \"Acircumflex\",\n 0x00c3: \"Atilde\",\n 0x00c4: \"Adiaeresis\",\n 0x00c5: \"Aring\",\n 0x00c6: \"AE\",\n 0x00c7: \"Ccedilla\",\n 0x00c8: \"Egrave\",\n 0x00c9: \"Eacute\",\n 0x00ca: \"Ecircumflex\",\n 0x00cb: \"Ediaeresis\",\n 0x00cc: \"Igrave\",\n 0x00cd: \"Iacute\",\n 0x00ce: \"Icircumflex\",\n 0x00cf: \"Idiaeresis\",\n 0x00d0: \"ETH\",\n 0x00d1: \"Ntilde\",\n 0x00d2: \"Ograve\",\n 0x00d3: \"Oacute\",\n 0x00d4: \"Ocircumflex\",\n 0x00d5: \"Otilde\",\n 0x00d6: \"Odiaeresis\",\n 0x00d7: \"multiply\",\n 0x00d8: \"Ooblique\",\n 0x00d9: \"Ugrave\",\n 0x00da: \"Uacute\",\n 0x00db: \"Ucircumflex\",\n 0x00dc: \"Udiaeresis\",\n 0x00dd: \"Yacute\",\n 0x00de: \"THORN\",\n 0x00df: \"ssharp\",\n 0x00e0: \"agrave\",\n 0x00e1: \"aacute\",\n 0x00e2: \"acircumflex\",\n 0x00e3: \"atilde\",\n 0x00e4: \"adiaeresis\",\n 0x00e5: \"aring\",\n 0x00e6: \"ae\",\n 0x00e7: \"ccedilla\",\n 0x00e8: \"egrave\",\n 0x00e9: \"eacute\",\n 0x00ea: \"ecircumflex\",\n 0x00eb: \"ediaeresis\",\n 0x00ec: \"igrave\",\n 0x00ed: \"iacute\",\n 0x00ee: \"icircumflex\",\n 0x00ef: \"idiaeresis\",\n 0x00f0: \"eth\",\n 0x00f1: \"ntilde\",\n 0x00f2: \"ograve\",\n 0x00f3: \"oacute\",\n 0x00f4: \"ocircumflex\",\n 0x00f5: \"otilde\",\n 0x00f6: \"odiaeresis\",\n 0x00f7: \"division\",\n 0x00f8: \"oslash\",\n 0x00f9: \"ugrave\",\n 0x00fa: \"uacute\",\n 0x00fb: \"ucircumflex\",\n 0x00fc: \"udiaeresis\",\n 0x00fd: \"yacute\",\n 0x00fe: \"thorn\",\n 0x00ff: \"ydiaeresis\",\n 0x0104: \"Aogonek\",\n 0x02d8: \"breve\",\n 0x0141: \"Lstroke\",\n 0x013d: \"Lcaron\",\n 0x015a: \"Sacute\",\n 0x0160: \"Scaron\",\n 0x015e: \"Scedilla\",\n 0x0164: \"Tcaron\",\n 0x0179: \"Zacute\",\n 0x017d: \"Zcaron\",\n 0x017b: \"Zabovedot\",\n 0x0105: \"aogonek\",\n 0x02db: \"ogonek\",\n 0x0142: \"lstroke\",\n 0x013e: \"lcaron\",\n 0x015b: \"sacute\",\n 0x02c7: \"caron\",\n 0x0161: \"scaron\",\n 0x015f: \"scedilla\",\n 0x0165: \"tcaron\",\n 0x017a: \"zacute\",\n 0x02dd: \"doubleacute\",\n 0x017e: \"zcaron\",\n 0x017c: \"zabovedot\",\n 0x0154: \"Racute\",\n 0x0102: \"Abreve\",\n 0x0139: \"Lacute\",\n 0x0106: \"Cacute\",\n 0x010c: \"Ccaron\",\n 0x0118: \"Eogonek\",\n 0x011a: \"Ecaron\",\n 0x010e: \"Dcaron\",\n 0x0110: \"Dstroke\",\n 0x0143: \"Nacute\",\n 0x0147: \"Ncaron\",\n 0x0150: \"Odoubleacute\",\n 0x0158: \"Rcaron\",\n 0x016e: \"Uring\",\n 0x0170: \"Udoubleacute\",\n 0x0162: \"Tcedilla\",\n 0x0155: \"racute\",\n 0x0103: \"abreve\",\n 0x013a: \"lacute\",\n 0x0107: \"cacute\",\n 0x010d: \"ccaron\",\n 0x0119: \"eogonek\",\n 0x011b: \"ecaron\",\n 0x010f: \"dcaron\",\n 0x0111: \"dstroke\",\n 0x0144: \"nacute\",\n 0x0148: \"ncaron\",\n 0x0151: \"odoubleacute\",\n 0x0159: \"rcaron\",\n 0x016f: \"uring\",\n 0x0171: \"udoubleacute\",\n 0x0163: \"tcedilla\",\n 0x02d9: \"abovedot\",\n 0x0126: \"Hstroke\",\n 0x0124: \"Hcircumflex\",\n 0x0130: \"Iabovedot\",\n 0x011e: \"Gbreve\",\n 0x0134: \"Jcircumflex\",\n 0x0127: \"hstroke\",\n 0x0125: \"hcircumflex\",\n 0x0131: \"idotless\",\n 0x011f: \"gbreve\",\n 0x0135: \"jcircumflex\",\n 0x010a: \"Cabovedot\",\n 0x0108: \"Ccircumflex\",\n 0x0120: \"Gabovedot\",\n 0x011c: \"Gcircumflex\",\n 0x016c: \"Ubreve\",\n 0x015c: \"Scircumflex\",\n 0x010b: \"cabovedot\",\n 0x0109: \"ccircumflex\",\n 0x0121: \"gabovedot\",\n 0x011d: \"gcircumflex\",\n 0x016d: \"ubreve\",\n 0x015d: \"scircumflex\",\n 0x0138: \"kra\",\n 0x0156: \"Rcedilla\",\n 0x0128: \"Itilde\",\n 0x013b: \"Lcedilla\",\n 0x0112: \"Emacron\",\n 0x0122: \"Gcedilla\",\n 0x0166: \"Tslash\",\n 0x0157: \"rcedilla\",\n 0x0129: \"itilde\",\n 0x013c: \"lcedilla\",\n 0x0113: \"emacron\",\n 0x0123: \"gcedilla\",\n 0x0167: \"tslash\",\n 0x014a: \"ENG\",\n 0x014b: \"eng\",\n 0x0100: \"Amacron\",\n 0x012e: \"Iogonek\",\n 0x0116: \"Eabovedot\",\n 0x012a: \"Imacron\",\n 0x0145: \"Ncedilla\",\n 0x014c: \"Omacron\",\n 0x0136: \"Kcedilla\",\n 0x0172: \"Uogonek\",\n 0x0168: \"Utilde\",\n 0x016a: \"Umacron\",\n 0x0101: \"amacron\",\n 0x012f: \"iogonek\",\n 0x0117: \"eabovedot\",\n 0x012b: \"imacron\",\n 0x0146: \"ncedilla\",\n 0x014d: \"omacron\",\n 0x0137: \"kcedilla\",\n 0x0173: \"uogonek\",\n 0x0169: \"utilde\",\n 0x016b: \"umacron\",\n 0x203e: \"overline\",\n 0x3002: \"kana_fullstop\",\n 0x300c: \"kana_openingbracket\",\n 0x300d: \"kana_closingbracket\",\n 0x3001: \"kana_comma\",\n 0x30fb: \"kana_conjunctive\",\n 0x30f2: \"kana_WO\",\n 0x30a1: \"kana_a\",\n 0x30a3: \"kana_i\",\n 0x30a5: \"kana_u\",\n 0x30a7: \"kana_e\",\n 0x30a9: \"kana_o\",\n 0x30e3: \"kana_ya\",\n 0x30e5: \"kana_yu\",\n 0x30e7: \"kana_yo\",\n 0x30c3: \"kana_tsu\",\n 0x30fc: \"prolongedsound\",\n 0x30a2: \"kana_A\",\n 0x30a4: \"kana_I\",\n 0x30a6: \"kana_U\",\n 0x30a8: \"kana_E\",\n 0x30aa: \"kana_O\",\n 0x30ab: \"kana_KA\",\n 0x30ad: \"kana_KI\",\n 0x30af: \"kana_KU\",\n 0x30b1: \"kana_KE\",\n 0x30b3: \"kana_KO\",\n 0x30b5: \"kana_SA\",\n 0x30b7: \"kana_SHI\",\n 0x30b9: \"kana_SU\",\n 0x30bb: \"kana_SE\",\n 0x30bd: \"kana_SO\",\n 0x30bf: \"kana_TA\",\n 0x30c1: \"kana_CHI\",\n 0x30c4: \"kana_TSU\",\n 0x30c6: \"kana_TE\",\n 0x30c8: \"kana_TO\",\n 0x30ca: \"kana_NA\",\n 0x30cb: \"kana_NI\",\n 0x30cc: \"kana_NU\",\n 0x30cd: \"kana_NE\",\n 0x30ce: \"kana_NO\",\n 0x30cf: \"kana_HA\",\n 0x30d2: \"kana_HI\",\n 0x30d5: \"kana_FU\",\n 0x30d8: \"kana_HE\",\n 0x30db: \"kana_HO\",\n 0x30de: \"kana_MA\",\n 0x30df: \"kana_MI\",\n 0x30e0: \"kana_MU\",\n 0x30e1: \"kana_ME\",\n 0x30e2: \"kana_MO\",\n 0x30e4: \"kana_YA\",\n 0x30e6: \"kana_YU\",\n 0x30e8: \"kana_YO\",\n 0x30e9: \"kana_RA\",\n 0x30ea: \"kana_RI\",\n 0x30eb: \"kana_RU\",\n 0x30ec: \"kana_RE\",\n 0x30ed: \"kana_RO\",\n 0x30ef: \"kana_WA\",\n 0x30f3: \"kana_N\",\n 0x309b: \"voicedsound\",\n 0x309c: \"semivoicedsound\",\n 0x060c: \"Arabic_comma\",\n 0x061b: \"Arabic_semicolon\",\n 0x061f: \"Arabic_question_mark\",\n 0x0621: \"Arabic_hamza\",\n 0x0622: \"Arabic_maddaonalef\",\n 0x0623: \"Arabic_hamzaonalef\",\n 0x0624: \"Arabic_hamzaonwaw\",\n 0x0625: \"Arabic_hamzaunderalef\",\n 0x0626: \"Arabic_hamzaonyeh\",\n 0x0627: \"Arabic_alef\",\n 0x0628: \"Arabic_beh\",\n 0x0629: \"Arabic_tehmarbuta\",\n 0x062a: \"Arabic_teh\",\n 0x062b: \"Arabic_theh\",\n 0x062c: \"Arabic_jeem\",\n 0x062d: \"Arabic_hah\",\n 0x062e: \"Arabic_khah\",\n 0x062f: \"Arabic_dal\",\n 0x0630: \"Arabic_thal\",\n 0x0631: \"Arabic_ra\",\n 0x0632: \"Arabic_zain\",\n 0x0633: \"Arabic_seen\",\n 0x0634: \"Arabic_sheen\",\n 0x0635: \"Arabic_sad\",\n 0x0636: \"Arabic_dad\",\n 0x0637: \"Arabic_tah\",\n 0x0638: \"Arabic_zah\",\n 0x0639: \"Arabic_ain\",\n 0x063a: \"Arabic_ghain\",\n 0x0640: \"Arabic_tatweel\",\n 0x0641: \"Arabic_feh\",\n 0x0642: \"Arabic_qaf\",\n 0x0643: \"Arabic_kaf\",\n 0x0644: \"Arabic_lam\",\n 0x0645: \"Arabic_meem\",\n 0x0646: \"Arabic_noon\",\n 0x0647: \"Arabic_ha\",\n 0x0648: \"Arabic_waw\",\n 0x0649: \"Arabic_alefmaksura\",\n 0x064a: \"Arabic_yeh\",\n 0x064b: \"Arabic_fathatan\",\n 0x064c: \"Arabic_dammatan\",\n 0x064d: \"Arabic_kasratan\",\n 0x064e: \"Arabic_fatha\",\n 0x064f: \"Arabic_damma\",\n 0x0650: \"Arabic_kasra\",\n 0x0651: \"Arabic_shadda\",\n 0x0652: \"Arabic_sukun\",\n 0x0452: \"Serbian_dje\",\n 0x0453: \"Macedonia_gje\",\n 0x0451: \"Cyrillic_io\",\n 0x0454: \"Ukrainian_ie\",\n 0x0455: \"Macedonia_dse\",\n 0x0456: \"Ukrainian_i\",\n 0x0457: \"Ukrainian_yi\",\n 0x0458: \"Cyrillic_je\",\n 0x0459: \"Cyrillic_lje\",\n 0x045a: \"Cyrillic_nje\",\n 0x045b: \"Serbian_tshe\",\n 0x045c: \"Macedonia_kje\",\n 0x045e: \"Byelorussian_shortu\",\n 0x045f: \"Cyrillic_dzhe\",\n 0x2116: \"numerosign\",\n 0x0402: \"Serbian_DJE\",\n 0x0403: \"Macedonia_GJE\",\n 0x0401: \"Cyrillic_IO\",\n 0x0404: \"Ukrainian_IE\",\n 0x0405: \"Macedonia_DSE\",\n 0x0406: \"Ukrainian_I\",\n 0x0407: \"Ukrainian_YI\",\n 0x0408: \"Cyrillic_JE\",\n 0x0409: \"Cyrillic_LJE\",\n 0x040a: \"Cyrillic_NJE\",\n 0x040b: \"Serbian_TSHE\",\n 0x040c: \"Macedonia_KJE\",\n 0x040e: \"Byelorussian_SHORTU\",\n 0x040f: \"Cyrillic_DZHE\",\n 0x044e: \"Cyrillic_yu\",\n 0x0430: \"Cyrillic_a\",\n 0x0431: \"Cyrillic_be\",\n 0x0446: \"Cyrillic_tse\",\n 0x0434: \"Cyrillic_de\",\n 0x0435: \"Cyrillic_ie\",\n 0x0444: \"Cyrillic_ef\",\n 0x0433: \"Cyrillic_ghe\",\n 0x0445: \"Cyrillic_ha\",\n 0x0438: \"Cyrillic_i\",\n 0x0439: \"Cyrillic_shorti\",\n 0x043a: \"Cyrillic_ka\",\n 0x043b: \"Cyrillic_el\",\n 0x043c: \"Cyrillic_em\",\n 0x043d: \"Cyrillic_en\",\n 0x043e: \"Cyrillic_o\",\n 0x043f: \"Cyrillic_pe\",\n 0x044f: \"Cyrillic_ya\",\n 0x0440: \"Cyrillic_er\",\n 0x0441: \"Cyrillic_es\",\n 0x0442: \"Cyrillic_te\",\n 0x0443: \"Cyrillic_u\",\n 0x0436: \"Cyrillic_zhe\",\n 0x0432: \"Cyrillic_ve\",\n 0x044c: \"Cyrillic_softsign\",\n 0x044b: \"Cyrillic_yeru\",\n 0x0437: \"Cyrillic_ze\",\n 0x0448: \"Cyrillic_sha\",\n 0x044d: \"Cyrillic_e\",\n 0x0449: \"Cyrillic_shcha\",\n 0x0447: \"Cyrillic_che\",\n 0x044a: \"Cyrillic_hardsign\",\n 0x042e: \"Cyrillic_YU\",\n 0x0410: \"Cyrillic_A\",\n 0x0411: \"Cyrillic_BE\",\n 0x0426: \"Cyrillic_TSE\",\n 0x0414: \"Cyrillic_DE\",\n 0x0415: \"Cyrillic_IE\",\n 0x0424: \"Cyrillic_EF\",\n 0x0413: \"Cyrillic_GHE\",\n 0x0425: \"Cyrillic_HA\",\n 0x0418: \"Cyrillic_I\",\n 0x0419: \"Cyrillic_SHORTI\",\n 0x041a: \"Cyrillic_KA\",\n 0x041b: \"Cyrillic_EL\",\n 0x041c: \"Cyrillic_EM\",\n 0x041d: \"Cyrillic_EN\",\n 0x041e: \"Cyrillic_O\",\n 0x041f: \"Cyrillic_PE\",\n 0x042f: \"Cyrillic_YA\",\n 0x0420: \"Cyrillic_ER\",\n 0x0421: \"Cyrillic_ES\",\n 0x0422: \"Cyrillic_TE\",\n 0x0423: \"Cyrillic_U\",\n 0x0416: \"Cyrillic_ZHE\",\n 0x0412: \"Cyrillic_VE\",\n 0x042c: \"Cyrillic_SOFTSIGN\",\n 0x042b: \"Cyrillic_YERU\",\n 0x0417: \"Cyrillic_ZE\",\n 0x0428: \"Cyrillic_SHA\",\n 0x042d: \"Cyrillic_E\",\n 0x0429: \"Cyrillic_SHCHA\",\n 0x0427: \"Cyrillic_CHE\",\n 0x042a: \"Cyrillic_HARDSIGN\",\n 0x0386: \"Greek_ALPHAaccent\",\n 0x0388: \"Greek_EPSILONaccent\",\n 0x0389: \"Greek_ETAaccent\",\n 0x038a: \"Greek_IOTAaccent\",\n 0x03aa: \"Greek_IOTAdiaeresis\",\n 0x038c: \"Greek_OMICRONaccent\",\n 0x038e: \"Greek_UPSILONaccent\",\n 0x03ab: \"Greek_UPSILONdieresis\",\n 0x038f: \"Greek_OMEGAaccent\",\n 0x0385: \"Greek_accentdieresis\",\n 0x2015: \"Greek_horizbar\",\n 0x03ac: \"Greek_alphaaccent\",\n 0x03ad: \"Greek_epsilonaccent\",\n 0x03ae: \"Greek_etaaccent\",\n 0x03af: \"Greek_iotaaccent\",\n 0x03ca: \"Greek_iotadieresis\",\n 0x0390: \"Greek_iotaaccentdieresis\",\n 0x03cc: \"Greek_omicronaccent\",\n 0x03cd: \"Greek_upsilonaccent\",\n 0x03cb: \"Greek_upsilondieresis\",\n 0x03b0: \"Greek_upsilonaccentdieresis\",\n 0x03ce: \"Greek_omegaaccent\",\n 0x0391: \"Greek_ALPHA\",\n 0x0392: \"Greek_BETA\",\n 0x0393: \"Greek_GAMMA\",\n 0x0394: \"Greek_DELTA\",\n 0x0395: \"Greek_EPSILON\",\n 0x0396: \"Greek_ZETA\",\n 0x0397: \"Greek_ETA\",\n 0x0398: \"Greek_THETA\",\n 0x0399: \"Greek_IOTA\",\n 0x039a: \"Greek_KAPPA\",\n 0x039b: \"Greek_LAMBDA\",\n 0x039c: \"Greek_MU\",\n 0x039d: \"Greek_NU\",\n 0x039e: \"Greek_XI\",\n 0x039f: \"Greek_OMICRON\",\n 0x03a0: \"Greek_PI\",\n 0x03a1: \"Greek_RHO\",\n 0x03a3: \"Greek_SIGMA\",\n 0x03a4: \"Greek_TAU\",\n 0x03a5: \"Greek_UPSILON\",\n 0x03a6: \"Greek_PHI\",\n 0x03a7: \"Greek_CHI\",\n 0x03a8: \"Greek_PSI\",\n 0x03a9: \"Greek_OMEGA\",\n 0x03b1: \"Greek_alpha\",\n 0x03b2: \"Greek_beta\",\n 0x03b3: \"Greek_gamma\",\n 0x03b4: \"Greek_delta\",\n 0x03b5: \"Greek_epsilon\",\n 0x03b6: \"Greek_zeta\",\n 0x03b7: \"Greek_eta\",\n 0x03b8: \"Greek_theta\",\n 0x03b9: \"Greek_iota\",\n 0x03ba: \"Greek_kappa\",\n 0x03bb: \"Greek_lambda\",\n 0x03bc: \"Greek_mu\",\n 0x03bd: \"Greek_nu\",\n 0x03be: \"Greek_xi\",\n 0x03bf: \"Greek_omicron\",\n 0x03c0: \"Greek_pi\",\n 0x03c1: \"Greek_rho\",\n 0x03c3: \"Greek_sigma\",\n 0x03c2: \"Greek_finalsmallsigma\",\n 0x03c4: \"Greek_tau\",\n 0x03c5: \"Greek_upsilon\",\n 0x03c6: \"Greek_phi\",\n 0x03c7: \"Greek_chi\",\n 0x03c8: \"Greek_psi\",\n 0x03c9: \"Greek_omega\",\n 0x23b7: \"leftradical\",\n 0x2320: \"topintegral\",\n 0x2321: \"botintegral\",\n 0x23a1: \"topleftsqbracket\",\n 0x23a3: \"botleftsqbracket\",\n 0x23a4: \"toprightsqbracket\",\n 0x23a6: \"botrightsqbracket\",\n 0x239b: \"topleftparens\",\n 0x239d: \"botleftparens\",\n 0x239e: \"toprightparens\",\n 0x23a0: \"botrightparens\",\n 0x23a8: \"leftmiddlecurlybrace\",\n 0x23ac: \"rightmiddlecurlybrace\",\n 0x2264: \"lessthanequal\",\n 0x2260: \"notequal\",\n 0x2265: \"greaterthanequal\",\n 0x222b: \"integral\",\n 0x2234: \"therefore\",\n 0x221d: \"variation\",\n 0x221e: \"infinity\",\n 0x2207: \"nabla\",\n 0x223c: \"approximate\",\n 0x2243: \"similarequal\",\n 0x21d4: \"ifonlyif\",\n 0x21d2: \"implies\",\n 0x2261: \"identical\",\n 0x221a: \"radical\",\n 0x2282: \"includedin\",\n 0x2283: \"includes\",\n 0x2229: \"intersection\",\n 0x222a: \"union\",\n 0x2227: \"logicaland\",\n 0x2228: \"logicalor\",\n 0x2202: \"partialderivative\",\n 0x0192: \"function\",\n 0x2190: \"leftarrow\",\n 0x2191: \"uparrow\",\n 0x2192: \"rightarrow\",\n 0x2193: \"downarrow\",\n 0x25c6: \"soliddiamond\",\n 0x2592: \"checkerboard\",\n 0x2409: \"ht\",\n 0x240c: \"ff\",\n 0x240d: \"cr\",\n 0x240a: \"lf\",\n 0x2424: \"nl\",\n 0x240b: \"vt\",\n 0x2518: \"lowrightcorner\",\n 0x2510: \"uprightcorner\",\n 0x250c: \"upleftcorner\",\n 0x2514: \"lowleftcorner\",\n 0x253c: \"crossinglines\",\n 0x23ba: \"horizlinescan1\",\n 0x23bb: \"horizlinescan3\",\n 0x2500: \"horizlinescan5\",\n 0x23bc: \"horizlinescan7\",\n 0x23bd: \"horizlinescan9\",\n 0x251c: \"leftt\",\n 0x2524: \"rightt\",\n 0x2534: \"bott\",\n 0x252c: \"topt\",\n 0x2502: \"vertbar\",\n 0x2003: \"emspace\",\n 0x2002: \"enspace\",\n 0x2004: \"em3space\",\n 0x2005: \"em4space\",\n 0x2007: \"digitspace\",\n 0x2008: \"punctspace\",\n 0x2009: \"thinspace\",\n 0x200a: \"hairspace\",\n 0x2014: \"emdash\",\n 0x2013: \"endash\",\n 0x2423: \"signifblank\",\n 0x2026: \"ellipsis\",\n 0x2025: \"doubbaselinedot\",\n 0x2153: \"onethird\",\n 0x2154: \"twothirds\",\n 0x2155: \"onefifth\",\n 0x2156: \"twofifths\",\n 0x2157: \"threefifths\",\n 0x2158: \"fourfifths\",\n 0x2159: \"onesixth\",\n 0x215a: \"fivesixths\",\n 0x2105: \"careof\",\n 0x2012: \"figdash\",\n 0x27e8: \"leftanglebracket\",\n 0x27e9: \"rightanglebracket\",\n 0x215b: \"oneeighth\",\n 0x215c: \"threeeighths\",\n 0x215d: \"fiveeighths\",\n 0x215e: \"seveneighths\",\n 0x2122: \"trademark\",\n 0x2613: \"signaturemark\",\n 0x25c1: \"leftopentriangle\",\n 0x25b7: \"rightopentriangle\",\n 0x25af: \"emopenrectangle\",\n 0x2018: \"leftsinglequotemark\",\n 0x2019: \"rightsinglequotemark\",\n 0x201c: \"leftdoublequotemark\",\n 0x201d: \"rightdoublequotemark\",\n 0x211e: \"prescription\",\n 0x2032: \"minutes\",\n 0x2033: \"seconds\",\n 0x271d: \"latincross\",\n 0x25ac: \"filledrectbullet\",\n 0x25c0: \"filledlefttribullet\",\n 0x25b6: \"filledrighttribullet\",\n 0x25cf: \"emfilledcircle\",\n 0x25ae: \"emfilledrect\",\n 0x25e6: \"enopencircbullet\",\n 0x25ab: \"enopensquarebullet\",\n 0x25ad: \"openrectbullet\",\n 0x25b3: \"opentribulletup\",\n 0x25bd: \"opentribulletdown\",\n 0x2606: \"openstar\",\n 0x2022: \"enfilledcircbullet\",\n 0x25aa: \"enfilledsqbullet\",\n 0x25b2: \"filledtribulletup\",\n 0x25bc: \"filledtribulletdown\",\n 0x261c: \"leftpointer\",\n 0x261e: \"rightpointer\",\n 0x2663: \"club\",\n 0x2666: \"diamond\",\n 0x2665: \"heart\",\n 0x2720: \"maltesecross\",\n 0x2020: \"dagger\",\n 0x2021: \"doubledagger\",\n 0x2713: \"checkmark\",\n 0x2717: \"ballotcross\",\n 0x266f: \"musicalsharp\",\n 0x266d: \"musicalflat\",\n 0x2642: \"malesymbol\",\n 0x2640: \"femalesymbol\",\n 0x260e: \"telephone\",\n 0x2315: \"telephonerecorder\",\n 0x2117: \"phonographcopyright\",\n 0x2038: \"caret\",\n 0x201a: \"singlelowquotemark\",\n 0x201e: \"doublelowquotemark\",\n 0x22a5: \"downtack\",\n 0x230a: \"downstile\",\n 0x2218: \"jot\",\n 0x2395: \"quad\",\n 0x22a4: \"uptack\",\n 0x25cb: \"circle\",\n 0x2308: \"upstile\",\n 0x22a2: \"lefttack\",\n 0x22a3: \"righttack\",\n 0x2017: \"hebrew_doublelowline\",\n 0x05d0: \"hebrew_aleph\",\n 0x05d1: \"hebrew_beth\",\n 0x05d2: \"hebrew_gimmel\",\n 0x05d3: \"hebrew_daleth\",\n 0x05d4: \"hebrew_he\",\n 0x05d5: \"hebrew_waw\",\n 0x05d6: \"hebrew_zayin\",\n 0x05d7: \"hebrew_het\",\n 0x05d8: \"hebrew_teth\",\n 0x05d9: \"hebrew_yod\",\n 0x05da: \"hebrew_finalkaph\",\n 0x05db: \"hebrew_kaph\",\n 0x05dc: \"hebrew_lamed\",\n 0x05dd: \"hebrew_finalmem\",\n 0x05de: \"hebrew_mem\",\n 0x05df: \"hebrew_finalnun\",\n 0x05e0: \"hebrew_nun\",\n 0x05e1: \"hebrew_samekh\",\n 0x05e2: \"hebrew_ayin\",\n 0x05e3: \"hebrew_finalpe\",\n 0x05e4: \"hebrew_pe\",\n 0x05e5: \"hebrew_finalzadi\",\n 0x05e6: \"hebrew_zadi\",\n 0x05e7: \"hebrew_qoph\",\n 0x05e8: \"hebrew_resh\",\n 0x05e9: \"hebrew_shin\",\n 0x05ea: \"hebrew_taw\",\n 0x0e01: \"Thai_kokai\",\n 0x0e02: \"Thai_khokhai\",\n 0x0e03: \"Thai_khokhuat\",\n 0x0e04: \"Thai_khokhwai\",\n 0x0e05: \"Thai_khokhon\",\n 0x0e06: \"Thai_khorakhang\",\n 0x0e07: \"Thai_ngongu\",\n 0x0e08: \"Thai_chochan\",\n 0x0e09: \"Thai_choching\",\n 0x0e0a: \"Thai_chochang\",\n 0x0e0b: \"Thai_soso\",\n 0x0e0c: \"Thai_chochoe\",\n 0x0e0d: \"Thai_yoying\",\n 0x0e0e: \"Thai_dochada\",\n 0x0e0f: \"Thai_topatak\",\n 0x0e10: \"Thai_thothan\",\n 0x0e11: \"Thai_thonangmontho\",\n 0x0e12: \"Thai_thophuthao\",\n 0x0e13: \"Thai_nonen\",\n 0x0e14: \"Thai_dodek\",\n 0x0e15: \"Thai_totao\",\n 0x0e16: \"Thai_thothung\",\n 0x0e17: \"Thai_thothahan\",\n 0x0e18: \"Thai_thothong\",\n 0x0e19: \"Thai_nonu\",\n 0x0e1a: \"Thai_bobaimai\",\n 0x0e1b: \"Thai_popla\",\n 0x0e1c: \"Thai_phophung\",\n 0x0e1d: \"Thai_fofa\",\n 0x0e1e: \"Thai_phophan\",\n 0x0e1f: \"Thai_fofan\",\n 0x0e20: \"Thai_phosamphao\",\n 0x0e21: \"Thai_moma\",\n 0x0e22: \"Thai_yoyak\",\n 0x0e23: \"Thai_rorua\",\n 0x0e24: \"Thai_ru\",\n 0x0e25: \"Thai_loling\",\n 0x0e26: \"Thai_lu\",\n 0x0e27: \"Thai_wowaen\",\n 0x0e28: \"Thai_sosala\",\n 0x0e29: \"Thai_sorusi\",\n 0x0e2a: \"Thai_sosua\",\n 0x0e2b: \"Thai_hohip\",\n 0x0e2c: \"Thai_lochula\",\n 0x0e2d: \"Thai_oang\",\n 0x0e2e: \"Thai_honokhuk\",\n 0x0e2f: \"Thai_paiyannoi\",\n 0x0e30: \"Thai_saraa\",\n 0x0e31: \"Thai_maihanakat\",\n 0x0e32: \"Thai_saraaa\",\n 0x0e33: \"Thai_saraam\",\n 0x0e34: \"Thai_sarai\",\n 0x0e35: \"Thai_saraii\",\n 0x0e36: \"Thai_saraue\",\n 0x0e37: \"Thai_sarauee\",\n 0x0e38: \"Thai_sarau\",\n 0x0e39: \"Thai_sarauu\",\n 0x0e3a: \"Thai_phinthu\",\n 0x0e3f: \"Thai_baht\",\n 0x0e40: \"Thai_sarae\",\n 0x0e41: \"Thai_saraae\",\n 0x0e42: \"Thai_sarao\",\n 0x0e43: \"Thai_saraaimaimuan\",\n 0x0e44: \"Thai_saraaimaimalai\",\n 0x0e45: \"Thai_lakkhangyao\",\n 0x0e46: \"Thai_maiyamok\",\n 0x0e47: \"Thai_maitaikhu\",\n 0x0e48: \"Thai_maiek\",\n 0x0e49: \"Thai_maitho\",\n 0x0e4a: \"Thai_maitri\",\n 0x0e4b: \"Thai_maichattawa\",\n 0x0e4c: \"Thai_thanthakhat\",\n 0x0e4d: \"Thai_nikhahit\",\n 0x0e50: \"Thai_leksun\",\n 0x0e51: \"Thai_leknung\",\n 0x0e52: \"Thai_leksong\",\n 0x0e53: \"Thai_leksam\",\n 0x0e54: \"Thai_leksi\",\n 0x0e55: \"Thai_lekha\",\n 0x0e56: \"Thai_lekhok\",\n 0x0e57: \"Thai_lekchet\",\n 0x0e58: \"Thai_lekpaet\",\n 0x0e59: \"Thai_lekkao\",\n 0x3131: \"Hangul_Kiyeog\",\n 0x3132: \"Hangul_SsangKiyeog\",\n 0x3133: \"Hangul_KiyeogSios\",\n 0x3134: \"Hangul_Nieun\",\n 0x3135: \"Hangul_NieunJieuj\",\n 0x3136: \"Hangul_NieunHieuh\",\n 0x3137: \"Hangul_Dikeud\",\n 0x3138: \"Hangul_SsangDikeud\",\n 0x3139: \"Hangul_Rieul\",\n 0x313a: \"Hangul_RieulKiyeog\",\n 0x313b: \"Hangul_RieulMieum\",\n 0x313c: \"Hangul_RieulPieub\",\n 0x313d: \"Hangul_RieulSios\",\n 0x313e: \"Hangul_RieulTieut\",\n 0x313f: \"Hangul_RieulPhieuf\",\n 0x3140: \"Hangul_RieulHieuh\",\n 0x3141: \"Hangul_Mieum\",\n 0x3142: \"Hangul_Pieub\",\n 0x3143: \"Hangul_SsangPieub\",\n 0x3144: \"Hangul_PieubSios\",\n 0x3145: \"Hangul_Sios\",\n 0x3146: \"Hangul_SsangSios\",\n 0x3147: \"Hangul_Ieung\",\n 0x3148: \"Hangul_Jieuj\",\n 0x3149: \"Hangul_SsangJieuj\",\n 0x314a: \"Hangul_Cieuc\",\n 0x314b: \"Hangul_Khieuq\",\n 0x314c: \"Hangul_Tieut\",\n 0x314d: \"Hangul_Phieuf\",\n 0x314e: \"Hangul_Hieuh\",\n 0x314f: \"Hangul_A\",\n 0x3150: \"Hangul_AE\",\n 0x3151: \"Hangul_YA\",\n 0x3152: \"Hangul_YAE\",\n 0x3153: \"Hangul_EO\",\n 0x3154: \"Hangul_E\",\n 0x3155: \"Hangul_YEO\",\n 0x3156: \"Hangul_YE\",\n 0x3157: \"Hangul_O\",\n 0x3158: \"Hangul_WA\",\n 0x3159: \"Hangul_WAE\",\n 0x315a: \"Hangul_OE\",\n 0x315b: \"Hangul_YO\",\n 0x315c: \"Hangul_U\",\n 0x315d: \"Hangul_WEO\",\n 0x315e: \"Hangul_WE\",\n 0x315f: \"Hangul_WI\",\n 0x3160: \"Hangul_YU\",\n 0x3161: \"Hangul_EU\",\n 0x3162: \"Hangul_YI\",\n 0x3163: \"Hangul_I\",\n 0x11a8: \"Hangul_J_Kiyeog\",\n 0x11a9: \"Hangul_J_SsangKiyeog\",\n 0x11aa: \"Hangul_J_KiyeogSios\",\n 0x11ab: \"Hangul_J_Nieun\",\n 0x11ac: \"Hangul_J_NieunJieuj\",\n 0x11ad: \"Hangul_J_NieunHieuh\",\n 0x11ae: \"Hangul_J_Dikeud\",\n 0x11af: \"Hangul_J_Rieul\",\n 0x11b0: \"Hangul_J_RieulKiyeog\",\n 0x11b1: \"Hangul_J_RieulMieum\",\n 0x11b2: \"Hangul_J_RieulPieub\",\n 0x11b3: \"Hangul_J_RieulSios\",\n 0x11b4: \"Hangul_J_RieulTieut\",\n 0x11b5: \"Hangul_J_RieulPhieuf\",\n 0x11b6: \"Hangul_J_RieulHieuh\",\n 0x11b7: \"Hangul_J_Mieum\",\n 0x11b8: \"Hangul_J_Pieub\",\n 0x11b9: \"Hangul_J_PieubSios\",\n 0x11ba: \"Hangul_J_Sios\",\n 0x11bb: \"Hangul_J_SsangSios\",\n 0x11bc: \"Hangul_J_Ieung\",\n 0x11bd: \"Hangul_J_Jieuj\",\n 0x11be: \"Hangul_J_Cieuc\",\n 0x11bf: \"Hangul_J_Khieuq\",\n 0x11c0: \"Hangul_J_Tieut\",\n 0x11c1: \"Hangul_J_Phieuf\",\n 0x11c2: \"Hangul_J_Hieuh\",\n 0x316d: \"Hangul_RieulYeorinHieuh\",\n 0x3171: \"Hangul_SunkyeongeumMieum\",\n 0x3178: \"Hangul_SunkyeongeumPieub\",\n 0x317f: \"Hangul_PanSios\",\n 0x3181: \"Hangul_KkogjiDalrinIeung\",\n 0x3184: \"Hangul_SunkyeongeumPhieuf\",\n 0x3186: \"Hangul_YeorinHieuh\",\n 0x318d: \"Hangul_AraeA\",\n 0x318e: \"Hangul_AraeAE\",\n 0x11eb: \"Hangul_J_PanSios\",\n 0x11f0: \"Hangul_J_KkogjiDalrinIeung\",\n 0x11f9: \"Hangul_J_YeorinHieuh\",\n 0x0152: \"OE\",\n 0x0153: \"oe\",\n 0x0178: \"Ydiaeresis\",\n 0x20a0: \"EcuSign\",\n 0x20a1: \"ColonSign\",\n 0x20a2: \"CruzeiroSign\",\n 0x20a3: \"FFrancSign\",\n 0x20a4: \"LiraSign\",\n 0x20a5: \"MillSign\",\n 0x20a6: \"NairaSign\",\n 0x20a7: \"PesetaSign\",\n 0x20a8: \"RupeeSign\",\n 0x20a9: \"WonSign\",\n 0x20aa: \"NewSheqelSign\",\n 0x20ab: \"DongSign\",\n 0x20ac: \"EuroSign\",\n 0x0300: \"dead_grave\",\n 0x0301: \"dead_acute\",\n 0x0302: \"dead_circumflex\",\n 0x0303: \"dead_tilde\",\n 0x0304: \"dead_macron\",\n 0x0306: \"dead_breve\",\n 0x0307: \"dead_abovedot\",\n 0x0308: \"dead_diaeresis\",\n 0x030a: \"dead_abovering\",\n 0x030b: \"dead_doubleacute\",\n 0x030c: \"dead_caron\",\n 0x0327: \"dead_cedilla\",\n 0x0328: \"dead_ogonek\",\n 0x0345: \"dead_iota\",\n 0x3099: \"dead_voiced_sound\",\n 0x309a: \"dead_semivoiced_sound\",\n 0x0008: \"BackSpace\",\n 0x0009: \"Tab\",\n 0x000a: \"Linefeed\",\n 0x000b: \"Clear\",\n 0x000d: \"Return\",\n 0x0013: \"Pause\",\n 0x0014: \"Scroll_Lock\",\n 0x0015: \"Sys_Req\",\n 0x001b: \"Escape\",\n 0x0491: \"Ukrainian_ghe_with_upturn\",\n 0x0490: \"Ukrainian_GHE_WITH_UPTURN\",\n 0x0587: \"Armenian_ligature_ew\",\n 0x0589: \"Armenian_verjaket\",\n 0x055d: \"Armenian_but\",\n 0x058a: \"Armenian_yentamna\",\n 0x055c: \"Armenian_amanak\",\n 0x055b: \"Armenian_shesht\",\n 0x055e: \"Armenian_paruyk\",\n 0x0531: \"Armenian_AYB\",\n 0x0561: \"Armenian_ayb\",\n 0x0532: \"Armenian_BEN\",\n 0x0562: \"Armenian_ben\",\n 0x0533: \"Armenian_GIM\",\n 0x0563: \"Armenian_gim\",\n 0x0534: \"Armenian_DA\",\n 0x0564: \"Armenian_da\",\n 0x0535: \"Armenian_YECH\",\n 0x0565: \"Armenian_yech\",\n 0x0536: \"Armenian_ZA\",\n 0x0566: \"Armenian_za\",\n 0x0537: \"Armenian_E\",\n 0x0567: \"Armenian_e\",\n 0x0538: \"Armenian_AT\",\n 0x0568: \"Armenian_at\",\n 0x0539: \"Armenian_TO\",\n 0x0569: \"Armenian_to\",\n 0x053a: \"Armenian_ZHE\",\n 0x056a: \"Armenian_zhe\",\n 0x053b: \"Armenian_INI\",\n 0x056b: \"Armenian_ini\",\n 0x053c: \"Armenian_LYUN\",\n 0x056c: \"Armenian_lyun\",\n 0x053d: \"Armenian_KHE\",\n 0x056d: \"Armenian_khe\",\n 0x053e: \"Armenian_TSA\",\n 0x056e: \"Armenian_tsa\",\n 0x053f: \"Armenian_KEN\",\n 0x056f: \"Armenian_ken\",\n 0x0540: \"Armenian_HO\",\n 0x0570: \"Armenian_ho\",\n 0x0541: \"Armenian_DZA\",\n 0x0571: \"Armenian_dza\",\n 0x0542: \"Armenian_GHAT\",\n 0x0572: \"Armenian_ghat\",\n 0x0543: \"Armenian_TCHE\",\n 0x0573: \"Armenian_tche\",\n 0x0544: \"Armenian_MEN\",\n 0x0574: \"Armenian_men\",\n 0x0545: \"Armenian_HI\",\n 0x0575: \"Armenian_hi\",\n 0x0546: \"Armenian_NU\",\n 0x0576: \"Armenian_nu\",\n 0x0547: \"Armenian_SHA\",\n 0x0577: \"Armenian_sha\",\n 0x0548: \"Armenian_VO\",\n 0x0578: \"Armenian_vo\",\n 0x0549: \"Armenian_CHA\",\n 0x0579: \"Armenian_cha\",\n 0x054a: \"Armenian_PE\",\n 0x057a: \"Armenian_pe\",\n 0x054b: \"Armenian_JE\",\n 0x057b: \"Armenian_je\",\n 0x054c: \"Armenian_RA\",\n 0x057c: \"Armenian_ra\",\n 0x054d: \"Armenian_SE\",\n 0x057d: \"Armenian_se\",\n 0x054e: \"Armenian_VEV\",\n 0x057e: \"Armenian_vev\",\n 0x054f: \"Armenian_TYUN\",\n 0x057f: \"Armenian_tyun\",\n 0x0550: \"Armenian_RE\",\n 0x0580: \"Armenian_re\",\n 0x0551: \"Armenian_TSO\",\n 0x0581: \"Armenian_tso\",\n 0x0552: \"Armenian_VYUN\",\n 0x0582: \"Armenian_vyun\",\n 0x0553: \"Armenian_PYUR\",\n 0x0583: \"Armenian_pyur\",\n 0x0554: \"Armenian_KE\",\n 0x0584: \"Armenian_ke\",\n 0x0555: \"Armenian_O\",\n 0x0585: \"Armenian_o\",\n 0x0556: \"Armenian_FE\",\n 0x0586: \"Armenian_fe\",\n 0x055a: \"Armenian_apostrophe\",\n 0x10d0: \"Georgian_an\",\n 0x10d1: \"Georgian_ban\",\n 0x10d2: \"Georgian_gan\",\n 0x10d3: \"Georgian_don\",\n 0x10d4: \"Georgian_en\",\n 0x10d5: \"Georgian_vin\",\n 0x10d6: \"Georgian_zen\",\n 0x10d7: \"Georgian_tan\",\n 0x10d8: \"Georgian_in\",\n 0x10d9: \"Georgian_kan\",\n 0x10da: \"Georgian_las\",\n 0x10db: \"Georgian_man\",\n 0x10dc: \"Georgian_nar\",\n 0x10dd: \"Georgian_on\",\n 0x10de: \"Georgian_par\",\n 0x10df: \"Georgian_zhar\",\n 0x10e0: \"Georgian_rae\",\n 0x10e1: \"Georgian_san\",\n 0x10e2: \"Georgian_tar\",\n 0x10e3: \"Georgian_un\",\n 0x10e4: \"Georgian_phar\",\n 0x10e5: \"Georgian_khar\",\n 0x10e6: \"Georgian_ghan\",\n 0x10e7: \"Georgian_qar\",\n 0x10e8: \"Georgian_shin\",\n 0x10e9: \"Georgian_chin\",\n 0x10ea: \"Georgian_can\",\n 0x10eb: \"Georgian_jil\",\n 0x10ec: \"Georgian_cil\",\n 0x10ed: \"Georgian_char\",\n 0x10ee: \"Georgian_xan\",\n 0x10ef: \"Georgian_jhan\",\n 0x10f0: \"Georgian_hae\",\n 0x10f1: \"Georgian_he\",\n 0x10f2: \"Georgian_hie\",\n 0x10f3: \"Georgian_we\",\n 0x10f4: \"Georgian_har\",\n 0x10f5: \"Georgian_hoe\",\n 0x10f6: \"Georgian_fi\",\n 0x1e02: \"Babovedot\",\n 0x1e03: \"babovedot\",\n 0x1e0a: \"Dabovedot\",\n 0x1e80: \"Wgrave\",\n 0x1e82: \"Wacute\",\n 0x1e0b: \"dabovedot\",\n 0x1ef2: \"Ygrave\",\n 0x1e1e: \"Fabovedot\",\n 0x1e1f: \"fabovedot\",\n 0x1e40: \"Mabovedot\",\n 0x1e41: \"mabovedot\",\n 0x1e56: \"Pabovedot\",\n 0x1e81: \"wgrave\",\n 0x1e57: \"pabovedot\",\n 0x1e83: \"wacute\",\n 0x1e60: \"Sabovedot\",\n 0x1ef3: \"ygrave\",\n 0x1e84: \"Wdiaeresis\",\n 0x1e85: \"wdiaeresis\",\n 0x1e61: \"sabovedot\",\n 0x0174: \"Wcircumflex\",\n 0x1e6a: \"Tabovedot\",\n 0x0176: \"Ycircumflex\",\n 0x0175: \"wcircumflex\",\n 0x1e6b: \"tabovedot\",\n 0x0177: \"ycircumflex\",\n 0x06f0: \"Farsi_0\",\n 0x06f1: \"Farsi_1\",\n 0x06f2: \"Farsi_2\",\n 0x06f3: \"Farsi_3\",\n 0x06f4: \"Farsi_4\",\n 0x06f5: \"Farsi_5\",\n 0x06f6: \"Farsi_6\",\n 0x06f7: \"Farsi_7\",\n 0x06f8: \"Farsi_8\",\n 0x06f9: \"Farsi_9\",\n 0x066a: \"Arabic_percent\",\n 0x0670: \"Arabic_superscript_alef\",\n 0x0679: \"Arabic_tteh\",\n 0x067e: \"Arabic_peh\",\n 0x0686: \"Arabic_tcheh\",\n 0x0688: \"Arabic_ddal\",\n 0x0691: \"Arabic_rreh\",\n 0x06d4: \"Arabic_fullstop\",\n 0x0660: \"Arabic_0\",\n 0x0661: \"Arabic_1\",\n 0x0662: \"Arabic_2\",\n 0x0663: \"Arabic_3\",\n 0x0664: \"Arabic_4\",\n 0x0665: \"Arabic_5\",\n 0x0666: \"Arabic_6\",\n 0x0667: \"Arabic_7\",\n 0x0668: \"Arabic_8\",\n 0x0669: \"Arabic_9\",\n 0x0653: \"Arabic_madda_above\",\n 0x0654: \"Arabic_hamza_above\",\n 0x0655: \"Arabic_hamza_below\",\n 0x0698: \"Arabic_jeh\",\n 0x06a4: \"Arabic_veh\",\n 0x06a9: \"Arabic_keheh\",\n 0x06af: \"Arabic_gaf\",\n 0x06ba: \"Arabic_noon_ghunna\",\n 0x06be: \"Arabic_heh_doachashmee\",\n 0x06cc: \"Farsi_yeh\",\n 0x06d2: \"Arabic_yeh_baree\",\n 0x06c1: \"Arabic_heh_goal\",\n 0x0492: \"Cyrillic_GHE_bar\",\n 0x0496: \"Cyrillic_ZHE_descender\",\n 0x049a: \"Cyrillic_KA_descender\",\n 0x049c: \"Cyrillic_KA_vertstroke\",\n 0x04a2: \"Cyrillic_EN_descender\",\n 0x04ae: \"Cyrillic_U_straight\",\n 0x04b0: \"Cyrillic_U_straight_bar\",\n 0x04b2: \"Cyrillic_HA_descender\",\n 0x04b6: \"Cyrillic_CHE_descender\",\n 0x04b8: \"Cyrillic_CHE_vertstroke\",\n 0x04ba: \"Cyrillic_SHHA\",\n 0x04d8: \"Cyrillic_SCHWA\",\n 0x04e2: \"Cyrillic_I_macron\",\n 0x04e8: \"Cyrillic_O_bar\",\n 0x04ee: \"Cyrillic_U_macron\",\n 0x0493: \"Cyrillic_ghe_bar\",\n 0x0497: \"Cyrillic_zhe_descender\",\n 0x049b: \"Cyrillic_ka_descender\",\n 0x049d: \"Cyrillic_ka_vertstroke\",\n 0x04a3: \"Cyrillic_en_descender\",\n 0x04af: \"Cyrillic_u_straight\",\n 0x04b1: \"Cyrillic_u_straight_bar\",\n 0x04b3: \"Cyrillic_ha_descender\",\n 0x04b7: \"Cyrillic_che_descender\",\n 0x04b9: \"Cyrillic_che_vertstroke\",\n 0x04bb: \"Cyrillic_shha\",\n 0x04d9: \"Cyrillic_schwa\",\n 0x04e3: \"Cyrillic_i_macron\",\n 0x04e9: \"Cyrillic_o_bar\",\n 0x04ef: \"Cyrillic_u_macron\",\n 0x1e8a: \"Xabovedot\",\n 0x012c: \"Ibreve\",\n 0x01b5: \"Zstroke\",\n 0x01e6: \"Gcaron\",\n 0x019f: \"Obarred\",\n 0x1e8b: \"xabovedot\",\n 0x012d: \"ibreve\",\n 0x01b6: \"zstroke\",\n 0x01e7: \"gcaron\",\n 0x01d2: \"ocaron\",\n 0x0275: \"obarred\",\n 0x018f: \"SCHWA\",\n 0x0259: \"schwa\",\n 0x1e36: \"Lbelowdot\",\n 0x1e37: \"lbelowdot\",\n 0x1ea0: \"Abelowdot\",\n 0x1ea1: \"abelowdot\",\n 0x1ea2: \"Ahook\",\n 0x1ea3: \"ahook\",\n 0x1ea4: \"Acircumflexacute\",\n 0x1ea5: \"acircumflexacute\",\n 0x1ea6: \"Acircumflexgrave\",\n 0x1ea7: \"acircumflexgrave\",\n 0x1ea8: \"Acircumflexhook\",\n 0x1ea9: \"acircumflexhook\",\n 0x1eaa: \"Acircumflextilde\",\n 0x1eab: \"acircumflextilde\",\n 0x1eac: \"Acircumflexbelowdot\",\n 0x1ead: \"acircumflexbelowdot\",\n 0x1eae: \"Abreveacute\",\n 0x1eaf: \"abreveacute\",\n 0x1eb0: \"Abrevegrave\",\n 0x1eb1: \"abrevegrave\",\n 0x1eb2: \"Abrevehook\",\n 0x1eb3: \"abrevehook\",\n 0x1eb4: \"Abrevetilde\",\n 0x1eb5: \"abrevetilde\",\n 0x1eb6: \"Abrevebelowdot\",\n 0x1eb7: \"abrevebelowdot\",\n 0x1eb8: \"Ebelowdot\",\n 0x1eb9: \"ebelowdot\",\n 0x1eba: \"Ehook\",\n 0x1ebb: \"ehook\",\n 0x1ebc: \"Etilde\",\n 0x1ebd: \"etilde\",\n 0x1ebe: \"Ecircumflexacute\",\n 0x1ebf: \"ecircumflexacute\",\n 0x1ec0: \"Ecircumflexgrave\",\n 0x1ec1: \"ecircumflexgrave\",\n 0x1ec2: \"Ecircumflexhook\",\n 0x1ec3: \"ecircumflexhook\",\n 0x1ec4: \"Ecircumflextilde\",\n 0x1ec5: \"ecircumflextilde\",\n 0x1ec6: \"Ecircumflexbelowdot\",\n 0x1ec7: \"ecircumflexbelowdot\",\n 0x1ec8: \"Ihook\",\n 0x1ec9: \"ihook\",\n 0x1eca: \"Ibelowdot\",\n 0x1ecb: \"ibelowdot\",\n 0x1ecc: \"Obelowdot\",\n 0x1ecd: \"obelowdot\",\n 0x1ece: \"Ohook\",\n 0x1ecf: \"ohook\",\n 0x1ed0: \"Ocircumflexacute\",\n 0x1ed1: \"ocircumflexacute\",\n 0x1ed2: \"Ocircumflexgrave\",\n 0x1ed3: \"ocircumflexgrave\",\n 0x1ed4: \"Ocircumflexhook\",\n 0x1ed5: \"ocircumflexhook\",\n 0x1ed6: \"Ocircumflextilde\",\n 0x1ed7: \"ocircumflextilde\",\n 0x1ed8: \"Ocircumflexbelowdot\",\n 0x1ed9: \"ocircumflexbelowdot\",\n 0x1eda: \"Ohornacute\",\n 0x1edb: \"ohornacute\",\n 0x1edc: \"Ohorngrave\",\n 0x1edd: \"ohorngrave\",\n 0x1ede: \"Ohornhook\",\n 0x1edf: \"ohornhook\",\n 0x1ee0: \"Ohorntilde\",\n 0x1ee1: \"ohorntilde\",\n 0x1ee2: \"Ohornbelowdot\",\n 0x1ee3: \"ohornbelowdot\",\n 0x1ee4: \"Ubelowdot\",\n 0x1ee5: \"ubelowdot\",\n 0x1ee6: \"Uhook\",\n 0x1ee7: \"uhook\",\n 0x1ee8: \"Uhornacute\",\n 0x1ee9: \"uhornacute\",\n 0x1eea: \"Uhorngrave\",\n 0x1eeb: \"uhorngrave\",\n 0x1eec: \"Uhornhook\",\n 0x1eed: \"uhornhook\",\n 0x1eee: \"Uhorntilde\",\n 0x1eef: \"uhorntilde\",\n 0x1ef0: \"Uhornbelowdot\",\n 0x1ef1: \"uhornbelowdot\",\n 0x1ef4: \"Ybelowdot\",\n 0x1ef5: \"ybelowdot\",\n 0x1ef6: \"Yhook\",\n 0x1ef7: \"yhook\",\n 0x1ef8: \"Ytilde\",\n 0x1ef9: \"ytilde\",\n 0x01a0: \"Ohorn\",\n 0x01a1: \"ohorn\",\n 0x01af: \"Uhorn\",\n 0x01b0: \"uhorn\",\n 0x0323: \"dead_belowdot\",\n 0x0309: \"dead_hook\",\n 0x031b: \"dead_horn\"\n});\n\n// mapping is based on https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values\nconst kbEventCodesToKeySyms = Object.freeze({\n \"AltLeft\": \"Alt_L\",\n \"AltRight\": \"Alt_R\",\n \"ArrowDown\": \"Down\",\n \"ArrowLeft\": \"Left\",\n \"ArrowRight\": \"Right\",\n \"ArrowUp\": \"Up\",\n \"Backspace\": \"BackSpace\",\n \"CapsLock\": \"Caps_Lock\",\n \"ControlLeft\": \"Control_L\",\n \"ControlRight\": \"Control_R\",\n \"Enter\": \"Return\",\n \"HyperLeft\": \"Hyper_L\",\n \"HyperRight\": \"Hyper_R\",\n \"NumLock\": \"Num_Lock\",\n \"NumpadEnter\": \"Return\",\n \"MetaLeft\": \"Meta_L\",\n \"MetaRight\": \"Meta_R\",\n \"PageDown\": \"Page_Down\",\n \"PageUp\": \"Page_Up\",\n \"ScrollLock\": \"Scroll_Lock\",\n \"ShiftLeft\": \"Shift_L\",\n \"ShiftRight\": \"Shift_R\",\n \"SuperLeft\": \"Super_L\",\n \"SuperRight\": \"Super_R\"\n});\n\n// these Keyboard Event codes direclty map to X11 Keysyms\nconst knownKbEventCodes = new Set([\n \"Clear\", \"Copy\", \"Cut\", \"Delete\", \"End\", \"F1\", \"F2\", \"F3\", \"F4\", \"F5\", \"F6\", \"F7\", \"F8\", \"F9\", \"F10\", \"F11\", \"F12\",\n \"Home\", \"Insert\", \"Paste\", \"Redo\", \"Tab\", \"Undo\"\n]);\n\n/**\n * Returns X11 keySym (defined in https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt) for a given key/code from the key[up/down] event\n * For keys with length 1 (ASCII, Cyrillic etc) - uses uniToKeySyms mapping\n * For keys with length > 1 (Modifiers, Whitespaces, Navigation etc) - uses knownKbEventCodes and kbEventCodesToKeySyms mapping\n *\n * @param {string} key 'key' from the key[up/down] event\n * @param {string} code 'code' from the key[up/down] event\n * @return {string} keySymString (X11 keysym string)\n */\nexport default function getKeysymString(key, code) {\n var keySym = \"Unidentified\";\n if (key.length === 1) {\n const keyCodeUni = key.charCodeAt(0);\n if (keyCodeUni in uniToKeySyms) {\n keySym = uniToKeySyms[keyCodeUni];\n }\n } else if (code in kbEventCodesToKeySyms) {\n keySym = kbEventCodesToKeySyms[code];\n } else if (knownKbEventCodes.has(code)) {\n keySym = code;\n }\n return keySym;\n}\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport getKeysymString from \"./keysyms.js\";\n\n/** @import ConsumerSession from \"./consumer-session.js\"; */\n\nconst eventsNames = Object.freeze([\n \"wheel\",\n \"contextmenu\",\n \"mousemove\",\n \"mousedown\",\n \"mouseup\",\n \"touchstart\",\n \"touchend\",\n \"touchmove\",\n \"touchcancel\",\n \"keyup\",\n \"keydown\"\n]);\n\nconst mouseEventsNames = Object.freeze({\n mousemove: \"MouseMove\",\n mousedown: \"MouseButtonPress\",\n mouseup: \"MouseButtonRelease\"\n});\n\nconst touchEventsNames = Object.freeze({\n touchstart: \"TouchDown\",\n touchend: \"TouchUp\",\n touchmove: \"TouchMotion\",\n touchcancel: \"TouchUp\"\n});\n\nconst keyboardEventsNames = Object.freeze({\n keydown: \"KeyPress\",\n keyup: \"KeyRelease\"\n});\n\nfunction getModifiers(event) {\n const modifiers = [];\n if (event.altKey) {\n modifiers.push(\"mod1-mask\");\n }\n\n if (event.ctrlKey) {\n modifiers.push(\"control-mask\");\n }\n\n if (event.metaKey) {\n modifiers.push(\"meta-mask\");\n }\n\n if (event.shiftKey) {\n modifiers.push(\"shift-mask\");\n }\n\n return modifiers.join(\"+\");\n}\n\n/**\n * Event name: \"info\".
\n * Triggered when a remote peer sends an information message over the control data channel.\n * @event GstWebRTCAPI#InfoEvent\n * @type {CustomEvent}\n * @property {object} detail - The info message\n * @see RemoteController\n */\n/**\n * Event name: \"controlResponse\".
\n * Triggered when a remote peer sends a response after a control request.\n * @event GstWebRTCAPI#ControlResponseEvent\n * @type {CustomEvent}\n * @property {object} detail - The response message\n * @see RemoteController\n */\n\n/**\n * @class RemoteController\n * @hideconstructor\n * @classdesc Manages a specific WebRTC data channel created by a remote GStreamer webrtcsink producer and offering\n * remote control of the producer through\n * [GstNavigation]{@link https://gstreamer.freedesktop.org/documentation/video/gstnavigation.html} events.\n *

The remote control data channel is created by the GStreamer webrtcsink element on the producer side. Then it is\n * announced through the consumer session thanks to the {@link gstWebRTCAPI#event:RemoteControllerChangedEvent}\n * event.

\n *

You can attach an {@link HTMLVideoElement} to the remote controller, then all mouse and keyboard events\n * emitted by this element will be automatically relayed to the remote producer.

\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @fires {@link GstWebRTCAPI#event:InfoEvent}\n * @fires {@link GstWebRTCAPI#event:ControlResponseEvent}\n * @see ConsumerSession#remoteController\n * @see RemoteController#attachVideoElement\n * @see https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/gstwebrtc-api#produce-a-gstreamer-interactive-webrtc-stream-with-remote-control\n */\nclass RemoteController extends EventTarget {\n constructor(rtcDataChannel, consumerSession) {\n super();\n\n this._rtcDataChannel = rtcDataChannel;\n this._consumerSession = consumerSession;\n\n this._videoElement = null;\n this._videoElementComputedStyle = null;\n this._videoElementKeyboard = null;\n this._lastTouchEventTimestamp = 0;\n this._requestCounter = 0;\n\n rtcDataChannel.addEventListener(\"close\", () => {\n if (this._rtcDataChannel === rtcDataChannel) {\n this.close();\n }\n });\n\n rtcDataChannel.addEventListener(\"error\", (event) => {\n if (this._rtcDataChannel === rtcDataChannel) {\n const error = event.error;\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: (error && error.message) || \"Remote controller error\",\n error: error || new Error(\"unknown error on the remote controller data channel\")\n }));\n }\n });\n\n rtcDataChannel.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(event.data);\n\n if (msg.type === \"ControlResponseMessage\") {\n this.dispatchEvent(new CustomEvent(\"controlResponse\", { detail: msg }));\n } else if (msg.type === \"InfoMessage\") {\n this.dispatchEvent(new CustomEvent(\"info\", { detail: msg }));\n }\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot parse control message from signaling server\",\n error: ex\n }));\n }\n });\n }\n\n\n /**\n * The underlying WebRTC data channel connected to a remote GStreamer webrtcsink producer offering remote control.\n * The value may be null if the remote controller has been closed.\n * @type {RTCDataChannel}\n * @readonly\n */\n get rtcDataChannel() {\n return this._rtcDataChannel;\n }\n\n /**\n * The consumer session associated with this remote controller.\n * @type {ConsumerSession}\n * @readonly\n */\n get consumerSession() {\n return this._consumerSession;\n }\n\n /**\n * The video element that is currently used to send all mouse and keyboard events to the remote producer. Value may\n * be null if no video element is attached.\n * @type {HTMLVideoElement}\n * @readonly\n * @see RemoteController#attachVideoElement\n */\n get videoElement() {\n return this._videoElement;\n }\n\n /**\n * Associates a video element with this remote controller.
\n * When a video element is attached to this remote controller, all mouse and keyboard events emitted by this\n * element will be sent to the remote GStreamer webrtcink producer.\n * @param {HTMLVideoElement|null} element - the video element to use to relay mouse and keyboard events,\n * or null to detach any previously attached element. If the provided element parameter is not null and not a\n * valid instance of an {@link HTMLVideoElement}, then the method does nothing.\n */\n attachVideoElement(element) {\n if ((element instanceof HTMLVideoElement) && (element !== this._videoElement)) {\n if (this._videoElement) {\n this.attachVideoElement(null);\n }\n\n this._videoElement = element;\n this._videoElementComputedStyle = window.getComputedStyle(element);\n\n for (const eventName of eventsNames) {\n element.addEventListener(eventName, this);\n }\n\n element.setAttribute(\"tabindex\", \"0\");\n } else if ((element === null) && this._videoElement) {\n const previousElement = this._videoElement;\n previousElement.removeAttribute(\"tabindex\");\n\n this._videoElement = null;\n this._videoElementComputedStyle = null;\n\n this._lastTouchEventTimestamp = 0;\n\n for (const eventName of eventsNames) {\n previousElement.removeEventListener(eventName, this);\n }\n }\n }\n\n /**\n * Send a request over the control data channel.
\n *\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @param {object|string} request - The request to send over the channel\n * @returns {number} The identifier attributed to the request, or -1 if an exception occurred\n */\n sendControlRequest(request) {\n try {\n if (!request || ((typeof (request) !== \"object\") && (typeof (request) !== \"string\"))) {\n throw new Error(\"invalid request\");\n }\n\n if (!this._rtcDataChannel) {\n throw new Error(\"remote controller data channel is closed\");\n }\n\n let message = {\n id: this._requestCounter++,\n request: request\n };\n\n this._rtcDataChannel.send(JSON.stringify(message));\n\n return message.id;\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: `cannot send control message over session ${this._consumerSession.sessionId} remote controller`,\n error: ex\n }));\n return -1;\n }\n }\n\n /**\n * Closes the remote controller channel.
\n * It immediately shuts down the underlying WebRTC data channel connected to a remote GStreamer webrtcsink\n * producer and detaches any video element that may be used to relay mouse and keyboard events.\n */\n close() {\n this.attachVideoElement(null);\n\n const rtcDataChannel = this._rtcDataChannel;\n this._rtcDataChannel = null;\n\n if (rtcDataChannel) {\n rtcDataChannel.close();\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n\n _sendGstNavigationEvent(data) {\n let request = {\n type: \"navigationEvent\",\n event: data\n };\n this.sendControlRequest(request);\n }\n\n _computeVideoMousePosition(event) {\n const mousePos = { x: 0, y: 0 };\n if (!this._videoElement || (this._videoElement.videoWidth <= 0) || (this._videoElement.videoHeight <= 0)) {\n return mousePos;\n }\n\n const padding = {\n left: parseFloat(this._videoElementComputedStyle.paddingLeft),\n right: parseFloat(this._videoElementComputedStyle.paddingRight),\n top: parseFloat(this._videoElementComputedStyle.paddingTop),\n bottom: parseFloat(this._videoElementComputedStyle.paddingBottom)\n };\n\n if ((\"offsetX\" in event) && (\"offsetY\" in event)) {\n mousePos.x = event.offsetX - padding.left;\n mousePos.y = event.offsetY - padding.top;\n } else {\n const clientRect = this._videoElement.getBoundingClientRect();\n const border = {\n left: parseFloat(this._videoElementComputedStyle.borderLeftWidth),\n top: parseFloat(this._videoElementComputedStyle.borderTopWidth)\n };\n mousePos.x = event.clientX - clientRect.left - border.left - padding.left;\n mousePos.y = event.clientY - clientRect.top - border.top - padding.top;\n }\n\n const videoOffset = {\n x: this._videoElement.clientWidth - (padding.left + padding.right),\n y: this._videoElement.clientHeight - (padding.top + padding.bottom)\n };\n\n const ratio = Math.min(videoOffset.x / this._videoElement.videoWidth, videoOffset.y / this._videoElement.videoHeight);\n videoOffset.x = Math.max(0.5 * (videoOffset.x - this._videoElement.videoWidth * ratio), 0);\n videoOffset.y = Math.max(0.5 * (videoOffset.y - this._videoElement.videoHeight * ratio), 0);\n\n const invRatio = (ratio !== 0) ? (1 / ratio) : 0;\n mousePos.x = (mousePos.x - videoOffset.x) * invRatio;\n mousePos.y = (mousePos.y - videoOffset.y) * invRatio;\n\n mousePos.x = Math.min(Math.max(mousePos.x, 0), this._videoElement.videoWidth);\n mousePos.y = Math.min(Math.max(mousePos.y, 0), this._videoElement.videoHeight);\n\n return mousePos;\n }\n\n handleEvent(event) {\n if (!this._videoElement) {\n return;\n }\n\n switch (event.type) {\n case \"wheel\":\n event.preventDefault();\n {\n const mousePos = this._computeVideoMousePosition(event);\n this._sendGstNavigationEvent({\n event: \"MouseScroll\",\n x: mousePos.x,\n y: mousePos.y,\n delta_x: -event.deltaX, // eslint-disable-line camelcase\n delta_y: -event.deltaY, // eslint-disable-line camelcase\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n });\n }\n break;\n\n case \"contextmenu\":\n event.preventDefault();\n break;\n\n case \"mousemove\":\n case \"mousedown\":\n case \"mouseup\":\n event.preventDefault();\n {\n const mousePos = this._computeVideoMousePosition(event);\n const data = {\n event: mouseEventsNames[event.type],\n x: mousePos.x,\n y: mousePos.y,\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n\n if (event.type !== \"mousemove\") {\n data.button = event.button + 1;\n\n if ((event.type === \"mousedown\") && (event.button === 0)) {\n this._videoElement.focus();\n }\n }\n\n this._sendGstNavigationEvent(data);\n }\n break;\n\n case \"touchstart\":\n case \"touchend\":\n case \"touchmove\":\n case \"touchcancel\":\n for (const touch of event.changedTouches) {\n const mousePos = this._computeVideoMousePosition(touch);\n const data = {\n event: touchEventsNames[event.type],\n identifier: touch.identifier,\n x: mousePos.x,\n y: mousePos.y,\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n\n if ((\"force\" in touch) && ((event.type === \"touchstart\") || (event.type === \"touchmove\"))) {\n data.pressure = touch.force;\n }\n\n this._sendGstNavigationEvent(data);\n }\n\n if (event.timeStamp > this._lastTouchEventTimestamp) {\n this._lastTouchEventTimestamp = event.timeStamp;\n this._sendGstNavigationEvent({\n event: \"TouchFrame\",\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n });\n }\n break;\n case \"keyup\":\n case \"keydown\":\n event.preventDefault();\n {\n const data = {\n event: keyboardEventsNames[event.type],\n key: getKeysymString(event.key, event.code),\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n this._sendGstNavigationEvent(data);\n }\n break;\n }\n }\n}\n\nexport default RemoteController;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport WebRTCSession from \"./webrtc-session.js\";\nimport SessionState from \"./session-state.js\";\nimport RemoteController from \"./remote-controller.js\";\n\n/**\n * Event name: \"streamsChanged\".
\n * Triggered when the underlying media streams of a {@link ConsumerSession} change.\n * @event GstWebRTCAPI#StreamsChangedEvent\n * @type {Event}\n * @see ConsumerSession#streams\n */\n/**\n * Event name: \"remoteControllerChanged\".
\n * Triggered when the underlying remote controller of a {@link ConsumerSession} changes.\n * @event GstWebRTCAPI#RemoteControllerChangedEvent\n * @type {Event}\n * @see ConsumerSession#remoteController\n */\n\n/**\n * @class ConsumerSession\n * @hideconstructor\n * @classdesc Consumer session managing a peer-to-peer WebRTC channel between a remote producer and this client\n * instance.\n *

Call {@link GstWebRTCAPI#createConsumerSession} to create a ConsumerSession instance.

\n * @extends {WebRTCSession}\n * @fires {@link GstWebRTCAPI#event:StreamsChangedEvent}\n * @fires {@link GstWebRTCAPI#event:RemoteControllerChangedEvent}\n */\nclass ConsumerSession extends WebRTCSession {\n constructor(peerId, comChannel, offerOptions) {\n super(peerId, comChannel);\n this._streams = [];\n this._remoteController = null;\n this._pendingCandidates = [];\n this._mungeStereoHack = false;\n\n this._offerOptions = offerOptions;\n\n this.addEventListener(\"closed\", () => {\n this._streams = [];\n this._pendingCandidates = [];\n\n if (this._remoteController) {\n this._remoteController.close();\n }\n });\n }\n\n /**\n * Defines whether the SDP should be munged in order to enable stereo with chrome.\n * @method\n * @param {boolean} enable - Enable or disable the hack, default is false\n */\n set mungeStereoHack(enable) {\n if (typeof (enable) !== \"boolean\") { return; }\n\n this._mungeStereoHack = enable;\n }\n\n /**\n * The array of remote media streams consumed locally through this WebRTC channel.\n * @type {MediaStream[]}\n * @readonly\n */\n get streams() {\n return this._streams;\n }\n\n /**\n * The remote controller associated with this WebRTC consumer session. Value may be null if consumer session\n * has no remote controller.\n * @type {RemoteController}\n * @readonly\n */\n get remoteController() {\n return this._remoteController;\n }\n\n /**\n * Connects the consumer session to its remote producer.
\n * This method must be called after creating the consumer session in order to start receiving the remote streams.\n * It registers this consumer session to the signaling server and gets ready to receive audio/video streams.\n *

Even on success, streaming can fail later if any error occurs during or after connection. In order to know\n * the effective streaming state, you should be listening to the [error]{@link GstWebRTCAPI#event:ErrorEvent},\n * [stateChanged]{@link GstWebRTCAPI#event:StateChangedEvent} and/or [closed]{@link GstWebRTCAPI#event:ClosedEvent}\n * events.

\n * @returns {boolean} true in case of success (may fail later during or after connection) or false in case of\n * immediate error (wrong session state or no connection to the signaling server).\n */\n connect() {\n if (!this._comChannel || (this._state === SessionState.closed)) {\n return false;\n }\n\n if (this._state !== SessionState.idle) {\n return true;\n }\n\n if (this._offerOptions) {\n this.ensurePeerConnection();\n\n this._rtcPeerConnection.createDataChannel(\"control\");\n\n this._rtcPeerConnection.createOffer(this._offerOptions).then((desc) => {\n if (this._rtcPeerConnection && desc) {\n return this._rtcPeerConnection.setLocalDescription(desc);\n } else {\n throw new Error(\"cannot send local offer to WebRTC peer\");\n }\n }).then(() => {\n if (this._rtcPeerConnection && this._comChannel) {\n const msg = {\n type: \"startSession\",\n peerId: this._peerId,\n offer: this._rtcPeerConnection.localDescription.toJSON().sdp\n };\n if (!this._comChannel.send(msg)) {\n throw new Error(\"cannot send startSession message to signaling server\");\n }\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n const msg = {\n type: \"startSession\",\n peerId: this._peerId\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot connect consumer session\",\n error: new Error(\"cannot send startSession message to signaling server\")\n }));\n\n this.close();\n return false;\n }\n\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n return true;\n }\n\n onSessionStarted(peerId, sessionId) {\n if ((this._peerId === peerId) && (this._state === SessionState.connecting) && !this._sessionId) {\n console.log(\"Session started\", this._sessionId);\n this._sessionId = sessionId;\n\n for (const candidate of this._pendingCandidates) {\n console.log(\"Sending delayed ICE with session id\", this._sessionId);\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: candidate.toJSON()\n });\n }\n\n this._pendingCandidates = [];\n }\n }\n\n ensurePeerConnection() {\n if (!this._rtcPeerConnection) {\n const connection = new RTCPeerConnection(this._comChannel.webrtcConfig);\n this._rtcPeerConnection = connection;\n\n connection.ontrack = (event) => {\n if ((this._rtcPeerConnection === connection) && event.streams && (event.streams.length > 0)) {\n if (this._state === SessionState.connecting) {\n this._state = SessionState.streaming;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n let streamsChanged = false;\n for (const stream of event.streams) {\n if (!this._streams.includes(stream)) {\n this._streams.push(stream);\n streamsChanged = true;\n }\n }\n\n if (streamsChanged) {\n this.dispatchEvent(new Event(\"streamsChanged\"));\n }\n }\n };\n\n connection.ondatachannel = (event) => {\n const rtcDataChannel = event.channel;\n if (rtcDataChannel && (rtcDataChannel.label === \"control\")) {\n if (this._remoteController) {\n const previousController = this._remoteController;\n this._remoteController = null;\n previousController.close();\n }\n\n const remoteController = new RemoteController(rtcDataChannel, this);\n this._remoteController = remoteController;\n this.dispatchEvent(new Event(\"remoteControllerChanged\"));\n\n remoteController.addEventListener(\"closed\", () => {\n if (this._remoteController === remoteController) {\n this._remoteController = null;\n this.dispatchEvent(new Event(\"remoteControllerChanged\"));\n }\n });\n }\n };\n\n connection.onicecandidate = (event) => {\n if ((this._rtcPeerConnection === connection) && event.candidate && this._comChannel) {\n if (this._sessionId) {\n console.log(\"Sending ICE with session id\", this._sessionId);\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: event.candidate.toJSON()\n });\n } else {\n this._pendingCandidates.push(event.candidate);\n }\n }\n };\n\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n }\n }\n\n // Work around Chrome not handling stereo Opus correctly.\n // See\n // https://chromium.googlesource.com/external/webrtc/+/194e3bcc53ffa3e98045934377726cb25d7579d2/webrtc/media/engine/webrtcvoiceengine.cc#302\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=8133\n //\n // Technically it's against the spec to modify the SDP\n // but there's no other API for this and this seems to\n // be the only possible workaround at this time.\n mungeStereo(offerSdp, answerSdp) {\n const stereoRegexp = /a=fmtp:.* sprop-stereo/g;\n let stereoPayloads = new Set();\n for (const m of offerSdp.matchAll(stereoRegexp)) {\n const payloadMatch = m[0].match(/a=fmtp:(\\d+) .*/);\n if (payloadMatch) {\n stereoPayloads.add(payloadMatch[1]);\n }\n }\n\n for (const payload of stereoPayloads) {\n const isStereoRegexp = new RegExp(\"a=fmtp:\" + payload + \".*stereo\");\n const answerIsStereo = answerSdp.match(isStereoRegexp);\n\n if (!answerIsStereo) {\n answerSdp = answerSdp.replaceAll(\"a=fmtp:\" + payload, \"a=fmtp:\" + payload + \" stereo=1;\");\n }\n }\n\n return answerSdp;\n }\n\n onSessionPeerMessage(msg) {\n if ((this._state === SessionState.closed) || !this._comChannel || !this._sessionId) {\n return;\n }\n\n this.ensurePeerConnection();\n\n if (msg.sdp) {\n if (this._offerOptions) {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).then(() => {\n console.log(\"done\");\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).then(() => {\n if (this._rtcPeerConnection) {\n return this._rtcPeerConnection.createAnswer();\n } else {\n return null;\n }\n }).then((desc) => {\n if (this._rtcPeerConnection && desc) {\n if (this._mungeStereoHack) {\n desc.sdp = this.mungeStereo(msg.sdp.sdp, desc.sdp);\n }\n\n return this._rtcPeerConnection.setLocalDescription(desc);\n } else {\n return null;\n }\n }).then(() => {\n if (this._rtcPeerConnection && this._comChannel) {\n console.log(\"Sending SDP with session id\", this._sessionId);\n const sdp = {\n type: \"peer\",\n sessionId: this._sessionId,\n sdp: this._rtcPeerConnection.localDescription.toJSON()\n };\n if (!this._comChannel.send(sdp)) {\n throw new Error(\"cannot send local SDP configuration to WebRTC peer\");\n }\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n }\n } else if (msg.ice) {\n const candidate = msg.ice.candidate ? new RTCIceCandidate(msg.ice) : null;\n this._rtcPeerConnection.addIceCandidate(candidate).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during ICE handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n throw new Error(`invalid empty peer message received from consumer session ${this._sessionId}`);\n }\n }\n}\n\nexport default ConsumerSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport WebRTCSession from \"./webrtc-session.js\";\nimport SessionState from \"./session-state.js\";\n\n/**\n * @class ClientSession\n * @hideconstructor\n * @classdesc Client session representing a link between a remote consumer and a local producer session.\n * @extends {WebRTCSession}\n */\nclass ClientSession extends WebRTCSession {\n constructor(peerId, sessionId, comChannel, stream) {\n super(peerId, comChannel);\n this._sessionId = sessionId;\n this._state = SessionState.streaming;\n\n const connection = new RTCPeerConnection(this._comChannel.webrtcConfig);\n this._rtcPeerConnection = connection;\n\n for (const track of stream.getTracks()) {\n connection.addTrack(track, stream);\n }\n\n connection.onicecandidate = (event) => {\n if ((this._rtcPeerConnection === connection) && event.candidate && this._comChannel) {\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: event.candidate.toJSON()\n });\n }\n };\n\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n\n connection.setLocalDescription().then(() => {\n if ((this._rtcPeerConnection === connection) && this._comChannel) {\n const sdp = {\n type: \"peer\",\n sessionId: this._sessionId,\n sdp: this._rtcPeerConnection.localDescription.toJSON()\n };\n if (!this._comChannel.send(sdp)) {\n throw new Error(\"cannot send local SDP configuration to WebRTC peer\");\n }\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n }\n\n onSessionPeerMessage(msg) {\n if ((this._state === SessionState.closed) || !this._rtcPeerConnection) {\n return;\n }\n\n if (msg.sdp) {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else if (msg.ice) {\n const candidate = new RTCIceCandidate(msg.ice);\n this._rtcPeerConnection.addIceCandidate(candidate).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during ICE handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n throw new Error(`invalid empty peer message received from producer's client session ${this._peerId}`);\n }\n }\n}\n\n/**\n * Event name: \"clientConsumerAdded\".
\n * Triggered when a remote consumer peer connects to a local {@link ProducerSession}.\n * @event GstWebRTCAPI#ClientConsumerAddedEvent\n * @type {CustomEvent}\n * @property {ClientSession} detail - The WebRTC session associated with the added consumer peer.\n * @see ProducerSession\n */\n/**\n * Event name: \"clientConsumerRemoved\".
\n * Triggered when a remote consumer peer disconnects from a local {@link ProducerSession}.\n * @event GstWebRTCAPI#ClientConsumerRemovedEvent\n * @type {CustomEvent}\n * @property {ClientSession} detail - The WebRTC session associated with the removed consumer peer.\n * @see ProducerSession\n */\n\n/**\n * @class ProducerSession\n * @hideconstructor\n * @classdesc Producer session managing the streaming out of a local {@link MediaStream}.
\n * It manages all underlying WebRTC connections to each peer client consuming the stream.\n *

Call {@link GstWebRTCAPI#createProducerSession} to create a ProducerSession instance.

\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:StateChangedEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @fires {@link GstWebRTCAPI#event:ClientConsumerAddedEvent}\n * @fires {@link GstWebRTCAPI#event:ClientConsumerRemovedEvent}\n */\nclass ProducerSession extends EventTarget {\n constructor(comChannel, stream, consumerId) {\n super();\n\n this._comChannel = comChannel;\n this._stream = stream;\n this._state = SessionState.idle;\n this._clientSessions = {};\n this._consumerId = consumerId;\n }\n\n /**\n * The local stream produced out by this session.\n * @type {MediaStream}\n * @readonly\n */\n get stream() {\n return this._stream;\n }\n\n /**\n * The current producer session state.\n * @type {SessionState}\n * @readonly\n */\n get state() {\n return this._state;\n }\n\n /**\n * Starts the producer session.
\n * This method must be called after creating the producer session in order to start streaming. It registers this\n * producer session to the signaling server and gets ready to serve peer requests from consumers.\n *

Even on success, streaming can fail later if any error occurs during or after connection. In order to know\n * the effective streaming state, you should be listening to the [error]{@link GstWebRTCAPI#event:ErrorEvent},\n * [stateChanged]{@link GstWebRTCAPI#event:StateChangedEvent} and/or [closed]{@link GstWebRTCAPI#event:ClosedEvent}\n * events.

\n * @returns {boolean} true in case of success (may fail later during or after connection) or false in case of\n * immediate error (wrong session state or no connection to the signaling server).\n */\n start() {\n if (!this._comChannel || (this._state === SessionState.closed)) {\n return false;\n }\n\n if (this._state !== SessionState.idle) {\n return true;\n }\n\n const msg = {\n type: \"setPeerStatus\",\n roles: [\"listener\", \"producer\"],\n meta: this._comChannel.meta\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot start producer session\",\n error: new Error(\"cannot register producer to signaling server\")\n }));\n\n this.close();\n return false;\n }\n\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n return true;\n }\n\n /**\n * Terminates the producer session.
\n * It immediately disconnects all peer consumers attached to this producer session and unregisters the producer\n * from the signaling server.\n */\n close() {\n if (this._state !== SessionState.closed) {\n for (const track of this._stream.getTracks()) {\n track.stop();\n }\n\n if ((this._state !== SessionState.idle) && this._comChannel) {\n this._comChannel.send({\n type: \"setPeerStatus\",\n roles: [\"listener\"],\n meta: this._comChannel.meta\n });\n }\n\n this._state = SessionState.closed;\n this.dispatchEvent(new Event(\"stateChanged\"));\n\n this._comChannel = null;\n this._stream = null;\n\n for (const clientSession of Object.values(this._clientSessions)) {\n clientSession.close();\n }\n this._clientSessions = {};\n\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n\n onProducerRegistered() {\n if (this._state === SessionState.connecting) {\n this._state = SessionState.streaming;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n if (this._consumerId) {\n const msg = {\n type: \"startSession\",\n peerId: this._consumerId\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot send session request to specified consumer\",\n error: new Error(\"cannot send startSession message to signaling server\")\n }));\n\n this.close();\n }\n }\n }\n\n onStartSessionMessage(msg) {\n if (this._comChannel && this._stream && !(msg.sessionId in this._clientSessions)) {\n const session = new ClientSession(msg.peerId, msg.sessionId, this._comChannel, this._stream);\n this._clientSessions[msg.sessionId] = session;\n\n session.addEventListener(\"closed\", (event) => {\n const sessionId = event.target.sessionId;\n if ((sessionId in this._clientSessions) && (this._clientSessions[sessionId] === session)) {\n delete this._clientSessions[sessionId];\n this.dispatchEvent(new CustomEvent(\"clientConsumerRemoved\", { detail: session }));\n }\n });\n\n session.addEventListener(\"error\", (event) => {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: `error from client consumer ${event.target.peerId}: ${event.message}`,\n error: event.error\n }));\n });\n\n this.dispatchEvent(new CustomEvent(\"clientConsumerAdded\", { detail: session }));\n }\n }\n\n onEndSessionMessage(msg) {\n if (msg.sessionId in this._clientSessions) {\n this._clientSessions[msg.sessionId].close();\n }\n }\n\n onSessionPeerMessage(msg) {\n if (msg.sessionId in this._clientSessions) {\n this._clientSessions[msg.sessionId].onSessionPeerMessage(msg);\n }\n }\n}\n\nexport default ProducerSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport ConsumerSession from \"./consumer-session.js\";\nimport ProducerSession from \"./producer-session.js\";\n\nconst SignallingServerMessageType = Object.freeze({\n welcome: \"welcome\",\n peerStatusChanged: \"peerStatusChanged\",\n list: \"list\",\n listConsumers: \"listConsumers\",\n sessionStarted: \"sessionStarted\",\n peer: \"peer\",\n startSession: \"startSession\",\n endSession: \"endSession\",\n error: \"error\"\n});\n\nfunction normalizePeer(peer, excludedId) {\n if (!peer || (typeof (peer) !== \"object\")) {\n return null;\n }\n\n const normalizedPeer = {\n id: \"\",\n meta: {}\n };\n\n if (peer.id && (typeof (peer.id) === \"string\")) {\n normalizedPeer.id = peer.id;\n } else if (peer.peerId && (typeof (peer.peerId) === \"string\")) {\n normalizedPeer.id = peer.peerId;\n } else {\n return null;\n }\n\n if (normalizedPeer.id === excludedId) {\n return null;\n }\n\n if (peer.meta && (typeof (peer.meta) === \"object\")) {\n normalizedPeer.meta = peer.meta;\n }\n\n Object.freeze(normalizedPeer.meta);\n return Object.freeze(normalizedPeer);\n}\n\nclass ComChannel extends EventTarget {\n constructor(url, meta, webrtcConfig) {\n super();\n\n this._meta = meta;\n this._webrtcConfig = webrtcConfig;\n this._ws = new WebSocket(url);\n this._ready = false;\n this._channelId = \"\";\n this._producerSession = null;\n this._consumerSessions = {};\n this._peers = {};\n\n this._ws.onerror = (event) => {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: event.message || \"WebSocket error\",\n error: event.error || new Error(\n this._ready ? \"transportation error\" : \"cannot connect to signaling server\")\n }));\n this.close();\n };\n\n this._ws.onclose = () => {\n this._ready = false;\n this._channelId = \"\";\n this._ws = null;\n\n this.closeAllConsumerSessions();\n\n if (this._producerSession) {\n this._producerSession.close();\n this._producerSession = null;\n }\n\n this.dispatchEvent(new Event(\"closed\"));\n };\n\n this._ws.onmessage = (event) => {\n try {\n const msg = JSON.parse(event.data);\n if (msg && (typeof (msg) === \"object\")) {\n switch (msg.type) {\n\n case SignallingServerMessageType.welcome:\n this._channelId = msg.peerId;\n try {\n this._ws.send(JSON.stringify({\n type: \"setPeerStatus\",\n roles: [\"listener\"],\n meta: meta\n }));\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot initialize connection to signaling server\",\n error: ex\n }));\n this.close();\n }\n break;\n\n case SignallingServerMessageType.peerStatusChanged: {\n if (msg.peerId === this._channelId) {\n if (!this._ready && msg.roles.includes(\"listener\")) {\n this._ready = true;\n this.dispatchEvent(new Event(\"ready\"));\n this.send({ type: \"list\" });\n this.send({ type: \"listConsumers\"});\n }\n\n if (this._producerSession && msg.roles.includes(\"producer\")) {\n this._producerSession.onProducerRegistered();\n }\n\n break;\n }\n\n const peer = normalizePeer(msg, this._channelId);\n if (!peer) {\n break;\n }\n\n const oldRoles = this._peers[msg.peerId] || [];\n this._peers[msg.peerId] = msg.roles;\n for (const role of [\"producer\", \"consumer\"]) {\n if (!oldRoles.includes(role) && msg.roles.includes(role)) {\n this.dispatchEvent(new CustomEvent(\"peerAdded\", { detail: { peer, role } }));\n } else if (oldRoles.includes(role) && !msg.roles.includes(role)) {\n this.dispatchEvent(new CustomEvent(\"peerRemoved\", { detail: { peerId: msg.peerId, role } }));\n }\n }\n break;\n }\n\n case SignallingServerMessageType.list: {\n this.clearPeers(\"producer\");\n this.addPeers(msg.producers, \"producer\");\n break;\n }\n\n case SignallingServerMessageType.listConsumers: {\n this.clearPeers(\"consumer\");\n this.addPeers(msg.consumers, \"consumer\");\n break;\n }\n\n case SignallingServerMessageType.sessionStarted:\n {\n const session = this.getConsumerSession(msg.peerId);\n if (session) {\n delete this._consumerSessions[msg.peerId];\n\n session.onSessionStarted(msg.peerId, msg.sessionId);\n if (session.sessionId && !(session.sessionId in this._consumerSessions)) {\n this._consumerSessions[session.sessionId] = session;\n } else {\n session.close();\n }\n }\n }\n break;\n\n case SignallingServerMessageType.peer:\n {\n const session = this.getConsumerSession(msg.sessionId);\n if (session) {\n session.onSessionPeerMessage(msg);\n } else if (this._producerSession) {\n this._producerSession.onSessionPeerMessage(msg);\n }\n }\n break;\n\n case SignallingServerMessageType.startSession:\n if (this._producerSession) {\n this._producerSession.onStartSessionMessage(msg);\n }\n break;\n\n case SignallingServerMessageType.endSession:\n {\n const session = this.getConsumerSession(msg.sessionId);\n if (session) {\n session.close();\n } else if (this._producerSession) {\n this._producerSession.onEndSessionMessage(msg);\n }\n }\n break;\n\n case SignallingServerMessageType.error:\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"error received from signaling server\",\n error: new Error(msg.details)\n }));\n break;\n\n default:\n throw new Error(`unknown message type: \"${msg.type}\"`);\n }\n }\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot parse incoming message from signaling server\",\n error: ex\n }));\n }\n };\n }\n\n get meta() {\n return this._meta;\n }\n\n get webrtcConfig() {\n return this._webrtcConfig;\n }\n\n get ready() {\n return this._ready;\n }\n\n get channelId() {\n return this._channelId;\n }\n\n get producerSession() {\n return this._producerSession;\n }\n\n createProducerSession(stream, consumerId) {\n if (!this._ready || !(stream instanceof MediaStream)) {\n return null;\n }\n\n if (this._producerSession) {\n if (this._producerSession.stream === stream) {\n return this._producerSession;\n } else {\n return null;\n }\n }\n\n const session = new ProducerSession(this, stream, consumerId);\n this._producerSession = session;\n\n session.addEventListener(\"closed\", () => {\n if (this._producerSession === session) {\n this._producerSession = null;\n }\n });\n\n return session;\n }\n\n createConsumerSession(producerId, offerOptions) {\n if (!this._ready || !producerId || (typeof (producerId) !== \"string\")) {\n return null;\n }\n\n if (offerOptions && (typeof(offerOptions) !== \"object\")) {\n offerOptions = undefined;\n }\n\n if (producerId in this._consumerSessions) {\n return this._consumerSessions[producerId];\n }\n\n for (const session of Object.values(this._consumerSessions)) {\n if (session.peerId === producerId) {\n return session;\n }\n }\n\n const session = new ConsumerSession(producerId, this, offerOptions);\n this._consumerSessions[producerId] = session;\n\n session.addEventListener(\"closed\", (event) => {\n let sessionId = event.target.sessionId;\n if (!sessionId) {\n sessionId = event.target.peerId;\n }\n\n if ((sessionId in this._consumerSessions) && (this._consumerSessions[sessionId] === session)) {\n delete this._consumerSessions[sessionId];\n }\n });\n\n return session;\n }\n\n getConsumerSession(sessionId) {\n if (sessionId in this._consumerSessions) {\n return this._consumerSessions[sessionId];\n } else {\n return null;\n }\n }\n\n closeAllConsumerSessions() {\n for (const session of Object.values(this._consumerSessions)) {\n session.close();\n }\n\n this._consumerSessions = {};\n }\n\n send(data) {\n if (this._ready && data && (typeof (data) === \"object\")) {\n try {\n this._ws.send(JSON.stringify(data));\n return true;\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot send message to signaling server\",\n error: ex\n }));\n }\n }\n\n return false;\n }\n\n close() {\n if (this._ws) {\n this._ready = false;\n this._channelId = \"\";\n this._ws.close();\n\n this.closeAllConsumerSessions();\n\n if (this._producerSession) {\n this._producerSession.close();\n this._producerSession = null;\n }\n }\n }\n\n clearPeers(role) {\n for (const peerId in this._peers) {\n if (this._peers[peerId].includes(role)) {\n delete this._peers[peerId];\n this.dispatchEvent(new CustomEvent(\"peerRemoved\", { detail: { peerId, role } }));\n }\n }\n }\n\n addPeers(items, role) {\n items.forEach(item => {\n const peer = normalizePeer(item, this._channelId);\n if (peer) {\n this._peers[peer.id] = [role];\n this.dispatchEvent(new CustomEvent(\"peerAdded\", { detail: { peer, role } }));\n }\n });\n };\n}\n\nexport default ComChannel;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport defaultConfig from \"./config.js\";\nimport ComChannel from \"./com-channel.js\";\nimport SessionState from \"./session-state.js\";\n\n/** @import ConsumerSession from \"./consumer-session.js\"; */\n/** @import ProducerSession from \"./producer-session.js\"; */\n/** @import GstWebRTCConfig from \"./config.js\"; */\n/** @import RTCOfferOptions from \"typescript/lib/lib.dom.js\"; */\n\nclass GstWebRTCAPI {\n /**\n * @class GstWebRTCAPI\n * @classdesc The API entry point that manages a WebRTC.\n * @constructor\n * @param {GstWebRTCConfig} [userConfig] - The user configuration.
\n * Only the parameters different from the default ones need to be provided.\n */\n constructor(userConfig) {\n this._channel = null;\n this._producers = {};\n this._consumers = {};\n this._connectionListeners = [];\n this._peerListeners = [];\n\n const config = Object.assign({}, defaultConfig);\n if (userConfig && (typeof (userConfig) === \"object\")) {\n Object.assign(config, userConfig);\n }\n\n if (typeof (config.meta) !== \"object\") {\n config.meta = null;\n }\n\n this._config = config;\n this.connectChannel();\n }\n\n /**\n * @interface ConnectionListener\n */\n /**\n * Callback method called when this client connects to the WebRTC signaling server.\n * The callback implementation should not throw any exception.\n * @method ConnectionListener#connected\n * @abstract\n * @param {string} clientId - The unique identifier of this WebRTC client.
This identifier is provided by the\n * signaling server to uniquely identify each connected peer.\n */\n /**\n * Callback method called when this client disconnects from the WebRTC signaling server.\n * The callback implementation should not throw any exception.\n * @method ConnectionListener#disconnected\n * @abstract\n */\n\n /**\n * Registers a connection listener that will be called each time the WebRTC API connects to or disconnects from the\n * signaling server.\n * @param {ConnectionListener} listener - The connection listener to register.\n * @returns {boolean} true in case of success (or if the listener was already registered), or false if the listener\n * doesn't implement all callback functions and cannot be registered.\n */\n registerConnectionListener(listener) {\n if (!listener || (typeof (listener) !== \"object\") ||\n (typeof (listener.connected) !== \"function\") ||\n (typeof (listener.disconnected) !== \"function\")) {\n return false;\n }\n\n if (!this._connectionListeners.includes(listener)) {\n this._connectionListeners.push(listener);\n }\n\n return true;\n }\n\n /**\n * Unregisters a connection listener.
\n * The removed listener will never be called again and can be garbage collected.\n * @param {ConnectionListener} listener - The connection listener to unregister.\n * @returns {boolean} true if the listener is found and unregistered, or false if the listener was not previously\n * registered.\n */\n unregisterConnectionListener(listener) {\n const idx = this._connectionListeners.indexOf(listener);\n if (idx >= 0) {\n this._connectionListeners.splice(idx, 1);\n return true;\n }\n\n return false;\n }\n\n /**\n * Unregisters all previously registered connection listeners.\n */\n unregisterAllConnectionListeners() {\n this._connectionListeners = [];\n }\n\n /**\n * Creates a new producer session.\n *

You can only create one producer session at a time.
\n * To request streaming from a new stream you will first need to close the previous producer session.

\n *

You can only request a producer session while you are connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @param {MediaStream} stream - The audio/video stream to offer as a producer through WebRTC.\n * @returns {ProducerSession} The created producer session or null in case of error. To start streaming,\n * you still need to call {@link ProducerSession#start} after adding on the returned session all the event\n * listeners you may need.\n */\n createProducerSession(stream) {\n if (this._channel) {\n return this._channel.createProducerSession(stream);\n }\n return null;\n }\n\n createProducerSessionForConsumer(stream, consumerId) {\n if (this._channel) {\n return this._channel.createProducerSession(stream, consumerId);\n }\n return null;\n }\n\n /**\n * Information about a remote peer registered by the signaling server.\n * @typedef {object} Peer\n * @readonly\n * @property {string} id - The unique peer identifier set by the signaling server (always non-empty).\n * @property {object} meta - Free-form object containing extra information about the peer (always non-null,\n * but may be empty). Its content depends on your application.\n */\n\n /**\n * Gets the list of all remote WebRTC producers available on the signaling server.\n *

The remote producers list is only populated once you've connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @returns {Peer[]} The list of remote WebRTC producers available.\n */\n getAvailableProducers() {\n return Object.values(this._producers);\n }\n\n /**\n * Gets the list of all remote WebRTC consumers available on the signaling server.\n *

The remote consumer list is only populated once you've connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @returns {Peer[]} The list of remote WebRTC consumers available.\n */\n getAvailableConsumers() {\n return Object.values(this._consumers);\n }\n\n /**\n * @interface PeerListener\n */\n /**\n * Callback method called when a remote producer is added on the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#producerAdded\n * @abstract\n * @param {Peer} producer - The remote producer added on server-side.\n */\n /**\n * Callback method called when a remote producer is removed from the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#producerRemoved\n * @abstract\n * @param {Peer} producer - The remote producer removed on server-side.\n */\n /**\n * Callback method called when a remote consumer is added on the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#consumerAdded\n * @abstract\n * @param {Peer} consumer - The remote consumer added on server-side.\n * */\n /**\n * Callback method called when a remote consumer is removed from the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#consumerRemoved\n * @abstract\n * @param {Peer} consumer - The remote consumer removed on server-side.\n * */\n\n /**\n * Registers a listener that will be called each time a peer is added or removed on the signaling server.\n * The listener can implement all or only some of the callback methods.\n * @param {PeerListener} listener - The peer listener to register.\n * @returns {boolean} true in case of success (or if the listener was already registered), or false if the listener\n * doesn't implement any methods from PeerListener and cannot be registered.\n */\n registerPeerListener(listener) {\n // refuse if no methods from PeerListener are implemented\n if (!listener || (typeof (listener) !== \"object\") ||\n ((typeof (listener.producerAdded) !== \"function\") &&\n (typeof (listener.producerRemoved) !== \"function\") &&\n (typeof (listener.consumerAdded) !== \"function\") &&\n (typeof (listener.consumerRemoved) !== \"function\"))) {\n return false;\n }\n\n if (!this._peerListeners.includes(listener)) {\n this._peerListeners.push(listener);\n }\n\n return true;\n }\n\n /**\n * Unregisters a peer listener.
\n * The removed listener will never be called again and can be garbage collected.\n * @param {PeerListener} listener - The peer listener to unregister.\n * @returns {boolean} true if the listener is found and unregistered, or false if the listener was not previously\n * registered.\n */\n unregisterPeerListener(listener) {\n const idx = this._peerListeners.indexOf(listener);\n if (idx >= 0) {\n this._peerListeners.splice(idx, 1);\n return true;\n }\n\n return false;\n }\n\n /**\n * Unregisters all previously registered peer listeners.\n */\n unregisterAllPeerListeners() {\n this._peerListeners = [];\n }\n\n /**\n * Creates a consumer session by connecting the local client to a remote WebRTC producer.\n *

You can only create one consumer session per remote producer.

\n *

You can only request a new consumer session while you are connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @param {string} producerId - The unique identifier of the remote producer to connect to.\n * @returns {ConsumerSession} The WebRTC session between the selected remote producer and this local\n * consumer, or null in case of error. To start connecting and receiving the remote streams, you still need to call\n * {@link ConsumerSession#connect} after adding on the returned session all the event listeners you may\n * need.\n */\n createConsumerSession(producerId) {\n if (this._channel) {\n return this._channel.createConsumerSession(producerId);\n }\n return null;\n }\n\n /**\n * Creates a consumer session by connecting the local client to a remote WebRTC producer and creating the offer.\n *

See {@link GstWebRTCAPI#createConsumerSession} for more information

\n * @param {string} producerId - The unique identifier of the remote producer to connect to.\n * @param {RTCOfferOptions} offerOptions - An object to use when creating the offer.\n * @returns {ConsumerSession} The WebRTC session between the selected remote producer and this local\n * consumer, or null in case of error. To start connecting and receiving the remote streams, you still need to call\n * {@link ConsumerSession#connect} after adding on the returned session all the event listeners you may\n * need.\n */\n createConsumerSessionWithOfferOptions(producerId, offerOptions) {\n if (this._channel) {\n return this._channel.createConsumerSession(producerId, offerOptions);\n }\n return null;\n }\n\n connectChannel() {\n if (this._channel) {\n const oldChannel = this._channel;\n this._channel = null;\n oldChannel.close();\n for (const key in this._producers) {\n this.triggerProducerRemoved(key);\n }\n for (const key in this._consumers) {\n this.triggerConsumerRemoved(key);\n }\n this._producers = {};\n this._consumers = {};\n this.triggerDisconnected();\n }\n\n this._channel = new ComChannel(\n this._config.signalingServerUrl,\n this._config.meta,\n this._config.webrtcConfig\n );\n\n this._channel.addEventListener(\"error\", (event) => {\n if (event.target === this._channel) {\n console.error(event.message, event.error);\n }\n });\n\n this._channel.addEventListener(\"closed\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n this._channel = null;\n for (const key in this._producers) {\n this.triggerProducerRemoved(key);\n }\n for (const key in this._consumers) {\n this.triggerConsumerRemoved(key);\n }\n this._producers = {};\n this._consumers = {};\n this.triggerDisconnected();\n if (this._config.reconnectionTimeout > 0) {\n window.setTimeout(() => {\n this.connectChannel();\n }, this._config.reconnectionTimeout);\n }\n });\n\n this._channel.addEventListener(\"ready\", (event) => {\n if (event.target === this._channel) {\n this.triggerConnected(this._channel.channelId);\n }\n });\n\n this._channel.addEventListener(\"peerAdded\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n\n if (event.detail.role === \"producer\") {\n this.triggerProducerAdded(event.detail.peer);\n } else {\n this.triggerConsumerAdded(event.detail.peer);\n }\n });\n\n this._channel.addEventListener(\"peerRemoved\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n\n if (event.detail.role === \"producer\") {\n this.triggerProducerRemoved(event.detail.peerId);\n } else {\n this.triggerConsumerRemoved(event.detail.peerId);\n }\n });\n }\n\n triggerConnected(clientId) {\n for (const listener of this._connectionListeners) {\n try {\n listener.connected(clientId);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerDisconnected() {\n for (const listener of this._connectionListeners) {\n try {\n listener.disconnected();\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerProducerAdded(producer) {\n if (producer.id in this._producers) {\n return;\n }\n\n this._producers[producer.id] = producer;\n for (const listener of this._peerListeners) {\n if (!listener.producerAdded) {\n continue;\n }\n\n try {\n listener.producerAdded(producer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerProducerRemoved(producerId) {\n if (!(producerId in this._producers)) {\n return;\n }\n\n const producer = this._producers[producerId];\n delete this._producers[producerId];\n\n for (const listener of this._peerListeners) {\n if (!listener.producerRemoved) {\n continue;\n }\n\n try {\n listener.producerRemoved(producer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerConsumerAdded(consumer) {\n if (consumer.id in this._consumers) {\n return;\n }\n\n this._consumers[consumer.id] = consumer;\n for (const listener of this._peerListeners) {\n if (!listener.consumerAdded) {\n continue;\n }\n\n try {\n listener.consumerAdded(consumer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerConsumerRemoved(consumerId) {\n if (!(consumerId in this._consumers)) {\n return;\n }\n\n const consumer = this._consumers[consumerId];\n delete this._consumers[consumerId];\n\n for (const listener of this._peerListeners) {\n if (!listener.consumerRemoved) {\n continue;\n }\n\n try {\n listener.consumerRemoved(consumer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n}\n\nGstWebRTCAPI.SessionState = SessionState;\n\nexport default GstWebRTCAPI;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport \"webrtc-adapter\";\nimport GstWebRTCAPI from \"./gstwebrtc-api.js\";\n\n/**\n * @external MediaStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStream\n */\n/**\n * @external RTCPeerConnection\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection\n */\n/**\n * @external RTCDataChannel\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel\n */\n/**\n * @external RTCOfferOptions\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer#options\n */\n/**\n * @external EventTarget\n * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget\n */\n/**\n * @external Event\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Event\n */\n/**\n * @external ErrorEvent\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent\n */\n/**\n * @external CustomEvent\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent\n */\n/**\n * @external Error\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n */\n/**\n * @external HTMLVideoElement\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement\n */\n\nif (!window.GstWebRTCAPI) {\n window.GstWebRTCAPI = GstWebRTCAPI;\n}\n\nexport default GstWebRTCAPI;\n"],"names":["SDPUtils","Math","random","toString","substring","localCName","generateIdentifier","splitLines","blob","trim","split","map","line","splitSections","part","index","getDescription","sections","getMediaSections","shift","matchPrefix","prefix","filter","indexOf","parseCandidate","parts","candidate","foundation","component","protocol","toLowerCase","priority","parseInt","ip","address","port","type","i","length","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","undefined","writeCandidate","sdp","push","toUpperCase","join","parseIceOptions","parseRtpMap","parsed","payloadType","name","clockRate","channels","numChannels","writeRtpMap","codec","pt","preferredPayloadType","parseExtmap","id","direction","uri","attributes","slice","writeExtmap","headerExtension","preferredId","parseFmtp","kv","j","writeFmtp","parameters","Object","keys","params","forEach","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","ssrc","colon","attribute","value","parseSsrcGroup","semantics","ssrcs","getMid","mediaSection","mid","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","tag","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","profile","rtpmapline","fmtps","wildcardRtcpFb","find","existingFeedback","writeRtpDescription","kind","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","primarySsrc","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","JSON","parse","stringify","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","obj","cname","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","stream","track","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","maxMessageSize","isNaN","sctpPort","fmt","sctpMapLines","writeSctpDescription","media","sctp","output","generateSessionId","substr","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","version","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","charAt","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","defineProperty","enumerable","get","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","logDisabled_","deprecationWarnings_","extractVersion","uastring","expr","pos","match","wrapPeerConnectionEvent","window","eventNameToWrap","wrapper","RTCPeerConnection","proto","nativeAddEventListener","addEventListener","nativeEventName","cb","apply","this","arguments","wrappedCallback","e","modifiedEvent","handleEvent","_eventMap","Map","set","nativeRemoveEventListener","removeEventListener","has","unwrappedCb","delete","size","configurable","disableLog","bool","Error","disableWarnings","log","console","deprecated","oldMethod","newMethod","warn","isObject","val","compactObject","data","reduce","accumulator","isObj","isEmptyObject","assign","walkStats","stats","base","resultSet","endsWith","filterStats","result","outbound","streamStatsType","filteredResult","trackStats","trackIdentifier","trackStat","trackId","logging","shimGetUserMedia","browserDetails","navigator","mediaDevices","constraintsToChrome_","c","mandatory","optional","cc","ideal","exact","min","max","oldname_","oc","mix","advanced","concat","shimConstraints_","constraints","func","audio","remap","b","video","face","facingMode","getSupportedFacingModeLies","getSupportedConstraints","matches","enumerateDevices","then","devices","dev","some","label","includes","deviceId","shimError_","PermissionDeniedError","PermissionDismissedError","InvalidStateError","DevicesNotFoundError","ConstraintNotSatisfiedError","TrackStartError","MediaDeviceFailedDueToShutdown","MediaDeviceKillSwitchOn","TabCaptureError","ScreenCaptureError","DeviceCaptureError","message","constraint","constraintName","getUserMedia","onSuccess","onError","webkitGetUserMedia","bind","origGetUserMedia","cs","getAudioTracks","getVideoTracks","getTracks","stop","DOMException","Promise","reject","shimGetDisplayMedia","getSourceId","getDisplayMedia","sourceId","widthSpecified","width","heightSpecified","height","frameRateSpecified","frameRate","chromeMediaSource","chromeMediaSourceId","maxFrameRate","maxWidth","maxHeight","error","shimMediaStream","MediaStream","webkitMediaStream","shimOnTrack","_ontrack","f","origSetRemoteDescription","setRemoteDescription","_ontrackpoly","te","receiver","getReceivers","event","Event","transceiver","streams","dispatchEvent","shimGetSendersWithDtmf","shimSenderWithDtmf","pc","dtmf","_dtmf","createDTMFSender","_pc","getSenders","_senders","origAddTrack","addTrack","sender","origRemoveTrack","removeTrack","idx","splice","origAddStream","addStream","origRemoveStream","removeStream","s","RTCRtpSender","origGetSenders","senders","shimGetStats","origGetStats","getStats","selector","onSucc","onErr","fixChromeStats_","response","standardReport","report","standardStats","timestamp","localcandidate","remotecandidate","names","stat","makeMapStats","successCallbackWrapper_","resolve","shimSenderReceiverGetStats","RTCRtpReceiver","origGetReceivers","receivers","srcElement","MediaStreamTrack","err","shimAddTrackRemoveTrackWithNative","getLocalStreams","_shimmedLocalStreams","streamId","existingSenders","newSenders","newSender","shimAddTrackRemoveTrack","origGetLocalStreams","nativeStreams","_reverseStreams","_streams","newStream","replaceInternalStreamId","internalId","externalStream","internalStream","replace","RegExp","RTCSessionDescription","signalingState","t","oldStream","method","nativeMethod","methodObj","args","desc","origSetLocalDescription","setLocalDescription","replaceExternalStreamId","origLocalDescription","getOwnPropertyDescriptor","streamid","shimPeerConnection","webkitRTCPeerConnection","RTCIceCandidate","fixNegotiationNeeded","target","getConfiguration","sdpSemantics","nativeGetUserMedia","getSettings","nativeGetSettings","applyConstraints","nativeApplyConstraints","preferredMediaSource","code","mediaSource","RTCTrackEvent","mozRTCPeerConnection","modernStatsTypes","inboundrtp","outboundrtp","candidatepair","nativeGetStats","shimSenderGetStats","shimReceiverGetStats","shimRemoveStream","shimRTCDataChannel","DataChannel","RTCDataChannel","shimAddTransceiver","origAddTransceiver","addTransceiver","setParametersPromises","sendEncodings","shouldPerformCheck","encodingParam","test","rid","TypeError","parseFloat","scaleResolutionDownBy","RangeError","maxFramerate","getParameters","encodings","setParameters","catch","shimGetParameters","origGetParameters","shimCreateOffer","origCreateOffer","createOffer","all","finally","shimCreateAnswer","origCreateAnswer","createAnswer","shimLocalStreamsAPI","_localStreams","_addTrack","tracks","shimRemoteStreamsAPI","getRemoteStreams","_remoteStreams","_onaddstream","_onaddstreampoly","shimCallbacksAPI","addIceCandidate","successCallback","failureCallback","options","promise","withCallback","_getUserMedia","shimConstraints","errcb","shimRTCIceServerUrls","OrigPeerConnection","pcConfig","pcConstraints","iceServers","newIceServers","server","urls","url","generateCertificate","shimTrackEventTransceiver","shimCreateOfferLegacy","offerOptions","offerToReceiveAudio","audioTransceiver","getTransceivers","setDirection","offerToReceiveVideo","videoTransceiver","shimAudioContext","AudioContext","webkitAudioContext","shimRTCIceCandidate","NativeRTCIceCandidate","nativeCandidate","parsedCandidate","toJSON","sdpMid","sdpMLineIndex","writable","shimRTCIceCandidateRelayProtocol","relayProtocol","shimMaxMessageSize","_sctp","browser","mLine","sctpInDescription","isFirefox","getRemoteFirefoxVersion","canSendMMS","remoteIsFirefox","canSendMaxMessageSize","getCanSendMaxMessageSize","remoteMMS","getMaxMessageSize","Number","POSITIVE_INFINITY","shimSendThrowTypeError","wrapDcSend","dc","origDataChannelSend","send","byteLength","readyState","origCreateDataChannel","createDataChannel","dataChannel","channel","shimConnectionState","completed","checking","iceConnectionState","_onconnectionstatechange","origMethod","_connectionstatechangepoly","_lastConnectionState","connectionState","newEvent","removeExtmapAllowMixed","nativeSRD","shimAddIceCandidateNullOrEmpty","nativeAddIceCandidate","shimParameterlessSetLocalDescription","nativeSetLocalDescription","shimChrome","shimFirefox","shimSafari","userAgent","mozGetUserMedia","isSecureContext","supportsUnifiedPlan","RTCRtpTransceiver","adapter","commonShim","browserShim","adapterFactory","freeze","meta","signalingServerUrl","reconnectionTimeout","webrtcConfig","bundlePolicy","SessionState","idle","connecting","streaming","closed","WebRTCSession","EventTarget","constructor","peerId","comChannel","super","_peerId","_sessionId","_comChannel","_state","_rtcPeerConnection","state","rtcPeerConnection","close","uniToKeySyms","kbEventCodesToKeySyms","knownKbEventCodes","Set","getKeysymString","keySym","keyCodeUni","charCodeAt","eventsNames","mouseEventsNames","mousemove","mousedown","mouseup","touchEventsNames","touchstart","touchend","touchmove","touchcancel","keyboardEventsNames","keydown","keyup","getModifiers","modifiers","altKey","ctrlKey","metaKey","shiftKey","RemoteController","rtcDataChannel","consumerSession","_rtcDataChannel","_consumerSession","_videoElement","_videoElementComputedStyle","_videoElementKeyboard","_lastTouchEventTimestamp","_requestCounter","ErrorEvent","msg","CustomEvent","detail","ex","videoElement","attachVideoElement","element","HTMLVideoElement","getComputedStyle","eventName","setAttribute","previousElement","removeAttribute","sendControlRequest","request","_sendGstNavigationEvent","_computeVideoMousePosition","mousePos","x","y","videoWidth","videoHeight","padding","paddingLeft","paddingRight","paddingTop","paddingBottom","offsetX","offsetY","clientRect","getBoundingClientRect","border","left","borderLeftWidth","top","borderTopWidth","clientX","clientY","videoOffset","clientWidth","clientHeight","ratio","invRatio","preventDefault","delta_x","deltaX","delta_y","deltaY","modifier_state","button","focus","touch","changedTouches","identifier","pressure","force","timeStamp","_remoteController","_pendingCandidates","_mungeStereoHack","_offerOptions","mungeStereoHack","enable","remoteController","connect","ensurePeerConnection","offer","localDescription","onSessionStarted","ice","connection","ontrack","streamsChanged","ondatachannel","previousController","onicecandidate","mungeStereo","offerSdp","answerSdp","stereoRegexp","stereoPayloads","m","matchAll","payloadMatch","add","payload","isStereoRegexp","replaceAll","onSessionPeerMessage","ClientSession","ProducerSession","consumerId","_stream","_clientSessions","_consumerId","start","roles","clientSession","values","onProducerRegistered","onStartSessionMessage","session","onEndSessionMessage","SignallingServerMessageType","welcome","peerStatusChanged","list","listConsumers","sessionStarted","peer","startSession","endSession","normalizePeer","excludedId","normalizedPeer","ComChannel","_meta","_webrtcConfig","_ws","WebSocket","_ready","_channelId","_producerSession","_consumerSessions","_peers","onerror","onclose","closeAllConsumerSessions","onmessage","oldRoles","clearPeers","addPeers","producers","consumers","getConsumerSession","details","ready","channelId","producerSession","createProducerSession","createConsumerSession","producerId","items","item","GstWebRTCAPI","userConfig","_channel","_producers","_consumers","_connectionListeners","_peerListeners","config","_config","connectChannel","registerConnectionListener","listener","unregisterConnectionListener","unregisterAllConnectionListeners","createProducerSessionForConsumer","getAvailableProducers","getAvailableConsumers","registerPeerListener","unregisterPeerListener","unregisterAllPeerListeners","createConsumerSessionWithOfferOptions","oldChannel","triggerProducerRemoved","triggerConsumerRemoved","triggerDisconnected","setTimeout","triggerConnected","triggerProducerAdded","triggerConsumerAdded","clientId","connected","disconnected","producer","producerAdded","producerRemoved","consumer","consumerAdded","consumerRemoved"],"sourceRoot":""} \ No newline at end of file diff --git a/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js b/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js new file mode 100644 index 0000000..8abcca4 --- /dev/null +++ b/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js @@ -0,0 +1,5 @@ +/*! gstwebrtc-api (https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/gstwebrtc-api), MPL-2.0 License, Copyright (C) 2022 Igalia S.L. , Author: Loïc Le Page */ +/*! Contains embedded adapter from webrtc-adapter (https://github.com/webrtcHacks/adapter), BSD 3-Clause License, Copyright (c) 2014, The WebRTC project authors. All rights reserved. Copyright (c) 2018, The adapter.js project authors. All rights reserved. */ + +(()=>{"use strict";var e={539:e=>{const t={generateIdentifier:function(){return Math.random().toString(36).substring(2,12)}};t.localCName=t.generateIdentifier(),t.splitLines=function(e){return e.trim().split("\n").map(e=>e.trim())},t.splitSections=function(e){return e.split("\nm=").map((e,t)=>(t>0?"m="+e:e).trim()+"\r\n")},t.getDescription=function(e){const n=t.splitSections(e);return n&&n[0]},t.getMediaSections=function(e){const n=t.splitSections(e);return n.shift(),n},t.matchPrefix=function(e,n){return t.splitLines(e).filter(e=>0===e.indexOf(n))},t.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1],attributes:t.slice(2).join(" ")}},t.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+(e.attributes?" "+e.attributes:"")+"\r\n"},t.parseFmtp=function(e){const t={};let n;const r=e.substring(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?r.push(t+"="+e.parameters[t]):r.push(t)}),t+="a=fmtp:"+n+" "+r.join(";")+"\r\n"}return t},t.parseRtcpFb=function(e){const t=e.substring(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},t.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach(e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"}),t},t.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substring(7,t),10)},r=e.indexOf(":",t);return r>-1?(n.attribute=e.substring(t+1,r),n.value=e.substring(r+1)):n.attribute=e.substring(t+1),n},t.parseSsrcGroup=function(e){const t=e.substring(13).split(" ");return{semantics:t.shift(),ssrcs:t.map(e=>parseInt(e,10))}},t.getMid=function(e){const n=t.matchPrefix(e,"a=mid:")[0];if(n)return n.substring(6)},t.parseFingerprint=function(e){const t=e.substring(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},t.getDtlsParameters=function(e,n){return{role:"auto",fingerprints:t.matchPrefix(e+n,"a=fingerprint:").map(t.parseFingerprint)}},t.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},t.parseCryptoLine=function(e){const t=e.substring(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},t.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?t.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},t.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substring(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},t.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},t.getCryptoParameters=function(e,n){return t.matchPrefix(e+n,"a=crypto:").map(t.parseCryptoLine)},t.getIceParameters=function(e,n){const r=t.matchPrefix(e+n,"a=ice-ufrag:")[0],i=t.matchPrefix(e+n,"a=ice-pwd:")[0];return r&&i?{usernameFragment:r.substring(12),password:i.substring(10)}:null},t.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},t.parseRtpParameters=function(e){const n={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=t.splitLines(e)[0].split(" ");n.profile=r[2];for(let i=3;i{n.headerExtensions.push(t.parseExtmap(e))});const i=t.matchPrefix(e,"a=rtcp-fb:* ").map(t.parseRtcpFb);return n.codecs.forEach(e=>{i.forEach(t=>{e.rtcpFeedback.find(e=>e.type===t.type&&e.parameter===t.parameter)||e.rtcpFeedback.push(t)})}),n},t.writeRtpDescription=function(e,n){let r="";r+="m="+e+" ",r+=n.codecs.length>0?"9":"0",r+=" "+(n.profile||"UDP/TLS/RTP/SAVPF")+" ",r+=n.codecs.map(e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",n.codecs.forEach(e=>{r+=t.writeRtpMap(e),r+=t.writeFmtp(e),r+=t.writeRtcpFb(e)});let i=0;return n.codecs.forEach(e=>{e.maxptime>i&&(i=e.maxptime)}),i>0&&(r+="a=maxptime:"+i+"\r\n"),n.headerExtensions&&n.headerExtensions.forEach(e=>{r+=t.writeExtmap(e)}),r},t.parseRtpEncodingParameters=function(e){const n=[],r=t.parseRtpParameters(e),i=-1!==r.fecMechanisms.indexOf("RED"),o=-1!==r.fecMechanisms.indexOf("ULPFEC"),s=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute),a=s.length>0&&s[0].ssrc;let c;const d=t.matchPrefix(e,"a=ssrc-group:FID").map(e=>e.substring(17).split(" ").map(e=>parseInt(e,10)));d.length>0&&d[0].length>1&&d[0][0]===a&&(c=d[0][1]),r.codecs.forEach(e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let t={ssrc:a,codecPayloadType:parseInt(e.parameters.apt,10)};a&&c&&(t.rtx={ssrc:c}),n.push(t),i&&(t=JSON.parse(JSON.stringify(t)),t.fec={ssrc:a,mechanism:o?"red+ulpfec":"red"},n.push(t))}}),0===n.length&&a&&n.push({ssrc:a});let l=t.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substring(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substring(5),10)*.95-16e3:void 0,n.forEach(e=>{e.maxBitrate=l})),n},t.parseRtcpParameters=function(e){const n={},r=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute)[0];r&&(n.cname=r.value,n.ssrc=r.ssrc);const i=t.matchPrefix(e,"a=rtcp-rsize");n.reducedSize=i.length>0,n.compound=0===i.length;const o=t.matchPrefix(e,"a=rtcp-mux");return n.mux=o.length>0,n},t.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},t.parseMsid=function(e){let n;const r=t.matchPrefix(e,"a=msid:");if(1===r.length)return n=r[0].substring(7).split(" "),{stream:n[0],track:n[1]};const i=t.matchPrefix(e,"a=ssrc:").map(e=>t.parseSsrcMedia(e)).filter(e=>"msid"===e.attribute);return i.length>0?(n=i[0].value.split(" "),{stream:n[0],track:n[1]}):void 0},t.parseSctpDescription=function(e){const n=t.parseMLine(e),r=t.matchPrefix(e,"a=max-message-size:");let i;r.length>0&&(i=parseInt(r[0].substring(19),10)),isNaN(i)&&(i=65536);const o=t.matchPrefix(e,"a=sctp-port:");if(o.length>0)return{port:parseInt(o[0].substring(12),10),protocol:n.fmt,maxMessageSize:i};const s=t.matchPrefix(e,"a=sctpmap:");if(s.length>0){const e=s[0].substring(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:i}}},t.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},t.generateSessionId=function(){return Math.random().toString().substr(2,22)},t.writeSessionBoilerplate=function(e,n,r){let i;const o=void 0!==n?n:2;i=e||t.generateSessionId();return"v=0\r\no="+(r||"thisisadapterortc")+" "+i+" "+o+" IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\n"},t.getDirection=function(e,n){const r=t.splitLines(e);for(let e=0;e{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e={};n.r(e),n.d(e,{fixNegotiationNeeded:()=>w,shimAddTrackRemoveTrack:()=>P,shimAddTrackRemoveTrackWithNative:()=>E,shimGetDisplayMedia:()=>y,shimGetSendersWithDtmf:()=>S,shimGetStats:()=>k,shimGetUserMedia:()=>C,shimMediaStream:()=>v,shimOnTrack:()=>b,shimPeerConnection:()=>R,shimSenderReceiverGetStats:()=>T});var t={};n.r(t),n.d(t,{shimAddTransceiver:()=>G,shimCreateAnswer:()=>U,shimCreateOffer:()=>N,shimGetDisplayMedia:()=>I,shimGetParameters:()=>j,shimGetUserMedia:()=>A,shimOnTrack:()=>x,shimPeerConnection:()=>O,shimRTCDataChannel:()=>H,shimReceiverGetStats:()=>M,shimRemoveStream:()=>L,shimSenderGetStats:()=>D});var r={};n.r(r),n.d(r,{shimAudioContext:()=>B,shimCallbacksAPI:()=>J,shimConstraints:()=>K,shimCreateOfferLegacy:()=>V,shimGetUserMedia:()=>q,shimLocalStreamsAPI:()=>F,shimRTCIceServerUrls:()=>W,shimRemoteStreamsAPI:()=>z,shimTrackEventTransceiver:()=>Y});var i={};n.r(i),n.d(i,{removeExtmapAllowMixed:()=>re,shimAddIceCandidateNullOrEmpty:()=>ie,shimConnectionState:()=>ne,shimMaxMessageSize:()=>ee,shimParameterlessSetLocalDescription:()=>oe,shimRTCIceCandidate:()=>$,shimRTCIceCandidateRelayProtocol:()=>Q,shimSendThrowTypeError:()=>te});let o=!0,s=!0;function a(e,t,n){const r=e.match(t);return r&&r.length>=n&&parseInt(r[n],10)}function c(e,t,n){if(!e.RTCPeerConnection)return;const r=e.RTCPeerConnection.prototype,i=r.addEventListener;r.addEventListener=function(e,r){if(e!==t)return i.apply(this,arguments);const o=e=>{const t=n(e);t&&(r.handleEvent?r.handleEvent(t):r(t))};return this._eventMap=this._eventMap||{},this._eventMap[t]||(this._eventMap[t]=new Map),this._eventMap[t].set(r,o),i.apply(this,[e,o])};const o=r.removeEventListener;r.removeEventListener=function(e,n){if(e!==t||!this._eventMap||!this._eventMap[t])return o.apply(this,arguments);if(!this._eventMap[t].has(n))return o.apply(this,arguments);const r=this._eventMap[t].get(n);return this._eventMap[t].delete(n),0===this._eventMap[t].size&&delete this._eventMap[t],0===Object.keys(this._eventMap).length&&delete this._eventMap,o.apply(this,[e,r])},Object.defineProperty(r,"on"+t,{get(){return this["_on"+t]},set(e){this["_on"+t]&&(this.removeEventListener(t,this["_on"+t]),delete this["_on"+t]),e&&this.addEventListener(t,this["_on"+t]=e)},enumerable:!0,configurable:!0})}function d(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(o=e,e?"adapter.js logging disabled":"adapter.js logging enabled")}function l(e){return"boolean"!=typeof e?new Error("Argument type: "+typeof e+". Please use a boolean."):(s=!e,"adapter.js deprecation warnings "+(e?"disabled":"enabled"))}function h(){if("object"==typeof window){if(o)return;"undefined"!=typeof console&&"function"==typeof console.log&&console.log.apply(console,arguments)}}function p(e,t){s&&console.warn(e+" is deprecated, please use "+t+" instead.")}function u(e){return"[object Object]"===Object.prototype.toString.call(e)}function m(e){return u(e)?Object.keys(e).reduce(function(t,n){const r=u(e[n]),i=r?m(e[n]):e[n],o=r&&!Object.keys(i).length;return void 0===i||o?t:Object.assign(t,{[n]:i})},{}):e}function _(e,t,n){t&&!n.has(t.id)&&(n.set(t.id,t),Object.keys(t).forEach(r=>{r.endsWith("Id")?_(e,e.get(t[r]),n):r.endsWith("Ids")&&t[r].forEach(t=>{_(e,e.get(t),n)})}))}function f(e,t,n){const r=n?"outbound-rtp":"inbound-rtp",i=new Map;if(null===t)return i;const o=[];return e.forEach(e=>{"track"===e.type&&e.trackIdentifier===t.id&&o.push(e)}),o.forEach(t=>{e.forEach(n=>{n.type===r&&n.trackId===t.id&&_(e,n,i)})}),i}const g=h;function C(e,t){const n=e&&e.navigator;if(!n.mediaDevices)return;const r=function(e){if("object"!=typeof e||e.mandatory||e.optional)return e;const t={};return Object.keys(e).forEach(n=>{if("require"===n||"advanced"===n||"mediaSource"===n)return;const r="object"==typeof e[n]?e[n]:{ideal:e[n]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);const i=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==r.ideal){t.optional=t.optional||[];let e={};"number"==typeof r.ideal?(e[i("min",n)]=r.ideal,t.optional.push(e),e={},e[i("max",n)]=r.ideal,t.optional.push(e)):(e[i("",n)]=r.ideal,t.optional.push(e))}void 0!==r.exact&&"number"!=typeof r.exact?(t.mandatory=t.mandatory||{},t.mandatory[i("",n)]=r.exact):["min","max"].forEach(e=>{void 0!==r[e]&&(t.mandatory=t.mandatory||{},t.mandatory[i(e,n)]=r[e])})}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},i=function(e,i){if(t.version>=61)return i(e);if((e=JSON.parse(JSON.stringify(e)))&&"object"==typeof e.audio){const t=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])};t((e=JSON.parse(JSON.stringify(e))).audio,"autoGainControl","googAutoGainControl"),t(e.audio,"noiseSuppression","googNoiseSuppression"),e.audio=r(e.audio)}if(e&&"object"==typeof e.video){let o=e.video.facingMode;o=o&&("object"==typeof o?o:{ideal:o});const s=t.version<66;if(o&&("user"===o.exact||"environment"===o.exact||"user"===o.ideal||"environment"===o.ideal)&&(!n.mediaDevices.getSupportedConstraints||!n.mediaDevices.getSupportedConstraints().facingMode||s)){let t;if(delete e.video.facingMode,"environment"===o.exact||"environment"===o.ideal?t=["back","rear"]:"user"!==o.exact&&"user"!==o.ideal||(t=["front"]),t)return n.mediaDevices.enumerateDevices().then(n=>{let s=(n=n.filter(e=>"videoinput"===e.kind)).find(e=>t.some(t=>e.label.toLowerCase().includes(t)));return!s&&n.length&&t.includes("back")&&(s=n[n.length-1]),s&&(e.video.deviceId=o.exact?{exact:s.deviceId}:{ideal:s.deviceId}),e.video=r(e.video),g("chrome: "+JSON.stringify(e)),i(e)})}e.video=r(e.video)}return g("chrome: "+JSON.stringify(e)),i(e)},o=function(e){return t.version>=64?e:{name:{PermissionDeniedError:"NotAllowedError",PermissionDismissedError:"NotAllowedError",InvalidStateError:"NotAllowedError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotAllowedError",MediaDeviceKillSwitchOn:"NotAllowedError",TabCaptureError:"AbortError",ScreenCaptureError:"AbortError",DeviceCaptureError:"AbortError"}[e.name]||e.name,message:e.message,constraint:e.constraint||e.constraintName,toString(){return this.name+(this.message&&": ")+this.message}}};if(n.getUserMedia=function(e,t,r){i(e,e=>{n.webkitGetUserMedia(e,t,e=>{r&&r(o(e))})})}.bind(n),n.mediaDevices.getUserMedia){const e=n.mediaDevices.getUserMedia.bind(n.mediaDevices);n.mediaDevices.getUserMedia=function(t){return i(t,t=>e(t).then(e=>{if(t.audio&&!e.getAudioTracks().length||t.video&&!e.getVideoTracks().length)throw e.getTracks().forEach(e=>{e.stop()}),new DOMException("","NotFoundError");return e},e=>Promise.reject(o(e))))}}}function y(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&("function"==typeof t?e.navigator.mediaDevices.getDisplayMedia=function(n){return t(n).then(t=>{const r=n.video&&n.video.width,i=n.video&&n.video.height,o=n.video&&n.video.frameRate;return n.video={mandatory:{chromeMediaSource:"desktop",chromeMediaSourceId:t,maxFrameRate:o||3}},r&&(n.video.mandatory.maxWidth=r),i&&(n.video.mandatory.maxHeight=i),e.navigator.mediaDevices.getUserMedia(n)})}:console.error("shimGetDisplayMedia: getSourceId argument is not a function"))}function v(e){e.MediaStream=e.MediaStream||e.webkitMediaStream}function b(e){if("object"==typeof e&&e.RTCPeerConnection&&!("ontrack"in e.RTCPeerConnection.prototype)){Object.defineProperty(e.RTCPeerConnection.prototype,"ontrack",{get(){return this._ontrack},set(e){this._ontrack&&this.removeEventListener("track",this._ontrack),this.addEventListener("track",this._ontrack=e)},enumerable:!0,configurable:!0});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){return this._ontrackpoly||(this._ontrackpoly=t=>{t.stream.addEventListener("addtrack",n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find(e=>e.track&&e.track.id===n.track.id):{track:n.track};const i=new Event("track");i.track=n.track,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)}),t.stream.getTracks().forEach(n=>{let r;r=e.RTCPeerConnection.prototype.getReceivers?this.getReceivers().find(e=>e.track&&e.track.id===n.id):{track:n};const i=new Event("track");i.track=n,i.receiver=r,i.transceiver={receiver:r},i.streams=[t.stream],this.dispatchEvent(i)})},this.addEventListener("addstream",this._ontrackpoly)),t.apply(this,arguments)}}else c(e,"track",e=>(e.transceiver||Object.defineProperty(e,"transceiver",{value:{receiver:e.receiver}}),e))}function S(e){if("object"==typeof e&&e.RTCPeerConnection&&!("getSenders"in e.RTCPeerConnection.prototype)&&"createDTMFSender"in e.RTCPeerConnection.prototype){const t=function(e,t){return{track:t,get dtmf(){return void 0===this._dtmf&&("audio"===t.kind?this._dtmf=e.createDTMFSender(t):this._dtmf=null),this._dtmf},_pc:e}};if(!e.RTCPeerConnection.prototype.getSenders){e.RTCPeerConnection.prototype.getSenders=function(){return this._senders=this._senders||[],this._senders.slice()};const n=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,r){let i=n.apply(this,arguments);return i||(i=t(this,e),this._senders.push(i)),i};const r=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){r.apply(this,arguments);const t=this._senders.indexOf(e);-1!==t&&this._senders.splice(t,1)}}const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._senders=this._senders||[],n.apply(this,[e]),e.getTracks().forEach(e=>{this._senders.push(t(this,e))})};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){this._senders=this._senders||[],r.apply(this,[e]),e.getTracks().forEach(e=>{const t=this._senders.find(t=>t.track===e);t&&this._senders.splice(this._senders.indexOf(t),1)})}}else if("object"==typeof e&&e.RTCPeerConnection&&"getSenders"in e.RTCPeerConnection.prototype&&"createDTMFSender"in e.RTCPeerConnection.prototype&&e.RTCRtpSender&&!("dtmf"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e},Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=this._pc.createDTMFSender(this.track):this._dtmf=null),this._dtmf}})}}function k(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,n,r]=arguments;if(arguments.length>0&&"function"==typeof e)return t.apply(this,arguments);if(0===t.length&&(0===arguments.length||"function"!=typeof e))return t.apply(this,[]);const i=function(e){const t={};return e.result().forEach(e=>{const n={id:e.id,timestamp:e.timestamp,type:{localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[e.type]||e.type};e.names().forEach(t=>{n[t]=e.stat(t)}),t[n.id]=n}),t},o=function(e){return new Map(Object.keys(e).map(t=>[t,e[t]]))};if(arguments.length>=2){const r=function(e){n(o(i(e)))};return t.apply(this,[r,e])}return new Promise((e,n)=>{t.apply(this,[function(t){e(o(i(t)))},n])}).then(n,r)}}function T(e){if(!("object"==typeof e&&e.RTCPeerConnection&&e.RTCRtpSender&&e.RTCRtpReceiver))return;if(!("getStats"in e.RTCRtpSender.prototype)){const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){const e=this;return this._pc.getStats().then(t=>f(t,e.track,!0))}}if(!("getStats"in e.RTCRtpReceiver.prototype)){const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e}),c(e,"track",e=>(e.receiver._pc=e.srcElement,e)),e.RTCRtpReceiver.prototype.getStats=function(){const e=this;return this._pc.getStats().then(t=>f(t,e.track,!1))}}if(!("getStats"in e.RTCRtpSender.prototype)||!("getStats"in e.RTCRtpReceiver.prototype))return;const t=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){if(arguments.length>0&&arguments[0]instanceof e.MediaStreamTrack){const e=arguments[0];let t,n,r;return this.getSenders().forEach(n=>{n.track===e&&(t?r=!0:t=n)}),this.getReceivers().forEach(t=>(t.track===e&&(n?r=!0:n=t),t.track===e)),r||t&&n?Promise.reject(new DOMException("There are more than one sender or receiver for the track.","InvalidAccessError")):t?t.getStats():n?n.getStats():Promise.reject(new DOMException("There is no sender or receiver for the track.","InvalidAccessError"))}return t.apply(this,arguments)}}function E(e){e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},Object.keys(this._shimmedLocalStreams).map(e=>this._shimmedLocalStreams[e][0])};const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,n){if(!n)return t.apply(this,arguments);this._shimmedLocalStreams=this._shimmedLocalStreams||{};const r=t.apply(this,arguments);return this._shimmedLocalStreams[n.id]?-1===this._shimmedLocalStreams[n.id].indexOf(r)&&this._shimmedLocalStreams[n.id].push(r):this._shimmedLocalStreams[n.id]=[n,r],r};const n=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){this._shimmedLocalStreams=this._shimmedLocalStreams||{},e.getTracks().forEach(e=>{if(this.getSenders().find(t=>t.track===e))throw new DOMException("Track already exists.","InvalidAccessError")});const t=this.getSenders();n.apply(this,arguments);const r=this.getSenders().filter(e=>-1===t.indexOf(e));this._shimmedLocalStreams[e.id]=[e].concat(r)};const r=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},delete this._shimmedLocalStreams[e.id],r.apply(this,arguments)};const i=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){return this._shimmedLocalStreams=this._shimmedLocalStreams||{},e&&Object.keys(this._shimmedLocalStreams).forEach(t=>{const n=this._shimmedLocalStreams[t].indexOf(e);-1!==n&&this._shimmedLocalStreams[t].splice(n,1),1===this._shimmedLocalStreams[t].length&&delete this._shimmedLocalStreams[t]}),i.apply(this,arguments)}}function P(e,t){if(!e.RTCPeerConnection)return;if(e.RTCPeerConnection.prototype.addTrack&&t.version>=65)return E(e);const n=e.RTCPeerConnection.prototype.getLocalStreams;e.RTCPeerConnection.prototype.getLocalStreams=function(){const e=n.apply(this);return this._reverseStreams=this._reverseStreams||{},e.map(e=>this._reverseStreams[e.id])};const r=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(t){if(this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},t.getTracks().forEach(e=>{if(this.getSenders().find(t=>t.track===e))throw new DOMException("Track already exists.","InvalidAccessError")}),!this._reverseStreams[t.id]){const n=new e.MediaStream(t.getTracks());this._streams[t.id]=n,this._reverseStreams[n.id]=t,t=n}r.apply(this,[t])};const i=e.RTCPeerConnection.prototype.removeStream;function o(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(i.id,"g"),r.id)}),new RTCSessionDescription({type:t.type,sdp:n})}e.RTCPeerConnection.prototype.removeStream=function(e){this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},i.apply(this,[this._streams[e.id]||e]),delete this._reverseStreams[this._streams[e.id]?this._streams[e.id].id:e.id],delete this._streams[e.id]},e.RTCPeerConnection.prototype.addTrack=function(t,n){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");const r=[].slice.call(arguments,1);if(1!==r.length||!r[0].getTracks().find(e=>e===t))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(this.getSenders().find(e=>e.track===t))throw new DOMException("Track already exists.","InvalidAccessError");this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{};const i=this._streams[n.id];if(i)i.addTrack(t),Promise.resolve().then(()=>{this.dispatchEvent(new Event("negotiationneeded"))});else{const r=new e.MediaStream([t]);this._streams[n.id]=r,this._reverseStreams[r.id]=n,this.addStream(r)}return this.getSenders().find(e=>e.track===t)},["createOffer","createAnswer"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){const e=arguments;return arguments.length&&"function"==typeof arguments[0]?n.apply(this,[t=>{const n=o(this,t);e[0].apply(null,[n])},t=>{e[1]&&e[1].apply(null,t)},arguments[2]]):n.apply(this,arguments).then(e=>o(this,e))}};e.RTCPeerConnection.prototype[t]=r[t]});const s=e.RTCPeerConnection.prototype.setLocalDescription;e.RTCPeerConnection.prototype.setLocalDescription=function(){return arguments.length&&arguments[0].type?(arguments[0]=function(e,t){let n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(t=>{const r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(r.id,"g"),i.id)}),new RTCSessionDescription({type:t.type,sdp:n})}(this,arguments[0]),s.apply(this,arguments)):s.apply(this,arguments)};const a=Object.getOwnPropertyDescriptor(e.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(e.RTCPeerConnection.prototype,"localDescription",{get(){const e=a.get.apply(this);return""===e.type?e:o(this,e)}}),e.RTCPeerConnection.prototype.removeTrack=function(e){if("closed"===this.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!e._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(!(e._pc===this))throw new DOMException("Sender was not created by this connection.","InvalidAccessError");let t;this._streams=this._streams||{},Object.keys(this._streams).forEach(n=>{this._streams[n].getTracks().find(t=>e.track===t)&&(t=this._streams[n])}),t&&(1===t.getTracks().length?this.removeStream(this._reverseStreams[t.id]):t.removeTrack(e.track),this.dispatchEvent(new Event("negotiationneeded")))}}function R(e,t){!e.RTCPeerConnection&&e.webkitRTCPeerConnection&&(e.RTCPeerConnection=e.webkitRTCPeerConnection),e.RTCPeerConnection&&t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]})}function w(e,t){c(e,"negotiationneeded",e=>{const n=e.target;if(!(t.version<72||n.getConfiguration&&"plan-b"===n.getConfiguration().sdpSemantics)||"stable"===n.signalingState)return e})}function A(e,t){const n=e&&e.navigator,r=e&&e.MediaStreamTrack;if(n.getUserMedia=function(e,t,r){p("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(t,r)},!(t.version>55&&"autoGainControl"in n.mediaDevices.getSupportedConstraints())){const e=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])},t=n.mediaDevices.getUserMedia.bind(n.mediaDevices);if(n.mediaDevices.getUserMedia=function(n){return"object"==typeof n&&"object"==typeof n.audio&&(n=JSON.parse(JSON.stringify(n)),e(n.audio,"autoGainControl","mozAutoGainControl"),e(n.audio,"noiseSuppression","mozNoiseSuppression")),t(n)},r&&r.prototype.getSettings){const t=r.prototype.getSettings;r.prototype.getSettings=function(){const n=t.apply(this,arguments);return e(n,"mozAutoGainControl","autoGainControl"),e(n,"mozNoiseSuppression","noiseSuppression"),n}}if(r&&r.prototype.applyConstraints){const t=r.prototype.applyConstraints;r.prototype.applyConstraints=function(n){return"audio"===this.kind&&"object"==typeof n&&(n=JSON.parse(JSON.stringify(n)),e(n,"autoGainControl","mozAutoGainControl"),e(n,"noiseSuppression","mozNoiseSuppression")),t.apply(this,[n])}}}}function I(e,t){e.navigator.mediaDevices&&"getDisplayMedia"in e.navigator.mediaDevices||e.navigator.mediaDevices&&(e.navigator.mediaDevices.getDisplayMedia=function(n){if(!n||!n.video){const e=new DOMException("getDisplayMedia without video constraints is undefined");return e.name="NotFoundError",e.code=8,Promise.reject(e)}return!0===n.video?n.video={mediaSource:t}:n.video.mediaSource=t,e.navigator.mediaDevices.getUserMedia(n)})}function x(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function O(e,t){if("object"!=typeof e||!e.RTCPeerConnection&&!e.mozRTCPeerConnection)return;!e.RTCPeerConnection&&e.mozRTCPeerConnection&&(e.RTCPeerConnection=e.mozRTCPeerConnection),t.version<53&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){const n=e.RTCPeerConnection.prototype[t],r={[t](){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}};e.RTCPeerConnection.prototype[t]=r[t]});const n={inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"},r=e.RTCPeerConnection.prototype.getStats;e.RTCPeerConnection.prototype.getStats=function(){const[e,i,o]=arguments;return r.apply(this,[e||null]).then(e=>{if(t.version<53&&!i)try{e.forEach(e=>{e.type=n[e.type]||e.type})}catch(t){if("TypeError"!==t.name)throw t;e.forEach((t,r)=>{e.set(r,Object.assign({},t,{type:n[t.type]||t.type}))})}return e}).then(i,o)}}function D(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpSender.prototype)return;const t=e.RTCPeerConnection.prototype.getSenders;t&&(e.RTCPeerConnection.prototype.getSenders=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e});const n=e.RTCPeerConnection.prototype.addTrack;n&&(e.RTCPeerConnection.prototype.addTrack=function(){const e=n.apply(this,arguments);return e._pc=this,e}),e.RTCRtpSender.prototype.getStats=function(){return this.track?this._pc.getStats(this.track):Promise.resolve(new Map)}}function M(e){if("object"!=typeof e||!e.RTCPeerConnection||!e.RTCRtpSender)return;if(e.RTCRtpSender&&"getStats"in e.RTCRtpReceiver.prototype)return;const t=e.RTCPeerConnection.prototype.getReceivers;t&&(e.RTCPeerConnection.prototype.getReceivers=function(){const e=t.apply(this,[]);return e.forEach(e=>e._pc=this),e}),c(e,"track",e=>(e.receiver._pc=e.srcElement,e)),e.RTCRtpReceiver.prototype.getStats=function(){return this._pc.getStats(this.track)}}function L(e){e.RTCPeerConnection&&!("removeStream"in e.RTCPeerConnection.prototype)&&(e.RTCPeerConnection.prototype.removeStream=function(e){p("removeStream","removeTrack"),this.getSenders().forEach(t=>{t.track&&e.getTracks().includes(t.track)&&this.removeTrack(t)})})}function H(e){e.DataChannel&&!e.RTCDataChannel&&(e.RTCDataChannel=e.DataChannel)}function G(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.addTransceiver;t&&(e.RTCPeerConnection.prototype.addTransceiver=function(){this.setParametersPromises=[];let e=arguments[1]&&arguments[1].sendEncodings;void 0===e&&(e=[]),e=[...e];const n=e.length>0;n&&e.forEach(e=>{if("rid"in e){if(!/^[a-z0-9]{0,16}$/i.test(e.rid))throw new TypeError("Invalid RID value provided.")}if("scaleResolutionDownBy"in e&&!(parseFloat(e.scaleResolutionDownBy)>=1))throw new RangeError("scale_resolution_down_by must be >= 1.0");if("maxFramerate"in e&&!(parseFloat(e.maxFramerate)>=0))throw new RangeError("max_framerate must be >= 0.0")});const r=t.apply(this,arguments);if(n){const{sender:t}=r,n=t.getParameters();(!("encodings"in n)||1===n.encodings.length&&0===Object.keys(n.encodings[0]).length)&&(n.encodings=e,t.sendEncodings=e,this.setParametersPromises.push(t.setParameters(n).then(()=>{delete t.sendEncodings}).catch(()=>{delete t.sendEncodings})))}return r})}function j(e){if("object"!=typeof e||!e.RTCRtpSender)return;const t=e.RTCRtpSender.prototype.getParameters;t&&(e.RTCRtpSender.prototype.getParameters=function(){const e=t.apply(this,arguments);return"encodings"in e||(e.encodings=[].concat(this.sendEncodings||[{}])),e})}function N(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}function U(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype.createAnswer;e.RTCPeerConnection.prototype.createAnswer=function(){return this.setParametersPromises&&this.setParametersPromises.length?Promise.all(this.setParametersPromises).then(()=>t.apply(this,arguments)).finally(()=>{this.setParametersPromises=[]}):t.apply(this,arguments)}}function F(e){if("object"==typeof e&&e.RTCPeerConnection){if("getLocalStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),!("addStream"in e.RTCPeerConnection.prototype)){const t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addStream=function(e){this._localStreams||(this._localStreams=[]),this._localStreams.includes(e)||this._localStreams.push(e),e.getAudioTracks().forEach(n=>t.call(this,n,e)),e.getVideoTracks().forEach(n=>t.call(this,n,e))},e.RTCPeerConnection.prototype.addTrack=function(e,...n){return n&&n.forEach(e=>{this._localStreams?this._localStreams.includes(e)||this._localStreams.push(e):this._localStreams=[e]}),t.apply(this,arguments)}}"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(e){this._localStreams||(this._localStreams=[]);const t=this._localStreams.indexOf(e);if(-1===t)return;this._localStreams.splice(t,1);const n=e.getTracks();this.getSenders().forEach(e=>{n.includes(e.track)&&this.removeTrack(e)})})}}function z(e){if("object"==typeof e&&e.RTCPeerConnection&&("getRemoteStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),!("onaddstream"in e.RTCPeerConnection.prototype))){Object.defineProperty(e.RTCPeerConnection.prototype,"onaddstream",{get(){return this._onaddstream},set(e){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=e),this.addEventListener("track",this._onaddstreampoly=e=>{e.streams.forEach(e=>{if(this._remoteStreams||(this._remoteStreams=[]),this._remoteStreams.includes(e))return;this._remoteStreams.push(e);const t=new Event("addstream");t.stream=e,this.dispatchEvent(t)})})}});const t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){const e=this;return this._onaddstreampoly||this.addEventListener("track",this._onaddstreampoly=function(t){t.streams.forEach(t=>{if(e._remoteStreams||(e._remoteStreams=[]),e._remoteStreams.indexOf(t)>=0)return;e._remoteStreams.push(t);const n=new Event("addstream");n.stream=t,e.dispatchEvent(n)})}),t.apply(e,arguments)}}}function J(e){if("object"!=typeof e||!e.RTCPeerConnection)return;const t=e.RTCPeerConnection.prototype,n=t.createOffer,r=t.createAnswer,i=t.setLocalDescription,o=t.setRemoteDescription,s=t.addIceCandidate;t.createOffer=function(e,t){const r=arguments.length>=2?arguments[2]:arguments[0],i=n.apply(this,[r]);return t?(i.then(e,t),Promise.resolve()):i},t.createAnswer=function(e,t){const n=arguments.length>=2?arguments[2]:arguments[0],i=r.apply(this,[n]);return t?(i.then(e,t),Promise.resolve()):i};let a=function(e,t,n){const r=i.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r};t.setLocalDescription=a,a=function(e,t,n){const r=o.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.setRemoteDescription=a,a=function(e,t,n){const r=s.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.addIceCandidate=a}function q(e){const t=e&&e.navigator;if(t.mediaDevices&&t.mediaDevices.getUserMedia){const e=t.mediaDevices,n=e.getUserMedia.bind(e);t.mediaDevices.getUserMedia=e=>n(K(e))}!t.getUserMedia&&t.mediaDevices&&t.mediaDevices.getUserMedia&&(t.getUserMedia=function(e,n,r){t.mediaDevices.getUserMedia(e).then(n,r)}.bind(t))}function K(e){return e&&void 0!==e.video?Object.assign({},e,{video:m(e.video)}):e}function W(e){if(!e.RTCPeerConnection)return;const t=e.RTCPeerConnection;e.RTCPeerConnection=function(e,n){if(e&&e.iceServers){const t=[];for(let n=0;nt.generateCertificate})}function Y(e){"object"==typeof e&&e.RTCTrackEvent&&"receiver"in e.RTCTrackEvent.prototype&&!("transceiver"in e.RTCTrackEvent.prototype)&&Object.defineProperty(e.RTCTrackEvent.prototype,"transceiver",{get(){return{receiver:this.receiver}}})}function V(e){const t=e.RTCPeerConnection.prototype.createOffer;e.RTCPeerConnection.prototype.createOffer=function(e){if(e){void 0!==e.offerToReceiveAudio&&(e.offerToReceiveAudio=!!e.offerToReceiveAudio);const t=this.getTransceivers().find(e=>"audio"===e.receiver.track.kind);!1===e.offerToReceiveAudio&&t?"sendrecv"===t.direction?t.setDirection?t.setDirection("sendonly"):t.direction="sendonly":"recvonly"===t.direction&&(t.setDirection?t.setDirection("inactive"):t.direction="inactive"):!0!==e.offerToReceiveAudio||t||this.addTransceiver("audio",{direction:"recvonly"}),void 0!==e.offerToReceiveVideo&&(e.offerToReceiveVideo=!!e.offerToReceiveVideo);const n=this.getTransceivers().find(e=>"video"===e.receiver.track.kind);!1===e.offerToReceiveVideo&&n?"sendrecv"===n.direction?n.setDirection?n.setDirection("sendonly"):n.direction="sendonly":"recvonly"===n.direction&&(n.setDirection?n.setDirection("inactive"):n.direction="inactive"):!0!==e.offerToReceiveVideo||n||this.addTransceiver("video",{direction:"recvonly"})}return t.apply(this,arguments)}}function B(e){"object"!=typeof e||e.AudioContext||(e.AudioContext=e.webkitAudioContext)}var Z=n(539),X=n.n(Z);function $(e){if(!e.RTCIceCandidate||e.RTCIceCandidate&&"foundation"in e.RTCIceCandidate.prototype)return;const t=e.RTCIceCandidate;e.RTCIceCandidate=function(e){if("object"==typeof e&&e.candidate&&0===e.candidate.indexOf("a=")&&((e=JSON.parse(JSON.stringify(e))).candidate=e.candidate.substring(2)),e.candidate&&e.candidate.length){const n=new t(e),r=X().parseCandidate(e.candidate);for(const e in r)e in n||Object.defineProperty(n,e,{value:r[e]});return n.toJSON=function(){return{candidate:n.candidate,sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex,usernameFragment:n.usernameFragment}},n}return new t(e)},e.RTCIceCandidate.prototype=t.prototype,c(e,"icecandidate",t=>(t.candidate&&Object.defineProperty(t,"candidate",{value:new e.RTCIceCandidate(t.candidate),writable:"false"}),t))}function Q(e){!e.RTCIceCandidate||e.RTCIceCandidate&&"relayProtocol"in e.RTCIceCandidate.prototype||c(e,"icecandidate",e=>{if(e.candidate){const t=X().parseCandidate(e.candidate.candidate);"relay"===t.type&&(e.candidate.relayProtocol={0:"tls",1:"tcp",2:"udp"}[t.priority>>24])}return e})}function ee(e,t){if(!e.RTCPeerConnection)return;"sctp"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"sctp",{get(){return void 0===this._sctp?null:this._sctp}});const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){if(this._sctp=null,"chrome"===t.browser&&t.version>=76){const{sdpSemantics:e}=this.getConfiguration();"plan-b"===e&&Object.defineProperty(this,"sctp",{get(){return void 0===this._sctp?null:this._sctp},enumerable:!0,configurable:!0})}if(function(e){if(!e||!e.sdp)return!1;const t=X().splitSections(e.sdp);return t.shift(),t.some(e=>{const t=X().parseMLine(e);return t&&"application"===t.kind&&-1!==t.protocol.indexOf("SCTP")})}(arguments[0])){const e=function(e){const t=e.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);if(null===t||t.length<2)return-1;const n=parseInt(t[1],10);return n!=n?-1:n}(arguments[0]),n=function(e){let n=65536;return"firefox"===t.browser&&(n=t.version<57?-1===e?16384:2147483637:t.version<60?57===t.version?65535:65536:2147483637),n}(e),r=function(e,n){let r=65536;"firefox"===t.browser&&57===t.version&&(r=65535);const i=X().matchPrefix(e.sdp,"a=max-message-size:");return i.length>0?r=parseInt(i[0].substring(19),10):"firefox"===t.browser&&-1!==n&&(r=2147483637),r}(arguments[0],e);let i;i=0===n&&0===r?Number.POSITIVE_INFINITY:0===n||0===r?Math.max(n,r):Math.min(n,r);const o={};Object.defineProperty(o,"maxMessageSize",{get:()=>i}),this._sctp=o}return n.apply(this,arguments)}}function te(e){if(!e.RTCPeerConnection||!("createDataChannel"in e.RTCPeerConnection.prototype))return;function t(e,t){const n=e.send;e.send=function(){const r=arguments[0],i=r.length||r.size||r.byteLength;if("open"===e.readyState&&t.sctp&&i>t.sctp.maxMessageSize)throw new TypeError("Message too large (can send a maximum of "+t.sctp.maxMessageSize+" bytes)");return n.apply(e,arguments)}}const n=e.RTCPeerConnection.prototype.createDataChannel;e.RTCPeerConnection.prototype.createDataChannel=function(){const e=n.apply(this,arguments);return t(e,this),e},c(e,"datachannel",e=>(t(e.channel,e.target),e))}function ne(e){if(!e.RTCPeerConnection||"connectionState"in e.RTCPeerConnection.prototype)return;const t=e.RTCPeerConnection.prototype;Object.defineProperty(t,"connectionState",{get(){return{completed:"connected",checking:"connecting"}[this.iceConnectionState]||this.iceConnectionState},enumerable:!0,configurable:!0}),Object.defineProperty(t,"onconnectionstatechange",{get(){return this._onconnectionstatechange||null},set(e){this._onconnectionstatechange&&(this.removeEventListener("connectionstatechange",this._onconnectionstatechange),delete this._onconnectionstatechange),e&&this.addEventListener("connectionstatechange",this._onconnectionstatechange=e)},enumerable:!0,configurable:!0}),["setLocalDescription","setRemoteDescription"].forEach(e=>{const n=t[e];t[e]=function(){return this._connectionstatechangepoly||(this._connectionstatechangepoly=e=>{const t=e.target;if(t._lastConnectionState!==t.connectionState){t._lastConnectionState=t.connectionState;const n=new Event("connectionstatechange",e);t.dispatchEvent(n)}return e},this.addEventListener("iceconnectionstatechange",this._connectionstatechangepoly)),n.apply(this,arguments)}})}function re(e,t){if(!e.RTCPeerConnection)return;if("chrome"===t.browser&&t.version>=71)return;if("safari"===t.browser&&t.version>=605)return;const n=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(t){if(t&&t.sdp&&-1!==t.sdp.indexOf("\na=extmap-allow-mixed")){const n=t.sdp.split("\n").filter(e=>"a=extmap-allow-mixed"!==e.trim()).join("\n");e.RTCSessionDescription&&t instanceof e.RTCSessionDescription?arguments[0]=new e.RTCSessionDescription({type:t.type,sdp:n}):t.sdp=n}return n.apply(this,arguments)}}function ie(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.addIceCandidate;n&&0!==n.length&&(e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?("chrome"===t.browser&&t.version<78||"firefox"===t.browser&&t.version<68||"safari"===t.browser)&&arguments[0]&&""===arguments[0].candidate?Promise.resolve():n.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())})}function oe(e,t){if(!e.RTCPeerConnection||!e.RTCPeerConnection.prototype)return;const n=e.RTCPeerConnection.prototype.setLocalDescription;n&&0!==n.length&&(e.RTCPeerConnection.prototype.setLocalDescription=function(){let e=arguments[0]||{};if("object"!=typeof e||e.type&&e.sdp)return n.apply(this,arguments);if(e={type:e.type,sdp:e.sdp},!e.type)switch(this.signalingState){case"stable":case"have-local-offer":case"have-remote-pranswer":e.type="offer";break;default:e.type="answer"}if(e.sdp||"offer"!==e.type&&"answer"!==e.type)return n.apply(this,[e]);return("offer"===e.type?this.createOffer:this.createAnswer).apply(this).then(e=>n.apply(this,[e]))})}!function({window:n}={},o={shimChrome:!0,shimFirefox:!0,shimSafari:!0}){const s=h,c=function(e){const t={browser:null,version:null};if(void 0===e||!e.navigator||!e.navigator.userAgent)return t.browser="Not a browser.",t;const{navigator:n}=e;if(n.mozGetUserMedia)t.browser="firefox",t.version=a(n.userAgent,/Firefox\/(\d+)\./,1);else if(n.webkitGetUserMedia||!1===e.isSecureContext&&e.webkitRTCPeerConnection)t.browser="chrome",t.version=a(n.userAgent,/Chrom(e|ium)\/(\d+)\./,2);else{if(!e.RTCPeerConnection||!n.userAgent.match(/AppleWebKit\/(\d+)\./))return t.browser="Not a supported browser.",t;t.browser="safari",t.version=a(n.userAgent,/AppleWebKit\/(\d+)\./,1),t.supportsUnifiedPlan=e.RTCRtpTransceiver&&"currentDirection"in e.RTCRtpTransceiver.prototype}return t}(n),p={browserDetails:c,commonShim:i,extractVersion:a,disableLog:d,disableWarnings:l,sdp:Z};switch(c.browser){case"chrome":if(!e||!R||!o.shimChrome)return s("Chrome shim is not included in this adapter release."),p;if(null===c.version)return s("Chrome shim can not determine version, not shimming."),p;s("adapter.js shimming chrome."),p.browserShim=e,ie(n,c),oe(n),C(n,c),v(n),R(n,c),b(n),P(n,c),S(n),k(n),T(n),w(n,c),$(n),Q(n),ne(n),ee(n,c),te(n),re(n,c);break;case"firefox":if(!t||!O||!o.shimFirefox)return s("Firefox shim is not included in this adapter release."),p;s("adapter.js shimming firefox."),p.browserShim=t,ie(n,c),oe(n),A(n,c),O(n,c),x(n),L(n),D(n),M(n),H(n),G(n),j(n),N(n),U(n),$(n),ne(n),ee(n,c),te(n);break;case"safari":if(!r||!o.shimSafari)return s("Safari shim is not included in this adapter release."),p;s("adapter.js shimming safari."),p.browserShim=r,ie(n,c),oe(n),W(n),V(n),J(n),F(n),z(n),Y(n),q(n),B(n),$(n),Q(n),ee(n,c),te(n),re(n,c);break;default:s("Unsupported browser!")}}({window:"undefined"==typeof window?void 0:window});const se=Object.freeze({meta:null,signalingServerUrl:"ws://127.0.0.1:8443",reconnectionTimeout:2500,webrtcConfig:{iceServers:[{urls:["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302"]}],bundlePolicy:"max-bundle"}}),ae={idle:0,connecting:1,streaming:2,closed:3};Object.freeze(ae);const ce=ae;class de extends EventTarget{constructor(e,t){super(),this._peerId=e,this._sessionId="",this._comChannel=t,this._state=ce.idle,this._rtcPeerConnection=null}get peerId(){return this._peerId}get sessionId(){return this._sessionId}get state(){return this._state}get rtcPeerConnection(){return this._rtcPeerConnection}close(){this._state!==ce.closed&&(this._state!==ce.idle&&this._comChannel&&this._sessionId&&this._comChannel.send({type:"endSession",sessionId:this._sessionId}),this._state=ce.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._rtcPeerConnection&&(this._rtcPeerConnection.close(),this._rtcPeerConnection=null,this.dispatchEvent(new Event("rtcPeerConnectionChanged"))),this.dispatchEvent(new Event("closed")))}}const le=de,he=Object.freeze({32:"space",33:"exclam",34:"quotedbl",35:"numbersign",36:"dollar",37:"percent",38:"ampersand",39:"apostrophe",40:"parenleft",41:"parenright",42:"asterisk",43:"plus",44:"comma",45:"minus",46:"period",47:"slash",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",58:"colon",59:"semicolon",60:"less",61:"equal",62:"greater",63:"question",64:"at",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",91:"bracketleft",92:"backslash",93:"bracketright",94:"asciicircum",95:"underscore",96:"grave",97:"a",98:"b",99:"c",100:"d",101:"e",102:"f",103:"g",104:"h",105:"i",106:"j",107:"k",108:"l",109:"m",110:"n",111:"o",112:"p",113:"q",114:"r",115:"s",116:"t",117:"u",118:"v",119:"w",120:"x",121:"y",122:"z",123:"braceleft",124:"bar",125:"braceright",126:"asciitilde",160:"nobreakspace",161:"exclamdown",162:"cent",163:"sterling",164:"currency",165:"yen",166:"brokenbar",167:"section",168:"diaeresis",169:"copyright",170:"ordfeminine",171:"guillemotleft",172:"notsign",173:"hyphen",174:"registered",175:"macron",176:"degree",177:"plusminus",178:"twosuperior",179:"threesuperior",180:"acute",181:"mu",182:"paragraph",183:"periodcentered",184:"cedilla",185:"onesuperior",186:"masculine",187:"guillemotright",188:"onequarter",189:"onehalf",190:"threequarters",191:"questiondown",192:"Agrave",193:"Aacute",194:"Acircumflex",195:"Atilde",196:"Adiaeresis",197:"Aring",198:"AE",199:"Ccedilla",200:"Egrave",201:"Eacute",202:"Ecircumflex",203:"Ediaeresis",204:"Igrave",205:"Iacute",206:"Icircumflex",207:"Idiaeresis",208:"ETH",209:"Ntilde",210:"Ograve",211:"Oacute",212:"Ocircumflex",213:"Otilde",214:"Odiaeresis",215:"multiply",216:"Ooblique",217:"Ugrave",218:"Uacute",219:"Ucircumflex",220:"Udiaeresis",221:"Yacute",222:"THORN",223:"ssharp",224:"agrave",225:"aacute",226:"acircumflex",227:"atilde",228:"adiaeresis",229:"aring",230:"ae",231:"ccedilla",232:"egrave",233:"eacute",234:"ecircumflex",235:"ediaeresis",236:"igrave",237:"iacute",238:"icircumflex",239:"idiaeresis",240:"eth",241:"ntilde",242:"ograve",243:"oacute",244:"ocircumflex",245:"otilde",246:"odiaeresis",247:"division",248:"oslash",249:"ugrave",250:"uacute",251:"ucircumflex",252:"udiaeresis",253:"yacute",254:"thorn",255:"ydiaeresis",260:"Aogonek",728:"breve",321:"Lstroke",317:"Lcaron",346:"Sacute",352:"Scaron",350:"Scedilla",356:"Tcaron",377:"Zacute",381:"Zcaron",379:"Zabovedot",261:"aogonek",731:"ogonek",322:"lstroke",318:"lcaron",347:"sacute",711:"caron",353:"scaron",351:"scedilla",357:"tcaron",378:"zacute",733:"doubleacute",382:"zcaron",380:"zabovedot",340:"Racute",258:"Abreve",313:"Lacute",262:"Cacute",268:"Ccaron",280:"Eogonek",282:"Ecaron",270:"Dcaron",272:"Dstroke",323:"Nacute",327:"Ncaron",336:"Odoubleacute",344:"Rcaron",366:"Uring",368:"Udoubleacute",354:"Tcedilla",341:"racute",259:"abreve",314:"lacute",263:"cacute",269:"ccaron",281:"eogonek",283:"ecaron",271:"dcaron",273:"dstroke",324:"nacute",328:"ncaron",337:"odoubleacute",345:"rcaron",367:"uring",369:"udoubleacute",355:"tcedilla",729:"abovedot",294:"Hstroke",292:"Hcircumflex",304:"Iabovedot",286:"Gbreve",308:"Jcircumflex",295:"hstroke",293:"hcircumflex",305:"idotless",287:"gbreve",309:"jcircumflex",266:"Cabovedot",264:"Ccircumflex",288:"Gabovedot",284:"Gcircumflex",364:"Ubreve",348:"Scircumflex",267:"cabovedot",265:"ccircumflex",289:"gabovedot",285:"gcircumflex",365:"ubreve",349:"scircumflex",312:"kra",342:"Rcedilla",296:"Itilde",315:"Lcedilla",274:"Emacron",290:"Gcedilla",358:"Tslash",343:"rcedilla",297:"itilde",316:"lcedilla",275:"emacron",291:"gcedilla",359:"tslash",330:"ENG",331:"eng",256:"Amacron",302:"Iogonek",278:"Eabovedot",298:"Imacron",325:"Ncedilla",332:"Omacron",310:"Kcedilla",370:"Uogonek",360:"Utilde",362:"Umacron",257:"amacron",303:"iogonek",279:"eabovedot",299:"imacron",326:"ncedilla",333:"omacron",311:"kcedilla",371:"uogonek",361:"utilde",363:"umacron",8254:"overline",12290:"kana_fullstop",12300:"kana_openingbracket",12301:"kana_closingbracket",12289:"kana_comma",12539:"kana_conjunctive",12530:"kana_WO",12449:"kana_a",12451:"kana_i",12453:"kana_u",12455:"kana_e",12457:"kana_o",12515:"kana_ya",12517:"kana_yu",12519:"kana_yo",12483:"kana_tsu",12540:"prolongedsound",12450:"kana_A",12452:"kana_I",12454:"kana_U",12456:"kana_E",12458:"kana_O",12459:"kana_KA",12461:"kana_KI",12463:"kana_KU",12465:"kana_KE",12467:"kana_KO",12469:"kana_SA",12471:"kana_SHI",12473:"kana_SU",12475:"kana_SE",12477:"kana_SO",12479:"kana_TA",12481:"kana_CHI",12484:"kana_TSU",12486:"kana_TE",12488:"kana_TO",12490:"kana_NA",12491:"kana_NI",12492:"kana_NU",12493:"kana_NE",12494:"kana_NO",12495:"kana_HA",12498:"kana_HI",12501:"kana_FU",12504:"kana_HE",12507:"kana_HO",12510:"kana_MA",12511:"kana_MI",12512:"kana_MU",12513:"kana_ME",12514:"kana_MO",12516:"kana_YA",12518:"kana_YU",12520:"kana_YO",12521:"kana_RA",12522:"kana_RI",12523:"kana_RU",12524:"kana_RE",12525:"kana_RO",12527:"kana_WA",12531:"kana_N",12443:"voicedsound",12444:"semivoicedsound",1548:"Arabic_comma",1563:"Arabic_semicolon",1567:"Arabic_question_mark",1569:"Arabic_hamza",1570:"Arabic_maddaonalef",1571:"Arabic_hamzaonalef",1572:"Arabic_hamzaonwaw",1573:"Arabic_hamzaunderalef",1574:"Arabic_hamzaonyeh",1575:"Arabic_alef",1576:"Arabic_beh",1577:"Arabic_tehmarbuta",1578:"Arabic_teh",1579:"Arabic_theh",1580:"Arabic_jeem",1581:"Arabic_hah",1582:"Arabic_khah",1583:"Arabic_dal",1584:"Arabic_thal",1585:"Arabic_ra",1586:"Arabic_zain",1587:"Arabic_seen",1588:"Arabic_sheen",1589:"Arabic_sad",1590:"Arabic_dad",1591:"Arabic_tah",1592:"Arabic_zah",1593:"Arabic_ain",1594:"Arabic_ghain",1600:"Arabic_tatweel",1601:"Arabic_feh",1602:"Arabic_qaf",1603:"Arabic_kaf",1604:"Arabic_lam",1605:"Arabic_meem",1606:"Arabic_noon",1607:"Arabic_ha",1608:"Arabic_waw",1609:"Arabic_alefmaksura",1610:"Arabic_yeh",1611:"Arabic_fathatan",1612:"Arabic_dammatan",1613:"Arabic_kasratan",1614:"Arabic_fatha",1615:"Arabic_damma",1616:"Arabic_kasra",1617:"Arabic_shadda",1618:"Arabic_sukun",1106:"Serbian_dje",1107:"Macedonia_gje",1105:"Cyrillic_io",1108:"Ukrainian_ie",1109:"Macedonia_dse",1110:"Ukrainian_i",1111:"Ukrainian_yi",1112:"Cyrillic_je",1113:"Cyrillic_lje",1114:"Cyrillic_nje",1115:"Serbian_tshe",1116:"Macedonia_kje",1118:"Byelorussian_shortu",1119:"Cyrillic_dzhe",8470:"numerosign",1026:"Serbian_DJE",1027:"Macedonia_GJE",1025:"Cyrillic_IO",1028:"Ukrainian_IE",1029:"Macedonia_DSE",1030:"Ukrainian_I",1031:"Ukrainian_YI",1032:"Cyrillic_JE",1033:"Cyrillic_LJE",1034:"Cyrillic_NJE",1035:"Serbian_TSHE",1036:"Macedonia_KJE",1038:"Byelorussian_SHORTU",1039:"Cyrillic_DZHE",1102:"Cyrillic_yu",1072:"Cyrillic_a",1073:"Cyrillic_be",1094:"Cyrillic_tse",1076:"Cyrillic_de",1077:"Cyrillic_ie",1092:"Cyrillic_ef",1075:"Cyrillic_ghe",1093:"Cyrillic_ha",1080:"Cyrillic_i",1081:"Cyrillic_shorti",1082:"Cyrillic_ka",1083:"Cyrillic_el",1084:"Cyrillic_em",1085:"Cyrillic_en",1086:"Cyrillic_o",1087:"Cyrillic_pe",1103:"Cyrillic_ya",1088:"Cyrillic_er",1089:"Cyrillic_es",1090:"Cyrillic_te",1091:"Cyrillic_u",1078:"Cyrillic_zhe",1074:"Cyrillic_ve",1100:"Cyrillic_softsign",1099:"Cyrillic_yeru",1079:"Cyrillic_ze",1096:"Cyrillic_sha",1101:"Cyrillic_e",1097:"Cyrillic_shcha",1095:"Cyrillic_che",1098:"Cyrillic_hardsign",1070:"Cyrillic_YU",1040:"Cyrillic_A",1041:"Cyrillic_BE",1062:"Cyrillic_TSE",1044:"Cyrillic_DE",1045:"Cyrillic_IE",1060:"Cyrillic_EF",1043:"Cyrillic_GHE",1061:"Cyrillic_HA",1048:"Cyrillic_I",1049:"Cyrillic_SHORTI",1050:"Cyrillic_KA",1051:"Cyrillic_EL",1052:"Cyrillic_EM",1053:"Cyrillic_EN",1054:"Cyrillic_O",1055:"Cyrillic_PE",1071:"Cyrillic_YA",1056:"Cyrillic_ER",1057:"Cyrillic_ES",1058:"Cyrillic_TE",1059:"Cyrillic_U",1046:"Cyrillic_ZHE",1042:"Cyrillic_VE",1068:"Cyrillic_SOFTSIGN",1067:"Cyrillic_YERU",1047:"Cyrillic_ZE",1064:"Cyrillic_SHA",1069:"Cyrillic_E",1065:"Cyrillic_SHCHA",1063:"Cyrillic_CHE",1066:"Cyrillic_HARDSIGN",902:"Greek_ALPHAaccent",904:"Greek_EPSILONaccent",905:"Greek_ETAaccent",906:"Greek_IOTAaccent",938:"Greek_IOTAdiaeresis",908:"Greek_OMICRONaccent",910:"Greek_UPSILONaccent",939:"Greek_UPSILONdieresis",911:"Greek_OMEGAaccent",901:"Greek_accentdieresis",8213:"Greek_horizbar",940:"Greek_alphaaccent",941:"Greek_epsilonaccent",942:"Greek_etaaccent",943:"Greek_iotaaccent",970:"Greek_iotadieresis",912:"Greek_iotaaccentdieresis",972:"Greek_omicronaccent",973:"Greek_upsilonaccent",971:"Greek_upsilondieresis",944:"Greek_upsilonaccentdieresis",974:"Greek_omegaaccent",913:"Greek_ALPHA",914:"Greek_BETA",915:"Greek_GAMMA",916:"Greek_DELTA",917:"Greek_EPSILON",918:"Greek_ZETA",919:"Greek_ETA",920:"Greek_THETA",921:"Greek_IOTA",922:"Greek_KAPPA",923:"Greek_LAMBDA",924:"Greek_MU",925:"Greek_NU",926:"Greek_XI",927:"Greek_OMICRON",928:"Greek_PI",929:"Greek_RHO",931:"Greek_SIGMA",932:"Greek_TAU",933:"Greek_UPSILON",934:"Greek_PHI",935:"Greek_CHI",936:"Greek_PSI",937:"Greek_OMEGA",945:"Greek_alpha",946:"Greek_beta",947:"Greek_gamma",948:"Greek_delta",949:"Greek_epsilon",950:"Greek_zeta",951:"Greek_eta",952:"Greek_theta",953:"Greek_iota",954:"Greek_kappa",955:"Greek_lambda",956:"Greek_mu",957:"Greek_nu",958:"Greek_xi",959:"Greek_omicron",960:"Greek_pi",961:"Greek_rho",963:"Greek_sigma",962:"Greek_finalsmallsigma",964:"Greek_tau",965:"Greek_upsilon",966:"Greek_phi",967:"Greek_chi",968:"Greek_psi",969:"Greek_omega",9143:"leftradical",8992:"topintegral",8993:"botintegral",9121:"topleftsqbracket",9123:"botleftsqbracket",9124:"toprightsqbracket",9126:"botrightsqbracket",9115:"topleftparens",9117:"botleftparens",9118:"toprightparens",9120:"botrightparens",9128:"leftmiddlecurlybrace",9132:"rightmiddlecurlybrace",8804:"lessthanequal",8800:"notequal",8805:"greaterthanequal",8747:"integral",8756:"therefore",8733:"variation",8734:"infinity",8711:"nabla",8764:"approximate",8771:"similarequal",8660:"ifonlyif",8658:"implies",8801:"identical",8730:"radical",8834:"includedin",8835:"includes",8745:"intersection",8746:"union",8743:"logicaland",8744:"logicalor",8706:"partialderivative",402:"function",8592:"leftarrow",8593:"uparrow",8594:"rightarrow",8595:"downarrow",9670:"soliddiamond",9618:"checkerboard",9225:"ht",9228:"ff",9229:"cr",9226:"lf",9252:"nl",9227:"vt",9496:"lowrightcorner",9488:"uprightcorner",9484:"upleftcorner",9492:"lowleftcorner",9532:"crossinglines",9146:"horizlinescan1",9147:"horizlinescan3",9472:"horizlinescan5",9148:"horizlinescan7",9149:"horizlinescan9",9500:"leftt",9508:"rightt",9524:"bott",9516:"topt",9474:"vertbar",8195:"emspace",8194:"enspace",8196:"em3space",8197:"em4space",8199:"digitspace",8200:"punctspace",8201:"thinspace",8202:"hairspace",8212:"emdash",8211:"endash",9251:"signifblank",8230:"ellipsis",8229:"doubbaselinedot",8531:"onethird",8532:"twothirds",8533:"onefifth",8534:"twofifths",8535:"threefifths",8536:"fourfifths",8537:"onesixth",8538:"fivesixths",8453:"careof",8210:"figdash",10216:"leftanglebracket",10217:"rightanglebracket",8539:"oneeighth",8540:"threeeighths",8541:"fiveeighths",8542:"seveneighths",8482:"trademark",9747:"signaturemark",9665:"leftopentriangle",9655:"rightopentriangle",9647:"emopenrectangle",8216:"leftsinglequotemark",8217:"rightsinglequotemark",8220:"leftdoublequotemark",8221:"rightdoublequotemark",8478:"prescription",8242:"minutes",8243:"seconds",10013:"latincross",9644:"filledrectbullet",9664:"filledlefttribullet",9654:"filledrighttribullet",9679:"emfilledcircle",9646:"emfilledrect",9702:"enopencircbullet",9643:"enopensquarebullet",9645:"openrectbullet",9651:"opentribulletup",9661:"opentribulletdown",9734:"openstar",8226:"enfilledcircbullet",9642:"enfilledsqbullet",9650:"filledtribulletup",9660:"filledtribulletdown",9756:"leftpointer",9758:"rightpointer",9827:"club",9830:"diamond",9829:"heart",10016:"maltesecross",8224:"dagger",8225:"doubledagger",10003:"checkmark",10007:"ballotcross",9839:"musicalsharp",9837:"musicalflat",9794:"malesymbol",9792:"femalesymbol",9742:"telephone",8981:"telephonerecorder",8471:"phonographcopyright",8248:"caret",8218:"singlelowquotemark",8222:"doublelowquotemark",8869:"downtack",8970:"downstile",8728:"jot",9109:"quad",8868:"uptack",9675:"circle",8968:"upstile",8866:"lefttack",8867:"righttack",8215:"hebrew_doublelowline",1488:"hebrew_aleph",1489:"hebrew_beth",1490:"hebrew_gimmel",1491:"hebrew_daleth",1492:"hebrew_he",1493:"hebrew_waw",1494:"hebrew_zayin",1495:"hebrew_het",1496:"hebrew_teth",1497:"hebrew_yod",1498:"hebrew_finalkaph",1499:"hebrew_kaph",1500:"hebrew_lamed",1501:"hebrew_finalmem",1502:"hebrew_mem",1503:"hebrew_finalnun",1504:"hebrew_nun",1505:"hebrew_samekh",1506:"hebrew_ayin",1507:"hebrew_finalpe",1508:"hebrew_pe",1509:"hebrew_finalzadi",1510:"hebrew_zadi",1511:"hebrew_qoph",1512:"hebrew_resh",1513:"hebrew_shin",1514:"hebrew_taw",3585:"Thai_kokai",3586:"Thai_khokhai",3587:"Thai_khokhuat",3588:"Thai_khokhwai",3589:"Thai_khokhon",3590:"Thai_khorakhang",3591:"Thai_ngongu",3592:"Thai_chochan",3593:"Thai_choching",3594:"Thai_chochang",3595:"Thai_soso",3596:"Thai_chochoe",3597:"Thai_yoying",3598:"Thai_dochada",3599:"Thai_topatak",3600:"Thai_thothan",3601:"Thai_thonangmontho",3602:"Thai_thophuthao",3603:"Thai_nonen",3604:"Thai_dodek",3605:"Thai_totao",3606:"Thai_thothung",3607:"Thai_thothahan",3608:"Thai_thothong",3609:"Thai_nonu",3610:"Thai_bobaimai",3611:"Thai_popla",3612:"Thai_phophung",3613:"Thai_fofa",3614:"Thai_phophan",3615:"Thai_fofan",3616:"Thai_phosamphao",3617:"Thai_moma",3618:"Thai_yoyak",3619:"Thai_rorua",3620:"Thai_ru",3621:"Thai_loling",3622:"Thai_lu",3623:"Thai_wowaen",3624:"Thai_sosala",3625:"Thai_sorusi",3626:"Thai_sosua",3627:"Thai_hohip",3628:"Thai_lochula",3629:"Thai_oang",3630:"Thai_honokhuk",3631:"Thai_paiyannoi",3632:"Thai_saraa",3633:"Thai_maihanakat",3634:"Thai_saraaa",3635:"Thai_saraam",3636:"Thai_sarai",3637:"Thai_saraii",3638:"Thai_saraue",3639:"Thai_sarauee",3640:"Thai_sarau",3641:"Thai_sarauu",3642:"Thai_phinthu",3647:"Thai_baht",3648:"Thai_sarae",3649:"Thai_saraae",3650:"Thai_sarao",3651:"Thai_saraaimaimuan",3652:"Thai_saraaimaimalai",3653:"Thai_lakkhangyao",3654:"Thai_maiyamok",3655:"Thai_maitaikhu",3656:"Thai_maiek",3657:"Thai_maitho",3658:"Thai_maitri",3659:"Thai_maichattawa",3660:"Thai_thanthakhat",3661:"Thai_nikhahit",3664:"Thai_leksun",3665:"Thai_leknung",3666:"Thai_leksong",3667:"Thai_leksam",3668:"Thai_leksi",3669:"Thai_lekha",3670:"Thai_lekhok",3671:"Thai_lekchet",3672:"Thai_lekpaet",3673:"Thai_lekkao",12593:"Hangul_Kiyeog",12594:"Hangul_SsangKiyeog",12595:"Hangul_KiyeogSios",12596:"Hangul_Nieun",12597:"Hangul_NieunJieuj",12598:"Hangul_NieunHieuh",12599:"Hangul_Dikeud",12600:"Hangul_SsangDikeud",12601:"Hangul_Rieul",12602:"Hangul_RieulKiyeog",12603:"Hangul_RieulMieum",12604:"Hangul_RieulPieub",12605:"Hangul_RieulSios",12606:"Hangul_RieulTieut",12607:"Hangul_RieulPhieuf",12608:"Hangul_RieulHieuh",12609:"Hangul_Mieum",12610:"Hangul_Pieub",12611:"Hangul_SsangPieub",12612:"Hangul_PieubSios",12613:"Hangul_Sios",12614:"Hangul_SsangSios",12615:"Hangul_Ieung",12616:"Hangul_Jieuj",12617:"Hangul_SsangJieuj",12618:"Hangul_Cieuc",12619:"Hangul_Khieuq",12620:"Hangul_Tieut",12621:"Hangul_Phieuf",12622:"Hangul_Hieuh",12623:"Hangul_A",12624:"Hangul_AE",12625:"Hangul_YA",12626:"Hangul_YAE",12627:"Hangul_EO",12628:"Hangul_E",12629:"Hangul_YEO",12630:"Hangul_YE",12631:"Hangul_O",12632:"Hangul_WA",12633:"Hangul_WAE",12634:"Hangul_OE",12635:"Hangul_YO",12636:"Hangul_U",12637:"Hangul_WEO",12638:"Hangul_WE",12639:"Hangul_WI",12640:"Hangul_YU",12641:"Hangul_EU",12642:"Hangul_YI",12643:"Hangul_I",4520:"Hangul_J_Kiyeog",4521:"Hangul_J_SsangKiyeog",4522:"Hangul_J_KiyeogSios",4523:"Hangul_J_Nieun",4524:"Hangul_J_NieunJieuj",4525:"Hangul_J_NieunHieuh",4526:"Hangul_J_Dikeud",4527:"Hangul_J_Rieul",4528:"Hangul_J_RieulKiyeog",4529:"Hangul_J_RieulMieum",4530:"Hangul_J_RieulPieub",4531:"Hangul_J_RieulSios",4532:"Hangul_J_RieulTieut",4533:"Hangul_J_RieulPhieuf",4534:"Hangul_J_RieulHieuh",4535:"Hangul_J_Mieum",4536:"Hangul_J_Pieub",4537:"Hangul_J_PieubSios",4538:"Hangul_J_Sios",4539:"Hangul_J_SsangSios",4540:"Hangul_J_Ieung",4541:"Hangul_J_Jieuj",4542:"Hangul_J_Cieuc",4543:"Hangul_J_Khieuq",4544:"Hangul_J_Tieut",4545:"Hangul_J_Phieuf",4546:"Hangul_J_Hieuh",12653:"Hangul_RieulYeorinHieuh",12657:"Hangul_SunkyeongeumMieum",12664:"Hangul_SunkyeongeumPieub",12671:"Hangul_PanSios",12673:"Hangul_KkogjiDalrinIeung",12676:"Hangul_SunkyeongeumPhieuf",12678:"Hangul_YeorinHieuh",12685:"Hangul_AraeA",12686:"Hangul_AraeAE",4587:"Hangul_J_PanSios",4592:"Hangul_J_KkogjiDalrinIeung",4601:"Hangul_J_YeorinHieuh",338:"OE",339:"oe",376:"Ydiaeresis",8352:"EcuSign",8353:"ColonSign",8354:"CruzeiroSign",8355:"FFrancSign",8356:"LiraSign",8357:"MillSign",8358:"NairaSign",8359:"PesetaSign",8360:"RupeeSign",8361:"WonSign",8362:"NewSheqelSign",8363:"DongSign",8364:"EuroSign",768:"dead_grave",769:"dead_acute",770:"dead_circumflex",771:"dead_tilde",772:"dead_macron",774:"dead_breve",775:"dead_abovedot",776:"dead_diaeresis",778:"dead_abovering",779:"dead_doubleacute",780:"dead_caron",807:"dead_cedilla",808:"dead_ogonek",837:"dead_iota",12441:"dead_voiced_sound",12442:"dead_semivoiced_sound",8:"BackSpace",9:"Tab",10:"Linefeed",11:"Clear",13:"Return",19:"Pause",20:"Scroll_Lock",21:"Sys_Req",27:"Escape",1169:"Ukrainian_ghe_with_upturn",1168:"Ukrainian_GHE_WITH_UPTURN",1415:"Armenian_ligature_ew",1417:"Armenian_verjaket",1373:"Armenian_but",1418:"Armenian_yentamna",1372:"Armenian_amanak",1371:"Armenian_shesht",1374:"Armenian_paruyk",1329:"Armenian_AYB",1377:"Armenian_ayb",1330:"Armenian_BEN",1378:"Armenian_ben",1331:"Armenian_GIM",1379:"Armenian_gim",1332:"Armenian_DA",1380:"Armenian_da",1333:"Armenian_YECH",1381:"Armenian_yech",1334:"Armenian_ZA",1382:"Armenian_za",1335:"Armenian_E",1383:"Armenian_e",1336:"Armenian_AT",1384:"Armenian_at",1337:"Armenian_TO",1385:"Armenian_to",1338:"Armenian_ZHE",1386:"Armenian_zhe",1339:"Armenian_INI",1387:"Armenian_ini",1340:"Armenian_LYUN",1388:"Armenian_lyun",1341:"Armenian_KHE",1389:"Armenian_khe",1342:"Armenian_TSA",1390:"Armenian_tsa",1343:"Armenian_KEN",1391:"Armenian_ken",1344:"Armenian_HO",1392:"Armenian_ho",1345:"Armenian_DZA",1393:"Armenian_dza",1346:"Armenian_GHAT",1394:"Armenian_ghat",1347:"Armenian_TCHE",1395:"Armenian_tche",1348:"Armenian_MEN",1396:"Armenian_men",1349:"Armenian_HI",1397:"Armenian_hi",1350:"Armenian_NU",1398:"Armenian_nu",1351:"Armenian_SHA",1399:"Armenian_sha",1352:"Armenian_VO",1400:"Armenian_vo",1353:"Armenian_CHA",1401:"Armenian_cha",1354:"Armenian_PE",1402:"Armenian_pe",1355:"Armenian_JE",1403:"Armenian_je",1356:"Armenian_RA",1404:"Armenian_ra",1357:"Armenian_SE",1405:"Armenian_se",1358:"Armenian_VEV",1406:"Armenian_vev",1359:"Armenian_TYUN",1407:"Armenian_tyun",1360:"Armenian_RE",1408:"Armenian_re",1361:"Armenian_TSO",1409:"Armenian_tso",1362:"Armenian_VYUN",1410:"Armenian_vyun",1363:"Armenian_PYUR",1411:"Armenian_pyur",1364:"Armenian_KE",1412:"Armenian_ke",1365:"Armenian_O",1413:"Armenian_o",1366:"Armenian_FE",1414:"Armenian_fe",1370:"Armenian_apostrophe",4304:"Georgian_an",4305:"Georgian_ban",4306:"Georgian_gan",4307:"Georgian_don",4308:"Georgian_en",4309:"Georgian_vin",4310:"Georgian_zen",4311:"Georgian_tan",4312:"Georgian_in",4313:"Georgian_kan",4314:"Georgian_las",4315:"Georgian_man",4316:"Georgian_nar",4317:"Georgian_on",4318:"Georgian_par",4319:"Georgian_zhar",4320:"Georgian_rae",4321:"Georgian_san",4322:"Georgian_tar",4323:"Georgian_un",4324:"Georgian_phar",4325:"Georgian_khar",4326:"Georgian_ghan",4327:"Georgian_qar",4328:"Georgian_shin",4329:"Georgian_chin",4330:"Georgian_can",4331:"Georgian_jil",4332:"Georgian_cil",4333:"Georgian_char",4334:"Georgian_xan",4335:"Georgian_jhan",4336:"Georgian_hae",4337:"Georgian_he",4338:"Georgian_hie",4339:"Georgian_we",4340:"Georgian_har",4341:"Georgian_hoe",4342:"Georgian_fi",7682:"Babovedot",7683:"babovedot",7690:"Dabovedot",7808:"Wgrave",7810:"Wacute",7691:"dabovedot",7922:"Ygrave",7710:"Fabovedot",7711:"fabovedot",7744:"Mabovedot",7745:"mabovedot",7766:"Pabovedot",7809:"wgrave",7767:"pabovedot",7811:"wacute",7776:"Sabovedot",7923:"ygrave",7812:"Wdiaeresis",7813:"wdiaeresis",7777:"sabovedot",372:"Wcircumflex",7786:"Tabovedot",374:"Ycircumflex",373:"wcircumflex",7787:"tabovedot",375:"ycircumflex",1776:"Farsi_0",1777:"Farsi_1",1778:"Farsi_2",1779:"Farsi_3",1780:"Farsi_4",1781:"Farsi_5",1782:"Farsi_6",1783:"Farsi_7",1784:"Farsi_8",1785:"Farsi_9",1642:"Arabic_percent",1648:"Arabic_superscript_alef",1657:"Arabic_tteh",1662:"Arabic_peh",1670:"Arabic_tcheh",1672:"Arabic_ddal",1681:"Arabic_rreh",1748:"Arabic_fullstop",1632:"Arabic_0",1633:"Arabic_1",1634:"Arabic_2",1635:"Arabic_3",1636:"Arabic_4",1637:"Arabic_5",1638:"Arabic_6",1639:"Arabic_7",1640:"Arabic_8",1641:"Arabic_9",1619:"Arabic_madda_above",1620:"Arabic_hamza_above",1621:"Arabic_hamza_below",1688:"Arabic_jeh",1700:"Arabic_veh",1705:"Arabic_keheh",1711:"Arabic_gaf",1722:"Arabic_noon_ghunna",1726:"Arabic_heh_doachashmee",1740:"Farsi_yeh",1746:"Arabic_yeh_baree",1729:"Arabic_heh_goal",1170:"Cyrillic_GHE_bar",1174:"Cyrillic_ZHE_descender",1178:"Cyrillic_KA_descender",1180:"Cyrillic_KA_vertstroke",1186:"Cyrillic_EN_descender",1198:"Cyrillic_U_straight",1200:"Cyrillic_U_straight_bar",1202:"Cyrillic_HA_descender",1206:"Cyrillic_CHE_descender",1208:"Cyrillic_CHE_vertstroke",1210:"Cyrillic_SHHA",1240:"Cyrillic_SCHWA",1250:"Cyrillic_I_macron",1256:"Cyrillic_O_bar",1262:"Cyrillic_U_macron",1171:"Cyrillic_ghe_bar",1175:"Cyrillic_zhe_descender",1179:"Cyrillic_ka_descender",1181:"Cyrillic_ka_vertstroke",1187:"Cyrillic_en_descender",1199:"Cyrillic_u_straight",1201:"Cyrillic_u_straight_bar",1203:"Cyrillic_ha_descender",1207:"Cyrillic_che_descender",1209:"Cyrillic_che_vertstroke",1211:"Cyrillic_shha",1241:"Cyrillic_schwa",1251:"Cyrillic_i_macron",1257:"Cyrillic_o_bar",1263:"Cyrillic_u_macron",7818:"Xabovedot",300:"Ibreve",437:"Zstroke",486:"Gcaron",415:"Obarred",7819:"xabovedot",301:"ibreve",438:"zstroke",487:"gcaron",466:"ocaron",629:"obarred",399:"SCHWA",601:"schwa",7734:"Lbelowdot",7735:"lbelowdot",7840:"Abelowdot",7841:"abelowdot",7842:"Ahook",7843:"ahook",7844:"Acircumflexacute",7845:"acircumflexacute",7846:"Acircumflexgrave",7847:"acircumflexgrave",7848:"Acircumflexhook",7849:"acircumflexhook",7850:"Acircumflextilde",7851:"acircumflextilde",7852:"Acircumflexbelowdot",7853:"acircumflexbelowdot",7854:"Abreveacute",7855:"abreveacute",7856:"Abrevegrave",7857:"abrevegrave",7858:"Abrevehook",7859:"abrevehook",7860:"Abrevetilde",7861:"abrevetilde",7862:"Abrevebelowdot",7863:"abrevebelowdot",7864:"Ebelowdot",7865:"ebelowdot",7866:"Ehook",7867:"ehook",7868:"Etilde",7869:"etilde",7870:"Ecircumflexacute",7871:"ecircumflexacute",7872:"Ecircumflexgrave",7873:"ecircumflexgrave",7874:"Ecircumflexhook",7875:"ecircumflexhook",7876:"Ecircumflextilde",7877:"ecircumflextilde",7878:"Ecircumflexbelowdot",7879:"ecircumflexbelowdot",7880:"Ihook",7881:"ihook",7882:"Ibelowdot",7883:"ibelowdot",7884:"Obelowdot",7885:"obelowdot",7886:"Ohook",7887:"ohook",7888:"Ocircumflexacute",7889:"ocircumflexacute",7890:"Ocircumflexgrave",7891:"ocircumflexgrave",7892:"Ocircumflexhook",7893:"ocircumflexhook",7894:"Ocircumflextilde",7895:"ocircumflextilde",7896:"Ocircumflexbelowdot",7897:"ocircumflexbelowdot",7898:"Ohornacute",7899:"ohornacute",7900:"Ohorngrave",7901:"ohorngrave",7902:"Ohornhook",7903:"ohornhook",7904:"Ohorntilde",7905:"ohorntilde",7906:"Ohornbelowdot",7907:"ohornbelowdot",7908:"Ubelowdot",7909:"ubelowdot",7910:"Uhook",7911:"uhook",7912:"Uhornacute",7913:"uhornacute",7914:"Uhorngrave",7915:"uhorngrave",7916:"Uhornhook",7917:"uhornhook",7918:"Uhorntilde",7919:"uhorntilde",7920:"Uhornbelowdot",7921:"uhornbelowdot",7924:"Ybelowdot",7925:"ybelowdot",7926:"Yhook",7927:"yhook",7928:"Ytilde",7929:"ytilde",416:"Ohorn",417:"ohorn",431:"Uhorn",432:"uhorn",803:"dead_belowdot",777:"dead_hook",795:"dead_horn"}),pe=Object.freeze({AltLeft:"Alt_L",AltRight:"Alt_R",ArrowDown:"Down",ArrowLeft:"Left",ArrowRight:"Right",ArrowUp:"Up",Backspace:"BackSpace",CapsLock:"Caps_Lock",ControlLeft:"Control_L",ControlRight:"Control_R",Enter:"Return",HyperLeft:"Hyper_L",HyperRight:"Hyper_R",NumLock:"Num_Lock",NumpadEnter:"Return",MetaLeft:"Meta_L",MetaRight:"Meta_R",PageDown:"Page_Down",PageUp:"Page_Up",ScrollLock:"Scroll_Lock",ShiftLeft:"Shift_L",ShiftRight:"Shift_R",SuperLeft:"Super_L",SuperRight:"Super_R"}),ue=new Set(["Clear","Copy","Cut","Delete","End","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","Home","Insert","Paste","Redo","Tab","Undo"]);function me(e,t){var n="Unidentified";if(1===e.length){const t=e.charCodeAt(0);t in he&&(n=he[t])}else t in pe?n=pe[t]:ue.has(t)&&(n=t);return n}const _e=Object.freeze(["wheel","contextmenu","mousemove","mousedown","mouseup","touchstart","touchend","touchmove","touchcancel","keyup","keydown"]),fe=Object.freeze({mousemove:"MouseMove",mousedown:"MouseButtonPress",mouseup:"MouseButtonRelease"}),ge=Object.freeze({touchstart:"TouchDown",touchend:"TouchUp",touchmove:"TouchMotion",touchcancel:"TouchUp"}),Ce=Object.freeze({keydown:"KeyPress",keyup:"KeyRelease"});function ye(e){const t=[];return e.altKey&&t.push("mod1-mask"),e.ctrlKey&&t.push("control-mask"),e.metaKey&&t.push("meta-mask"),e.shiftKey&&t.push("shift-mask"),t.join("+")}class ve extends EventTarget{constructor(e,t){super(),this._rtcDataChannel=e,this._consumerSession=t,this._videoElement=null,this._videoElementComputedStyle=null,this._videoElementKeyboard=null,this._lastTouchEventTimestamp=0,this._requestCounter=0,e.addEventListener("close",()=>{this._rtcDataChannel===e&&this.close()}),e.addEventListener("error",t=>{if(this._rtcDataChannel===e){const e=t.error;this.dispatchEvent(new ErrorEvent("error",{message:e&&e.message||"Remote controller error",error:e||new Error("unknown error on the remote controller data channel")}))}}),e.addEventListener("message",e=>{try{const t=JSON.parse(e.data);"ControlResponseMessage"===t.type?this.dispatchEvent(new CustomEvent("controlResponse",{detail:t})):"InfoMessage"===t.type&&this.dispatchEvent(new CustomEvent("info",{detail:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse control message from signaling server",error:e}))}})}get rtcDataChannel(){return this._rtcDataChannel}get consumerSession(){return this._consumerSession}get videoElement(){return this._videoElement}attachVideoElement(e){if(e instanceof HTMLVideoElement&&e!==this._videoElement){this._videoElement&&this.attachVideoElement(null),this._videoElement=e,this._videoElementComputedStyle=window.getComputedStyle(e);for(const t of _e)e.addEventListener(t,this);e.setAttribute("tabindex","0")}else if(null===e&&this._videoElement){const e=this._videoElement;e.removeAttribute("tabindex"),this._videoElement=null,this._videoElementComputedStyle=null,this._lastTouchEventTimestamp=0;for(const t of _e)e.removeEventListener(t,this)}}sendControlRequest(e){try{if(!e||"object"!=typeof e&&"string"!=typeof e)throw new Error("invalid request");if(!this._rtcDataChannel)throw new Error("remote controller data channel is closed");let t={id:this._requestCounter++,request:e};return this._rtcDataChannel.send(JSON.stringify(t)),t.id}catch(e){return this.dispatchEvent(new ErrorEvent("error",{message:`cannot send control message over session ${this._consumerSession.sessionId} remote controller`,error:e})),-1}}close(){this.attachVideoElement(null);const e=this._rtcDataChannel;this._rtcDataChannel=null,e&&(e.close(),this.dispatchEvent(new Event("closed")))}_sendGstNavigationEvent(e){let t={type:"navigationEvent",event:e};this.sendControlRequest(t)}_computeVideoMousePosition(e){const t={x:0,y:0};if(!this._videoElement||this._videoElement.videoWidth<=0||this._videoElement.videoHeight<=0)return t;const n=parseFloat(this._videoElementComputedStyle.paddingLeft),r=parseFloat(this._videoElementComputedStyle.paddingRight),i=parseFloat(this._videoElementComputedStyle.paddingTop),o=parseFloat(this._videoElementComputedStyle.paddingBottom);if("offsetX"in e&&"offsetY"in e)t.x=e.offsetX-n,t.y=e.offsetY-i;else{const r=this._videoElement.getBoundingClientRect(),o={left:parseFloat(this._videoElementComputedStyle.borderLeftWidth),top:parseFloat(this._videoElementComputedStyle.borderTopWidth)};t.x=e.clientX-r.left-o.left-n,t.y=e.clientY-r.top-o.top-i}const s={x:this._videoElement.clientWidth-(n+r),y:this._videoElement.clientHeight-(i+o)},a=Math.min(s.x/this._videoElement.videoWidth,s.y/this._videoElement.videoHeight);s.x=Math.max(.5*(s.x-this._videoElement.videoWidth*a),0),s.y=Math.max(.5*(s.y-this._videoElement.videoHeight*a),0);const c=0!==a?1/a:0;return t.x=(t.x-s.x)*c,t.y=(t.y-s.y)*c,t.x=Math.min(Math.max(t.x,0),this._videoElement.videoWidth),t.y=Math.min(Math.max(t.y,0),this._videoElement.videoHeight),t}handleEvent(e){if(this._videoElement)switch(e.type){case"wheel":e.preventDefault();{const t=this._computeVideoMousePosition(e);this._sendGstNavigationEvent({event:"MouseScroll",x:t.x,y:t.y,delta_x:-e.deltaX,delta_y:-e.deltaY,modifier_state:ye(e)})}break;case"contextmenu":e.preventDefault();break;case"mousemove":case"mousedown":case"mouseup":e.preventDefault();{const t=this._computeVideoMousePosition(e),n={event:fe[e.type],x:t.x,y:t.y,modifier_state:ye(e)};"mousemove"!==e.type&&(n.button=e.button+1,"mousedown"===e.type&&0===e.button&&this._videoElement.focus()),this._sendGstNavigationEvent(n)}break;case"touchstart":case"touchend":case"touchmove":case"touchcancel":for(const t of e.changedTouches){const n=this._computeVideoMousePosition(t),r={event:ge[e.type],identifier:t.identifier,x:n.x,y:n.y,modifier_state:ye(e)};!("force"in t)||"touchstart"!==e.type&&"touchmove"!==e.type||(r.pressure=t.force),this._sendGstNavigationEvent(r)}e.timeStamp>this._lastTouchEventTimestamp&&(this._lastTouchEventTimestamp=e.timeStamp,this._sendGstNavigationEvent({event:"TouchFrame",modifier_state:ye(e)}));break;case"keyup":case"keydown":e.preventDefault();{const t={event:Ce[e.type],key:me(e.key,e.code),modifier_state:ye(e)};this._sendGstNavigationEvent(t)}}}}const be=ve;const Se=class extends le{constructor(e,t,n){super(e,t),this._streams=[],this._remoteController=null,this._pendingCandidates=[],this._mungeStereoHack=!1,this._offerOptions=n,this.addEventListener("closed",()=>{this._streams=[],this._pendingCandidates=[],this._remoteController&&this._remoteController.close()})}set mungeStereoHack(e){"boolean"==typeof e&&(this._mungeStereoHack=e)}get streams(){return this._streams}get remoteController(){return this._remoteController}connect(){if(!this._comChannel||this._state===ce.closed)return!1;if(this._state!==ce.idle)return!0;if(this._offerOptions)this.ensurePeerConnection(),this._rtcPeerConnection.createDataChannel("control"),this._rtcPeerConnection.createOffer(this._offerOptions).then(e=>{if(this._rtcPeerConnection&&e)return this._rtcPeerConnection.setLocalDescription(e);throw new Error("cannot send local offer to WebRTC peer")}).then(()=>{if(this._rtcPeerConnection&&this._comChannel){const e={type:"startSession",peerId:this._peerId,offer:this._rtcPeerConnection.localDescription.toJSON().sdp};if(!this._comChannel.send(e))throw new Error("cannot send startSession message to signaling server");this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged"))}}).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{const e={type:"startSession",peerId:this._peerId};if(!this._comChannel.send(e))return this.dispatchEvent(new ErrorEvent("error",{message:"cannot connect consumer session",error:new Error("cannot send startSession message to signaling server")})),this.close(),!1;this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged"))}return!0}onSessionStarted(e,t){if(this._peerId===e&&this._state===ce.connecting&&!this._sessionId){console.log("Session started",this._sessionId),this._sessionId=t;for(const e of this._pendingCandidates)console.log("Sending delayed ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.toJSON()});this._pendingCandidates=[]}}ensurePeerConnection(){if(!this._rtcPeerConnection){const e=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=e,e.ontrack=t=>{if(this._rtcPeerConnection===e&&t.streams&&t.streams.length>0){this._state===ce.connecting&&(this._state=ce.streaming,this.dispatchEvent(new Event("stateChanged")));let e=!1;for(const n of t.streams)this._streams.includes(n)||(this._streams.push(n),e=!0);e&&this.dispatchEvent(new Event("streamsChanged"))}},e.ondatachannel=e=>{const t=e.channel;if(t&&"control"===t.label){if(this._remoteController){const e=this._remoteController;this._remoteController=null,e.close()}const e=new be(t,this);this._remoteController=e,this.dispatchEvent(new Event("remoteControllerChanged")),e.addEventListener("closed",()=>{this._remoteController===e&&(this._remoteController=null,this.dispatchEvent(new Event("remoteControllerChanged")))})}},e.onicecandidate=t=>{this._rtcPeerConnection===e&&t.candidate&&this._comChannel&&(this._sessionId?(console.log("Sending ICE with session id",this._sessionId),this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:t.candidate.toJSON()})):this._pendingCandidates.push(t.candidate))},this.dispatchEvent(new Event("rtcPeerConnectionChanged"))}}mungeStereo(e,t){const n=/a=fmtp:.* sprop-stereo/g;let r=new Set;for(const t of e.matchAll(n)){const e=t[0].match(/a=fmtp:(\d+) .*/);e&&r.add(e[1])}for(const e of r){const n=new RegExp("a=fmtp:"+e+".*stereo");t.match(n)||(t=t.replaceAll("a=fmtp:"+e,"a=fmtp:"+e+" stereo=1;"))}return t}onSessionPeerMessage(e){if(this._state!==ce.closed&&this._comChannel&&this._sessionId)if(this.ensurePeerConnection(),e.sdp)this._offerOptions?this._rtcPeerConnection.setRemoteDescription(e.sdp).then(()=>{console.log("done")}).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())}):this._rtcPeerConnection.setRemoteDescription(e.sdp).then(()=>this._rtcPeerConnection?this._rtcPeerConnection.createAnswer():null).then(t=>this._rtcPeerConnection&&t?(this._mungeStereoHack&&(t.sdp=this.mungeStereo(e.sdp.sdp,t.sdp)),this._rtcPeerConnection.setLocalDescription(t)):null).then(()=>{if(this._rtcPeerConnection&&this._comChannel){console.log("Sending SDP with session id",this._sessionId);const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}}).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{if(!e.ice)throw new Error(`invalid empty peer message received from consumer session ${this._sessionId}`);{const t=e.ice.candidate?new RTCIceCandidate(e.ice):null;this._rtcPeerConnection.addIceCandidate(t).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())})}}}};class ke extends le{constructor(e,t,n,r){super(e,n),this._sessionId=t,this._state=ce.streaming;const i=new RTCPeerConnection(this._comChannel.webrtcConfig);this._rtcPeerConnection=i;for(const e of r.getTracks())i.addTrack(e,r);i.onicecandidate=e=>{this._rtcPeerConnection===i&&e.candidate&&this._comChannel&&this._comChannel.send({type:"peer",sessionId:this._sessionId,ice:e.candidate.toJSON()})},this.dispatchEvent(new Event("rtcPeerConnectionChanged")),i.setLocalDescription().then(()=>{if(this._rtcPeerConnection===i&&this._comChannel){const e={type:"peer",sessionId:this._sessionId,sdp:this._rtcPeerConnection.localDescription.toJSON()};if(!this._comChannel.send(e))throw new Error("cannot send local SDP configuration to WebRTC peer")}}).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())})}onSessionPeerMessage(e){if(this._state!==ce.closed&&this._rtcPeerConnection)if(e.sdp)this._rtcPeerConnection.setRemoteDescription(e.sdp).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during SDP handshake",error:e})),this.close())});else{if(!e.ice)throw new Error(`invalid empty peer message received from producer's client session ${this._peerId}`);{const t=new RTCIceCandidate(e.ice);this._rtcPeerConnection.addIceCandidate(t).catch(e=>{this._state!==ce.closed&&(this.dispatchEvent(new ErrorEvent("error",{message:"an unrecoverable error occurred during ICE handshake",error:e})),this.close())})}}}}class Te extends EventTarget{constructor(e,t,n){super(),this._comChannel=e,this._stream=t,this._state=ce.idle,this._clientSessions={},this._consumerId=n}get stream(){return this._stream}get state(){return this._state}start(){if(!this._comChannel||this._state===ce.closed)return!1;if(this._state!==ce.idle)return!0;const e={type:"setPeerStatus",roles:["listener","producer"],meta:this._comChannel.meta};return this._comChannel.send(e)?(this._state=ce.connecting,this.dispatchEvent(new Event("stateChanged")),!0):(this.dispatchEvent(new ErrorEvent("error",{message:"cannot start producer session",error:new Error("cannot register producer to signaling server")})),this.close(),!1)}close(){if(this._state!==ce.closed){for(const e of this._stream.getTracks())e.stop();this._state!==ce.idle&&this._comChannel&&this._comChannel.send({type:"setPeerStatus",roles:["listener"],meta:this._comChannel.meta}),this._state=ce.closed,this.dispatchEvent(new Event("stateChanged")),this._comChannel=null,this._stream=null;for(const e of Object.values(this._clientSessions))e.close();this._clientSessions={},this.dispatchEvent(new Event("closed"))}}onProducerRegistered(){if(this._state===ce.connecting&&(this._state=ce.streaming,this.dispatchEvent(new Event("stateChanged"))),this._consumerId){const e={type:"startSession",peerId:this._consumerId};this._comChannel.send(e)||(this.dispatchEvent(new ErrorEvent("error",{message:"cannot send session request to specified consumer",error:new Error("cannot send startSession message to signaling server")})),this.close())}}onStartSessionMessage(e){if(this._comChannel&&this._stream&&!(e.sessionId in this._clientSessions)){const t=new ke(e.peerId,e.sessionId,this._comChannel,this._stream);this._clientSessions[e.sessionId]=t,t.addEventListener("closed",e=>{const n=e.target.sessionId;n in this._clientSessions&&this._clientSessions[n]===t&&(delete this._clientSessions[n],this.dispatchEvent(new CustomEvent("clientConsumerRemoved",{detail:t})))}),t.addEventListener("error",e=>{this.dispatchEvent(new ErrorEvent("error",{message:`error from client consumer ${e.target.peerId}: ${e.message}`,error:e.error}))}),this.dispatchEvent(new CustomEvent("clientConsumerAdded",{detail:t}))}}onEndSessionMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].close()}onSessionPeerMessage(e){e.sessionId in this._clientSessions&&this._clientSessions[e.sessionId].onSessionPeerMessage(e)}}const Ee=Te,Pe=Object.freeze({welcome:"welcome",peerStatusChanged:"peerStatusChanged",list:"list",listConsumers:"listConsumers",sessionStarted:"sessionStarted",peer:"peer",startSession:"startSession",endSession:"endSession",error:"error"});function Re(e,t){if(!e||"object"!=typeof e)return null;const n={id:"",meta:{}};if(e.id&&"string"==typeof e.id)n.id=e.id;else{if(!e.peerId||"string"!=typeof e.peerId)return null;n.id=e.peerId}return n.id===t?null:(e.meta&&"object"==typeof e.meta&&(n.meta=e.meta),Object.freeze(n.meta),Object.freeze(n))}class we extends EventTarget{constructor(e,t,n){super(),this._meta=t,this._webrtcConfig=n,this._ws=new WebSocket(e),this._ready=!1,this._channelId="",this._producerSession=null,this._consumerSessions={},this._peers={},this._ws.onerror=e=>{this.dispatchEvent(new ErrorEvent("error",{message:e.message||"WebSocket error",error:e.error||new Error(this._ready?"transportation error":"cannot connect to signaling server")})),this.close()},this._ws.onclose=()=>{this._ready=!1,this._channelId="",this._ws=null,this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null),this.dispatchEvent(new Event("closed"))},this._ws.onmessage=e=>{try{const n=JSON.parse(e.data);if(n&&"object"==typeof n)switch(n.type){case Pe.welcome:this._channelId=n.peerId;try{this._ws.send(JSON.stringify({type:"setPeerStatus",roles:["listener"],meta:t}))}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot initialize connection to signaling server",error:e})),this.close()}break;case Pe.peerStatusChanged:{if(n.peerId===this._channelId){!this._ready&&n.roles.includes("listener")&&(this._ready=!0,this.dispatchEvent(new Event("ready")),this.send({type:"list"}),this.send({type:"listConsumers"})),this._producerSession&&n.roles.includes("producer")&&this._producerSession.onProducerRegistered();break}const e=Re(n,this._channelId);if(!e)break;const t=this._peers[n.peerId]||[];this._peers[n.peerId]=n.roles;for(const r of["producer","consumer"])!t.includes(r)&&n.roles.includes(r)?this.dispatchEvent(new CustomEvent("peerAdded",{detail:{peer:e,role:r}})):t.includes(r)&&!n.roles.includes(r)&&this.dispatchEvent(new CustomEvent("peerRemoved",{detail:{peerId:n.peerId,role:r}}));break}case Pe.list:this.clearPeers("producer"),this.addPeers(n.producers,"producer");break;case Pe.listConsumers:this.clearPeers("consumer"),this.addPeers(n.consumers,"consumer");break;case Pe.sessionStarted:{const e=this.getConsumerSession(n.peerId);e&&(delete this._consumerSessions[n.peerId],e.onSessionStarted(n.peerId,n.sessionId),e.sessionId&&!(e.sessionId in this._consumerSessions)?this._consumerSessions[e.sessionId]=e:e.close())}break;case Pe.peer:{const e=this.getConsumerSession(n.sessionId);e?e.onSessionPeerMessage(n):this._producerSession&&this._producerSession.onSessionPeerMessage(n)}break;case Pe.startSession:this._producerSession&&this._producerSession.onStartSessionMessage(n);break;case Pe.endSession:{const e=this.getConsumerSession(n.sessionId);e?e.close():this._producerSession&&this._producerSession.onEndSessionMessage(n)}break;case Pe.error:this.dispatchEvent(new ErrorEvent("error",{message:"error received from signaling server",error:new Error(n.details)}));break;default:throw new Error(`unknown message type: "${n.type}"`)}}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot parse incoming message from signaling server",error:e}))}}}get meta(){return this._meta}get webrtcConfig(){return this._webrtcConfig}get ready(){return this._ready}get channelId(){return this._channelId}get producerSession(){return this._producerSession}createProducerSession(e,t){if(!(this._ready&&e instanceof MediaStream))return null;if(this._producerSession)return this._producerSession.stream===e?this._producerSession:null;const n=new Ee(this,e,t);return this._producerSession=n,n.addEventListener("closed",()=>{this._producerSession===n&&(this._producerSession=null)}),n}createConsumerSession(e,t){if(!this._ready||!e||"string"!=typeof e)return null;if(t&&"object"!=typeof t&&(t=void 0),e in this._consumerSessions)return this._consumerSessions[e];for(const t of Object.values(this._consumerSessions))if(t.peerId===e)return t;const n=new Se(e,this,t);return this._consumerSessions[e]=n,n.addEventListener("closed",e=>{let t=e.target.sessionId;t||(t=e.target.peerId),t in this._consumerSessions&&this._consumerSessions[t]===n&&delete this._consumerSessions[t]}),n}getConsumerSession(e){return e in this._consumerSessions?this._consumerSessions[e]:null}closeAllConsumerSessions(){for(const e of Object.values(this._consumerSessions))e.close();this._consumerSessions={}}send(e){if(this._ready&&e&&"object"==typeof e)try{return this._ws.send(JSON.stringify(e)),!0}catch(e){this.dispatchEvent(new ErrorEvent("error",{message:"cannot send message to signaling server",error:e}))}return!1}close(){this._ws&&(this._ready=!1,this._channelId="",this._ws.close(),this.closeAllConsumerSessions(),this._producerSession&&(this._producerSession.close(),this._producerSession=null))}clearPeers(e){for(const t in this._peers)this._peers[t].includes(e)&&(delete this._peers[t],this.dispatchEvent(new CustomEvent("peerRemoved",{detail:{peerId:t,role:e}})))}addPeers(e,t){e.forEach(e=>{const n=Re(e,this._channelId);n&&(this._peers[n.id]=[t],this.dispatchEvent(new CustomEvent("peerAdded",{detail:{peer:n,role:t}})))})}}const Ae=we;class Ie{constructor(e){this._channel=null,this._producers={},this._consumers={},this._connectionListeners=[],this._peerListeners=[];const t=Object.assign({},se);e&&"object"==typeof e&&Object.assign(t,e),"object"!=typeof t.meta&&(t.meta=null),this._config=t,this.connectChannel()}registerConnectionListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.connected||"function"!=typeof e.disconnected)&&(this._connectionListeners.includes(e)||this._connectionListeners.push(e),!0)}unregisterConnectionListener(e){const t=this._connectionListeners.indexOf(e);return t>=0&&(this._connectionListeners.splice(t,1),!0)}unregisterAllConnectionListeners(){this._connectionListeners=[]}createProducerSession(e){return this._channel?this._channel.createProducerSession(e):null}createProducerSessionForConsumer(e,t){return this._channel?this._channel.createProducerSession(e,t):null}getAvailableProducers(){return Object.values(this._producers)}getAvailableConsumers(){return Object.values(this._consumers)}registerPeerListener(e){return!(!e||"object"!=typeof e||"function"!=typeof e.producerAdded&&"function"!=typeof e.producerRemoved&&"function"!=typeof e.consumerAdded&&"function"!=typeof e.consumerRemoved)&&(this._peerListeners.includes(e)||this._peerListeners.push(e),!0)}unregisterPeerListener(e){const t=this._peerListeners.indexOf(e);return t>=0&&(this._peerListeners.splice(t,1),!0)}unregisterAllPeerListeners(){this._peerListeners=[]}createConsumerSession(e){return this._channel?this._channel.createConsumerSession(e):null}createConsumerSessionWithOfferOptions(e,t){return this._channel?this._channel.createConsumerSession(e,t):null}connectChannel(){if(this._channel){const e=this._channel;this._channel=null,e.close();for(const e in this._producers)this.triggerProducerRemoved(e);for(const e in this._consumers)this.triggerConsumerRemoved(e);this._producers={},this._consumers={},this.triggerDisconnected()}this._channel=new Ae(this._config.signalingServerUrl,this._config.meta,this._config.webrtcConfig),this._channel.addEventListener("error",e=>{e.target===this._channel&&console.error(e.message,e.error)}),this._channel.addEventListener("closed",e=>{if(e.target===this._channel){this._channel=null;for(const e in this._producers)this.triggerProducerRemoved(e);for(const e in this._consumers)this.triggerConsumerRemoved(e);this._producers={},this._consumers={},this.triggerDisconnected(),this._config.reconnectionTimeout>0&&window.setTimeout(()=>{this.connectChannel()},this._config.reconnectionTimeout)}}),this._channel.addEventListener("ready",e=>{e.target===this._channel&&this.triggerConnected(this._channel.channelId)}),this._channel.addEventListener("peerAdded",e=>{e.target===this._channel&&("producer"===e.detail.role?this.triggerProducerAdded(e.detail.peer):this.triggerConsumerAdded(e.detail.peer))}),this._channel.addEventListener("peerRemoved",e=>{e.target===this._channel&&("producer"===e.detail.role?this.triggerProducerRemoved(e.detail.peerId):this.triggerConsumerRemoved(e.detail.peerId))})}triggerConnected(e){for(const t of this._connectionListeners)try{t.connected(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerDisconnected(){for(const e of this._connectionListeners)try{e.disconnected()}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerProducerAdded(e){if(!(e.id in this._producers)){this._producers[e.id]=e;for(const t of this._peerListeners)if(t.producerAdded)try{t.producerAdded(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}}triggerProducerRemoved(e){if(!(e in this._producers))return;const t=this._producers[e];delete this._producers[e];for(const e of this._peerListeners)if(e.producerRemoved)try{e.producerRemoved(t)}catch(e){console.error("a listener callback should not throw any exception",e)}}triggerConsumerAdded(e){if(!(e.id in this._consumers)){this._consumers[e.id]=e;for(const t of this._peerListeners)if(t.consumerAdded)try{t.consumerAdded(e)}catch(e){console.error("a listener callback should not throw any exception",e)}}}triggerConsumerRemoved(e){if(!(e in this._consumers))return;const t=this._consumers[e];delete this._consumers[e];for(const e of this._peerListeners)if(e.consumerRemoved)try{e.consumerRemoved(t)}catch(e){console.error("a listener callback should not throw any exception",e)}}}Ie.SessionState=ce;const xe=Ie;window.GstWebRTCAPI||(window.GstWebRTCAPI=xe)})()})(); +//# sourceMappingURL=gstwebrtc-api-3.0.0.min.js.map \ No newline at end of file diff --git a/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js.map b/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js.map new file mode 100644 index 0000000..46726d2 --- /dev/null +++ b/web/javascript/webrtc/gstwebrtc-api-3.0.0.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"gstwebrtc-api-3.0.0.min.js","mappings":"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","sources":["webpack://gstwebrtc-api/./node_modules/sdp/sdp.js","webpack://gstwebrtc-api/webpack/bootstrap","webpack://gstwebrtc-api/webpack/runtime/compat get default export","webpack://gstwebrtc-api/webpack/runtime/define property getters","webpack://gstwebrtc-api/webpack/runtime/hasOwnProperty shorthand","webpack://gstwebrtc-api/webpack/runtime/make namespace object","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/utils.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/safari/safari_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/common_shim.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/adapter_factory.js","webpack://gstwebrtc-api/./node_modules/webrtc-adapter/src/js/adapter_core.js","webpack://gstwebrtc-api/./src/config.js","webpack://gstwebrtc-api/./src/session-state.js","webpack://gstwebrtc-api/./src/webrtc-session.js","webpack://gstwebrtc-api/./src/keysyms.js","webpack://gstwebrtc-api/./src/remote-controller.js","webpack://gstwebrtc-api/./src/consumer-session.js","webpack://gstwebrtc-api/./src/producer-session.js","webpack://gstwebrtc-api/./src/com-channel.js","webpack://gstwebrtc-api/./src/gstwebrtc-api.js","webpack://gstwebrtc-api/./src/index.js"],"sourcesContent":["/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substring(2, 12);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substring(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substring(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n attributes: parts.slice(2).join(' '),\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri +\n (headerExtension.attributes ? ' ' + headerExtension.attributes : '') +\n '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\n// Non-key-value such as telephone-events `0-15` get parsed as\n// {`0-15`:undefined}\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substring(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substring(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substring(7, sp), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substring(sp + 1, colon);\n parts.value = line.substring(colon + 1);\n } else {\n parts.attribute = line.substring(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substring(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substring(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substring(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substring(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substring(12),\n password: pwd.substring(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n description.profile = mline[2];\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.forEach(codec => {\n wildcardRtcpFb.forEach(fb=> {\n const duplicate = codec.rtcpFeedback.find(existingFeedback => {\n return existingFeedback.type === fb.type &&\n existingFeedback.parameter === fb.parameter;\n });\n if (!duplicate) {\n codec.rtcpFeedback.push(fb);\n }\n });\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substring(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substring(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substring(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substring(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substring(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 22);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substring(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substring(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substring(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substring(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n const match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n const nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n const wrappedCallback = (e) => {\n const modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n if (cb.handleEvent) {\n cb.handleEvent(modifiedEvent);\n } else {\n cb(modifiedEvent);\n }\n }\n };\n this._eventMap = this._eventMap || {};\n if (!this._eventMap[eventNameToWrap]) {\n this._eventMap[eventNameToWrap] = new Map();\n }\n this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n const nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[eventNameToWrap]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n if (!this._eventMap[eventNameToWrap].has(cb)) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n this._eventMap[eventNameToWrap].delete(cb);\n if (this._eventMap[eventNameToWrap].size === 0) {\n delete this._eventMap[eventNameToWrap];\n }\n if (Object.keys(this._eventMap).length === 0) {\n delete this._eventMap;\n }\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get() {\n return this['_on' + eventNameToWrap];\n },\n set(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\nexport function disableLog(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\nexport function detectBrowser(window) {\n // Returned result object.\n const result = {browser: null, version: null};\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator ||\n !window.navigator.userAgent) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n const {navigator} = window;\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia ||\n (window.isSecureContext === false && window.webkitRTCPeerConnection)) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n // more complicated fallback to webkitRTCPeerConnection.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n 'currentDirection' in window.RTCRtpTransceiver.prototype;\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n if (!isObject(data)) {\n return data;\n }\n\n return Object.keys(data).reduce(function(accumulator, key) {\n const isObj = isObject(data[key]);\n const value = isObj ? compactObject(data[key]) : data[key];\n const isEmptyObject = isObj && !Object.keys(value).length;\n if (value === undefined || isEmptyObject) {\n return accumulator;\n }\n return Object.assign(accumulator, {[key]: value});\n }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n if (!base || resultSet.has(base.id)) {\n return;\n }\n resultSet.set(base.id, base);\n Object.keys(base).forEach(name => {\n if (name.endsWith('Id')) {\n walkStats(stats, stats.get(base[name]), resultSet);\n } else if (name.endsWith('Ids')) {\n base[name].forEach(id => {\n walkStats(stats, stats.get(id), resultSet);\n });\n }\n });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n const filteredResult = new Map();\n if (track === null) {\n return filteredResult;\n }\n const trackStats = [];\n result.forEach(value => {\n if (value.type === 'track' &&\n value.trackIdentifier === track.id) {\n trackStats.push(value);\n }\n });\n trackStats.forEach(trackStat => {\n result.forEach(stats => {\n if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n walkStats(result, stats, filteredResult);\n }\n });\n });\n return filteredResult;\n}\n\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n\n if (!navigator.mediaDevices) {\n return;\n }\n\n const constraintsToChrome_ = function(c) {\n if (typeof c !== 'object' || c.mandatory || c.optional) {\n return c;\n }\n const cc = {};\n Object.keys(c).forEach(key => {\n if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n return;\n }\n const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n if (r.exact !== undefined && typeof r.exact === 'number') {\n r.min = r.max = r.exact;\n }\n const oldname_ = function(prefix, name) {\n if (prefix) {\n return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n }\n return (name === 'deviceId') ? 'sourceId' : name;\n };\n if (r.ideal !== undefined) {\n cc.optional = cc.optional || [];\n let oc = {};\n if (typeof r.ideal === 'number') {\n oc[oldname_('min', key)] = r.ideal;\n cc.optional.push(oc);\n oc = {};\n oc[oldname_('max', key)] = r.ideal;\n cc.optional.push(oc);\n } else {\n oc[oldname_('', key)] = r.ideal;\n cc.optional.push(oc);\n }\n }\n if (r.exact !== undefined && typeof r.exact !== 'number') {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_('', key)] = r.exact;\n } else {\n ['min', 'max'].forEach(mix => {\n if (r[mix] !== undefined) {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_(mix, key)] = r[mix];\n }\n });\n }\n });\n if (c.advanced) {\n cc.optional = (cc.optional || []).concat(c.advanced);\n }\n return cc;\n };\n\n const shimConstraints_ = function(constraints, func) {\n if (browserDetails.version >= 61) {\n return func(constraints);\n }\n constraints = JSON.parse(JSON.stringify(constraints));\n if (constraints && typeof constraints.audio === 'object') {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n constraints = JSON.parse(JSON.stringify(constraints));\n remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n constraints.audio = constraintsToChrome_(constraints.audio);\n }\n if (constraints && typeof constraints.video === 'object') {\n // Shim facingMode for mobile & surface pro.\n let face = constraints.video.facingMode;\n face = face && ((typeof face === 'object') ? face : {ideal: face});\n const getSupportedFacingModeLies = browserDetails.version < 66;\n\n if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n face.ideal === 'user' || face.ideal === 'environment')) &&\n !(navigator.mediaDevices.getSupportedConstraints &&\n navigator.mediaDevices.getSupportedConstraints().facingMode &&\n !getSupportedFacingModeLies)) {\n delete constraints.video.facingMode;\n let matches;\n if (face.exact === 'environment' || face.ideal === 'environment') {\n matches = ['back', 'rear'];\n } else if (face.exact === 'user' || face.ideal === 'user') {\n matches = ['front'];\n }\n if (matches) {\n // Look for matches in label, or use last cam for back (typical).\n return navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n devices = devices.filter(d => d.kind === 'videoinput');\n let dev = devices.find(d => matches.some(match =>\n d.label.toLowerCase().includes(match)));\n if (!dev && devices.length && matches.includes('back')) {\n dev = devices[devices.length - 1]; // more likely the back cam\n }\n if (dev) {\n constraints.video.deviceId = face.exact\n ? {exact: dev.deviceId}\n : {ideal: dev.deviceId};\n }\n constraints.video = constraintsToChrome_(constraints.video);\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n });\n }\n }\n constraints.video = constraintsToChrome_(constraints.video);\n }\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n };\n\n const shimError_ = function(e) {\n if (browserDetails.version >= 64) {\n return e;\n }\n return {\n name: {\n PermissionDeniedError: 'NotAllowedError',\n PermissionDismissedError: 'NotAllowedError',\n InvalidStateError: 'NotAllowedError',\n DevicesNotFoundError: 'NotFoundError',\n ConstraintNotSatisfiedError: 'OverconstrainedError',\n TrackStartError: 'NotReadableError',\n MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n MediaDeviceKillSwitchOn: 'NotAllowedError',\n TabCaptureError: 'AbortError',\n ScreenCaptureError: 'AbortError',\n DeviceCaptureError: 'AbortError'\n }[e.name] || e.name,\n message: e.message,\n constraint: e.constraint || e.constraintName,\n toString() {\n return this.name + (this.message && ': ') + this.message;\n }\n };\n };\n\n const getUserMedia_ = function(constraints, onSuccess, onError) {\n shimConstraints_(constraints, c => {\n navigator.webkitGetUserMedia(c, onSuccess, e => {\n if (onError) {\n onError(shimError_(e));\n }\n });\n });\n };\n navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n // function which returns a Promise, it does not accept spec-style\n // constraints.\n if (navigator.mediaDevices.getUserMedia) {\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(cs) {\n return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n if (c.audio && !stream.getAudioTracks().length ||\n c.video && !stream.getVideoTracks().length) {\n stream.getTracks().forEach(track => {\n track.stop();\n });\n throw new DOMException('', 'NotFoundError');\n }\n return stream;\n }, e => Promise.reject(shimError_(e))));\n };\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n // getSourceId is a function that returns a promise resolving with\n // the sourceId of the screen/window/tab to be shared.\n if (typeof getSourceId !== 'function') {\n console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n 'a function');\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n return getSourceId(constraints)\n .then(sourceId => {\n const widthSpecified = constraints.video && constraints.video.width;\n const heightSpecified = constraints.video &&\n constraints.video.height;\n const frameRateSpecified = constraints.video &&\n constraints.video.frameRate;\n constraints.video = {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: sourceId,\n maxFrameRate: frameRateSpecified || 3\n }\n };\n if (widthSpecified) {\n constraints.video.mandatory.maxWidth = widthSpecified;\n }\n if (heightSpecified) {\n constraints.video.mandatory.maxHeight = heightSpecified;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n });\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n get() {\n return this._ontrack;\n },\n set(f) {\n if (this._ontrack) {\n this.removeEventListener('track', this._ontrack);\n }\n this.addEventListener('track', this._ontrack = f);\n },\n enumerable: true,\n configurable: true\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n if (!this._ontrackpoly) {\n this._ontrackpoly = (e) => {\n // onaddstream does not fire when a track is added to an existing\n // stream. But stream.onaddtrack is implemented so we use that.\n e.stream.addEventListener('addtrack', te => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === te.track.id);\n } else {\n receiver = {track: te.track};\n }\n\n const event = new Event('track');\n event.track = te.track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n e.stream.getTracks().forEach(track => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === track.id);\n } else {\n receiver = {track};\n }\n const event = new Event('track');\n event.track = track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n };\n this.addEventListener('addstream', this._ontrackpoly);\n }\n return origSetRemoteDescription.apply(this, arguments);\n };\n } else {\n // even if RTCRtpTransceiver is in window, it is only used and\n // emitted in unified-plan. Unfortunately this means we need\n // to unconditionally wrap the event.\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n if (!e.transceiver) {\n Object.defineProperty(e, 'transceiver',\n {value: {receiver: e.receiver}});\n }\n return e;\n });\n }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n if (typeof window === 'object' && window.RTCPeerConnection &&\n !('getSenders' in window.RTCPeerConnection.prototype) &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype) {\n const shimSenderWithDtmf = function(pc, track) {\n return {\n track,\n get dtmf() {\n if (this._dtmf === undefined) {\n if (track.kind === 'audio') {\n this._dtmf = pc.createDTMFSender(track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n },\n _pc: pc\n };\n };\n\n // augment addTrack when getSenders is not available.\n if (!window.RTCPeerConnection.prototype.getSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n this._senders = this._senders || [];\n return this._senders.slice(); // return a copy of the internal state.\n };\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n let sender = origAddTrack.apply(this, arguments);\n if (!sender) {\n sender = shimSenderWithDtmf(this, track);\n this._senders.push(sender);\n }\n return sender;\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n origRemoveTrack.apply(this, arguments);\n const idx = this._senders.indexOf(sender);\n if (idx !== -1) {\n this._senders.splice(idx, 1);\n }\n };\n }\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._senders = this._senders || [];\n origAddStream.apply(this, [stream]);\n stream.getTracks().forEach(track => {\n this._senders.push(shimSenderWithDtmf(this, track));\n });\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._senders = this._senders || [];\n origRemoveStream.apply(this, [stream]);\n\n stream.getTracks().forEach(track => {\n const sender = this._senders.find(s => s.track === track);\n if (sender) { // remove sender\n this._senders.splice(this._senders.indexOf(sender), 1);\n }\n });\n };\n } else if (typeof window === 'object' && window.RTCPeerConnection &&\n 'getSenders' in window.RTCPeerConnection.prototype &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype &&\n window.RTCRtpSender &&\n !('dtmf' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = this._pc.createDTMFSender(this.track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n}\n\nexport function shimGetStats(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n\n // If selector is a function then we are in the old style stats so just\n // pass back the original getStats format to avoid breaking old users.\n if (arguments.length > 0 && typeof selector === 'function') {\n return origGetStats.apply(this, arguments);\n }\n\n // When spec-style getStats is supported, return those when called with\n // either no arguments or the selector argument is null.\n if (origGetStats.length === 0 && (arguments.length === 0 ||\n typeof selector !== 'function')) {\n return origGetStats.apply(this, []);\n }\n\n const fixChromeStats_ = function(response) {\n const standardReport = {};\n const reports = response.result();\n reports.forEach(report => {\n const standardStats = {\n id: report.id,\n timestamp: report.timestamp,\n type: {\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[report.type] || report.type\n };\n report.names().forEach(name => {\n standardStats[name] = report.stat(name);\n });\n standardReport[standardStats.id] = standardStats;\n });\n\n return standardReport;\n };\n\n // shim getStats with maplike support\n const makeMapStats = function(stats) {\n return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n };\n\n if (arguments.length >= 2) {\n const successCallbackWrapper_ = function(response) {\n onSucc(makeMapStats(fixChromeStats_(response)));\n };\n\n return origGetStats.apply(this, [successCallbackWrapper_,\n selector]);\n }\n\n // promise-support\n return new Promise((resolve, reject) => {\n origGetStats.apply(this, [\n function(response) {\n resolve(makeMapStats(fixChromeStats_(response)));\n }, reject]);\n }).then(onSucc, onErr);\n };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender && window.RTCRtpReceiver)) {\n return;\n }\n\n // shim sender stats.\n if (!('getStats' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n const sender = this;\n return this._pc.getStats().then(result =>\n /* Note: this will include stats of all senders that\n * send a track with the same id as sender.track as\n * it is not possible to identify the RTCRtpSender.\n */\n utils.filterStats(result, sender.track, true));\n };\n }\n\n // shim receiver stats.\n if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers =\n function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n const receiver = this;\n return this._pc.getStats().then(result =>\n utils.filterStats(result, receiver.track, false));\n };\n }\n\n if (!('getStats' in window.RTCRtpSender.prototype &&\n 'getStats' in window.RTCRtpReceiver.prototype)) {\n return;\n }\n\n // shim RTCPeerConnection.getStats(track).\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n if (arguments.length > 0 &&\n arguments[0] instanceof window.MediaStreamTrack) {\n const track = arguments[0];\n let sender;\n let receiver;\n let err;\n this.getSenders().forEach(s => {\n if (s.track === track) {\n if (sender) {\n err = true;\n } else {\n sender = s;\n }\n }\n });\n this.getReceivers().forEach(r => {\n if (r.track === track) {\n if (receiver) {\n err = true;\n } else {\n receiver = r;\n }\n }\n return r.track === track;\n });\n if (err || (sender && receiver)) {\n return Promise.reject(new DOMException(\n 'There are more than one sender or receiver for the track.',\n 'InvalidAccessError'));\n } else if (sender) {\n return sender.getStats();\n } else if (receiver) {\n return receiver.getStats();\n }\n return Promise.reject(new DOMException(\n 'There is no sender or receiver for the track.',\n 'InvalidAccessError'));\n }\n return origGetStats.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n // shim addTrack/removeTrack with native variants in order to make\n // the interactions with legacy getLocalStreams behave as in other browsers.\n // Keeps a mapping stream.id => [stream, rtpsenders...]\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n return Object.keys(this._shimmedLocalStreams)\n .map(streamId => this._shimmedLocalStreams[streamId][0]);\n };\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (!stream) {\n return origAddTrack.apply(this, arguments);\n }\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n const sender = origAddTrack.apply(this, arguments);\n if (!this._shimmedLocalStreams[stream.id]) {\n this._shimmedLocalStreams[stream.id] = [stream, sender];\n } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n this._shimmedLocalStreams[stream.id].push(sender);\n }\n return sender;\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n const existingSenders = this.getSenders();\n origAddStream.apply(this, arguments);\n const newSenders = this.getSenders()\n .filter(newSender => existingSenders.indexOf(newSender) === -1);\n this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n delete this._shimmedLocalStreams[stream.id];\n return origRemoveStream.apply(this, arguments);\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n if (sender) {\n Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n if (idx !== -1) {\n this._shimmedLocalStreams[streamId].splice(idx, 1);\n }\n if (this._shimmedLocalStreams[streamId].length === 1) {\n delete this._shimmedLocalStreams[streamId];\n }\n });\n }\n return origRemoveTrack.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // shim addTrack and removeTrack.\n if (window.RTCPeerConnection.prototype.addTrack &&\n browserDetails.version >= 65) {\n return shimAddTrackRemoveTrackWithNative(window);\n }\n\n // also shim pc.getLocalStreams when addTrack is shimmed\n // to return the original streams.\n const origGetLocalStreams = window.RTCPeerConnection.prototype\n .getLocalStreams;\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n const nativeStreams = origGetLocalStreams.apply(this);\n this._reverseStreams = this._reverseStreams || {};\n return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n // Add identity mapping for consistency with addTrack.\n // Unless this is being used with a stream from addTrack.\n if (!this._reverseStreams[stream.id]) {\n const newStream = new window.MediaStream(stream.getTracks());\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n stream = newStream;\n }\n origAddStream.apply(this, [stream]);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n delete this._reverseStreams[(this._streams[stream.id] ?\n this._streams[stream.id].id : stream.id)];\n delete this._streams[stream.id];\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n const streams = [].slice.call(arguments, 1);\n if (streams.length !== 1 ||\n !streams[0].getTracks().find(t => t === track)) {\n // this is not fully correct but all we can manage without\n // [[associated MediaStreams]] internal slot.\n throw new DOMException(\n 'The adapter.js addTrack polyfill only supports a single ' +\n ' stream which is associated with the specified track.',\n 'NotSupportedError');\n }\n\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n const oldStream = this._streams[stream.id];\n if (oldStream) {\n // this is using odd Chrome behaviour, use with caution:\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n // Note: we rely on the high-level addTrack/dtmf shim to\n // create the sender with a dtmf sender.\n oldStream.addTrack(track);\n\n // Trigger ONN async.\n Promise.resolve().then(() => {\n this.dispatchEvent(new Event('negotiationneeded'));\n });\n } else {\n const newStream = new window.MediaStream([track]);\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n this.addStream(newStream);\n }\n return this.getSenders().find(s => s.track === track);\n };\n\n // replace the internal stream id with the external one and\n // vice versa.\n function replaceInternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n externalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n function replaceExternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n internalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n ['createOffer', 'createAnswer'].forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n const args = arguments;\n const isLegacyCall = arguments.length &&\n typeof arguments[0] === 'function';\n if (isLegacyCall) {\n return nativeMethod.apply(this, [\n (description) => {\n const desc = replaceInternalStreamId(this, description);\n args[0].apply(null, [desc]);\n },\n (err) => {\n if (args[1]) {\n args[1].apply(null, err);\n }\n }, arguments[2]\n ]);\n }\n return nativeMethod.apply(this, arguments)\n .then(description => replaceInternalStreamId(this, description));\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n\n const origSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n if (!arguments.length || !arguments[0].type) {\n return origSetLocalDescription.apply(this, arguments);\n }\n arguments[0] = replaceExternalStreamId(this, arguments[0]);\n return origSetLocalDescription.apply(this, arguments);\n };\n\n // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n const origLocalDescription = Object.getOwnPropertyDescriptor(\n window.RTCPeerConnection.prototype, 'localDescription');\n Object.defineProperty(window.RTCPeerConnection.prototype,\n 'localDescription', {\n get() {\n const description = origLocalDescription.get.apply(this);\n if (description.type === '') {\n return description;\n }\n return replaceInternalStreamId(this, description);\n }\n });\n\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n // We can not yet check for sender instanceof RTCRtpSender\n // since we shim RTPSender. So we check if sender._pc is set.\n if (!sender._pc) {\n throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.', 'TypeError');\n }\n const isLocal = sender._pc === this;\n if (!isLocal) {\n throw new DOMException('Sender was not created by this connection.',\n 'InvalidAccessError');\n }\n\n // Search for the native stream the senders track belongs to.\n this._streams = this._streams || {};\n let stream;\n Object.keys(this._streams).forEach(streamid => {\n const hasTrack = this._streams[streamid].getTracks()\n .find(track => sender.track === track);\n if (hasTrack) {\n stream = this._streams[streamid];\n }\n });\n\n if (stream) {\n if (stream.getTracks().length === 1) {\n // if this is the last track of the stream, remove the stream. This\n // takes care of any shimmed _senders.\n this.removeStream(this._reverseStreams[stream.id]);\n } else {\n // relying on the same odd chrome behaviour as above.\n stream.removeTrack(sender.track);\n }\n this.dispatchEvent(new Event('negotiationneeded'));\n }\n };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.webkitRTCPeerConnection;\n }\n if (!window.RTCPeerConnection) {\n return;\n }\n\n // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n if (browserDetails.version < 53) {\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n const pc = e.target;\n if (browserDetails.version < 72 || (pc.getConfiguration &&\n pc.getConfiguration().sdpSemantics === 'plan-b')) {\n if (pc.signalingState !== 'stable') {\n return;\n }\n }\n return e;\n });\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n const MediaStreamTrack = window && window.MediaStreamTrack;\n\n navigator.getUserMedia = function(constraints, onSuccess, onError) {\n // Replace Firefox 44+'s deprecation warning with unprefixed version.\n utils.deprecated('navigator.getUserMedia',\n 'navigator.mediaDevices.getUserMedia');\n navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n };\n\n if (!(browserDetails.version > 55 &&\n 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n\n const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n if (typeof c === 'object' && typeof c.audio === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeGetUserMedia(c);\n };\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n MediaStreamTrack.prototype.getSettings = function() {\n const obj = nativeGetSettings.apply(this, arguments);\n remap(obj, 'mozAutoGainControl', 'autoGainControl');\n remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n return obj;\n };\n }\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n const nativeApplyConstraints =\n MediaStreamTrack.prototype.applyConstraints;\n MediaStreamTrack.prototype.applyConstraints = function(c) {\n if (this.kind === 'audio' && typeof c === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c, 'autoGainControl', 'mozAutoGainControl');\n remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeApplyConstraints.apply(this, [c]);\n };\n }\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n if (!(constraints && constraints.video)) {\n const err = new DOMException('getDisplayMedia without video ' +\n 'constraints is undefined');\n err.name = 'NotFoundError';\n // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n err.code = 8;\n return Promise.reject(err);\n }\n if (constraints.video === true) {\n constraints.video = {mediaSource: preferredMediaSource};\n } else {\n constraints.video.mediaSource = preferredMediaSource;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCTrackEvent &&\n ('receiver' in window.RTCTrackEvent.prototype) &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (typeof window !== 'object' ||\n !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n return; // probably media.peerconnection.enabled=false in about:config\n }\n if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.mozRTCPeerConnection;\n }\n\n if (browserDetails.version < 53) {\n // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n\n const modernStatsTypes = {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n };\n\n const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n return nativeGetStats.apply(this, [selector || null])\n .then(stats => {\n if (browserDetails.version < 53 && !onSucc) {\n // Shim only promise getStats with spec-hyphens in type names\n // Leave callback version alone; misc old uses of forEach before Map\n try {\n stats.forEach(stat => {\n stat.type = modernStatsTypes[stat.type] || stat.type;\n });\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n stats.forEach((stat, i) => {\n stats.set(i, Object.assign({}, stat, {\n type: modernStatsTypes[stat.type] || stat.type\n }));\n });\n }\n }\n return stats;\n })\n .then(onSucc, onErr);\n };\n}\n\nexport function shimSenderGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n return;\n }\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n return this.track ? this._pc.getStats(this.track) :\n Promise.resolve(new Map());\n };\n}\n\nexport function shimReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n return;\n }\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n return this._pc.getStats(this.track);\n };\n}\n\nexport function shimRemoveStream(window) {\n if (!window.RTCPeerConnection ||\n 'removeStream' in window.RTCPeerConnection.prototype) {\n return;\n }\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n utils.deprecated('removeStream', 'removeTrack');\n this.getSenders().forEach(sender => {\n if (sender.track && stream.getTracks().includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n}\n\nexport function shimRTCDataChannel(window) {\n // rename DataChannel to RTCDataChannel (native fix in FF60):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n if (window.DataChannel && !window.RTCDataChannel) {\n window.RTCDataChannel = window.DataChannel;\n }\n}\n\nexport function shimAddTransceiver(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n if (origAddTransceiver) {\n window.RTCPeerConnection.prototype.addTransceiver =\n function addTransceiver() {\n this.setParametersPromises = [];\n // WebIDL input coercion and validation\n let sendEncodings = arguments[1] && arguments[1].sendEncodings;\n if (sendEncodings === undefined) {\n sendEncodings = [];\n }\n sendEncodings = [...sendEncodings];\n const shouldPerformCheck = sendEncodings.length > 0;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n sendEncodings.forEach((encodingParam) => {\n if ('rid' in encodingParam) {\n const ridRegex = /^[a-z0-9]{0,16}$/i;\n if (!ridRegex.test(encodingParam.rid)) {\n throw new TypeError('Invalid RID value provided.');\n }\n }\n if ('scaleResolutionDownBy' in encodingParam) {\n if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n throw new RangeError('scale_resolution_down_by must be >= 1.0');\n }\n }\n if ('maxFramerate' in encodingParam) {\n if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n throw new RangeError('max_framerate must be >= 0.0');\n }\n }\n });\n }\n const transceiver = origAddTransceiver.apply(this, arguments);\n if (shouldPerformCheck) {\n // Check if the init options were applied. If not we do this in an\n // asynchronous way and save the promise reference in a global object.\n // This is an ugly hack, but at the same time is way more robust than\n // checking the sender parameters before and after the createOffer\n // Also note that after the createoffer we are not 100% sure that\n // the params were asynchronously applied so we might miss the\n // opportunity to recreate offer.\n const {sender} = transceiver;\n const params = sender.getParameters();\n if (!('encodings' in params) ||\n // Avoid being fooled by patched getParameters() below.\n (params.encodings.length === 1 &&\n Object.keys(params.encodings[0]).length === 0)) {\n params.encodings = sendEncodings;\n sender.sendEncodings = sendEncodings;\n this.setParametersPromises.push(sender.setParameters(params)\n .then(() => {\n delete sender.sendEncodings;\n }).catch(() => {\n delete sender.sendEncodings;\n })\n );\n }\n }\n return transceiver;\n };\n }\n}\n\nexport function shimGetParameters(window) {\n if (!(typeof window === 'object' && window.RTCRtpSender)) {\n return;\n }\n const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n if (origGetParameters) {\n window.RTCRtpSender.prototype.getParameters =\n function getParameters() {\n const params = origGetParameters.apply(this, arguments);\n if (!('encodings' in params)) {\n params.encodings = [].concat(this.sendEncodings || [{}]);\n }\n return params;\n };\n }\n}\n\nexport function shimCreateOffer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateOffer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimCreateAnswer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateAnswer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateAnswer.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n return this._localStreams;\n };\n }\n if (!('addStream' in window.RTCPeerConnection.prototype)) {\n const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n // Try to emulate Chrome's behaviour of adding in audio-video order.\n // Safari orders by track id.\n stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n stream));\n stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n stream));\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, ...streams) {\n if (streams) {\n streams.forEach((stream) => {\n if (!this._localStreams) {\n this._localStreams = [stream];\n } else if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n });\n }\n return _addTrack.apply(this, arguments);\n };\n }\n if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n const index = this._localStreams.indexOf(stream);\n if (index === -1) {\n return;\n }\n this._localStreams.splice(index, 1);\n const tracks = stream.getTracks();\n this.getSenders().forEach(sender => {\n if (tracks.includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getRemoteStreams =\n function getRemoteStreams() {\n return this._remoteStreams ? this._remoteStreams : [];\n };\n }\n if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n get() {\n return this._onaddstream;\n },\n set(f) {\n if (this._onaddstream) {\n this.removeEventListener('addstream', this._onaddstream);\n this.removeEventListener('track', this._onaddstreampoly);\n }\n this.addEventListener('addstream', this._onaddstream = f);\n this.addEventListener('track', this._onaddstreampoly = (e) => {\n e.streams.forEach(stream => {\n if (!this._remoteStreams) {\n this._remoteStreams = [];\n }\n if (this._remoteStreams.includes(stream)) {\n return;\n }\n this._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n this.dispatchEvent(event);\n });\n });\n }\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n const pc = this;\n if (!this._onaddstreampoly) {\n this.addEventListener('track', this._onaddstreampoly = function(e) {\n e.streams.forEach(stream => {\n if (!pc._remoteStreams) {\n pc._remoteStreams = [];\n }\n if (pc._remoteStreams.indexOf(stream) >= 0) {\n return;\n }\n pc._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n pc.dispatchEvent(event);\n });\n });\n }\n return origSetRemoteDescription.apply(pc, arguments);\n };\n }\n}\n\nexport function shimCallbacksAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n const prototype = window.RTCPeerConnection.prototype;\n const origCreateOffer = prototype.createOffer;\n const origCreateAnswer = prototype.createAnswer;\n const setLocalDescription = prototype.setLocalDescription;\n const setRemoteDescription = prototype.setRemoteDescription;\n const addIceCandidate = prototype.addIceCandidate;\n\n prototype.createOffer =\n function createOffer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateOffer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n prototype.createAnswer =\n function createAnswer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateAnswer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n let withCallback = function(description, successCallback, failureCallback) {\n const promise = setLocalDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setLocalDescription = withCallback;\n\n withCallback = function(description, successCallback, failureCallback) {\n const promise = setRemoteDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setRemoteDescription = withCallback;\n\n withCallback = function(candidate, successCallback, failureCallback) {\n const promise = addIceCandidate.apply(this, [candidate]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n // shim not needed in Safari 12.1\n const mediaDevices = navigator.mediaDevices;\n const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n navigator.mediaDevices.getUserMedia = (constraints) => {\n return _getUserMedia(shimConstraints(constraints));\n };\n }\n\n if (!navigator.getUserMedia && navigator.mediaDevices &&\n navigator.mediaDevices.getUserMedia) {\n navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(cb, errcb);\n }.bind(navigator);\n }\n}\n\nexport function shimConstraints(constraints) {\n if (constraints && constraints.video !== undefined) {\n return Object.assign({},\n constraints,\n {video: utils.compactObject(constraints.video)}\n );\n }\n\n return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n const OrigPeerConnection = window.RTCPeerConnection;\n window.RTCPeerConnection =\n function RTCPeerConnection(pcConfig, pcConstraints) {\n if (pcConfig && pcConfig.iceServers) {\n const newIceServers = [];\n for (let i = 0; i < pcConfig.iceServers.length; i++) {\n let server = pcConfig.iceServers[i];\n if (server.urls === undefined && server.url) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n server = JSON.parse(JSON.stringify(server));\n server.urls = server.url;\n delete server.url;\n newIceServers.push(server);\n } else {\n newIceServers.push(pcConfig.iceServers[i]);\n }\n }\n pcConfig.iceServers = newIceServers;\n }\n return new OrigPeerConnection(pcConfig, pcConstraints);\n };\n window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n // wrap static methods. Currently just generateCertificate.\n if ('generateCertificate' in OrigPeerConnection) {\n Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n get() {\n return OrigPeerConnection.generateCertificate;\n }\n });\n }\n}\n\nexport function shimTrackEventTransceiver(window) {\n // Add event.transceiver member over deprecated event.receiver\n if (typeof window === 'object' && window.RTCTrackEvent &&\n 'receiver' in window.RTCTrackEvent.prototype &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimCreateOfferLegacy(window) {\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer =\n function createOffer(offerOptions) {\n if (offerOptions) {\n if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveAudio =\n !!offerOptions.offerToReceiveAudio;\n }\n const audioTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'audio');\n if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n if (audioTransceiver.direction === 'sendrecv') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('sendonly');\n } else {\n audioTransceiver.direction = 'sendonly';\n }\n } else if (audioTransceiver.direction === 'recvonly') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('inactive');\n } else {\n audioTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveAudio === true &&\n !audioTransceiver) {\n this.addTransceiver('audio', {direction: 'recvonly'});\n }\n\n if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveVideo =\n !!offerOptions.offerToReceiveVideo;\n }\n const videoTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'video');\n if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n if (videoTransceiver.direction === 'sendrecv') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('sendonly');\n } else {\n videoTransceiver.direction = 'sendonly';\n }\n } else if (videoTransceiver.direction === 'recvonly') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('inactive');\n } else {\n videoTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveVideo === true &&\n !videoTransceiver) {\n this.addTransceiver('video', {direction: 'recvonly'});\n }\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimAudioContext(window) {\n if (typeof window !== 'object' || window.AudioContext) {\n return;\n }\n window.AudioContext = window.webkitAudioContext;\n}\n\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n // foundation is arbitrarily chosen as an indicator for full support for\n // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n const NativeRTCIceCandidate = window.RTCIceCandidate;\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n // Remove the a= which shouldn't be part of the candidate string.\n if (typeof args === 'object' && args.candidate &&\n args.candidate.indexOf('a=') === 0) {\n args = JSON.parse(JSON.stringify(args));\n args.candidate = args.candidate.substring(2);\n }\n\n if (args.candidate && args.candidate.length) {\n // Augment the native candidate with the parsed fields.\n const nativeCandidate = new NativeRTCIceCandidate(args);\n const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n for (const key in parsedCandidate) {\n if (!(key in nativeCandidate)) {\n Object.defineProperty(nativeCandidate, key,\n {value: parsedCandidate[key]});\n }\n }\n\n // Override serializer to not serialize the extra attributes.\n nativeCandidate.toJSON = function toJSON() {\n return {\n candidate: nativeCandidate.candidate,\n sdpMid: nativeCandidate.sdpMid,\n sdpMLineIndex: nativeCandidate.sdpMLineIndex,\n usernameFragment: nativeCandidate.usernameFragment,\n };\n };\n return nativeCandidate;\n }\n return new NativeRTCIceCandidate(args);\n };\n window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n Object.defineProperty(e, 'candidate', {\n value: new window.RTCIceCandidate(e.candidate),\n writable: 'false'\n });\n }\n return e;\n });\n}\n\nexport function shimRTCIceCandidateRelayProtocol(window) {\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'relayProtocol' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n const parsedCandidate = SDPUtils.parseCandidate(e.candidate.candidate);\n if (parsedCandidate.type === 'relay') {\n // This is a libwebrtc-specific mapping of local type preference\n // to relayProtocol.\n e.candidate.relayProtocol = {\n 0: 'tls',\n 1: 'tcp',\n 2: 'udp',\n }[parsedCandidate.priority >> 24];\n }\n }\n return e;\n });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n if (!('sctp' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n }\n });\n }\n\n const sctpInDescription = function(description) {\n if (!description || !description.sdp) {\n return false;\n }\n const sections = SDPUtils.splitSections(description.sdp);\n sections.shift();\n return sections.some(mediaSection => {\n const mLine = SDPUtils.parseMLine(mediaSection);\n return mLine && mLine.kind === 'application'\n && mLine.protocol.indexOf('SCTP') !== -1;\n });\n };\n\n const getRemoteFirefoxVersion = function(description) {\n // TODO: Is there a better solution for detecting Firefox?\n const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n if (match === null || match.length < 2) {\n return -1;\n }\n const version = parseInt(match[1], 10);\n // Test for NaN (yes, this is ugly)\n return version !== version ? -1 : version;\n };\n\n const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n // Every implementation we know can send at least 64 KiB.\n // Note: Although Chrome is technically able to send up to 256 KiB, the\n // data does not reach the other peer reliably.\n // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n let canSendMaxMessageSize = 65536;\n if (browserDetails.browser === 'firefox') {\n if (browserDetails.version < 57) {\n if (remoteIsFirefox === -1) {\n // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n // fragmentation.\n canSendMaxMessageSize = 16384;\n } else {\n // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n // messages. Thus, supporting ~2 GiB when sending.\n canSendMaxMessageSize = 2147483637;\n }\n } else if (browserDetails.version < 60) {\n // Currently, all FF >= 57 will reset the remote maximum message size\n // to the default value when a data channel is created at a later\n // stage. :(\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n canSendMaxMessageSize =\n browserDetails.version === 57 ? 65535 : 65536;\n } else {\n // FF >= 60 supports sending ~2 GiB\n canSendMaxMessageSize = 2147483637;\n }\n }\n return canSendMaxMessageSize;\n };\n\n const getMaxMessageSize = function(description, remoteIsFirefox) {\n // Note: 65536 bytes is the default value from the SDP spec. Also,\n // every implementation we know supports receiving 65536 bytes.\n let maxMessageSize = 65536;\n\n // FF 57 has a slightly incorrect default remote max message size, so\n // we need to adjust it here to avoid a failure when sending.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n if (browserDetails.browser === 'firefox'\n && browserDetails.version === 57) {\n maxMessageSize = 65535;\n }\n\n const match = SDPUtils.matchPrefix(description.sdp,\n 'a=max-message-size:');\n if (match.length > 0) {\n maxMessageSize = parseInt(match[0].substring(19), 10);\n } else if (browserDetails.browser === 'firefox' &&\n remoteIsFirefox !== -1) {\n // If the maximum message size is not present in the remote SDP and\n // both local and remote are Firefox, the remote peer can receive\n // ~2 GiB.\n maxMessageSize = 2147483637;\n }\n return maxMessageSize;\n };\n\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n this._sctp = null;\n // Chrome decided to not expose .sctp in plan-b mode.\n // As usual, adapter.js has to do an 'ugly worakaround'\n // to cover up the mess.\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n const {sdpSemantics} = this.getConfiguration();\n if (sdpSemantics === 'plan-b') {\n Object.defineProperty(this, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n if (sctpInDescription(arguments[0])) {\n // Check if the remote is FF.\n const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n // Get the maximum message size the local peer is capable of sending\n const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n // Get the maximum message size of the remote peer.\n const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n // Determine final maximum message size\n let maxMessageSize;\n if (canSendMMS === 0 && remoteMMS === 0) {\n maxMessageSize = Number.POSITIVE_INFINITY;\n } else if (canSendMMS === 0 || remoteMMS === 0) {\n maxMessageSize = Math.max(canSendMMS, remoteMMS);\n } else {\n maxMessageSize = Math.min(canSendMMS, remoteMMS);\n }\n\n // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n // attribute.\n const sctp = {};\n Object.defineProperty(sctp, 'maxMessageSize', {\n get() {\n return maxMessageSize;\n }\n });\n this._sctp = sctp;\n }\n\n return origSetRemoteDescription.apply(this, arguments);\n };\n}\n\nexport function shimSendThrowTypeError(window) {\n if (!(window.RTCPeerConnection &&\n 'createDataChannel' in window.RTCPeerConnection.prototype)) {\n return;\n }\n\n // Note: Although Firefox >= 57 has a native implementation, the maximum\n // message size can be reset for all data channels at a later stage.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n function wrapDcSend(dc, pc) {\n const origDataChannelSend = dc.send;\n dc.send = function send() {\n const data = arguments[0];\n const length = data.length || data.size || data.byteLength;\n if (dc.readyState === 'open' &&\n pc.sctp && length > pc.sctp.maxMessageSize) {\n throw new TypeError('Message too large (can send a maximum of ' +\n pc.sctp.maxMessageSize + ' bytes)');\n }\n return origDataChannelSend.apply(dc, arguments);\n };\n }\n const origCreateDataChannel =\n window.RTCPeerConnection.prototype.createDataChannel;\n window.RTCPeerConnection.prototype.createDataChannel =\n function createDataChannel() {\n const dataChannel = origCreateDataChannel.apply(this, arguments);\n wrapDcSend(dataChannel, this);\n return dataChannel;\n };\n utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n wrapDcSend(e.channel, e.target);\n return e;\n });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n if (!window.RTCPeerConnection ||\n 'connectionState' in window.RTCPeerConnection.prototype) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n Object.defineProperty(proto, 'connectionState', {\n get() {\n return {\n completed: 'connected',\n checking: 'connecting'\n }[this.iceConnectionState] || this.iceConnectionState;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(proto, 'onconnectionstatechange', {\n get() {\n return this._onconnectionstatechange || null;\n },\n set(cb) {\n if (this._onconnectionstatechange) {\n this.removeEventListener('connectionstatechange',\n this._onconnectionstatechange);\n delete this._onconnectionstatechange;\n }\n if (cb) {\n this.addEventListener('connectionstatechange',\n this._onconnectionstatechange = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n\n ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n const origMethod = proto[method];\n proto[method] = function() {\n if (!this._connectionstatechangepoly) {\n this._connectionstatechangepoly = e => {\n const pc = e.target;\n if (pc._lastConnectionState !== pc.connectionState) {\n pc._lastConnectionState = pc.connectionState;\n const newEvent = new Event('connectionstatechange', e);\n pc.dispatchEvent(newEvent);\n }\n return e;\n };\n this.addEventListener('iceconnectionstatechange',\n this._connectionstatechangepoly);\n }\n return origMethod.apply(this, arguments);\n };\n });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n if (!window.RTCPeerConnection) {\n return;\n }\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n return;\n }\n if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n return;\n }\n const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription(desc) {\n if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n const sdp = desc.sdp.split('\\n').filter((line) => {\n return line.trim() !== 'a=extmap-allow-mixed';\n }).join('\\n');\n // Safari enforces read-only-ness of RTCSessionDescription fields.\n if (window.RTCSessionDescription &&\n desc instanceof window.RTCSessionDescription) {\n arguments[0] = new window.RTCSessionDescription({\n type: desc.type,\n sdp,\n });\n } else {\n desc.sdp = sdp;\n }\n }\n return nativeSRD.apply(this, arguments);\n };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n // Support for addIceCandidate(null or undefined)\n // as well as addIceCandidate({candidate: \"\", ...})\n // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n // Note: must be called before other polyfills which change the signature.\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeAddIceCandidate =\n window.RTCPeerConnection.prototype.addIceCandidate;\n if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.addIceCandidate =\n function addIceCandidate() {\n if (!arguments[0]) {\n if (arguments[1]) {\n arguments[1].apply(null);\n }\n return Promise.resolve();\n }\n // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n // in older versions.\n // Native support for ignoring exists for Chrome M77+.\n // Safari ignores as well, exact version unknown but works in the same\n // version that also ignores addIceCandidate(null).\n if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n || (browserDetails.browser === 'firefox'\n && browserDetails.version < 68)\n || (browserDetails.browser === 'safari'))\n && arguments[0] && arguments[0].candidate === '') {\n return Promise.resolve();\n }\n return nativeAddIceCandidate.apply(this, arguments);\n };\n}\n\n// Note: Make sure to call this ahead of APIs that modify\n// setLocalDescription.length\nexport function shimParameterlessSetLocalDescription(window, browserDetails) {\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n let desc = arguments[0] || {};\n if (typeof desc !== 'object' || (desc.type && desc.sdp)) {\n return nativeSetLocalDescription.apply(this, arguments);\n }\n // The remaining steps should technically happen when SLD comes off the\n // RTCPeerConnection's operations chain (not ahead of going on it), but\n // this is too difficult to shim. Instead, this shim only covers the\n // common case where the operations chain is empty. This is imperfect, but\n // should cover many cases. Rationale: Even if we can't reduce the glare\n // window to zero on imperfect implementations, there's value in tapping\n // into the perfect negotiation pattern that several browsers support.\n desc = {type: desc.type, sdp: desc.sdp};\n if (!desc.type) {\n switch (this.signalingState) {\n case 'stable':\n case 'have-local-offer':\n case 'have-remote-pranswer':\n desc.type = 'offer';\n break;\n default:\n desc.type = 'answer';\n break;\n }\n }\n if (desc.sdp || (desc.type !== 'offer' && desc.type !== 'answer')) {\n return nativeSetLocalDescription.apply(this, [desc]);\n }\n const func = desc.type === 'offer' ? this.createOffer : this.createAnswer;\n return func.apply(this)\n .then(d => nativeSetLocalDescription.apply(this, [d]));\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\nimport * as utils from './utils';\n\n// Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\nimport * as sdp from 'sdp';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n shimChrome: true,\n shimFirefox: true,\n shimSafari: true,\n}) {\n // Utils.\n const logging = utils.log;\n const browserDetails = utils.detectBrowser(window);\n\n const adapter = {\n browserDetails,\n commonShim,\n extractVersion: utils.extractVersion,\n disableLog: utils.disableLog,\n disableWarnings: utils.disableWarnings,\n // Expose sdp as a convenience. For production apps include directly.\n sdp,\n };\n\n // Shim browser if found.\n switch (browserDetails.browser) {\n case 'chrome':\n if (!chromeShim || !chromeShim.shimPeerConnection ||\n !options.shimChrome) {\n logging('Chrome shim is not included in this adapter release.');\n return adapter;\n }\n if (browserDetails.version === null) {\n logging('Chrome shim can not determine version, not shimming.');\n return adapter;\n }\n logging('adapter.js shimming chrome.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = chromeShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n chromeShim.shimGetUserMedia(window, browserDetails);\n chromeShim.shimMediaStream(window, browserDetails);\n chromeShim.shimPeerConnection(window, browserDetails);\n chromeShim.shimOnTrack(window, browserDetails);\n chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n chromeShim.shimGetStats(window, browserDetails);\n chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n case 'firefox':\n if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n !options.shimFirefox) {\n logging('Firefox shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming firefox.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = firefoxShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n firefoxShim.shimGetUserMedia(window, browserDetails);\n firefoxShim.shimPeerConnection(window, browserDetails);\n firefoxShim.shimOnTrack(window, browserDetails);\n firefoxShim.shimRemoveStream(window, browserDetails);\n firefoxShim.shimSenderGetStats(window, browserDetails);\n firefoxShim.shimReceiverGetStats(window, browserDetails);\n firefoxShim.shimRTCDataChannel(window, browserDetails);\n firefoxShim.shimAddTransceiver(window, browserDetails);\n firefoxShim.shimGetParameters(window, browserDetails);\n firefoxShim.shimCreateOffer(window, browserDetails);\n firefoxShim.shimCreateAnswer(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'safari':\n if (!safariShim || !options.shimSafari) {\n logging('Safari shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming safari.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = safariShim;\n\n // Must be called before shimCallbackAPI.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n commonShim.shimParameterlessSetLocalDescription(window, browserDetails);\n\n safariShim.shimRTCIceServerUrls(window, browserDetails);\n safariShim.shimCreateOfferLegacy(window, browserDetails);\n safariShim.shimCallbacksAPI(window, browserDetails);\n safariShim.shimLocalStreamsAPI(window, browserDetails);\n safariShim.shimRemoteStreamsAPI(window, browserDetails);\n safariShim.shimTrackEventTransceiver(window, browserDetails);\n safariShim.shimGetUserMedia(window, browserDetails);\n safariShim.shimAudioContext(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n default:\n logging('Unsupported browser!');\n break;\n }\n\n return adapter;\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n/**\n * GStreamer WebRTC configuration.\n *

The config can be passed on creation of the GstWebRTCAPI class.

\n *

For example:\n *

\n *     const signalingProtocol = window.location.protocol.startsWith(\"https\") ? \"wss\" : \"ws\";\n *     const api = new GstWebRTCAPI({\n *         meta: { name: `WebClient-${Date.now()}` },\n *         signalingServerUrl: `${signalingProtocol}://${window.location.host}/webrtc`\n *     });\n * 

\n * @typedef {object} GstWebRTCConfig\n * @property {object} meta=null - Client free-form information that will be exchanged with all peers through the\n * signaling meta property, its content depends on your application.\n * @property {string} signalingServerUrl=ws://127.0.0.1:8443 - The WebRTC signaling server URL.\n * @property {number} reconnectionTimeout=2500 - Timeout in milliseconds to reconnect to the signaling server in\n * case of an unexpected disconnection.\n * @property {object} webrtcConfig={iceServers...} - The WebRTC peer connection configuration passed to\n * {@link RTCPeerConnection}. Default configuration only includes a list of free STUN servers\n * (stun[0-4].l.google.com:19302).\n */\n\n/**\n * Default GstWebRTCAPI configuration.\n * @type {GstWebRTCConfig}\n */\nconst defaultConfig = Object.freeze({\n meta: null,\n signalingServerUrl: \"ws://127.0.0.1:8443\",\n reconnectionTimeout: 2500,\n webrtcConfig: {\n iceServers: [\n {\n urls: [\n \"stun:stun.l.google.com:19302\",\n \"stun:stun1.l.google.com:19302\"\n ]\n }\n ],\n bundlePolicy: \"max-bundle\"\n }\n});\n\nexport default defaultConfig;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n/**\n * Session states enumeration.
\n * Session state always increases from idle to closed and never switches backwards.\n * @enum {number}\n * @readonly\n */\nconst SessionState = /** @type {const} */ {\n /**\n * (0) Default state when creating a new session, goes to connecting when starting the session.\n */\n idle: 0,\n /**\n * (1) Session is trying to connect to remote peers, goes to streaming in case of\n * success or closed in case of error.\n */\n connecting: 1,\n /**\n * (2) Session is correctly connected to remote peers and currently streaming audio/video, goes\n * to closed when any peer closes the session.\n */\n streaming: 2,\n /**\n * (3) Session is closed and can be garbage collected, state will not change anymore.\n */\n closed: 3\n};\nObject.freeze(SessionState);\n\nexport default SessionState;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport SessionState from \"./session-state.js\";\n\n/** @import ProducerSession from \"./producer-session.js\"; */\n/** @import ClientSession from \"./producer-session.js\"; */\n\n/**\n * Event name: \"error\".
\n * Triggered when any kind of error occurs.\n *

When emitted by a session, it is in general an unrecoverable error. Normally, the session is automatically closed\n * but in the specific case of a {@link ProducerSession}, when the error occurs on an underlying\n * {@link ClientSession} between the producer session and a remote client consuming the streamed media,\n * then only the failing {@link ClientSession} is closed. The producer session can keep on serving the\n * other consumer peers.

\n * @event GstWebRTCAPI#ErrorEvent\n * @type {ErrorEvent}\n * @property {string} message - The error message.\n * @property {Error} error - The error exception.\n * @see WebRTCSession\n * @see RemoteController\n */\n/**\n * Event name: \"stateChanged\".
\n * Triggered each time a session state changes.\n * @event GstWebRTCAPI#StateChangedEvent\n * @type {Event}\n * @see WebRTCSession#state\n */\n/**\n * Event name: \"rtcPeerConnectionChanged\".
\n * Triggered each time a session internal {@link RTCPeerConnection} changes. This can occur during the session\n * connecting state when the peer-to-peer WebRTC connection is established, and when closing the\n * {@link WebRTCSession}.\n * @event GstWebRTCAPI#RTCPeerConnectionChangedEvent\n * @type {Event}\n * @see WebRTCSession#rtcPeerConnection\n */\n/**\n * Event name: \"closed\".
\n * Triggered when a session is definitively closed (then it can be garbage collected as session instances are not\n * reusable).\n * @event GstWebRTCAPI#ClosedEvent\n * @type {Event}\n */\n\n/**\n * @class WebRTCSession\n * @hideconstructor\n * @classdesc Manages a WebRTC session between a producer and a consumer (peer-to-peer channel).\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:StateChangedEvent}\n * @fires {@link GstWebRTCAPI#event:RTCPeerConnectionChangedEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @see ConsumerSession\n * @see ProducerSession\n * @see ClientSession\n */\nclass WebRTCSession extends EventTarget {\n constructor(peerId, comChannel) {\n super();\n\n this._peerId = peerId;\n this._sessionId = \"\";\n this._comChannel = comChannel;\n this._state = SessionState.idle;\n this._rtcPeerConnection = null;\n }\n\n /**\n * Unique identifier of the remote peer to which this session is connected.\n * @type {string}\n * @readonly\n */\n get peerId() {\n return this._peerId;\n }\n\n /**\n * Unique identifier of this session (defined by the signaling server).
\n * The local session ID equals \"\" until it is created on server side. This is done during the connection handshake.\n * The local session ID is guaranteed to be valid and to correctly reflect the signaling server value once\n * session state has switched to {@link SessionState#streaming}.\n * @type {string}\n * @readonly\n */\n get sessionId() {\n return this._sessionId;\n }\n\n /**\n * The current WebRTC session state.\n * @type {SessionState}\n * @readonly\n */\n get state() {\n return this._state;\n }\n\n /**\n * The internal {@link RTCPeerConnection} used to manage the underlying WebRTC connection with session\n * peer. Value may be null if session has no active WebRTC connection. You can listen to the\n * {@link GstWebRTCAPI#event:RTCPeerConnectionChangedEvent} event to be informed when the connection is established\n * or destroyed.\n * @type {RTCPeerConnection}\n * @readonly\n */\n get rtcPeerConnection() {\n return this._rtcPeerConnection;\n }\n\n /**\n * Terminates the WebRTC session.
\n * It immediately disconnects the remote peer attached to this session and unregisters the session from the\n * signaling server.\n */\n close() {\n if (this._state !== SessionState.closed) {\n if ((this._state !== SessionState.idle) && this._comChannel && this._sessionId) {\n this._comChannel.send({\n type: \"endSession\",\n sessionId: this._sessionId\n });\n }\n\n this._state = SessionState.closed;\n this.dispatchEvent(new Event(\"stateChanged\"));\n\n this._comChannel = null;\n\n if (this._rtcPeerConnection) {\n this._rtcPeerConnection.close();\n this._rtcPeerConnection = null;\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n }\n\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n}\n\nexport default WebRTCSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\n// mapping is based on https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt\nconst uniToKeySyms = Object.freeze({\n 0x0020: \"space\",\n 0x0021: \"exclam\",\n 0x0022: \"quotedbl\",\n 0x0023: \"numbersign\",\n 0x0024: \"dollar\",\n 0x0025: \"percent\",\n 0x0026: \"ampersand\",\n 0x0027: \"apostrophe\",\n 0x0028: \"parenleft\",\n 0x0029: \"parenright\",\n 0x002a: \"asterisk\",\n 0x002b: \"plus\",\n 0x002c: \"comma\",\n 0x002d: \"minus\",\n 0x002e: \"period\",\n 0x002f: \"slash\",\n 0x0030: \"0\",\n 0x0031: \"1\",\n 0x0032: \"2\",\n 0x0033: \"3\",\n 0x0034: \"4\",\n 0x0035: \"5\",\n 0x0036: \"6\",\n 0x0037: \"7\",\n 0x0038: \"8\",\n 0x0039: \"9\",\n 0x003a: \"colon\",\n 0x003b: \"semicolon\",\n 0x003c: \"less\",\n 0x003d: \"equal\",\n 0x003e: \"greater\",\n 0x003f: \"question\",\n 0x0040: \"at\",\n 0x0041: \"A\",\n 0x0042: \"B\",\n 0x0043: \"C\",\n 0x0044: \"D\",\n 0x0045: \"E\",\n 0x0046: \"F\",\n 0x0047: \"G\",\n 0x0048: \"H\",\n 0x0049: \"I\",\n 0x004a: \"J\",\n 0x004b: \"K\",\n 0x004c: \"L\",\n 0x004d: \"M\",\n 0x004e: \"N\",\n 0x004f: \"O\",\n 0x0050: \"P\",\n 0x0051: \"Q\",\n 0x0052: \"R\",\n 0x0053: \"S\",\n 0x0054: \"T\",\n 0x0055: \"U\",\n 0x0056: \"V\",\n 0x0057: \"W\",\n 0x0058: \"X\",\n 0x0059: \"Y\",\n 0x005a: \"Z\",\n 0x005b: \"bracketleft\",\n 0x005c: \"backslash\",\n 0x005d: \"bracketright\",\n 0x005e: \"asciicircum\",\n 0x005f: \"underscore\",\n 0x0060: \"grave\",\n 0x0061: \"a\",\n 0x0062: \"b\",\n 0x0063: \"c\",\n 0x0064: \"d\",\n 0x0065: \"e\",\n 0x0066: \"f\",\n 0x0067: \"g\",\n 0x0068: \"h\",\n 0x0069: \"i\",\n 0x006a: \"j\",\n 0x006b: \"k\",\n 0x006c: \"l\",\n 0x006d: \"m\",\n 0x006e: \"n\",\n 0x006f: \"o\",\n 0x0070: \"p\",\n 0x0071: \"q\",\n 0x0072: \"r\",\n 0x0073: \"s\",\n 0x0074: \"t\",\n 0x0075: \"u\",\n 0x0076: \"v\",\n 0x0077: \"w\",\n 0x0078: \"x\",\n 0x0079: \"y\",\n 0x007a: \"z\",\n 0x007b: \"braceleft\",\n 0x007c: \"bar\",\n 0x007d: \"braceright\",\n 0x007e: \"asciitilde\",\n 0x00a0: \"nobreakspace\",\n 0x00a1: \"exclamdown\",\n 0x00a2: \"cent\",\n 0x00a3: \"sterling\",\n 0x00a4: \"currency\",\n 0x00a5: \"yen\",\n 0x00a6: \"brokenbar\",\n 0x00a7: \"section\",\n 0x00a8: \"diaeresis\",\n 0x00a9: \"copyright\",\n 0x00aa: \"ordfeminine\",\n 0x00ab: \"guillemotleft\",\n 0x00ac: \"notsign\",\n 0x00ad: \"hyphen\",\n 0x00ae: \"registered\",\n 0x00af: \"macron\",\n 0x00b0: \"degree\",\n 0x00b1: \"plusminus\",\n 0x00b2: \"twosuperior\",\n 0x00b3: \"threesuperior\",\n 0x00b4: \"acute\",\n 0x00b5: \"mu\",\n 0x00b6: \"paragraph\",\n 0x00b7: \"periodcentered\",\n 0x00b8: \"cedilla\",\n 0x00b9: \"onesuperior\",\n 0x00ba: \"masculine\",\n 0x00bb: \"guillemotright\",\n 0x00bc: \"onequarter\",\n 0x00bd: \"onehalf\",\n 0x00be: \"threequarters\",\n 0x00bf: \"questiondown\",\n 0x00c0: \"Agrave\",\n 0x00c1: \"Aacute\",\n 0x00c2: \"Acircumflex\",\n 0x00c3: \"Atilde\",\n 0x00c4: \"Adiaeresis\",\n 0x00c5: \"Aring\",\n 0x00c6: \"AE\",\n 0x00c7: \"Ccedilla\",\n 0x00c8: \"Egrave\",\n 0x00c9: \"Eacute\",\n 0x00ca: \"Ecircumflex\",\n 0x00cb: \"Ediaeresis\",\n 0x00cc: \"Igrave\",\n 0x00cd: \"Iacute\",\n 0x00ce: \"Icircumflex\",\n 0x00cf: \"Idiaeresis\",\n 0x00d0: \"ETH\",\n 0x00d1: \"Ntilde\",\n 0x00d2: \"Ograve\",\n 0x00d3: \"Oacute\",\n 0x00d4: \"Ocircumflex\",\n 0x00d5: \"Otilde\",\n 0x00d6: \"Odiaeresis\",\n 0x00d7: \"multiply\",\n 0x00d8: \"Ooblique\",\n 0x00d9: \"Ugrave\",\n 0x00da: \"Uacute\",\n 0x00db: \"Ucircumflex\",\n 0x00dc: \"Udiaeresis\",\n 0x00dd: \"Yacute\",\n 0x00de: \"THORN\",\n 0x00df: \"ssharp\",\n 0x00e0: \"agrave\",\n 0x00e1: \"aacute\",\n 0x00e2: \"acircumflex\",\n 0x00e3: \"atilde\",\n 0x00e4: \"adiaeresis\",\n 0x00e5: \"aring\",\n 0x00e6: \"ae\",\n 0x00e7: \"ccedilla\",\n 0x00e8: \"egrave\",\n 0x00e9: \"eacute\",\n 0x00ea: \"ecircumflex\",\n 0x00eb: \"ediaeresis\",\n 0x00ec: \"igrave\",\n 0x00ed: \"iacute\",\n 0x00ee: \"icircumflex\",\n 0x00ef: \"idiaeresis\",\n 0x00f0: \"eth\",\n 0x00f1: \"ntilde\",\n 0x00f2: \"ograve\",\n 0x00f3: \"oacute\",\n 0x00f4: \"ocircumflex\",\n 0x00f5: \"otilde\",\n 0x00f6: \"odiaeresis\",\n 0x00f7: \"division\",\n 0x00f8: \"oslash\",\n 0x00f9: \"ugrave\",\n 0x00fa: \"uacute\",\n 0x00fb: \"ucircumflex\",\n 0x00fc: \"udiaeresis\",\n 0x00fd: \"yacute\",\n 0x00fe: \"thorn\",\n 0x00ff: \"ydiaeresis\",\n 0x0104: \"Aogonek\",\n 0x02d8: \"breve\",\n 0x0141: \"Lstroke\",\n 0x013d: \"Lcaron\",\n 0x015a: \"Sacute\",\n 0x0160: \"Scaron\",\n 0x015e: \"Scedilla\",\n 0x0164: \"Tcaron\",\n 0x0179: \"Zacute\",\n 0x017d: \"Zcaron\",\n 0x017b: \"Zabovedot\",\n 0x0105: \"aogonek\",\n 0x02db: \"ogonek\",\n 0x0142: \"lstroke\",\n 0x013e: \"lcaron\",\n 0x015b: \"sacute\",\n 0x02c7: \"caron\",\n 0x0161: \"scaron\",\n 0x015f: \"scedilla\",\n 0x0165: \"tcaron\",\n 0x017a: \"zacute\",\n 0x02dd: \"doubleacute\",\n 0x017e: \"zcaron\",\n 0x017c: \"zabovedot\",\n 0x0154: \"Racute\",\n 0x0102: \"Abreve\",\n 0x0139: \"Lacute\",\n 0x0106: \"Cacute\",\n 0x010c: \"Ccaron\",\n 0x0118: \"Eogonek\",\n 0x011a: \"Ecaron\",\n 0x010e: \"Dcaron\",\n 0x0110: \"Dstroke\",\n 0x0143: \"Nacute\",\n 0x0147: \"Ncaron\",\n 0x0150: \"Odoubleacute\",\n 0x0158: \"Rcaron\",\n 0x016e: \"Uring\",\n 0x0170: \"Udoubleacute\",\n 0x0162: \"Tcedilla\",\n 0x0155: \"racute\",\n 0x0103: \"abreve\",\n 0x013a: \"lacute\",\n 0x0107: \"cacute\",\n 0x010d: \"ccaron\",\n 0x0119: \"eogonek\",\n 0x011b: \"ecaron\",\n 0x010f: \"dcaron\",\n 0x0111: \"dstroke\",\n 0x0144: \"nacute\",\n 0x0148: \"ncaron\",\n 0x0151: \"odoubleacute\",\n 0x0159: \"rcaron\",\n 0x016f: \"uring\",\n 0x0171: \"udoubleacute\",\n 0x0163: \"tcedilla\",\n 0x02d9: \"abovedot\",\n 0x0126: \"Hstroke\",\n 0x0124: \"Hcircumflex\",\n 0x0130: \"Iabovedot\",\n 0x011e: \"Gbreve\",\n 0x0134: \"Jcircumflex\",\n 0x0127: \"hstroke\",\n 0x0125: \"hcircumflex\",\n 0x0131: \"idotless\",\n 0x011f: \"gbreve\",\n 0x0135: \"jcircumflex\",\n 0x010a: \"Cabovedot\",\n 0x0108: \"Ccircumflex\",\n 0x0120: \"Gabovedot\",\n 0x011c: \"Gcircumflex\",\n 0x016c: \"Ubreve\",\n 0x015c: \"Scircumflex\",\n 0x010b: \"cabovedot\",\n 0x0109: \"ccircumflex\",\n 0x0121: \"gabovedot\",\n 0x011d: \"gcircumflex\",\n 0x016d: \"ubreve\",\n 0x015d: \"scircumflex\",\n 0x0138: \"kra\",\n 0x0156: \"Rcedilla\",\n 0x0128: \"Itilde\",\n 0x013b: \"Lcedilla\",\n 0x0112: \"Emacron\",\n 0x0122: \"Gcedilla\",\n 0x0166: \"Tslash\",\n 0x0157: \"rcedilla\",\n 0x0129: \"itilde\",\n 0x013c: \"lcedilla\",\n 0x0113: \"emacron\",\n 0x0123: \"gcedilla\",\n 0x0167: \"tslash\",\n 0x014a: \"ENG\",\n 0x014b: \"eng\",\n 0x0100: \"Amacron\",\n 0x012e: \"Iogonek\",\n 0x0116: \"Eabovedot\",\n 0x012a: \"Imacron\",\n 0x0145: \"Ncedilla\",\n 0x014c: \"Omacron\",\n 0x0136: \"Kcedilla\",\n 0x0172: \"Uogonek\",\n 0x0168: \"Utilde\",\n 0x016a: \"Umacron\",\n 0x0101: \"amacron\",\n 0x012f: \"iogonek\",\n 0x0117: \"eabovedot\",\n 0x012b: \"imacron\",\n 0x0146: \"ncedilla\",\n 0x014d: \"omacron\",\n 0x0137: \"kcedilla\",\n 0x0173: \"uogonek\",\n 0x0169: \"utilde\",\n 0x016b: \"umacron\",\n 0x203e: \"overline\",\n 0x3002: \"kana_fullstop\",\n 0x300c: \"kana_openingbracket\",\n 0x300d: \"kana_closingbracket\",\n 0x3001: \"kana_comma\",\n 0x30fb: \"kana_conjunctive\",\n 0x30f2: \"kana_WO\",\n 0x30a1: \"kana_a\",\n 0x30a3: \"kana_i\",\n 0x30a5: \"kana_u\",\n 0x30a7: \"kana_e\",\n 0x30a9: \"kana_o\",\n 0x30e3: \"kana_ya\",\n 0x30e5: \"kana_yu\",\n 0x30e7: \"kana_yo\",\n 0x30c3: \"kana_tsu\",\n 0x30fc: \"prolongedsound\",\n 0x30a2: \"kana_A\",\n 0x30a4: \"kana_I\",\n 0x30a6: \"kana_U\",\n 0x30a8: \"kana_E\",\n 0x30aa: \"kana_O\",\n 0x30ab: \"kana_KA\",\n 0x30ad: \"kana_KI\",\n 0x30af: \"kana_KU\",\n 0x30b1: \"kana_KE\",\n 0x30b3: \"kana_KO\",\n 0x30b5: \"kana_SA\",\n 0x30b7: \"kana_SHI\",\n 0x30b9: \"kana_SU\",\n 0x30bb: \"kana_SE\",\n 0x30bd: \"kana_SO\",\n 0x30bf: \"kana_TA\",\n 0x30c1: \"kana_CHI\",\n 0x30c4: \"kana_TSU\",\n 0x30c6: \"kana_TE\",\n 0x30c8: \"kana_TO\",\n 0x30ca: \"kana_NA\",\n 0x30cb: \"kana_NI\",\n 0x30cc: \"kana_NU\",\n 0x30cd: \"kana_NE\",\n 0x30ce: \"kana_NO\",\n 0x30cf: \"kana_HA\",\n 0x30d2: \"kana_HI\",\n 0x30d5: \"kana_FU\",\n 0x30d8: \"kana_HE\",\n 0x30db: \"kana_HO\",\n 0x30de: \"kana_MA\",\n 0x30df: \"kana_MI\",\n 0x30e0: \"kana_MU\",\n 0x30e1: \"kana_ME\",\n 0x30e2: \"kana_MO\",\n 0x30e4: \"kana_YA\",\n 0x30e6: \"kana_YU\",\n 0x30e8: \"kana_YO\",\n 0x30e9: \"kana_RA\",\n 0x30ea: \"kana_RI\",\n 0x30eb: \"kana_RU\",\n 0x30ec: \"kana_RE\",\n 0x30ed: \"kana_RO\",\n 0x30ef: \"kana_WA\",\n 0x30f3: \"kana_N\",\n 0x309b: \"voicedsound\",\n 0x309c: \"semivoicedsound\",\n 0x060c: \"Arabic_comma\",\n 0x061b: \"Arabic_semicolon\",\n 0x061f: \"Arabic_question_mark\",\n 0x0621: \"Arabic_hamza\",\n 0x0622: \"Arabic_maddaonalef\",\n 0x0623: \"Arabic_hamzaonalef\",\n 0x0624: \"Arabic_hamzaonwaw\",\n 0x0625: \"Arabic_hamzaunderalef\",\n 0x0626: \"Arabic_hamzaonyeh\",\n 0x0627: \"Arabic_alef\",\n 0x0628: \"Arabic_beh\",\n 0x0629: \"Arabic_tehmarbuta\",\n 0x062a: \"Arabic_teh\",\n 0x062b: \"Arabic_theh\",\n 0x062c: \"Arabic_jeem\",\n 0x062d: \"Arabic_hah\",\n 0x062e: \"Arabic_khah\",\n 0x062f: \"Arabic_dal\",\n 0x0630: \"Arabic_thal\",\n 0x0631: \"Arabic_ra\",\n 0x0632: \"Arabic_zain\",\n 0x0633: \"Arabic_seen\",\n 0x0634: \"Arabic_sheen\",\n 0x0635: \"Arabic_sad\",\n 0x0636: \"Arabic_dad\",\n 0x0637: \"Arabic_tah\",\n 0x0638: \"Arabic_zah\",\n 0x0639: \"Arabic_ain\",\n 0x063a: \"Arabic_ghain\",\n 0x0640: \"Arabic_tatweel\",\n 0x0641: \"Arabic_feh\",\n 0x0642: \"Arabic_qaf\",\n 0x0643: \"Arabic_kaf\",\n 0x0644: \"Arabic_lam\",\n 0x0645: \"Arabic_meem\",\n 0x0646: \"Arabic_noon\",\n 0x0647: \"Arabic_ha\",\n 0x0648: \"Arabic_waw\",\n 0x0649: \"Arabic_alefmaksura\",\n 0x064a: \"Arabic_yeh\",\n 0x064b: \"Arabic_fathatan\",\n 0x064c: \"Arabic_dammatan\",\n 0x064d: \"Arabic_kasratan\",\n 0x064e: \"Arabic_fatha\",\n 0x064f: \"Arabic_damma\",\n 0x0650: \"Arabic_kasra\",\n 0x0651: \"Arabic_shadda\",\n 0x0652: \"Arabic_sukun\",\n 0x0452: \"Serbian_dje\",\n 0x0453: \"Macedonia_gje\",\n 0x0451: \"Cyrillic_io\",\n 0x0454: \"Ukrainian_ie\",\n 0x0455: \"Macedonia_dse\",\n 0x0456: \"Ukrainian_i\",\n 0x0457: \"Ukrainian_yi\",\n 0x0458: \"Cyrillic_je\",\n 0x0459: \"Cyrillic_lje\",\n 0x045a: \"Cyrillic_nje\",\n 0x045b: \"Serbian_tshe\",\n 0x045c: \"Macedonia_kje\",\n 0x045e: \"Byelorussian_shortu\",\n 0x045f: \"Cyrillic_dzhe\",\n 0x2116: \"numerosign\",\n 0x0402: \"Serbian_DJE\",\n 0x0403: \"Macedonia_GJE\",\n 0x0401: \"Cyrillic_IO\",\n 0x0404: \"Ukrainian_IE\",\n 0x0405: \"Macedonia_DSE\",\n 0x0406: \"Ukrainian_I\",\n 0x0407: \"Ukrainian_YI\",\n 0x0408: \"Cyrillic_JE\",\n 0x0409: \"Cyrillic_LJE\",\n 0x040a: \"Cyrillic_NJE\",\n 0x040b: \"Serbian_TSHE\",\n 0x040c: \"Macedonia_KJE\",\n 0x040e: \"Byelorussian_SHORTU\",\n 0x040f: \"Cyrillic_DZHE\",\n 0x044e: \"Cyrillic_yu\",\n 0x0430: \"Cyrillic_a\",\n 0x0431: \"Cyrillic_be\",\n 0x0446: \"Cyrillic_tse\",\n 0x0434: \"Cyrillic_de\",\n 0x0435: \"Cyrillic_ie\",\n 0x0444: \"Cyrillic_ef\",\n 0x0433: \"Cyrillic_ghe\",\n 0x0445: \"Cyrillic_ha\",\n 0x0438: \"Cyrillic_i\",\n 0x0439: \"Cyrillic_shorti\",\n 0x043a: \"Cyrillic_ka\",\n 0x043b: \"Cyrillic_el\",\n 0x043c: \"Cyrillic_em\",\n 0x043d: \"Cyrillic_en\",\n 0x043e: \"Cyrillic_o\",\n 0x043f: \"Cyrillic_pe\",\n 0x044f: \"Cyrillic_ya\",\n 0x0440: \"Cyrillic_er\",\n 0x0441: \"Cyrillic_es\",\n 0x0442: \"Cyrillic_te\",\n 0x0443: \"Cyrillic_u\",\n 0x0436: \"Cyrillic_zhe\",\n 0x0432: \"Cyrillic_ve\",\n 0x044c: \"Cyrillic_softsign\",\n 0x044b: \"Cyrillic_yeru\",\n 0x0437: \"Cyrillic_ze\",\n 0x0448: \"Cyrillic_sha\",\n 0x044d: \"Cyrillic_e\",\n 0x0449: \"Cyrillic_shcha\",\n 0x0447: \"Cyrillic_che\",\n 0x044a: \"Cyrillic_hardsign\",\n 0x042e: \"Cyrillic_YU\",\n 0x0410: \"Cyrillic_A\",\n 0x0411: \"Cyrillic_BE\",\n 0x0426: \"Cyrillic_TSE\",\n 0x0414: \"Cyrillic_DE\",\n 0x0415: \"Cyrillic_IE\",\n 0x0424: \"Cyrillic_EF\",\n 0x0413: \"Cyrillic_GHE\",\n 0x0425: \"Cyrillic_HA\",\n 0x0418: \"Cyrillic_I\",\n 0x0419: \"Cyrillic_SHORTI\",\n 0x041a: \"Cyrillic_KA\",\n 0x041b: \"Cyrillic_EL\",\n 0x041c: \"Cyrillic_EM\",\n 0x041d: \"Cyrillic_EN\",\n 0x041e: \"Cyrillic_O\",\n 0x041f: \"Cyrillic_PE\",\n 0x042f: \"Cyrillic_YA\",\n 0x0420: \"Cyrillic_ER\",\n 0x0421: \"Cyrillic_ES\",\n 0x0422: \"Cyrillic_TE\",\n 0x0423: \"Cyrillic_U\",\n 0x0416: \"Cyrillic_ZHE\",\n 0x0412: \"Cyrillic_VE\",\n 0x042c: \"Cyrillic_SOFTSIGN\",\n 0x042b: \"Cyrillic_YERU\",\n 0x0417: \"Cyrillic_ZE\",\n 0x0428: \"Cyrillic_SHA\",\n 0x042d: \"Cyrillic_E\",\n 0x0429: \"Cyrillic_SHCHA\",\n 0x0427: \"Cyrillic_CHE\",\n 0x042a: \"Cyrillic_HARDSIGN\",\n 0x0386: \"Greek_ALPHAaccent\",\n 0x0388: \"Greek_EPSILONaccent\",\n 0x0389: \"Greek_ETAaccent\",\n 0x038a: \"Greek_IOTAaccent\",\n 0x03aa: \"Greek_IOTAdiaeresis\",\n 0x038c: \"Greek_OMICRONaccent\",\n 0x038e: \"Greek_UPSILONaccent\",\n 0x03ab: \"Greek_UPSILONdieresis\",\n 0x038f: \"Greek_OMEGAaccent\",\n 0x0385: \"Greek_accentdieresis\",\n 0x2015: \"Greek_horizbar\",\n 0x03ac: \"Greek_alphaaccent\",\n 0x03ad: \"Greek_epsilonaccent\",\n 0x03ae: \"Greek_etaaccent\",\n 0x03af: \"Greek_iotaaccent\",\n 0x03ca: \"Greek_iotadieresis\",\n 0x0390: \"Greek_iotaaccentdieresis\",\n 0x03cc: \"Greek_omicronaccent\",\n 0x03cd: \"Greek_upsilonaccent\",\n 0x03cb: \"Greek_upsilondieresis\",\n 0x03b0: \"Greek_upsilonaccentdieresis\",\n 0x03ce: \"Greek_omegaaccent\",\n 0x0391: \"Greek_ALPHA\",\n 0x0392: \"Greek_BETA\",\n 0x0393: \"Greek_GAMMA\",\n 0x0394: \"Greek_DELTA\",\n 0x0395: \"Greek_EPSILON\",\n 0x0396: \"Greek_ZETA\",\n 0x0397: \"Greek_ETA\",\n 0x0398: \"Greek_THETA\",\n 0x0399: \"Greek_IOTA\",\n 0x039a: \"Greek_KAPPA\",\n 0x039b: \"Greek_LAMBDA\",\n 0x039c: \"Greek_MU\",\n 0x039d: \"Greek_NU\",\n 0x039e: \"Greek_XI\",\n 0x039f: \"Greek_OMICRON\",\n 0x03a0: \"Greek_PI\",\n 0x03a1: \"Greek_RHO\",\n 0x03a3: \"Greek_SIGMA\",\n 0x03a4: \"Greek_TAU\",\n 0x03a5: \"Greek_UPSILON\",\n 0x03a6: \"Greek_PHI\",\n 0x03a7: \"Greek_CHI\",\n 0x03a8: \"Greek_PSI\",\n 0x03a9: \"Greek_OMEGA\",\n 0x03b1: \"Greek_alpha\",\n 0x03b2: \"Greek_beta\",\n 0x03b3: \"Greek_gamma\",\n 0x03b4: \"Greek_delta\",\n 0x03b5: \"Greek_epsilon\",\n 0x03b6: \"Greek_zeta\",\n 0x03b7: \"Greek_eta\",\n 0x03b8: \"Greek_theta\",\n 0x03b9: \"Greek_iota\",\n 0x03ba: \"Greek_kappa\",\n 0x03bb: \"Greek_lambda\",\n 0x03bc: \"Greek_mu\",\n 0x03bd: \"Greek_nu\",\n 0x03be: \"Greek_xi\",\n 0x03bf: \"Greek_omicron\",\n 0x03c0: \"Greek_pi\",\n 0x03c1: \"Greek_rho\",\n 0x03c3: \"Greek_sigma\",\n 0x03c2: \"Greek_finalsmallsigma\",\n 0x03c4: \"Greek_tau\",\n 0x03c5: \"Greek_upsilon\",\n 0x03c6: \"Greek_phi\",\n 0x03c7: \"Greek_chi\",\n 0x03c8: \"Greek_psi\",\n 0x03c9: \"Greek_omega\",\n 0x23b7: \"leftradical\",\n 0x2320: \"topintegral\",\n 0x2321: \"botintegral\",\n 0x23a1: \"topleftsqbracket\",\n 0x23a3: \"botleftsqbracket\",\n 0x23a4: \"toprightsqbracket\",\n 0x23a6: \"botrightsqbracket\",\n 0x239b: \"topleftparens\",\n 0x239d: \"botleftparens\",\n 0x239e: \"toprightparens\",\n 0x23a0: \"botrightparens\",\n 0x23a8: \"leftmiddlecurlybrace\",\n 0x23ac: \"rightmiddlecurlybrace\",\n 0x2264: \"lessthanequal\",\n 0x2260: \"notequal\",\n 0x2265: \"greaterthanequal\",\n 0x222b: \"integral\",\n 0x2234: \"therefore\",\n 0x221d: \"variation\",\n 0x221e: \"infinity\",\n 0x2207: \"nabla\",\n 0x223c: \"approximate\",\n 0x2243: \"similarequal\",\n 0x21d4: \"ifonlyif\",\n 0x21d2: \"implies\",\n 0x2261: \"identical\",\n 0x221a: \"radical\",\n 0x2282: \"includedin\",\n 0x2283: \"includes\",\n 0x2229: \"intersection\",\n 0x222a: \"union\",\n 0x2227: \"logicaland\",\n 0x2228: \"logicalor\",\n 0x2202: \"partialderivative\",\n 0x0192: \"function\",\n 0x2190: \"leftarrow\",\n 0x2191: \"uparrow\",\n 0x2192: \"rightarrow\",\n 0x2193: \"downarrow\",\n 0x25c6: \"soliddiamond\",\n 0x2592: \"checkerboard\",\n 0x2409: \"ht\",\n 0x240c: \"ff\",\n 0x240d: \"cr\",\n 0x240a: \"lf\",\n 0x2424: \"nl\",\n 0x240b: \"vt\",\n 0x2518: \"lowrightcorner\",\n 0x2510: \"uprightcorner\",\n 0x250c: \"upleftcorner\",\n 0x2514: \"lowleftcorner\",\n 0x253c: \"crossinglines\",\n 0x23ba: \"horizlinescan1\",\n 0x23bb: \"horizlinescan3\",\n 0x2500: \"horizlinescan5\",\n 0x23bc: \"horizlinescan7\",\n 0x23bd: \"horizlinescan9\",\n 0x251c: \"leftt\",\n 0x2524: \"rightt\",\n 0x2534: \"bott\",\n 0x252c: \"topt\",\n 0x2502: \"vertbar\",\n 0x2003: \"emspace\",\n 0x2002: \"enspace\",\n 0x2004: \"em3space\",\n 0x2005: \"em4space\",\n 0x2007: \"digitspace\",\n 0x2008: \"punctspace\",\n 0x2009: \"thinspace\",\n 0x200a: \"hairspace\",\n 0x2014: \"emdash\",\n 0x2013: \"endash\",\n 0x2423: \"signifblank\",\n 0x2026: \"ellipsis\",\n 0x2025: \"doubbaselinedot\",\n 0x2153: \"onethird\",\n 0x2154: \"twothirds\",\n 0x2155: \"onefifth\",\n 0x2156: \"twofifths\",\n 0x2157: \"threefifths\",\n 0x2158: \"fourfifths\",\n 0x2159: \"onesixth\",\n 0x215a: \"fivesixths\",\n 0x2105: \"careof\",\n 0x2012: \"figdash\",\n 0x27e8: \"leftanglebracket\",\n 0x27e9: \"rightanglebracket\",\n 0x215b: \"oneeighth\",\n 0x215c: \"threeeighths\",\n 0x215d: \"fiveeighths\",\n 0x215e: \"seveneighths\",\n 0x2122: \"trademark\",\n 0x2613: \"signaturemark\",\n 0x25c1: \"leftopentriangle\",\n 0x25b7: \"rightopentriangle\",\n 0x25af: \"emopenrectangle\",\n 0x2018: \"leftsinglequotemark\",\n 0x2019: \"rightsinglequotemark\",\n 0x201c: \"leftdoublequotemark\",\n 0x201d: \"rightdoublequotemark\",\n 0x211e: \"prescription\",\n 0x2032: \"minutes\",\n 0x2033: \"seconds\",\n 0x271d: \"latincross\",\n 0x25ac: \"filledrectbullet\",\n 0x25c0: \"filledlefttribullet\",\n 0x25b6: \"filledrighttribullet\",\n 0x25cf: \"emfilledcircle\",\n 0x25ae: \"emfilledrect\",\n 0x25e6: \"enopencircbullet\",\n 0x25ab: \"enopensquarebullet\",\n 0x25ad: \"openrectbullet\",\n 0x25b3: \"opentribulletup\",\n 0x25bd: \"opentribulletdown\",\n 0x2606: \"openstar\",\n 0x2022: \"enfilledcircbullet\",\n 0x25aa: \"enfilledsqbullet\",\n 0x25b2: \"filledtribulletup\",\n 0x25bc: \"filledtribulletdown\",\n 0x261c: \"leftpointer\",\n 0x261e: \"rightpointer\",\n 0x2663: \"club\",\n 0x2666: \"diamond\",\n 0x2665: \"heart\",\n 0x2720: \"maltesecross\",\n 0x2020: \"dagger\",\n 0x2021: \"doubledagger\",\n 0x2713: \"checkmark\",\n 0x2717: \"ballotcross\",\n 0x266f: \"musicalsharp\",\n 0x266d: \"musicalflat\",\n 0x2642: \"malesymbol\",\n 0x2640: \"femalesymbol\",\n 0x260e: \"telephone\",\n 0x2315: \"telephonerecorder\",\n 0x2117: \"phonographcopyright\",\n 0x2038: \"caret\",\n 0x201a: \"singlelowquotemark\",\n 0x201e: \"doublelowquotemark\",\n 0x22a5: \"downtack\",\n 0x230a: \"downstile\",\n 0x2218: \"jot\",\n 0x2395: \"quad\",\n 0x22a4: \"uptack\",\n 0x25cb: \"circle\",\n 0x2308: \"upstile\",\n 0x22a2: \"lefttack\",\n 0x22a3: \"righttack\",\n 0x2017: \"hebrew_doublelowline\",\n 0x05d0: \"hebrew_aleph\",\n 0x05d1: \"hebrew_beth\",\n 0x05d2: \"hebrew_gimmel\",\n 0x05d3: \"hebrew_daleth\",\n 0x05d4: \"hebrew_he\",\n 0x05d5: \"hebrew_waw\",\n 0x05d6: \"hebrew_zayin\",\n 0x05d7: \"hebrew_het\",\n 0x05d8: \"hebrew_teth\",\n 0x05d9: \"hebrew_yod\",\n 0x05da: \"hebrew_finalkaph\",\n 0x05db: \"hebrew_kaph\",\n 0x05dc: \"hebrew_lamed\",\n 0x05dd: \"hebrew_finalmem\",\n 0x05de: \"hebrew_mem\",\n 0x05df: \"hebrew_finalnun\",\n 0x05e0: \"hebrew_nun\",\n 0x05e1: \"hebrew_samekh\",\n 0x05e2: \"hebrew_ayin\",\n 0x05e3: \"hebrew_finalpe\",\n 0x05e4: \"hebrew_pe\",\n 0x05e5: \"hebrew_finalzadi\",\n 0x05e6: \"hebrew_zadi\",\n 0x05e7: \"hebrew_qoph\",\n 0x05e8: \"hebrew_resh\",\n 0x05e9: \"hebrew_shin\",\n 0x05ea: \"hebrew_taw\",\n 0x0e01: \"Thai_kokai\",\n 0x0e02: \"Thai_khokhai\",\n 0x0e03: \"Thai_khokhuat\",\n 0x0e04: \"Thai_khokhwai\",\n 0x0e05: \"Thai_khokhon\",\n 0x0e06: \"Thai_khorakhang\",\n 0x0e07: \"Thai_ngongu\",\n 0x0e08: \"Thai_chochan\",\n 0x0e09: \"Thai_choching\",\n 0x0e0a: \"Thai_chochang\",\n 0x0e0b: \"Thai_soso\",\n 0x0e0c: \"Thai_chochoe\",\n 0x0e0d: \"Thai_yoying\",\n 0x0e0e: \"Thai_dochada\",\n 0x0e0f: \"Thai_topatak\",\n 0x0e10: \"Thai_thothan\",\n 0x0e11: \"Thai_thonangmontho\",\n 0x0e12: \"Thai_thophuthao\",\n 0x0e13: \"Thai_nonen\",\n 0x0e14: \"Thai_dodek\",\n 0x0e15: \"Thai_totao\",\n 0x0e16: \"Thai_thothung\",\n 0x0e17: \"Thai_thothahan\",\n 0x0e18: \"Thai_thothong\",\n 0x0e19: \"Thai_nonu\",\n 0x0e1a: \"Thai_bobaimai\",\n 0x0e1b: \"Thai_popla\",\n 0x0e1c: \"Thai_phophung\",\n 0x0e1d: \"Thai_fofa\",\n 0x0e1e: \"Thai_phophan\",\n 0x0e1f: \"Thai_fofan\",\n 0x0e20: \"Thai_phosamphao\",\n 0x0e21: \"Thai_moma\",\n 0x0e22: \"Thai_yoyak\",\n 0x0e23: \"Thai_rorua\",\n 0x0e24: \"Thai_ru\",\n 0x0e25: \"Thai_loling\",\n 0x0e26: \"Thai_lu\",\n 0x0e27: \"Thai_wowaen\",\n 0x0e28: \"Thai_sosala\",\n 0x0e29: \"Thai_sorusi\",\n 0x0e2a: \"Thai_sosua\",\n 0x0e2b: \"Thai_hohip\",\n 0x0e2c: \"Thai_lochula\",\n 0x0e2d: \"Thai_oang\",\n 0x0e2e: \"Thai_honokhuk\",\n 0x0e2f: \"Thai_paiyannoi\",\n 0x0e30: \"Thai_saraa\",\n 0x0e31: \"Thai_maihanakat\",\n 0x0e32: \"Thai_saraaa\",\n 0x0e33: \"Thai_saraam\",\n 0x0e34: \"Thai_sarai\",\n 0x0e35: \"Thai_saraii\",\n 0x0e36: \"Thai_saraue\",\n 0x0e37: \"Thai_sarauee\",\n 0x0e38: \"Thai_sarau\",\n 0x0e39: \"Thai_sarauu\",\n 0x0e3a: \"Thai_phinthu\",\n 0x0e3f: \"Thai_baht\",\n 0x0e40: \"Thai_sarae\",\n 0x0e41: \"Thai_saraae\",\n 0x0e42: \"Thai_sarao\",\n 0x0e43: \"Thai_saraaimaimuan\",\n 0x0e44: \"Thai_saraaimaimalai\",\n 0x0e45: \"Thai_lakkhangyao\",\n 0x0e46: \"Thai_maiyamok\",\n 0x0e47: \"Thai_maitaikhu\",\n 0x0e48: \"Thai_maiek\",\n 0x0e49: \"Thai_maitho\",\n 0x0e4a: \"Thai_maitri\",\n 0x0e4b: \"Thai_maichattawa\",\n 0x0e4c: \"Thai_thanthakhat\",\n 0x0e4d: \"Thai_nikhahit\",\n 0x0e50: \"Thai_leksun\",\n 0x0e51: \"Thai_leknung\",\n 0x0e52: \"Thai_leksong\",\n 0x0e53: \"Thai_leksam\",\n 0x0e54: \"Thai_leksi\",\n 0x0e55: \"Thai_lekha\",\n 0x0e56: \"Thai_lekhok\",\n 0x0e57: \"Thai_lekchet\",\n 0x0e58: \"Thai_lekpaet\",\n 0x0e59: \"Thai_lekkao\",\n 0x3131: \"Hangul_Kiyeog\",\n 0x3132: \"Hangul_SsangKiyeog\",\n 0x3133: \"Hangul_KiyeogSios\",\n 0x3134: \"Hangul_Nieun\",\n 0x3135: \"Hangul_NieunJieuj\",\n 0x3136: \"Hangul_NieunHieuh\",\n 0x3137: \"Hangul_Dikeud\",\n 0x3138: \"Hangul_SsangDikeud\",\n 0x3139: \"Hangul_Rieul\",\n 0x313a: \"Hangul_RieulKiyeog\",\n 0x313b: \"Hangul_RieulMieum\",\n 0x313c: \"Hangul_RieulPieub\",\n 0x313d: \"Hangul_RieulSios\",\n 0x313e: \"Hangul_RieulTieut\",\n 0x313f: \"Hangul_RieulPhieuf\",\n 0x3140: \"Hangul_RieulHieuh\",\n 0x3141: \"Hangul_Mieum\",\n 0x3142: \"Hangul_Pieub\",\n 0x3143: \"Hangul_SsangPieub\",\n 0x3144: \"Hangul_PieubSios\",\n 0x3145: \"Hangul_Sios\",\n 0x3146: \"Hangul_SsangSios\",\n 0x3147: \"Hangul_Ieung\",\n 0x3148: \"Hangul_Jieuj\",\n 0x3149: \"Hangul_SsangJieuj\",\n 0x314a: \"Hangul_Cieuc\",\n 0x314b: \"Hangul_Khieuq\",\n 0x314c: \"Hangul_Tieut\",\n 0x314d: \"Hangul_Phieuf\",\n 0x314e: \"Hangul_Hieuh\",\n 0x314f: \"Hangul_A\",\n 0x3150: \"Hangul_AE\",\n 0x3151: \"Hangul_YA\",\n 0x3152: \"Hangul_YAE\",\n 0x3153: \"Hangul_EO\",\n 0x3154: \"Hangul_E\",\n 0x3155: \"Hangul_YEO\",\n 0x3156: \"Hangul_YE\",\n 0x3157: \"Hangul_O\",\n 0x3158: \"Hangul_WA\",\n 0x3159: \"Hangul_WAE\",\n 0x315a: \"Hangul_OE\",\n 0x315b: \"Hangul_YO\",\n 0x315c: \"Hangul_U\",\n 0x315d: \"Hangul_WEO\",\n 0x315e: \"Hangul_WE\",\n 0x315f: \"Hangul_WI\",\n 0x3160: \"Hangul_YU\",\n 0x3161: \"Hangul_EU\",\n 0x3162: \"Hangul_YI\",\n 0x3163: \"Hangul_I\",\n 0x11a8: \"Hangul_J_Kiyeog\",\n 0x11a9: \"Hangul_J_SsangKiyeog\",\n 0x11aa: \"Hangul_J_KiyeogSios\",\n 0x11ab: \"Hangul_J_Nieun\",\n 0x11ac: \"Hangul_J_NieunJieuj\",\n 0x11ad: \"Hangul_J_NieunHieuh\",\n 0x11ae: \"Hangul_J_Dikeud\",\n 0x11af: \"Hangul_J_Rieul\",\n 0x11b0: \"Hangul_J_RieulKiyeog\",\n 0x11b1: \"Hangul_J_RieulMieum\",\n 0x11b2: \"Hangul_J_RieulPieub\",\n 0x11b3: \"Hangul_J_RieulSios\",\n 0x11b4: \"Hangul_J_RieulTieut\",\n 0x11b5: \"Hangul_J_RieulPhieuf\",\n 0x11b6: \"Hangul_J_RieulHieuh\",\n 0x11b7: \"Hangul_J_Mieum\",\n 0x11b8: \"Hangul_J_Pieub\",\n 0x11b9: \"Hangul_J_PieubSios\",\n 0x11ba: \"Hangul_J_Sios\",\n 0x11bb: \"Hangul_J_SsangSios\",\n 0x11bc: \"Hangul_J_Ieung\",\n 0x11bd: \"Hangul_J_Jieuj\",\n 0x11be: \"Hangul_J_Cieuc\",\n 0x11bf: \"Hangul_J_Khieuq\",\n 0x11c0: \"Hangul_J_Tieut\",\n 0x11c1: \"Hangul_J_Phieuf\",\n 0x11c2: \"Hangul_J_Hieuh\",\n 0x316d: \"Hangul_RieulYeorinHieuh\",\n 0x3171: \"Hangul_SunkyeongeumMieum\",\n 0x3178: \"Hangul_SunkyeongeumPieub\",\n 0x317f: \"Hangul_PanSios\",\n 0x3181: \"Hangul_KkogjiDalrinIeung\",\n 0x3184: \"Hangul_SunkyeongeumPhieuf\",\n 0x3186: \"Hangul_YeorinHieuh\",\n 0x318d: \"Hangul_AraeA\",\n 0x318e: \"Hangul_AraeAE\",\n 0x11eb: \"Hangul_J_PanSios\",\n 0x11f0: \"Hangul_J_KkogjiDalrinIeung\",\n 0x11f9: \"Hangul_J_YeorinHieuh\",\n 0x0152: \"OE\",\n 0x0153: \"oe\",\n 0x0178: \"Ydiaeresis\",\n 0x20a0: \"EcuSign\",\n 0x20a1: \"ColonSign\",\n 0x20a2: \"CruzeiroSign\",\n 0x20a3: \"FFrancSign\",\n 0x20a4: \"LiraSign\",\n 0x20a5: \"MillSign\",\n 0x20a6: \"NairaSign\",\n 0x20a7: \"PesetaSign\",\n 0x20a8: \"RupeeSign\",\n 0x20a9: \"WonSign\",\n 0x20aa: \"NewSheqelSign\",\n 0x20ab: \"DongSign\",\n 0x20ac: \"EuroSign\",\n 0x0300: \"dead_grave\",\n 0x0301: \"dead_acute\",\n 0x0302: \"dead_circumflex\",\n 0x0303: \"dead_tilde\",\n 0x0304: \"dead_macron\",\n 0x0306: \"dead_breve\",\n 0x0307: \"dead_abovedot\",\n 0x0308: \"dead_diaeresis\",\n 0x030a: \"dead_abovering\",\n 0x030b: \"dead_doubleacute\",\n 0x030c: \"dead_caron\",\n 0x0327: \"dead_cedilla\",\n 0x0328: \"dead_ogonek\",\n 0x0345: \"dead_iota\",\n 0x3099: \"dead_voiced_sound\",\n 0x309a: \"dead_semivoiced_sound\",\n 0x0008: \"BackSpace\",\n 0x0009: \"Tab\",\n 0x000a: \"Linefeed\",\n 0x000b: \"Clear\",\n 0x000d: \"Return\",\n 0x0013: \"Pause\",\n 0x0014: \"Scroll_Lock\",\n 0x0015: \"Sys_Req\",\n 0x001b: \"Escape\",\n 0x0491: \"Ukrainian_ghe_with_upturn\",\n 0x0490: \"Ukrainian_GHE_WITH_UPTURN\",\n 0x0587: \"Armenian_ligature_ew\",\n 0x0589: \"Armenian_verjaket\",\n 0x055d: \"Armenian_but\",\n 0x058a: \"Armenian_yentamna\",\n 0x055c: \"Armenian_amanak\",\n 0x055b: \"Armenian_shesht\",\n 0x055e: \"Armenian_paruyk\",\n 0x0531: \"Armenian_AYB\",\n 0x0561: \"Armenian_ayb\",\n 0x0532: \"Armenian_BEN\",\n 0x0562: \"Armenian_ben\",\n 0x0533: \"Armenian_GIM\",\n 0x0563: \"Armenian_gim\",\n 0x0534: \"Armenian_DA\",\n 0x0564: \"Armenian_da\",\n 0x0535: \"Armenian_YECH\",\n 0x0565: \"Armenian_yech\",\n 0x0536: \"Armenian_ZA\",\n 0x0566: \"Armenian_za\",\n 0x0537: \"Armenian_E\",\n 0x0567: \"Armenian_e\",\n 0x0538: \"Armenian_AT\",\n 0x0568: \"Armenian_at\",\n 0x0539: \"Armenian_TO\",\n 0x0569: \"Armenian_to\",\n 0x053a: \"Armenian_ZHE\",\n 0x056a: \"Armenian_zhe\",\n 0x053b: \"Armenian_INI\",\n 0x056b: \"Armenian_ini\",\n 0x053c: \"Armenian_LYUN\",\n 0x056c: \"Armenian_lyun\",\n 0x053d: \"Armenian_KHE\",\n 0x056d: \"Armenian_khe\",\n 0x053e: \"Armenian_TSA\",\n 0x056e: \"Armenian_tsa\",\n 0x053f: \"Armenian_KEN\",\n 0x056f: \"Armenian_ken\",\n 0x0540: \"Armenian_HO\",\n 0x0570: \"Armenian_ho\",\n 0x0541: \"Armenian_DZA\",\n 0x0571: \"Armenian_dza\",\n 0x0542: \"Armenian_GHAT\",\n 0x0572: \"Armenian_ghat\",\n 0x0543: \"Armenian_TCHE\",\n 0x0573: \"Armenian_tche\",\n 0x0544: \"Armenian_MEN\",\n 0x0574: \"Armenian_men\",\n 0x0545: \"Armenian_HI\",\n 0x0575: \"Armenian_hi\",\n 0x0546: \"Armenian_NU\",\n 0x0576: \"Armenian_nu\",\n 0x0547: \"Armenian_SHA\",\n 0x0577: \"Armenian_sha\",\n 0x0548: \"Armenian_VO\",\n 0x0578: \"Armenian_vo\",\n 0x0549: \"Armenian_CHA\",\n 0x0579: \"Armenian_cha\",\n 0x054a: \"Armenian_PE\",\n 0x057a: \"Armenian_pe\",\n 0x054b: \"Armenian_JE\",\n 0x057b: \"Armenian_je\",\n 0x054c: \"Armenian_RA\",\n 0x057c: \"Armenian_ra\",\n 0x054d: \"Armenian_SE\",\n 0x057d: \"Armenian_se\",\n 0x054e: \"Armenian_VEV\",\n 0x057e: \"Armenian_vev\",\n 0x054f: \"Armenian_TYUN\",\n 0x057f: \"Armenian_tyun\",\n 0x0550: \"Armenian_RE\",\n 0x0580: \"Armenian_re\",\n 0x0551: \"Armenian_TSO\",\n 0x0581: \"Armenian_tso\",\n 0x0552: \"Armenian_VYUN\",\n 0x0582: \"Armenian_vyun\",\n 0x0553: \"Armenian_PYUR\",\n 0x0583: \"Armenian_pyur\",\n 0x0554: \"Armenian_KE\",\n 0x0584: \"Armenian_ke\",\n 0x0555: \"Armenian_O\",\n 0x0585: \"Armenian_o\",\n 0x0556: \"Armenian_FE\",\n 0x0586: \"Armenian_fe\",\n 0x055a: \"Armenian_apostrophe\",\n 0x10d0: \"Georgian_an\",\n 0x10d1: \"Georgian_ban\",\n 0x10d2: \"Georgian_gan\",\n 0x10d3: \"Georgian_don\",\n 0x10d4: \"Georgian_en\",\n 0x10d5: \"Georgian_vin\",\n 0x10d6: \"Georgian_zen\",\n 0x10d7: \"Georgian_tan\",\n 0x10d8: \"Georgian_in\",\n 0x10d9: \"Georgian_kan\",\n 0x10da: \"Georgian_las\",\n 0x10db: \"Georgian_man\",\n 0x10dc: \"Georgian_nar\",\n 0x10dd: \"Georgian_on\",\n 0x10de: \"Georgian_par\",\n 0x10df: \"Georgian_zhar\",\n 0x10e0: \"Georgian_rae\",\n 0x10e1: \"Georgian_san\",\n 0x10e2: \"Georgian_tar\",\n 0x10e3: \"Georgian_un\",\n 0x10e4: \"Georgian_phar\",\n 0x10e5: \"Georgian_khar\",\n 0x10e6: \"Georgian_ghan\",\n 0x10e7: \"Georgian_qar\",\n 0x10e8: \"Georgian_shin\",\n 0x10e9: \"Georgian_chin\",\n 0x10ea: \"Georgian_can\",\n 0x10eb: \"Georgian_jil\",\n 0x10ec: \"Georgian_cil\",\n 0x10ed: \"Georgian_char\",\n 0x10ee: \"Georgian_xan\",\n 0x10ef: \"Georgian_jhan\",\n 0x10f0: \"Georgian_hae\",\n 0x10f1: \"Georgian_he\",\n 0x10f2: \"Georgian_hie\",\n 0x10f3: \"Georgian_we\",\n 0x10f4: \"Georgian_har\",\n 0x10f5: \"Georgian_hoe\",\n 0x10f6: \"Georgian_fi\",\n 0x1e02: \"Babovedot\",\n 0x1e03: \"babovedot\",\n 0x1e0a: \"Dabovedot\",\n 0x1e80: \"Wgrave\",\n 0x1e82: \"Wacute\",\n 0x1e0b: \"dabovedot\",\n 0x1ef2: \"Ygrave\",\n 0x1e1e: \"Fabovedot\",\n 0x1e1f: \"fabovedot\",\n 0x1e40: \"Mabovedot\",\n 0x1e41: \"mabovedot\",\n 0x1e56: \"Pabovedot\",\n 0x1e81: \"wgrave\",\n 0x1e57: \"pabovedot\",\n 0x1e83: \"wacute\",\n 0x1e60: \"Sabovedot\",\n 0x1ef3: \"ygrave\",\n 0x1e84: \"Wdiaeresis\",\n 0x1e85: \"wdiaeresis\",\n 0x1e61: \"sabovedot\",\n 0x0174: \"Wcircumflex\",\n 0x1e6a: \"Tabovedot\",\n 0x0176: \"Ycircumflex\",\n 0x0175: \"wcircumflex\",\n 0x1e6b: \"tabovedot\",\n 0x0177: \"ycircumflex\",\n 0x06f0: \"Farsi_0\",\n 0x06f1: \"Farsi_1\",\n 0x06f2: \"Farsi_2\",\n 0x06f3: \"Farsi_3\",\n 0x06f4: \"Farsi_4\",\n 0x06f5: \"Farsi_5\",\n 0x06f6: \"Farsi_6\",\n 0x06f7: \"Farsi_7\",\n 0x06f8: \"Farsi_8\",\n 0x06f9: \"Farsi_9\",\n 0x066a: \"Arabic_percent\",\n 0x0670: \"Arabic_superscript_alef\",\n 0x0679: \"Arabic_tteh\",\n 0x067e: \"Arabic_peh\",\n 0x0686: \"Arabic_tcheh\",\n 0x0688: \"Arabic_ddal\",\n 0x0691: \"Arabic_rreh\",\n 0x06d4: \"Arabic_fullstop\",\n 0x0660: \"Arabic_0\",\n 0x0661: \"Arabic_1\",\n 0x0662: \"Arabic_2\",\n 0x0663: \"Arabic_3\",\n 0x0664: \"Arabic_4\",\n 0x0665: \"Arabic_5\",\n 0x0666: \"Arabic_6\",\n 0x0667: \"Arabic_7\",\n 0x0668: \"Arabic_8\",\n 0x0669: \"Arabic_9\",\n 0x0653: \"Arabic_madda_above\",\n 0x0654: \"Arabic_hamza_above\",\n 0x0655: \"Arabic_hamza_below\",\n 0x0698: \"Arabic_jeh\",\n 0x06a4: \"Arabic_veh\",\n 0x06a9: \"Arabic_keheh\",\n 0x06af: \"Arabic_gaf\",\n 0x06ba: \"Arabic_noon_ghunna\",\n 0x06be: \"Arabic_heh_doachashmee\",\n 0x06cc: \"Farsi_yeh\",\n 0x06d2: \"Arabic_yeh_baree\",\n 0x06c1: \"Arabic_heh_goal\",\n 0x0492: \"Cyrillic_GHE_bar\",\n 0x0496: \"Cyrillic_ZHE_descender\",\n 0x049a: \"Cyrillic_KA_descender\",\n 0x049c: \"Cyrillic_KA_vertstroke\",\n 0x04a2: \"Cyrillic_EN_descender\",\n 0x04ae: \"Cyrillic_U_straight\",\n 0x04b0: \"Cyrillic_U_straight_bar\",\n 0x04b2: \"Cyrillic_HA_descender\",\n 0x04b6: \"Cyrillic_CHE_descender\",\n 0x04b8: \"Cyrillic_CHE_vertstroke\",\n 0x04ba: \"Cyrillic_SHHA\",\n 0x04d8: \"Cyrillic_SCHWA\",\n 0x04e2: \"Cyrillic_I_macron\",\n 0x04e8: \"Cyrillic_O_bar\",\n 0x04ee: \"Cyrillic_U_macron\",\n 0x0493: \"Cyrillic_ghe_bar\",\n 0x0497: \"Cyrillic_zhe_descender\",\n 0x049b: \"Cyrillic_ka_descender\",\n 0x049d: \"Cyrillic_ka_vertstroke\",\n 0x04a3: \"Cyrillic_en_descender\",\n 0x04af: \"Cyrillic_u_straight\",\n 0x04b1: \"Cyrillic_u_straight_bar\",\n 0x04b3: \"Cyrillic_ha_descender\",\n 0x04b7: \"Cyrillic_che_descender\",\n 0x04b9: \"Cyrillic_che_vertstroke\",\n 0x04bb: \"Cyrillic_shha\",\n 0x04d9: \"Cyrillic_schwa\",\n 0x04e3: \"Cyrillic_i_macron\",\n 0x04e9: \"Cyrillic_o_bar\",\n 0x04ef: \"Cyrillic_u_macron\",\n 0x1e8a: \"Xabovedot\",\n 0x012c: \"Ibreve\",\n 0x01b5: \"Zstroke\",\n 0x01e6: \"Gcaron\",\n 0x019f: \"Obarred\",\n 0x1e8b: \"xabovedot\",\n 0x012d: \"ibreve\",\n 0x01b6: \"zstroke\",\n 0x01e7: \"gcaron\",\n 0x01d2: \"ocaron\",\n 0x0275: \"obarred\",\n 0x018f: \"SCHWA\",\n 0x0259: \"schwa\",\n 0x1e36: \"Lbelowdot\",\n 0x1e37: \"lbelowdot\",\n 0x1ea0: \"Abelowdot\",\n 0x1ea1: \"abelowdot\",\n 0x1ea2: \"Ahook\",\n 0x1ea3: \"ahook\",\n 0x1ea4: \"Acircumflexacute\",\n 0x1ea5: \"acircumflexacute\",\n 0x1ea6: \"Acircumflexgrave\",\n 0x1ea7: \"acircumflexgrave\",\n 0x1ea8: \"Acircumflexhook\",\n 0x1ea9: \"acircumflexhook\",\n 0x1eaa: \"Acircumflextilde\",\n 0x1eab: \"acircumflextilde\",\n 0x1eac: \"Acircumflexbelowdot\",\n 0x1ead: \"acircumflexbelowdot\",\n 0x1eae: \"Abreveacute\",\n 0x1eaf: \"abreveacute\",\n 0x1eb0: \"Abrevegrave\",\n 0x1eb1: \"abrevegrave\",\n 0x1eb2: \"Abrevehook\",\n 0x1eb3: \"abrevehook\",\n 0x1eb4: \"Abrevetilde\",\n 0x1eb5: \"abrevetilde\",\n 0x1eb6: \"Abrevebelowdot\",\n 0x1eb7: \"abrevebelowdot\",\n 0x1eb8: \"Ebelowdot\",\n 0x1eb9: \"ebelowdot\",\n 0x1eba: \"Ehook\",\n 0x1ebb: \"ehook\",\n 0x1ebc: \"Etilde\",\n 0x1ebd: \"etilde\",\n 0x1ebe: \"Ecircumflexacute\",\n 0x1ebf: \"ecircumflexacute\",\n 0x1ec0: \"Ecircumflexgrave\",\n 0x1ec1: \"ecircumflexgrave\",\n 0x1ec2: \"Ecircumflexhook\",\n 0x1ec3: \"ecircumflexhook\",\n 0x1ec4: \"Ecircumflextilde\",\n 0x1ec5: \"ecircumflextilde\",\n 0x1ec6: \"Ecircumflexbelowdot\",\n 0x1ec7: \"ecircumflexbelowdot\",\n 0x1ec8: \"Ihook\",\n 0x1ec9: \"ihook\",\n 0x1eca: \"Ibelowdot\",\n 0x1ecb: \"ibelowdot\",\n 0x1ecc: \"Obelowdot\",\n 0x1ecd: \"obelowdot\",\n 0x1ece: \"Ohook\",\n 0x1ecf: \"ohook\",\n 0x1ed0: \"Ocircumflexacute\",\n 0x1ed1: \"ocircumflexacute\",\n 0x1ed2: \"Ocircumflexgrave\",\n 0x1ed3: \"ocircumflexgrave\",\n 0x1ed4: \"Ocircumflexhook\",\n 0x1ed5: \"ocircumflexhook\",\n 0x1ed6: \"Ocircumflextilde\",\n 0x1ed7: \"ocircumflextilde\",\n 0x1ed8: \"Ocircumflexbelowdot\",\n 0x1ed9: \"ocircumflexbelowdot\",\n 0x1eda: \"Ohornacute\",\n 0x1edb: \"ohornacute\",\n 0x1edc: \"Ohorngrave\",\n 0x1edd: \"ohorngrave\",\n 0x1ede: \"Ohornhook\",\n 0x1edf: \"ohornhook\",\n 0x1ee0: \"Ohorntilde\",\n 0x1ee1: \"ohorntilde\",\n 0x1ee2: \"Ohornbelowdot\",\n 0x1ee3: \"ohornbelowdot\",\n 0x1ee4: \"Ubelowdot\",\n 0x1ee5: \"ubelowdot\",\n 0x1ee6: \"Uhook\",\n 0x1ee7: \"uhook\",\n 0x1ee8: \"Uhornacute\",\n 0x1ee9: \"uhornacute\",\n 0x1eea: \"Uhorngrave\",\n 0x1eeb: \"uhorngrave\",\n 0x1eec: \"Uhornhook\",\n 0x1eed: \"uhornhook\",\n 0x1eee: \"Uhorntilde\",\n 0x1eef: \"uhorntilde\",\n 0x1ef0: \"Uhornbelowdot\",\n 0x1ef1: \"uhornbelowdot\",\n 0x1ef4: \"Ybelowdot\",\n 0x1ef5: \"ybelowdot\",\n 0x1ef6: \"Yhook\",\n 0x1ef7: \"yhook\",\n 0x1ef8: \"Ytilde\",\n 0x1ef9: \"ytilde\",\n 0x01a0: \"Ohorn\",\n 0x01a1: \"ohorn\",\n 0x01af: \"Uhorn\",\n 0x01b0: \"uhorn\",\n 0x0323: \"dead_belowdot\",\n 0x0309: \"dead_hook\",\n 0x031b: \"dead_horn\"\n});\n\n// mapping is based on https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values\nconst kbEventCodesToKeySyms = Object.freeze({\n \"AltLeft\": \"Alt_L\",\n \"AltRight\": \"Alt_R\",\n \"ArrowDown\": \"Down\",\n \"ArrowLeft\": \"Left\",\n \"ArrowRight\": \"Right\",\n \"ArrowUp\": \"Up\",\n \"Backspace\": \"BackSpace\",\n \"CapsLock\": \"Caps_Lock\",\n \"ControlLeft\": \"Control_L\",\n \"ControlRight\": \"Control_R\",\n \"Enter\": \"Return\",\n \"HyperLeft\": \"Hyper_L\",\n \"HyperRight\": \"Hyper_R\",\n \"NumLock\": \"Num_Lock\",\n \"NumpadEnter\": \"Return\",\n \"MetaLeft\": \"Meta_L\",\n \"MetaRight\": \"Meta_R\",\n \"PageDown\": \"Page_Down\",\n \"PageUp\": \"Page_Up\",\n \"ScrollLock\": \"Scroll_Lock\",\n \"ShiftLeft\": \"Shift_L\",\n \"ShiftRight\": \"Shift_R\",\n \"SuperLeft\": \"Super_L\",\n \"SuperRight\": \"Super_R\"\n});\n\n// these Keyboard Event codes direclty map to X11 Keysyms\nconst knownKbEventCodes = new Set([\n \"Clear\", \"Copy\", \"Cut\", \"Delete\", \"End\", \"F1\", \"F2\", \"F3\", \"F4\", \"F5\", \"F6\", \"F7\", \"F8\", \"F9\", \"F10\", \"F11\", \"F12\",\n \"Home\", \"Insert\", \"Paste\", \"Redo\", \"Tab\", \"Undo\"\n]);\n\n/**\n * Returns X11 keySym (defined in https://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt) for a given key/code from the key[up/down] event\n * For keys with length 1 (ASCII, Cyrillic etc) - uses uniToKeySyms mapping\n * For keys with length > 1 (Modifiers, Whitespaces, Navigation etc) - uses knownKbEventCodes and kbEventCodesToKeySyms mapping\n *\n * @param {string} key 'key' from the key[up/down] event\n * @param {string} code 'code' from the key[up/down] event\n * @return {string} keySymString (X11 keysym string)\n */\nexport default function getKeysymString(key, code) {\n var keySym = \"Unidentified\";\n if (key.length === 1) {\n const keyCodeUni = key.charCodeAt(0);\n if (keyCodeUni in uniToKeySyms) {\n keySym = uniToKeySyms[keyCodeUni];\n }\n } else if (code in kbEventCodesToKeySyms) {\n keySym = kbEventCodesToKeySyms[code];\n } else if (knownKbEventCodes.has(code)) {\n keySym = code;\n }\n return keySym;\n}\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport getKeysymString from \"./keysyms.js\";\n\n/** @import ConsumerSession from \"./consumer-session.js\"; */\n\nconst eventsNames = Object.freeze([\n \"wheel\",\n \"contextmenu\",\n \"mousemove\",\n \"mousedown\",\n \"mouseup\",\n \"touchstart\",\n \"touchend\",\n \"touchmove\",\n \"touchcancel\",\n \"keyup\",\n \"keydown\"\n]);\n\nconst mouseEventsNames = Object.freeze({\n mousemove: \"MouseMove\",\n mousedown: \"MouseButtonPress\",\n mouseup: \"MouseButtonRelease\"\n});\n\nconst touchEventsNames = Object.freeze({\n touchstart: \"TouchDown\",\n touchend: \"TouchUp\",\n touchmove: \"TouchMotion\",\n touchcancel: \"TouchUp\"\n});\n\nconst keyboardEventsNames = Object.freeze({\n keydown: \"KeyPress\",\n keyup: \"KeyRelease\"\n});\n\nfunction getModifiers(event) {\n const modifiers = [];\n if (event.altKey) {\n modifiers.push(\"mod1-mask\");\n }\n\n if (event.ctrlKey) {\n modifiers.push(\"control-mask\");\n }\n\n if (event.metaKey) {\n modifiers.push(\"meta-mask\");\n }\n\n if (event.shiftKey) {\n modifiers.push(\"shift-mask\");\n }\n\n return modifiers.join(\"+\");\n}\n\n/**\n * Event name: \"info\".
\n * Triggered when a remote peer sends an information message over the control data channel.\n * @event GstWebRTCAPI#InfoEvent\n * @type {CustomEvent}\n * @property {object} detail - The info message\n * @see RemoteController\n */\n/**\n * Event name: \"controlResponse\".
\n * Triggered when a remote peer sends a response after a control request.\n * @event GstWebRTCAPI#ControlResponseEvent\n * @type {CustomEvent}\n * @property {object} detail - The response message\n * @see RemoteController\n */\n\n/**\n * @class RemoteController\n * @hideconstructor\n * @classdesc Manages a specific WebRTC data channel created by a remote GStreamer webrtcsink producer and offering\n * remote control of the producer through\n * [GstNavigation]{@link https://gstreamer.freedesktop.org/documentation/video/gstnavigation.html} events.\n *

The remote control data channel is created by the GStreamer webrtcsink element on the producer side. Then it is\n * announced through the consumer session thanks to the {@link gstWebRTCAPI#event:RemoteControllerChangedEvent}\n * event.

\n *

You can attach an {@link HTMLVideoElement} to the remote controller, then all mouse and keyboard events\n * emitted by this element will be automatically relayed to the remote producer.

\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @fires {@link GstWebRTCAPI#event:InfoEvent}\n * @fires {@link GstWebRTCAPI#event:ControlResponseEvent}\n * @see ConsumerSession#remoteController\n * @see RemoteController#attachVideoElement\n * @see https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/tree/main/net/webrtc/gstwebrtc-api#produce-a-gstreamer-interactive-webrtc-stream-with-remote-control\n */\nclass RemoteController extends EventTarget {\n constructor(rtcDataChannel, consumerSession) {\n super();\n\n this._rtcDataChannel = rtcDataChannel;\n this._consumerSession = consumerSession;\n\n this._videoElement = null;\n this._videoElementComputedStyle = null;\n this._videoElementKeyboard = null;\n this._lastTouchEventTimestamp = 0;\n this._requestCounter = 0;\n\n rtcDataChannel.addEventListener(\"close\", () => {\n if (this._rtcDataChannel === rtcDataChannel) {\n this.close();\n }\n });\n\n rtcDataChannel.addEventListener(\"error\", (event) => {\n if (this._rtcDataChannel === rtcDataChannel) {\n const error = event.error;\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: (error && error.message) || \"Remote controller error\",\n error: error || new Error(\"unknown error on the remote controller data channel\")\n }));\n }\n });\n\n rtcDataChannel.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(event.data);\n\n if (msg.type === \"ControlResponseMessage\") {\n this.dispatchEvent(new CustomEvent(\"controlResponse\", { detail: msg }));\n } else if (msg.type === \"InfoMessage\") {\n this.dispatchEvent(new CustomEvent(\"info\", { detail: msg }));\n }\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot parse control message from signaling server\",\n error: ex\n }));\n }\n });\n }\n\n\n /**\n * The underlying WebRTC data channel connected to a remote GStreamer webrtcsink producer offering remote control.\n * The value may be null if the remote controller has been closed.\n * @type {RTCDataChannel}\n * @readonly\n */\n get rtcDataChannel() {\n return this._rtcDataChannel;\n }\n\n /**\n * The consumer session associated with this remote controller.\n * @type {ConsumerSession}\n * @readonly\n */\n get consumerSession() {\n return this._consumerSession;\n }\n\n /**\n * The video element that is currently used to send all mouse and keyboard events to the remote producer. Value may\n * be null if no video element is attached.\n * @type {HTMLVideoElement}\n * @readonly\n * @see RemoteController#attachVideoElement\n */\n get videoElement() {\n return this._videoElement;\n }\n\n /**\n * Associates a video element with this remote controller.
\n * When a video element is attached to this remote controller, all mouse and keyboard events emitted by this\n * element will be sent to the remote GStreamer webrtcink producer.\n * @param {HTMLVideoElement|null} element - the video element to use to relay mouse and keyboard events,\n * or null to detach any previously attached element. If the provided element parameter is not null and not a\n * valid instance of an {@link HTMLVideoElement}, then the method does nothing.\n */\n attachVideoElement(element) {\n if ((element instanceof HTMLVideoElement) && (element !== this._videoElement)) {\n if (this._videoElement) {\n this.attachVideoElement(null);\n }\n\n this._videoElement = element;\n this._videoElementComputedStyle = window.getComputedStyle(element);\n\n for (const eventName of eventsNames) {\n element.addEventListener(eventName, this);\n }\n\n element.setAttribute(\"tabindex\", \"0\");\n } else if ((element === null) && this._videoElement) {\n const previousElement = this._videoElement;\n previousElement.removeAttribute(\"tabindex\");\n\n this._videoElement = null;\n this._videoElementComputedStyle = null;\n\n this._lastTouchEventTimestamp = 0;\n\n for (const eventName of eventsNames) {\n previousElement.removeEventListener(eventName, this);\n }\n }\n }\n\n /**\n * Send a request over the control data channel.
\n *\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @param {object|string} request - The request to send over the channel\n * @returns {number} The identifier attributed to the request, or -1 if an exception occurred\n */\n sendControlRequest(request) {\n try {\n if (!request || ((typeof (request) !== \"object\") && (typeof (request) !== \"string\"))) {\n throw new Error(\"invalid request\");\n }\n\n if (!this._rtcDataChannel) {\n throw new Error(\"remote controller data channel is closed\");\n }\n\n let message = {\n id: this._requestCounter++,\n request: request\n };\n\n this._rtcDataChannel.send(JSON.stringify(message));\n\n return message.id;\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: `cannot send control message over session ${this._consumerSession.sessionId} remote controller`,\n error: ex\n }));\n return -1;\n }\n }\n\n /**\n * Closes the remote controller channel.
\n * It immediately shuts down the underlying WebRTC data channel connected to a remote GStreamer webrtcsink\n * producer and detaches any video element that may be used to relay mouse and keyboard events.\n */\n close() {\n this.attachVideoElement(null);\n\n const rtcDataChannel = this._rtcDataChannel;\n this._rtcDataChannel = null;\n\n if (rtcDataChannel) {\n rtcDataChannel.close();\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n\n _sendGstNavigationEvent(data) {\n let request = {\n type: \"navigationEvent\",\n event: data\n };\n this.sendControlRequest(request);\n }\n\n _computeVideoMousePosition(event) {\n const mousePos = { x: 0, y: 0 };\n if (!this._videoElement || (this._videoElement.videoWidth <= 0) || (this._videoElement.videoHeight <= 0)) {\n return mousePos;\n }\n\n const padding = {\n left: parseFloat(this._videoElementComputedStyle.paddingLeft),\n right: parseFloat(this._videoElementComputedStyle.paddingRight),\n top: parseFloat(this._videoElementComputedStyle.paddingTop),\n bottom: parseFloat(this._videoElementComputedStyle.paddingBottom)\n };\n\n if ((\"offsetX\" in event) && (\"offsetY\" in event)) {\n mousePos.x = event.offsetX - padding.left;\n mousePos.y = event.offsetY - padding.top;\n } else {\n const clientRect = this._videoElement.getBoundingClientRect();\n const border = {\n left: parseFloat(this._videoElementComputedStyle.borderLeftWidth),\n top: parseFloat(this._videoElementComputedStyle.borderTopWidth)\n };\n mousePos.x = event.clientX - clientRect.left - border.left - padding.left;\n mousePos.y = event.clientY - clientRect.top - border.top - padding.top;\n }\n\n const videoOffset = {\n x: this._videoElement.clientWidth - (padding.left + padding.right),\n y: this._videoElement.clientHeight - (padding.top + padding.bottom)\n };\n\n const ratio = Math.min(videoOffset.x / this._videoElement.videoWidth, videoOffset.y / this._videoElement.videoHeight);\n videoOffset.x = Math.max(0.5 * (videoOffset.x - this._videoElement.videoWidth * ratio), 0);\n videoOffset.y = Math.max(0.5 * (videoOffset.y - this._videoElement.videoHeight * ratio), 0);\n\n const invRatio = (ratio !== 0) ? (1 / ratio) : 0;\n mousePos.x = (mousePos.x - videoOffset.x) * invRatio;\n mousePos.y = (mousePos.y - videoOffset.y) * invRatio;\n\n mousePos.x = Math.min(Math.max(mousePos.x, 0), this._videoElement.videoWidth);\n mousePos.y = Math.min(Math.max(mousePos.y, 0), this._videoElement.videoHeight);\n\n return mousePos;\n }\n\n handleEvent(event) {\n if (!this._videoElement) {\n return;\n }\n\n switch (event.type) {\n case \"wheel\":\n event.preventDefault();\n {\n const mousePos = this._computeVideoMousePosition(event);\n this._sendGstNavigationEvent({\n event: \"MouseScroll\",\n x: mousePos.x,\n y: mousePos.y,\n delta_x: -event.deltaX, // eslint-disable-line camelcase\n delta_y: -event.deltaY, // eslint-disable-line camelcase\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n });\n }\n break;\n\n case \"contextmenu\":\n event.preventDefault();\n break;\n\n case \"mousemove\":\n case \"mousedown\":\n case \"mouseup\":\n event.preventDefault();\n {\n const mousePos = this._computeVideoMousePosition(event);\n const data = {\n event: mouseEventsNames[event.type],\n x: mousePos.x,\n y: mousePos.y,\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n\n if (event.type !== \"mousemove\") {\n data.button = event.button + 1;\n\n if ((event.type === \"mousedown\") && (event.button === 0)) {\n this._videoElement.focus();\n }\n }\n\n this._sendGstNavigationEvent(data);\n }\n break;\n\n case \"touchstart\":\n case \"touchend\":\n case \"touchmove\":\n case \"touchcancel\":\n for (const touch of event.changedTouches) {\n const mousePos = this._computeVideoMousePosition(touch);\n const data = {\n event: touchEventsNames[event.type],\n identifier: touch.identifier,\n x: mousePos.x,\n y: mousePos.y,\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n\n if ((\"force\" in touch) && ((event.type === \"touchstart\") || (event.type === \"touchmove\"))) {\n data.pressure = touch.force;\n }\n\n this._sendGstNavigationEvent(data);\n }\n\n if (event.timeStamp > this._lastTouchEventTimestamp) {\n this._lastTouchEventTimestamp = event.timeStamp;\n this._sendGstNavigationEvent({\n event: \"TouchFrame\",\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n });\n }\n break;\n case \"keyup\":\n case \"keydown\":\n event.preventDefault();\n {\n const data = {\n event: keyboardEventsNames[event.type],\n key: getKeysymString(event.key, event.code),\n modifier_state: getModifiers(event) // eslint-disable-line camelcase\n };\n this._sendGstNavigationEvent(data);\n }\n break;\n }\n }\n}\n\nexport default RemoteController;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport WebRTCSession from \"./webrtc-session.js\";\nimport SessionState from \"./session-state.js\";\nimport RemoteController from \"./remote-controller.js\";\n\n/**\n * Event name: \"streamsChanged\".
\n * Triggered when the underlying media streams of a {@link ConsumerSession} change.\n * @event GstWebRTCAPI#StreamsChangedEvent\n * @type {Event}\n * @see ConsumerSession#streams\n */\n/**\n * Event name: \"remoteControllerChanged\".
\n * Triggered when the underlying remote controller of a {@link ConsumerSession} changes.\n * @event GstWebRTCAPI#RemoteControllerChangedEvent\n * @type {Event}\n * @see ConsumerSession#remoteController\n */\n\n/**\n * @class ConsumerSession\n * @hideconstructor\n * @classdesc Consumer session managing a peer-to-peer WebRTC channel between a remote producer and this client\n * instance.\n *

Call {@link GstWebRTCAPI#createConsumerSession} to create a ConsumerSession instance.

\n * @extends {WebRTCSession}\n * @fires {@link GstWebRTCAPI#event:StreamsChangedEvent}\n * @fires {@link GstWebRTCAPI#event:RemoteControllerChangedEvent}\n */\nclass ConsumerSession extends WebRTCSession {\n constructor(peerId, comChannel, offerOptions) {\n super(peerId, comChannel);\n this._streams = [];\n this._remoteController = null;\n this._pendingCandidates = [];\n this._mungeStereoHack = false;\n\n this._offerOptions = offerOptions;\n\n this.addEventListener(\"closed\", () => {\n this._streams = [];\n this._pendingCandidates = [];\n\n if (this._remoteController) {\n this._remoteController.close();\n }\n });\n }\n\n /**\n * Defines whether the SDP should be munged in order to enable stereo with chrome.\n * @method\n * @param {boolean} enable - Enable or disable the hack, default is false\n */\n set mungeStereoHack(enable) {\n if (typeof (enable) !== \"boolean\") { return; }\n\n this._mungeStereoHack = enable;\n }\n\n /**\n * The array of remote media streams consumed locally through this WebRTC channel.\n * @type {MediaStream[]}\n * @readonly\n */\n get streams() {\n return this._streams;\n }\n\n /**\n * The remote controller associated with this WebRTC consumer session. Value may be null if consumer session\n * has no remote controller.\n * @type {RemoteController}\n * @readonly\n */\n get remoteController() {\n return this._remoteController;\n }\n\n /**\n * Connects the consumer session to its remote producer.
\n * This method must be called after creating the consumer session in order to start receiving the remote streams.\n * It registers this consumer session to the signaling server and gets ready to receive audio/video streams.\n *

Even on success, streaming can fail later if any error occurs during or after connection. In order to know\n * the effective streaming state, you should be listening to the [error]{@link GstWebRTCAPI#event:ErrorEvent},\n * [stateChanged]{@link GstWebRTCAPI#event:StateChangedEvent} and/or [closed]{@link GstWebRTCAPI#event:ClosedEvent}\n * events.

\n * @returns {boolean} true in case of success (may fail later during or after connection) or false in case of\n * immediate error (wrong session state or no connection to the signaling server).\n */\n connect() {\n if (!this._comChannel || (this._state === SessionState.closed)) {\n return false;\n }\n\n if (this._state !== SessionState.idle) {\n return true;\n }\n\n if (this._offerOptions) {\n this.ensurePeerConnection();\n\n this._rtcPeerConnection.createDataChannel(\"control\");\n\n this._rtcPeerConnection.createOffer(this._offerOptions).then((desc) => {\n if (this._rtcPeerConnection && desc) {\n return this._rtcPeerConnection.setLocalDescription(desc);\n } else {\n throw new Error(\"cannot send local offer to WebRTC peer\");\n }\n }).then(() => {\n if (this._rtcPeerConnection && this._comChannel) {\n const msg = {\n type: \"startSession\",\n peerId: this._peerId,\n offer: this._rtcPeerConnection.localDescription.toJSON().sdp\n };\n if (!this._comChannel.send(msg)) {\n throw new Error(\"cannot send startSession message to signaling server\");\n }\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n const msg = {\n type: \"startSession\",\n peerId: this._peerId\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot connect consumer session\",\n error: new Error(\"cannot send startSession message to signaling server\")\n }));\n\n this.close();\n return false;\n }\n\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n return true;\n }\n\n onSessionStarted(peerId, sessionId) {\n if ((this._peerId === peerId) && (this._state === SessionState.connecting) && !this._sessionId) {\n console.log(\"Session started\", this._sessionId);\n this._sessionId = sessionId;\n\n for (const candidate of this._pendingCandidates) {\n console.log(\"Sending delayed ICE with session id\", this._sessionId);\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: candidate.toJSON()\n });\n }\n\n this._pendingCandidates = [];\n }\n }\n\n ensurePeerConnection() {\n if (!this._rtcPeerConnection) {\n const connection = new RTCPeerConnection(this._comChannel.webrtcConfig);\n this._rtcPeerConnection = connection;\n\n connection.ontrack = (event) => {\n if ((this._rtcPeerConnection === connection) && event.streams && (event.streams.length > 0)) {\n if (this._state === SessionState.connecting) {\n this._state = SessionState.streaming;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n let streamsChanged = false;\n for (const stream of event.streams) {\n if (!this._streams.includes(stream)) {\n this._streams.push(stream);\n streamsChanged = true;\n }\n }\n\n if (streamsChanged) {\n this.dispatchEvent(new Event(\"streamsChanged\"));\n }\n }\n };\n\n connection.ondatachannel = (event) => {\n const rtcDataChannel = event.channel;\n if (rtcDataChannel && (rtcDataChannel.label === \"control\")) {\n if (this._remoteController) {\n const previousController = this._remoteController;\n this._remoteController = null;\n previousController.close();\n }\n\n const remoteController = new RemoteController(rtcDataChannel, this);\n this._remoteController = remoteController;\n this.dispatchEvent(new Event(\"remoteControllerChanged\"));\n\n remoteController.addEventListener(\"closed\", () => {\n if (this._remoteController === remoteController) {\n this._remoteController = null;\n this.dispatchEvent(new Event(\"remoteControllerChanged\"));\n }\n });\n }\n };\n\n connection.onicecandidate = (event) => {\n if ((this._rtcPeerConnection === connection) && event.candidate && this._comChannel) {\n if (this._sessionId) {\n console.log(\"Sending ICE with session id\", this._sessionId);\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: event.candidate.toJSON()\n });\n } else {\n this._pendingCandidates.push(event.candidate);\n }\n }\n };\n\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n }\n }\n\n // Work around Chrome not handling stereo Opus correctly.\n // See\n // https://chromium.googlesource.com/external/webrtc/+/194e3bcc53ffa3e98045934377726cb25d7579d2/webrtc/media/engine/webrtcvoiceengine.cc#302\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=8133\n //\n // Technically it's against the spec to modify the SDP\n // but there's no other API for this and this seems to\n // be the only possible workaround at this time.\n mungeStereo(offerSdp, answerSdp) {\n const stereoRegexp = /a=fmtp:.* sprop-stereo/g;\n let stereoPayloads = new Set();\n for (const m of offerSdp.matchAll(stereoRegexp)) {\n const payloadMatch = m[0].match(/a=fmtp:(\\d+) .*/);\n if (payloadMatch) {\n stereoPayloads.add(payloadMatch[1]);\n }\n }\n\n for (const payload of stereoPayloads) {\n const isStereoRegexp = new RegExp(\"a=fmtp:\" + payload + \".*stereo\");\n const answerIsStereo = answerSdp.match(isStereoRegexp);\n\n if (!answerIsStereo) {\n answerSdp = answerSdp.replaceAll(\"a=fmtp:\" + payload, \"a=fmtp:\" + payload + \" stereo=1;\");\n }\n }\n\n return answerSdp;\n }\n\n onSessionPeerMessage(msg) {\n if ((this._state === SessionState.closed) || !this._comChannel || !this._sessionId) {\n return;\n }\n\n this.ensurePeerConnection();\n\n if (msg.sdp) {\n if (this._offerOptions) {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).then(() => {\n console.log(\"done\");\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).then(() => {\n if (this._rtcPeerConnection) {\n return this._rtcPeerConnection.createAnswer();\n } else {\n return null;\n }\n }).then((desc) => {\n if (this._rtcPeerConnection && desc) {\n if (this._mungeStereoHack) {\n desc.sdp = this.mungeStereo(msg.sdp.sdp, desc.sdp);\n }\n\n return this._rtcPeerConnection.setLocalDescription(desc);\n } else {\n return null;\n }\n }).then(() => {\n if (this._rtcPeerConnection && this._comChannel) {\n console.log(\"Sending SDP with session id\", this._sessionId);\n const sdp = {\n type: \"peer\",\n sessionId: this._sessionId,\n sdp: this._rtcPeerConnection.localDescription.toJSON()\n };\n if (!this._comChannel.send(sdp)) {\n throw new Error(\"cannot send local SDP configuration to WebRTC peer\");\n }\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n }\n } else if (msg.ice) {\n const candidate = msg.ice.candidate ? new RTCIceCandidate(msg.ice) : null;\n this._rtcPeerConnection.addIceCandidate(candidate).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during ICE handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n throw new Error(`invalid empty peer message received from consumer session ${this._sessionId}`);\n }\n }\n}\n\nexport default ConsumerSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport WebRTCSession from \"./webrtc-session.js\";\nimport SessionState from \"./session-state.js\";\n\n/**\n * @class ClientSession\n * @hideconstructor\n * @classdesc Client session representing a link between a remote consumer and a local producer session.\n * @extends {WebRTCSession}\n */\nclass ClientSession extends WebRTCSession {\n constructor(peerId, sessionId, comChannel, stream) {\n super(peerId, comChannel);\n this._sessionId = sessionId;\n this._state = SessionState.streaming;\n\n const connection = new RTCPeerConnection(this._comChannel.webrtcConfig);\n this._rtcPeerConnection = connection;\n\n for (const track of stream.getTracks()) {\n connection.addTrack(track, stream);\n }\n\n connection.onicecandidate = (event) => {\n if ((this._rtcPeerConnection === connection) && event.candidate && this._comChannel) {\n this._comChannel.send({\n type: \"peer\",\n sessionId: this._sessionId,\n ice: event.candidate.toJSON()\n });\n }\n };\n\n this.dispatchEvent(new Event(\"rtcPeerConnectionChanged\"));\n\n connection.setLocalDescription().then(() => {\n if ((this._rtcPeerConnection === connection) && this._comChannel) {\n const sdp = {\n type: \"peer\",\n sessionId: this._sessionId,\n sdp: this._rtcPeerConnection.localDescription.toJSON()\n };\n if (!this._comChannel.send(sdp)) {\n throw new Error(\"cannot send local SDP configuration to WebRTC peer\");\n }\n }\n }).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n }\n\n onSessionPeerMessage(msg) {\n if ((this._state === SessionState.closed) || !this._rtcPeerConnection) {\n return;\n }\n\n if (msg.sdp) {\n this._rtcPeerConnection.setRemoteDescription(msg.sdp).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during SDP handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else if (msg.ice) {\n const candidate = new RTCIceCandidate(msg.ice);\n this._rtcPeerConnection.addIceCandidate(candidate).catch((ex) => {\n if (this._state !== SessionState.closed) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"an unrecoverable error occurred during ICE handshake\",\n error: ex\n }));\n\n this.close();\n }\n });\n } else {\n throw new Error(`invalid empty peer message received from producer's client session ${this._peerId}`);\n }\n }\n}\n\n/**\n * Event name: \"clientConsumerAdded\".
\n * Triggered when a remote consumer peer connects to a local {@link ProducerSession}.\n * @event GstWebRTCAPI#ClientConsumerAddedEvent\n * @type {CustomEvent}\n * @property {ClientSession} detail - The WebRTC session associated with the added consumer peer.\n * @see ProducerSession\n */\n/**\n * Event name: \"clientConsumerRemoved\".
\n * Triggered when a remote consumer peer disconnects from a local {@link ProducerSession}.\n * @event GstWebRTCAPI#ClientConsumerRemovedEvent\n * @type {CustomEvent}\n * @property {ClientSession} detail - The WebRTC session associated with the removed consumer peer.\n * @see ProducerSession\n */\n\n/**\n * @class ProducerSession\n * @hideconstructor\n * @classdesc Producer session managing the streaming out of a local {@link MediaStream}.
\n * It manages all underlying WebRTC connections to each peer client consuming the stream.\n *

Call {@link GstWebRTCAPI#createProducerSession} to create a ProducerSession instance.

\n * @extends {EventTarget}\n * @fires {@link GstWebRTCAPI#event:ErrorEvent}\n * @fires {@link GstWebRTCAPI#event:StateChangedEvent}\n * @fires {@link GstWebRTCAPI#event:ClosedEvent}\n * @fires {@link GstWebRTCAPI#event:ClientConsumerAddedEvent}\n * @fires {@link GstWebRTCAPI#event:ClientConsumerRemovedEvent}\n */\nclass ProducerSession extends EventTarget {\n constructor(comChannel, stream, consumerId) {\n super();\n\n this._comChannel = comChannel;\n this._stream = stream;\n this._state = SessionState.idle;\n this._clientSessions = {};\n this._consumerId = consumerId;\n }\n\n /**\n * The local stream produced out by this session.\n * @type {MediaStream}\n * @readonly\n */\n get stream() {\n return this._stream;\n }\n\n /**\n * The current producer session state.\n * @type {SessionState}\n * @readonly\n */\n get state() {\n return this._state;\n }\n\n /**\n * Starts the producer session.
\n * This method must be called after creating the producer session in order to start streaming. It registers this\n * producer session to the signaling server and gets ready to serve peer requests from consumers.\n *

Even on success, streaming can fail later if any error occurs during or after connection. In order to know\n * the effective streaming state, you should be listening to the [error]{@link GstWebRTCAPI#event:ErrorEvent},\n * [stateChanged]{@link GstWebRTCAPI#event:StateChangedEvent} and/or [closed]{@link GstWebRTCAPI#event:ClosedEvent}\n * events.

\n * @returns {boolean} true in case of success (may fail later during or after connection) or false in case of\n * immediate error (wrong session state or no connection to the signaling server).\n */\n start() {\n if (!this._comChannel || (this._state === SessionState.closed)) {\n return false;\n }\n\n if (this._state !== SessionState.idle) {\n return true;\n }\n\n const msg = {\n type: \"setPeerStatus\",\n roles: [\"listener\", \"producer\"],\n meta: this._comChannel.meta\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot start producer session\",\n error: new Error(\"cannot register producer to signaling server\")\n }));\n\n this.close();\n return false;\n }\n\n this._state = SessionState.connecting;\n this.dispatchEvent(new Event(\"stateChanged\"));\n return true;\n }\n\n /**\n * Terminates the producer session.
\n * It immediately disconnects all peer consumers attached to this producer session and unregisters the producer\n * from the signaling server.\n */\n close() {\n if (this._state !== SessionState.closed) {\n for (const track of this._stream.getTracks()) {\n track.stop();\n }\n\n if ((this._state !== SessionState.idle) && this._comChannel) {\n this._comChannel.send({\n type: \"setPeerStatus\",\n roles: [\"listener\"],\n meta: this._comChannel.meta\n });\n }\n\n this._state = SessionState.closed;\n this.dispatchEvent(new Event(\"stateChanged\"));\n\n this._comChannel = null;\n this._stream = null;\n\n for (const clientSession of Object.values(this._clientSessions)) {\n clientSession.close();\n }\n this._clientSessions = {};\n\n this.dispatchEvent(new Event(\"closed\"));\n }\n }\n\n onProducerRegistered() {\n if (this._state === SessionState.connecting) {\n this._state = SessionState.streaming;\n this.dispatchEvent(new Event(\"stateChanged\"));\n }\n\n if (this._consumerId) {\n const msg = {\n type: \"startSession\",\n peerId: this._consumerId\n };\n if (!this._comChannel.send(msg)) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot send session request to specified consumer\",\n error: new Error(\"cannot send startSession message to signaling server\")\n }));\n\n this.close();\n }\n }\n }\n\n onStartSessionMessage(msg) {\n if (this._comChannel && this._stream && !(msg.sessionId in this._clientSessions)) {\n const session = new ClientSession(msg.peerId, msg.sessionId, this._comChannel, this._stream);\n this._clientSessions[msg.sessionId] = session;\n\n session.addEventListener(\"closed\", (event) => {\n const sessionId = event.target.sessionId;\n if ((sessionId in this._clientSessions) && (this._clientSessions[sessionId] === session)) {\n delete this._clientSessions[sessionId];\n this.dispatchEvent(new CustomEvent(\"clientConsumerRemoved\", { detail: session }));\n }\n });\n\n session.addEventListener(\"error\", (event) => {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: `error from client consumer ${event.target.peerId}: ${event.message}`,\n error: event.error\n }));\n });\n\n this.dispatchEvent(new CustomEvent(\"clientConsumerAdded\", { detail: session }));\n }\n }\n\n onEndSessionMessage(msg) {\n if (msg.sessionId in this._clientSessions) {\n this._clientSessions[msg.sessionId].close();\n }\n }\n\n onSessionPeerMessage(msg) {\n if (msg.sessionId in this._clientSessions) {\n this._clientSessions[msg.sessionId].onSessionPeerMessage(msg);\n }\n }\n}\n\nexport default ProducerSession;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport ConsumerSession from \"./consumer-session.js\";\nimport ProducerSession from \"./producer-session.js\";\n\nconst SignallingServerMessageType = Object.freeze({\n welcome: \"welcome\",\n peerStatusChanged: \"peerStatusChanged\",\n list: \"list\",\n listConsumers: \"listConsumers\",\n sessionStarted: \"sessionStarted\",\n peer: \"peer\",\n startSession: \"startSession\",\n endSession: \"endSession\",\n error: \"error\"\n});\n\nfunction normalizePeer(peer, excludedId) {\n if (!peer || (typeof (peer) !== \"object\")) {\n return null;\n }\n\n const normalizedPeer = {\n id: \"\",\n meta: {}\n };\n\n if (peer.id && (typeof (peer.id) === \"string\")) {\n normalizedPeer.id = peer.id;\n } else if (peer.peerId && (typeof (peer.peerId) === \"string\")) {\n normalizedPeer.id = peer.peerId;\n } else {\n return null;\n }\n\n if (normalizedPeer.id === excludedId) {\n return null;\n }\n\n if (peer.meta && (typeof (peer.meta) === \"object\")) {\n normalizedPeer.meta = peer.meta;\n }\n\n Object.freeze(normalizedPeer.meta);\n return Object.freeze(normalizedPeer);\n}\n\nclass ComChannel extends EventTarget {\n constructor(url, meta, webrtcConfig) {\n super();\n\n this._meta = meta;\n this._webrtcConfig = webrtcConfig;\n this._ws = new WebSocket(url);\n this._ready = false;\n this._channelId = \"\";\n this._producerSession = null;\n this._consumerSessions = {};\n this._peers = {};\n\n this._ws.onerror = (event) => {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: event.message || \"WebSocket error\",\n error: event.error || new Error(\n this._ready ? \"transportation error\" : \"cannot connect to signaling server\")\n }));\n this.close();\n };\n\n this._ws.onclose = () => {\n this._ready = false;\n this._channelId = \"\";\n this._ws = null;\n\n this.closeAllConsumerSessions();\n\n if (this._producerSession) {\n this._producerSession.close();\n this._producerSession = null;\n }\n\n this.dispatchEvent(new Event(\"closed\"));\n };\n\n this._ws.onmessage = (event) => {\n try {\n const msg = JSON.parse(event.data);\n if (msg && (typeof (msg) === \"object\")) {\n switch (msg.type) {\n\n case SignallingServerMessageType.welcome:\n this._channelId = msg.peerId;\n try {\n this._ws.send(JSON.stringify({\n type: \"setPeerStatus\",\n roles: [\"listener\"],\n meta: meta\n }));\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot initialize connection to signaling server\",\n error: ex\n }));\n this.close();\n }\n break;\n\n case SignallingServerMessageType.peerStatusChanged: {\n if (msg.peerId === this._channelId) {\n if (!this._ready && msg.roles.includes(\"listener\")) {\n this._ready = true;\n this.dispatchEvent(new Event(\"ready\"));\n this.send({ type: \"list\" });\n this.send({ type: \"listConsumers\"});\n }\n\n if (this._producerSession && msg.roles.includes(\"producer\")) {\n this._producerSession.onProducerRegistered();\n }\n\n break;\n }\n\n const peer = normalizePeer(msg, this._channelId);\n if (!peer) {\n break;\n }\n\n const oldRoles = this._peers[msg.peerId] || [];\n this._peers[msg.peerId] = msg.roles;\n for (const role of [\"producer\", \"consumer\"]) {\n if (!oldRoles.includes(role) && msg.roles.includes(role)) {\n this.dispatchEvent(new CustomEvent(\"peerAdded\", { detail: { peer, role } }));\n } else if (oldRoles.includes(role) && !msg.roles.includes(role)) {\n this.dispatchEvent(new CustomEvent(\"peerRemoved\", { detail: { peerId: msg.peerId, role } }));\n }\n }\n break;\n }\n\n case SignallingServerMessageType.list: {\n this.clearPeers(\"producer\");\n this.addPeers(msg.producers, \"producer\");\n break;\n }\n\n case SignallingServerMessageType.listConsumers: {\n this.clearPeers(\"consumer\");\n this.addPeers(msg.consumers, \"consumer\");\n break;\n }\n\n case SignallingServerMessageType.sessionStarted:\n {\n const session = this.getConsumerSession(msg.peerId);\n if (session) {\n delete this._consumerSessions[msg.peerId];\n\n session.onSessionStarted(msg.peerId, msg.sessionId);\n if (session.sessionId && !(session.sessionId in this._consumerSessions)) {\n this._consumerSessions[session.sessionId] = session;\n } else {\n session.close();\n }\n }\n }\n break;\n\n case SignallingServerMessageType.peer:\n {\n const session = this.getConsumerSession(msg.sessionId);\n if (session) {\n session.onSessionPeerMessage(msg);\n } else if (this._producerSession) {\n this._producerSession.onSessionPeerMessage(msg);\n }\n }\n break;\n\n case SignallingServerMessageType.startSession:\n if (this._producerSession) {\n this._producerSession.onStartSessionMessage(msg);\n }\n break;\n\n case SignallingServerMessageType.endSession:\n {\n const session = this.getConsumerSession(msg.sessionId);\n if (session) {\n session.close();\n } else if (this._producerSession) {\n this._producerSession.onEndSessionMessage(msg);\n }\n }\n break;\n\n case SignallingServerMessageType.error:\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"error received from signaling server\",\n error: new Error(msg.details)\n }));\n break;\n\n default:\n throw new Error(`unknown message type: \"${msg.type}\"`);\n }\n }\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot parse incoming message from signaling server\",\n error: ex\n }));\n }\n };\n }\n\n get meta() {\n return this._meta;\n }\n\n get webrtcConfig() {\n return this._webrtcConfig;\n }\n\n get ready() {\n return this._ready;\n }\n\n get channelId() {\n return this._channelId;\n }\n\n get producerSession() {\n return this._producerSession;\n }\n\n createProducerSession(stream, consumerId) {\n if (!this._ready || !(stream instanceof MediaStream)) {\n return null;\n }\n\n if (this._producerSession) {\n if (this._producerSession.stream === stream) {\n return this._producerSession;\n } else {\n return null;\n }\n }\n\n const session = new ProducerSession(this, stream, consumerId);\n this._producerSession = session;\n\n session.addEventListener(\"closed\", () => {\n if (this._producerSession === session) {\n this._producerSession = null;\n }\n });\n\n return session;\n }\n\n createConsumerSession(producerId, offerOptions) {\n if (!this._ready || !producerId || (typeof (producerId) !== \"string\")) {\n return null;\n }\n\n if (offerOptions && (typeof(offerOptions) !== \"object\")) {\n offerOptions = undefined;\n }\n\n if (producerId in this._consumerSessions) {\n return this._consumerSessions[producerId];\n }\n\n for (const session of Object.values(this._consumerSessions)) {\n if (session.peerId === producerId) {\n return session;\n }\n }\n\n const session = new ConsumerSession(producerId, this, offerOptions);\n this._consumerSessions[producerId] = session;\n\n session.addEventListener(\"closed\", (event) => {\n let sessionId = event.target.sessionId;\n if (!sessionId) {\n sessionId = event.target.peerId;\n }\n\n if ((sessionId in this._consumerSessions) && (this._consumerSessions[sessionId] === session)) {\n delete this._consumerSessions[sessionId];\n }\n });\n\n return session;\n }\n\n getConsumerSession(sessionId) {\n if (sessionId in this._consumerSessions) {\n return this._consumerSessions[sessionId];\n } else {\n return null;\n }\n }\n\n closeAllConsumerSessions() {\n for (const session of Object.values(this._consumerSessions)) {\n session.close();\n }\n\n this._consumerSessions = {};\n }\n\n send(data) {\n if (this._ready && data && (typeof (data) === \"object\")) {\n try {\n this._ws.send(JSON.stringify(data));\n return true;\n } catch (ex) {\n this.dispatchEvent(new ErrorEvent(\"error\", {\n message: \"cannot send message to signaling server\",\n error: ex\n }));\n }\n }\n\n return false;\n }\n\n close() {\n if (this._ws) {\n this._ready = false;\n this._channelId = \"\";\n this._ws.close();\n\n this.closeAllConsumerSessions();\n\n if (this._producerSession) {\n this._producerSession.close();\n this._producerSession = null;\n }\n }\n }\n\n clearPeers(role) {\n for (const peerId in this._peers) {\n if (this._peers[peerId].includes(role)) {\n delete this._peers[peerId];\n this.dispatchEvent(new CustomEvent(\"peerRemoved\", { detail: { peerId, role } }));\n }\n }\n }\n\n addPeers(items, role) {\n items.forEach(item => {\n const peer = normalizePeer(item, this._channelId);\n if (peer) {\n this._peers[peer.id] = [role];\n this.dispatchEvent(new CustomEvent(\"peerAdded\", { detail: { peer, role } }));\n }\n });\n };\n}\n\nexport default ComChannel;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport defaultConfig from \"./config.js\";\nimport ComChannel from \"./com-channel.js\";\nimport SessionState from \"./session-state.js\";\n\n/** @import ConsumerSession from \"./consumer-session.js\"; */\n/** @import ProducerSession from \"./producer-session.js\"; */\n/** @import GstWebRTCConfig from \"./config.js\"; */\n/** @import RTCOfferOptions from \"typescript/lib/lib.dom.js\"; */\n\nclass GstWebRTCAPI {\n /**\n * @class GstWebRTCAPI\n * @classdesc The API entry point that manages a WebRTC.\n * @constructor\n * @param {GstWebRTCConfig} [userConfig] - The user configuration.
\n * Only the parameters different from the default ones need to be provided.\n */\n constructor(userConfig) {\n this._channel = null;\n this._producers = {};\n this._consumers = {};\n this._connectionListeners = [];\n this._peerListeners = [];\n\n const config = Object.assign({}, defaultConfig);\n if (userConfig && (typeof (userConfig) === \"object\")) {\n Object.assign(config, userConfig);\n }\n\n if (typeof (config.meta) !== \"object\") {\n config.meta = null;\n }\n\n this._config = config;\n this.connectChannel();\n }\n\n /**\n * @interface ConnectionListener\n */\n /**\n * Callback method called when this client connects to the WebRTC signaling server.\n * The callback implementation should not throw any exception.\n * @method ConnectionListener#connected\n * @abstract\n * @param {string} clientId - The unique identifier of this WebRTC client.
This identifier is provided by the\n * signaling server to uniquely identify each connected peer.\n */\n /**\n * Callback method called when this client disconnects from the WebRTC signaling server.\n * The callback implementation should not throw any exception.\n * @method ConnectionListener#disconnected\n * @abstract\n */\n\n /**\n * Registers a connection listener that will be called each time the WebRTC API connects to or disconnects from the\n * signaling server.\n * @param {ConnectionListener} listener - The connection listener to register.\n * @returns {boolean} true in case of success (or if the listener was already registered), or false if the listener\n * doesn't implement all callback functions and cannot be registered.\n */\n registerConnectionListener(listener) {\n if (!listener || (typeof (listener) !== \"object\") ||\n (typeof (listener.connected) !== \"function\") ||\n (typeof (listener.disconnected) !== \"function\")) {\n return false;\n }\n\n if (!this._connectionListeners.includes(listener)) {\n this._connectionListeners.push(listener);\n }\n\n return true;\n }\n\n /**\n * Unregisters a connection listener.
\n * The removed listener will never be called again and can be garbage collected.\n * @param {ConnectionListener} listener - The connection listener to unregister.\n * @returns {boolean} true if the listener is found and unregistered, or false if the listener was not previously\n * registered.\n */\n unregisterConnectionListener(listener) {\n const idx = this._connectionListeners.indexOf(listener);\n if (idx >= 0) {\n this._connectionListeners.splice(idx, 1);\n return true;\n }\n\n return false;\n }\n\n /**\n * Unregisters all previously registered connection listeners.\n */\n unregisterAllConnectionListeners() {\n this._connectionListeners = [];\n }\n\n /**\n * Creates a new producer session.\n *

You can only create one producer session at a time.
\n * To request streaming from a new stream you will first need to close the previous producer session.

\n *

You can only request a producer session while you are connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @param {MediaStream} stream - The audio/video stream to offer as a producer through WebRTC.\n * @returns {ProducerSession} The created producer session or null in case of error. To start streaming,\n * you still need to call {@link ProducerSession#start} after adding on the returned session all the event\n * listeners you may need.\n */\n createProducerSession(stream) {\n if (this._channel) {\n return this._channel.createProducerSession(stream);\n }\n return null;\n }\n\n createProducerSessionForConsumer(stream, consumerId) {\n if (this._channel) {\n return this._channel.createProducerSession(stream, consumerId);\n }\n return null;\n }\n\n /**\n * Information about a remote peer registered by the signaling server.\n * @typedef {object} Peer\n * @readonly\n * @property {string} id - The unique peer identifier set by the signaling server (always non-empty).\n * @property {object} meta - Free-form object containing extra information about the peer (always non-null,\n * but may be empty). Its content depends on your application.\n */\n\n /**\n * Gets the list of all remote WebRTC producers available on the signaling server.\n *

The remote producers list is only populated once you've connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @returns {Peer[]} The list of remote WebRTC producers available.\n */\n getAvailableProducers() {\n return Object.values(this._producers);\n }\n\n /**\n * Gets the list of all remote WebRTC consumers available on the signaling server.\n *

The remote consumer list is only populated once you've connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @returns {Peer[]} The list of remote WebRTC consumers available.\n */\n getAvailableConsumers() {\n return Object.values(this._consumers);\n }\n\n /**\n * @interface PeerListener\n */\n /**\n * Callback method called when a remote producer is added on the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#producerAdded\n * @abstract\n * @param {Peer} producer - The remote producer added on server-side.\n */\n /**\n * Callback method called when a remote producer is removed from the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#producerRemoved\n * @abstract\n * @param {Peer} producer - The remote producer removed on server-side.\n */\n /**\n * Callback method called when a remote consumer is added on the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#consumerAdded\n * @abstract\n * @param {Peer} consumer - The remote consumer added on server-side.\n * */\n /**\n * Callback method called when a remote consumer is removed from the signaling server.\n * The callback implementation should not throw any exception.\n * @method PeerListener#consumerRemoved\n * @abstract\n * @param {Peer} consumer - The remote consumer removed on server-side.\n * */\n\n /**\n * Registers a listener that will be called each time a peer is added or removed on the signaling server.\n * The listener can implement all or only some of the callback methods.\n * @param {PeerListener} listener - The peer listener to register.\n * @returns {boolean} true in case of success (or if the listener was already registered), or false if the listener\n * doesn't implement any methods from PeerListener and cannot be registered.\n */\n registerPeerListener(listener) {\n // refuse if no methods from PeerListener are implemented\n if (!listener || (typeof (listener) !== \"object\") ||\n ((typeof (listener.producerAdded) !== \"function\") &&\n (typeof (listener.producerRemoved) !== \"function\") &&\n (typeof (listener.consumerAdded) !== \"function\") &&\n (typeof (listener.consumerRemoved) !== \"function\"))) {\n return false;\n }\n\n if (!this._peerListeners.includes(listener)) {\n this._peerListeners.push(listener);\n }\n\n return true;\n }\n\n /**\n * Unregisters a peer listener.
\n * The removed listener will never be called again and can be garbage collected.\n * @param {PeerListener} listener - The peer listener to unregister.\n * @returns {boolean} true if the listener is found and unregistered, or false if the listener was not previously\n * registered.\n */\n unregisterPeerListener(listener) {\n const idx = this._peerListeners.indexOf(listener);\n if (idx >= 0) {\n this._peerListeners.splice(idx, 1);\n return true;\n }\n\n return false;\n }\n\n /**\n * Unregisters all previously registered peer listeners.\n */\n unregisterAllPeerListeners() {\n this._peerListeners = [];\n }\n\n /**\n * Creates a consumer session by connecting the local client to a remote WebRTC producer.\n *

You can only create one consumer session per remote producer.

\n *

You can only request a new consumer session while you are connected to the signaling server. You can use the\n * {@link ConnectionListener} interface and {@link GstWebRTCAPI#registerConnectionListener} method to\n * listen to the connection state.

\n * @param {string} producerId - The unique identifier of the remote producer to connect to.\n * @returns {ConsumerSession} The WebRTC session between the selected remote producer and this local\n * consumer, or null in case of error. To start connecting and receiving the remote streams, you still need to call\n * {@link ConsumerSession#connect} after adding on the returned session all the event listeners you may\n * need.\n */\n createConsumerSession(producerId) {\n if (this._channel) {\n return this._channel.createConsumerSession(producerId);\n }\n return null;\n }\n\n /**\n * Creates a consumer session by connecting the local client to a remote WebRTC producer and creating the offer.\n *

See {@link GstWebRTCAPI#createConsumerSession} for more information

\n * @param {string} producerId - The unique identifier of the remote producer to connect to.\n * @param {RTCOfferOptions} offerOptions - An object to use when creating the offer.\n * @returns {ConsumerSession} The WebRTC session between the selected remote producer and this local\n * consumer, or null in case of error. To start connecting and receiving the remote streams, you still need to call\n * {@link ConsumerSession#connect} after adding on the returned session all the event listeners you may\n * need.\n */\n createConsumerSessionWithOfferOptions(producerId, offerOptions) {\n if (this._channel) {\n return this._channel.createConsumerSession(producerId, offerOptions);\n }\n return null;\n }\n\n connectChannel() {\n if (this._channel) {\n const oldChannel = this._channel;\n this._channel = null;\n oldChannel.close();\n for (const key in this._producers) {\n this.triggerProducerRemoved(key);\n }\n for (const key in this._consumers) {\n this.triggerConsumerRemoved(key);\n }\n this._producers = {};\n this._consumers = {};\n this.triggerDisconnected();\n }\n\n this._channel = new ComChannel(\n this._config.signalingServerUrl,\n this._config.meta,\n this._config.webrtcConfig\n );\n\n this._channel.addEventListener(\"error\", (event) => {\n if (event.target === this._channel) {\n console.error(event.message, event.error);\n }\n });\n\n this._channel.addEventListener(\"closed\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n this._channel = null;\n for (const key in this._producers) {\n this.triggerProducerRemoved(key);\n }\n for (const key in this._consumers) {\n this.triggerConsumerRemoved(key);\n }\n this._producers = {};\n this._consumers = {};\n this.triggerDisconnected();\n if (this._config.reconnectionTimeout > 0) {\n window.setTimeout(() => {\n this.connectChannel();\n }, this._config.reconnectionTimeout);\n }\n });\n\n this._channel.addEventListener(\"ready\", (event) => {\n if (event.target === this._channel) {\n this.triggerConnected(this._channel.channelId);\n }\n });\n\n this._channel.addEventListener(\"peerAdded\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n\n if (event.detail.role === \"producer\") {\n this.triggerProducerAdded(event.detail.peer);\n } else {\n this.triggerConsumerAdded(event.detail.peer);\n }\n });\n\n this._channel.addEventListener(\"peerRemoved\", (event) => {\n if (event.target !== this._channel) {\n return;\n }\n\n if (event.detail.role === \"producer\") {\n this.triggerProducerRemoved(event.detail.peerId);\n } else {\n this.triggerConsumerRemoved(event.detail.peerId);\n }\n });\n }\n\n triggerConnected(clientId) {\n for (const listener of this._connectionListeners) {\n try {\n listener.connected(clientId);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerDisconnected() {\n for (const listener of this._connectionListeners) {\n try {\n listener.disconnected();\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerProducerAdded(producer) {\n if (producer.id in this._producers) {\n return;\n }\n\n this._producers[producer.id] = producer;\n for (const listener of this._peerListeners) {\n if (!listener.producerAdded) {\n continue;\n }\n\n try {\n listener.producerAdded(producer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerProducerRemoved(producerId) {\n if (!(producerId in this._producers)) {\n return;\n }\n\n const producer = this._producers[producerId];\n delete this._producers[producerId];\n\n for (const listener of this._peerListeners) {\n if (!listener.producerRemoved) {\n continue;\n }\n\n try {\n listener.producerRemoved(producer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerConsumerAdded(consumer) {\n if (consumer.id in this._consumers) {\n return;\n }\n\n this._consumers[consumer.id] = consumer;\n for (const listener of this._peerListeners) {\n if (!listener.consumerAdded) {\n continue;\n }\n\n try {\n listener.consumerAdded(consumer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n\n triggerConsumerRemoved(consumerId) {\n if (!(consumerId in this._consumers)) {\n return;\n }\n\n const consumer = this._consumers[consumerId];\n delete this._consumers[consumerId];\n\n for (const listener of this._peerListeners) {\n if (!listener.consumerRemoved) {\n continue;\n }\n\n try {\n listener.consumerRemoved(consumer);\n } catch (ex) {\n console.error(\"a listener callback should not throw any exception\", ex);\n }\n }\n }\n}\n\nGstWebRTCAPI.SessionState = SessionState;\n\nexport default GstWebRTCAPI;\n","/*\n * gstwebrtc-api\n *\n * Copyright (C) 2022 Igalia S.L. \n * Author: Loïc Le Page \n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport \"webrtc-adapter\";\nimport GstWebRTCAPI from \"./gstwebrtc-api.js\";\n\n/**\n * @external MediaStream\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStream\n */\n/**\n * @external RTCPeerConnection\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection\n */\n/**\n * @external RTCDataChannel\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel\n */\n/**\n * @external RTCOfferOptions\n * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer#options\n */\n/**\n * @external EventTarget\n * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget\n */\n/**\n * @external Event\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Event\n */\n/**\n * @external ErrorEvent\n * @see https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent\n */\n/**\n * @external CustomEvent\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent\n */\n/**\n * @external Error\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n */\n/**\n * @external HTMLVideoElement\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement\n */\n\nif (!window.GstWebRTCAPI) {\n window.GstWebRTCAPI = GstWebRTCAPI;\n}\n\nexport default GstWebRTCAPI;\n"],"names":["SDPUtils","Math","random","toString","substring","localCName","generateIdentifier","splitLines","blob","trim","split","map","line","splitSections","part","index","getDescription","sections","getMediaSections","shift","matchPrefix","prefix","filter","indexOf","parseCandidate","parts","candidate","foundation","component","protocol","toLowerCase","priority","parseInt","ip","address","port","type","i","length","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","undefined","writeCandidate","sdp","push","toUpperCase","join","parseIceOptions","parseRtpMap","parsed","payloadType","name","clockRate","channels","numChannels","writeRtpMap","codec","pt","preferredPayloadType","parseExtmap","id","direction","uri","attributes","slice","writeExtmap","headerExtension","preferredId","parseFmtp","kv","j","writeFmtp","parameters","Object","keys","params","forEach","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","ssrc","colon","attribute","value","parseSsrcGroup","semantics","ssrcs","getMid","mediaSection","mid","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","tag","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","profile","rtpmapline","fmtps","wildcardRtcpFb","find","existingFeedback","writeRtpDescription","kind","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","primarySsrc","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","JSON","parse","stringify","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","obj","cname","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","stream","track","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","maxMessageSize","isNaN","sctpPort","fmt","sctpMapLines","writeSctpDescription","media","sctp","output","generateSessionId","substr","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","version","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","charAt","module","exports","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","defineProperty","enumerable","get","prop","prototype","hasOwnProperty","call","r","Symbol","toStringTag","logDisabled_","deprecationWarnings_","extractVersion","uastring","expr","pos","match","wrapPeerConnectionEvent","window","eventNameToWrap","wrapper","RTCPeerConnection","proto","nativeAddEventListener","addEventListener","nativeEventName","cb","apply","this","arguments","wrappedCallback","e","modifiedEvent","handleEvent","_eventMap","Map","set","nativeRemoveEventListener","removeEventListener","has","unwrappedCb","delete","size","configurable","disableLog","bool","Error","disableWarnings","log","console","deprecated","oldMethod","newMethod","warn","isObject","val","compactObject","data","reduce","accumulator","isObj","isEmptyObject","assign","walkStats","stats","base","resultSet","endsWith","filterStats","result","outbound","streamStatsType","filteredResult","trackStats","trackIdentifier","trackStat","trackId","logging","shimGetUserMedia","browserDetails","navigator","mediaDevices","constraintsToChrome_","c","mandatory","optional","cc","ideal","exact","min","max","oldname_","oc","mix","advanced","concat","shimConstraints_","constraints","func","audio","remap","b","video","face","facingMode","getSupportedFacingModeLies","getSupportedConstraints","matches","enumerateDevices","then","devices","dev","some","label","includes","deviceId","shimError_","PermissionDeniedError","PermissionDismissedError","InvalidStateError","DevicesNotFoundError","ConstraintNotSatisfiedError","TrackStartError","MediaDeviceFailedDueToShutdown","MediaDeviceKillSwitchOn","TabCaptureError","ScreenCaptureError","DeviceCaptureError","message","constraint","constraintName","getUserMedia","onSuccess","onError","webkitGetUserMedia","bind","origGetUserMedia","cs","getAudioTracks","getVideoTracks","getTracks","stop","DOMException","Promise","reject","shimGetDisplayMedia","getSourceId","getDisplayMedia","sourceId","widthSpecified","width","heightSpecified","height","frameRateSpecified","frameRate","chromeMediaSource","chromeMediaSourceId","maxFrameRate","maxWidth","maxHeight","error","shimMediaStream","MediaStream","webkitMediaStream","shimOnTrack","_ontrack","f","origSetRemoteDescription","setRemoteDescription","_ontrackpoly","te","receiver","getReceivers","event","Event","transceiver","streams","dispatchEvent","shimGetSendersWithDtmf","shimSenderWithDtmf","pc","dtmf","_dtmf","createDTMFSender","_pc","getSenders","_senders","origAddTrack","addTrack","sender","origRemoveTrack","removeTrack","idx","splice","origAddStream","addStream","origRemoveStream","removeStream","s","RTCRtpSender","origGetSenders","senders","shimGetStats","origGetStats","getStats","selector","onSucc","onErr","fixChromeStats_","response","standardReport","report","standardStats","timestamp","localcandidate","remotecandidate","names","stat","makeMapStats","successCallbackWrapper_","resolve","shimSenderReceiverGetStats","RTCRtpReceiver","origGetReceivers","receivers","srcElement","MediaStreamTrack","err","shimAddTrackRemoveTrackWithNative","getLocalStreams","_shimmedLocalStreams","streamId","existingSenders","newSenders","newSender","shimAddTrackRemoveTrack","origGetLocalStreams","nativeStreams","_reverseStreams","_streams","newStream","replaceInternalStreamId","internalId","externalStream","internalStream","replace","RegExp","RTCSessionDescription","signalingState","t","oldStream","method","nativeMethod","methodObj","args","desc","origSetLocalDescription","setLocalDescription","replaceExternalStreamId","origLocalDescription","getOwnPropertyDescriptor","streamid","shimPeerConnection","webkitRTCPeerConnection","RTCIceCandidate","fixNegotiationNeeded","target","getConfiguration","sdpSemantics","nativeGetUserMedia","getSettings","nativeGetSettings","applyConstraints","nativeApplyConstraints","preferredMediaSource","code","mediaSource","RTCTrackEvent","mozRTCPeerConnection","modernStatsTypes","inboundrtp","outboundrtp","candidatepair","nativeGetStats","shimSenderGetStats","shimReceiverGetStats","shimRemoveStream","shimRTCDataChannel","DataChannel","RTCDataChannel","shimAddTransceiver","origAddTransceiver","addTransceiver","setParametersPromises","sendEncodings","shouldPerformCheck","encodingParam","test","rid","TypeError","parseFloat","scaleResolutionDownBy","RangeError","maxFramerate","getParameters","encodings","setParameters","catch","shimGetParameters","origGetParameters","shimCreateOffer","origCreateOffer","createOffer","all","finally","shimCreateAnswer","origCreateAnswer","createAnswer","shimLocalStreamsAPI","_localStreams","_addTrack","tracks","shimRemoteStreamsAPI","getRemoteStreams","_remoteStreams","_onaddstream","_onaddstreampoly","shimCallbacksAPI","addIceCandidate","successCallback","failureCallback","options","promise","withCallback","_getUserMedia","shimConstraints","errcb","shimRTCIceServerUrls","OrigPeerConnection","pcConfig","pcConstraints","iceServers","newIceServers","server","urls","url","generateCertificate","shimTrackEventTransceiver","shimCreateOfferLegacy","offerOptions","offerToReceiveAudio","audioTransceiver","getTransceivers","setDirection","offerToReceiveVideo","videoTransceiver","shimAudioContext","AudioContext","webkitAudioContext","shimRTCIceCandidate","NativeRTCIceCandidate","nativeCandidate","parsedCandidate","toJSON","sdpMid","sdpMLineIndex","writable","shimRTCIceCandidateRelayProtocol","relayProtocol","shimMaxMessageSize","_sctp","browser","mLine","sctpInDescription","isFirefox","getRemoteFirefoxVersion","canSendMMS","remoteIsFirefox","canSendMaxMessageSize","getCanSendMaxMessageSize","remoteMMS","getMaxMessageSize","Number","POSITIVE_INFINITY","shimSendThrowTypeError","wrapDcSend","dc","origDataChannelSend","send","byteLength","readyState","origCreateDataChannel","createDataChannel","dataChannel","channel","shimConnectionState","completed","checking","iceConnectionState","_onconnectionstatechange","origMethod","_connectionstatechangepoly","_lastConnectionState","connectionState","newEvent","removeExtmapAllowMixed","nativeSRD","shimAddIceCandidateNullOrEmpty","nativeAddIceCandidate","shimParameterlessSetLocalDescription","nativeSetLocalDescription","shimChrome","shimFirefox","shimSafari","userAgent","mozGetUserMedia","isSecureContext","supportsUnifiedPlan","RTCRtpTransceiver","adapter","commonShim","browserShim","adapterFactory","freeze","meta","signalingServerUrl","reconnectionTimeout","webrtcConfig","bundlePolicy","SessionState","idle","connecting","streaming","closed","WebRTCSession","EventTarget","constructor","peerId","comChannel","super","_peerId","_sessionId","_comChannel","_state","_rtcPeerConnection","state","rtcPeerConnection","close","uniToKeySyms","kbEventCodesToKeySyms","knownKbEventCodes","Set","getKeysymString","keySym","keyCodeUni","charCodeAt","eventsNames","mouseEventsNames","mousemove","mousedown","mouseup","touchEventsNames","touchstart","touchend","touchmove","touchcancel","keyboardEventsNames","keydown","keyup","getModifiers","modifiers","altKey","ctrlKey","metaKey","shiftKey","RemoteController","rtcDataChannel","consumerSession","_rtcDataChannel","_consumerSession","_videoElement","_videoElementComputedStyle","_videoElementKeyboard","_lastTouchEventTimestamp","_requestCounter","ErrorEvent","msg","CustomEvent","detail","ex","videoElement","attachVideoElement","element","HTMLVideoElement","getComputedStyle","eventName","setAttribute","previousElement","removeAttribute","sendControlRequest","request","_sendGstNavigationEvent","_computeVideoMousePosition","mousePos","x","y","videoWidth","videoHeight","padding","paddingLeft","paddingRight","paddingTop","paddingBottom","offsetX","offsetY","clientRect","getBoundingClientRect","border","left","borderLeftWidth","top","borderTopWidth","clientX","clientY","videoOffset","clientWidth","clientHeight","ratio","invRatio","preventDefault","delta_x","deltaX","delta_y","deltaY","modifier_state","button","focus","touch","changedTouches","identifier","pressure","force","timeStamp","_remoteController","_pendingCandidates","_mungeStereoHack","_offerOptions","mungeStereoHack","enable","remoteController","connect","ensurePeerConnection","offer","localDescription","onSessionStarted","ice","connection","ontrack","streamsChanged","ondatachannel","previousController","onicecandidate","mungeStereo","offerSdp","answerSdp","stereoRegexp","stereoPayloads","m","matchAll","payloadMatch","add","payload","isStereoRegexp","replaceAll","onSessionPeerMessage","ClientSession","ProducerSession","consumerId","_stream","_clientSessions","_consumerId","start","roles","clientSession","values","onProducerRegistered","onStartSessionMessage","session","onEndSessionMessage","SignallingServerMessageType","welcome","peerStatusChanged","list","listConsumers","sessionStarted","peer","startSession","endSession","normalizePeer","excludedId","normalizedPeer","ComChannel","_meta","_webrtcConfig","_ws","WebSocket","_ready","_channelId","_producerSession","_consumerSessions","_peers","onerror","onclose","closeAllConsumerSessions","onmessage","oldRoles","clearPeers","addPeers","producers","consumers","getConsumerSession","details","ready","channelId","producerSession","createProducerSession","createConsumerSession","producerId","items","item","userConfig","_channel","_producers","_consumers","_connectionListeners","_peerListeners","config","_config","connectChannel","registerConnectionListener","listener","unregisterConnectionListener","unregisterAllConnectionListeners","createProducerSessionForConsumer","getAvailableProducers","getAvailableConsumers","registerPeerListener","unregisterPeerListener","unregisterAllPeerListeners","createConsumerSessionWithOfferOptions","oldChannel","triggerProducerRemoved","triggerConsumerRemoved","triggerDisconnected","setTimeout","triggerConnected","triggerProducerAdded","triggerConsumerAdded","clientId","connected","disconnected","producer","producerAdded","producerRemoved","consumer","consumerAdded","consumerRemoved","GstWebRTCAPI"],"sourceRoot":""} \ No newline at end of file diff --git a/web/javascript/webrtc/index.html b/web/javascript/webrtc/index.html new file mode 100644 index 0000000..a6d23f9 --- /dev/null +++ b/web/javascript/webrtc/index.html @@ -0,0 +1,556 @@ + + + + + + GstWebRTC API + + + + +
+

GstWebRTC API

+
+
+
+ none + +
+
+
+
+
+
+
+ +
+
+
+

Remote streams

+
    +
+
+
+

Available consumers

+
    +
+
+
+ + diff --git a/web/manifest.json b/web/manifest.json index f4381cf..f49a640 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -1,35 +1,35 @@ -{ - "name": "kataglyphis_inference_engine", - "short_name": "kataglyphis_inference_engine", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} +{ + "name": "kataglyphis-webfrontendproject", + "short_name": "kataglyphis-webfrontendproject", + "start_url": ".", + "display": "standalone", + "background_color": "#0B1220", + "theme_color": "#0B1220", + "description": "Find out about ME", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} \ No newline at end of file diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4ae40f5..50de5b3 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -39,7 +39,7 @@ add_definitions(-DUNICODE -D_UNICODE) # of modifying this function. function(APPLY_STANDARD_SETTINGS TARGET) target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W3 /WX /wd4100 -Wno-cast-function-type-mismatch -Wno-unused-function) + target_compile_options(${TARGET} PRIVATE /W3 /wd4100 -Wno-cast-function-type-mismatch -Wno-unused-function) # /WX target_compile_options(${TARGET} PRIVATE /EHsc) target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") @@ -49,6 +49,16 @@ endfunction() set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") add_subdirectory(${FLUTTER_MANAGED_DIR}) +# Clang-CL compatibility: Ignore unused MSVC-specific flags like /await +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wno-unused-command-line-argument) + add_compile_options(-Wno-unused-lambda-capture) + add_compile_options(-Wno-error=unused-lambda-capture) + # Disable treat-warnings-as-errors globally to work around plugin bugs + # This overrides the /WX flag set by APPLY_STANDARD_SETTINGS + add_compile_options(-Wno-error) +endif() + # Application build; see runner/CMakeLists.txt. add_subdirectory("runner") diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index e2fe41a..96e1018 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { KataglyphisNativeInferencePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("KataglyphisNativeInferencePluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f791518..cc1b2a2 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,11 +4,12 @@ list(APPEND FLUTTER_PLUGIN_LIST kataglyphis_native_inference + permission_handler_windows url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - rust_lib_kataglyphis_inference_engine + kataglyphis_rustprojecttemplate ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20c..c15afd9 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ