[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = \"run -p alvr_xtask --\"\n\n[target.x86_64-pc-windows-msvc]\nrustflags = [\"-C\", \"target-feature=+crt-static\"]\n"
  },
  {
    "path": ".clang-format",
    "content": "BasedOnStyle:  WebKit\nIndentWidth: 4\nColumnLimit: 100\nBinPackArguments: false\nBinPackParameters: false\nAlignAfterOpenBracket: BlockIndent\nBreakBeforeBraces: Attach\nInsertNewlineAtEOF: true\n"
  },
  {
    "path": ".editorconfig",
    "content": "# Editor configuration, see https://editorconfig.org\n\n[*.{c,cpp,h,hpp}]\nindent_style = space\nindent_size = 4\ntab_width = 4\nmax_line_length = 100\nend_of_line = lf\ninsert_final_newline = true\n\n[.editorconfig]\nindent_style = space\nindent_size = 4\ntab_width = 4\nend_of_line = lf\ninsert_final_newline = true\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n\nalvr/server_openvr/cpp/alvr_server/include/** linguist-vendored\nalvr/server_openvr/cpp/alvr_server/nvEncodeAPI.h linguist-vendored\nalvr/server_openvr/cpp/platform/win32/NvCodecUtils.h linguist-vendored\nalvr/server_openvr/cpp/platform/win32/NvEncoder.cpp linguist-vendored\nalvr/server_openvr/cpp/platform/win32/NvEncoder.h linguist-vendored\nalvr/server_openvr/cpp/shared/** linguist-vendored\n# note: some of these folders contain ALVR code, but only a minor amount\nalvr/vulkan_layer/layer/** linguist-vendored\nalvr/vulkan_layer/util/** linguist-vendored\nalvr/vulkan_layer/wsi/** linguist-vendored\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: alvr\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!-- Note: If the bug affects multiple games, please open an issue for each game with the name of the game in the title. -->\n\n\n## Description\n<!-- Please add a brief summary of your issue -->\n\n<!-- If this is a regression, please do some bisection testing in the nightly releases history to find the first release that manifests the problem. -->\n\n## General Troubleshooting\n- [ ] I carefully followed the instructions in the [README](https://github.com/alvr-org/ALVR/blob/master/README.md) and successfully completed the setup wizard\n- [ ] I read the [ALVR GitHub Wiki](https://github.com/alvr-org/ALVR/wiki)\n\n## Environment\n\n### Hardware\n**Note**: for Linux, an upload to the [`hw-probe`](https://linux-hardware.org/) database is preferred: `hw-probe -all -upload`\n\n**CPU**:\n\n**GPU**:\n\n**GPU Driver Version**:\n\n**Audio**:\n\n### Installation\n**ALVR Version**:\n\n**ALVR Settings File**:\n\n**SteamVR Version**:\n\n**Install Type**:\n- [ ] Packaged (`exe`, `deb`, `rpm`, etc)\n- [ ] Portable (`zip`)\n- [ ] Source\n\n**OS Name and Version** (`winver` on Windows or `grep PRETTY_NAME /etc/os-release` on most Linux distributions):\n\n<!-- Feature Requests\nThe quickest way to get a new feature is to file a pull request; these will be considered, but may be closed if they're something we're not actively planning to work on. -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n  - package-ecosystem: \"cargo\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"monthly\"\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [ \"version-update:semver-minor\",\"version-update:semver-patch\" ]\n"
  },
  {
    "path": ".github/workflows/prepare-release.yml",
    "content": "name: Create release\n\nenv:\n  CARGO_TERM_COLOR: always\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Version\"\n        required: false\n        default: \"\"\n\njobs:\n  prepare_release:\n    runs-on: windows-2022\n    outputs:\n      release_ref: ${{ steps.output_ref.outputs.release_ref }}\n      upload_url: ${{ steps.create_release.outputs.upload_url }}\n      release_id: ${{ steps.create_release.outputs.id }}\n    steps:\n      - name: Configure git\n        run: git config --global core.autocrlf false\n\n      - uses: actions/checkout@v2\n\n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n\n      - name: Bump version\n        id: bump_version\n        env:\n          RUST_BACKTRACE: 1\n        run: |\n          $versionarg = \"${{ github.event.inputs.version }}\"\n          $versionarg = If ($versionarg.Length -gt 0) { \"--version $versionarg\" } else { \"\" }\n          $out = cargo xtask bump $versionarg.split()\n          echo $out\n          cargo update -p alvr_common\n          echo \"::set-output name=version_tag::$(echo $out | sls -CaseSensitive -Pattern '^v.*$')\"\n\n      - name: Push changes\n        uses: stefanzweifel/git-auto-commit-action@v4\n        with:\n          commit_message: \"[Auto] Bump version\"\n\n      - name: Output ref for later checkouts\n        id: output_ref\n        run: echo \"::set-output name=release_ref::$(git rev-parse HEAD)\"\n\n      - name: Create Release\n        id: create_release\n        uses: actions/create-release@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ steps.bump_version.outputs.version_tag }}\n          release_name: ALVR ${{ steps.bump_version.outputs.version_tag }}\n          draft: true\n          prerelease: false\n          commitish: ${{ steps.output_ref.outputs.release_ref }}\n\n  build_windows_streamer:\n    runs-on: windows-2022\n    needs: [prepare_release]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ needs.prepare_release.outputs.release_ref }}\n          submodules: true\n\n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          override: true\n\n      - uses: crazy-max/ghaction-chocolatey@v1\n        with:\n          args: install zip unzip pkgconfiglite wixtoolset\n\n      - name: Build and package ALVR\n        id: build\n        env:\n          RUST_BACKTRACE: 1\n        run: |\n          cargo xtask package-streamer --gpl --ci\n          cargo xtask package-launcher --ci\n\n      - name: Upload streamer\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: ./build/alvr_streamer_windows.zip\n          asset_name: alvr_streamer_windows.zip\n          asset_content_type: application/zip\n      - name: Upload launcher\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: ./build/alvr_launcher_windows.zip\n          asset_name: alvr_launcher_windows.zip\n          asset_content_type: application/zip\n\n  build_linux_streamer:\n    runs-on: ubuntu-22.04\n    needs: [prepare_release]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ needs.prepare_release.outputs.release_ref }}\n          submodules: true\n\n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          override: true\n\n      - name: Build and install dependencies\n        env:\n          RUST_BACKTRACE: 1\n        run: |\n          sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 25088A0359807596\n          echo \"deb http://ppa.launchpad.net/pipewire-debian/pipewire-upstream/ubuntu $(lsb_release -cs) main\" | sudo tee -a /etc/apt/sources.list.d/pipewire-upstream.list\n          sudo apt-get update\n          sudo apt-get install libfuse2 build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libxrandr-dev libunwind-dev libgtk-3-dev libpipewire-0.3-dev libspa-0.2-dev\n\n      - name: Build and package ALVR (.tar.gz)\n        id: build\n        env:\n          RUST_BACKTRACE: 1\n        run: |\n          cargo xtask package-streamer --gpl --ci\n          cargo xtask package-launcher --ci\n\n      - name: Upload streamer (tar.gz)\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: ./build/alvr_streamer_linux.tar.gz\n          asset_name: alvr_streamer_linux.tar.gz\n          asset_content_type: application/gzip\n      - name: Upload launcher\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: ./build/alvr_launcher_linux.tar.gz\n          asset_name: alvr_launcher_linux.tar.gz\n          asset_content_type: application/gzip\n\n  build_flatpak_bundle:\n    runs-on: ubuntu-latest\n    needs: [prepare_release]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ needs.prepare_release.outputs.release_ref }}\n          submodules: true\n\n      - name: Build and install dependencies\n        env:\n          RUST_BACKTRACE: 1\n        run: |\n          sudo apt-get update\n          sudo apt-get install flatpak flatpak-builder\n          sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo\n\n      - name: Build and package ALVR flatpak (.flatpak)\n        id: build_flatpak\n        run: |\n          sudo flatpak-builder --repo=.flatpak-repo --install-deps-from=flathub --force-clean --default-branch=stable --arch=x86_64 .flatpak-build-dir alvr/xtask/flatpak/com.valvesoftware.Steam.Utility.alvr.json\n          flatpak build-bundle .flatpak-repo com.valvesoftware.Steam.Utility.alvr.flatpak com.valvesoftware.Steam.Utility.alvr stable --runtime\n\n      - name: Upload flatpak streamer for Linux\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: com.valvesoftware.Steam.Utility.alvr.flatpak\n          asset_name: com.valvesoftware.Steam.Utility.alvr.flatpak\n          asset_content_type: application/octet-stream\n\n  build_android_client:\n    runs-on: ubuntu-latest\n    needs: [prepare_release]\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ needs.prepare_release.outputs.release_ref }}\n          submodules: true\n        \n      - uses: actions-rs/toolchain@v1\n        with:\n          toolchain: stable\n          target: aarch64-linux-android\n          override: true\n      - uses: actions/setup-java@v2\n        with:\n          distribution: \"temurin\"\n          java-version: \"17\"\n      - uses: android-actions/setup-android@v3\n        with:\n          packages: \"platforms;android-32\"\n      - uses: nttld/setup-ndk@v1\n        id: setup-ndk\n        with:\n          ndk-version: r26b\n\n      - name: Build and package ALVR\n        id: build\n        env:\n          RUST_BACKTRACE: 1\n          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}\n        run: cargo xtask package-client --ci\n\n      - name: Sign APK\n        uses: ilharp/sign-android-release@v1\n        id: sign_apk\n        with:\n          releaseDir: build/alvr_client_android\n          signingKey: ${{ secrets.SIGNING_KEY }}\n          keyAlias: ${{ secrets.KEY_ALIAS }}\n          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}\n          keyPassword: ${{ secrets.KEY_PASSWORD }}\n          buildToolsVersion: 34.0.0\n\n      - name: Upload APK\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.prepare_release.outputs.upload_url }}\n          asset_path: ${{ steps.sign_apk.outputs.signedFile }}\n          asset_name: alvr_client_android.apk\n          asset_content_type: application/vnd.android.package-archive\n"
  },
  {
    "path": ".github/workflows/queue.yml",
    "content": "name: Merge queue only checks\n\n# Merge queue checks need to be selected as required for prs to actually run, so simply skip them on prs\non:\n  pull_request:\n  merge_group:\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  check-linux-old:\n    if: github.event_name == 'merge_group'\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Install dependencies\n        run: |\n          sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 25088A0359807596\n          echo \"deb http://ppa.launchpad.net/pipewire-debian/pipewire-upstream/ubuntu $(lsb_release -cs) main\" | sudo tee -a /etc/apt/sources.list.d/pipewire-upstream.list\n          sudo add-apt-repository universe\n          sudo apt-get update\n          sudo apt-get install libfuse2 build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libx265-dev cmake libasound2-dev libjack-jackd2-dev libxrandr-dev libunwind-dev libgtk-3-dev libpipewire-0.3-dev libspa-0.2-dev\n\n      - name: Prepare deps\n        env:\n          RUST_BACKTRACE: 1\n        run:  cargo xtask prepare-deps --platform linux --no-nvidia\n\n      - run: cargo clippy\n\n  check-msrv-windows:\n    if: github.event_name == 'merge_group'\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      # Fix actions rust install and msrv both being stupid\n      - run: rustup update\n\n      - name: Prepare deps\n        env:\n          RUST_BACKTRACE: 1\n        run: cargo xtask prepare-deps --platform windows\n\n      - run: cargo xtask check-msrv\n\n  check-msrv-linux:\n    if: github.event_name == 'merge_group'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Install dependencies\n        run: |\n          sudo apt update\n          sudo apt install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libasound2-dev libxrandr-dev libunwind-dev libgtk-3-dev libpipewire-0.3-dev libspa-0.2-dev\n\n      - name: Prepare deps\n        env:\n          RUST_BACKTRACE: 1\n        run:  cargo xtask prepare-deps --platform linux --no-nvidia\n\n      - run: cargo xtask check-msrv\n\n  check-licenses:\n    if: github.event_name == 'merge_group'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      - run: cargo xtask check-licenses\n"
  },
  {
    "path": ".github/workflows/rust.yml",
    "content": "name: Rust\n\non:\n  pull_request:\n    branches: [master]\n  merge_group:\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  check-windows:\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Prepare deps\n        env:\n          RUST_BACKTRACE: 1\n        run: cargo xtask prepare-deps --platform windows\n\n      - run: cargo xtask clippy --ci\n\n  check-linux:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Install dependencies\n        run: |\n          sudo apt update\n          sudo apt install build-essential pkg-config nasm libva-dev libdrm-dev libvulkan-dev libx264-dev libasound2-dev libxrandr-dev libunwind-dev libgtk-3-dev libpipewire-0.3-dev libspa-0.2-dev\n\n      - name: Prepare deps\n        env:\n          RUST_BACKTRACE: 1\n        run:  cargo xtask prepare-deps --platform linux --no-nvidia\n\n      - run: cargo xtask clippy --ci\n\n  check-macos:\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n      - uses: Swatinem/rust-cache@v2\n\n      # This step currently does nothing on macos, but might in the future\n      # - run: cargo xtask prepare-deps --platform macos\n\n      - run: cargo clippy\n\n  build-android:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          targets: aarch64-linux-android\n      - uses: Swatinem/rust-cache@v2\n      - uses: actions/setup-java@v2\n        with:\n          distribution: 'temurin'\n          java-version: '17'\n      - uses: android-actions/setup-android@v3\n        with:\n          packages: 'platforms;android-32'\n      - uses: nttld/setup-ndk@v1\n        id: setup-ndk\n        with:\n          ndk-version: r26b\n\n      - name: Install deps\n        run: cargo install cargo-apk\n\n        # Create folder without content to make the build succeed\n      - run: mkdir -p deps/android_openxr/arm64-v8a\n\n      - name: Build client\n        env:\n          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}\n        working-directory: ./alvr/client_openxr\n        run: cargo xtask build-client\n\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: true\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Run tests\n        run: cargo test -p alvr_session\n\n  check-format:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: rustfmt\n\n      - run: cargo xtask check-format\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: 'Close stale issues'\non:\n  schedule:\n    - cron: '0 0 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v9\n        with:\n          stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'\n          exempt-issue-labels: 'bug,documentation,enhancement,good-first-issue,hep-wanted,needs-testing,release'\n          exempt-draft-pr: true\n"
  },
  {
    "path": ".github/workflows/wiki.yml",
    "content": "name: Publish to GitHub Wiki\n\non:\n  push:\n    branches: [master]\n  workflow_dispatch:\n\njobs:\n  wiki:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Publish to wiki\n        uses: cmbrose/github-docs-to-wiki@v0.24\n        with:\n          githubToken: ${{ secrets.GH_PAT }}\n          rootDocsFolder: \"wiki\"\n"
  },
  {
    "path": ".gitignore",
    "content": "Debug\nRelease\n*.opensdf\n*.sdf\n*.suo\n*.filters\n*.user\n*.dll\n*.exe\n*.mexw64\n*.aps\n\n# editor\n.vs/\n.idea/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\nrelease-files/\n\n# Ignore generated file with harvested file paths for WiX\nwix/harvested.wxs\n\n# Other WiX files\n*.wixobj\n*.wixpdb\n*.msi\n\n# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\n#Cargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# build result\n/build/\ndist\n\n.vscode/\n.DS_Store\n.ccls-cache\n\n# dependencies installed by xtask\ndeps\n\n# dependencies installed by node\nnode_modules\n\n# The .gitignore for android is project folder.\n\n# flatpak\n.flatpak*\n\nALVR-Launcher\n\n#allows adding CMake files for some IDEs to parse C++ files correctly\nCMakeLists.txt\ncmake-build-debug/\n\n*.jks"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"openvr\"]\n\tpath = openvr\n\turl = https://github.com/ValveSoftware/openvr.git\n\tshallow = true\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## v20.11.0\n\n* Add flatpak launcher (by @failboat78 #2207)\n* Fix Linux/Nvenc error popups (by @failboat78 #2338)\n* Add debug groups (by @zarik5 #2332)\n* Fix black screen on Focus 3 (by @zarik5 #2346)\n* Fix launcher installation popup (by @zarik5 #2356)\n* Fix crash on invalid haptics (by @zarik5 #2355)\n* Rework logging (by @zarik5 #2351)\n* Disable \"Adapt to framerate\" by default (by @zarik5)\n* Show body skeleton in lobby (by @zarik5 #2366)\n* Add multimodal input (by @zarik5 #2367)\n* Fix high CPU usage on Linux (by @The-personified-devil #2372 #2375)\n* Fix stream being half width resolution (by @zarik5 #2378)\n\n## v20.10.0\n\n* Internal refactors of the client graphics (wgpu) and server code architecture (by @zarik5)\n* Fix Quest 3 not supporting 120Hz (by @The-personified-devil)\n* Tracking timing jitter (by @zarik5 #2285)\n* Support VRChat hand tracking (by @ReinaS-64892 @AdalynBlack @zarik5 #2295, @zarik5 #2313 #2323)\n* Fix crash with some games (by @szneqz #2309)\n* Fix crash when shutting down launcher (by @zarik5 #2320)\n* Fix crash with vibration on Focus 3 (by @zarik5 #2324)\n\n### v20.9.1 (2024-07-06)\n\n* Fix performance issues on lobby room.\n\n## v20.9.0 (2024-07-06)\n\n* Display license inside the dashboard (by @zarik5 #2117)\n* Add GPU checks for Linux (by @Meister1593 #2110)\n* Reorder settings (by @zarik5 #2119)\n* Shallow rename \"client\" -> \"device\" (by @zarik5 #2120)\n* Update manifest for AppLab (by @zarik5 #2146)\n* Allow recentering in the lobby (by @zarik5 #2155)\n* Tweak graph labels and colors (by @zarik5 #2176)\n* Fix controller detection on Pico Neo 3 Link (by @HoLo85 #2192)\n* Loosen restrictions on device selection with VoiceMeeter (by @xuan25 #2209)\n* Fix HEVC black screen on Linux/VAAPI (by @Nibor62 #2203)\n* Show hands and controllers in the lobby (by @zarik5 #2218)\n* Add PipeWire support on Linux (by @Meister1593 #1973)\n\n### v20.8.1 (2024-05-08)\n\n* Fix crash on Linux (by @SniperJoe #2108)\n\n## v20.8.0 (2024-05-04)\n\n* Bring back settings tabs (by @Meister1593 #2076)\n* Make FFE shader much lighter (by @yoyobuae #2083)\n* Improve firewall rules on Linux (by @Meister1593 #2078)\n* Fix error message not displaying correctly on Linux sometimes (by @SniperJoe #2088)\n* Fix Nvidia encoder on some Linux systems (by @Xaphiosis @nowrep #2074)\n* Fix client warmstart crash (by @ShootingKing-AM #2084)\n* Fix segfault on Linux (by @SniperJoe #2090)\n* Fix protocol break in v20.7.0 (by @zarik5 2098)\n\n### v20.7.1 (2024-04-11)\n\n* Fix colors on Pico (by @shinyquagsire23 and @zarik5)\n* Fix joystick gesture offset on right hand (by @jarettmillard #2065)\n\n## v20.7.0 (2024-04-06)\n\n* Add AV1 support on Windows/AMD (by @barnabwhy #1967)\n* Add AV1 support on Linux/Nvidia (by @wsippel #1975, @Vixea #1994)\n* Add HDR support on Windows (by @shinyquagsire23 #2030)\n* Add full range encoding support (by @shinyquagsire23 #2011, @Vixea #1971)\n* Fix VAAPI encoder on Intel GPUs (by @nowrep #1981)\n* Add Pre-Analysis support on Windows/AMD (by @barnabwhy #1985)\n* Add linux hardware encoding checks (by @Meister1593 #2042 #2055)\n* Add full body tracking support on Quest (by @barnabwhy #1979, @galister #1984)\n* Fix aux-bones in SteamVR (by @shinyquagsire23 #2009)\n* Make USB connection work when WiFi is disabled (by @Meister1593 #1962)\n* Add mDNS discovery on the server (by @zarik5 #1978)\n* Fix audio when disconnecting headphones in headset (by @Okabintaro #2025 #2040)\n* Add compatibility layer for surround audio devices (by @barnabwhy #2026)\n* Fix black screen on Vive Focus 3, XR Elite (by @zarik5)\n* Fix stuttering on Linux after recentering (by @galister #2017)\n* Fixes for Flatpak (by @jkcdarunday #1980, @Meister1593 #2039 #2044)\n* Change colors of lobby room (by @barnabwhy #1968)\n* Remove WIX installer (by @zarik5)\n* Remove AppImage (by @Meister1593 #2056)\n\n### v20.6.1 (2024-01-26)\n\n* Add AV1 support, only for Linux/VAAPI, with 10bits support (by @wsippel #1955 #1964)\n* Fix image corruption on h264/VAAPI (by @galister / @nowrep #1956)\n\n## v20.6.0 (2024-01-10)\n\n* Add tongue tracking for Quest Pro (by @zarik5)\n  * This is a breaking change in the protocol, but only affects Quest Pro users.\n  * Only VRCFT ALVR module v1.2.0 and up is supported\n* Add Quest 3 emulation mode + icons for SteamVR HUD (by @Goodguy140 #1926)\n* Add Type of Service (ToS) socket settings, tested only on Linux (by @Vixea #1946)\n* Add software decoding option and fallback (by @20kdc #1933)\n* Add Baseline encoding option for h264 (by @20kdc #1932)\n* Fix ADB connection (by @The-personified-devil #1942)\n* Fix rare bug preventing reconnections on wifi (by @zarik5)\n\n## v20.5.0 (2023-12-05)\n\n* Fix Vulkan layer GPU loading (by @nairaner #1847)\n* Fix dynamic bitrate for VAAPI (by @nowrep #1863)\n* Add notification tips (by @zarik5 #1865)\n* Fix hand tracking for Lynx R1 (by @technobaboo #1874)\n* Various wiki updates\n* Fix battery update during streming (by @zarik5)\n* Fix playspace recentering delay (by @zarik5)\n* Support eye tracking for Pico 4 Pro (by @zarik5 @Meister1593 #1897)\n* Add desktop file for Flatpak (by @Vixea #1906)\n* Install audio dependencies from the setup wizard (by @Meister1593 #1893, @zarik5)\n* Significantly reduce latency with NVENC on Linux (by @nowrep @Xaphiosis #1911)\n* Fix SteamVR hanging when restarting on Linux (by @Vixea @zarik5)\n* Other dashboard updates\n\n### v20.4.3 (2023-10-06)\n\n* Fix dashboard crash on Windows\n* Fix settings reset bug when upgrading (session extrapolation failed)\n\n### v20.4.2 (2023-09-22)\n\n* Fix YVR crash because of invalid controller bindings\n\n### v20.4.1 (2023-09-19)\n\n* Fix inverted `Enable skeleton` switch\n* Add `Only touch` gestures option\n\n## v20.4.0 (2023-09-15)\n\n* Full hand tracking gestures support, with joystick (by @barnabwhy #1794)\n* Fully remappable controller buttons (by @zarik5 #1817)\n* Custom controller profile (by @zarik5)\n* Fix Nvidia support on Linux (by @Killrmemz #1830)\n\n### v20.3.1 (2023-09-11)\n\n* Fix some controller buttons not working\n* Fix changing controller emulation profile not triggering a SteamVR restart\n* Add back Rift S controller emulation profile\n\n## v20.3.0 (2023-09-08)\n\n* Add Lynx R1 headset support (by @zarik5 #1823)\n  * Currently there is an issue with hand tracking which is being investigated\n* Make settings sections collapsible (by @zarik5)\n* Other UI tweaks (by @zarik5)\n* *Actually* fix controller freeze (by @zarik5)\n* Fix Pico controller buttons (by @zarik5 @galister @Meister1593 #1820)\n* Fix bitrate hikes when \"Adapt to framerate\" is enabled (by @zarik5)\n* Fix Nvenc encoder on Linux (by @Killrmemz #1824)\n* Timeout connection if lingering (by @zarik5)\n* Fix warmstart crash on client (by @ShootingKing-AM #1813)\n\n### v20.2.1 (2023-08-27)\n\n* Fix VRCFaceTracking mode panicing.\n* (Potential) Fix for dashboard crash on Wayland.\n\n## v20.2.0 (2023-08-26)\n\n* Add Flatpak build (by @CharlieQLe #1683 #1724 #1735 #1742, @Meister1593 #1769)\n* Finish VRCFaceTracking support (by @zarik5)\n  * You can download the ALVR Module from the VRCFaceTracking app itself.\n  * Only supports the Quest Pro at the moment.\n* New more performant sockets implementation (by @zarik5)\n  * Zero copy + zero allocations, and provides better packet prioritization.\n* Avoid controller freezing during high latency (by @zarik5)\n* Add message popups on Linux (disabled on the appimage build) (by @zarik5 #1711)\n* Show backtrace on unhandled exceptions (Windows only) (by @zarik5)\n  * Previously these would make SteamVR hard crash without any useful log\n* Optionally show full backtraces for logs (by @zarik5)\n* Add option to select client log level (by zarik5)\n* Make Log tab stick to bottom (by @zarik5)\n* Encoder fixes on Linux (by @nowrep #1751 #1753 #1767 #1768 #1796, @Vixea #1805)\n* Use Constant bitrate mode by default\n* Support rolling video recording (by @zarik5)\n* Fix OpenGL crash on the client (by @ShootingKing-AM #1801)\n* Fix white dashboard bug on Linux (by @zarik5)\n\n## v20.1.0 (2023-06-20)\n\n* Fix firewall rules on Windows (by @zarik5)\n* Fix firewall rules on linux for the tar.gz (by @Vixea #1675)\n* Add bitrate graph (by @zarik5 #1689)\n* Add encoder latency limiter (by @zarik5 #1678)\n* Fix network latency limiter (by @zarik5)\n* Fix image corruption on AMD (by @zarik5 #1681)\n* Fix dashboard audio dropdowns on Linux (by @zarik5)\n* Add connection status for clients (by @zarik5 #1688)\n* Fix HMD plugged status (by @zarik5)\n* Fix crash on some Unreal Engine 5 games (by @deiteris #1685)\n* Add option to disable game render optimization (by @zarik5)\n* Add separate history size for bitrate (by @zarik5)\n\n# v20.0.0 (2023-06-02)\n\n* New OpenXR-based client, add support for Vive Focus 3/XR Elite, Pico 4/Neo 3 and YVR 1/2. Worked on by:\n  * @zarik5 #1321\n  * @galister #1321, #1442\n  * @deiteris #1434, #1439, #1445\n* New egui (OpenGL) dashboard\n  * The launcher is replaced by the new dashboard executable.\n  * by @Kirottu #1247 #1274, @zarik5, @m00nwtchr #1292, @TheComputerNerd88 #1574 #1575 #1576 1582\n* Add position and rotation recentering modes (by @zarik5 #1321)\n  * Defaults to local floor and local yaw.\n* Add support for eye and face tracking (by @zarik5 #1577)\n  * Currently supporting VRChat Eye OSC, VRCFaceTracking support coming soon\n* Reduce game rendering latency (by @zarik5)\n* Apply some settings in real-time (by @zarik5 #1635)\n* New more consistent controller prediction algorithm (by @zarik5 #1561)\n* Controller input fixes (by @zarik5 #1560)\n* Soft-toggle controllers at runtime (by @galister #1600)\n* New wiki hosted in the main git tree (by @m00nwtchr #1309)\n* Send client log to streamer (by @zarik5)\n* Encoder improvements (by @nowrep #1562 #1565 #1568, @deiteris #1403 #1422 #1400 #1402, @zarik5 #1564)\n* Remove Forward Error Correction (by @zarik5: #1384, #1389; @deiteris: #1386, #1387, #1390)\n* Unified code for NAL parsing (by @deiteris #1403, #1422, #1400, #1402)\n* Some tweaks for alvr_client_core compatibility (by @ShootingKing-AM #1580 #1578 #1586 #1624 #1621)\n* Fix server build with clang (by @nowrep)\n\n### v19.1.1 (2023-03-03)\n\n* Relax discovery protocol for future ALVR versions\n\n## v19.1.0 (2023-02-14)\n\n* Encoder improvements and new Linux server compositor:\n  * @deiteris #1227, #1252, #1281, #1287, #1302, #1304, #1318, #1331, #1336, #1368, #1393, #1367, #1397\n  * @Vixea #1227, #1254, #1412\n  * @nowrep #1251, #1261, #1253, #1267, #1264, #1266, #1273, #1272, #1277, #1280, #1279, #1278, #1282, #1265, #1294, #1295, #1312, #1314, #1316, #1325, #1328, #1326, #1330, #1334, #1338, #1329, #1346, #1350, #1357, #1352, #1348, #1365, #1349, #1361, #1370, #1372, #1393\n  * @m00nwtchr #1347\n  * @galister #1428, #1429\n* Controller fixes (by @Timocop #1236 #1241)\n* Vulkan layer fixes (by @nowrep #1291, #1293, #1324, #1339, #1376)\n* Show client IP in the headset and dashboard (by @zarik5)\n* Disable Wi-Fi scans (by @zarik5)\n* Reduce lag after loading screens (by @zarik5)\n* Fix server debug builds (by @nowrep #1288)\n* Add trigger/grip threshold (by @sctanf)\n* Don't spam stdout on Linux (by @nowrep #1317)\n* Fix recentering on Linux (by @nowrep #1353)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Style\n\nChecklist for code style. This is on top of common Rust styling rules. These rules are not mandatory but I might point them out if not respected in PRs :) -zarik\n\n## Naming\n\n- Respect Rust naming conventions (not respecting this will cause a warning).\n- Add useful information in the name, with the exception of indices for iterating.\n- Do not put type or scope information in the name.\n- Avoid abbreviations.\n- Avoid prefixes and suffixes.\n- if necessary prefer suffixes rather than prefixes.\n- `_ref` or `_mut` suffixes are accepted when you want to put emphasis on that the variable is a mutable reference, and so assigning values has side effects, even if not consumed later. Suffixes are not needed if the variable is only mutable or instead a immutable reference.\n- Use `maybe_` prefix if the variable is an `Option` or `Result`. Omit if it's clear from the context. Never use it for parameter or field definitions.\n- Shadowing is encouraged.\n- If shadowing cannot be used and two variables have similar meaning but different types, suffix the variable with the least useful or least specialized type with its type. Example:\n\n    ```rust\n    let myfile_string = \"./file.txt\";\n    let myfile = Path::new(my_file_string);\n    ```\n\n- If both directory and file paths are used in the same context, suffix directories with `_dir` and files with `_path`. Suffix file names with `_fname`.\n\n## Top level definitions\n\n- For each file, define in order: private imports, public imports, ffi bindings import, private constants, public constants, private structs, public structs, private top level functions, public top level functions.\n\n### Imports\n\n- Do not leave spaces between imports, only between the private and public import blocks.\n- Define imports in alphabetical order (use cargo fmt).\n- Group imports using braces when there are common parts of the path.\n\n### Structs\n\n- Prefer adding trait bounds to the impl type parameters instead of struct type parameters\n- Define in order: struct, Default impl, custom impl, Drop impl, all in the same module. Do not split the custom impl.\n\n## Spacing\n\nSmartly use empty newlines between blocks of code to improve legibility\n\n- Rust recommends not to specify the return keyword when returning at the end of a function. To put emphasis on the return expression, isolate it with a empty new line just before.\n- Inside each block (function, if/else/while/for etc or just braces) make so that there are roughly 2 to 6 blocks of code separated by empty lines for the amount of code that fits in a single screen (long functions that span multiple screens can have way more than 6 block). Check existing codebase for an example.\n- Spaces between groups of statements should be done so the groups are similar is size and that each achieves a specific purpose. You should be able to easily describe what the group does in few words, even if you don't comment it (because the meaning should be self evident).\n- Do not define variables at the start of the function/block, but do define them at the start of the functional group delimited by spaces.\n\n## Comments\n\n- It's important to use comments when the meaning or inner workings of a piece of code is not clear from the context. Well-named symbols (variables and functions) are often enough. In doubt do use comments.\n- Do not add comments about how certain parts of the language/std library work, unless it's about quirks of features.\n\n## Panicking and error handling\n\n- Use of `panic!()` is discouraged\n- When matching exhaustively, prefer `unreachable!()` over `panic!()` for certain branches.\n- `unwrap()` is discouraged, but prefer `unwrap()` over `expect()` (bubble up the error instead).\n- Prefer `.get()` to index a collection rather than `[]`, unless extremely certain it will never index out of bounds.\n- Add a `// # Safety` comment before a statement that contains a `unwrap()` or raw indexing, explaining why it should never crash.\n- Use `todo!()` to mark unfinished code (it returns `!` and so it helps with the missing return statement).\n\n## Code repetition and maintainability\n\n- Lean towards the DRY rule, without overdoing it.\n- Extract a piece of code (in a function or lambda) only when it is used two or more times and it doesn't depend on many parameters.\n- Always extract constants for \"arbitrary\" values (literals) which are chosen with no absolute rule and makes sense to change in the future. Example: time interval between resending discovery packet. Opposite example: number of eyes on a human head (it's always going to be 2, no need to use a constant to change its value in the future :) ).\n- Prefer defining constants at the start of the file, even if used locally in a single function.\n- Prefer using \"complex\" types for constants, if the std library allows it. Example: prefer using `Duration` instead of an integer type if the constant represents a time duration. Same with `Path` vs string.\n\n## Structural soundness\n\n- Try to avoid invalid states in the data, using Rust enums. Example: do not use `resumed` + `streaming` boolean variables if the state `resumed == false` + `streaming == true` is invalid. Instead use `enum State { Paused, Resumed, Streaming }`.\n- Make full use of pattern matching with `if let` and `while let`, this reduces the use of `unwrap()`.\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\"alvr/*\"]\n\n[workspace.package]\nversion = \"21.0.0-dev12\"\nedition = \"2024\"\nrust-version = \"1.88\"\nauthors = [\"alvr-org\"]\nlicense = \"MIT\"\n\n[workspace.dependencies]\nalvr_adb = { path = \"alvr/adb\" }\nalvr_audio = { path = \"alvr/audio\" }\nalvr_client_core = { path = \"alvr/client_core\" }\nalvr_common = { path = \"alvr/common\" }\nalvr_events = { path = \"alvr/events\" }\nalvr_filesystem = { path = \"alvr/filesystem\" }\nalvr_graphics = { path = \"alvr/graphics\" }\nalvr_gui_common = { path = \"alvr/gui_common\" }\nalvr_packets = { path = \"alvr/packets\" }\nalvr_server_core = { path = \"alvr/server_core\"}\nalvr_server_io = { path = \"alvr/server_io\" }\nalvr_session = { path = \"alvr/session\" }\nalvr_sockets = { path = \"alvr/sockets\" }\nalvr_system_info = { path = \"alvr/system_info\" }\n\n[profile.release]\ndebug = \"limited\"\nstrip = false\n\n[profile.distribution]\ninherits = \"release\"\nlto = true\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2018-2019 polygraphene\nCopyright (c) 2020-2024 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"> <img width=\"500\" src=\"resources/ALVR-Grey.svg\"/> </p>\n\n# ALVR - Air Light VR\n\n[![badge-discord][]][link-discord] [![badge-matrix][]][link-matrix] [![badge-opencollective][]][link-opencollective]\n\nStream VR games from your PC to your headset over Wi-Fi.  \nThis is a fork of [ALVR](https://github.com/polygraphene/ALVR).\n\n### Direct download (latest version):\n### [Windows Launcher](https://github.com/alvr-org/ALVR/releases/latest/download/alvr_launcher_windows.zip) | [Linux Launcher](https://github.com/alvr-org/ALVR/releases/latest/download/alvr_launcher_linux.tar.gz)\n\n## Compatibility\n\n|          VR Headset          |                                        Support                                         |\n| :--------------------------: | :------------------------------------------------------------------------------------: |\n|       Apple Vision Pro       |    :heavy_check_mark: ([store link](https://apps.apple.com/app/alvr/id6479728026))     |\n|      Quest 1/2/3/3S/Pro      | :heavy_check_mark: ([store link](https://www.meta.com/experiences/7674846229245715) *) |\n|     Pico Neo 3/4/4 Ultra     |                                   :heavy_check_mark:                                   |\n|    Play For Dream YVR 1/2/MR |                                   :heavy_check_mark:                                   |\n| Vive Focus 3/Vision/XR Elite |                                   :heavy_check_mark:                                   |\n|           Lynx R1            |                                   :heavy_check_mark:                                   |\n|     PhoneVR (smartphone)     |     :heavy_check_mark: ** ([repo](https://github.com/PhoneVR-Developers/PhoneVR))      |\n|        Android/Monado        |                                      :warning: **                                      |\n|          Oculus Go           |                 :x: ([old repo](https://github.com/polygraphene/ALVR))                 |\n\n\\* ALVR for Quest 1 is not available through the Meta store.  \n\\** Works on some smartphones, but has not been extensively tested.  \n\n|     PC OS      |                                    Support                                    |\n| :------------: | :---------------------------------------------------------------------------: |\n| Windows 10/11  | :heavy_check_mark: ([store link](https://store.steampowered.com/app/3312710)) |\n| Windows XP/7/8 |                                      :x:                                      |\n|     Linux      |                             :heavy_check_mark:***                             |\n|     macOS      |                                      :x:                                      |\n\n\\*** Please check the wiki for detailed compatibility information.\n\n### Requirements\n\n-   A supported standalone VR headset (see compatibility table above).\n-   SteamVR.\n-   A high-end gaming PC:\n    -   See the OS compatibility table above.\n    -   NVIDIA GPU with NVENC support (GTX 1000 series or newer), an AMD GPU with AMF VCE support, or an INTEL GPU with VPL support (Arc, Tiger Lake or newer), with the latest drivers.\n    -   On laptops with both an integrated GPU (Intel HD, AMD iGPU) and a dedicated GPU (NVIDIA GTX/RTX, AMD HD/R5/R7), make sure to assign the dedicated GPU (or \"high performance graphics adapter\") to ALVR and SteamVR for the best performance and compatibility.  \n        (NVIDIA: Nvidia Control Panel → 3D Settings → Application Settings; AMD: similar method)\n\n-   Network:\n    -   802.11ac 5 GHz Wi-Fi for the headset, and wired Ethernet for the PC is recommended.\n    -   The PC and the headset must be connected to the same router (or use a routed connection as described [here](https://github.com/alvr-org/ALVR/wiki/ALVR-v14-and-Above)).\n\n## Installation\n\nFollow the [installation guide](https://github.com/alvr-org/ALVR/wiki/Installation-guide).\n\n## Troubleshooting\n\n-   See the [Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Troubleshooting) page, and [Linux Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting) if applicable.\n-   Configuration recommendations and additional information can be found [here](https://github.com/alvr-org/ALVR/wiki/Information-and-Recommendations).\n\n## Uninstallation\n\nOpen `ALVR Dashboard.exe`, go to the `Installation` tab, then press `Remove firewall rules`.  \nClose the ALVR window and delete the ALVR folder.\n\n## Build from Source\n\nFollow the [build guide](https://github.com/alvr-org/ALVR/wiki/Building-From-Source).\n\n## License\n\nALVR is licensed under the [MIT License](LICENSE).\n\n## Privacy Policy\n\nALVR apps do not directly collect any personal data.\n\n## Donate\n\nIf you would like to support this project, you can donate through our [Open Source Collective account](https://opencollective.com/alvr).\n\n[badge-discord]: https://img.shields.io/discord/720612397580025886?style=for-the-badge&logo=discord&color=5865F2 \"Join us on Discord\"\n[link-discord]: https://discord.gg/ALVR\n[badge-matrix]: https://img.shields.io/static/v1?label=chat&message=%23alvr&style=for-the-badge&logo=matrix&color=blueviolet \"Join us on Matrix\"\n[link-matrix]: https://matrix.to/#/#alvr:ckie.dev?via=ckie.dev\n[badge-opencollective]: https://img.shields.io/opencollective/all/alvr?style=for-the-badge&logo=opencollective&color=79a3e6 \"Donate\"\n[link-opencollective]: https://opencollective.com/alvr\n"
  },
  {
    "path": "about.toml",
    "content": "accepted = [\n    \"MIT\",\n    \"Apache-2.0\",\n    \"Apache-2.0 WITH LLVM-exception\",\n    \"BSD-2-Clause\",\n    \"BSD-3-Clause\",\n    \"BSL-1.0\",\n    \"bzip2-1.0.6\",\n    \"CC0-1.0\",\n    \"CDLA-Permissive-2.0\",\n    \"ISC\",\n    \"LicenseRef-UFL-1.0\",\n    \"MPL-2.0\",\n    \"OFL-1.1\",\n    \"OpenSSL\",\n    \"Ubuntu-font-1.0\",\n    \"Unicode-DFS-2016\",\n    \"Unicode-3.0\",\n    \"Unlicense\",\n    \"Zlib\",\n    \"zlib-acknowledgement\",\n]\ntargets = [\n    \"x86_64-pc-windows-msvc\",\n    \"x86_64-unknown-linux-gnu\",\n    \"aarch64-linux-android\",\n    \"wasm32-unknown-unknown\",\n]\nworkarounds = [\"ring\"]\nfilter-noassertion = true\n"
  },
  {
    "path": "alvr/adb/Cargo.toml",
    "content": "[package]\nname = \"alvr_adb\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_filesystem.workspace = true\nalvr_system_info.workspace = true\nalvr_session.workspace = true\n\nanyhow = \"1\"\nureq = \"3\"\nzip = \"4\"\n"
  },
  {
    "path": "alvr/adb/src/commands.rs",
    "content": "// https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/docs/user/adb.1.md\n\nuse crate::parse::{self, Device, ForwardedPorts};\nuse alvr_filesystem as afs;\nuse anyhow::{Context, Result, anyhow};\nuse std::{\n    collections::HashSet,\n    io::{Cursor, Read},\n    process::Command,\n    str::FromStr,\n    time::Duration,\n};\nuse zip::ZipArchive;\n\n#[cfg(windows)]\nuse std::os::windows::process::CommandExt;\n\n// https://developer.android.com/tools/releases/platform-tools#revisions\n// NOTE: At the time of writing this comment, the revisions section above\n// shows the latest version as 35.0.2, but the latest that can be downloaded\n// by specifying a version is 35.0.0\nconst PLATFORM_TOOLS_VERSION: &str = \"-latest\"; // E.g. \"_r35.0.0\"\n\n#[cfg(target_os = \"linux\")]\nconst PLATFORM_TOOLS_OS: &str = \"linux\";\n#[cfg(target_os = \"macos\")]\nconst PLATFORM_TOOLS_OS: &str = \"darwin\";\n#[cfg(windows)]\nconst PLATFORM_TOOLS_OS: &str = \"windows\";\n\nconst REQUEST_TIMEOUT: Duration = Duration::from_secs(5);\n\nfn get_command(adb_path: &str, args: &[&str]) -> Command {\n    let mut command = Command::new(adb_path);\n    command.args(args);\n\n    #[cfg(windows)]\n    command.creation_flags(0x08000000); // CREATE_NO_WINDOW\n\n    command\n}\n\npub fn download(url: &str, progress_callback: impl Fn(usize, Option<usize>)) -> Result<Vec<u8>> {\n    let agent: ureq::Agent = ureq::Agent::config_builder()\n        .timeout_global(Some(REQUEST_TIMEOUT))\n        .build()\n        .into();\n    let response = agent.get(url).call()?;\n    let maybe_expected_size = response\n        .headers()\n        .get(\"Content-Length\")\n        .and_then(|v| v.to_str().ok()?.parse::<usize>().ok());\n    let mut result = maybe_expected_size\n        .map(Vec::with_capacity)\n        .unwrap_or_default();\n    let mut reader = response.into_body().into_reader();\n    let mut buffer = vec![0; 65535];\n    loop {\n        let read_count: usize = reader.read(&mut buffer)?;\n        if read_count == 0 {\n            break;\n        }\n        result.extend_from_slice(&buffer[..read_count]);\n        let current_size = result.len();\n        (progress_callback)(current_size, maybe_expected_size);\n    }\n\n    Ok(result)\n}\n\n///////////\n// Activity\n\npub fn get_process_id(\n    adb_path: &str,\n    device_serial: &str,\n    process_name: &str,\n) -> Result<Option<usize>> {\n    let output = get_command(\n        adb_path,\n        &[\"-s\", device_serial, \"shell\", \"pidof\", process_name],\n    )\n    .output()\n    .context(format!(\"Failed to get ID of process {process_name}\"))?;\n    let text = String::from_utf8_lossy(&output.stdout).trim().to_owned();\n    if text.is_empty() {\n        return Ok(None);\n    }\n    let process_id = text\n        .parse::<usize>()\n        .context(\"Failed to parse process ID\")?;\n\n    Ok(Some(process_id))\n}\n\npub fn is_activity_resumed(\n    adb_path: &str,\n    device_serial: &str,\n    activity_name: &str,\n) -> Result<bool> {\n    let output = get_command(\n        adb_path,\n        &[\n            \"-s\",\n            device_serial,\n            \"shell\",\n            \"dumpsys\",\n            \"activity\",\n            activity_name,\n        ],\n    )\n    .output()\n    .context(format!(\"Failed to get state of activity {activity_name}\"))?;\n    let text = String::from_utf8_lossy(&output.stdout);\n    if let Some(line) = text\n        .lines()\n        .map(|l| l.trim())\n        .find(|l| l.contains(\"mResumed\"))\n    {\n        let (entry, _) = line\n            .split_once(' ')\n            .ok_or(anyhow!(\"Failed to split resumed state line\"))?;\n        let (_, value) = entry\n            .split_once('=')\n            .ok_or(anyhow!(\"Failed to split resumed state entry\"))?;\n        match value {\n            \"true\" => Ok(true),\n            \"false\" => Ok(false),\n            _ => Err(anyhow!(\"Failed to parse resumed state value\"))?,\n        }\n    } else {\n        Err(anyhow!(\"Failed to find resumed state line\"))\n    }\n}\n\n///////////////////\n// ADB Installation\n\npub fn require_adb(\n    layout: &afs::Layout,\n    progress_callback: impl Fn(usize, Option<usize>),\n) -> Result<String> {\n    if let Some(path) = get_adb_path(layout) {\n        Ok(path)\n    } else {\n        install_adb(layout, progress_callback).context(\"Failed to install ADB\")?;\n        Ok(get_adb_path(layout).context(\"Failed to get ADB path after installation\")?)\n    }\n}\n\nfn install_adb(\n    layout: &afs::Layout,\n    progress_callback: impl Fn(usize, Option<usize>),\n) -> Result<()> {\n    let mut reader = Cursor::new(download_adb(progress_callback)?);\n    ZipArchive::new(&mut reader)?.extract(layout.executables_dir.clone())?;\n\n    Ok(())\n}\n\nfn download_adb(progress_callback: impl Fn(usize, Option<usize>)) -> Result<Vec<u8>> {\n    let url = get_platform_tools_url();\n\n    download(&url, progress_callback).context(format!(\"Failed to download ADB from {url}\"))\n}\n\nfn get_platform_tools_url() -> String {\n    format!(\n        \"https://dl.google.com/android/repository/platform-tools{PLATFORM_TOOLS_VERSION}-{PLATFORM_TOOLS_OS}.zip\"\n    )\n}\n\n///////////////\n// Applications\n\npub fn start_application(adb_path: &str, device_serial: &str, application_id: &str) -> Result<()> {\n    get_command(\n        adb_path,\n        &[\n            \"-s\",\n            device_serial,\n            \"shell\",\n            \"monkey\",\n            \"-p\",\n            application_id,\n            \"1\",\n        ],\n    )\n    .output()\n    .context(format!(\"Failed to start {application_id}\"))?;\n\n    Ok(())\n}\n\n//////////\n// Devices\n\npub fn list_devices(adb_path: &str) -> Result<Vec<Device>> {\n    let output = get_command(adb_path, &[\"devices\", \"-l\"])\n        .output()\n        .context(\"Failed to list ADB devices\")?;\n    let text = String::from_utf8_lossy(&output.stdout);\n    let devices = text\n        .lines()\n        .skip(1)\n        .filter_map(parse::parse_device)\n        .collect();\n\n    Ok(devices)\n}\n\n///////////\n// Packages\n\npub fn install_package(adb_path: &str, device_serial: &str, apk_path: &str) -> Result<()> {\n    get_command(adb_path, &[\"-s\", device_serial, \"install\", \"-r\", apk_path])\n        .output()\n        .context(format!(\"Failed to install {apk_path}\"))?;\n\n    Ok(())\n}\n\npub fn is_package_installed(\n    adb_path: &str,\n    device_serial: &str,\n    application_id: &str,\n) -> Result<bool> {\n    let found = list_installed_packages(adb_path, device_serial)\n        .context(format!(\n            \"Failed to check if package {application_id} is installed\"\n        ))?\n        .contains(application_id);\n\n    Ok(found)\n}\n\npub fn uninstall_package(adb_path: &str, device_serial: &str, application_id: &str) -> Result<()> {\n    get_command(\n        adb_path,\n        &[\"-s\", device_serial, \"uninstall\", application_id],\n    )\n    .output()\n    .context(format!(\"Failed to uninstall {application_id}\"))?;\n\n    Ok(())\n}\n\npub fn list_installed_packages(adb_path: &str, device_serial: &str) -> Result<HashSet<String>> {\n    let output = get_command(\n        adb_path,\n        &[\"-s\", device_serial, \"shell\", \"pm\", \"list\", \"package\"],\n    )\n    .output()\n    .context(\"Failed to list installed packages\")?;\n    let text = String::from_utf8_lossy(&output.stdout);\n    let packages = text.lines().map(|l| l.replace(\"package:\", \"\")).collect();\n\n    Ok(packages)\n}\n\n////////\n// Paths\n\n/// Returns the path of a local (i.e. installed by ALVR) or OS version of `adb` if found, `None` otherwise.\npub fn get_adb_path(layout: &afs::Layout) -> Option<String> {\n    let exe_name = afs::exec_fname(\"adb\").to_owned();\n    let adb_path = get_command(&exe_name, &[])\n        .output()\n        .is_ok()\n        .then_some(exe_name);\n\n    adb_path.or_else(|| {\n        let path = layout.local_adb_exe();\n\n        path.try_exists()\n            .unwrap_or(false)\n            .then(|| path.to_string_lossy().to_string())\n    })\n}\n\n////////\n// Utility\npub fn get_uptime(adb_path: &str, device_serial: &str) -> Result<Duration> {\n    let output = get_command(\n        adb_path,\n        &[\"-s\", device_serial, \"shell\", \"cat\", \"/proc/uptime\"],\n    )\n    .output()\n    .context(\"Failed to get system uptime\")?;\n\n    let output_str = String::from_utf8_lossy(&output.stdout);\n\n    let uptime_string = output_str\n        .split_ascii_whitespace()\n        .next()\n        .context(\"Empty result from /proc/uptime\")?;\n\n    let uptime = f64::from_str(uptime_string).context(\"Cannot parse uptime into an f64\")?;\n\n    Duration::try_from_secs_f64(uptime).context(\"Invalid f64 value for a duration \")\n}\n\n//////////////////\n// Port forwarding\n\npub fn list_forwarded_ports(adb_path: &str, device_serial: &str) -> Result<Vec<ForwardedPorts>> {\n    let output = get_command(adb_path, &[\"-s\", device_serial, \"forward\", \"--list\"])\n        .output()\n        .context(format!(\n            \"Failed to list forwarded ports of device {device_serial:?}\"\n        ))?;\n    let text = String::from_utf8_lossy(&output.stdout);\n    let forwarded_ports = text\n        .lines()\n        .filter_map(parse::parse_forwarded_ports)\n        .collect();\n\n    Ok(forwarded_ports)\n}\n\npub fn forward_port(adb_path: &str, device_serial: &str, port: u16) -> Result<()> {\n    get_command(\n        adb_path,\n        &[\n            \"-s\",\n            device_serial,\n            \"forward\",\n            &format!(\"tcp:{port}\"),\n            &format!(\"tcp:{port}\"),\n        ],\n    )\n    .output()\n    .context(format!(\n        \"Failed to forward port {port:?} of device {device_serial:?}\"\n    ))?;\n\n    Ok(())\n}\n\n/////////\n// Server\n\npub fn kill_server(adb_path: &str) -> Result<()> {\n    get_command(adb_path, &[\"kill-server\"])\n        .output()\n        .context(\"Failed to kill ADB server\")?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/adb/src/lib.rs",
    "content": "pub mod commands;\nmod parse;\n\nuse alvr_common::anyhow::Result;\nuse alvr_common::{dbg_connection, error, warn};\nuse alvr_session::WiredClientAutoLaunchConfig;\nuse alvr_system_info::{\n    ClientFlavor, PACKAGE_NAME_GITHUB_DEV, PACKAGE_NAME_GITHUB_STABLE, PACKAGE_NAME_STORE,\n};\nuse std::collections::HashSet;\nuse std::time::Duration;\n\npub enum WiredConnectionStatus {\n    Ready,\n    NotReady(String),\n}\n\npub struct WiredConnection {\n    adb_path: String,\n}\n\nimpl WiredConnection {\n    pub fn new(\n        layout: &alvr_filesystem::Layout,\n        download_progress_callback: impl Fn(usize, Option<usize>),\n    ) -> Result<Self> {\n        let adb_path = commands::require_adb(layout, download_progress_callback)?;\n\n        Ok(Self { adb_path })\n    }\n\n    pub fn setup(\n        &self,\n        control_port: u16,\n        stream_port: u16,\n        client_type: &ClientFlavor,\n        client_autolaunch: Option<WiredClientAutoLaunchConfig>,\n    ) -> Result<WiredConnectionStatus> {\n        let Some(device_serial) = commands::list_devices(&self.adb_path)?\n            .into_iter()\n            .filter_map(|d| d.serial)\n            .find(|s| !s.starts_with(\"127.0.0.1\"))\n        else {\n            return Ok(WiredConnectionStatus::NotReady(\n                \"No wired devices found\".to_owned(),\n            ));\n        };\n\n        let ports = HashSet::from([control_port, stream_port]);\n        let forwarded_ports: HashSet<u16> =\n            commands::list_forwarded_ports(&self.adb_path, &device_serial)?\n                .into_iter()\n                .map(|f| f.local)\n                .collect();\n        let missing_ports = ports.difference(&forwarded_ports);\n        for port in missing_ports {\n            commands::forward_port(&self.adb_path, &device_serial, *port)?;\n            dbg_connection!(\n                \"setup_wired_connection: Forwarded port {port} of device {device_serial}\"\n            );\n        }\n\n        let Some(process_name) = get_process_name(&self.adb_path, &device_serial, client_type)\n        else {\n            return Ok(WiredConnectionStatus::NotReady(\n                \"No suitable ALVR client is installed\".to_owned(),\n            ));\n        };\n\n        if commands::get_process_id(&self.adb_path, &device_serial, &process_name)?.is_none() {\n            if let Some(client_autolaunch) = client_autolaunch {\n                if client_autolaunch.boot_delay > 0 {\n                    match commands::get_uptime(&self.adb_path, &device_serial) {\n                        Ok(uptime) => {\n                            if uptime < Duration::from_secs(client_autolaunch.boot_delay.into()) {\n                                return Ok(WiredConnectionStatus::NotReady(\n                                    \"Waiting for device boot\".to_owned(),\n                                ));\n                            }\n                        }\n                        Err(failure) => {\n                            warn!(\"wired_connection: get_uptime failed with {}\", failure);\n                        }\n                    }\n                }\n\n                commands::start_application(&self.adb_path, &device_serial, &process_name)?;\n                Ok(WiredConnectionStatus::NotReady(\n                    \"Starting ALVR client\".to_owned(),\n                ))\n            } else {\n                Ok(WiredConnectionStatus::NotReady(\n                    \"ALVR client is not running\".to_owned(),\n                ))\n            }\n        } else if !commands::is_activity_resumed(&self.adb_path, &device_serial, &process_name)? {\n            Ok(WiredConnectionStatus::NotReady(\n                \"ALVR client is paused\".to_owned(),\n            ))\n        } else {\n            Ok(WiredConnectionStatus::Ready)\n        }\n    }\n}\n\nimpl Drop for WiredConnection {\n    fn drop(&mut self) {\n        dbg_connection!(\"wired_connection: Killing ADB server\");\n        if let Err(e) = commands::kill_server(&self.adb_path) {\n            error!(\"{e:?}\");\n        }\n    }\n}\n\npub fn get_process_name(\n    adb_path: &str,\n    device_serial: &str,\n    flavor: &ClientFlavor,\n) -> Option<String> {\n    let fallbacks = match flavor {\n        ClientFlavor::Store => {\n            if alvr_common::is_stable() {\n                vec![PACKAGE_NAME_STORE, PACKAGE_NAME_GITHUB_STABLE]\n            } else {\n                vec![PACKAGE_NAME_GITHUB_DEV]\n            }\n        }\n        ClientFlavor::Github => {\n            if alvr_common::is_stable() {\n                vec![PACKAGE_NAME_GITHUB_STABLE, PACKAGE_NAME_STORE]\n            } else {\n                vec![PACKAGE_NAME_GITHUB_DEV]\n            }\n        }\n        ClientFlavor::Custom(name) => {\n            if alvr_common::is_stable() {\n                vec![name, PACKAGE_NAME_STORE, PACKAGE_NAME_GITHUB_STABLE]\n            } else {\n                vec![name, PACKAGE_NAME_GITHUB_DEV]\n            }\n        }\n    };\n\n    fallbacks\n        .iter()\n        .find(|name| {\n            commands::is_package_installed(adb_path, device_serial, name)\n                .is_ok_and(|installed| installed)\n        })\n        .map(|name| (*name).to_string())\n}\n"
  },
  {
    "path": "alvr/adb/src/parse.rs",
    "content": "// https://cs.android.com/android/platform/superproject/main/+/7dbe542b9a93fb3cee6c528e16e2d02a26da7cc0:packages/modules/adb/transport.cpp;l=1409\n// The serial number is printed with a \"%-22s\" format, meaning that it's a left-aligned space-padded string of 22 characters.\nconst SERIAL_NUMBER_COLUMN_LENGTH: usize = 22;\n\n// https://cs.android.com/android/platform/superproject/main/+/7dbe542b9a93fb3cee6c528e16e2d02a26da7cc0:packages/modules/adb/adb.h;l=104-122\n#[derive(Debug)]\npub enum ConnectionState {\n    Authorizing,\n    Bootloader,\n    Connecting,\n    Detached,\n    Device,\n    Host,\n    NoPermissions, // https://cs.android.com/android/platform/superproject/main/+/main:system/core/diagnose_usb/diagnose_usb.cpp;l=83-90\n    Offline,\n    Recovery,\n    Rescue,\n    Sideload,\n    Unauthorized,\n}\n\npub fn parse_connection_state(value: &str) -> Option<ConnectionState> {\n    match value {\n        \"authorizing\" => Some(ConnectionState::Authorizing),\n        \"bootloader\" => Some(ConnectionState::Bootloader),\n        \"connecting\" => Some(ConnectionState::Connecting),\n        \"detached\" => Some(ConnectionState::Detached),\n        \"device\" => Some(ConnectionState::Device),\n        \"host\" => Some(ConnectionState::Host),\n        \"offline\" => Some(ConnectionState::Offline),\n        \"recovery\" => Some(ConnectionState::Recovery),\n        \"rescue\" => Some(ConnectionState::Rescue),\n        \"sideload\" => Some(ConnectionState::Sideload),\n        \"unauthorized\" => Some(ConnectionState::Unauthorized),\n        _ => None,\n    }\n}\n\nfn parse_pair(pair: &str) -> Option<String> {\n    let mut slice = pair.split(':');\n    let _key = slice.next();\n\n    slice.next().map(|value| value.to_string())\n}\n\n// https://cs.android.com/android/platform/superproject/main/+/7dbe542b9a93fb3cee6c528e16e2d02a26da7cc0:packages/modules/adb/adb.h;l=95-100\n#[derive(Debug)]\npub enum TransportType {\n    Usb,\n    Local,\n    Any,\n    Host,\n}\n\npub fn parse_transport_type(pair: &str) -> Option<TransportType> {\n    let mut slice = pair.split(':');\n    let _key = slice.next();\n\n    if let Ok(value) = slice.next()?.parse::<u8>() {\n        match value {\n            0 => Some(TransportType::Usb),\n            1 => Some(TransportType::Local),\n            2 => Some(TransportType::Any),\n            3 => Some(TransportType::Host),\n            _ => None,\n        }\n    } else {\n        None\n    }\n}\n\n// https://cs.android.com/android/platform/superproject/main/+/7dbe542b9a93fb3cee6c528e16e2d02a26da7cc0:packages/modules/adb/transport.cpp;l=1398\n#[derive(Debug)]\npub struct Device {\n    pub connection_state: Option<ConnectionState>,\n    pub device: Option<String>,\n    pub model: Option<String>,\n    pub product: Option<String>,\n    pub serial: Option<String>,\n    pub transport_type: Option<TransportType>,\n}\n\npub fn parse_device(line: &str) -> Option<Device> {\n    if line.len() < SERIAL_NUMBER_COLUMN_LENGTH {\n        return None;\n    }\n    let (left, right) = line.split_at(SERIAL_NUMBER_COLUMN_LENGTH);\n    let serial = (!left.contains(\"(no serial number)\")).then(|| left.trim().to_owned());\n    let mut remaining = right.trim();\n\n    let connection_state = if remaining.starts_with(\"no permissions\") {\n        // Since the current user's name can be printed in the error message,\n        // we are gambling that there's not a \"]\" in it.\n        if let Some((_, right)) = remaining.split_once(']') {\n            remaining = right;\n            Some(ConnectionState::NoPermissions)\n        } else {\n            None\n        }\n    } else if let Some((left, right)) = remaining.split_once(' ') {\n        remaining = right;\n        parse_connection_state(left)\n    } else {\n        None\n    };\n\n    let mut slices = remaining.split_whitespace();\n    let product = slices.next().and_then(parse_pair);\n    let model = slices.next().and_then(parse_pair);\n    let device = slices.next().and_then(parse_pair);\n    let transport_type = slices.next().and_then(parse_transport_type);\n\n    Some(Device {\n        connection_state,\n        device,\n        model,\n        product,\n        serial,\n        transport_type,\n    })\n}\n\n#[derive(Debug)]\npub struct ForwardedPorts {\n    pub local: u16,\n    pub remote: u16,\n    pub serial: String,\n}\n\npub fn parse_forwarded_ports(line: &str) -> Option<ForwardedPorts> {\n    let mut slices = line.split_whitespace();\n    let serial = slices.next();\n    let local = parse_port(slices.next()?);\n    let remote = parse_port(slices.next()?);\n\n    if let Some(serial) = serial\n        && let Some(local) = local\n        && let Some(remote) = remote\n    {\n        Some(ForwardedPorts {\n            local,\n            remote,\n            serial: serial.to_owned(),\n        })\n    } else {\n        None\n    }\n}\n\nfn parse_port(value: &str) -> Option<u16> {\n    let mut slices = value.split(':');\n    let _protocol = slices.next();\n    let maybe_port = slices.next();\n\n    maybe_port.and_then(|p| p.parse::<u16>().ok())\n}\n"
  },
  {
    "path": "alvr/audio/Cargo.toml",
    "content": "[package]\nname = \"alvr_audio\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_session.workspace = true\nalvr_sockets.workspace = true\n\ncpal = \"0.16\"\nrodio = \"0.21\"\nserde = \"1\"\n\n[target.'cfg(windows)'.dependencies]\nwidestring = \"1\"\nwindows = { version = \"0.61\", features = [\n    \"Win32_Devices_FunctionDiscovery\",\n    \"Win32_Foundation\",\n    \"Win32_Media_Audio_Endpoints\",\n    \"Win32_System_Com_StructuredStorage\",\n    \"Win32_System_Variant\",\n    \"Win32_UI_Shell_PropertiesSystem\",\n] }\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\npipewire = { version = \"0.8.0\", features = [\"v0_3_49\"] }\nlibspa-sys = \"0.8.0\"\n"
  },
  {
    "path": "alvr/audio/src/lib.rs",
    "content": "#[cfg(windows)]\nmod windows;\n\n#[cfg(target_os = \"linux\")]\npub mod linux;\n\n#[cfg(windows)]\npub use crate::windows::*;\n\nuse alvr_common::{\n    ConnectionError, ToAny,\n    anyhow::{self, Context, Result, bail},\n    info,\n    parking_lot::Mutex,\n};\nuse alvr_session::{AudioBufferingConfig, CustomAudioDeviceConfig, MicrophoneDevicesConfig};\nuse alvr_sockets::{StreamReceiver, StreamSender};\nuse cpal::{\n    BufferSize, Host, Sample, SampleFormat, StreamConfig,\n    traits::{DeviceTrait, HostTrait, StreamTrait},\n};\nuse rodio::{OutputStreamBuilder, Source};\nuse std::{collections::VecDeque, sync::Arc, thread, time::Duration};\n\npub use cpal::Device;\n\nfn device_from_custom_config(\n    host: &Host,\n    config: &CustomAudioDeviceConfig,\n    is_output: bool,\n) -> Result<Device> {\n    let mut devices = if is_output {\n        host.output_devices()?\n    } else {\n        host.input_devices()?\n    };\n\n    Ok(match config {\n        CustomAudioDeviceConfig::NameSubstring(name_substring) => devices\n            .find(|d| {\n                d.name()\n                    .map(|name| name.to_lowercase().contains(&name_substring.to_lowercase()))\n                    .unwrap_or(false)\n            })\n            .with_context(|| {\n                format!(\"Cannot find audio device which name contains \\\"{name_substring}\\\"\")\n            })?,\n        CustomAudioDeviceConfig::Index(index) => devices\n            .nth(*index)\n            .with_context(|| format!(\"Cannot find audio device at index {index}\"))?,\n    })\n}\n\npub fn new_output(config: Option<&CustomAudioDeviceConfig>) -> Result<Device> {\n    let host = cpal::default_host();\n\n    let device = match config {\n        None => host\n            .default_output_device()\n            .context(\"No output audio device found\")?,\n        Some(config) => device_from_custom_config(&host, config, true)?,\n    };\n\n    Ok(device)\n}\n\npub fn new_input(config: Option<CustomAudioDeviceConfig>) -> Result<Device> {\n    let host = cpal::default_host();\n\n    let device = match config {\n        None => host\n            .default_input_device()\n            .context(\"No input audio device found\")?,\n        Some(config) => device_from_custom_config(&host, &config, false)?,\n    };\n\n    Ok(device)\n}\n\n// returns (sink, source)\npub fn new_virtual_microphone_pair(config: MicrophoneDevicesConfig) -> Result<(Device, Device)> {\n    // No-op on Windows (this is windows specific code)\n    let host = cpal::default_host();\n\n    let (sink_name, source_name) = match config {\n        MicrophoneDevicesConfig::Automatic => {\n            // NOTE: This will iterate over all devices for every option it tries, if the audio\n            // code is slow, change that first\n            return [\n                MicrophoneDevicesConfig::VAC,\n                MicrophoneDevicesConfig::VBCable,\n                MicrophoneDevicesConfig::VoiceMeeter,\n                MicrophoneDevicesConfig::VoiceMeeterAux,\n                MicrophoneDevicesConfig::VoiceMeeterVaio3,\n            ]\n            .into_iter()\n            .find_map(|cable_type| new_virtual_microphone_pair(cable_type).ok())\n            .context(\"No microphones found\");\n        }\n\n        MicrophoneDevicesConfig::VAC => (\"Line 1\", \"Line 1\"),\n        MicrophoneDevicesConfig::VBCable => (\"CABLE Input\", \"CABLE Output\"),\n        MicrophoneDevicesConfig::VoiceMeeter => (\"VoiceMeeter Input\", \"VoiceMeeter Output\"),\n        MicrophoneDevicesConfig::VoiceMeeterAux => {\n            (\"VoiceMeeter Aux Input\", \"VoiceMeeter Aux Output\")\n        }\n        MicrophoneDevicesConfig::VoiceMeeterVaio3 => {\n            (\"VoiceMeeter VAIO3 Input\", \"VoiceMeeter VAIO3 Output\")\n        }\n\n        MicrophoneDevicesConfig::Custom { sink, source } => {\n            return Ok((\n                device_from_custom_config(&host, &sink, true)?,\n                device_from_custom_config(&host, &source, false)?,\n            ));\n        }\n    };\n\n    let sink = host\n            .output_devices()?\n            .find(|d| d.name().unwrap_or_default().contains(sink_name))\n            .context(\"Virtual Audio Cable, VB-CABLE or VoiceMeeter not found. Please install or reinstall one\")?;\n\n    let source = host\n        .input_devices()?\n        .find(|d| d.name().unwrap_or_default().contains(source_name))\n        .context(\"Matching output microphone not found. Did you rename it?\")?;\n\n    Ok((sink, source))\n}\n\npub fn input_sample_rate(dev: &Device) -> Result<u32> {\n    let config = dev\n        .default_input_config()\n        // On Windows, loopback devices are not recognized as input devices. Use output config.\n        .or_else(|_| dev.default_output_config())?;\n\n    Ok(config.sample_rate().0)\n}\n\npub enum AudioRecordState {\n    Recording,\n    ShouldStop,\n    Err(Option<anyhow::Error>),\n}\n\npub enum AudioChannel {\n    FrontLeft,\n    FrontRight,\n    Center,\n    SurroundLeft,\n    SurroundRight,\n    BackLeft,\n    BackRight,\n    Top,\n    HighFrontLeft,\n    HighFrontRight,\n    HighFrontCenter,\n    HighBackLeft,\n    HighBackRight,\n    LowFrequency,\n}\n\nfn downmix_channels(channels: &[AudioChannel], data: &[u8], out_channels: u16) -> Vec<u8> {\n    let mut left = 0.0;\n    let mut right = 0.0;\n\n    for i in 0..channels.len() {\n        let [l, r] = match &channels[i] {\n            AudioChannel::FrontLeft => [1.0, 0.0],\n            AudioChannel::FrontRight => [0.0, 1.0],\n            AudioChannel::Center => [0.707, 0.707],\n            AudioChannel::SurroundLeft => [0.707, 0.0],\n            AudioChannel::SurroundRight => [0.0, 0.707],\n            AudioChannel::BackLeft => [0.707, 0.0],\n            AudioChannel::BackRight => [0.0, 0.707],\n            AudioChannel::Top => [0.577, 0.577],\n            AudioChannel::HighFrontLeft => [0.707, 0.0],\n            AudioChannel::HighFrontRight => [0.0, 0.707],\n            AudioChannel::HighFrontCenter => [0.5, 0.5],\n            AudioChannel::HighBackLeft => [0.5, 0.0],\n            AudioChannel::HighBackRight => [0.0, 0.5],\n            _ => [0.0, 0.0],\n        };\n        let val = i16::from_ne_bytes([data[i * 2], data[i * 2 + 1]]).to_sample::<f32>();\n        left += val * l;\n        right += val * r;\n    }\n\n    if out_channels == 1 {\n        let bytes = ((left + right) / 2.0).to_sample::<i16>().to_ne_bytes();\n        vec![bytes[0], bytes[1]]\n    } else {\n        let left_bytes = left.to_sample::<i16>().to_ne_bytes();\n        let right_bytes = right.to_sample::<i16>().to_ne_bytes();\n        vec![left_bytes[0], left_bytes[1], right_bytes[0], right_bytes[1]]\n    }\n}\n\nfn downmix_audio(data: Vec<u8>, in_channels: u16, out_channels: u16) -> Vec<u8> {\n    if in_channels == out_channels {\n        data\n    } else if in_channels == 1 && out_channels == 2 {\n        data.chunks_exact(2)\n            .flat_map(|c| vec![c[0], c[1], c[0], c[1]])\n            .collect()\n    } else {\n        let channels = match in_channels {\n            2 => vec![AudioChannel::FrontLeft, AudioChannel::FrontRight],\n            3 => vec![\n                AudioChannel::FrontLeft,\n                AudioChannel::FrontRight,\n                AudioChannel::LowFrequency,\n            ],\n            4 => vec![\n                AudioChannel::FrontLeft,\n                AudioChannel::FrontRight,\n                AudioChannel::BackLeft,\n                AudioChannel::BackRight,\n            ],\n            6 => vec![\n                AudioChannel::FrontRight,\n                AudioChannel::Center,\n                AudioChannel::LowFrequency,\n                AudioChannel::SurroundLeft, // Sometimes actually BackLeft, has same level so it's okay\n                AudioChannel::SurroundRight, // Sometimes actually BackRight, has same level so it's okay\n            ],\n            8 => vec![\n                AudioChannel::FrontLeft,\n                AudioChannel::FrontRight,\n                AudioChannel::Center,\n                AudioChannel::LowFrequency,\n                AudioChannel::BackLeft,\n                AudioChannel::BackRight,\n                AudioChannel::SurroundLeft,\n                AudioChannel::SurroundRight,\n            ],\n            _ => unreachable!(\"Invalid input channel count\"),\n        };\n\n        data.chunks_exact(in_channels as usize * 2)\n            .flat_map(|c| downmix_channels(&channels, c, out_channels))\n            .collect()\n    }\n}\n\n#[allow(unused_variables)]\npub fn record_audio_blocking(\n    is_running: Arc<dyn Fn() -> bool + Send + Sync>,\n    mut sender: StreamSender<()>,\n    device: &Device,\n    channels_count: u16,\n    mute: bool,\n) -> Result<()> {\n    let config = device\n        .default_input_config()\n        // On Windows, loopback devices are not recognized as input devices. Use output config.\n        .or_else(|_| device.default_output_config())?;\n\n    if config.channels() > 8 {\n        bail!(\n            \"Audio devices with more than 8 channels are not supported. {}\",\n            \"Please turn off surround audio.\"\n        );\n    } else if config.channels() == 5 || config.channels() == 7 {\n        bail!(\n            \"Audio devices with {} channels are not supported.\",\n            config.channels()\n        );\n    }\n\n    let stream_config = StreamConfig {\n        channels: config.channels(),\n        sample_rate: config.sample_rate(),\n        buffer_size: BufferSize::Default,\n    };\n\n    let state = Arc::new(Mutex::new(AudioRecordState::Recording));\n\n    let stream = device.build_input_stream_raw(\n        &stream_config,\n        config.sample_format(),\n        {\n            let state = Arc::clone(&state);\n            let is_running = Arc::clone(&is_running);\n            move |data, _| {\n                let data = if config.sample_format() == SampleFormat::F32 {\n                    data.bytes()\n                        .chunks_exact(4)\n                        .flat_map(|b| {\n                            f32::from_ne_bytes([b[0], b[1], b[2], b[3]])\n                                .to_sample::<i16>()\n                                .to_ne_bytes()\n                                .to_vec()\n                        })\n                        .collect()\n                } else {\n                    data.bytes().to_vec()\n                };\n\n                let data = downmix_audio(data, config.channels(), channels_count);\n\n                if is_running() {\n                    sender.send_header_with_payload(&(), &data).ok();\n                } else {\n                    *state.lock() = AudioRecordState::ShouldStop;\n                }\n            }\n        },\n        {\n            let state = Arc::clone(&state);\n            move |e| *state.lock() = AudioRecordState::Err(Some(e.into()))\n        },\n        None,\n    )?;\n\n    #[cfg(windows)]\n    if mute && device.supports_output() {\n        crate::windows::set_mute_windows_device(device, true).ok();\n    }\n\n    let mut res = stream.play().to_any();\n\n    if res.is_ok() {\n        while matches!(*state.lock(), AudioRecordState::Recording) && is_running() {\n            thread::sleep(Duration::from_millis(500))\n        }\n\n        if let AudioRecordState::Err(e) = &mut *state.lock() {\n            res = Err(e.take().unwrap());\n        }\n    }\n\n    #[cfg(windows)]\n    if mute && device.supports_output() {\n        set_mute_windows_device(device, false).ok();\n    }\n\n    res\n}\n\n// Audio callback. This is designed to be as less complex as possible. Still, when needed, this\n// callback can render a fade-out autonomously.\n#[inline]\npub fn get_next_frame_batch(\n    sample_buffer: &mut VecDeque<f32>,\n    channels_count: usize,\n    batch_frames_count: usize,\n) -> Vec<f32> {\n    if sample_buffer.len() / channels_count >= batch_frames_count {\n        let mut batch = sample_buffer\n            .drain(0..batch_frames_count * channels_count)\n            .collect::<Vec<_>>();\n\n        if sample_buffer.len() / channels_count < batch_frames_count {\n            // Render fade-out. It is completely contained in the current batch\n            for f in 0..batch_frames_count {\n                let volume = 1. - f as f32 / batch_frames_count as f32;\n                for c in 0..channels_count {\n                    batch[f * channels_count + c] *= volume;\n                }\n            }\n        }\n        // fade-ins and cross-fades are rendered in the receive loop directly inside sample_buffer.\n\n        batch\n    } else {\n        vec![0.; batch_frames_count * channels_count]\n    }\n}\n\n// The receive loop is resposible for ensuring smooth transitions in case of disruptions (buffer\n// underflow, overflow, packet loss). In case the computation takes too much time, the audio\n// callback will gracefully handle an interruption, and the callback timing and sound wave\n// continuity will not be affected.\npub fn receive_samples_loop(\n    is_running: impl Fn() -> bool,\n    receiver: &mut StreamReceiver<()>,\n    sample_buffer: Arc<Mutex<VecDeque<f32>>>,\n    channels_count: usize,\n    batch_frames_count: usize,\n    average_buffer_frames_count: usize,\n) -> Result<()> {\n    let mut recovery_sample_buffer = vec![];\n    while is_running() {\n        let data = match receiver.recv(Duration::from_millis(500)) {\n            Ok(data) => data,\n            Err(ConnectionError::TryAgain(_)) => continue,\n            Err(ConnectionError::Other(e)) => return Err(e),\n        };\n        let (_, packet) = data.get()?;\n\n        let new_samples = packet\n            .chunks_exact(2)\n            .map(|c| i16::from_ne_bytes([c[0], c[1]]).to_sample::<f32>())\n            .collect::<Vec<_>>();\n\n        let mut sample_buffer_ref = sample_buffer.lock();\n\n        if data.had_packet_loss() {\n            info!(\"Audio packet loss!\");\n\n            if sample_buffer_ref.len() / channels_count < batch_frames_count {\n                sample_buffer_ref.clear();\n            } else {\n                // clear remaining samples\n                sample_buffer_ref.drain(batch_frames_count * channels_count..);\n            }\n\n            recovery_sample_buffer.clear();\n        }\n\n        if sample_buffer_ref.len() / channels_count < batch_frames_count {\n            recovery_sample_buffer.extend(sample_buffer_ref.drain(..));\n        }\n\n        if sample_buffer_ref.is_empty() || data.had_packet_loss() {\n            recovery_sample_buffer.extend(&new_samples);\n\n            if recovery_sample_buffer.len() / channels_count\n                > average_buffer_frames_count + batch_frames_count\n            {\n                // Fade-in\n                for f in 0..batch_frames_count {\n                    let volume = f as f32 / batch_frames_count as f32;\n                    for c in 0..channels_count {\n                        recovery_sample_buffer[f * channels_count + c] *= volume;\n                    }\n                }\n\n                if data.had_packet_loss()\n                    && sample_buffer_ref.len() / channels_count == batch_frames_count\n                {\n                    // Add a fade-out to make a cross-fade.\n                    for f in 0..batch_frames_count {\n                        let volume = 1. - f as f32 / batch_frames_count as f32;\n                        for c in 0..channels_count {\n                            recovery_sample_buffer[f * channels_count + c] +=\n                                sample_buffer_ref[f * channels_count + c] * volume;\n                        }\n                    }\n\n                    sample_buffer_ref.clear();\n                }\n\n                sample_buffer_ref.extend(recovery_sample_buffer.drain(..));\n                info!(\"Audio recovered\");\n            }\n        } else {\n            sample_buffer_ref.extend(&new_samples);\n        }\n\n        // todo: use smarter policy with EventTiming\n        let buffer_frames_size = sample_buffer_ref.len() / channels_count;\n        if buffer_frames_size > 2 * average_buffer_frames_count + batch_frames_count {\n            info!(\"Audio buffer overflow! size: {buffer_frames_size}\");\n\n            let drained_samples = sample_buffer_ref\n                .drain(0..(buffer_frames_size - average_buffer_frames_count) * channels_count)\n                .collect::<Vec<_>>();\n\n            // Render a cross-fade.\n            for f in 0..batch_frames_count {\n                let volume = f as f32 / batch_frames_count as f32;\n                for c in 0..channels_count {\n                    let index = f * channels_count + c;\n                    sample_buffer_ref[index] = sample_buffer_ref[index]\n                        .mul_add(volume, drained_samples[index] * (1. - volume));\n                }\n            }\n        }\n    }\n\n    Ok(())\n}\n\nstruct StreamingSource {\n    sample_buffer: Arc<Mutex<VecDeque<f32>>>,\n    current_batch: Vec<f32>,\n    current_batch_cursor: usize,\n    channels_count: usize,\n    sample_rate: u32,\n    batch_frames_count: usize,\n}\n\nimpl Source for StreamingSource {\n    fn current_span_len(&self) -> Option<usize> {\n        None\n    }\n\n    fn channels(&self) -> u16 {\n        self.channels_count as _\n    }\n\n    fn sample_rate(&self) -> u32 {\n        self.sample_rate\n    }\n\n    fn total_duration(&self) -> Option<std::time::Duration> {\n        None\n    }\n}\n\nimpl Iterator for StreamingSource {\n    type Item = f32;\n\n    #[inline]\n    fn next(&mut self) -> Option<f32> {\n        if self.current_batch_cursor == 0 {\n            self.current_batch = get_next_frame_batch(\n                &mut self.sample_buffer.lock(),\n                self.channels_count,\n                self.batch_frames_count,\n            );\n        }\n\n        let sample = self.current_batch[self.current_batch_cursor];\n\n        self.current_batch_cursor =\n            (self.current_batch_cursor + 1) % (self.batch_frames_count * self.channels_count);\n\n        Some(sample)\n    }\n}\n\npub fn play_audio_loop(\n    is_running: impl Fn() -> bool,\n    device: &Device,\n    channels_count: u16,\n    sample_rate: u32,\n    config: AudioBufferingConfig,\n    receiver: &mut StreamReceiver<()>,\n) -> Result<()> {\n    // Size of a chunk of frames. It corresponds to the duration if a fade-in/out in frames.\n    let batch_frames_count = sample_rate as usize * config.batch_ms as usize / 1000;\n\n    // Average buffer size in frames\n    let average_buffer_frames_count =\n        sample_rate as usize * config.average_buffering_ms as usize / 1000;\n\n    let sample_buffer = Arc::new(Mutex::new(VecDeque::new()));\n\n    let stream = OutputStreamBuilder::from_device(device.clone())?.open_stream()?;\n\n    stream.mixer().add(StreamingSource {\n        sample_buffer: Arc::clone(&sample_buffer),\n        current_batch: vec![],\n        current_batch_cursor: 0,\n        channels_count: channels_count as _,\n        sample_rate,\n        batch_frames_count,\n    });\n\n    receive_samples_loop(\n        is_running,\n        receiver,\n        sample_buffer,\n        channels_count as _,\n        batch_frames_count,\n        average_buffer_frames_count,\n    )\n    .ok();\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/audio/src/linux.rs",
    "content": "use alvr_common::{ConnectionError, anyhow::Result, debug, error, parking_lot::Mutex};\nuse alvr_session::AudioBufferingConfig;\nuse alvr_sockets::{StreamReceiver, StreamSender};\n\nuse std::os::unix::fs::FileTypeExt;\nuse std::{\n    collections::VecDeque,\n    fs, io,\n    path::Path,\n    sync::{\n        Arc,\n        atomic::{AtomicBool, Ordering},\n    },\n    thread::{self, sleep},\n    time::Duration,\n};\n\nuse pipewire::{\n    channel::Receiver,\n    context::Context,\n    core::Core,\n    keys,\n    main_loop::MainLoop,\n    properties,\n    spa::{\n        param::audio::{AudioFormat, AudioInfoRaw},\n        pod::{self, Pod, Value, serialize::PodSerializer},\n        utils::Direction,\n    },\n    stream::{Stream, StreamFlags, StreamListener, StreamState},\n};\n\npub fn try_load_pipewire() -> Result<()> {\n    if let Err(e) = probe_pipewire() {\n        if !matches!(e, pipewire::Error::CreationFailed) {\n            return Err(e.into());\n        }\n        error!(\"Could not initialize PipeWire.\");\n\n        let is_under_flatpak = std::env::var(\"FLATPAK_ID\").is_ok();\n        let is_pw_socket_available =\n            std::env::var(\"XDG_RUNTIME_DIR\").is_ok_and(|xdg_runtime_dir| {\n                let pw_socket_path = Path::new(&xdg_runtime_dir).join(\"pipewire-0\");\n                fs::metadata(&pw_socket_path).is_ok_and(|m| m.file_type().is_socket())\n            });\n\n        if is_under_flatpak && !is_pw_socket_available {\n            error!(\n                \"Please visit the following page to find help on how to fix broken audio on flatpak.\"\n            );\n            error!(\n                \"https://github.com/alvr-org/ALVR/wiki/Installing-ALVR-and-using-SteamVR-on-Linux-through-Flatpak#failed-to-create-pipewire-errors\"\n            );\n        }\n        error!(\"Make sure PipeWire is installed on your system, running and it's version is at least 0.3.49.\n        To retry, please restart SteamVR with ALVR.\");\n    }\n    Ok(())\n}\n\nfn probe_pipewire() -> Result<(), pipewire::Error> {\n    let mainloop = MainLoop::new(None)?;\n    let context = Context::new(&mainloop)?;\n    context.connect(None)?;\n    Ok(())\n}\n\n#[derive(Clone, Copy)]\npub struct AudioInfo {\n    pub sample_rate: u32,\n    pub channel_count: u32,\n}\n\nstruct Terminate;\n\n// fixme: Opening pavucontrol while audio is actively streaming\n//  will cause audio cut out for short time,\n//  possibly related to fast state changes caused by pavucontrol\nstatic MIC_STREAMING: AtomicBool = AtomicBool::new(false);\n\npub fn audio_loop(\n    is_running: impl Fn() -> bool,\n    sender: StreamSender<()>,\n    speaker_info: Option<AudioInfo>,\n    receiver: &mut StreamReceiver<()>,\n    mic_info: Option<(AudioInfo, AudioBufferingConfig)>,\n) {\n    let sample_queue = Arc::new(Mutex::new(VecDeque::new()));\n    MIC_STREAMING.store(false, Ordering::Relaxed);\n\n    let (pw_sender, pw_receiver) = pipewire::channel::channel();\n\n    // Stall pipewire startup until we're actually streaming to not cause latency by packet buildup\n    if !is_running() {\n        return;\n    }\n\n    let pw_thread = thread::spawn({\n        let sample_queue = sample_queue.clone();\n        let mic_info = mic_info.as_ref().map(|(info, _)| *info);\n\n        move || {\n            if let Err(e) = pw_main_loop(pw_receiver, sender, speaker_info, sample_queue, mic_info)\n            {\n                error!(\"Unhandled pipewire audio device error, please report it on GitHub: {e}\");\n            }\n            debug!(\"Pipewire audio loop exiting\");\n        }\n    });\n\n    while is_running() {\n        if let Some((mic_info, buffering)) = &mic_info {\n            let rate = mic_info.sample_rate as usize;\n\n            let batch_frames_count = rate * buffering.batch_ms as usize / 1000;\n            let average_buffer_frames_count = rate * buffering.average_buffering_ms as usize / 1000;\n\n            if let Err(e) = crate::receive_samples_loop(\n                || is_running() && MIC_STREAMING.load(Ordering::Relaxed),\n                receiver,\n                sample_queue.clone(),\n                mic_info.channel_count as usize,\n                batch_frames_count,\n                average_buffer_frames_count,\n            ) {\n                error!(\"Receive samples loop encountered error {e:?}\");\n            }\n\n            // if we end up here then no consumer is currently connected to the output\n            // so discard audio packets to not cause a buildup\n            if matches!(\n                receiver.recv(Duration::from_millis(500)),\n                Err(ConnectionError::Other(_))\n            ) {\n                break;\n            }\n        } else {\n            sleep(Duration::from_millis(500));\n        }\n    }\n\n    if pw_sender.send(Terminate).is_err() {\n        error!(\n            \"Couldn't send pipewire termination signal, deinitializing forcefully.\n                Restart the VR app to reinitialize the audio device.\"\n        );\n\n        unsafe { pipewire::deinit() };\n    }\n\n    pw_thread.join().ok();\n}\n\nfn pw_main_loop(\n    pw_receiver: Receiver<Terminate>,\n    audio_sender: StreamSender<()>,\n    speaker_info: Option<AudioInfo>,\n    sample_queue: Arc<Mutex<VecDeque<f32>>>,\n    mic_info: Option<AudioInfo>,\n) -> Result<(), pipewire::Error> {\n    debug!(\"Starting pipewire thread\");\n    let mainloop = MainLoop::new(None)?;\n\n    let _receiver = pw_receiver.attach(mainloop.as_ref(), {\n        let mainloop = mainloop.clone();\n        move |_| mainloop.quit()\n    });\n\n    let context = Context::new(&mainloop)?;\n    let pw_core = context.connect(None)?;\n\n    let _speaker = if let Some(info) = speaker_info {\n        debug!(\"Creating pw output audio stream\");\n        Some(create_speaker_stream(\n            &pw_core,\n            audio_sender,\n            info.sample_rate,\n            info.channel_count,\n        )?)\n    } else {\n        None\n    };\n\n    let _mic = if let Some(info) = mic_info {\n        debug!(\"Creating pw microphone stream\");\n        Some(create_mic_stream(\n            &pw_core,\n            sample_queue,\n            info.sample_rate,\n            info.channel_count,\n        )?)\n    } else {\n        None\n    };\n\n    debug!(\"Running pipewire thread\");\n    mainloop.run();\n\n    Ok(())\n}\n\nfn audio_info_to_vec(audio_info: AudioInfoRaw) -> Vec<u8> {\n    PodSerializer::serialize(\n        io::Cursor::new(Vec::new()),\n        &Value::Object(pod::Object {\n            type_: libspa_sys::SPA_TYPE_OBJECT_Format,\n            id: libspa_sys::SPA_PARAM_EnumFormat,\n            properties: audio_info.into(),\n        }),\n    )\n    .unwrap()\n    .0\n    .into_inner()\n}\n\nfn create_speaker_stream(\n    pw_core: &Core,\n    mut sender: StreamSender<()>,\n    sample_rate: u32,\n    channel_count: u32,\n) -> Result<(Stream, StreamListener<i16>), pipewire::Error> {\n    let stream = Stream::new(\n        pw_core,\n        \"alvr-audio\",\n        properties::properties! {\n            *keys::NODE_NAME => \"ALVR Audio\",\n            *keys::MEDIA_NAME => \"alvr-audio\",\n            *keys::MEDIA_TYPE => \"Audio\",\n            *keys::MEDIA_CATEGORY => \"Capture\",\n            *keys::MEDIA_CLASS => \"Audio/Sink\",\n            *keys::MEDIA_ROLE => \"Game\",\n        },\n    )?;\n\n    let listener: StreamListener<i16> = stream\n        .add_local_listener()\n        .process(move |stream, _| {\n            if let Some(mut pw_buf) = stream.dequeue_buffer()\n                && let Some(pw_buf) = pw_buf.datas_mut().first_mut()\n            {\n                let size = pw_buf.chunk_mut().size() as usize;\n\n                if let Some(data) = pw_buf.data() {\n                    // Data is given as s16le in the correct layout by pipewire already,\n                    // no need to do conversions\n                    sender.send_header_with_payload(&(), &data[0..size]).ok();\n                }\n            }\n        })\n        .register()?;\n\n    let mut audio_info = AudioInfoRaw::new();\n    audio_info.set_format(AudioFormat::S16LE);\n    audio_info.set_rate(sample_rate);\n    audio_info.set_channels(channel_count);\n\n    stream.connect(\n        Direction::Input,\n        None,\n        StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS,\n        &mut [Pod::from_bytes(&audio_info_to_vec(audio_info)).unwrap()],\n    )?;\n\n    Ok((stream, listener))\n}\n\nfn create_mic_stream(\n    pw_core: &Core,\n    sample_queue: Arc<Mutex<VecDeque<f32>>>,\n    sample_rate: u32,\n    channel_count: u32,\n) -> Result<(Stream, StreamListener<f32>), pipewire::Error> {\n    let stream = Stream::new(\n        pw_core,\n        \"alvr-mic\",\n        properties::properties! {\n            *keys::NODE_NAME => \"ALVR Microphone\",\n            *keys::MEDIA_NAME => \"alvr-mic\",\n            *keys::MEDIA_TYPE => \"Audio\",\n            *keys::MEDIA_CATEGORY => \"Playback\",\n            *keys::MEDIA_CLASS => \"Audio/Source\",\n            *keys::MEDIA_ROLE => \"Communication\",\n        },\n    )?;\n\n    let chan_size = std::mem::size_of::<f32>();\n    let listener: StreamListener<f32> = stream\n        .add_local_listener()\n        .state_changed(move |_, _, _, new_state| {\n            MIC_STREAMING.store(new_state == StreamState::Streaming, Ordering::Relaxed);\n        })\n        .process(move |stream, _| {\n            fill_pw_buf(\n                sample_queue.clone(),\n                chan_size,\n                channel_count as usize,\n                stream,\n            );\n        })\n        .register()?;\n\n    let mut audio_info = AudioInfoRaw::new();\n    audio_info.set_format(AudioFormat::F32LE);\n    audio_info.set_rate(sample_rate);\n    audio_info.set_channels(channel_count);\n\n    stream.connect(\n        Direction::Output,\n        None,\n        StreamFlags::AUTOCONNECT | StreamFlags::MAP_BUFFERS,\n        &mut [Pod::from_bytes(&audio_info_to_vec(audio_info)).unwrap()],\n    )?;\n\n    Ok((stream, listener))\n}\n\nfn fill_pw_buf(\n    sample_queue: Arc<Mutex<VecDeque<f32>>>,\n    chan_size: usize,\n    chan_count: usize,\n    stream: &pipewire::stream::StreamRef,\n) {\n    if let Some(mut pw_buf) = stream.dequeue_buffer() {\n        let requested = pw_buf.requested() as usize;\n\n        if let Some(pw_data) = pw_buf.datas_mut().first_mut()\n            && let Some(slice) = pw_data.data()\n            && let Some(mut samples) = sample_queue.try_lock()\n        {\n            // TODO: Would it be more correct to try to split it up over multiple datas?\n            // Or is the requested size already right for the first data chunk?\n\n            let mut it = slice\n                .chunks_exact_mut(chan_size)\n                .take(requested * chan_count);\n            let pw_sample_count = it.len();\n\n            let (front, back) = samples.as_slices();\n            let copy_sample =\n                |(chunk, sample): (&mut [u8], &f32)| chunk.copy_from_slice(&sample.to_le_bytes());\n\n            // Split up so the compiler actually optimizes this properly\n            it.by_ref().zip(front).for_each(copy_sample);\n            it.zip(back).for_each(copy_sample);\n\n            let sample_count = pw_sample_count.min(samples.len());\n            drop(samples.drain(..sample_count));\n\n            let chunk = pw_data.chunk_mut();\n            *chunk.offset_mut() = 0;\n            *chunk.stride_mut() = (chan_size * chan_count) as _;\n            *chunk.size_mut() = (sample_count * chan_size) as _;\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/audio/src/windows.rs",
    "content": "use alvr_common::anyhow::{Result, bail};\nuse cpal::{Device, platform::DeviceInner};\nuse rodio::DeviceTrait;\nuse windows::{\n    Win32::{\n        Devices::FunctionDiscovery::PKEY_Device_FriendlyName,\n        Media::Audio::{\n            DEVICE_STATE_ACTIVE, Endpoints::IAudioEndpointVolume, IMMDevice, IMMDeviceEnumerator,\n            MMDeviceEnumerator, eCapture, eRender,\n        },\n        System::Com::{self, CLSCTX_ALL, COINIT_MULTITHREADED, STGM_READ},\n    },\n    core::GUID,\n};\n\nfn get_windows_device(device: &Device) -> Result<IMMDevice> {\n    let device_name = device.name()?;\n\n    unsafe {\n        // This will fail the second time is called, ignore the error\n        Com::CoInitializeEx(None, COINIT_MULTITHREADED).ok().ok();\n\n        let imm_device_enumerator: IMMDeviceEnumerator =\n            Com::CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?;\n\n        let direction = if device.supports_output() {\n            eRender\n        } else {\n            eCapture\n        };\n        let imm_device_collection =\n            imm_device_enumerator.EnumAudioEndpoints(direction, DEVICE_STATE_ACTIVE)?;\n\n        for i in 0..imm_device_collection.GetCount()? {\n            let imm_device = imm_device_collection.Item(i)?;\n\n            let imm_device_name = imm_device\n                .OpenPropertyStore(STGM_READ)?\n                .GetValue(&PKEY_Device_FriendlyName)?\n                .to_string();\n\n            if imm_device_name == device_name {\n                return Ok(imm_device);\n            }\n        }\n\n        bail!(\"No device found with specified name\")\n    }\n}\n\npub fn get_windows_device_id(device: &Device) -> Result<String> {\n    unsafe {\n        let imm_device = get_windows_device(device)?;\n\n        let id_str_ptr = imm_device.GetId()?;\n        let id_str = id_str_ptr.to_string()?;\n        Com::CoTaskMemFree(Some(id_str_ptr.0 as _));\n\n        Ok(id_str)\n    }\n}\n\n// device must be an output device\npub fn set_mute_windows_device(device: &Device, mute: bool) -> Result<()> {\n    unsafe {\n        let imm_device = get_windows_device(device)?;\n\n        let endpoint_volume = imm_device.Activate::<IAudioEndpointVolume>(CLSCTX_ALL, None)?;\n\n        endpoint_volume.SetMute(mute, &GUID::zeroed())?;\n    }\n\n    Ok(())\n}\n\npub fn is_same_device(device1: &Device, device2: &Device) -> bool {\n    let DeviceInner::Wasapi(dev1) = device1.as_inner();\n    let DeviceInner::Wasapi(dev2) = device2.as_inner();\n\n    dev1 == dev2\n}\n"
  },
  {
    "path": "alvr/client_core/Cargo.toml",
    "content": "[package]\nname = \"alvr_client_core\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[lib]\ncrate-type = [\"rlib\", \"staticlib\", \"cdylib\"]\n\n[features]\nlink-stdcpp-shared = []\ndefault = [\"link-stdcpp-shared\"]\n\n[dependencies]\nalvr_audio.workspace = true\nalvr_common.workspace = true\nalvr_graphics.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\nalvr_sockets.workspace = true\nalvr_system_info.workspace = true\n\napp_dirs2 = \"2\"\nmdns-sd = \"0.14\"\nrand = \"0.9\"\nserde = \"1\"\nserde_json = \"1\"\n\n[target.'cfg(target_os = \"android\")'.dependencies]\nandroid_logger = \"0.15\"\nndk = { version = \"0.9\", features = [\"api-level-28\", \"audio\", \"media\"] }\nndk-context = \"0.1\"\n\n[target.'cfg(not(target_os = \"android\"))'.dependencies]\nenv_logger = \"0.11\"\n"
  },
  {
    "path": "alvr/client_core/LICENSE",
    "content": "Copyright (c) 2020-2024 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "alvr/client_core/README.md",
    "content": "# alvr_client_core\n\nRust crate containing all major components for an ALVR client except the XR-API-related code.\n"
  },
  {
    "path": "alvr/client_core/build.rs",
    "content": "fn main() {\n    let platform_name = std::env::var(\"CARGO_CFG_TARGET_OS\").unwrap();\n\n    if platform_name == \"android\" {\n        println!(\"cargo:rustc-link-lib=log\");\n        println!(\"cargo:rustc-link-lib=EGL\");\n        println!(\"cargo:rustc-link-lib=GLESv3\");\n        println!(\"cargo:rustc-link-lib=android\");\n\n        #[cfg(feature = \"link-stdcpp-shared\")]\n        println!(\"cargo:rustc-link-lib=c++_shared\");\n    }\n}\n"
  },
  {
    "path": "alvr/client_core/cbindgen.toml",
    "content": "language = \"C\"\nheader = \"/* ALVR is licensed under the MIT license. https://github.com/alvr-org/ALVR/blob/master/LICENSE */\"\npragma_once = true\nautogen_warning = \"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */\"\ncpp_compat = true\ntab_width = 4\ndocumentation_style = \"c99\"\n\n[enum]\nrename_variants = \"QualifiedScreamingSnakeCase\"\n\n[parse]\nparse_deps = true\ninclude = [\"alvr_common\"]\n\n"
  },
  {
    "path": "alvr/client_core/src/audio.rs",
    "content": "use alvr_audio::Device;\nuse alvr_common::{\n    anyhow::{Result, bail},\n    parking_lot::Mutex,\n};\nuse alvr_session::AudioBufferingConfig;\nuse alvr_sockets::{StreamReceiver, StreamSender};\nuse ndk::audio::{\n    AudioCallbackResult, AudioDirection, AudioError, AudioFormat, AudioInputPreset,\n    AudioPerformanceMode, AudioSharingMode, AudioStreamBuilder,\n};\nuse std::{\n    collections::VecDeque,\n    mem, slice,\n    sync::{Arc, mpsc},\n    time::Duration,\n};\n\nconst INPUT_SAMPLES_MAX_BUFFER_COUNT: usize = 20;\nconst INPUT_RECV_TIMEOUT: Duration = Duration::from_millis(20);\n\n#[allow(unused_variables)]\npub fn record_audio_blocking(\n    is_running: Arc<dyn Fn() -> bool + Send + Sync>,\n    mut sender: StreamSender<()>,\n    device: &Device,\n    channels_count: u16,\n    mute: bool,\n) -> Result<()> {\n    assert_eq!(\n        channels_count, 1,\n        \"This code only supports mono microphone input\"\n    );\n\n    let sample_rate = alvr_audio::input_sample_rate(device)?;\n\n    let error = Arc::new(Mutex::new(None::<AudioError>));\n\n    let (samples_sender, samples_receiver) =\n        mpsc::sync_channel::<Vec<u8>>(INPUT_SAMPLES_MAX_BUFFER_COUNT);\n\n    let stream = AudioStreamBuilder::new()?\n        .direction(AudioDirection::Input)\n        .channel_count(1)\n        .sample_rate(sample_rate as _)\n        .format(AudioFormat::PCM_I16)\n        .input_preset(AudioInputPreset::VoiceCommunication)\n        .performance_mode(AudioPerformanceMode::LowLatency)\n        .sharing_mode(AudioSharingMode::Shared)\n        .data_callback(Box::new(move |_, data_ptr, frames_count| {\n            let buffer_size = frames_count as usize * mem::size_of::<i16>();\n\n            let sample_buffer =\n                unsafe { slice::from_raw_parts(data_ptr as *mut u8, buffer_size) }.to_vec();\n\n            // it will block only when the channel is full\n            samples_sender.send(sample_buffer).ok();\n\n            AudioCallbackResult::Continue\n        }))\n        .error_callback({\n            let error = Arc::clone(&error);\n            Box::new(move |_, e| *error.lock() = Some(e.into()))\n        })\n        .open_stream()?;\n\n    // If configuration changed, the stream must be restarted\n    if stream.channel_count() != 1\n        || stream.sample_rate() != sample_rate as i32\n        || stream.format() != AudioFormat::PCM_I16\n    {\n        bail!(\"Invalid audio configuration\");\n    }\n\n    stream.request_start()?;\n\n    while is_running() && error.lock().is_none() {\n        while let Ok(sample_buffer) = samples_receiver.recv_timeout(INPUT_RECV_TIMEOUT) {\n            sender.send_header_with_payload(&(), &sample_buffer).ok();\n        }\n    }\n\n    // Note: request_stop() is asynchronous, and must be called always, even on error\n    stream.request_stop()?;\n\n    if let Some(e) = *error.lock() {\n        return Err(e.into());\n    }\n\n    Ok(())\n}\n\n#[allow(unused_variables)]\npub fn play_audio_loop(\n    is_running: impl Fn() -> bool,\n    device: &Device,\n    channels_count: u16,\n    sample_rate: u32,\n    config: AudioBufferingConfig,\n    receiver: &mut StreamReceiver<()>,\n) -> Result<()> {\n    assert_eq!(channels_count, 2, \"This code only supports stereo output\");\n\n    // the client sends invalid sample rates sometimes, and we crash if we try and use one\n    // (batch_frames_count ends up zero and the audio callback gets confused)\n    if sample_rate < 8000 {\n        bail!(\"Invalid audio sample rate\");\n    }\n\n    let batch_frames_count = sample_rate as usize * config.batch_ms as usize / 1000;\n    let average_buffer_frames_count =\n        sample_rate as usize * config.average_buffering_ms as usize / 1000;\n\n    let sample_buffer = Arc::new(Mutex::new(VecDeque::new()));\n    let error = Arc::new(Mutex::new(None));\n\n    let stream = AudioStreamBuilder::new()?\n        .direction(AudioDirection::Output)\n        .channel_count(2)\n        .sample_rate(sample_rate as _)\n        .format(AudioFormat::PCM_Float)\n        .frames_per_data_callback(batch_frames_count as _)\n        .performance_mode(AudioPerformanceMode::LowLatency)\n        .sharing_mode(AudioSharingMode::Shared)\n        .data_callback({\n            let sample_buffer = Arc::clone(&sample_buffer);\n            Box::new(move |_, data_ptr, frames_count| {\n                assert!(frames_count == batch_frames_count as i32);\n\n                let samples = alvr_audio::get_next_frame_batch(\n                    &mut *sample_buffer.lock(),\n                    2,\n                    batch_frames_count as _,\n                );\n\n                let out_frames = unsafe {\n                    // 2 channels\n                    slice::from_raw_parts_mut(data_ptr as *mut f32, frames_count as usize * 2)\n                };\n                out_frames.copy_from_slice(&samples);\n\n                AudioCallbackResult::Continue\n            })\n        })\n        .error_callback({\n            let error = Arc::clone(&error);\n            Box::new(move |_, e| *error.lock() = Some(e))\n        })\n        .open_stream()?;\n\n    // If configuration changed, the stream must be restarted\n    if stream.channel_count() != 2\n        || stream.sample_rate() != sample_rate as i32\n        || stream.format() != AudioFormat::PCM_Float\n        || stream.frames_per_data_callback() != Some(batch_frames_count as _)\n    {\n        bail!(\"Invalid audio configuration\");\n    }\n\n    stream.request_start()?;\n\n    alvr_audio::receive_samples_loop(\n        || is_running() && error.lock().is_none(),\n        receiver,\n        sample_buffer,\n        2,\n        batch_frames_count,\n        average_buffer_frames_count,\n    )\n    .ok();\n\n    // Note: request_stop() is asynchronous, and must be called always, even on error\n    stream.request_stop()?;\n\n    if let Some(e) = *error.lock() {\n        return Err(e.into());\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/client_core/src/c_api.rs",
    "content": "#![expect(dead_code)]\n\nuse crate::{\n    ClientCapabilities, ClientCoreContext, ClientCoreEvent, storage,\n    video_decoder::{self, VideoDecoderConfig, VideoDecoderSource},\n};\nuse alvr_common::{\n    AlvrCodecType, AlvrFov, AlvrPose, AlvrQuat, AlvrViewParams, DeviceMotion, Pose, ViewParams,\n    anyhow::Result,\n    debug, error,\n    glam::{UVec2, Vec2, Vec3},\n    info,\n    parking_lot::Mutex,\n    warn,\n};\nuse alvr_graphics::{\n    GraphicsContext, HandData, LobbyRenderer, LobbyViewParams, SDR_FORMAT_GL, StreamRenderer,\n    StreamViewParams,\n};\nuse alvr_packets::{ButtonEntry, ButtonValue, FaceData, TrackingData};\nuse alvr_session::{\n    CodecType, FoveatedEncodingConfig, MediacodecPropType, MediacodecProperty, UpscalingConfig,\n};\nuse std::{\n    cell::RefCell,\n    ffi::{CStr, CString, c_char, c_void},\n    ptr,\n    rc::Rc,\n    slice,\n    time::{Duration, Instant},\n};\n\nstatic CLIENT_CORE_CONTEXT: Mutex<Option<ClientCoreContext>> = Mutex::new(None);\nstatic HUD_MESSAGE: Mutex<String> = Mutex::new(String::new());\nstatic SETTINGS: Mutex<String> = Mutex::new(String::new());\nstatic SERVER_VERSION: Mutex<String> = Mutex::new(String::new());\nstatic DECODER_CONFIG_BUFFER: Mutex<Vec<u8>> = Mutex::new(vec![]);\n\n// Core interface:\n\n#[repr(C)]\npub struct AlvrClientCapabilities {\n    default_view_width: u32,\n    default_view_height: u32,\n    max_view_width: u32,\n    max_view_height: u32,\n    refresh_rates: *const f32,\n    refresh_rates_count: u64,\n    foveated_encoding: bool,\n    encoder_high_profile: bool,\n    encoder_10_bits: bool,\n    encoder_av1: bool,\n    prefer_10bit: bool,\n    preferred_encoding_gamma: f32,\n    prefer_hdr: bool,\n}\n\n#[repr(u8)]\npub enum AlvrEvent {\n    HudMessageUpdated,\n    StreamingStarted {\n        view_width: u32,\n        view_height: u32,\n        refresh_rate_hint: f32,\n        encoding_gamma: f32,\n        enable_foveated_encoding: bool,\n        enable_hdr: bool,\n    },\n    StreamingStopped,\n    Haptics {\n        device_id: u64,\n        duration_s: f32,\n        frequency: f32,\n        amplitude: f32,\n    },\n    /// Note: All subsequent DecoderConfig events should be ignored until reconnection\n    DecoderConfig {\n        codec: AlvrCodecType,\n    },\n    // Unimplemented\n    RealTimeConfig {},\n}\n\n#[repr(C)]\npub struct AlvrVideoFrameData {\n    callback_context: *mut c_void,\n    timestamp_ns: u64,\n    buffer_ptr: *const u8,\n    buffer_size: u64,\n}\n\n#[repr(C)]\n#[derive(Clone, Default)]\npub struct AlvrDeviceMotion {\n    device_id: u64,\n    pose: AlvrPose,\n    linear_velocity: [f32; 3],\n    angular_velocity: [f32; 3],\n}\n\n#[allow(dead_code)]\n#[repr(C)]\npub enum AlvrButtonValue {\n    Binary(bool),\n    Scalar(f32),\n}\n\n#[allow(dead_code)]\n#[repr(u8)]\npub enum AlvrLogLevel {\n    Error,\n    Warn,\n    Info,\n    Debug,\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_initialize_logging() {\n    crate::init_logging();\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_path_string_to_id(path: *const c_char) -> u64 {\n    alvr_common::hash_string(unsafe { CStr::from_ptr(path) }.to_str().unwrap())\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_log(level: AlvrLogLevel, message: *const c_char) {\n    let message = unsafe { CStr::from_ptr(message) }.to_str().unwrap();\n    match level {\n        AlvrLogLevel::Error => error!(\"[ALVR NATIVE] {message}\"),\n        AlvrLogLevel::Warn => warn!(\"[ALVR NATIVE] {message}\"),\n        AlvrLogLevel::Info => info!(\"[ALVR NATIVE] {message}\"),\n        AlvrLogLevel::Debug => debug!(\"[ALVR NATIVE] {message}\"),\n    }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(not(debug_assertions), expect(unused_variables))]\npub extern \"C\" fn alvr_dbg_client_impl(message: *const c_char) {\n    alvr_common::dbg_client_impl!(\"{}\", unsafe { CStr::from_ptr(message) }.to_str().unwrap())\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(not(debug_assertions), expect(unused_variables))]\npub extern \"C\" fn alvr_dbg_decoder(message: *const c_char) {\n    alvr_common::dbg_decoder!(\"{}\", unsafe { CStr::from_ptr(message) }.to_str().unwrap())\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_log_time(tag: *const c_char) {\n    let tag = unsafe { CStr::from_ptr(tag) }.to_str().unwrap();\n    error!(\"[ALVR NATIVE] {tag}: {:?}\", Instant::now());\n}\n\nfn string_to_c_str(buffer: *mut c_char, value: &str) -> u64 {\n    let cstring = CString::new(value).unwrap();\n    if !buffer.is_null() {\n        unsafe {\n            ptr::copy_nonoverlapping(cstring.as_ptr(), buffer, cstring.as_bytes_with_nul().len());\n        }\n    }\n\n    cstring.as_bytes_with_nul().len() as u64\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_mdns_service(service_buffer: *mut c_char) -> u64 {\n    string_to_c_str(service_buffer, alvr_sockets::MDNS_SERVICE_TYPE)\n}\n\n/// To make sure the value is correct, call after alvr_initialize()\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_hostname(hostname_buffer: *mut c_char) -> u64 {\n    string_to_c_str(hostname_buffer, &storage::Config::load().hostname)\n}\n\n/// To make sure the value is correct, call after alvr_initialize()\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_protocol_id(protocol_buffer: *mut c_char) -> u64 {\n    string_to_c_str(protocol_buffer, &storage::Config::load().protocol_id)\n}\n\n#[cfg(target_os = \"android\")]\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_try_get_permission(permission: *const c_char) {\n    alvr_system_info::try_get_permission(unsafe { CStr::from_ptr(permission) }.to_str().unwrap());\n}\n\n/// NB: for android, `context` must be thread safe.\n#[cfg(target_os = \"android\")]\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_initialize_android_context(java_vm: *mut c_void, context: *mut c_void) {\n    unsafe { ndk_context::initialize_android_context(java_vm, context) };\n}\n\n/// On android, alvr_initialize_android_context() must be called first, then alvr_initialize().\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_initialize(capabilities: AlvrClientCapabilities) {\n    let default_view_resolution = UVec2::new(\n        capabilities.default_view_width,\n        capabilities.default_view_height,\n    );\n\n    let max_view_resolution = UVec2::new(capabilities.max_view_width, capabilities.max_view_height);\n\n    let refresh_rates = unsafe {\n        slice::from_raw_parts(\n            capabilities.refresh_rates,\n            capabilities.refresh_rates_count as usize,\n        )\n    }\n    .to_vec();\n\n    let capabilities = ClientCapabilities {\n        platform: alvr_system_info::platform(None, None),\n        default_view_resolution,\n        max_view_resolution,\n        refresh_rates,\n        foveated_encoding: capabilities.foveated_encoding,\n        encoder_high_profile: capabilities.encoder_high_profile,\n        encoder_10_bits: capabilities.encoder_10_bits,\n        encoder_av1: capabilities.encoder_av1,\n        prefer_10bit: capabilities.prefer_10bit,\n        preferred_encoding_gamma: capabilities.preferred_encoding_gamma,\n        prefer_hdr: capabilities.prefer_hdr,\n    };\n    *CLIENT_CORE_CONTEXT.lock() = Some(ClientCoreContext::new(capabilities));\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_destroy() {\n    *CLIENT_CORE_CONTEXT.lock() = None;\n\n    #[cfg(target_os = \"android\")]\n    unsafe {\n        ndk_context::release_android_context()\n    };\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_resume() {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.resume();\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_pause() {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.pause();\n    }\n}\n\n/// Returns true if there was a new event\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_poll_event(out_event: *mut AlvrEvent) -> bool {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock()\n        && let Some(event) = context.poll_event()\n    {\n        let event = match event {\n            ClientCoreEvent::UpdateHudMessage(message) => {\n                *HUD_MESSAGE.lock() = message;\n\n                AlvrEvent::HudMessageUpdated\n            }\n            ClientCoreEvent::StreamingStarted(stream_config) => {\n                *SETTINGS.lock() = serde_json::to_string(&stream_config.settings).unwrap();\n                *SERVER_VERSION.lock() = stream_config.server_version.to_string();\n\n                AlvrEvent::StreamingStarted {\n                    view_width: stream_config.negotiated_config.view_resolution.x,\n                    view_height: stream_config.negotiated_config.view_resolution.y,\n                    refresh_rate_hint: stream_config.negotiated_config.refresh_rate_hint,\n                    encoding_gamma: stream_config.negotiated_config.encoding_gamma,\n                    enable_foveated_encoding: stream_config\n                        .negotiated_config\n                        .enable_foveated_encoding,\n                    enable_hdr: stream_config.negotiated_config.enable_hdr,\n                }\n            }\n            ClientCoreEvent::StreamingStopped => AlvrEvent::StreamingStopped,\n            ClientCoreEvent::Haptics {\n                device_id,\n                duration,\n                frequency,\n                amplitude,\n            } => AlvrEvent::Haptics {\n                device_id,\n                duration_s: duration.as_secs_f32(),\n                frequency,\n                amplitude,\n            },\n            ClientCoreEvent::DecoderConfig { codec, config_nal } => {\n                *DECODER_CONFIG_BUFFER.lock() = config_nal;\n\n                AlvrEvent::DecoderConfig {\n                    codec: match codec {\n                        CodecType::H264 => AlvrCodecType::H264,\n                        CodecType::Hevc => AlvrCodecType::Hevc,\n                        CodecType::AV1 => AlvrCodecType::AV1,\n                    },\n                }\n            }\n            ClientCoreEvent::RealTimeConfig(_) => AlvrEvent::RealTimeConfig {},\n        };\n\n        unsafe { *out_event = event };\n\n        true\n    } else {\n        false\n    }\n}\n\n// Returns the length of the message. message_buffer can be null.\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_hud_message(message_buffer: *mut c_char) -> u64 {\n    let cstring = CString::new(HUD_MESSAGE.lock().clone()).unwrap();\n    if !message_buffer.is_null() {\n        unsafe {\n            ptr::copy_nonoverlapping(\n                cstring.as_ptr(),\n                message_buffer,\n                cstring.as_bytes_with_nul().len(),\n            );\n        }\n    }\n\n    cstring.as_bytes_with_nul().len() as u64\n}\n\n/// Settings will be updated after receiving StreamingStarted event\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_settings_json(out_buffer: *mut c_char) -> u64 {\n    string_to_c_str(out_buffer, &SETTINGS.lock())\n}\n\n/// Will be updated after receiving StreamingStarted event\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_server_version(out_buffer: *mut c_char) -> u64 {\n    string_to_c_str(out_buffer, &SERVER_VERSION.lock())\n}\n\n/// Returns the number of bytes of the decoder_buffer\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_decoder_config(out_buffer: *mut c_char) -> u64 {\n    let buffer = DECODER_CONFIG_BUFFER.lock();\n\n    let size = buffer.len();\n\n    if !out_buffer.is_null() {\n        unsafe { ptr::copy_nonoverlapping(buffer.as_ptr(), out_buffer.cast(), size) }\n    }\n\n    size as u64\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_battery(device_id: u64, gauge_value: f32, is_plugged: bool) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_battery(device_id, gauge_value, is_plugged);\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_playspace(width: f32, height: f32) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_playspace(Some(Vec2::new(width, height)));\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_active_interaction_profile(\n    device_id: u64,\n    profile_id: u64,\n    input_ids_ptr: *const u64,\n    input_ids_count: u64,\n) {\n    let input_ids = if !input_ids_ptr.is_null() {\n        unsafe { slice::from_raw_parts(input_ids_ptr, input_ids_count as usize) }\n    } else {\n        &[]\n    };\n\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_active_interaction_profile(\n            device_id,\n            profile_id,\n            input_ids.iter().cloned().collect(),\n        );\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_button(path_id: u64, value: AlvrButtonValue) {\n    let value = match value {\n        AlvrButtonValue::Binary(value) => ButtonValue::Binary(value),\n        AlvrButtonValue::Scalar(value) => ButtonValue::Scalar(value),\n    };\n\n    // crate::send_buttons(vec![ButtonEntry { path_id, value }]);\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_buttons(vec![ButtonEntry { path_id, value }]);\n    }\n}\n\n/// The view poses need to be in local space, as if the head is at the origin.\n/// view_params: array of 2\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_view_params(view_params: *const AlvrViewParams) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_view_params(unsafe {\n            [\n                alvr_common::from_capi_view_params(&(*view_params)),\n                alvr_common::from_capi_view_params(&(*view_params.offset(1))),\n            ]\n        });\n    }\n}\n\n/// hand_skeleton:\n/// * outer ptr: array of 2 (can be null);\n/// * inner ptr: array of 26 (can be null if hand is absent)\n///\n/// combined_eye_gaze: can be null if eye gaze is absent\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_tracking(\n    poll_timestamp_ns: u64,\n    device_motions: *const AlvrDeviceMotion,\n    device_motions_count: u64,\n    hand_skeletons: *const *const AlvrPose,\n    combined_eye_gaze: *const AlvrQuat,\n) {\n    let mut raw_motions = vec![AlvrDeviceMotion::default(); device_motions_count as _];\n    unsafe {\n        ptr::copy_nonoverlapping(\n            device_motions,\n            raw_motions.as_mut_ptr(),\n            device_motions_count as usize,\n        );\n    }\n\n    let device_motions = raw_motions\n        .into_iter()\n        .map(|motion| {\n            (\n                motion.device_id,\n                DeviceMotion {\n                    pose: alvr_common::from_capi_pose(&motion.pose),\n                    linear_velocity: Vec3::from_slice(&motion.linear_velocity),\n                    angular_velocity: Vec3::from_slice(&motion.angular_velocity),\n                },\n            )\n        })\n        .collect::<Vec<_>>();\n\n    let hand_skeletons = if !hand_skeletons.is_null() {\n        let hand_skeletons = unsafe { slice::from_raw_parts(hand_skeletons, 2) };\n        let hand_skeletons = hand_skeletons\n            .iter()\n            .map(|&hand_skeleton| {\n                (!hand_skeleton.is_null()).then(|| {\n                    let hand_skeleton = unsafe { slice::from_raw_parts(hand_skeleton, 26) };\n\n                    let mut array = [Pose::IDENTITY; 26];\n\n                    for (pose, capi_pose) in array.iter_mut().zip(hand_skeleton.iter()) {\n                        *pose = Pose {\n                            orientation: alvr_common::from_capi_quat(&capi_pose.orientation),\n                            position: Vec3::from_slice(&capi_pose.position),\n                        };\n                    }\n\n                    array\n                })\n            })\n            .collect::<Vec<_>>();\n\n        [hand_skeletons[0], hand_skeletons[1]]\n    } else {\n        [None, None]\n    };\n\n    let eyes_combined = if !combined_eye_gaze.is_null() {\n        Some(alvr_common::from_capi_quat(unsafe { &*combined_eye_gaze }))\n    } else {\n        None\n    };\n\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.send_tracking(TrackingData {\n            poll_timestamp: Duration::from_nanos(poll_timestamp_ns),\n            device_motions,\n            hand_skeletons,\n            face: FaceData {\n                eyes_combined,\n                ..Default::default()\n            },\n            body: None,\n        });\n    }\n}\n\n/// Safety: `context` must be thread safe and valid until the StreamingStopped event.\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_set_decoder_input_callback(\n    callback_context: *mut c_void,\n    callback: extern \"C\" fn(AlvrVideoFrameData) -> bool,\n) {\n    struct CallbackContext(*mut c_void);\n    unsafe impl Send for CallbackContext {}\n\n    let callback_context = CallbackContext(callback_context);\n\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.set_decoder_input_callback(Box::new(move |timestamp, buffer| {\n            // Make sure to capture the struct itself instead of just the pointer to make the\n            // borrow checker happy\n            let callback_context = &callback_context;\n\n            callback(AlvrVideoFrameData {\n                callback_context: callback_context.0,\n                timestamp_ns: timestamp.as_nanos() as u64,\n                buffer_ptr: buffer.as_ptr(),\n                buffer_size: buffer.len() as u64,\n            })\n        }));\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_frame_decoded(target_timestamp_ns: u64) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.report_frame_decoded(Duration::from_nanos(target_timestamp_ns));\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_fatal_decoder_error(message: *const c_char) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.report_fatal_decoder_error(unsafe { CStr::from_ptr(message).to_str().unwrap() });\n    }\n}\n\n/// out_view_params must be a vector of 2 elements\n/// out_view_params is populated only if the core context is valid\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_compositor_start(\n    target_timestamp_ns: u64,\n    out_view_params: *mut AlvrViewParams,\n) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        let view_params =\n            context.report_compositor_start(Duration::from_nanos(target_timestamp_ns));\n\n        unsafe {\n            *out_view_params = alvr_common::to_capi_view_params(&view_params[0]);\n            *out_view_params.offset(1) = alvr_common::to_capi_view_params(&view_params[1]);\n        }\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_submit(target_timestamp_ns: u64, vsync_queue_ns: u64) {\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.report_submit(\n            Duration::from_nanos(target_timestamp_ns),\n            Duration::from_nanos(vsync_queue_ns),\n        );\n    }\n}\n\n// OpenGL-related interface\n\nthread_local! {\n    static GRAPHICS_CONTEXT: RefCell<Option<Rc<GraphicsContext>>> = const { RefCell::new(None) };\n    static LOBBY_RENDERER: RefCell<Option<LobbyRenderer>> = const { RefCell::new(None) };\n    static STREAM_RENDERER: RefCell<Option<StreamRenderer>> = const { RefCell::new(None) };\n}\n\n#[repr(C)]\npub struct AlvrLobbyViewParams {\n    swapchain_index: u32,\n    view_params: AlvrViewParams,\n}\n\n#[repr(C)]\npub struct AlvrStreamViewParams {\n    swapchain_index: u32,\n    reprojection_rotation: AlvrQuat,\n    fov: AlvrFov,\n}\n\n#[repr(C)]\npub struct AlvrStreamConfig {\n    view_resolution_width: u32,\n    view_resolution_height: u32,\n    swapchain_textures: *mut *const u32,\n    swapchain_length: u32,\n    enable_foveation: bool,\n    foveation_center_size_x: f32,\n    foveation_center_size_y: f32,\n    foveation_center_shift_x: f32,\n    foveation_center_shift_y: f32,\n    foveation_edge_ratio_x: f32,\n    foveation_edge_ratio_y: f32,\n    enable_upscaling: bool,\n    upscaling_edge_direction: bool,\n    upscaling_edge_threshold: f32,\n    upscaling_edge_sharpness: f32,\n    upscale_factor: f32,\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_initialize_opengl() {\n    GRAPHICS_CONTEXT.set(Some(Rc::new(GraphicsContext::new_gl())));\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_destroy_opengl() {\n    GRAPHICS_CONTEXT.set(None);\n}\n\nfn convert_swapchain_array(\n    swapchain_textures: *mut *const u32,\n    swapchain_length: u32,\n) -> [Vec<u32>; 2] {\n    let swapchain_length = swapchain_length as usize;\n    let mut left_swapchain = vec![0; swapchain_length];\n    unsafe {\n        ptr::copy_nonoverlapping(\n            *swapchain_textures,\n            left_swapchain.as_mut_ptr(),\n            swapchain_length,\n        )\n    };\n    let mut right_swapchain = vec![0; swapchain_length];\n    unsafe {\n        ptr::copy_nonoverlapping(\n            *swapchain_textures.offset(1),\n            right_swapchain.as_mut_ptr(),\n            swapchain_length,\n        )\n    };\n\n    [left_swapchain, right_swapchain]\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_resume_opengl(\n    preferred_view_width: u32,\n    preferred_view_height: u32,\n    swapchain_textures: *mut *const u32,\n    swapchain_length: u32,\n) {\n    LOBBY_RENDERER.set(Some(LobbyRenderer::new(\n        GRAPHICS_CONTEXT.with_borrow(|c| c.as_ref().unwrap().clone()),\n        UVec2::new(preferred_view_width, preferred_view_height),\n        convert_swapchain_array(swapchain_textures, swapchain_length),\n        \"\",\n    )));\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_pause_opengl() {\n    STREAM_RENDERER.set(None);\n    LOBBY_RENDERER.set(None)\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_update_hud_message_opengl(message: *const c_char) {\n    LOBBY_RENDERER.with_borrow(|renderer| {\n        if let Some(renderer) = renderer {\n            renderer.update_hud_message(unsafe { CStr::from_ptr(message) }.to_str().unwrap());\n        }\n    });\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_start_stream_opengl(config: AlvrStreamConfig) {\n    let view_resolution = UVec2::new(config.view_resolution_width, config.view_resolution_height);\n    let swapchain_textures =\n        convert_swapchain_array(config.swapchain_textures, config.swapchain_length);\n    let foveated_encoding = config.enable_foveation.then_some(FoveatedEncodingConfig {\n        force_enable: true,\n        center_size_x: config.foveation_center_size_x,\n        center_size_y: config.foveation_center_size_y,\n        center_shift_x: config.foveation_center_shift_x,\n        center_shift_y: config.foveation_center_shift_y,\n        edge_ratio_x: config.foveation_edge_ratio_x,\n        edge_ratio_y: config.foveation_edge_ratio_y,\n    });\n    let upscaling = config.enable_upscaling.then_some(UpscalingConfig {\n        edge_direction: config.upscaling_edge_direction,\n        edge_sharpness: config.upscaling_edge_sharpness,\n        edge_threshold: config.upscaling_edge_threshold,\n        upscale_factor: config.upscale_factor,\n    });\n\n    STREAM_RENDERER.set(Some(StreamRenderer::new(\n        GRAPHICS_CONTEXT.with_borrow(|c| c.as_ref().unwrap().clone()),\n        view_resolution,\n        alvr_graphics::compute_target_view_resolution(view_resolution, &upscaling),\n        swapchain_textures,\n        SDR_FORMAT_GL,\n        foveated_encoding,\n        true,\n        false, // TODO: limited range fix config\n        1.0,   // TODO: encoding gamma config\n        upscaling,\n    )));\n}\n\n// todo: support hands\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_render_lobby_opengl(\n    view_inputs: *const AlvrLobbyViewParams,\n    render_background: bool,\n) {\n    let view_inputs = unsafe {\n        [\n            LobbyViewParams {\n                swapchain_index: (*view_inputs).swapchain_index,\n                view_params: alvr_common::from_capi_view_params(&(*view_inputs).view_params),\n            },\n            LobbyViewParams {\n                swapchain_index: (*view_inputs.offset(1)).swapchain_index,\n                view_params: alvr_common::from_capi_view_params(\n                    &(*view_inputs.offset(1)).view_params,\n                ),\n            },\n        ]\n    };\n\n    LOBBY_RENDERER.with_borrow(|renderer| {\n        if let Some(renderer) = renderer {\n            renderer.render(\n                view_inputs,\n                [\n                    HandData {\n                        grip_motion: None,\n                        detached_grip_motion: None,\n                        skeleton_joints: None,\n                    },\n                    HandData {\n                        grip_motion: None,\n                        detached_grip_motion: None,\n                        skeleton_joints: None,\n                    },\n                ],\n                None,\n                None,\n                render_background,\n                false,\n            );\n        }\n    });\n}\n\n/// view_params: array of 2\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_render_stream_opengl(\n    hardware_buffer: *mut c_void,\n    view_params: *const AlvrStreamViewParams,\n) {\n    STREAM_RENDERER.with_borrow(|renderer| {\n        if let Some(renderer) = renderer {\n            let left_params = unsafe { &*view_params };\n            let right_params = unsafe { &*view_params.offset(1) };\n            renderer.render(\n                hardware_buffer,\n                [\n                    StreamViewParams {\n                        swapchain_index: left_params.swapchain_index,\n                        input_view_params: ViewParams {\n                            pose: Pose::IDENTITY,\n                            fov: alvr_common::from_capi_fov(&left_params.fov),\n                        },\n                        output_view_params: ViewParams {\n                            pose: Pose {\n                                orientation: alvr_common::from_capi_quat(\n                                    &left_params.reprojection_rotation,\n                                ),\n                                position: Vec3::ZERO,\n                            },\n                            fov: alvr_common::from_capi_fov(&left_params.fov),\n                        },\n                    },\n                    StreamViewParams {\n                        swapchain_index: right_params.swapchain_index,\n                        input_view_params: ViewParams {\n                            pose: Pose::IDENTITY,\n                            fov: alvr_common::from_capi_fov(&right_params.fov),\n                        },\n                        output_view_params: ViewParams {\n                            pose: Pose {\n                                orientation: alvr_common::from_capi_quat(\n                                    &right_params.reprojection_rotation,\n                                ),\n                                position: Vec3::ZERO,\n                            },\n                            fov: alvr_common::from_capi_fov(&right_params.fov),\n                        },\n                    },\n                ],\n                None,\n            );\n        }\n    });\n}\n\n// Decoder-related interface\n\nstatic DECODER_SOURCE: Mutex<Option<VideoDecoderSource>> = Mutex::new(None);\n\n#[repr(u8)]\npub enum AlvrMediacodecPropType {\n    Float,\n    Int32,\n    Int64,\n    String,\n}\n\n#[repr(C)]\npub union AlvrMediacodecPropValue {\n    float_: f32,\n    int32: i32,\n    int64: i64,\n    string: *const c_char,\n}\n\n#[repr(C)]\npub struct AlvrMediacodecOption {\n    key: *const c_char,\n    ty: AlvrMediacodecPropType,\n    value: AlvrMediacodecPropValue,\n}\n\n#[repr(C)]\npub struct AlvrDecoderConfig {\n    codec: AlvrCodecType,\n    force_software_decoder: bool,\n    max_buffering_frames: f32,\n    buffering_history_weight: f32,\n    options: *const AlvrMediacodecOption,\n    options_count: u64,\n    config_buffer: *const u8,\n    config_buffer_size: u64,\n}\n\n/// alvr_initialize() must be called before alvr_create_decoder\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_create_decoder(config: AlvrDecoderConfig) {\n    let config = VideoDecoderConfig {\n        codec: match config.codec {\n            AlvrCodecType::H264 => CodecType::H264,\n            AlvrCodecType::Hevc => CodecType::Hevc,\n            AlvrCodecType::AV1 => CodecType::AV1,\n        },\n        force_software_decoder: config.force_software_decoder,\n        max_buffering_frames: config.max_buffering_frames,\n        buffering_history_weight: config.buffering_history_weight,\n        options: if !config.options.is_null() {\n            let options =\n                unsafe { slice::from_raw_parts(config.options, config.options_count as usize) };\n            options\n                .iter()\n                .map(|option| unsafe {\n                    let key = CStr::from_ptr(option.key).to_str().unwrap();\n                    let prop = match option.ty {\n                        AlvrMediacodecPropType::Float => MediacodecProperty {\n                            ty: MediacodecPropType::Float,\n                            value: option.value.float_.to_string(),\n                        },\n                        AlvrMediacodecPropType::Int32 => MediacodecProperty {\n                            ty: MediacodecPropType::Int32,\n                            value: option.value.int32.to_string(),\n                        },\n                        AlvrMediacodecPropType::Int64 => MediacodecProperty {\n                            ty: MediacodecPropType::Int64,\n                            value: option.value.int64.to_string(),\n                        },\n                        AlvrMediacodecPropType::String => MediacodecProperty {\n                            ty: MediacodecPropType::String,\n                            value: CStr::from_ptr(option.value.string)\n                                .to_str()\n                                .unwrap()\n                                .to_owned(),\n                        },\n                    };\n\n                    (key.to_owned(), prop)\n                })\n                .collect()\n        } else {\n            vec![]\n        },\n        config_buffer: unsafe {\n            slice::from_raw_parts(config.config_buffer, config.config_buffer_size as usize).to_vec()\n        },\n    };\n\n    let (mut sink, source) =\n        video_decoder::create_decoder(config, |maybe_timestamp: Result<Duration>| {\n            if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n                match maybe_timestamp {\n                    Ok(timestamp) => context.report_frame_decoded(timestamp),\n                    Err(e) => context.report_fatal_decoder_error(&e.to_string()),\n                }\n            }\n        });\n\n    *DECODER_SOURCE.lock() = Some(source);\n\n    if let Some(context) = &*CLIENT_CORE_CONTEXT.lock() {\n        context.set_decoder_input_callback(Box::new(move |timestamp, buffer| {\n            sink.push_nal(timestamp, buffer)\n        }));\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_destroy_decoder() {\n    *DECODER_SOURCE.lock() = None;\n}\n\n// Returns true if the timestamp and buffer has been written to\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_frame(\n    out_timestamp_ns: *mut u64,\n    out_buffer_ptr: *mut *mut c_void,\n) -> bool {\n    if let Some(source) = &mut *DECODER_SOURCE.lock()\n        && let Some((timestamp, buffer_ptr)) = source.get_frame()\n    {\n        unsafe {\n            *out_timestamp_ns = timestamp.as_nanos() as u64;\n            *out_buffer_ptr = buffer_ptr;\n        }\n\n        true\n    } else {\n        false\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_rotation_delta(source: AlvrQuat, destination: AlvrQuat) -> AlvrQuat {\n    alvr_common::to_capi_quat(\n        &(alvr_common::from_capi_quat(&source).inverse()\n            * alvr_common::from_capi_quat(&destination)),\n    )\n}\n"
  },
  {
    "path": "alvr/client_core/src/connection.rs",
    "content": "#![allow(clippy::if_same_then_else)]\n\nuse crate::{\n    ClientCapabilities, ClientCoreEvent,\n    logging_backend::{LOG_CHANNEL_SENDER, LogMirrorData},\n    sockets::AnnouncerSocket,\n    statistics::StatisticsManager,\n    storage::Config,\n};\nuse alvr_common::{\n    ALVR_VERSION, AnyhowToCon, ConResult, ConnectionError, ConnectionState, LifecycleState,\n    ViewParams, dbg_connection, debug, error, info,\n    parking_lot::{Condvar, Mutex, RwLock},\n    wait_rwlock, warn,\n};\nuse alvr_packets::{\n    AUDIO, ClientConnectionResult, ClientControlPacket, ClientStatistics, ConnectionAcceptedInfo,\n    HAPTICS, Haptics, STATISTICS, ServerControlPacket, StreamConfigPacket, TRACKING, TrackingData,\n    VIDEO, VideoPacketHeader, VideoStreamingCapabilities, VideoStreamingCapabilitiesExt,\n};\nuse alvr_session::{SocketProtocol, settings_schema::Switch};\nuse alvr_sockets::{\n    ControlSocketSender, KEEPALIVE_INTERVAL, KEEPALIVE_TIMEOUT, PeerType, ProtoControlSocket,\n    StreamSender, StreamSocketBuilder,\n};\nuse std::{\n    collections::VecDeque,\n    sync::{Arc, mpsc},\n    thread,\n    time::{Duration, Instant},\n};\n\n#[cfg(target_os = \"android\")]\nuse crate::audio;\n#[cfg(not(target_os = \"android\"))]\nuse alvr_audio as audio;\n\nconst INITIAL_MESSAGE: &str = concat!(\n    \"Searching for streamer...\\n\",\n    \"Open ALVR on your PC then click \\\"Trust\\\"\\n\",\n    \"next to the device entry\",\n);\nconst SUCCESS_CONNECT_MESSAGE: &str = \"Successful connection!\\nPlease wait...\";\nconst STREAM_STARTING_MESSAGE: &str = \"The stream will begin soon\\nPlease wait...\";\nconst SERVER_RESTART_MESSAGE: &str = \"The streamer is restarting\\nPlease wait...\";\nconst SERVER_DISCONNECTED_MESSAGE: &str = \"The streamer has disconnected.\";\nconst CONNECTION_TIMEOUT_MESSAGE: &str = \"Connection timeout.\";\n\nconst SOCKET_INIT_RETRY_INTERVAL: Duration = Duration::from_millis(500);\nconst CONNECTION_RETRY_INTERVAL: Duration = Duration::from_secs(1);\nconst HANDSHAKE_ACTION_TIMEOUT: Duration = Duration::from_secs(2);\nconst STREAMING_RECV_TIMEOUT: Duration = Duration::from_millis(500);\n\nconst MAX_UNREAD_PACKETS: usize = 10; // Applies per stream\n\npub type DecoderCallback = dyn FnMut(Duration, &[u8]) -> bool + Send;\n\n#[derive(Default)]\npub struct ConnectionContext {\n    pub state: RwLock<ConnectionState>,\n    pub disconnected_notif: Condvar,\n    pub control_sender: Mutex<Option<ControlSocketSender<ClientControlPacket>>>,\n    pub tracking_sender: Mutex<Option<StreamSender<TrackingData>>>,\n    pub statistics_sender: Mutex<Option<StreamSender<ClientStatistics>>>,\n    pub statistics_manager: Mutex<Option<StatisticsManager>>,\n    pub decoder_callback: Mutex<Option<Box<DecoderCallback>>>,\n    pub global_view_params_queue: Mutex<VecDeque<(Duration, [ViewParams; 2])>>,\n    pub max_prediction: RwLock<Duration>,\n}\n\nfn set_hud_message(event_queue: &Mutex<VecDeque<ClientCoreEvent>>, message: &str) {\n    let message = format!(\n        \"ALVR v{}\\nhostname: {}\\nIP: {}\\n\\n{message}\",\n        *ALVR_VERSION,\n        Config::load().hostname,\n        alvr_system_info::local_ip(),\n    );\n\n    event_queue\n        .lock()\n        .push_back(ClientCoreEvent::UpdateHudMessage(message));\n}\n\nfn is_streaming(ctx: &ConnectionContext) -> bool {\n    *ctx.state.read() == ConnectionState::Streaming\n}\n\npub fn connection_lifecycle_loop(\n    capabilities: ClientCapabilities,\n    ctx: Arc<ConnectionContext>,\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    event_queue: Arc<Mutex<VecDeque<ClientCoreEvent>>>,\n) {\n    dbg_connection!(\"connection_lifecycle_loop: Begin\");\n\n    set_hud_message(&event_queue, INITIAL_MESSAGE);\n\n    while *lifecycle_state.read() != LifecycleState::ShuttingDown {\n        if *lifecycle_state.read() == LifecycleState::Resumed {\n            if let Err(e) = connection_pipeline(\n                capabilities.clone(),\n                Arc::clone(&ctx),\n                Arc::clone(&lifecycle_state),\n                Arc::clone(&event_queue),\n            ) {\n                let message = format!(\"Connection error:\\n{e}\\nCheck the PC for more details\");\n                set_hud_message(&event_queue, &message);\n                error!(\"Connection error: {e}\");\n            }\n        } else {\n            debug!(\"Skip try connection because the device is sleeping\");\n        }\n\n        *ctx.state.write() = ConnectionState::Disconnected;\n        ctx.disconnected_notif.notify_all();\n\n        thread::sleep(CONNECTION_RETRY_INTERVAL);\n    }\n\n    dbg_connection!(\"connection_lifecycle_loop: End\");\n}\n\nfn connection_pipeline(\n    capabilities: ClientCapabilities,\n    ctx: Arc<ConnectionContext>,\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    event_queue: Arc<Mutex<VecDeque<ClientCoreEvent>>>,\n) -> ConResult {\n    dbg_connection!(\"connection_pipeline: Begin\");\n\n    let (mut proto_control_socket, server_ip) = {\n        let config = Config::load();\n        let announcer_socket = AnnouncerSocket::new(&config.hostname).to_con()?;\n        let listener_socket =\n            alvr_sockets::get_server_listener(HANDSHAKE_ACTION_TIMEOUT).to_con()?;\n\n        loop {\n            if *lifecycle_state.write() != LifecycleState::Resumed {\n                return Ok(());\n            }\n\n            announcer_socket.announce().ok();\n\n            if let Ok(pair) = ProtoControlSocket::connect_to(\n                SOCKET_INIT_RETRY_INTERVAL,\n                PeerType::Server(&listener_socket),\n            ) {\n                set_hud_message(&event_queue, SUCCESS_CONNECT_MESSAGE);\n                break pair;\n            }\n        }\n    };\n\n    let mut connection_state_lock = ctx.state.write();\n    let disconnect_notif = Arc::new(Condvar::new());\n\n    *connection_state_lock = ConnectionState::Connecting;\n\n    // TODO: Don't fetch cpal sample rate, get directly from AAudio\n    let microphone_sample_rate =\n        alvr_audio::input_sample_rate(&alvr_audio::new_input(None).to_con()?).to_con()?;\n\n    dbg_connection!(\"connection_pipeline: Send stream capabilities\");\n    proto_control_socket\n        .send(&ClientConnectionResult::ConnectionAccepted(Box::new(\n            ConnectionAcceptedInfo {\n                client_protocol_id: alvr_common::protocol_id_u64(),\n                platform_string: capabilities.platform.to_string(),\n                server_ip,\n                streaming_capabilities: Some(\n                    VideoStreamingCapabilities {\n                        default_view_resolution: capabilities.default_view_resolution,\n                        max_view_resolution: capabilities.max_view_resolution,\n                        refresh_rates: capabilities.refresh_rates,\n                        microphone_sample_rate,\n                        foveated_encoding: capabilities.foveated_encoding,\n                        encoder_high_profile: capabilities.encoder_high_profile,\n                        encoder_10_bits: capabilities.encoder_10_bits,\n                        encoder_av1: capabilities.encoder_av1,\n                        prefer_10bit: capabilities.prefer_10bit,\n                        preferred_encoding_gamma: capabilities.preferred_encoding_gamma,\n                        prefer_hdr: capabilities.prefer_hdr,\n                        ext_str: String::new(),\n                    }\n                    .with_ext(VideoStreamingCapabilitiesExt {}),\n                ),\n            },\n        )))\n        .to_con()?;\n    let config_packet =\n        proto_control_socket.recv::<StreamConfigPacket>(HANDSHAKE_ACTION_TIMEOUT)?;\n    dbg_connection!(\"connection_pipeline: stream config received\");\n\n    let stream_config = config_packet.to_stream_config().to_con()?;\n\n    let streaming_start_event = ClientCoreEvent::StreamingStarted(Box::new(stream_config.clone()));\n\n    let settings = stream_config.settings;\n    let negotiated_config = stream_config.negotiated_config;\n\n    *ctx.max_prediction.write() = Duration::from_millis(settings.headset.max_prediction_ms);\n\n    *ctx.statistics_manager.lock() = Some(StatisticsManager::new(\n        settings.connection.statistics_history_size,\n    ));\n\n    let (mut control_sender, mut control_receiver) = proto_control_socket\n        .split(STREAMING_RECV_TIMEOUT)\n        .to_con()?;\n\n    match control_receiver.recv(HANDSHAKE_ACTION_TIMEOUT) {\n        Ok(ServerControlPacket::StartStream) => {\n            info!(\"Stream starting\");\n            set_hud_message(&event_queue, STREAM_STARTING_MESSAGE);\n        }\n        Ok(ServerControlPacket::Restarting) => {\n            info!(\"Server restarting\");\n            set_hud_message(&event_queue, SERVER_RESTART_MESSAGE);\n            return Ok(());\n        }\n        Err(e) => {\n            info!(\"Server disconnected. Cause: {e}\");\n            set_hud_message(&event_queue, SERVER_DISCONNECTED_MESSAGE);\n            return Ok(());\n        }\n        _ => {\n            info!(\"Unexpected packet\");\n            set_hud_message(&event_queue, \"Unexpected packet\");\n            return Ok(());\n        }\n    }\n\n    let stream_protocol = if negotiated_config.wired {\n        SocketProtocol::Tcp\n    } else {\n        settings.connection.stream_protocol\n    };\n\n    dbg_connection!(\"connection_pipeline: create StreamSocket\");\n    let stream_socket_builder = StreamSocketBuilder::listen_for_server(\n        Duration::from_secs(1),\n        settings.connection.stream_port,\n        stream_protocol,\n        settings.connection.dscp,\n        settings.connection.client_buffer_config,\n    )\n    .to_con()?;\n\n    dbg_connection!(\"connection_pipeline: Send StreamReady\");\n    if let Err(e) = control_sender.send(&ClientControlPacket::StreamReady) {\n        info!(\"Server disconnected. Cause: {e:?}\");\n        set_hud_message(&event_queue, SERVER_DISCONNECTED_MESSAGE);\n        return Ok(());\n    }\n\n    dbg_connection!(\"connection_pipeline: accept connection\");\n    let mut stream_socket = stream_socket_builder.accept_from_server(\n        server_ip,\n        settings.connection.stream_port,\n        settings.connection.packet_size as _,\n        HANDSHAKE_ACTION_TIMEOUT,\n    )?;\n\n    info!(\"Connected to server\");\n\n    let mut video_receiver =\n        stream_socket.subscribe_to_stream::<VideoPacketHeader>(VIDEO, MAX_UNREAD_PACKETS);\n    let mut game_audio_receiver = stream_socket.subscribe_to_stream(AUDIO, MAX_UNREAD_PACKETS);\n    let tracking_sender = stream_socket.request_stream(TRACKING);\n    let mut haptics_receiver =\n        stream_socket.subscribe_to_stream::<Haptics>(HAPTICS, MAX_UNREAD_PACKETS);\n    let statistics_sender = stream_socket.request_stream(STATISTICS);\n\n    let video_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        move || {\n            let mut stream_corrupted = true;\n            while is_streaming(&ctx) {\n                let data = match video_receiver.recv(STREAMING_RECV_TIMEOUT) {\n                    Ok(data) => data,\n                    Err(ConnectionError::TryAgain(_)) => continue,\n                    Err(ConnectionError::Other(_)) => return,\n                };\n                let Ok((header, nal)) = data.get() else {\n                    return;\n                };\n\n                if let Some(stats) = &mut *ctx.statistics_manager.lock() {\n                    stats.report_video_packet_received(header.timestamp);\n                }\n\n                if header.is_idr {\n                    stream_corrupted = false;\n                } else if data.had_packet_loss() {\n                    stream_corrupted = true;\n                    if let Some(sender) = &mut *ctx.control_sender.lock() {\n                        sender.send(&ClientControlPacket::RequestIdr).ok();\n                    }\n                    warn!(\"Network dropped video packet\");\n                }\n\n                if !stream_corrupted || !settings.connection.avoid_video_glitching {\n                    // The view params must be enqueued before calling the decoder callback, there\n                    // is no problem if the callback fails\n                    {\n                        let global_view_params_queue_lock =\n                            &mut ctx.global_view_params_queue.lock();\n\n                        global_view_params_queue_lock\n                            .push_back((header.timestamp, header.global_view_params));\n\n                        while global_view_params_queue_lock.len() > 128 {\n                            global_view_params_queue_lock.pop_front();\n                        }\n                    }\n\n                    let submitted = ctx\n                        .decoder_callback\n                        .lock()\n                        .as_mut()\n                        .is_some_and(|callback| callback(header.timestamp, nal));\n\n                    if !submitted {\n                        stream_corrupted = true;\n                        if let Some(sender) = &mut *ctx.control_sender.lock() {\n                            sender.send(&ClientControlPacket::RequestIdr).ok();\n                        }\n                        warn!(\"Dropped video packet. Reason: Decoder saturation\")\n                    }\n                } else {\n                    if let Some(sender) = &mut *ctx.control_sender.lock() {\n                        sender.send(&ClientControlPacket::RequestIdr).ok();\n                    }\n                    warn!(\"Dropped video packet. Reason: Waiting for IDR frame\")\n                }\n            }\n        }\n    });\n\n    let game_audio_thread = if let Switch::Enabled(config) = settings.audio.game_audio {\n        let device = alvr_audio::new_output(None).to_con()?;\n        thread::spawn({\n            let ctx = Arc::clone(&ctx);\n            move || {\n                while is_streaming(&ctx) {\n                    alvr_common::show_err(audio::play_audio_loop(\n                        || is_streaming(&ctx),\n                        &device,\n                        2,\n                        negotiated_config.game_audio_sample_rate,\n                        config.buffering.clone(),\n                        &mut game_audio_receiver,\n                    ));\n                }\n            }\n        })\n    } else {\n        thread::spawn(|| ())\n    };\n\n    let microphone_thread = if matches!(settings.audio.microphone, Switch::Enabled(_)) {\n        let device = alvr_audio::new_input(None).to_con()?;\n\n        let microphone_sender = stream_socket.request_stream(AUDIO);\n\n        thread::spawn({\n            let ctx = Arc::clone(&ctx);\n            move || {\n                while is_streaming(&ctx) {\n                    let ctx = Arc::clone(&ctx);\n                    match audio::record_audio_blocking(\n                        Arc::new(move || is_streaming(&ctx)),\n                        microphone_sender.clone(),\n                        &device,\n                        1,\n                        false,\n                    ) {\n                        Ok(()) => break,\n                        Err(e) => {\n                            error!(\"Audio record error: {e}\");\n\n                            continue;\n                        }\n                    }\n                }\n            }\n        })\n    } else {\n        thread::spawn(|| ())\n    };\n\n    let haptics_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let event_queue = Arc::clone(&event_queue);\n        move || {\n            while is_streaming(&ctx) {\n                let data = match haptics_receiver.recv(STREAMING_RECV_TIMEOUT) {\n                    Ok(packet) => packet,\n                    Err(ConnectionError::TryAgain(_)) => continue,\n                    Err(ConnectionError::Other(_)) => return,\n                };\n                let Ok(haptics) = data.get_header() else {\n                    return;\n                };\n\n                event_queue.lock().push_back(ClientCoreEvent::Haptics {\n                    device_id: haptics.device_id,\n                    duration: haptics.duration,\n                    frequency: haptics.frequency,\n                    amplitude: haptics.amplitude,\n                });\n            }\n        }\n    });\n\n    let (log_channel_sender, log_channel_receiver) = mpsc::channel();\n\n    let control_send_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let event_queue = Arc::clone(&event_queue);\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        move || {\n            let mut keepalive_deadline = Instant::now();\n\n            #[cfg(target_os = \"android\")]\n            let mut battery_deadline = Instant::now();\n\n            while is_streaming(&ctx) && *lifecycle_state.read() == LifecycleState::Resumed {\n                if let Ok(packet) = log_channel_receiver.recv_timeout(STREAMING_RECV_TIMEOUT)\n                    && let Some(sender) = &mut *ctx.control_sender.lock()\n                    && let Err(e) = sender.send(&packet)\n                {\n                    info!(\"Server disconnected. Cause: {e:?}\");\n                    set_hud_message(&event_queue, SERVER_DISCONNECTED_MESSAGE);\n\n                    break;\n                }\n\n                if Instant::now() > keepalive_deadline\n                    && let Some(sender) = &mut *ctx.control_sender.lock()\n                {\n                    sender.send(&ClientControlPacket::KeepAlive).ok();\n\n                    keepalive_deadline = Instant::now() + KEEPALIVE_INTERVAL;\n                }\n\n                #[cfg(target_os = \"android\")]\n                if Instant::now() > battery_deadline {\n                    let (gauge_value, is_plugged) = alvr_system_info::get_battery_status();\n                    if let Some(sender) = &mut *ctx.control_sender.lock() {\n                        sender\n                            .send(&ClientControlPacket::Battery(crate::BatteryInfo {\n                                device_id: *alvr_common::HEAD_ID,\n                                gauge_value,\n                                is_plugged,\n                            }))\n                            .ok();\n                    }\n\n                    battery_deadline = Instant::now() + Duration::from_secs(5);\n                }\n            }\n\n            disconnect_notif.notify_one();\n        }\n    });\n\n    let control_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let event_queue = Arc::clone(&event_queue);\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        move || {\n            let mut disconnection_deadline = Instant::now() + KEEPALIVE_TIMEOUT;\n            while is_streaming(&ctx) {\n                let maybe_packet = control_receiver.recv(STREAMING_RECV_TIMEOUT);\n\n                match maybe_packet {\n                    Ok(ServerControlPacket::DecoderConfig(config)) => {\n                        event_queue\n                            .lock()\n                            .push_back(ClientCoreEvent::DecoderConfig {\n                                codec: config.codec,\n                                config_nal: config.config_buffer,\n                            });\n                    }\n                    Ok(ServerControlPacket::Restarting) => {\n                        info!(\"{SERVER_RESTART_MESSAGE}\");\n                        set_hud_message(&event_queue, SERVER_RESTART_MESSAGE);\n                        disconnect_notif.notify_one();\n                    }\n                    Ok(ServerControlPacket::RealTimeConfig(config)) => {\n                        event_queue\n                            .lock()\n                            .push_back(ClientCoreEvent::RealTimeConfig(config));\n                    }\n                    Ok(ServerControlPacket::StartStream) => {\n                        error!(\"Unexpected StartStream paceket\");\n                    }\n                    Ok(ServerControlPacket::KeepAlive) => (),\n                    Ok(\n                        ServerControlPacket::Reserved(_) | ServerControlPacket::ReservedBuffer(_),\n                    ) => {}\n                    Err(ConnectionError::TryAgain(_)) => {\n                        if Instant::now() > disconnection_deadline {\n                            info!(\"{CONNECTION_TIMEOUT_MESSAGE}\");\n                            set_hud_message(&event_queue, CONNECTION_TIMEOUT_MESSAGE);\n                            disconnect_notif.notify_one();\n                        } else {\n                            continue;\n                        }\n                    }\n                    Err(e) => {\n                        info!(\"{SERVER_DISCONNECTED_MESSAGE} Cause: {e}\");\n                        set_hud_message(&event_queue, SERVER_DISCONNECTED_MESSAGE);\n                        disconnect_notif.notify_one();\n                    }\n                }\n\n                disconnection_deadline = Instant::now() + KEEPALIVE_TIMEOUT;\n            }\n        }\n    });\n\n    let stream_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let event_queue = Arc::clone(&event_queue);\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        move || {\n            while is_streaming(&ctx) {\n                match stream_socket.recv() {\n                    Ok(()) => (),\n                    Err(ConnectionError::TryAgain(_)) => continue,\n                    Err(e) => {\n                        info!(\"Client disconnected. Cause: {e}\");\n                        set_hud_message(&event_queue, SERVER_DISCONNECTED_MESSAGE);\n                        disconnect_notif.notify_one();\n                    }\n                }\n            }\n        }\n    });\n\n    *ctx.control_sender.lock() = Some(control_sender);\n    *ctx.tracking_sender.lock() = Some(tracking_sender);\n    *ctx.statistics_sender.lock() = Some(statistics_sender);\n    if let Switch::Enabled(filter_level) = settings.extra.logging.client_log_report_level {\n        *LOG_CHANNEL_SENDER.lock() = Some(LogMirrorData {\n            sender: log_channel_sender,\n            filter_level,\n            debug_groups_config: settings.extra.logging.debug_groups,\n        });\n    }\n    event_queue.lock().push_back(streaming_start_event);\n\n    *connection_state_lock = ConnectionState::Streaming;\n\n    dbg_connection!(\"connection_pipeline: Unlock streams\");\n\n    // Unlock CONNECTION_STATE and block thread\n    wait_rwlock(&disconnect_notif, &mut connection_state_lock);\n\n    *connection_state_lock = ConnectionState::Disconnecting;\n\n    *ctx.control_sender.lock() = None;\n    *ctx.tracking_sender.lock() = None;\n    *ctx.statistics_sender.lock() = None;\n    *LOG_CHANNEL_SENDER.lock() = None;\n\n    event_queue\n        .lock()\n        .push_back(ClientCoreEvent::StreamingStopped);\n\n    // Remove lock to allow threads to properly exit:\n    drop(connection_state_lock);\n\n    dbg_connection!(\"connection_pipeline: Destroying streams\");\n\n    video_receive_thread.join().ok();\n    game_audio_thread.join().ok();\n    microphone_thread.join().ok();\n    haptics_receive_thread.join().ok();\n    control_send_thread.join().ok();\n    control_receive_thread.join().ok();\n    stream_receive_thread.join().ok();\n\n    dbg_connection!(\"connection_pipeline: End\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/client_core/src/lib.rs",
    "content": "#![allow(\n    non_upper_case_globals,\n    non_snake_case,\n    clippy::missing_safety_doc,\n    clippy::unseparated_literal_suffix\n)]\n\nmod c_api;\nmod connection;\nmod logging_backend;\nmod sockets;\nmod statistics;\nmod storage;\n\n#[cfg(target_os = \"android\")]\nmod audio;\n\npub mod video_decoder;\n\nuse alvr_common::{\n    ConnectionState, LifecycleState, ViewParams, dbg_client_core, error,\n    glam::{UVec2, Vec2},\n    parking_lot::{Mutex, RwLock},\n    warn,\n};\nuse alvr_packets::{\n    BatteryInfo, ButtonEntry, ClientControlPacket, RealTimeConfig, StreamConfig, TrackingData,\n};\nuse alvr_session::CodecType;\nuse alvr_system_info::Platform;\nuse connection::{ConnectionContext, DecoderCallback};\nuse std::{\n    collections::{HashSet, VecDeque},\n    sync::Arc,\n    thread::{self, JoinHandle},\n    time::Duration,\n};\nuse storage::Config;\n\npub use logging_backend::init_logging;\n\npub enum ClientCoreEvent {\n    UpdateHudMessage(String),\n    StreamingStarted(Box<StreamConfig>),\n    StreamingStopped,\n    Haptics {\n        device_id: u64,\n        duration: Duration,\n        frequency: f32,\n        amplitude: f32,\n    },\n    // Note: All subsequent DecoderConfig events should be ignored until reconnection\n    DecoderConfig {\n        codec: CodecType,\n        config_nal: Vec<u8>,\n    },\n    RealTimeConfig(RealTimeConfig),\n}\n\n// Note: this struct may change without breaking network protocol changes\n#[derive(Clone)]\npub struct ClientCapabilities {\n    pub platform: Platform,\n    pub default_view_resolution: UVec2,\n    pub max_view_resolution: UVec2,\n    pub refresh_rates: Vec<f32>,\n    pub foveated_encoding: bool,\n    pub encoder_high_profile: bool,\n    pub encoder_10_bits: bool,\n    pub encoder_av1: bool,\n    pub prefer_10bit: bool,\n    pub preferred_encoding_gamma: f32,\n    pub prefer_hdr: bool,\n}\n\npub struct ClientCoreContext {\n    platform: Platform,\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    event_queue: Arc<Mutex<VecDeque<ClientCoreEvent>>>,\n    connection_context: Arc<ConnectionContext>,\n    connection_thread: Arc<Mutex<Option<JoinHandle<()>>>>,\n    last_good_global_view_params: Mutex<[ViewParams; 2]>,\n}\n\nimpl ClientCoreContext {\n    pub fn new(capabilities: ClientCapabilities) -> Self {\n        dbg_client_core!(\"Create\");\n\n        // Make sure to reset config in case of version compat mismatch.\n        if Config::load().protocol_id != alvr_common::protocol_id() {\n            // NB: Config::default() sets the current protocol ID\n            Config::default().store();\n        }\n\n        #[cfg(target_os = \"android\")]\n        {\n            dbg_client_core!(\"Getting permissions\");\n            alvr_system_info::try_get_permission(alvr_system_info::MICROPHONE_PERMISSION);\n            alvr_system_info::set_wifi_lock(true);\n        }\n\n        let platform = capabilities.platform;\n\n        let lifecycle_state = Arc::new(RwLock::new(LifecycleState::Idle));\n        let event_queue = Arc::new(Mutex::new(VecDeque::new()));\n        let connection_context = Arc::new(ConnectionContext::default());\n        let connection_thread = thread::spawn({\n            let lifecycle_state = Arc::clone(&lifecycle_state);\n            let connection_context = Arc::clone(&connection_context);\n            let event_queue = Arc::clone(&event_queue);\n            move || {\n                connection::connection_lifecycle_loop(\n                    capabilities,\n                    connection_context,\n                    lifecycle_state,\n                    event_queue,\n                )\n            }\n        });\n\n        Self {\n            platform,\n            lifecycle_state,\n            event_queue,\n            connection_context,\n            connection_thread: Arc::new(Mutex::new(Some(connection_thread))),\n            last_good_global_view_params: Mutex::new([ViewParams::DUMMY; 2]),\n        }\n    }\n\n    pub fn resume(&self) {\n        dbg_client_core!(\"resume\");\n\n        *self.lifecycle_state.write() = LifecycleState::Resumed;\n    }\n\n    pub fn pause(&self) {\n        dbg_client_core!(\"pause\");\n\n        let mut connection_state_lock = self.connection_context.state.write();\n\n        *self.lifecycle_state.write() = LifecycleState::Idle;\n\n        // We want to shutdown streaming when pausing.\n        if *connection_state_lock != ConnectionState::Disconnected {\n            alvr_common::wait_rwlock(\n                &self.connection_context.disconnected_notif,\n                &mut connection_state_lock,\n            );\n        }\n    }\n\n    pub fn poll_event(&self) -> Option<ClientCoreEvent> {\n        dbg_client_core!(\"poll_event\");\n\n        self.event_queue.lock().pop_front()\n    }\n\n    pub fn send_battery(&self, device_id: u64, gauge_value: f32, is_plugged: bool) {\n        dbg_client_core!(\"send_battery\");\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender\n                .send(&ClientControlPacket::Battery(BatteryInfo {\n                    device_id,\n                    gauge_value,\n                    is_plugged,\n                }))\n                .ok();\n        }\n    }\n\n    pub fn send_playspace(&self, area: Option<Vec2>) {\n        dbg_client_core!(\"send_playspace\");\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender.send(&ClientControlPacket::PlayspaceSync(area)).ok();\n        }\n    }\n\n    pub fn send_active_interaction_profile(\n        &self,\n        device_id: u64,\n        profile_id: u64,\n        input_ids: HashSet<u64>,\n    ) {\n        dbg_client_core!(\"send_active_interaction_profile\");\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender\n                .send(&ClientControlPacket::ActiveInteractionProfile {\n                    device_id,\n                    profile_id,\n                    input_ids,\n                })\n                .ok();\n        }\n    }\n\n    pub fn send_buttons(&self, entries: Vec<ButtonEntry>) {\n        dbg_client_core!(\"send_buttons\");\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender.send(&ClientControlPacket::Buttons(entries)).ok();\n        }\n    }\n\n    // These must be in its local space, as if the head pose is in the origin.\n    pub fn send_view_params(&self, views: [ViewParams; 2]) {\n        dbg_client_core!(\"send_view_params\");\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender\n                .send(&ClientControlPacket::LocalViewParams(views))\n                .ok();\n        }\n    }\n\n    pub fn send_tracking(&self, data: TrackingData) {\n        dbg_client_core!(\"send_tracking\");\n\n        if let Some(sender) = &mut *self.connection_context.tracking_sender.lock() {\n            sender.send_header(&data).ok();\n\n            if let Some(stats) = &mut *self.connection_context.statistics_manager.lock() {\n                stats.report_input_acquired(data.poll_timestamp);\n            }\n        }\n    }\n\n    pub fn send_proximity_state(&self, headset_is_worn: bool) {\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender\n                .send(&ClientControlPacket::ProximityState(headset_is_worn))\n                .ok();\n        }\n    }\n\n    pub fn get_total_prediction_offset(&self) -> Duration {\n        dbg_client_core!(\"get_total_prediction_offset\");\n\n        if let Some(stats) = &*self.connection_context.statistics_manager.lock() {\n            Duration::min(\n                stats.average_total_pipeline_latency(),\n                *self.connection_context.max_prediction.read(),\n            )\n        } else {\n            Duration::ZERO\n        }\n    }\n\n    /// The callback should return true if the frame was successfully submitted to the decoder\n    pub fn set_decoder_input_callback(&self, callback: Box<DecoderCallback>) {\n        dbg_client_core!(\"set_decoder_input_callback\");\n\n        *self.connection_context.decoder_callback.lock() = Some(callback);\n\n        if let Some(sender) = &mut *self.connection_context.control_sender.lock() {\n            sender.send(&ClientControlPacket::RequestIdr).ok();\n        }\n    }\n\n    pub fn report_frame_decoded(&self, timestamp: Duration) {\n        dbg_client_core!(\"report_frame_decoded\");\n\n        if let Some(stats) = &mut *self.connection_context.statistics_manager.lock() {\n            stats.report_frame_decoded(timestamp);\n        }\n    }\n\n    pub fn report_fatal_decoder_error(&self, error: &str) {\n        error!(\"Fatal decoder error, restarting connection: {error}\");\n\n        // The connection loop observes changes on this value\n        *self.connection_context.state.write() = ConnectionState::Disconnecting;\n    }\n\n    pub fn report_compositor_start(&self, timestamp: Duration) -> [ViewParams; 2] {\n        dbg_client_core!(\"report_compositor_start\");\n\n        if let Some(stats) = &mut *self.connection_context.statistics_manager.lock() {\n            stats.report_compositor_start(timestamp);\n        }\n\n        let global_view_params_lock = &mut *self.last_good_global_view_params.lock();\n        for (ts, params) in &*self.connection_context.global_view_params_queue.lock() {\n            if *ts == timestamp {\n                *global_view_params_lock = *params;\n                break;\n            }\n        }\n\n        *global_view_params_lock\n    }\n\n    pub fn report_submit(&self, timestamp: Duration, vsync_queue: Duration) {\n        dbg_client_core!(\"report_submit\");\n\n        if let Some(stats) = &mut *self.connection_context.statistics_manager.lock() {\n            stats.report_submit(timestamp, vsync_queue);\n\n            if let Some(sender) = &mut *self.connection_context.statistics_sender.lock() {\n                if let Some(stats) = stats.summary(timestamp) {\n                    sender.send_header(&stats).ok();\n                } else {\n                    warn!(\"Statistics summary not ready!\");\n                }\n            }\n        }\n    }\n\n    pub fn platform(&self) -> Platform {\n        self.platform\n    }\n}\n\nimpl Drop for ClientCoreContext {\n    fn drop(&mut self) {\n        dbg_client_core!(\"Drop\");\n\n        *self.lifecycle_state.write() = LifecycleState::ShuttingDown;\n\n        if let Some(thread) = self.connection_thread.lock().take() {\n            thread.join().ok();\n        }\n\n        #[cfg(target_os = \"android\")]\n        alvr_system_info::set_wifi_lock(false);\n    }\n}\n"
  },
  {
    "path": "alvr/client_core/src/logging_backend.rs",
    "content": "use alvr_common::{\n    DebugGroupsConfig, LogSeverity,\n    log::{Level, Record},\n    parking_lot::Mutex,\n};\nuse alvr_packets::ClientControlPacket;\nuse std::{\n    sync::{LazyLock, mpsc},\n    time::{Duration, Instant},\n};\n\nconst LOG_REPEAT_TIMEOUT: Duration = Duration::from_secs(1);\n\npub struct LogMirrorData {\n    pub sender: mpsc::Sender<ClientControlPacket>,\n    pub filter_level: LogSeverity,\n    pub debug_groups_config: DebugGroupsConfig,\n}\n\npub static LOG_CHANNEL_SENDER: Mutex<Option<LogMirrorData>> = Mutex::new(None);\n\nstruct RepeatedLogEvent {\n    message: String,\n    repetition_times: usize,\n    initial_timestamp: Instant,\n}\n\nstatic LAST_LOG_EVENT: LazyLock<Mutex<RepeatedLogEvent>> = LazyLock::new(|| {\n    Mutex::new(RepeatedLogEvent {\n        message: \"\".into(),\n        repetition_times: 0,\n        initial_timestamp: Instant::now(),\n    })\n});\n\npub fn init_logging() {\n    fn send_log(record: &Record) -> bool {\n        let Some(data) = &*LOG_CHANNEL_SENDER.lock() else {\n            // if channel has not been setup, always print everything to stdout\n            // todo: the client debug groups settings should be moved client side when feasible\n            return true;\n        };\n\n        let level = match record.level() {\n            Level::Error => LogSeverity::Error,\n            Level::Warn => LogSeverity::Warning,\n            Level::Info => LogSeverity::Info,\n            Level::Debug | Level::Trace => LogSeverity::Debug,\n        };\n        if level < data.filter_level {\n            return false;\n        }\n\n        let message = format!(\"{}\", record.args());\n\n        if !alvr_common::filter_debug_groups(&message, &data.debug_groups_config) {\n            return false;\n        }\n\n        let mut last_log_event_lock = LAST_LOG_EVENT.lock();\n\n        if last_log_event_lock.message == message\n            && last_log_event_lock.initial_timestamp + LOG_REPEAT_TIMEOUT > Instant::now()\n        {\n            last_log_event_lock.repetition_times += 1;\n        } else {\n            if last_log_event_lock.repetition_times > 1 {\n                data.sender\n                    .send(ClientControlPacket::Log {\n                        level: LogSeverity::Info,\n                        message: format!(\n                            \"Last log line repeated {} times\",\n                            last_log_event_lock.repetition_times\n                        ),\n                    })\n                    .ok();\n            }\n\n            *last_log_event_lock = RepeatedLogEvent {\n                message: message.clone(),\n                repetition_times: 1,\n                initial_timestamp: Instant::now(),\n            };\n\n            data.sender\n                .send(ClientControlPacket::Log { level, message })\n                .ok();\n        }\n\n        true\n    }\n\n    #[cfg(target_os = \"android\")]\n    {\n        android_logger::init_once(\n            android_logger::Config::default()\n                .with_tag(\"[ALVR NATIVE-RUST]\")\n                .format(|f, record| {\n                    if send_log(record) {\n                        writeln!(f, \"{}\", record.args())\n                    } else {\n                        Ok(())\n                    }\n                })\n                .with_max_level(alvr_common::log::LevelFilter::Info),\n        );\n    }\n    #[cfg(not(target_os = \"android\"))]\n    {\n        use std::io::Write;\n        env_logger::builder()\n            .format(|f, record| {\n                if send_log(record) {\n                    writeln!(f, \"{}\", record.args())\n                } else {\n                    Ok(())\n                }\n            })\n            .try_init()\n            .ok();\n    }\n\n    alvr_common::set_panic_hook();\n}\n"
  },
  {
    "path": "alvr/client_core/src/sockets.rs",
    "content": "use alvr_common::anyhow::{Result, bail};\nuse mdns_sd::{ServiceDaemon, ServiceInfo};\n\npub struct AnnouncerSocket {\n    hostname: String,\n    daemon: ServiceDaemon,\n}\n\nimpl AnnouncerSocket {\n    pub fn new(hostname: &str) -> Result<Self> {\n        let daemon = ServiceDaemon::new()?;\n\n        Ok(Self {\n            daemon,\n            hostname: hostname.to_owned(),\n        })\n    }\n\n    pub fn announce(&self) -> Result<()> {\n        let local_ip = alvr_system_info::local_ip();\n        if local_ip.is_unspecified() {\n            bail!(\"IP is unspecified\");\n        }\n\n        self.daemon.register(ServiceInfo::new(\n            alvr_sockets::MDNS_SERVICE_TYPE,\n            &format!(\"alvr{}\", rand::random::<u16>()),\n            &self.hostname,\n            local_ip,\n            5353,\n            &[(\n                alvr_sockets::MDNS_PROTOCOL_KEY,\n                alvr_common::protocol_id().as_str(),\n            )][..],\n        )?)?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "alvr/client_core/src/statistics.rs",
    "content": "use alvr_common::SlidingWindowAverage;\nuse alvr_packets::ClientStatistics;\nuse std::{\n    collections::VecDeque,\n    time::{Duration, Instant},\n};\n\nstruct HistoryFrame {\n    input_acquired: Instant,\n    video_packet_received: Instant,\n    client_stats: ClientStatistics,\n}\n\npub struct StatisticsManager {\n    history_buffer: VecDeque<HistoryFrame>,\n    max_history_size: usize,\n    prev_vsync: Instant,\n    total_pipeline_latency_average: SlidingWindowAverage<Duration>,\n}\n\nimpl StatisticsManager {\n    pub fn new(max_history_size: usize) -> Self {\n        Self {\n            max_history_size,\n            history_buffer: VecDeque::new(),\n            prev_vsync: Instant::now(),\n            total_pipeline_latency_average: SlidingWindowAverage::new(\n                Duration::ZERO,\n                max_history_size,\n            ),\n        }\n    }\n\n    pub fn report_input_acquired(&mut self, target_timestamp: Duration) {\n        if !self\n            .history_buffer\n            .iter()\n            .any(|frame| frame.client_stats.target_timestamp == target_timestamp)\n        {\n            self.history_buffer.push_front(HistoryFrame {\n                input_acquired: Instant::now(),\n                // this is just a placeholder because Instant does not have a default value\n                video_packet_received: Instant::now(),\n                client_stats: ClientStatistics {\n                    target_timestamp,\n                    ..Default::default()\n                },\n            });\n        }\n\n        if self.history_buffer.len() > self.max_history_size {\n            self.history_buffer.pop_back();\n        }\n    }\n\n    pub fn report_video_packet_received(&mut self, target_timestamp: Duration) {\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.client_stats.target_timestamp == target_timestamp)\n        {\n            frame.video_packet_received = Instant::now();\n        }\n    }\n\n    pub fn report_frame_decoded(&mut self, target_timestamp: Duration) {\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.client_stats.target_timestamp == target_timestamp)\n        {\n            frame.client_stats.video_decode =\n                Instant::now().saturating_duration_since(frame.video_packet_received);\n        }\n    }\n\n    pub fn report_compositor_start(&mut self, target_timestamp: Duration) {\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.client_stats.target_timestamp == target_timestamp)\n        {\n            frame.client_stats.video_decoder_queue = Instant::now().saturating_duration_since(\n                frame.video_packet_received + frame.client_stats.video_decode,\n            );\n        }\n    }\n\n    // vsync_queue is the latency between this call and the vsync. it cannot be measured by ALVR and\n    // should be reported by the VR runtime\n    pub fn report_submit(&mut self, target_timestamp: Duration, vsync_queue: Duration) {\n        let now = Instant::now();\n\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.client_stats.target_timestamp == target_timestamp)\n        {\n            frame.client_stats.rendering = now.saturating_duration_since(\n                frame.video_packet_received\n                    + frame.client_stats.video_decode\n                    + frame.client_stats.video_decoder_queue,\n            );\n            frame.client_stats.vsync_queue = vsync_queue;\n            frame.client_stats.total_pipeline_latency =\n                now.saturating_duration_since(frame.input_acquired) + vsync_queue;\n            self.total_pipeline_latency_average\n                .submit_sample(frame.client_stats.total_pipeline_latency);\n\n            let vsync = now + vsync_queue;\n            frame.client_stats.frame_interval = vsync.saturating_duration_since(self.prev_vsync);\n            self.prev_vsync = vsync;\n        }\n    }\n\n    pub fn summary(&self, target_timestamp: Duration) -> Option<ClientStatistics> {\n        self.history_buffer\n            .iter()\n            .find(|frame| frame.client_stats.target_timestamp == target_timestamp)\n            .map(|frame| frame.client_stats.clone())\n    }\n\n    // latency used for head prediction\n    pub fn average_total_pipeline_latency(&self) -> Duration {\n        self.total_pipeline_latency_average.get_average()\n    }\n}\n"
  },
  {
    "path": "alvr/client_core/src/storage.rs",
    "content": "use alvr_common::{error, info};\nuse app_dirs2::{AppDataType, AppInfo};\nuse rand::Rng;\nuse serde::{Deserialize, Serialize};\nuse std::{fs, path::PathBuf};\n\nfn config_path() -> PathBuf {\n    app_dirs2::app_root(\n        AppDataType::UserConfig,\n        &AppInfo {\n            name: \"ALVR Client\",\n            author: \"ALVR\",\n        },\n    )\n    .unwrap()\n    .join(\"session.json\")\n}\n\n#[derive(Serialize, Deserialize)]\npub struct Config {\n    pub hostname: String,\n    pub protocol_id: String,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        let mut rng = rand::rng();\n\n        Self {\n            hostname: format!(\n                \"{}{}{}{}.client.local.\",\n                rng.random_range(0..10),\n                rng.random_range(0..10),\n                rng.random_range(0..10),\n                rng.random_range(0..10),\n            ),\n            protocol_id: alvr_common::protocol_id(),\n        }\n    }\n}\n\nimpl Config {\n    pub fn load() -> Self {\n        if let Ok(config_string) = fs::read_to_string(config_path()) {\n            // Failure happens if the Config signature changed between versions.\n            // todo: recover data from mismatched Config signature. low priority\n            if let Ok(config) = serde_json::from_str(&config_string) {\n                return config;\n            } else {\n                info!(\"Error parsing ALVR config. Using default\");\n            }\n        } else {\n            info!(\"Error reading ALVR config. Using default\");\n        }\n\n        let config = Config::default();\n        config.store();\n\n        config\n    }\n\n    pub fn store(&self) {\n        let config_string = serde_json::to_string(self).unwrap();\n        if let Err(e) = fs::write(config_path(), config_string) {\n            error!(\"Error writing ALVR config: {e}\")\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_core/src/video_decoder/android.rs",
    "content": "use super::VideoDecoderConfig;\nuse alvr_common::{\n    RelaxedAtomic, ToAny,\n    anyhow::{Context, Result, anyhow, bail},\n    error, info,\n    parking_lot::{Condvar, Mutex},\n    warn,\n};\nuse alvr_session::{CodecType, MediacodecPropType};\nuse ndk::{\n    hardware_buffer::HardwareBufferUsage,\n    media::{\n        image_reader::{AcquireResult, Image, ImageFormat, ImageReader},\n        media_codec::{\n            DequeuedInputBufferResult, DequeuedOutputBufferInfoResult, MediaCodec,\n            MediaCodecDirection, MediaFormat,\n        },\n    },\n};\nuse std::{\n    collections::VecDeque,\n    ffi::c_void,\n    ops::Deref,\n    ptr,\n    sync::{Arc, Weak},\n    thread::{self, JoinHandle},\n    time::Duration,\n};\n\nstruct FakeThreadSafe<T>(T);\nunsafe impl<T> Send for FakeThreadSafe<T> {}\nunsafe impl<T> Sync for FakeThreadSafe<T> {}\n\nimpl<T> Deref for FakeThreadSafe<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\ntype SharedMediaCodec = Arc<FakeThreadSafe<MediaCodec>>;\n\npub struct VideoDecoderSink {\n    inner: Arc<Mutex<Option<SharedMediaCodec>>>,\n}\n\nunsafe impl Send for VideoDecoderSink {}\n\nimpl VideoDecoderSink {\n    // Block until the buffer has been written or timeout is reached. Returns false if timeout.\n    pub fn push_frame_nal(&mut self, timestamp: Duration, data: &[u8]) -> Result<bool> {\n        let Some(decoder) = &*self.inner.lock() else {\n            // This might happen only during destruction\n            return Ok(false);\n        };\n\n        match decoder.dequeue_input_buffer(Duration::ZERO) {\n            Ok(DequeuedInputBufferResult::Buffer(mut buffer)) => {\n                unsafe {\n                    ptr::copy_nonoverlapping(\n                        data.as_ptr(),\n                        buffer.buffer_mut().as_mut_ptr().cast(),\n                        data.len(),\n                    )\n                };\n\n                // NB: the function expects the timestamp in micros, but nanos is used to have\n                // complete precision, so when converted back to Duration it can compare correctly\n                // to other Durations\n                decoder.queue_input_buffer(buffer, 0, data.len(), timestamp.as_nanos() as _, 0)?;\n\n                Ok(true)\n            }\n            Ok(DequeuedInputBufferResult::TryAgainLater) => Ok(false),\n            Err(e) => bail!(\"{e}\"),\n        }\n    }\n}\n\nstruct QueuedImage {\n    timestamp: Duration,\n    image: Image,\n    in_use: bool,\n}\nunsafe impl Send for QueuedImage {}\n\n// Access the image queue synchronously.\npub struct VideoDecoderSource {\n    running: Arc<RelaxedAtomic>,\n    dequeue_thread: Option<JoinHandle<()>>,\n    image_queue: Arc<Mutex<VecDeque<QueuedImage>>>,\n    config: VideoDecoderConfig,\n    buffering_running_average: f32,\n}\n\nunsafe impl Send for VideoDecoderSource {}\n\nimpl VideoDecoderSource {\n    // The application MUST finish using the returned buffer before calling this function again\n    pub fn dequeue_frame(&mut self) -> Option<(Duration, *mut c_void)> {\n        let mut image_queue_lock = self.image_queue.lock();\n\n        if let Some(queued_image) = image_queue_lock.front()\n            && queued_image.in_use\n        {\n            // image is released and ready to be reused by the decoder\n            image_queue_lock.pop_front();\n        }\n\n        // use running average to give more weight to recent samples\n        self.buffering_running_average = self.buffering_running_average\n            * self.config.buffering_history_weight\n            + image_queue_lock.len() as f32 * (1. - self.config.buffering_history_weight);\n        if self.buffering_running_average > self.config.max_buffering_frames as f32 {\n            image_queue_lock.pop_front();\n        }\n\n        if let Some(queued_image) = image_queue_lock.front_mut() {\n            queued_image.in_use = true;\n\n            Some((\n                queued_image.timestamp,\n                queued_image\n                    .image\n                    .hardware_buffer()\n                    .unwrap()\n                    .as_ptr()\n                    .cast(),\n            ))\n        } else {\n            // TODO: add back when implementing proper phase sync\n            //warn!(\"Video frame queue underflow!\");\n            None\n        }\n    }\n}\n\nimpl Drop for VideoDecoderSource {\n    fn drop(&mut self) {\n        self.running.set(false);\n\n        // Destruction of decoder, buffered images and ImageReader\n        self.dequeue_thread.take().map(|t| t.join());\n    }\n}\n\nfn mime_for_codec(codec: CodecType) -> &'static str {\n    match codec {\n        CodecType::H264 => \"video/avc\",\n        CodecType::Hevc => \"video/hevc\",\n        CodecType::AV1 => \"video/av01\",\n    }\n}\n\n// Attempts to create a MediaCodec, and then configure and start it.\nfn decoder_attempt_setup(\n    codec_type: CodecType,\n    is_software: bool,\n    format: &MediaFormat,\n    image_reader: &ImageReader,\n) -> Result<MediaCodec> {\n    let decoder = if is_software {\n        let sw_codec_name = match codec_type {\n            CodecType::H264 => \"OMX.google.h264.decoder\",\n            CodecType::Hevc => \"OMX.google.hevc.decoder\",\n            CodecType::AV1 => bail!(\"AV1 is not supported for software decoding\"),\n        };\n        MediaCodec::from_codec_name(&sw_codec_name)\n            .ok_or(anyhow!(\"no such codec: {}\", &sw_codec_name))?\n    } else {\n        let mime = mime_for_codec(codec_type);\n        MediaCodec::from_decoder_type(&mime)\n            .ok_or(anyhow!(\"unable to find decoder for mime type: {}\", &mime))?\n    };\n    decoder\n        .configure(\n            &format,\n            Some(&image_reader.window()?),\n            MediaCodecDirection::Decoder,\n        )\n        .with_context(|| format!(\"failed to configure decoder\"))?;\n\n    decoder\n        .start()\n        .with_context(|| format!(\"failed to start decoder\"))?;\n\n    Ok(decoder)\n}\n\n// Since we leak the ImageReader, and we pass frame_result_callback to it which contains a reference\n// to ClientCoreContext, to avoid circular references we need to use a Weak reference.\nfn decoder_lifecycle(\n    config: VideoDecoderConfig,\n    csd_0: Vec<u8>,\n    frame_result_callback: Weak<impl Fn(Result<Duration>) + Send + Sync + 'static>,\n    running: Arc<RelaxedAtomic>,\n    decoder_sink: Arc<Mutex<Option<SharedMediaCodec>>>,\n    decoder_ready_notifier: Arc<Condvar>,\n    image_queue: Arc<Mutex<VecDeque<QueuedImage>>>,\n    image_reader: &mut ImageReader,\n) -> Result<()> {\n    // 2x: keep the target buffering in the middle of the max amount of queuable frames\n    let available_buffering_frames = (2. * config.max_buffering_frames).ceil() as usize;\n\n    image_reader.set_image_listener(Box::new({\n        let image_queue = Arc::clone(&image_queue);\n        move |image_reader| {\n            let mut image_queue_lock = image_queue.lock();\n\n            if image_queue_lock.len() > available_buffering_frames {\n                warn!(\"Video frame queue overflow!\");\n                image_queue_lock.pop_front();\n            }\n\n            match image_reader.acquire_next_image() {\n                Ok(AcquireResult::Image(image)) => {\n                    let timestamp = Duration::from_nanos(image.timestamp().unwrap() as u64);\n\n                    if let Some(callback) = frame_result_callback.upgrade() {\n                        callback(Ok(timestamp));\n                    }\n\n                    image_queue_lock.push_back(QueuedImage {\n                        timestamp,\n                        image,\n                        in_use: false,\n                    });\n                }\n                Ok(e) => {\n                    error!(\"ImageReader error: {e:?}\");\n\n                    image_queue_lock.pop_front();\n                }\n                Err(e) => {\n                    error!(\"ImageReader error: {e}\");\n\n                    image_queue_lock.clear();\n                }\n            }\n        }\n    }))?;\n\n    // Documentation says that this call is necessary to properly dispose acquired buffers.\n    // todo: find out how to use it and avoid leaking the ImageReader\n    image_reader.set_buffer_removed_listener(Box::new(|_, _| ()))?;\n\n    let mime = mime_for_codec(config.codec);\n\n    let mut format = MediaFormat::new();\n    format.set_str(\"mime\", mime);\n    // Given https://github.com/alvr-org/ALVR/pull/1933#discussion_r1431902906 - change at own risk.\n    // It might be harmless, it might not be, but it's definitely a risk.\n    format.set_i32(\"width\", 512);\n    format.set_i32(\"height\", 1024);\n    format.set_buffer(\"csd-0\", &csd_0);\n\n    for (key, prop) in &config.options {\n        let maybe_error = match prop.ty {\n            MediacodecPropType::Float => prop\n                .value\n                .parse()\n                .map(|value| format.set_f32(key, value))\n                .to_any(),\n            MediacodecPropType::Int32 => prop\n                .value\n                .parse()\n                .map(|value| format.set_i32(key, value))\n                .to_any(),\n            MediacodecPropType::Int64 => prop\n                .value\n                .parse()\n                .map(|value| format.set_i64(key, value))\n                .to_any(),\n            MediacodecPropType::String => Ok(format.set_str(key, &prop.value)),\n        };\n\n        if let Err(e) = maybe_error {\n            error!(\"Failed to set property {key} to {}: {e}\", prop.value);\n        }\n    }\n\n    info!(\"Using AMediaCodec format:{} \", format);\n\n    let decoder = if config.force_software_decoder {\n        decoder_attempt_setup(config.codec, true, &format, &image_reader)?\n    } else {\n        // Hardware decoders sometimes fail at the CSD-0.\n        // May as well fall back if this occurs.\n        match decoder_attempt_setup(config.codec, false, &format, &image_reader) {\n            Ok(d) => d,\n            Err(e) => {\n                // would be \"warn!\" but this is a severe caveat and a pretty major error.\n                error!(\"Attempting software fallback due to error in default decoder: {e:#}\");\n\n                decoder_attempt_setup(config.codec, true, &format, &image_reader)?\n            }\n        }\n    };\n\n    let decoder = Arc::new(FakeThreadSafe(decoder));\n\n    {\n        let mut decoder_lock = decoder_sink.lock();\n\n        *decoder_lock = Some(Arc::clone(&decoder));\n\n        decoder_ready_notifier.notify_one();\n    }\n\n    let mut error_counter = 0;\n    while running.value() {\n        match decoder.dequeue_output_buffer(Duration::from_millis(1)) {\n            Ok(DequeuedOutputBufferInfoResult::Buffer(buffer)) => {\n                // The buffer timestamp is actually nanoseconds\n                let presentation_time_ns = buffer.info().presentation_time_us();\n\n                if let Err(e) = decoder.release_output_buffer_at_time(buffer, presentation_time_ns)\n                {\n                    error!(\"Decoder dequeue error: {e}\");\n                }\n            }\n            Ok(DequeuedOutputBufferInfoResult::TryAgainLater) => continue,\n            Ok(i) => info!(\"Decoder dequeue event: {i:?}\"),\n            Err(e) => {\n                error!(\"Decoder dequeue error: {e}\");\n\n                error_counter += 1;\n                if error_counter > 10 {\n                    bail!(\"Too many decoder errors: {e}\");\n                }\n\n                // lessen logcat flood (just in case)\n                thread::sleep(Duration::from_millis(50));\n\n                continue;\n            }\n        }\n\n        error_counter = 0;\n    }\n\n    // Destroy all resources\n    decoder_sink.lock().take(); // Make sure the shared ref is deleted first\n    decoder.stop()?;\n    drop(decoder);\n\n    Ok(())\n}\n\n// Create a sink/source pair\npub fn video_decoder_split(\n    config: VideoDecoderConfig,\n    csd_0: Vec<u8>,\n    frame_result_callback: impl Fn(Result<Duration>) + Send + Sync + 'static,\n) -> Result<(VideoDecoderSink, VideoDecoderSource)> {\n    let running = Arc::new(RelaxedAtomic::new(true));\n    let decoder_sink = Arc::new(Mutex::new(None::<SharedMediaCodec>));\n    let decoder_ready_notifier = Arc::new(Condvar::new());\n    let image_queue = Arc::new(Mutex::new(VecDeque::<QueuedImage>::new()));\n\n    let dequeue_thread = thread::spawn({\n        let config = config.clone();\n        let running = Arc::clone(&running);\n        let decoder_sink = Arc::clone(&decoder_sink);\n        let decoder_ready_notifier = Arc::clone(&decoder_ready_notifier);\n        let image_queue = Arc::clone(&image_queue);\n        move || {\n            const MAX_BUFFERING_FRAMES: usize = 10;\n            let mut image_reader = match ImageReader::new_with_usage(\n                1,\n                1,\n                ImageFormat::PRIVATE,\n                HardwareBufferUsage::GPU_SAMPLED_IMAGE,\n                MAX_BUFFERING_FRAMES as i32,\n            ) {\n                Ok(reader) => reader,\n                Err(e) => {\n                    frame_result_callback(Err(anyhow!(\"{e}\")));\n                    return;\n                }\n            };\n\n            let frame_result_callback = Arc::new(frame_result_callback);\n\n            if let Err(e) = decoder_lifecycle(\n                config,\n                csd_0,\n                Arc::downgrade(&frame_result_callback),\n                running,\n                decoder_sink,\n                decoder_ready_notifier,\n                Arc::clone(&image_queue),\n                &mut image_reader,\n            ) {\n                frame_result_callback(Err(e));\n            }\n\n            image_queue.lock().clear();\n            error!(\"FIXME: Leaking Imagereader!\");\n            Box::leak(Box::new(image_reader));\n        }\n    });\n\n    // Make sure the decoder is ready: we don't want to try to enqueue frame and lose them, to avoid\n    // image corruption.\n    {\n        let mut decoder_lock = decoder_sink.lock();\n\n        if decoder_lock.is_none() {\n            // No spurious wakeups\n            decoder_ready_notifier.wait(&mut decoder_lock);\n        }\n    }\n\n    let sink = VideoDecoderSink {\n        inner: decoder_sink,\n    };\n    let source = VideoDecoderSource {\n        running,\n        dequeue_thread: Some(dequeue_thread),\n        image_queue,\n        config,\n        buffering_running_average: 0.0,\n    };\n\n    Ok((sink, source))\n}\n"
  },
  {
    "path": "alvr/client_core/src/video_decoder/mod.rs",
    "content": "#[cfg(target_os = \"android\")]\nmod android;\n\nuse alvr_common::anyhow::Result;\nuse alvr_session::{CodecType, MediacodecProperty};\nuse std::time::Duration;\n\n#[derive(Clone, Default, PartialEq)]\npub struct VideoDecoderConfig {\n    pub codec: CodecType,\n    pub force_software_decoder: bool,\n    pub max_buffering_frames: f32,\n    pub buffering_history_weight: f32,\n    pub options: Vec<(String, MediacodecProperty)>,\n    pub config_buffer: Vec<u8>,\n}\n\npub struct VideoDecoderSink {\n    #[cfg(target_os = \"android\")]\n    inner: android::VideoDecoderSink,\n}\n\nimpl VideoDecoderSink {\n    // returns true if frame has been successfully enqueued\n    #[allow(unused_variables)]\n    pub fn push_nal(&mut self, timestamp: Duration, nal: &[u8]) -> bool {\n        #[cfg(target_os = \"android\")]\n        {\n            alvr_common::show_err(self.inner.push_frame_nal(timestamp, nal)).unwrap_or(false)\n        }\n        #[cfg(not(target_os = \"android\"))]\n        false\n    }\n}\n\npub struct VideoDecoderSource {\n    #[cfg(target_os = \"android\")]\n    inner: android::VideoDecoderSource,\n}\n\nimpl VideoDecoderSource {\n    /// If a frame is available, return the timestamp and the AHardwareBuffer.\n    pub fn get_frame(&mut self) -> Option<(Duration, *mut std::ffi::c_void)> {\n        #[cfg(target_os = \"android\")]\n        {\n            self.inner.dequeue_frame()\n        }\n        #[cfg(not(target_os = \"android\"))]\n        None\n    }\n}\n\n// report_frame_decoded: (target_timestamp: Duration) -> ()\n#[allow(unused_variables)]\npub fn create_decoder(\n    config: VideoDecoderConfig,\n    report_frame_decoded: impl Fn(Result<Duration>) + Send + Sync + 'static,\n) -> (VideoDecoderSink, VideoDecoderSource) {\n    #[cfg(target_os = \"android\")]\n    {\n        let (sink, source) = android::video_decoder_split(\n            config.clone(),\n            config.config_buffer,\n            report_frame_decoded,\n        )\n        .unwrap();\n\n        (\n            VideoDecoderSink { inner: sink },\n            VideoDecoderSource { inner: source },\n        )\n    }\n    #[cfg(not(target_os = \"android\"))]\n    (VideoDecoderSink {}, VideoDecoderSource {})\n}\n"
  },
  {
    "path": "alvr/client_mock/Cargo.toml",
    "content": "[package]\nname = \"alvr_client_mock\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_client_core.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\nalvr_system_info.workspace = true\n\neframe = \"0.32\"\nenv_logger = \"0.11\"\nrand = \"0.9\"\n"
  },
  {
    "path": "alvr/client_mock/src/main.rs",
    "content": "use alvr_client_core::{ClientCapabilities, ClientCoreContext, ClientCoreEvent};\nuse alvr_common::{\n    DeviceMotion, HEAD_ID, Pose, RelaxedAtomic, ViewParams,\n    glam::{Quat, UVec2, Vec3},\n    parking_lot::RwLock,\n};\nuse alvr_packets::{FaceData, TrackingData};\nuse alvr_session::CodecType;\nuse eframe::{\n    Frame, NativeOptions,\n    egui::{CentralPanel, Context, RichText, Slider, ViewportBuilder},\n};\nuse std::{\n    f32::consts::{FRAC_PI_2, PI},\n    sync::{\n        Arc,\n        mpsc::{self, TryRecvError},\n    },\n    thread,\n    time::{Duration, Instant},\n};\n\n#[derive(Clone, PartialEq)]\nstruct WindowInput {\n    height: f32,\n    yaw: f32,\n    pitch: f32,\n    use_random_orientation: bool,\n    emulated_decode_ms: u64,\n    emulated_compositor_ms: u64,\n    emulated_vsync_ms: u64,\n}\n\nimpl Default for WindowInput {\n    fn default() -> Self {\n        Self {\n            height: 1.5,\n            yaw: 0.0,\n            pitch: 0.0,\n            use_random_orientation: true,\n            emulated_decode_ms: 5,\n            emulated_compositor_ms: 1,\n            emulated_vsync_ms: 25,\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct WindowOutput {\n    hud_message: String,\n    connected: bool,\n    fps: f32,\n    resolution: UVec2,\n    decoder_codec: Option<CodecType>,\n    current_frame_timestamp: Duration,\n}\n\nimpl Default for WindowOutput {\n    fn default() -> Self {\n        Self {\n            hud_message: \"\".into(),\n            connected: false,\n            fps: 1.0,\n            resolution: UVec2::ZERO,\n            decoder_codec: None,\n            current_frame_timestamp: Duration::ZERO,\n        }\n    }\n}\n\npub struct Window {\n    input: WindowInput,\n    input_sender: mpsc::Sender<WindowInput>,\n    output: WindowOutput,\n    output_receiver: mpsc::Receiver<WindowOutput>,\n}\n\nimpl Window {\n    fn new(\n        input_sender: mpsc::Sender<WindowInput>,\n        output_receiver: mpsc::Receiver<WindowOutput>,\n    ) -> Self {\n        Self {\n            input: WindowInput::default(),\n            input_sender,\n            output: WindowOutput::default(),\n            output_receiver,\n        }\n    }\n}\n\nimpl eframe::App for Window {\n    fn update(&mut self, context: &Context, _: &mut Frame) {\n        while let Ok(output) = self.output_receiver.try_recv() {\n            self.output = output;\n        }\n\n        let mut input = self.input.clone();\n\n        CentralPanel::default().show(context, |ui| {\n            ui.vertical_centered(|ui| {\n                ui.heading(RichText::new(&self.output.hud_message));\n            });\n            ui.label(format!(\"Connected: {}\", self.output.connected));\n            ui.label(format!(\"FPS: {}\", self.output.fps));\n            ui.label(format!(\"View resolution: {}\", self.output.resolution));\n            ui.label(format!(\"Codec: {:?}\", self.output.decoder_codec));\n            ui.label(format!(\n                \"Current frame: {:?}\",\n                self.output.current_frame_timestamp\n            ));\n            ui.add_space(10.0);\n            ui.horizontal(|ui| {\n                ui.label(\"Height:\");\n                ui.add(Slider::new(&mut input.height, 0.0..=2.0));\n            });\n            ui.horizontal(|ui| {\n                ui.label(\"Yaw:\");\n                ui.add(Slider::new(&mut input.yaw, -PI..=PI));\n            });\n            ui.horizontal(|ui| {\n                ui.label(\"Pitch:\");\n                ui.add(Slider::new(&mut input.pitch, -FRAC_PI_2..=FRAC_PI_2));\n            });\n            ui.checkbox(\n                &mut input.use_random_orientation,\n                \"Use randomized orientation offset\",\n            );\n        });\n\n        if input != self.input {\n            self.input = input;\n\n            self.input_sender.send(self.input.clone()).ok();\n        }\n\n        context.request_repaint();\n    }\n}\n\nfn tracking_thread(\n    context: Arc<ClientCoreContext>,\n    streaming: Arc<RelaxedAtomic>,\n    fps: f32,\n    input: Arc<RwLock<WindowInput>>,\n) {\n    let timestamp_origin = Instant::now();\n    context.send_view_params([ViewParams::DUMMY; 2]);\n\n    let mut loop_deadline = Instant::now();\n    while streaming.value() {\n        let input_lock = input.read();\n\n        let mut orientation =\n            Quat::from_rotation_y(input_lock.yaw) * Quat::from_rotation_x(input_lock.pitch);\n\n        if input_lock.use_random_orientation {\n            orientation *= Quat::from_rotation_z(rand::random::<f32>() * 0.001);\n        }\n\n        let position = Vec3::new(0.0, input_lock.height, 0.0);\n\n        context.send_tracking(TrackingData {\n            poll_timestamp: timestamp_origin.elapsed(),\n            device_motions: vec![(\n                *HEAD_ID,\n                DeviceMotion {\n                    pose: Pose {\n                        orientation,\n                        position,\n                    },\n                    linear_velocity: Vec3::ZERO,\n                    angular_velocity: Vec3::ZERO,\n                },\n            )],\n            hand_skeletons: [None, None],\n            face: FaceData::default(),\n            body: None,\n        });\n\n        drop(input_lock);\n\n        loop_deadline += Duration::from_secs_f32(1.0 / fps / 3.0);\n        thread::sleep(loop_deadline.saturating_duration_since(Instant::now()))\n    }\n}\n\nfn client_thread(\n    output_sender: mpsc::Sender<WindowOutput>,\n    input_receiver: mpsc::Receiver<WindowInput>,\n) {\n    let capabilities = ClientCapabilities {\n        platform: alvr_system_info::platform(None, None),\n        default_view_resolution: UVec2::new(1920, 1832),\n        max_view_resolution: UVec2::new(1920, 1832),\n        refresh_rates: vec![60.0, 72.0, 80.0, 90.0, 120.0],\n        foveated_encoding: false,\n        encoder_high_profile: false,\n        encoder_10_bits: false,\n        encoder_av1: false,\n        prefer_10bit: false,\n        preferred_encoding_gamma: 1.0,\n        prefer_hdr: false,\n    };\n    let client_core_context = Arc::new(ClientCoreContext::new(capabilities));\n\n    client_core_context.resume();\n\n    let streaming = Arc::new(RelaxedAtomic::new(false));\n    let got_decoder_config = Arc::new(RelaxedAtomic::new(false));\n    let mut maybe_tracking_thread = None;\n\n    let mut window_output = WindowOutput::default();\n    let window_input = Arc::new(RwLock::new(WindowInput::default()));\n\n    let mut deadline = Instant::now();\n    'main_loop: loop {\n        let input_lock = window_input.read();\n\n        while let Some(event) = client_core_context.poll_event() {\n            match event {\n                ClientCoreEvent::UpdateHudMessage(message) => {\n                    window_output.hud_message = message;\n                }\n                ClientCoreEvent::StreamingStarted(config) => {\n                    window_output.fps = config.negotiated_config.refresh_rate_hint;\n                    window_output.connected = true;\n                    window_output.resolution = config.negotiated_config.view_resolution;\n\n                    streaming.set(true);\n\n                    let context = Arc::clone(&client_core_context);\n                    let streaming = Arc::clone(&streaming);\n                    let input = Arc::clone(&window_input);\n                    maybe_tracking_thread = Some(thread::spawn(move || {\n                        tracking_thread(\n                            context,\n                            streaming,\n                            config.negotiated_config.refresh_rate_hint,\n                            input,\n                        )\n                    }));\n                }\n                ClientCoreEvent::StreamingStopped => {\n                    streaming.set(false);\n                    got_decoder_config.set(false);\n\n                    if let Some(thread) = maybe_tracking_thread.take() {\n                        thread.join().ok();\n                    }\n\n                    window_output.fps = 1.0;\n                    window_output.connected = false;\n                    window_output.resolution = UVec2::ZERO;\n                    window_output.decoder_codec = None;\n                }\n                ClientCoreEvent::DecoderConfig { codec, .. } => {\n                    got_decoder_config.set(true);\n\n                    window_output.decoder_codec = Some(codec);\n                }\n                ClientCoreEvent::Haptics { .. } | ClientCoreEvent::RealTimeConfig(_) => (),\n            }\n\n            output_sender.send(window_output.clone()).ok();\n        }\n\n        thread::sleep(Duration::from_millis(3));\n\n        client_core_context.report_compositor_start(window_output.current_frame_timestamp);\n\n        thread::sleep(Duration::from_millis(input_lock.emulated_compositor_ms));\n\n        client_core_context.report_submit(\n            window_output.current_frame_timestamp,\n            Duration::from_millis(input_lock.emulated_vsync_ms),\n        );\n\n        drop(input_lock);\n\n        match input_receiver.try_recv() {\n            Ok(input) => *window_input.write() = input,\n            Err(TryRecvError::Disconnected) => break 'main_loop,\n            Err(TryRecvError::Empty) => (),\n        }\n\n        deadline += Duration::from_secs_f32(1.0 / window_output.fps);\n        thread::sleep(deadline.saturating_duration_since(Instant::now()));\n    }\n\n    streaming.set(false);\n    if let Some(thread) = maybe_tracking_thread {\n        thread.join().unwrap();\n    }\n\n    client_core_context.pause()\n\n    // client_core_context destroy is called here on drop\n}\n\nfn main() {\n    env_logger::init();\n\n    let (input_sender, input_receiver) = mpsc::channel::<WindowInput>();\n    let (output_sender, output_receiver) = mpsc::channel::<WindowOutput>();\n\n    let client_thread = thread::spawn(|| {\n        client_thread(output_sender, input_receiver);\n    });\n\n    eframe::run_native(\n        \"Mock client\",\n        NativeOptions {\n            viewport: ViewportBuilder::default().with_inner_size((400.0, 400.0)),\n            ..Default::default()\n        },\n        Box::new(|_| Ok(Box::new(Window::new(input_sender, output_receiver)))),\n    )\n    .ok();\n\n    client_thread.join().unwrap();\n}\n"
  },
  {
    "path": "alvr/client_openxr/Cargo.toml",
    "content": "[package]\nname = \"alvr_client_openxr\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nalvr_common.workspace = true\nalvr_client_core.workspace = true\nalvr_graphics.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\nalvr_system_info.workspace = true\n\nopenxr = { git = \"https://github.com/zmerp/openxrs\", rev = \"e7c1b155e79ff8b58c2f6558d28e1398ebe08d2d\" }\n\n[target.'cfg(target_os = \"android\")'.dependencies]\nandroid-activity = { version = \"0.6\", features = [\"native-activity\"] }\njni = \"0.21\"\nlibc = \"0.2\"\nndk-context = \"0.1\"\n\n[package.metadata.android]\npackage = \"alvr.client.dev\"                # Changed for Meta Store\ninstall_location = \"auto\"\nbuild_targets = [\"aarch64-linux-android\"]\nruntime_libs = \"../../deps/android_openxr\"\nresources = \"resources\"\n\n[package.metadata.android.signing.release]\npath = \"../../build/alvr_client_android/debug.keystore\"\nkeystore_password = \"alvrclient\"\n\n[package.metadata.android.signing.distribution]\npath = \"../../build/alvr_client_android/debug.keystore\"\nkeystore_password = \"alvrclient\"\n\n[package.metadata.android.sdk]\nmin_sdk_version = 28\ntarget_sdk_version = 32\n\n[[package.metadata.android.uses_feature]]\nname = \"android.hardware.microphone\"\nrequired = true\n[[package.metadata.android.uses_feature]]\nname = \"android.hardware.vr.headtracking\"\nrequired = true\nversion = 1\n[[package.metadata.android.uses_feature]]\nopengles_version = [3, 2]\nrequired = true\n\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.ACCESS_WIFI_STATE\"\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.ACCESS_NETWORK_STATE\"\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.CHANGE_WIFI_MULTICAST_STATE\"\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.INTERNET\"\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.RECORD_AUDIO\"\n# WAKE_LOCK is needed for proper wifi locking\n# https://developer.android.com/reference/android/net/wifi/WifiManager.WifiLock\n[[package.metadata.android.uses_permission]]\nname = \"android.permission.WAKE_LOCK\"\n[[package.metadata.android.uses_permission]]\nname = \"org.khronos.openxr.permission.OPENXR\"\n[[package.metadata.android.uses_permission]]\nname = \"org.khronos.openxr.permission.OPENXR_SYSTEM\"\n\n[[package.metadata.android.queries.intent]]\nactions = [\"org.khronos.openxr.OpenXRRuntimeService\"]\n[[package.metadata.android.queries.provider]]\nname = \"org.khronos.openxr\"\nauthorities = \"org.khronos.openxr.runtime_broker;org.khronos.openxr.system_runtime_broker\"\n\n[package.metadata.android.application]\ndebuggable = false\ntheme = \"@android:style/Theme.Black.NoTitleBar.Fullscreen\"\nicon = \"@mipmap/ic_launcher\"\nlabel = \"ALVR\"\n\n[package.metadata.android.application.activity]\nconfig_changes = \"density|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|uiMode\"\nlaunch_mode = \"singleTask\"\norientation = \"landscape\"\n\n[[package.metadata.android.application.activity.intent_filter]]\nactions = [\"android.intent.action.MAIN\"]\ncategories = [\n    \"android.intent.category.LAUNCHER\",\n    \"com.oculus.intent.category.VR\",\n    \"com.yvr.intent.category.VR\",\n    \"org.khronos.openxr.intent.category.IMMERSIVE_HMD\",\n]\n\n# Quest entries\n[[package.metadata.android.uses_feature]]\nname = \"oculus.software.eye_tracking\"\nrequired = false\n[[package.metadata.android.uses_feature]]\nname = \"oculus.software.face_tracking\"\nrequired = false\n[[package.metadata.android.uses_feature]]\nname = \"oculus.software.handtracking\"\nrequired = false\n[[package.metadata.android.uses_feature]]\nname = \"com.oculus.feature.PASSTHROUGH\"\nrequired = false\n[[package.metadata.android.uses_feature]]\nname = \"com.oculus.software.body_tracking\"\nrequired = false\n[[package.metadata.android.uses_permission]]\nname = \"com.oculus.permission.BODY_TRACKING\"\n[[package.metadata.android.uses_permission]]\nname = \"com.oculus.permission.EYE_TRACKING\"\n[[package.metadata.android.uses_permission]]\nname = \"com.oculus.permission.FACE_TRACKING\"\n[[package.metadata.android.uses_permission]]\nname = \"com.oculus.permission.HAND_TRACKING\"\n[[package.metadata.android.uses_permission]]\nname = \"com.oculus.permission.WIFI_LOCK\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.oculus.intent.category.VR\"\nvalue = \"vr_only\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.oculus.supportedDevices\"\n# Note: value is changed for the Meta store, which requires an explicit list of platforms.\n# \"all\" is required to support Quest 1 which doesn't have newer platform names registered.\nvalue = \"all\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.oculus.vr.focusaware\"\nvalue = \"true\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.oculus.handtracking.frequency\"\nvalue = \"HIGH\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.oculus.handtracking.version\"\nvalue = \"V2.0\"\n\n# Vive entries\n[[package.metadata.android.uses_feature]]\nname = \"wave.feature.eyetracking\"\nrequired = false\n[[package.metadata.android.uses_feature]]\nname = \"wave.feature.handtracking\"\nrequired = true\n[[package.metadata.android.uses_feature]]\nname = \"wave.feature.lipexpression\"\nrequired = false\n[[package.metadata.android.application.meta_data]]\nname = \"minWaveSDKVersion\"\nvalue = \"1\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.htc.vr.content.NumController\"\nvalue = \"1,2\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.htc.vr.content.NumDoFController\"\nvalue = \"3,6DoF\"\n[[package.metadata.android.application.meta_data]]\nname = \"com.htc.vr.content.NumDoFHmd\"\nvalue = \"3,6DoF\"\n\n# Pico entries\n[[package.metadata.android.uses_permission]]\nname = \"com.picovr.permission.EYE_TRACKING\"\n[[package.metadata.android.uses_permission]]\nname = \"com.picovr.permission.FACE_TRACKING\"\n[[package.metadata.android.application.meta_data]]\nname = \"eyetracking_calibration\"\nvalue = \"true\"\n[[package.metadata.android.application.meta_data]]\nname = \"handtracking\"\nvalue = \"1\"\n[[package.metadata.android.application.meta_data]]\nname = \"picovr.software.eye_tracking\"\nvalue = \"1\"\n[[package.metadata.android.application.meta_data]]\nname = \"picovr.software.face_tracking\"\nvalue = \"true\"\n[[package.metadata.android.application.meta_data]]\nname = \"pvr.app.type\"\nvalue = \"vr\"\n[[package.metadata.android.application.meta_data]]\nname = \"pvr.display.orientation\"\nvalue = \"180\"\n[[package.metadata.android.application.meta_data]]\nname = \"pvr.sdk.version\"\nvalue = \"OpenXR\"\n[[package.metadata.android.application.meta_data]]\nname = \"pxr.sdk.version_code\"\nvalue = \"5900\"\n\n# Yvr entries\n[[package.metadata.android.application.meta_data]]\nname = \"com.yvr.intent.category.VR\"\nvalue = \"vr_only\"\n\n# Lynx entries\n[[package.metadata.android.queries.package]]\nname = \"com.ultraleap.tracking.service\"\n[[package.metadata.android.queries.package]]\nname = \"com.ultraleap.openxr.api_layer\"\n\n# Android XR\n[[package.metadata.android.uses_feature]]\nname = \"android.software.xr.api.spatial\"\nrequired = true\n\n[[package.metadata.android.uses_feature]]\nname = \"android.software.xr.api.openxr\"\nrequired = true\n\n[[package.metadata.android.uses_feature]]\nname = \"android.software.xr.input.controller\"\nrequired = false\n\n[[package.metadata.android.uses_feature]]\nname = \"android.software.xr.input.hand_tracking\"\nrequired = false\n\n[[package.metadata.android.uses_feature]]\nname = \"android.software.xr.input.eye_tracking\"\nrequired = false\n\n[[package.metadata.android.application.uses_native_library]]\nname = \"libopenxr.google.so\"\nrequired = false\n\n[[package.metadata.android.application.property]]\nname = \"android.window.PROPERTY_XR_ACTIVITY_START_MODE\"\nvalue = \"XR_ACTIVITY_START_MODE_FULL_SPACE_UNMANAGED\"\n\n[[package.metadata.android.application.property]]\nname = \"android.window.PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED\"\nvalue = \"XR_BOUNDARY_TYPE_LARGE\"\n"
  },
  {
    "path": "alvr/client_openxr/cbindgen.toml",
    "content": "language = \"C\"\nheader = \"/* ALVR is licensed under the MIT license. https://github.com/alvr-org/ALVR/blob/master/LICENSE */\"\npragma_once = true\nautogen_warning = \"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */\"\ncpp_compat = true\ntab_width = 4\ndocumentation_style = \"c99\"\n\n[enum]\nrename_variants = \"QualifiedScreamingSnakeCase\"\n"
  },
  {
    "path": "alvr/client_openxr/resources/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"247.19\"\n    android:viewportHeight=\"247.2\">\n  <group android:scaleX=\"0.48997378\"\n      android:scaleY=\"0.49\"\n      android:translateX=\"63.03669\"\n      android:translateY=\"63.036\">\n    <group>\n      <clip-path\n          android:pathData=\"m19.24,27.3 l48.9,43.32 -19.6,44.33 18.17,46.96 -45.21,41.35 2.17,43.95 223.52,-0.01 0.01,-247.19 -215.11,0.14z\"/>\n      <path\n          android:pathData=\"M123.59,123.6m-115.38,0a115.38,115.38 0,1 1,230.77 0a115.38,115.38 0,1 1,-230.77 0\"\n          android:strokeWidth=\"16.4055\"\n          android:fillColor=\"#00000000\">\n        <aapt:attr name=\"android:strokeColor\">\n          <gradient \n              android:startX=\"-0\"\n              android:startY=\"123.6\"\n              android:endX=\"247.17\"\n              android:endY=\"123.6\"\n              android:type=\"linear\">\n            <item android:offset=\"0\" android:color=\"#FF3ED2E3\"/>\n            <item android:offset=\"1\" android:color=\"#FF1D89CF\"/>\n          </gradient>\n        </aapt:attr>\n      </path>\n      <path\n          android:pathData=\"M123.59,123.6m-85.3,0a85.3,85.3 0,1 1,170.6 0a85.3,85.3 0,1 1,-170.6 0\"\n          android:strokeWidth=\"15.875\"\n          android:fillColor=\"#00000000\">\n        <aapt:attr name=\"android:strokeColor\">\n          <gradient \n              android:startX=\"32.22\"\n              android:startY=\"123.6\"\n              android:endX=\"214.95\"\n              android:endY=\"123.6\"\n              android:type=\"linear\">\n            <item android:offset=\"0\" android:color=\"#FF3ED2E3\"/>\n            <item android:offset=\"1\" android:color=\"#FF2192D1\"/>\n          </gradient>\n        </aapt:attr>\n      </path>\n      <path\n          android:pathData=\"M123.59,123.6m-56.33,0a56.33,56.33 0,1 1,112.67 0a56.33,56.33 0,1 1,-112.67 0\"\n          android:strokeWidth=\"15.5123\"\n          android:fillColor=\"#00000000\">\n        <aapt:attr name=\"android:strokeColor\">\n          <gradient \n              android:startX=\"63.25\"\n              android:startY=\"123.6\"\n              android:endX=\"183.92\"\n              android:endY=\"123.6\"\n              android:type=\"linear\">\n            <item android:offset=\"0\" android:color=\"#FF3ED2E3\"/>\n            <item android:offset=\"1\" android:color=\"#FF279ED4\"/>\n          </gradient>\n        </aapt:attr>\n      </path>\n    </group>\n  </group>\n</vector>\n"
  },
  {
    "path": "alvr/client_openxr/resources/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "alvr/client_openxr/resources/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "alvr/client_openxr/src/c_api.rs",
    "content": "#[cfg(target_os = \"android\")]\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_entry_point(java_vm: *mut std::ffi::c_void, context: *mut std::ffi::c_void) {\n    unsafe { ndk_context::initialize_android_context(java_vm, context) };\n\n    crate::entry_point();\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/body_tracking_bd.rs",
    "content": "use crate::extra_extensions::get_instance_proc;\nuse openxr::{self as xr, AnyGraphics, sys};\nuse std::{\n    ffi::{CString, c_char, c_void},\n    ptr,\n    sync::LazyLock,\n};\n\npub const BD_BODY_TRACKING_EXTENSION_NAME: &str = \"XR_BD_body_tracking\";\n\nstatic TYPE_BODY_TRACKER_CREATE_INFO_BD: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000385001));\nstatic TYPE_BODY_JOINTS_LOCATE_INFO_BD: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000385002));\nstatic TYPE_BODY_JOINT_LOCATIONS_BD: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000385003));\nstatic TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000385004));\n\npub const BODY_JOINT_COUNT_BD: usize = 24;\n\n#[repr(transparent)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]\npub struct XrBodyTrackerBD(u64);\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct BodyJointSetBD(i32);\nimpl BodyJointSetBD {\n    pub const BODY_WITHOUT_ARM: BodyJointSetBD = Self(1i32);\n    pub const FULL_BODY_JOINTS: BodyJointSetBD = Self(2i32);\n}\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct BodyTrackingStatusCodeBD(i32);\nimpl BodyTrackingStatusCodeBD {\n    pub const INVALID: BodyTrackingStatusCodeBD = Self(0i32);\n}\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct BodyTrackingErrorCodeBD(i32);\nimpl BodyTrackingErrorCodeBD {\n    pub const INNER_EXCEPTION: BodyTrackingErrorCodeBD = Self(0i32);\n    pub const TRACKER_NOT_CALIBRATED: BodyTrackingErrorCodeBD = Self(1i32);\n}\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct CalibAppFlagBD(i32);\nimpl CalibAppFlagBD {\n    pub const MOTION_TRACKER_2: CalibAppFlagBD = Self(1i32);\n}\n\n#[repr(C)]\nstruct BodyTrackerCreateInfoBD {\n    ty: xr::StructureType,\n    next: *const c_void,\n    joint_set: BodyJointSetBD,\n}\n\n#[repr(C)]\nstruct BodyJointsLocateInfoBD {\n    ty: xr::StructureType,\n    next: *const c_void,\n    base_space: sys::Space,\n    time: sys::Time,\n}\n\n#[repr(C)]\npub struct BodyJointLocationBD {\n    pub location_flags: sys::SpaceLocationFlags,\n    pub pose: sys::Posef,\n    pub radius: f32,\n}\n\n#[repr(C)]\nstruct BodyJointLocationsBD {\n    ty: xr::StructureType,\n    next: *const c_void,\n    all_joint_poses_tracked: sys::Bool32,\n    joint_count: u32,\n    joint_locations: *mut BodyJointLocationBD,\n}\n\n#[repr(C)]\nstruct SystemBodyTrackingPropertiesBD {\n    ty: xr::StructureType,\n    next: *const c_void,\n    supports_body_tracking: sys::Bool32,\n}\n\ntype CreateBodyTrackerBD = unsafe extern \"system\" fn(\n    sys::Session,\n    *const BodyTrackerCreateInfoBD,\n    *mut XrBodyTrackerBD,\n) -> sys::Result;\n\ntype DestroyBodyTrackerBD = unsafe extern \"system\" fn(XrBodyTrackerBD) -> sys::Result;\n\ntype LocateBodyJointsBD = unsafe extern \"system\" fn(\n    XrBodyTrackerBD,\n    *const BodyJointsLocateInfoBD,\n    *mut BodyJointLocationsBD,\n) -> sys::Result;\n\ntype StartBodyTrackingCalibAppBD =\n    unsafe extern \"system\" fn(sys::Instance, *const c_char, CalibAppFlagBD) -> sys::Result;\n\ntype GetBodyTrackingStateBD = unsafe extern \"system\" fn(\n    sys::Instance,\n    *mut BodyTrackingStatusCodeBD,\n    *mut BodyTrackingErrorCodeBD,\n) -> sys::Result;\n\npub struct BodyTrackerBD {\n    handle: XrBodyTrackerBD,\n    session: xr::Session<AnyGraphics>,\n    destroy_body_tracker: DestroyBodyTrackerBD,\n    locate_body_joints: LocateBodyJointsBD,\n    get_body_tracking_state: GetBodyTrackingStateBD,\n}\n\nimpl BodyTrackerBD {\n    pub fn new<G>(\n        session: xr::Session<G>,\n        joint_set: BodyJointSetBD,\n        extra_extensions: &[String],\n        system: xr::SystemId,\n        prompt_calibration: bool,\n    ) -> xr::Result<Self> {\n        if !extra_extensions.contains(&BD_BODY_TRACKING_EXTENSION_NAME.to_owned()) {\n            return Err(sys::Result::ERROR_EXTENSION_NOT_PRESENT);\n        }\n\n        let create_body_tracker: CreateBodyTrackerBD =\n            get_instance_proc(&session, \"xrCreateBodyTrackerBD\")?;\n        let start_body_tracking_calib_app: StartBodyTrackingCalibAppBD =\n            get_instance_proc(&session, \"xrStartBodyTrackingCalibAppBD\")?;\n        let get_body_tracking_state: GetBodyTrackingStateBD =\n            get_instance_proc(&session, \"xrGetBodyTrackingStateBD\")?;\n        let destroy_body_tracker = get_instance_proc(&session, \"xrDestroyBodyTrackerBD\")?;\n        let locate_body_joints = get_instance_proc(&session, \"xrLocateBodyJointsBD\")?;\n\n        let props = super::get_props(\n            &session,\n            system,\n            SystemBodyTrackingPropertiesBD {\n                ty: *TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_BD,\n                next: ptr::null(),\n                supports_body_tracking: sys::FALSE,\n            },\n        )?;\n\n        if props.supports_body_tracking == sys::FALSE {\n            return Err(sys::Result::ERROR_FEATURE_UNSUPPORTED);\n        }\n\n        let mut handle = XrBodyTrackerBD(0);\n        let info = BodyTrackerCreateInfoBD {\n            ty: *TYPE_BODY_TRACKER_CREATE_INFO_BD,\n            next: ptr::null(),\n            joint_set,\n        };\n\n        unsafe {\n            super::xr_res(create_body_tracker(session.as_raw(), &info, &mut handle))?;\n        };\n\n        let mut status_code = BodyTrackingStatusCodeBD::INVALID;\n        let mut error_code = BodyTrackingErrorCodeBD::INNER_EXCEPTION;\n\n        if prompt_calibration {\n            unsafe {\n                super::xr_res(get_body_tracking_state(\n                    session.instance().as_raw(),\n                    &mut status_code,\n                    &mut error_code,\n                ))?;\n\n                // todo: include actual Android package name\n                let package_name = CString::new(\"\").unwrap();\n\n                if status_code == BodyTrackingStatusCodeBD::INVALID\n                    || error_code == BodyTrackingErrorCodeBD::TRACKER_NOT_CALIBRATED\n                {\n                    super::xr_res(start_body_tracking_calib_app(\n                        session.instance().as_raw(),\n                        package_name.as_ptr(),\n                        CalibAppFlagBD::MOTION_TRACKER_2,\n                    ))?;\n                }\n            }\n        }\n\n        Ok(Self {\n            handle,\n            session: session.into_any_graphics(),\n            destroy_body_tracker,\n            locate_body_joints,\n            get_body_tracking_state,\n        })\n    }\n\n    pub fn locate_body_joints(\n        &self,\n        time: xr::Time,\n        reference_space: &xr::Space,\n    ) -> xr::Result<Option<Vec<BodyJointLocationBD>>> {\n        let mut status_code = BodyTrackingStatusCodeBD::INVALID;\n        let mut error_code = BodyTrackingErrorCodeBD::INNER_EXCEPTION;\n\n        unsafe {\n            super::xr_res((self.get_body_tracking_state)(\n                self.session.instance().as_raw(),\n                &mut status_code,\n                &mut error_code,\n            ))?;\n        }\n\n        if status_code == BodyTrackingStatusCodeBD::INVALID {\n            return Ok(None);\n        }\n\n        let locate_info = BodyJointsLocateInfoBD {\n            ty: *TYPE_BODY_JOINTS_LOCATE_INFO_BD,\n            next: ptr::null(),\n            base_space: reference_space.as_raw(),\n            time,\n        };\n\n        let joint_count = BODY_JOINT_COUNT_BD;\n        let mut locations: Vec<BodyJointLocationBD> = Vec::with_capacity(joint_count);\n\n        let mut location_info = BodyJointLocationsBD {\n            ty: *TYPE_BODY_JOINT_LOCATIONS_BD,\n            next: ptr::null(),\n            all_joint_poses_tracked: sys::FALSE,\n            joint_count: joint_count as u32,\n            joint_locations: locations.as_mut_ptr(),\n        };\n\n        unsafe {\n            super::xr_res((self.locate_body_joints)(\n                self.handle,\n                &locate_info,\n                &mut location_info,\n            ))?;\n\n            Ok(if location_info.all_joint_poses_tracked.into() {\n                locations.set_len(joint_count);\n\n                Some(locations)\n            } else {\n                None\n            })\n        }\n    }\n}\n\nimpl Drop for BodyTrackerBD {\n    fn drop(&mut self) {\n        unsafe {\n            (self.destroy_body_tracker)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/body_tracking_fb.rs",
    "content": "#![allow(dead_code)]\n\nuse crate::extra_extensions::get_instance_proc;\nuse openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::{ptr, sync::LazyLock};\n\npub const META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME: &str = \"XR_META_body_tracking_full_body\";\npub static BODY_JOINT_SET_FULL_BODY_META: LazyLock<xr::BodyJointSetFB> =\n    LazyLock::new(|| xr::BodyJointSetFB::from_raw(1000274000));\npub const META_BODY_TRACKING_FIDELITY_EXTENSION_NAME: &str = \"XR_META_body_tracking_fidelity\";\npub static SYSTEM_PROPERTIES_BODY_TRACKING_FIDELITY_META: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000284001));\npub const FULL_BODY_JOINT_COUNT_META: usize = 84;\n\n#[repr(C)]\nstruct SystemPropertiesBodyTrackingFullBodyMETA {\n    ty: xr::StructureType,\n    next: *mut std::ffi::c_void,\n    supports_full_body_tracking: sys::Bool32,\n}\n\npub struct BodyTrackerFB {\n    handle: sys::BodyTrackerFB,\n    ext_fns: raw::BodyTrackingFB,\n}\n\n#[repr(C)]\nstruct SystemPropertiesBodyTrackingFidelityMETA {\n    ty: xr::StructureType,\n    next: *mut std::ffi::c_void,\n    supports_body_tracking_fidelity: sys::Bool32,\n}\n\n#[repr(C)]\nenum BodyTrackingFidelityMode {\n    Low = 1,\n    High = 2,\n}\n\ntype RequestBodyTrackingFidelityMETA =\n    unsafe extern \"system\" fn(sys::BodyTrackerFB, BodyTrackingFidelityMode) -> sys::Result;\n\nimpl BodyTrackerFB {\n    pub fn new<G>(\n        session: &xr::Session<G>,\n        system: xr::SystemId,\n        body_joint_set: xr::BodyJointSetFB,\n        prefer_high_fidelity: bool,\n    ) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .fb_body_tracking\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let mut handle = sys::BodyTrackerFB::NULL;\n        let info = sys::BodyTrackerCreateInfoFB {\n            ty: sys::BodyTrackerCreateInfoFB::TYPE,\n            next: ptr::null(),\n            body_joint_set,\n        };\n        let body_tracking_fidelity_props = super::get_props(\n            session,\n            system,\n            SystemPropertiesBodyTrackingFidelityMETA {\n                ty: *SYSTEM_PROPERTIES_BODY_TRACKING_FIDELITY_META,\n                next: ptr::null_mut(),\n                supports_body_tracking_fidelity: sys::FALSE,\n            },\n        )?;\n        let preferred_fidelity_mode: BodyTrackingFidelityMode = if prefer_high_fidelity {\n            BodyTrackingFidelityMode::High\n        } else {\n            BodyTrackingFidelityMode::Low\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_body_tracker)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?;\n\n            if body_tracking_fidelity_props.supports_body_tracking_fidelity == sys::TRUE {\n                let request_body_tracking_fidelity: RequestBodyTrackingFidelityMETA =\n                    get_instance_proc(session, \"xrRequestBodyTrackingFidelityMETA\")?;\n                super::xr_res(request_body_tracking_fidelity(\n                    handle,\n                    preferred_fidelity_mode,\n                ))\n                .ok(); // This is very unlikely to fail as the void falls back to Low on an invalid call.\n            }\n        };\n\n        Ok(Self { handle, ext_fns })\n    }\n\n    pub fn locate_body_joints(\n        &self,\n        time: xr::Time,\n        reference_space: &xr::Space,\n        joint_count: usize,\n    ) -> xr::Result<Option<Vec<xr::BodyJointLocationFB>>> {\n        let locate_info = sys::BodyJointsLocateInfoFB {\n            ty: sys::BodyJointsLocateInfoFB::TYPE,\n            next: ptr::null(),\n            base_space: reference_space.as_raw(),\n            time,\n        };\n        let mut locations: Vec<sys::BodyJointLocationFB> = Vec::with_capacity(joint_count);\n        let mut location_info = sys::BodyJointLocationsFB {\n            ty: sys::BodyJointLocationsFB::TYPE,\n            next: ptr::null_mut(),\n            is_active: sys::FALSE,\n            confidence: 0.0,\n            joint_count: joint_count as u32,\n            joint_locations: locations.as_mut_ptr(),\n            skeleton_changed_count: 0,\n            time: xr::Time::from_nanos(0),\n        };\n        unsafe {\n            super::xr_res((self.ext_fns.locate_body_joints)(\n                self.handle,\n                &locate_info,\n                &mut location_info,\n            ))?;\n\n            Ok(if location_info.is_active.into() {\n                locations.set_len(joint_count);\n\n                Some(locations)\n            } else {\n                None\n            })\n        }\n    }\n}\n\nimpl Drop for BodyTrackerFB {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_body_tracker)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/eye_gaze_interaction.rs",
    "content": "use openxr::{self as xr, sys};\nuse std::ptr;\n\npub fn supports_eye_gaze_interaction<G>(session: &xr::Session<G>, system: xr::SystemId) -> bool {\n    if session.instance().exts().ext_eye_gaze_interaction.is_none() {\n        return false;\n    }\n\n    super::get_props(\n        session,\n        system,\n        sys::SystemEyeGazeInteractionPropertiesEXT {\n            ty: sys::SystemEyeGazeInteractionPropertiesEXT::TYPE,\n            next: ptr::null_mut(),\n            supports_eye_gaze_interaction: sys::FALSE,\n        },\n    )\n    .map(|props| props.supports_eye_gaze_interaction.into())\n    .unwrap_or(false)\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/eye_tracking_social.rs",
    "content": "use openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::ptr;\n\npub struct EyeTrackerSocial {\n    handle: sys::EyeTrackerFB,\n    ext_fns: raw::EyeTrackingSocialFB,\n}\n\nimpl EyeTrackerSocial {\n    pub fn new<G>(session: &xr::Session<G>) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .fb_eye_tracking_social\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let mut handle = sys::EyeTrackerFB::NULL;\n        let info = sys::EyeTrackerCreateInfoFB {\n            ty: sys::EyeTrackerCreateInfoFB::TYPE,\n            next: ptr::null(),\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_eye_tracker)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?\n        };\n\n        Ok(Self { handle, ext_fns })\n    }\n\n    pub fn get_eye_gazes(\n        &self,\n        base: &xr::Space,\n        time: xr::Time,\n    ) -> xr::Result<[Option<xr::Posef>; 2]> {\n        let gaze_info = sys::EyeGazesInfoFB {\n            ty: sys::EyeGazesInfoFB::TYPE,\n            next: ptr::null(),\n            base_space: base.as_raw(),\n            time,\n        };\n\n        let mut eye_gazes = sys::EyeGazesFB::out(ptr::null_mut());\n\n        let eye_gazes = unsafe {\n            super::xr_res((self.ext_fns.get_eye_gazes)(\n                self.handle,\n                &gaze_info,\n                eye_gazes.as_mut_ptr(),\n            ))?;\n\n            eye_gazes.assume_init()\n        };\n\n        let left_valid: bool = eye_gazes.gaze[0].is_valid.into();\n        let right_valid: bool = eye_gazes.gaze[1].is_valid.into();\n\n        Ok([\n            left_valid.then(|| eye_gazes.gaze[0].gaze_pose),\n            right_valid.then(|| eye_gazes.gaze[1].gaze_pose),\n        ])\n    }\n}\n\nimpl Drop for EyeTrackerSocial {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_eye_tracker)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/face_tracking2_fb.rs",
    "content": "use openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::ptr;\n\npub struct FaceTracker2FB {\n    // Keeping a reference to the session to ensure that the tracker handle remains valid\n    _session: xr::Session<xr::AnyGraphics>,\n    handle: sys::FaceTracker2FB,\n    ext_fns: raw::FaceTracking2FB,\n}\n\nimpl FaceTracker2FB {\n    pub fn new<G>(session: xr::Session<G>, visual: bool, audio: bool) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .fb_face_tracking2\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let mut requested_data_sources = vec![];\n        if visual {\n            requested_data_sources.push(sys::FaceTrackingDataSource2FB::VISUAL);\n        }\n        if audio {\n            requested_data_sources.push(sys::FaceTrackingDataSource2FB::AUDIO);\n        }\n\n        let mut handle = sys::FaceTracker2FB::NULL;\n        let info = sys::FaceTrackerCreateInfo2FB {\n            ty: sys::FaceTrackerCreateInfo2FB::TYPE,\n            next: ptr::null(),\n            face_expression_set: xr::FaceExpressionSet2FB::DEFAULT,\n            requested_data_source_count: requested_data_sources.len() as u32,\n            requested_data_sources: requested_data_sources.as_mut_ptr(),\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_face_tracker2)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?\n        };\n\n        Ok(Self {\n            _session: session.into_any_graphics(),\n            handle,\n            ext_fns,\n        })\n    }\n\n    pub fn get_face_expression_weights(&self, time: xr::Time) -> xr::Result<Option<Vec<f32>>> {\n        let expression_info = sys::FaceExpressionInfo2FB {\n            ty: sys::FaceExpressionInfo2FB::TYPE,\n            next: ptr::null(),\n            time,\n        };\n\n        let weights_count = xr::FaceExpression2FB::COUNT.into_raw() as usize;\n        let confidence_count = xr::FaceConfidence2FB::COUNT.into_raw() as usize;\n\n        let mut weights: Vec<f32> = Vec::with_capacity(weights_count);\n        let mut confidences: Vec<f32> = vec![0.0; confidence_count];\n\n        let mut expression_weights = sys::FaceExpressionWeights2FB {\n            ty: sys::FaceExpressionWeights2FB::TYPE,\n            next: ptr::null_mut(),\n            weight_count: weights_count as u32,\n            weights: weights.as_mut_ptr(),\n            confidence_count: confidence_count as u32,\n            confidences: confidences.as_mut_ptr(),\n            is_valid: sys::FALSE,\n            is_eye_following_blendshapes_valid: sys::FALSE,\n            data_source: sys::FaceTrackingDataSource2FB::from_raw(0),\n            time: xr::Time::from_nanos(0),\n        };\n\n        unsafe {\n            super::xr_res((self.ext_fns.get_face_expression_weights2)(\n                self.handle,\n                &expression_info,\n                &mut expression_weights,\n            ))?;\n\n            if expression_weights.is_valid.into() {\n                weights.set_len(weights_count);\n\n                Ok(Some(weights))\n            } else {\n                Ok(None)\n            }\n        }\n    }\n}\n\nimpl Drop for FaceTracker2FB {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_face_tracker2)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/face_tracking_pico.rs",
    "content": "use crate::extra_extensions::get_instance_proc;\nuse openxr::{self as xr, sys};\n\nconst TRACKING_MODE_FACE_BIT: u64 = 0x00000008;\nconst PICO_FACE_EXPRESSION_COUNT: usize = 52;\n\n#[repr(C)]\nstruct FaceTrackingDataPICO {\n    time: sys::Time,\n    blend_shape_weight: [f32; 72],\n    is_video_input_valid: [f32; 10],\n    laughing_probability: f32,\n    emotion_probability: [f32; 10],\n    reserved: [f32; 128],\n}\n\ntype StartEyeTrackingPICO = unsafe extern \"system\" fn(sys::Session) -> sys::Result;\n\ntype StopEyeTrackingPICO = unsafe extern \"system\" fn(sys::Session, u64) -> sys::Result;\n\ntype SetTrackingModePICO = unsafe extern \"system\" fn(sys::Session, u64) -> sys::Result;\n\ntype GetFaceTrackingDataPICO = unsafe extern \"system\" fn(\n    sys::Session,\n    sys::Time,\n    i32,\n    *mut FaceTrackingDataPICO,\n) -> sys::Result;\n\npub struct FaceTrackerPico {\n    session: xr::Session<xr::AnyGraphics>,\n    start_eye_tracking: StartEyeTrackingPICO,\n    stop_eye_tracking: StopEyeTrackingPICO,\n    set_tracking_mode: SetTrackingModePICO,\n    get_face_tracking_data: GetFaceTrackingDataPICO,\n}\n\nimpl FaceTrackerPico {\n    pub fn new<G>(session: xr::Session<G>) -> xr::Result<Self> {\n        session\n            .instance()\n            .exts()\n            .ext_eye_gaze_interaction\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let start_eye_tracking = get_instance_proc(&session, \"xrStartEyeTrackingPICO\")?;\n        let stop_eye_tracking = get_instance_proc(&session, \"xrStopEyeTrackingPICO\")?;\n        let set_tracking_mode = get_instance_proc(&session, \"xrSetTrackingModePICO\")?;\n        let get_face_tracking_data = get_instance_proc(&session, \"xrGetFaceTrackingDataPICO\")?;\n\n        Ok(Self {\n            session: session.into_any_graphics(),\n            start_eye_tracking,\n            stop_eye_tracking,\n            set_tracking_mode,\n            get_face_tracking_data,\n        })\n    }\n\n    pub fn get_face_tracking_data(&self, time: xr::Time) -> xr::Result<Option<Vec<f32>>> {\n        let mut face_tracking_data = FaceTrackingDataPICO {\n            time: xr::Time::from_nanos(0),\n            blend_shape_weight: [0.0; 72],\n            is_video_input_valid: [0.0; 10],\n            laughing_probability: 0.0,\n            emotion_probability: [0.0; 10],\n            reserved: [0.0; 128],\n        };\n\n        unsafe {\n            super::xr_res((self.get_face_tracking_data)(\n                self.session.as_raw(),\n                time,\n                0,\n                &mut face_tracking_data,\n            ))?;\n\n            if face_tracking_data.time.as_nanos() != 0 {\n                let blend_shape_slice =\n                    face_tracking_data.blend_shape_weight[..PICO_FACE_EXPRESSION_COUNT].to_vec();\n\n                Ok(Some(blend_shape_slice))\n            } else {\n                Ok(None)\n            }\n        }\n    }\n\n    pub fn start_face_tracking(&self) -> xr::Result<()> {\n        unsafe {\n            super::xr_res((self.start_eye_tracking)(self.session.as_raw()))?;\n            super::xr_res((self.set_tracking_mode)(\n                self.session.as_raw(),\n                TRACKING_MODE_FACE_BIT,\n            ))\n        }\n    }\n\n    pub fn stop_face_tracking(&self) -> xr::Result<()> {\n        unsafe {\n            super::xr_res((self.stop_eye_tracking)(\n                self.session.as_raw(),\n                TRACKING_MODE_FACE_BIT,\n            ))\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/facial_tracking_htc.rs",
    "content": "use openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::ptr;\n\npub struct FacialTrackerHTC {\n    // Keeping a reference to the session to ensure that the tracker handle remains valid\n    _session: xr::Session<xr::AnyGraphics>,\n    handle: sys::FacialTrackerHTC,\n    ext_fns: raw::FacialTrackingHTC,\n    expression_count: usize,\n}\n\nimpl FacialTrackerHTC {\n    pub fn new<G>(\n        session: xr::Session<G>,\n        system: xr::SystemId,\n        facial_tracking_type: xr::FacialTrackingTypeHTC,\n    ) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .htc_facial_tracking\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let props = super::get_props(\n            &session,\n            system,\n            sys::SystemFacialTrackingPropertiesHTC {\n                ty: sys::SystemFacialTrackingPropertiesHTC::TYPE,\n                next: ptr::null_mut(),\n                support_eye_facial_tracking: sys::FALSE,\n                support_lip_facial_tracking: sys::FALSE,\n            },\n        )?;\n\n        let expression_count = if facial_tracking_type == sys::FacialTrackingTypeHTC::EYE_DEFAULT\n            && props.support_eye_facial_tracking.into()\n        {\n            sys::FACIAL_EXPRESSION_EYE_COUNT_HTC\n        } else if facial_tracking_type == sys::FacialTrackingTypeHTC::LIP_DEFAULT\n            && props.support_lip_facial_tracking.into()\n        {\n            sys::FACIAL_EXPRESSION_LIP_COUNT_HTC\n        } else {\n            return Err(sys::Result::ERROR_FEATURE_UNSUPPORTED);\n        };\n\n        let mut handle = sys::FacialTrackerHTC::NULL;\n        let info = sys::FacialTrackerCreateInfoHTC {\n            ty: sys::FacialTrackerCreateInfoHTC::TYPE,\n            next: ptr::null(),\n            facial_tracking_type,\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_facial_tracker)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?\n        };\n\n        Ok(Self {\n            _session: session.into_any_graphics(),\n            handle,\n            ext_fns,\n            expression_count,\n        })\n    }\n\n    pub fn get_facial_expressions(&self, time: xr::Time) -> xr::Result<Option<Vec<f32>>> {\n        let mut weights = Vec::with_capacity(self.expression_count);\n\n        let mut facial_expressions = sys::FacialExpressionsHTC {\n            ty: sys::FacialExpressionsHTC::TYPE,\n            next: ptr::null_mut(),\n            is_active: sys::FALSE,\n            sample_time: time,\n            expression_count: self.expression_count as u32,\n            expression_weightings: weights.as_mut_ptr(),\n        };\n\n        unsafe {\n            super::xr_res((self.ext_fns.get_facial_expressions)(\n                self.handle,\n                &mut facial_expressions,\n            ))?;\n\n            if facial_expressions.is_active.into() {\n                weights.set_len(self.expression_count);\n\n                Ok(Some(weights))\n            } else {\n                Ok(None)\n            }\n        }\n    }\n}\n\nimpl Drop for FacialTrackerHTC {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_facial_tracker)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/mod.rs",
    "content": "mod body_tracking_bd;\nmod body_tracking_fb;\nmod eye_gaze_interaction;\nmod eye_tracking_social;\nmod face_tracking2_fb;\nmod face_tracking_pico;\nmod facial_tracking_htc;\nmod motion_tracking_bd;\nmod multimodal_input;\nmod passthrough_fb;\nmod passthrough_htc;\n\npub use body_tracking_bd::*;\npub use body_tracking_fb::*;\npub use eye_gaze_interaction::*;\npub use eye_tracking_social::*;\npub use face_tracking_pico::*;\npub use face_tracking2_fb::*;\npub use facial_tracking_htc::*;\npub use motion_tracking_bd::*;\npub use multimodal_input::*;\npub use passthrough_fb::*;\npub use passthrough_htc::*;\nuse std::ffi::CString;\nuse std::mem;\n\nuse openxr::{self as xr, sys};\n\nfn xr_res(result: sys::Result) -> xr::Result<()> {\n    if result.into_raw() >= 0 {\n        Ok(())\n    } else {\n        Err(result)\n    }\n}\n\nfn get_props<G, T>(\n    session: &xr::Session<G>,\n    system: xr::SystemId,\n    default_struct: T,\n) -> xr::Result<T> {\n    let instance = session.instance();\n\n    let mut props = default_struct;\n    let mut system_properties = sys::SystemProperties::out((&raw mut props).cast());\n    let result = unsafe {\n        (instance.fp().get_system_properties)(\n            instance.as_raw(),\n            system,\n            system_properties.as_mut_ptr(),\n        )\n    };\n\n    xr_res(result).map(|_| props)\n}\n\nfn get_instance_proc<G, FnTy>(session: &xr::Session<G>, method_name: &str) -> xr::Result<FnTy> {\n    unsafe {\n        let method_name = CString::new(method_name).unwrap();\n        let mut function_handle = None;\n\n        xr_res((session.instance().fp().get_instance_proc_addr)(\n            session.instance().as_raw(),\n            method_name.as_ptr(),\n            &mut function_handle,\n        ))?;\n\n        function_handle\n            .map(|pfn| mem::transmute_copy(&pfn))\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/motion_tracking_bd.rs",
    "content": "use crate::extra_extensions::get_instance_proc;\nuse openxr::{self as xr, AnyGraphics, sys};\nuse std::ffi::{CString, c_char};\n\npub const BD_MOTION_TRACKING_EXTENSION_NAME: &str = \"XR_BD_motion_tracking\";\npub const PICO_CONFIGURATION_EXTENSION_NAME: &str = \"XR_PICO_configuration\";\n\n#[repr(C)]\n#[derive(Default)]\nstruct MotionTrackerConnectStateBD {\n    tracker_count: i32,\n    serials: [MotionTrackerSerialBD; 6],\n}\n\n#[repr(C)]\n#[derive(Ord, Eq, PartialEq, PartialOrd, Default)]\npub struct MotionTrackerSerialBD {\n    pub serial: [u8; 24],\n}\n\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct MotionTrackerConfidenceBD(i32);\n\n#[repr(C)]\npub struct MotionTrackerLocationBD {\n    pub pose: sys::Posef,\n    pub angular_velocity: sys::Vector3f,\n    pub linear_velocity: sys::Vector3f,\n    pub angular_acceleration: sys::Vector3f,\n    pub linear_acceleration: sys::Vector3f,\n}\n\n#[repr(C)]\npub struct MotionTrackerLocationsBD {\n    pub serial: MotionTrackerSerialBD,\n    pub local_pose: MotionTrackerLocationBD,\n    pub confidence: MotionTrackerConfidenceBD,\n    pub global_pose: MotionTrackerLocationBD,\n}\n\ntype GetMotionTrackerConnectStateBD =\n    unsafe extern \"system\" fn(sys::Instance, *mut MotionTrackerConnectStateBD) -> sys::Result;\n\ntype GetMotionTrackerLocationsBD = unsafe extern \"system\" fn(\n    sys::Instance,\n    sys::Time,\n    *const MotionTrackerSerialBD,\n    *mut MotionTrackerLocationsBD,\n) -> sys::Result;\n\ntype SetConfigPICO = unsafe extern \"system\" fn(sys::Session, i32, *const c_char) -> sys::Result;\n\npub struct MotionTrackerBD {\n    session: xr::Session<AnyGraphics>,\n    get_motion_tracker_connect_state: GetMotionTrackerConnectStateBD,\n    get_motion_tracker_locations: GetMotionTrackerLocationsBD,\n}\n\nimpl MotionTrackerBD {\n    pub fn new<G>(session: xr::Session<G>, extra_extensions: &[String]) -> xr::Result<Self> {\n        if !extra_extensions.contains(&BD_MOTION_TRACKING_EXTENSION_NAME.to_owned())\n            || !extra_extensions.contains(&PICO_CONFIGURATION_EXTENSION_NAME.to_owned())\n        {\n            return Err(sys::Result::ERROR_EXTENSION_NOT_PRESENT);\n        }\n\n        let get_motion_tracker_connect_state =\n            get_instance_proc(&session, \"xrGetMotionTrackerConnectStateBD\")?;\n        let get_motion_tracker_locations =\n            get_instance_proc(&session, \"xrGetMotionTrackerLocationsBD\")?;\n        let set_config: SetConfigPICO = get_instance_proc(&session, \"xrSetConfigPICO\")?;\n\n        unsafe {\n            //Floor height tracking origin\n            let str = CString::new(\"1\").unwrap();\n            //Set config property for tracking origin\n            super::xr_res(set_config(session.as_raw(), 1, str.as_ptr()))?;\n        };\n\n        Ok(Self {\n            session: session.into_any_graphics(),\n            get_motion_tracker_connect_state,\n            get_motion_tracker_locations,\n        })\n    }\n\n    pub fn locate_motion_trackers(\n        &self,\n        time: xr::Time,\n    ) -> xr::Result<Option<Vec<MotionTrackerLocationsBD>>> {\n        let mut locations = Vec::with_capacity(3);\n        let mut connect_state = MotionTrackerConnectStateBD::default();\n\n        unsafe {\n            super::xr_res((self.get_motion_tracker_connect_state)(\n                self.session.instance().as_raw(),\n                &mut connect_state,\n            ))?;\n\n            // Pico doesn't provide a way to connect more than three trackers now\n            if connect_state.tracker_count > 3 {\n                connect_state.tracker_count = 3\n            }\n\n            for i in 0..connect_state.tracker_count as usize {\n                let mut location = MotionTrackerLocationsBD {\n                    serial: MotionTrackerSerialBD { serial: [0; 24] },\n                    local_pose: MotionTrackerLocationBD {\n                        pose: xr::Posef::IDENTITY,\n                        angular_velocity: Default::default(),\n                        linear_velocity: Default::default(),\n                        angular_acceleration: Default::default(),\n                        linear_acceleration: Default::default(),\n                    },\n                    confidence: MotionTrackerConfidenceBD(0),\n                    global_pose: MotionTrackerLocationBD {\n                        pose: xr::Posef::IDENTITY,\n                        angular_velocity: Default::default(),\n                        linear_velocity: Default::default(),\n                        angular_acceleration: Default::default(),\n                        linear_acceleration: Default::default(),\n                    },\n                };\n\n                super::xr_res((self.get_motion_tracker_locations)(\n                    self.session.instance().as_raw(),\n                    time,\n                    &connect_state.serials[i],\n                    &mut location,\n                ))?;\n\n                locations.push(location);\n            }\n        }\n\n        Ok(Some(locations))\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/multimodal_input.rs",
    "content": "// Code taken from:\n// https://github.com/meta-quest/Meta-OpenXR-SDK/blob/main/OpenXR/meta_openxr_preview/meta_simultaneous_hands_and_controllers.h\n\nuse crate::extra_extensions::get_instance_proc;\nuse openxr::{\n    self as xr,\n    sys::{self},\n};\nuse std::{ffi::c_void, ptr, sync::LazyLock};\n\npub const META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME: &str =\n    \"XR_META_simultaneous_hands_and_controllers\";\npub const META_DETACHED_CONTROLLERS_EXTENSION_NAME: &str = \"XR_META_detached_controllers\";\n\nstatic TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META: LazyLock<xr::StructureType> =\n    LazyLock::new(|| xr::StructureType::from_raw(1000532001));\nstatic TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META: LazyLock<\n    xr::StructureType,\n> = LazyLock::new(|| xr::StructureType::from_raw(1000532002));\nstatic TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META: LazyLock<\n    xr::StructureType,\n> = LazyLock::new(|| xr::StructureType::from_raw(1000532003));\n\n#[repr(C)]\nstruct SystemSymultaneousHandsAndControllersPropertiesMETA {\n    ty: xr::StructureType,\n    next: *const c_void,\n    supports_simultaneous_hands_and_controllers: sys::Bool32,\n}\n\n#[repr(C)]\nstruct SimultaneousHandsAndControllersTrackingResumeInfoMETA {\n    ty: xr::StructureType,\n    next: *const c_void,\n}\n#[repr(C)]\nstruct SimultaneousHandsAndControllersTrackingPauseInfoMETA {\n    ty: xr::StructureType,\n    next: *const c_void,\n}\n\ntype ResumeSimultaneousHandsAndControllersTrackingMETA = unsafe extern \"system\" fn(\n    sys::Session,\n    *const SimultaneousHandsAndControllersTrackingResumeInfoMETA,\n) -> sys::Result;\ntype PauseSimultaneousHandsAndControllersTrackingMETA = unsafe extern \"system\" fn(\n    sys::Session,\n    *const SimultaneousHandsAndControllersTrackingPauseInfoMETA,\n) -> sys::Result;\n\npub struct MultimodalMeta {\n    session: xr::Session<xr::AnyGraphics>,\n    resume_simultaneous_hands_and_controllers_tracking_meta:\n        ResumeSimultaneousHandsAndControllersTrackingMETA,\n    pause_simultaneous_hands_and_controllers_tracking_meta:\n        PauseSimultaneousHandsAndControllersTrackingMETA,\n}\n\nimpl MultimodalMeta {\n    pub fn new<G>(\n        session: xr::Session<G>,\n        extra_extensions: &[String],\n        system: xr::SystemId,\n    ) -> xr::Result<Self> {\n        if !extra_extensions\n            .contains(&META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME.to_owned())\n            || !extra_extensions.contains(&META_DETACHED_CONTROLLERS_EXTENSION_NAME.to_owned())\n        {\n            return Err(sys::Result::ERROR_EXTENSION_NOT_PRESENT);\n        }\n\n        let resume_simultaneous_hands_and_controllers_tracking_meta = get_instance_proc(\n            &session,\n            \"xrResumeSimultaneousHandsAndControllersTrackingMETA\",\n        )?;\n        let pause_simultaneous_hands_and_controllers_tracking_meta = get_instance_proc(\n            &session,\n            \"xrPauseSimultaneousHandsAndControllersTrackingMETA\",\n        )?;\n\n        let props = super::get_props(\n            &session,\n            system,\n            SystemSymultaneousHandsAndControllersPropertiesMETA {\n                ty: *TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META,\n                next: ptr::null(),\n                supports_simultaneous_hands_and_controllers: xr::sys::FALSE,\n            },\n        )?;\n\n        if props.supports_simultaneous_hands_and_controllers.into() {\n            Ok(Self {\n                session: session.into_any_graphics(),\n                resume_simultaneous_hands_and_controllers_tracking_meta,\n                pause_simultaneous_hands_and_controllers_tracking_meta,\n            })\n        } else {\n            Err(sys::Result::ERROR_FEATURE_UNSUPPORTED)\n        }\n    }\n\n    pub fn resume(&self) -> xr::Result<()> {\n        let resume_info = SimultaneousHandsAndControllersTrackingResumeInfoMETA {\n            ty: *TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META,\n            next: ptr::null(),\n        };\n        unsafe {\n            super::xr_res((self\n                .resume_simultaneous_hands_and_controllers_tracking_meta)(\n                self.session.as_raw(),\n                &resume_info,\n            ))\n        }\n    }\n\n    pub fn pause(&self) -> xr::Result<()> {\n        let pause_info = SimultaneousHandsAndControllersTrackingPauseInfoMETA {\n            ty: *TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META,\n            next: ptr::null(),\n        };\n        unsafe {\n            super::xr_res((self\n                .pause_simultaneous_hands_and_controllers_tracking_meta)(\n                self.session.as_raw(),\n                &pause_info,\n            ))\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/passthrough_fb.rs",
    "content": "use alvr_system_info::Platform;\nuse openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::ptr;\n\npub struct PassthroughFB {\n    handle: sys::PassthroughFB,\n    layer_handle: sys::PassthroughLayerFB,\n    layer: sys::CompositionLayerPassthroughFB,\n    ext_fns: raw::PassthroughFB,\n}\n\nimpl PassthroughFB {\n    pub fn new(session: &xr::Session<xr::OpenGlEs>, platform: Platform) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .fb_passthrough\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let mut handle = sys::PassthroughFB::NULL;\n        let info = sys::PassthroughCreateInfoFB {\n            ty: sys::PassthroughCreateInfoFB::TYPE,\n            next: ptr::null(),\n            flags: sys::PassthroughFlagsFB::IS_RUNNING_AT_CREATION,\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_passthrough)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?\n        };\n\n        let mut layer_handle = sys::PassthroughLayerFB::NULL;\n        let info = sys::PassthroughLayerCreateInfoFB {\n            ty: sys::PassthroughLayerCreateInfoFB::TYPE,\n            next: ptr::null(),\n            passthrough: handle,\n            flags: sys::PassthroughFlagsFB::IS_RUNNING_AT_CREATION,\n            purpose: sys::PassthroughLayerPurposeFB::RECONSTRUCTION,\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_passthrough_layer)(\n                session.as_raw(),\n                &info,\n                &mut layer_handle,\n            ))?\n        };\n\n        let layer = sys::CompositionLayerPassthroughFB {\n            ty: sys::CompositionLayerPassthroughFB::TYPE,\n            next: ptr::null(),\n            flags: xr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA,\n            space: sys::Space::NULL,\n            layer_handle,\n        };\n\n        // HACK: YVR runtime seems to ignore IS_RUNNING_AT_CREATION on versions <= 3.0.1\n        if platform.is_yvr() {\n            unsafe { super::xr_res((ext_fns.passthrough_start)(handle))? };\n        }\n\n        Ok(Self {\n            handle,\n            layer_handle,\n            layer,\n            ext_fns,\n        })\n    }\n\n    // return reference to make sure the passthrough handle is not dropped while the layer is in use\n    pub fn layer(&self) -> &sys::CompositionLayerPassthroughFB {\n        &self.layer\n    }\n}\n\nimpl Drop for PassthroughFB {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_passthrough_layer)(self.layer_handle);\n            (self.ext_fns.destroy_passthrough)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/extra_extensions/passthrough_htc.rs",
    "content": "use openxr::{\n    self as xr, raw,\n    sys::{self, Handle},\n};\nuse std::ptr;\n\npub struct PassthroughHTC {\n    handle: sys::PassthroughHTC,\n    layer: sys::CompositionLayerPassthroughHTC,\n    ext_fns: raw::PassthroughHTC,\n}\n\nimpl PassthroughHTC {\n    pub fn new(session: &xr::Session<xr::OpenGlEs>) -> xr::Result<Self> {\n        let ext_fns = session\n            .instance()\n            .exts()\n            .htc_passthrough\n            .ok_or(sys::Result::ERROR_EXTENSION_NOT_PRESENT)?;\n\n        let mut handle = sys::PassthroughHTC::NULL;\n        let info = sys::PassthroughCreateInfoHTC {\n            ty: sys::PassthroughCreateInfoHTC::TYPE,\n            next: ptr::null(),\n            form: sys::PassthroughFormHTC::PLANAR,\n        };\n        unsafe {\n            super::xr_res((ext_fns.create_passthrough)(\n                session.as_raw(),\n                &info,\n                &mut handle,\n            ))?\n        };\n\n        let layer = sys::CompositionLayerPassthroughHTC {\n            ty: sys::CompositionLayerPassthroughHTC::TYPE,\n            next: ptr::null(),\n            layer_flags: xr::CompositionLayerFlags::EMPTY,\n            space: sys::Space::NULL,\n            passthrough: handle,\n            color: sys::PassthroughColorHTC {\n                ty: sys::PassthroughColorHTC::TYPE,\n                next: ptr::null(),\n                alpha: 1.0,\n            },\n        };\n\n        Ok(Self {\n            handle,\n            layer,\n            ext_fns,\n        })\n    }\n\n    // return reference to make sure the passthrough handle is not dropped while the layer is in use\n    pub fn layer(&self) -> &sys::CompositionLayerPassthroughHTC {\n        &self.layer\n    }\n}\n\nimpl Drop for PassthroughHTC {\n    fn drop(&mut self) {\n        unsafe {\n            (self.ext_fns.destroy_passthrough)(self.handle);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/graphics.rs",
    "content": "use alvr_common::glam::UVec2;\nuse alvr_graphics::GraphicsContext;\nuse alvr_session::ClientsidePostProcessingConfig;\nuse openxr as xr;\nuse std::ptr;\n\n#[allow(unused)]\npub fn session_create_info(ctx: &GraphicsContext) -> xr::opengles::SessionCreateInfo {\n    #[cfg(target_os = \"android\")]\n    {\n        xr::opengles::SessionCreateInfo::Android {\n            display: ctx.egl_display.as_ptr(),\n            config: ctx.egl_config.as_ptr(),\n            context: ctx.egl_context.as_ptr(),\n        }\n    }\n    #[cfg(not(target_os = \"android\"))]\n    unimplemented!()\n}\n\npub fn swapchain_format(\n    gfx_ctx: &GraphicsContext,\n    session: &xr::Session<xr::OpenGlEs>,\n    enable_hdr: bool,\n) -> u32 {\n    gfx_ctx.make_current();\n\n    let formats = session.enumerate_swapchain_formats().unwrap();\n    alvr_graphics::choose_swapchain_format(&formats, enable_hdr)\n}\n\n#[allow(unused_variables)]\npub fn create_swapchain(\n    session: &xr::Session<xr::OpenGlEs>,\n    gfx_ctx: &GraphicsContext,\n    resolution: UVec2,\n    format: u32,\n    foveation: Option<&xr::FoveationProfileFB>,\n) -> xr::Swapchain<xr::OpenGlEs> {\n    gfx_ctx.make_current();\n\n    let swapchain_info = xr::SwapchainCreateInfo {\n        create_flags: xr::SwapchainCreateFlags::EMPTY,\n        usage_flags: xr::SwapchainUsageFlags::COLOR_ATTACHMENT | xr::SwapchainUsageFlags::SAMPLED,\n        format,\n        sample_count: 1,\n        width: resolution.x,\n        height: resolution.y,\n        face_count: 1,\n        array_size: 1,\n        mip_count: 1,\n    };\n\n    session.create_swapchain(&swapchain_info).unwrap()\n}\n\npub struct ProjectionLayerAlphaConfig {\n    pub premultiplied: bool,\n}\n\n// This is needed to work around lifetime limitations. Deref cannot be implemented because there are\n// nested references, and in a way or the other I would get `cannot return reference to temporary\n// value`\npub struct ProjectionLayerBuilder<'a> {\n    reference_space: &'a xr::Space,\n    layers: [xr::CompositionLayerProjectionView<'a, xr::OpenGlEs>; 2],\n    alpha: Option<ProjectionLayerAlphaConfig>,\n    composition_layer_settings: Option<xr::sys::CompositionLayerSettingsFB>,\n}\n\nimpl<'a> ProjectionLayerBuilder<'a> {\n    pub fn new(\n        reference_space: &'a xr::Space,\n        layers: [xr::CompositionLayerProjectionView<'a, xr::OpenGlEs>; 2],\n        alpha: Option<ProjectionLayerAlphaConfig>,\n        clientside_post_processing_config: Option<ClientsidePostProcessingConfig>,\n    ) -> Self {\n        let composition_layer_settings = clientside_post_processing_config\n            .map(|post_processing| {\n                xr::CompositionLayerSettingsFlagsFB::from_raw(\n                    (post_processing.sharpening as u64) | (post_processing.super_sampling as u64),\n                )\n            })\n            .filter(|&flags| flags != xr::CompositionLayerSettingsFlagsFB::EMPTY)\n            .map(|flags| xr::sys::CompositionLayerSettingsFB {\n                ty: xr::StructureType::COMPOSITION_LAYER_SETTINGS_FB,\n                next: std::ptr::null(),\n                layer_flags: flags,\n            });\n        Self {\n            reference_space,\n            layers,\n            alpha,\n            composition_layer_settings,\n        }\n    }\n\n    pub fn build(&self) -> xr::CompositionLayerProjection<'_, xr::OpenGlEs> {\n        let mut flags = xr::CompositionLayerFlags::EMPTY;\n\n        if let Some(alpha) = &self.alpha {\n            flags |= xr::CompositionLayerFlags::BLEND_TEXTURE_SOURCE_ALPHA;\n\n            if !alpha.premultiplied {\n                flags |= xr::CompositionLayerFlags::UNPREMULTIPLIED_ALPHA;\n            }\n        }\n\n        let layer = xr::CompositionLayerProjection::new()\n            .layer_flags(flags)\n            .space(self.reference_space)\n            .views(&self.layers);\n\n        if let Some(composition_layer_settings) = self.composition_layer_settings.as_ref() {\n            unsafe {\n                xr::CompositionLayerProjection::from_raw(xr::sys::CompositionLayerProjection {\n                    next: ptr::from_ref(composition_layer_settings).cast(),\n                    ..layer.into_raw()\n                })\n            }\n        } else {\n            layer\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/interaction.rs",
    "content": "use crate::{\n    Platform,\n    extra_extensions::{\n        self, BODY_JOINT_SET_FULL_BODY_META, BodyJointSetBD, BodyTrackerBD, BodyTrackerFB,\n        EyeTrackerSocial, FULL_BODY_JOINT_COUNT_META, FaceTracker2FB, FaceTrackerPico,\n        FacialTrackerHTC, MotionTrackerBD, MultimodalMeta,\n    },\n};\nuse alvr_common::{\n    glam::{Quat, Vec3},\n    *,\n};\nuse alvr_graphics::HandData;\nuse alvr_packets::{ButtonEntry, ButtonValue, FaceData, FaceExpressions, StreamConfig};\nuse alvr_session::{BodyTrackingBDConfig, BodyTrackingSourcesConfig, FaceTrackingSourcesConfig};\nuse openxr as xr;\nuse std::{\n    collections::{HashMap, HashSet},\n    time::Duration,\n};\nuse xr::SpaceLocationFlags;\n\nconst IPD_CHANGE_EPS: f32 = 0.001;\n\n// Most OpenXR runtime, including Meta's one, do not follow perfectly the specification regarding\n// controller pose. The Z axis should point down through the center of the controller grip, the X\n// axis should go out perpendicular from the palm, and the position should be aligned roughtly with\n// the center of the palm.\n// https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html\n// Note: right controller offsets are calculated from left controller offsets by mirroring along the\n// Y-Z plane.\nfn get_controller_offset(platform: Platform, is_right_hand: bool) -> Pose {\n    const DEG_TO_RAD: f32 = std::f32::consts::PI / 180.0;\n\n    let left_offset = match platform {\n        Platform::Quest1 => Pose {\n            position: Vec3::new(-0.013, -0.005, 0.0),\n            orientation: Quat::from_rotation_x(-20.0 * DEG_TO_RAD),\n        },\n        // todo: check Quest 2\n        p if p.is_quest() => Pose {\n            position: Vec3::new(-0.005, -0.005, 0.00),\n            orientation: Quat::from_rotation_x(-15.0 * DEG_TO_RAD),\n        },\n        Platform::PicoNeo3 => Pose {\n            position: Vec3::new(-0.013, -0.035, 0.0),\n            orientation: Quat::IDENTITY,\n        },\n        // todo: check (base) Pico 4\n        p if p.is_pico() => Pose {\n            position: Vec3::new(-0.01, -0.035, 0.0),\n            orientation: Quat::from_rotation_y(6.0 * DEG_TO_RAD)\n                * Quat::from_rotation_x(-6.0 * DEG_TO_RAD),\n        },\n        p if p.is_vive() => Pose {\n            position: Vec3::new(0.0, 0.0, -0.02),\n            orientation: Quat::IDENTITY,\n        },\n        Platform::SamsungGalaxyXR => Pose {\n            position: Vec3::new(0.0, 0.0, 0.055),\n            orientation: Quat::IDENTITY,\n        },\n        _ => Pose::IDENTITY,\n    };\n\n    if is_right_hand {\n        let p = left_offset.position;\n        let q = left_offset.orientation;\n\n        Pose {\n            position: Vec3::new(-p[0], p[1], p[2]),\n            orientation: Quat::from_xyzw(-q.x, q.y, q.z, -q.w),\n        }\n    } else {\n        left_offset\n    }\n}\n\nfn check_ext_object<T>(name: &str, result: xr::Result<T>) -> Option<T> {\n    match result {\n        Ok(obj) => Some(obj),\n        Err(xr::sys::Result::ERROR_FEATURE_UNSUPPORTED) => {\n            warn!(\"Cannot create unsupported {name}\");\n            None\n        }\n        Err(xr::sys::Result::ERROR_EXTENSION_NOT_PRESENT) => None,\n        Err(e) => {\n            warn!(\"Failed to create {name}: {e}\");\n            None\n        }\n    }\n}\n\npub enum ButtonAction {\n    Binary(xr::Action<bool>),\n    Scalar(xr::Action<f32>),\n}\n\npub struct HandInteraction {\n    pub controllers_profile_id: u64,\n    pub input_ids: HashSet<u64>,\n    pub pose_offset: Pose,\n\n    pub grip_action: xr::Action<xr::Posef>,\n    pub grip_space: xr::Space,\n\n    #[expect(dead_code)]\n    pub aim_action: xr::Action<xr::Posef>,\n    #[expect(dead_code)]\n    pub aim_space: xr::Space,\n\n    pub detached_grip_action: Option<xr::Action<xr::Posef>>,\n    pub detached_grip_space: Option<xr::Space>,\n\n    pub vibration_action: xr::Action<xr::Haptic>,\n    pub skeleton_tracker: Option<xr::HandTracker>,\n}\n\npub enum FaceExpressionsTracker {\n    Fb(FaceTracker2FB),\n    Pico(FaceTrackerPico),\n    Htc {\n        eye: Option<FacialTrackerHTC>,\n        lip: Option<FacialTrackerHTC>,\n    },\n}\n\npub struct FaceSources {\n    eyes_combined: Option<(xr::Action<xr::Posef>, xr::Space)>,\n    eyes_social: Option<EyeTrackerSocial>,\n    face_expressions_tracker: Option<FaceExpressionsTracker>,\n}\n\npub enum BodyTracker {\n    Fb {\n        tracker: BodyTrackerFB,\n        joint_count: usize,\n    },\n    BodyBD(BodyTrackerBD),\n    MotionBD(MotionTrackerBD),\n}\n\n#[derive(Clone)]\npub struct InteractionSourcesConfig {\n    pub face_tracking: Option<FaceTrackingSourcesConfig>,\n    pub body_tracking: Option<BodyTrackingSourcesConfig>,\n    pub prefers_multimodal_input: bool,\n}\n\nimpl InteractionSourcesConfig {\n    pub fn new(config: &StreamConfig) -> Self {\n        Self {\n            face_tracking: config\n                .settings\n                .headset\n                .face_tracking\n                .as_option()\n                .map(|c| c.sources.clone()),\n            body_tracking: config\n                .settings\n                .headset\n                .body_tracking\n                .as_option()\n                .map(|c| c.sources.clone()),\n            prefers_multimodal_input: config\n                .settings\n                .headset\n                .multimodal_tracking\n                .as_option()\n                .is_some_and(|c| c.enabled),\n        }\n    }\n}\n\npub struct InteractionContext {\n    xr_session: xr::Session<xr::OpenGlEs>,\n    xr_system: xr::SystemId,\n    extra_extensions: Vec<String>,\n    platform: Platform,\n    pub action_set: xr::ActionSet,\n    pub button_actions: HashMap<u64, ButtonAction>,\n    pub hands_interaction: [HandInteraction; 2],\n    multimodal_handle: Option<MultimodalMeta>,\n    pub multimodal_hands_enabled: bool,\n    pub face_sources: FaceSources,\n    pub body_source: Option<BodyTracker>,\n}\n\nimpl InteractionContext {\n    pub fn new(\n        xr_session: xr::Session<xr::OpenGlEs>,\n        extra_extensions: Vec<String>,\n        xr_system: xr::SystemId,\n        platform: Platform,\n    ) -> Self {\n        let xr_instance = xr_session.instance();\n\n        let action_set = xr_instance\n            .create_action_set(\"alvr_interaction\", \"ALVR interaction\", 0)\n            .unwrap();\n\n        let mut bindings = vec![];\n\n        fn binding<'a, T: xr::ActionTy>(action: &'a xr::Action<T>, path: &str) -> xr::Binding<'a> {\n            xr::Binding::new(action, action.instance().string_to_path(path).unwrap())\n        }\n\n        let controllers_profile_path = match platform {\n            p if p.is_quest() => QUEST_CONTROLLER_PROFILE_PATH, // todo: create new controller profile for quest pro and 3\n            Platform::PicoG3 => PICO_G3_CONTROLLER_PROFILE_PATH,\n            Platform::PicoNeo3 => PICO_NEO3_CONTROLLER_PROFILE_PATH,\n            Platform::Pico4Ultra => PICO4S_CONTROLLER_PROFILE_PATH,\n            Platform::Pico4 | Platform::Pico4Pro | Platform::Pico4Enterprise => {\n                PICO4_CONTROLLER_PROFILE_PATH\n            }\n            p if p.is_pico() => PICO4S_CONTROLLER_PROFILE_PATH,\n            p if p.is_vive() => FOCUS3_CONTROLLER_PROFILE_PATH,\n            p if p.is_yvr() => YVR_CONTROLLER_PROFILE_PATH,\n            _ => QUEST_CONTROLLER_PROFILE_PATH,\n        };\n        let controllers_profile_id = alvr_common::hash_string(controllers_profile_path);\n\n        // Create actions:\n\n        let mut button_actions = HashMap::new();\n        let button_set = CONTROLLER_PROFILE_INFO\n            .get(&controllers_profile_id)\n            .unwrap()\n            .button_set\n            .clone();\n        for button_id in &button_set {\n            let info = BUTTON_INFO.get(button_id).unwrap();\n\n            let name = info.path[1..].replace('/', \"_\");\n            let display_name = format!(\n                \"{}{}\",\n                name[0..1].to_uppercase(),\n                name[1..].replace('_', \" \")\n            );\n\n            let action = match info.button_type {\n                ButtonType::Binary => ButtonAction::Binary(\n                    action_set.create_action(&name, &display_name, &[]).unwrap(),\n                ),\n                ButtonType::Scalar => ButtonAction::Scalar(\n                    action_set.create_action(&name, &display_name, &[]).unwrap(),\n                ),\n            };\n            button_actions.insert(*button_id, action);\n        }\n\n        let left_grip_action = action_set\n            .create_action(\"left_grip_pose\", \"Left grip pose\", &[])\n            .unwrap();\n        let right_grip_action = action_set\n            .create_action(\"right_grip_pose\", \"Right grip pose\", &[])\n            .unwrap();\n\n        let left_aim_action = action_set\n            .create_action(\"left_aim_pose\", \"Left aim pose\", &[])\n            .unwrap();\n        let right_aim_action = action_set\n            .create_action(\"right_aim_pose\", \"Right aim pose\", &[])\n            .unwrap();\n\n        let left_vibration_action = action_set\n            .create_action(\"left_hand_vibration\", \"Left hand vibration\", &[])\n            .unwrap();\n        let right_vibration_action = action_set\n            .create_action(\"right_hand_vibration\", \"Right hand vibration\", &[])\n            .unwrap();\n\n        // Create action bindings:\n\n        for (id, action) in &button_actions {\n            let path = &BUTTON_INFO.get(id).unwrap().path;\n            match action {\n                ButtonAction::Binary(action) => {\n                    bindings.push(binding(action, path));\n                }\n                ButtonAction::Scalar(action) => {\n                    bindings.push(binding(action, path));\n                }\n            }\n        }\n\n        bindings.push(binding(\n            &left_grip_action,\n            \"/user/hand/left/input/grip/pose\",\n        ));\n        bindings.push(binding(\n            &right_grip_action,\n            \"/user/hand/right/input/grip/pose\",\n        ));\n\n        bindings.push(binding(&left_aim_action, \"/user/hand/left/input/aim/pose\"));\n        bindings.push(binding(\n            &right_aim_action,\n            \"/user/hand/right/input/aim/pose\",\n        ));\n\n        bindings.push(binding(\n            &left_vibration_action,\n            \"/user/hand/left/output/haptic\",\n        ));\n        bindings.push(binding(\n            &right_vibration_action,\n            \"/user/hand/right/output/haptic\",\n        ));\n\n        let multimodal_handle = check_ext_object(\n            \"MultimodalMeta\",\n            MultimodalMeta::new(xr_session.clone(), &extra_extensions, xr_system),\n        );\n\n        let mut left_detached_grip_action = None;\n        let mut right_detached_grip_action = None;\n        if multimodal_handle.is_some() {\n            // Note: when multimodal input is enabled, both controllers and hands will always be\n            // active. Held controllers and detached controllers are sent to the server as separate\n            // devices.\n            let left_detached_grip_action = left_detached_grip_action.insert(\n                action_set\n                    .create_action::<xr::Posef>(\n                        \"left_detached_grip_pose\",\n                        \"Left detached grip pose\",\n                        &[],\n                    )\n                    .unwrap(),\n            );\n            let right_detached_grip_action = right_detached_grip_action.insert(\n                action_set\n                    .create_action::<xr::Posef>(\n                        \"right_detached_grip_pose\",\n                        \"Right detached grip pose\",\n                        &[],\n                    )\n                    .unwrap(),\n            );\n\n            bindings.push(binding(\n                left_detached_grip_action,\n                \"/user/detached_controller_meta/left/input/grip/pose\",\n            ));\n            bindings.push(binding(\n                right_detached_grip_action,\n                \"/user/detached_controller_meta/right/input/grip/pose\",\n            ));\n        }\n\n        // Apply bindings:\n        xr_instance\n            .suggest_interaction_profile_bindings(\n                xr_instance\n                    .string_to_path(controllers_profile_path)\n                    .unwrap(),\n                &bindings,\n            )\n            .unwrap();\n\n        let left_grip_space = left_grip_action\n            .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n            .unwrap();\n        let right_grip_space = right_grip_action\n            .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n            .unwrap();\n\n        let left_aim_space = left_aim_action\n            .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n            .unwrap();\n        let right_aim_space = right_aim_action\n            .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n            .unwrap();\n\n        let left_detached_grip_space = left_detached_grip_action.as_ref().map(|action| {\n            action\n                .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n                .unwrap()\n        });\n        let right_detached_grip_space = right_detached_grip_action.as_ref().map(|action| {\n            action\n                .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n                .unwrap()\n        });\n\n        let left_hand_tracker = check_ext_object(\n            \"HandTracker (left)\",\n            xr_session.create_hand_tracker(xr::Hand::LEFT),\n        );\n        let right_hand_tracker = check_ext_object(\n            \"HandTracker (right)\",\n            xr_session.create_hand_tracker(xr::Hand::RIGHT),\n        );\n\n        let eyes_combined =\n            if extra_extensions::supports_eye_gaze_interaction(&xr_session, xr_system) {\n                if matches!(platform, Platform::QuestPro) {\n                    #[cfg(target_os = \"android\")]\n                    alvr_system_info::try_get_permission(\"com.oculus.permission.EYE_TRACKING\");\n                } else if matches!(\n                    platform,\n                    Platform::PicoNeo3 | Platform::Pico4Pro | Platform::Pico4Enterprise\n                ) {\n                    #[cfg(target_os = \"android\")]\n                    alvr_system_info::try_get_permission(\"com.picovr.permission.EYE_TRACKING\");\n                }\n\n                let action = action_set\n                    .create_action(\"combined_eye_gaze\", \"Combined eye gaze\", &[])\n                    .unwrap();\n\n                let res = xr_instance.suggest_interaction_profile_bindings(\n                    xr_instance\n                        .string_to_path(\"/interaction_profiles/ext/eye_gaze_interaction\")\n                        .unwrap(),\n                    &[binding(&action, \"/user/eyes_ext/input/gaze_ext/pose\")],\n                );\n                if res.is_err() {\n                    warn!(\"Failed to register combined eye gaze input: {res:?}\");\n                }\n\n                let space = action\n                    .create_space(&xr_session, xr::Path::NULL, xr::Posef::IDENTITY)\n                    .unwrap();\n\n                Some((action, space))\n            } else {\n                None\n            };\n\n        // Note: HTC facial tracking can only be created at startup before xrBeginSession. We don't\n        // know the reason.\n        let face_expressions_tracker = if platform.is_vive() {\n            let eye = check_ext_object(\n                \"FacialTrackerHTC (eyes)\",\n                FacialTrackerHTC::new(\n                    xr_session.clone(),\n                    xr_system,\n                    xr::FacialTrackingTypeHTC::EYE_DEFAULT,\n                ),\n            );\n            let lip = check_ext_object(\n                \"FacialTrackerHTC (lips)\",\n                FacialTrackerHTC::new(\n                    xr_session.clone(),\n                    xr_system,\n                    xr::FacialTrackingTypeHTC::LIP_DEFAULT,\n                ),\n            );\n            Some(FaceExpressionsTracker::Htc { eye, lip })\n        } else {\n            None\n        };\n\n        xr_session.attach_action_sets(&[&action_set]).unwrap();\n\n        Self {\n            xr_session,\n            xr_system,\n            extra_extensions,\n            platform,\n            action_set,\n            button_actions,\n            hands_interaction: [\n                HandInteraction {\n                    controllers_profile_id,\n                    input_ids: button_set.clone(),\n                    pose_offset: get_controller_offset(platform, false),\n                    grip_action: left_grip_action,\n                    grip_space: left_grip_space,\n                    aim_action: left_aim_action,\n                    aim_space: left_aim_space,\n                    detached_grip_action: left_detached_grip_action,\n                    detached_grip_space: left_detached_grip_space,\n                    vibration_action: left_vibration_action,\n                    skeleton_tracker: left_hand_tracker,\n                },\n                HandInteraction {\n                    controllers_profile_id,\n                    input_ids: button_set,\n                    pose_offset: get_controller_offset(platform, true),\n                    grip_action: right_grip_action,\n                    grip_space: right_grip_space,\n                    aim_action: right_aim_action,\n                    aim_space: right_aim_space,\n                    detached_grip_action: right_detached_grip_action,\n                    detached_grip_space: right_detached_grip_space,\n                    vibration_action: right_vibration_action,\n                    skeleton_tracker: right_hand_tracker,\n                },\n            ],\n            multimodal_handle,\n            multimodal_hands_enabled: false,\n            face_sources: FaceSources {\n                eyes_combined,\n                eyes_social: None,\n                face_expressions_tracker,\n            },\n            body_source: None,\n        }\n    }\n\n    pub fn select_sources(&mut self, config: &InteractionSourcesConfig) {\n        // First of all, disable/delete all sources. This ensures there are no conflicts\n        if let Some(handle) = &mut self.multimodal_handle {\n            handle.pause().ok();\n        }\n\n        if let Some(FaceExpressionsTracker::Pico(tracker)) =\n            &self.face_sources.face_expressions_tracker\n        {\n            tracker.stop_face_tracking().ok();\n        }\n\n        self.multimodal_hands_enabled = false;\n        self.face_sources.eyes_social = None;\n\n        // HTC trackers must not be destroyed or the app will crash\n        if !matches!(\n            self.face_sources.face_expressions_tracker,\n            Some(FaceExpressionsTracker::Htc { .. })\n        ) {\n            self.face_sources.face_expressions_tracker = None;\n        }\n\n        self.body_source = None;\n\n        if let Some(config) = &config.face_tracking {\n            if matches!(self.platform, Platform::QuestPro)\n                && matches!(config, FaceTrackingSourcesConfig::PreferFullFaceTracking)\n            {\n                #[cfg(target_os = \"android\")]\n                {\n                    alvr_system_info::try_get_permission(\"android.permission.RECORD_AUDIO\");\n                    alvr_system_info::try_get_permission(\"com.oculus.permission.FACE_TRACKING\")\n                }\n            }\n\n            if matches!(\n                self.platform,\n                Platform::PicoNeo3 | Platform::Pico4Pro | Platform::Pico4Enterprise\n            ) && matches!(config, FaceTrackingSourcesConfig::PreferFullFaceTracking)\n                && extra_extensions::supports_eye_gaze_interaction(&self.xr_session, self.xr_system)\n            {\n                #[cfg(target_os = \"android\")]\n                {\n                    alvr_system_info::try_get_permission(\"android.permission.RECORD_AUDIO\");\n                    alvr_system_info::try_get_permission(\"com.picovr.permission.FACE_TRACKING\")\n                }\n            }\n        }\n\n        if config.body_tracking.is_some()\n            && self.platform.is_quest()\n            && self.platform != Platform::Quest1\n        {\n            #[cfg(target_os = \"android\")]\n            alvr_system_info::try_get_permission(\"com.oculus.permission.BODY_TRACKING\")\n        }\n\n        // Note: We cannot enable multimodal if fb body tracking is active. It would result in a\n        // ERROR_RUNTIME_FAILURE crash.\n        if config.prefers_multimodal_input\n            && config.body_tracking.is_none()\n            && let Some(handle) = &mut self.multimodal_handle\n            && handle.resume().is_ok()\n        {\n            self.multimodal_hands_enabled = true;\n        }\n\n        if let Some(config) = &config.face_tracking {\n            // Note: this is actually used by multiple vendors\n            self.face_sources.eyes_social =\n                check_ext_object(\"EyeTrackerSocial\", EyeTrackerSocial::new(&self.xr_session));\n\n            if matches!(config, FaceTrackingSourcesConfig::PreferFullFaceTracking) {\n                if let Some(tracker) = check_ext_object(\n                    \"FaceTracker2FB\",\n                    FaceTracker2FB::new(self.xr_session.clone(), true, true),\n                ) {\n                    self.face_sources.face_expressions_tracker =\n                        Some(FaceExpressionsTracker::Fb(tracker))\n                } else if let Some(tracker) = check_ext_object(\n                    \"FaceTrackerPico\",\n                    FaceTrackerPico::new(self.xr_session.clone()),\n                ) {\n                    tracker.start_face_tracking().ok();\n\n                    self.face_sources.face_expressions_tracker =\n                        Some(FaceExpressionsTracker::Pico(tracker));\n                }\n                // For vive, face trackers are always created at startup regardless of settings, and\n                // also cannot be destroyed early.\n            }\n        }\n\n        if let Some(config) = &config.body_tracking {\n            if config.meta.prefer_full_body {\n                self.body_source = check_ext_object(\n                    \"BodyTrackerFB (full set)\",\n                    BodyTrackerFB::new(\n                        &self.xr_session,\n                        self.xr_system,\n                        *BODY_JOINT_SET_FULL_BODY_META,\n                        config.meta.prefer_high_fidelity,\n                    ),\n                )\n                .map(|tracker| BodyTracker::Fb {\n                    tracker,\n                    joint_count: FULL_BODY_JOINT_COUNT_META,\n                });\n            }\n            if self.body_source.is_none() {\n                self.body_source = check_ext_object(\n                    \"BodyTrackerFB (default set)\",\n                    BodyTrackerFB::new(\n                        &self.xr_session,\n                        self.xr_system,\n                        xr::BodyJointSetFB::DEFAULT,\n                        config.meta.prefer_high_fidelity,\n                    ),\n                )\n                .map(|tracker| BodyTracker::Fb {\n                    tracker,\n                    joint_count: xr::BodyJointFB::COUNT.into_raw() as usize,\n                });\n            }\n            if self.body_source.is_none() {\n                match config.bd {\n                    BodyTrackingBDConfig::BodyTracking {\n                        high_accuracy,\n                        prompt_calibration_on_start,\n                    } => {\n                        if high_accuracy {\n                            self.body_source = check_ext_object(\n                                \"BodyTrackerBD (high accuracy)\",\n                                BodyTrackerBD::new(\n                                    self.xr_session.clone(),\n                                    BodyJointSetBD::FULL_BODY_JOINTS,\n                                    &self.extra_extensions,\n                                    self.xr_system,\n                                    prompt_calibration_on_start,\n                                ),\n                            )\n                            .map(BodyTracker::BodyBD);\n                        }\n                        if self.body_source.is_none() {\n                            self.body_source = check_ext_object(\n                                \"BodyTrackerBD (low accuracy)\",\n                                BodyTrackerBD::new(\n                                    self.xr_session.clone(),\n                                    BodyJointSetBD::BODY_WITHOUT_ARM,\n                                    &self.extra_extensions,\n                                    self.xr_system,\n                                    prompt_calibration_on_start,\n                                ),\n                            )\n                            .map(BodyTracker::BodyBD);\n                        }\n                    }\n                    BodyTrackingBDConfig::ObjectTracking => {\n                        self.body_source = check_ext_object(\n                            \"MotionTrackerBD (object tracking)\",\n                            MotionTrackerBD::new(self.xr_session.clone(), &self.extra_extensions),\n                        )\n                        .map(BodyTracker::MotionBD);\n                    }\n                }\n            }\n        }\n    }\n}\n\npub fn get_reference_space(\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    ty: xr::ReferenceSpaceType,\n) -> xr::Space {\n    xr_session\n        .create_reference_space(ty, xr::Posef::IDENTITY)\n        .unwrap()\n}\n\npub fn get_head_data(\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    platform: Platform,\n    stage_reference_space: &xr::Space,\n    view_reference_space: &xr::Space,\n    time: Duration,\n    future_time: Duration,\n    last_view_params: &[ViewParams; 2],\n) -> Option<(DeviceMotion, Option<[ViewParams; 2]>)> {\n    let xr_time = crate::to_xr_time(time);\n\n    let (head_location, head_velocity) = view_reference_space\n        .relate(stage_reference_space, xr_time)\n        .ok()?;\n\n    if !head_location\n        .location_flags\n        .contains(xr::SpaceLocationFlags::ORIENTATION_VALID)\n    {\n        return None;\n    }\n\n    let (view_flags, views) = xr_session\n        .locate_views(\n            xr::ViewConfigurationType::PRIMARY_STEREO,\n            xr_time,\n            stage_reference_space,\n        )\n        .ok()?;\n\n    if !view_flags.contains(xr::ViewStateFlags::POSITION_VALID)\n        || !view_flags.contains(xr::ViewStateFlags::ORIENTATION_VALID)\n    {\n        return None;\n    }\n\n    let mut motion = DeviceMotion {\n        pose: crate::from_xr_pose(head_location.pose),\n        linear_velocity: if head_velocity\n            .velocity_flags\n            .contains(xr::SpaceVelocityFlags::LINEAR_VALID)\n        {\n            crate::from_xr_vec3(head_velocity.linear_velocity)\n        } else {\n            Vec3::ZERO\n        },\n        angular_velocity: if head_velocity\n            .velocity_flags\n            .contains(xr::SpaceVelocityFlags::ANGULAR_VALID)\n        {\n            crate::from_xr_vec3(head_velocity.angular_velocity)\n        } else {\n            Vec3::ZERO\n        },\n    };\n\n    // Some headsets use wrong frame of reference for linear and angular velocities.\n    if platform.is_pico() || platform.is_vive() || platform.is_yvr() {\n        let xr_future_time = crate::to_xr_time(future_time);\n\n        let predicted_location = view_reference_space\n            .locate(stage_reference_space, xr_future_time)\n            .ok()?;\n\n        if !predicted_location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::ORIENTATION_VALID)\n        {\n            return None;\n        }\n\n        let time_offset = future_time.saturating_sub(time);\n\n        if !time_offset.is_zero() {\n            let time_offset_s = time_offset.as_secs_f32();\n\n            motion.linear_velocity = (crate::from_xr_vec3(predicted_location.pose.position)\n                - motion.pose.position)\n                / time_offset_s;\n            motion.angular_velocity = (crate::from_xr_quat(predicted_location.pose.orientation)\n                * motion.pose.orientation.inverse())\n            .to_scaled_axis()\n                / time_offset_s;\n        }\n    }\n\n    let last_ipd_m = last_view_params[0]\n        .pose\n        .position\n        .distance(last_view_params[1].pose.position);\n    let current_ipd_m = crate::from_xr_vec3(views[1].pose.position)\n        .distance(crate::from_xr_vec3(views[0].pose.position));\n    let view_params = if f32::abs(current_ipd_m - last_ipd_m) > IPD_CHANGE_EPS {\n        Some([\n            ViewParams {\n                pose: motion.pose.inverse() * crate::from_xr_pose(views[0].pose),\n                fov: crate::from_xr_fov(views[0].fov),\n            },\n            ViewParams {\n                pose: motion.pose.inverse() * crate::from_xr_pose(views[1].pose),\n                fov: crate::from_xr_fov(views[1].fov),\n            },\n        ])\n    } else {\n        None\n    };\n\n    Some((motion, view_params))\n}\n\n#[expect(clippy::too_many_arguments)]\npub fn get_hand_data(\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    platform: Platform,\n    reference_space: &xr::Space,\n    time: Duration,\n    future_time: Duration,\n    hand_source: &HandInteraction,\n    last_controller_pose: &mut Pose,\n    last_palm_pose: &mut Pose,\n) -> HandData {\n    let xr_time = crate::to_xr_time(time);\n    let xr_now = crate::xr_runtime_now(xr_session.instance()).unwrap_or(xr_time);\n\n    let grip_motion = if hand_source\n        .grip_action\n        .is_active(xr_session, xr::Path::NULL)\n        .unwrap_or(false)\n        && let Ok((location, velocity)) = hand_source.grip_space.relate(reference_space, xr_time)\n    {\n        let orientation_valid = location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::ORIENTATION_VALID);\n        let position_valid = location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::POSITION_VALID);\n\n        if orientation_valid {\n            last_controller_pose.orientation = crate::from_xr_quat(location.pose.orientation);\n        }\n\n        if position_valid {\n            last_controller_pose.position = crate::from_xr_vec3(location.pose.position);\n        }\n\n        let pose = *last_controller_pose * hand_source.pose_offset;\n\n        let mut linear_velocity = crate::from_xr_vec3(velocity.linear_velocity);\n        let mut angular_velocity = crate::from_xr_vec3(velocity.angular_velocity);\n\n        let time_offset = future_time.saturating_sub(time);\n\n        // Some headsets use wrong frame of reference for linear and angular velocities.\n        if (platform.is_pico() || platform.is_vive())\n            && !time_offset.is_zero()\n            && let Ok(future_location) = hand_source\n                .grip_space\n                .locate(reference_space, crate::to_xr_time(future_time))\n            && future_location.location_flags.contains(\n                xr::SpaceLocationFlags::ORIENTATION_VALID | xr::SpaceLocationFlags::POSITION_VALID,\n            )\n        {\n            let time_offset_s = time_offset.as_secs_f32();\n\n            linear_velocity = (crate::from_xr_vec3(future_location.pose.position)\n                - last_controller_pose.position)\n                / time_offset_s;\n            angular_velocity = (crate::from_xr_quat(future_location.pose.orientation)\n                * last_controller_pose.orientation.inverse())\n            .to_scaled_axis()\n                / time_offset_s;\n        }\n\n        Some(DeviceMotion {\n            pose,\n            linear_velocity,\n            angular_velocity,\n        })\n    } else {\n        None\n    };\n\n    let detached_grip_motion = if let Some(detached_grip_action) = &hand_source.detached_grip_action\n        && detached_grip_action\n            .is_active(xr_session, xr::Path::NULL)\n            .unwrap_or(false)\n        && let Ok((location, velocity)) = hand_source\n            .detached_grip_space\n            .as_ref()\n            .unwrap()\n            .relate(reference_space, xr_time)\n    {\n        if location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::ORIENTATION_VALID)\n        {\n            last_controller_pose.orientation = crate::from_xr_quat(location.pose.orientation);\n        }\n\n        if location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::POSITION_VALID)\n        {\n            last_controller_pose.position = crate::from_xr_vec3(location.pose.position);\n        }\n\n        Some(DeviceMotion {\n            pose: *last_controller_pose,\n            linear_velocity: crate::from_xr_vec3(velocity.linear_velocity),\n            angular_velocity: crate::from_xr_vec3(velocity.angular_velocity),\n        })\n    } else {\n        None\n    };\n\n    let skeleton_joints = if let Some(tracker) = &hand_source.skeleton_tracker\n        && let Some(joint_locations) = reference_space\n            .locate_hand_joints(tracker, xr_now)\n            .ok()\n            .flatten()\n    {\n        if joint_locations[0]\n            .location_flags\n            .contains(xr::SpaceLocationFlags::ORIENTATION_VALID)\n        {\n            last_palm_pose.orientation = crate::from_xr_quat(joint_locations[0].pose.orientation);\n        }\n\n        if joint_locations[0]\n            .location_flags\n            .contains(xr::SpaceLocationFlags::POSITION_VALID)\n        {\n            last_palm_pose.position = crate::from_xr_vec3(joint_locations[0].pose.position);\n        }\n\n        let mut joints: [_; 26] = joint_locations\n            .iter()\n            .map(|j| crate::from_xr_pose(j.pose))\n            .collect::<Vec<_>>()\n            .try_into()\n            .unwrap();\n\n        joints[0] = *last_palm_pose;\n\n        Some(joints)\n    } else {\n        None\n    };\n\n    HandData {\n        grip_motion,\n        detached_grip_motion,\n        skeleton_joints,\n    }\n}\n\npub fn update_buttons(\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    button_actions: &HashMap<u64, ButtonAction>,\n) -> Vec<ButtonEntry> {\n    let mut button_entries = Vec::with_capacity(2);\n    for (id, action) in button_actions {\n        match action {\n            ButtonAction::Binary(action) => {\n                let Ok(state) = action.state(xr_session, xr::Path::NULL) else {\n                    continue;\n                };\n\n                if state.changed_since_last_sync {\n                    button_entries.push(ButtonEntry {\n                        path_id: *id,\n                        value: ButtonValue::Binary(state.current_state),\n                    });\n                }\n            }\n            ButtonAction::Scalar(action) => {\n                let Ok(state) = action.state(xr_session, xr::Path::NULL) else {\n                    continue;\n                };\n\n                if state.changed_since_last_sync {\n                    button_entries.push(ButtonEntry {\n                        path_id: *id,\n                        value: ButtonValue::Scalar(state.current_state),\n                    });\n                }\n            }\n        }\n    }\n\n    button_entries\n}\n\n// Note: Using the headset view space in order to get heading-independent eye gazes\npub fn get_face_data(\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    sources: &FaceSources,\n    view_reference_space: &xr::Space,\n    time: Duration,\n) -> FaceData {\n    let xr_time = crate::to_xr_time(time);\n\n    let eyes_combined = if let Some((action, space)) = &sources.eyes_combined\n        && action\n            .is_active(xr_session, xr::Path::NULL)\n            .unwrap_or(false)\n        && let Ok(location) = space.locate(view_reference_space, xr_time)\n        && location\n            .location_flags\n            .contains(xr::SpaceLocationFlags::ORIENTATION_VALID)\n    {\n        Some(crate::from_xr_quat(location.pose.orientation))\n    } else {\n        None\n    };\n\n    let eyes_social = if let Some(tracker) = &sources.eyes_social\n        && let Ok(gazes) = tracker.get_eye_gazes(view_reference_space, xr_time)\n    {\n        [\n            gazes[0].map(|p| crate::from_xr_quat(p.orientation)),\n            gazes[1].map(|p| crate::from_xr_quat(p.orientation)),\n        ]\n    } else {\n        [None, None]\n    };\n\n    let face_expressions = if let Some(tracker) = &sources.face_expressions_tracker {\n        match tracker {\n            FaceExpressionsTracker::Fb(tracker) => tracker\n                .get_face_expression_weights(xr_time)\n                .ok()\n                .flatten()\n                .map(|weights| FaceExpressions::Fb(weights.into_iter().collect())),\n            FaceExpressionsTracker::Pico(face_tracker_pico) => face_tracker_pico\n                .get_face_tracking_data(xr_time)\n                .ok()\n                .flatten()\n                .map(|weights| FaceExpressions::Pico(weights.into_iter().collect())),\n            FaceExpressionsTracker::Htc { eye, lip } => {\n                let eye = eye\n                    .as_ref()\n                    .and_then(|tracker| tracker.get_facial_expressions(xr_time).ok().flatten());\n                let lip = lip\n                    .as_ref()\n                    .and_then(|tracker| tracker.get_facial_expressions(xr_time).ok().flatten());\n\n                Some(FaceExpressions::Htc { eye, lip })\n            }\n        }\n    } else {\n        None\n    };\n\n    FaceData {\n        eyes_combined,\n        eyes_social,\n        face_expressions,\n    }\n}\n\npub fn get_body_skeleton(\n    source: &BodyTracker,\n    reference_space: &xr::Space,\n    time: Duration,\n) -> Option<BodySkeleton> {\n    let xr_time = crate::to_xr_time(time);\n\n    let check_and_convert_pose = |pose, location_flags: &xr::SpaceLocationFlags| {\n        if location_flags\n            .contains(SpaceLocationFlags::ORIENTATION_VALID | SpaceLocationFlags::POSITION_VALID)\n        {\n            Some(crate::from_xr_pose(pose))\n        } else {\n            None\n        }\n    };\n\n    match source {\n        BodyTracker::Fb {\n            tracker,\n            joint_count,\n        } => {\n            if let Some(joints) = tracker\n                .locate_body_joints(xr_time, reference_space, *joint_count)\n                .ok()\n                .flatten()\n            {\n                let joints = joints\n                    .iter()\n                    .map(|joint| check_and_convert_pose(joint.pose, &joint.location_flags))\n                    .collect::<Vec<_>>();\n\n                Some(BodySkeleton::Fb(Box::new(BodySkeletonFb {\n                    upper_body: joints[..18].try_into().unwrap(),\n                    lower_body: (joints.len() >= 84).then(|| joints[70..84].try_into().unwrap()),\n                })))\n            } else {\n                None\n            }\n        }\n        BodyTracker::BodyBD(tracker) => {\n            if let Some(joints) = tracker\n                .locate_body_joints(xr_time, reference_space)\n                .ok()\n                .flatten()\n            {\n                let joints = joints\n                    .iter()\n                    .map(|joint| check_and_convert_pose(joint.pose, &joint.location_flags))\n                    .collect::<Vec<_>>();\n\n                Some(BodySkeleton::Bd(Box::new(BodySkeletonBd(\n                    joints.try_into().unwrap(),\n                ))))\n            } else {\n                None\n            }\n        }\n        // Motion trackers are polled separately\n        BodyTracker::MotionBD(_) => None,\n    }\n}\n\npub fn get_bd_motion_trackers(source: &BodyTracker, time: Duration) -> Vec<(u64, DeviceMotion)> {\n    let xr_time = crate::to_xr_time(time);\n\n    if let BodyTracker::MotionBD(tracker) = source\n        && let Some(mut trackers) = tracker.locate_motion_trackers(xr_time).ok().flatten()\n    {\n        let mut joints = Vec::<(u64, DeviceMotion)>::with_capacity(3);\n\n        let joints_ids = [\n            *GENERIC_TRACKER_1_ID,\n            *GENERIC_TRACKER_2_ID,\n            *GENERIC_TRACKER_3_ID,\n        ];\n\n        trackers.sort_by(|a, b| a.serial.cmp(&b.serial));\n\n        for (i, item) in trackers.iter().enumerate() {\n            joints.push((\n                joints_ids[i],\n                DeviceMotion {\n                    pose: crate::from_xr_pose(item.local_pose.pose),\n                    linear_velocity: crate::from_xr_vec3(item.local_pose.linear_velocity),\n                    angular_velocity: crate::from_xr_vec3(item.local_pose.angular_velocity),\n                },\n            ))\n        }\n\n        return joints;\n    }\n\n    Vec::new()\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/lib.rs",
    "content": "mod c_api;\nmod extra_extensions;\nmod graphics;\nmod interaction;\nmod lobby;\nmod passthrough;\nmod stream;\n\nuse crate::stream::ParsedStreamConfig;\nuse alvr_client_core::{ClientCapabilities, ClientCoreContext, ClientCoreEvent};\nuse alvr_common::{\n    Fov, HAND_LEFT_ID, Pose, debug, error,\n    glam::{Quat, UVec2, Vec3},\n    info,\n    parking_lot::RwLock,\n};\nuse alvr_graphics::GraphicsContext;\nuse alvr_session::{BodyTrackingBDConfig, BodyTrackingSourcesConfig, PerformanceLevel};\nuse alvr_system_info::Platform;\nuse extra_extensions::{\n    BD_BODY_TRACKING_EXTENSION_NAME, BD_MOTION_TRACKING_EXTENSION_NAME,\n    META_BODY_TRACKING_FIDELITY_EXTENSION_NAME, META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME,\n    META_DETACHED_CONTROLLERS_EXTENSION_NAME,\n    META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME, PICO_CONFIGURATION_EXTENSION_NAME,\n};\nuse interaction::{InteractionContext, InteractionSourcesConfig};\nuse lobby::Lobby;\nuse openxr as xr;\nuse passthrough::PassthroughLayer;\nuse std::{ffi::CStr, path::Path, rc::Rc, sync::Arc, thread, time::Duration};\nuse stream::StreamContext;\n\nfn from_xr_vec3(v: xr::Vector3f) -> Vec3 {\n    Vec3::new(v.x, v.y, v.z)\n}\n\nfn to_xr_vec3(v: Vec3) -> xr::Vector3f {\n    xr::Vector3f {\n        x: v.x,\n        y: v.y,\n        z: v.z,\n    }\n}\n\nfn from_xr_quat(q: xr::Quaternionf) -> Quat {\n    Quat::from_xyzw(q.x, q.y, q.z, q.w)\n}\n\nfn to_xr_quat(q: Quat) -> xr::Quaternionf {\n    xr::Quaternionf {\n        x: q.x,\n        y: q.y,\n        z: q.z,\n        w: q.w,\n    }\n}\n\nfn from_xr_pose(p: xr::Posef) -> Pose {\n    Pose {\n        orientation: from_xr_quat(p.orientation),\n        position: from_xr_vec3(p.position),\n    }\n}\n\nfn to_xr_pose(p: Pose) -> xr::Posef {\n    xr::Posef {\n        orientation: to_xr_quat(p.orientation),\n        position: to_xr_vec3(p.position),\n    }\n}\n\nfn from_xr_fov(f: xr::Fovf) -> Fov {\n    Fov {\n        left: f.angle_left,\n        right: f.angle_right,\n        up: f.angle_up,\n        down: f.angle_down,\n    }\n}\n\nfn to_xr_fov(f: Fov) -> xr::Fovf {\n    xr::Fovf {\n        angle_left: f.left,\n        angle_right: f.right,\n        angle_up: f.up,\n        angle_down: f.down,\n    }\n}\n\nfn from_xr_time(timestamp: xr::Time) -> Duration {\n    Duration::from_nanos(timestamp.as_nanos() as _)\n}\n\nfn to_xr_time(timestamp: Duration) -> xr::Time {\n    xr::Time::from_nanos(timestamp.as_nanos() as _)\n}\n\nfn to_perf_settings_level(level: PerformanceLevel) -> xr::PerfSettingsLevelEXT {\n    match level {\n        PerformanceLevel::PowerSavings => xr::PerfSettingsLevelEXT::POWER_SAVINGS,\n        PerformanceLevel::SustainedLow => xr::PerfSettingsLevelEXT::SUSTAINED_LOW,\n        PerformanceLevel::SustainedHigh => xr::PerfSettingsLevelEXT::SUSTAINED_HIGH,\n        PerformanceLevel::Boost => xr::PerfSettingsLevelEXT::BOOST,\n    }\n}\n\nfn set_performance_level(\n    xr_instance: &xr::Instance,\n    xr_session: &xr::Session<xr::OpenGlEs>,\n    domain: xr::PerfSettingsDomainEXT,\n    level: PerformanceLevel,\n) {\n    if let Some(performance_settings) = xr_instance.exts().ext_performance_settings {\n        unsafe {\n            (performance_settings.perf_settings_set_performance_level)(\n                xr_session.as_raw(),\n                domain,\n                to_perf_settings_level(level),\n            );\n        }\n    }\n}\n\nfn default_view() -> xr::View {\n    xr::View {\n        pose: xr::Posef {\n            orientation: xr::Quaternionf {\n                x: 0.0,\n                y: 0.0,\n                z: 0.0,\n                w: 1.0,\n            },\n            position: xr::Vector3f::default(),\n        },\n        fov: xr::Fovf {\n            angle_left: -1.0,\n            angle_right: 1.0,\n            angle_up: 1.0,\n            angle_down: -1.0,\n        },\n    }\n}\n\n// This exists to circumvent dead-code analysis\nfn create_session(\n    xr_instance: &xr::Instance,\n    xr_system: xr::SystemId,\n    graphics_context: &GraphicsContext,\n) -> (\n    xr::Session<xr::OpenGlEs>,\n    xr::FrameWaiter,\n    xr::FrameStream<xr::OpenGlEs>,\n) {\n    #[allow(unreachable_code)]\n    unsafe {\n        xr_instance\n            .create_session(xr_system, &graphics::session_create_info(graphics_context))\n            .unwrap()\n    }\n}\n\npub fn entry_point() {\n    alvr_client_core::init_logging();\n\n    const LEGACY_OPENXR_VERSION: xr::Version = xr::Version::new(1, 0, 34);\n    const CURRENT_OPENXR_VERSION: xr::Version = xr::Version::new(1, 1, 36);\n\n    // Using a provisional platform, before we can get the runtime info\n    let (loader_suffix, openxr_version) = match alvr_system_info::platform(None, None) {\n        Platform::Quest1 => (\"_quest1\", LEGACY_OPENXR_VERSION),\n        Platform::PicoNeo3\n        | Platform::PicoG3\n        | Platform::Pico4\n        | Platform::Pico4Pro\n        | Platform::Pico4Enterprise => (\"_pico_old\", LEGACY_OPENXR_VERSION),\n        p if p.is_vive() => (\"\", LEGACY_OPENXR_VERSION),\n        p if p.is_yvr() => (\"_yvr\", LEGACY_OPENXR_VERSION),\n        Platform::Lynx => (\"_lynx\", LEGACY_OPENXR_VERSION),\n        _ => (\"\", CURRENT_OPENXR_VERSION),\n    };\n    let xr_entry = unsafe {\n        xr::Entry::load_from(Path::new(&format!(\"libopenxr_loader{loader_suffix}.so\"))).unwrap()\n    };\n\n    #[cfg(target_os = \"android\")]\n    xr_entry.initialize_android_loader().unwrap();\n\n    let available_extensions = xr_entry.enumerate_extensions().unwrap();\n    info!(\"OpenXR available extensions: {available_extensions:#?}\");\n    info!(\n        \"Extra available extensions: {:#?}\",\n        available_extensions\n            .other\n            .iter()\n            .map(|vec| CStr::from_bytes_with_nul(vec)\n                .unwrap()\n                .to_str()\n                .unwrap()\n                .to_owned())\n            .collect::<Vec<_>>()\n    );\n\n    // todo: switch to vulkan\n    assert!(available_extensions.khr_opengl_es_enable);\n\n    let mut exts = xr::ExtensionSet::default();\n    exts.bd_controller_interaction = available_extensions.bd_controller_interaction;\n    exts.ext_eye_gaze_interaction = available_extensions.ext_eye_gaze_interaction;\n    exts.ext_hand_tracking = available_extensions.ext_hand_tracking;\n    exts.ext_local_floor = available_extensions.ext_local_floor;\n    exts.ext_performance_settings = available_extensions.ext_performance_settings;\n    exts.ext_user_presence = available_extensions.ext_user_presence;\n    exts.fb_body_tracking = available_extensions.fb_body_tracking;\n    exts.fb_color_space = available_extensions.fb_color_space;\n    exts.fb_composition_layer_settings = available_extensions.fb_composition_layer_settings;\n    exts.fb_display_refresh_rate = available_extensions.fb_display_refresh_rate;\n    exts.fb_eye_tracking_social = available_extensions.fb_eye_tracking_social;\n    exts.fb_face_tracking2 = available_extensions.fb_face_tracking2;\n    exts.fb_foveation = available_extensions.fb_foveation;\n    exts.fb_foveation_configuration = available_extensions.fb_foveation_configuration;\n    exts.fb_passthrough = available_extensions.fb_passthrough;\n    exts.fb_swapchain_update_state = available_extensions.fb_swapchain_update_state;\n    exts.htc_facial_tracking = available_extensions.htc_facial_tracking;\n    exts.htc_passthrough = available_extensions.htc_passthrough;\n    exts.htc_vive_focus3_controller_interaction =\n        available_extensions.htc_vive_focus3_controller_interaction;\n    #[cfg(target_os = \"android\")]\n    {\n        exts.khr_android_create_instance = true;\n    }\n    exts.khr_convert_timespec_time = true;\n    exts.khr_opengl_es_enable = true;\n    exts.other = available_extensions\n        .other\n        .into_iter()\n        .filter(|ext| {\n            [\n                META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME,\n                META_BODY_TRACKING_FIDELITY_EXTENSION_NAME,\n                META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME,\n                META_DETACHED_CONTROLLERS_EXTENSION_NAME,\n                BD_BODY_TRACKING_EXTENSION_NAME,\n                BD_MOTION_TRACKING_EXTENSION_NAME,\n                PICO_CONFIGURATION_EXTENSION_NAME,\n            ]\n            .contains(&CStr::from_bytes_with_nul(ext).unwrap().to_str().unwrap())\n        })\n        .collect();\n\n    let available_layers = xr_entry.enumerate_layers().unwrap();\n    info!(\"OpenXR available layers: {available_layers:#?}\");\n\n    let other_exts = exts\n        .other\n        .iter()\n        .map(|vec| {\n            CStr::from_bytes_with_nul(vec)\n                .unwrap()\n                .to_str()\n                .unwrap()\n                .to_owned()\n        })\n        .collect::<Vec<_>>();\n\n    let xr_instance = xr_entry\n        .create_instance(\n            &xr::ApplicationInfo {\n                application_name: \"ALVR Client\",\n                application_version: 0,\n                engine_name: \"ALVR\",\n                engine_version: 0,\n                api_version: openxr_version,\n            },\n            &exts,\n            &[],\n        )\n        .unwrap();\n\n    let platform = alvr_system_info::platform(\n        xr_instance\n            .properties()\n            .ok()\n            .map(|s| s.runtime_name.to_owned()),\n        xr_instance\n            .properties()\n            .ok()\n            .map(|s| s.runtime_version.into_raw()),\n    );\n\n    let graphics_context = Rc::new(GraphicsContext::new_gl());\n\n    let mut last_lobby_message = String::new();\n\n    'session_loop: loop {\n        let xr_system = xr_instance\n            .system(xr::FormFactor::HEAD_MOUNTED_DISPLAY)\n            .unwrap();\n\n        // mandatory call\n        let _ = xr_instance\n            .graphics_requirements::<xr::OpenGlEs>(xr_system)\n            .unwrap();\n\n        let (xr_session, mut xr_frame_waiter, mut xr_frame_stream) =\n            create_session(&xr_instance, xr_system, &graphics_context);\n\n        let views_config = xr_instance\n            .enumerate_view_configuration_views(\n                xr_system,\n                xr::ViewConfigurationType::PRIMARY_STEREO,\n            )\n            .unwrap();\n        assert_eq!(views_config.len(), 2);\n\n        let default_view_resolution = UVec2::new(\n            views_config[0].recommended_image_rect_width,\n            views_config[0].recommended_image_rect_height,\n        );\n\n        let max_view_resolution = UVec2::new(\n            views_config[0].max_image_rect_width,\n            views_config[0].max_image_rect_height,\n        );\n\n        let refresh_rates = if exts.fb_display_refresh_rate {\n            xr_session.enumerate_display_refresh_rates().unwrap()\n        } else {\n            vec![90.0]\n        };\n\n        if exts.fb_color_space {\n            xr_session\n                .set_color_space(xr::ColorSpaceFB::REC709)\n                .unwrap();\n        }\n\n        let capabilities = ClientCapabilities {\n            platform,\n            default_view_resolution,\n            max_view_resolution,\n            refresh_rates,\n            foveated_encoding: platform != Platform::Unknown,\n            encoder_high_profile: platform != Platform::Unknown,\n            encoder_10_bits: platform != Platform::Unknown,\n            encoder_av1: matches!(\n                platform,\n                Platform::Quest3 | Platform::Quest3S | Platform::Pico4Ultra\n            ),\n            prefer_10bit: false,\n            preferred_encoding_gamma: 1.0,\n            prefer_hdr: false,\n        };\n        let core_context = Arc::new(ClientCoreContext::new(capabilities));\n\n        let interaction_context = Arc::new(RwLock::new(InteractionContext::new(\n            xr_session.clone(),\n            other_exts.clone(),\n            xr_system,\n            platform,\n        )));\n\n        let mut lobby = Lobby::new(\n            xr_session.clone(),\n            Rc::clone(&graphics_context),\n            Arc::clone(&interaction_context),\n            platform,\n            UVec2::min(default_view_resolution * 2, max_view_resolution),\n            &last_lobby_message,\n        );\n\n        // For Meta/Quest enabling body tracking would disable multimodal input\n        let lobby_body_tracking_config = if platform.is_pico() {\n            Some(BodyTrackingSourcesConfig {\n                bd: BodyTrackingBDConfig::BodyTracking {\n                    high_accuracy: true,\n                    prompt_calibration_on_start: false,\n                },\n                meta: Default::default(),\n            })\n        } else {\n            None\n        };\n        let lobby_interaction_sources = InteractionSourcesConfig {\n            face_tracking: None,\n            body_tracking: lobby_body_tracking_config,\n            prefers_multimodal_input: true,\n        };\n        interaction_context\n            .write()\n            .select_sources(&lobby_interaction_sources);\n\n        let mut session_running = false;\n        let mut stream_context = None::<StreamContext>;\n        let mut passthrough_layer = None;\n\n        let mut event_storage = xr::EventDataBuffer::new();\n        let mut headset_is_worn = true;\n        'render_loop: loop {\n            while let Some(event) = xr_instance.poll_event(&mut event_storage).unwrap() {\n                match event {\n                    xr::Event::EventsLost(event) => {\n                        error!(\"OpenXR: lost {} events!\", event.lost_event_count());\n                    }\n                    xr::Event::InstanceLossPending(_) => break 'session_loop,\n                    xr::Event::SessionStateChanged(event) => match event.state() {\n                        xr::SessionState::READY => {\n                            xr_session\n                                .begin(xr::ViewConfigurationType::PRIMARY_STEREO)\n                                .unwrap();\n\n                            core_context.resume();\n\n                            passthrough_layer = PassthroughLayer::new(&xr_session, platform).ok();\n\n                            session_running = true;\n                        }\n                        xr::SessionState::STOPPING => {\n                            session_running = false;\n\n                            passthrough_layer = None;\n\n                            core_context.pause();\n\n                            xr_session.end().unwrap();\n                        }\n                        xr::SessionState::EXITING | xr::SessionState::LOSS_PENDING => {\n                            break 'render_loop;\n                        }\n                        _ => (),\n                    },\n                    xr::Event::ReferenceSpaceChangePending(event) => {\n                        info!(\n                            \"ReferenceSpaceChangePending type: {:?}\",\n                            event.reference_space_type()\n                        );\n\n                        lobby.update_reference_space();\n\n                        if let Some(stream) = &mut stream_context {\n                            stream.update_reference_space();\n                        }\n                    }\n                    xr::Event::PerfSettingsEXT(event) => {\n                        info!(\n                            \"Perf: from {:?} to {:?}, domain: {:?}/{:?}\",\n                            event.from_level(),\n                            event.to_level(),\n                            event.domain(),\n                            event.sub_domain(),\n                        );\n                    }\n                    xr::Event::InteractionProfileChanged(_)\n                    | xr::Event::PassthroughStateChangedFB(_) => {\n                        // todo\n                    }\n                    xr::Event::UserPresenceChangedEXT(event) => {\n                        debug!(\"user present: {:?}\", event.is_user_present());\n                        headset_is_worn = event.is_user_present();\n\n                        core_context.send_proximity_state(event.is_user_present());\n                    }\n                    xr::Event::Unknown => {\n                        // use event_storage.as_raw(), reinterpret as sys::BaseInStructure, get type\n                        // and then reinterpret as the event struct\n                    }\n                    _ => (),\n                }\n            }\n\n            if !session_running {\n                thread::sleep(Duration::from_millis(100));\n                continue;\n            }\n\n            while let Some(event) = core_context.poll_event() {\n                match event {\n                    ClientCoreEvent::UpdateHudMessage(message) => {\n                        last_lobby_message.clone_from(&message);\n                        lobby.update_hud_message(&message);\n                    }\n                    ClientCoreEvent::StreamingStarted(config) => {\n                        let config = ParsedStreamConfig::new(&config);\n\n                        let context = StreamContext::new(\n                            Arc::clone(&core_context),\n                            xr_session.clone(),\n                            Rc::clone(&graphics_context),\n                            Arc::clone(&interaction_context),\n                            config,\n                        );\n\n                        if !context.uses_passthrough() {\n                            passthrough_layer = None;\n                        }\n\n                        stream_context = Some(context);\n\n                        core_context.send_proximity_state(headset_is_worn);\n                    }\n                    ClientCoreEvent::StreamingStopped => {\n                        if passthrough_layer.is_none() {\n                            passthrough_layer = PassthroughLayer::new(&xr_session, platform).ok();\n                        }\n\n                        interaction_context\n                            .write()\n                            .select_sources(&lobby_interaction_sources);\n\n                        stream_context = None;\n                    }\n                    ClientCoreEvent::Haptics {\n                        device_id,\n                        duration,\n                        frequency,\n                        amplitude,\n                    } => {\n                        let idx = if device_id == *HAND_LEFT_ID { 0 } else { 1 };\n                        let action =\n                            &interaction_context.read().hands_interaction[idx].vibration_action;\n\n                        action\n                            .apply_feedback(\n                                &xr_session,\n                                xr::Path::NULL,\n                                &xr::HapticVibration::new()\n                                    .amplitude(amplitude.clamp(0.0, 1.0))\n                                    .frequency(frequency.max(0.0))\n                                    .duration(xr::Duration::from_nanos(duration.as_nanos() as _)),\n                            )\n                            .unwrap();\n                    }\n                    ClientCoreEvent::DecoderConfig { codec, config_nal } => {\n                        if let Some(stream) = &mut stream_context {\n                            stream.maybe_initialize_decoder(codec, config_nal);\n                        }\n                    }\n                    ClientCoreEvent::RealTimeConfig(config) => {\n                        if config.passthrough.is_some() && passthrough_layer.is_none() {\n                            passthrough_layer = PassthroughLayer::new(&xr_session, platform).ok();\n                        } else if config.passthrough.is_none() && passthrough_layer.is_some() {\n                            passthrough_layer = None;\n                        }\n\n                        if let Some(cpu_performance_level) = &config.cpu_performance_level {\n                            set_performance_level(\n                                &xr_instance,\n                                &xr_session,\n                                xr::PerfSettingsDomainEXT::CPU,\n                                cpu_performance_level.clone(),\n                            );\n                        }\n\n                        if let Some(gpu_performance_level) = &config.gpu_performance_level {\n                            set_performance_level(\n                                &xr_instance,\n                                &xr_session,\n                                xr::PerfSettingsDomainEXT::GPU,\n                                gpu_performance_level.clone(),\n                            );\n                        }\n\n                        if let Some(stream) = &mut stream_context {\n                            stream.update_real_time_config(&config);\n                        }\n                    }\n                }\n            }\n\n            let frame_state = match xr_frame_waiter.wait() {\n                Ok(state) => state,\n                Err(e) => {\n                    error!(\"{e}\");\n                    panic!();\n                }\n            };\n            let frame_interval =\n                Duration::from_nanos(frame_state.predicted_display_period.as_nanos() as _);\n            let vsync_time =\n                Duration::from_nanos(frame_state.predicted_display_time.as_nanos() as _);\n\n            xr_frame_stream.begin().unwrap();\n\n            if !frame_state.should_render {\n                xr_frame_stream\n                    .end(\n                        frame_state.predicted_display_time,\n                        xr::EnvironmentBlendMode::OPAQUE,\n                        &[],\n                    )\n                    .unwrap();\n\n                continue;\n            }\n\n            // todo: allow rendering lobby and stream layers at the same time and add cross fade\n            let (layer, display_time) = if let Some(stream) = &mut stream_context {\n                stream.render(frame_interval, vsync_time)\n            } else {\n                (lobby.render(vsync_time), vsync_time)\n            };\n\n            let layers: &[&xr::CompositionLayerBase<_>] =\n                if let Some(passthrough_layer) = &passthrough_layer {\n                    &[passthrough_layer, &layer.build()]\n                } else {\n                    &[&layer.build()]\n                };\n\n            graphics_context.make_current();\n            let res = xr_frame_stream.end(\n                to_xr_time(display_time),\n                xr::EnvironmentBlendMode::OPAQUE,\n                layers,\n            );\n\n            if let Err(e) = res {\n                let time = to_xr_time(display_time);\n                error!(\"End frame failed! {e}, timestamp: {display_time:?}, time: {time:?}\");\n\n                if !platform.is_vive() {\n                    xr_frame_stream\n                        .end(\n                            frame_state.predicted_display_time,\n                            xr::EnvironmentBlendMode::OPAQUE,\n                            &[],\n                        )\n                        .unwrap();\n                }\n            }\n        }\n    }\n\n    // grapics_context is dropped here\n}\n\n#[allow(unused)]\nfn xr_runtime_now(xr_instance: &xr::Instance) -> Option<xr::Time> {\n    xr_instance\n        .now()\n        .ok()\n        .filter(|&time_nanos| time_nanos.as_nanos() > 0)\n}\n\n#[cfg(target_os = \"android\")]\n#[unsafe(no_mangle)]\nfn android_main(app: android_activity::AndroidApp) {\n    use android_activity::{InputStatus, MainEvent, PollEvent};\n\n    let rendering_thread = thread::spawn(|| {\n        // workaround for the Pico runtime\n        let context = ndk_context::android_context();\n        let vm = unsafe { jni::JavaVM::from_raw(context.vm().cast()) }.unwrap();\n        let _env = vm.attach_current_thread().unwrap();\n\n        entry_point();\n    });\n\n    let mut should_quit = false;\n    while !should_quit {\n        app.poll_events(Some(Duration::from_millis(100)), |event| match event {\n            PollEvent::Main(MainEvent::Destroy) => {\n                should_quit = true;\n            }\n            PollEvent::Main(MainEvent::InputAvailable) => {\n                if let Ok(mut iter) = app.input_events_iter() {\n                    while iter.next(|_| InputStatus::Unhandled) {}\n                }\n            }\n            _ => (),\n        });\n    }\n\n    // Note: the quit event is sent from OpenXR too, this will return rather quicly.\n    rendering_thread.join().unwrap();\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/lobby.rs",
    "content": "use crate::{\n    graphics::{self, ProjectionLayerAlphaConfig, ProjectionLayerBuilder},\n    interaction::{self, InteractionContext},\n};\nuse alvr_common::{Pose, ViewParams, glam::UVec2, parking_lot::RwLock};\nuse alvr_graphics::{GraphicsContext, LobbyRenderer, LobbyViewParams, SDR_FORMAT_GL};\nuse alvr_system_info::Platform;\nuse openxr as xr;\nuse std::{rc::Rc, sync::Arc, time::Duration};\n\n// todo: add interaction?\npub struct Lobby {\n    xr_session: xr::Session<xr::OpenGlEs>,\n    interaction_ctx: Arc<RwLock<InteractionContext>>,\n    platform: Platform,\n    reference_space: xr::Space,\n    swapchains: [xr::Swapchain<xr::OpenGlEs>; 2],\n    view_resolution: UVec2,\n    reference_space_type: xr::ReferenceSpaceType,\n    renderer: LobbyRenderer,\n}\n\nimpl Lobby {\n    pub fn new(\n        xr_session: xr::Session<xr::OpenGlEs>,\n        gfx_ctx: Rc<GraphicsContext>,\n        interaction_ctx: Arc<RwLock<InteractionContext>>,\n        platform: Platform,\n        view_resolution: UVec2,\n        initial_hud_message: &str,\n    ) -> Self {\n        let reference_space_type = if xr_session.instance().exts().ext_local_floor.is_some() {\n            xr::ReferenceSpaceType::LOCAL_FLOOR_EXT\n        } else {\n            // The Quest 1 doesn't support LOCAL_FLOOR_EXT, recentering is required for AppLab, but\n            // the Quest 1 is excluded from AppLab anyway.\n            xr::ReferenceSpaceType::STAGE\n        };\n\n        let reference_space = interaction::get_reference_space(&xr_session, reference_space_type);\n\n        let swapchains = [\n            graphics::create_swapchain(&xr_session, &gfx_ctx, view_resolution, SDR_FORMAT_GL, None),\n            graphics::create_swapchain(&xr_session, &gfx_ctx, view_resolution, SDR_FORMAT_GL, None),\n        ];\n\n        let renderer = LobbyRenderer::new(\n            gfx_ctx,\n            view_resolution,\n            [\n                swapchains[0]\n                    .enumerate_images()\n                    .unwrap()\n                    .iter()\n                    .map(|i| *i as _)\n                    .collect(),\n                swapchains[1]\n                    .enumerate_images()\n                    .unwrap()\n                    .iter()\n                    .map(|i| *i as _)\n                    .collect(),\n            ],\n            initial_hud_message,\n        );\n\n        Self {\n            xr_session,\n            interaction_ctx,\n            platform,\n            reference_space,\n            swapchains,\n            view_resolution,\n            reference_space_type,\n            renderer,\n        }\n    }\n\n    pub fn update_reference_space(&mut self) {\n        self.reference_space =\n            interaction::get_reference_space(&self.xr_session, self.reference_space_type);\n    }\n\n    pub fn update_hud_message(&self, message: &str) {\n        self.renderer.update_hud_message(message);\n    }\n\n    pub fn render(&mut self, vsync_time: Duration) -> ProjectionLayerBuilder<'_> {\n        let xr_vsync_time = crate::to_xr_time(vsync_time);\n\n        let (flags, maybe_views) = self\n            .xr_session\n            .locate_views(\n                xr::ViewConfigurationType::PRIMARY_STEREO,\n                xr_vsync_time,\n                &self.reference_space,\n            )\n            .unwrap();\n\n        let views = if flags.contains(xr::ViewStateFlags::ORIENTATION_VALID) {\n            maybe_views\n        } else {\n            vec![crate::default_view(), crate::default_view()]\n        };\n\n        self.xr_session\n            .sync_actions(&[(&self.interaction_ctx.read().action_set).into()])\n            .ok();\n\n        // future_time doesn't have to be any particular value, just something after vsync_time\n        let future_time = vsync_time + Duration::from_millis(80);\n        let left_hand_data = interaction::get_hand_data(\n            &self.xr_session,\n            self.platform,\n            &self.reference_space,\n            vsync_time,\n            future_time,\n            &self.interaction_ctx.read().hands_interaction[0],\n            &mut Pose::default(),\n            &mut Pose::default(),\n        );\n        let right_hand_data = interaction::get_hand_data(\n            &self.xr_session,\n            self.platform,\n            &self.reference_space,\n            vsync_time,\n            future_time,\n            &self.interaction_ctx.read().hands_interaction[1],\n            &mut Pose::default(),\n            &mut Pose::default(),\n        );\n\n        let additional_motions = self\n            .interaction_ctx\n            .read()\n            .body_source\n            .as_ref()\n            .map(|source| {\n                interaction::get_bd_motion_trackers(source, vsync_time)\n                    .iter()\n                    .map(|(_, motion)| *motion)\n                    .collect()\n            });\n\n        let body_skeleton = self\n            .interaction_ctx\n            .read()\n            .body_source\n            .as_ref()\n            .and_then(|source| {\n                interaction::get_body_skeleton(source, &self.reference_space, vsync_time)\n            });\n\n        let left_swapchain_idx = self.swapchains[0].acquire_image().unwrap();\n        let right_swapchain_idx = self.swapchains[1].acquire_image().unwrap();\n\n        self.swapchains[0]\n            .wait_image(xr::Duration::INFINITE)\n            .unwrap();\n        self.swapchains[1]\n            .wait_image(xr::Duration::INFINITE)\n            .unwrap();\n\n        self.renderer.render(\n            [\n                LobbyViewParams {\n                    view_params: ViewParams {\n                        pose: crate::from_xr_pose(views[0].pose),\n                        fov: crate::from_xr_fov(views[0].fov),\n                    },\n                    swapchain_index: left_swapchain_idx,\n                },\n                LobbyViewParams {\n                    view_params: ViewParams {\n                        pose: crate::from_xr_pose(views[1].pose),\n                        fov: crate::from_xr_fov(views[1].fov),\n                    },\n                    swapchain_index: right_swapchain_idx,\n                },\n            ],\n            [left_hand_data, right_hand_data],\n            body_skeleton,\n            additional_motions,\n            false,\n            cfg!(debug_assertions),\n        );\n\n        self.swapchains[0].release_image().unwrap();\n        self.swapchains[1].release_image().unwrap();\n\n        let rect = xr::Rect2Di {\n            offset: xr::Offset2Di { x: 0, y: 0 },\n            extent: xr::Extent2Di {\n                width: self.view_resolution.x as _,\n                height: self.view_resolution.y as _,\n            },\n        };\n\n        ProjectionLayerBuilder::new(\n            &self.reference_space,\n            [\n                xr::CompositionLayerProjectionView::new()\n                    .pose(views[0].pose)\n                    .fov(views[0].fov)\n                    .sub_image(\n                        xr::SwapchainSubImage::new()\n                            .swapchain(&self.swapchains[0])\n                            .image_array_index(0)\n                            .image_rect(rect),\n                    ),\n                xr::CompositionLayerProjectionView::new()\n                    .pose(views[1].pose)\n                    .fov(views[1].fov)\n                    .sub_image(\n                        xr::SwapchainSubImage::new()\n                            .swapchain(&self.swapchains[1])\n                            .image_array_index(0)\n                            .image_rect(rect),\n                    ),\n            ],\n            Some(ProjectionLayerAlphaConfig {\n                premultiplied: true,\n            }),\n            None,\n        )\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/passthrough.rs",
    "content": "use crate::extra_extensions::{PassthroughFB, PassthroughHTC};\nuse alvr_common::anyhow::{Result, bail};\nuse alvr_system_info::Platform;\nuse openxr::{self as xr};\nuse std::{marker::PhantomData, ops::Deref, ptr};\n\npub struct PassthroughLayer<'a> {\n    handle_fb: Option<PassthroughFB>,\n    handle_htc: Option<PassthroughHTC>,\n    _marker: PhantomData<&'a ()>,\n}\n\nimpl PassthroughLayer<'_> {\n    pub fn new(session: &xr::Session<xr::OpenGlEs>, platform: Platform) -> Result<Self> {\n        let mut handle_fb = None;\n        let mut handle_htc = None;\n\n        let exts = session.instance().exts();\n        if exts.fb_passthrough.is_some() {\n            handle_fb = Some(PassthroughFB::new(session, platform)?);\n        } else if exts.htc_passthrough.is_some() {\n            handle_htc = Some(PassthroughHTC::new(session)?);\n        } else {\n            bail!(\"No passthrough extension available\");\n        };\n\n        Ok(Self {\n            handle_fb,\n            handle_htc,\n            _marker: PhantomData,\n        })\n    }\n}\n\nimpl<'a> Deref for PassthroughLayer<'a> {\n    type Target = xr::CompositionLayerBase<'a, xr::OpenGlEs>;\n\n    fn deref(&self) -> &Self::Target {\n        if let Some(handle) = &self.handle_fb {\n            unsafe { &*ptr::from_ref(handle.layer()).cast() }\n        } else if let Some(handle) = &self.handle_htc {\n            unsafe { &*ptr::from_ref(handle.layer()).cast() }\n        } else {\n            panic!(\"No passthrough extension available\");\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/client_openxr/src/stream.rs",
    "content": "use crate::{\n    graphics::{self, ProjectionLayerAlphaConfig, ProjectionLayerBuilder},\n    interaction::{self, InteractionContext, InteractionSourcesConfig},\n};\nuse alvr_client_core::{\n    ClientCoreContext,\n    video_decoder::{self, VideoDecoderConfig, VideoDecoderSource},\n};\nuse alvr_common::{\n    DETACHED_CONTROLLER_LEFT_ID, DETACHED_CONTROLLER_RIGHT_ID, HAND_LEFT_ID, HAND_RIGHT_ID,\n    HEAD_ID, Pose, RelaxedAtomic, ViewParams,\n    anyhow::Result,\n    error,\n    glam::{UVec2, Vec2},\n    parking_lot::RwLock,\n};\nuse alvr_graphics::{GraphicsContext, StreamRenderer, StreamViewParams};\nuse alvr_packets::{RealTimeConfig, StreamConfig, TrackingData};\nuse alvr_session::{\n    ClientsideFoveationConfig, ClientsideFoveationMode, ClientsidePostProcessingConfig, CodecType,\n    FoveatedEncodingConfig, MediacodecProperty, PassthroughMode, UpscalingConfig,\n};\nuse alvr_system_info::Platform;\nuse openxr as xr;\nuse std::{\n    ptr,\n    rc::Rc,\n    sync::Arc,\n    thread::{self, JoinHandle},\n    time::{Duration, Instant},\n};\n\nconst DECODER_MAX_TIMEOUT_MULTIPLIER: f32 = 0.8;\n\npub struct ParsedStreamConfig {\n    pub view_resolution: UVec2,\n    pub refresh_rate_hint: f32,\n    pub encoding_gamma: f32,\n    pub enable_hdr: bool,\n    pub passthrough: Option<PassthroughMode>,\n    pub foveated_encoding_config: Option<FoveatedEncodingConfig>,\n    pub clientside_foveation_config: Option<ClientsideFoveationConfig>,\n    pub clientside_post_processing: Option<ClientsidePostProcessingConfig>,\n    pub upscaling: Option<UpscalingConfig>,\n    pub force_software_decoder: bool,\n    pub max_buffering_frames: f32,\n    pub buffering_history_weight: f32,\n    pub decoder_options: Vec<(String, MediacodecProperty)>,\n    pub interaction_sources: InteractionSourcesConfig,\n}\n\nimpl ParsedStreamConfig {\n    pub fn new(config: &StreamConfig) -> Self {\n        Self {\n            view_resolution: config.negotiated_config.view_resolution,\n            refresh_rate_hint: config.negotiated_config.refresh_rate_hint,\n            encoding_gamma: config.negotiated_config.encoding_gamma,\n            enable_hdr: config.negotiated_config.enable_hdr,\n            passthrough: config.settings.video.passthrough.as_option().cloned(),\n            foveated_encoding_config: config\n                .negotiated_config\n                .enable_foveated_encoding\n                .then(|| config.settings.video.foveated_encoding.as_option().cloned())\n                .flatten(),\n            clientside_foveation_config: config\n                .settings\n                .video\n                .clientside_foveation\n                .as_option()\n                .cloned(),\n            clientside_post_processing: config\n                .settings\n                .video\n                .clientside_post_processing\n                .as_option()\n                .cloned(),\n            upscaling: config.settings.video.upscaling.as_option().cloned(),\n            force_software_decoder: config.settings.video.force_software_decoder,\n            max_buffering_frames: config.settings.video.max_buffering_frames,\n            buffering_history_weight: config.settings.video.buffering_history_weight,\n            decoder_options: config.settings.video.mediacodec_extra_options.clone(),\n            interaction_sources: InteractionSourcesConfig::new(config),\n        }\n    }\n}\n\npub struct StreamContext {\n    core_context: Arc<ClientCoreContext>,\n    xr_session: xr::Session<xr::OpenGlEs>,\n    interaction_context: Arc<RwLock<InteractionContext>>,\n    stage_reference_space: Arc<xr::Space>,\n    view_reference_space: Arc<xr::Space>,\n    swapchains: [xr::Swapchain<xr::OpenGlEs>; 2],\n    last_good_view_params: [ViewParams; 2],\n    input_thread: Option<JoinHandle<()>>,\n    input_thread_running: Arc<RelaxedAtomic>,\n    config: ParsedStreamConfig,\n    target_view_resolution: UVec2,\n    renderer: StreamRenderer,\n    decoder: Option<(VideoDecoderConfig, VideoDecoderSource)>,\n    use_custom_reprojection: bool,\n}\n\nimpl StreamContext {\n    pub fn new(\n        core_ctx: Arc<ClientCoreContext>,\n        xr_session: xr::Session<xr::OpenGlEs>,\n        gfx_ctx: Rc<GraphicsContext>,\n        interaction_ctx: Arc<RwLock<InteractionContext>>,\n        config: ParsedStreamConfig,\n    ) -> StreamContext {\n        interaction_ctx\n            .write()\n            .select_sources(&config.interaction_sources);\n\n        let xr_exts = xr_session.instance().exts();\n\n        if xr_exts.fb_display_refresh_rate.is_some() {\n            xr_session\n                .request_display_refresh_rate(config.refresh_rate_hint)\n                .unwrap();\n        }\n\n        let foveation_profile = if let Some(config) = &config.clientside_foveation_config\n            && xr_exts.fb_swapchain_update_state.is_some()\n            && xr_exts.fb_foveation.is_some()\n            && xr_exts.fb_foveation_configuration.is_some()\n        {\n            let level;\n            let dynamic;\n            match config.mode {\n                ClientsideFoveationMode::Static { level: lvl } => {\n                    level = lvl;\n                    dynamic = false;\n                }\n                ClientsideFoveationMode::Dynamic { max_level } => {\n                    level = max_level;\n                    dynamic = true;\n                }\n            };\n\n            xr_session\n                .create_foveation_profile(Some(xr::FoveationLevelProfile {\n                    level: xr::FoveationLevelFB::from_raw(level as i32),\n                    vertical_offset: config.vertical_offset_deg,\n                    dynamic: xr::FoveationDynamicFB::from_raw(dynamic as i32),\n                }))\n                .ok()\n        } else {\n            None\n        };\n\n        let target_view_resolution = alvr_graphics::compute_target_view_resolution(\n            config.view_resolution,\n            &config.upscaling,\n        );\n        let format = graphics::swapchain_format(&gfx_ctx, &xr_session, config.enable_hdr);\n\n        let swapchains = [\n            graphics::create_swapchain(\n                &xr_session,\n                &gfx_ctx,\n                target_view_resolution,\n                format,\n                foveation_profile.as_ref(),\n            ),\n            graphics::create_swapchain(\n                &xr_session,\n                &gfx_ctx,\n                target_view_resolution,\n                format,\n                foveation_profile.as_ref(),\n            ),\n        ];\n\n        let renderer = StreamRenderer::new(\n            gfx_ctx,\n            config.view_resolution,\n            target_view_resolution,\n            [\n                swapchains[0]\n                    .enumerate_images()\n                    .unwrap()\n                    .iter()\n                    .map(|i| *i as _)\n                    .collect(),\n                swapchains[1]\n                    .enumerate_images()\n                    .unwrap()\n                    .iter()\n                    .map(|i| *i as _)\n                    .collect(),\n            ],\n            format,\n            config.foveated_encoding_config.clone(),\n            core_ctx.platform() != Platform::Lynx\n                && !((core_ctx.platform().is_pico()\n                    || (core_ctx.platform() == Platform::SamsungGalaxyXR))\n                    && config.enable_hdr),\n            // TODO: Find a driver heuristic for the limited range bug instead?\n            core_ctx.platform() != Platform::SamsungGalaxyXR && !config.enable_hdr,\n            config.encoding_gamma,\n            config.upscaling.clone(),\n        );\n\n        {\n            let int_ctx = interaction_ctx.read();\n            core_ctx.send_active_interaction_profile(\n                *HAND_LEFT_ID,\n                int_ctx.hands_interaction[0].controllers_profile_id,\n                int_ctx.hands_interaction[0].input_ids.clone(),\n            );\n            core_ctx.send_active_interaction_profile(\n                *HAND_RIGHT_ID,\n                int_ctx.hands_interaction[1].controllers_profile_id,\n                int_ctx.hands_interaction[1].input_ids.clone(),\n            );\n        }\n\n        let input_thread_running = Arc::new(RelaxedAtomic::new(false));\n\n        let stage_reference_space = Arc::new(interaction::get_reference_space(\n            &xr_session,\n            xr::ReferenceSpaceType::STAGE,\n        ));\n        let view_reference_space = Arc::new(interaction::get_reference_space(\n            &xr_session,\n            xr::ReferenceSpaceType::VIEW,\n        ));\n\n        let mut this = StreamContext {\n            use_custom_reprojection: core_ctx.platform().is_yvr(),\n            core_context: core_ctx,\n            xr_session,\n            interaction_context: interaction_ctx,\n            stage_reference_space,\n            view_reference_space,\n            swapchains,\n            last_good_view_params: [ViewParams::DUMMY; 2],\n            input_thread: None,\n            input_thread_running,\n            config,\n            target_view_resolution,\n            renderer,\n            decoder: None,\n        };\n\n        this.update_reference_space();\n\n        this\n    }\n\n    pub fn uses_passthrough(&self) -> bool {\n        self.config.passthrough.is_some()\n    }\n\n    pub fn update_reference_space(&mut self) {\n        self.input_thread_running.set(false);\n\n        self.stage_reference_space = Arc::new(interaction::get_reference_space(\n            &self.xr_session,\n            xr::ReferenceSpaceType::STAGE,\n        ));\n        self.view_reference_space = Arc::new(interaction::get_reference_space(\n            &self.xr_session,\n            xr::ReferenceSpaceType::VIEW,\n        ));\n\n        self.core_context.send_playspace(\n            self.xr_session\n                .reference_space_bounds_rect(xr::ReferenceSpaceType::STAGE)\n                .unwrap()\n                .map(|a| Vec2::new(a.width, a.height)),\n        );\n\n        if let Some(running) = self.input_thread.take() {\n            running.join().ok();\n        }\n\n        self.input_thread_running.set(true);\n\n        self.input_thread = Some(thread::spawn({\n            let core_ctx = Arc::clone(&self.core_context);\n            let xr_session = self.xr_session.clone();\n            let interaction_ctx = Arc::clone(&self.interaction_context);\n            let stage_reference_space = Arc::clone(&self.stage_reference_space);\n            let view_reference_space = Arc::clone(&self.view_reference_space);\n            let refresh_rate = self.config.refresh_rate_hint;\n            let running = Arc::clone(&self.input_thread_running);\n            move || {\n                stream_input_loop(\n                    &core_ctx,\n                    xr_session,\n                    &interaction_ctx,\n                    &stage_reference_space,\n                    &view_reference_space,\n                    refresh_rate,\n                    running,\n                )\n            }\n        }));\n    }\n\n    pub fn maybe_initialize_decoder(&mut self, codec: CodecType, config_nal: Vec<u8>) {\n        let new_config = VideoDecoderConfig {\n            codec,\n            force_software_decoder: self.config.force_software_decoder,\n            max_buffering_frames: self.config.max_buffering_frames,\n            buffering_history_weight: self.config.buffering_history_weight,\n            options: self.config.decoder_options.clone(),\n            config_buffer: config_nal,\n        };\n\n        let maybe_config = if let Some((config, _)) = &self.decoder {\n            (new_config != *config).then_some(new_config)\n        } else {\n            Some(new_config)\n        };\n\n        if let Some(config) = maybe_config {\n            let (mut sink, source) = video_decoder::create_decoder(config.clone(), {\n                let ctx = Arc::clone(&self.core_context);\n                move |maybe_timestamp: Result<Duration>| match maybe_timestamp {\n                    Ok(timestamp) => ctx.report_frame_decoded(timestamp),\n                    Err(e) => ctx.report_fatal_decoder_error(&e.to_string()),\n                }\n            });\n            self.decoder = Some((config, source));\n\n            self.core_context.set_decoder_input_callback(Box::new(\n                move |timestamp, buffer| -> bool { sink.push_nal(timestamp, buffer) },\n            ));\n        }\n    }\n\n    pub fn update_real_time_config(&mut self, config: &RealTimeConfig) {\n        self.config.passthrough = config.passthrough.clone();\n        self.config.clientside_post_processing = config.clientside_post_processing.clone();\n    }\n\n    pub fn render(\n        &mut self,\n        frame_interval: Duration,\n        vsync_time: Duration,\n    ) -> (ProjectionLayerBuilder<'_>, Duration) {\n        let xr_vsync_time = xr::Time::from_nanos(vsync_time.as_nanos() as _);\n        let frame_poll_deadline = Instant::now()\n            + Duration::from_secs_f32(\n                frame_interval.as_secs_f32() * DECODER_MAX_TIMEOUT_MULTIPLIER,\n            );\n        let mut frame_result = None;\n        if let Some((_, source)) = &mut self.decoder {\n            while frame_result.is_none() && Instant::now() < frame_poll_deadline {\n                frame_result = source.get_frame();\n                thread::sleep(Duration::from_micros(500));\n            }\n        }\n\n        let (timestamp, view_params, buffer_ptr) =\n            if let Some((timestamp, buffer_ptr)) = frame_result {\n                let view_params = self.core_context.report_compositor_start(timestamp);\n\n                self.last_good_view_params = view_params;\n\n                (timestamp, view_params, buffer_ptr)\n            } else {\n                (vsync_time, self.last_good_view_params, ptr::null_mut())\n            };\n\n        let left_swapchain_idx = self.swapchains[0].acquire_image().unwrap();\n        let right_swapchain_idx = self.swapchains[1].acquire_image().unwrap();\n\n        self.swapchains[0]\n            .wait_image(xr::Duration::INFINITE)\n            .unwrap();\n        self.swapchains[1]\n            .wait_image(xr::Duration::INFINITE)\n            .unwrap();\n\n        let (flags, maybe_views) = self\n            .xr_session\n            .locate_views(\n                xr::ViewConfigurationType::PRIMARY_STEREO,\n                xr_vsync_time,\n                &self.stage_reference_space,\n            )\n            .unwrap();\n\n        let current_headset_views = if flags.contains(xr::ViewStateFlags::ORIENTATION_VALID) {\n            maybe_views\n        } else {\n            vec![crate::default_view(), crate::default_view()]\n        };\n\n        // The poses and FoVs we received from the PC runtime, which may differ and/or include\n        // altered FoVs based on settings and view conversions done for canting.\n        let input_view_params = view_params;\n        let mut output_view_params = input_view_params;\n        // Avoid passing invalid timestamp to runtime.\n        // `timestamp` is generally a current vsync time, but may be repeated if frames are\n        // dropped. Some runtimes dislike it if the timestamp is repeated for too long, so after\n        // one second we begin presenting a lagged vsync time instead.\n        let mut openxr_display_time =\n            Duration::max(timestamp, vsync_time.saturating_sub(Duration::from_secs(1)));\n\n        // (shinyquagsire23) I don't entirely trust runtimes to implement CompositionLayerProjectionView\n        // correctly, but if we do trust them, avoid doing rotation ourselves. Otherwise, rerender.\n        // Ex: YVR/PFDMR has issues with aspect ratio mismatches and passthrough compositing.\n        if self.use_custom_reprojection {\n            output_view_params = [\n                ViewParams {\n                    pose: crate::from_xr_pose(current_headset_views[0].pose),\n                    fov: crate::from_xr_fov(current_headset_views[0].fov),\n                },\n                ViewParams {\n                    pose: crate::from_xr_pose(current_headset_views[1].pose),\n                    fov: crate::from_xr_fov(current_headset_views[1].fov),\n                },\n            ];\n\n            openxr_display_time = vsync_time;\n        }\n\n        self.renderer.render(\n            buffer_ptr,\n            [\n                StreamViewParams {\n                    swapchain_index: left_swapchain_idx,\n                    input_view_params: input_view_params[0],\n                    output_view_params: output_view_params[0],\n                },\n                StreamViewParams {\n                    swapchain_index: right_swapchain_idx,\n                    input_view_params: input_view_params[1],\n                    output_view_params: output_view_params[1],\n                },\n            ],\n            self.config.passthrough.as_ref(),\n        );\n\n        self.swapchains[0].release_image().unwrap();\n        self.swapchains[1].release_image().unwrap();\n\n        if !buffer_ptr.is_null()\n            && let Some(xr_now) = crate::xr_runtime_now(self.xr_session.instance())\n        {\n            self.core_context.report_submit(\n                timestamp,\n                vsync_time.saturating_sub(Duration::from_nanos(xr_now.as_nanos() as u64)),\n            );\n        }\n\n        let rect = xr::Rect2Di {\n            offset: xr::Offset2Di { x: 0, y: 0 },\n            extent: xr::Extent2Di {\n                width: self.target_view_resolution.x as _,\n                height: self.target_view_resolution.y as _,\n            },\n        };\n\n        let clientside_post_processing = self\n            .xr_session\n            .instance()\n            .exts()\n            .fb_composition_layer_settings\n            .and(self.config.clientside_post_processing.clone());\n\n        let layer = ProjectionLayerBuilder::new(\n            &self.stage_reference_space,\n            [\n                xr::CompositionLayerProjectionView::new()\n                    .pose(crate::to_xr_pose(output_view_params[0].pose))\n                    .fov(crate::to_xr_fov(output_view_params[0].fov))\n                    .sub_image(\n                        xr::SwapchainSubImage::new()\n                            .swapchain(&self.swapchains[0])\n                            .image_array_index(0)\n                            .image_rect(rect),\n                    ),\n                xr::CompositionLayerProjectionView::new()\n                    .pose(crate::to_xr_pose(output_view_params[1].pose))\n                    .fov(crate::to_xr_fov(output_view_params[1].fov))\n                    .sub_image(\n                        xr::SwapchainSubImage::new()\n                            .swapchain(&self.swapchains[1])\n                            .image_array_index(0)\n                            .image_rect(rect),\n                    ),\n            ],\n            self.config\n                .passthrough\n                .clone()\n                .map(|mode| ProjectionLayerAlphaConfig {\n                    premultiplied: matches!(\n                        mode,\n                        PassthroughMode::Blend {\n                            premultiplied_alpha: true,\n                            ..\n                        } | PassthroughMode::RgbChromaKey(_)\n                            | PassthroughMode::HsvChromaKey(_)\n                    ),\n                }),\n            clientside_post_processing,\n        );\n\n        (layer, openxr_display_time)\n    }\n}\n\nimpl Drop for StreamContext {\n    fn drop(&mut self) {\n        self.input_thread_running.set(false);\n        self.input_thread.take().unwrap().join().ok();\n    }\n}\n\nfn stream_input_loop(\n    core_ctx: &ClientCoreContext,\n    xr_session: xr::Session<xr::OpenGlEs>,\n    interaction_ctx: &RwLock<InteractionContext>,\n    stage_reference_space: &xr::Space,\n    view_reference_space: &xr::Space,\n    refresh_rate: f32,\n    running: Arc<RelaxedAtomic>,\n) {\n    let mut last_controller_poses = [Pose::IDENTITY; 2];\n    let mut last_palm_poses = [Pose::IDENTITY; 2];\n    let mut last_view_params = [ViewParams::DUMMY; 2];\n\n    let mut deadline = Instant::now();\n    let frame_interval = Duration::from_secs_f32(1.0 / refresh_rate);\n    while running.value() {\n        let int_ctx = &*interaction_ctx.read();\n        // Streaming related inputs are updated here. Make sure every input poll is done in this\n        // thread\n        if let Err(e) = xr_session.sync_actions(&[(&int_ctx.action_set).into()]) {\n            error!(\"{e}\");\n            return;\n        }\n\n        let Some(now) = crate::xr_runtime_now(xr_session.instance()).map(crate::from_xr_time)\n        else {\n            error!(\"Cannot poll tracking: invalid time\");\n            return;\n        };\n\n        let target_time = now + core_ctx.get_total_prediction_offset();\n\n        let Some((head_motion, local_views)) = interaction::get_head_data(\n            &xr_session,\n            core_ctx.platform(),\n            stage_reference_space,\n            view_reference_space,\n            now,\n            target_time,\n            &last_view_params,\n        ) else {\n            continue;\n        };\n\n        if let Some(views) = local_views {\n            core_ctx.send_view_params(views);\n            last_view_params = views;\n        }\n\n        let mut device_motions = Vec::with_capacity(3);\n\n        device_motions.push((*HEAD_ID, head_motion));\n\n        let left_hand_data = crate::interaction::get_hand_data(\n            &xr_session,\n            core_ctx.platform(),\n            stage_reference_space,\n            now,\n            target_time,\n            &int_ctx.hands_interaction[0],\n            &mut last_controller_poses[0],\n            &mut last_palm_poses[0],\n        );\n        let right_hand_data = crate::interaction::get_hand_data(\n            &xr_session,\n            core_ctx.platform(),\n            stage_reference_space,\n            now,\n            target_time,\n            &int_ctx.hands_interaction[1],\n            &mut last_controller_poses[1],\n            &mut last_palm_poses[1],\n        );\n\n        // Note: When multimodal input is enabled, we are sure that when free hands are used\n        // (not holding controllers) the controller data is None.\n        if (int_ctx.multimodal_hands_enabled || left_hand_data.skeleton_joints.is_none())\n            && let Some(motion) = left_hand_data.grip_motion\n        {\n            device_motions.push((*HAND_LEFT_ID, motion));\n        }\n        if (int_ctx.multimodal_hands_enabled || right_hand_data.skeleton_joints.is_none())\n            && let Some(motion) = right_hand_data.grip_motion\n        {\n            device_motions.push((*HAND_RIGHT_ID, motion));\n        }\n\n        if int_ctx.multimodal_hands_enabled\n            && let Some(detached_controller) = left_hand_data.detached_grip_motion\n        {\n            device_motions.push((*DETACHED_CONTROLLER_LEFT_ID, detached_controller));\n        }\n        if int_ctx.multimodal_hands_enabled\n            && let Some(detached_controller) = right_hand_data.detached_grip_motion\n        {\n            device_motions.push((*DETACHED_CONTROLLER_RIGHT_ID, detached_controller));\n        }\n\n        let face = interaction::get_face_data(\n            &xr_session,\n            &int_ctx.face_sources,\n            view_reference_space,\n            now,\n        );\n\n        let body = int_ctx\n            .body_source\n            .as_ref()\n            .and_then(|source| interaction::get_body_skeleton(source, stage_reference_space, now));\n\n        if let Some(source) = &int_ctx.body_source {\n            device_motions.append(&mut interaction::get_bd_motion_trackers(source, now));\n        }\n\n        // Even though the server is already adding the motion-to-photon latency, here we use\n        // target_time as the poll_timestamp to compensate for the fact that video frames are sent\n        // with the poll timestamp instead of the vsync time. This is to ensure correctness when\n        // submitting frames to OpenXR. This won't cause any desync with the server because no time\n        // sync step is performed between client and server.\n        core_ctx.send_tracking(TrackingData {\n            poll_timestamp: target_time,\n            device_motions,\n            hand_skeletons: [\n                left_hand_data.skeleton_joints,\n                right_hand_data.skeleton_joints,\n            ],\n            face,\n            body,\n        });\n\n        let button_entries = interaction::update_buttons(&xr_session, &int_ctx.button_actions);\n        if !button_entries.is_empty() {\n            core_ctx.send_buttons(button_entries);\n        }\n\n        deadline += frame_interval / 3;\n        thread::sleep(deadline.saturating_duration_since(Instant::now()));\n    }\n}\n"
  },
  {
    "path": "alvr/common/Cargo.toml",
    "content": "[package]\nname = \"alvr_common\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nanyhow = { version = \"1\", features = [\"backtrace\"] }\nbacktrace = \"0.3\"\nglam = { version = \"0.30\", features = [\"serde\"] }\nlog = \"0.4\"\nparking_lot = \"0.12\"\npaste = \"1\"\nsemver = { version = \"1\", features = [\"serde\"] }\nserde = { version = \"1\", features = [\"derive\"] }\nsettings-schema = { git = \"https://github.com/alvr-org/settings-schema-rs\", rev = \"676185f\" }\n# settings-schema = { path = \"../../../../settings-schema-rs/settings-schema\" }\n"
  },
  {
    "path": "alvr/common/LICENSE",
    "content": "Copyright (c) 2020-2021 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "alvr/common/README.md",
    "content": "# alvr_common\n\nThis crate contains some basic functionality shared across all crates in the workspace. In particular it contains constants, logging front-end, and re-exports frequently-used external dependencies.\n"
  },
  {
    "path": "alvr/common/src/average.rs",
    "content": "use std::{collections::VecDeque, time::Duration};\n\npub struct SlidingWindowAverage<T> {\n    history_buffer: VecDeque<T>,\n    max_history_size: usize,\n}\n\nimpl<T> SlidingWindowAverage<T> {\n    pub fn new(initial_value: T, max_history_size: usize) -> Self {\n        Self {\n            history_buffer: [initial_value].into_iter().collect(),\n            max_history_size,\n        }\n    }\n\n    pub fn submit_sample(&mut self, sample: T) {\n        if self.history_buffer.len() >= self.max_history_size {\n            self.history_buffer.pop_front();\n        }\n\n        self.history_buffer.push_back(sample);\n    }\n\n    pub fn retain(&mut self, count: usize) {\n        self.history_buffer\n            .drain(0..self.history_buffer.len().saturating_sub(count));\n    }\n}\n\nimpl SlidingWindowAverage<f32> {\n    pub fn get_average(&self) -> f32 {\n        self.history_buffer.iter().sum::<f32>() / self.history_buffer.len() as f32\n    }\n}\n\nimpl SlidingWindowAverage<Duration> {\n    pub fn get_average(&self) -> Duration {\n        self.history_buffer.iter().sum::<Duration>() / self.history_buffer.len() as u32\n    }\n}\n"
  },
  {
    "path": "alvr/common/src/c_api.rs",
    "content": "use glam::{Quat, Vec3};\n\nuse crate::{Fov, Pose, ViewParams};\n\n#[repr(C)]\npub struct AlvrFov {\n    /// Negative, radians\n    pub left: f32,\n    /// Positive, radians\n    pub right: f32,\n    /// Positive, radians\n    pub up: f32,\n    /// Negative, radians\n    pub down: f32,\n}\n\n#[repr(C)]\n#[derive(Clone, Default)]\npub struct AlvrQuat {\n    pub x: f32,\n    pub y: f32,\n    pub z: f32,\n    pub w: f32,\n}\n\n#[repr(C)]\n#[derive(Clone, Default)]\npub struct AlvrPose {\n    pub orientation: AlvrQuat,\n    pub position: [f32; 3],\n}\n\n#[repr(C)]\npub struct AlvrViewParams {\n    pub pose: AlvrPose,\n    pub fov: AlvrFov,\n}\n\n#[repr(u8)]\npub enum AlvrCodecType {\n    H264 = 0,\n    Hevc = 1,\n    AV1 = 2,\n}\n\npub fn to_capi_fov(fov: &Fov) -> AlvrFov {\n    AlvrFov {\n        left: fov.left,\n        right: fov.right,\n        up: fov.up,\n        down: fov.down,\n    }\n}\n\npub fn from_capi_fov(fov: &AlvrFov) -> Fov {\n    Fov {\n        left: fov.left,\n        right: fov.right,\n        up: fov.up,\n        down: fov.down,\n    }\n}\n\npub fn from_capi_quat(quat: &AlvrQuat) -> Quat {\n    Quat::from_xyzw(quat.x, quat.y, quat.z, quat.w)\n}\n\npub fn to_capi_quat(quat: &Quat) -> AlvrQuat {\n    AlvrQuat {\n        x: quat.x,\n        y: quat.y,\n        z: quat.z,\n        w: quat.w,\n    }\n}\n\npub fn to_capi_pose(pose: &Pose) -> AlvrPose {\n    AlvrPose {\n        orientation: to_capi_quat(&pose.orientation),\n        position: pose.position.to_array(),\n    }\n}\n\npub fn from_capi_pose(pose: &AlvrPose) -> Pose {\n    Pose {\n        orientation: from_capi_quat(&pose.orientation),\n        position: Vec3::from_slice(&pose.position),\n    }\n}\n\npub fn to_capi_view_params(view_params: &ViewParams) -> AlvrViewParams {\n    AlvrViewParams {\n        pose: to_capi_pose(&view_params.pose),\n        fov: to_capi_fov(&view_params.fov),\n    }\n}\n\npub fn from_capi_view_params(view_params: &AlvrViewParams) -> ViewParams {\n    ViewParams {\n        pose: from_capi_pose(&view_params.pose),\n        fov: from_capi_fov(&view_params.fov),\n    }\n}\n"
  },
  {
    "path": "alvr/common/src/connection_result.rs",
    "content": "use anyhow::{Result, anyhow};\nuse std::{\n    error::Error,\n    fmt::Display,\n    io,\n    sync::mpsc::{RecvTimeoutError, TryRecvError},\n};\n\npub enum ConnectionError {\n    TryAgain(anyhow::Error),\n    Other(anyhow::Error),\n}\n\nimpl Display for ConnectionError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let (ConnectionError::TryAgain(e) | ConnectionError::Other(e)) = self;\n        write!(f, \"{e:?}\")\n    }\n}\n\npub type ConResult<T = ()> = Result<T, ConnectionError>;\n\npub fn try_again<T>() -> ConResult<T> {\n    Err(ConnectionError::TryAgain(anyhow!(\"Try again\")))\n}\n\n#[macro_export]\nmacro_rules! con_bail {\n    ($($args:tt)+) => {\n        return Err(alvr_common::ConnectionError::Other(alvr_common::anyhow::anyhow!($($args)+)))\n    };\n}\n\npub trait ToCon<T> {\n    /// Convert result to ConResult. The error is always mapped to `Other()`\n    fn to_con(self) -> ConResult<T>;\n}\n\nimpl<T> ToCon<T> for Option<T> {\n    fn to_con(self) -> ConResult<T> {\n        self.ok_or_else(|| ConnectionError::Other(anyhow!(\"Unexpected None\")))\n    }\n}\n\nimpl<T, E: Error + Send + Sync + 'static> ToCon<T> for Result<T, E> {\n    fn to_con(self) -> ConResult<T> {\n        self.map_err(|e| ConnectionError::Other(e.into()))\n    }\n}\n\npub trait AnyhowToCon<T> {\n    fn to_con(self) -> ConResult<T>;\n}\n\nimpl<T> AnyhowToCon<T> for Result<T, anyhow::Error> {\n    fn to_con(self) -> ConResult<T> {\n        self.map_err(ConnectionError::Other)\n    }\n}\n\npub trait HandleTryAgain<T> {\n    fn handle_try_again(self) -> ConResult<T>;\n}\n\nimpl<T> HandleTryAgain<T> for io::Result<T> {\n    fn handle_try_again(self) -> ConResult<T> {\n        self.map_err(|e| {\n            // Ignore ERROR_IO_PENDING on Windows (code 997)\n            if e.kind() == io::ErrorKind::TimedOut\n                || e.kind() == io::ErrorKind::WouldBlock\n                || (cfg!(windows) && e.raw_os_error() == Some(997))\n            {\n                ConnectionError::TryAgain(e.into())\n            } else {\n                ConnectionError::Other(e.into())\n            }\n        })\n    }\n}\n\nimpl<T> HandleTryAgain<T> for std::result::Result<T, RecvTimeoutError> {\n    fn handle_try_again(self) -> ConResult<T> {\n        self.map_err(|e| match e {\n            RecvTimeoutError::Timeout => ConnectionError::TryAgain(e.into()),\n            RecvTimeoutError::Disconnected => ConnectionError::Other(e.into()),\n        })\n    }\n}\n\nimpl<T> HandleTryAgain<T> for std::result::Result<T, TryRecvError> {\n    fn handle_try_again(self) -> ConResult<T> {\n        self.map_err(|e| match e {\n            TryRecvError::Empty => ConnectionError::TryAgain(e.into()),\n            TryRecvError::Disconnected => ConnectionError::Other(e.into()),\n        })\n    }\n}\n"
  },
  {
    "path": "alvr/common/src/inputs.rs",
    "content": "use crate::hash_string;\nuse std::{\n    collections::{HashMap, HashSet},\n    sync::LazyLock,\n};\n\nmacro_rules! interaction_profile {\n    ($ty:ident, $path:expr) => {\n        paste::paste! {\n            pub const [<$ty _CONTROLLER_PROFILE_PATH>]: &str =\n                concat!(\"/interaction_profiles/\", $path, \"_controller\");\n            pub static [<$ty _CONTROLLER_PROFILE_ID>]: LazyLock<u64> =\n                LazyLock::new(|| hash_string([<$ty _CONTROLLER_PROFILE_PATH>]));\n        }\n    };\n}\n\ninteraction_profile!(QUEST, \"oculus/touch\");\ninteraction_profile!(VIVE, \"htc/vive\");\ninteraction_profile!(INDEX, \"valve/index\");\ninteraction_profile!(PICO_NEO3, \"bytedance/pico_neo3\");\ninteraction_profile!(PICO4, \"bytedance/pico4\");\ninteraction_profile!(PICO4S, \"bytedance/pico4s\");\ninteraction_profile!(PICO_G3, \"bytedance/pico_g3\");\ninteraction_profile!(PSVR2, \"sony/playstation_vr2_sense\");\ninteraction_profile!(FOCUS3, \"htc/vive_focus3\");\ninteraction_profile!(YVR, \"yvr/touch\");\n\nmacro_rules! devices {\n    ($(($name:ident, $path:expr),)*) => {\n        paste::paste! {\n            $(\n                pub const [<$name _PATH>]: &str = $path;\n                pub static [<$name _ID>]: LazyLock<u64> = LazyLock::new(|| hash_string([<$name _PATH>]));\n            )*\n\n            pub static DEVICE_ID_TO_PATH: LazyLock<HashMap<u64, &str>> = LazyLock::new(|| {\n                [\n                    $((*[<$name _ID>], [<$name _PATH>]),)*\n                ]\n                .into_iter()\n                .collect()\n            });\n        }\n    };\n}\n\ndevices! {\n    (HEAD, \"/user/head\"),\n    (HAND_LEFT, \"/user/hand/left\"),\n    (HAND_RIGHT, \"/user/hand/right\"),\n    (HAND_TRACKER_LEFT,\"/user/hand_tracker/left\"),\n    (HAND_TRACKER_RIGHT, \"/user/hand_tracker/right\"),\n    (BODY_CHEST, \"/user/body/chest\"),\n    (BODY_HIPS, \"/user/body/waist\"),\n    (BODY_LEFT_ELBOW, \"/user/body/left_elbow\"),\n    (BODY_RIGHT_ELBOW, \"/user/body/right_elbow\"),\n    (BODY_LEFT_KNEE, \"/user/body/left_knee\"),\n    (BODY_LEFT_FOOT, \"/user/body/left_foot\"),\n    (BODY_RIGHT_KNEE, \"/user/body/right_knee\"),\n    (BODY_RIGHT_FOOT, \"/user/body/right_foot\"),\n    (DETACHED_CONTROLLER_LEFT, \"/user/detached_controller_meta/left\"),\n    (DETACHED_CONTROLLER_RIGHT, \"/user/detached_controller_meta/right\"),\n    (GENERIC_TRACKER_1, \"/user/generic_tracker/1\"),\n    (GENERIC_TRACKER_2, \"/user/generic_tracker/2\"),\n    (GENERIC_TRACKER_3, \"/user/generic_tracker/3\"),\n}\n\npub enum ButtonType {\n    Binary,\n    Scalar,\n}\n\npub struct ButtonInfo {\n    pub path: &'static str,\n    pub button_type: ButtonType,\n    pub device_id: u64,\n}\n\nmacro_rules! controller_inputs {\n    ($(($inputs:ident, $paths:literal, $ty:ident),)*) => {\n        paste::paste! {\n            $(\n                pub const [<LEFT_ $inputs _PATH>]: &str =\n                    concat!(\"/user/hand/left/input/\", $paths);\n                pub static [<LEFT_ $inputs _ID>]: LazyLock<u64> =\n                    LazyLock::new(|| hash_string([<LEFT_ $inputs _PATH>]));\n                pub const [<RIGHT_ $inputs _PATH>]: &str =\n                    concat!(\"/user/hand/right/input/\", $paths);\n                pub static [<RIGHT_ $inputs _ID>]: LazyLock<u64> =\n                    LazyLock::new(|| hash_string([<RIGHT_ $inputs _PATH>]));\n            )*\n\n            pub static BUTTON_INFO: LazyLock<HashMap<u64, ButtonInfo>> = LazyLock::new(|| {\n                [\n                    $((\n                        *[<LEFT_ $inputs _ID>],\n                        ButtonInfo {\n                            path: [<LEFT_ $inputs _PATH>],\n                            button_type: ButtonType::$ty,\n                            device_id: *HAND_LEFT_ID,\n                        },\n                    ),\n                    (\n                        *[<RIGHT_ $inputs _ID>],\n                        ButtonInfo {\n                            path: [<RIGHT_ $inputs _PATH>],\n                            button_type: ButtonType::$ty,\n                            device_id: *HAND_RIGHT_ID,\n                        },\n                    ),)*\n                ]\n                .into_iter()\n                .collect()\n            });\n        }\n    };\n}\n\ncontroller_inputs! {\n    (SYSTEM_CLICK, \"system/click\", Binary),\n    (SYSTEM_TOUCH, \"system/touch\", Binary),\n    (MENU_CLICK, \"menu/click\", Binary),\n    (MENU_TOUCH, \"menu/touch\", Binary),\n    (BACK_CLICK, \"back/click\", Binary),\n    (A_CLICK, \"a/click\", Binary),\n    (A_TOUCH, \"a/touch\", Binary),\n    (B_CLICK, \"b/click\", Binary),\n    (B_TOUCH, \"b/touch\", Binary),\n    (X_CLICK, \"x/click\", Binary),\n    (X_TOUCH, \"x/touch\", Binary),\n    (Y_CLICK, \"y/click\", Binary),\n    (Y_TOUCH, \"y/touch\", Binary),\n    (SQUEEZE_CLICK, \"squeeze/click\", Binary),\n    (SQUEEZE_TOUCH, \"squeeze/touch\", Binary),\n    (SQUEEZE_VALUE, \"squeeze/value\", Scalar),\n    (SQUEEZE_FORCE, \"squeeze/force\", Scalar),\n    (TRIGGER_CLICK, \"trigger/click\", Binary),\n    (TRIGGER_VALUE, \"trigger/value\", Scalar),\n    (TRIGGER_TOUCH, \"trigger/touch\", Binary),\n    (THUMBSTICK_X, \"thumbstick/x\", Scalar),\n    (THUMBSTICK_Y, \"thumbstick/y\", Scalar),\n    (THUMBSTICK_CLICK, \"thumbstick/click\", Binary),\n    (THUMBSTICK_TOUCH, \"thumbstick/touch\", Binary),\n    (TRACKPAD_X, \"trackpad/x\", Scalar),\n    (TRACKPAD_Y, \"trackpad/y\", Scalar),\n    (TRACKPAD_CLICK, \"trackpad/click\", Binary),\n    (TRACKPAD_FORCE, \"trackpad/force\", Scalar),\n    (TRACKPAD_TOUCH, \"trackpad/touch\", Binary),\n    (THUMBREST_TOUCH, \"thumbrest/touch\", Binary),\n}\n\npub struct InteractionProfileInfo {\n    pub path: &'static str,\n    pub button_set: HashSet<u64>,\n}\n\npub static CONTROLLER_PROFILE_INFO: LazyLock<HashMap<u64, InteractionProfileInfo>> =\n    LazyLock::new(|| {\n        [\n            (\n                *QUEST_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: QUEST_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_THUMBREST_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *VIVE_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: VIVE_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRACKPAD_X_ID,\n                        *LEFT_TRACKPAD_Y_ID,\n                        *LEFT_TRACKPAD_CLICK_ID,\n                        *LEFT_TRACKPAD_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_MENU_CLICK_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRACKPAD_X_ID,\n                        *RIGHT_TRACKPAD_Y_ID,\n                        *RIGHT_TRACKPAD_CLICK_ID,\n                        *RIGHT_TRACKPAD_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *INDEX_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: INDEX_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_SYSTEM_TOUCH_ID,\n                        *LEFT_A_CLICK_ID,\n                        *LEFT_A_TOUCH_ID,\n                        *LEFT_B_CLICK_ID,\n                        *LEFT_B_TOUCH_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_SQUEEZE_FORCE_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_TRACKPAD_X_ID,\n                        *LEFT_TRACKPAD_Y_ID,\n                        *LEFT_TRACKPAD_FORCE_ID,\n                        *LEFT_TRACKPAD_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SYSTEM_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_SQUEEZE_FORCE_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_TRACKPAD_X_ID,\n                        *RIGHT_TRACKPAD_Y_ID,\n                        *RIGHT_TRACKPAD_FORCE_ID,\n                        *RIGHT_TRACKPAD_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *PICO_G3_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: PICO_G3_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRACKPAD_Y_ID,\n                        *LEFT_TRACKPAD_X_ID,\n                        *LEFT_TRACKPAD_CLICK_ID,\n                        *LEFT_TRACKPAD_TOUCH_ID,\n                        *RIGHT_MENU_CLICK_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRACKPAD_Y_ID,\n                        *RIGHT_TRACKPAD_X_ID,\n                        *RIGHT_TRACKPAD_CLICK_ID,\n                        *RIGHT_TRACKPAD_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *PICO_NEO3_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: PICO_NEO3_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_THUMBREST_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_MENU_CLICK_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *PICO4_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: PICO4_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_THUMBREST_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *PICO4S_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: PICO4S_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_THUMBREST_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *PSVR2_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: PSVR2_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_MENU_TOUCH_ID,\n                        *LEFT_SYSTEM_CLICK_ID,\n                        *LEFT_SYSTEM_TOUCH_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_SQUEEZE_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SYSTEM_TOUCH_ID,\n                        *RIGHT_MENU_CLICK_ID,\n                        *RIGHT_MENU_TOUCH_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_SQUEEZE_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *FOCUS3_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: FOCUS3_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        // *LEFT_SQUEEZE_TOUCH_ID, // not actually working\n                        *LEFT_SQUEEZE_VALUE_ID,\n                        *LEFT_TRIGGER_CLICK_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_THUMBREST_TOUCH_ID,\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        // *RIGHT_SQUEEZE_TOUCH_ID, // not actually working\n                        *RIGHT_SQUEEZE_VALUE_ID,\n                        *RIGHT_TRIGGER_CLICK_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID,\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n            (\n                *YVR_CONTROLLER_PROFILE_ID,\n                InteractionProfileInfo {\n                    path: YVR_CONTROLLER_PROFILE_PATH,\n                    button_set: [\n                        *LEFT_X_CLICK_ID,\n                        *LEFT_X_TOUCH_ID,\n                        *LEFT_Y_CLICK_ID,\n                        *LEFT_Y_TOUCH_ID,\n                        *LEFT_MENU_CLICK_ID,\n                        *LEFT_SQUEEZE_CLICK_ID,\n                        *LEFT_TRIGGER_TOUCH_ID,\n                        *LEFT_TRIGGER_VALUE_ID,\n                        *LEFT_THUMBSTICK_X_ID,\n                        *LEFT_THUMBSTICK_Y_ID,\n                        *LEFT_THUMBSTICK_CLICK_ID,\n                        *LEFT_THUMBSTICK_TOUCH_ID,\n                        *LEFT_THUMBREST_TOUCH_ID, // might not actually be present?\n                        *RIGHT_A_CLICK_ID,\n                        *RIGHT_A_TOUCH_ID,\n                        *RIGHT_B_CLICK_ID,\n                        *RIGHT_B_TOUCH_ID,\n                        *RIGHT_SYSTEM_CLICK_ID,\n                        *RIGHT_SQUEEZE_CLICK_ID,\n                        *RIGHT_TRIGGER_TOUCH_ID,\n                        *RIGHT_TRIGGER_VALUE_ID,\n                        *RIGHT_THUMBSTICK_X_ID,\n                        *RIGHT_THUMBSTICK_Y_ID,\n                        *RIGHT_THUMBSTICK_CLICK_ID,\n                        *RIGHT_THUMBSTICK_TOUCH_ID,\n                        *RIGHT_THUMBREST_TOUCH_ID, // might not actually be present?\n                    ]\n                    .into_iter()\n                    .collect(),\n                },\n            ),\n        ]\n        .into_iter()\n        .collect()\n    });\n"
  },
  {
    "path": "alvr/common/src/lib.rs",
    "content": "mod average;\nmod c_api;\nmod connection_result;\nmod inputs;\nmod logging;\nmod primitives;\nmod version;\n\nuse parking_lot::{Condvar, Mutex, RwLockWriteGuard};\nuse serde::{Deserialize, Serialize};\nuse std::sync::atomic::{AtomicBool, Ordering};\n\npub use anyhow;\npub use glam;\npub use log;\npub use parking_lot;\npub use semver;\npub use settings_schema;\n\npub use average::*;\npub use c_api::*;\npub use connection_result::*;\npub use inputs::*;\npub use log::{debug, error, info, warn};\npub use logging::*;\npub use primitives::*;\npub use version::*;\n\npub const ALVR_NAME: &str = \"ALVR\";\n\n// Simple wrapper for AtomicBool when using Ordering::Relaxed. Deref cannot be implemented (cannot\n// return local reference)\n#[derive(Default)]\npub struct RelaxedAtomic(AtomicBool);\n\nimpl RelaxedAtomic {\n    pub const fn new(initial_value: bool) -> Self {\n        Self(AtomicBool::new(initial_value))\n    }\n\n    pub fn value(&self) -> bool {\n        self.0.load(Ordering::Relaxed)\n    }\n\n    pub fn set(&self, value: bool) {\n        self.0.store(value, Ordering::Relaxed);\n    }\n}\n\n#[derive(PartialEq, Eq, Debug)]\npub enum LifecycleState {\n    StartingUp,\n    Idle,\n    Resumed,\n    ShuttingDown,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]\npub enum ConnectionState {\n    #[default]\n    Disconnected,\n    Connecting,\n    Connected,\n    Streaming,\n    Disconnecting,\n}\n\npub fn wait_rwlock<T>(condvar: &Condvar, guard: &mut RwLockWriteGuard<'_, T>) {\n    let staging_mutex = Mutex::<()>::new(());\n    let mut inner_guard = staging_mutex.lock();\n    RwLockWriteGuard::unlocked(guard, move || {\n        condvar.wait(&mut inner_guard);\n    });\n}\n"
  },
  {
    "path": "alvr/common/src/logging.rs",
    "content": "use anyhow::Result;\nuse backtrace::Backtrace;\nuse parking_lot::Mutex;\nuse serde::{Deserialize, Serialize};\nuse settings_schema::SettingsSchema;\nuse std::{error::Error, fmt::Display, sync::OnceLock};\n\npub const SERVER_IMPL_DBG_LABEL: &str = \"SERVER IMPL\";\npub const CLIENT_IMPL_DBG_LABEL: &str = \"CLIENT IMPL\";\npub const SERVER_CORE_DBG_LABEL: &str = \"SERVER CORE\";\npub const CLIENT_CORE_DBG_LABEL: &str = \"CLIENT CORE\";\npub const CONNECTION_DBG_LABEL: &str = \"CONNECTION\";\npub const SOCKETS_DBG_LABEL: &str = \"SOCKETS\";\npub const SERVER_GFX_DBG_LABEL: &str = \"SERVER GFX\";\npub const CLIENT_GFX_DBG_LABEL: &str = \"CLIENT GFX\";\npub const ENCODER_DBG_LABEL: &str = \"ENCODER\";\npub const DECODER_DBG_LABEL: &str = \"DECODER\";\n\nstatic POPUP_CALLBACK: OnceLock<fn(&str, &str, LogSeverity)> = OnceLock::new();\n\n#[macro_export]\nmacro_rules! dbg_server_impl {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::SERVER_IMPL_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_client_impl {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::CLIENT_IMPL_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_server_core {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::SERVER_CORE_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_client_core {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::CLIENT_CORE_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_connection {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::CONNECTION_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_sockets {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::SOCKETS_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_server_gfx {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::SERVER_GFX_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_client_gfx {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::CLIENT_GFX_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_encoder {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::ENCODER_DBG_LABEL, $($args)*);\n    };\n}\n\n#[macro_export]\nmacro_rules! dbg_decoder {\n    ($($args:tt)*) => {\n        #[cfg(debug_assertions)]\n        $crate::log::debug!(target: $crate::DECODER_DBG_LABEL, $($args)*);\n    };\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct DebugGroupsConfig {\n    #[schema(flag = \"steamvr-restart\")]\n    pub server_impl: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub client_impl: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub server_core: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub client_core: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub connection: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub sockets: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub server_gfx: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub client_gfx: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub encoder: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub decoder: bool,\n}\n\npub fn filter_debug_groups(target: &str, config: &DebugGroupsConfig) -> bool {\n    match target {\n        SERVER_IMPL_DBG_LABEL => config.server_impl,\n        CLIENT_IMPL_DBG_LABEL => config.client_impl,\n        SERVER_CORE_DBG_LABEL => config.server_core,\n        CLIENT_CORE_DBG_LABEL => config.client_core,\n        CONNECTION_DBG_LABEL => config.connection,\n        SOCKETS_DBG_LABEL => config.sockets,\n        SERVER_GFX_DBG_LABEL => config.server_gfx,\n        CLIENT_GFX_DBG_LABEL => config.client_gfx,\n        ENCODER_DBG_LABEL => config.encoder,\n        DECODER_DBG_LABEL => config.decoder,\n        _ => true,\n    }\n}\n\npub fn is_enabled_debug_group(target: &str, config: &DebugGroupsConfig) -> bool {\n    (config.server_impl && target == SERVER_IMPL_DBG_LABEL)\n        || (config.client_impl && target == CLIENT_IMPL_DBG_LABEL)\n        || (config.server_core && target == SERVER_CORE_DBG_LABEL)\n        || (config.client_core && target == CLIENT_CORE_DBG_LABEL)\n        || (config.connection && target == CONNECTION_DBG_LABEL)\n        || (config.sockets && target == SOCKETS_DBG_LABEL)\n        || (config.server_gfx && target == SERVER_GFX_DBG_LABEL)\n        || (config.client_gfx && target == CLIENT_GFX_DBG_LABEL)\n        || (config.encoder && target == ENCODER_DBG_LABEL)\n        || (config.decoder && target == DECODER_DBG_LABEL)\n}\n\n#[derive(\n    SettingsSchema, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord,\n)]\npub enum LogSeverity {\n    Error = 3,\n    Warning = 2,\n    Info = 1,\n    Debug = 0,\n}\n\nimpl LogSeverity {\n    pub fn from_log_level(level: log::Level) -> Self {\n        match level {\n            log::Level::Error => LogSeverity::Error,\n            log::Level::Warn => LogSeverity::Warning,\n            log::Level::Info => LogSeverity::Info,\n            log::Level::Debug | log::Level::Trace => LogSeverity::Debug,\n        }\n    }\n\n    pub fn into_log_level(self) -> log::Level {\n        match self {\n            LogSeverity::Error => log::Level::Error,\n            LogSeverity::Warning => log::Level::Warn,\n            LogSeverity::Info => log::Level::Info,\n            LogSeverity::Debug => log::Level::Debug,\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct LogEntry {\n    pub severity: LogSeverity,\n    pub content: String,\n}\n\n// The callback has parameters in order: title, message, severity\npub fn set_popup_callback(callback: fn(&str, &str, LogSeverity)) {\n    POPUP_CALLBACK.set(callback).ok();\n}\n\npub fn set_panic_hook() {\n    std::panic::set_hook(Box::new(|panic_info| {\n        let err_str = format!(\n            \"What happened:\\n{panic_info}\\n\\nBacktrace:\\n{:?}\",\n            Backtrace::new()\n        );\n\n        log::error!(\"ALVR panicked: {err_str}\");\n\n        std::thread::spawn({\n            let panic_str = panic_info.to_string();\n            move || {\n                if let Some(callback) = POPUP_CALLBACK.get() {\n                    callback(\"ALVR panicked\", &panic_str, LogSeverity::Error);\n                }\n            }\n        });\n    }))\n}\n\npub fn show_w<W: Display + Send + 'static>(w: W) {\n    log::warn!(\"{w}\");\n\n    std::thread::spawn(move || {\n        if let Some(callback) = POPUP_CALLBACK.get() {\n            callback(\"ALVR warning\", &w.to_string(), LogSeverity::Warning);\n        }\n    });\n}\n\npub fn show_warn<T, E: Display + Send + 'static>(res: Result<T, E>) -> Option<T> {\n    res.map_err(show_w).ok()\n}\n\nfn show_e_block<E: Display>(e: E, blocking: bool) {\n    log::error!(\"{e}\");\n\n    // Store the last error shown in a message box. Do not open a new message box if the content\n    // of the error has not changed\n\n    static LAST_MESSAGEBOX_ERROR: Mutex<String> = Mutex::new(String::new());\n\n    let err_string = e.to_string();\n    let last_messagebox_error_ref = &mut *LAST_MESSAGEBOX_ERROR.lock();\n    if *last_messagebox_error_ref != err_string {\n        let show_msgbox = {\n            let err_string = err_string.clone();\n            move || {\n                if let Some(callback) = POPUP_CALLBACK.get() {\n                    callback(\"ALVR error\", &err_string, LogSeverity::Error);\n                }\n            }\n        };\n\n        if blocking {\n            show_msgbox();\n        } else {\n            std::thread::spawn(show_msgbox);\n        }\n\n        *last_messagebox_error_ref = err_string;\n    }\n}\n\npub fn show_e<E: Display>(e: E) {\n    show_e_block(e, false);\n}\n\npub fn show_e_dbg<E: std::fmt::Debug>(e: E) {\n    show_e_block(format!(\"{e:?}\"), false);\n}\n\npub fn show_e_blocking<E: Display>(e: E) {\n    show_e_block(e, true);\n}\n\npub fn show_err<T, E: Display>(res: Result<T, E>) -> Option<T> {\n    res.map_err(|e| show_e_block(e, false)).ok()\n}\n\npub fn show_err_blocking<T, E: Display>(res: Result<T, E>) -> Option<T> {\n    res.map_err(|e| show_e_block(e, true)).ok()\n}\n\npub trait ToAny<T> {\n    fn to_any(self) -> Result<T>;\n}\n\nimpl<T> ToAny<T> for Option<T> {\n    fn to_any(self) -> Result<T> {\n        match self {\n            Some(value) => Ok(value),\n            None => Err(anyhow::anyhow!(\"Unexpected None\")),\n        }\n    }\n}\n\nimpl<T, E: Error + Send + Sync + 'static> ToAny<T> for Result<T, E> {\n    fn to_any(self) -> Result<T> {\n        match self {\n            Ok(value) => Ok(value),\n            Err(e) => Err(e.into()),\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/common/src/primitives.rs",
    "content": "use glam::{Quat, Vec3};\nuse serde::{Deserialize, Serialize};\nuse std::{ops::Mul, time::Duration};\n\n// Field of view in radians\n#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]\npub struct Fov {\n    pub left: f32,\n    pub right: f32,\n    pub up: f32,\n    pub down: f32,\n}\n\nimpl Fov {\n    pub const DUMMY: Self = Fov {\n        left: -1.0,\n        right: 1.0,\n        up: 1.0,\n        down: -1.0,\n    };\n}\n\n#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug)]\npub struct Pose {\n    pub orientation: Quat,\n    pub position: Vec3,\n}\n\nimpl Pose {\n    pub const IDENTITY: Self = Pose {\n        orientation: Quat::IDENTITY,\n        position: Vec3::ZERO,\n    };\n\n    pub fn inverse(&self) -> Pose {\n        let inverse_orientation = self.orientation.conjugate();\n        Pose {\n            orientation: inverse_orientation,\n            position: inverse_orientation * -self.position,\n        }\n    }\n}\n\nimpl Mul<Pose> for Pose {\n    type Output = Pose;\n\n    fn mul(self, rhs: Pose) -> Pose {\n        Pose {\n            orientation: self.orientation * rhs.orientation,\n            position: self.position + self.orientation * rhs.position,\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug)]\npub struct DeviceMotion {\n    pub pose: Pose,\n    pub linear_velocity: Vec3,\n    pub angular_velocity: Vec3,\n}\n\nimpl Mul<DeviceMotion> for Pose {\n    type Output = DeviceMotion;\n\n    fn mul(self, rhs: DeviceMotion) -> DeviceMotion {\n        DeviceMotion {\n            pose: self * rhs.pose,\n            linear_velocity: self.orientation * rhs.linear_velocity,\n            angular_velocity: self.orientation * rhs.angular_velocity,\n        }\n    }\n}\n\n// Calculate difference ensuring maximum precision is preserved\nfn difference_seconds(from: Duration, to: Duration) -> f32 {\n    to.saturating_sub(from).as_secs_f32() - from.saturating_sub(to).as_secs_f32()\n}\n\nimpl DeviceMotion {\n    pub const IDENTITY: Self = DeviceMotion {\n        pose: Pose::IDENTITY,\n        linear_velocity: Vec3::ZERO,\n        angular_velocity: Vec3::ZERO,\n    };\n\n    pub fn predict(&self, from_timestamp: Duration, to_timestamp: Duration) -> Self {\n        let delta_time_s = difference_seconds(from_timestamp, to_timestamp);\n\n        let delta_position = self.linear_velocity * delta_time_s;\n        let delta_orientation = Quat::from_scaled_axis(self.angular_velocity * delta_time_s);\n\n        DeviceMotion {\n            pose: Pose {\n                orientation: delta_orientation * self.pose.orientation,\n                position: self.pose.position + delta_position,\n            },\n            linear_velocity: self.linear_velocity,\n            angular_velocity: self.angular_velocity,\n        }\n    }\n}\n\n// Per eye view parameters\n#[derive(Serialize, Deserialize, Clone, Copy)]\npub struct ViewParams {\n    pub pose: Pose,\n    pub fov: Fov,\n}\n\nimpl ViewParams {\n    pub const DUMMY: Self = ViewParams {\n        pose: Pose::IDENTITY,\n        fov: Fov::DUMMY,\n    };\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct BodySkeletonFb {\n    pub upper_body: [Option<Pose>; 18],\n    pub lower_body: Option<[Option<Pose>; 14]>,\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct BodySkeletonBd(pub [Option<Pose>; 24]);\n\n#[derive(Serialize, Deserialize, Clone)]\npub enum BodySkeleton {\n    Fb(Box<BodySkeletonFb>),\n    Bd(Box<BodySkeletonBd>),\n}\n"
  },
  {
    "path": "alvr/common/src/version.rs",
    "content": "use semver::Version;\nuse std::{\n    collections::hash_map::DefaultHasher,\n    hash::{Hash, Hasher},\n    sync::LazyLock,\n};\n\npub static ALVR_VERSION: LazyLock<Version> =\n    LazyLock::new(|| Version::parse(env!(\"CARGO_PKG_VERSION\")).unwrap());\n\n// Consistent across architectures, might not be consistent across different compiler versions.\npub fn hash_string(string: &str) -> u64 {\n    let mut hasher = DefaultHasher::new();\n    string.hash(&mut hasher);\n    hasher.finish()\n}\n\npub fn is_nightly() -> bool {\n    ALVR_VERSION.build.contains(\"nightly\")\n}\n\npub fn is_stable() -> bool {\n    ALVR_VERSION.pre.is_empty() && !is_nightly()\n}\n\n// Semver compatible versions will produce the same protocol ID. Protocol IDs are not ordered\n// As a convention, encode/decode the protocol ID bytes as little endian.\n// Only makor and\npub fn protocol_id() -> String {\n    if ALVR_VERSION.pre.is_empty() {\n        ALVR_VERSION.major.to_string()\n    } else {\n        format!(\"{}-{}\", ALVR_VERSION.major, ALVR_VERSION.pre)\n    }\n}\n\npub fn protocol_id_u64() -> u64 {\n    hash_string(&protocol_id())\n}\n\n// deprecated\npub fn is_version_compatible(other_version: &Version) -> bool {\n    let protocol_string = if other_version.pre.is_empty() {\n        other_version.major.to_string()\n    } else {\n        format!(\"{}-{}\", other_version.major, other_version.pre)\n    };\n\n    protocol_id_u64() == hash_string(&protocol_string)\n}\n"
  },
  {
    "path": "alvr/dashboard/Cargo.toml",
    "content": "[package]\nname = \"alvr_dashboard\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_adb.workspace = true\nalvr_common.workspace = true\nalvr_events.workspace = true\nalvr_filesystem.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\nalvr_sockets.workspace = true\nalvr_gui_common.workspace = true\nalvr_audio.workspace = true\n\nchrono = \"0.4\"\neframe = \"0.32\"\nenv_logger = \"0.11\"\nico = \"0.4\"\nrand = \"0.9\"\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nsettings-schema = { git = \"https://github.com/alvr-org/settings-schema-rs\", rev = \"676185f\" }\nstatrs = \"0.18\"\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nalvr_server_io.workspace = true\nsysinfo = \"0.37\"\ntungstenite = \"0.27\"\nureq = { version = \"3\", features = [\"json\"] }\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\nwgpu = \"25\"\nlibva = { package = \"cros-libva\", version = \"0.0.7\" } # Latest that works on Ubuntu 22.04\nnvml-wrapper = \"0.11.0\"\n\n[target.'cfg(windows)'.build-dependencies]\nwinres = \"0.1\"\n"
  },
  {
    "path": "alvr/dashboard/README.md",
    "content": "# alvr_gui\n\nCrate for GUI-related code. It needs a backend to display the UI.\n"
  },
  {
    "path": "alvr/dashboard/build.rs",
    "content": "#[cfg(windows)]\nfn main() {\n    let mut resource = winres::WindowsResource::new();\n    resource.set_icon(\"resources/dashboard.ico\");\n    resource.compile().unwrap();\n}\n\n#[cfg(not(windows))]\nfn main() {}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/about.rs",
    "content": "use alvr_common::ALVR_VERSION;\nuse alvr_gui_common::theme;\nuse eframe::egui::{Frame, RichText, ScrollArea, Ui};\n\npub fn about_tab_ui(ui: &mut Ui) {\n    ui.label(RichText::new(format!(\"ALVR streamer v{}\", *ALVR_VERSION)).size(30.0));\n    ui.add_space(10.0);\n    ui.hyperlink_to(\"Visit us on GitHub\", \"https://github.com/alvr-org/ALVR\");\n    ui.hyperlink_to(\"Join us on Discord\", \"https://discord.gg/ALVR\");\n    ui.hyperlink_to(\n        \"Latest release\",\n        \"https://github.com/alvr-org/ALVR/releases/latest\",\n    );\n    ui.hyperlink_to(\n        \"Donate to ALVR on Open Collective\",\n        \"https://opencollective.com/alvr\",\n    );\n    ui.add_space(10.0);\n    ui.label(\"License:\");\n    Frame::group(ui.style())\n        .fill(theme::DARKER_BG)\n        .inner_margin(theme::FRAME_PADDING)\n        .show(ui, |ui| {\n            ScrollArea::new([false, true])\n                .id_salt(\"license_scroll\")\n                .show(ui, |ui| ui.label(include_str!(\"../../../../../LICENSE\")))\n        });\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/debug.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse eframe::egui::Ui;\n\npub fn debug_tab_ui(ui: &mut Ui) -> Option<ServerRequest> {\n    let mut request = None;\n\n    ui.label(\n        \"Recording from ALVR using the buttons below is not suitable for capturing gameplay.\nFor that, use other means of recording, for example through headset or desktop VR output.\",\n    );\n\n    ui.columns(4, |ui| {\n        if ui[0].button(\"Capture frame\").clicked() {\n            request = Some(ServerRequest::CaptureFrame);\n        }\n\n        if ui[1].button(\"Insert IDR\").clicked() {\n            request = Some(ServerRequest::InsertIdr);\n        }\n\n        if ui[2].button(\"Start recording\").clicked() {\n            request = Some(ServerRequest::StartRecording);\n        }\n\n        if ui[3].button(\"Stop recording\").clicked() {\n            request = Some(ServerRequest::StopRecording);\n        }\n    });\n\n    request\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/devices.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse alvr_common::ConnectionState;\nuse alvr_gui_common::theme::{self, log_colors};\nuse alvr_packets::ClientConnectionsAction;\nuse alvr_session::{ClientConnectionConfig, SessionConfig};\nuse alvr_sockets::WIRED_CLIENT_HOSTNAME;\nuse eframe::{\n    egui::{self, Frame, Grid, Layout, ProgressBar, RichText, TextEdit, Ui, Window},\n    emath::{Align, Align2},\n    epaint::Color32,\n};\n\nstruct EditPopupState {\n    new_devices: bool,\n    hostname: String,\n    ips: Vec<String>,\n}\n\npub struct DevicesTab {\n    new_devices: Option<Vec<(String, ClientConnectionConfig)>>,\n    trusted_devices: Option<Vec<(String, ClientConnectionConfig)>>,\n    edit_popup_state: Option<EditPopupState>,\n    adb_download_progress: Option<f32>,\n}\n\nimpl DevicesTab {\n    pub fn new() -> Self {\n        Self {\n            new_devices: None,\n            trusted_devices: None,\n            edit_popup_state: None,\n            adb_download_progress: None,\n        }\n    }\n\n    pub fn update_client_list(&mut self, session: &SessionConfig) {\n        let (trusted_clients, untrusted_clients) =\n            session\n                .client_connections\n                .clone()\n                .into_iter()\n                .partition::<Vec<_>, _>(|(_, data)| data.trusted);\n\n        self.trusted_devices = Some(trusted_clients);\n        self.new_devices = Some(untrusted_clients);\n    }\n\n    pub fn update_adb_download_progress(&mut self, progress: f32) {\n        self.adb_download_progress = Some(progress);\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui, connected_to_server: bool) -> Vec<ServerRequest> {\n        let mut requests = vec![];\n\n        if self.new_devices.is_none() {\n            requests.push(ServerRequest::GetSession);\n        }\n\n        if !connected_to_server {\n            Frame::group(ui.style())\n                .inner_margin(theme::FRAME_PADDING)\n                .fill(log_colors::WARNING_LIGHT)\n                .show(ui, |ui| {\n                    Grid::new(0).num_columns(2).show(ui, |ui| {\n                        ui.horizontal(|ui| {\n                            ui.add_space(theme::FRAME_TEXT_SPACING);\n                            ui.heading(\n                                RichText::new(\n                                    \"ALVR requires running SteamVR! \\\n                                    Devices will not be discovered or connected.\",\n                                )\n                                .color(Color32::BLACK)\n                                .size(16.0),\n                            );\n                        });\n\n                        #[cfg(not(target_arch = \"wasm32\"))]\n                        ui.with_layout(Layout::right_to_left(eframe::emath::Align::Center), |ui| {\n                            if ui.button(\"Launch SteamVR\").clicked() {\n                                crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();\n                            }\n                        });\n                    })\n                });\n        }\n\n        ui.vertical_centered_justified(|ui| {\n            if let Some(clients) = &mut self.trusted_devices\n                && let Some(request) = wired_client_section(\n                    ui,\n                    clients\n                        .iter()\n                        .find(|(hostname, _)| hostname == WIRED_CLIENT_HOSTNAME),\n                    self.adb_download_progress,\n                )\n            {\n                requests.push(request);\n            }\n\n            ui.add_space(theme::FRAME_PADDING);\n\n            if let Some(clients) = &self.new_devices\n                && let Some(request) = new_clients_section(ui, clients)\n            {\n                requests.push(request);\n            }\n\n            ui.add_space(theme::FRAME_PADDING);\n\n            if let Some(clients) = &mut self.trusted_devices\n                && let Some(request) = trusted_clients_section(\n                    ui,\n                    clients\n                        .iter()\n                        .filter(|(hostname, _)| hostname != WIRED_CLIENT_HOSTNAME)\n                        .collect::<Vec<_>>()\n                        .as_slice(),\n                    &mut self.edit_popup_state,\n                )\n            {\n                requests.push(request);\n            }\n        });\n\n        if let Some(mut state) = self.edit_popup_state.take() {\n            Window::new(\"Edit connection\")\n                .anchor(Align2::CENTER_CENTER, (0.0, 0.0))\n                .resizable(false)\n                .collapsible(false)\n                .show(ui.ctx(), |ui| {\n                    ui.add_space(5.0);\n\n                    ui.columns(2, |ui| {\n                        ui[0].horizontal(|ui| {\n                            ui.add_space(5.0);\n                            ui.label(\"Hostname:\");\n                        });\n                        ui[1].add_enabled(\n                            state.new_devices,\n                            TextEdit::singleline(&mut state.hostname),\n                        );\n\n                        ui[0].horizontal(|ui| {\n                            ui.add_space(5.0);\n                            ui.label(\"IP Addresses:\");\n                        });\n                        for address in &mut state.ips {\n                            ui[1].text_edit_singleline(address);\n                        }\n                        if ui[1].button(\"Add new\").clicked() {\n                            state.ips.push(\"192.168.X.X\".into());\n                        }\n                    });\n\n                    ui.columns(2, |ui| {\n                        if ui[0].button(\"Cancel\").clicked() {\n                            return;\n                        }\n\n                        if ui[1].button(\"Save\").clicked() {\n                            let manual_ips =\n                                state.ips.iter().filter_map(|s| s.parse().ok()).collect();\n\n                            if state.new_devices {\n                                requests.push(ServerRequest::UpdateClientList {\n                                    hostname: state.hostname,\n                                    action: ClientConnectionsAction::AddIfMissing {\n                                        trusted: true,\n                                        manual_ips,\n                                    },\n                                });\n                            } else {\n                                requests.push(ServerRequest::UpdateClientList {\n                                    hostname: state.hostname,\n                                    action: ClientConnectionsAction::SetManualIps(manual_ips),\n                                });\n                            }\n                        } else {\n                            self.edit_popup_state = Some(state);\n                        }\n                    })\n                });\n        }\n\n        requests\n    }\n}\n\nfn wired_client_section(\n    ui: &mut Ui,\n    maybe_client: Option<&(String, ClientConnectionConfig)>,\n    adb_download_progress: Option<f32>,\n) -> Option<ServerRequest> {\n    let mut request = None;\n\n    Frame::group(ui.style())\n        .fill(theme::SECTION_BG)\n        .inner_margin(egui::vec2(\n            theme::FRAME_PADDING + theme::FRAME_TEXT_SPACING,\n            theme::FRAME_PADDING,\n        ))\n        .show(ui, |ui| {\n            ui.horizontal(|ui| {\n                Grid::new(\"wired-client\")\n                    .num_columns(2)\n                    .spacing(egui::vec2(8.0, 8.0))\n                    .show(ui, |ui| {\n                        ui.heading(\"Wired Connection\");\n                        ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                            let mut wired = maybe_client.is_some();\n\n                            if alvr_gui_common::switch(ui, &mut wired).changed() {\n                                if wired {\n                                    request = Some(ServerRequest::UpdateClientList {\n                                        hostname: WIRED_CLIENT_HOSTNAME.to_owned(),\n                                        action: ClientConnectionsAction::AddIfMissing {\n                                            trusted: true,\n                                            manual_ips: Vec::new(),\n                                        },\n                                    });\n                                } else {\n                                    request = Some(ServerRequest::UpdateClientList {\n                                        hostname: WIRED_CLIENT_HOSTNAME.to_owned(),\n                                        action: ClientConnectionsAction::RemoveEntry,\n                                    });\n                                }\n                            }\n                            ui.horizontal(|ui| {\n                                ui.add_space(theme::FRAME_TEXT_SPACING);\n                            });\n                        });\n                        ui.end_row();\n\n                        if let Some(progress) = adb_download_progress.filter(|p| *p < 1.0) {\n                            ui.horizontal(|ui| {\n                                ui.label(\"ADB download progress\");\n                            });\n                            ui.horizontal(|ui| {\n                                ui.add(ProgressBar::new(progress).animate(true).show_percentage());\n                            });\n                            ui.end_row();\n                        } else if let Some((_, data)) = maybe_client {\n                            ui.horizontal(|ui| {\n                                ui.label(&data.display_name);\n                            });\n                            ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                                connection_label(ui, &data.connection_state);\n                            });\n                            ui.end_row();\n                        }\n                    });\n            });\n        });\n\n    request\n}\n\nfn new_clients_section(\n    ui: &mut Ui,\n    clients: &[(String, ClientConnectionConfig)],\n) -> Option<ServerRequest> {\n    let mut request = None;\n\n    Frame::group(ui.style())\n        .inner_margin(theme::FRAME_PADDING)\n        .fill(theme::SECTION_BG)\n        .show(ui, |ui| {\n            ui.vertical_centered_justified(|ui| {\n                ui.horizontal(|ui| {\n                    ui.add_space(theme::FRAME_TEXT_SPACING);\n                    ui.heading(\"New Wireless Devices\");\n\n                    // Extend to the right\n                    ui.with_layout(Layout::right_to_left(Align::Center), |_| ());\n                });\n            });\n            for (hostname, _) in clients {\n                Frame::group(ui.style())\n                    .fill(theme::DARKER_BG)\n                    .inner_margin(egui::vec2(15.0, 12.0))\n                    .show(ui, |ui| {\n                        Grid::new(format!(\"{hostname}-new-clients\"))\n                            .num_columns(2)\n                            .spacing(egui::vec2(8.0, 8.0))\n                            .show(ui, |ui| {\n                                ui.horizontal(|ui| {\n                                    ui.label(hostname);\n                                });\n                                ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                                    if ui.button(\"Trust\").clicked() {\n                                        request = Some(ServerRequest::UpdateClientList {\n                                            hostname: hostname.clone(),\n                                            action: ClientConnectionsAction::Trust,\n                                        });\n                                    };\n                                });\n                                ui.end_row();\n                            });\n                    });\n            }\n        });\n\n    request\n}\n\nfn trusted_clients_section(\n    ui: &mut Ui,\n    clients: &[&(String, ClientConnectionConfig)],\n    edit_popup_state: &mut Option<EditPopupState>,\n) -> Option<ServerRequest> {\n    let mut request = None;\n\n    Frame::group(ui.style())\n        .fill(theme::SECTION_BG)\n        .inner_margin(theme::FRAME_PADDING)\n        .show(ui, |ui| {\n            Grid::new(0).num_columns(2).show(ui, |ui| {\n                ui.horizontal(|ui| {\n                    ui.add_space(theme::FRAME_TEXT_SPACING);\n                    ui.heading(\"Trusted Wireless Devices\");\n                });\n\n                ui.with_layout(Layout::right_to_left(eframe::emath::Align::Center), |ui| {\n                    if ui.button(\"Add device manually\").clicked() {\n                        *edit_popup_state = Some(EditPopupState {\n                            hostname: \"XXXX.client.local.\".into(),\n                            new_devices: true,\n                            ips: Vec::new(),\n                        });\n                    }\n                });\n            });\n\n            for (hostname, data) in clients {\n                Frame::group(ui.style())\n                    .fill(theme::DARKER_BG)\n                    .inner_margin(egui::vec2(15.0, 12.0))\n                    .show(ui, |ui| {\n                        Grid::new(format!(\"{hostname}-clients\"))\n                            .num_columns(2)\n                            .spacing(egui::vec2(8.0, 8.0))\n                            .show(ui, |ui| {\n                                ui.label(&data.display_name);\n                                ui.horizontal(|ui| {\n                                    ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                                        connection_label(ui, &data.connection_state)\n                                    });\n                                });\n\n                                ui.end_row();\n\n                                ui.label(format!(\n                                    \"{hostname}: {}\",\n                                    data.current_ip\n                                        .map_or_else(|| \"Unknown IP\".into(), |ip| ip.to_string()),\n                                ));\n                                ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                                    if ui.button(\"Remove\").clicked() {\n                                        request = Some(ServerRequest::UpdateClientList {\n                                            hostname: hostname.clone(),\n                                            action: ClientConnectionsAction::RemoveEntry,\n                                        });\n                                    }\n                                    if ui.button(\"Edit\").clicked() {\n                                        *edit_popup_state = Some(EditPopupState {\n                                            new_devices: false,\n                                            hostname: hostname.to_owned(),\n                                            ips: data\n                                                .manual_ips\n                                                .iter()\n                                                .map(|addr| addr.to_string())\n                                                .collect::<Vec<String>>(),\n                                        });\n                                    }\n                                });\n                            });\n                    });\n            }\n        });\n\n    request\n}\n\nfn connection_label(ui: &mut Ui, connection_state: &ConnectionState) {\n    match connection_state {\n        ConnectionState::Disconnected => ui.colored_label(Color32::GRAY, \"Disconnected\"),\n        ConnectionState::Connecting => ui.colored_label(log_colors::WARNING_LIGHT, \"Connecting\"),\n        ConnectionState::Connected => ui.colored_label(theme::OK_GREEN, \"Connected\"),\n        ConnectionState::Streaming => ui.colored_label(theme::OK_GREEN, \"Streaming\"),\n        ConnectionState::Disconnecting => {\n            ui.colored_label(log_colors::WARNING_LIGHT, \"Disconnecting\")\n        }\n    };\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/installation.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse alvr_gui_common::theme;\nuse eframe::egui::{Frame, Grid, RichText, ScrollArea, Ui};\nuse std::{\n    path::PathBuf,\n    time::{Duration, Instant},\n};\n\nconst DRIVER_UPDATE_INTERVAL: Duration = Duration::from_secs(1);\n\npub enum InstallationTabRequest {\n    OpenSetupWizard,\n    ServerRequest(ServerRequest),\n}\n\npub struct InstallationTab {\n    drivers: Vec<PathBuf>,\n    last_update_instant: Instant,\n}\n\nimpl InstallationTab {\n    pub fn new() -> Self {\n        Self {\n            drivers: vec![],\n            last_update_instant: Instant::now(),\n        }\n    }\n\n    pub fn update_drivers(&mut self, list: Vec<PathBuf>) {\n        self.drivers = list;\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Vec<InstallationTabRequest> {\n        let mut requests = vec![];\n\n        let now = Instant::now();\n        if now > self.last_update_instant + DRIVER_UPDATE_INTERVAL {\n            requests.push(InstallationTabRequest::ServerRequest(\n                ServerRequest::GetDriverList,\n            ));\n\n            self.last_update_instant = now;\n        }\n\n        ui.vertical_centered_justified(|ui| {\n            if ui.button(\"Run setup wizard\").clicked() {\n                requests.push(InstallationTabRequest::OpenSetupWizard);\n            }\n            ui.columns(2, |ui| {\n                if ui[0].button(\"Add firewall rules\").clicked() {\n                    requests.push(InstallationTabRequest::ServerRequest(\n                        ServerRequest::AddFirewallRules,\n                    ));\n                }\n                if ui[1].button(\"Remove firewall rules\").clicked() {\n                    requests.push(InstallationTabRequest::ServerRequest(\n                        ServerRequest::RemoveFirewallRules,\n                    ));\n                }\n            });\n\n            Frame::group(ui.style())\n                .fill(theme::SECTION_BG)\n                .inner_margin(theme::FRAME_PADDING)\n                .show(ui, |ui| {\n                    ui.vertical_centered_justified(|ui| {\n                        ui.label(RichText::new(\"Registered drivers\").size(18.0));\n                    });\n\n                    Grid::new(0).num_columns(2).show(ui, |ui| {\n                        for driver_path in &self.drivers {\n                            if ui.button(\"Remove\").clicked() {\n                                requests.push(InstallationTabRequest::ServerRequest(\n                                    ServerRequest::UnregisterDriver(driver_path.clone()),\n                                ));\n                            }\n\n                            ScrollArea::new([true, false])\n                                .auto_shrink([false, false])\n                                .id_salt(driver_path)\n                                .show(ui, |ui| {\n                                    ui.label(driver_path.to_string_lossy());\n                                });\n                            ui.end_row();\n                        }\n                    });\n\n                    if ui.button(\"Register ALVR driver\").clicked() {\n                        requests.push(InstallationTabRequest::ServerRequest(\n                            ServerRequest::RegisterAlvrDriver,\n                        ));\n                    }\n                });\n        });\n\n        requests\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/logs.rs",
    "content": "use alvr_common::LogSeverity;\nuse alvr_events::{Event, EventType};\nuse alvr_gui_common::theme::log_colors;\nuse alvr_session::{RawEventsConfig, Settings};\nuse eframe::{\n    egui::{Grid, OpenUrl, OutputCommand, RichText, ScrollArea, Ui},\n    epaint::Color32,\n};\nuse settings_schema::Switch;\nuse std::collections::VecDeque;\n\nstruct Entry {\n    color: Color32,\n    timestamp: String,\n    ty: String,\n    message: String,\n}\n\npub struct LogsTab {\n    raw_events_config: Switch<RawEventsConfig>,\n    entries: VecDeque<Entry>,\n    log_limit: usize,\n}\n\nimpl LogsTab {\n    pub fn new() -> Self {\n        Self {\n            raw_events_config: Switch::Enabled(RawEventsConfig {\n                hide_spammy_events: false,\n            }),\n            entries: VecDeque::new(),\n            log_limit: 1000,\n        }\n    }\n\n    pub fn update_settings(&mut self, settings: &Settings) {\n        self.raw_events_config = settings.extra.logging.show_raw_events.clone();\n    }\n\n    pub fn push_event(&mut self, event: Event) {\n        let color = if let EventType::Log(entry) = &event.event_type {\n            Some(match entry.severity {\n                LogSeverity::Error => log_colors::ERROR_LIGHT,\n                LogSeverity::Warning => log_colors::WARNING_LIGHT,\n                LogSeverity::Info => log_colors::INFO_LIGHT,\n                LogSeverity::Debug => log_colors::DEBUG_LIGHT,\n            })\n        } else if let Switch::Enabled(config) = &self.raw_events_config {\n            (!config.hide_spammy_events\n                || !matches!(\n                    event.event_type,\n                    EventType::StatisticsSummary(_)\n                        | EventType::GraphStatistics(_)\n                        | EventType::Tracking(_)\n                ))\n            .then_some(log_colors::EVENT_LIGHT)\n        } else {\n            None\n        };\n\n        if let Some(color) = color {\n            self.entries.push_back(Entry {\n                color,\n                timestamp: event.timestamp.clone(),\n                ty: event.event_type_string(),\n                message: event.message(),\n            });\n\n            if self.entries.len() > self.log_limit {\n                self.entries.pop_front();\n            }\n        }\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) {\n        ui.horizontal(|ui| {\n            if ui.button(\"Copy all\").clicked() {\n                ui.output_mut(|out| {\n                    out.commands\n                        .push(OutputCommand::CopyText(self.entries.iter().fold(\n                            String::new(),\n                            |acc, entry| {\n                                format!(\n                                    \"{}{} [{}] {}\\n\",\n                                    acc, entry.timestamp, entry.ty, entry.message\n                                )\n                            },\n                        )));\n                })\n            }\n            if ui.button(\"Open logs directory\").clicked() {\n                let log_dir = crate::get_filesystem_layout().log_dir;\n                ui.ctx().open_url(OpenUrl::same_tab(format!(\n                    \"file://{}\",\n                    log_dir.to_string_lossy()\n                )));\n            }\n            if ui.button(\"Clear all\").clicked() {\n                self.entries.clear();\n            }\n        });\n\n        ScrollArea::both()\n            .stick_to_bottom(true)\n            .auto_shrink([false, false])\n            .show(ui, |ui| {\n                Grid::new(0)\n                    .spacing((10.0, 2.0))\n                    .num_columns(3)\n                    .striped(true)\n                    .show(ui, |ui| {\n                        for entry in &self.entries {\n                            ui.colored_label(\n                                entry.color,\n                                RichText::new(&entry.timestamp).size(12.0),\n                            );\n                            ui.colored_label(entry.color, RichText::new(&entry.ty).size(12.0));\n                            ui.colored_label(entry.color, RichText::new(&entry.message).size(12.0));\n\n                            ui.end_row();\n                        }\n                    });\n            });\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/mod.rs",
    "content": "mod about;\nmod debug;\nmod devices;\nmod logs;\nmod new_version_popup;\nmod notifications;\nmod settings;\nmod settings_controls;\nmod setup_wizard;\nmod statistics;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nmod installation;\n\npub use about::*;\npub use debug::*;\npub use devices::*;\npub use logs::*;\npub use new_version_popup::*;\npub use notifications::*;\npub use settings::*;\npub use settings_controls::*;\npub use setup_wizard::*;\npub use statistics::*;\n\n#[cfg(not(target_arch = \"wasm32\"))]\npub use installation::*;\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/new_version_popup.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse alvr_gui_common::ModalButton;\nuse alvr_packets::PathValuePair;\nuse eframe::egui::{self, Context, OpenUrl, Ui};\nuse std::{path::PathBuf, process::Command};\n\npub enum CloseAction {\n    Close,\n    CloseWithRequest(ServerRequest),\n}\n\npub struct NewVersionPopup {\n    version: String,\n    message: String,\n    launcher_path: Option<PathBuf>,\n}\n\nimpl NewVersionPopup {\n    pub fn new(version: String, message: String) -> Self {\n        let mut launcher_path = None;\n\n        let layout = crate::get_filesystem_layout();\n        if let Some(path) = layout.launcher_exe()\n            && path.exists()\n        {\n            launcher_path = Some(path);\n        }\n\n        Self {\n            version,\n            message,\n            launcher_path,\n        }\n    }\n\n    pub fn ui(&self, context: &Context, shutdown_alvr_cb: impl Fn()) -> Option<CloseAction> {\n        let no_remind_button =\n            ModalButton::Custom(\"Don't remind me again for this version\".to_string());\n\n        let result = alvr_gui_common::modal(\n            context,\n            \"New ALVR version available\",\n            Some(|ui: &mut Ui| {\n                ui.horizontal(|ui| {\n                    ui.add_space(10.0);\n\n                    ui.vertical(|ui| {\n                        ui.heading(format!(\"ALVR v{}\", self.version));\n\n                        ui.horizontal(|ui| {\n                            ui.spacing_mut().item_spacing.x = 5.0;\n                            ui.style_mut().spacing.button_padding = egui::vec2(10.0, 4.0);\n\n                            ui.heading(\"You can download this version using the launcher:\");\n\n                            if let Some(path) = &self.launcher_path {\n                                if ui.button(\"Open Launcher\").clicked()\n                                    && Command::new(path).spawn().is_ok()\n                                {\n                                    shutdown_alvr_cb();\n                                }\n                            } else if ui.button(\"Download Launcher\").clicked() {\n                                let base_url =\n                                    \"https://github.com/alvr-org/ALVR/releases/latest/download/\";\n                                let file = if cfg!(windows) {\n                                    \"alvr_launcher_windows.zip\"\n                                } else {\n                                    \"alvr_launcher_linux.tar.gz\"\n                                };\n\n                                context.open_url(OpenUrl::new_tab(format!(\"{base_url}{file}\")));\n                            }\n                        });\n\n                        ui.add_space(10.0);\n\n                        ui.label(&self.message);\n                        ui.hyperlink_to(\n                            \"Releases page\",\n                            \"https://github.com/alvr-org/ALVR/releases\",\n                        );\n                    });\n\n                    ui.add_space(10.0);\n                });\n            }),\n            &[no_remind_button.clone(), ModalButton::Close],\n            Some(490.0),\n        );\n\n        if let Some(button) = result {\n            if button == no_remind_button {\n                Some(CloseAction::CloseWithRequest(\n                    ServerRequest::SetSessionValues(vec![PathValuePair {\n                        path: alvr_packets::parse_path(\n                            \"session_settings.extra.new_version_popup.content.hide_while_version\",\n                        ),\n                        value: serde_json::Value::String(self.version.clone()),\n                    }]),\n                ))\n            } else {\n                Some(CloseAction::Close)\n            }\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/notifications.rs",
    "content": "use alvr_common::{LogEntry, LogSeverity};\nuse alvr_gui_common::theme::{self, log_colors};\nuse alvr_session::Settings;\nuse eframe::{\n    egui::{self, Frame, Label, Layout, RichText, TextWrapMode, TopBottomPanel},\n    emath::Align,\n    epaint::Color32,\n};\nuse rand::seq::IndexedRandom;\nuse std::time::Duration;\n\n#[cfg(target_arch = \"wasm32\")]\nuse instant::Instant;\n#[cfg(not(target_arch = \"wasm32\"))]\nuse std::time::Instant;\n\nconst TIMEOUT: Duration = Duration::from_secs(5);\nconst NO_NOTIFICATIONS_MESSAGE: &str = \"No new notifications\";\nconst NOTIFICATION_TIPS: &[&str] = &[\n    // The following tips are ordered roughtly in the order settings appear\n    r#\"If you started having crashes after changing some settings, reset ALVR by re-running \"Run setup wizard\" from the \"Installation\" tab and clicking \"Reset settings\".\"#,\n    r#\"Some settings are hidden by default. Click the \"Expand\" button next to some settings to expand the submenus.\"#,\n    r#\"It's highly advisable to keep audio settings as default in ALVR and modify the default audio device in the taskbar tray.\"#,\n    r#\"Increasing \"Video\"->\"Maximum buffering\" may reduce stutters at the cost of more latency.\"#,\n    r#\"Sometimes switching between h264 and HEVC codecs is necessary on certain GPUs to fix crashing or fallback to software encoding.\"#,\n    r#\"If you're using an NVIDIA GPU, it's best to use high-bitrate H264; if you're using an AMD GPU, HEVC might look better.\"#,\n    r#\"If you experience \"white snow\" flickering, set \"Presets\"->\"Resolution\" to \"Low\" and disable \"Video\"->\"Foveated encoding\".\"#,\n    r#\"Increasing \"Video\"->\"Color correction\"->\"Sharpness\" may improve the perceived image quality.\"#,\n    r#\"If you have problems syncing external controllers or trackers to ALVR tracking space, add one element to \"Headset\"->\"Extra OpenVR properties\", then set a custom \"Tracking system name string\".\"#,\n    r#\"To change the visual appearance of controllers, set \"Headset\"->\"Controllers\"->\"Emulation mode\".\"#,\n    r#\"ALVR supports custom button bindings! If you need help, please ask us on our Discord server.\"#,\n    r#\"ALVR supports hand tracking gestures (\"Presets\"->\"Hand tracking interaction\"->\"ALVR bindings\"). Check out wiki how to use them properly: https://github.com/alvr-org/ALVR/wiki/Hand-tracking-controller-bindings.\"#,\n    r#\"If hand tracking gestures are annoying, you can disable them in \"Headset\"->\"Controllers\"->\"Hand tracking interaction\". Alternatively, you can enable \"Hand tracking interaction\"->\"Only touch\".\"#,\n    r#\"You can fine-tune the controllers' responsiveness with \"Headset\"->\"Controllers\"->\"Prediction\".\"#,\n    r#\"If the visual controller/hand models do not match the physical controller's position, you can tweak the offset in \"Headset\"->\"Controllers\"->\"Left controller position/rotation offset\" (affects both controllers).\"#,\n    r#\"When using external trackers or controllers, you should set both \"Headset\"->\"Position/Rotation recentering mode\" to \"Disabled\".\"#,\n    r#\"You can enable tilt mode. Set \"Headset\"->\"Position recentering mode\" to \"Local\" and \"Headset\"->\"Rotation recentering mode\" to \"Tilted\".\"#,\n    r#\"If you often experience image glitching, you can trade that with stutter frames using \"Connection\"->\"Avoid video glitching\".\"#,\n    r#\"You can run custom commands/programs at headset connection/disconnection using \"Connection\"->\"Enable on connect/disconnect script\".\"#,\n    r#\"In case you want to report a bug, to get a log file, enable \"Extra\"->\"Logging\"->\"Log to disk\". The log will be inside \"session_log.txt\".\"#,\n    r#\"For hacking purposes, you can enable \"Extra\"->\"Logging\"->\"Log tracking\", \"Log button presses\" and \"Log haptics\". You can get the data using a websocket at ws://localhost:8082/api/events.\"#,\n    r#\"In case you want to report a bug and share your log, you should enable \"Extra\"->\"Logging\"->\"Prefer backtrace\".\"#,\n    r#\"You can quickly cycle through tips like this one by toggling \"Extra\"->\"Logging\"->\"Show notification tip\".\"#,\n    r#\"It's handy to enable \"Extra\"->\"SteamVR Launcher\"->\"Open and close SteamVR automatically\".\"#,\n    r#\"If you want to share a video recording for reporting a bug, you can enable \"Extra\"->\"Capture\"->\"Rolling video files\" to limit the file size of the upload.\"#,\n    // Miscellaneous\n    r#\"If your headset does not appear in the device list, it might be in a different subnet. Try \"Add device manually\" with IP shown from inside device.\"#,\n];\n\npub struct NotificationBar {\n    message: String,\n    current_level: LogSeverity,\n    receive_instant: Instant,\n    min_notification_level: LogSeverity,\n    tip_message: Option<String>,\n    expanded: bool,\n}\n\nimpl NotificationBar {\n    pub fn new() -> Self {\n        Self {\n            message: NO_NOTIFICATIONS_MESSAGE.into(),\n            current_level: LogSeverity::Debug,\n            receive_instant: Instant::now(),\n            min_notification_level: LogSeverity::Debug,\n            tip_message: None,\n            expanded: false,\n        }\n    }\n\n    pub fn update_settings(&mut self, settings: &Settings) {\n        self.min_notification_level = settings.extra.logging.notification_level;\n\n        if settings.extra.logging.show_notification_tip {\n            if self.tip_message.is_none() {\n                self.tip_message = NOTIFICATION_TIPS\n                    .choose(&mut rand::rng())\n                    .map(|s| format!(\"Tip: {s}\"));\n            }\n        } else {\n            self.tip_message = None;\n        }\n    }\n\n    pub fn push_notification(&mut self, event: LogEntry, from_dashboard: bool) {\n        let now = Instant::now();\n        let min_severity = if from_dashboard {\n            if cfg!(debug_assertions) {\n                LogSeverity::Debug\n            } else {\n                LogSeverity::Info\n            }\n        } else {\n            self.min_notification_level\n        };\n\n        if event.severity >= min_severity\n            && (now > self.receive_instant + TIMEOUT || event.severity >= self.current_level)\n        {\n            self.message = event.content;\n            self.current_level = event.severity;\n            self.receive_instant = now;\n        }\n    }\n\n    pub fn ui(&mut self, context: &egui::Context) {\n        let now = Instant::now();\n        if now > self.receive_instant + TIMEOUT {\n            self.message = self\n                .tip_message\n                .clone()\n                .unwrap_or_else(|| NO_NOTIFICATIONS_MESSAGE.into());\n            self.current_level = LogSeverity::Debug;\n        }\n\n        let (fg, bg) = match self.current_level {\n            LogSeverity::Error => (Color32::BLACK, log_colors::ERROR_LIGHT),\n            LogSeverity::Warning => (Color32::BLACK, log_colors::WARNING_LIGHT),\n            LogSeverity::Info => (Color32::BLACK, log_colors::INFO_LIGHT),\n            LogSeverity::Debug => (theme::FG, theme::LIGHTER_BG),\n        };\n\n        let mut bottom_bar = TopBottomPanel::bottom(\"bottom_panel\").frame(\n            Frame::default()\n                .inner_margin(egui::vec2(10.0, 5.0))\n                .fill(bg),\n        );\n        let alignment = if !self.expanded {\n            bottom_bar = bottom_bar.max_height(26.0);\n\n            Align::TOP\n        } else {\n            Align::Center\n        };\n        let wrapping = if !self.expanded {\n            TextWrapMode::Truncate\n        } else {\n            TextWrapMode::Wrap\n        };\n\n        bottom_bar.show(context, |ui| {\n            ui.with_layout(Layout::right_to_left(alignment), |ui| {\n                if !self.expanded {\n                    if ui.small_button(\"Expand\").clicked() {\n                        self.expanded = true;\n                    }\n                } else if ui.button(\"Reduce\").clicked() {\n                    self.expanded = false;\n                }\n                ui.with_layout(Layout::left_to_right(alignment), |ui| {\n                    //A LayoutJob that has its TextWrapping updated to fill the available space would probably be a more elegant solution.\n                    ui.add(\n                        Label::new(RichText::new(&self.message).color(fg).size(12.0))\n                            .wrap_mode(wrapping),\n                    );\n                })\n            })\n        });\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings.rs",
    "content": "use super::{\n    NestingInfo, SettingControl,\n    presets::{PresetControl, builtin_schema},\n};\nuse crate::dashboard::ServerRequest;\nuse alvr_gui_common::{DisplayString, theme};\nuse alvr_session::{SessionSettings, Settings};\nuse eframe::egui::{Align, Frame, Grid, Layout, RichText, ScrollArea, Ui};\n#[cfg(target_arch = \"wasm32\")]\nuse instant::Instant;\nuse serde_json as json;\nuse settings_schema::SchemaNode;\nuse std::time::Duration;\n#[cfg(not(target_arch = \"wasm32\"))]\nuse std::time::Instant;\n\nconst DATA_UPDATE_INTERVAL: Duration = Duration::from_secs(1);\nconst MIN_COLUMN_SIZE: f32 = 300.0;\n\nstruct TopLevelEntry {\n    id: DisplayString,\n    control: SettingControl,\n}\n\npub struct SettingsTab {\n    selected_top_tab_id: String,\n    resolution_preset: PresetControl,\n    framerate_preset: PresetControl,\n    encoder_preset: PresetControl,\n    foveation_preset: PresetControl,\n    codec_preset: PresetControl,\n    game_audio_preset: PresetControl,\n    microphone_preset: PresetControl,\n    hand_tracking_interaction_preset: PresetControl,\n    eye_face_tracking_preset: PresetControl,\n    top_level_entries: Vec<TopLevelEntry>,\n    session_settings_json: Option<json::Value>,\n    last_update_instant: Instant,\n}\n\nimpl SettingsTab {\n    pub fn new() -> Self {\n        let nesting_info = NestingInfo {\n            path: vec![\"session_settings\".into()],\n            indentation_level: 0,\n        };\n        let schema = Settings::schema(alvr_session::session_settings_default());\n\n        // Top level node must be a section\n        let SchemaNode::Section { entries, .. } = schema else {\n            unreachable!();\n        };\n\n        let top_level_entries = entries\n            .into_iter()\n            .map(|entry| {\n                let id = entry.name;\n                let display = super::get_display_name(&id, &entry.strings);\n\n                let mut nesting_info = nesting_info.clone();\n                nesting_info.path.push(id.clone().into());\n\n                TopLevelEntry {\n                    id: DisplayString { id, display },\n                    control: SettingControl::new(nesting_info, entry.content),\n                }\n            })\n            .collect();\n\n        Self {\n            selected_top_tab_id: \"presets\".into(),\n            resolution_preset: PresetControl::new(builtin_schema::resolution_schema()),\n            framerate_preset: PresetControl::new(builtin_schema::framerate_schema()),\n            encoder_preset: PresetControl::new(builtin_schema::encoder_preset_schema()),\n            foveation_preset: PresetControl::new(builtin_schema::foveation_preset_schema()),\n            codec_preset: PresetControl::new(builtin_schema::codec_preset_schema()),\n            game_audio_preset: PresetControl::new(builtin_schema::game_audio_schema()),\n            microphone_preset: PresetControl::new(builtin_schema::microphone_schema()),\n            hand_tracking_interaction_preset: PresetControl::new(\n                builtin_schema::hand_tracking_interaction_schema(),\n            ),\n            eye_face_tracking_preset: PresetControl::new(builtin_schema::eye_face_tracking_schema()),\n            top_level_entries,\n            session_settings_json: None,\n            last_update_instant: Instant::now(),\n        }\n    }\n\n    pub fn update_session(&mut self, session_settings: &SessionSettings) {\n        let settings_json = json::to_value(session_settings).unwrap();\n\n        self.resolution_preset\n            .update_session_settings(&settings_json);\n        self.framerate_preset\n            .update_session_settings(&settings_json);\n        self.encoder_preset.update_session_settings(&settings_json);\n        self.foveation_preset\n            .update_session_settings(&settings_json);\n        self.codec_preset.update_session_settings(&settings_json);\n        self.game_audio_preset\n            .update_session_settings(&settings_json);\n        self.microphone_preset\n            .update_session_settings(&settings_json);\n        self.hand_tracking_interaction_preset\n            .update_session_settings(&settings_json);\n        self.eye_face_tracking_preset\n            .update_session_settings(&settings_json);\n\n        self.session_settings_json = Some(settings_json);\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Vec<ServerRequest> {\n        let mut requests = vec![];\n\n        let now = Instant::now();\n        if now > self.last_update_instant + DATA_UPDATE_INTERVAL {\n            if self.session_settings_json.is_none() {\n                requests.push(ServerRequest::GetSession);\n            }\n\n            self.last_update_instant = now;\n        }\n\n        let mut path_value_pairs = vec![];\n        ui.with_layout(Layout::left_to_right(Align::Min), |ui| {\n            Frame::group(ui.style())\n                .fill(theme::DARKER_BG)\n                .inner_margin(theme::FRAME_PADDING)\n                .show(ui, |ui| {\n                    ui.horizontal_wrapped(|ui| {\n                        ui.selectable_value(\n                            &mut self.selected_top_tab_id,\n                            \"presets\".into(),\n                            RichText::new(\"Presets\").raised().size(15.0),\n                        );\n                        for entry in &mut self.top_level_entries {\n                            ui.selectable_value(\n                                &mut self.selected_top_tab_id,\n                                entry.id.id.clone(),\n                                RichText::new(entry.id.display.clone()).raised().size(15.0),\n                            );\n                        }\n                    })\n                })\n        });\n\n        if self.selected_top_tab_id == \"presets\" {\n            ScrollArea::new([false, true])\n                .id_salt(\"presets_scroll\")\n                .show(ui, |ui| {\n                    Grid::new(\"presets_grid\")\n                        .striped(true)\n                        .num_columns(2)\n                        .min_col_width(MIN_COLUMN_SIZE)\n                        .show(ui, |ui| {\n                            path_value_pairs.extend(self.resolution_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.framerate_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.encoder_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.foveation_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.codec_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.game_audio_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.microphone_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.hand_tracking_interaction_preset.ui(ui));\n                            ui.end_row();\n\n                            path_value_pairs.extend(self.eye_face_tracking_preset.ui(ui));\n                            ui.end_row();\n                        })\n                });\n        } else {\n            ScrollArea::new([false, true])\n                .id_salt(format!(\"{}_scroll\", self.selected_top_tab_id))\n                .show(ui, |ui| {\n                    Grid::new(format!(\"{}_grid\", self.selected_top_tab_id))\n                        .striped(true)\n                        .num_columns(2)\n                        .min_col_width(MIN_COLUMN_SIZE)\n                        .show(ui, |ui| {\n                            if let Some(session_fragment) = &mut self.session_settings_json {\n                                let session_fragments_mut =\n                                    session_fragment.as_object_mut().unwrap();\n\n                                let entry = self\n                                    .top_level_entries\n                                    .iter_mut()\n                                    .find(|entry: &&mut TopLevelEntry| {\n                                        entry.id.id == self.selected_top_tab_id\n                                    })\n                                    .unwrap();\n\n                                let response = entry.control.ui(\n                                    ui,\n                                    &mut session_fragments_mut[&entry.id.id],\n                                    false,\n                                );\n\n                                if let Some(response) = response {\n                                    path_value_pairs.push(response);\n                                }\n\n                                ui.end_row();\n                            }\n                        })\n                });\n        }\n\n        if !path_value_pairs.is_empty() {\n            requests.push(ServerRequest::SetSessionValues(path_value_pairs));\n        }\n\n        requests\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/array.rs",
    "content": "use super::{NestingInfo, SettingControl, collapsible};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::egui::Ui;\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    controls: Vec<SettingControl>,\n}\n\nimpl Control {\n    pub fn new(nesting_info: NestingInfo, schema_array: Vec<SchemaNode>) -> Self {\n        let controls = schema_array\n            .into_iter()\n            .enumerate()\n            .map(|(idx, schema)| {\n                let mut nesting_info = nesting_info.clone();\n                nesting_info.path.push(\"content\".into());\n                nesting_info.path.push(idx.into());\n\n                SettingControl::new(nesting_info, schema)\n            })\n            .collect();\n\n        Self {\n            nesting_info,\n            controls,\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let mut request = None;\n\n        let collapsed =\n            collapsible::collapsible_button(ui, &self.nesting_info, session_fragment, &mut request);\n\n        if !collapsed {\n            let session_array_mut = session_fragment[\"content\"].as_array_mut().unwrap();\n\n            for (idx, control) in self.controls.iter_mut().enumerate() {\n                ui.end_row();\n\n                request = control\n                    .ui(ui, &mut session_array_mut[idx], false)\n                    .or(request);\n            }\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/boolean.rs",
    "content": "use super::{NestingInfo, reset};\nuse alvr_packets::PathValuePair;\nuse eframe::{\n    egui::{Layout, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default: bool,\n    default_string: String,\n}\n\nimpl Control {\n    pub fn new(nesting_info: NestingInfo, default: bool) -> Self {\n        let default_string = if default { \"ON\".into() } else { \"OFF\".into() };\n\n        Self {\n            nesting_info,\n            default,\n            default_string,\n        }\n    }\n\n    pub fn ui(\n        &self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let json::Value::Bool(enabled_mut) = session_fragment else {\n            unreachable!()\n        };\n\n        let mut request = None;\n\n        fn get_request(nesting_info: &NestingInfo, enabled: bool) -> Option<PathValuePair> {\n            Some(PathValuePair {\n                path: nesting_info.path.clone(),\n                value: json::Value::Bool(enabled),\n            })\n        }\n\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            if alvr_gui_common::switch(ui, enabled_mut).clicked() {\n                request = get_request(&self.nesting_info, *enabled_mut);\n            }\n\n            if reset::reset_button(ui, *enabled_mut != self.default, &self.default_string).clicked()\n            {\n                request = get_request(&self.nesting_info, self.default);\n            }\n        });\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/choice.rs",
    "content": "use super::{NestingInfo, SettingControl, reset};\nuse alvr_gui_common::DisplayString;\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::{ChoiceControlType, SchemaEntry, SchemaNode};\nuse eframe::{\n    egui::{ComboBox, Layout, Ui},\n    emath::Align,\n};\nuse serde_json as json;\nuse std::collections::HashMap;\n\nfn get_display_name(id: &str, strings: &HashMap<String, String>) -> String {\n    strings.get(\"display_name\").cloned().unwrap_or_else(|| {\n        let mut chars = id.chars();\n\n        let mut new_chars = vec![chars.next().unwrap()];\n        for c in chars {\n            let new_c = c.to_ascii_lowercase();\n\n            if new_c != c {\n                new_chars.push(' ');\n            }\n\n            new_chars.push(new_c);\n        }\n\n        new_chars.into_iter().collect::<String>()\n    })\n}\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default_variant: String,\n    default_string: String,\n    variant_labels: Vec<DisplayString>,\n    variant_indices: HashMap<String, usize>,\n    variant_controls: HashMap<String, SettingControl>,\n    gui: ChoiceControlType,\n    combobox_id: usize,\n}\n\nimpl Control {\n    pub fn new(\n        nesting_info: NestingInfo,\n        default: String,\n        schema_variants: Vec<SchemaEntry<Option<SchemaNode>>>,\n        gui: Option<ChoiceControlType>,\n    ) -> Self {\n        let variant_labels = schema_variants\n            .iter()\n            .map(|entry| DisplayString {\n                id: entry.name.clone(),\n                display: get_display_name(&entry.name, &entry.strings),\n            })\n            .collect::<Vec<_>>();\n\n        let default_string = format!(\n            \"\\\"{}\\\"\",\n            variant_labels\n                .iter()\n                .find(|d| d.id == default)\n                .cloned()\n                .unwrap()\n                .display\n        );\n\n        let variant_indices = schema_variants\n            .iter()\n            .enumerate()\n            .map(|(idx, entry)| (entry.name.clone(), idx))\n            .collect();\n\n        let variant_controls = schema_variants\n            .into_iter()\n            .map(|entry| {\n                let mut nesting_info = nesting_info.clone();\n                nesting_info.path.push(entry.name.clone().into());\n\n                let control = if let Some(schema) = entry.content {\n                    SettingControl::new(nesting_info, schema)\n                } else {\n                    SettingControl::None\n                };\n\n                (entry.name, control)\n            })\n            .collect();\n\n        Self {\n            nesting_info,\n            default_variant: default,\n            default_string,\n            variant_labels,\n            variant_indices,\n            variant_controls,\n            gui: gui.unwrap_or(ChoiceControlType::Dropdown),\n            combobox_id: alvr_gui_common::get_id(),\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let session_variants_mut = session_fragment.as_object_mut().unwrap();\n        let json::Value::String(variant_mut) = &mut session_variants_mut[\"variant\"] else {\n            unreachable!()\n        };\n\n        fn get_request(nesting_info: &NestingInfo, variant: &str) -> Option<PathValuePair> {\n            super::get_single_value(\n                nesting_info,\n                \"variant\".into(),\n                json::Value::String(variant.to_owned()),\n            )\n        }\n\n        let mut request = None;\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            if matches!(&self.gui, ChoiceControlType::ButtonGroup) {\n                if alvr_gui_common::button_group_clicked(ui, &self.variant_labels, variant_mut) {\n                    request = get_request(&self.nesting_info, variant_mut);\n                }\n            } else if let Some(mut index) = self.variant_indices.get(variant_mut).cloned() {\n                let response = ComboBox::from_id_salt(self.combobox_id).show_index(\n                    ui,\n                    &mut index,\n                    self.variant_labels.len(),\n                    |idx| self.variant_labels[idx].display.clone(),\n                );\n                if response.changed() {\n                    variant_mut.clone_from(&self.variant_labels[index].id);\n                    request = get_request(&self.nesting_info, variant_mut);\n                }\n\n                if cfg!(debug_assertions) {\n                    response.on_hover_text(&self.variant_labels[index].id);\n                }\n            } else {\n                let mut index = 0;\n                let response = ComboBox::from_id_salt(self.combobox_id).show_index(\n                    ui,\n                    &mut index,\n                    self.variant_labels.len() + 1,\n                    |idx| {\n                        if idx == 0 {\n                            \"Preset not applied\".into()\n                        } else {\n                            self.variant_labels[idx - 1].display.clone()\n                        }\n                    },\n                );\n                if response.changed() {\n                    variant_mut.clone_from(&self.variant_labels[index].id);\n                    request = get_request(&self.nesting_info, variant_mut);\n                }\n            }\n\n            if reset::reset_button(\n                ui,\n                *variant_mut != self.default_variant,\n                &self.default_string,\n            )\n            .clicked()\n            {\n                request = get_request(&self.nesting_info, &self.default_variant);\n            }\n        });\n\n        if let Some(control) = self.variant_controls.get_mut(&*variant_mut)\n            && !matches!(control, SettingControl::None)\n        {\n            ui.end_row();\n\n            //fixes \"cannot borrow `*session_variants` as mutable more than once at a time\"\n            let variant = variant_mut.clone();\n            request = control\n                .ui(ui, &mut session_variants_mut[&variant], false)\n                .or(request);\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/collapsible.rs",
    "content": "use super::NestingInfo;\nuse alvr_packets::PathValuePair;\nuse eframe::egui::Ui;\nuse serde_json as json;\n\npub fn collapsible_button(\n    ui: &mut Ui,\n    nesting_info: &NestingInfo,\n    session_fragment: &mut json::Value,\n    request: &mut Option<PathValuePair>,\n) -> bool {\n    let json::Value::Bool(state_mut) = &mut session_fragment[\"gui_collapsed\"] else {\n        unreachable!()\n    };\n\n    if (*state_mut && ui.small_button(\"Expand\").clicked())\n        || (!*state_mut && ui.small_button(\"Collapse\").clicked())\n    {\n        *state_mut = !*state_mut;\n        *request = super::get_single_value(\n            nesting_info,\n            \"gui_collapsed\".into(),\n            json::Value::Bool(*state_mut),\n        );\n    }\n\n    *state_mut\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/dictionary.rs",
    "content": "use super::{INDENTATION_STEP, NestingInfo, SettingControl, reset};\nuse crate::dashboard::components::{\n    collapsible,\n    up_down::{self, UpDownResult},\n};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::{\n    egui::{Layout, TextEdit, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\nstruct Entry {\n    editing_key: Option<String>,\n    control: SettingControl,\n}\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default_key: String,\n    default_value: SchemaNode,\n    default: Vec<json::Value>,\n    controls: Vec<Entry>,\n}\n\nimpl Control {\n    pub fn new(\n        nesting_info: NestingInfo,\n        default_key: String,\n        default_value: SchemaNode,\n        default: Vec<(String, json::Value)>,\n    ) -> Self {\n        Self {\n            nesting_info,\n            default_key,\n            default_value,\n            default: default\n                .into_iter()\n                .map(|pair| json::to_value(pair).unwrap())\n                .collect(),\n            controls: vec![],\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        fn get_content_request(\n            nesting_info: &NestingInfo,\n            entries: Vec<json::Value>,\n        ) -> Option<PathValuePair> {\n            super::get_single_value(\n                nesting_info,\n                \"content\".into(),\n                json::to_value(entries).unwrap(),\n            )\n        }\n\n        let mut request = None;\n        let collapsed = ui\n            .with_layout(Layout::left_to_right(Align::Center), |ui| {\n                let collapsed = collapsible::collapsible_button(\n                    ui,\n                    &self.nesting_info,\n                    session_fragment,\n                    &mut request,\n                );\n\n                if reset::reset_button(ui, true, \"default list\").clicked() {\n                    request = get_content_request(&self.nesting_info, self.default.clone())\n                }\n\n                collapsed\n            })\n            .inner;\n\n        let session_content = session_fragment[\"content\"].as_array_mut().unwrap();\n\n        while session_content.len() > self.controls.len() {\n            let mut nesting_info = self.nesting_info.clone();\n            nesting_info.path.extend_from_slice(&[\n                \"content\".into(),\n                self.controls.len().into(),\n                1.into(),\n            ]);\n\n            self.controls.push(Entry {\n                editing_key: None,\n                control: SettingControl::new(nesting_info, self.default_value.clone()),\n            });\n        }\n        while session_content.len() < self.controls.len() {\n            self.controls.pop();\n        }\n\n        if !collapsed {\n            ui.end_row();\n\n            let mut idx = 0;\n            while idx < self.controls.len() {\n                let delete_entry = ui\n                    .horizontal(|ui| {\n                        ui.add_space(INDENTATION_STEP * self.nesting_info.indentation_level as f32);\n\n                        let delete_entry = ui.button(\"❌\").clicked();\n\n                        let up_down_result = up_down::up_down_buttons(ui, idx, self.controls.len());\n\n                        if up_down_result != UpDownResult::None {\n                            if up_down_result == UpDownResult::Up {\n                                session_content.swap(idx, idx - 1);\n                            } else {\n                                session_content.swap(idx, idx + 1);\n                            }\n\n                            request =\n                                get_content_request(&self.nesting_info, session_content.clone());\n                        }\n\n                        let json::Value::String(text_mut) = &mut session_content[idx][0] else {\n                            unreachable!()\n                        };\n\n                        let editing_key_mut = &mut self.controls[idx].editing_key;\n\n                        let textbox = if let Some(editing_key_mut) = editing_key_mut {\n                            TextEdit::singleline(editing_key_mut)\n                        } else {\n                            TextEdit::singleline(text_mut)\n                        };\n\n                        let response = ui.add(textbox.desired_width(f32::INFINITY));\n                        if response.lost_focus() {\n                            if let Some(editing_key_mut) = editing_key_mut {\n                                let mut nesting_info = self.nesting_info.clone();\n                                nesting_info\n                                    .path\n                                    .extend_from_slice(&[\"content\".into(), idx.into()]);\n\n                                request = super::get_single_value(\n                                    &nesting_info,\n                                    0.into(),\n                                    json::Value::String(editing_key_mut.clone()),\n                                );\n\n                                text_mut.clone_from(editing_key_mut);\n                            }\n\n                            *editing_key_mut = None;\n                        }\n                        if response.gained_focus() {\n                            *editing_key_mut = Some(text_mut.clone());\n                        };\n\n                        delete_entry\n                    })\n                    .inner;\n\n                if delete_entry {\n                    session_content.remove(idx);\n                    self.controls.remove(idx);\n\n                    request = get_content_request(&self.nesting_info, session_content.clone());\n                } else {\n                    request = self.controls[idx]\n                        .control\n                        .ui(ui, &mut session_content[idx][1], true)\n                        .or(request);\n                }\n\n                ui.end_row();\n\n                idx += 1;\n            }\n\n            ui.label(\" \");\n            if ui.button(\"Add entry\").clicked() {\n                let mut session_content =\n                    session_fragment[\"content\"].as_array_mut().unwrap().clone();\n                session_content.push(json::Value::Array(vec![\n                    json::Value::String(self.default_key.clone()),\n                    session_fragment[\"value\"].clone(),\n                ]));\n\n                request = get_content_request(&self.nesting_info, session_content);\n            }\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/mod.rs",
    "content": "pub mod array;\npub mod boolean;\npub mod choice;\npub mod collapsible;\npub mod dictionary;\npub mod notice;\npub mod number;\npub mod optional;\npub mod presets;\npub mod reset;\npub mod section;\npub mod switch;\npub mod text;\npub mod up_down;\npub mod vector;\n\nuse alvr_packets::{PathSegment, PathValuePair};\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::egui::Ui;\nuse serde_json as json;\nuse std::collections::HashMap;\n\npub const INDENTATION_STEP: f32 = 20.0;\n\nfn get_single_value(\n    nesting_info: &NestingInfo,\n    leaf: PathSegment,\n    new_value: json::Value,\n) -> Option<PathValuePair> {\n    let mut path = nesting_info.path.clone();\n    path.push(leaf);\n\n    Some(PathValuePair {\n        path,\n        value: new_value,\n    })\n}\n\nfn grid_flow_inline(ui: &mut Ui, allow_inline: bool) {\n    if !allow_inline {\n        // Note: ui.add_space() does not work\n        ui.label(\" \");\n    }\n}\n\npub fn get_display_name(id: &str, strings: &HashMap<String, String>) -> String {\n    strings.get(\"display_name\").cloned().unwrap_or_else(|| {\n        let mut chars = id.chars();\n        chars.next().unwrap().to_uppercase().collect::<String>()\n            + chars.as_str().replace('_', \" \").as_str()\n    })\n}\n\npub fn f64_eq(f1: f64, f2: f64) -> bool {\n    f64::abs(f1 - f2) < f32::EPSILON as f64\n    // Alternative solution:\n    // format!(\"{:.6}\", f1) == format!(\"{:.6}\", f2)\n}\n\npub fn json_values_eq(a: &serde_json::Value, b: &serde_json::Value) -> bool {\n    // Note: is_f64() will exclude integers, while just as_f64() will silently do the conversion\n    if let serde_json::Value::Number(n1) = a\n        && let serde_json::Value::Number(n2) = b\n        && (n1.is_f64() || n2.is_f64())\n        && let Some(f1) = n1.as_f64()\n        && let Some(f2) = n2.as_f64()\n    {\n        f64_eq(f1, f2)\n    } else {\n        a == b\n    }\n}\n\n#[derive(Clone)]\npub struct NestingInfo {\n    pub path: Vec<PathSegment>,\n    pub indentation_level: usize,\n}\n\npub enum SettingControl {\n    Section(section::Control),\n    Choice(choice::Control),\n    Optional(optional::Control),\n    Switch(switch::Control),\n    Boolean(boolean::Control),\n    Text(text::Control),\n    Numeric(number::Control),\n    Array(array::Control),\n    Vector(vector::Control),\n    Dictionary(dictionary::Control),\n    None,\n}\n\nimpl SettingControl {\n    pub fn new(nesting_info: NestingInfo, schema: SchemaNode) -> Self {\n        match schema {\n            SchemaNode::Section {\n                entries,\n                gui_collapsible,\n            } => Self::Section(section::Control::new(\n                nesting_info,\n                entries,\n                gui_collapsible,\n            )),\n            SchemaNode::Choice {\n                default,\n                variants,\n                gui,\n            } => Self::Choice(choice::Control::new(nesting_info, default, variants, gui)),\n            SchemaNode::Optional {\n                default_set,\n                content,\n            } => Self::Optional(optional::Control::new(nesting_info, default_set, *content)),\n            SchemaNode::Switch {\n                default_enabled,\n                content,\n            } => Self::Switch(switch::Control::new(\n                nesting_info,\n                default_enabled,\n                *content,\n            )),\n            SchemaNode::Boolean { default } => {\n                Self::Boolean(boolean::Control::new(nesting_info, default))\n            }\n            SchemaNode::Number {\n                default,\n                ty,\n                gui,\n                suffix,\n            } => Self::Numeric(number::Control::new(nesting_info, default, ty, gui, suffix)),\n            SchemaNode::Text { default } => Self::Text(text::Control::new(nesting_info, default)),\n            SchemaNode::Array(schema_array) => {\n                Self::Array(array::Control::new(nesting_info, schema_array))\n            }\n            SchemaNode::Vector {\n                default_element,\n                default,\n            } => Self::Vector(vector::Control::new(\n                nesting_info,\n                *default_element,\n                default,\n            )),\n            SchemaNode::Dictionary {\n                default_key,\n                default_value,\n                default,\n            } => Self::Dictionary(dictionary::Control::new(\n                nesting_info,\n                default_key,\n                *default_value,\n                default,\n            )),\n            _ => Self::None,\n        }\n    }\n\n    // inline: first field child, could be rendered beside the field label\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        match self {\n            Self::Section(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Choice(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Optional(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Switch(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Boolean(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Text(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Numeric(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Array(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Vector(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::Dictionary(control) => control.ui(ui, session_fragment, allow_inline),\n            Self::None => {\n                grid_flow_inline(ui, allow_inline);\n                ui.add_enabled_ui(false, |ui| ui.label(\"Unimplemented UI\"));\n\n                None\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/notice.rs",
    "content": "use alvr_gui_common::theme::{self, log_colors};\nuse eframe::{\n    egui::{Frame, Label, RichText, Ui},\n    epaint::Color32,\n};\n\npub fn notice(ui: &mut Ui, text: &str) {\n    Frame::group(ui.style())\n        .fill(log_colors::WARNING_LIGHT)\n        .corner_radius(theme::CORNER_RADIUS)\n        .show(ui, |ui| {\n            ui.add(Label::new(RichText::new(text).size(11.0).color(Color32::BLACK)).wrap());\n        });\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/number.rs",
    "content": "use super::{NestingInfo, reset};\nuse crate::dashboard::components::f64_eq;\nuse alvr_gui_common::theme::SCROLLBAR_DOT_DIAMETER;\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::{NumberType, NumericGuiType};\nuse eframe::{\n    egui::{DragValue, Layout, Slider, Ui},\n    emath::Align,\n};\nuse json::Number;\nuse serde_json as json;\n\nfn to_json_value(number: f64, ty: NumberType) -> json::Value {\n    match ty {\n        NumberType::UnsignedInteger => json::Value::from(number.abs() as u64),\n        NumberType::SignedInteger => json::Value::from(number as i64),\n        NumberType::Float => json::Value::Number(Number::from_f64(number).unwrap()),\n    }\n}\n\npub struct Control {\n    nesting_info: NestingInfo,\n    editing_value_f64: Option<f64>,\n    default: f64,\n    default_string: String,\n    ty: NumberType,\n    gui_type: NumericGuiType,\n    suffix: Option<String>,\n}\n\nimpl Control {\n    pub fn new(\n        nesting_info: NestingInfo,\n        default: f64,\n        ty: NumberType,\n        gui: NumericGuiType,\n        suffix: Option<String>,\n    ) -> Self {\n        let default_string = format!(\"{default}{}\", suffix.clone().unwrap_or_default());\n\n        Self {\n            nesting_info,\n            editing_value_f64: None,\n            default,\n            default_string,\n            ty,\n            gui_type: gui,\n            suffix,\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let mut session_value = session_fragment.as_f64().unwrap();\n\n        let mut request = None;\n\n        fn get_request(\n            nesting_info: &NestingInfo,\n            number: f64,\n            ty: NumberType,\n        ) -> Option<PathValuePair> {\n            Some(PathValuePair {\n                path: nesting_info.path.clone(),\n                value: to_json_value(number, ty),\n            })\n        }\n\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            let editing_value_mut = if let Some(editing_value_mut) = &mut self.editing_value_f64 {\n                editing_value_mut\n            } else {\n                &mut session_value\n            };\n\n            let response = match &self.gui_type {\n                NumericGuiType::Slider {\n                    range,\n                    step,\n                    logarithmic,\n                } => {\n                    let mut slider = Slider::new(editing_value_mut, range.clone())\n                        .logarithmic(*logarithmic)\n                        .show_value(false);\n                    if let Some(step) = step {\n                        slider = slider.step_by(*step);\n                    }\n                    if !matches!(self.ty, NumberType::Float) {\n                        slider = slider.integer();\n                    }\n                    let slider_response = {\n                        ui.style_mut().spacing.interact_size.y = SCROLLBAR_DOT_DIAMETER;\n                        ui.add(slider)\n                    };\n\n                    let mut drag_value = DragValue::new(editing_value_mut);\n                    // Note: the following ifs cannot be merged with the ones above to avoid double\n                    // mutable borrow of editing_value_mut.\n                    if let Some(step) = step {\n                        drag_value = drag_value.speed(*step);\n                    }\n                    if !matches!(self.ty, NumberType::Float) {\n                        drag_value = drag_value.fixed_decimals(0);\n                    }\n                    if let Some(suffix) = &self.suffix {\n                        drag_value = drag_value.suffix(suffix);\n                    }\n                    let textbox_response = ui.add(drag_value);\n\n                    slider_response.union(textbox_response)\n                }\n                NumericGuiType::TextBox => {\n                    let mut drag_value = DragValue::new(editing_value_mut);\n\n                    if !matches!(self.ty, NumberType::Float) {\n                        drag_value = drag_value.fixed_decimals(0);\n                    }\n                    if let Some(suffix) = &self.suffix {\n                        drag_value = drag_value.suffix(suffix);\n                    }\n\n                    ui.add(drag_value)\n                }\n            };\n\n            if response.drag_started() || response.gained_focus() || response.clicked() {\n                self.editing_value_f64 = Some(session_value)\n            } else if response.drag_stopped() || response.lost_focus() {\n                request = get_request(&self.nesting_info, *editing_value_mut, self.ty);\n                *session_fragment = to_json_value(*editing_value_mut, self.ty);\n\n                self.editing_value_f64 = None;\n            }\n\n            if reset::reset_button(\n                ui,\n                !f64_eq(session_value, self.default),\n                &self.default_string,\n            )\n            .clicked()\n            {\n                request = get_request(&self.nesting_info, self.default, self.ty);\n            }\n        });\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/optional.rs",
    "content": "use super::{NestingInfo, SettingControl, reset};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::{\n    egui::{Layout, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default_set: bool,\n    default_string: String,\n    content_control: Box<SettingControl>,\n}\n\nimpl Control {\n    pub fn new(nesting_info: NestingInfo, default_set: bool, schema_content: SchemaNode) -> Self {\n        let default_string = if default_set {\n            \"Set\".into()\n        } else {\n            \"Default\".into()\n        };\n\n        let control = {\n            let mut nesting_info = nesting_info.clone();\n            nesting_info.path.push(\"content\".into());\n\n            SettingControl::new(nesting_info, schema_content)\n        };\n\n        Self {\n            nesting_info,\n            default_set,\n            default_string,\n            content_control: Box::new(control),\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let session_switch_mut = session_fragment.as_object_mut().unwrap();\n\n        let json::Value::Bool(set_mut) = &mut session_switch_mut[\"set\"] else {\n            unreachable!()\n        };\n\n        let mut request = None;\n\n        fn get_request(nesting_info: &NestingInfo, enabled: bool) -> Option<PathValuePair> {\n            super::get_single_value(nesting_info, \"set\".into(), json::Value::Bool(enabled))\n        }\n\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            if ui.selectable_value(set_mut, false, \"Default\").clicked()\n                || ui.selectable_value(set_mut, true, \"Set\").clicked()\n            {\n                request = get_request(&self.nesting_info, *set_mut);\n            }\n\n            if reset::reset_button(ui, *set_mut != self.default_set, &self.default_string).clicked()\n            {\n                request = get_request(&self.nesting_info, self.default_set);\n            }\n        });\n\n        if *set_mut {\n            ui.end_row();\n\n            request = self\n                .content_control\n                .ui(ui, &mut session_switch_mut[\"content\"], false)\n                .or(request);\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/presets/builtin_schema.rs",
    "content": "use super::schema::{\n    HigherOrderChoiceOption, HigherOrderChoiceSchema, PresetModifier, PresetSchemaNode,\n};\nuse crate::dashboard::components::presets::schema::PresetModifierOperation;\nuse settings_schema::ChoiceControlType;\nuse std::{\n    collections::{HashMap, HashSet},\n    str::FromStr,\n};\n\nfn string_modifier(target_path: &str, value: &str) -> PresetModifier {\n    PresetModifier {\n        target_path: target_path.into(),\n        operation: PresetModifierOperation::Assign(serde_json::Value::String(value.into())),\n    }\n}\nfn num_modifier(target_path: &str, value: &str) -> PresetModifier {\n    PresetModifier {\n        target_path: target_path.into(),\n        operation: PresetModifierOperation::Assign(serde_json::Value::Number(\n            serde_json::Number::from_str(value).unwrap(),\n        )),\n    }\n}\nfn bool_modifier(target_path: &str, value: bool) -> PresetModifier {\n    PresetModifier {\n        target_path: target_path.into(),\n        operation: PresetModifierOperation::Assign(serde_json::Value::Bool(value)),\n    }\n}\n\npub fn resolution_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Resolution\".into(),\n        strings: [(\n            \"help\".into(),\n            \"Choosing too high resolution (commonly 'High (width: 5184)') may result in high latency or black screen.\".into(),\n        )]\n        .into_iter()\n        .collect(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [\n            (\"Very Low (width: 3072)\", \"1536\"),\n            (\"Low (width: 3712)\", \"1856\"),\n            (\"Medium (width: 4288)\", \"2144\"),\n            (\"High (width: 5184)\", \"2592\"),\n            (\"Ultra (width: 5632)\", \"2816\"),\n            (\"Extreme (width: 6080)\", \"3040\"),\n        ]\n        .into_iter()\n        .map(|(key, value)| HigherOrderChoiceOption {\n            display_name: key.into(),\n            modifiers: [\n                string_modifier(\n                    \"session_settings.video.transcoding_view_resolution.variant\",\n                    \"Absolute\",\n                ),\n                num_modifier(\n                    \"session_settings.video.transcoding_view_resolution.Absolute.width\",\n                    value,\n                ),\n                bool_modifier(\n                    \"session_settings.video.transcoding_view_resolution.Absolute.height.set\",\n                    false,\n                ),\n                string_modifier(\n                    \"session_settings.video.emulated_headset_view_resolution.variant\",\n                    \"Absolute\",\n                ),\n                num_modifier(\n                    \"session_settings.video.emulated_headset_view_resolution.Absolute.width\",\n                    value,\n                ),\n                bool_modifier(\n                    \"session_settings.video.emulated_headset_view_resolution.Absolute.height.set\",\n                    false,\n                ),\n            ]\n            .into_iter()\n            .collect(),\n            content: None,\n        })\n        .collect(),\n        default_option_display_name: \"Medium (width: 4288)\".into(),\n        gui: ChoiceControlType::Dropdown,\n    })\n}\n\npub fn framerate_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Preferred framerate\".into(),\n        strings: HashMap::new(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [60, 72, 80, 90, 120]\n            .into_iter()\n            .map(|framerate| HigherOrderChoiceOption {\n                display_name: format!(\"{framerate}Hz\"),\n                modifiers: [num_modifier(\n                    \"session_settings.video.preferred_fps\",\n                    &format!(\"{:?}\", framerate as f32),\n                )]\n                .into_iter()\n                .collect(),\n                content: None,\n            })\n            .collect(),\n        default_option_display_name: \"72Hz\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\npub fn codec_preset_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Codec preset\".into(),\n        strings: [\n            (\n            \"notice\".into(),\n            \"AV1 encoding is only supported on RDNA3, Ada Lovelace, Intel ARC or newer GPUs (AMD RX 7xxx+ , NVIDIA RTX 40xx+, Intel ARC)\nand on headsets that have XR2 Gen 2 onboard (Quest 3, Pico 4 Ultra).\\n\nH264 encoding is currently NOT supported on Intel ARC GPUs on Windows.\"\n                .into(),\n            ),\n        ]\n        .into_iter()\n        .collect(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [(\"H264\", \"H264\"), (\"HEVC\", \"Hevc\"), (\"AV1\", \"AV1\")]\n            .into_iter()\n            .map(|(key, val_codec)| HigherOrderChoiceOption {\n                display_name: key.into(),\n                modifiers: [string_modifier(\n                    \"session_settings.video.preferred_codec.variant\",\n                    val_codec,\n                )]\n                .into_iter()\n                .collect(),\n                content: None,\n            })\n            .collect(),\n        default_option_display_name: \"H264\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\npub fn encoder_preset_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Encoder preset\".into(),\n        strings: [(\n            \"help\".into(),\n            \"Selecting a quality too high may result in stuttering or still image!\".into(),\n        )]\n        .into_iter()\n        .collect(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [\n            (\"Speed\", \"Speed\", \"P1\"),\n            (\"Balanced\", \"Balanced\", \"P3\"),\n            (\"Quality\", \"Quality\", \"P5\"),\n        ]\n        .into_iter()\n        .map(|(key, val_amd, val_nv)| HigherOrderChoiceOption {\n            display_name: key.into(),\n            modifiers: [\n                string_modifier(\n                    \"session_settings.video.encoder_config.nvenc.quality_preset.variant\",\n                    val_nv,\n                ),\n                string_modifier(\n                    \"session_settings.video.encoder_config.quality_preset.variant\",\n                    val_amd,\n                ),\n            ]\n            .into_iter()\n            .collect(),\n            content: None,\n        })\n        .collect(),\n        default_option_display_name: \"Speed\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\npub fn foveation_preset_schema() -> PresetSchemaNode {\n    const PREFIX: &str = \"session_settings.video.foveated_encoding\";\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Foveation preset\".into(),\n        strings: [(\n            \"help\".into(),\n            \"Foveation affects pixelation on the edges of \\\n            the screen and significantly reduces codec latency. \nIt is not recommended to fully disable it, as it may cause \\\nshutterring and high encode/decode latency!\"\n                .into(),\n        )]\n        .into_iter()\n        .collect(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [\n            (\"Light\", 0.80, 0.80, 8.0, 8.0),\n            (\"Medium\", 0.66, 0.60, 6.0, 6.0),\n            (\"High\", 0.45, 0.40, 4.0, 5.0),\n        ]\n        .into_iter()\n        .map(\n            |(key, val_size_x, val_size_y, val_edge_x, val_edge_y)| HigherOrderChoiceOption {\n                display_name: key.into(),\n                modifiers: [\n                    bool_modifier(&format!(\"{PREFIX}.enabled\"), true),\n                    num_modifier(\n                        &format!(\"{PREFIX}.content.center_size_x\"),\n                        &val_size_x.to_string(),\n                    ),\n                    num_modifier(\n                        &format!(\"{PREFIX}.content.center_size_y\"),\n                        &val_size_y.to_string(),\n                    ),\n                    num_modifier(\n                        &format!(\"{PREFIX}.content.edge_ratio_x\"),\n                        &val_edge_x.to_string(),\n                    ),\n                    num_modifier(\n                        &format!(\"{PREFIX}.content.edge_ratio_y\"),\n                        &val_edge_y.to_string(),\n                    ),\n                ]\n                .into_iter()\n                .collect(),\n                content: None,\n            },\n        )\n        .collect(),\n        default_option_display_name: \"High\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\n#[cfg(target_os = \"linux\")]\npub fn game_audio_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Headset speaker\".into(),\n        strings: HashMap::new(),\n        flags: HashSet::new(),\n        options: [\n            HigherOrderChoiceOption {\n                display_name: \"Disabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.audio.game_audio.enabled\",\n                    false,\n                )],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"Enabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.audio.game_audio.enabled\",\n                    true,\n                )],\n                content: None,\n            },\n        ]\n        .into_iter()\n        .collect(),\n        default_option_display_name: \"Enabled\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\n#[cfg(target_os = \"linux\")]\npub fn microphone_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Headset microphone\".into(),\n        strings: HashMap::new(),\n        flags: HashSet::new(),\n        options: [\n            HigherOrderChoiceOption {\n                display_name: \"Disabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.audio.microphone.enabled\",\n                    false,\n                )],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"Enabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.audio.microphone.enabled\",\n                    true,\n                )],\n                content: None,\n            },\n        ]\n        .into_iter()\n        .collect(),\n        default_option_display_name: \"Enabled\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\n#[cfg(not(target_os = \"linux\"))]\npub fn game_audio_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Headset speaker\".into(),\n        strings: [(\n            \"notice\".into(),\n            \"You can change the default audio device from the system taskbar tray (bottom right)\"\n                .into(),\n        )]\n        .into_iter()\n        .collect(),\n        flags: HashSet::new(),\n        options: vec![\n            HigherOrderChoiceOption {\n                display_name: \"Disabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.audio.game_audio.enabled\",\n                    false,\n                )],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"System Default\".to_owned(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.audio.game_audio.enabled\", true),\n                    bool_modifier(\n                        \"session_settings.audio.game_audio.content.device.set\",\n                        false,\n                    ),\n                ],\n                content: None,\n            },\n        ]\n        .into_iter()\n        .collect(),\n        default_option_display_name: \"System Default\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\n#[cfg(not(target_os = \"linux\"))]\npub fn microphone_schema() -> PresetSchemaNode {\n    let mut microhone_options = vec![HigherOrderChoiceOption {\n        display_name: \"Disabled\".to_owned(),\n        modifiers: vec![bool_modifier(\n            \"session_settings.audio.microphone.enabled\",\n            false,\n        )],\n        content: None,\n    }];\n\n    if cfg!(windows) {\n        for (key, display_name) in [\n            (\"Automatic\", \"Automatic\"),\n            (\"VAC\", \"Virtual Audio Cable\"),\n            (\"VBCable\", \"VB Cable\"),\n            (\"VoiceMeeter\", \"VoiceMeeter\"),\n            (\"VoiceMeeterAux\", \"VoiceMeeter Aux\"),\n            (\"VoiceMeeterVaio3\", \"VoiceMeeter VAIO3\"),\n        ] {\n            microhone_options.push(HigherOrderChoiceOption {\n                display_name: display_name.into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.audio.microphone.enabled\", true),\n                    string_modifier(\n                        \"session_settings.audio.microphone.content.devices.variant\",\n                        key,\n                    ),\n                ],\n                content: None,\n            })\n        }\n    }\n\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Headset microphone\".into(),\n        strings: HashMap::new(),\n        flags: HashSet::new(),\n        options: microhone_options.into_iter().collect(),\n        default_option_display_name: \"Disabled\".into(),\n        gui: ChoiceControlType::Dropdown,\n    })\n}\n\npub fn hand_tracking_interaction_schema() -> PresetSchemaNode {\n    const HELP: &str = r\"Disabled: hands cannot emulate buttons. Useful for using Joy-Cons or other non-native controllers.\nSteamVR Input 2.0: create separate SteamVR devices for hand tracking.\nALVR bindings: use ALVR hand tracking button bindings. Check the wiki for help.\n\";\n\n    const PREFIX: &str = \"session_settings.headset.controllers.content\";\n\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Hand tracking interaction\".into(),\n        strings: [(\"help\".into(), HELP.into())].into_iter().collect(),\n        flags: [\"steamvr-restart\".into()].into_iter().collect(),\n        options: [\n            HigherOrderChoiceOption {\n                display_name: \"Disabled\".into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.headset.controllers.enabled\", true),\n                    bool_modifier(\n                        &format!(\"{PREFIX}.hand_skeleton.content.steamvr_input_2_0\"),\n                        false,\n                    ),\n                    bool_modifier(\n                        &format!(\"{PREFIX}.hand_tracking_interaction.enabled\"),\n                        false,\n                    ),\n                ],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"SteamVR Input 2.0\".into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.headset.controllers.enabled\", true),\n                    bool_modifier(&format!(\"{PREFIX}.hand_skeleton.enabled\"), true),\n                    bool_modifier(\n                        &format!(\"{PREFIX}.hand_skeleton.content.steamvr_input_2_0\"),\n                        true,\n                    ),\n                    bool_modifier(\n                        &format!(\"{PREFIX}.hand_tracking_interaction.enabled\"),\n                        false,\n                    ),\n                ],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"ALVR bindings\".into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.headset.controllers.enabled\", true),\n                    bool_modifier(\n                        &format!(\"{PREFIX}.hand_skeleton.content.steamvr_input_2_0\"),\n                        false,\n                    ),\n                    bool_modifier(&format!(\"{PREFIX}.hand_tracking_interaction.enabled\"), true),\n                ],\n                content: None,\n            },\n        ]\n        .into_iter()\n        .collect(),\n        default_option_display_name: \"SteamVR Input 2.0\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n\npub fn eye_face_tracking_schema() -> PresetSchemaNode {\n    PresetSchemaNode::HigherOrderChoice(HigherOrderChoiceSchema {\n        name: \"Eye and face tracking\".into(),\n        strings: HashMap::new(),\n        flags: HashSet::new(),\n        options: [\n            HigherOrderChoiceOption {\n                display_name: \"Disabled\".into(),\n                modifiers: vec![bool_modifier(\n                    \"session_settings.headset.face_tracking.enabled\",\n                    false,\n                )],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"VRChat Eye OSC\".into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.headset.face_tracking.enabled\", true),\n                    string_modifier(\n                        \"session_settings.headset.face_tracking.content.sink.variant\",\n                        \"VrchatEyeOsc\",\n                    ),\n                ],\n                content: None,\n            },\n            HigherOrderChoiceOption {\n                display_name: \"VRCFaceTracking\".into(),\n                modifiers: vec![\n                    bool_modifier(\"session_settings.headset.face_tracking.enabled\", true),\n                    string_modifier(\n                        \"session_settings.headset.face_tracking.content.sink.variant\",\n                        \"VrcFaceTracking\",\n                    ),\n                ],\n                content: None,\n            },\n        ]\n        .into_iter()\n        .collect(),\n        default_option_display_name: \"Disabled\".into(),\n        gui: ChoiceControlType::ButtonGroup,\n    })\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/presets/higher_order_choice.rs",
    "content": "use super::schema::{HigherOrderChoiceSchema, PresetModifierOperation};\nuse crate::dashboard::components::{self, NestingInfo, SettingControl};\nuse alvr_packets::{PathSegment, PathValuePair};\nuse eframe::egui::Ui;\nuse serde_json as json;\nuse settings_schema::{SchemaEntry, SchemaNode};\nuse std::collections::{HashMap, HashSet};\n\npub struct Control {\n    name: String,\n    modifiers: HashMap<String, Vec<PathValuePair>>,\n    control: SettingControl,\n    preset_json: json::Value,\n}\n\nimpl Control {\n    pub fn new(schema: HigherOrderChoiceSchema) -> Self {\n        let modifiers = schema\n            .options\n            .iter()\n            .map(|option| {\n                (\n                    option.display_name.clone(),\n                    option\n                        .modifiers\n                        .iter()\n                        .map(|modifier| match &modifier.operation {\n                            PresetModifierOperation::Assign(value) => PathValuePair {\n                                path: alvr_packets::parse_path(&modifier.target_path),\n                                value: value.clone(),\n                            },\n                        })\n                        .collect(),\n                )\n            })\n            .collect();\n\n        let mut strings = schema.strings;\n        strings.insert(\"display_name\".into(), schema.name.clone());\n\n        let control_schema = SchemaNode::Section {\n            entries: vec![SchemaEntry {\n                name: schema.name.clone(),\n                strings,\n                flags: schema.flags,\n                content: SchemaNode::Choice {\n                    default: schema\n                        .options\n                        .iter()\n                        .find(|option| option.display_name == schema.default_option_display_name)\n                        .unwrap()\n                        .display_name\n                        .clone(),\n                    variants: schema\n                        .options\n                        .into_iter()\n                        .map(|option| SchemaEntry {\n                            name: option.display_name.clone(),\n                            strings: [(\"display_name\".into(), option.display_name)]\n                                .into_iter()\n                                .collect(),\n                            flags: HashSet::new(),\n                            content: None,\n                        })\n                        .collect(),\n                    gui: Some(schema.gui),\n                },\n            }],\n            gui_collapsible: false,\n        };\n\n        let control = SettingControl::new(\n            NestingInfo {\n                path: vec![],\n                indentation_level: 0,\n            },\n            control_schema,\n        );\n\n        let preset_json = json::json!({ {&schema.name}: { \"variant\": \"\" } });\n\n        Self {\n            name: schema.name,\n            modifiers,\n            control,\n            preset_json,\n        }\n    }\n\n    pub fn update_session_settings(&mut self, session_setting_json: &json::Value) {\n        let mut selected_option = String::new();\n\n        'outer: for (key, descs) in &self.modifiers {\n            for desc in descs {\n                let mut session_ref = session_setting_json;\n\n                // Note: the first path segment is always \"settings_schema\". Skip that.\n                for segment in &desc.path[1..] {\n                    session_ref = match segment {\n                        PathSegment::Name(name) => {\n                            if let Some(name) = session_ref.get(name) {\n                                name\n                            } else {\n                                continue 'outer;\n                            }\n                        }\n                        PathSegment::Index(index) => {\n                            if let Some(index) = session_ref.get(index) {\n                                index\n                            } else {\n                                continue 'outer;\n                            }\n                        }\n                    };\n                }\n\n                if !components::json_values_eq(session_ref, &desc.value) {\n                    continue 'outer;\n                }\n            }\n\n            // At this point the session matches all modifiers\n            selected_option.clone_from(key);\n\n            break;\n        }\n\n        // Note: if no modifier matched, the control will unselect all options\n        self.preset_json[&self.name][\"variant\"] = json::Value::String(selected_option);\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Vec<PathValuePair> {\n        if let Some(desc) = self.control.ui(ui, &mut self.preset_json, false) {\n            // todo: handle children requests\n            self.modifiers[desc.value.as_str().unwrap()].clone()\n        } else {\n            vec![]\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/presets/mirror.rs",
    "content": "\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/presets/mod.rs",
    "content": "mod higher_order_choice;\nmod mirror;\n\npub mod builtin_schema;\npub mod schema;\n\nuse self::schema::PresetSchemaNode;\nuse alvr_packets::PathValuePair;\nuse eframe::egui::Ui;\nuse serde_json as json;\n\npub enum PresetControl {\n    HigherOrderChoice(higher_order_choice::Control),\n    // Mirror(...)\n}\n\nimpl PresetControl {\n    pub fn new(schema: PresetSchemaNode) -> Self {\n        match schema {\n            PresetSchemaNode::HigherOrderChoice(schema) => {\n                Self::HigherOrderChoice(higher_order_choice::Control::new(schema))\n            }\n            PresetSchemaNode::Mirror(_) => unimplemented!(),\n        }\n    }\n\n    pub fn update_session_settings(&mut self, session_settings_json: &json::Value) {\n        match self {\n            Self::HigherOrderChoice(control) => {\n                control.update_session_settings(session_settings_json)\n            }\n        }\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Vec<PathValuePair> {\n        match self {\n            Self::HigherOrderChoice(control) => control.ui(ui),\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/presets/schema.rs",
    "content": "use serde::{Deserialize, Serialize};\nuse serde_json as json;\nuse settings_schema::ChoiceControlType;\nuse std::collections::{HashMap, HashSet};\n\n#[derive(Serialize, Deserialize, Clone)]\npub enum PresetModifierOperation {\n    Assign(json::Value),\n}\n\n#[derive(Serialize, Deserialize)]\npub struct PresetModifier {\n    // session-style path\n    pub target_path: String,\n    pub operation: PresetModifierOperation,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct HigherOrderChoiceOption {\n    pub display_name: String,\n    pub modifiers: Vec<PresetModifier>,\n    pub content: Option<PresetSchemaNode>,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct HigherOrderChoiceSchema {\n    pub name: String,\n    pub strings: HashMap<String, String>,\n    pub flags: HashSet<String>,\n    pub options: Vec<HigherOrderChoiceOption>,\n    pub default_option_display_name: String,\n    pub gui: ChoiceControlType,\n}\n\n#[derive(Serialize, Deserialize)]\npub enum PresetSchemaNode {\n    HigherOrderChoice(HigherOrderChoiceSchema),\n\n    // session-style path\n    Mirror(String),\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/reset.rs",
    "content": "use eframe::{\n    egui::{self, Button, Layout, Response, Ui},\n    emath::Align,\n};\n\npub fn reset_button(ui: &mut Ui, enabled: bool, default_str: &str) -> Response {\n    ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n        ui.add_space(5.0);\n\n        let height = ui.spacing().interact_size.y;\n        ui.add_enabled(\n            enabled,\n            Button::new(\"⟲\").min_size(egui::vec2(height, height)),\n        )\n        .on_hover_text(format!(\"Reset to {default_str}\"))\n    })\n    .inner\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/section.rs",
    "content": "use super::{INDENTATION_STEP, NestingInfo, SettingControl, collapsible, notice};\nuse alvr_gui_common::{\n    DisplayString,\n    theme::{\n        OK_GREEN,\n        log_colors::{INFO_LIGHT, WARNING_LIGHT},\n    },\n};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::{SchemaEntry, SchemaNode};\nuse eframe::egui::Ui;\nuse serde_json as json;\n\nstruct Entry {\n    id: DisplayString,\n    help: Option<String>,\n    notice: Option<String>,\n    hidden: bool,\n    steamvr_restart_flag: bool,\n    real_time_flag: bool,\n    control: SettingControl,\n}\n\npub struct Control {\n    nesting_info: NestingInfo,\n    entries: Vec<Entry>,\n    gui_collapsible: bool,\n}\n\nimpl Control {\n    pub fn new(\n        mut nesting_info: NestingInfo,\n        schema_entries: Vec<SchemaEntry<SchemaNode>>,\n        gui_collapsible: bool,\n    ) -> Self {\n        nesting_info.indentation_level += 1;\n\n        let entries = schema_entries\n            .into_iter()\n            .map(|entry| {\n                let id = entry.name;\n                let display = super::get_display_name(&id, &entry.strings);\n                let help = entry.strings.get(\"help\").cloned();\n                let notice = entry.strings.get(\"notice\").cloned();\n                let hidden = entry.flags.contains(\"hidden\");\n                let steamvr_restart_flag = entry.flags.contains(\"steamvr-restart\");\n                let real_time_flag = entry.flags.contains(\"real-time\");\n\n                let mut nesting_info = nesting_info.clone();\n                nesting_info.path.push(id.clone().into());\n\n                Entry {\n                    id: DisplayString { id, display },\n                    help,\n                    notice,\n                    hidden,\n                    steamvr_restart_flag,\n                    real_time_flag,\n                    control: SettingControl::new(nesting_info, entry.content),\n                }\n            })\n            .collect();\n\n        Self {\n            nesting_info,\n            entries,\n            gui_collapsible,\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        let entries_count = self.entries.len();\n\n        let mut request = None;\n\n        let collapsed = if self.gui_collapsible {\n            super::grid_flow_inline(ui, allow_inline);\n\n            let collapsed = collapsible::collapsible_button(\n                ui,\n                &self.nesting_info,\n                session_fragment,\n                &mut request,\n            );\n\n            if !collapsed {\n                ui.end_row();\n            }\n\n            collapsed\n        } else {\n            if allow_inline {\n                ui.end_row();\n            }\n\n            false\n        };\n\n        if !collapsed {\n            for (i, entry) in self.entries.iter_mut().enumerate() {\n                if entry.hidden {\n                    continue;\n                }\n\n                ui.horizontal(|ui| {\n                    ui.add_space(INDENTATION_STEP * self.nesting_info.indentation_level as f32);\n                    let label_res = ui.label(&entry.id.display);\n                    if cfg!(debug_assertions) {\n                        label_res.on_hover_text_at_pointer(&*entry.id);\n                    }\n\n                    if let Some(string) = &entry.help {\n                        ui.colored_label(INFO_LIGHT, \"❓\")\n                            .on_hover_text_at_pointer(string);\n                    }\n                    if entry.steamvr_restart_flag {\n                        ui.colored_label(WARNING_LIGHT, \"⚠\")\n                            .on_hover_text_at_pointer(\n                                \"Changing this setting will make SteamVR restart!\\n\\\n                                Please save your in-game progress first\",\n                            );\n                    }\n                    if entry.real_time_flag {\n                        // The emoji is blue but it will be green in the UI\n                        ui.colored_label(OK_GREEN, \"🔵\").on_hover_text_at_pointer(\n                            \"This setting can be changed in real-time during streaming!\",\n                        );\n                    }\n                });\n\n                if let Some(string) = &entry.notice {\n                    notice::notice(ui, string);\n\n                    ui.end_row();\n\n                    ui.label(\" \");\n                }\n\n                request = entry\n                    .control\n                    .ui(ui, &mut session_fragment[&entry.id.id], true)\n                    .or(request);\n\n                if i != entries_count - 1 {\n                    ui.end_row();\n                }\n            }\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/switch.rs",
    "content": "use super::{NestingInfo, SettingControl, reset};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::{\n    egui::{Layout, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default_enabled: bool,\n    default_string: String,\n    content_control: Box<SettingControl>,\n}\n\nimpl Control {\n    pub fn new(\n        nesting_info: NestingInfo,\n        default_enabled: bool,\n        schema_content: SchemaNode,\n    ) -> Self {\n        let default_string = if default_enabled {\n            \"ON\".into()\n        } else {\n            \"OFF\".into()\n        };\n\n        let control = {\n            let mut nesting_info = nesting_info.clone();\n            nesting_info.path.push(\"content\".into());\n\n            SettingControl::new(nesting_info, schema_content)\n        };\n\n        Self {\n            nesting_info,\n            default_enabled,\n            default_string,\n            content_control: Box::new(control),\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let session_switch_mut = session_fragment.as_object_mut().unwrap();\n\n        let json::Value::Bool(enabled_mut) = &mut session_switch_mut[\"enabled\"] else {\n            unreachable!()\n        };\n\n        let mut request = None;\n\n        fn get_request(nesting_info: &NestingInfo, enabled: bool) -> Option<PathValuePair> {\n            super::get_single_value(nesting_info, \"enabled\".into(), json::Value::Bool(enabled))\n        }\n\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            if alvr_gui_common::switch(ui, enabled_mut).clicked() {\n                request = get_request(&self.nesting_info, *enabled_mut);\n            }\n\n            if reset::reset_button(\n                ui,\n                *enabled_mut != self.default_enabled,\n                &self.default_string,\n            )\n            .clicked()\n            {\n                request = get_request(&self.nesting_info, self.default_enabled);\n            }\n        });\n\n        if *enabled_mut {\n            ui.end_row();\n\n            request = self\n                .content_control\n                .ui(ui, &mut session_switch_mut[\"content\"], false)\n                .or(request);\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/text.rs",
    "content": "use super::{NestingInfo, reset};\nuse alvr_packets::PathValuePair;\nuse eframe::{\n    egui::{Layout, TextEdit, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    editing_value: Option<String>,\n    default: String,\n    default_string: String,\n}\n\nimpl Control {\n    pub fn new(nesting_info: NestingInfo, default: String) -> Self {\n        let default_string = format!(\"\\\"{default}\\\"\");\n\n        Self {\n            nesting_info,\n            editing_value: None,\n            default,\n            default_string,\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        let json::Value::String(text_mut) = session_fragment else {\n            unreachable!()\n        };\n\n        let mut request = None;\n\n        fn get_request(nesting_info: &NestingInfo, text: &str) -> Option<PathValuePair> {\n            Some(PathValuePair {\n                path: nesting_info.path.clone(),\n                value: json::Value::String(text.to_owned()),\n            })\n        }\n\n        ui.with_layout(Layout::left_to_right(Align::Center), |ui| {\n            let textbox = if let Some(editing_value_mut) = &mut self.editing_value {\n                TextEdit::singleline(editing_value_mut)\n            } else {\n                TextEdit::singleline(text_mut)\n            };\n\n            let response = ui.add(textbox.desired_width(250.));\n            if response.lost_focus() {\n                if let Some(editing_value_mut) = &mut self.editing_value {\n                    request = get_request(&self.nesting_info, editing_value_mut);\n                    text_mut.clone_from(editing_value_mut);\n                }\n\n                self.editing_value = None;\n            }\n            if response.gained_focus() {\n                self.editing_value = Some(text_mut.clone());\n            };\n\n            if reset::reset_button(ui, *text_mut != self.default, &self.default_string).clicked() {\n                request = get_request(&self.nesting_info, &self.default);\n            }\n        });\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/up_down.rs",
    "content": "use eframe::{\n    egui::{self, Button, Layout, Ui},\n    emath::Align,\n};\n\n#[derive(PartialEq, Eq)]\npub enum UpDownResult {\n    Up,\n    Down,\n    None,\n}\n\npub fn up_down_buttons(ui: &mut Ui, index: usize, count: usize) -> UpDownResult {\n    ui.with_layout(Layout::top_down(Align::LEFT), |ui| {\n        ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);\n\n        let up_clicked = ui\n            .add_visible(index > 0, Button::new(\"⬆\").small())\n            .clicked();\n        let down_clicked = ui\n            .add_visible(index < count - 1, Button::new(\"⬇\").small())\n            .clicked();\n\n        if up_clicked {\n            UpDownResult::Up\n        } else if down_clicked {\n            UpDownResult::Down\n        } else {\n            UpDownResult::None\n        }\n    })\n    .inner\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/settings_controls/vector.rs",
    "content": "use super::{INDENTATION_STEP, NestingInfo, SettingControl, reset};\nuse crate::dashboard::components::{\n    collapsible,\n    up_down::{self, UpDownResult},\n};\nuse alvr_packets::PathValuePair;\nuse alvr_session::settings_schema::SchemaNode;\nuse eframe::{\n    egui::{Layout, Ui},\n    emath::Align,\n};\nuse serde_json as json;\n\npub struct Control {\n    nesting_info: NestingInfo,\n    default_element: SchemaNode,\n    default: Vec<json::Value>,\n    controls: Vec<SettingControl>,\n}\n\nimpl Control {\n    pub fn new(\n        nesting_info: NestingInfo,\n        default_element: SchemaNode,\n        default: Vec<json::Value>,\n    ) -> Self {\n        Self {\n            nesting_info,\n            default_element,\n            default,\n            controls: vec![],\n        }\n    }\n\n    pub fn ui(\n        &mut self,\n        ui: &mut Ui,\n        session_fragment: &mut json::Value,\n        allow_inline: bool,\n    ) -> Option<PathValuePair> {\n        super::grid_flow_inline(ui, allow_inline);\n\n        fn get_content_request(\n            nesting_info: &NestingInfo,\n            elements: Vec<json::Value>,\n        ) -> Option<PathValuePair> {\n            super::get_single_value(nesting_info, \"content\".into(), json::Value::Array(elements))\n        }\n\n        let mut request = None;\n        let collapsed = ui\n            .with_layout(Layout::left_to_right(Align::Center), |ui| {\n                let collapsed = collapsible::collapsible_button(\n                    ui,\n                    &self.nesting_info,\n                    session_fragment,\n                    &mut request,\n                );\n\n                if reset::reset_button(ui, true, \"default list\").clicked() {\n                    request = get_content_request(&self.nesting_info, self.default.clone())\n                }\n\n                collapsed\n            })\n            .inner;\n\n        let session_content = session_fragment[\"content\"].as_array_mut().unwrap();\n\n        while session_content.len() > self.controls.len() {\n            let mut nesting_info = self.nesting_info.clone();\n            nesting_info.path.push(\"content\".into());\n            nesting_info.path.push(self.controls.len().into());\n\n            self.controls.push(SettingControl::new(\n                nesting_info,\n                self.default_element.clone(),\n            ))\n        }\n        while session_content.len() < self.controls.len() {\n            self.controls.pop();\n        }\n\n        if !collapsed {\n            ui.end_row();\n\n            let mut idx = 0;\n            while idx < self.controls.len() {\n                let delete_element = ui\n                    .horizontal(|ui| {\n                        ui.add_space(INDENTATION_STEP * self.nesting_info.indentation_level as f32);\n\n                        let delete_element = ui.button(\"❌\").clicked();\n\n                        let up_down_result = up_down::up_down_buttons(ui, idx, self.controls.len());\n\n                        if up_down_result != UpDownResult::None {\n                            if up_down_result == UpDownResult::Up {\n                                session_content.swap(idx, idx - 1);\n                            } else {\n                                session_content.swap(idx, idx + 1);\n                            }\n\n                            request =\n                                get_content_request(&self.nesting_info, session_content.clone());\n                        }\n\n                        delete_element\n                    })\n                    .inner;\n\n                if delete_element {\n                    session_content.remove(idx);\n                    self.controls.remove(idx);\n\n                    request = get_content_request(&self.nesting_info, session_content.clone());\n                } else {\n                    request = self.controls[idx]\n                        .ui(ui, &mut session_content[idx], true)\n                        .or(request);\n                }\n\n                ui.end_row();\n\n                idx += 1;\n            }\n\n            ui.label(\" \");\n            if ui.button(\"Add element\").clicked() {\n                let mut session_content =\n                    session_fragment[\"content\"].as_array_mut().unwrap().clone();\n                session_content.push(session_fragment[\"element\"].clone());\n\n                request = get_content_request(&self.nesting_info, session_content);\n            }\n        }\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/setup_wizard.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse eframe::{\n    egui::{Button, Label, Layout, RichText, Ui},\n    emath::Align,\n};\n\npub enum SetupWizardRequest {\n    ServerRequest(ServerRequest),\n    Close { finished: bool },\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Page {\n    Welcome = 0,\n    ResetSettings = 1,\n    HardwareRequirements = 2,\n    SoftwareRequirements = 3,\n    Firewall = 4,\n    Recommendations = 5,\n    Finished = 6,\n}\n\nfn index_to_page(index: usize) -> Page {\n    match index {\n        0 => Page::Welcome,\n        1 => Page::ResetSettings,\n        2 => Page::HardwareRequirements,\n        3 => Page::SoftwareRequirements,\n        4 => Page::Firewall,\n        5 => Page::Recommendations,\n        6 => Page::Finished,\n        _ => panic!(\"Invalid page index\"),\n    }\n}\n\nfn page_content(\n    ui: &mut Ui,\n    subtitle: &str,\n    paragraph: &str,\n    interactible_content: impl FnMut(&mut Ui),\n) {\n    ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n        ui.add_space(60.0);\n        ui.with_layout(Layout::left_to_right(Align::Min), |ui| {\n            ui.add_space(60.0);\n            ui.with_layout(Layout::top_down(Align::LEFT), |ui| {\n                ui.add_space(15.0);\n                ui.heading(RichText::new(subtitle).size(20.0));\n                ui.add(Label::new(RichText::new(paragraph).size(14.0)).wrap());\n                ui.add_space(30.0);\n                ui.vertical_centered(interactible_content);\n            });\n        })\n    });\n}\n\npub struct SetupWizard {\n    page: Page,\n}\n\nimpl SetupWizard {\n    pub fn new() -> Self {\n        Self {\n            page: Page::Welcome,\n        }\n    }\n\n    pub fn ui(&mut self, ui: &mut Ui) -> Option<SetupWizardRequest> {\n        let mut request = None;\n\n        ui.horizontal(|ui| {\n            ui.add_space(60.0);\n            ui.vertical(|ui| {\n                ui.add_space(30.0);\n                ui.heading(RichText::new(\"Welcome to ALVR\").size(30.0));\n                ui.add_space(5.0);\n            });\n            ui.with_layout(Layout::right_to_left(Align::Center), |ui| {\n                ui.add_space(15.0);\n                if ui.button(\"❌\").clicked() {\n                    request = Some(SetupWizardRequest::Close { finished: false });\n                }\n            })\n        });\n        ui.separator();\n        match &self.page {\n            Page::Welcome => page_content(\n                ui,\n                \"This setup wizard will help you setup ALVR.\",\n                \"\",\n                |_| (),\n            ),\n            Page::ResetSettings => page_content(\n                ui,\n                \"Reset settings\",\n                \"It is recommended to reset your settings everytime you update ALVR.\",\n                |ui| {\n                    if ui.button(\"Reset settings\").clicked() {\n                        request = Some(SetupWizardRequest::ServerRequest(\n                            ServerRequest::UpdateSession(Box::default()),\n                        ));\n                    }\n                },\n            ),\n            Page::HardwareRequirements => page_content(\n                ui,\n                \"Hardware requirements\",\n                r\"ALVR requires a dedicated and recent graphics card. Low-end Intel integrated graphics may fail to work.\nMake sure you have at least one output audio device.\",\n                |_| (),\n            ),\n            Page::SoftwareRequirements => page_content(\n                ui,\n                \"Software requirements\",\n                if cfg!(windows) {\n                    r\"To stream the headset microphone on Windows you need to install Virtual Audio Cable, VB-Cable, Voicemeeter\"\n                } else if cfg!(target_os = \"linux\") {\n                    r\"You need the PipeWire (0.3.49+ version) audio system to be able to stream audio and use microphone.\"\n                } else {\n                    r\"Unsupported OS\"\n                },\n                #[allow(unused_variables)]\n                |ui| {\n                    #[cfg(windows)]\n                    if ui.button(\"Download Virtual Audio Cable (Lite)\").clicked() {\n                        ui.ctx().open_url(eframe::egui::OpenUrl::same_tab(\n                            \"https://software.muzychenko.net/freeware/vac470lite.zip\",\n                        ));\n                    }\n                },\n            ),\n            Page::Firewall => page_content(\n                ui,\n                \"Firewall\",\n                r\"To communicate with the headset, some firewall rules need to be set.\nThis requires administrator rights!\",\n                |ui| {\n                    if ui.button(\"Add firewall rules\").clicked() {\n                        request = Some(SetupWizardRequest::ServerRequest(\n                            ServerRequest::AddFirewallRules,\n                        ));\n                    }\n                },\n            ),\n            Page::Recommendations => page_content(\n                ui,\n                \"Recommendations\",\n                r\"ALVR supports multiple types of PC hardware and headsets but not all might work correctly with default settings. Please try tweaking different settings like resolution, bitrate, encoder and others if your ALVR experience is not optimal.\",\n                |_| (),\n            ),\n            Page::Finished => page_content(\n                ui,\n                \"Finished\",\n                r#\"You can always restart this setup wizard from the \"Installation\" tab on the left.\"#,\n                |_| (),\n            ),\n        };\n\n        ui.with_layout(Layout::bottom_up(Align::RIGHT), |ui| {\n            ui.add_space(30.0);\n            ui.horizontal(|ui| {\n                ui.add_space(15.0);\n                if self.page == Page::Finished {\n                    if ui.button(\"Finish\").clicked() {\n                        request = Some(SetupWizardRequest::Close { finished: true });\n                    }\n                } else if ui.button(\"Next\").clicked() {\n                    self.page = index_to_page(self.page as usize + 1);\n                }\n                if ui\n                    .add_visible(self.page != Page::Welcome, Button::new(\"Back\"))\n                    .clicked()\n                {\n                    self.page = index_to_page(self.page as usize - 1);\n                }\n            });\n            ui.separator();\n        });\n\n        request\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/components/statistics.rs",
    "content": "use crate::dashboard::{ServerRequest, theme::graph_colors};\nuse alvr_events::{GraphStatistics, StatisticsSummary};\nuse alvr_gui_common::theme;\nuse eframe::{\n    egui::{\n        Align2, Color32, CornerRadius, FontId, Frame, Grid, Painter, Rect, RichText, ScrollArea,\n        Shape, Stroke, Ui, pos2, vec2,\n    },\n    emath::RectTransform,\n    epaint::Pos2,\n};\nuse statrs::statistics::{self, OrderStatistics};\nuse std::{collections::VecDeque, ops::RangeInclusive};\n\nconst GRAPH_HISTORY_SIZE: usize = 1000;\nconst UPPER_QUANTILE: f64 = 0.90;\n\nfn draw_lines(painter: &Painter, points: Vec<Pos2>, color: Color32) {\n    painter.add(Shape::line(points, Stroke::new(1.0, color)));\n}\n\npub struct StatisticsTab {\n    history: VecDeque<GraphStatistics>,\n    last_statistics_summary: Option<StatisticsSummary>,\n}\n\nimpl StatisticsTab {\n    pub fn new() -> Self {\n        Self {\n            history: vec![GraphStatistics::default(); GRAPH_HISTORY_SIZE]\n                .into_iter()\n                .collect(),\n            last_statistics_summary: None,\n        }\n    }\n\n    pub fn update_statistics(&mut self, statistics: StatisticsSummary) {\n        self.last_statistics_summary = Some(statistics);\n    }\n\n    pub fn update_graph_statistics(&mut self, statistics: GraphStatistics) {\n        self.history.pop_front();\n        self.history.push_back(statistics);\n    }\n\n    pub fn ui(&self, ui: &mut Ui) -> Option<ServerRequest> {\n        if let Some(stats) = &self.last_statistics_summary {\n            ScrollArea::new([false, true]).show(ui, |ui| {\n                let available_width = ui.available_width();\n                self.draw_latency_graph(ui, available_width);\n                self.draw_fps_graph(ui, available_width);\n                self.draw_bitrate_graph(ui, available_width);\n                self.draw_statistics_overview(ui, stats);\n            });\n        } else {\n            ui.heading(\n                \"No statistics available. \n            Start SteamVR and connect to a device to gather statistics.\",\n            );\n        }\n\n        None\n    }\n\n    fn draw_graph(\n        &self,\n        ui: &mut Ui,\n        available_width: f32,\n        title: &str,\n        data_range: RangeInclusive<f32>,\n        graph_content: impl FnOnce(&Painter, RectTransform),\n        tooltip_content: impl FnOnce(&mut Ui, &GraphStatistics),\n    ) {\n        ui.add_space(10.0);\n        ui.label(RichText::new(title).size(20.0));\n\n        let canvas_response = Frame::canvas(ui.style()).show(ui, |ui| {\n            ui.ctx().request_repaint();\n            let size = available_width * vec2(1.0, 0.2);\n\n            let (_id, canvas_rect) = ui.allocate_space(size);\n\n            let max = *data_range.end();\n            let min = *data_range.start();\n            let data_rect = Rect::from_x_y_ranges(0.0..=GRAPH_HISTORY_SIZE as f32, max..=min);\n            let to_screen = RectTransform::from_to(data_rect, canvas_rect);\n\n            let painter = ui.painter().with_clip_rect(canvas_rect);\n\n            if max == min {\n                // Drawing using a 0 sized rectangle causes a crash\n                return data_rect;\n            }\n\n            graph_content(&painter, to_screen);\n\n            ui.painter().text(\n                to_screen * pos2(0.0, min),\n                Align2::LEFT_BOTTOM,\n                format!(\"{min:.0}\"),\n                FontId::monospace(20.0),\n                Color32::GRAY,\n            );\n            ui.painter().text(\n                to_screen * pos2(0.0, max),\n                Align2::LEFT_TOP,\n                format!(\"{max:.0}\"),\n                FontId::monospace(20.0),\n                Color32::GRAY,\n            );\n\n            data_rect\n        });\n\n        if let Some(pos) = canvas_response.response.hover_pos() {\n            let graph_pos =\n                RectTransform::from_to(canvas_response.response.rect, canvas_response.inner) * pos;\n            let history_index = (graph_pos.x as usize).clamp(0, GRAPH_HISTORY_SIZE - 1);\n\n            canvas_response\n                .response\n                .on_hover_ui_at_pointer(|ui| tooltip_content(ui, &self.history[history_index]));\n        }\n    }\n\n    fn draw_latency_graph(&self, ui: &mut Ui, available_width: f32) {\n        let mut data = statistics::Data::new(\n            self.history\n                .iter()\n                .map(|stats| stats.total_pipeline_latency_s as f64)\n                .collect::<Vec<_>>(),\n        );\n\n        self.draw_graph(\n            ui,\n            available_width,\n            \"Latency\",\n            0.0..=(data.quantile(UPPER_QUANTILE) * 1.2) as f32 * 1000.0,\n            |painter, to_screen_trans| {\n                for i in 0..GRAPH_HISTORY_SIZE {\n                    let stats = &self.history[i];\n                    let mut offset = 0.0;\n                    for (value, color) in &[\n                        (stats.game_time_s, graph_colors::RENDER_EXTERNAL),\n                        (stats.server_compositor_s, graph_colors::RENDER),\n                        (stats.encoder_s, graph_colors::TRANSCODE),\n                        (stats.network_s, graph_colors::NETWORK),\n                        (stats.decoder_s, graph_colors::TRANSCODE),\n                        (stats.decoder_queue_s, graph_colors::IDLE),\n                        (stats.client_compositor_s, graph_colors::RENDER),\n                        (stats.vsync_queue_s, graph_colors::RENDER_EXTERNAL),\n                    ] {\n                        painter.rect_filled(\n                            Rect {\n                                min: to_screen_trans * pos2(i as f32, offset + value * 1000.0),\n                                max: to_screen_trans * pos2(i as f32 + 2.0, offset),\n                            },\n                            CornerRadius::ZERO,\n                            *color,\n                        );\n                        offset += value * 1000.0;\n                    }\n                }\n            },\n            |ui, stats| {\n                use graph_colors::*;\n\n                Grid::new(\"latency_tooltip\").num_columns(2).show(ui, |ui| {\n                    fn label(ui: &mut Ui, text: &str, value_s: f32, color: Color32) {\n                        ui.colored_label(color, text);\n                        ui.colored_label(color, format!(\"{:.2}ms\", value_s * 1000.0));\n                        ui.end_row();\n                    }\n\n                    let transmission_total_latency_s = stats.server_compositor_s\n                        + stats.encoder_s\n                        + stats.network_s\n                        + stats.decoder_s\n                        + stats.decoder_queue_s\n                        + stats.client_compositor_s;\n\n                    label(\n                        ui,\n                        \"Motion to Photon Latency\",\n                        stats.total_pipeline_latency_s,\n                        theme::FG,\n                    );\n                    label(ui, \"ALVR Latency\", transmission_total_latency_s, theme::FG);\n                    label(\n                        ui,\n                        \"Client System (not ALVR latency)\",\n                        stats.vsync_queue_s,\n                        RENDER_EXTERNAL_LABEL,\n                    );\n                    label(\n                        ui,\n                        \"Client App Compositor\",\n                        stats.client_compositor_s,\n                        RENDER,\n                    );\n                    label(ui, \"Frame Buffering\", stats.decoder_queue_s, IDLE);\n                    label(ui, \"Decode\", stats.decoder_s, TRANSCODE);\n                    label(ui, \"Network\", stats.network_s, NETWORK);\n                    label(ui, \"Encode\", stats.encoder_s, TRANSCODE);\n                    label(ui, \"Streamer Compositor\", stats.server_compositor_s, RENDER);\n                    label(\n                        ui,\n                        \"Game Render (not ALVR latency)\",\n                        stats.game_time_s,\n                        RENDER_EXTERNAL_LABEL,\n                    );\n                });\n            },\n        );\n    }\n\n    fn draw_fps_graph(&self, ui: &mut Ui, available_width: f32) {\n        let mut data = statistics::Data::new(\n            self.history\n                .iter()\n                .map(|stats| stats.client_fps)\n                .chain(self.history.iter().map(|stats| stats.server_fps))\n                .map(|v| v as f64)\n                .collect::<Vec<_>>(),\n        );\n        let upper_quantile = data.quantile(UPPER_QUANTILE);\n        let lower_quantile = data.quantile(1.0 - UPPER_QUANTILE);\n\n        let max = upper_quantile + (upper_quantile - lower_quantile);\n        let min = f64::max(0.0, lower_quantile - (upper_quantile - lower_quantile));\n\n        self.draw_graph(\n            ui,\n            available_width,\n            \"Framerate\",\n            min as f32..=max as f32,\n            |painter, to_screen_trans| {\n                let (server_fps_points, client_fps_points) = (0..GRAPH_HISTORY_SIZE)\n                    .map(|i| {\n                        (\n                            to_screen_trans * pos2(i as f32, self.history[i].server_fps),\n                            to_screen_trans * pos2(i as f32, self.history[i].client_fps),\n                        )\n                    })\n                    .unzip();\n\n                draw_lines(painter, server_fps_points, graph_colors::SERVER_FPS);\n                draw_lines(painter, client_fps_points, graph_colors::CLIENT_FPS);\n            },\n            |ui, stats| {\n                Grid::new(\"fps_tooltip\").num_columns(2).show(ui, |ui| {\n                    fn label(ui: &mut Ui, text: &str, value: f32, color: Color32) {\n                        ui.colored_label(color, text);\n                        ui.colored_label(color, format!(\"{value:.2}Hz\"));\n                        ui.end_row();\n                    }\n\n                    label(ui, \"Server FPS\", stats.server_fps, graph_colors::SERVER_FPS);\n                    label(ui, \"Client FPS\", stats.client_fps, graph_colors::CLIENT_FPS);\n                });\n            },\n        );\n    }\n\n    fn draw_bitrate_graph(&self, ui: &mut Ui, available_width: f32) {\n        let mut data = statistics::Data::new(\n            self.history\n                .iter()\n                .map(|stats| stats.throughput_bps as f64)\n                .collect::<Vec<_>>(),\n        );\n\n        self.draw_graph(\n            ui,\n            available_width,\n            \"Bitrate and Throughput\",\n            0.0..=(data.quantile(UPPER_QUANTILE) * 1.2) as f32 / 1e6,\n            |painter, to_screen_trans| {\n                let mut scaled_calculated = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut decoder_latency_limiter = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut network_latency_limiter = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut encoder_latency_limiter = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut max_throughput = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut min_throughput = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut requested_bitrate = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut recorded_throughput = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                let mut recorded_bitrate = Vec::with_capacity(GRAPH_HISTORY_SIZE);\n                for i in 0..GRAPH_HISTORY_SIZE {\n                    let d = &self.history[i].bitrate_directives;\n\n                    if let Some(value) = d.scaled_calculated_throughput_bps {\n                        scaled_calculated.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    if let Some(value) = d.decoder_latency_limiter_bps {\n                        decoder_latency_limiter.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    if let Some(value) = d.network_latency_limiter_bps {\n                        network_latency_limiter.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    if let Some(value) = d.encoder_latency_limiter_bps {\n                        encoder_latency_limiter.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    if let Some(value) = d.manual_max_throughput_bps {\n                        max_throughput.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    if let Some(value) = d.manual_min_throughput_bps {\n                        min_throughput.push(to_screen_trans * pos2(i as f32, value / 1e6))\n                    }\n                    requested_bitrate\n                        .push(to_screen_trans * pos2(i as f32, d.requested_bitrate_bps / 1e6));\n                    recorded_throughput.push(\n                        to_screen_trans * pos2(i as f32, self.history[i].throughput_bps / 1e6),\n                    );\n                    recorded_bitrate\n                        .push(to_screen_trans * pos2(i as f32, self.history[i].bitrate_bps / 1e6));\n                }\n\n                draw_lines(\n                    painter,\n                    scaled_calculated,\n                    graph_colors::INITIAL_CALCULATED_THROUGHPUT,\n                );\n                draw_lines(\n                    painter,\n                    encoder_latency_limiter,\n                    graph_colors::ENCODER_DECODER_LATENCY_LIMITER,\n                );\n                draw_lines(\n                    painter,\n                    network_latency_limiter,\n                    graph_colors::NETWORK_LATENCY_LIMITER,\n                );\n                draw_lines(\n                    painter,\n                    decoder_latency_limiter,\n                    graph_colors::ENCODER_DECODER_LATENCY_LIMITER,\n                );\n                draw_lines(\n                    painter,\n                    max_throughput,\n                    graph_colors::MIN_MAX_LATENCY_THROUGHPUT,\n                );\n                draw_lines(\n                    painter,\n                    min_throughput,\n                    graph_colors::MIN_MAX_LATENCY_THROUGHPUT,\n                );\n                draw_lines(painter, requested_bitrate, graph_colors::REQUESTED_BITRATE);\n                draw_lines(\n                    painter,\n                    recorded_throughput,\n                    graph_colors::RECORDED_THROUGHPUT,\n                );\n                draw_lines(painter, recorded_bitrate, theme::FG);\n            },\n            |ui, stats| {\n                Grid::new(\"bitrate_tooltip\").num_columns(2).show(ui, |ui| {\n                    fn maybe_label(\n                        ui: &mut Ui,\n                        text: &str,\n                        maybe_value_bps: Option<f32>,\n                        color: Color32,\n                    ) {\n                        if let Some(value) = maybe_value_bps {\n                            ui.colored_label(color, text);\n                            ui.colored_label(color, format!(\"{:.2} Mbps\", value / 1e6));\n                            ui.end_row();\n                        }\n                    }\n\n                    let td = &stats.bitrate_directives;\n\n                    maybe_label(\n                        ui,\n                        \"Initial calculated throughput\",\n                        td.scaled_calculated_throughput_bps,\n                        graph_colors::INITIAL_CALCULATED_THROUGHPUT,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Encoder latency limiter\",\n                        td.encoder_latency_limiter_bps,\n                        graph_colors::ENCODER_DECODER_LATENCY_LIMITER,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Network latency limiter\",\n                        td.network_latency_limiter_bps,\n                        graph_colors::NETWORK_LATENCY_LIMITER,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Decoder latency limiter\",\n                        td.decoder_latency_limiter_bps\n                            .filter(|l| *l < stats.throughput_bps),\n                        graph_colors::ENCODER_DECODER_LATENCY_LIMITER,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Manual max throughput\",\n                        td.manual_max_throughput_bps,\n                        graph_colors::MIN_MAX_LATENCY_THROUGHPUT,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Manual min throughput\",\n                        td.manual_min_throughput_bps,\n                        graph_colors::MIN_MAX_LATENCY_THROUGHPUT,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Requested bitrate\",\n                        Some(td.requested_bitrate_bps),\n                        graph_colors::REQUESTED_BITRATE,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Recorded throughput\",\n                        Some(stats.throughput_bps),\n                        graph_colors::RECORDED_THROUGHPUT,\n                    );\n                    maybe_label(\n                        ui,\n                        \"Recorded bitrate\",\n                        Some(stats.bitrate_bps),\n                        graph_colors::RECORDED_BITRATE,\n                    );\n                });\n\n                ui.small(\"Note: throughput is the peak bitrate, packet_size/network_latency.\");\n            },\n        )\n    }\n\n    fn draw_statistics_overview(&self, ui: &mut Ui, statistics: &StatisticsSummary) {\n        ui.add_space(10.0);\n\n        ui.columns(2, |ui| {\n            ui[0].label(\"Total packets:\");\n            ui[1].label(format!(\n                \"{} packets ({} packets/s)\",\n                statistics.video_packets_total, statistics.video_packets_per_sec\n            ));\n\n            ui[0].label(\"Total sent:\");\n            ui[1].label(format!(\"{} MB\", statistics.video_mbytes_total));\n\n            ui[0].label(\"Bitrate:\");\n            ui[1].label(format!(\"{:.1} Mbps\", statistics.video_mbits_per_sec));\n\n            ui[0].label(\"Total latency:\");\n            ui[1].label(format!(\"{:.0} ms\", statistics.total_latency_ms));\n\n            ui[0].label(\"Encoder latency:\");\n            ui[1].label(format!(\"{:.2} ms\", statistics.encode_latency_ms));\n\n            ui[0].label(\"Transport latency:\");\n            ui[1].label(format!(\"{:.2} ms\", statistics.network_latency_ms));\n\n            ui[0].label(\"Decoder latency:\");\n            ui[1].label(format!(\"{:.2} ms\", statistics.decode_latency_ms));\n\n            ui[0].label(\"Client FPS:\");\n            ui[1].label(format!(\"{} FPS\", statistics.client_fps));\n\n            ui[0].label(\"Streamer FPS:\");\n            ui[1].label(format!(\"{} FPS\", statistics.server_fps));\n\n            ui[0].label(\"Headset battery\");\n            ui[1].label(format!(\n                \"{}% ({})\",\n                statistics.battery_hmd,\n                if statistics.hmd_plugged {\n                    \"plugged\"\n                } else {\n                    \"unplugged\"\n                }\n            ));\n        });\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/dashboard/mod.rs",
    "content": "mod components;\n\nuse self::components::{\n    DevicesTab, LogsTab, NotificationBar, SettingsTab, SetupWizard, SetupWizardRequest,\n};\nuse crate::{\n    DataSources,\n    dashboard::components::{CloseAction, NewVersionPopup, StatisticsTab},\n};\nuse alvr_common::{\n    LogEntry,\n    parking_lot::{Condvar, Mutex},\n};\nuse alvr_events::EventType;\nuse alvr_gui_common::theme;\nuse alvr_packets::{ClientConnectionsAction, PathValuePair};\nuse alvr_session::SessionConfig;\nuse eframe::egui::{\n    self, Align, CentralPanel, Direction, Frame, Layout, Margin, RichText, SidePanel,\n};\nuse serde::{Deserialize, Serialize};\nuse std::{collections::BTreeMap, path::PathBuf, sync::Arc};\n\n#[derive(Serialize, Deserialize, Debug)]\npub enum ServerRequest {\n    Log(LogEntry),\n    GetSession,\n    UpdateSession(Box<SessionConfig>),\n    SetSessionValues(Vec<PathValuePair>),\n    UpdateClientList {\n        hostname: String,\n        action: ClientConnectionsAction,\n    },\n    CaptureFrame,\n    InsertIdr,\n    StartRecording,\n    StopRecording,\n    AddFirewallRules,\n    RemoveFirewallRules,\n    GetDriverList,\n    RegisterAlvrDriver,\n    UnregisterDriver(PathBuf),\n    RestartSteamvr,\n    ShutdownSteamvr,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]\nenum Tab {\n    Devices,\n    Statistics,\n    Settings,\n    #[cfg(not(target_arch = \"wasm32\"))]\n    Installation,\n    Logs,\n    Debug,\n    About,\n}\n\npub struct Dashboard {\n    data_sources: DataSources,\n    just_opened: bool,\n    server_restarting: Arc<Mutex<bool>>,\n    server_restarting_condvar: Arc<Condvar>,\n    selected_tab: Tab,\n    tab_labels: BTreeMap<Tab, &'static str>,\n    connections_tab: DevicesTab,\n    statistics_tab: StatisticsTab,\n    settings_tab: SettingsTab,\n    #[cfg(not(target_arch = \"wasm32\"))]\n    installation_tab: components::InstallationTab,\n    logs_tab: LogsTab,\n    notification_bar: NotificationBar,\n    setup_wizard: SetupWizard,\n    new_version_popup: Option<components::NewVersionPopup>,\n    setup_wizard_open: bool,\n    session: Option<SessionConfig>,\n}\n\nimpl Dashboard {\n    pub fn new(creation_context: &eframe::CreationContext<'_>, data_sources: DataSources) -> Self {\n        alvr_gui_common::theme::set_theme(&creation_context.egui_ctx);\n\n        data_sources.request(ServerRequest::GetSession);\n\n        Self {\n            data_sources,\n            just_opened: true,\n            server_restarting: Arc::new(Mutex::new(false)),\n            server_restarting_condvar: Arc::new(Condvar::new()),\n            selected_tab: Tab::Devices,\n            tab_labels: [\n                (Tab::Devices, \"🔌  Devices\"),\n                (Tab::Statistics, \"📈  Statistics\"),\n                (Tab::Settings, \"🔧  Settings\"),\n                #[cfg(not(target_arch = \"wasm32\"))]\n                (Tab::Installation, \"💾  Installation\"),\n                (Tab::Logs, \"📝  Logs\"),\n                (Tab::Debug, \"🐞  Debug\"),\n                (Tab::About, \"ℹ  About\"),\n            ]\n            .into_iter()\n            .collect(),\n            connections_tab: DevicesTab::new(),\n            statistics_tab: StatisticsTab::new(),\n            settings_tab: SettingsTab::new(),\n            #[cfg(not(target_arch = \"wasm32\"))]\n            installation_tab: components::InstallationTab::new(),\n            logs_tab: LogsTab::new(),\n            notification_bar: NotificationBar::new(),\n            setup_wizard: SetupWizard::new(),\n            setup_wizard_open: false,\n            session: None,\n            new_version_popup: None,\n        }\n    }\n\n    // This call may block\n    fn restart_steamvr(&self, requests: &mut Vec<ServerRequest>) {\n        requests.push(ServerRequest::RestartSteamvr);\n\n        let mut server_restarting_lock = self.server_restarting.lock();\n\n        if *server_restarting_lock {\n            self.server_restarting_condvar\n                .wait(&mut server_restarting_lock);\n        }\n\n        *server_restarting_lock = true;\n\n        #[cfg(not(target_arch = \"wasm32\"))]\n        std::thread::spawn({\n            let server_restarting = Arc::clone(&self.server_restarting);\n            let condvar = Arc::clone(&self.server_restarting_condvar);\n            move || {\n                crate::steamvr_launcher::LAUNCHER.lock().restart_steamvr();\n\n                *server_restarting.lock() = false;\n                condvar.notify_one();\n            }\n        });\n    }\n}\n\nimpl eframe::App for Dashboard {\n    fn update(&mut self, context: &egui::Context, _: &mut eframe::Frame) {\n        let mut requests = vec![];\n\n        let connected_to_server = self.data_sources.server_connected();\n\n        while let Some(event) = self.data_sources.poll_event() {\n            self.logs_tab.push_event(event.inner.clone());\n\n            match event.inner.event_type {\n                EventType::Log(log_event) => {\n                    self.notification_bar\n                        .push_notification(log_event, event.from_dashboard);\n                }\n                EventType::GraphStatistics(graph_statistics) => self\n                    .statistics_tab\n                    .update_graph_statistics(graph_statistics),\n                EventType::StatisticsSummary(statistics) => {\n                    self.statistics_tab.update_statistics(statistics)\n                }\n                EventType::Session(session) => {\n                    let settings = session.to_settings();\n\n                    self.connections_tab.update_client_list(&session);\n                    self.settings_tab.update_session(&session.session_settings);\n                    self.logs_tab.update_settings(&settings);\n                    self.notification_bar.update_settings(&settings);\n                    if self.just_opened {\n                        if settings.extra.open_setup_wizard {\n                            self.setup_wizard_open = true;\n                        }\n\n                        self.just_opened = false;\n                    }\n\n                    self.session = Some(*session);\n                }\n                EventType::ServerRequestsSelfRestart => self.restart_steamvr(&mut requests),\n                #[cfg(not(target_arch = \"wasm32\"))]\n                EventType::DriversList(list) => self.installation_tab.update_drivers(list),\n                EventType::Adb(adb_event) => self\n                    .connections_tab\n                    .update_adb_download_progress(adb_event.download_progress),\n                EventType::NewVersionFound { version, message } => {\n                    self.new_version_popup = Some(NewVersionPopup::new(version, message));\n                }\n                EventType::DebugGroup { .. }\n                | EventType::Tracking(_)\n                | EventType::Buttons(_)\n                | EventType::Haptics(_) => (),\n            }\n        }\n\n        if *self.server_restarting.lock() {\n            CentralPanel::default().show(context, |ui| {\n                // todo: find a way to center both vertically and horizontally\n                ui.vertical_centered(|ui| {\n                    ui.add_space(100.0);\n                    ui.heading(RichText::new(\"SteamVR is restarting\").size(30.0));\n                });\n            });\n\n            return;\n        }\n\n        self.notification_bar.ui(context);\n\n        if self.setup_wizard_open {\n            CentralPanel::default().show(context, |ui| {\n                if let Some(request) = self.setup_wizard.ui(ui) {\n                    match request {\n                        SetupWizardRequest::ServerRequest(request) => {\n                            requests.push(request);\n                        }\n                        SetupWizardRequest::Close { finished } => {\n                            if finished {\n                                requests.push(ServerRequest::SetSessionValues(vec![\n                                    PathValuePair {\n                                        path: alvr_packets::parse_path(\n                                            \"session_settings.extra.open_setup_wizard\",\n                                        ),\n                                        value: serde_json::Value::Bool(false),\n                                    },\n                                ]))\n                            }\n\n                            self.setup_wizard_open = false;\n                        }\n                    }\n                }\n            });\n        } else {\n            SidePanel::left(\"side_panel\")\n                .resizable(false)\n                .frame(\n                    Frame::new()\n                        .fill(theme::LIGHTER_BG)\n                        .inner_margin(Margin::same(7)),\n                )\n                .exact_width(160.0)\n                .show(context, |ui| {\n                    ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                        ui.add_space(13.0);\n                        ui.heading(RichText::new(\"ALVR\").size(25.0).strong());\n                        egui::warn_if_debug_build(ui);\n                    });\n\n                    ui.with_layout(Layout::top_down_justified(Align::Min), |ui| {\n                        for (tab, label) in &self.tab_labels {\n                            ui.selectable_value(&mut self.selected_tab, *tab, *label);\n                        }\n                    });\n\n                    #[cfg(not(target_arch = \"wasm32\"))]\n                    ui.with_layout(\n                        Layout::bottom_up(Align::Center).with_cross_justify(true),\n                        |ui| {\n                            ui.add_space(5.0);\n\n                            if connected_to_server {\n                                if ui.button(\"Restart SteamVR\").clicked() {\n                                    self.restart_steamvr(&mut requests);\n                                }\n                            } else if ui.button(\"Launch SteamVR\").clicked() {\n                                crate::steamvr_launcher::LAUNCHER.lock().launch_steamvr();\n                            }\n\n                            ui.horizontal(|ui| {\n                                ui.add_space(4.0);\n                                ui.label(RichText::new(\"SteamVR:\").size(13.0));\n                                ui.add_space(-10.0);\n                                ui.with_layout(\n                                    Layout::centered_and_justified(Direction::LeftToRight),\n                                    |ui| {\n                                        ui.label(\n                                            if connected_to_server {\n                                                RichText::new(\"Connected\").color(theme::OK_GREEN)\n                                            } else {\n                                                RichText::new(\"Disconnected\").color(theme::KO_RED)\n                                            }\n                                            .size(13.0),\n                                        )\n                                    },\n                                );\n                            })\n                        },\n                    )\n                });\n\n            CentralPanel::default()\n                .frame(Frame::new().inner_margin(Margin::same(20)).fill(theme::BG))\n                .show(context, |ui| {\n                    ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {\n                        ui.heading(RichText::new(self.tab_labels[&self.selected_tab]).size(25.0));\n                        match self.selected_tab {\n                            Tab::Devices => {\n                                requests.extend(self.connections_tab.ui(ui, connected_to_server));\n                            }\n                            Tab::Statistics => {\n                                if let Some(request) = self.statistics_tab.ui(ui) {\n                                    requests.push(request);\n                                }\n                            }\n                            Tab::Settings => {\n                                requests.extend(self.settings_tab.ui(ui));\n                            }\n                            #[cfg(not(target_arch = \"wasm32\"))]\n                            Tab::Installation => {\n                                for request in self.installation_tab.ui(ui) {\n                                    match request {\n                                        components::InstallationTabRequest::OpenSetupWizard => {\n                                            self.setup_wizard_open = true\n                                        }\n                                        components::InstallationTabRequest::ServerRequest(\n                                            request,\n                                        ) => {\n                                            requests.push(request);\n                                        }\n                                    }\n                                }\n                            }\n                            Tab::Logs => self.logs_tab.ui(ui),\n                            Tab::Debug => {\n                                if let Some(request) = components::debug_tab_ui(ui) {\n                                    requests.push(request);\n                                }\n                            }\n                            Tab::About => components::about_tab_ui(ui),\n                        }\n                    })\n                });\n        }\n\n        let shutdown_alvr = || {\n            self.data_sources.request(ServerRequest::ShutdownSteamvr);\n\n            crate::steamvr_launcher::LAUNCHER\n                .lock()\n                .ensure_steamvr_shutdown();\n        };\n\n        if let Some(popup) = &self.new_version_popup\n            && let Some(action) = popup.ui(context, shutdown_alvr)\n        {\n            if let CloseAction::CloseWithRequest(request) = action {\n                requests.push(request);\n            }\n\n            self.new_version_popup = None;\n        }\n\n        for request in requests {\n            self.data_sources.request(request);\n        }\n\n        if context.input(|state| state.viewport().close_requested())\n            && self.session.as_ref().is_some_and(|s| {\n                s.to_settings()\n                    .extra\n                    .steamvr_launcher\n                    .open_close_steamvr_with_dashboard\n            })\n        {\n            shutdown_alvr();\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/data_sources.rs",
    "content": "use crate::dashboard::ServerRequest;\nuse alvr_common::{\n    ALVR_VERSION, RelaxedAtomic, debug, error, info,\n    parking_lot::Mutex,\n    semver::{Version, VersionReq},\n    warn,\n};\nuse alvr_events::{Event, EventType};\nuse alvr_packets::FirewallRulesAction;\nuse alvr_server_io::ServerSessionManager;\nuse eframe::egui;\nuse serde::Serialize;\nuse std::{\n    io::ErrorKind,\n    net::{SocketAddr, TcpStream},\n    str::FromStr,\n    sync::{Arc, mpsc},\n    thread::{self, JoinHandle},\n    time::{Duration, Instant},\n};\nuse tungstenite::{\n    client::IntoClientRequest,\n    http::{HeaderValue, Uri},\n};\n\nconst LOCAL_REQUEST_TIMEOUT: Duration = Duration::from_millis(200);\nconst REMOTE_REQUEST_TIMEOUT: Duration = Duration::from_secs(2);\n\nenum SessionSource {\n    Local(Box<ServerSessionManager>),\n    Remote, // Note: the remote (server) is probably living as a separate process in the same PC\n}\n\nfn get_local_session_source() -> ServerSessionManager {\n    let session_file_path = crate::get_filesystem_layout().session();\n    ServerSessionManager::new(Some(session_file_path))\n}\n\npub fn clean_session() {\n    let mut session_manager = get_local_session_source();\n\n    session_manager.clean_client_list();\n\n    #[cfg(target_os = \"linux\")]\n    {\n        let has_nvidia = wgpu::Instance::new(&wgpu::InstanceDescriptor {\n            backends: wgpu::Backends::VULKAN,\n            ..Default::default()\n        })\n        .enumerate_adapters(wgpu::Backends::VULKAN)\n        .iter()\n        .any(|adapter| adapter.get_info().vendor == 0x10de);\n\n        if has_nvidia {\n            session_manager\n                .session_mut()\n                .session_settings\n                .extra\n                .patches\n                .linux_async_reprojection = false;\n        }\n    }\n\n    if session_manager.session().server_version != *ALVR_VERSION {\n        let mut session_ref = session_manager.session_mut();\n        session_ref.server_version = ALVR_VERSION.clone();\n        session_ref.client_connections.clear();\n        session_ref.session_settings.extra.open_setup_wizard = true;\n        session_ref\n            .session_settings\n            .extra\n            .new_version_popup\n            .content\n            .hide_while_version = ALVR_VERSION.to_string();\n    }\n}\n\n// Disallows all methods for mutating (and overwriting to disk) the session\npub fn get_read_only_local_session() -> Arc<ServerSessionManager> {\n    Arc::new(get_local_session_source())\n}\n\nfn report_event_local(\n    context: &egui::Context,\n    sender: &mpsc::Sender<PolledEvent>,\n    event_type: EventType,\n) {\n    sender\n        .send(PolledEvent {\n            inner: Event {\n                timestamp: \"\".into(),\n                event_type,\n            },\n            from_dashboard: false,\n        })\n        .ok();\n    context.request_repaint();\n}\n\nfn report_session_local(\n    context: &egui::Context,\n    sender: &mpsc::Sender<PolledEvent>,\n    session_manager: &ServerSessionManager,\n) {\n    report_event_local(\n        context,\n        sender,\n        EventType::Session(Box::new(session_manager.session().clone())),\n    )\n}\n\npub struct PolledEvent {\n    pub inner: Event,\n    pub from_dashboard: bool,\n}\n\npub struct DataSources {\n    running: Arc<RelaxedAtomic>,\n    requests_sender: mpsc::Sender<ServerRequest>,\n    events_receiver: mpsc::Receiver<PolledEvent>,\n    server_connected: Arc<RelaxedAtomic>,\n    version_check_thread: Option<JoinHandle<Option<()>>>,\n    requests_thread: Option<JoinHandle<()>>,\n    events_thread: Option<JoinHandle<()>>,\n    ping_thread: Option<JoinHandle<()>>,\n}\n\nimpl DataSources {\n    pub fn new(\n        context: egui::Context,\n        events_sender: mpsc::Sender<PolledEvent>,\n        events_receiver: mpsc::Receiver<PolledEvent>,\n    ) -> Self {\n        let filesystem_layout = crate::get_filesystem_layout();\n\n        let running = Arc::new(RelaxedAtomic::new(true));\n        let (requests_sender, requests_receiver) = mpsc::channel();\n        let server_connected = Arc::new(RelaxedAtomic::new(false));\n\n        let session_manager = get_local_session_source();\n        let port = session_manager.settings().connection.web_server_port;\n        let session_source = Arc::new(Mutex::new(SessionSource::Local(Box::new(session_manager))));\n\n        let version_check_thread = thread::spawn({\n            let context = context.clone();\n            let session_source = Arc::clone(&session_source);\n            let events_sender = events_sender.clone();\n            move || {\n                let version_requirement = {\n                    // Best-effort: the check will succeed only when the server is not already running,\n                    // no retries\n                    let SessionSource::Local(session_manager_lock) = &mut *session_source.lock()\n                    else {\n                        return None;\n                    };\n\n                    let version = &session_manager_lock\n                        .settings()\n                        .extra\n                        .new_version_popup\n                        .as_option()?\n                        .hide_while_version;\n\n                    VersionReq::parse(&format!(\">{version}\")).unwrap()\n                };\n\n                let request_agent: ureq::Agent = ureq::Agent::config_builder()\n                    .timeout_global(Some(REMOTE_REQUEST_TIMEOUT))\n                    .build()\n                    .into();\n                if let Ok(response) = request_agent\n                    .get(\"https://api.github.com/repos/alvr-org/ALVR/releases/latest\")\n                    .call()\n                {\n                    let version_data =\n                        response.into_body().read_json::<serde_json::Value>().ok()?;\n\n                    let version_str = version_data\n                        .get(\"tag_name\")\n                        .and_then(|v| Some(v.as_str()?.trim_start_matches(\"v\")))?;\n\n                    let version = version_str.parse::<Version>().ok();\n\n                    if version\n                        .map(|v| version_requirement.matches(&v))\n                        .unwrap_or(false)\n                    {\n                        let message = version_data\n                            .get(\"body\")\n                            .and_then(|v| v.as_str())\n                            .unwrap_or(\"Error parsing release body\");\n\n                        report_event_local(\n                            &context,\n                            &events_sender,\n                            EventType::NewVersionFound {\n                                version: version_str.to_string(),\n                                message: message.to_string(),\n                            },\n                        );\n                    }\n                }\n\n                None\n            }\n        });\n\n        let requests_thread = thread::spawn({\n            let running = Arc::clone(&running);\n            let context = context.clone();\n            let session_source = Arc::clone(&session_source);\n            let events_sender = events_sender.clone();\n            move || {\n                let base_uri = format!(\"http://127.0.0.1:{port}\");\n                let rq: ureq::Agent = ureq::Agent::config_builder()\n                    .timeout_global(Some(LOCAL_REQUEST_TIMEOUT))\n                    .build()\n                    .into();\n\n                while running.value() {\n                    while let Ok(request) = requests_receiver.try_recv() {\n                        debug!(\n                            \"Dashboard request: {}\",\n                            serde_json::to_string(&request).unwrap()\n                        );\n\n                        if let SessionSource::Local(session_manager) = &mut *session_source.lock() {\n                            match request {\n                                ServerRequest::Log(_) => (),\n                                ServerRequest::GetSession => {\n                                    report_session_local(&context, &events_sender, session_manager);\n                                }\n                                ServerRequest::UpdateSession(session) => {\n                                    *session_manager.session_mut() = *session;\n\n                                    report_session_local(&context, &events_sender, session_manager);\n                                }\n                                ServerRequest::SetSessionValues(values) => {\n                                    if let Err(e) = session_manager.set_session_values(values) {\n                                        error!(\"Failed to set session value: {e}\")\n                                    }\n\n                                    report_session_local(&context, &events_sender, session_manager);\n                                }\n                                ServerRequest::UpdateClientList { hostname, action } => {\n                                    session_manager.update_client_connections(hostname, action);\n\n                                    report_session_local(&context, &events_sender, session_manager);\n                                }\n                                ServerRequest::AddFirewallRules => {\n                                    if let Err(e) = alvr_server_io::firewall_rules(\n                                        FirewallRulesAction::Add,\n                                        &filesystem_layout,\n                                    ) {\n                                        error!(\"Failed to add firewall rules! code: {e}\");\n                                    } else {\n                                        info!(\"Successfully added firewall rules!\");\n                                    }\n                                }\n                                ServerRequest::RemoveFirewallRules => {\n                                    if let Err(e) = alvr_server_io::firewall_rules(\n                                        FirewallRulesAction::Remove,\n                                        &filesystem_layout,\n                                    ) {\n                                        error!(\"Failed to remove firewall rules! code: {e}\");\n                                    } else {\n                                        info!(\"Successfully removed firewall rules!\");\n                                    }\n                                }\n                                ServerRequest::RegisterAlvrDriver => {\n                                    let alvr_driver_dir =\n                                        filesystem_layout.openvr_driver_root_dir.clone();\n\n                                    alvr_server_io::driver_registration(&[alvr_driver_dir], true)\n                                        .ok();\n\n                                    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n                                        report_event_local(\n                                            &context,\n                                            &events_sender,\n                                            EventType::DriversList(list),\n                                        )\n                                    }\n                                }\n                                ServerRequest::UnregisterDriver(path) => {\n                                    alvr_server_io::driver_registration(&[path], false).ok();\n\n                                    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n                                        report_event_local(\n                                            &context,\n                                            &events_sender,\n                                            EventType::DriversList(list),\n                                        )\n                                    }\n                                }\n                                ServerRequest::GetDriverList => {\n                                    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n                                        report_event_local(\n                                            &context,\n                                            &events_sender,\n                                            EventType::DriversList(list),\n                                        )\n                                    }\n                                }\n                                ServerRequest::CaptureFrame\n                                | ServerRequest::InsertIdr\n                                | ServerRequest::StartRecording\n                                | ServerRequest::StopRecording => {\n                                    warn!(\n                                        \"Cannot perform action, streamer (SteamVR) is not connected.\"\n                                    )\n                                }\n                                ServerRequest::RestartSteamvr | ServerRequest::ShutdownSteamvr => {\n                                    warn!(\"Streamer not launched, can't signal SteamVR shutdown\")\n                                }\n                            }\n                        } else {\n                            let get = |path: &str| {\n                                rq.get(format!(\"{base_uri}/api/{path}\"))\n                                    .header(\"X-ALVR\", \"true\")\n                                    .call()\n                                    .ok();\n                            };\n\n                            fn post_body(\n                                rq: &ureq::Agent,\n                                base_uri: &str,\n                                path: &str,\n                                body: Option<impl Serialize>,\n                            ) {\n                                let builder = rq\n                                    .post(format!(\"{base_uri}/api/{path}\"))\n                                    .header(\"X-ALVR\", \"true\");\n                                if let Some(body) = body {\n                                    builder.send_json(body).ok();\n                                } else {\n                                    builder.send_empty().ok();\n                                }\n                            }\n                            let post = |path: &str| post_body(&rq, &base_uri, path, None::<()>);\n\n                            match request {\n                                ServerRequest::Log(entry) => {\n                                    post_body(&rq, &base_uri, \"log\", Some(entry))\n                                }\n                                ServerRequest::GetSession => get(\"session\"),\n                                ServerRequest::UpdateSession(session) => {\n                                    post_body(&rq, &base_uri, \"session\", Some(&*session))\n                                }\n                                ServerRequest::SetSessionValues(values) => {\n                                    post_body(&rq, &base_uri, \"session/values\", Some(values))\n                                }\n                                ServerRequest::UpdateClientList { hostname, action } => post_body(\n                                    &rq,\n                                    &base_uri,\n                                    \"session/client-connections\",\n                                    Some((hostname, action)),\n                                ),\n                                ServerRequest::AddFirewallRules => post(\"firewall-rules/add\"),\n                                ServerRequest::RemoveFirewallRules => post(\"firewall-rules/remove\"),\n                                ServerRequest::GetDriverList => get(\"drivers\"),\n                                ServerRequest::RegisterAlvrDriver => post(\"drivers/register-alvr\"),\n                                ServerRequest::UnregisterDriver(path) => {\n                                    post_body(&rq, &base_uri, \"drivers/unregister\", Some(path))\n                                }\n                                ServerRequest::CaptureFrame => post(\"capture-frame\"),\n                                ServerRequest::InsertIdr => post(\"insert-idr\"),\n                                ServerRequest::StartRecording => post(\"recording/start\"),\n                                ServerRequest::StopRecording => post(\"recording/stop\"),\n                                ServerRequest::RestartSteamvr => post(\"restart-steamvr\"),\n                                ServerRequest::ShutdownSteamvr => post(\"shutdown-steamvr\"),\n                            }\n                        }\n                    }\n\n                    thread::sleep(Duration::from_millis(100));\n                }\n            }\n        });\n\n        let events_thread = thread::spawn({\n            let running = Arc::clone(&running);\n            let session_source = Arc::clone(&session_source);\n            move || {\n                while running.value() {\n                    if matches!(*session_source.lock(), SessionSource::Local(_)) {\n                        thread::sleep(Duration::from_millis(100));\n\n                        continue;\n                    }\n\n                    let uri = Uri::from_str(&format!(\"ws://127.0.0.1:{port}/api/events\")).unwrap();\n\n                    let maybe_socket = TcpStream::connect_timeout(\n                        &SocketAddr::from_str(&format!(\"127.0.0.1:{port}\")).unwrap(),\n                        Duration::from_millis(500),\n                    );\n                    let Ok(socket) = maybe_socket else {\n                        thread::sleep(Duration::from_millis(500));\n\n                        continue;\n                    };\n\n                    let mut req = uri.into_client_request().unwrap();\n                    req.headers_mut()\n                        .insert(\"X-ALVR\", HeaderValue::from_str(\"true\").unwrap());\n\n                    let Ok((mut ws, _)) = tungstenite::client(req, socket) else {\n                        thread::sleep(Duration::from_millis(500));\n\n                        continue;\n                    };\n\n                    ws.get_mut().set_nonblocking(true).ok();\n\n                    while running.value() {\n                        match ws.read() {\n                            Ok(tungstenite::Message::Text(json_string)) => {\n                                debug!(\"Server event: {json_string}\");\n                                if let Ok(event) = serde_json::from_str(&json_string) {\n                                    events_sender\n                                        .send(PolledEvent {\n                                            inner: event,\n                                            from_dashboard: false,\n                                        })\n                                        .ok();\n                                    context.request_repaint();\n                                }\n                            }\n                            Err(e) => {\n                                if let tungstenite::Error::Io(e) = e\n                                    && e.kind() == ErrorKind::WouldBlock\n                                {\n                                    thread::sleep(Duration::from_millis(50));\n\n                                    continue;\n                                }\n\n                                break;\n                            }\n                            _ => (),\n                        }\n                    }\n                }\n            }\n        });\n\n        let ping_thread = thread::spawn({\n            let running = Arc::clone(&running);\n            let session_source = Arc::clone(&session_source);\n            let server_connected = Arc::clone(&server_connected);\n            move || {\n                const PING_INTERVAL: Duration = Duration::from_secs(1);\n                let mut deadline = Instant::now();\n                let uri = format!(\"http://127.0.0.1:{port}/api/version\");\n\n                let request_agent: ureq::Agent = ureq::Agent::config_builder()\n                    .timeout_global(Some(LOCAL_REQUEST_TIMEOUT))\n                    .build()\n                    .into();\n\n                loop {\n                    let maybe_server_version = request_agent\n                        .get(&uri)\n                        .header(\"X-ALVR\", \"true\")\n                        .call()\n                        .ok()\n                        .and_then(|r| {\n                            Version::from_str(&r.into_body().read_to_string().ok()?).ok()\n                        });\n\n                    let connected = if let Some(version) = maybe_server_version {\n                        // We need exact match because we don't do session extrapolation at the\n                        // dashboard level. In the future we may relax the contraint and consider\n                        // protocol compatibility check for dashboard.\n                        let matches = version == *alvr_common::ALVR_VERSION;\n\n                        if !matches {\n                            error!(\n                                \"Server version mismatch: found {version}. Please remove all previous ALVR installations\"\n                            );\n                        }\n\n                        matches\n                    } else {\n                        false\n                    };\n\n                    {\n                        let mut session_source_lock = session_source.lock();\n                        if connected && matches!(*session_source_lock, SessionSource::Local(_)) {\n                            info!(\"Server connected\");\n                            *session_source_lock = SessionSource::Remote;\n                        } else if !connected\n                            && matches!(*session_source_lock, SessionSource::Remote)\n                        {\n                            info!(\"Server disconnected\");\n                            *session_source_lock =\n                                SessionSource::Local(Box::new(get_local_session_source()));\n                        }\n                    }\n\n                    server_connected.set(connected);\n\n                    deadline += PING_INTERVAL;\n\n                    while Instant::now() < deadline {\n                        if !running.value() {\n                            return;\n                        }\n                        thread::sleep(Duration::from_millis(100));\n                    }\n                }\n            }\n        });\n\n        Self {\n            requests_sender,\n            events_receiver,\n            server_connected,\n            running,\n            version_check_thread: Some(version_check_thread),\n            requests_thread: Some(requests_thread),\n            events_thread: Some(events_thread),\n            ping_thread: Some(ping_thread),\n        }\n    }\n\n    pub fn request(&self, request: ServerRequest) {\n        self.requests_sender.send(request).ok();\n    }\n\n    pub fn poll_event(&self) -> Option<PolledEvent> {\n        self.events_receiver.try_recv().ok()\n    }\n\n    pub fn server_connected(&self) -> bool {\n        self.server_connected.value()\n    }\n}\n\nimpl Drop for DataSources {\n    fn drop(&mut self) {\n        self.running.set(false);\n\n        self.version_check_thread.take().unwrap().join().ok();\n        self.requests_thread.take().unwrap().join().ok();\n        self.events_thread.take().unwrap().join().ok();\n        self.ping_thread.take().unwrap().join().ok();\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/data_sources_wasm.rs",
    "content": "use alvr_events::Event;\nuse alvr_packets::ServerRequest;\nuse eframe::{egui, web_sys};\nuse ewebsock::{WsEvent, WsMessage, WsReceiver};\nuse gloo_net::http::Request;\n\npub struct DataSources {\n    context: egui::Context,\n    ws_receiver: Option<WsReceiver>,\n}\n\nimpl DataSources {\n    pub fn new(context: egui::Context) -> Self {\n        Self {\n            context,\n            ws_receiver: None,\n        }\n    }\n\n    pub fn request(&self, request: ServerRequest) {\n        let context = self.context.clone();\n        wasm_bindgen_futures::spawn_local(async move {\n            Request::post(\"/api/dashboard-request\")\n                .header(\"X-ALVR\", \"true\")\n                .body(serde_json::to_string(&request).unwrap())\n                .send()\n                .await\n                .ok();\n\n            context.request_repaint();\n        })\n    }\n\n    pub fn poll_event(&mut self) -> Option<Event> {\n        if self.ws_receiver.is_none() {\n            let host = web_sys::window().unwrap().location().host().unwrap();\n            // TODO: Set X-ALVR\n            //let mut options = ewebsock::Options::default();\n            //options.additional_headers = vec!((\"X-ALVR\", \"true\"));\n            let Ok((_, receiver)) = ewebsock::connect(format!(\"ws://{host}/api/events\")) else {\n                return None;\n            };\n            self.ws_receiver = Some(receiver);\n        }\n\n        if let Some(event) = self.ws_receiver.as_ref().unwrap().try_recv() {\n            match event {\n                WsEvent::Message(WsMessage::Text(json_string)) => {\n                    serde_json::from_str(&json_string).ok()\n                }\n                WsEvent::Error(_) | WsEvent::Closed => {\n                    // recreate the ws connection next poll_event invocation\n                    self.ws_receiver = None;\n\n                    None\n                }\n                _ => None,\n            }\n        } else {\n            None\n        }\n    }\n\n    pub fn server_connected(&self) -> bool {\n        true\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/linux_checks.rs",
    "content": "pub fn audio_check() {\n    // No check for result, just show errors in logs\n    let _ = alvr_audio::linux::try_load_pipewire();\n}\n"
  },
  {
    "path": "alvr/dashboard/src/logging_backend.rs",
    "content": "use crate::data_sources::PolledEvent;\nuse alvr_common::{LogEntry, LogSeverity, log::LevelFilter, parking_lot::Mutex};\nuse alvr_events::{Event, EventType};\nuse std::{\n    io::Write,\n    sync::{Arc, mpsc},\n};\n\npub fn init_logging(event_sender: mpsc::Sender<PolledEvent>) {\n    let event_sender = Arc::new(Mutex::new(event_sender));\n\n    env_logger::Builder::new()\n        .filter(Some(\"alvr_events\"), LevelFilter::Off)\n        .filter(Some(\"naga\"), LevelFilter::Off)\n        .filter(Some(\"ureq\"), LevelFilter::Off)\n        .filter(Some(\"wgpu_core\"), LevelFilter::Off)\n        .filter(Some(\"wgpu_hal\"), LevelFilter::Off)\n        .filter_level(if cfg!(debug_assertions) {\n            LevelFilter::Debug\n        } else {\n            LevelFilter::Info\n        })\n        .format(move |f, record| {\n            let timestamp = chrono::Local::now().format(\"%H:%M:%S.%3f\").to_string();\n\n            event_sender\n                .lock()\n                .send(PolledEvent {\n                    inner: Event {\n                        timestamp: timestamp.clone(),\n                        event_type: EventType::Log(LogEntry {\n                            severity: LogSeverity::from_log_level(record.level()),\n                            content: format!(\"{}\", record.args()),\n                        }),\n                    },\n                    from_dashboard: true,\n                })\n                .ok();\n\n            writeln!(\n                f,\n                \"[{} {} {}] {}\",\n                timestamp,\n                record.level(),\n                record.module_path().unwrap_or_default(),\n                record.args()\n            )\n        })\n        .init();\n}\n"
  },
  {
    "path": "alvr/dashboard/src/main.rs",
    "content": "// hide console window on Windows in release\n#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")]\n\nmod dashboard;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nmod data_sources;\n#[cfg(target_arch = \"wasm32\")]\nmod data_sources_wasm;\n#[cfg(not(target_arch = \"wasm32\"))]\n#[cfg(target_os = \"linux\")]\nmod linux_checks;\n#[cfg(not(target_arch = \"wasm32\"))]\nmod logging_backend;\n#[cfg(not(target_arch = \"wasm32\"))]\nmod steamvr_launcher;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nuse data_sources::DataSources;\n#[cfg(target_arch = \"wasm32\")]\nuse data_sources_wasm::DataSources;\n\nuse alvr_filesystem as afs;\nuse dashboard::Dashboard;\n\nfn get_filesystem_layout() -> afs::Layout {\n    afs::filesystem_layout_from_dashboard_exe(&std::env::current_exe().unwrap()).unwrap()\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn main() {\n    use alvr_common::ALVR_VERSION;\n    use alvr_common::info;\n    use alvr_filesystem as afs;\n    use eframe::{\n        NativeOptions,\n        egui::{IconData, ViewportBuilder},\n    };\n    use ico::IconDir;\n    use std::{env, ffi::OsStr, fs};\n    use std::{io::Cursor, sync::mpsc};\n\n    let (server_events_sender, server_events_receiver) = mpsc::channel();\n    logging_backend::init_logging(server_events_sender.clone());\n\n    // Kill any other dashboard instance\n    let self_path = std::env::current_exe().unwrap().canonicalize().unwrap();\n    for proc in sysinfo::System::new_all().processes_by_name(OsStr::new(&afs::dashboard_fname())) {\n        // According to implementation notes, on linux the returned path can be empty due to\n        // privileges, so canonicalize can fail\n        if let Some(other_path) = proc.exe().and_then(|path| path.canonicalize().ok())\n            && other_path != self_path\n        {\n            info!(\n                \"Killing other dashboard process with path {}\",\n                other_path.display()\n            );\n            proc.kill();\n        }\n    }\n\n    #[cfg(target_os = \"linux\")]\n    linux_checks::audio_check();\n\n    data_sources::clean_session();\n\n    if data_sources::get_read_only_local_session()\n        .settings()\n        .extra\n        .steamvr_launcher\n        .open_close_steamvr_with_dashboard\n    {\n        steamvr_launcher::LAUNCHER.lock().launch_steamvr()\n    }\n\n    let ico = IconDir::read(Cursor::new(include_bytes!(\"../resources/dashboard.ico\"))).unwrap();\n    let image = ico.entries().first().unwrap().decode().unwrap();\n\n    // Workaround for the steam deck\n    if fs::read_to_string(\"/sys/devices/virtual/dmi/id/board_vendor\")\n        .map(|vendor| vendor.trim() == \"Valve\")\n        .unwrap_or(false)\n    {\n        unsafe { env::set_var(\"WINIT_X11_SCALE_FACTOR\", \"1\") };\n    }\n\n    eframe::run_native(\n        &format!(\"ALVR Dashboard (streamer v{})\", *ALVR_VERSION),\n        NativeOptions {\n            viewport: ViewportBuilder::default()\n                .with_app_id(\"alvr.dashboard\")\n                .with_inner_size((900.0, 600.0))\n                .with_icon(IconData {\n                    rgba: image.rgba_data().to_owned(),\n                    width: image.width(),\n                    height: image.height(),\n                }),\n            centered: true,\n            ..Default::default()\n        },\n        {\n            Box::new(move |creation_context| {\n                let data_source = DataSources::new(\n                    creation_context.egui_ctx.clone(),\n                    server_events_sender,\n                    server_events_receiver,\n                );\n\n                Ok(Box::new(Dashboard::new(creation_context, data_source)))\n            })\n        },\n    )\n    .unwrap();\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn main() {\n    console_error_panic_hook::set_once();\n    wasm_logger::init(wasm_logger::Config::default());\n\n    wasm_bindgen_futures::spawn_local(async {\n        eframe::WebRunner::new()\n            .start(\"dashboard_canvas\", eframe::WebOptions::default(), {\n                Box::new(move |creation_context| {\n                    let context = creation_context.egui_ctx.clone();\n                    Box::new(Dashboard::new(creation_context, DataSources::new(context)))\n                })\n            })\n            .await\n            .ok();\n    });\n}\n"
  },
  {
    "path": "alvr/dashboard/src/steamvr_launcher/linux_steamvr.rs",
    "content": "use std::fs;\nuse std::path::Path;\nuse std::process::Command;\n\nuse alvr_common::anyhow::bail;\nuse alvr_common::{debug, error, info, warn};\nuse sysinfo::Process;\n\npub fn launch_steamvr_with_steam() {\n    Command::new(\"steam\")\n        .args([\"steam://rungameid/250820\"])\n        .spawn()\n        .ok();\n}\n\npub fn terminate_process(process: &Process) {\n    process.kill_with(sysinfo::Signal::Term);\n}\n\npub fn maybe_wrap_vrcompositor_launcher() -> alvr_common::anyhow::Result<()> {\n    let steamvr_bin_dir = alvr_server_io::steamvr_root_dir()?\n        .join(\"bin\")\n        .join(\"linux64\");\n    let steamvr_vrserver_path = steamvr_bin_dir.join(\"vrserver\");\n    debug!(\n        \"File path used to check for linux files: {}\",\n        steamvr_vrserver_path.display()\n    );\n    match steamvr_vrserver_path.try_exists() {\n        Ok(exists) => {\n            if !exists {\n                bail!(\n                    \"SteamVR Linux files missing, aborting startup, please re-check compatibility tools for SteamVR, verify integrity of files for SteamVR and make sure you're not using Flatpak Steam with non-Flatpak ALVR.\"\n                );\n            }\n        }\n        Err(e) => {\n            return Err(e.into());\n        }\n    };\n\n    let launcher_path = steamvr_bin_dir.join(\"vrcompositor\");\n    // In case of SteamVR update, vrcompositor will be restored\n    if fs::read_link(&launcher_path).is_ok() {\n        fs::remove_file(&launcher_path)?; // recreate the link\n    } else {\n        fs::rename(&launcher_path, steamvr_bin_dir.join(\"vrcompositor.real\"))?;\n    }\n\n    std::os::unix::fs::symlink(\n        crate::get_filesystem_layout().vrcompositor_wrapper(),\n        &launcher_path,\n    )?;\n\n    Ok(())\n}\n#[derive(PartialEq)]\nenum DeviceInfo {\n    Nvidia,\n    Amd { device_type: wgpu::DeviceType },\n    Intel { device_type: wgpu::DeviceType },\n    Unknown,\n}\n\npub fn linux_hardware_checks() {\n    let wgpu_adapters = wgpu::Instance::new(&wgpu::InstanceDescriptor {\n        backends: wgpu::Backends::VULKAN,\n        ..Default::default()\n    })\n    .enumerate_adapters(wgpu::Backends::VULKAN);\n    let device_infos = wgpu_adapters\n        .iter()\n        .filter(|adapter| {\n            adapter.get_info().device_type == wgpu::DeviceType::DiscreteGpu\n                || adapter.get_info().device_type == wgpu::DeviceType::IntegratedGpu\n        })\n        .map(|adapter| {\n            let vendor = match adapter.get_info().vendor {\n                0x10de => DeviceInfo::Nvidia,\n                0x1002 => DeviceInfo::Amd {\n                    device_type: adapter.get_info().device_type,\n                },\n                0x8086 => DeviceInfo::Intel {\n                    device_type: adapter.get_info().device_type,\n                },\n                _ => DeviceInfo::Unknown,\n            };\n\n            (adapter, vendor)\n        })\n        .collect::<Vec<_>>();\n    linux_gpu_checks(&device_infos);\n    linux_encoder_checks(&device_infos);\n}\n\nfn linux_gpu_checks(device_infos: &[(&wgpu::Adapter, DeviceInfo)]) {\n    let have_intel_igpu = device_infos.iter().any(|gpu| {\n        gpu.1\n            == DeviceInfo::Intel {\n                device_type: wgpu::DeviceType::IntegratedGpu,\n            }\n    });\n    debug!(\"have_intel_igpu: {}\", have_intel_igpu);\n    let have_amd_igpu = device_infos.iter().any(|gpu| {\n        gpu.1\n            == DeviceInfo::Amd {\n                device_type: wgpu::DeviceType::IntegratedGpu,\n            }\n    });\n    debug!(\"have_amd_igpu: {}\", have_amd_igpu);\n\n    let have_igpu = have_intel_igpu || have_amd_igpu;\n    debug!(\"have_igpu: {}\", have_igpu);\n\n    let have_nvidia_dgpu = device_infos.iter().any(|gpu| gpu.1 == DeviceInfo::Nvidia);\n    debug!(\"have_nvidia_dgpu: {}\", have_nvidia_dgpu);\n\n    let have_amd_dgpu = device_infos.iter().any(|gpu| {\n        gpu.1\n            == DeviceInfo::Amd {\n                device_type: wgpu::DeviceType::DiscreteGpu,\n            }\n    });\n    debug!(\"have_amd_dgpu: {}\", have_amd_dgpu);\n\n    if have_amd_igpu || have_amd_dgpu {\n        let is_any_amd_driver_invalid = device_infos.iter().any(|gpu| {\n            info!(\"Driver name: {}\", gpu.0.get_info().driver);\n            match gpu.0.get_info().driver.as_str() {\n                \"AMD proprietary driver\" | \"AMD open-source driver\" => true, // AMDGPU-Pro | AMDVLK\n                _ => false,\n            }\n        });\n        if is_any_amd_driver_invalid {\n            error!(\n                \"Amdvlk or amdgpu-pro vulkan drivers detected, SteamVR may not function properly. \\\n            Please remove them or make them unavailable for SteamVR and games you're trying to launch.\\n\\\n            For more detailed info visit the wiki: \\\n            https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting#artifacting-no-steamvr-overlay-or-graphical-glitches-in-streaming-view\"\n            )\n        }\n    }\n\n    let have_intel_dgpu = device_infos.iter().any(|gpu| {\n        gpu.1\n            == DeviceInfo::Intel {\n                device_type: wgpu::DeviceType::DiscreteGpu,\n            }\n    });\n    debug!(\"have_intel_dgpu: {}\", have_intel_dgpu);\n\n    let steamvr_root_dir = match alvr_server_io::steamvr_root_dir() {\n        Ok(dir) => dir,\n        Err(e) => {\n            error!(\n                \"Couldn't find OpenVR or SteamVR files. \\\n                Please make sure you have installed and ran SteamVR at least once. \\\n                Or if you're using Flatpak Steam, make sure to use ALVR Dashboard from Flatpak ALVR. {e}\"\n            );\n            return;\n        }\n    };\n\n    let vrmonitor_path_string = steamvr_root_dir\n        .join(\"bin\")\n        .join(\"vrmonitor.sh\")\n        .into_os_string()\n        .into_string()\n        .unwrap();\n    debug!(\"vrmonitor_path: {}\", vrmonitor_path_string);\n\n    let steamvr_opts = \"For functioning VR you need to put the following line into SteamVR's launch options and restart it:\";\n    let game_opts = \"And this similar line to the launch options of ALL games that you're trying to launch from steam:\";\n\n    let mut vrmonitor_path_written = false;\n    if have_igpu {\n        if have_nvidia_dgpu {\n            let base_path = \"/usr/share/vulkan/icd.d/nvidia_icd\";\n            let nvidia_vk_override_path = if Path::new(&format!(\"{base_path}.json\")).exists() {\n                format!(\"VK_DRIVER_FILES={base_path}.json\")\n            } else if Path::new(&format!(\"{base_path}.x86_64.json\")).exists() {\n                format!(\"VK_DRIVER_FILES={base_path}.x86_64.json\")\n            } else {\n                \"__VK_LAYER_NV_optimus=NVIDIA_only\".to_string()\n            };\n            let nv_options = format!(\n                \"__GLX_VENDOR_LIBRARY_NAME=nvidia __NV_PRIME_RENDER_OFFLOAD=1 {nvidia_vk_override_path}\"\n            );\n\n            warn!(\"{steamvr_opts}\\n{nv_options} {vrmonitor_path_string} %command%\");\n            warn!(\"{game_opts}\\n{nv_options} %command%\");\n\n            vrmonitor_path_written = true;\n        } else if have_intel_dgpu || have_amd_dgpu {\n            warn!(\"{steamvr_opts}\\nDRI_PRIME=1 {vrmonitor_path_string} %command%\");\n            warn!(\"{game_opts}\\nDRI_PRIME=1 %command%\");\n            vrmonitor_path_written = true;\n        } else {\n            warn!(\n                \"Beware, using just integrated graphics might lead to very poor performance in SteamVR and VR games.\"\n            );\n            warn!(\n                \"For more information, please refer to the wiki: https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting\"\n            )\n        }\n    }\n    if !vrmonitor_path_written {\n        warn!(\n            \"Make sure you have put the following line in your SteamVR launch options and restart it:\\n\\\n            {vrmonitor_path_string} %command%\"\n        )\n    }\n}\n\nfn linux_encoder_checks(device_infos: &[(&wgpu::Adapter, DeviceInfo)]) {\n    for device_info in device_infos {\n        match device_info.1 {\n            DeviceInfo::Nvidia => {\n                match nvml_wrapper::Nvml::init() {\n                    Ok(nvml) => {\n                        let device_count = nvml.device_count().unwrap();\n                        debug!(\"nvml device count: {}\", device_count);\n                        // fixme: on multi-gpu nvidia system will do it twice,\n                        for index in 0..device_count {\n                            match nvml.device_by_index(index) {\n                                Ok(device) => {\n                                    debug!(\"nvml device name: {}\", device.name().unwrap());\n                                    probe_nvenc_encoder_profile(\n                                        &device,\n                                        nvml_wrapper::enum_wrappers::device::EncoderType::H264,\n                                        \"H264\",\n                                    );\n                                    probe_nvenc_encoder_profile(\n                                        &device,\n                                        nvml_wrapper::enum_wrappers::device::EncoderType::HEVC,\n                                        \"HEVC\",\n                                    );\n                                    // todo: probe for AV1 when will be available in nvml-wrapper\n                                }\n                                Err(e) => {\n                                    error!(\"Failed to acquire NVML device with error: {}\", e)\n                                }\n                            }\n                        }\n                    }\n                    Err(e) => {\n                        alvr_common::show_e(format!(\"Can't initialize NVML engine, error: {e}.\"))\n                    }\n                }\n            }\n            DeviceInfo::Amd { device_type: _ } | DeviceInfo::Intel { device_type: _ } => {\n                let libva_display_open = libva::Display::open();\n                if let Some(libva_display) = libva_display_open {\n                    if let Ok(vendor_string) = libva_display.query_vendor_string() {\n                        info!(\"GPU Encoder vendor: {}\", vendor_string);\n                    }\n                    probe_libva_encoder_profile(\n                        &libva_display,\n                        libva::VAProfile::VAProfileH264Main,\n                        \"H264\",\n                        true,\n                    );\n                    probe_libva_encoder_profile(\n                        &libva_display,\n                        libva::VAProfile::VAProfileHEVCMain,\n                        \"HEVC\",\n                        true,\n                    );\n                    probe_libva_encoder_profile(\n                        &libva_display,\n                        libva::VAProfile::VAProfileAV1Profile0,\n                        \"AV1\",\n                        false,\n                    );\n                } else {\n                    alvr_common::show_e(\n                        \"Couldn't find VA-API runtime on system, \\\n                        you unlikely to have hardware encoding. \\\n                        Please install VA-API runtime for your distribution \\\n                        and make sure it works (Manjaro, Fedora affected). \\\n                        For detailed advice, check wiki: \\\n                        https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting#failed-to-create-vaapi-encoder\",\n                    );\n                }\n            }\n            _ => alvr_common::show_e(\n                \"Couldn't determine gpu for hardware encoding. \\\n            You will likely fallback to software encoding.\",\n            ),\n        }\n    }\n}\n\nfn probe_nvenc_encoder_profile(\n    device: &nvml_wrapper::Device,\n    encoder_type: nvml_wrapper::enum_wrappers::device::EncoderType,\n    profile_name: &str,\n) {\n    match device.encoder_capacity(encoder_type) {\n        Ok(_) => {\n            info!(\"GPU supports {} profile.\", profile_name);\n        }\n        Err(e) => {\n            if matches!(e, nvml_wrapper::error::NvmlError::NotSupported) {\n                alvr_common::show_e(format!(\n                    \"Your NVIDIA gpu doesn't support {profile_name}. Please make sure CUDA is installed properly. Error: {e}\"\n                ))\n            } else {\n                error!(\"{}\", e)\n            }\n        }\n    }\n}\n\nfn probe_libva_encoder_profile(\n    libva_display: &std::rc::Rc<libva::Display>,\n    profile_type: libva::VAProfile::Type,\n    profile_name: &str,\n    is_critical: bool,\n) {\n    let profile_probe = libva_display.query_config_entrypoints(profile_type);\n    let mut message = String::new();\n    if profile_probe.is_err() {\n        message = format!(\"Couldn't find {profile_name} encoder.\");\n    } else if let Ok(profile) = profile_probe {\n        if profile.is_empty() {\n            message = format!(\"{profile_name} profile entrypoint is empty.\");\n        } else if !profile.contains(&libva::VAEntrypoint::VAEntrypointEncSlice) {\n            message = format!(\"{profile_name} profile does not contain encoding entrypoint.\");\n        }\n    }\n    if !message.is_empty() {\n        if is_critical {\n            error!(\"{} Your gpu may not suport encoding with this.\", message);\n        } else {\n            info!(\n                \"{}\n                Your gpu may not suport encoding with this. \\\n            If you're not using this encoder, ignore this message.\",\n                message\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/dashboard/src/steamvr_launcher/mod.rs",
    "content": "#[cfg(target_os = \"linux\")]\nmod linux_steamvr;\n#[cfg(windows)]\nmod windows_steamvr;\n\nuse crate::data_sources;\nuse alvr_adb::commands as adb;\nuse alvr_common::{\n    anyhow::{Context, Result},\n    debug, error,\n    glam::bool,\n    parking_lot::Mutex,\n    warn,\n};\nuse alvr_filesystem::{self as afs};\nuse serde_json::{self, json};\nuse std::{\n    ffi::OsStr,\n    fs,\n    marker::PhantomData,\n    process::Command,\n    thread,\n    time::{Duration, Instant},\n};\nuse sysinfo::{ProcessesToUpdate, System};\n\nconst SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(10);\nconst DRIVER_KEY: &str = \"driver_alvr_server\";\nconst BLOCKED_KEY: &str = \"blocked_by_safe_mode\";\n\npub fn is_steamvr_running() -> bool {\n    System::new_all()\n        .processes_by_name(OsStr::new(&afs::exec_fname(\"vrserver\")))\n        .count()\n        != 0\n}\n\npub fn maybe_kill_steamvr() {\n    let mut system = System::new_all();\n\n    #[allow(unused_variables)]\n    for process in system.processes_by_name(OsStr::new(&afs::exec_fname(\"vrmonitor\"))) {\n        debug!(\"Killing vrmonitor\");\n\n        #[cfg(target_os = \"linux\")]\n        linux_steamvr::terminate_process(process);\n        #[cfg(windows)]\n        windows_steamvr::kill_process(process.pid().as_u32());\n\n        thread::sleep(Duration::from_secs(1));\n    }\n\n    system.refresh_processes(ProcessesToUpdate::All, true);\n\n    #[allow(unused_variables)]\n    for process in system.processes_by_name(OsStr::new(&afs::exec_fname(\"vrserver\"))) {\n        debug!(\"Killing vrserver\");\n\n        #[cfg(target_os = \"linux\")]\n        linux_steamvr::terminate_process(process);\n        #[cfg(windows)]\n        windows_steamvr::kill_process(process.pid().as_u32());\n\n        thread::sleep(Duration::from_secs(1));\n    }\n}\n\nfn unblock_alvr_driver() -> Result<()> {\n    if !cfg!(target_os = \"linux\") {\n        return Ok(());\n    }\n\n    let path = alvr_server_io::steamvr_settings_file_path()?;\n    let text = fs::read_to_string(&path).with_context(|| format!(\"Failed to read {path:?}\"))?;\n    let new_text = unblock_alvr_driver_within_vrsettings(text.as_str())\n        .with_context(|| \"Failed to rewrite .vrsettings.\")?;\n    fs::write(&path, new_text)\n        .with_context(|| \"Failed to write .vrsettings back after changing it.\")?;\n    Ok(())\n}\n\n// Reads and writes back steamvr.vrsettings in order to\n// ensure the ALVR driver is not blocked (safe mode).\nfn unblock_alvr_driver_within_vrsettings(text: &str) -> Result<String> {\n    let mut settings = serde_json::from_str::<serde_json::Value>(text)?;\n    let values = settings\n        .as_object_mut()\n        .with_context(|| \"Failed to parse .vrsettings.\")?;\n    let blocked = values\n        .get(DRIVER_KEY)\n        .and_then(|driver| driver.get(BLOCKED_KEY))\n        .and_then(|blocked| blocked.as_bool())\n        .unwrap_or(false);\n\n    if blocked {\n        debug!(\"Unblocking ALVR driver in SteamVR.\");\n        if !values.contains_key(DRIVER_KEY) {\n            values.insert(DRIVER_KEY.into(), json!({}));\n        }\n        let driver = settings[DRIVER_KEY]\n            .as_object_mut()\n            .with_context(|| \"Did not find ALVR key in settings.\")?;\n        driver.insert(BLOCKED_KEY.into(), json!(false)); // overwrites if present\n    } else {\n        debug!(\"ALVR is not blocked in SteamVR.\");\n    }\n\n    Ok(serde_json::to_string_pretty(&settings)?)\n}\n\npub struct Launcher {\n    _phantom: PhantomData<()>,\n}\n\nimpl Launcher {\n    pub fn launch_steamvr(&self) {\n        // The ADB server might be left running because of an unclean termination of SteamVR.\n        // Kill it unconditionally to ensure clean state, regardless of current connection mode.\n        // Note: this will also kill a system-wide ADB server not started by ALVR.\n        if let Some(path) = adb::get_adb_path(&crate::get_filesystem_layout()) {\n            adb::kill_server(&path).ok();\n        }\n\n        #[cfg(target_os = \"linux\")]\n        linux_steamvr::linux_hardware_checks();\n\n        let alvr_driver_dir = crate::get_filesystem_layout().openvr_driver_root_dir;\n\n        // Make sure to unregister any other ALVR driver because it would cause a socket conflict\n        let other_alvr_dirs = alvr_server_io::get_registered_drivers()\n            .unwrap_or_default()\n            .into_iter()\n            .filter(|path| {\n                path.to_string_lossy().to_lowercase().contains(\"alvr\") && *path != alvr_driver_dir\n            })\n            .collect::<Vec<_>>();\n        alvr_server_io::driver_registration(&other_alvr_dirs, false).ok();\n\n        alvr_server_io::driver_registration(&[alvr_driver_dir], true).ok();\n\n        if let Err(err) = unblock_alvr_driver() {\n            warn!(\"Failed to unblock ALVR driver: {:?}\", err);\n        }\n\n        #[cfg(target_os = \"linux\")]\n        {\n            let vrcompositor_wrap_result = linux_steamvr::maybe_wrap_vrcompositor_launcher();\n            alvr_common::show_err(linux_steamvr::maybe_wrap_vrcompositor_launcher());\n            if vrcompositor_wrap_result.is_err() {\n                return;\n            }\n        }\n\n        if is_steamvr_running() {\n            return;\n        }\n\n        debug!(\"SteamVR is dead. Launching...\");\n\n        if data_sources::get_read_only_local_session()\n            .settings()\n            .extra\n            .steamvr_launcher\n            .direct_launch\n        {\n            let start_script = afs::filesystem_layout_invalid().server_start_script();\n\n            if start_script.exists() {\n                debug!(\"Running VR server start script: {}\", start_script.display());\n\n                if let Err(e) = Command::new(&start_script).spawn() {\n                    error!(\"Failed to run VR server start script: {e}\");\n                }\n            } else if let Ok(steamvr_bin_dir) =\n                alvr_server_io::steamvr_root_dir().map(|root| root.join(\"bin\"))\n            {\n                let steamvr_path = if cfg!(windows) {\n                    steamvr_bin_dir.join(\"win64\").join(\"vrstartup.exe\")\n                } else {\n                    steamvr_bin_dir.join(\"vrmonitor.sh\")\n                };\n\n                debug!(\"Launching SteamVR from path: {}\", steamvr_path.display());\n\n                if let Err(e) = Command::new(&steamvr_path).spawn() {\n                    error!(\n                        \"Failed to run SteamVR from automatically detected path {} with error: {e}\",\n                        steamvr_path.display()\n                    );\n                }\n            } else {\n                error!(\"Failed to find SteamVR files to directly launch SteamVR\");\n            }\n        } else {\n            #[cfg(windows)]\n            windows_steamvr::launch_steamvr_with_steam();\n\n            #[cfg(target_os = \"linux\")]\n            linux_steamvr::launch_steamvr_with_steam();\n        }\n    }\n\n    pub fn ensure_steamvr_shutdown(&self) {\n        debug!(\"Waiting for SteamVR to shutdown...\");\n        let start_time = Instant::now();\n        while start_time.elapsed() < SHUTDOWN_TIMEOUT && is_steamvr_running() {\n            thread::sleep(Duration::from_millis(500));\n        }\n\n        maybe_kill_steamvr();\n    }\n\n    pub fn restart_steamvr(&self) {\n        self.ensure_steamvr_shutdown();\n        self.launch_steamvr();\n    }\n}\n\n// Singleton with exclusive access\npub static LAUNCHER: Mutex<Launcher> = Mutex::new(Launcher {\n    _phantom: PhantomData,\n});\n"
  },
  {
    "path": "alvr/dashboard/src/steamvr_launcher/windows_steamvr.rs",
    "content": "use std::os::windows::process::CommandExt;\nuse std::process::Command;\n\nconst CREATE_NO_WINDOW: u32 = 0x0800_0000;\n\npub fn launch_steamvr_with_steam() {\n    Command::new(\"cmd\")\n        .args([\"/C\", \"start\", \"steam://rungameid/250820\"])\n        .creation_flags(CREATE_NO_WINDOW)\n        .spawn()\n        .ok();\n}\n\npub fn kill_process(pid: u32) {\n    Command::new(\"taskkill.exe\")\n        .args([\"/PID\", &pid.to_string(), \"/F\"])\n        .creation_flags(CREATE_NO_WINDOW)\n        .output()\n        .ok();\n}\n"
  },
  {
    "path": "alvr/events/Cargo.toml",
    "content": "[package]\nname = \"alvr_events\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\n\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\n"
  },
  {
    "path": "alvr/events/src/lib.rs",
    "content": "use alvr_common::{DeviceMotion, LogEntry, LogSeverity, Pose, info};\nuse alvr_packets::{ButtonValue, FaceData};\nuse alvr_session::SessionConfig;\nuse serde::{Deserialize, Serialize};\nuse std::{path::PathBuf, time::Duration};\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct StatisticsSummary {\n    pub video_packets_total: usize,\n    pub video_packets_per_sec: usize,\n    pub video_mbytes_total: usize,\n    pub video_mbits_per_sec: f32,\n    pub total_latency_ms: f32,\n    pub network_latency_ms: f32,\n    pub encode_latency_ms: f32,\n    pub decode_latency_ms: f32,\n    pub client_fps: u32,\n    pub server_fps: u32,\n    pub battery_hmd: u32,\n    pub hmd_plugged: bool,\n}\n\n// Bitrate statistics minus the empirical output value\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct BitrateDirectives {\n    pub scaled_calculated_throughput_bps: Option<f32>,\n    pub decoder_latency_limiter_bps: Option<f32>,\n    pub network_latency_limiter_bps: Option<f32>,\n    pub encoder_latency_limiter_bps: Option<f32>,\n    pub manual_max_throughput_bps: Option<f32>,\n    pub manual_min_throughput_bps: Option<f32>,\n    pub requested_bitrate_bps: f32,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct GraphStatistics {\n    pub total_pipeline_latency_s: f32,\n    pub game_time_s: f32,\n    pub server_compositor_s: f32,\n    pub encoder_s: f32,\n    pub network_s: f32,\n    pub decoder_s: f32,\n    pub decoder_queue_s: f32,\n    pub client_compositor_s: f32,\n    pub vsync_queue_s: f32,\n    pub client_fps: f32,\n    pub server_fps: f32,\n    pub bitrate_directives: BitrateDirectives,\n    pub throughput_bps: f32,\n    pub bitrate_bps: f32,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct TrackingEvent {\n    pub device_motions: Vec<(String, DeviceMotion)>,\n    pub hand_skeletons: [Option<[Pose; 26]>; 2],\n    pub face: FaceData,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct ButtonEvent {\n    pub path: String,\n    pub value: ButtonValue,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct HapticsEvent {\n    pub path: String,\n    pub duration: Duration,\n    pub frequency: f32,\n    pub amplitude: f32,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug, Default)]\npub struct AdbEvent {\n    pub download_progress: f32,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\n#[serde(tag = \"id\", content = \"data\")]\npub enum EventType {\n    Log(LogEntry),\n    DebugGroup { group: String, message: String },\n    Session(Box<SessionConfig>),\n    StatisticsSummary(StatisticsSummary),\n    GraphStatistics(GraphStatistics),\n    Tracking(Box<TrackingEvent>),\n    Buttons(Vec<ButtonEvent>),\n    Haptics(HapticsEvent),\n    DriversList(Vec<PathBuf>),\n    ServerRequestsSelfRestart,\n    Adb(AdbEvent),\n    NewVersionFound { version: String, message: String },\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct Event {\n    pub timestamp: String,\n    pub event_type: EventType,\n}\n\nimpl Event {\n    pub fn event_type_string(&self) -> String {\n        match &self.event_type {\n            EventType::Log(entry) => match entry.severity {\n                LogSeverity::Error => \"ERROR\".into(),\n                LogSeverity::Warning => \"WARNING\".into(),\n                LogSeverity::Info => \"INFO\".into(),\n                LogSeverity::Debug => \"DEBUG\".into(),\n            },\n            EventType::DebugGroup { group, .. } => group.clone(),\n            EventType::Session(_) => \"SESSION\".to_string(),\n            EventType::StatisticsSummary(_) => \"STATS\".to_string(),\n            EventType::GraphStatistics(_) => \"GRAPH\".to_string(),\n            EventType::Tracking(_) => \"TRACKING\".to_string(),\n            EventType::Buttons(_) => \"BUTTONS\".to_string(),\n            EventType::Haptics(_) => \"HAPTICS\".to_string(),\n            EventType::DriversList(_) => \"DRV LIST\".to_string(),\n            EventType::ServerRequestsSelfRestart => \"RESTART\".to_string(),\n            EventType::Adb(_) => \"ADB\".to_string(),\n            EventType::NewVersionFound { .. } => \"NEW VER\".to_string(),\n        }\n    }\n\n    pub fn message(&self) -> String {\n        match &self.event_type {\n            EventType::Log(log_entry) => log_entry.content.clone(),\n            EventType::DebugGroup { message, .. } => message.clone(),\n            EventType::Session(_) => \"Updated\".into(),\n            EventType::StatisticsSummary(_) | EventType::GraphStatistics(_) => \"\".into(),\n            EventType::Tracking(tracking) => serde_json::to_string(tracking).unwrap(),\n            EventType::Buttons(buttons) => serde_json::to_string(buttons).unwrap(),\n            EventType::Haptics(haptics) => serde_json::to_string(haptics).unwrap(),\n            EventType::DriversList(drivers) => serde_json::to_string(drivers).unwrap(),\n            EventType::ServerRequestsSelfRestart => \"Request for server restart\".into(),\n            EventType::Adb(adb) => serde_json::to_string(adb).unwrap(),\n            EventType::NewVersionFound { version, .. } => version.clone(),\n        }\n    }\n}\n\npub fn send_event(event_type: EventType) {\n    info!(\"{}\", serde_json::to_string(&event_type).unwrap());\n}\n"
  },
  {
    "path": "alvr/filesystem/Cargo.toml",
    "content": "[package]\nname = \"alvr_filesystem\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\ndirs = \"6\"\n"
  },
  {
    "path": "alvr/filesystem/build.rs",
    "content": "// This is needed so that `OUT_DIR` is set and `afs::target_dir` works\nfn main() {}\n"
  },
  {
    "path": "alvr/filesystem/src/lib.rs",
    "content": "use std::{\n    env::{\n        self,\n        consts::{DLL_EXTENSION, DLL_PREFIX, DLL_SUFFIX, EXE_SUFFIX, OS},\n    },\n    path::{Path, PathBuf},\n};\n\npub fn exec_fname(name: &str) -> String {\n    format!(\"{name}{EXE_SUFFIX}\")\n}\n\npub fn dynlib_fname(name: &str) -> String {\n    format!(\"{DLL_PREFIX}{name}{DLL_SUFFIX}\")\n}\n\npub fn target_dir() -> PathBuf {\n    // use `.parent().unwrap()` instead of `../` to maintain canonicalized form\n    Path::new(env!(\"OUT_DIR\"))\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_owned()\n}\n\npub fn workspace_dir() -> PathBuf {\n    Path::new(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .to_owned()\n}\n\npub fn crate_dir(name: &str) -> PathBuf {\n    Path::new(env!(\"CARGO_MANIFEST_DIR\"))\n        .parent()\n        .unwrap()\n        .join(name)\n}\n\npub fn deps_dir() -> PathBuf {\n    workspace_dir().join(\"deps\")\n}\n\npub fn build_dir() -> PathBuf {\n    workspace_dir().join(\"build\")\n}\n\npub fn streamer_build_dir() -> PathBuf {\n    build_dir().join(format!(\"alvr_streamer_{OS}\"))\n}\n\npub fn launcher_fname() -> String {\n    exec_fname(\"ALVR Launcher\")\n}\n\npub fn launcher_build_dir() -> PathBuf {\n    build_dir().join(format!(\"alvr_launcher_{OS}\"))\n}\n\npub fn launcher_build_exe_path() -> PathBuf {\n    launcher_build_dir().join(launcher_fname())\n}\n\npub fn installer_path() -> PathBuf {\n    env::temp_dir().join(exec_fname(\"alvr_installer\"))\n}\n\npub fn dashboard_fname() -> &'static str {\n    if cfg!(windows) {\n        \"ALVR Dashboard.exe\"\n    } else {\n        \"alvr_dashboard\"\n    }\n}\n\n// Layout of the ALVR installation. All paths are absolute\n#[derive(Clone, Default, Debug)]\npub struct Layout {\n    // directory containing the dashboard executable\n    pub executables_dir: PathBuf,\n    // (linux only) directory where libalvr_vulkan_layer.so is saved\n    pub libraries_dir: PathBuf,\n    // parent directory of resources like the dashboard and presets folders\n    pub static_resources_dir: PathBuf,\n    // directory for storing configuration files (session.json)\n    pub config_dir: PathBuf,\n    // directory for storing log\n    pub log_dir: PathBuf,\n    // directory to register in openVR driver path\n    pub openvr_driver_root_dir: PathBuf,\n    // (linux only) parent directory of the executable to wrap vrcompositor\n    pub vrcompositor_wrapper_dir: PathBuf,\n    // (linux only) parent directory of the firewall script\n    pub firewall_script_dir: PathBuf,\n    // (linux only) parent directory of the firewalld config\n    pub firewalld_config_dir: PathBuf,\n    // (linux only) parent directory of the ufw config\n    pub ufw_config_dir: PathBuf,\n    // (linux only) directory where the vulkan layer manifest is saved\n    pub vulkan_layer_manifest_dir: PathBuf,\n    pub launcher_root: Option<PathBuf>,\n}\n\nimpl Layout {\n    pub fn new(root: &Path) -> Self {\n        #[cfg(target_os = \"linux\")]\n        {\n            let or_path =\n                |opt: Option<&'static str>, path| opt.map_or(root.join(path), PathBuf::from);\n\n            // Get paths from environment or use FHS compliant paths\n            let executables_dir = or_path(option_env!(\"ALVR_EXECUTABLES_DIR\"), \"bin\");\n            let libraries_dir = or_path(option_env!(\"ALVR_LIBRARIES_DIR\"), \"lib64\");\n            let static_resources_dir =\n                or_path(option_env!(\"ALVR_STATIC_RESOURCES_DIR\"), \"share/alvr\");\n            let openvr_driver_root_dir =\n                or_path(option_env!(\"ALVR_OPENVR_DRIVER_ROOT_DIR\"), \"lib64/alvr\");\n            let vrcompositor_wrapper_dir =\n                or_path(option_env!(\"ALVR_VRCOMPOSITOR_WRAPPER_DIR\"), \"libexec/alvr\");\n            let firewall_script_dir = or_path(option_env!(\"FIREWALL_SCRIPT_DIR\"), \"libexec/alvr\");\n            let firewalld_config_dir = or_path(option_env!(\"FIREWALLD_CONFIG_DIR\"), \"libexec/alvr\");\n            let ufw_config_dir = or_path(option_env!(\"UFW_CONFIG_DIR\"), \"libexec/alvr\");\n            let vulkan_layer_manifest_dir = or_path(\n                option_env!(\"ALVR_VULKAN_LAYER_MANIFEST_DIR\"),\n                \"share/vulkan/explicit_layer.d\",\n            );\n\n            let config_dir = option_env!(\"ALVR_CONFIG_DIR\")\n                .map_or_else(|| dirs::config_dir().unwrap().join(\"alvr\"), PathBuf::from);\n            let log_dir = option_env!(\"ALVR_LOG_DIR\")\n                .map_or_else(|| dirs::home_dir().unwrap(), PathBuf::from);\n\n            Self {\n                executables_dir,\n                libraries_dir,\n                static_resources_dir,\n                config_dir,\n                log_dir,\n                openvr_driver_root_dir,\n                vrcompositor_wrapper_dir,\n                firewall_script_dir,\n                firewalld_config_dir,\n                ufw_config_dir,\n                vulkan_layer_manifest_dir,\n                launcher_root: root\n                    .parent()\n                    .and_then(|p| p.parent())\n                    .and_then(|p| p.parent())\n                    .map(|p| p.to_owned()),\n            }\n        }\n        #[cfg(not(target_os = \"linux\"))]\n        Self {\n            executables_dir: root.to_owned(),\n            libraries_dir: root.to_owned(),\n            static_resources_dir: root.to_owned(),\n            config_dir: root.to_owned(),\n            log_dir: root.to_owned(),\n            openvr_driver_root_dir: root.to_owned(),\n            vrcompositor_wrapper_dir: root.to_owned(),\n            firewall_script_dir: root.to_owned(),\n            firewalld_config_dir: root.to_owned(),\n            ufw_config_dir: root.to_owned(),\n            vulkan_layer_manifest_dir: root.to_owned(),\n            launcher_root: root.parent().and_then(|p| p.parent()).map(|p| p.to_owned()),\n        }\n    }\n\n    pub fn dashboard_exe(&self) -> PathBuf {\n        self.executables_dir.join(dashboard_fname())\n    }\n\n    pub fn local_adb_exe(&self) -> PathBuf {\n        self.executables_dir\n            .join(\"platform-tools\")\n            .join(exec_fname(\"adb\"))\n    }\n\n    pub fn resources_dir(&self) -> PathBuf {\n        self.openvr_driver_root_dir.join(\"resources\")\n    }\n\n    pub fn dashboard_dir(&self) -> PathBuf {\n        self.static_resources_dir.join(\"dashboard\")\n    }\n\n    pub fn presets_dir(&self) -> PathBuf {\n        self.static_resources_dir.join(\"presets\")\n    }\n\n    pub fn session(&self) -> PathBuf {\n        self.config_dir.join(\"session.json\")\n    }\n\n    pub fn session_log(&self) -> PathBuf {\n        if cfg!(target_os = \"linux\") {\n            self.log_dir.join(\"alvr_session_log.txt\")\n        } else {\n            self.log_dir.join(\"session_log.txt\")\n        }\n    }\n\n    pub fn server_start_script(&self) -> PathBuf {\n        self.config_dir.join(if cfg!(windows) {\n            \"start_server.bat\"\n        } else {\n            \"start_server.sh\"\n        })\n    }\n\n    pub fn connect_script(&self) -> PathBuf {\n        self.config_dir.join(if cfg!(windows) {\n            \"on_connect.bat\"\n        } else {\n            \"on_connect.sh\"\n        })\n    }\n\n    pub fn disconnect_script(&self) -> PathBuf {\n        self.config_dir.join(if cfg!(windows) {\n            \"on_disconnect.bat\"\n        } else {\n            \"on_disconnect.sh\"\n        })\n    }\n\n    pub fn crash_log(&self) -> PathBuf {\n        self.log_dir.join(\"crash_log.txt\")\n    }\n\n    pub fn openvr_driver_lib_dir(&self) -> PathBuf {\n        let platform = if cfg!(windows) {\n            \"win64\"\n        } else if cfg!(target_os = \"linux\") {\n            \"linux64\"\n        } else if cfg!(target_os = \"macos\") {\n            \"macos\"\n        } else {\n            unimplemented!()\n        };\n\n        self.openvr_driver_root_dir.join(\"bin\").join(platform)\n    }\n\n    // path to the shared library to be loaded by openVR\n    pub fn openvr_driver_lib(&self) -> PathBuf {\n        self.openvr_driver_lib_dir()\n            .join(format!(\"driver_alvr_server.{DLL_EXTENSION}\"))\n    }\n\n    // path to the manifest file for openVR\n    pub fn openvr_driver_manifest(&self) -> PathBuf {\n        self.openvr_driver_root_dir.join(\"driver.vrdrivermanifest\")\n    }\n\n    pub fn vrcompositor_wrapper(&self) -> PathBuf {\n        self.vrcompositor_wrapper_dir.join(\"vrcompositor-wrapper\")\n    }\n\n    pub fn drm_lease_shim(&self) -> PathBuf {\n        self.vrcompositor_wrapper_dir.join(\"alvr_drm_lease_shim.so\")\n    }\n\n    pub fn vulkan_layer(&self) -> PathBuf {\n        self.libraries_dir.join(dynlib_fname(\"alvr_vulkan_layer\"))\n    }\n\n    pub fn firewall_script(&self) -> PathBuf {\n        self.firewall_script_dir.join(\"alvr_fw_config.sh\")\n    }\n\n    pub fn firewalld_config(&self) -> PathBuf {\n        self.firewalld_config_dir.join(\"alvr-firewalld.xml\")\n    }\n\n    pub fn ufw_config(&self) -> PathBuf {\n        self.ufw_config_dir.join(\"ufw-alvr\")\n    }\n\n    pub fn vulkan_layer_manifest(&self) -> PathBuf {\n        self.vulkan_layer_manifest_dir.join(\"alvr_x86_64.json\")\n    }\n\n    pub fn launcher_exe(&self) -> Option<PathBuf> {\n        self.launcher_root\n            .as_ref()\n            .map(|root| root.join(launcher_fname()))\n    }\n}\n\nfn layout_from_env() -> Option<Layout> {\n    option_env!(\"ALVR_ROOT_DIR\").map(|path| Layout::new(Path::new(path)))\n}\n\n// The path should include the executable file name\n// The path argument is used only if ALVR is built as portable\npub fn filesystem_layout_from_dashboard_exe(path: &Path) -> Option<Layout> {\n    layout_from_env().or_else(|| {\n        let root = if cfg!(target_os = \"linux\") {\n            // FHS path is expected\n            path.parent()?.parent()?.to_owned()\n        } else {\n            path.parent()?.to_owned()\n        };\n\n        Some(Layout::new(&root))\n    })\n}\n\n// The dir argument is used only if ALVR is built as portable\npub fn filesystem_layout_from_openvr_driver_root_dir(dir: &Path) -> Option<Layout> {\n    layout_from_env().or_else(|| {\n        let root = if cfg!(target_os = \"linux\") {\n            // FHS path is expected\n            dir.parent()?.parent()?.to_owned()\n        } else {\n            dir.to_owned()\n        };\n\n        Some(Layout::new(&root))\n    })\n}\n\n// Use this when there is no way of determining the current path. The resulting Layout paths will\n// be invalid, except for the ones that disregard the relative path (for example the config dir) and\n// the ones that have been overridden.\npub fn filesystem_layout_invalid() -> Layout {\n    layout_from_env().unwrap_or_else(|| Layout::new(Path::new(\"./\")))\n}\n"
  },
  {
    "path": "alvr/graphics/Cargo.toml",
    "content": "[package]\nname = \"alvr_graphics\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_session.workspace = true\n\nglow = \"0.16\"\nglyph_brush_layout = \"0.2\"\nkhronos-egl = { version = \"6\", features = [\"dynamic\"] }\npollster = \"0.4\"\nwgpu = \"25\"\n"
  },
  {
    "path": "alvr/graphics/resources/lobby_line.wgsl",
    "content": "struct PushConstant {\n    transform: mat4x4f,\n    color: u32,\n}\nvar<push_constant> pc: PushConstant;\n\n@vertex\nfn vertex_main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {\n    return pc.transform * vec4f(0.0, 0.0, -f32(vertex_index), 1.0);\n}\n\n@fragment\nfn fragment_main() -> @location(0) vec4f {\n    return unpack4x8unorm(pc.color);\n}\n"
  },
  {
    "path": "alvr/graphics/resources/lobby_quad.wgsl",
    "content": "struct PushConstant {\n    transform: mat4x4f,\n    object_type: u32,\n    floor_side: f32,\n}\nvar<push_constant> pc: PushConstant;\n\n@group(0) @binding(0) var hud_texture: texture_2d<f32>;\n@group(0) @binding(1) var hud_sampler: sampler;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4f,\n    @location(0) uv: vec2f,\n}\n\n@vertex\nfn vertex_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n\n    result.uv = vec2f(f32(vertex_index & 1), f32(vertex_index >> 1));\n    result.position = pc.transform * vec4f(result.uv.x - 0.5, 0.5 - result.uv.y, 0.0, 1.0);\n\n    return result;\n}\n\n@fragment\nfn fragment_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n    if pc.object_type == 0 { // Ground\n        let world_xz = (uv - 0.5) * pc.floor_side;\n\n        let ground_center = vec3f(0.0, 0.0, 0.0);\n        let ground_horizon = vec3f(0.0, 0.0, 0.015);\n\n        let grid_close = vec3f(0.114, 0.545, 0.804);\n        let grid_far = vec3f(0.259, 0.863, 0.886);\n\n        let line_fade_start = 10.0;\n        let line_fade_end = 50.0;\n        let line_fade_dist = line_fade_end - line_fade_start;\n\n        let line_bloom = 10.0;\n\n        let distance = length(world_xz);\n\n        // Pick a coordinate to visualize in a grid\n        let cell_size = 2.0;\n        let coord = world_xz / cell_size;\n\n        // Compute anti-aliased world-space grid lines\n        let screen_space_line_width = 1.0 * fwidth(coord); // todo: make resolution agnostic?\n        let grid = abs(fract(coord - 0.5) - 0.5) / screen_space_line_width;\n\n        // Create mask for grid lines and fade over distance\n        var line = clamp(1.0 - min(grid.x, grid.y), 0.0, 1.0);\n        line *= clamp((line_fade_start - distance) / line_fade_dist, 0.0, 1.0);\n    \n        // Fill in normal ground colour\n        var out_color = ground_center * (1.0 - line);\n\n        // Add cheap and simple \"bloom\" to the grid lines\n        line *= 1.0 + line_bloom;\n\n        // Fill in grid line colour\n        out_color += line * mix(grid_far, grid_close, clamp((line_fade_end - distance) / line_fade_end, 0.0, 1.0));\n\n        // Fade to the horizon colour over distance\n        if distance > 10.0 {\n            let coef = 1.0 - 10.0 / distance;\n            out_color = (1.0 - coef) * out_color + coef * ground_horizon;\n        }\n\n        return vec4f(out_color, 1.0);\n    } else { // HUD\n        return textureSample(hud_texture, hud_sampler, uv);\n    }\n}\n"
  },
  {
    "path": "alvr/graphics/resources/staging_fragment.glsl",
    "content": "#version 300 es\n#extension GL_OES_EGL_image_external_essl3 : enable\n\nprecision mediump float;\n\nuniform samplerExternalOES tex;\n\n// Convert from limited colors to full\nconst float LIMITED_MIN = 16.0 / 255.0;\nconst float LIMITED_MAX = 235.0 / 255.0;\n\nin vec2 uv;\nout vec4 out_color;\n\nvoid main() {\n    vec3 color = texture(tex, uv).rgb;\n#ifdef FIX_LIMITED_RANGE\n    color = LIMITED_MIN + ((LIMITED_MAX - LIMITED_MIN) * color);\n#endif\n    out_color = vec4(color, 1.0);\n}\n"
  },
  {
    "path": "alvr/graphics/resources/staging_vertex.glsl",
    "content": "#version 300 es\n\nuniform int view_idx;\n\nout vec2 uv;\n\nvoid main() {\n    vec2 screen_uv = vec2(gl_VertexID & 1, gl_VertexID >> 1);\n    gl_Position = vec4((screen_uv - 0.5f) * 2.f, 0, 1);\n    uv = vec2((screen_uv.x + float(view_idx)) / 2.f, screen_uv.y);\n}\n"
  },
  {
    "path": "alvr/graphics/resources/stream.wgsl",
    "content": "const DIV12: f32 = 1.0 / 12.92;\nconst DIV1: f32 = 1.0 / 1.055;\nconst THRESHOLD: f32 = 0.04045;\nconst GAMMA: vec3f = vec3f(2.4);\n\noverride ENABLE_SRGB_CORRECTION: bool;\noverride ENCODING_GAMMA: f32;\n\noverride ENABLE_UPSCALING: bool = false;\noverride UPSCALE_USE_EDGE_DIRECTION: bool = true;\noverride UPSCALE_EDGE_THRESHOLD: f32 = 4.0/255.0;\noverride UPSCALE_EDGE_SHARPNESS: f32 = 2.0;\n\noverride ENABLE_FFE: bool = false;\n\noverride VIEW_WIDTH_RATIO: f32 = 0.0;\noverride VIEW_HEIGHT_RATIO: f32 = 0.0;\noverride EDGE_X_RATIO: f32 = 0.0;\noverride EDGE_Y_RATIO: f32 = 0.0;\n\noverride C1_X: f32 = 0.0;\noverride C1_Y: f32 = 0.0;\noverride C2_X: f32 = 0.0;\noverride C2_Y: f32 = 0.0;\noverride LO_BOUND_X: f32 = 0.0;\noverride LO_BOUND_Y: f32 = 0.0;\noverride HI_BOUND_X: f32 = 0.0;\noverride HI_BOUND_Y: f32 = 0.0;\n\noverride A_LEFT_X: f32 = 0.0;\noverride A_LEFT_Y: f32 = 0.0;\noverride B_LEFT_X: f32 = 0.0;\noverride B_LEFT_Y: f32 = 0.0;\n\noverride A_RIGHT_X: f32 = 0.0;\noverride A_RIGHT_Y: f32 = 0.0;\noverride B_RIGHT_X: f32 = 0.0;\noverride B_RIGHT_Y: f32 = 0.0;\noverride C_RIGHT_X: f32 = 0.0;\noverride C_RIGHT_Y: f32 = 0.0;\n\nstruct PushConstant {\n    reprojection_transform: mat4x4f,\n    view_idx: u32,\n    passthrough_mode: u32, // 0: Blend, 1: RGB chroma key, 2: HSV chroma key\n    blend_alpha: f32,\n    _align: u32,\n    ck_channel0: vec4f,\n    ck_channel1: vec4f,\n    ck_channel2: vec4f,\n}\nvar<push_constant> pc: PushConstant;\n\n@group(0) @binding(0) var stream_texture: texture_2d<f32>;\n@group(0) @binding(1) var stream_sampler: sampler;\n\nstruct VertexOutput {\n    @builtin(position) position: vec4f,\n    @location(0) uv: vec2f,\n}\n\n@vertex\nfn vertex_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {\n    var result: VertexOutput;\n\n    result.uv = vec2f(f32(vertex_index & 1), f32(vertex_index >> 1));\n    result.position = pc.reprojection_transform * vec4f(result.uv.x - 0.5, 0.5 - result.uv.y, 0.0, 1.0);\n\n    return result;\n}\n\n@fragment\nfn fragment_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n    var corrected_uv = uv;\n    // tell upscaler to target a lower resolution for the edges\n    var upscale_source_resolution = 1.0;\n    if ENABLE_FFE {\n        let view_size_ratio = vec2f(VIEW_WIDTH_RATIO, VIEW_HEIGHT_RATIO);\n        let edge_ratio = vec2f(EDGE_X_RATIO, EDGE_Y_RATIO);\n\n        let c1 = vec2f(C1_X, C1_Y);\n        let c2 = vec2f(C2_X, C2_Y);\n        let lo_bound = vec2f(LO_BOUND_X, LO_BOUND_Y);\n        let hi_bound = vec2f(HI_BOUND_X, HI_BOUND_Y);\n\n        let a_left = vec2f(A_LEFT_X, A_LEFT_Y);\n        let b_left = vec2f(B_LEFT_X, B_LEFT_Y);\n\n        let a_right = vec2f(A_RIGHT_X, A_RIGHT_Y);\n        let b_right = vec2f(B_RIGHT_X, B_RIGHT_Y);\n        let c_right = vec2f(C_RIGHT_X, C_RIGHT_Y);\n\n        if pc.view_idx == 1 {\n            corrected_uv.x = 1.0 - corrected_uv.x;\n        }\n\n        let center = (corrected_uv - c1) * edge_ratio / c2;\n        let left_edge = (-b_left + sqrt(b_left * b_left + 4.0 * a_left * corrected_uv)) / (2.0 * a_left);\n        let right_edge = (-b_right + sqrt(b_right * b_right - 4.0 * (c_right - a_right * corrected_uv))) / (2.0 * a_right);\n\n        if corrected_uv.x < lo_bound.x {\n            corrected_uv.x = left_edge.x;\n            upscale_source_resolution = upscale_source_resolution * edge_ratio.x;\n        } else if corrected_uv.x > hi_bound.x {\n            corrected_uv.x = right_edge.x;\n            upscale_source_resolution = upscale_source_resolution * edge_ratio.x;\n        } else {\n            corrected_uv.x = center.x;\n        }\n\n        if corrected_uv.y < lo_bound.y {\n            corrected_uv.y = left_edge.y;\n            upscale_source_resolution = upscale_source_resolution * edge_ratio.y;\n        } else if corrected_uv.y > hi_bound.y {\n            corrected_uv.y = right_edge.y;\n            upscale_source_resolution = upscale_source_resolution * edge_ratio.y;\n        } else {\n            corrected_uv.y = center.y;\n        }\n\n        corrected_uv = corrected_uv * view_size_ratio;\n\n        if pc.view_idx == 1 {\n            corrected_uv.x = 1.0 - corrected_uv.x;\n        }\n    }\n\n    var color: vec3f;\n    if ENABLE_UPSCALING {\n        color = sgsr(vec4f(corrected_uv.x, corrected_uv.y, 0.0, 0.0), upscale_source_resolution).xyz;\n    } else {\n        color = textureSample(stream_texture, stream_sampler, corrected_uv).rgb;\n    }\n\n    if ENABLE_SRGB_CORRECTION {\n        let condition = vec3f(f32(color.r < THRESHOLD), f32(color.g < THRESHOLD), f32(color.b < THRESHOLD));\n        let lowValues = color * DIV12;\n        let highValues = pow((color + vec3f(0.055)) * DIV1, GAMMA);\n        color = condition * lowValues + (1.0 - condition) * highValues;\n    }\n\n    if ENCODING_GAMMA != 0.0 {\n        let enc_condition = vec3f(f32(color.r < 0.0), f32(color.g < 0.0), f32(color.b < 0.0));\n        let enc_lowValues = color;\n        let enc_highValues = pow(color, vec3f(ENCODING_GAMMA));\n        color = enc_condition * enc_lowValues + (1.0 - enc_condition) * enc_highValues;\n    }\n\n    var alpha = pc.blend_alpha; // Default to Blend passthrough mode\n    if pc.passthrough_mode != 0 { // Chroma key\n        var current = color;\n        if pc.passthrough_mode == 2 { // HSV mode\n            current = rgb_to_hsv(color);\n        }\n        let mask = chroma_key_mask(current);\n\n        // Note: because of this calculation, we require premultiplied alpha option in the XR layer\n        color = max(color * mask, vec3f(0.0));\n        alpha = mask;\n    }\n\n    return vec4f(color, alpha);\n}\n\nfn chroma_key_mask(color: vec3f) -> f32 {\n    let start_max = vec3f(pc.ck_channel0.x, pc.ck_channel1.x, pc.ck_channel2.x);\n    let start_min = vec3f(pc.ck_channel0.y, pc.ck_channel1.y, pc.ck_channel2.y);\n    let end_min = vec3f(pc.ck_channel0.z, pc.ck_channel1.z, pc.ck_channel2.z);\n    let end_max = vec3f(pc.ck_channel0.w, pc.ck_channel1.w, pc.ck_channel2.w);\n\n    let start_mask = smoothstep(start_min.yz, start_max.yz, color.yz);\n    let end_mask = smoothstep(end_min.yz, end_max.yz, color.yz);\n    let sv_mask = max(start_mask, end_mask);\n    // create t1 < t2 < t3, based on the rotated hue starting with start_max.x representing 0.\n    let t3 = end_max.x - start_max.x;\n    // check hue range\n    if t3 > 1.0 {\n        return max(sv_mask.x, sv_mask.y);\n    }\n    let x = fract(color.x - start_max.x);\n    var t1 = fract(start_min.x - start_max.x);\n    var t2 = fract(end_min.x - start_max.x);\n\n    return max(max(smoothstep(t1, 0.0, x), smoothstep(t2, t3, x)), max(sv_mask.x, sv_mask.y));\n}\n\nfn rgb_to_hsv(rgb: vec3f) -> vec3f {\n    let cmax = max(rgb.r, max(rgb.g, rgb.b));\n    let cmin = min(rgb.r, min(rgb.g, rgb.b));\n    let delta = cmax - cmin;\n\n    var h = 0.0;\n    var s = 0.0;\n    let v = cmax;\n\n    if cmax > cmin {\n        s = delta / cmax;\n\n        if rgb.r == cmax {\n            h = (rgb.g - rgb.b) / delta;\n        } else if rgb.g == cmax {\n            h = 2.0 + (rgb.b - rgb.r) / delta;\n        } else {\n            h = 4.0 + (rgb.r - rgb.g) / delta;\n        }\n        h = fract(h / 6.0);\n    }\n\n    return vec3f(h, s, v);\n}\n\n//============================================================================================================\n//\n//\n//                  Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.\n//                              SPDX-License-Identifier: BSD-3-Clause\n//\n//============================================================================================================\n\nfn fastLanczos2(x: f32) -> f32\n{\n    var wA: f32 = x - 4.0;\n    let wB: f32 = x * wA - wA;\n    wA *= wA;\n    return wB * wA;\n}\n\nfn weightY(dx: f32, dy: f32, c: f32, data: vec3f) -> vec2f {\n    let stdA: f32 = data.x;\n    let dir: vec2f = data.yz;\n    let edgeDis: f32 = ((dx * dir.y) + (dy * dir.x));\n    let x: f32 = (((dx * dx) + (dy * dy)) + ((edgeDis * edgeDis) * ((clamp(((c * c) * stdA), 0.0, 1.0) * 0.7) + -1.0)));\n    let w: f32 = fastLanczos2(x);\n    return vec2f(w, w * c);\n}\n\nfn weightYned(dx: f32, dy: f32, c: f32, data: f32) -> vec2f {\n    let stdA: f32 = data;\n    let x: f32 = ((dx * dx) + (dy * dy)) * 0.55 + clamp(abs(c) * stdA, 0.0, 1.0);\n    let w: f32 = fastLanczos2(x);\n    return vec2f(w, w * c);\n}\n\nfn edgeDirection(left: vec4f, right: vec4f) -> vec2f\n{\n    var dir: vec2f;\n    let RxLz: f32 = (right.x + (-left.z));\n    let RwLy: f32 = (right.w + (-left.y));\n    var delta: vec2f;\n    delta.x = (RxLz + RwLy);\n    delta.y = (RxLz + (-RwLy));\n    let lengthInv: f32 = inverseSqrt((delta.x * delta.x + 3.075740e-05) + (delta.y * delta.y));\n    dir.x = (delta.x * lengthInv);\n    dir.y = (delta.y * lengthInv);\n    return dir;\n}\n\nfn sgsr(in_TEXCOORD0: vec4f, source_resolution_multiplier: f32) -> vec4f {\n    // https://github.com/SnapdragonStudios/snapdragon-gsr/issues/2\n    let dim = vec2f(textureDimensions(stream_texture)) * source_resolution_multiplier;\n    let viewport_info = vec4f(1/dim.x, 1/dim.y, dim.x, dim.y);\n\n    var color: vec4f;\n    let texSample = textureSampleLevel(stream_texture, stream_sampler, in_TEXCOORD0.xy, 0.0);\n    color.x = texSample.x;\n    color.y = texSample.y;\n    color.z = texSample.z;\n\n    // all of these 1 values are the OperationMode\n    // see https://github.com/SnapdragonStudios/snapdragon-gsr/tree/main/sgsr/v1#operation-mode\n    let imgCoord: vec2f = (in_TEXCOORD0.xy * viewport_info.zw) + vec2f(-0.5, 0.5);\n    let imgCoordPixel: vec2f = floor(imgCoord);\n    var coord: vec2f = (imgCoordPixel * viewport_info.xy);\n    let pl: vec2f = (imgCoord + (-imgCoordPixel));\n    var left: vec4f = textureGather(1, stream_texture, stream_sampler, coord);\n\n    let edgeVote: f32 = abs(left.z - left.y) + abs(color[1] - left.y) + abs(color[1] - left.z);\n    if edgeVote > UPSCALE_EDGE_THRESHOLD {\n        coord.x += viewport_info.x;\n\n        var right: vec4f = textureGather(1, stream_texture, stream_sampler, coord + vec2f(viewport_info.x, 0.0));\n        var upDown: vec4f;\n        let texGatherA = textureGather(1, stream_texture, stream_sampler, coord + vec2f(0.0, -viewport_info.y));\n        upDown.x = texGatherA.w;\n        upDown.y = texGatherA.z;\n        let texGatherB = textureGather(1, stream_texture, stream_sampler, coord + vec2f(0.0, viewport_info.y));\n        upDown.z = texGatherB.y;\n        upDown.w = texGatherB.x;\n\n        let mean: f32 = (left.y + left.z + right.x + right.w) * 0.25;\n        left = left - vec4(mean);\n        right = right - vec4(mean);\n        upDown = upDown - vec4(mean);\n        color.w = color[1] - mean;\n\n        let sum: f32 = (((((abs(left.x) + abs(left.y)) + abs(left.z)) + abs(left.w)) + (((abs(right.x) + abs(right.y)) + abs(right.z)) + abs(right.w))) + (((abs(upDown.x) + abs(upDown.y)) + abs(upDown.z)) + abs(upDown.w)));\n        let sumMean: f32 = 1.014185e+01 / sum;\n        let stdA: f32 = (sumMean * sumMean);\n\n        var aWY: vec2f;\n        if UPSCALE_USE_EDGE_DIRECTION {\n            let data = vec3f(stdA, edgeDirection(left, right));\n            aWY = weightY(pl.x, pl.y + 1.0, upDown.x, data);\n            aWY += weightY(pl.x - 1.0, pl.y + 1.0, upDown.y, data);\n            aWY += weightY(pl.x - 1.0, pl.y - 2.0, upDown.z, data);\n            aWY += weightY(pl.x, pl.y - 2.0, upDown.w, data);\n            aWY += weightY(pl.x + 1.0, pl.y - 1.0, left.x, data);\n            aWY += weightY(pl.x, pl.y - 1.0, left.y, data);\n            aWY += weightY(pl.x, pl.y, left.z, data);\n            aWY += weightY(pl.x + 1.0, pl.y, left.w, data);\n            aWY += weightY(pl.x - 1.0, pl.y - 1.0, right.x, data);\n            aWY += weightY(pl.x - 2.0, pl.y - 1.0, right.y, data);\n            aWY += weightY(pl.x - 2.0, pl.y, right.z, data);\n            aWY += weightY(pl.x - 1.0, pl.y, right.w, data);\n        } else {\n            let data: f32 = stdA;\n            aWY = weightYned(pl.x, pl.y + 1.0, upDown.x, data);\n            aWY += weightYned(pl.x - 1.0, pl.y + 1.0, upDown.y, data);\n            aWY += weightYned(pl.x - 1.0, pl.y - 2.0, upDown.z, data);\n            aWY += weightYned(pl.x, pl.y - 2.0, upDown.w, data);\n            aWY += weightYned(pl.x + 1.0, pl.y - 1.0, left.x, data);\n            aWY += weightYned(pl.x, pl.y - 1.0, left.y, data);\n            aWY += weightYned(pl.x, pl.y, left.z, data);\n            aWY += weightYned(pl.x + 1.0, pl.y, left.w, data);\n            aWY += weightYned(pl.x - 1.0, pl.y - 1.0, right.x, data);\n            aWY += weightYned(pl.x - 2.0, pl.y - 1.0, right.y, data);\n            aWY += weightYned(pl.x - 2.0, pl.y, right.z, data);\n            aWY += weightYned(pl.x - 1.0, pl.y, right.w, data);\n        }\n\n        let finalY: f32 = aWY.y / aWY.x;\n        let maxY: f32 = max(max(left.y, left.z), max(right.x, right.w));\n        let minY: f32 = min(min(left.y, left.z), min(right.x, right.w));\n        var deltaY: f32 = clamp(UPSCALE_EDGE_SHARPNESS * finalY, minY, maxY) - color.w;\n\n        //smooth high contrast input\n        deltaY = clamp(deltaY, -23.0 / 255.0, 23.0 / 255.0);\n\n        color.x = clamp((color.x + deltaY), 0.0, 1.0);\n        color.y = clamp((color.y + deltaY), 0.0, 1.0);\n        color.z = clamp((color.z + deltaY), 0.0, 1.0);\n    }\n\n    color.w = 1.0; //assume alpha channel is not used\n\n    return color;\n}\n"
  },
  {
    "path": "alvr/graphics/src/lib.rs",
    "content": "mod lobby;\nmod staging;\nmod stream;\n\npub use lobby::*;\npub use stream::*;\n\nuse alvr_common::{\n    DeviceMotion, Fov, Pose,\n    glam::{Mat4, UVec2, Vec4},\n};\nuse glow::{self as gl, HasContext};\nuse khronos_egl as egl;\nuse std::{ffi::c_void, ptr};\nuse wgpu::{\n    Device, Extent3d, Instance, Queue, Texture, TextureDescriptor, TextureDimension, TextureFormat,\n    TextureUsages, TextureView,\n};\n\npub const SDR_FORMAT: TextureFormat = TextureFormat::Rgba8Unorm;\npub const SDR_FORMAT_GL: u32 = gl::RGBA8;\npub const GL_TEXTURE_EXTERNAL_OES: u32 = 0x8D65;\npub const MAX_PUSH_CONSTANTS_SIZE: u32 = 128;\n\ntype CreateImageFn = unsafe extern \"C\" fn(\n    egl::EGLDisplay,\n    egl::EGLContext,\n    egl::Enum,\n    egl::EGLClientBuffer,\n    *const egl::Int,\n) -> egl::EGLImage;\ntype DestroyImageFn = unsafe extern \"C\" fn(egl::EGLDisplay, egl::EGLImage) -> egl::Boolean;\ntype GetNativeClientBufferFn = unsafe extern \"C\" fn(*const c_void) -> egl::EGLClientBuffer;\ntype ImageTargetTexture2DFn = unsafe extern \"C\" fn(egl::Enum, egl::EGLImage);\n\npub struct HandData {\n    pub grip_motion: Option<DeviceMotion>,\n    pub detached_grip_motion: Option<DeviceMotion>,\n    pub skeleton_joints: Option<[Pose; 26]>,\n}\n\npub fn check_error(gl: &gl::Context, message_context: &str) {\n    let err = unsafe { gl.get_error() };\n    if err != glow::NO_ERROR {\n        alvr_common::error!(\"gl error {message_context} -> {err}\");\n        std::process::abort();\n    }\n}\n\nmacro_rules! ck {\n    ($gl_ctx:ident.$($gl_cmd:tt)*) => {{\n        let res = $gl_ctx.$($gl_cmd)*;\n\n        #[cfg(debug_assertions)]\n        crate::check_error(&$gl_ctx, &format!(\"{}:{}: {}\", file!(), line!(), stringify!($($gl_cmd)*)));\n\n        res\n    }};\n}\npub(crate) use ck;\n\nfn projection_from_fov(fov: Fov) -> Mat4 {\n    const NEAR: f32 = 0.1;\n\n    let tanl = f32::tan(fov.left);\n    let tanr = f32::tan(fov.right);\n    let tanu = f32::tan(fov.up);\n    let tand = f32::tan(fov.down);\n    let a = 2.0 / (tanr - tanl);\n    let b = 2.0 / (tanu - tand);\n    let c = (tanr + tanl) / (tanr - tanl);\n    let d = (tanu + tand) / (tanu - tand);\n\n    // note: for wgpu compatibility, the b and d components should be flipped. Maybe a bug in the\n    // viewport handling in wgpu?\n    Mat4::from_cols(\n        Vec4::new(a, 0.0, c, 0.0),\n        Vec4::new(0.0, -b, -d, 0.0),\n        Vec4::new(0.0, 0.0, -1.0, -NEAR),\n        Vec4::new(0.0, 0.0, -1.0, 0.0),\n    )\n    .transpose()\n}\n\npub fn choose_swapchain_format(supported_formats: &[u32], enable_hdr: bool) -> u32 {\n    // Priority-sorted list of swapchain formats we'll accept--\n    let mut app_supported_swapchain_formats = vec![gl::SRGB8_ALPHA8, gl::RGBA8];\n\n    // float16 is required for HDR output. However, float16 swapchains\n    // have a high perf cost, so only use these if HDR is enabled.\n    if enable_hdr {\n        app_supported_swapchain_formats.insert(0, gl::RGBA16F);\n    }\n\n    for format in app_supported_swapchain_formats {\n        if supported_formats.contains(&format) {\n            return format;\n        }\n    }\n\n    // If we can't enumerate, default to a required format\n    gl::RGBA8\n}\n\npub fn gl_format_to_wgpu(format: u32) -> TextureFormat {\n    match format {\n        gl::SRGB8_ALPHA8 => TextureFormat::Rgba8UnormSrgb,\n        gl::RGBA8 => TextureFormat::Rgba8Unorm,\n        gl::RGBA16F => TextureFormat::Rgba16Float,\n        _ => panic!(\"Unsupported GL format: {format}\"),\n    }\n}\n\npub fn create_texture(device: &Device, resolution: UVec2, format: TextureFormat) -> Texture {\n    device.create_texture(&TextureDescriptor {\n        label: None,\n        size: Extent3d {\n            width: resolution.x,\n            height: resolution.y,\n            depth_or_array_layers: 1,\n        },\n        mip_level_count: 1,\n        sample_count: 1,\n        dimension: TextureDimension::D2,\n        format,\n        usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,\n        view_formats: &[],\n    })\n}\n\n#[cfg(not(any(target_os = \"macos\", target_os = \"ios\")))]\nfn create_texture_from_gles(\n    device: &Device,\n    texture: u32,\n    resolution: UVec2,\n    format: TextureFormat,\n) -> Texture {\n    use std::num::NonZeroU32;\n    use wgpu::{\n        TextureUses,\n        hal::{self, MemoryFlags, api},\n    };\n\n    let size = Extent3d {\n        width: resolution.x,\n        height: resolution.y,\n        depth_or_array_layers: 1,\n    };\n\n    unsafe {\n        let hal_texture = device.as_hal::<api::Gles, _, _>(|device| {\n            device.unwrap().texture_from_raw(\n                NonZeroU32::new(texture).unwrap(),\n                &hal::TextureDescriptor {\n                    label: None,\n                    size,\n                    mip_level_count: 1,\n                    sample_count: 1,\n                    dimension: TextureDimension::D2,\n                    format,\n                    usage: TextureUses::COLOR_TARGET,\n                    memory_flags: MemoryFlags::empty(),\n                    view_formats: vec![],\n                },\n                Some(Box::new(|| ())),\n            )\n        });\n\n        device.create_texture_from_hal::<api::Gles>(\n            hal_texture,\n            &TextureDescriptor {\n                label: None,\n                size,\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: TextureDimension::D2,\n                format,\n                usage: TextureUsages::RENDER_ATTACHMENT,\n                view_formats: &[],\n            },\n        )\n    }\n}\n\n#[cfg(any(target_os = \"macos\", target_os = \"ios\"))]\nfn create_texture_from_gles(_: &Device, _: u32, _: UVec2, _: TextureFormat) -> Texture {\n    unimplemented!()\n}\n\n// This is used to convert OpenXR swapchains to wgpu\npub fn create_gl_swapchain(\n    device: &Device,\n    gl_textures: &[u32],\n    resolution: UVec2,\n    format: TextureFormat,\n) -> Vec<TextureView> {\n    gl_textures\n        .iter()\n        .map(|gl_tex| {\n            create_texture_from_gles(device, *gl_tex, resolution, format)\n                .create_view(&Default::default())\n        })\n        .collect()\n}\n\npub struct GraphicsContext {\n    _instance: Instance,\n\n    #[cfg(not(any(windows, target_os = \"macos\", target_os = \"ios\")))]\n    adapter: wgpu::Adapter,\n\n    device: Device,\n    queue: Queue,\n    pub egl_display: egl::Display,\n    pub egl_config: egl::Config,\n    pub egl_context: egl::Context,\n    pub gl_context: gl::Context,\n\n    #[cfg(not(any(windows, target_os = \"macos\", target_os = \"ios\")))]\n    dummy_surface: egl::Surface,\n\n    create_image: CreateImageFn,\n    destroy_image: DestroyImageFn,\n    get_native_client_buffer: GetNativeClientBufferFn,\n    image_target_texture_2d: ImageTargetTexture2DFn,\n}\n\nimpl GraphicsContext {\n    #[cfg(not(any(windows, target_os = \"macos\", target_os = \"ios\")))]\n    pub fn new_gl() -> Self {\n        use std::mem;\n        use wgpu::{\n            Backends, DeviceDescriptor, Features, InstanceDescriptor, InstanceFlags, Limits,\n            MemoryHints, Trace, hal::api,\n        };\n\n        const CREATE_IMAGE_FN_STR: &str = \"eglCreateImageKHR\";\n        const DESTROY_IMAGE_FN_STR: &str = \"eglDestroyImageKHR\";\n        const GET_NATIVE_CLIENT_BUFFER_FN_STR: &str = \"eglGetNativeClientBufferANDROID\";\n        const IMAGE_TARGET_TEXTURE_2D_FN_STR: &str = \"glEGLImageTargetTexture2DOES\";\n\n        let flags = if cfg!(debug_assertions) {\n            InstanceFlags::DEBUG | InstanceFlags::VALIDATION\n        } else {\n            InstanceFlags::empty()\n        };\n\n        let instance = Instance::new(&InstanceDescriptor {\n            backends: Backends::GL,\n            flags,\n            ..Default::default()\n        });\n\n        let adapter = instance.enumerate_adapters(Backends::GL).remove(0);\n        let (device, queue) = pollster::block_on(adapter.request_device(&DeviceDescriptor {\n            label: None,\n            required_features: Features::PUSH_CONSTANTS,\n            required_limits: Limits {\n                max_push_constant_size: MAX_PUSH_CONSTANTS_SIZE,\n                ..adapter.limits()\n            },\n            memory_hints: MemoryHints::Performance,\n            trace: Trace::Off,\n        }))\n        .unwrap();\n\n        let raw_instance = unsafe { instance.as_hal::<api::Gles>() }.unwrap();\n\n        let egl_display = raw_instance.raw_display();\n        let egl_config = raw_instance.egl_config();\n\n        let (\n            egl_context,\n            gl_context,\n            dummy_surface,\n            create_image,\n            destroy_image,\n            get_native_client_buffer,\n            image_target_texture_2d,\n        ) = unsafe {\n            adapter.as_hal::<api::Gles, _, _>(|raw_adapter| {\n                let adapter_context = raw_adapter.unwrap().adapter_context();\n                let egl_instance = adapter_context.egl_instance().unwrap();\n\n                let egl_context = egl::Context::from_ptr(adapter_context.raw_context());\n\n                const PBUFFER_ATTRIBS: [i32; 5] = [egl::WIDTH, 16, egl::HEIGHT, 16, egl::NONE];\n                let dummy_surface = egl_instance\n                    .create_pbuffer_surface(egl_display, egl_config, &PBUFFER_ATTRIBS)\n                    .unwrap();\n\n                egl_instance\n                    .make_current(\n                        egl_display,\n                        Some(dummy_surface),\n                        Some(dummy_surface),\n                        Some(egl_context),\n                    )\n                    .unwrap();\n\n                let gl_context = gl::Context::from_loader_function(|fn_name| {\n                    egl_instance\n                        .get_proc_address(fn_name)\n                        .map_or(ptr::null(), |f| f as *const c_void)\n                });\n\n                let get_fn_ptr = |fn_name| {\n                    egl_instance\n                        .get_proc_address(fn_name)\n                        .map_or(ptr::null(), |f| f as *const c_void)\n                };\n\n                let create_image: CreateImageFn = mem::transmute(get_fn_ptr(CREATE_IMAGE_FN_STR));\n                let destroy_image: DestroyImageFn =\n                    mem::transmute(get_fn_ptr(DESTROY_IMAGE_FN_STR));\n                let get_native_client_buffer: GetNativeClientBufferFn =\n                    mem::transmute(get_fn_ptr(GET_NATIVE_CLIENT_BUFFER_FN_STR));\n                let image_target_texture_2d: ImageTargetTexture2DFn =\n                    mem::transmute(get_fn_ptr(IMAGE_TARGET_TEXTURE_2D_FN_STR));\n\n                (\n                    egl_context,\n                    gl_context,\n                    dummy_surface,\n                    create_image,\n                    destroy_image,\n                    get_native_client_buffer,\n                    image_target_texture_2d,\n                )\n            })\n        };\n\n        Self {\n            _instance: instance,\n            adapter,\n            device,\n            queue,\n            egl_display,\n            egl_config,\n            egl_context,\n            gl_context,\n            dummy_surface,\n            create_image,\n            destroy_image,\n            get_native_client_buffer,\n            image_target_texture_2d,\n        }\n    }\n\n    #[cfg(any(windows, target_os = \"macos\", target_os = \"ios\"))]\n    pub fn new_gl() -> Self {\n        unimplemented!()\n    }\n\n    pub fn make_current(&self) {\n        #[cfg(not(any(windows, target_os = \"macos\", target_os = \"ios\")))]\n        unsafe {\n            self.adapter\n                .as_hal::<wgpu::hal::api::Gles, _, _>(|raw_adapter| {\n                    let egl_instance = raw_adapter\n                        .unwrap()\n                        .adapter_context()\n                        .egl_instance()\n                        .unwrap();\n\n                    egl_instance\n                        .make_current(\n                            self.egl_display,\n                            Some(self.dummy_surface),\n                            Some(self.dummy_surface),\n                            Some(self.egl_context),\n                        )\n                        .unwrap();\n                })\n        };\n    }\n\n    /// # Safety\n    /// `buffer` must be a valid AHardwareBuffer.\n    /// `texture` must be a valid GL texture.\n    pub unsafe fn render_ahardwarebuffer_using_texture(\n        &self,\n        buffer: *const c_void,\n        texture: gl::Texture,\n        render_cb: impl FnOnce(),\n    ) {\n        const EGL_NATIVE_BUFFER_ANDROID: u32 = 0x3140;\n\n        if !buffer.is_null() {\n            let client_buffer = unsafe { (self.get_native_client_buffer)(buffer) };\n            check_error(&self.gl_context, \"get_native_client_buffer\");\n\n            let image = unsafe {\n                (self.create_image)(\n                    self.egl_display.as_ptr(),\n                    egl::NO_CONTEXT,\n                    EGL_NATIVE_BUFFER_ANDROID,\n                    client_buffer,\n                    ptr::null(),\n                )\n            };\n            check_error(&self.gl_context, \"create_image\");\n\n            unsafe {\n                self.gl_context\n                    .bind_texture(GL_TEXTURE_EXTERNAL_OES, Some(texture))\n            };\n            check_error(&self.gl_context, \"bind texture OES\");\n\n            unsafe { (self.image_target_texture_2d)(GL_TEXTURE_EXTERNAL_OES, image) };\n            check_error(&self.gl_context, \"image_target_texture_2d\");\n\n            render_cb();\n\n            unsafe { (self.destroy_image)(self.egl_display.as_ptr(), image) };\n            check_error(&self.gl_context, \"destroy_image\");\n        }\n    }\n}\n\n#[cfg(not(windows))]\nimpl Default for GraphicsContext {\n    fn default() -> Self {\n        Self::new_gl()\n    }\n}\n"
  },
  {
    "path": "alvr/graphics/src/lobby.rs",
    "content": "use super::{GraphicsContext, MAX_PUSH_CONSTANTS_SIZE, SDR_FORMAT};\nuse crate::HandData;\nuse alvr_common::{\n    BodySkeleton, DeviceMotion, ViewParams,\n    glam::{IVec2, Mat4, Quat, UVec2, Vec3},\n};\nuse glyph_brush_layout::{\n    FontId, GlyphPositioner, HorizontalAlign, Layout, SectionGeometry, SectionText, VerticalAlign,\n    ab_glyph::{Font, FontRef, ScaleFont},\n};\nuse std::{f32::consts::FRAC_PI_2, mem, rc::Rc};\nuse wgpu::{\n    BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,\n    BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor,\n    BlendOperation, BlendState, Color, ColorTargetState, ColorWrites, CommandEncoderDescriptor,\n    Device, Extent3d, FilterMode, FragmentState, LoadOp, Operations, Origin3d,\n    PipelineLayoutDescriptor, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPass,\n    RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor,\n    SamplerBindingType, SamplerDescriptor, ShaderModuleDescriptor, ShaderStages, StoreOp,\n    TexelCopyBufferLayout, TexelCopyTextureInfo, Texture, TextureAspect, TextureSampleType,\n    TextureView, TextureViewDimension, VertexState, include_wgsl,\n};\n\nconst TRANSFORM_CONST_SIZE: u32 = mem::size_of::<Mat4>() as u32;\nconst OBJECT_TYPE_CONST_SIZE: u32 = mem::size_of::<u32>() as u32;\nconst FLOOR_SIDE_CONST_SIZE: u32 = mem::size_of::<f32>() as u32;\nconst COLOR_CONST_SIZE: u32 = mem::size_of::<u32>() as u32;\n\nconst QUAD_PUSH_CONTANTS_SIZE: u32 =\n    TRANSFORM_CONST_SIZE + OBJECT_TYPE_CONST_SIZE + FLOOR_SIDE_CONST_SIZE;\nconst LINE_PUSH_CONTANTS_SIZE: u32 = TRANSFORM_CONST_SIZE + COLOR_CONST_SIZE;\nconst _: () = assert!(\n    QUAD_PUSH_CONTANTS_SIZE <= MAX_PUSH_CONSTANTS_SIZE\n        && LINE_PUSH_CONTANTS_SIZE <= MAX_PUSH_CONSTANTS_SIZE,\n    \"Push constants size exceeds the maximum size\"\n);\n\nconst TRANSFORM_CONST_OFFSET: u32 = 0;\nconst OBJECT_TYPE_CONST_OFFSET: u32 = TRANSFORM_CONST_SIZE;\nconst FLOOR_SIDE_CONST_OFFSET: u32 = OBJECT_TYPE_CONST_OFFSET + OBJECT_TYPE_CONST_SIZE;\nconst COLOR_CONST_OFFSET: u32 = TRANSFORM_CONST_SIZE;\n\nconst FLOOR_SIDE: f32 = 300.0;\nconst HUD_DIST: f32 = 5.0;\nconst HUD_SIDE: f32 = 3.5;\nconst HUD_TEXTURE_SIDE: usize = 1024;\nconst FONT_SIZE: f32 = 50.0;\n\nconst FAST_BORDER_OFFSETS: [IVec2; 8] = [\n    IVec2::new(0, -3),\n    IVec2::new(2, -2),\n    IVec2::new(3, 0),\n    IVec2::new(2, 2),\n    IVec2::new(0, 3),\n    IVec2::new(-2, 2),\n    IVec2::new(-3, 0),\n    IVec2::new(-2, -2),\n];\nconst MAX_BORDER_OFFSET: i32 = 3;\n\nconst HAND_SKELETON_BONES: [(usize, usize); 19] = [\n    // Thumb\n    (2, 3),\n    (3, 4),\n    (4, 5),\n    // Index\n    (6, 7),\n    (7, 8),\n    (8, 9),\n    (9, 10),\n    // Middle\n    (11, 12),\n    (12, 13),\n    (13, 14),\n    (14, 15),\n    // Ring\n    (16, 17),\n    (17, 18),\n    (18, 19),\n    (19, 20),\n    // Pinky\n    (21, 22),\n    (22, 23),\n    (23, 24),\n    (24, 25),\n];\n\nconst BODY_JOINT_RELATIONS_FB: [(usize, usize); 30] = [\n    // Spine\n    (1, 2),\n    (2, 3),\n    (3, 4),\n    (4, 5),\n    (5, 6),\n    (6, 7),\n    // Left arm\n    (5, 8),\n    (8, 9),\n    (9, 10),\n    (10, 11),\n    (11, 12),\n    // Right arm\n    (5, 13),\n    (13, 14),\n    (14, 15),\n    (15, 16),\n    (16, 17),\n    // Left leg\n    (1, 18),\n    (18, 19),\n    (19, 20),\n    (20, 21),\n    (21, 22),\n    (22, 23),\n    (23, 24),\n    // Right leg\n    (1, 25),\n    (25, 26),\n    (26, 27),\n    (27, 28),\n    (28, 29),\n    (29, 30),\n    (30, 31),\n];\n\nconst BODY_JOINT_RELATIONS_BD: [(usize, usize); 23] = [\n    // Left leg\n    (0, 1),\n    (1, 4),\n    (4, 7),\n    (7, 10),\n    // Right leg\n    (0, 2),\n    (2, 5),\n    (5, 8),\n    (8, 11),\n    // Spine\n    (0, 3),\n    (3, 6),\n    (6, 9),\n    (9, 12),\n    (12, 15),\n    // Left arm\n    (9, 13),\n    (13, 16),\n    (16, 18),\n    (18, 20),\n    (20, 22),\n    // Right arm\n    (9, 14),\n    (14, 17),\n    (17, 19),\n    (19, 21),\n    (21, 23),\n];\n\nfn create_pipeline(\n    device: &Device,\n    label: &str,\n    bind_group_layouts: &[&BindGroupLayout],\n    push_constants_len: u32,\n    shader: ShaderModuleDescriptor,\n    topology: PrimitiveTopology,\n) -> RenderPipeline {\n    let shader_module = device.create_shader_module(shader);\n    device.create_render_pipeline(&RenderPipelineDescriptor {\n        label: Some(label),\n        // Note: Layout cannot be inferred because of a bug with push constants\n        layout: Some(&device.create_pipeline_layout(&PipelineLayoutDescriptor {\n            label: Some(label),\n            bind_group_layouts,\n            push_constant_ranges: &[PushConstantRange {\n                stages: ShaderStages::VERTEX_FRAGMENT,\n                range: 0..push_constants_len,\n            }],\n        })),\n        vertex: VertexState {\n            module: &shader_module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            buffers: &[],\n        },\n        primitive: PrimitiveState {\n            topology,\n            ..Default::default()\n        },\n        depth_stencil: None,\n        multisample: Default::default(),\n        fragment: Some(FragmentState {\n            module: &shader_module,\n            entry_point: None,\n            compilation_options: Default::default(),\n            targets: &[Some(ColorTargetState {\n                format: SDR_FORMAT,\n                blend: Some(BlendState {\n                    color: BlendComponent {\n                        src_factor: BlendFactor::SrcAlpha,\n                        dst_factor: BlendFactor::OneMinusSrcAlpha,\n                        operation: BlendOperation::Add,\n                    },\n                    alpha: BlendComponent {\n                        src_factor: BlendFactor::One,\n                        dst_factor: BlendFactor::OneMinusSrcAlpha,\n                        operation: BlendOperation::Add,\n                    },\n                }),\n                write_mask: ColorWrites::ALL,\n            })],\n        }),\n        multiview: None,\n        cache: None,\n    })\n}\n\npub struct LobbyViewParams {\n    pub swapchain_index: u32,\n    pub view_params: ViewParams,\n}\n\npub struct LobbyRenderer {\n    context: Rc<GraphicsContext>,\n    quad_pipeline: RenderPipeline,\n    line_pipeline: RenderPipeline,\n    hud_texture: Texture,\n    bind_group: BindGroup,\n    render_targets: [Vec<TextureView>; 2],\n}\n\nimpl LobbyRenderer {\n    pub fn new(\n        context: Rc<GraphicsContext>,\n        view_resolution: UVec2,\n        swapchain_textures: [Vec<u32>; 2],\n        initial_hud_message: &str,\n    ) -> Self {\n        let device = &context.device;\n\n        let hud_texture =\n            super::create_texture(device, UVec2::ONE * HUD_TEXTURE_SIDE as u32, SDR_FORMAT);\n\n        let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::FRAGMENT,\n                    ty: BindingType::Texture {\n                        sample_type: TextureSampleType::Float { filterable: true },\n                        view_dimension: TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: None,\n                },\n                BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: ShaderStages::FRAGMENT,\n                    ty: BindingType::Sampler(SamplerBindingType::Filtering),\n                    count: None,\n                },\n            ],\n        });\n\n        let quad_pipeline = create_pipeline(\n            device,\n            \"lobby_quad\",\n            &[&bind_group_layout],\n            QUAD_PUSH_CONTANTS_SIZE,\n            include_wgsl!(\"../resources/lobby_quad.wgsl\"),\n            PrimitiveTopology::TriangleStrip,\n        );\n\n        let line_pipeline = create_pipeline(\n            device,\n            \"lobby_line\",\n            &[],\n            LINE_PUSH_CONTANTS_SIZE,\n            include_wgsl!(\"../resources/lobby_line.wgsl\"),\n            PrimitiveTopology::LineList,\n        );\n\n        let bind_group = device.create_bind_group(&BindGroupDescriptor {\n            label: None,\n            layout: &bind_group_layout,\n            entries: &[\n                BindGroupEntry {\n                    binding: 0,\n                    resource: BindingResource::TextureView(\n                        &hud_texture.create_view(&Default::default()),\n                    ),\n                },\n                BindGroupEntry {\n                    binding: 1,\n                    resource: BindingResource::Sampler(&device.create_sampler(\n                        &SamplerDescriptor {\n                            mag_filter: FilterMode::Linear,\n                            min_filter: FilterMode::Linear,\n                            ..Default::default()\n                        },\n                    )),\n                },\n            ],\n        });\n\n        let render_targets = [\n            super::create_gl_swapchain(device, &swapchain_textures[0], view_resolution, SDR_FORMAT),\n            super::create_gl_swapchain(device, &swapchain_textures[1], view_resolution, SDR_FORMAT),\n        ];\n\n        let this = Self {\n            context,\n            quad_pipeline,\n            line_pipeline,\n            hud_texture,\n            bind_group,\n            render_targets,\n        };\n\n        this.update_hud_message(initial_hud_message);\n\n        this\n    }\n\n    pub fn update_hud_message(&self, message: &str) {\n        let ubuntu_font =\n            FontRef::try_from_slice(include_bytes!(\"../resources/Ubuntu-Medium.ttf\")).unwrap();\n\n        let section_glyphs = Layout::default()\n            .h_align(HorizontalAlign::Center)\n            .v_align(VerticalAlign::Center)\n            .calculate_glyphs(\n                &[&ubuntu_font],\n                &SectionGeometry {\n                    screen_position: (\n                        HUD_TEXTURE_SIDE as f32 / 2_f32,\n                        HUD_TEXTURE_SIDE as f32 / 2_f32,\n                    ),\n                    ..Default::default()\n                },\n                &[SectionText {\n                    text: message,\n                    scale: FONT_SIZE.into(),\n                    font_id: FontId(0),\n                }],\n            );\n\n        let scaled_font = ubuntu_font.as_scaled(FONT_SIZE);\n\n        let mut buffer = vec![0; HUD_TEXTURE_SIDE * HUD_TEXTURE_SIDE * 4];\n\n        for section_glyph in section_glyphs {\n            if let Some(outlined) = scaled_font.outline_glyph(section_glyph.glyph) {\n                let bounds = outlined.px_bounds();\n\n                outlined.draw(|x, y, alpha| {\n                    let x = x as i32 + bounds.min.x as i32;\n                    let y = y as i32 + bounds.min.y as i32;\n\n                    if x >= MAX_BORDER_OFFSET\n                        && y >= MAX_BORDER_OFFSET\n                        && x < HUD_TEXTURE_SIDE as i32 - MAX_BORDER_OFFSET\n                        && y < HUD_TEXTURE_SIDE as i32 - MAX_BORDER_OFFSET\n                    {\n                        let coord = (y as usize * HUD_TEXTURE_SIDE + x as usize) * 4;\n                        let value = (alpha * 255.0) as u8;\n\n                        buffer[coord] = value;\n                        buffer[coord + 1] = value;\n                        buffer[coord + 2] = value;\n\n                        // Render opacity with border\n                        for offset in &FAST_BORDER_OFFSETS {\n                            let coord = ((y + offset.y) as usize * HUD_TEXTURE_SIDE\n                                + (x + offset.x) as usize)\n                                * 4;\n                            buffer[coord + 3] = u8::max(buffer[coord + 3], value);\n                        }\n                    }\n                });\n            }\n        }\n\n        self.context.queue.write_texture(\n            TexelCopyTextureInfo {\n                texture: &self.hud_texture,\n                mip_level: 0,\n                origin: Origin3d::ZERO,\n                aspect: TextureAspect::All,\n            },\n            &buffer,\n            TexelCopyBufferLayout {\n                offset: 0,\n                bytes_per_row: Some(HUD_TEXTURE_SIDE as u32 * 4),\n                rows_per_image: Some(HUD_TEXTURE_SIDE as u32),\n            },\n            Extent3d {\n                width: HUD_TEXTURE_SIDE as u32,\n                height: HUD_TEXTURE_SIDE as u32,\n                depth_or_array_layers: 1,\n            },\n        );\n    }\n\n    pub fn render(\n        &self,\n        view_params: [LobbyViewParams; 2],\n        hand_data: [HandData; 2],\n        body_skeleton: Option<BodySkeleton>,\n        additional_motions: Option<Vec<DeviceMotion>>,\n        render_background: bool,\n        show_velocities: bool,\n    ) {\n        let mut encoder = self\n            .context\n            .device\n            .create_command_encoder(&CommandEncoderDescriptor {\n                label: Some(\"lobby_command_encoder\"),\n            });\n\n        for (view_idx, view_input) in view_params.iter().enumerate() {\n            let view = Mat4::from_rotation_translation(\n                view_input.view_params.pose.orientation,\n                view_input.view_params.pose.position,\n            )\n            .inverse();\n            let view_proj = super::projection_from_fov(view_input.view_params.fov) * view;\n\n            let clear_color = if render_background {\n                Color {\n                    r: 0.0,\n                    g: 0.0,\n                    b: 0.02,\n                    a: 1.0,\n                }\n            } else {\n                Color::TRANSPARENT\n            };\n\n            let mut pass = encoder.begin_render_pass(&RenderPassDescriptor {\n                label: Some(&format!(\"lobby_view_{view_idx}\")),\n                color_attachments: &[Some(RenderPassColorAttachment {\n                    view: &self.render_targets[view_idx][view_input.swapchain_index as usize],\n                    resolve_target: None,\n                    ops: Operations {\n                        load: LoadOp::Clear(clear_color),\n                        store: StoreOp::Store,\n                    },\n                })],\n                ..Default::default()\n            });\n\n            fn transform_draw(pass: &mut RenderPass, transform: Mat4, vertices_count: u32) {\n                let data = transform\n                    .to_cols_array()\n                    .iter()\n                    .flat_map(|v| v.to_le_bytes())\n                    .collect::<Vec<u8>>();\n                pass.set_push_constants(\n                    ShaderStages::VERTEX_FRAGMENT,\n                    TRANSFORM_CONST_OFFSET,\n                    &data,\n                );\n                pass.draw(0..vertices_count, 0..1);\n            }\n\n            // Draw the following geometry in the correct order (depth buffer is disabled)\n\n            // Bind quad pipeline\n            pass.set_pipeline(&self.quad_pipeline);\n            pass.set_bind_group(0, &self.bind_group, &[]);\n\n            if render_background {\n                // Render ground\n                pass.set_push_constants(\n                    ShaderStages::VERTEX_FRAGMENT,\n                    OBJECT_TYPE_CONST_OFFSET,\n                    &0_u32.to_le_bytes(),\n                );\n                pass.set_push_constants(\n                    ShaderStages::VERTEX_FRAGMENT,\n                    FLOOR_SIDE_CONST_OFFSET,\n                    &FLOOR_SIDE.to_le_bytes(),\n                );\n                let transform = view_proj\n                    * Mat4::from_rotation_x(-FRAC_PI_2)\n                    * Mat4::from_scale(Vec3::ONE * FLOOR_SIDE);\n                transform_draw(&mut pass, transform, 4);\n            }\n\n            // Render HUD\n            pass.set_push_constants(\n                ShaderStages::VERTEX_FRAGMENT,\n                OBJECT_TYPE_CONST_OFFSET,\n                &1_u32.to_le_bytes(),\n            );\n            for i in 0..4 {\n                let transform = Mat4::from_rotation_y(FRAC_PI_2 * i as f32)\n                    * Mat4::from_translation(Vec3::new(0.0, HUD_SIDE / 2.0, -HUD_DIST))\n                    * Mat4::from_scale(Vec3::ONE * HUD_SIDE);\n                transform_draw(&mut pass, view_proj * transform, 4);\n            }\n\n            fn draw_crosshair(\n                pass: &mut RenderPass,\n                motion: &DeviceMotion,\n                view_proj: Mat4,\n                show_velocities: bool,\n                color: &[u8; 4],\n            ) {\n                let hand_transform = Mat4::from_scale_rotation_translation(\n                    Vec3::ONE * 0.2,\n                    motion.pose.orientation,\n                    motion.pose.position,\n                );\n\n                // Draw crosshair\n                let segment_rotations = [\n                    Mat4::IDENTITY,\n                    Mat4::from_rotation_y(FRAC_PI_2),\n                    Mat4::from_rotation_x(FRAC_PI_2),\n                ];\n                pass.set_push_constants(ShaderStages::VERTEX_FRAGMENT, COLOR_CONST_OFFSET, color);\n                for rot in &segment_rotations {\n                    let transform = hand_transform\n                        * *rot\n                        * Mat4::from_scale(Vec3::ONE * 0.5)\n                        * Mat4::from_translation(Vec3::Z * 0.5);\n                    transform_draw(pass, view_proj * transform, 2);\n                }\n\n                if show_velocities {\n                    // Draw linear velocity\n                    let transform = Mat4::from_scale_rotation_translation(\n                        Vec3::ONE * motion.linear_velocity.length() * 0.2,\n                        Quat::from_rotation_arc(-Vec3::Z, motion.linear_velocity.normalize()),\n                        motion.pose.position,\n                    );\n                    pass.set_push_constants(\n                        ShaderStages::VERTEX_FRAGMENT,\n                        COLOR_CONST_OFFSET,\n                        &[255, 0, 0, 255],\n                    );\n                    transform_draw(pass, view_proj * transform, 2);\n\n                    // Draw angular velocity\n                    let transform = Mat4::from_scale_rotation_translation(\n                        Vec3::ONE * motion.angular_velocity.length() * 0.01,\n                        Quat::from_rotation_arc(-Vec3::Z, motion.angular_velocity.normalize()),\n                        motion.pose.position,\n                    );\n                    pass.set_push_constants(\n                        ShaderStages::VERTEX_FRAGMENT,\n                        COLOR_CONST_OFFSET,\n                        &[0, 255, 0, 255],\n                    );\n                    transform_draw(pass, view_proj * transform, 2);\n                }\n            }\n\n            // Render hands and body skeleton\n            pass.set_pipeline(&self.line_pipeline);\n            for data in &hand_data {\n                if let Some(skeleton) = data.skeleton_joints {\n                    pass.set_push_constants(\n                        ShaderStages::VERTEX_FRAGMENT,\n                        COLOR_CONST_OFFSET,\n                        &[255, 255, 255, 255],\n                    );\n\n                    for (joint1_idx, joint2_idx) in HAND_SKELETON_BONES {\n                        let j1_pose = skeleton[joint1_idx];\n                        let j2_pose = skeleton[joint2_idx];\n\n                        let transform = Mat4::from_scale_rotation_translation(\n                            Vec3::ONE * Vec3::distance(j1_pose.position, j2_pose.position),\n                            j1_pose.orientation,\n                            j1_pose.position,\n                        );\n                        transform_draw(&mut pass, view_proj * transform, 2);\n                    }\n                }\n\n                if let Some(motion) = data.grip_motion {\n                    draw_crosshair(\n                        &mut pass,\n                        &motion,\n                        view_proj,\n                        show_velocities,\n                        &[255, 255, 255, 255],\n                    );\n                }\n\n                if let Some(motion) = data.detached_grip_motion {\n                    draw_crosshair(\n                        &mut pass,\n                        &motion,\n                        view_proj,\n                        show_velocities,\n                        &[50, 50, 50, 255],\n                    );\n                }\n            }\n\n            if let Some(motions) = &additional_motions {\n                for motion in motions {\n                    draw_crosshair(\n                        &mut pass,\n                        motion,\n                        view_proj,\n                        show_velocities,\n                        &[255, 255, 255, 255],\n                    );\n                }\n            }\n\n            if let Some(skeleton) = &body_skeleton {\n                let (joints, relations) = match skeleton {\n                    BodySkeleton::Fb(skeleton) => {\n                        let mut joints = skeleton.upper_body.to_vec();\n                        if let Some(lower_body) = skeleton.lower_body {\n                            joints.extend(lower_body);\n                        }\n\n                        let relations = BODY_JOINT_RELATIONS_FB.as_slice();\n\n                        (joints, relations)\n                    }\n                    BodySkeleton::Bd(joints) => {\n                        (joints.0.to_vec(), BODY_JOINT_RELATIONS_BD.as_slice())\n                    }\n                };\n\n                for (joint1_idx, joint2_idx) in relations {\n                    if let (Some(Some(j1_pose)), Some(Some(j2_pose))) =\n                        (joints.get(*joint1_idx), joints.get(*joint2_idx))\n                    {\n                        let transform = Mat4::from_scale_rotation_translation(\n                            Vec3::ONE * Vec3::distance(j1_pose.position, j2_pose.position),\n                            Quat::from_rotation_arc(\n                                -Vec3::Z,\n                                (j2_pose.position - j1_pose.position).normalize(),\n                            ),\n                            j1_pose.position,\n                        );\n                        transform_draw(&mut pass, view_proj * transform, 2);\n                    }\n                }\n            }\n        }\n\n        self.context.queue.submit(Some(encoder.finish()));\n    }\n}\n"
  },
  {
    "path": "alvr/graphics/src/staging.rs",
    "content": "use super::{GraphicsContext, ck};\nuse crate::GL_TEXTURE_EXTERNAL_OES;\nuse alvr_common::glam::{IVec2, UVec2};\nuse glow::{self as gl, HasContext};\nuse std::{ffi::c_void, rc::Rc};\n\nfn create_program(\n    gl: &gl::Context,\n    vertex_shader_source: &str,\n    fragment_shader_source: &str,\n) -> gl::Program {\n    unsafe {\n        let vertex_shader = ck!(gl.create_shader(gl::VERTEX_SHADER).unwrap());\n        ck!(gl.shader_source(vertex_shader, vertex_shader_source));\n        ck!(gl.compile_shader(vertex_shader));\n        if !gl.get_shader_compile_status(vertex_shader) {\n            panic!(\n                \"Failed to compile vertex shader: {}\",\n                gl.get_shader_info_log(vertex_shader)\n            );\n        }\n\n        let fragment_shader = ck!(gl.create_shader(gl::FRAGMENT_SHADER).unwrap());\n        ck!(gl.shader_source(fragment_shader, fragment_shader_source));\n        ck!(gl.compile_shader(fragment_shader));\n        if !gl.get_shader_compile_status(fragment_shader) {\n            panic!(\n                \"Failed to compile fragment shader: {}\",\n                gl.get_shader_info_log(fragment_shader)\n            );\n        }\n\n        let program = ck!(gl.create_program().unwrap());\n        ck!(gl.attach_shader(program, vertex_shader));\n        ck!(gl.attach_shader(program, fragment_shader));\n        ck!(gl.link_program(program));\n        if !gl.get_program_link_status(program) {\n            panic!(\n                \"Failed to link program: {}\",\n                gl.get_program_info_log(program)\n            );\n        }\n\n        ck!(gl.delete_shader(vertex_shader));\n        ck!(gl.delete_shader(fragment_shader));\n\n        program\n    }\n}\n\npub struct StagingRenderer {\n    context: Rc<GraphicsContext>,\n    program: gl::Program,\n    view_idx_uloc: gl::UniformLocation,\n    surface_texture: gl::Texture,\n    framebuffers: [gl::Framebuffer; 2],\n    viewport_size: IVec2,\n}\n\nimpl StagingRenderer {\n    pub fn new(\n        context: Rc<GraphicsContext>,\n        staging_textures: [gl::Texture; 2],\n        view_resolution: UVec2,\n        fix_limited_range: bool,\n    ) -> Self {\n        let gl = &context.gl_context;\n        context.make_current();\n\n        // Add #defines into the shader after the first line\n        let mut frag_lines: Vec<&str> = include_str!(\"../resources/staging_fragment.glsl\")\n            .lines()\n            .collect();\n        if fix_limited_range {\n            frag_lines.insert(1, \"#line 0 1\\n#define FIX_LIMITED_RANGE\");\n        }\n        let frag_str = frag_lines.join(\"\\n\");\n\n        let program = create_program(\n            gl,\n            include_str!(\"../resources/staging_vertex.glsl\"),\n            frag_str.as_str(),\n        );\n\n        unsafe {\n            // This is an external surface and storage should not be initialized\n            let surface_texture = ck!(gl.create_texture().unwrap());\n\n            let mut framebuffers = vec![];\n            for tex in staging_textures {\n                let framebuffer = ck!(gl.create_framebuffer().unwrap());\n                ck!(gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(framebuffer)));\n                ck!(gl.framebuffer_texture_2d(\n                    gl::DRAW_FRAMEBUFFER,\n                    gl::COLOR_ATTACHMENT0,\n                    gl::TEXTURE_2D,\n                    Some(tex),\n                    0,\n                ));\n\n                framebuffers.push(framebuffer);\n            }\n\n            ck!(gl.bind_framebuffer(gl::FRAMEBUFFER, None));\n\n            let view_idx_uloc = ck!(gl.get_uniform_location(program, \"view_idx\")).unwrap();\n\n            Self {\n                context,\n                program,\n                surface_texture,\n                view_idx_uloc,\n                framebuffers: framebuffers.try_into().unwrap(),\n                viewport_size: view_resolution.as_ivec2(),\n            }\n        }\n    }\n\n    #[allow(unused_variables)]\n    pub fn render(&self, hardware_buffer: *mut c_void) {\n        let gl = &self.context.gl_context;\n        self.context.make_current();\n\n        unsafe {\n            self.context.render_ahardwarebuffer_using_texture(\n                hardware_buffer,\n                self.surface_texture,\n                || {\n                    ck!(gl.use_program(Some(self.program)));\n\n                    ck!(gl.viewport(0, 0, self.viewport_size.x, self.viewport_size.y));\n                    ck!(gl.disable(gl::SCISSOR_TEST));\n                    ck!(gl.disable(gl::STENCIL_TEST));\n\n                    for (i, framebuffer) in self.framebuffers.iter().enumerate() {\n                        ck!(gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(*framebuffer)));\n\n                        ck!(gl.active_texture(gl::TEXTURE0));\n                        ck!(gl.bind_texture(GL_TEXTURE_EXTERNAL_OES, Some(self.surface_texture)));\n                        ck!(gl.bind_sampler(0, None));\n                        ck!(gl.uniform_1_i32(Some(&self.view_idx_uloc), i as i32));\n                        ck!(gl.draw_arrays(gl::TRIANGLE_STRIP, 0, 4));\n                    }\n                },\n            )\n        };\n    }\n}\n\nimpl Drop for StagingRenderer {\n    fn drop(&mut self) {\n        let gl = &self.context.gl_context;\n        self.context.make_current();\n\n        unsafe {\n            ck!(gl.delete_program(self.program));\n            ck!(gl.delete_texture(self.surface_texture));\n            for framebuffer in &self.framebuffers {\n                ck!(gl.delete_framebuffer(*framebuffer));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/graphics/src/stream.rs",
    "content": "use super::{GraphicsContext, MAX_PUSH_CONSTANTS_SIZE, staging::StagingRenderer};\nuse alvr_common::{\n    ViewParams,\n    glam::{self, Mat4, UVec2, Vec3, Vec4},\n};\nuse alvr_session::{FoveatedEncodingConfig, PassthroughMode, UpscalingConfig};\nuse std::{ffi::c_void, iter, mem, rc::Rc};\nuse wgpu::{\n    BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,\n    BindGroupLayoutEntry, BindingResource, BindingType, Color, ColorTargetState, ColorWrites,\n    FragmentState, LoadOp, PipelineCompilationOptions, PipelineLayoutDescriptor, PrimitiveState,\n    PrimitiveTopology, PushConstantRange, RenderPass, RenderPassColorAttachment,\n    RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, SamplerBindingType,\n    SamplerDescriptor, ShaderStages, StoreOp, TextureSampleType, TextureView,\n    TextureViewDescriptor, TextureViewDimension, VertexState, include_wgsl,\n};\n\nconst FLOAT_SIZE: u32 = mem::size_of::<f32>() as u32;\nconst U32_SIZE: u32 = mem::size_of::<u32>() as u32;\nconst VEC4_SIZE: u32 = mem::size_of::<Vec4>() as u32;\nconst TRANSFORM_SIZE: u32 = mem::size_of::<Mat4>() as u32;\n\nconst TRANSFORM_CONST_OFFSET: u32 = 0;\nconst VIEW_INDEX_CONST_OFFSET: u32 = TRANSFORM_SIZE;\nconst PASSTHROUGH_MODE_OFFSET: u32 = VIEW_INDEX_CONST_OFFSET + U32_SIZE;\nconst ALPHA_CONST_OFFSET: u32 = PASSTHROUGH_MODE_OFFSET + U32_SIZE;\nconst CK_CHANNEL0_CONST_OFFSET: u32 = ALPHA_CONST_OFFSET + FLOAT_SIZE + U32_SIZE;\nconst CK_CHANNEL1_CONST_OFFSET: u32 = CK_CHANNEL0_CONST_OFFSET + VEC4_SIZE;\nconst CK_CHANNEL2_CONST_OFFSET: u32 = CK_CHANNEL1_CONST_OFFSET + VEC4_SIZE;\nconst PUSH_CONSTANTS_SIZE: u32 = CK_CHANNEL2_CONST_OFFSET + VEC4_SIZE;\n\nconst _: () = assert!(\n    PUSH_CONSTANTS_SIZE <= MAX_PUSH_CONSTANTS_SIZE,\n    \"Push constants size exceeds the maximum size\"\n);\n\npub struct StreamViewParams {\n    pub swapchain_index: u32,\n    pub input_view_params: ViewParams,\n    pub output_view_params: ViewParams,\n}\n\n#[derive(Debug)]\nstruct ViewObjects {\n    bind_group: BindGroup,\n    render_target: Vec<TextureView>,\n}\n\npub struct StreamRenderer {\n    context: Rc<GraphicsContext>,\n    staging_renderer: StagingRenderer,\n    pipeline: RenderPipeline,\n    views_objects: [ViewObjects; 2],\n}\n\nimpl StreamRenderer {\n    #[expect(clippy::too_many_arguments)]\n    #[cfg_attr(any(target_os = \"macos\", target_os = \"ios\"), expect(unused))]\n    pub fn new(\n        context: Rc<GraphicsContext>,\n        base_view_resolution: UVec2,\n        target_view_resolution: UVec2,\n        swapchain_textures: [Vec<u32>; 2],\n        target_format: u32,\n        foveated_encoding: Option<FoveatedEncodingConfig>,\n        enable_srgb_correction: bool,\n        fix_limited_range: bool,\n        encoding_gamma: f32,\n        upscaling: Option<UpscalingConfig>,\n    ) -> Self {\n        let device = &context.device;\n\n        let target_format = super::gl_format_to_wgpu(target_format);\n\n        let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n            label: None,\n            entries: &[\n                BindGroupLayoutEntry {\n                    binding: 0,\n                    visibility: ShaderStages::FRAGMENT,\n                    ty: BindingType::Texture {\n                        sample_type: TextureSampleType::Float { filterable: true },\n                        view_dimension: TextureViewDimension::D2,\n                        multisampled: false,\n                    },\n                    count: None,\n                },\n                BindGroupLayoutEntry {\n                    binding: 1,\n                    visibility: ShaderStages::FRAGMENT,\n                    ty: BindingType::Sampler(SamplerBindingType::Filtering),\n                    count: None,\n                },\n            ],\n        });\n\n        let shader_module = device.create_shader_module(include_wgsl!(\"../resources/stream.wgsl\"));\n\n        let mut constants = vec![];\n\n        constants.extend([\n            (\"ENABLE_SRGB_CORRECTION\", enable_srgb_correction.into()),\n            (\"ENCODING_GAMMA\", encoding_gamma.into()),\n        ]);\n\n        let staging_resolution = if let Some(foveated_encoding) = foveated_encoding {\n            let (staging_resolution, ffe_constants) =\n                foveated_encoding_shader_constants(base_view_resolution, foveated_encoding);\n            constants.extend(ffe_constants);\n\n            staging_resolution\n        } else {\n            base_view_resolution\n        };\n\n        if let Some(upscaling) = upscaling {\n            constants.extend([\n                (\"ENABLE_UPSCALING\", true.into()),\n                (\n                    \"UPSCALE_USE_EDGE_DIRECTION\",\n                    upscaling.edge_direction.into(),\n                ),\n                (\n                    \"UPSCALE_EDGE_THRESHOLD\",\n                    (upscaling.edge_threshold / 255.0).into(),\n                ),\n                (\"UPSCALE_EDGE_SHARPNESS\", upscaling.edge_sharpness.into()),\n            ]);\n        };\n\n        let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {\n            label: None,\n            // Note: Layout cannot be inferred because of a bug with push constants\n            layout: Some(&device.create_pipeline_layout(&PipelineLayoutDescriptor {\n                label: None,\n                bind_group_layouts: &[&bind_group_layout],\n                push_constant_ranges: &[PushConstantRange {\n                    stages: ShaderStages::VERTEX_FRAGMENT,\n                    range: 0..PUSH_CONSTANTS_SIZE,\n                }],\n            })),\n            vertex: VertexState {\n                module: &shader_module,\n                entry_point: None,\n                compilation_options: PipelineCompilationOptions {\n                    constants: &constants,\n                    zero_initialize_workgroup_memory: false,\n                },\n                buffers: &[],\n            },\n            primitive: PrimitiveState {\n                topology: PrimitiveTopology::TriangleStrip,\n                ..Default::default()\n            },\n            depth_stencil: None,\n            multisample: Default::default(),\n            fragment: Some(FragmentState {\n                module: &shader_module,\n                entry_point: None,\n                compilation_options: PipelineCompilationOptions {\n                    constants: &constants,\n                    zero_initialize_workgroup_memory: false,\n                },\n                targets: &[Some(ColorTargetState {\n                    format: target_format,\n                    blend: None,\n                    write_mask: ColorWrites::ALL,\n                })],\n            }),\n            multiview: None,\n            cache: None,\n        });\n\n        let sampler = device.create_sampler(&SamplerDescriptor {\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            ..Default::default()\n        });\n\n        let mut view_objects = vec![];\n        let mut staging_textures_gl = vec![];\n        for target_swapchain in &swapchain_textures {\n            let staging_texture = super::create_texture(device, staging_resolution, target_format);\n\n            let bind_group = device.create_bind_group(&BindGroupDescriptor {\n                label: None,\n                layout: &bind_group_layout,\n                entries: &[\n                    BindGroupEntry {\n                        binding: 0,\n                        resource: BindingResource::TextureView(\n                            &staging_texture.create_view(&TextureViewDescriptor::default()),\n                        ),\n                    },\n                    BindGroupEntry {\n                        binding: 1,\n                        resource: BindingResource::Sampler(&sampler),\n                    },\n                ],\n            });\n\n            let render_target = super::create_gl_swapchain(\n                device,\n                target_swapchain,\n                target_view_resolution,\n                target_format,\n            );\n\n            view_objects.push(ViewObjects {\n                bind_group,\n                render_target,\n            });\n\n            #[cfg(not(any(target_os = \"macos\", target_os = \"ios\")))]\n            {\n                let staging_texture_gl = unsafe {\n                    staging_texture.as_hal::<wgpu::hal::api::Gles, _, _>(|tex| {\n                        let wgpu::hal::gles::TextureInner::Texture { raw, .. } = tex.unwrap().inner\n                        else {\n                            panic!(\"invalid texture type\");\n                        };\n                        raw\n                    })\n                };\n                staging_textures_gl.push(staging_texture_gl);\n            }\n        }\n\n        let staging_renderer = StagingRenderer::new(\n            Rc::clone(&context),\n            staging_textures_gl.try_into().unwrap(),\n            staging_resolution,\n            fix_limited_range,\n        );\n\n        Self {\n            context,\n            staging_renderer,\n            pipeline,\n            views_objects: view_objects.try_into().unwrap(),\n        }\n    }\n\n    /// # Safety\n    /// `hardware_buffer` must be a valid pointer to a ANativeWindowBuffer.\n    pub fn render(\n        &self,\n        hardware_buffer: *mut c_void,\n        view_params: [StreamViewParams; 2],\n        passthrough: Option<&PassthroughMode>,\n    ) {\n        // if hardware_buffer is available copy stream to staging texture\n        if !hardware_buffer.is_null() {\n            self.staging_renderer.render(hardware_buffer);\n        }\n\n        let mut encoder = self\n            .context\n            .device\n            .create_command_encoder(&Default::default());\n\n        for (view_idx, view_params) in view_params.iter().enumerate() {\n            let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {\n                label: None,\n                color_attachments: &[Some(RenderPassColorAttachment {\n                    view: &self.views_objects[view_idx].render_target\n                        [view_params.swapchain_index as usize],\n                    resolve_target: None,\n                    ops: wgpu::Operations {\n                        load: LoadOp::Clear(Color::BLACK),\n                        store: StoreOp::Store,\n                    },\n                })],\n                ..Default::default()\n            });\n\n            let input_fov = view_params.input_view_params.fov;\n\n            let tanl = f32::tan(input_fov.left);\n            let tanr = f32::tan(input_fov.right);\n            let tanu = f32::tan(input_fov.up);\n            let tand = f32::tan(input_fov.down);\n\n            let width = tanr - tanl;\n            let height = tanu - tand;\n            let quad_depth = 1000.0;\n\n            let output_mat4 = Mat4::from_translation(view_params.output_view_params.pose.position)\n                * Mat4::from_quat(view_params.output_view_params.pose.orientation);\n            let input_mat4 = Mat4::from_translation(view_params.input_view_params.pose.position)\n                * Mat4::from_quat(view_params.input_view_params.pose.orientation);\n\n            // The image is at z = -1.0, so we use tangents for the size\n            let model_mat =\n                Mat4::from_translation(Vec3 {\n                    x: 0.0,\n                    y: 0.0,\n                    z: -quad_depth * 0.5,\n                }) * Mat4::from_scale(Vec3::new(quad_depth, quad_depth, quad_depth * 0.5))\n                    * Mat4::from_translation(Vec3::new(\n                        width / 2.0 + tanl,\n                        height / 2.0 + tand,\n                        -1.0,\n                    ))\n                    * Mat4::from_scale(Vec3::new(width, height, 1.));\n            let view_mat = output_mat4.inverse() * input_mat4;\n            let proj_mat = super::projection_from_fov(view_params.output_view_params.fov);\n\n            let transform = proj_mat * view_mat * model_mat;\n\n            let transform_bytes = transform\n                .to_cols_array()\n                .iter()\n                .flat_map(|v| v.to_le_bytes())\n                .collect::<Vec<u8>>();\n\n            render_pass.set_pipeline(&self.pipeline);\n            render_pass.set_push_constants(\n                ShaderStages::VERTEX_FRAGMENT,\n                TRANSFORM_CONST_OFFSET,\n                &transform_bytes,\n            );\n            render_pass.set_push_constants(\n                ShaderStages::VERTEX_FRAGMENT,\n                VIEW_INDEX_CONST_OFFSET,\n                &(view_idx as u32).to_le_bytes(),\n            );\n            render_pass.set_bind_group(0, &self.views_objects[view_idx].bind_group, &[]);\n            set_passthrough_push_constants(&mut render_pass, passthrough);\n            render_pass.draw(0..4, 0..1);\n        }\n\n        self.context.queue.submit(iter::once(encoder.finish()));\n    }\n}\n\nfn set_passthrough_push_constants(render_pass: &mut RenderPass, config: Option<&PassthroughMode>) {\n    const DEG_TO_NORM: f32 = 1. / 360.;\n\n    fn set_u32(render_pass: &mut RenderPass, offset: u32, value: u32) {\n        render_pass.set_push_constants(ShaderStages::VERTEX_FRAGMENT, offset, &value.to_le_bytes());\n    }\n\n    fn set_float(render_pass: &mut RenderPass, offset: u32, value: f32) {\n        render_pass.set_push_constants(ShaderStages::VERTEX_FRAGMENT, offset, &value.to_le_bytes());\n    }\n\n    fn set_vec4(render_pass: &mut RenderPass, offset: u32, value: Vec4) {\n        render_pass.set_push_constants(\n            ShaderStages::VERTEX_FRAGMENT,\n            offset,\n            &value.x.to_le_bytes(),\n        );\n        render_pass.set_push_constants(\n            ShaderStages::VERTEX_FRAGMENT,\n            offset + FLOAT_SIZE,\n            &value.y.to_le_bytes(),\n        );\n        render_pass.set_push_constants(\n            ShaderStages::VERTEX_FRAGMENT,\n            offset + 2 * FLOAT_SIZE,\n            &value.z.to_le_bytes(),\n        );\n        render_pass.set_push_constants(\n            ShaderStages::VERTEX_FRAGMENT,\n            offset + 3 * FLOAT_SIZE,\n            &value.w.to_le_bytes(),\n        );\n    }\n\n    match config {\n        None => {\n            set_u32(render_pass, PASSTHROUGH_MODE_OFFSET, 0);\n            set_float(render_pass, ALPHA_CONST_OFFSET, 1.);\n        }\n        Some(PassthroughMode::Blend { threshold, .. }) => {\n            set_u32(render_pass, PASSTHROUGH_MODE_OFFSET, 0);\n            set_float(render_pass, ALPHA_CONST_OFFSET, 1. - threshold);\n        }\n        Some(PassthroughMode::RgbChromaKey(config)) => {\n            set_u32(render_pass, PASSTHROUGH_MODE_OFFSET, 1);\n\n            let norm = |v| v as f32 / 255.;\n\n            let red = norm(config.red);\n            let green = norm(config.green);\n            let blue = norm(config.blue);\n\n            let thresh = norm(config.distance_threshold);\n\n            let up_feather = 1. + config.feathering;\n            let down_feather = 1. - config.feathering;\n\n            let range_vec =\n                thresh * Vec4::new(-up_feather, -down_feather, down_feather, up_feather);\n\n            set_vec4(render_pass, CK_CHANNEL0_CONST_OFFSET, red + range_vec);\n            set_vec4(render_pass, CK_CHANNEL1_CONST_OFFSET, green + range_vec);\n            set_vec4(render_pass, CK_CHANNEL2_CONST_OFFSET, blue + range_vec);\n        }\n        Some(PassthroughMode::HsvChromaKey(config)) => {\n            set_u32(render_pass, PASSTHROUGH_MODE_OFFSET, 2);\n\n            set_vec4(\n                render_pass,\n                CK_CHANNEL0_CONST_OFFSET,\n                Vec4::new(\n                    config.hue_start_max_deg,\n                    config.hue_start_min_deg,\n                    config.hue_end_min_deg,\n                    config.hue_end_max_deg,\n                ) * DEG_TO_NORM,\n            );\n\n            set_vec4(\n                render_pass,\n                CK_CHANNEL1_CONST_OFFSET,\n                Vec4::new(\n                    config.saturation_start_max,\n                    config.saturation_start_min,\n                    config.saturation_end_min,\n                    config.saturation_end_max,\n                ),\n            );\n\n            set_vec4(\n                render_pass,\n                CK_CHANNEL2_CONST_OFFSET,\n                Vec4::new(\n                    config.value_start_max,\n                    config.value_start_min,\n                    config.value_end_min,\n                    config.value_end_max,\n                ),\n            );\n        }\n    }\n}\n\npub fn foveated_encoding_shader_constants(\n    expanded_view_resolution: UVec2,\n    config: FoveatedEncodingConfig,\n) -> (UVec2, Vec<(&'static str, f64)>) {\n    let view_resolution = expanded_view_resolution.as_vec2();\n\n    let center_size = glam::vec2(config.center_size_x, config.center_size_y);\n    let center_shift = glam::vec2(config.center_shift_x, config.center_shift_y);\n    let edge_ratio = glam::vec2(config.edge_ratio_x, config.edge_ratio_y);\n\n    let edge_size = view_resolution - center_size * view_resolution;\n    let center_size_aligned =\n        1. - (edge_size / (edge_ratio * 2.)).ceil() * (edge_ratio * 2.) / view_resolution;\n\n    let edge_size_aligned = view_resolution - center_size_aligned * view_resolution;\n    let center_shift_aligned = (center_shift * edge_size_aligned / (edge_ratio * 2.)).ceil()\n        * (edge_ratio * 2.)\n        / edge_size_aligned;\n\n    let foveation_scale = center_size_aligned + (1. - center_size_aligned) / edge_ratio;\n\n    let optimized_view_resolution = foveation_scale * view_resolution;\n\n    let optimized_view_resolution_aligned =\n        optimized_view_resolution.map(|v| (v / 32.).ceil() * 32.);\n\n    let view_ratio_aligned = optimized_view_resolution / optimized_view_resolution_aligned;\n\n    let c0 = (1. - center_size_aligned) * 0.5;\n    let c1 = (edge_ratio - 1.) * c0 * (center_shift_aligned + 1.) / edge_ratio;\n    let c2 = (edge_ratio - 1.) * center_size_aligned + 1.;\n\n    let lo_bound = c0 * (center_shift_aligned + 1.);\n    let hi_bound = c0 * (center_shift_aligned - 1.) + 1.;\n    let lo_bound_c = c0 * (center_shift_aligned + 1.) / c2;\n    let hi_bound_c = c0 * (center_shift_aligned - 1.) / c2 + 1.;\n\n    let a_left = c2 * (1. - edge_ratio) / (edge_ratio * lo_bound_c);\n    let b_left = (c1 + c2 * lo_bound_c) / lo_bound_c;\n\n    let a_right = c2 * (edge_ratio - 1.) / (edge_ratio * (1. - hi_bound_c));\n    let b_right = (c2 - edge_ratio * c1 - 2. * edge_ratio * c2\n        + c2 * edge_ratio * (1. - hi_bound_c)\n        + edge_ratio)\n        / (edge_ratio * (1. - hi_bound_c));\n    let c_right = (c2 * edge_ratio - c2) * (c1 - hi_bound_c + c2 * hi_bound_c)\n        / (edge_ratio * (1. - hi_bound_c) * (1. - hi_bound_c));\n\n    let constants = [\n        (\"ENABLE_FFE\", 1.),\n        (\"VIEW_WIDTH_RATIO\", view_ratio_aligned.x),\n        (\"VIEW_HEIGHT_RATIO\", view_ratio_aligned.y),\n        (\"EDGE_X_RATIO\", edge_ratio.x),\n        (\"EDGE_Y_RATIO\", edge_ratio.y),\n        (\"C1_X\", c1.x),\n        (\"C1_Y\", c1.y),\n        (\"C2_X\", c2.x),\n        (\"C2_Y\", c2.y),\n        (\"LO_BOUND_X\", lo_bound.x),\n        (\"LO_BOUND_Y\", lo_bound.y),\n        (\"HI_BOUND_X\", hi_bound.x),\n        (\"HI_BOUND_Y\", hi_bound.y),\n        (\"A_LEFT_X\", a_left.x),\n        (\"A_LEFT_Y\", a_left.y),\n        (\"B_LEFT_X\", b_left.x),\n        (\"B_LEFT_Y\", b_left.y),\n        (\"A_RIGHT_X\", a_right.x),\n        (\"A_RIGHT_Y\", a_right.y),\n        (\"B_RIGHT_X\", b_right.x),\n        (\"B_RIGHT_Y\", b_right.y),\n        (\"C_RIGHT_X\", c_right.x),\n        (\"C_RIGHT_Y\", c_right.y),\n    ]\n    .iter()\n    .map(|(k, v)| (*k, *v as f64))\n    .collect();\n\n    (optimized_view_resolution_aligned.as_uvec2(), constants)\n}\n\npub fn compute_target_view_resolution(\n    resolution: UVec2,\n    upscaling: &Option<UpscalingConfig>,\n) -> UVec2 {\n    let mut target_resolution = resolution.as_vec2();\n    if let Some(upscaling) = upscaling {\n        target_resolution *= upscaling.upscale_factor;\n    }\n    target_resolution.as_uvec2()\n}\n"
  },
  {
    "path": "alvr/gui_common/Cargo.toml",
    "content": "[package]\nname = \"alvr_gui_common\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\n\negui = \"0.32\"\n"
  },
  {
    "path": "alvr/gui_common/src/basic_components/button_group.rs",
    "content": "use crate::DisplayString;\nuse egui::Ui;\n\n// todo: use a custom widget\npub fn button_group_clicked(\n    ui: &mut Ui,\n    options: &[DisplayString],\n    selection: &mut String,\n) -> bool {\n    let mut clicked = false;\n    for id in options {\n        let res = ui.selectable_value(selection, (**id).clone(), &id.display);\n        if res.clicked() {\n            clicked = true;\n        }\n\n        if cfg!(debug_assertions) {\n            res.on_hover_text((**id).clone());\n        }\n    }\n\n    clicked\n}\n"
  },
  {
    "path": "alvr/gui_common/src/basic_components/mod.rs",
    "content": "mod button_group;\nmod modal;\nmod switch;\n\npub use button_group::*;\npub use modal::*;\npub use switch::*;\n"
  },
  {
    "path": "alvr/gui_common/src/basic_components/modal.rs",
    "content": "use egui::{Align, Align2, Context, Layout, Ui, Window};\nuse std::fmt::{self, Display, Formatter};\n\n#[derive(Clone, PartialEq)]\npub enum ModalButton {\n    Ok,\n    Cancel,\n    Close,\n    Custom(String),\n}\n\nimpl Display for ModalButton {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        match self {\n            ModalButton::Ok => write!(f, \"OK\"),\n            ModalButton::Cancel => write!(f, \"Cancel\"),\n            ModalButton::Close => write!(f, \"Close\"),\n            ModalButton::Custom(text) => write!(f, \"{text}\"),\n        }\n    }\n}\n\npub fn modal(\n    context: &Context,\n    title: &str,\n    content: Option<impl FnOnce(&mut Ui)>,\n    buttons: &[ModalButton],\n    width: Option<f32>,\n) -> Option<ModalButton> {\n    let mut response = None;\n\n    let mut window = Window::new(title)\n        .anchor(Align2::CENTER_CENTER, (0.0, 0.0))\n        .collapsible(false)\n        .resizable(false);\n\n    if let Some(w) = width {\n        window = window.min_width(w).max_width(w);\n    }\n\n    window.show(context, |ui| {\n        ui.vertical_centered_justified(|ui| {\n            if let Some(content) = content {\n                ui.add_space(10.0);\n                content(ui);\n                ui.add_space(10.0);\n            }\n\n            ui.columns(buttons.len(), |cols| {\n                for (idx, response_type) in buttons.iter().enumerate() {\n                    cols[idx].with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                        if ui.button(response_type.to_string()).clicked() {\n                            response = Some(response_type.clone());\n                        }\n                    });\n                }\n            });\n        });\n    });\n\n    response\n}\n"
  },
  {
    "path": "alvr/gui_common/src/basic_components/switch.rs",
    "content": "use crate::theme;\nuse egui::{self, Response, Sense, StrokeKind, Ui, WidgetInfo, WidgetType};\n\npub fn switch(ui: &mut Ui, on: &mut bool) -> Response {\n    let desired_size = theme::SWITCH_DOT_DIAMETER * egui::vec2(2.0, 1.0);\n    let (rect, mut response) = ui.allocate_exact_size(desired_size, Sense::click());\n    if response.clicked() {\n        *on = !*on;\n        response.mark_changed();\n    }\n    response.widget_info(|| WidgetInfo::selected(WidgetType::Checkbox, true, *on, \"\"));\n\n    let how_on = ui.ctx().animate_bool(response.id, *on);\n    let visuals = ui.style().interact_selectable(&response, *on);\n    let rect = rect.expand(visuals.expansion);\n    let radius = 0.5 * rect.height();\n    ui.painter().rect(\n        rect,\n        radius,\n        visuals.bg_fill,\n        visuals.bg_stroke,\n        StrokeKind::Middle,\n    );\n    let circle_x = egui::lerp((rect.left() + radius)..=(rect.right() - radius), how_on);\n    let center = egui::pos2(circle_x, rect.center().y);\n    ui.painter()\n        .circle(center, 0.75 * radius, visuals.bg_fill, visuals.fg_stroke);\n\n    response\n}\n"
  },
  {
    "path": "alvr/gui_common/src/lib.rs",
    "content": "mod basic_components;\npub mod theme;\n\npub use basic_components::*;\n\nuse std::{ops::Deref, sync::atomic::AtomicUsize};\n\npub fn get_id() -> usize {\n    static NEXT_ID: AtomicUsize = AtomicUsize::new(0);\n\n    NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)\n}\n\n#[derive(Clone)]\npub struct DisplayString {\n    pub id: String,\n    pub display: String,\n}\n\nimpl From<(String, String)> for DisplayString {\n    fn from((id, display): (String, String)) -> Self {\n        Self { id, display }\n    }\n}\n\nimpl Deref for DisplayString {\n    type Target = String;\n\n    fn deref(&self) -> &String {\n        &self.id\n    }\n}\n"
  },
  {
    "path": "alvr/gui_common/src/theme.rs",
    "content": "use egui::{self, Color32, Context, CornerRadius, Stroke, TextStyle, ThemePreference, Visuals};\n\npub const ACCENT: Color32 = Color32::from_rgb(0, 76, 176);\npub const BG: Color32 = Color32::from_rgb(30, 30, 30);\npub const LIGHTER_BG: Color32 = Color32::from_rgb(36, 36, 36);\npub const SECTION_BG: Color32 = Color32::from_rgb(36, 36, 36);\npub const DARKER_BG: Color32 = Color32::from_rgb(26, 26, 26);\npub const SEPARATOR_BG: Color32 = Color32::from_rgb(69, 69, 69);\npub const FG: Color32 = Color32::from_rgb(250, 250, 250);\npub const SCROLLBAR_DOT_DIAMETER: f32 = 20.0;\npub const SWITCH_DOT_DIAMETER: f32 = SCROLLBAR_DOT_DIAMETER;\npub const FRAME_PADDING: f32 = 10.0;\npub const CORNER_RADIUS: u8 = 10;\npub const FRAME_TEXT_SPACING: f32 = 5.0;\n\npub const OK_GREEN: Color32 = Color32::GREEN;\npub const KO_RED: Color32 = Color32::RED;\n\npub mod log_colors {\n    use egui::epaint::Color32;\n\n    pub const ERROR_LIGHT: Color32 = Color32::from_rgb(255, 50, 50);\n    pub const WARNING_LIGHT: Color32 = Color32::from_rgb(205, 147, 9);\n    pub const INFO_LIGHT: Color32 = Color32::from_rgb(134, 171, 241);\n    pub const DEBUG_LIGHT: Color32 = Color32::LIGHT_GRAY;\n    pub const EVENT_LIGHT: Color32 = Color32::GRAY;\n}\n\n// Graph colors\npub mod graph_colors {\n    use egui::Color32;\n\n    // Colors taken from https://colorhunt.co/palette/ff6b6bffd93d6bcb774d96ff\n    pub const RENDER_EXTERNAL: Color32 = Color32::from_rgb(64, 64, 64);\n    pub const RENDER_EXTERNAL_LABEL: Color32 = Color32::GRAY;\n    pub const RENDER: Color32 = Color32::RED;\n    pub const IDLE: Color32 = Color32::from_rgb(255, 217, 61);\n    pub const TRANSCODE: Color32 = Color32::from_rgb(107, 203, 119);\n    pub const NETWORK: Color32 = Color32::from_rgb(77, 150, 255);\n\n    pub const SERVER_FPS: Color32 = Color32::LIGHT_BLUE;\n    pub const CLIENT_FPS: Color32 = Color32::KHAKI;\n\n    pub const INITIAL_CALCULATED_THROUGHPUT: Color32 = Color32::GRAY;\n    pub const ENCODER_DECODER_LATENCY_LIMITER: Color32 = TRANSCODE;\n    pub const NETWORK_LATENCY_LIMITER: Color32 = NETWORK;\n    pub const MIN_MAX_LATENCY_THROUGHPUT: Color32 = Color32::RED;\n    pub const REQUESTED_BITRATE: Color32 = Color32::GREEN;\n    pub const RECORDED_THROUGHPUT: Color32 = Color32::KHAKI;\n    pub const RECORDED_BITRATE: Color32 = super::FG;\n}\n\npub fn set_theme(ctx: &Context) {\n    ctx.set_theme(ThemePreference::Dark);\n\n    let mut style = (*ctx.style()).clone();\n    style.spacing.slider_width = 200_f32; // slider width can only be set globally\n    style.spacing.interact_size.x = 35.0;\n    style.spacing.interact_size.y = 35.0;\n\n    style.spacing.item_spacing = egui::vec2(15.0, 15.0);\n    style.spacing.button_padding = egui::vec2(10.0, 10.0);\n    style.spacing.window_margin = egui::Margin::from(FRAME_PADDING);\n\n    style.text_styles.get_mut(&TextStyle::Body).unwrap().size = 14.0;\n    style.interaction.tooltip_delay = 0.0;\n\n    ctx.set_style(style);\n\n    let mut visuals = Visuals::dark();\n\n    let corner_radius = CornerRadius::same(CORNER_RADIUS);\n\n    visuals.widgets.active.bg_fill = ACCENT;\n    visuals.widgets.active.fg_stroke = Stroke::new(1.0, FG);\n    visuals.widgets.active.corner_radius = corner_radius;\n\n    visuals.widgets.inactive.fg_stroke = Stroke::new(1.0, FG);\n    visuals.widgets.inactive.corner_radius = corner_radius;\n\n    visuals.widgets.hovered.corner_radius = corner_radius;\n\n    visuals.widgets.open.bg_fill = SEPARATOR_BG;\n    visuals.widgets.open.corner_radius = corner_radius;\n\n    visuals.selection.bg_fill = ACCENT;\n    visuals.selection.stroke = Stroke::new(1.0, FG);\n\n    visuals.faint_bg_color = DARKER_BG;\n\n    visuals.widgets.noninteractive.bg_fill = BG;\n    visuals.widgets.noninteractive.fg_stroke = Stroke::new(1.0, FG);\n    visuals.widgets.noninteractive.bg_stroke = Stroke::new(0.5, SEPARATOR_BG);\n    visuals.widgets.noninteractive.corner_radius =\n        CornerRadius::same(CORNER_RADIUS + FRAME_PADDING as u8); // Frame corner radius\n\n    ctx.set_visuals(visuals);\n}\n"
  },
  {
    "path": "alvr/launcher/Cargo.toml",
    "content": "[package]\nname = \"alvr_launcher\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_adb.workspace = true\nalvr_common.workspace = true\nalvr_filesystem.workspace = true\nalvr_gui_common.workspace = true\nalvr_system_info.workspace = true\n\nanyhow = \"1\"\neframe = \"0.32\"\nflate2 = \"1.0.18\"\nfutures-util = \"0.3.28\"\nico = \"0.4\"\nopen = \"5\"\nreqwest = { version = \"0.12\", default-features = false, features = [\n    \"rustls-tls\",\n    \"stream\",\n    \"json\",\n    \"http2\",\n] }\nserde_json = \"1\"\ntar = \"0.4\"\ntokio = { version = \"1\", features = [\"rt-multi-thread\"] }\nzip = \"4\"\n\n[target.'cfg(windows)'.build-dependencies]\nwinres = \"0.1\"\n"
  },
  {
    "path": "alvr/launcher/build.rs",
    "content": "#[cfg(windows)]\nfn main() {\n    let mut resource = winres::WindowsResource::new();\n    resource.set_icon(\"../dashboard/resources/dashboard.ico\");\n    resource.compile().unwrap();\n}\n\n#[cfg(not(windows))]\nfn main() {}\n"
  },
  {
    "path": "alvr/launcher/src/actions.rs",
    "content": "use crate::{\n    InstallationInfo, Progress, ReleaseChannelsInfo, ReleaseInfo, UiMessage, WorkerMessage,\n};\nuse alvr_common::{ToAny, anyhow::Result, semver::Version};\nuse anyhow::{Context, bail};\nuse flate2::read::GzDecoder;\nuse futures_util::StreamExt;\nuse std::{\n    env,\n    fs::{self, File},\n    io::{Cursor, Write},\n    path::PathBuf,\n    process::Command,\n    sync::mpsc::{Receiver, Sender},\n};\n\nconst APK_NAME: &str = \"client.apk\";\n\npub fn installations_dir() -> PathBuf {\n    data_dir().join(\"installations\")\n}\n\npub fn worker(\n    ui_message_receiver: Receiver<UiMessage>,\n    worker_message_sender: Sender<WorkerMessage>,\n) {\n    tokio::runtime::Runtime::new()\n        .expect(\"Failed to create tokio runtime\")\n        .block_on(async {\n            let req_client = reqwest::Client::builder()\n                .user_agent(\"ALVR-Launcher\")\n                .build()\n                .unwrap();\n            let version_data = match fetch_all_releases(&req_client).await {\n                Ok(data) => data,\n                Err(e) => {\n                    eprintln!(\"Error fetching version data: {e}\");\n                    return;\n                }\n            };\n\n            worker_message_sender\n                .send(WorkerMessage::ReleaseChannelsInfo(version_data))\n                .unwrap();\n\n            loop {\n                let Ok(message) = ui_message_receiver.recv() else {\n                    return;\n                };\n                let res = match message {\n                    UiMessage::Quit => return,\n                    UiMessage::InstallServer {\n                        release_info,\n                        session_version,\n                    } => {\n                        install_server(\n                            &worker_message_sender,\n                            release_info,\n                            session_version,\n                            &req_client,\n                        )\n                        .await\n                    }\n                    UiMessage::InstallClient(release_info) => {\n                        install_and_launch_apk(&worker_message_sender, release_info)\n                    }\n                };\n                match res {\n                    Ok(()) => worker_message_sender.send(WorkerMessage::Done).unwrap(),\n                    Err(e) => worker_message_sender\n                        .send(WorkerMessage::Error(e.to_string()))\n                        .unwrap(),\n                }\n            }\n        });\n}\n\nasync fn fetch_all_releases(client: &reqwest::Client) -> Result<ReleaseChannelsInfo> {\n    Ok(ReleaseChannelsInfo {\n        stable: fetch_releases_for_repo(\n            client,\n            \"https://api.github.com/repos/alvr-org/ALVR/releases\",\n        )\n        .await?,\n        nightly: fetch_releases_for_repo(\n            client,\n            \"https://api.github.com/repos/alvr-org/ALVR-nightly/releases\",\n        )\n        .await?,\n    })\n}\n\nasync fn fetch_releases_for_repo(client: &reqwest::Client, url: &str) -> Result<Vec<ReleaseInfo>> {\n    let response: serde_json::Value = client.get(url).send().await?.json().await?;\n\n    let mut releases = Vec::new();\n    for value in response.as_array().to_any()? {\n        releases.push(ReleaseInfo {\n            version: value[\"tag_name\"].as_str().to_any()?.into(),\n            assets: value[\"assets\"]\n                .as_array()\n                .to_any()?\n                .iter()\n                .filter_map(|value| {\n                    Some((\n                        value[\"name\"].as_str()?.into(),\n                        value[\"browser_download_url\"].as_str()?.into(),\n                    ))\n                })\n                .collect(),\n        })\n    }\n    Ok(releases)\n}\n\npub fn get_release(\n    release_channels_info: &ReleaseChannelsInfo,\n    version: &str,\n) -> Option<ReleaseInfo> {\n    release_channels_info\n        .stable\n        .iter()\n        .find(|release| release.version == version)\n        .cloned()\n        .or_else(|| {\n            release_channels_info\n                .nightly\n                .iter()\n                .find(|release| release.version == version)\n                .cloned()\n        })\n}\n\nfn install_and_launch_apk(\n    worker_message_sender: &Sender<WorkerMessage>,\n    release: ReleaseInfo,\n) -> Result<()> {\n    worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n        message: \"Starting install\".into(),\n        progress: 0.0,\n    }))?;\n\n    let root = installations_dir().join(&release.version);\n    let apk_name = \"alvr_client_android.apk\";\n    let apk_path = root.join(apk_name);\n    if !apk_path.exists() {\n        let apk_url = release\n            .assets\n            .get(apk_name)\n            .ok_or(anyhow::anyhow!(\"Unable to determine download URL\"))?;\n        let apk_buffer = alvr_adb::commands::download(apk_url, |downloaded, total| {\n            let progress = total.map_or(0.0, |t| downloaded as f32 / t as f32);\n            worker_message_sender\n                .send(WorkerMessage::ProgressUpdate(Progress {\n                    message: \"Downloading Client APK\".into(),\n                    progress,\n                }))\n                .ok();\n        })?;\n        let mut file = File::create(&apk_path)?;\n        file.write_all(&apk_buffer)?;\n    }\n\n    let layout = alvr_filesystem::Layout::new(&root);\n    let adb_path = alvr_adb::commands::require_adb(&layout, |downloaded, total| {\n        let progress = total.map_or(0.0, |t| downloaded as f32 / t as f32);\n        worker_message_sender\n            .send(WorkerMessage::ProgressUpdate(Progress {\n                message: \"Downloading ADB\".into(),\n                progress,\n            }))\n            .ok();\n    })?;\n\n    let device_serial = alvr_adb::commands::list_devices(&adb_path)?\n        .iter()\n        .find_map(|d| d.serial.clone())\n        .ok_or(anyhow::anyhow!(\"Failed to find connected device\"))?;\n\n    let v = if release.version.starts_with('v') {\n        release.version[1..].to_string()\n    } else {\n        release.version\n    };\n    let version = Version::parse(&v).context(\"Failed to parse release version\")?;\n    let stable = version.pre.is_empty() && !version.build.contains(\"nightly\");\n    let application_id = if stable {\n        alvr_system_info::PACKAGE_NAME_GITHUB_STABLE\n    } else {\n        alvr_system_info::PACKAGE_NAME_GITHUB_DEV\n    };\n\n    if alvr_adb::commands::is_package_installed(&adb_path, &device_serial, application_id)? {\n        worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n            message: \"Uninstalling old APK\".into(),\n            progress: 0.0,\n        }))?;\n        alvr_adb::commands::uninstall_package(&adb_path, &device_serial, application_id)?;\n    }\n\n    worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n        message: \"Installing new APK\".into(),\n        progress: 0.0,\n    }))?;\n    alvr_adb::commands::install_package(&adb_path, &device_serial, &apk_path.to_string_lossy())?;\n\n    alvr_adb::commands::start_application(&adb_path, &device_serial, application_id)?;\n\n    Ok(())\n}\n\nasync fn download(\n    worker_message_sender: &Sender<WorkerMessage>,\n    message: &str,\n    url: &str,\n    client: &reqwest::Client,\n) -> Result<Vec<u8>> {\n    let res = client.get(url).send().await?;\n    let total_size = res.content_length();\n    let mut stream = res.bytes_stream();\n    let mut buffer = Vec::new();\n    while let Some(item) = stream.next().await {\n        buffer.extend(item?);\n\n        match total_size {\n            Some(total_size) => {\n                worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n                    message: message.into(),\n                    progress: buffer.len() as f32 / total_size as f32,\n                }))?\n            }\n            None => worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n                message: format!(\"{message} (Progress unavailable)\"),\n                progress: 0.5,\n            }))?,\n        }\n    }\n\n    Ok(buffer)\n}\n\nasync fn install_server(\n    worker_message_sender: &Sender<WorkerMessage>,\n    release_info: ReleaseInfo,\n    session_version: Option<String>,\n    req_client: &reqwest::Client,\n) -> Result<()> {\n    worker_message_sender.send(WorkerMessage::ProgressUpdate(Progress {\n        message: \"Starting install\".into(),\n        progress: 0.0,\n    }))?;\n\n    let file_name = if cfg!(windows) {\n        \"alvr_streamer_windows.zip\"\n    } else {\n        \"alvr_streamer_linux.tar.gz\"\n    };\n\n    let url = release_info\n        .assets\n        .get(file_name)\n        .ok_or(anyhow::anyhow!(\"Unable to determine download link\"))?;\n\n    let buffer = download(\n        worker_message_sender,\n        \"Downloading Streamer\",\n        url,\n        req_client,\n    )\n    .await?;\n\n    let installation_dir = installations_dir().join(&release_info.version);\n\n    fs::create_dir_all(&installation_dir)?;\n\n    let mut buffer = Cursor::new(buffer);\n    if cfg!(windows) {\n        zip::ZipArchive::new(&mut buffer)?.extract(&installation_dir)?;\n    } else {\n        tar::Archive::new(&mut GzDecoder::new(&mut buffer)).unpack(&installation_dir)?;\n    }\n\n    if let Some(session_version) = session_version {\n        if !cfg!(windows) {\n            unreachable!(\"The session copying code should only be hit on Windows!\")\n        }\n\n        for inst in get_installations() {\n            if inst.version == session_version {\n                let source = alvr_filesystem::filesystem_layout_from_openvr_driver_root_dir(\n                    &installations_dir().join(session_version),\n                )\n                .unwrap()\n                .session();\n\n                let destination = alvr_filesystem::filesystem_layout_from_openvr_driver_root_dir(\n                    &installation_dir,\n                )\n                .unwrap()\n                .session();\n\n                fs::copy(source, destination)?;\n\n                break;\n            }\n        }\n    }\n\n    Ok(())\n}\n\npub fn data_dir() -> PathBuf {\n    if cfg!(target_os = \"linux\") {\n        PathBuf::from(env::var(\"HOME\").expect(\"Failed to determine home directory\"))\n            .join(\".local/share/ALVR-Launcher\")\n    } else {\n        env::current_exe()\n            .expect(\"Unable to determine executable directory\")\n            .parent()\n            .unwrap()\n            .to_owned()\n    }\n}\n\npub fn get_installations() -> Vec<InstallationInfo> {\n    match fs::read_dir(installations_dir()) {\n        Ok(entries) => entries\n            .into_iter()\n            .filter_map(|entry| {\n                entry\n                    .ok()\n                    .filter(|entry| match entry.file_type() {\n                        Ok(file_type) => file_type.is_dir(),\n                        Err(e) => {\n                            eprintln!(\"Failed to read entry file type: {e}\");\n                            false\n                        }\n                    })\n                    .map(|entry| {\n                        let has_session_json = if cfg!(windows) {\n                            alvr_filesystem::filesystem_layout_from_openvr_driver_root_dir(\n                                &entry.path(),\n                            )\n                            .map(|layout| layout.session().exists())\n                            .unwrap_or(false)\n                        } else {\n                            // On linux, the launcher does not need to manage the session files\n                            false\n                        };\n\n                        InstallationInfo {\n                            version: entry.file_name().to_string_lossy().into(),\n                            is_apk_downloaded: entry.path().join(APK_NAME).exists(),\n                            has_session_json,\n                        }\n                    })\n            })\n            .collect(),\n        Err(e) => {\n            eprintln!(\"Failed to read versions dir: {e}\");\n            Vec::new()\n        }\n    }\n}\n\npub fn launch_dashboard(version: &str) -> Result<()> {\n    let installation_dir = installations_dir().join(version);\n\n    let dashboard_path = if cfg!(windows) {\n        installation_dir.join(\"ALVR Dashboard.exe\")\n    } else if cfg!(target_os = \"linux\") {\n        installation_dir.join(\"alvr_streamer_linux/bin/alvr_dashboard\")\n    } else {\n        bail!(\"Unsupported platform\")\n    };\n\n    Command::new(dashboard_path).spawn()?;\n\n    Ok(())\n}\n\npub fn delete_installation(version: &str) -> Result<()> {\n    fs::remove_dir_all(installations_dir().join(version))?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/launcher/src/main.rs",
    "content": "mod actions;\nmod ui;\n\nuse eframe::egui::{IconData, ViewportBuilder};\nuse ico::IconDir;\nuse std::{collections::BTreeMap, env, fs, io::Cursor, sync::mpsc, thread};\nuse ui::Launcher;\n\npub struct ReleaseChannelsInfo {\n    stable: Vec<ReleaseInfo>,\n    nightly: Vec<ReleaseInfo>,\n}\n\npub struct Progress {\n    message: String,\n    progress: f32,\n}\n\npub enum WorkerMessage {\n    ReleaseChannelsInfo(ReleaseChannelsInfo),\n    ProgressUpdate(Progress),\n    Done,\n    Error(String),\n}\n\n#[derive(Clone)]\npub struct ReleaseInfo {\n    version: String,\n    assets: BTreeMap<String, String>,\n}\n\npub enum UiMessage {\n    InstallServer {\n        release_info: ReleaseInfo,\n        session_version: Option<String>,\n    },\n    InstallClient(ReleaseInfo),\n    Quit,\n}\n\npub struct InstallationInfo {\n    version: String,\n    is_apk_downloaded: bool,\n    has_session_json: bool, // Only relevant on Windows\n}\n\nfn main() {\n    let (worker_message_sender, worker_message_receiver) = mpsc::channel::<WorkerMessage>();\n    let (ui_message_sender, ui_message_receiver) = mpsc::channel::<UiMessage>();\n\n    let worker_handle =\n        thread::spawn(|| actions::worker(ui_message_receiver, worker_message_sender));\n\n    let ico = IconDir::read(Cursor::new(include_bytes!(\n        \"../../dashboard/resources/dashboard.ico\"\n    )))\n    .unwrap();\n    let image = ico.entries().first().unwrap().decode().unwrap();\n\n    // Workaround for the steam deck\n    if fs::read_to_string(\"/sys/devices/virtual/dmi/id/board_vendor\")\n        .map(|vendor| vendor.trim() == \"Valve\")\n        .unwrap_or(false)\n    {\n        unsafe { env::set_var(\"WINIT_X11_SCALE_FACTOR\", \"1\") };\n    }\n\n    eframe::run_native(\n        \"ALVR Launcher\",\n        eframe::NativeOptions {\n            viewport: ViewportBuilder::default()\n                .with_app_id(\"alvr.launcher\")\n                .with_inner_size((700.0, 400.0))\n                .with_icon(IconData {\n                    rgba: image.rgba_data().to_owned(),\n                    width: image.width(),\n                    height: image.height(),\n                }),\n            ..Default::default()\n        },\n        Box::new(move |cc| {\n            Ok(Box::new(Launcher::new(\n                cc,\n                worker_message_receiver,\n                ui_message_sender,\n            )))\n        }),\n    )\n    .expect(\"Failed to run eframe\");\n\n    worker_handle.join().unwrap();\n}\n"
  },
  {
    "path": "alvr/launcher/src/ui.rs",
    "content": "use crate::{InstallationInfo, Progress, ReleaseChannelsInfo, UiMessage, WorkerMessage, actions};\nuse alvr_gui_common::ModalButton;\nuse eframe::{\n    egui::{\n        self, Button, CentralPanel, ComboBox, Context, Frame, Grid, Layout, ProgressBar, RichText,\n        Ui, ViewportCommand,\n    },\n    emath::Align,\n    epaint::Color32,\n};\nuse std::{\n    mem,\n    sync::mpsc::{Receiver, Sender},\n};\n\nenum State {\n    Default,\n    Installing(Progress),\n    Error(String),\n}\n\n#[derive(Default)]\nenum PopupType {\n    #[default]\n    None,\n    DeleteInstallation(String),\n    EditVersion(String),\n    AddVersion {\n        version_selection: Version,\n        session_version_selection: Option<String>,\n    },\n}\n\n#[derive(Clone, PartialEq, Eq)]\nenum ReleaseChannelType {\n    Stable,\n    Nightly,\n}\n\n#[derive(Clone, PartialEq, Eq)]\nstruct Version {\n    string: String,\n    release_channel: ReleaseChannelType,\n}\n\npub struct Launcher {\n    worker_message_receiver: Receiver<WorkerMessage>,\n    ui_message_sender: Sender<UiMessage>,\n    state: State,\n    release_channels_info: Option<ReleaseChannelsInfo>,\n    installations: Vec<InstallationInfo>,\n    popup: PopupType,\n}\n\nimpl Launcher {\n    pub fn new(\n        cc: &eframe::CreationContext,\n        worker_message_receiver: Receiver<WorkerMessage>,\n        ui_message_sender: Sender<UiMessage>,\n    ) -> Self {\n        alvr_gui_common::theme::set_theme(&cc.egui_ctx);\n\n        Self {\n            worker_message_receiver,\n            ui_message_sender,\n            state: State::Default,\n            release_channels_info: None,\n            installations: actions::get_installations(),\n            popup: PopupType::None,\n        }\n    }\n\n    fn version_popup(\n        &self,\n        ctx: &Context,\n        mut version: Version,\n        mut session_version: Option<String>,\n    ) -> PopupType {\n        let response = alvr_gui_common::modal(\n            ctx,\n            \"Add version\",\n            {\n                // Safety: unwrap is safe because the \"Add release\" button is available after populating the release_channels_info.\n                let release_channels_info = self.release_channels_info.as_ref().unwrap();\n                Some(|ui: &mut Ui| {\n                    let version_str = version.string.clone();\n                    let versions: Vec<_> = match &version.release_channel {\n                        ReleaseChannelType::Stable => release_channels_info\n                            .stable\n                            .iter()\n                            .map(|release| Version {\n                                string: release.version.clone(),\n                                release_channel: ReleaseChannelType::Stable,\n                            })\n                            .collect(),\n\n                        ReleaseChannelType::Nightly => release_channels_info\n                            .nightly\n                            .iter()\n                            .map(|release| Version {\n                                string: release.version.clone(),\n                                release_channel: ReleaseChannelType::Nightly,\n                            })\n                            .collect(),\n                    };\n                    let installations_with_session: Vec<_> = self\n                        .installations\n                        .iter()\n                        .filter(|installation| installation.has_session_json)\n                        .map(|installation| installation.version.clone())\n                        .collect();\n\n                    Grid::new(\"add-version-grid\").num_columns(2).show(ui, |ui| {\n                        ui.label(\"Channel\");\n                        ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n                            let channel_str = match version.release_channel {\n                                ReleaseChannelType::Stable => \"Stable\",\n                                ReleaseChannelType::Nightly => \"Nightly\",\n                            };\n\n                            ComboBox::from_id_salt(\"channel\")\n                                .selected_text(channel_str)\n                                .show_ui(ui, |ui| {\n                                    ui.selectable_value(\n                                        &mut version,\n                                        Version {\n                                            string: release_channels_info.stable[0].version.clone(),\n                                            release_channel: ReleaseChannelType::Stable,\n                                        },\n                                        \"Stable\",\n                                    );\n                                    ui.selectable_value(\n                                        &mut version,\n                                        Version {\n                                            string: release_channels_info.nightly[0]\n                                                .version\n                                                .clone(),\n                                            release_channel: ReleaseChannelType::Nightly,\n                                        },\n                                        \"Nightly\",\n                                    );\n                                })\n                        });\n                        ui.end_row();\n\n                        ui.label(\"Version\");\n                        ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n                            ComboBox::from_id_salt(\"version\")\n                                .selected_text(version_str)\n                                .show_ui(ui, |ui| {\n                                    for ver in versions {\n                                        ui.selectable_value(&mut version, ver.clone(), ver.string);\n                                    }\n                                })\n                        });\n                        ui.end_row();\n\n                        if cfg!(windows) {\n                            ui.label(\"Copy session from:\");\n                            ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n                                ComboBox::from_id_salt(\"session\")\n                                    .selected_text(session_version.clone().unwrap_or(\"None\".into()))\n                                    .show_ui(ui, |ui| {\n                                        ui.selectable_value(&mut session_version, None, \"None\");\n                                        for ver_str in installations_with_session {\n                                            ui.selectable_value(\n                                                &mut session_version,\n                                                Some(ver_str.clone()),\n                                                ver_str,\n                                            );\n                                        }\n                                    })\n                            });\n                            ui.end_row();\n                        }\n                    });\n                })\n            },\n            &[ModalButton::Cancel, ModalButton::Custom(\"Install\".into())],\n            None,\n        );\n\n        match response {\n            Some(ModalButton::Cancel) => PopupType::None,\n            Some(ModalButton::Custom(_)) => {\n                let release_info = match &version.release_channel {\n                    ReleaseChannelType::Stable => self\n                        .release_channels_info\n                        .as_ref()\n                        .unwrap()\n                        .stable\n                        .iter()\n                        .find(|release| release.version == version.string)\n                        .unwrap()\n                        .clone(),\n                    ReleaseChannelType::Nightly => self\n                        .release_channels_info\n                        .as_ref()\n                        .unwrap()\n                        .nightly\n                        .iter()\n                        .find(|release| release.version == version.string)\n                        .unwrap()\n                        .clone(),\n                };\n\n                self.ui_message_sender\n                    .send(UiMessage::InstallServer {\n                        release_info,\n                        session_version,\n                    })\n                    .ok();\n\n                PopupType::None\n            }\n            _ => PopupType::AddVersion {\n                version_selection: version,\n                session_version_selection: session_version,\n            },\n        }\n    }\n\n    fn edit_popup(&self, ctx: &Context, version: String) -> PopupType {\n        let mut delete_version = false;\n        let response = alvr_gui_common::modal(\n            ctx,\n            \"Edit version\",\n            Some(|ui: &mut Ui| {\n                ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {\n                    delete_version = ui.button(\"Delete version\").clicked();\n                });\n            }),\n            &[ModalButton::Close],\n            None,\n        );\n\n        if delete_version {\n            PopupType::DeleteInstallation(version)\n        } else if matches!(response, Some(ModalButton::Close)) {\n            PopupType::None\n        } else {\n            PopupType::EditVersion(version)\n        }\n    }\n\n    fn delete_popup(&mut self, ctx: &Context, version: String) -> PopupType {\n        let response = alvr_gui_common::modal(\n            ctx,\n            \"Are you sure?\",\n            Some({\n                let version = version.clone();\n                move |ui: &mut Ui| {\n                    ui.with_layout(Layout::top_down(Align::Center), |ui| {\n                        ui.label(format!(\"This will permanently delete version {version}\"));\n                    });\n                }\n            }),\n            &[\n                ModalButton::Cancel,\n                ModalButton::Custom(\"Delete version\".into()),\n            ],\n            None,\n        );\n\n        match response {\n            Some(ModalButton::Cancel) => PopupType::None,\n            Some(ModalButton::Custom(_)) => {\n                if let Err(e) = actions::delete_installation(&version) {\n                    self.state = State::Error(format!(\"Failed to delete version: {e}\"));\n                }\n\n                self.installations = actions::get_installations();\n\n                PopupType::None\n            }\n            _ => PopupType::DeleteInstallation(version),\n        }\n    }\n}\n\nimpl eframe::App for Launcher {\n    fn update(&mut self, ctx: &Context, _: &mut eframe::Frame) {\n        while let Ok(msg) = self.worker_message_receiver.try_recv() {\n            match msg {\n                WorkerMessage::ReleaseChannelsInfo(data) => self.release_channels_info = Some(data),\n                WorkerMessage::ProgressUpdate(progress) => {\n                    self.state = State::Installing(progress);\n                }\n                WorkerMessage::Done => {\n                    // Refresh installations\n                    self.installations = actions::get_installations();\n                    self.state = State::Default;\n                }\n                WorkerMessage::Error(e) => self.state = State::Error(e),\n            }\n        }\n\n        CentralPanel::default().show(ctx, |ui| match &self.state {\n            State::Default => {\n                ui.with_layout(Layout::top_down(Align::Center), |ui| {\n                    ui.label(RichText::new(\"ALVR Launcher\").size(25.0).strong());\n                    ui.label(match &self.release_channels_info {\n                        Some(data) => format!(\"Latest stable release: {}\", data.stable[0].version),\n                        None => \"Fetching latest release...\".into(),\n                    });\n\n                    for installation in &self.installations {\n                        let path = actions::installations_dir().join(&installation.version);\n\n                        Frame::group(ui.style())\n                            .fill(alvr_gui_common::theme::SECTION_BG)\n                            .inner_margin(egui::vec2(10.0, 5.0))\n                            .show(ui, |ui| {\n                                Grid::new(&installation.version)\n                                    .num_columns(2)\n                                    .show(ui, |ui| {\n                                        ui.label(&installation.version);\n                                        ui.with_layout(Layout::right_to_left(Align::Min), |ui| {\n                                            if ui.button(\"Edit\").clicked() {\n                                                self.popup = PopupType::EditVersion(\n                                                    installation.version.clone(),\n                                                );\n                                            }\n\n                                            if ui.button(\"Open directory\").clicked() {\n                                                open::that_in_background(path);\n                                            }\n\n                                            let release_info = self\n                                                .release_channels_info\n                                                .as_ref()\n                                                .and_then(|info| {\n                                                    actions::get_release(\n                                                        info,\n                                                        &installation.version,\n                                                    )\n                                                });\n                                            if ui\n                                                .add_enabled(\n                                                    release_info.is_some()\n                                                        || installation.is_apk_downloaded,\n                                                    Button::new(\"Install APK\"),\n                                                )\n                                                .clicked()\n                                            {\n                                                if let Some(release_info) = release_info {\n                                                    self.ui_message_sender\n                                                        .send(UiMessage::InstallClient(\n                                                            release_info,\n                                                        ))\n                                                        .ok();\n                                                } else {\n                                                    self.state = State::Error(\n                                                        \"Failed to get release info\".into(),\n                                                    );\n                                                }\n                                            };\n\n                                            if ui.button(\"Launch\").clicked() {\n                                                match actions::launch_dashboard(\n                                                    &installation.version,\n                                                ) {\n                                                    Ok(()) => {\n                                                        self.ui_message_sender\n                                                            .send(UiMessage::Quit)\n                                                            .ok();\n                                                        ctx.send_viewport_cmd(\n                                                            ViewportCommand::Close,\n                                                        );\n                                                    }\n                                                    Err(e) => {\n                                                        self.state = State::Error(e.to_string());\n                                                    }\n                                                }\n                                            }\n                                        })\n                                    })\n                            });\n                    }\n\n                    if ui\n                        .add_enabled(\n                            self.release_channels_info.is_some(),\n                            Button::new(\"Add version\"),\n                        )\n                        .clicked()\n                    {\n                        self.popup = PopupType::AddVersion {\n                            version_selection: Version {\n                                string: self.release_channels_info.as_ref().unwrap().stable[0]\n                                    .version\n                                    .clone(),\n                                release_channel: ReleaseChannelType::Stable,\n                            },\n                            session_version_selection: None,\n                        };\n                    }\n\n                    let popup = match mem::take(&mut self.popup) {\n                        PopupType::AddVersion {\n                            version_selection,\n                            session_version_selection,\n                        } => self.version_popup(ctx, version_selection, session_version_selection),\n                        PopupType::EditVersion(version) => self.edit_popup(ctx, version),\n                        PopupType::DeleteInstallation(version) => self.delete_popup(ctx, version),\n                        PopupType::None => PopupType::None,\n                    };\n                    self.popup = popup;\n                });\n            }\n            State::Installing(progress) => {\n                ui.with_layout(Layout::top_down(Align::Center), |ui| {\n                    ui.label(&progress.message);\n                    ui.add(ProgressBar::new(progress.progress).animate(true));\n                });\n            }\n            State::Error(e) => {\n                let e = e.clone(); // Avoid borrowing issues with the closure for the layout\n                ui.with_layout(Layout::top_down(Align::Center), |ui| {\n                    ui.colored_label(Color32::LIGHT_RED, \"Error!\");\n                    ui.label(e);\n\n                    if ui.button(\"Close\").clicked() {\n                        self.state = State::Default;\n                    }\n                });\n            }\n        });\n\n        if ctx.input(|i| i.viewport().close_requested()) {\n            self.ui_message_sender.send(UiMessage::Quit).ok();\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/packets/Cargo.toml",
    "content": "[package]\nname = \"alvr_packets\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_session.workspace = true\n\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\n"
  },
  {
    "path": "alvr/packets/src/lib.rs",
    "content": "use alvr_common::{\n    BodySkeleton, ConnectionState, DeviceMotion, LogSeverity, Pose, ViewParams,\n    anyhow::Result,\n    glam::{Quat, UVec2, Vec2},\n    semver::Version,\n};\nuse alvr_session::{\n    ClientsidePostProcessingConfig, CodecType, PassthroughMode, PerformanceLevel, SessionConfig,\n    Settings,\n};\nuse serde::{Deserialize, Serialize};\nuse serde_json as json;\nuse std::{\n    collections::HashSet,\n    fmt::{self, Debug},\n    net::IpAddr,\n    time::Duration,\n};\n\npub const TRACKING: u16 = 0;\npub const HAPTICS: u16 = 1;\npub const AUDIO: u16 = 2;\npub const VIDEO: u16 = 3;\npub const STATISTICS: u16 = 4;\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct VideoStreamingCapabilitiesExt {\n    // Nothing for now\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct VideoStreamingCapabilities {\n    pub default_view_resolution: UVec2,\n    pub max_view_resolution: UVec2,\n    pub refresh_rates: Vec<f32>,\n    pub microphone_sample_rate: u32,\n    pub foveated_encoding: bool,\n    pub encoder_high_profile: bool,\n    pub encoder_10_bits: bool,\n    pub encoder_av1: bool,\n    pub prefer_10bit: bool,\n    pub preferred_encoding_gamma: f32,\n    pub prefer_hdr: bool,\n    pub ext_str: String,\n}\n\nimpl VideoStreamingCapabilities {\n    pub fn with_ext(self, ext: VideoStreamingCapabilitiesExt) -> Self {\n        Self {\n            ext_str: json::to_string(&ext).unwrap(),\n            ..self\n        }\n    }\n\n    pub fn ext(&self) -> Result<VideoStreamingCapabilitiesExt> {\n        let _ext_json = json::from_str::<json::Value>(&self.ext_str)?;\n\n        // decode values here\n\n        Ok(VideoStreamingCapabilitiesExt {})\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct ConnectionAcceptedInfo {\n    pub client_protocol_id: u64,\n    pub platform_string: String,\n    pub server_ip: IpAddr,\n    pub streaming_capabilities: Option<VideoStreamingCapabilities>,\n}\n\n#[derive(Serialize, Deserialize)]\npub enum ClientConnectionResult {\n    ConnectionAccepted(Box<ConnectionAcceptedInfo>),\n    ClientStandby,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct NegotiatedStreamingConfigExt {\n    // Nothing for now\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct NegotiatedStreamingConfig {\n    pub view_resolution: UVec2,\n    pub refresh_rate_hint: f32,\n    pub game_audio_sample_rate: u32,\n    pub enable_foveated_encoding: bool,\n    pub encoding_gamma: f32,\n    pub enable_hdr: bool,\n    pub wired: bool,\n    pub ext_str: String,\n}\n\nimpl NegotiatedStreamingConfig {\n    pub fn with_ext(self, ext: NegotiatedStreamingConfigExt) -> Self {\n        Self {\n            ext_str: json::to_string(&ext).unwrap(),\n            ..self\n        }\n    }\n\n    pub fn ext(&self) -> Result<NegotiatedStreamingConfigExt> {\n        let _ext_json = json::from_str::<json::Value>(&self.ext_str)?;\n\n        // decode values here\n\n        Ok(NegotiatedStreamingConfigExt {})\n    }\n}\n\n#[derive(Serialize, Deserialize)]\npub struct StreamConfigPacket {\n    pub session: String, // JSON session that allows for extrapolation\n    pub negotiated: NegotiatedStreamingConfig,\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct StreamConfig {\n    pub server_version: Version,\n    pub settings: Settings,\n    pub negotiated_config: NegotiatedStreamingConfig,\n}\n\nimpl StreamConfigPacket {\n    pub fn new(session: &SessionConfig, negotiated: NegotiatedStreamingConfig) -> Result<Self> {\n        Ok(Self {\n            session: json::to_string(session)?,\n            negotiated,\n        })\n    }\n\n    pub fn to_stream_config(self) -> Result<StreamConfig> {\n        let mut session_config = SessionConfig::default();\n        session_config.merge_from_json(&json::from_str(&self.session)?)?;\n        let settings = session_config.to_settings();\n\n        Ok(StreamConfig {\n            server_version: session_config.server_version,\n            settings,\n            negotiated_config: self.negotiated,\n        })\n    }\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct DecoderInitializationConfig {\n    pub codec: CodecType,\n    pub config_buffer: Vec<u8>, // e.g. SPS + PPS NALs\n    pub ext_str: String,\n}\n\n#[derive(Serialize, Deserialize)]\npub enum ServerControlPacket {\n    StartStream,\n    DecoderConfig(DecoderInitializationConfig),\n    Restarting,\n    KeepAlive,\n    RealTimeConfig(RealTimeConfig),\n    Reserved(String),\n    ReservedBuffer(Vec<u8>),\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub struct BatteryInfo {\n    pub device_id: u64,\n    pub gauge_value: f32, // range [0, 1]\n    pub is_plugged: bool,\n}\n\n#[derive(Serialize, Deserialize, Clone, Copy, Debug)]\npub enum ButtonValue {\n    Binary(bool),\n    Scalar(f32),\n}\n\n#[derive(Serialize, Deserialize)]\npub struct ButtonEntry {\n    pub path_id: u64,\n    pub value: ButtonValue,\n}\n\n#[derive(Serialize, Deserialize)]\npub enum ClientControlPacket {\n    PlayspaceSync(Option<Vec2>),\n    RequestIdr,\n    KeepAlive,\n    StreamReady, // This flag notifies the server the client streaming socket is ready listening\n    LocalViewParams([ViewParams; 2]), // In relation to head\n    Battery(BatteryInfo),\n    Buttons(Vec<ButtonEntry>),\n    ActiveInteractionProfile {\n        device_id: u64,\n        profile_id: u64,\n        input_ids: HashSet<u64>,\n    },\n    Log {\n        level: LogSeverity,\n        message: String,\n    },\n    ProximityState(bool),\n    Reserved(String),\n    ReservedBuffer(Vec<u8>),\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub enum FaceExpressions {\n    Fb(Vec<f32>),   // 70 values\n    Pico(Vec<f32>), // 52 values\n    Htc {\n        eye: Option<Vec<f32>>, // 14 values\n        lip: Option<Vec<f32>>, // 37 values\n    },\n}\n\n#[derive(Serialize, Deserialize, Clone, Default, Debug)]\npub struct FaceData {\n    // Can be used for foveated eye tracking\n    pub eyes_combined: Option<Quat>,\n    // Should be used only for social presence\n    pub eyes_social: [Option<Quat>; 2],\n\n    pub face_expressions: Option<FaceExpressions>,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct TrackingData {\n    pub poll_timestamp: Duration,\n    pub device_motions: Vec<(u64, DeviceMotion)>,\n    pub hand_skeletons: [Option<[Pose; 26]>; 2],\n    pub face: FaceData,\n    pub body: Option<BodySkeleton>,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct VideoPacketHeader {\n    pub timestamp: Duration,\n    pub global_view_params: [ViewParams; 2],\n    pub is_idr: bool,\n}\n\n#[derive(Serialize, Deserialize)]\npub struct Haptics {\n    pub device_id: u64,\n    pub duration: Duration,\n    pub frequency: f32,\n    pub amplitude: f32,\n}\n\n#[derive(Serialize, Deserialize, Clone)]\npub enum PathSegment {\n    Name(String),\n    Index(usize),\n}\n\nimpl Debug for PathSegment {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            PathSegment::Name(name) => write!(f, \"{name}\"),\n            PathSegment::Index(index) => write!(f, \"[{index}]\"),\n        }\n    }\n}\n\nimpl From<&str> for PathSegment {\n    fn from(value: &str) -> Self {\n        PathSegment::Name(value.to_owned())\n    }\n}\n\nimpl From<String> for PathSegment {\n    fn from(value: String) -> Self {\n        PathSegment::Name(value)\n    }\n}\n\nimpl From<usize> for PathSegment {\n    fn from(value: usize) -> Self {\n        PathSegment::Index(value)\n    }\n}\n\n// todo: support indices\npub fn parse_path(path: &str) -> Vec<PathSegment> {\n    path.split('.').map(|s| s.into()).collect()\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub enum ClientConnectionsAction {\n    AddIfMissing {\n        trusted: bool,\n        manual_ips: Vec<IpAddr>,\n    },\n    SetDisplayName(String),\n    Trust,\n    SetManualIps(Vec<IpAddr>),\n    RemoveEntry,\n    UpdateCurrentIp(Option<IpAddr>),\n    SetConnectionState(ConnectionState),\n}\n\n#[derive(Serialize, Deserialize, Default, Clone)]\npub struct ClientStatistics {\n    pub target_timestamp: Duration, // identifies the frame\n    pub frame_interval: Duration,\n    pub video_decode: Duration,\n    pub video_decoder_queue: Duration,\n    pub rendering: Duration,\n    pub vsync_queue: Duration,\n    pub total_pipeline_latency: Duration,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct PathValuePair {\n    pub path: Vec<PathSegment>,\n    pub value: json::Value,\n}\n\n#[derive(Serialize, Deserialize, Debug)]\npub enum FirewallRulesAction {\n    Add,\n    Remove,\n}\n\n// Note: server sends a packet to the client at low frequency, binary encoding, without ensuring\n// compatibility between different versions, even if within the same major version.\n#[derive(Serialize, Deserialize, PartialEq, Clone)]\npub struct RealTimeConfig {\n    pub passthrough: Option<PassthroughMode>,\n    pub clientside_post_processing: Option<ClientsidePostProcessingConfig>,\n    pub cpu_performance_level: Option<PerformanceLevel>,\n    pub gpu_performance_level: Option<PerformanceLevel>,\n    pub ext_str: String,\n}\n\nimpl RealTimeConfig {\n    pub fn from_settings(settings: &Settings) -> Self {\n        Self {\n            passthrough: settings.video.passthrough.clone().into_option(),\n            clientside_post_processing: settings\n                .video\n                .clientside_post_processing\n                .clone()\n                .into_option(),\n            cpu_performance_level: settings.headset.performance_level.clone().cpu.into_option(),\n            gpu_performance_level: settings.headset.performance_level.clone().gpu.into_option(),\n            ext_str: String::new(), // No extensions for now\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/Cargo.toml",
    "content": "[package]\nname = \"alvr_server_core\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors = [\"alvr-org\", \"Valve Corporation\"]\nlicense = \"MIT\"\n\n[lib]\ncrate-type = [\"rlib\", \"cdylib\"]\n\n[features]\ntrace-performance = [\"profiling/profile-with-tracy\"]\n\n[dependencies]\nalvr_adb.workspace = true\nalvr_audio.workspace = true\nalvr_common.workspace = true\nalvr_events.workspace = true\nalvr_filesystem.workspace = true\nalvr_packets.workspace = true\nalvr_server_io.workspace = true\nalvr_session.workspace = true\nalvr_sockets.workspace = true\n\nash = \"0.38\"\naxum = { version = \"0.8\", features = [\"ws\"] }\nchrono = \"0.4\"\nfern = \"0.7\"\nflume = \"0.11\"\nmdns-sd = \"0.14\"\nprofiling = { version = \"1\", optional = true }\nrfd = \"0.15\"\nrosc = \"0.11\"\ntokio = { version = \"1\", features = [\n    \"rt-multi-thread\",\n    \"macros\",\n    \"process\",\n    \"io-util\",\n    \"net\",\n    \"fs\",\n] }\ntower-http = { version = \"0.6\", features = [\"cors\", \"set-header\"] }\nserde = \"1\"\nserde_json = \"1\"\nsysinfo = \"0.37\"\n"
  },
  {
    "path": "alvr/server_core/cbindgen.toml",
    "content": "language = \"C\"\nheader = \"/* ALVR is licensed under the MIT license. https://github.com/alvr-org/ALVR/blob/master/LICENSE */\"\npragma_once = true\nautogen_warning = \"/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */\"\ncpp_compat = true\ntab_width = 4\ndocumentation_style = \"c99\"\n\n[enum]\nrename_variants = \"QualifiedScreamingSnakeCase\"\n\n[parse]\nparse_deps = true\ninclude = [\"alvr_common\"]\n"
  },
  {
    "path": "alvr/server_core/src/bitrate.rs",
    "content": "use alvr_common::SlidingWindowAverage;\nuse alvr_events::BitrateDirectives;\nuse alvr_session::{\n    BitrateAdaptiveFramerateConfig, BitrateConfig, BitrateMode, settings_schema::Switch,\n};\nuse std::{\n    collections::VecDeque,\n    time::{Duration, Instant},\n};\n\nconst UPDATE_INTERVAL: Duration = Duration::from_secs(1);\n\npub struct DynamicEncoderParams {\n    pub bitrate_bps: f32,\n    pub framerate: f32,\n}\n\npub struct BitrateManager {\n    nominal_frame_interval: Duration,\n    frame_interval_average: SlidingWindowAverage<Duration>,\n    // note: why packet_sizes_bits_history is a queue and not a sliding average? Because some\n    // network samples will be dropped but not any packet size sample\n    packet_bytes_history: VecDeque<(Duration, usize)>,\n    packet_bytes_average: SlidingWindowAverage<f32>,\n    network_latency_average: SlidingWindowAverage<Duration>,\n    encoder_latency_average: SlidingWindowAverage<Duration>,\n    decoder_latency_overstep_count: usize,\n    last_frame_instant: Instant,\n    last_update_instant: Instant,\n    dynamic_decoder_max_bytes_per_frame: f32,\n    previous_config: Option<BitrateConfig>,\n    update_needed: bool,\n}\n\nimpl BitrateManager {\n    pub fn new(max_history_size: usize, initial_framerate: f32) -> Self {\n        Self {\n            nominal_frame_interval: Duration::from_secs_f32(1. / initial_framerate),\n            frame_interval_average: SlidingWindowAverage::new(\n                Duration::from_millis(16),\n                max_history_size,\n            ),\n            packet_bytes_history: VecDeque::new(),\n            packet_bytes_average: SlidingWindowAverage::new(50000.0, max_history_size),\n            network_latency_average: SlidingWindowAverage::new(\n                Duration::from_millis(5),\n                max_history_size,\n            ),\n            encoder_latency_average: SlidingWindowAverage::new(\n                Duration::from_millis(5),\n                max_history_size,\n            ),\n            decoder_latency_overstep_count: 0,\n            last_frame_instant: Instant::now(),\n            last_update_instant: Instant::now(),\n            dynamic_decoder_max_bytes_per_frame: f32::MAX,\n            previous_config: None,\n            update_needed: true,\n        }\n    }\n\n    // Note: This is used to calculate the framerate/frame interval. The frame present is the most\n    // accurate event for this use.\n    pub fn report_frame_present(&mut self, config: &Switch<BitrateAdaptiveFramerateConfig>) {\n        let now = Instant::now();\n\n        let interval = now - self.last_frame_instant;\n        self.last_frame_instant = now;\n\n        if let Some(config) = config.as_option() {\n            let interval_ratio =\n                interval.as_secs_f32() / self.frame_interval_average.get_average().as_secs_f32();\n\n            self.frame_interval_average.submit_sample(interval);\n\n            if interval_ratio > config.framerate_reset_threshold_multiplier\n                || interval_ratio < 1.0 / config.framerate_reset_threshold_multiplier\n            {\n                // Clear most of the samples, keep some for stability\n                self.frame_interval_average.retain(5);\n                self.update_needed = true;\n            }\n        }\n    }\n\n    pub fn report_frame_encoded(\n        &mut self,\n        timestamp: Duration,\n        encoder_latency: Duration,\n        size_bytes: usize,\n    ) {\n        self.encoder_latency_average.submit_sample(encoder_latency);\n\n        self.packet_bytes_history.push_back((timestamp, size_bytes));\n    }\n\n    // decoder_latency is used to learn a suitable maximum bitrate bound to avoid decoder runaway\n    // latency\n    pub fn report_frame_latencies(\n        &mut self,\n        config: &BitrateMode,\n        timestamp: Duration,\n        network_latency: Duration,\n        decoder_latency: Duration,\n    ) {\n        if network_latency.is_zero() {\n            return;\n        }\n\n        while let Some(&(history_timestamp, size_bytes)) = self.packet_bytes_history.front() {\n            if history_timestamp == timestamp {\n                self.packet_bytes_average.submit_sample(size_bytes as f32);\n                self.network_latency_average.submit_sample(network_latency);\n\n                self.packet_bytes_history.pop_front();\n\n                break;\n            } else {\n                self.packet_bytes_history.pop_front();\n            }\n        }\n\n        if let BitrateMode::Adaptive {\n            decoder_latency_limiter: Switch::Enabled(config),\n            ..\n        } = &config\n        {\n            if decoder_latency > Duration::from_millis(config.max_decoder_latency_ms) {\n                self.decoder_latency_overstep_count += 1;\n\n                if self.decoder_latency_overstep_count == config.latency_overstep_frames {\n                    self.dynamic_decoder_max_bytes_per_frame = f32::min(\n                        self.packet_bytes_average.get_average(),\n                        self.dynamic_decoder_max_bytes_per_frame,\n                    ) * config\n                        .latency_overstep_multiplier;\n\n                    self.update_needed = true;\n\n                    self.decoder_latency_overstep_count = 0;\n                }\n            } else {\n                self.decoder_latency_overstep_count = 0;\n            }\n        }\n    }\n\n    pub fn get_encoder_params(\n        &mut self,\n        config: &BitrateConfig,\n    ) -> Option<(DynamicEncoderParams, BitrateDirectives)> {\n        let now = Instant::now();\n\n        if self.previous_config.as_ref() != Some(config) {\n            self.previous_config = Some(config.clone());\n            // Continue method. Always update bitrate in this case\n        } else if !self.update_needed\n            && (now < self.last_update_instant + UPDATE_INTERVAL\n                || matches!(config.mode, BitrateMode::ConstantMbps(_)))\n        {\n            return None;\n        }\n\n        self.last_update_instant = now;\n        self.update_needed = false;\n\n        let frame_interval = if config.adapt_to_framerate.enabled() {\n            self.frame_interval_average.get_average()\n        } else {\n            self.nominal_frame_interval\n        };\n\n        let mut bitrate_directives = BitrateDirectives::default();\n\n        let bitrate_bps = match &config.mode {\n            BitrateMode::ConstantMbps(bitrate_mbps) => *bitrate_mbps as f32 * 1e6,\n            BitrateMode::Adaptive {\n                saturation_multiplier,\n                max_throughput_mbps,\n                min_throughput_mbps,\n                max_network_latency_ms,\n                encoder_latency_limiter,\n                decoder_latency_limiter,\n            } => {\n                let packet_bytes_average = self.packet_bytes_average.get_average();\n                let network_latency_average_s =\n                    self.network_latency_average.get_average().as_secs_f32();\n\n                let mut throughput_bps =\n                    packet_bytes_average * 8.0 * saturation_multiplier / network_latency_average_s;\n                bitrate_directives.scaled_calculated_throughput_bps = Some(throughput_bps);\n\n                if decoder_latency_limiter.enabled() {\n                    throughput_bps =\n                        f32::min(throughput_bps, self.dynamic_decoder_max_bytes_per_frame);\n                    bitrate_directives.decoder_latency_limiter_bps =\n                        Some(self.dynamic_decoder_max_bytes_per_frame);\n                }\n\n                if let Switch::Enabled(max_ms) = max_network_latency_ms {\n                    let max_bps =\n                        throughput_bps * (*max_ms as f32 / 1000.0) / network_latency_average_s;\n                    throughput_bps = f32::min(throughput_bps, max_bps);\n\n                    bitrate_directives.network_latency_limiter_bps = Some(max_bps);\n                }\n\n                if let Switch::Enabled(config) = encoder_latency_limiter {\n                    // Note: this assumes linear relationship between bitrate and encoder latency\n                    // but this may not be the case\n                    let saturation = self.encoder_latency_average.get_average().as_secs_f32()\n                        / self.nominal_frame_interval.as_secs_f32();\n                    let max_bps = throughput_bps * config.max_saturation_multiplier / saturation;\n                    bitrate_directives.encoder_latency_limiter_bps = Some(max_bps);\n\n                    if saturation > config.max_saturation_multiplier {\n                        throughput_bps = f32::min(throughput_bps, max_bps);\n                    }\n                }\n\n                if let Switch::Enabled(max) = max_throughput_mbps {\n                    let max_bps = *max as f32 * 1e6;\n                    throughput_bps = f32::min(throughput_bps, max_bps);\n\n                    bitrate_directives.manual_max_throughput_bps = Some(max_bps);\n                }\n                if let Switch::Enabled(min) = min_throughput_mbps {\n                    let min_bps = *min as f32 * 1e6;\n                    throughput_bps = f32::max(throughput_bps, min_bps);\n\n                    bitrate_directives.manual_min_throughput_bps = Some(min_bps);\n                }\n\n                // NB: Here we assign the calculated throughput to the requested bitrate. This is\n                // crucial for the working of the adaptive bitrate algorithm. The goal is to\n                // optimally occupy the available bandwidth, which is when the bitrate corresponds\n                // to the throughput.\n                throughput_bps\n            }\n        };\n\n        bitrate_directives.requested_bitrate_bps = bitrate_bps;\n\n        Some((\n            DynamicEncoderParams {\n                bitrate_bps,\n                framerate: 1.0 / f32::min(frame_interval.as_secs_f32(), 1.0),\n            },\n            bitrate_directives,\n        ))\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/c_api.rs",
    "content": "#![allow(dead_code, unused_variables)]\n#![allow(clippy::missing_safety_doc)]\n\nuse crate::{\n    SESSION_MANAGER, ServerCoreContext, ServerCoreEvent, logging_backend, tracking::HandType,\n};\nuse alvr_common::{\n    AlvrCodecType, AlvrPose, AlvrViewParams, log,\n    parking_lot::{Mutex, RwLock},\n};\nuse alvr_packets::{ButtonEntry, ButtonValue, Haptics};\nuse alvr_session::CodecType;\nuse std::{\n    collections::{HashMap, VecDeque},\n    ffi::{CStr, CString, c_char},\n    path::PathBuf,\n    ptr,\n    str::FromStr,\n    sync::{LazyLock, mpsc},\n    time::{Duration, Instant},\n};\n\nstatic SERVER_CORE_CONTEXT: RwLock<Option<ServerCoreContext>> = RwLock::new(None);\nstatic EVENTS_RECEIVER: Mutex<Option<mpsc::Receiver<ServerCoreEvent>>> = Mutex::new(None);\nstatic BUTTONS_QUEUE: Mutex<VecDeque<Vec<ButtonEntry>>> = Mutex::new(VecDeque::new());\n\n#[repr(C)]\npub struct AlvrDeviceMotion {\n    pub pose: AlvrPose,\n    pub linear_velocity: [f32; 3],\n    pub angular_velocity: [f32; 3],\n}\n\n#[repr(u8)]\npub enum AlvrHandType {\n    Left = 0,\n    Right = 1,\n}\n\n#[repr(C)]\npub union AlvrButtonValue {\n    pub scalar: bool,\n    pub float: f32,\n}\n\n// the profile is implied\n#[repr(C)]\npub struct AlvrButtonEntry {\n    pub id: u64,\n    pub value: AlvrButtonValue,\n}\n\n#[repr(C)]\npub struct AlvrBatteryInfo {\n    pub device_id: u64,\n    /// range [0, 1]\n    pub gauge_value: f32,\n    pub is_plugged: bool,\n}\n\n#[repr(u8)]\npub enum AlvrEvent {\n    ClientConnected,\n    ClientDisconnected,\n    Battery(AlvrBatteryInfo),\n    PlayspaceSync([f32; 2]),\n    LocalViewParams([AlvrViewParams; 2]), // In relation to head\n    TrackingUpdated { sample_timestamp_ns: u64 },\n    ButtonsUpdated,\n    RequestIDR,\n    CaptureFrame,\n    RestartPending,\n    ShutdownPending,\n    ProximityState(bool),\n}\n\n#[repr(C)]\npub struct AlvrTargetConfig {\n    game_render_width: u32,\n    game_render_height: u32,\n    stream_width: u32,\n    stream_height: u32,\n}\n\n#[repr(C)]\npub struct AlvrDeviceConfig {\n    device_id: u64,\n    interaction_profile_id: u64,\n}\n\n#[repr(C)]\npub struct AlvrDynamicEncoderParams {\n    bitrate_bps: f32,\n    framerate: f32,\n}\n\nfn string_to_c_str(buffer: *mut c_char, value: &str) -> u64 {\n    let cstring = CString::new(value).unwrap();\n    if !buffer.is_null() {\n        unsafe {\n            ptr::copy_nonoverlapping(cstring.as_ptr(), buffer, cstring.as_bytes_with_nul().len());\n        }\n    }\n\n    cstring.as_bytes_with_nul().len() as u64\n}\n\n// Get ALVR server time. The libalvr user should provide timestamps in the provided time frame of\n// reference in the following functions\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_time_ns() -> u64 {\n    Instant::now().elapsed().as_nanos() as u64\n}\n\n// The libalvr user is responsible of interpreting values and calling functions using\n// device/input/output identifiers obtained using this function\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_path_to_id(path_string: *const c_char) -> u64 {\n    alvr_common::hash_string(unsafe { CStr::from_ptr(path_string) }.to_str().unwrap())\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_error(string_ptr: *const c_char) {\n    alvr_common::show_e(unsafe { CStr::from_ptr(string_ptr) }.to_string_lossy());\n}\n\npub unsafe fn log(level: log::Level, string_ptr: *const c_char) {\n    log::log!(\n        level,\n        \"{}\",\n        unsafe { CStr::from_ptr(string_ptr) }.to_string_lossy()\n    );\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_warn(string_ptr: *const c_char) {\n    unsafe { log(log::Level::Warn, string_ptr) };\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_info(string_ptr: *const c_char) {\n    unsafe { log(log::Level::Info, string_ptr) };\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_dbg_server_impl(string_ptr: *const c_char) {\n    alvr_common::dbg_server_impl!(\n        \"{}\",\n        unsafe { CStr::from_ptr(string_ptr) }.to_string_lossy()\n    );\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_dbg_encoder(string_ptr: *const c_char) {\n    alvr_common::dbg_encoder!(\n        \"{}\",\n        unsafe { CStr::from_ptr(string_ptr) }.to_string_lossy()\n    );\n}\n\n// Should not be used in production\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_log_periodically(tag_ptr: *const c_char, message_ptr: *const c_char) {\n    const INTERVAL: Duration = Duration::from_secs(1);\n    static LASTEST_TAG_TIMESTAMPS: LazyLock<Mutex<HashMap<String, Instant>>> =\n        LazyLock::new(|| Mutex::new(HashMap::new()));\n\n    let tag = unsafe { CStr::from_ptr(tag_ptr) }.to_string_lossy();\n    let message = unsafe { CStr::from_ptr(message_ptr) }.to_string_lossy();\n\n    let mut timestamps_ref = LASTEST_TAG_TIMESTAMPS.lock();\n    let old_timestamp = timestamps_ref\n        .entry(tag.to_string())\n        .or_insert_with(Instant::now);\n    if *old_timestamp + INTERVAL < Instant::now() {\n        *old_timestamp += INTERVAL;\n\n        log::warn!(\"{}: {}\", tag, message);\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_get_settings_json(buffer: *mut c_char) -> u64 {\n    string_to_c_str(buffer, &serde_json::to_string(&crate::settings()).unwrap())\n}\n\n/// This must be called before alvr_initialize()\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_initialize_environment(\n    config_dir: *const c_char,\n    log_dir: *const c_char,\n) {\n    let config_dir =\n        PathBuf::from_str(unsafe { CStr::from_ptr(config_dir) }.to_str().unwrap()).unwrap();\n    let log_dir = PathBuf::from_str(unsafe { CStr::from_ptr(log_dir) }.to_str().unwrap()).unwrap();\n\n    crate::initialize_environment(alvr_filesystem::Layout {\n        config_dir,\n        log_dir,\n        ..Default::default()\n    });\n}\n\n/// Either session_log_path or crash_log_path can be null, in which case log is outputted to\n/// stdout/stderr on Windows.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_initialize_logging(\n    session_log_path: *const c_char,\n    crash_log_path: *const c_char,\n) {\n    let session_log_path = (!session_log_path.is_null()).then(|| {\n        PathBuf::from_str(\n            unsafe { CStr::from_ptr(session_log_path) }\n                .to_str()\n                .unwrap(),\n        )\n        .unwrap()\n    });\n    let crash_log_path = (!crash_log_path.is_null()).then(|| {\n        PathBuf::from_str(unsafe { CStr::from_ptr(crash_log_path) }.to_str().unwrap()).unwrap()\n    });\n\n    logging_backend::init_logging(session_log_path, crash_log_path);\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_initialize() -> AlvrTargetConfig {\n    let (context, receiver) = ServerCoreContext::new();\n    *SERVER_CORE_CONTEXT.write() = Some(context);\n    *EVENTS_RECEIVER.lock() = Some(receiver);\n\n    let session_manager_lock = SESSION_MANAGER.read();\n    let restart_settings = &session_manager_lock.session().openvr_config;\n\n    AlvrTargetConfig {\n        game_render_width: restart_settings.target_eye_resolution_width,\n        game_render_height: restart_settings.target_eye_resolution_height,\n        stream_width: restart_settings.eye_resolution_width,\n        stream_height: restart_settings.eye_resolution_height,\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_start_connection() {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.start_connection();\n    }\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_poll_event(out_event: *mut AlvrEvent, timeout_ns: u64) -> bool {\n    if let Some(receiver) = &*EVENTS_RECEIVER.lock()\n        && let Ok(event) = receiver.recv_timeout(Duration::from_nanos(timeout_ns))\n    {\n        match event {\n            ServerCoreEvent::ClientConnected => unsafe {\n                *out_event = AlvrEvent::ClientConnected;\n            },\n            ServerCoreEvent::ClientDisconnected => unsafe {\n                *out_event = AlvrEvent::ClientDisconnected;\n            },\n            ServerCoreEvent::Battery(battery) => unsafe {\n                *out_event = AlvrEvent::Battery(AlvrBatteryInfo {\n                    device_id: battery.device_id,\n                    gauge_value: battery.gauge_value,\n                    is_plugged: battery.is_plugged,\n                });\n            },\n            ServerCoreEvent::PlayspaceSync(bounds) => unsafe {\n                *out_event = AlvrEvent::PlayspaceSync(bounds.to_array())\n            },\n            ServerCoreEvent::LocalViewParams(config) => unsafe {\n                *out_event = AlvrEvent::LocalViewParams([\n                    alvr_common::to_capi_view_params(&config[0]),\n                    alvr_common::to_capi_view_params(&config[1]),\n                ])\n            },\n            ServerCoreEvent::Tracking { poll_timestamp } => unsafe {\n                *out_event = AlvrEvent::TrackingUpdated {\n                    sample_timestamp_ns: poll_timestamp.as_nanos() as u64,\n                };\n            },\n            ServerCoreEvent::Buttons(entries) => {\n                BUTTONS_QUEUE.lock().push_back(entries);\n                unsafe { *out_event = AlvrEvent::ButtonsUpdated };\n            }\n            ServerCoreEvent::RequestIDR => unsafe { *out_event = AlvrEvent::RequestIDR },\n            ServerCoreEvent::CaptureFrame => unsafe { *out_event = AlvrEvent::CaptureFrame },\n            ServerCoreEvent::RestartPending => unsafe {\n                *out_event = AlvrEvent::RestartPending;\n            },\n            ServerCoreEvent::ShutdownPending => unsafe {\n                *out_event = AlvrEvent::ShutdownPending;\n            },\n            ServerCoreEvent::GameRenderLatencyFeedback(_)\n            | ServerCoreEvent::SetOpenvrProperty { .. } => {} // implementation not needed\n            ServerCoreEvent::ProximityState(headset_is_worn) => unsafe {\n                *out_event = AlvrEvent::ProximityState(headset_is_worn);\n            },\n        }\n\n        true\n    } else {\n        false\n    }\n}\n\n/// Returns false if there is no tracking sample for the requested sample timestamp\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_get_device_motion(\n    device_id: u64,\n    sample_timestamp_ns: u64,\n    out_motion: *mut AlvrDeviceMotion,\n) -> bool {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read()\n        && let Some(motion) =\n            context.get_device_motion(device_id, Duration::from_nanos(sample_timestamp_ns))\n    {\n        unsafe {\n            *out_motion = AlvrDeviceMotion {\n                pose: alvr_common::to_capi_pose(&motion.pose),\n                linear_velocity: motion.linear_velocity.to_array(),\n                angular_velocity: motion.angular_velocity.to_array(),\n            };\n        }\n\n        true\n    } else {\n        false\n    }\n}\n\n/// out_skeleton must be an array of length 26\n/// Returns false if there is no tracking sample for the requested sample timestamp\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_get_hand_skeleton(\n    hand_type: AlvrHandType,\n    sample_timestamp_ns: u64,\n    out_skeleton: *mut AlvrPose,\n) -> bool {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read()\n        && let Some(skeleton) = context.get_hand_skeleton(\n            match hand_type {\n                AlvrHandType::Left => HandType::Left,\n                AlvrHandType::Right => HandType::Right,\n            },\n            Duration::from_nanos(sample_timestamp_ns),\n        )\n    {\n        for (i, joint_pose) in skeleton.iter().enumerate() {\n            unsafe { *out_skeleton.add(i) = alvr_common::to_capi_pose(joint_pose) };\n        }\n\n        true\n    } else {\n        false\n    }\n}\n\n/// Call with null out_entries to get the buffer length\n/// call with non-null out_entries to get the buttons and advanced the internal queue\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_get_buttons(out_entries: *mut AlvrButtonEntry) -> u64 {\n    let entries_count = BUTTONS_QUEUE.lock().front().map_or(0, |e| e.len()) as u64;\n\n    if out_entries.is_null() {\n        return entries_count;\n    }\n\n    if let Some(button_entries) = BUTTONS_QUEUE.lock().pop_front() {\n        for (i, entry) in button_entries.into_iter().enumerate() {\n            let out_entry = unsafe { &mut *out_entries.add(i) };\n            out_entry.id = entry.path_id;\n            match entry.value {\n                ButtonValue::Binary(value) => out_entry.value.scalar = value,\n                ButtonValue::Scalar(value) => out_entry.value.float = value,\n            }\n        }\n\n        entries_count\n    } else {\n        0\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_send_haptics(\n    device_id: u64,\n    duration_s: f32,\n    frequency: f32,\n    amplitude: f32,\n) {\n    if let Ok(duration) = Duration::try_from_secs_f32(duration_s)\n        && let Some(context) = &*SERVER_CORE_CONTEXT.read()\n    {\n        context.send_haptics(Haptics {\n            device_id,\n            duration,\n            frequency,\n            amplitude,\n        });\n    }\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_set_video_config_nals(\n    codec: AlvrCodecType,\n    buffer_ptr: *const u8,\n    len: i32,\n) {\n    let codec = match codec {\n        AlvrCodecType::H264 => CodecType::H264,\n        AlvrCodecType::Hevc => CodecType::Hevc,\n        AlvrCodecType::AV1 => CodecType::AV1,\n    };\n\n    let mut config_buffer = vec![0; len as usize];\n\n    unsafe { ptr::copy_nonoverlapping(buffer_ptr, config_buffer.as_mut_ptr(), len as usize) };\n\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.set_video_config_nals(config_buffer, codec);\n    }\n}\n\n/// global_view_params must be an array of length 2\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_send_video_nal(\n    timestamp_ns: u64,\n    global_view_params: *const AlvrViewParams,\n    is_idr: bool,\n    buffer_ptr: *mut u8,\n    len: i32,\n) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, len as usize) };\n\n        let global_view_params = unsafe {\n            [\n                alvr_common::from_capi_view_params(&(*global_view_params)),\n                alvr_common::from_capi_view_params(&(*global_view_params.add(1))),\n            ]\n        };\n\n        context.send_video_nal(\n            Duration::from_nanos(timestamp_ns),\n            global_view_params,\n            is_idr,\n            buffer.to_vec(),\n        );\n    }\n}\n\n/// Returns true if updated\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_get_dynamic_encoder_params(\n    out_params: *mut AlvrDynamicEncoderParams,\n) -> bool {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read()\n        && let Some(params) = context.get_dynamic_encoder_params()\n    {\n        unsafe {\n            (*out_params).bitrate_bps = params.bitrate_bps;\n            (*out_params).framerate = params.framerate;\n        }\n\n        true\n    } else {\n        false\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_composed(timestamp_ns: u64, offset_ns: u64) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.report_composed(\n            Duration::from_nanos(timestamp_ns),\n            Duration::from_nanos(offset_ns),\n        );\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_report_present(timestamp_ns: u64, offset_ns: u64) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.report_present(\n            Duration::from_nanos(timestamp_ns),\n            Duration::from_nanos(offset_ns),\n        );\n    }\n}\n\n/// Retr  un true if a valid value is provided\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn alvr_duration_until_next_vsync(out_ns: *mut u64) -> bool {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read()\n        && let Some(duration) = context.duration_until_next_vsync()\n    {\n        unsafe { *out_ns = duration.as_nanos() as u64 };\n\n        true\n    } else {\n        false\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_restart() {\n    if let Some(context) = SERVER_CORE_CONTEXT.write().take() {\n        context.restart();\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn alvr_shutdown() {\n    SERVER_CORE_CONTEXT.write().take();\n}\n"
  },
  {
    "path": "alvr/server_core/src/connection.rs",
    "content": "use crate::{\n    ConnectionContext, FILESYSTEM_LAYOUT, SESSION_MANAGER, ServerCoreEvent,\n    bitrate::BitrateManager,\n    hand_gestures::HandGestureManager,\n    input_mapping::ButtonMappingManager,\n    sockets::WelcomeSocket,\n    statistics::StatisticsManager,\n    tracking::{self, TrackingManager},\n};\nuse alvr_adb::{WiredConnection, WiredConnectionStatus};\nuse alvr_common::{\n    AnyhowToCon, BUTTON_INFO, CONTROLLER_PROFILE_INFO, ConResult, ConnectionError, ConnectionState,\n    LifecycleState, QUEST_CONTROLLER_PROFILE_PATH, con_bail, dbg_connection, debug, error,\n    glam::{UVec2, Vec2},\n    info,\n    parking_lot::{Condvar, Mutex, RwLock},\n    settings_schema::Switch,\n    warn,\n};\nuse alvr_events::{AdbEvent, ButtonEvent, EventType};\nuse alvr_packets::{\n    AUDIO, ClientConnectionResult, ClientConnectionsAction, ClientControlPacket, ClientStatistics,\n    HAPTICS, NegotiatedStreamingConfig, NegotiatedStreamingConfigExt, RealTimeConfig, STATISTICS,\n    ServerControlPacket, StreamConfigPacket, TRACKING, TrackingData, VIDEO, VideoPacketHeader,\n};\nuse alvr_session::{\n    BodyTrackingSinkConfig, CodecType, ControllersEmulationMode, FrameSize, H264Profile,\n    OpenvrConfig, SessionConfig, SocketProtocol,\n};\nuse alvr_sockets::{\n    CONTROL_PORT, KEEPALIVE_INTERVAL, KEEPALIVE_TIMEOUT, PeerType, ProtoControlSocket,\n    StreamSocketBuilder, WIRED_CLIENT_HOSTNAME,\n};\nuse std::{\n    collections::HashMap,\n    net::{IpAddr, Ipv4Addr},\n    process::Command,\n    sync::{Arc, mpsc::RecvTimeoutError},\n    thread,\n    time::{Duration, Instant},\n};\n\nconst RETRY_CONNECT_MIN_INTERVAL: Duration = Duration::from_secs(1);\nconst HANDSHAKE_ACTION_TIMEOUT: Duration = Duration::from_secs(2);\npub const STREAMING_RECV_TIMEOUT: Duration = Duration::from_millis(500);\nconst REAL_TIME_UPDATE_INTERVAL: Duration = Duration::from_secs(1);\n\nconst MAX_UNREAD_PACKETS: usize = 10; // Applies per stream\n\npub struct VideoPacket {\n    pub header: VideoPacketHeader,\n    pub payload: Vec<u8>,\n}\n\nfn align32(value: f32) -> u32 {\n    ((value / 32.).floor() * 32.) as u32\n}\n\nfn is_streaming(client_hostname: &str) -> bool {\n    SESSION_MANAGER\n        .read()\n        .client_list()\n        .get(client_hostname)\n        .is_some_and(|c| c.connection_state == ConnectionState::Streaming)\n}\n\npub fn contruct_openvr_config(session: &SessionConfig) -> OpenvrConfig {\n    let old_config = session.openvr_config.clone();\n    let settings = session.to_settings();\n\n    let mut controller_is_tracker = false;\n    let mut controller_profile = 0;\n    let mut use_separate_hand_trackers = false;\n    let controllers_enabled = if let Switch::Enabled(config) = &settings.headset.controllers {\n        controller_is_tracker =\n            matches!(config.emulation_mode, ControllersEmulationMode::ViveTracker);\n        // These numbers don't mean anything, they're just for triggering SteamVR resets.\n        // Gaps are included in the numbering to make adding other controllers\n        // a bit easier though.\n        controller_profile = match config.emulation_mode {\n            ControllersEmulationMode::RiftSTouch => 0,\n            ControllersEmulationMode::Quest1Touch => 1,\n            ControllersEmulationMode::Quest2Touch => 2,\n            ControllersEmulationMode::Quest3Plus => 3,\n            ControllersEmulationMode::QuestPro => 4,\n            ControllersEmulationMode::Pico4 => 10,\n            ControllersEmulationMode::ValveIndex => 20,\n            ControllersEmulationMode::ViveWand => 40,\n            ControllersEmulationMode::ViveTracker => 41,\n            ControllersEmulationMode::PSVR2Sense => 60,\n            ControllersEmulationMode::Custom { .. } => 500,\n        };\n        use_separate_hand_trackers = config\n            .hand_skeleton\n            .as_option()\n            .is_some_and(|c| c.steamvr_input_2_0);\n\n        true\n    } else {\n        false\n    };\n\n    let body_tracking_vive_enabled =\n        if let Switch::Enabled(config) = &settings.headset.body_tracking {\n            matches!(config.sink, BodyTrackingSinkConfig::FakeViveTracker)\n        } else if let Switch::Enabled(config) = settings.headset.multimodal_tracking {\n            config.detached_controllers_steamvr_sink\n        } else {\n            false\n        };\n\n    // Should be true if using full body tracking\n    let body_tracking_has_legs = settings\n        .headset\n        .body_tracking\n        .as_option()\n        .map(|c| c.sources.meta.prefer_full_body)\n        .unwrap_or(false);\n\n    let mut foveation_center_size_x = 0.0;\n    let mut foveation_center_size_y = 0.0;\n    let mut foveation_center_shift_x = 0.0;\n    let mut foveation_center_shift_y = 0.0;\n    let mut foveation_edge_ratio_x = 0.0;\n    let mut foveation_edge_ratio_y = 0.0;\n    let enable_foveated_encoding = if let Switch::Enabled(config) = settings.video.foveated_encoding\n    {\n        foveation_center_size_x = config.center_size_x;\n        foveation_center_size_y = config.center_size_y;\n        foveation_center_shift_x = config.center_shift_x;\n        foveation_center_shift_y = config.center_shift_y;\n        foveation_edge_ratio_x = config.edge_ratio_x;\n        foveation_edge_ratio_y = config.edge_ratio_y;\n\n        true\n    } else {\n        false\n    };\n\n    let mut brightness = 0.0;\n    let mut contrast = 0.0;\n    let mut saturation = 0.0;\n    let mut gamma = 0.0;\n    let mut sharpening = 0.0;\n    let enable_color_correction = if let Switch::Enabled(config) = settings.video.color_correction {\n        brightness = config.brightness;\n        contrast = config.contrast;\n        saturation = config.saturation;\n        gamma = config.gamma;\n        sharpening = config.sharpening;\n        true\n    } else {\n        false\n    };\n\n    let nvenc_overrides = settings.video.encoder_config.nvenc;\n    let amf_controls = settings.video.encoder_config.amf;\n    let hdr_controls = settings.video.encoder_config.hdr;\n\n    OpenvrConfig {\n        tracking_ref_only: settings.headset.tracking_ref_only,\n        enable_vive_tracker_proxy: settings.headset.enable_vive_tracker_proxy,\n        minimum_idr_interval_ms: settings.connection.minimum_idr_interval_ms,\n        adapter_index: settings.video.adapter_index,\n        codec: settings.video.preferred_codec as _,\n        h264_profile: settings.video.encoder_config.h264_profile as u32,\n        rate_control_mode: settings.video.encoder_config.rate_control_mode as u32,\n        filler_data: settings.video.encoder_config.filler_data,\n        entropy_coding: settings.video.encoder_config.entropy_coding as u32,\n        force_hdr_srgb_correction: hdr_controls.force_hdr_srgb_correction,\n        clamp_hdr_extended_range: hdr_controls.clamp_hdr_extended_range,\n        enable_amf_pre_analysis: amf_controls.enable_pre_analysis,\n        enable_vbaq: settings.video.encoder_config.enable_vbaq,\n        enable_amf_hmqb: amf_controls.enable_hmqb,\n        use_amf_preproc: amf_controls.use_preproc,\n        amf_preproc_sigma: amf_controls.preproc_sigma,\n        amf_preproc_tor: amf_controls.preproc_tor,\n        nvenc_quality_preset: nvenc_overrides.quality_preset as u32,\n        encoder_quality_preset: settings.video.encoder_config.quality_preset as u32,\n        force_sw_encoding: settings\n            .video\n            .encoder_config\n            .software\n            .force_software_encoding,\n        sw_thread_count: settings.video.encoder_config.software.thread_count,\n        controllers_enabled,\n        controller_is_tracker,\n        body_tracking_vive_enabled,\n        body_tracking_has_legs,\n        enable_foveated_encoding,\n        foveation_center_size_x,\n        foveation_center_size_y,\n        foveation_center_shift_x,\n        foveation_center_shift_y,\n        foveation_edge_ratio_x,\n        foveation_edge_ratio_y,\n        enable_color_correction,\n        brightness,\n        contrast,\n        saturation,\n        gamma,\n        sharpening,\n        linux_async_compute: settings.extra.patches.linux_async_compute,\n        linux_async_reprojection: settings.extra.patches.linux_async_reprojection,\n        nvenc_tuning_preset: nvenc_overrides.tuning_preset as u32,\n        nvenc_multi_pass: nvenc_overrides.multi_pass as u32,\n        nvenc_adaptive_quantization_mode: nvenc_overrides.adaptive_quantization_mode as u32,\n        nvenc_low_delay_key_frame_scale: nvenc_overrides.low_delay_key_frame_scale,\n        nvenc_refresh_rate: nvenc_overrides.refresh_rate,\n        enable_intra_refresh: nvenc_overrides.enable_intra_refresh,\n        intra_refresh_period: nvenc_overrides.intra_refresh_period,\n        intra_refresh_count: nvenc_overrides.intra_refresh_count,\n        max_num_ref_frames: nvenc_overrides.max_num_ref_frames,\n        gop_length: nvenc_overrides.gop_length,\n        p_frame_strategy: nvenc_overrides.p_frame_strategy,\n        nvenc_rate_control_mode: nvenc_overrides.rate_control_mode,\n        rc_buffer_size: nvenc_overrides.rc_buffer_size,\n        rc_initial_delay: nvenc_overrides.rc_initial_delay,\n        rc_max_bitrate: nvenc_overrides.rc_max_bitrate,\n        rc_average_bitrate: nvenc_overrides.rc_average_bitrate,\n        nvenc_enable_weighted_prediction: nvenc_overrides.enable_weighted_prediction,\n        capture_frame_dir: settings.extra.capture.capture_frame_dir,\n        amd_bitrate_corruption_fix: settings.video.bitrate.image_corruption_fix,\n        use_separate_hand_trackers,\n        _controller_profile: controller_profile,\n        _server_impl_debug: settings.extra.logging.debug_groups.server_impl,\n        _client_impl_debug: settings.extra.logging.debug_groups.client_impl,\n        _server_core_debug: settings.extra.logging.debug_groups.server_core,\n        _client_core_debug: settings.extra.logging.debug_groups.client_core,\n        _connection_debug: settings.extra.logging.debug_groups.connection,\n        _sockets_debug: settings.extra.logging.debug_groups.sockets,\n        _server_gfx_debug: settings.extra.logging.debug_groups.server_gfx,\n        _client_gfx_debug: settings.extra.logging.debug_groups.client_gfx,\n        _encoder_debug: settings.extra.logging.debug_groups.encoder,\n        _decoder_debug: settings.extra.logging.debug_groups.decoder,\n        ..old_config\n    }\n}\n\n// Alternate connection trials with manual IPs and clients discovered on the local network\npub fn handshake_loop(ctx: Arc<ConnectionContext>, lifecycle_state: Arc<RwLock<LifecycleState>>) {\n    dbg_connection!(\"handshake_loop: Begin\");\n\n    let welcome_socket = match WelcomeSocket::new() {\n        Ok(socket) => socket,\n        Err(e) => {\n            error!(\"Failed to create discovery socket: {e:?}\");\n            return;\n        }\n    };\n\n    let mut wired_connection = None;\n\n    while *lifecycle_state.read() != LifecycleState::ShuttingDown {\n        dbg_connection!(\"handshake_loop: Try connect to wired device\");\n\n        let mut wired_client_ips = HashMap::new();\n        if SESSION_MANAGER\n            .read()\n            .client_list()\n            .iter()\n            .any(|(hostname, info)| {\n                info.connection_state == ConnectionState::Disconnected\n                    && hostname.as_str() == WIRED_CLIENT_HOSTNAME\n            })\n        {\n            // Make sure the wired connection is created once and kept alive\n            let wired_connection = if let Some(connection) = &wired_connection {\n                connection\n            } else {\n                let connection = match WiredConnection::new(\n                    FILESYSTEM_LAYOUT.get().unwrap(),\n                    |downloaded, maybe_total| {\n                        if let Some(total) = maybe_total {\n                            alvr_events::send_event(EventType::Adb(AdbEvent {\n                                download_progress: downloaded as f32 / total as f32,\n                            }));\n                        };\n                    },\n                ) {\n                    Ok(connection) => connection,\n                    Err(e) => {\n                        error!(\"{e:?}\");\n                        thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                        continue;\n                    }\n                };\n\n                wired_connection = Some(connection);\n\n                wired_connection.as_ref().unwrap()\n            };\n\n            let stream_port;\n            let client_type;\n            let client_autolaunch;\n            {\n                let session_manager_lock = SESSION_MANAGER.read();\n                let connection = &session_manager_lock.settings().connection;\n                stream_port = connection.stream_port;\n                client_type = connection.wired_client_type.clone();\n                client_autolaunch = connection.wired_client_autolaunch.as_option().cloned();\n            }\n\n            let status = match wired_connection.setup(\n                CONTROL_PORT,\n                stream_port,\n                &client_type,\n                client_autolaunch,\n            ) {\n                Ok(status) => status,\n                Err(e) => {\n                    error!(\"{e:?}\");\n                    thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                    continue;\n                }\n            };\n\n            #[cfg_attr(not(debug_assertions), expect(unused_variables))]\n            if let WiredConnectionStatus::NotReady(s) = status {\n                dbg_connection!(\"handshake_loop: Wired connection not ready: {s}\");\n                thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                continue;\n            }\n\n            let client_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);\n            wired_client_ips.insert(client_ip, WIRED_CLIENT_HOSTNAME.to_owned());\n        }\n\n        if !wired_client_ips.is_empty()\n            && try_connect(\n                Arc::clone(&ctx),\n                Arc::clone(&lifecycle_state),\n                wired_client_ips,\n            )\n            .is_ok()\n        {\n            thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n            continue;\n        }\n\n        dbg_connection!(\"handshake_loop: Try connect to manual IPs\");\n\n        let available_manual_client_ips = {\n            let mut manual_client_ips = HashMap::new();\n            for (hostname, connection_info) in\n                SESSION_MANAGER\n                    .read()\n                    .client_list()\n                    .iter()\n                    .filter(|(hostname, info)| {\n                        info.connection_state == ConnectionState::Disconnected\n                            && hostname.as_str() != WIRED_CLIENT_HOSTNAME\n                    })\n            {\n                for ip in &connection_info.manual_ips {\n                    manual_client_ips.insert(*ip, hostname.clone());\n                }\n            }\n            manual_client_ips\n        };\n\n        if !available_manual_client_ips.is_empty()\n            && try_connect(\n                Arc::clone(&ctx),\n                Arc::clone(&lifecycle_state),\n                available_manual_client_ips,\n            )\n            .is_ok()\n        {\n            thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n            continue;\n        }\n\n        let discovery_config = SESSION_MANAGER\n            .read()\n            .settings()\n            .connection\n            .client_discovery\n            .clone();\n        if let Switch::Enabled(config) = discovery_config {\n            dbg_connection!(\"handshake_loop: Discovering clients\");\n\n            let clients = match welcome_socket.recv_all() {\n                Ok(clients) => clients,\n                Err(e) => {\n                    warn!(\"mDNS listening error: {e:?}\");\n\n                    thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                    continue;\n                }\n            };\n\n            if clients.is_empty() {\n                thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                continue;\n            }\n\n            for (client_hostname, client_ip) in clients {\n                let trusted = {\n                    let mut session_manager = SESSION_MANAGER.write();\n\n                    session_manager.update_client_connections(\n                        client_hostname.clone(),\n                        ClientConnectionsAction::AddIfMissing {\n                            trusted: false,\n                            manual_ips: vec![],\n                        },\n                    );\n\n                    if config.auto_trust_clients {\n                        session_manager.update_client_connections(\n                            client_hostname.clone(),\n                            ClientConnectionsAction::Trust,\n                        );\n                    }\n\n                    session_manager\n                        .client_list()\n                        .get(&client_hostname)\n                        .is_some_and(|c| c.trusted)\n                };\n\n                // do not attempt connection if the client is already connected\n                if trusted\n                    && SESSION_MANAGER\n                        .read()\n                        .client_list()\n                        .get(&client_hostname)\n                        .is_some_and(|c| c.connection_state == ConnectionState::Disconnected)\n                    && let Err(e) = try_connect(\n                        Arc::clone(&ctx),\n                        Arc::clone(&lifecycle_state),\n                        [(client_ip, client_hostname.clone())].into_iter().collect(),\n                    )\n                {\n                    error!(\"Could not initiate connection for {client_hostname}: {e}\");\n                }\n\n                thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n            }\n        } else {\n            thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n        }\n    }\n\n    alvr_common::dbg_connection!(\"handshake_loop: Joining connection threads\");\n\n    // At this point, LIFECYCLE_STATE == ShuttingDown, so all threads are already terminating\n    for thread in ctx.connection_threads.lock().drain(..) {\n        thread.join().ok();\n    }\n\n    alvr_common::dbg_connection!(\"handshake_loop: End\");\n}\n\nfn try_connect(\n    ctx: Arc<ConnectionContext>,\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    mut client_ips: HashMap<IpAddr, String>,\n) -> ConResult {\n    dbg_connection!(\"try_connect: Finding client and creating control socket\");\n\n    let (proto_socket, client_ip) = ProtoControlSocket::connect_to(\n        Duration::from_secs(1),\n        PeerType::AnyClient(client_ips.keys().cloned().collect()),\n    )?;\n\n    let Some(client_hostname) = client_ips.remove(&client_ip) else {\n        con_bail!(\"unreachable\");\n    };\n\n    dbg_connection!(\"try_connect: Pushing new client connection thread\");\n\n    ctx.connection_threads.lock().push(thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        move || {\n            if let Err(e) = connection_pipeline(\n                Arc::clone(&ctx),\n                lifecycle_state,\n                proto_socket,\n                client_hostname.clone(),\n                client_ip,\n            ) {\n                error!(\"Handshake error for {client_hostname}: {e}\");\n            }\n\n            let mut clients_to_be_removed = ctx.clients_to_be_removed.lock();\n\n            let action = if clients_to_be_removed.contains(&client_hostname) {\n                clients_to_be_removed.remove(&client_hostname);\n\n                ClientConnectionsAction::RemoveEntry\n            } else {\n                ClientConnectionsAction::SetConnectionState(ConnectionState::Disconnected)\n            };\n            SESSION_MANAGER\n                .write()\n                .update_client_connections(client_hostname, action);\n        }\n    }));\n\n    Ok(())\n}\n\nfn connection_pipeline(\n    ctx: Arc<ConnectionContext>,\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    mut proto_socket: ProtoControlSocket,\n    client_hostname: String,\n    client_ip: IpAddr,\n) -> ConResult {\n    dbg_connection!(\"connection_pipeline: Begin\");\n\n    // This session lock will make sure settings and client list cannot be changed while connecting\n    // to thos client, no other client can connect until handshake is finished. It will then be\n    // temporarily relocked while shutting down the threads.\n    let mut session_manager_lock = SESSION_MANAGER.write();\n\n    dbg_connection!(\"connection_pipeline: Setting client state in session\");\n    session_manager_lock.update_client_connections(\n        client_hostname.clone(),\n        ClientConnectionsAction::SetConnectionState(ConnectionState::Connecting),\n    );\n    session_manager_lock.update_client_connections(\n        client_hostname.clone(),\n        ClientConnectionsAction::UpdateCurrentIp(Some(client_ip)),\n    );\n\n    let disconnect_notif = Arc::new(Condvar::new());\n\n    dbg_connection!(\"connection_pipeline: Getting client status packet\");\n    let connection_result = match proto_socket.recv(HANDSHAKE_ACTION_TIMEOUT) {\n        Ok(r) => r,\n        Err(ConnectionError::TryAgain(e)) => {\n            debug!(\n                \"Failed to recive client connection packet. This is normal for USB connection.\\n{e}\"\n            );\n\n            return Ok(());\n        }\n        Err(e) => return Err(e),\n    };\n\n    let maybe_streaming_caps =\n        if let ClientConnectionResult::ConnectionAccepted(info) = connection_result {\n            session_manager_lock.update_client_connections(\n                client_hostname.clone(),\n                ClientConnectionsAction::SetDisplayName(info.platform_string),\n            );\n\n            if info.client_protocol_id != alvr_common::protocol_id_u64() {\n                warn!(\n                    \"Trusted client is incompatible! Expected protocol ID: {}, found: {}\",\n                    alvr_common::protocol_id_u64(),\n                    info.client_protocol_id,\n                );\n\n                return Ok(());\n            }\n\n            info.streaming_capabilities\n        } else {\n            debug!(\"Found client in standby. Retrying\");\n            return Ok(());\n        };\n\n    let Some(streaming_caps) = maybe_streaming_caps else {\n        con_bail!(\"Only streaming clients are supported for now\");\n    };\n\n    dbg_connection!(\"connection_pipeline: setting up negotiated streaming config\");\n\n    let initial_settings = session_manager_lock.settings().clone();\n\n    fn get_view_res(config: FrameSize, default_res: UVec2) -> UVec2 {\n        let res = match config {\n            FrameSize::Scale(scale) => default_res.as_vec2() * scale,\n            FrameSize::Absolute { width, height } => {\n                let width = width as f32;\n                Vec2::new(\n                    width,\n                    height.map_or_else(\n                        || {\n                            let default_res = default_res.as_vec2();\n                            width * default_res.y / default_res.x\n                        },\n                        |h| h as f32,\n                    ),\n                )\n            }\n        };\n\n        UVec2::new(align32(res.x), align32(res.y))\n    }\n\n    let mut transcoding_view_resolution = get_view_res(\n        initial_settings.video.transcoding_view_resolution.clone(),\n        streaming_caps.default_view_resolution,\n    );\n    if transcoding_view_resolution.x > streaming_caps.max_view_resolution.x\n        || transcoding_view_resolution.y > streaming_caps.max_view_resolution.y\n    {\n        warn!(\n            \"Chosen resolution {}x{} exceeds client maximum supported resolution of {}x{}. \\\n            Using maximum supported resolution at same aspect ratio.\",\n            transcoding_view_resolution.x,\n            transcoding_view_resolution.y,\n            streaming_caps.max_view_resolution.x,\n            streaming_caps.max_view_resolution.y,\n        );\n\n        let transcoding_ratio =\n            transcoding_view_resolution.x as f32 / transcoding_view_resolution.y as f32;\n\n        if transcoding_ratio\n            > streaming_caps.max_view_resolution.x as f32\n                / streaming_caps.max_view_resolution.y as f32\n        {\n            transcoding_view_resolution = UVec2::new(\n                align32(streaming_caps.max_view_resolution.x as f32),\n                align32(streaming_caps.max_view_resolution.x as f32 / transcoding_ratio),\n            );\n        } else {\n            transcoding_view_resolution = UVec2::new(\n                align32(streaming_caps.max_view_resolution.y as f32 * transcoding_ratio),\n                align32(streaming_caps.max_view_resolution.y as f32),\n            );\n        }\n    }\n\n    let emulated_headset_view_resolution = get_view_res(\n        initial_settings\n            .video\n            .emulated_headset_view_resolution\n            .clone(),\n        streaming_caps.default_view_resolution,\n    );\n\n    let fps = {\n        let mut best_match = 0_f32;\n        let mut min_diff = f32::MAX;\n        for rate in &streaming_caps.refresh_rates {\n            let diff = (*rate - initial_settings.video.preferred_fps).abs();\n            if diff < min_diff {\n                best_match = *rate;\n                min_diff = diff;\n            }\n        }\n        best_match\n    };\n\n    if !streaming_caps\n        .refresh_rates\n        .contains(&initial_settings.video.preferred_fps)\n    {\n        warn!(\"Chosen refresh rate not supported. Using {fps}Hz\");\n    }\n\n    let enable_foveated_encoding =\n        if let Switch::Enabled(config) = &initial_settings.video.foveated_encoding {\n            let enable = streaming_caps.foveated_encoding || config.force_enable;\n\n            if !enable {\n                warn!(\"Foveated encoding is not supported by the client.\");\n            }\n\n            enable\n        } else {\n            false\n        };\n\n    let encoder_profile = if initial_settings.video.encoder_config.h264_profile == H264Profile::High\n    {\n        let profile = if streaming_caps.encoder_high_profile {\n            H264Profile::High\n        } else {\n            H264Profile::Main\n        };\n\n        if profile != H264Profile::High {\n            warn!(\"High profile encoding is not supported by the client.\");\n        }\n\n        profile\n    } else {\n        initial_settings.video.encoder_config.h264_profile\n    };\n\n    let mut enable_10_bits_encoding = initial_settings\n        .video\n        .encoder_config\n        .use_10bit\n        .unwrap_or(streaming_caps.prefer_10bit);\n\n    if enable_10_bits_encoding && !streaming_caps.encoder_10_bits {\n        warn!(\"10 bits encoding is not supported by the client.\");\n        enable_10_bits_encoding = false\n    }\n\n    let enable_hdr = initial_settings\n        .video\n        .encoder_config\n        .hdr\n        .enable\n        .unwrap_or(streaming_caps.prefer_hdr);\n\n    let encoding_gamma = initial_settings\n        .video\n        .encoder_config\n        .encoding_gamma\n        .unwrap_or(streaming_caps.preferred_encoding_gamma);\n\n    let codec = if initial_settings.video.preferred_codec == CodecType::AV1 {\n        let codec = if streaming_caps.encoder_av1 {\n            CodecType::AV1\n        } else {\n            CodecType::Hevc\n        };\n\n        if codec != CodecType::AV1 {\n            warn!(\"AV1 encoding is not supported by the client.\");\n        }\n\n        codec\n    } else {\n        initial_settings.video.preferred_codec\n    };\n\n    #[cfg(not(target_os = \"windows\"))]\n    let game_audio_sample_rate = 44100;\n\n    #[cfg(target_os = \"windows\")]\n    let game_audio_sample_rate =\n        if let Switch::Enabled(game_audio_config) = &initial_settings.audio.game_audio {\n            let game_audio_device =\n                alvr_audio::new_output(game_audio_config.device.as_ref()).to_con()?;\n\n            if let Switch::Enabled(microphone_config) = &initial_settings.audio.microphone\n                && matches!(\n                    microphone_config.devices,\n                    alvr_session::MicrophoneDevicesConfig::VAC\n                        | alvr_session::MicrophoneDevicesConfig::VBCable\n                )\n            {\n                let (sink, _) =\n                    alvr_audio::new_virtual_microphone_pair(microphone_config.devices.clone())\n                        .to_con()?;\n\n                // VoiceMeeter and Custom devices may have arbitrary internal routing.\n                // Therefore, we cannot detect the loopback issue without knowing the routing.\n                if alvr_audio::is_same_device(&game_audio_device, &sink) {\n                    con_bail!(\"Game audio and microphone cannot point to the same device!\");\n                }\n            }\n\n            alvr_audio::input_sample_rate(&game_audio_device).to_con()?\n        } else {\n            0\n        };\n\n    let wired = client_ip.is_loopback();\n\n    dbg_connection!(\"connection_pipeline: send streaming config\");\n    let stream_config_packet = StreamConfigPacket::new(\n        session_manager_lock.session(),\n        NegotiatedStreamingConfig {\n            view_resolution: transcoding_view_resolution,\n            refresh_rate_hint: fps,\n            game_audio_sample_rate,\n            enable_foveated_encoding,\n            encoding_gamma,\n            enable_hdr,\n            wired,\n            ext_str: String::new(),\n        }\n        .with_ext(NegotiatedStreamingConfigExt {}),\n    )\n    .to_con()?;\n    proto_socket.send(&stream_config_packet).to_con()?;\n\n    let (mut control_sender, mut control_receiver) =\n        proto_socket.split(STREAMING_RECV_TIMEOUT).to_con()?;\n\n    let mut new_openvr_config = contruct_openvr_config(session_manager_lock.session());\n    new_openvr_config.eye_resolution_width = transcoding_view_resolution.x;\n    new_openvr_config.eye_resolution_height = transcoding_view_resolution.y;\n    new_openvr_config.target_eye_resolution_width = emulated_headset_view_resolution.x;\n    new_openvr_config.target_eye_resolution_height = emulated_headset_view_resolution.y;\n    new_openvr_config.refresh_rate = fps as _;\n    new_openvr_config.enable_foveated_encoding = enable_foveated_encoding;\n    new_openvr_config.h264_profile = encoder_profile as _;\n    new_openvr_config.use_10bit_encoder = enable_10_bits_encoding;\n    new_openvr_config.enable_hdr = enable_hdr;\n    new_openvr_config.encoding_gamma = encoding_gamma;\n    new_openvr_config.codec = codec as _;\n\n    if session_manager_lock.session().openvr_config != new_openvr_config {\n        session_manager_lock.session_mut().openvr_config = new_openvr_config;\n\n        control_sender.send(&ServerControlPacket::Restarting).ok();\n\n        crate::notify_restart_driver();\n    }\n\n    dbg_connection!(\"connection_pipeline: Send StartStream packet\");\n    control_sender\n        .send(&ServerControlPacket::StartStream)\n        .to_con()?;\n\n    let signal = control_receiver.recv(HANDSHAKE_ACTION_TIMEOUT)?;\n    if !matches!(signal, ClientControlPacket::StreamReady) {\n        con_bail!(\"Got unexpected packet waiting for stream ack\");\n    }\n    dbg_connection!(\"connection_pipeline: Got StreamReady packet\");\n\n    *ctx.statistics_manager.write() = Some(StatisticsManager::new(\n        initial_settings.connection.statistics_history_size,\n        Duration::from_secs_f32(1.0 / fps),\n        if let Switch::Enabled(config) = &initial_settings.headset.controllers {\n            config.steamvr_pipeline_frames\n        } else {\n            0.0\n        },\n    ));\n\n    *ctx.bitrate_manager.lock() =\n        BitrateManager::new(initial_settings.video.bitrate.history_size, fps);\n\n    let stream_protocol = if wired {\n        SocketProtocol::Tcp\n    } else {\n        initial_settings.connection.stream_protocol\n    };\n\n    dbg_connection!(\"connection_pipeline: StreamSocket connect_to_client\");\n    let mut stream_socket = StreamSocketBuilder::connect_to_client(\n        HANDSHAKE_ACTION_TIMEOUT,\n        client_ip,\n        initial_settings.connection.stream_port,\n        stream_protocol,\n        initial_settings.connection.dscp,\n        initial_settings.connection.server_buffer_config,\n        initial_settings.connection.packet_size as _,\n    )?;\n\n    let mut video_sender = stream_socket.request_stream(VIDEO);\n    let game_audio_sender: alvr_sockets::StreamSender<()> = stream_socket.request_stream(AUDIO);\n    let mut microphone_receiver: alvr_sockets::StreamReceiver<()> =\n        stream_socket.subscribe_to_stream(AUDIO, MAX_UNREAD_PACKETS);\n    let tracking_receiver =\n        stream_socket.subscribe_to_stream::<TrackingData>(TRACKING, MAX_UNREAD_PACKETS);\n    let haptics_sender = stream_socket.request_stream(HAPTICS);\n    let mut statics_receiver =\n        stream_socket.subscribe_to_stream::<ClientStatistics>(STATISTICS, MAX_UNREAD_PACKETS);\n\n    let (video_channel_sender, video_channel_receiver) =\n        std::sync::mpsc::sync_channel(initial_settings.connection.max_queued_server_video_frames);\n    *ctx.video_channel_sender.lock() = Some(video_channel_sender);\n    *ctx.haptics_sender.lock() = Some(haptics_sender);\n\n    let video_send_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let client_hostname = client_hostname.clone();\n        move || {\n            while is_streaming(&client_hostname) {\n                let VideoPacket {\n                    mut header,\n                    payload,\n                } = match video_channel_receiver.recv_timeout(STREAMING_RECV_TIMEOUT) {\n                    Ok(packet) => packet,\n                    Err(RecvTimeoutError::Timeout) => continue,\n                    Err(RecvTimeoutError::Disconnected) => return,\n                };\n\n                ctx.tracking_manager\n                    .read()\n                    .unrecenter_view_params(&mut header.global_view_params);\n\n                // todo: use get_buffer and make encoder write to socket buffers directly to avoid copy\n                video_sender\n                    .send_header_with_payload(&header, &payload)\n                    .ok();\n            }\n        }\n    });\n\n    #[cfg_attr(target_os = \"linux\", allow(unused_variables))]\n    let game_audio_thread = if let Switch::Enabled(config) =\n        initial_settings.audio.game_audio.clone()\n    {\n        #[cfg(windows)]\n        let ctx = Arc::clone(&ctx);\n\n        let client_hostname = client_hostname.clone();\n        thread::spawn(move || {\n            #[cfg(not(target_os = \"linux\"))]\n            while is_streaming(&client_hostname) {\n                {\n                    let device = match alvr_audio::new_output(config.device.as_ref()) {\n                        Ok(data) => data,\n                        Err(e) => {\n                            warn!(\"New audio device failed: {e:?}\");\n                            thread::sleep(RETRY_CONNECT_MIN_INTERVAL);\n                            continue;\n                        }\n                    };\n\n                    #[cfg(windows)]\n                    if let Ok(id) = alvr_audio::get_windows_device_id(&device) {\n                        let prop = alvr_session::OpenvrProperty {\n                            key: alvr_session::OpenvrPropKey::AudioDefaultPlaybackDeviceIdString,\n                            value: id,\n                        };\n                        ctx.events_sender\n                            .send(ServerCoreEvent::SetOpenvrProperty {\n                                device_id: *alvr_common::HEAD_ID,\n                                prop,\n                            })\n                            .ok();\n                    } else {\n                        continue;\n                    };\n\n                    if let Err(e) = alvr_audio::record_audio_blocking(\n                        Arc::new({\n                            let client_hostname = client_hostname.clone();\n                            move || is_streaming(&client_hostname)\n                        }),\n                        game_audio_sender.clone(),\n                        &device,\n                        2,\n                        config.mute_when_streaming,\n                    ) {\n                        error!(\"Audio record error: {e:?}\");\n                    }\n\n                    #[cfg(windows)]\n                    if let Ok(id) = alvr_audio::new_output(None)\n                        .and_then(|d| alvr_audio::get_windows_device_id(&d))\n                    {\n                        let prop = alvr_session::OpenvrProperty {\n                            key: alvr_session::OpenvrPropKey::AudioDefaultPlaybackDeviceIdString,\n                            value: id,\n                        };\n                        ctx.events_sender\n                            .send(ServerCoreEvent::SetOpenvrProperty {\n                                device_id: *alvr_common::HEAD_ID,\n                                prop,\n                            })\n                            .ok();\n                    }\n                }\n            }\n        })\n    } else {\n        thread::spawn(|| ())\n    };\n\n    #[cfg(not(target_os = \"linux\"))]\n    let microphone_thread = if let Switch::Enabled(config) =\n        initial_settings.audio.microphone.clone()\n    {\n        #[allow(unused_variables)]\n        let (sink, source) = alvr_audio::new_virtual_microphone_pair(config.devices).to_con()?;\n\n        #[cfg(windows)]\n        if let Ok(id) = alvr_audio::get_windows_device_id(&source) {\n            ctx.events_sender\n                .send(ServerCoreEvent::SetOpenvrProperty {\n                    device_id: *alvr_common::HEAD_ID,\n                    prop: alvr_session::OpenvrProperty {\n                        key: alvr_session::OpenvrPropKey::AudioDefaultRecordingDeviceIdString,\n                        value: id,\n                    },\n                })\n                .ok();\n        }\n\n        let client_hostname = client_hostname.clone();\n        thread::spawn(move || {\n            alvr_common::show_err(alvr_audio::play_audio_loop(\n                {\n                    let client_hostname = client_hostname.clone();\n                    move || is_streaming(&client_hostname)\n                },\n                &sink,\n                1,\n                streaming_caps.microphone_sample_rate,\n                config.buffering,\n                &mut microphone_receiver,\n            ));\n        })\n    } else {\n        thread::spawn(|| ())\n    };\n\n    #[cfg(target_os = \"linux\")]\n    let microphone_thread = {\n        use alvr_audio::linux::{self, AudioInfo};\n        let mic = if let Switch::Enabled(config) = initial_settings.audio.microphone.clone() {\n            Some((\n                AudioInfo {\n                    sample_rate: streaming_caps.microphone_sample_rate,\n                    channel_count: 1,\n                },\n                config.buffering,\n            ))\n        } else {\n            None\n        };\n\n        let audio_info = initial_settings\n            .audio\n            .game_audio\n            .enabled()\n            .then_some(AudioInfo {\n                sample_rate: game_audio_sample_rate,\n                channel_count: 2,\n            });\n\n        if mic.is_some() || audio_info.is_some() {\n            let client_hostname = client_hostname.clone();\n            thread::spawn(move || {\n                linux::audio_loop(\n                    {\n                        let client_hostname = client_hostname.clone();\n                        move || is_streaming(&client_hostname)\n                    },\n                    game_audio_sender,\n                    audio_info,\n                    &mut microphone_receiver,\n                    mic,\n                );\n            })\n        } else {\n            thread::spawn(|| ())\n        }\n    };\n\n    *ctx.tracking_manager.write() =\n        TrackingManager::new(initial_settings.connection.statistics_history_size);\n    let hand_gesture_manager = Arc::new(Mutex::new(HandGestureManager::new()));\n\n    let tracking_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let hand_gesture_manager = Arc::clone(&hand_gesture_manager);\n        let initial_settings = initial_settings.clone();\n        let client_hostname = client_hostname.clone();\n        move || {\n            tracking::tracking_loop(\n                &ctx,\n                initial_settings,\n                hand_gesture_manager,\n                tracking_receiver,\n                || is_streaming(&client_hostname),\n            );\n        }\n    });\n\n    let statistics_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n        let client_hostname = client_hostname.clone();\n        move || {\n            while is_streaming(&client_hostname) {\n                let data = match statics_receiver.recv(STREAMING_RECV_TIMEOUT) {\n                    Ok(stats) => stats,\n                    Err(ConnectionError::TryAgain(_)) => continue,\n                    Err(ConnectionError::Other(_)) => return,\n                };\n                let Ok(client_stats) = data.get_header() else {\n                    return;\n                };\n\n                if let Some(stats) = &mut *ctx.statistics_manager.write() {\n                    let timestamp = client_stats.target_timestamp;\n                    let decoder_latency = client_stats.video_decode;\n                    let (network_latency, game_latency) = stats.report_statistics(client_stats);\n\n                    ctx.events_sender\n                        .send(ServerCoreEvent::GameRenderLatencyFeedback(game_latency))\n                        .ok();\n\n                    let session_manager_lock = SESSION_MANAGER.read();\n                    ctx.bitrate_manager.lock().report_frame_latencies(\n                        &session_manager_lock.settings().video.bitrate.mode,\n                        timestamp,\n                        network_latency,\n                        decoder_latency,\n                    );\n                }\n            }\n        }\n    });\n\n    let control_sender = Arc::new(Mutex::new(control_sender));\n\n    let real_time_update_thread = thread::spawn({\n        let control_sender = Arc::clone(&control_sender);\n        let client_hostname = client_hostname.clone();\n        move || {\n            let mut previous_config = None;\n            while is_streaming(&client_hostname) {\n                let config = {\n                    let session_manager_lock = SESSION_MANAGER.read();\n                    let settings = session_manager_lock.settings();\n\n                    RealTimeConfig::from_settings(settings)\n                };\n\n                let same_config = previous_config.as_ref().is_some_and(|prev| config == *prev);\n                if !same_config {\n                    previous_config = Some(config.clone());\n\n                    control_sender\n                        .lock()\n                        .send(&ServerControlPacket::RealTimeConfig(config))\n                        .ok();\n                }\n\n                thread::sleep(REAL_TIME_UPDATE_INTERVAL);\n            }\n        }\n    });\n\n    let keepalive_thread = thread::spawn({\n        let control_sender = Arc::clone(&control_sender);\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        let client_hostname = client_hostname.clone();\n        move || {\n            while is_streaming(&client_hostname) {\n                if let Err(e) = control_sender.lock().send(&ServerControlPacket::KeepAlive) {\n                    info!(\"Client disconnected. Cause: {e:?}\");\n\n                    disconnect_notif.notify_one();\n\n                    return;\n                }\n\n                thread::sleep(KEEPALIVE_INTERVAL);\n            }\n        }\n    });\n\n    let control_receive_thread = thread::spawn({\n        let ctx = Arc::clone(&ctx);\n\n        let controllers_config = session_manager_lock\n            .settings()\n            .headset\n            .controllers\n            .as_option();\n        let mut controller_button_mapping_manager = controllers_config.map(|config| {\n            if let Some(mappings) = &config.button_mappings {\n                ButtonMappingManager::new_manual(mappings)\n            } else {\n                ButtonMappingManager::new_automatic(\n                    &CONTROLLER_PROFILE_INFO\n                        .get(&alvr_common::hash_string(QUEST_CONTROLLER_PROFILE_PATH))\n                        .unwrap()\n                        .button_set,\n                    &config.emulation_mode,\n                    &config.button_mapping_config,\n                )\n            }\n        });\n        let controllers_emulation_mode =\n            controllers_config.map(|config| config.emulation_mode.clone());\n\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        let control_sender = Arc::clone(&control_sender);\n        let client_hostname = client_hostname.clone();\n        move || {\n            let mut disconnection_deadline = Instant::now() + KEEPALIVE_TIMEOUT;\n            while is_streaming(&client_hostname) {\n                let packet = match control_receiver.recv(STREAMING_RECV_TIMEOUT) {\n                    Ok(packet) => packet,\n                    Err(ConnectionError::TryAgain(_)) => {\n                        if Instant::now() > disconnection_deadline {\n                            info!(\"Client disconnected. Timeout\");\n                            break;\n                        } else {\n                            continue;\n                        }\n                    }\n                    Err(e) => {\n                        info!(\"Client disconnected. Cause: {e}\");\n                        break;\n                    }\n                };\n\n                match packet {\n                    ClientControlPacket::PlayspaceSync(packet) => {\n                        if !initial_settings.headset.tracking_ref_only {\n                            let session_manager_lock = SESSION_MANAGER.read();\n                            let config = &session_manager_lock.settings().headset;\n                            ctx.tracking_manager.write().recenter(\n                                config.position_recentering_mode,\n                                config.rotation_recentering_mode,\n                            );\n\n                            let area = packet.unwrap_or(Vec2::new(2.0, 2.0));\n                            let wh = area.x * area.y;\n                            if wh.is_finite() && wh > 0.0 {\n                                info!(\"Received new playspace with size: {}\", area);\n                                ctx.events_sender\n                                    .send(ServerCoreEvent::PlayspaceSync(area))\n                                    .ok();\n                            } else {\n                                warn!(\"Received invalid playspace size: {}\", area);\n                                ctx.events_sender\n                                    .send(ServerCoreEvent::PlayspaceSync(Vec2::new(2.0, 2.0)))\n                                    .ok();\n                            }\n                        }\n                    }\n                    ClientControlPacket::RequestIdr => {\n                        if let Some(config) = ctx.decoder_config.lock().clone() {\n                            control_sender\n                                .lock()\n                                .send(&ServerControlPacket::DecoderConfig(config))\n                                .ok();\n                        }\n                        ctx.events_sender.send(ServerCoreEvent::RequestIDR).ok();\n                    }\n                    ClientControlPacket::LocalViewParams(params) => {\n                        ctx.events_sender\n                            .send(ServerCoreEvent::LocalViewParams(params))\n                            .ok();\n                    }\n                    ClientControlPacket::Battery(packet) => {\n                        ctx.events_sender\n                            .send(ServerCoreEvent::Battery(packet.clone()))\n                            .ok();\n\n                        if let Some(stats) = &mut *ctx.statistics_manager.write() {\n                            stats.report_battery(\n                                packet.device_id,\n                                packet.gauge_value,\n                                packet.is_plugged,\n                            );\n                        }\n                    }\n                    ClientControlPacket::Buttons(entries) => {\n                        {\n                            let session_manager_lock = SESSION_MANAGER.read();\n                            if session_manager_lock\n                                .settings()\n                                .extra\n                                .logging\n                                .log_button_presses\n                            {\n                                alvr_events::send_event(EventType::Buttons(\n                                    entries\n                                        .iter()\n                                        .map(|e| ButtonEvent {\n                                            path: BUTTON_INFO.get(&e.path_id).map_or_else(\n                                                || format!(\"Unknown (ID: {:#16x})\", e.path_id),\n                                                |info| info.path.to_owned(),\n                                            ),\n                                            value: e.value,\n                                        })\n                                        .collect(),\n                                ));\n                            }\n                        }\n\n                        if let Some(manager) = &mut controller_button_mapping_manager {\n                            let button_entries = entries\n                                .iter()\n                                .flat_map(|entry| manager.map_button(entry))\n                                .collect::<Vec<_>>();\n\n                            if !button_entries.is_empty() {\n                                ctx.events_sender\n                                    .send(ServerCoreEvent::Buttons(button_entries))\n                                    .ok();\n                            }\n                        };\n                    }\n                    ClientControlPacket::ActiveInteractionProfile { input_ids, .. } => {\n                        controller_button_mapping_manager = if let Switch::Enabled(config) =\n                            &SESSION_MANAGER.read().settings().headset.controllers\n                        {\n                            if let Some(mappings) = &config.button_mappings {\n                                Some(ButtonMappingManager::new_manual(mappings))\n                            } else {\n                                controllers_emulation_mode.as_ref().map(|emulation_mode| {\n                                    ButtonMappingManager::new_automatic(\n                                        &input_ids,\n                                        emulation_mode,\n                                        &config.button_mapping_config,\n                                    )\n                                })\n                            }\n                        } else {\n                            None\n                        };\n                    }\n                    ClientControlPacket::Log { level, message } => {\n                        info!(\"Client {client_hostname}: [{level:?}] {message}\")\n                    }\n                    ClientControlPacket::KeepAlive | ClientControlPacket::StreamReady => (),\n                    ClientControlPacket::ProximityState(headset_is_worn) => {\n                        ctx.events_sender\n                            .send(ServerCoreEvent::ProximityState(headset_is_worn))\n                            .ok();\n                    }\n                    ClientControlPacket::Reserved(_) | ClientControlPacket::ReservedBuffer(_) => (),\n                }\n\n                disconnection_deadline = Instant::now() + KEEPALIVE_TIMEOUT;\n            }\n\n            disconnect_notif.notify_one()\n        }\n    });\n\n    let stream_receive_thread = thread::spawn({\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        let client_hostname = client_hostname.clone();\n        move || {\n            while is_streaming(&client_hostname) {\n                match stream_socket.recv() {\n                    Ok(()) => (),\n                    Err(ConnectionError::TryAgain(_)) => continue,\n                    Err(e) => {\n                        info!(\"Client disconnected. Cause: {e}\");\n\n                        disconnect_notif.notify_one();\n\n                        return;\n                    }\n                }\n            }\n        }\n    });\n\n    let lifecycle_check_thread = thread::spawn({\n        let disconnect_notif = Arc::clone(&disconnect_notif);\n        let client_hostname = client_hostname.clone();\n        move || {\n            while SESSION_MANAGER\n                .read()\n                .client_list()\n                .get(&client_hostname)\n                .is_some_and(|c| c.connection_state == ConnectionState::Streaming)\n                && *lifecycle_state.read() == LifecycleState::Resumed\n            {\n                thread::sleep(STREAMING_RECV_TIMEOUT);\n            }\n\n            disconnect_notif.notify_one()\n        }\n    });\n\n    {\n        if initial_settings.connection.enable_on_connect_script {\n            let on_connect_script = FILESYSTEM_LAYOUT.get().map(|l| l.connect_script()).unwrap();\n            info!(\n                \"Running on connect script (connect): {}\",\n                on_connect_script.display()\n            );\n            if let Err(e) = Command::new(&on_connect_script)\n                .env(\"ACTION\", \"connect\")\n                .spawn()\n            {\n                warn!(\"Failed to run connect script: {e}\");\n            }\n        }\n    }\n\n    if initial_settings.extra.capture.startup_video_recording {\n        info!(\"Creating recording file\");\n        crate::create_recording_file(&ctx, session_manager_lock.settings());\n    }\n\n    session_manager_lock.update_client_connections(\n        client_hostname.clone(),\n        ClientConnectionsAction::SetConnectionState(ConnectionState::Streaming),\n    );\n\n    ctx.events_sender\n        .send(ServerCoreEvent::ClientConnected)\n        .ok();\n\n    dbg_connection!(\"connection_pipeline: handshake finished; unlocking streams\");\n    alvr_common::wait_rwlock(&disconnect_notif, &mut session_manager_lock);\n    dbg_connection!(\"connection_pipeline: Begin connection shutdown\");\n\n    // This requests shutdown from threads\n    *ctx.video_channel_sender.lock() = None;\n    *ctx.haptics_sender.lock() = None;\n\n    *ctx.video_recording_file.lock() = None;\n\n    session_manager_lock.update_client_connections(\n        client_hostname,\n        ClientConnectionsAction::SetConnectionState(ConnectionState::Disconnecting),\n    );\n\n    let enable_on_disconnect_script = session_manager_lock\n        .settings()\n        .connection\n        .enable_on_disconnect_script;\n    if enable_on_disconnect_script {\n        let on_disconnect_script = FILESYSTEM_LAYOUT\n            .get()\n            .map(|l| l.disconnect_script())\n            .unwrap();\n        info!(\n            \"Running on disconnect script (disconnect): {}\",\n            on_disconnect_script.display()\n        );\n        if let Err(e) = Command::new(&on_disconnect_script)\n            .env(\"ACTION\", \"disconnect\")\n            .spawn()\n        {\n            warn!(\"Failed to run disconnect script: {e}\");\n        }\n    }\n\n    // Allow threads to shutdown correctly\n    drop(session_manager_lock);\n\n    // Ensure shutdown of threads\n    dbg_connection!(\"connection_pipeline: Shutdown threads\");\n    video_send_thread.join().ok();\n    game_audio_thread.join().ok();\n    microphone_thread.join().ok();\n    tracking_receive_thread.join().ok();\n    statistics_thread.join().ok();\n    real_time_update_thread.join().ok();\n    control_receive_thread.join().ok();\n    stream_receive_thread.join().ok();\n    keepalive_thread.join().ok();\n    lifecycle_check_thread.join().ok();\n\n    ctx.events_sender\n        .send(ServerCoreEvent::ClientDisconnected)\n        .ok();\n\n    dbg_connection!(\"connection_pipeline: End\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "alvr/server_core/src/hand_gestures.rs",
    "content": "use crate::input_mapping::ButtonMappingManager;\nuse alvr_common::{\n    glam::{Vec2, Vec3},\n    *,\n};\nuse alvr_packets::{ButtonEntry, ButtonValue};\nuse alvr_session::HandTrackingInteractionConfig;\nuse std::{\n    collections::{HashMap, HashSet},\n    hash::Hash,\n    sync::LazyLock,\n    time::{Duration, SystemTime, UNIX_EPOCH},\n};\n\nfn lerp_pose(a: Pose, b: Pose, fac: f32) -> Pose {\n    Pose {\n        orientation: a.orientation.lerp(b.orientation, fac),\n        position: a.position.lerp(b.position, fac),\n    }\n}\n\npub static HAND_GESTURE_BUTTON_SET: LazyLock<HashSet<u64>> = LazyLock::new(|| {\n    HashSet::from([\n        *LEFT_X_CLICK_ID,\n        *LEFT_X_TOUCH_ID,\n        *LEFT_Y_CLICK_ID,\n        *LEFT_Y_TOUCH_ID,\n        *LEFT_MENU_CLICK_ID,\n        *LEFT_SQUEEZE_VALUE_ID,\n        *LEFT_TRIGGER_VALUE_ID,\n        *LEFT_TRIGGER_TOUCH_ID,\n        *LEFT_THUMBSTICK_X_ID,\n        *LEFT_THUMBSTICK_Y_ID,\n        *LEFT_THUMBSTICK_CLICK_ID,\n        *LEFT_THUMBSTICK_TOUCH_ID,\n        *RIGHT_A_CLICK_ID,\n        *RIGHT_A_TOUCH_ID,\n        *RIGHT_B_CLICK_ID,\n        *RIGHT_B_TOUCH_ID,\n        *RIGHT_SQUEEZE_VALUE_ID,\n        *RIGHT_TRIGGER_VALUE_ID,\n        *RIGHT_TRIGGER_TOUCH_ID,\n        *RIGHT_THUMBSTICK_X_ID,\n        *RIGHT_THUMBSTICK_Y_ID,\n        *RIGHT_THUMBSTICK_CLICK_ID,\n        *RIGHT_THUMBSTICK_TOUCH_ID,\n    ])\n});\n\n#[derive(Debug, Clone)]\npub struct HandGesture {\n    pub id: HandGestureId,\n    pub active: bool,\n    pub clicked: bool,\n    pub touching: bool,\n    pub value: f32,\n}\n\npub struct GestureAction {\n    last_activated: u128,\n    last_deactivated: u128,\n    entering: bool,\n    entering_since: u128,\n    exiting: bool,\n    exiting_since: u128,\n    active: bool,\n}\n\n#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]\n#[allow(dead_code)]\npub enum HandGestureId {\n    // Pinches\n    ThumbIndexPinch,\n    ThumbMiddlePinch,\n    ThumbRingPinch,\n    ThumbLittlePinch,\n    // Curls\n    ThumbCurl,\n    IndexCurl,\n    MiddleCurl,\n    RingCurl,\n    LittleCurl,\n    GripCurl,\n    // Complex\n    JoystickX,\n    JoystickY,\n}\n\npub struct HandGestureManager {\n    gesture_data_left: HashMap<HandGestureId, GestureAction>,\n    gesture_data_right: HashMap<HandGestureId, GestureAction>,\n}\n\nimpl HandGestureManager {\n    pub fn new() -> Self {\n        Self {\n            gesture_data_left: HashMap::new(),\n            gesture_data_right: HashMap::new(),\n        }\n    }\n\n    pub fn get_active_gestures(\n        &mut self,\n        hand_skeleton: &[Pose; 26],\n        config: &HandTrackingInteractionConfig,\n        device_id: u64,\n    ) -> Vec<HandGesture> {\n        // global joints\n        let gj = hand_skeleton;\n\n        // if we model the tip of the finger as a spherical object, we should account for its radius\n        // these are intentionally under the average by ~5mm since the touch and trigger distances are already configurable in settings\n        let thumb_rad: f32 = 0.0075; // average thumb is ~20mm in diameter\n        let index_rad: f32 = 0.0065; // average index finger is ~18mm in diameter\n        let middle_rad: f32 = 0.0065; // average middle finger is ~18mm in diameter\n        let ring_rad: f32 = 0.006; // average ring finger is ~17mm in diameter\n        let little_rad: f32 = 0.005; // average pinky finger is ~15mm in diameter\n        let palm_depth: f32 = 0.005; // average palm bones are ~10mm from the skin\n\n        // we add the radius of the finger and thumb because we're measuring the distance between the surface of them, not their centers\n        let pinch_min = config.pinch_touch_distance * 0.01;\n        let pinch_max = config.pinch_trigger_distance * 0.01;\n        let curl_min = config.curl_touch_distance * 0.01;\n        let curl_max = config.curl_trigger_distance * 0.01;\n\n        let palm: Pose = gj[0];\n        let thumb_tip: Pose = gj[5];\n        let index_metacarpal: Pose = gj[6];\n        let index_proximal: Pose = gj[7];\n        let index_intermediate: Pose = gj[8];\n        let index_distal: Pose = gj[9];\n        let index_tip: Pose = gj[10];\n        let middle_metacarpal: Pose = gj[11];\n        let middle_proximal: Pose = gj[12];\n        let middle_tip: Pose = gj[15];\n        let ring_metacarpal: Pose = gj[16];\n        let ring_proximal: Pose = gj[17];\n        let ring_tip: Pose = gj[20];\n        let little_metacarpal: Pose = gj[21];\n        let little_proximal: Pose = gj[22];\n        let little_tip: Pose = gj[25];\n\n        let mut gestures = [\n            // Thumb & index pinch\n            HandGesture {\n                id: HandGestureId::ThumbIndexPinch,\n                active: self.is_gesture_active(\n                    HandGestureId::ThumbIndexPinch,\n                    thumb_tip,\n                    thumb_rad,\n                    index_tip,\n                    index_rad,\n                    pinch_max,\n                    config.repeat_delay,\n                    config.activation_delay,\n                    config.deactivation_delay,\n                    device_id,\n                ),\n                clicked: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, index_tip, index_rad, pinch_min),\n                touching: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, index_tip, index_rad, pinch_max),\n                value: self.get_gesture_hover(\n                    thumb_tip, thumb_rad, index_tip, index_rad, pinch_min, pinch_max,\n                ),\n            },\n            // Thumb & middle pinch\n            HandGesture {\n                id: HandGestureId::ThumbMiddlePinch,\n                active: self.is_gesture_active(\n                    HandGestureId::ThumbMiddlePinch,\n                    thumb_tip,\n                    thumb_rad,\n                    middle_tip,\n                    middle_rad,\n                    pinch_max,\n                    config.repeat_delay,\n                    config.activation_delay,\n                    config.deactivation_delay,\n                    device_id,\n                ),\n                clicked: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, middle_tip, middle_rad, pinch_min),\n                touching: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, middle_tip, middle_rad, pinch_max),\n                value: self.get_gesture_hover(\n                    thumb_tip, thumb_rad, middle_tip, middle_rad, pinch_min, pinch_max,\n                ),\n            },\n            // Thumb & ring pinch\n            HandGesture {\n                id: HandGestureId::ThumbRingPinch,\n                active: self.is_gesture_active(\n                    HandGestureId::ThumbRingPinch,\n                    thumb_tip,\n                    thumb_rad,\n                    ring_tip,\n                    ring_rad,\n                    pinch_max,\n                    config.repeat_delay,\n                    config.activation_delay,\n                    config.deactivation_delay,\n                    device_id,\n                ),\n                clicked: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, ring_tip, ring_rad, pinch_min),\n                touching: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, ring_tip, ring_rad, pinch_max),\n                value: self.get_gesture_hover(\n                    thumb_tip, thumb_rad, ring_tip, ring_rad, pinch_min, pinch_max,\n                ),\n            },\n            // Thumb & little pinch\n            HandGesture {\n                id: HandGestureId::ThumbLittlePinch,\n                active: self.is_gesture_active(\n                    HandGestureId::ThumbLittlePinch,\n                    thumb_tip,\n                    thumb_rad,\n                    little_tip,\n                    little_rad,\n                    pinch_max,\n                    config.repeat_delay,\n                    config.activation_delay,\n                    config.deactivation_delay,\n                    device_id,\n                ),\n                clicked: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, little_tip, little_rad, pinch_min),\n                touching: self\n                    .test_gesture_dist(thumb_tip, thumb_rad, little_tip, little_rad, pinch_max),\n                value: self.get_gesture_hover(\n                    thumb_tip, thumb_rad, little_tip, little_rad, pinch_min, pinch_max,\n                ),\n            },\n        ]\n        .into_iter()\n        .collect::<Vec<_>>();\n\n        // Finger curls\n        let thumb_curl =\n            self.get_gesture_hover(palm, palm_depth, thumb_tip, thumb_rad, curl_min, curl_max);\n        let index_curl = self.get_gesture_hover(\n            lerp_pose(index_metacarpal, index_proximal, 0.5),\n            palm_depth,\n            index_tip,\n            index_rad,\n            curl_min,\n            curl_max,\n        );\n        let middle_curl = self.get_gesture_hover(\n            lerp_pose(middle_metacarpal, middle_proximal, 0.5),\n            palm_depth,\n            middle_tip,\n            middle_rad,\n            curl_min,\n            curl_max,\n        );\n        let ring_curl = self.get_gesture_hover(\n            lerp_pose(ring_metacarpal, ring_proximal, 0.5),\n            palm_depth,\n            ring_tip,\n            ring_rad,\n            curl_min,\n            curl_max,\n        );\n        let little_curl = self.get_gesture_hover(\n            lerp_pose(little_metacarpal, little_proximal, 0.5),\n            palm_depth,\n            little_tip,\n            little_rad,\n            curl_min,\n            curl_max,\n        );\n\n        // Grip\n        let grip_curl = (middle_curl + ring_curl + little_curl) / 3.0;\n        let grip_active = grip_curl > 0.0;\n\n        gestures.push(HandGesture {\n            id: HandGestureId::GripCurl,\n            active: grip_active,\n            clicked: grip_curl == 1.0,\n            touching: grip_curl > 0.0,\n            value: grip_curl,\n        });\n\n        // Joystick\n        let joystick_range = config.joystick_range * 0.01;\n        let joystick_center = lerp_pose(index_intermediate, index_distal, 0.5);\n\n        let joystick_up = joystick_center\n            .orientation\n            .mul_vec3(if device_id == *HAND_LEFT_ID {\n                Vec3::X\n            } else {\n                Vec3::NEG_X\n            });\n        let joystick_horizontal_vec =\n            index_intermediate\n                .orientation\n                .mul_vec3(if device_id == *HAND_LEFT_ID {\n                    Vec3::Y\n                } else {\n                    Vec3::NEG_Y\n                });\n        let joystick_vertical_vec = index_intermediate.orientation.mul_vec3(Vec3::Z);\n\n        let joystick_offset_horizontal_direction = if device_id == *HAND_LEFT_ID {\n            1.0\n        } else {\n            -1.0\n        };\n        let joystick_pos = self.get_joystick_values(\n            joystick_center,\n            thumb_tip,\n            joystick_range,\n            joystick_horizontal_vec,\n            joystick_vertical_vec,\n            config.joystick_offset_horizontal * 0.01 * joystick_offset_horizontal_direction,\n            config.joystick_offset_vertical * 0.01,\n        );\n        let joystick_contact = index_curl >= 0.75\n            && grip_curl > 0.5\n            && joystick_center.position.distance(thumb_tip.position) <= joystick_range * 3.0\n            && (thumb_tip.position - joystick_center.position).dot(joystick_up)\n                / joystick_up.length()\n                <= joystick_range * 2.0;\n\n        let joystick_deadzone: f32 = config.joystick_deadzone * 0.01;\n\n        gestures.push(HandGesture {\n            id: HandGestureId::ThumbCurl,\n            active: thumb_curl >= 0.0,\n            touching: thumb_curl >= 0.0,\n            clicked: thumb_curl >= 0.5,\n            value: thumb_curl,\n        });\n        gestures.push(HandGesture {\n            id: HandGestureId::JoystickX,\n            active: joystick_contact,\n            touching: joystick_contact,\n            clicked: false,\n            value: if joystick_contact && joystick_pos.x.abs() >= joystick_deadzone {\n                joystick_pos.x\n            } else {\n                0.0\n            },\n        });\n        gestures.push(HandGesture {\n            id: HandGestureId::JoystickY,\n            active: joystick_contact,\n            touching: joystick_contact,\n            clicked: false,\n            value: if joystick_contact && joystick_pos.y.abs() >= joystick_deadzone {\n                joystick_pos.y\n            } else {\n                0.0\n            },\n        });\n\n        gestures\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn is_gesture_active(\n        &mut self,\n        gesture_id: HandGestureId,\n        first_anchor: Pose,\n        first_radius: f32,\n        second_anchor: Pose,\n        second_radius: f32,\n        activation_dist: f32,\n        repeat_delay: u32,\n        in_delay: u32,\n        out_delay: u32,\n        device_id: u64,\n    ) -> bool {\n        let in_range = first_anchor.position.distance(second_anchor.position)\n            < (activation_dist + first_radius + second_radius);\n\n        let gesture_data = if device_id == *HAND_LEFT_ID {\n            &mut self.gesture_data_left\n        } else {\n            &mut self.gesture_data_right\n        };\n\n        gesture_data.entry(gesture_id).or_insert(GestureAction {\n            last_activated: 0,\n            last_deactivated: 0,\n            entering: false,\n            entering_since: 0,\n            exiting: false,\n            exiting_since: 0,\n            active: false,\n        });\n        let g: &mut GestureAction = gesture_data.get_mut(&gesture_id).unwrap();\n\n        // Disable entering/exiting state if we leave/enter range\n        if in_range {\n            g.exiting = false;\n        } else {\n            g.entering = false;\n        }\n\n        // Get current time, for comparison\n        let time_millis = SystemTime::now()\n            .duration_since(UNIX_EPOCH)\n            .unwrap_or(Duration::from_millis(0))\n            .as_millis();\n\n        // Transitioning from inactive to active\n        if in_range && !g.active {\n            // Don't transition state unless the duration of repeat_delay has passed since last deactivation\n            if g.last_deactivated < time_millis - u128::from(repeat_delay) {\n                if g.entering {\n                    // Don't transition state unless gesture has been in range for the duration of in_delay\n                    if g.entering_since < time_millis - u128::from(in_delay) {\n                        g.last_activated = time_millis;\n                        g.entering = false;\n                        g.active = true;\n                    }\n                } else {\n                    // Begin tracking entering state\n                    g.entering = true;\n                    g.entering_since = time_millis;\n                }\n            }\n        }\n\n        // Transitioning from inactive to active\n        if !in_range && g.active {\n            if g.exiting {\n                // Don't transition state unless gesture has been out of range for the duration of out_delay\n                if g.exiting_since < time_millis - u128::from(out_delay) {\n                    g.last_deactivated = time_millis;\n                    g.exiting = false;\n                    g.active = false;\n                }\n            } else {\n                // Begin tracking exiting state\n                g.exiting = true;\n                g.exiting_since = time_millis;\n            }\n        }\n\n        g.active\n    }\n\n    fn test_gesture_dist(\n        &self,\n        first_anchor: Pose,\n        first_radius: f32,\n        second_anchor: Pose,\n        second_radius: f32,\n        activation_dist: f32,\n    ) -> bool {\n        first_anchor.position.distance(second_anchor.position)\n            < (activation_dist + first_radius + second_radius)\n    }\n\n    fn get_gesture_hover(\n        &self,\n        first_anchor: Pose,\n        first_radius: f32,\n        second_anchor: Pose,\n        second_radius: f32,\n        min_dist: f32,\n        max_dist: f32,\n    ) -> f32 {\n        (1.0 - (first_anchor.position.distance(second_anchor.position)\n            - min_dist\n            - first_radius\n            - second_radius)\n            / (max_dist + first_radius + second_radius))\n            .clamp(0.0, 1.0)\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn get_joystick_values(\n        &self,\n        center: Pose,\n        anchor: Pose,\n        joy_radius: f32,\n        hori_vec: Vec3,\n        vert_vec: Vec3,\n        offset_hori: f32,\n        offset_vert: f32,\n    ) -> Vec2 {\n        let x = (anchor.position - center.position).dot(hori_vec) / hori_vec.length() + offset_hori;\n\n        let y = (anchor.position - center.position).dot(vert_vec) / vert_vec.length() + offset_vert;\n\n        Vec2 {\n            x: (x / joy_radius).clamp(-1.0, 1.0),\n            y: (y / joy_radius).clamp(-1.0, 1.0),\n        }\n    }\n}\n\nfn get_click_bind_for_gesture(device_id: u64, gesture_id: HandGestureId) -> Option<u64> {\n    if device_id == *HAND_LEFT_ID {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*LEFT_TRIGGER_CLICK_ID),\n            HandGestureId::ThumbMiddlePinch => Some(*LEFT_Y_CLICK_ID),\n            HandGestureId::ThumbRingPinch => Some(*LEFT_X_CLICK_ID),\n            HandGestureId::ThumbLittlePinch => Some(*LEFT_MENU_CLICK_ID),\n            HandGestureId::GripCurl => Some(*LEFT_SQUEEZE_CLICK_ID),\n            HandGestureId::ThumbCurl => Some(*LEFT_THUMBSTICK_CLICK_ID),\n            _ => None,\n        }\n    } else {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*RIGHT_TRIGGER_CLICK_ID),\n            HandGestureId::ThumbMiddlePinch => Some(*RIGHT_B_CLICK_ID),\n            HandGestureId::ThumbRingPinch => Some(*RIGHT_A_CLICK_ID),\n            HandGestureId::GripCurl => Some(*RIGHT_SQUEEZE_CLICK_ID),\n            HandGestureId::ThumbCurl => Some(*RIGHT_THUMBSTICK_CLICK_ID),\n            _ => None,\n        }\n    }\n}\n\nfn get_touch_bind_for_gesture(device_id: u64, gesture_id: HandGestureId) -> Option<u64> {\n    if device_id == *HAND_LEFT_ID {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*LEFT_TRIGGER_TOUCH_ID),\n            HandGestureId::ThumbMiddlePinch => Some(*LEFT_Y_TOUCH_ID),\n            HandGestureId::ThumbRingPinch => Some(*LEFT_X_TOUCH_ID),\n            HandGestureId::JoystickX | HandGestureId::JoystickY => Some(*LEFT_THUMBSTICK_TOUCH_ID),\n            _ => None,\n        }\n    } else {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*RIGHT_TRIGGER_TOUCH_ID),\n            HandGestureId::ThumbMiddlePinch => Some(*RIGHT_B_TOUCH_ID),\n            HandGestureId::ThumbRingPinch => Some(*RIGHT_A_TOUCH_ID),\n            HandGestureId::JoystickX | HandGestureId::JoystickY => Some(*RIGHT_THUMBSTICK_TOUCH_ID),\n            _ => None,\n        }\n    }\n}\n\nfn get_hover_bind_for_gesture(device_id: u64, gesture_id: HandGestureId) -> Option<u64> {\n    if device_id == *HAND_LEFT_ID {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*LEFT_TRIGGER_VALUE_ID),\n            HandGestureId::GripCurl => Some(*LEFT_SQUEEZE_VALUE_ID),\n            HandGestureId::JoystickX => Some(*LEFT_THUMBSTICK_X_ID),\n            HandGestureId::JoystickY => Some(*LEFT_THUMBSTICK_Y_ID),\n            _ => None,\n        }\n    } else {\n        match gesture_id {\n            HandGestureId::ThumbIndexPinch => Some(*RIGHT_TRIGGER_VALUE_ID),\n            HandGestureId::GripCurl => Some(*RIGHT_SQUEEZE_VALUE_ID),\n            HandGestureId::JoystickX => Some(*RIGHT_THUMBSTICK_X_ID),\n            HandGestureId::JoystickY => Some(*RIGHT_THUMBSTICK_Y_ID),\n            _ => None,\n        }\n    }\n}\n\npub fn trigger_hand_gesture_actions(\n    button_mapping_manager: &mut ButtonMappingManager,\n    device_id: u64,\n    gestures: &[HandGesture],\n    only_touch: bool,\n) -> Vec<ButtonEntry> {\n    let mut button_entries = vec![];\n\n    for gesture in gestures {\n        // Click bind\n        if !only_touch && let Some(click_bind) = get_click_bind_for_gesture(device_id, gesture.id) {\n            button_entries.append(&mut button_mapping_manager.map_button(&ButtonEntry {\n                path_id: click_bind,\n                value: ButtonValue::Binary(gesture.active && gesture.clicked),\n            }));\n        }\n\n        // Touch bind\n        if let Some(touch_bind) = get_touch_bind_for_gesture(device_id, gesture.id) {\n            button_entries.append(&mut button_mapping_manager.map_button(&ButtonEntry {\n                path_id: touch_bind,\n                value: ButtonValue::Binary(gesture.active && gesture.touching),\n            }));\n        }\n\n        // Hover bind\n        if !only_touch && let Some(hover_bind) = get_hover_bind_for_gesture(device_id, gesture.id) {\n            button_entries.append(&mut button_mapping_manager.map_button(&ButtonEntry {\n                path_id: hover_bind,\n                value: ButtonValue::Scalar(if gesture.active { gesture.value } else { 0.0 }),\n            }));\n        }\n    }\n\n    button_entries\n}\n"
  },
  {
    "path": "alvr/server_core/src/haptics.rs",
    "content": "use alvr_packets::Haptics;\nuse alvr_session::HapticsConfig;\nuse std::time::Duration;\n\npub fn map_haptics(config: &HapticsConfig, haptics: Haptics) -> Haptics {\n    Haptics {\n        duration: Duration::max(\n            haptics.duration,\n            Duration::from_secs_f32(config.min_duration_s),\n        ),\n        amplitude: config.intensity_multiplier\n            * f32::powf(haptics.amplitude, config.amplitude_curve),\n        ..haptics\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/input_mapping.rs",
    "content": "use alvr_common::*;\nuse alvr_packets::{ButtonEntry, ButtonValue};\nuse alvr_session::{\n    AutomaticButtonMappingConfig, BinaryToScalarStates, ButtonBindingTarget, ButtonMappingType,\n    ControllersEmulationMode, HysteresisThreshold, Range,\n};\nuse std::collections::{HashMap, HashSet};\n\npub fn registered_button_set(\n    controllers_emulation_mode: &ControllersEmulationMode,\n) -> HashSet<u64> {\n    match &controllers_emulation_mode {\n        ControllersEmulationMode::RiftSTouch\n        | ControllersEmulationMode::Quest1Touch\n        | ControllersEmulationMode::Quest2Touch\n        | ControllersEmulationMode::Quest3Plus\n        | ControllersEmulationMode::QuestPro => CONTROLLER_PROFILE_INFO\n            .get(&QUEST_CONTROLLER_PROFILE_ID)\n            .unwrap()\n            .button_set\n            .clone(),\n        ControllersEmulationMode::Pico4 => CONTROLLER_PROFILE_INFO\n            .get(&PICO4_CONTROLLER_PROFILE_ID)\n            .unwrap()\n            .button_set\n            .clone(),\n        ControllersEmulationMode::PSVR2Sense => CONTROLLER_PROFILE_INFO\n            .get(&PSVR2_CONTROLLER_PROFILE_ID)\n            .unwrap()\n            .button_set\n            .clone(),\n        ControllersEmulationMode::ValveIndex => CONTROLLER_PROFILE_INFO\n            .get(&INDEX_CONTROLLER_PROFILE_ID)\n            .unwrap()\n            .button_set\n            .clone(),\n        ControllersEmulationMode::ViveWand => CONTROLLER_PROFILE_INFO\n            .get(&VIVE_CONTROLLER_PROFILE_ID)\n            .unwrap()\n            .button_set\n            .clone(),\n        ControllersEmulationMode::ViveTracker => HashSet::new(),\n        ControllersEmulationMode::Custom { button_set, .. } => button_set\n            .iter()\n            .map(|b| alvr_common::hash_string(b))\n            .collect(),\n    }\n}\n\npub struct BindingTarget {\n    destination: u64,\n    mapping_type: ButtonMappingType,\n    binary_conditions: Vec<u64>,\n}\n\n// Inputs relative to the same physical button\n#[derive(Clone, Copy)]\npub struct ButtonInputs {\n    click: Option<u64>,\n    touch: Option<u64>,\n    value: Option<u64>,\n    force: Option<u64>,\n}\n\nfn click(click: u64) -> ButtonInputs {\n    ButtonInputs {\n        click: Some(click),\n        touch: None,\n        value: None,\n        force: None,\n    }\n}\n\nfn ct(set: &HashSet<u64>, click: u64, touch: u64) -> ButtonInputs {\n    ButtonInputs {\n        click: Some(click),\n        touch: set.contains(&touch).then_some(touch),\n        value: None,\n        force: None,\n    }\n}\n\nfn value(value: u64) -> ButtonInputs {\n    ButtonInputs {\n        click: None,\n        touch: None,\n        value: Some(value),\n        force: None,\n    }\n}\n\nfn ctv(set: &HashSet<u64>, click: u64, touch: u64, value: u64) -> ButtonInputs {\n    ButtonInputs {\n        click: set.contains(&click).then_some(click),\n        touch: set.contains(&touch).then_some(touch),\n        value: set.contains(&value).then_some(value),\n        force: None,\n    }\n}\n\nfn ctvf(set: &HashSet<u64>, click: u64, touch: u64, value: u64, force: u64) -> ButtonInputs {\n    ButtonInputs {\n        click: set.contains(&click).then_some(click),\n        touch: set.contains(&touch).then_some(touch),\n        value: set.contains(&value).then_some(value),\n        force: set.contains(&force).then_some(force),\n    }\n}\n\nfn passthrough(target: u64) -> BindingTarget {\n    BindingTarget {\n        destination: target,\n        mapping_type: ButtonMappingType::Passthrough,\n        binary_conditions: vec![],\n    }\n}\n\nfn binary_to_scalar(target: u64, map: BinaryToScalarStates) -> BindingTarget {\n    BindingTarget {\n        destination: target,\n        mapping_type: ButtonMappingType::BinaryToScalar(map),\n        binary_conditions: vec![],\n    }\n}\n\nfn hysteresis_threshold(target: u64, map: HysteresisThreshold) -> BindingTarget {\n    BindingTarget {\n        destination: target,\n        mapping_type: ButtonMappingType::HysteresisThreshold(map),\n        binary_conditions: vec![],\n    }\n}\n\nfn remap(target: u64, map: Range) -> BindingTarget {\n    BindingTarget {\n        destination: target,\n        mapping_type: ButtonMappingType::Remap(map),\n        binary_conditions: vec![],\n    }\n}\n\n// Map two buttons with eterogeneous inputs\nfn map_button_pair_automatic(\n    source: ButtonInputs,\n    destination: ButtonInputs,\n    config: &AutomaticButtonMappingConfig,\n) -> impl Iterator<Item = (u64, Vec<BindingTarget>)> {\n    let click_to_value = BinaryToScalarStates { off: 0.0, on: 1.0 };\n\n    let mut entries = vec![];\n    if let Some(source_click) = source.click {\n        let mut targets = vec![];\n\n        if let Some(destination_click) = destination.click {\n            targets.push(passthrough(destination_click));\n        }\n        if source.touch.is_none()\n            && let Some(destination_touch) = destination.touch\n        {\n            targets.push(passthrough(destination_touch));\n        }\n\n        if source.value.is_none()\n            && let Some(destination_value) = destination.value\n        {\n            targets.push(binary_to_scalar(destination_value, click_to_value));\n        }\n\n        entries.push((source_click, targets));\n    }\n    if let Some(source_touch) = source.touch {\n        let mut targets = vec![];\n        if let Some(destination_touch) = destination.touch {\n            targets.push(passthrough(destination_touch));\n        }\n        entries.push((source_touch, targets));\n    }\n    if let Some(source_value) = source.value {\n        let mut targets = vec![];\n        let mut remap_for_touch = false;\n        let mut remap_for_force = false;\n\n        if source.click.is_none()\n            && let Some(destination_click) = destination.click\n        {\n            targets.push(hysteresis_threshold(\n                destination_click,\n                config.click_threshold,\n            ));\n        }\n        if source.touch.is_none()\n            && let Some(destination_touch) = destination.touch\n        {\n            targets.push(hysteresis_threshold(\n                destination_touch,\n                config.touch_threshold,\n            ));\n            remap_for_touch = true;\n        }\n        if source.force.is_none()\n            && let Some(destination_force) = destination.force\n        {\n            targets.push(remap(\n                destination_force,\n                Range {\n                    min: config.force_threshold,\n                    max: 1.0,\n                },\n            ));\n            remap_for_force = true;\n        }\n\n        if let Some(destination_value) = destination.value {\n            if !remap_for_touch && !remap_for_force {\n                targets.push(passthrough(destination_value));\n            } else {\n                let low = if remap_for_touch {\n                    config.touch_threshold.value\n                } else {\n                    0.0\n                };\n                let high = if remap_for_force {\n                    config.force_threshold\n                } else {\n                    1.0\n                };\n                targets.push(remap(\n                    destination_value,\n                    Range {\n                        min: low,\n                        max: high,\n                    },\n                ));\n            }\n        }\n\n        entries.push((source_value, targets));\n    }\n\n    entries.into_iter()\n}\n\npub fn automatic_bindings(\n    source_set: &HashSet<u64>,\n    destination_set: &HashSet<u64>,\n    config: &AutomaticButtonMappingConfig,\n) -> HashMap<u64, Vec<BindingTarget>> {\n    let s_set = source_set;\n    let d_set = destination_set;\n\n    let mut bindings = HashMap::new();\n\n    // Menu buttons\n    if s_set.contains(&*LEFT_MENU_CLICK_ID) {\n        let click = click(*LEFT_MENU_CLICK_ID);\n        if d_set.contains(&*LEFT_MENU_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(click, click, config));\n        } else if d_set.contains(&*LEFT_SYSTEM_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                click,\n                ct(s_set, *LEFT_SYSTEM_CLICK_ID, *LEFT_SYSTEM_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*LEFT_SYSTEM_CLICK_ID) {\n        let click = click(*LEFT_SYSTEM_CLICK_ID);\n        if d_set.contains(&*LEFT_SYSTEM_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                click,\n                ct(s_set, *LEFT_SYSTEM_CLICK_ID, *LEFT_SYSTEM_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*LEFT_MENU_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(click, click, config));\n        }\n    }\n    if s_set.contains(&*RIGHT_MENU_CLICK_ID) {\n        let click = click(*RIGHT_MENU_CLICK_ID);\n        if d_set.contains(&*RIGHT_MENU_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(click, click, config));\n        } else if d_set.contains(&*RIGHT_SYSTEM_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                click,\n                ct(s_set, *RIGHT_SYSTEM_CLICK_ID, *RIGHT_SYSTEM_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_SYSTEM_CLICK_ID) {\n        let click = click(*RIGHT_SYSTEM_CLICK_ID);\n        if d_set.contains(&*RIGHT_SYSTEM_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                click,\n                ct(s_set, *RIGHT_SYSTEM_CLICK_ID, *RIGHT_SYSTEM_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*RIGHT_MENU_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(click, click, config));\n        }\n    }\n\n    // A/X buttons\n    if s_set.contains(&*LEFT_X_CLICK_ID) {\n        let source = ct(s_set, *LEFT_X_CLICK_ID, *LEFT_X_TOUCH_ID);\n        if d_set.contains(&*LEFT_X_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_X_CLICK_ID, *LEFT_X_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*LEFT_A_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_A_CLICK_ID, *LEFT_A_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*LEFT_TRACKPAD_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_TRACKPAD_CLICK_ID, *LEFT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_A_CLICK_ID) {\n        let source = ct(s_set, *RIGHT_A_CLICK_ID, *RIGHT_A_TOUCH_ID);\n        if d_set.contains(&*RIGHT_A_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *RIGHT_A_CLICK_ID, *RIGHT_A_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*RIGHT_TRACKPAD_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *RIGHT_TRACKPAD_CLICK_ID, *RIGHT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n\n    // B/Y buttons\n    if s_set.contains(&*LEFT_Y_CLICK_ID) {\n        let source = ct(s_set, *LEFT_Y_CLICK_ID, *LEFT_Y_TOUCH_ID);\n        if d_set.contains(&*LEFT_Y_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_Y_CLICK_ID, *LEFT_Y_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*LEFT_B_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_B_CLICK_ID, *LEFT_B_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_B_CLICK_ID) && d_set.contains(&*RIGHT_B_CLICK_ID) {\n        bindings.extend(map_button_pair_automatic(\n            ct(s_set, *RIGHT_B_CLICK_ID, *RIGHT_B_TOUCH_ID),\n            ct(d_set, *RIGHT_B_CLICK_ID, *RIGHT_B_TOUCH_ID),\n            config,\n        ));\n    }\n\n    // Squeeze buttons\n    if (s_set.contains(&*LEFT_SQUEEZE_CLICK_ID) || s_set.contains(&*LEFT_SQUEEZE_VALUE_ID))\n        && (d_set.contains(&*LEFT_SQUEEZE_CLICK_ID) || d_set.contains(&*LEFT_SQUEEZE_VALUE_ID))\n    {\n        bindings.extend(map_button_pair_automatic(\n            ctvf(\n                s_set,\n                *LEFT_SQUEEZE_CLICK_ID,\n                *LEFT_SQUEEZE_TOUCH_ID,\n                *LEFT_SQUEEZE_VALUE_ID,\n                *LEFT_SQUEEZE_FORCE_ID,\n            ),\n            ctvf(\n                d_set,\n                *LEFT_SQUEEZE_CLICK_ID,\n                *LEFT_SQUEEZE_TOUCH_ID,\n                *LEFT_SQUEEZE_VALUE_ID,\n                *LEFT_SQUEEZE_FORCE_ID,\n            ),\n            config,\n        ));\n    }\n    if (s_set.contains(&*RIGHT_SQUEEZE_CLICK_ID) || s_set.contains(&*RIGHT_SQUEEZE_VALUE_ID))\n        && (d_set.contains(&*RIGHT_SQUEEZE_CLICK_ID) || d_set.contains(&*RIGHT_SQUEEZE_VALUE_ID))\n    {\n        bindings.extend(map_button_pair_automatic(\n            ctvf(\n                s_set,\n                *RIGHT_SQUEEZE_CLICK_ID,\n                *RIGHT_SQUEEZE_TOUCH_ID,\n                *RIGHT_SQUEEZE_VALUE_ID,\n                *RIGHT_SQUEEZE_FORCE_ID,\n            ),\n            ctvf(\n                d_set,\n                *RIGHT_SQUEEZE_CLICK_ID,\n                *RIGHT_SQUEEZE_TOUCH_ID,\n                *RIGHT_SQUEEZE_VALUE_ID,\n                *RIGHT_SQUEEZE_FORCE_ID,\n            ),\n            config,\n        ));\n    }\n\n    // Trigger buttons\n    if (s_set.contains(&*LEFT_TRIGGER_CLICK_ID) || s_set.contains(&*LEFT_TRIGGER_VALUE_ID))\n        && (d_set.contains(&*LEFT_TRIGGER_CLICK_ID) || d_set.contains(&*LEFT_TRIGGER_VALUE_ID))\n    {\n        bindings.extend(map_button_pair_automatic(\n            ctv(\n                s_set,\n                *LEFT_TRIGGER_CLICK_ID,\n                *LEFT_TRIGGER_TOUCH_ID,\n                *LEFT_TRIGGER_VALUE_ID,\n            ),\n            ctv(\n                d_set,\n                *LEFT_TRIGGER_CLICK_ID,\n                *LEFT_TRIGGER_TOUCH_ID,\n                *LEFT_TRIGGER_VALUE_ID,\n            ),\n            config,\n        ));\n    }\n    if (s_set.contains(&*RIGHT_TRIGGER_CLICK_ID) || s_set.contains(&*RIGHT_TRIGGER_VALUE_ID))\n        && (d_set.contains(&*RIGHT_TRIGGER_CLICK_ID) || d_set.contains(&*RIGHT_TRIGGER_VALUE_ID))\n    {\n        bindings.extend(map_button_pair_automatic(\n            ctv(\n                s_set,\n                *RIGHT_TRIGGER_CLICK_ID,\n                *RIGHT_TRIGGER_TOUCH_ID,\n                *RIGHT_TRIGGER_VALUE_ID,\n            ),\n            ctv(\n                d_set,\n                *RIGHT_TRIGGER_CLICK_ID,\n                *RIGHT_TRIGGER_TOUCH_ID,\n                *RIGHT_TRIGGER_VALUE_ID,\n            ),\n            config,\n        ));\n    }\n\n    // Thumbsticks\n    if s_set.contains(&*LEFT_THUMBSTICK_X_ID) {\n        let x = value(*LEFT_THUMBSTICK_X_ID);\n        let y = value(*LEFT_THUMBSTICK_Y_ID);\n        if d_set.contains(&*LEFT_THUMBSTICK_X_ID) {\n            bindings.extend(map_button_pair_automatic(x, x, config));\n            bindings.extend(map_button_pair_automatic(y, y, config));\n        } else if d_set.contains(&*LEFT_TRACKPAD_X_ID) {\n            bindings.extend(map_button_pair_automatic(\n                x,\n                value(*LEFT_TRACKPAD_X_ID),\n                config,\n            ));\n            bindings.extend(map_button_pair_automatic(\n                y,\n                value(*LEFT_TRACKPAD_Y_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*LEFT_THUMBSTICK_CLICK_ID) {\n        let source = ct(s_set, *LEFT_THUMBSTICK_CLICK_ID, *LEFT_THUMBSTICK_TOUCH_ID);\n        if d_set.contains(&*LEFT_THUMBSTICK_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_THUMBSTICK_CLICK_ID, *LEFT_THUMBSTICK_TOUCH_ID),\n                config,\n            ));\n        } else if d_set.contains(&*LEFT_TRACKPAD_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *LEFT_TRACKPAD_CLICK_ID, *LEFT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_THUMBSTICK_X_ID) {\n        let x = value(*RIGHT_THUMBSTICK_X_ID);\n        let y = value(*RIGHT_THUMBSTICK_Y_ID);\n        if d_set.contains(&*RIGHT_THUMBSTICK_X_ID) {\n            bindings.extend(map_button_pair_automatic(x, x, config));\n            bindings.extend(map_button_pair_automatic(y, y, config));\n        } else if d_set.contains(&*RIGHT_TRACKPAD_X_ID) {\n            bindings.extend(map_button_pair_automatic(\n                x,\n                value(*RIGHT_TRACKPAD_X_ID),\n                config,\n            ));\n            bindings.extend(map_button_pair_automatic(\n                y,\n                value(*RIGHT_TRACKPAD_Y_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_THUMBSTICK_CLICK_ID) {\n        let source = ct(\n            s_set,\n            *RIGHT_THUMBSTICK_CLICK_ID,\n            *RIGHT_THUMBSTICK_TOUCH_ID,\n        );\n        if d_set.contains(&*RIGHT_THUMBSTICK_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(\n                    d_set,\n                    *RIGHT_THUMBSTICK_CLICK_ID,\n                    *RIGHT_THUMBSTICK_TOUCH_ID,\n                ),\n                config,\n            ));\n        } else if d_set.contains(&*RIGHT_TRACKPAD_CLICK_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                ct(d_set, *RIGHT_TRACKPAD_CLICK_ID, *RIGHT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n\n    // Thumbrests\n    if s_set.contains(&*LEFT_THUMBREST_TOUCH_ID) {\n        let source = value(*LEFT_THUMBREST_TOUCH_ID);\n        if d_set.contains(&*LEFT_THUMBREST_TOUCH_ID) {\n            bindings.extend(map_button_pair_automatic(source, source, config));\n        } else if d_set.contains(&*LEFT_TRACKPAD_TOUCH_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                value(*LEFT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n    if s_set.contains(&*RIGHT_THUMBREST_TOUCH_ID) {\n        let source = value(*RIGHT_THUMBREST_TOUCH_ID);\n        if d_set.contains(&*RIGHT_THUMBREST_TOUCH_ID) {\n            bindings.extend(map_button_pair_automatic(source, source, config));\n        } else if d_set.contains(&*RIGHT_TRACKPAD_TOUCH_ID) {\n            bindings.extend(map_button_pair_automatic(\n                source,\n                value(*RIGHT_TRACKPAD_TOUCH_ID),\n                config,\n            ));\n        }\n    }\n\n    bindings\n}\n\npub struct ButtonMappingManager {\n    mappings: HashMap<u64, Vec<BindingTarget>>,\n    binary_source_states: HashMap<u64, bool>,\n    hysteresis_states: HashMap<u64, HashMap<u64, bool>>,\n}\n\nimpl ButtonMappingManager {\n    pub fn new_automatic(\n        source: &HashSet<u64>,\n        controllers_emulation_mode: &ControllersEmulationMode,\n        button_mapping_config: &AutomaticButtonMappingConfig,\n    ) -> Self {\n        let button_set = registered_button_set(controllers_emulation_mode);\n        Self {\n            mappings: automatic_bindings(source, &button_set, button_mapping_config),\n            binary_source_states: HashMap::new(),\n            hysteresis_states: HashMap::new(),\n        }\n    }\n\n    pub fn new_manual(mappings: &[(String, Vec<ButtonBindingTarget>)]) -> Self {\n        let mappings = mappings\n            .iter()\n            .map(|(key, value)| {\n                (\n                    alvr_common::hash_string(key),\n                    value\n                        .iter()\n                        .map(|b| BindingTarget {\n                            destination: alvr_common::hash_string(&b.destination),\n                            mapping_type: b.mapping_type.clone(),\n                            binary_conditions: b\n                                .binary_conditions\n                                .iter()\n                                .map(|c| alvr_common::hash_string(c))\n                                .collect(),\n                        })\n                        .collect(),\n                )\n            })\n            .collect();\n\n        Self {\n            mappings,\n            binary_source_states: HashMap::new(),\n            hysteresis_states: HashMap::new(),\n        }\n    }\n\n    // Apply any button changes that are mapped to this specific button\n    pub fn map_button(&mut self, source_button: &ButtonEntry) -> Vec<ButtonEntry> {\n        if let ButtonValue::Binary(value) = source_button.value {\n            let val_ref = self\n                .binary_source_states\n                .entry(source_button.path_id)\n                .or_default();\n\n            if value == *val_ref {\n                return vec![];\n            }\n\n            // NB: Update value\n            *val_ref = value;\n        }\n\n        let mut destination_buttons = vec![];\n\n        if let Some(mappings) = self.mappings.get(&source_button.path_id) {\n            'mapping: for mapping in mappings {\n                let destination_value = match (&mapping.mapping_type, source_button.value) {\n                    (ButtonMappingType::Passthrough, value) => value,\n                    (\n                        ButtonMappingType::HysteresisThreshold(threshold),\n                        ButtonValue::Scalar(value),\n                    ) => {\n                        let state = self\n                            .hysteresis_states\n                            .entry(source_button.path_id)\n                            .or_default()\n                            .entry(mapping.destination)\n                            .or_default();\n\n                        if *state && value < threshold.value - threshold.deviation {\n                            *state = false;\n                        } else if !*state && value > threshold.value + threshold.deviation {\n                            *state = true;\n                        } else {\n                            // No change needed\n                            continue;\n                        }\n\n                        ButtonValue::Binary(*state)\n                    }\n                    (ButtonMappingType::BinaryToScalar(levels), ButtonValue::Binary(value)) => {\n                        if value {\n                            ButtonValue::Scalar(levels.on)\n                        } else {\n                            ButtonValue::Scalar(levels.off)\n                        }\n                    }\n                    (ButtonMappingType::Remap(range), ButtonValue::Scalar(value)) => {\n                        let value = (value - range.min) / (range.max - range.min);\n                        ButtonValue::Scalar(value.clamp(0.0, 1.0))\n                    }\n                    _ => {\n                        error!(\"Failed to map button!\");\n                        continue;\n                    }\n                };\n\n                for source_id in &mapping.binary_conditions {\n                    if !self\n                        .binary_source_states\n                        .get(source_id)\n                        .copied()\n                        .unwrap_or(false)\n                    {\n                        continue 'mapping;\n                    }\n                }\n\n                destination_buttons.push(ButtonEntry {\n                    path_id: mapping.destination,\n                    value: destination_value,\n                });\n            }\n        } else {\n            let button_name = BUTTON_INFO\n                .get(&source_button.path_id)\n                .map_or(\"Unknown\", |info| info.path);\n            info!(\"Received button not mapped: {button_name}\");\n        }\n\n        destination_buttons\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/lib.rs",
    "content": "mod bitrate;\nmod c_api;\nmod connection;\nmod hand_gestures;\nmod haptics;\nmod input_mapping;\nmod logging_backend;\nmod sockets;\nmod statistics;\nmod tracking;\nmod web_server;\n\npub use c_api::*;\npub use logging_backend::init_logging;\npub use tracking::HandType;\n\nuse crate::connection::VideoPacket;\nuse alvr_common::{\n    ConnectionState, DEVICE_ID_TO_PATH, DeviceMotion, LifecycleState, Pose, RelaxedAtomic,\n    ViewParams, dbg_server_core, error,\n    glam::Vec2,\n    parking_lot::{Mutex, RwLock},\n    settings_schema::Switch,\n    warn,\n};\nuse alvr_events::{EventType, HapticsEvent};\nuse alvr_filesystem as afs;\nuse alvr_packets::{\n    BatteryInfo, ButtonEntry, ClientConnectionsAction, DecoderInitializationConfig, Haptics,\n    VideoPacketHeader,\n};\nuse alvr_server_io::ServerSessionManager;\nuse alvr_session::{CodecType, OpenvrProperty, Settings};\nuse alvr_sockets::StreamSender;\nuse bitrate::{BitrateManager, DynamicEncoderParams};\nuse statistics::StatisticsManager;\nuse std::{\n    collections::HashSet,\n    env,\n    ffi::OsStr,\n    fs::File,\n    io::Write,\n    sync::{\n        Arc, LazyLock, OnceLock,\n        atomic::{AtomicBool, Ordering},\n        mpsc::{self, SyncSender, TrySendError},\n    },\n    thread::{self, JoinHandle},\n    time::{Duration, Instant},\n};\nuse tokio::{runtime::Runtime, sync::broadcast};\nuse tracking::TrackingManager;\n\nstatic FILESYSTEM_LAYOUT: OnceLock<afs::Layout> = OnceLock::new();\n\n// This is lazily initialized when initializing logging or ServerCoreContext. So FILESYSTEM_LAYOUT\n// needs to be initialized first using initialize_environment().\n// NB: this must remain a global because only one instance should exist for the whole application\n// execution time.\nstatic SESSION_MANAGER: LazyLock<RwLock<ServerSessionManager>> = LazyLock::new(|| {\n    RwLock::new(ServerSessionManager::new(\n        FILESYSTEM_LAYOUT.get().map(|l| l.session()),\n    ))\n});\n\npub fn initialize_environment(layout: afs::Layout) {\n    FILESYSTEM_LAYOUT.set(layout).unwrap();\n\n    // This ensures that the session is written to disk\n    SESSION_MANAGER.write().session_mut();\n}\n\npub enum ServerCoreEvent {\n    SetOpenvrProperty {\n        device_id: u64,\n        prop: OpenvrProperty,\n    },\n    ClientConnected,\n    ClientDisconnected,\n    Battery(BatteryInfo),\n    PlayspaceSync(Vec2),\n    LocalViewParams([ViewParams; 2]), // In relation to head\n    Tracking {\n        poll_timestamp: Duration,\n    },\n    Buttons(Vec<ButtonEntry>), // Note: this is after mapping\n    RequestIDR,\n    CaptureFrame,\n    GameRenderLatencyFeedback(Duration), // only used for SteamVR\n    ShutdownPending,\n    RestartPending,\n    ProximityState(bool),\n}\n\npub struct ConnectionContext {\n    events_sender: mpsc::Sender<ServerCoreEvent>,\n    statistics_manager: RwLock<Option<StatisticsManager>>,\n    bitrate_manager: Mutex<BitrateManager>,\n    tracking_manager: RwLock<TrackingManager>,\n    decoder_config: Mutex<Option<DecoderInitializationConfig>>,\n    video_mirror_sender: Mutex<Option<broadcast::Sender<Vec<u8>>>>,\n    video_recording_file: Mutex<Option<File>>,\n    connection_threads: Mutex<Vec<JoinHandle<()>>>,\n    clients_to_be_removed: Mutex<HashSet<String>>,\n    video_channel_sender: Mutex<Option<SyncSender<VideoPacket>>>,\n    haptics_sender: Mutex<Option<StreamSender<Haptics>>>,\n}\n\npub fn create_recording_file(connection_context: &ConnectionContext, settings: &Settings) {\n    let codec = settings.video.preferred_codec;\n    let ext = match codec {\n        CodecType::H264 => \"h264\",\n        CodecType::Hevc => \"h265\",\n        CodecType::AV1 => \"av1\",\n    };\n\n    let path = FILESYSTEM_LAYOUT.get().unwrap().log_dir.join(format!(\n        \"recording.{}.{ext}\",\n        chrono::Local::now().format(\"%F.%H-%M-%S\")\n    ));\n\n    match File::create(path) {\n        Ok(mut file) => {\n            if let Some(config) = &*connection_context.decoder_config.lock() {\n                file.write_all(&config.config_buffer).ok();\n            }\n\n            *connection_context.video_recording_file.lock() = Some(file);\n\n            connection_context\n                .events_sender\n                .send(ServerCoreEvent::RequestIDR)\n                .ok();\n        }\n        Err(e) => {\n            error!(\"Failed to record video on disk: {e}\");\n        }\n    }\n}\n\npub fn notify_restart_driver() {\n    if sysinfo::System::new_all()\n        .processes_by_name(OsStr::new(&afs::dashboard_fname()))\n        .next()\n        .is_some()\n    {\n        alvr_events::send_event(EventType::ServerRequestsSelfRestart);\n    } else {\n        error!(\"Cannot restart SteamVR. No dashboard process found on local device.\");\n    }\n}\n\npub fn settings() -> Settings {\n    SESSION_MANAGER.read().settings().clone()\n}\n\npub fn registered_button_set() -> HashSet<u64> {\n    let session_manager = SESSION_MANAGER.read();\n    if let Switch::Enabled(input_mapping) = &session_manager.settings().headset.controllers {\n        input_mapping::registered_button_set(&input_mapping.emulation_mode)\n    } else {\n        HashSet::new()\n    }\n}\n\npub struct ServerCoreContext {\n    lifecycle_state: Arc<RwLock<LifecycleState>>,\n    is_restarting: RelaxedAtomic,\n    connection_context: Arc<ConnectionContext>,\n    connection_thread: Arc<RwLock<Option<JoinHandle<()>>>>,\n    webserver_runtime: Option<Runtime>,\n}\n\nimpl ServerCoreContext {\n    pub fn new() -> (Self, mpsc::Receiver<ServerCoreEvent>) {\n        dbg_server_core!(\"Creating\");\n\n        if SESSION_MANAGER\n            .read()\n            .settings()\n            .extra\n            .logging\n            .prefer_backtrace\n        {\n            unsafe { env::set_var(\"RUST_BACKTRACE\", \"1\") };\n        }\n\n        SESSION_MANAGER.write().clean_client_list();\n\n        let (events_sender, events_receiver) = mpsc::channel();\n\n        // Create a temporary StatisticsManager until a headset connects\n        let initial_settings = SESSION_MANAGER.read().settings().clone();\n        let stats = StatisticsManager::new(\n            initial_settings.connection.statistics_history_size,\n            Duration::from_secs_f32(1.0 / 90.0),\n            if let Switch::Enabled(config) = &initial_settings.headset.controllers {\n                config.steamvr_pipeline_frames\n            } else {\n                0.0\n            },\n        );\n\n        let connection_context = Arc::new(ConnectionContext {\n            events_sender,\n            statistics_manager: RwLock::new(Some(stats)),\n            bitrate_manager: Mutex::new(BitrateManager::new(256, 60.0)),\n            tracking_manager: RwLock::new(TrackingManager::new(\n                initial_settings.connection.statistics_history_size,\n            )),\n            decoder_config: Mutex::new(None),\n            video_mirror_sender: Mutex::new(None),\n            video_recording_file: Mutex::new(None),\n            connection_threads: Mutex::new(Vec::new()),\n            clients_to_be_removed: Mutex::new(HashSet::new()),\n            video_channel_sender: Mutex::new(None),\n            haptics_sender: Mutex::new(None),\n        });\n\n        let webserver_runtime = Runtime::new().unwrap();\n        webserver_runtime.spawn({\n            let connection_context = Arc::clone(&connection_context);\n            async move { alvr_common::show_err(web_server::web_server(connection_context).await) }\n        });\n\n        (\n            Self {\n                lifecycle_state: Arc::new(RwLock::new(LifecycleState::StartingUp)),\n                is_restarting: RelaxedAtomic::new(false),\n                connection_context,\n\n                connection_thread: Arc::new(RwLock::new(None)),\n                webserver_runtime: Some(webserver_runtime),\n            },\n            events_receiver,\n        )\n    }\n\n    pub fn start_connection(&self) {\n        dbg_server_core!(\"start_connection\");\n\n        // Note: Idle state is not used on the server side\n        *self.lifecycle_state.write() = LifecycleState::Resumed;\n\n        let connection_context = Arc::clone(&self.connection_context);\n        let lifecycle_state = Arc::clone(&self.lifecycle_state);\n        *self.connection_thread.write() = Some(thread::spawn(move || {\n            connection::handshake_loop(connection_context, lifecycle_state);\n        }));\n    }\n\n    pub fn get_device_motion(\n        &self,\n        device_id: u64,\n        sample_timestamp: Duration,\n    ) -> Option<DeviceMotion> {\n        dbg_server_core!(\"get_device_motion: dev={device_id} sample_ts={sample_timestamp:?}\");\n\n        self.connection_context\n            .tracking_manager\n            .read()\n            .get_device_motion(device_id, sample_timestamp)\n    }\n\n    pub fn get_hand_skeleton(\n        &self,\n        hand_type: HandType,\n        timestamp: Duration,\n    ) -> Option<[Pose; 26]> {\n        dbg_server_core!(\"get_hand_skeleton: hand={hand_type:?} ts={timestamp:?}\");\n\n        self.connection_context\n            .tracking_manager\n            .read()\n            .get_hand_skeleton(hand_type, timestamp)\n            .copied()\n    }\n\n    pub fn get_motion_to_photon_latency(&self) -> Duration {\n        dbg_server_core!(\"get_motion_to_photon_latency\");\n\n        let latency = self\n            .connection_context\n            .statistics_manager\n            .read()\n            .as_ref()\n            .map(|stats| stats.motion_to_photon_latency_average())\n            .unwrap_or_default();\n\n        let max_prediction =\n            Duration::from_millis(SESSION_MANAGER.read().settings().headset.max_prediction_ms);\n\n        if latency > max_prediction {\n            warn!(\"Latency is too high. Clamping prediction\");\n\n            max_prediction\n        } else {\n            latency\n        }\n    }\n\n    pub fn get_tracker_pose_time_offset(&self) -> Duration {\n        dbg_server_core!(\"get_tracker_pose_time_offset\");\n\n        self.connection_context\n            .statistics_manager\n            .read()\n            .as_ref()\n            .map(|stats| stats.tracker_pose_time_offset())\n            .unwrap_or_default()\n    }\n\n    pub fn send_haptics(&self, haptics: Haptics) {\n        dbg_server_core!(\"send_haptics\");\n\n        let haptics_config = {\n            let session_manager_lock = SESSION_MANAGER.read();\n\n            if session_manager_lock.settings().extra.logging.log_haptics {\n                alvr_events::send_event(EventType::Haptics(HapticsEvent {\n                    path: DEVICE_ID_TO_PATH.get(&haptics.device_id).map_or_else(\n                        || format!(\"Unknown (ID: {:#16x})\", haptics.device_id),\n                        |p| (*p).to_owned(),\n                    ),\n                    duration: haptics.duration,\n                    frequency: haptics.frequency,\n                    amplitude: haptics.amplitude,\n                }))\n            }\n\n            session_manager_lock\n                .settings()\n                .headset\n                .controllers\n                .as_option()\n                .and_then(|c| c.haptics.as_option().cloned())\n        };\n\n        if let (Some(config), Some(sender)) = (\n            haptics_config,\n            &mut *self.connection_context.haptics_sender.lock(),\n        ) {\n            sender\n                .send_header(&haptics::map_haptics(&config, haptics))\n                .ok();\n        }\n    }\n\n    pub fn set_video_config_nals(&self, config_buffer: Vec<u8>, codec: CodecType) {\n        dbg_server_core!(\"set_video_config_nals\");\n\n        if let Some(sender) = &*self.connection_context.video_mirror_sender.lock() {\n            sender.send(config_buffer.clone()).ok();\n        }\n\n        if let Some(file) = &mut *self.connection_context.video_recording_file.lock() {\n            file.write_all(&config_buffer).ok();\n        }\n\n        *self.connection_context.decoder_config.lock() = Some(DecoderInitializationConfig {\n            codec,\n            config_buffer,\n            ext_str: String::new(),\n        });\n    }\n\n    pub fn send_video_nal(\n        &self,\n        timestamp: Duration,\n        global_view_params: [ViewParams; 2],\n        is_idr: bool,\n        nal_buffer: Vec<u8>,\n    ) {\n        dbg_server_core!(\"send_video_nal\");\n\n        // start in the corrupts state, the client didn't receive the initial IDR yet.\n        static STREAM_CORRUPTED: AtomicBool = AtomicBool::new(true);\n        static LAST_IDR_INSTANT: LazyLock<Mutex<Instant>> =\n            LazyLock::new(|| Mutex::new(Instant::now()));\n\n        if let Some(sender) = &*self.connection_context.video_channel_sender.lock() {\n            let buffer_size = nal_buffer.len();\n\n            if is_idr {\n                STREAM_CORRUPTED.store(false, Ordering::SeqCst);\n            }\n\n            if let Switch::Enabled(config) = &SESSION_MANAGER\n                .read()\n                .settings()\n                .extra\n                .capture\n                .rolling_video_files\n                && Instant::now()\n                    > *LAST_IDR_INSTANT.lock() + Duration::from_secs(config.duration_s)\n            {\n                self.connection_context\n                    .events_sender\n                    .send(ServerCoreEvent::RequestIDR)\n                    .ok();\n\n                if is_idr {\n                    create_recording_file(\n                        &self.connection_context,\n                        SESSION_MANAGER.read().settings(),\n                    );\n                    *LAST_IDR_INSTANT.lock() = Instant::now();\n                }\n            }\n\n            if !STREAM_CORRUPTED.load(Ordering::SeqCst)\n                || !SESSION_MANAGER\n                    .read()\n                    .settings()\n                    .connection\n                    .avoid_video_glitching\n            {\n                if let Some(sender) = &*self.connection_context.video_mirror_sender.lock() {\n                    sender.send(nal_buffer.clone()).ok();\n                }\n\n                if let Some(file) = &mut *self.connection_context.video_recording_file.lock() {\n                    file.write_all(&nal_buffer).ok();\n                }\n\n                let sender_result = sender.try_send(VideoPacket {\n                    header: VideoPacketHeader {\n                        timestamp,\n                        global_view_params,\n                        is_idr,\n                    },\n                    payload: nal_buffer,\n                });\n                if matches!(sender_result, Err(TrySendError::Full(_))) {\n                    STREAM_CORRUPTED.store(true, Ordering::SeqCst);\n                    self.connection_context\n                        .events_sender\n                        .send(ServerCoreEvent::RequestIDR)\n                        .ok();\n                    warn!(\"Dropping video packet. Reason: Can't push to network\");\n                }\n            } else {\n                warn!(\"Dropping video packet. Reason: Waiting for IDR frame\");\n            }\n\n            if let Some(stats) = &mut *self.connection_context.statistics_manager.write() {\n                let encoder_latency = stats.report_frame_encoded(timestamp, buffer_size);\n\n                self.connection_context\n                    .bitrate_manager\n                    .lock()\n                    .report_frame_encoded(timestamp, encoder_latency, buffer_size);\n            }\n        }\n    }\n\n    pub fn get_dynamic_encoder_params(&self) -> Option<DynamicEncoderParams> {\n        dbg_server_core!(\"get_dynamic_encoder_params\");\n\n        let pair = {\n            let session_manager_lock = SESSION_MANAGER.read();\n            self.connection_context\n                .bitrate_manager\n                .lock()\n                .get_encoder_params(&session_manager_lock.settings().video.bitrate)\n        };\n\n        if let Some((params, stats)) = pair {\n            if let Some(stats_manager) = &mut *self.connection_context.statistics_manager.write() {\n                stats_manager.report_throughput_stats(stats);\n            }\n\n            Some(params)\n        } else {\n            None\n        }\n    }\n\n    pub fn report_composed(&self, target_timestamp: Duration, offset: Duration) {\n        dbg_server_core!(\"report_composed\");\n\n        if let Some(stats) = &mut *self.connection_context.statistics_manager.write() {\n            stats.report_frame_composed(target_timestamp, offset);\n        }\n    }\n\n    pub fn report_present(&self, target_timestamp: Duration, offset: Duration) {\n        dbg_server_core!(\"report_present\");\n\n        if let Some(stats) = &mut *self.connection_context.statistics_manager.write() {\n            stats.report_frame_present(target_timestamp, offset);\n        }\n\n        let session_manager_lock = SESSION_MANAGER.read();\n        self.connection_context\n            .bitrate_manager\n            .lock()\n            .report_frame_present(\n                &session_manager_lock\n                    .settings()\n                    .video\n                    .bitrate\n                    .adapt_to_framerate,\n            );\n    }\n\n    pub fn duration_until_next_vsync(&self) -> Option<Duration> {\n        dbg_server_core!(\"duration_until_next_vsync\");\n\n        self.connection_context\n            .statistics_manager\n            .write()\n            .as_mut()\n            .map(|stats| stats.duration_until_next_vsync())\n    }\n\n    pub fn restart(self) {\n        dbg_server_core!(\"restart\");\n\n        self.is_restarting.set(true);\n\n        // drop is called here for self\n    }\n}\n\nimpl Drop for ServerCoreContext {\n    fn drop(&mut self) {\n        dbg_server_core!(\"Drop\");\n\n        // Invoke connection runtimes shutdown\n        *self.lifecycle_state.write() = LifecycleState::ShuttingDown;\n\n        dbg_server_core!(\"Setting clients as Disconnecting\");\n        {\n            let mut session_manager_lock = SESSION_MANAGER.write();\n\n            let hostnames = session_manager_lock\n                .client_list()\n                .iter()\n                .filter(|&(_, info)| {\n                    !matches!(\n                        info.connection_state,\n                        ConnectionState::Disconnected | ConnectionState::Disconnecting\n                    )\n                })\n                .map(|(hostname, _)| hostname.clone())\n                .collect::<Vec<_>>();\n\n            for hostname in hostnames {\n                session_manager_lock.update_client_connections(\n                    hostname,\n                    ClientConnectionsAction::SetConnectionState(ConnectionState::Disconnecting),\n                );\n            }\n        }\n\n        dbg_server_core!(\"Joining connection thread\");\n        if let Some(thread) = self.connection_thread.write().take() {\n            thread.join().ok();\n        }\n\n        // apply openvr config for the next launch\n        dbg_server_core!(\"Setting restart settings chache\");\n        {\n            let mut session_manager_lock = SESSION_MANAGER.write();\n            session_manager_lock.session_mut().openvr_config =\n                connection::contruct_openvr_config(session_manager_lock.session());\n        }\n\n        // todo: check if this is still needed\n        while SESSION_MANAGER\n            .read()\n            .client_list()\n            .iter()\n            .any(|(_, info)| info.connection_state != ConnectionState::Disconnected)\n        {\n            thread::sleep(Duration::from_millis(100));\n        }\n\n        // Dropping the webserver runtime is bugged on linux and will prevent StemVR shutdown\n        if !cfg!(target_os = \"linux\") {\n            self.webserver_runtime.take();\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/logging_backend.rs",
    "content": "use crate::SESSION_MANAGER;\nuse alvr_common::{LogEntry, LogSeverity, log::LevelFilter};\nuse alvr_events::{Event, EventType};\nuse chrono::Local;\nuse fern::Dispatch;\nuse std::{fs, path::PathBuf, sync::LazyLock};\nuse tokio::sync::broadcast;\n\nstatic CHANNEL_CAPACITY: usize = 256;\npub static EVENTS_SENDER: LazyLock<broadcast::Sender<Event>> =\n    LazyLock::new(|| broadcast::channel(CHANNEL_CAPACITY).0);\n\npub fn init_logging(session_log_path: Option<PathBuf>, crash_log_path: Option<PathBuf>) {\n    let debug_groups_config = SESSION_MANAGER\n        .read()\n        .settings()\n        .extra\n        .logging\n        .debug_groups\n        .clone();\n\n    let mut log_dispatch = Dispatch::new()\n        // Note: meta::target() is in the format <crate>::<module>\n        .filter({\n            let debug_groups_config = debug_groups_config.clone();\n            move |meta| {\n                !meta.target().starts_with(\"mdns_sd\")\n                    && (meta.level() <= LevelFilter::Info\n                        || alvr_common::filter_debug_groups(meta.target(), &debug_groups_config))\n            }\n        })\n        .format(move |out, message, record| {\n            let maybe_event = format!(\"{message}\");\n            let event_type = if maybe_event.starts_with('{') && maybe_event.ends_with('}') {\n                serde_json::from_str(&maybe_event).unwrap()\n            } else if record.level() == LevelFilter::Debug\n                && alvr_common::is_enabled_debug_group(record.target(), &debug_groups_config)\n            {\n                EventType::DebugGroup {\n                    group: record.target().to_string(),\n                    message: message.to_string(),\n                }\n            } else {\n                EventType::Log(LogEntry {\n                    severity: LogSeverity::from_log_level(record.level()),\n                    content: message.to_string(),\n                })\n            };\n            let event = Event {\n                timestamp: Local::now().format(\"%H:%M:%S.%3f\").to_string(),\n                event_type,\n            };\n            out.finish(format_args!(\n                \"{} [{}] {}\",\n                event.timestamp,\n                event.event_type_string(),\n                event.message(),\n            ));\n\n            EVENTS_SENDER.send(event).ok();\n        });\n\n    if cfg!(debug_assertions) {\n        log_dispatch = log_dispatch.level(LevelFilter::Debug)\n    } else {\n        log_dispatch = log_dispatch.level(LevelFilter::Info);\n    }\n\n    log_dispatch = if let Some(path) = session_log_path {\n        log_dispatch.chain(\n            fs::OpenOptions::new()\n                .write(true)\n                .create(true)\n                .truncate(true)\n                .open(path)\n                .unwrap(),\n        )\n    } else if cfg!(target_os = \"linux\") {\n        // this sink is required to make sure all log gets processed and forwarded to the websocket\n        log_dispatch.chain(\n            fs::OpenOptions::new()\n                .write(true)\n                .open(\"/dev/null\")\n                .unwrap(),\n        )\n    } else {\n        log_dispatch.chain(std::io::stdout())\n    };\n\n    log_dispatch = if let Some(path) = crash_log_path {\n        log_dispatch.chain(\n            Dispatch::new()\n                .level(LevelFilter::Error)\n                .chain(fern::log_file(path).unwrap()),\n        )\n    } else if cfg!(target_os = \"linux\") {\n        log_dispatch.chain(\n            fs::OpenOptions::new()\n                .write(true)\n                .open(\"/dev/null\")\n                .unwrap(),\n        )\n    } else {\n        log_dispatch.chain(std::io::stderr())\n    };\n\n    log_dispatch.apply().unwrap();\n\n    fn popup_callback(title: &str, message: &str, severity: LogSeverity) {\n        let level = match severity {\n            LogSeverity::Error => rfd::MessageLevel::Error,\n            LogSeverity::Warning => rfd::MessageLevel::Warning,\n            LogSeverity::Info | LogSeverity::Debug => rfd::MessageLevel::Info,\n        };\n\n        rfd::MessageDialog::new()\n            .set_title(title)\n            .set_description(message)\n            .set_level(level)\n            .show();\n    }\n    alvr_common::set_popup_callback(popup_callback);\n\n    alvr_common::set_panic_hook();\n}\n"
  },
  {
    "path": "alvr/server_core/src/sockets.rs",
    "content": "use alvr_common::{\n    ToAny,\n    anyhow::{Result, bail},\n    warn,\n};\nuse flume::TryRecvError;\nuse mdns_sd::{Receiver, ServiceDaemon, ServiceEvent};\nuse std::{collections::HashMap, net::IpAddr};\n\npub struct WelcomeSocket {\n    mdns_receiver: Receiver<ServiceEvent>,\n}\n\nimpl WelcomeSocket {\n    pub fn new() -> Result<Self> {\n        let mdns_receiver = ServiceDaemon::new()?.browse(alvr_sockets::MDNS_SERVICE_TYPE)?;\n\n        Ok(Self { mdns_receiver })\n    }\n\n    // Returns: client IP, client hostname\n    pub fn recv_all(&self) -> Result<HashMap<String, IpAddr>> {\n        let mut clients = HashMap::new();\n\n        loop {\n            match self.mdns_receiver.try_recv() {\n                Ok(event) => {\n                    if let ServiceEvent::ServiceResolved(info) = event {\n                        let hostname = info\n                            .get_property_val_str(alvr_sockets::MDNS_DEVICE_ID_KEY)\n                            .unwrap_or_else(|| info.get_hostname());\n                        let address = *info.get_addresses().iter().next().to_any()?;\n\n                        let client_protocol = info\n                            .get_property_val_str(alvr_sockets::MDNS_PROTOCOL_KEY)\n                            .to_any()?;\n                        let server_protocol = alvr_common::protocol_id();\n                        let client_is_dev = client_protocol.contains(\"-dev\");\n                        let server_is_dev = server_protocol.contains(\"-dev\");\n\n                        if client_protocol != server_protocol {\n                            let reason = if client_is_dev && server_is_dev {\n                                \"Please use matching nightly versions.\"\n                            } else if client_is_dev {\n                                \"Please use nightly server or stable client.\"\n                            } else if server_is_dev {\n                                \"Please use stable server or nightly client.\"\n                            } else {\n                                \"Please use matching stable versions.\"\n                            };\n                            let protocols = format!(\n                                \"Protocols: server={server_protocol}, client={client_protocol}\"\n                            );\n                            warn!(\"Found incompatible client {hostname}! {reason}\\n{protocols}\");\n                        }\n\n                        clients.insert(hostname.into(), address);\n                    }\n                }\n                Err(TryRecvError::Empty) => break,\n                Err(e) => bail!(e),\n            }\n        }\n\n        Ok(clients)\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/statistics.rs",
    "content": "use alvr_common::{HEAD_ID, SlidingWindowAverage};\nuse alvr_events::{BitrateDirectives, EventType, GraphStatistics, StatisticsSummary};\nuse alvr_packets::ClientStatistics;\nuse std::{\n    collections::{HashMap, VecDeque},\n    time::{Duration, Instant},\n};\n\nconst FULL_REPORT_INTERVAL: Duration = Duration::from_millis(500);\nconst EPS_INTERVAL: Duration = Duration::from_micros(1);\n\npub struct HistoryFrame {\n    target_timestamp: Duration,\n    tracking_received: Instant,\n    frame_present: Instant,\n    frame_composed: Instant,\n    frame_encoded: Instant,\n    video_packet_bytes: usize,\n    total_pipeline_latency: Duration,\n}\n\nimpl Default for HistoryFrame {\n    fn default() -> Self {\n        let now = Instant::now();\n        Self {\n            target_timestamp: Duration::ZERO,\n            tracking_received: now,\n            frame_present: now,\n            frame_composed: now,\n            frame_encoded: now,\n            video_packet_bytes: 0,\n            total_pipeline_latency: Duration::ZERO,\n        }\n    }\n}\n\n#[derive(Default, Clone)]\nstruct BatteryData {\n    gauge_value: f32,\n    is_plugged: bool,\n}\n\npub struct StatisticsManager {\n    history_buffer: VecDeque<HistoryFrame>,\n    max_history_size: usize,\n    last_full_report_instant: Instant,\n    last_frame_present_instant: Instant,\n    last_frame_present_interval: Duration,\n    video_packets_total: usize,\n    video_packets_partial_sum: usize,\n    video_bytes_total: usize,\n    video_bytes_partial_sum: usize,\n    battery_gauges: HashMap<u64, BatteryData>,\n    steamvr_pipeline_latency: Duration,\n    motion_to_photon_latency_average: SlidingWindowAverage<Duration>,\n    last_vsync_time: Instant,\n    frame_interval: Duration,\n    last_throughput_directives: BitrateDirectives,\n}\n\nimpl StatisticsManager {\n    // history size used to calculate average total pipeline latency\n    pub fn new(\n        max_history_size: usize,\n        nominal_server_frame_interval: Duration,\n        steamvr_pipeline_frames: f32,\n    ) -> Self {\n        Self {\n            history_buffer: VecDeque::new(),\n            max_history_size,\n            last_full_report_instant: Instant::now(),\n            last_frame_present_instant: Instant::now(),\n            last_frame_present_interval: Duration::ZERO,\n            video_packets_total: 0,\n            video_packets_partial_sum: 0,\n            video_bytes_total: 0,\n            video_bytes_partial_sum: 0,\n            battery_gauges: HashMap::new(),\n            steamvr_pipeline_latency: Duration::from_secs_f32(\n                steamvr_pipeline_frames * nominal_server_frame_interval.as_secs_f32(),\n            ),\n            motion_to_photon_latency_average: SlidingWindowAverage::new(\n                Duration::ZERO,\n                max_history_size,\n            ),\n            last_vsync_time: Instant::now(),\n            frame_interval: nominal_server_frame_interval,\n            last_throughput_directives: BitrateDirectives::default(),\n        }\n    }\n\n    pub fn report_tracking_received(&mut self, target_timestamp: Duration) {\n        if !self\n            .history_buffer\n            .iter()\n            .any(|frame| frame.target_timestamp == target_timestamp)\n        {\n            self.history_buffer.push_front(HistoryFrame {\n                target_timestamp,\n                tracking_received: Instant::now(),\n                ..Default::default()\n            });\n        }\n\n        if self.history_buffer.len() > self.max_history_size {\n            self.history_buffer.pop_back();\n        }\n    }\n\n    pub fn report_frame_present(&mut self, target_timestamp: Duration, offset: Duration) {\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.target_timestamp == target_timestamp)\n        {\n            let now = Instant::now() - offset;\n\n            self.last_frame_present_interval =\n                now.saturating_duration_since(self.last_frame_present_instant);\n            self.last_frame_present_instant = now;\n\n            frame.frame_present = now;\n        }\n    }\n\n    pub fn report_frame_composed(&mut self, target_timestamp: Duration, offset: Duration) {\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.target_timestamp == target_timestamp)\n        {\n            frame.frame_composed = Instant::now() - offset;\n        }\n    }\n\n    // returns encoding interval\n    pub fn report_frame_encoded(\n        &mut self,\n        target_timestamp: Duration,\n        bytes_count: usize,\n    ) -> Duration {\n        self.video_packets_total += 1;\n        self.video_packets_partial_sum += 1;\n        self.video_bytes_total += bytes_count;\n        self.video_bytes_partial_sum += bytes_count;\n\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.target_timestamp == target_timestamp)\n        {\n            frame.frame_encoded = Instant::now();\n\n            frame.video_packet_bytes = bytes_count;\n\n            frame\n                .frame_encoded\n                .saturating_duration_since(frame.frame_composed)\n        } else {\n            Duration::ZERO\n        }\n    }\n\n    pub fn report_battery(&mut self, device_id: u64, gauge_value: f32, is_plugged: bool) {\n        *self.battery_gauges.entry(device_id).or_default() = BatteryData {\n            gauge_value,\n            is_plugged,\n        };\n    }\n\n    pub fn report_throughput_stats(&mut self, stats: BitrateDirectives) {\n        self.last_throughput_directives = stats;\n    }\n\n    // Called every frame. Some statistics are reported once every frame\n    // Returns (network latency, game time latency)\n    pub fn report_statistics(&mut self, client_stats: ClientStatistics) -> (Duration, Duration) {\n        self.motion_to_photon_latency_average\n            .submit_sample(client_stats.total_pipeline_latency);\n\n        if let Some(frame) = self\n            .history_buffer\n            .iter_mut()\n            .find(|frame| frame.target_timestamp == client_stats.target_timestamp)\n        {\n            frame.total_pipeline_latency = client_stats.total_pipeline_latency;\n\n            let game_time_latency = frame\n                .frame_present\n                .saturating_duration_since(frame.tracking_received);\n\n            let server_compositor_latency = frame\n                .frame_composed\n                .saturating_duration_since(frame.frame_present);\n\n            let encoder_latency = frame\n                .frame_encoded\n                .saturating_duration_since(frame.frame_composed);\n\n            // The network latency cannot be estiamed directly. It is what's left of the total\n            // latency after subtracting all other latency intervals. In particular it contains the\n            // transport latency of the tracking packet and the interval between the first video\n            // packet is sent and the last video packet is received for a specific frame.\n            // For safety, use saturating_sub to avoid a crash if for some reason the network\n            // latency is miscalculated as negative.\n            let network_latency = frame.total_pipeline_latency.saturating_sub(\n                game_time_latency\n                    + server_compositor_latency\n                    + encoder_latency\n                    + client_stats.video_decode\n                    + client_stats.video_decoder_queue\n                    + client_stats.rendering\n                    + client_stats.vsync_queue,\n            );\n\n            let client_fps =\n                1.0 / Duration::max(client_stats.frame_interval, EPS_INTERVAL).as_secs_f32();\n            let server_fps =\n                1.0 / Duration::max(self.last_frame_present_interval, EPS_INTERVAL).as_secs_f32();\n\n            if self.last_full_report_instant + FULL_REPORT_INTERVAL < Instant::now() {\n                self.last_full_report_instant += FULL_REPORT_INTERVAL;\n\n                let interval_secs = FULL_REPORT_INTERVAL.as_secs_f32();\n\n                alvr_events::send_event(EventType::StatisticsSummary(StatisticsSummary {\n                    video_packets_total: self.video_packets_total,\n                    video_packets_per_sec: (self.video_packets_partial_sum as f32 / interval_secs)\n                        as _,\n                    video_mbytes_total: (self.video_bytes_total as f32 / 1e6) as usize,\n                    video_mbits_per_sec: self.video_bytes_partial_sum as f32 * 8.\n                        / 1e6\n                        / interval_secs,\n                    total_latency_ms: client_stats.total_pipeline_latency.as_secs_f32() * 1000.,\n                    network_latency_ms: network_latency.as_secs_f32() * 1000.,\n                    encode_latency_ms: encoder_latency.as_secs_f32() * 1000.,\n                    decode_latency_ms: client_stats.video_decode.as_secs_f32() * 1000.,\n                    client_fps: client_fps as _,\n                    server_fps: server_fps as _,\n                    battery_hmd: (self\n                        .battery_gauges\n                        .get(&HEAD_ID)\n                        .cloned()\n                        .unwrap_or_default()\n                        .gauge_value\n                        * 100.) as u32,\n                    hmd_plugged: self\n                        .battery_gauges\n                        .get(&HEAD_ID)\n                        .cloned()\n                        .unwrap_or_default()\n                        .is_plugged,\n                }));\n\n                self.video_packets_partial_sum = 0;\n                self.video_bytes_partial_sum = 0;\n            }\n\n            let packet_bits = frame.video_packet_bytes as f32 * 8.0;\n            let throughput_bps =\n                packet_bits / Duration::max(network_latency, EPS_INTERVAL).as_secs_f32();\n            let bitrate_bps = packet_bits\n                / Duration::max(self.last_frame_present_interval, EPS_INTERVAL).as_secs_f32();\n\n            // todo: use target timestamp in nanoseconds. the dashboard needs to use the first\n            // timestamp as the graph time origin.\n            alvr_events::send_event(EventType::GraphStatistics(GraphStatistics {\n                total_pipeline_latency_s: client_stats.total_pipeline_latency.as_secs_f32(),\n                game_time_s: game_time_latency.as_secs_f32(),\n                server_compositor_s: server_compositor_latency.as_secs_f32(),\n                encoder_s: encoder_latency.as_secs_f32(),\n                network_s: network_latency.as_secs_f32(),\n                decoder_s: client_stats.video_decode.as_secs_f32(),\n                decoder_queue_s: client_stats.video_decoder_queue.as_secs_f32(),\n                client_compositor_s: client_stats.rendering.as_secs_f32(),\n                vsync_queue_s: client_stats.vsync_queue.as_secs_f32(),\n                client_fps,\n                server_fps,\n                bitrate_directives: self.last_throughput_directives.clone(),\n                throughput_bps,\n                bitrate_bps,\n            }));\n\n            (network_latency, game_time_latency)\n        } else {\n            (Duration::ZERO, Duration::ZERO)\n        }\n    }\n\n    pub fn motion_to_photon_latency_average(&self) -> Duration {\n        self.motion_to_photon_latency_average.get_average()\n    }\n\n    pub fn tracker_pose_time_offset(&self) -> Duration {\n        // This is the opposite of the client's StatisticsManager::tracker_prediction_offset().\n        self.steamvr_pipeline_latency\n    }\n\n    // NB: this call is non-blocking, waiting should be done externally\n    pub fn duration_until_next_vsync(&mut self) -> Duration {\n        let now = Instant::now();\n\n        // update the last vsync if it's too old\n        while self.last_vsync_time + self.frame_interval < now {\n            self.last_vsync_time += self.frame_interval;\n        }\n\n        (self.last_vsync_time + self.frame_interval).saturating_duration_since(now)\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/tracking/body.rs",
    "content": "use alvr_common::{\n    BODY_CHEST_ID, BODY_HIPS_ID, BODY_LEFT_ELBOW_ID, BODY_LEFT_FOOT_ID, BODY_LEFT_KNEE_ID,\n    BODY_RIGHT_ELBOW_ID, BODY_RIGHT_FOOT_ID, BODY_RIGHT_KNEE_ID, BodySkeleton,\n    DETACHED_CONTROLLER_LEFT_ID, DETACHED_CONTROLLER_RIGHT_ID, DeviceMotion, GENERIC_TRACKER_1_ID,\n    GENERIC_TRACKER_2_ID, GENERIC_TRACKER_3_ID, HEAD_ID, anyhow::Result, glam::Vec3,\n};\nuse alvr_session::BodyTrackingSinkConfig;\nuse rosc::{OscMessage, OscPacket, OscType};\nuse std::{collections::HashMap, net::UdpSocket, sync::LazyLock};\n\nconst CHEST_FB: usize = 5;\nconst HIPS_FB: usize = 1;\nconst LEFT_ARM_LOWER_FB: usize = 11;\nconst RIGHT_ARM_LOWER_FB: usize = 16;\nconst LEFT_LOWER_LEG_META: usize = 1;\nconst LEFT_FOOT_BALL_META: usize = 6;\nconst RIGHT_LOWER_LEG_META: usize = 8;\nconst RIGHT_FOOT_BALL_META: usize = 13;\nconst PELVIS_BD: usize = 0;\nconst LEFT_KNEE_BD: usize = 4;\nconst RIGHT_KNEE_BD: usize = 5;\nconst SPINE3_BD: usize = 9;\nconst LEFT_FOOT_BD: usize = 10;\nconst RIGHT_FOOT_BD: usize = 11;\nconst LEFT_ELBOW_BD: usize = 18;\nconst RIGHT_ELBOW_BD: usize = 19;\n\nstatic BODY_TRACKER_OSC_PATH_MAP: LazyLock<HashMap<u64, &'static str>> = LazyLock::new(|| {\n    HashMap::from([\n        (*HEAD_ID, \"/tracking/trackers/head/\"),\n        (*BODY_CHEST_ID, \"/tracking/trackers/1/\"),\n        (*BODY_HIPS_ID, \"/tracking/trackers/2/\"),\n        (*BODY_LEFT_ELBOW_ID, \"/tracking/trackers/3/\"),\n        (*BODY_RIGHT_ELBOW_ID, \"/tracking/trackers/4/\"),\n        (*BODY_LEFT_KNEE_ID, \"/tracking/trackers/5/\"),\n        (*BODY_LEFT_FOOT_ID, \"/tracking/trackers/6/\"),\n        (*BODY_RIGHT_KNEE_ID, \"/tracking/trackers/7/\"),\n        (*BODY_RIGHT_FOOT_ID, \"/tracking/trackers/8/\"),\n    ])\n});\n\npub struct BodyTrackingSink {\n    config: BodyTrackingSinkConfig,\n    socket: Option<UdpSocket>,\n}\n\nimpl BodyTrackingSink {\n    pub fn new(config: BodyTrackingSinkConfig, local_osc_port: u16) -> Result<Self> {\n        match config {\n            BodyTrackingSinkConfig::VrchatBodyOsc { port } => {\n                let socket = UdpSocket::bind(format!(\"127.0.0.1:{local_osc_port}\"))?;\n                socket.connect(format!(\"127.0.0.1:{port}\"))?;\n\n                Ok(Self {\n                    config,\n                    socket: Some(socket),\n                })\n            }\n            BodyTrackingSinkConfig::FakeViveTracker => Ok(Self {\n                config,\n                socket: None,\n            }),\n        }\n    }\n\n    fn send_osc_message(&self, path: &str, args: Vec<OscType>) {\n        if let Some(socket) = &self.socket {\n            socket\n                .send(\n                    &rosc::encoder::encode(&OscPacket::Message(OscMessage {\n                        addr: path.into(),\n                        args,\n                    }))\n                    .unwrap(),\n                )\n                .ok();\n        }\n    }\n\n    pub fn send_tracking(&self, device_motions: &[(u64, DeviceMotion)]) {\n        match self.config {\n            BodyTrackingSinkConfig::VrchatBodyOsc { .. } => {\n                for (id, motion) in device_motions {\n                    if BODY_TRACKER_OSC_PATH_MAP.contains_key(id) {\n                        // Only do position because rotation isn't quite right\n                        let position = motion.pose.position;\n                        self.send_osc_message(\n                            format!(\n                                \"{}{}\",\n                                BODY_TRACKER_OSC_PATH_MAP.get(id).unwrap(),\n                                \"position\"\n                            )\n                            .as_str(),\n                            vec![\n                                OscType::Float(position.x),\n                                OscType::Float(position.y),\n                                OscType::Float(-position.z),\n                            ],\n                        );\n                    }\n                }\n            }\n            BodyTrackingSinkConfig::FakeViveTracker => {}\n        }\n    }\n}\n\npub fn get_default_body_trackers_from_detached_controllers(\n    device_motions: &[(u64, DeviceMotion)],\n) -> Vec<(u64, DeviceMotion)> {\n    let mut poses = Vec::new();\n    for (id, motion) in device_motions {\n        if *id == *DETACHED_CONTROLLER_LEFT_ID {\n            poses.push((*BODY_LEFT_ELBOW_ID, *motion));\n        } else if *id == *DETACHED_CONTROLLER_RIGHT_ID {\n            poses.push((*BODY_RIGHT_ELBOW_ID, *motion));\n        }\n    }\n\n    poses\n}\n\n// TODO: make this customizable\npub fn get_default_body_trackers_from_motion_trackers_bd(\n    device_motions: &[(u64, DeviceMotion)],\n) -> Vec<(u64, DeviceMotion)> {\n    let mut poses = Vec::new();\n    for (id, motion) in device_motions {\n        if *id == *GENERIC_TRACKER_1_ID {\n            poses.push((*BODY_HIPS_ID, *motion));\n        } else if *id == *GENERIC_TRACKER_2_ID {\n            poses.push((*BODY_LEFT_FOOT_ID, *motion));\n        } else if *id == *GENERIC_TRACKER_3_ID {\n            poses.push((*BODY_RIGHT_FOOT_ID, *motion));\n        }\n    }\n\n    poses\n}\n\n// Obtain predefined joints as trackers\n// TODO: make this customizable\npub fn extract_default_trackers(skeleton: &BodySkeleton) -> Vec<(u64, DeviceMotion)> {\n    let mut poses = Vec::new();\n\n    match skeleton {\n        BodySkeleton::Fb(skeleton) => {\n            if let Some(pose) = skeleton.upper_body[CHEST_FB] {\n                poses.push((*BODY_CHEST_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.upper_body[HIPS_FB] {\n                poses.push((*BODY_HIPS_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.upper_body[LEFT_ARM_LOWER_FB] {\n                poses.push((*BODY_LEFT_ELBOW_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.upper_body[RIGHT_ARM_LOWER_FB] {\n                poses.push((*BODY_RIGHT_ELBOW_ID, pose));\n            }\n\n            if let Some(lower_body) = skeleton.lower_body {\n                if let Some(pose) = lower_body[LEFT_LOWER_LEG_META] {\n                    poses.push((*BODY_LEFT_KNEE_ID, pose));\n                }\n\n                if let Some(pose) = lower_body[LEFT_FOOT_BALL_META] {\n                    poses.push((*BODY_LEFT_FOOT_ID, pose));\n                }\n\n                if let Some(pose) = lower_body[RIGHT_LOWER_LEG_META] {\n                    poses.push((*BODY_RIGHT_KNEE_ID, pose));\n                }\n\n                if let Some(pose) = lower_body[RIGHT_FOOT_BALL_META] {\n                    poses.push((*BODY_RIGHT_FOOT_ID, pose));\n                }\n            }\n        }\n        BodySkeleton::Bd(skeleton) => {\n            if let Some(pose) = skeleton.0[SPINE3_BD] {\n                poses.push((*BODY_HIPS_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[PELVIS_BD] {\n                poses.push((*BODY_CHEST_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[LEFT_ELBOW_BD] {\n                poses.push((*BODY_LEFT_ELBOW_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[RIGHT_ELBOW_BD] {\n                poses.push((*BODY_RIGHT_ELBOW_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[LEFT_KNEE_BD] {\n                poses.push((*BODY_LEFT_KNEE_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[LEFT_FOOT_BD] {\n                poses.push((*BODY_LEFT_FOOT_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[RIGHT_KNEE_BD] {\n                poses.push((*BODY_RIGHT_KNEE_ID, pose));\n            }\n\n            if let Some(pose) = skeleton.0[RIGHT_FOOT_BD] {\n                poses.push((*BODY_RIGHT_FOOT_ID, pose));\n            }\n        }\n    }\n\n    poses\n        .iter()\n        .map(|(id, pose)| {\n            (\n                *id,\n                DeviceMotion {\n                    pose: *pose,\n                    linear_velocity: Vec3::ZERO,\n                    angular_velocity: Vec3::ZERO,\n                },\n            )\n        })\n        .collect()\n}\n"
  },
  {
    "path": "alvr/server_core/src/tracking/face.rs",
    "content": "use alvr_common::{anyhow::Result, glam::EulerRot};\nuse alvr_packets::{FaceData, FaceExpressions};\nuse alvr_session::FaceTrackingSinkConfig;\nuse rosc::{OscMessage, OscPacket, OscType};\nuse std::{f32::consts::PI, net::UdpSocket};\n\nconst RAD_TO_DEG: f32 = 180.0 / PI;\n\nconst VRCFT_PORT: u16 = 0xA1F7;\n\npub struct FaceTrackingSink {\n    config: FaceTrackingSinkConfig,\n    socket: UdpSocket,\n    packet_buffer: Vec<u8>,\n}\n\nimpl FaceTrackingSink {\n    pub fn new(config: FaceTrackingSinkConfig, local_osc_port: u16) -> Result<Self> {\n        let port = match config {\n            FaceTrackingSinkConfig::VrchatEyeOsc { port } => port,\n            FaceTrackingSinkConfig::VrcFaceTracking => VRCFT_PORT,\n        };\n\n        let socket = UdpSocket::bind(format!(\"127.0.0.1:{local_osc_port}\"))?;\n        socket.connect(format!(\"127.0.0.1:{port}\"))?;\n\n        Ok(Self {\n            config,\n            socket,\n            packet_buffer: vec![],\n        })\n    }\n\n    fn send_osc_message(&self, path: &str, args: Vec<OscType>) {\n        self.socket\n            .send(\n                &rosc::encoder::encode(&OscPacket::Message(OscMessage {\n                    addr: path.into(),\n                    args,\n                }))\n                .unwrap(),\n            )\n            .ok();\n    }\n\n    fn append_packet_vrcft(&mut self, prefix: [u8; 8], data: &[f32]) {\n        self.packet_buffer.extend(prefix);\n\n        for val in data {\n            self.packet_buffer.extend(val.to_le_bytes());\n        }\n    }\n\n    pub fn send_tracking(&mut self, face_data: &FaceData) {\n        match self.config {\n            FaceTrackingSinkConfig::VrchatEyeOsc { .. } => {\n                if let [Some(left), Some(right)] = face_data.eyes_social {\n                    let (left_pitch, left_yaw, _) = left.to_euler(EulerRot::XYZ);\n                    let (right_pitch, right_yaw, _) = right.to_euler(EulerRot::XYZ);\n\n                    self.send_osc_message(\n                        \"/tracking/eye/LeftRightPitchYaw\",\n                        vec![\n                            OscType::Float(-left_pitch * RAD_TO_DEG),\n                            OscType::Float(-left_yaw * RAD_TO_DEG),\n                            OscType::Float(-right_pitch * RAD_TO_DEG),\n                            OscType::Float(-right_yaw * RAD_TO_DEG),\n                        ],\n                    );\n                } else if let Some(quat) = face_data.eyes_combined {\n                    let (pitch, yaw, _) = quat.to_euler(EulerRot::XYZ);\n\n                    self.send_osc_message(\n                        \"/tracking/eye/CenterPitchYaw\",\n                        vec![\n                            OscType::Float(-pitch * RAD_TO_DEG),\n                            OscType::Float(-yaw * RAD_TO_DEG),\n                        ],\n                    );\n                }\n\n                let (left_eye_blink, right_eye_blink) = match &face_data.face_expressions {\n                    Some(FaceExpressions::Fb(items)) => (Some(items[12]), Some(items[13])),\n                    Some(FaceExpressions::Pico(items)) => (Some(items[28]), Some(items[38])),\n                    Some(FaceExpressions::Htc { eye, .. }) => {\n                        (eye.as_ref().map(|v| v[0]), eye.as_ref().map(|v| v[2]))\n                    }\n                    _ => (None, None),\n                };\n\n                if let (Some(left), Some(right)) = (left_eye_blink, right_eye_blink) {\n                    self.send_osc_message(\n                        \"/tracking/eye/EyesClosedAmount\",\n                        vec![OscType::Float((left + right) / 2.0)],\n                    );\n                } else if let Some(blink) = left_eye_blink.or(right_eye_blink) {\n                    self.send_osc_message(\n                        \"/tracking/eye/EyesClosedAmount\",\n                        vec![OscType::Float(blink)],\n                    );\n                }\n            }\n            FaceTrackingSinkConfig::VrcFaceTracking => {\n                self.packet_buffer.clear();\n\n                if let [Some(left_quat), Some(right_quat)] = face_data.eyes_social {\n                    let mut vec = left_quat.to_array().to_vec();\n                    vec.extend_from_slice(&right_quat.to_array());\n                    self.append_packet_vrcft(*b\"EyesQuat\", &vec);\n                } else if let Some(quat) = face_data.eyes_combined {\n                    self.append_packet_vrcft(*b\"CombQuat\", &quat.to_array());\n                }\n\n                match &face_data.face_expressions {\n                    Some(FaceExpressions::Fb(items)) => {\n                        self.append_packet_vrcft(*b\"Face2Fb\\0\", items);\n                    }\n                    Some(FaceExpressions::Pico(items)) => {\n                        self.append_packet_vrcft(*b\"FacePico\", items);\n                    }\n                    Some(FaceExpressions::Htc { eye, lip }) => {\n                        if let Some(arr) = eye {\n                            self.append_packet_vrcft(*b\"EyesHtc\\0\", arr);\n                        }\n\n                        if let Some(arr) = lip {\n                            self.append_packet_vrcft(*b\"LipHtc\\0\\0\", arr);\n                        }\n                    }\n                    None => (),\n                }\n\n                self.socket.send(&self.packet_buffer).ok();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/tracking/mod.rs",
    "content": "mod body;\nmod face;\nmod vmc;\n\npub use body::*;\npub use face::*;\npub use vmc::*;\n\nuse crate::{\n    ConnectionContext, SESSION_MANAGER, ServerCoreEvent,\n    connection::STREAMING_RECV_TIMEOUT,\n    hand_gestures::{self, HAND_GESTURE_BUTTON_SET, HandGestureManager},\n    input_mapping::ButtonMappingManager,\n};\nuse alvr_common::{\n    BODY_CHEST_ID, BODY_HIPS_ID, BODY_LEFT_ELBOW_ID, BODY_LEFT_FOOT_ID, BODY_LEFT_KNEE_ID,\n    BODY_RIGHT_ELBOW_ID, BODY_RIGHT_FOOT_ID, BODY_RIGHT_KNEE_ID, ConnectionError,\n    DEVICE_ID_TO_PATH, DeviceMotion, HAND_LEFT_ID, HAND_RIGHT_ID, HEAD_ID, Pose, ViewParams,\n    glam::{Quat, Vec3},\n    parking_lot::Mutex,\n};\nuse alvr_events::{EventType, TrackingEvent};\nuse alvr_packets::TrackingData;\nuse alvr_session::{\n    BodyTrackingConfig, HeadsetConfig, PositionRecenteringMode, RotationRecenteringMode, Settings,\n    VMCConfig, settings_schema::Switch,\n};\nuse alvr_sockets::StreamReceiver;\nuse std::{\n    cmp::Ordering,\n    collections::{HashMap, VecDeque},\n    f32::consts::PI,\n    sync::Arc,\n    time::Duration,\n};\n\nconst DEG_TO_RAD: f32 = PI / 180.0;\n\n#[derive(Debug)]\npub enum HandType {\n    Left = 0,\n    Right = 1,\n}\n\n// todo: Move this struct to Settings and use it for every tracked device\n#[derive(Default)]\nstruct MotionConfig {\n    // Position offset applied after rotation offset\n    pose_offset: Pose,\n    linear_velocity_cutoff: f32,\n    angular_velocity_cutoff: f32,\n}\n\npub struct TrackingManager {\n    last_head_pose: Pose,             // client's reference space\n    inverse_recentering_origin: Pose, // client's reference space\n    device_motions_history: HashMap<u64, VecDeque<(Duration, DeviceMotion)>>,\n    hand_skeletons_history: [VecDeque<(Duration, [Pose; 26])>; 2],\n    max_history_size: usize,\n}\n\nimpl TrackingManager {\n    pub fn new(max_history_size: usize) -> TrackingManager {\n        TrackingManager {\n            last_head_pose: Pose::IDENTITY,\n            inverse_recentering_origin: Pose::IDENTITY,\n            device_motions_history: HashMap::new(),\n            hand_skeletons_history: [VecDeque::new(), VecDeque::new()],\n            max_history_size,\n        }\n    }\n\n    pub fn recenter(\n        &mut self,\n        position_recentering_mode: PositionRecenteringMode,\n        rotation_recentering_mode: RotationRecenteringMode,\n    ) {\n        let position = match position_recentering_mode {\n            PositionRecenteringMode::Disabled => Vec3::ZERO,\n            PositionRecenteringMode::LocalFloor => {\n                let mut pos = self.last_head_pose.position;\n                pos.y = 0.0;\n\n                pos\n            }\n            PositionRecenteringMode::Local { view_height } => {\n                self.last_head_pose.position - Vec3::new(0.0, view_height, 0.0)\n            }\n        };\n\n        let orientation = match rotation_recentering_mode {\n            RotationRecenteringMode::Disabled => Quat::IDENTITY,\n            RotationRecenteringMode::Yaw => {\n                let mut rot = self.last_head_pose.orientation;\n                // extract yaw rotation\n                rot.x = 0.0;\n                rot.z = 0.0;\n                rot = rot.normalize();\n\n                rot\n            }\n            RotationRecenteringMode::Tilted => self.last_head_pose.orientation,\n        };\n\n        self.inverse_recentering_origin = Pose {\n            position,\n            orientation,\n        }\n        .inverse();\n    }\n\n    pub fn recenter_pose(&self, pose: Pose) -> Pose {\n        self.inverse_recentering_origin * pose\n    }\n\n    pub fn recenter_motion(&self, motion: DeviceMotion) -> DeviceMotion {\n        self.inverse_recentering_origin * motion\n    }\n\n    // Performs all kinds of tracking transformations, driven by settings.\n    pub fn report_device_motions(\n        &mut self,\n        headset_config: &HeadsetConfig,\n        timestamp: Duration,\n        device_motions: &[(u64, DeviceMotion)],\n    ) {\n        let mut device_motion_configs = HashMap::new();\n        device_motion_configs.insert(*HEAD_ID, MotionConfig::default());\n        device_motion_configs.extend([\n            (*BODY_CHEST_ID, MotionConfig::default()),\n            (*BODY_HIPS_ID, MotionConfig::default()),\n            (*BODY_LEFT_ELBOW_ID, MotionConfig::default()),\n            (*BODY_RIGHT_ELBOW_ID, MotionConfig::default()),\n            (*BODY_LEFT_KNEE_ID, MotionConfig::default()),\n            (*BODY_LEFT_FOOT_ID, MotionConfig::default()),\n            (*BODY_RIGHT_KNEE_ID, MotionConfig::default()),\n            (*BODY_RIGHT_FOOT_ID, MotionConfig::default()),\n        ]);\n\n        if let Switch::Enabled(controllers) = &headset_config.controllers {\n            device_motion_configs.insert(\n                *HAND_LEFT_ID,\n                MotionConfig {\n                    pose_offset: Pose::IDENTITY,\n                    linear_velocity_cutoff: controllers.linear_velocity_cutoff,\n                    angular_velocity_cutoff: controllers.angular_velocity_cutoff * DEG_TO_RAD,\n                },\n            );\n\n            device_motion_configs.insert(\n                *HAND_RIGHT_ID,\n                MotionConfig {\n                    pose_offset: Pose::IDENTITY,\n                    linear_velocity_cutoff: controllers.linear_velocity_cutoff,\n                    angular_velocity_cutoff: controllers.angular_velocity_cutoff * DEG_TO_RAD,\n                },\n            );\n        }\n\n        for &(device_id, mut motion) in device_motions {\n            if device_id == *HEAD_ID {\n                self.last_head_pose = motion.pose;\n            }\n\n            if let Some(config) = device_motion_configs.get(&device_id) {\n                motion = self.recenter_motion(motion);\n\n                motion.pose = motion.pose * config.pose_offset;\n\n                fn cutoff(v: Vec3, threshold: f32) -> Vec3 {\n                    if v.length_squared() > threshold * threshold {\n                        v\n                    } else {\n                        Vec3::ZERO\n                    }\n                }\n\n                motion.linear_velocity =\n                    cutoff(motion.linear_velocity, config.linear_velocity_cutoff);\n                motion.angular_velocity =\n                    cutoff(motion.angular_velocity, config.angular_velocity_cutoff);\n            }\n\n            if let Some(motions) = self.device_motions_history.get_mut(&device_id) {\n                motions.push_front((timestamp, motion));\n\n                if motions.len() > self.max_history_size {\n                    motions.pop_back();\n                }\n            } else {\n                self.device_motions_history\n                    .insert(device_id, VecDeque::from(vec![(timestamp, motion)]));\n            }\n        }\n    }\n\n    // If the exact sample_timestamp is not found, use the closest one if it's not older. This makes\n    // sure that we return None if there is no newer sample and always return Some otherwise.\n    pub fn get_device_motion(\n        &self,\n        device_id: u64,\n        sample_timestamp: Duration,\n    ) -> Option<DeviceMotion> {\n        self.device_motions_history\n            .get(&device_id)\n            .and_then(|motions| {\n                // Get first element to initialize a valid motion reference\n                if let Some((_, motion)) = motions.front() {\n                    let mut best_timestamp_diff = Duration::MAX;\n                    let mut best_motion_ref = motion;\n\n                    // Note: we are iterating from most recent to oldest\n                    for (ts, m) in motions {\n                        match ts.cmp(&sample_timestamp) {\n                            Ordering::Equal => return Some(*m),\n                            Ordering::Greater => {\n                                let diff = ts.saturating_sub(sample_timestamp);\n                                if diff < best_timestamp_diff {\n                                    best_timestamp_diff = diff;\n                                    best_motion_ref = m;\n                                }\n                            }\n                            Ordering::Less => continue,\n                        }\n                    }\n\n                    (best_timestamp_diff != Duration::MAX).then_some(*best_motion_ref)\n                } else {\n                    None\n                }\n            })\n    }\n\n    pub fn report_hand_skeleton(\n        &mut self,\n        hand_type: HandType,\n        timestamp: Duration,\n        mut skeleton: [Pose; 26],\n    ) {\n        for pose in &mut skeleton {\n            *pose = self.recenter_pose(*pose);\n        }\n\n        let skeleton_history = &mut self.hand_skeletons_history[hand_type as usize];\n\n        skeleton_history.push_back((timestamp, skeleton));\n\n        if skeleton_history.len() > self.max_history_size {\n            skeleton_history.pop_front();\n        }\n    }\n\n    pub fn get_hand_skeleton(\n        &self,\n        hand_type: HandType,\n        sample_timestamp: Duration,\n    ) -> Option<&[Pose; 26]> {\n        self.hand_skeletons_history[hand_type as usize]\n            .iter()\n            .find(|(timestamp, _)| *timestamp == sample_timestamp)\n            .map(|(_, skeleton)| skeleton)\n    }\n\n    pub fn unrecenter_view_params(&self, view_params: &mut [ViewParams; 2]) {\n        for params in view_params {\n            params.pose = self.inverse_recentering_origin.inverse() * params.pose;\n        }\n    }\n}\n\npub fn tracking_loop(\n    ctx: &ConnectionContext,\n    initial_settings: Settings,\n    hand_gesture_manager: Arc<Mutex<HandGestureManager>>,\n    mut tracking_receiver: StreamReceiver<TrackingData>,\n    is_streaming: impl Fn() -> bool,\n) {\n    let mut gestures_button_mapping_manager =\n        initial_settings\n            .headset\n            .controllers\n            .as_option()\n            .map(|config| {\n                ButtonMappingManager::new_automatic(\n                    &HAND_GESTURE_BUTTON_SET,\n                    &config.emulation_mode,\n                    &config.button_mapping_config,\n                )\n            });\n\n    let mut face_tracking_sink = initial_settings\n        .headset\n        .face_tracking\n        .into_option()\n        .and_then(|config| {\n            FaceTrackingSink::new(config.sink, initial_settings.connection.osc_local_port).ok()\n        });\n\n    let mut body_tracking_sink = initial_settings\n        .headset\n        .body_tracking\n        .into_option()\n        .and_then(|config| {\n            BodyTrackingSink::new(config.sink, initial_settings.connection.osc_local_port).ok()\n        });\n\n    let mut vmc_sink = initial_settings\n        .headset\n        .vmc\n        .into_option()\n        .and_then(|config| VMCSink::new(config).ok());\n\n    while is_streaming() {\n        let data = match tracking_receiver.recv(STREAMING_RECV_TIMEOUT) {\n            Ok(tracking) => tracking,\n            Err(ConnectionError::TryAgain(_)) => continue,\n            Err(ConnectionError::Other(_)) => return,\n        };\n        let Ok(mut tracking) = data.get_header() else {\n            return;\n        };\n\n        let timestamp = tracking.poll_timestamp;\n\n        if let Some(stats) = &mut *ctx.statistics_manager.write() {\n            stats.report_tracking_received(timestamp);\n        }\n\n        let controllers_config = {\n            let data_lock = SESSION_MANAGER.read();\n            data_lock\n                .settings()\n                .headset\n                .controllers\n                .clone()\n                .into_option()\n        };\n\n        let device_motion_keys = {\n            let mut tracking_manager_lock = ctx.tracking_manager.write();\n            let session_manager_lock = SESSION_MANAGER.read();\n            let headset_config = &session_manager_lock.settings().headset;\n\n            tracking.device_motions.extend_from_slice(\n                &body::get_default_body_trackers_from_detached_controllers(\n                    &tracking.device_motions,\n                ),\n            );\n            tracking.device_motions.extend_from_slice(\n                &body::get_default_body_trackers_from_motion_trackers_bd(&tracking.device_motions),\n            );\n            if let Some(skeleton) = &tracking.body {\n                tracking\n                    .device_motions\n                    .extend_from_slice(&body::extract_default_trackers(skeleton));\n            }\n\n            let device_motion_keys = tracking\n                .device_motions\n                .iter()\n                .map(|(id, _)| *id)\n                .collect::<Vec<_>>();\n\n            let velocity_multiplier = session_manager_lock.settings().extra.velocities_multiplier;\n            tracking.device_motions.iter_mut().for_each(|(_, motion)| {\n                motion.linear_velocity *= velocity_multiplier;\n                motion.angular_velocity *= velocity_multiplier;\n            });\n\n            tracking_manager_lock.report_device_motions(\n                headset_config,\n                timestamp,\n                &tracking.device_motions,\n            );\n\n            if let Some(skeleton) = tracking.hand_skeletons[0] {\n                tracking_manager_lock.report_hand_skeleton(HandType::Left, timestamp, skeleton);\n            }\n            if let Some(skeleton) = tracking.hand_skeletons[1] {\n                tracking_manager_lock.report_hand_skeleton(HandType::Right, timestamp, skeleton);\n            }\n\n            if let Some(sink) = &mut face_tracking_sink {\n                sink.send_tracking(&tracking.face);\n            }\n\n            if session_manager_lock.settings().extra.logging.log_tracking {\n                let device_motions = device_motion_keys\n                    .iter()\n                    .filter_map(move |id| {\n                        Some((\n                            (*DEVICE_ID_TO_PATH.get(id)?).into(),\n                            tracking_manager_lock\n                                .get_device_motion(*id, timestamp)\n                                .unwrap(),\n                        ))\n                    })\n                    .collect::<Vec<(String, DeviceMotion)>>();\n\n                alvr_events::send_event(EventType::Tracking(Box::new(TrackingEvent {\n                    device_motions,\n                    hand_skeletons: tracking.hand_skeletons,\n                    face: tracking.face,\n                })))\n            }\n\n            device_motion_keys\n        };\n\n        // Handle hand gestures\n        if let (Some(gestures_config), Some(gestures_button_mapping_manager)) = (\n            controllers_config\n                .as_ref()\n                .and_then(|c| c.hand_tracking_interaction.as_option()),\n            &mut gestures_button_mapping_manager,\n        ) {\n            let mut hand_gesture_manager_lock = hand_gesture_manager.lock();\n\n            if !device_motion_keys.contains(&*HAND_LEFT_ID)\n                && let Some(hand_skeleton) = tracking.hand_skeletons[0]\n            {\n                ctx.events_sender\n                    .send(ServerCoreEvent::Buttons(\n                        hand_gestures::trigger_hand_gesture_actions(\n                            gestures_button_mapping_manager,\n                            *HAND_LEFT_ID,\n                            &hand_gesture_manager_lock.get_active_gestures(\n                                &hand_skeleton,\n                                gestures_config,\n                                *HAND_LEFT_ID,\n                            ),\n                            gestures_config.only_touch,\n                        ),\n                    ))\n                    .ok();\n            }\n            if !device_motion_keys.contains(&*HAND_RIGHT_ID)\n                && let Some(hand_skeleton) = tracking.hand_skeletons[1]\n            {\n                ctx.events_sender\n                    .send(ServerCoreEvent::Buttons(\n                        hand_gestures::trigger_hand_gesture_actions(\n                            gestures_button_mapping_manager,\n                            *HAND_RIGHT_ID,\n                            &hand_gesture_manager_lock.get_active_gestures(\n                                &hand_skeleton,\n                                gestures_config,\n                                *HAND_RIGHT_ID,\n                            ),\n                            gestures_config.only_touch,\n                        ),\n                    ))\n                    .ok();\n            }\n        }\n\n        ctx.events_sender\n            .send(ServerCoreEvent::Tracking {\n                poll_timestamp: tracking.poll_timestamp,\n            })\n            .ok();\n\n        let publish_vmc = matches!(\n            SESSION_MANAGER.read().settings().headset.vmc,\n            Switch::Enabled(VMCConfig { publish: true, .. })\n        );\n        if publish_vmc {\n            let orientation_correction = matches!(\n                SESSION_MANAGER.read().settings().headset.vmc,\n                Switch::Enabled(VMCConfig {\n                    orientation_correction: true,\n                    ..\n                })\n            );\n\n            if let Some(sink) = &mut vmc_sink {\n                let tracking_manager_lock = ctx.tracking_manager.read();\n                let device_motions = device_motion_keys\n                    .iter()\n                    .map(move |id| {\n                        (\n                            *id,\n                            tracking_manager_lock\n                                .get_device_motion(*id, timestamp)\n                                .unwrap(),\n                        )\n                    })\n                    .collect::<Vec<(u64, DeviceMotion)>>();\n\n                if let Some(skeleton) = tracking.hand_skeletons[0] {\n                    sink.send_hand_tracking(HandType::Left, &skeleton, orientation_correction);\n                }\n                if let Some(skeleton) = tracking.hand_skeletons[1] {\n                    sink.send_hand_tracking(HandType::Right, &skeleton, orientation_correction);\n                }\n                sink.send_tracking(&device_motions, orientation_correction);\n            }\n        }\n\n        let track_body = matches!(\n            SESSION_MANAGER.read().settings().headset.body_tracking,\n            Switch::Enabled(BodyTrackingConfig { tracked: true, .. })\n        );\n        if track_body && let Some(sink) = &mut body_tracking_sink {\n            let tracking_manager_lock = ctx.tracking_manager.read();\n            let device_motions = device_motion_keys\n                .iter()\n                .map(move |id| {\n                    (\n                        *id,\n                        tracking_manager_lock\n                            .get_device_motion(*id, timestamp)\n                            .unwrap(),\n                    )\n                })\n                .collect::<Vec<_>>();\n            sink.send_tracking(&device_motions);\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/tracking/vmc.rs",
    "content": "use crate::tracking::HandType;\nuse alvr_common::{\n    BODY_CHEST_ID, BODY_HIPS_ID, BODY_LEFT_ELBOW_ID, BODY_LEFT_FOOT_ID, BODY_LEFT_KNEE_ID,\n    BODY_RIGHT_ELBOW_ID, BODY_RIGHT_FOOT_ID, BODY_RIGHT_KNEE_ID, DeviceMotion, HAND_LEFT_ID,\n    HAND_RIGHT_ID, HEAD_ID, Pose, anyhow::Result, glam::Quat,\n};\nuse alvr_session::VMCConfig;\nuse rosc::{OscMessage, OscPacket, OscType};\nuse std::{collections::HashMap, net::UdpSocket, sync::LazyLock};\n\n// Transform DeviceMotion into Unity HumanBodyBones\n// https://docs.unity3d.com/ScriptReference/HumanBodyBones.html\nstatic DEVICE_MOTIONS_VMC_MAP: LazyLock<HashMap<u64, &'static str>> = LazyLock::new(|| {\n    HashMap::from([\n        (*HAND_LEFT_ID, \"LeftHand\"),\n        (*HAND_RIGHT_ID, \"RightHand\"),\n        (*BODY_CHEST_ID, \"Chest\"),\n        (*BODY_HIPS_ID, \"Hips\"),\n        (*BODY_LEFT_ELBOW_ID, \"LeftLowerArm\"),\n        (*BODY_RIGHT_ELBOW_ID, \"RightLowerArm\"),\n        (*BODY_LEFT_KNEE_ID, \"LeftLowerLeg\"),\n        (*BODY_LEFT_FOOT_ID, \"LeftFoot\"),\n        (*BODY_RIGHT_KNEE_ID, \"RightLowerLeg\"),\n        (*BODY_RIGHT_FOOT_ID, \"RightFoot\"),\n        (*HEAD_ID, \"Head\"),\n    ])\n});\n\n#[expect(clippy::approx_constant)]\nstatic DEVICE_MOTIONS_ROTATION_MAP: LazyLock<HashMap<u64, Quat>> = LazyLock::new(|| {\n    HashMap::from([\n        (\n            *HAND_LEFT_ID,\n            Quat::from_xyzw(-0.03538, 0.25483, -0.00000, -0.96634),\n        ),\n        (\n            *HAND_RIGHT_ID,\n            Quat::from_xyzw(-0.05859, -0.20524, -0.00000, 0.97696),\n        ),\n        (\n            *BODY_CHEST_ID,\n            Quat::from_xyzw(-0.49627, 0.49516, -0.43469, -0.56531),\n        ),\n        (\n            *BODY_HIPS_ID,\n            Quat::from_xyzw(-0.49274, 0.49568, -0.42416, -0.57584),\n        ),\n        (\n            *BODY_RIGHT_ELBOW_ID,\n            Quat::from_xyzw(-0.63465, -0.11567, 0.00000, 0.76410),\n        ),\n        (\n            *BODY_LEFT_KNEE_ID,\n            Quat::from_xyzw(0.51049, 0.47862, 0.42815, -0.57185),\n        ),\n        (\n            *BODY_LEFT_FOOT_ID,\n            Quat::from_xyzw(-0.59103, 0.38818, 0.00000, -0.70711),\n        ),\n        (\n            *BODY_RIGHT_KNEE_ID,\n            Quat::from_xyzw(-0.52823, 0.45434, -0.58530, -0.41470),\n        ),\n        (\n            *BODY_RIGHT_FOOT_ID,\n            Quat::from_xyzw(0.70228, -0.08246, 0.70711, 0.00000),\n        ),\n    ])\n});\n\nstatic HAND_SKELETON_VMC_MAP: [[(usize, &str); 1]; 2] = [[(0, \"LeftHand\")], [(0, \"RightHand\")]];\n\nstatic HAND_SKELETON_ROTATIONS: LazyLock<[HashMap<usize, Quat>; 2]> = LazyLock::new(|| {\n    [\n        HashMap::from([(0, Quat::from_xyzw(-0.03566, 0.25481, 0.00000, -0.96633))]),\n        HashMap::from([(0, Quat::from_xyzw(-0.05880, -0.20574, -0.00000, 0.97684))]),\n    ]\n});\n\npub struct VMCSink {\n    socket: Option<UdpSocket>,\n}\n\nimpl VMCSink {\n    pub fn new(config: VMCConfig) -> Result<Self> {\n        let socket = UdpSocket::bind(\"0.0.0.0:0\")?;\n        socket.connect(format!(\"{}:{}\", config.host, config.port))?;\n\n        Ok(Self {\n            socket: Some(socket),\n        })\n    }\n\n    fn send_osc_message(&self, path: &str, args: Vec<OscType>) {\n        if let Some(socket) = &self.socket {\n            socket\n                .send(\n                    &rosc::encoder::encode(&OscPacket::Message(OscMessage {\n                        addr: path.into(),\n                        args,\n                    }))\n                    .unwrap(),\n                )\n                .ok();\n        }\n    }\n\n    pub fn send_hand_tracking(\n        &self,\n        hand_type: HandType,\n        skeleton: &[Pose; 26],\n        orientation_correction: bool,\n    ) {\n        let hand_id = hand_type as usize;\n        for (part, vmc_str) in HAND_SKELETON_VMC_MAP[hand_id] {\n            let corrected_orientation = {\n                let mut q = skeleton[part].orientation;\n                if orientation_correction {\n                    if HAND_SKELETON_ROTATIONS[hand_id].contains_key(&part) {\n                        q *= *HAND_SKELETON_ROTATIONS[hand_id].get(&part).unwrap();\n                    }\n                    q.z = -q.z;\n                    q.w = -q.w;\n                }\n                q\n            };\n\n            self.send_osc_message(\n                \"/VMC/Ext/Bone/Pos\",\n                vec![\n                    OscType::String(vmc_str.to_string()),\n                    OscType::Float(skeleton[part].position.x),\n                    OscType::Float(skeleton[part].position.y),\n                    OscType::Float(skeleton[part].position.z),\n                    OscType::Float(corrected_orientation.x),\n                    OscType::Float(corrected_orientation.y),\n                    OscType::Float(corrected_orientation.z),\n                    OscType::Float(corrected_orientation.w),\n                ],\n            );\n        }\n    }\n\n    pub fn send_tracking(\n        &self,\n        device_motions: &[(u64, DeviceMotion)],\n        orientation_correction: bool,\n    ) {\n        for (id, motion) in device_motions {\n            if DEVICE_MOTIONS_VMC_MAP.contains_key(id) {\n                let corrected_orientation = {\n                    let mut q = motion.pose.orientation;\n                    if orientation_correction {\n                        if DEVICE_MOTIONS_ROTATION_MAP.contains_key(id) {\n                            q *= *DEVICE_MOTIONS_ROTATION_MAP.get(id).unwrap();\n                        }\n                        q.z = -q.z;\n                        q.w = -q.w;\n                    }\n                    q\n                };\n\n                self.send_osc_message(\n                    \"/VMC/Ext/Bone/Pos\",\n                    vec![\n                        OscType::String((*DEVICE_MOTIONS_VMC_MAP.get(id).unwrap()).to_string()),\n                        OscType::Float(motion.pose.position.x),\n                        OscType::Float(motion.pose.position.y),\n                        OscType::Float(motion.pose.position.z),\n                        OscType::Float(corrected_orientation.x),\n                        OscType::Float(corrected_orientation.y),\n                        OscType::Float(corrected_orientation.z),\n                        OscType::Float(corrected_orientation.w),\n                    ],\n                );\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_core/src/web_server.rs",
    "content": "use crate::{\n    ConnectionContext, FILESYSTEM_LAYOUT, SESSION_MANAGER, ServerCoreEvent,\n    logging_backend::EVENTS_SENDER,\n};\nuse alvr_common::{ConnectionState, LogEntry, anyhow::Result, error, info, log};\nuse alvr_events::{ButtonEvent, EventType};\nuse alvr_packets::{ButtonEntry, ClientConnectionsAction, FirewallRulesAction, PathValuePair};\nuse alvr_session::SessionConfig;\nuse axum::{\n    Json, Router,\n    extract::{Request, State, WebSocketUpgrade, ws::Message},\n    http::{\n        HeaderValue, Method, StatusCode,\n        header::{CACHE_CONTROL, CONTENT_TYPE},\n    },\n    middleware,\n    response::Response,\n    routing,\n};\nuse serde_json as json;\nuse std::{net::SocketAddr, path::PathBuf, sync::Arc};\nuse tokio::{net::TcpListener, sync::broadcast::error::RecvError};\nuse tower_http::{\n    cors::{self, CorsLayer},\n    set_header::SetResponseHeaderLayer,\n};\n\nconst X_ALVR: &str = \"X-ALVR\";\n\n// This is the actual core part of cors\n// We require the X-ALVR header, but the browser forces a cors preflight\n// if the site tries to send a request with it set since it's not-whitelisted\n//\n// The dashboard can just set the header and be allowed through without the preflight\n// thus not getting blocked by allow_untrusted_http being disabled\nasync fn ensure_preflight(request: Request, next: middleware::Next) -> Response {\n    if request.headers().contains_key(X_ALVR) || request.method() == Method::OPTIONS {\n        next.run(request).await\n    } else {\n        Response::builder()\n            .status(StatusCode::BAD_REQUEST)\n            .body(format!(\"missing {X_ALVR} header\").into())\n            .unwrap()\n    }\n}\n\npub async fn web_server(connection_context: Arc<ConnectionContext>) -> Result<()> {\n    let allow_untrusted_http;\n    let web_server_port;\n\n    {\n        let session_manager = SESSION_MANAGER.read();\n        allow_untrusted_http = session_manager.settings().connection.allow_untrusted_http;\n        web_server_port = session_manager.settings().connection.web_server_port;\n    }\n\n    let mut cors = CorsLayer::new()\n        .allow_methods([Method::GET, Method::POST])\n        .allow_headers([CONTENT_TYPE, X_ALVR.parse().unwrap()]);\n    if allow_untrusted_http {\n        cors = cors.allow_origin(cors::Any);\n    }\n\n    let router = Router::new()\n        .nest(\n            \"/api\",\n            Router::new()\n                .route(\"/events\", routing::get(events_websocket))\n                .route(\"/log\", routing::post(set_log))\n                .nest(\n                    \"/session\",\n                    Router::new()\n                        .route(\"/\", routing::get(get_session).post(update_session))\n                        .route(\"/values\", routing::post(set_session_values))\n                        .route(\n                            \"/client-connections\",\n                            routing::post(update_client_connections),\n                        ),\n                )\n                .route(\"/buttons\", routing::post(set_buttons))\n                .route(\"/insert-idr\", routing::post(insert_idr))\n                .route(\"/capture-frame\", routing::post(capture_frame))\n                .nest(\n                    \"/recording\",\n                    Router::new()\n                        .route(\"/start\", routing::post(start_recording))\n                        .route(\"/stop\", routing::post(stop_recording)),\n                )\n                .nest(\n                    \"/firewall-rules\",\n                    Router::new()\n                        .route(\"/add\", routing::post(add_firewall_rules))\n                        .route(\"/remove\", routing::post(remove_firewall_rules)),\n                )\n                .nest(\n                    \"/drivers\",\n                    Router::new()\n                        .route(\"/\", routing::get(get_driver_list))\n                        .route(\"/register-alvr\", routing::post(register_alvr_driver))\n                        .route(\"/unregister\", routing::post(unregister_driver)),\n                )\n                .nest(\n                    \"/steamvr\",\n                    Router::new()\n                        .route(\"/restart\", routing::post(restart_steamvr))\n                        .route(\"/shutdown\", routing::post(shutdown_steamvr)),\n                )\n                .route(\n                    \"/version\",\n                    routing::get(async || alvr_common::ALVR_VERSION.to_string()),\n                )\n                .route(\"/ping\", routing::get(async || ())),\n        )\n        .layer(cors)\n        .layer(SetResponseHeaderLayer::overriding(\n            CACHE_CONTROL,\n            HeaderValue::from_static(\"no-cache, no-store, must-revalidate\"),\n        ))\n        .layer(middleware::from_fn(ensure_preflight))\n        .with_state(connection_context);\n\n    axum::serve(\n        TcpListener::bind(SocketAddr::new([0, 0, 0, 0].into(), web_server_port))\n            .await\n            .unwrap(),\n        router,\n    )\n    .await?;\n\n    Ok(())\n}\n\nasync fn events_websocket(ws: WebSocketUpgrade) -> Response {\n    ws.on_upgrade(async |mut ws| {\n        let mut events_receiver = EVENTS_SENDER.subscribe();\n\n        loop {\n            match events_receiver.recv().await {\n                Ok(event) => {\n                    if let Err(e) = ws\n                        .send(Message::Text(json::to_string(&event).unwrap().into()))\n                        .await\n                    {\n                        info!(\"Failed to send event with websocket: {e}\");\n                        break;\n                    }\n                }\n                Err(RecvError::Lagged(_)) => (),\n                Err(RecvError::Closed) => break,\n            }\n        }\n    })\n}\n\nasync fn set_log(Json(entry): Json<LogEntry>) {\n    let level = entry.severity.into_log_level();\n    log::log!(level, \"{}\", entry.content);\n}\n\nasync fn get_session() {\n    alvr_events::send_event(EventType::Session(Box::new(\n        crate::SESSION_MANAGER.read().session().clone(),\n    )));\n}\n\nasync fn update_session(Json(config): Json<SessionConfig>) {\n    *SESSION_MANAGER.write().session_mut() = config;\n}\n\nasync fn set_session_values(Json(descs): Json<Vec<PathValuePair>>) {\n    SESSION_MANAGER.write().set_session_values(descs).ok();\n}\n\nasync fn update_client_connections(\n    State(ctx): State<Arc<ConnectionContext>>,\n    Json((hostname, mut action)): Json<(String, ClientConnectionsAction)>,\n) {\n    let mut session_manager = SESSION_MANAGER.write();\n    if matches!(action, ClientConnectionsAction::RemoveEntry)\n        && let Some(entry) = session_manager.client_list().get(&hostname)\n        && entry.connection_state != ConnectionState::Disconnected\n    {\n        ctx.clients_to_be_removed.lock().insert(hostname.clone());\n\n        action = ClientConnectionsAction::SetConnectionState(ConnectionState::Disconnecting);\n    }\n\n    session_manager.update_client_connections(hostname, action);\n}\n\nasync fn insert_idr(State(ctx): State<Arc<ConnectionContext>>) {\n    ctx.events_sender.send(ServerCoreEvent::RequestIDR).ok();\n}\n\nasync fn capture_frame(State(ctx): State<Arc<ConnectionContext>>) {\n    ctx.events_sender.send(ServerCoreEvent::CaptureFrame).ok();\n}\n\nasync fn start_recording(State(ctx): State<Arc<ConnectionContext>>) {\n    crate::create_recording_file(&ctx, crate::SESSION_MANAGER.read().settings())\n}\n\nasync fn stop_recording(State(ctx): State<Arc<ConnectionContext>>) {\n    *ctx.video_recording_file.lock() = None;\n}\n\nasync fn add_firewall_rules() {\n    if let Err(e) =\n        alvr_server_io::firewall_rules(FirewallRulesAction::Add, FILESYSTEM_LAYOUT.get().unwrap())\n    {\n        error!(\"Failed to add firewall rules! code: {e}\");\n    } else {\n        info!(\"Successfully added firewall rules!\");\n    }\n}\n\nasync fn remove_firewall_rules() {\n    if let Err(e) = alvr_server_io::firewall_rules(\n        FirewallRulesAction::Remove,\n        FILESYSTEM_LAYOUT.get().unwrap(),\n    ) {\n        error!(\"Failed to remove firewall rules! code: {e}\");\n    } else {\n        info!(\"Successfully removed firewall rules!\");\n    }\n}\n\nasync fn get_driver_list() {\n    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n        alvr_events::send_event(EventType::DriversList(list));\n    }\n}\n\nasync fn register_alvr_driver() {\n    alvr_server_io::driver_registration(\n        &[FILESYSTEM_LAYOUT\n            .get()\n            .unwrap()\n            .openvr_driver_root_dir\n            .clone()],\n        true,\n    )\n    .ok();\n\n    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n        alvr_events::send_event(EventType::DriversList(list));\n    }\n}\n\nasync fn unregister_driver(Json(path): Json<PathBuf>) {\n    alvr_server_io::driver_registration(&[path], false).ok();\n\n    if let Ok(list) = alvr_server_io::get_registered_drivers() {\n        alvr_events::send_event(EventType::DriversList(list));\n    }\n}\n\nasync fn restart_steamvr(State(ctx): State<Arc<ConnectionContext>>) {\n    ctx.events_sender.send(ServerCoreEvent::RestartPending).ok();\n}\n\nasync fn shutdown_steamvr(State(ctx): State<Arc<ConnectionContext>>) {\n    ctx.events_sender\n        .send(ServerCoreEvent::ShutdownPending)\n        .ok();\n}\n\nasync fn set_buttons(\n    State(ctx): State<Arc<ConnectionContext>>,\n    Json(button_events): Json<Vec<ButtonEvent>>,\n) {\n    let button_entries = button_events\n        .iter()\n        .map(|b| ButtonEntry {\n            path_id: alvr_common::hash_string(&b.path),\n            value: b.value,\n        })\n        .collect();\n\n    ctx.events_sender\n        .send(ServerCoreEvent::Buttons(button_entries))\n        .ok();\n}\n"
  },
  {
    "path": "alvr/server_io/Cargo.toml",
    "content": "[package]\nname = \"alvr_server_io\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_events.workspace = true\nalvr_filesystem.workspace = true\nalvr_packets.workspace = true\nalvr_session.workspace = true\n\nencoding_rs_io = \"0.1\"\ndirs = \"6\"\nrunas = \"^1.2\" # version 1.1 is broken\nserde_json = \"1\"\n"
  },
  {
    "path": "alvr/server_io/README.md",
    "content": "# alvr_server_io\n\nContains functionality for data storage and system info retrieval. Shared between server and dashboard executable.\n"
  },
  {
    "path": "alvr/server_io/src/firewall.rs",
    "content": "use crate::openvrpaths;\nuse alvr_packets::FirewallRulesAction;\nuse std::{\n    env, fs,\n    path::{Path, PathBuf},\n    process::Command,\n};\n\nfn netsh_add_rule_command_string(rule_name: &str, program_path: &Path) -> String {\n    format!(\n        \"netsh advfirewall firewall add rule name=\\\"{}\\\" dir=in program=\\\"{}\\\" action=allow\",\n        rule_name,\n        program_path.to_string_lossy()\n    )\n}\n\nfn netsh_delete_rule_command_string(rule_name: &str) -> String {\n    format!(\"netsh advfirewall firewall delete rule name=\\\"{rule_name}\\\"\")\n}\n\n// Errors:\n// 1: firewall rule is already set\n// 126: pkexec request dismissed\n// other: command failed\npub fn firewall_rules(\n    action: FirewallRulesAction,\n    filesystem_layout: &alvr_filesystem::Layout,\n) -> Result<(), i32> {\n    let exit_status = if cfg!(target_os = \"linux\") {\n        let action = if matches!(action, FirewallRulesAction::Add) {\n            \"add\"\n        } else {\n            \"remove\"\n        };\n        // run as normal user since we use pkexec to sudo\n        Command::new(\"bash\")\n            .arg(\n                PathBuf::from(\"../\").join(\n                    filesystem_layout\n                        .firewall_script_dir\n                        .join(\"alvr_fw_config.sh\"),\n                ),\n            )\n            .arg(action)\n            .status()\n            .map_err(|_| -1)?\n    } else {\n        let script_path = env::temp_dir().join(\"alvr_firewall_rules.bat\");\n        let firewall_rules_script_content = if matches!(action, FirewallRulesAction::Add) {\n            format!(\n                \"{}\\n{}\",\n                netsh_add_rule_command_string(\n                    \"SteamVR ALVR vrserver\",\n                    &openvrpaths::steamvr_root_dir()\n                        .map_err(|_| -1)?\n                        .join(\"bin\")\n                        .join(\"win64\")\n                        .join(\"vrserver.exe\")\n                ),\n                netsh_add_rule_command_string(\n                    \"SteamVR ALVR vrserver\",\n                    &openvrpaths::steamvr_root_dir()\n                        .map_err(|_| -1)?\n                        .join(\"bin\")\n                        .join(\"win32\")\n                        .join(\"vrserver.exe\")\n                ),\n            )\n        } else {\n            netsh_delete_rule_command_string(\"SteamVR ALVR vrserver\")\n        };\n        fs::write(&script_path, firewall_rules_script_content).map_err(|_| -1)?;\n\n        // run with admin privileges\n        runas::Command::new(script_path)\n            .gui(true) // UAC, if available\n            .status()\n            .map_err(|_| -1)?\n    };\n\n    if exit_status.success() {\n        Ok(())\n    } else {\n        Err(exit_status.code().unwrap())\n    }\n}\n"
  },
  {
    "path": "alvr/server_io/src/lib.rs",
    "content": "mod firewall;\nmod openvr_drivers;\nmod openvrpaths;\n\npub use firewall::*;\npub use openvr_drivers::*;\npub use openvrpaths::*;\n\nuse alvr_common::{\n    ConnectionState,\n    anyhow::{Result, bail},\n    error, info,\n};\nuse alvr_events::EventType;\nuse alvr_packets::{ClientConnectionsAction, PathSegment, PathValuePair};\nuse alvr_session::{ClientConnectionConfig, SessionConfig, Settings};\nuse serde_json as json;\nuse std::{\n    collections::{HashMap, hash_map::Entry},\n    fmt::{self, Debug},\n    fs,\n    ops::{Deref, DerefMut},\n    path::{Path, PathBuf},\n};\n\nfn save_session(session: &SessionConfig, path: &Path) -> Result<()> {\n    fs::write(path, json::to_string_pretty(session)?)?;\n\n    Ok(())\n}\n\n// SessionConfig wrapper that saves session.json on destruction.\npub struct SessionLock<'a> {\n    session_desc: &'a mut SessionConfig,\n    session_path: Option<&'a Path>,\n    settings: &'a mut Settings,\n}\n\nimpl Deref for SessionLock<'_> {\n    type Target = SessionConfig;\n    fn deref(&self) -> &SessionConfig {\n        self.session_desc\n    }\n}\n\nimpl DerefMut for SessionLock<'_> {\n    fn deref_mut(&mut self) -> &mut SessionConfig {\n        self.session_desc\n    }\n}\n\nimpl Drop for SessionLock<'_> {\n    fn drop(&mut self) {\n        if let Some(session_path) = self.session_path {\n            save_session(self.session_desc, session_path).ok();\n        }\n\n        *self.settings = self.session_desc.to_settings();\n        alvr_events::send_event(EventType::Session(Box::new(self.session_desc.clone())));\n    }\n}\n\n// Correct usage:\n// SessionManager should be used behind a Mutex. Each write of the session should be preceded by a\n// read, within the same lock.\n// fixme: the dashboard is doing this wrong because it is holding its own session state. If read and\n// write need to happen on separate threads, a critical region should be implemented.\npub struct ServerSessionManager {\n    session_config: SessionConfig,\n    settings: Settings,\n    session_path: Option<PathBuf>,\n}\n\nimpl ServerSessionManager {\n    pub fn new(session_path: Option<PathBuf>) -> Self {\n        let session_config = if let Some(session_path) = &session_path {\n            let config_dir = session_path.parent().unwrap();\n            fs::create_dir_all(config_dir).ok();\n            Self::load_session(session_path, config_dir)\n        } else {\n            SessionConfig::default()\n        };\n\n        Self {\n            session_config: session_config.clone(),\n            settings: session_config.to_settings(),\n            session_path,\n        }\n    }\n\n    fn load_session(session_path: &Path, config_dir: &Path) -> SessionConfig {\n        let session_string = fs::read_to_string(session_path).unwrap_or_default();\n\n        if session_string.is_empty() {\n            return SessionConfig::default();\n        }\n\n        let session_json = json::from_str::<json::Value>(&session_string)\n            .unwrap_or_else(|e| {\n                error!(\n                    \"{} {} {}\\n{}\",\n                    \"Failed to load session.json.\",\n                    \"Its contents will be reset and the original file content stored as session_invalid.json.\",\n                    \"See error message below for details:\",\n                    e\n                );\n                json::Value::Null\n            });\n\n        if session_json.is_null() {\n            fs::write(config_dir.join(\"session_invalid.json\"), &session_string).ok();\n            return SessionConfig::default();\n        }\n\n        json::from_value(session_json.clone()).unwrap_or_else(|_| {\n            fs::write(config_dir.join(\"session_old.json\"), &session_string).ok();\n            let mut session_desc = SessionConfig::default();\n            match session_desc.merge_from_json(&session_json) {\n                Ok(_) => info!(\n                    \"{} {}\",\n                    \"Session extrapolated successfully.\",\n                    \"Old session.json is stored as session_old.json\"\n                ),\n                Err(e) => error!(\n                    \"{} {} {}\",\n                    \"Error while extrapolating session.\",\n                    \"Old session.json is stored as session_old.json.\",\n                    e\n                ),\n            }\n            // not essential, but useful to avoid duplicated errors\n            save_session(&session_desc, session_path).ok();\n\n            session_desc\n        })\n    }\n\n    // prefer settings()\n    pub fn session(&self) -> &SessionConfig {\n        &self.session_config\n    }\n\n    pub fn session_mut(&mut self) -> SessionLock<'_> {\n        SessionLock {\n            session_desc: &mut self.session_config,\n            session_path: self.session_path.as_deref(),\n            settings: &mut self.settings,\n        }\n    }\n\n    pub fn settings(&self) -> &Settings {\n        &self.settings\n    }\n\n    // Note: \"value\" can be any session subtree, in json format.\n    pub fn set_session_values(&mut self, descs: Vec<PathValuePair>) -> Result<()> {\n        let mut session_json = serde_json::to_value(self.session_config.clone()).unwrap();\n\n        for desc in descs {\n            let mut session_ref = &mut session_json;\n            for segment in &desc.path {\n                session_ref = match segment {\n                    PathSegment::Name(name) => {\n                        if let Some(name) = session_ref.get_mut(name) {\n                            name\n                        } else {\n                            bail!(\"From path {:?}: segment \\\"{name}\\\" not found\", desc.path);\n                        }\n                    }\n                    PathSegment::Index(index) => {\n                        if let Some(index) = session_ref.get_mut(index) {\n                            index\n                        } else {\n                            bail!(\"From path {:?}: segment [{index}] not found\", desc.path);\n                        }\n                    }\n                };\n            }\n            *session_ref = desc.value.clone();\n        }\n\n        // session_json has been updated\n        self.session_config = serde_json::from_value(session_json)?;\n        self.settings = self.session_config.to_settings();\n\n        if let Some(session_path) = &self.session_path {\n            save_session(&self.session_config, session_path)?;\n        }\n\n        alvr_events::send_event(EventType::Session(Box::new(self.session_config.clone())));\n\n        Ok(())\n    }\n\n    pub fn client_list(&self) -> &HashMap<String, ClientConnectionConfig> {\n        &self.session_config.client_connections\n    }\n\n    pub fn update_client_connections(&mut self, hostname: String, action: ClientConnectionsAction) {\n        let mut client_connections = self.session_config.client_connections.clone();\n\n        let maybe_client_entry = client_connections.entry(hostname);\n\n        let mut updated = false;\n        match action {\n            ClientConnectionsAction::AddIfMissing {\n                trusted,\n                manual_ips,\n            } => {\n                if let Entry::Vacant(new_entry) = maybe_client_entry {\n                    let client_connection_desc = ClientConnectionConfig {\n                        display_name: \"Unknown\".into(),\n                        current_ip: None,\n                        manual_ips: manual_ips.into_iter().collect(),\n                        trusted,\n                        connection_state: ConnectionState::Disconnected,\n                    };\n                    new_entry.insert(client_connection_desc);\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::SetDisplayName(name) => {\n                if let Entry::Occupied(mut entry) = maybe_client_entry {\n                    entry.get_mut().display_name = name;\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::Trust => {\n                if let Entry::Occupied(mut entry) = maybe_client_entry {\n                    entry.get_mut().trusted = true;\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::SetManualIps(ips) => {\n                if let Entry::Occupied(mut entry) = maybe_client_entry {\n                    entry.get_mut().manual_ips = ips.into_iter().collect();\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::RemoveEntry => {\n                if let Entry::Occupied(entry) = maybe_client_entry {\n                    entry.remove_entry();\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::UpdateCurrentIp(current_ip) => {\n                if let Entry::Occupied(mut entry) = maybe_client_entry\n                    && entry.get().current_ip != current_ip\n                {\n                    entry.get_mut().current_ip = current_ip;\n\n                    updated = true;\n                }\n            }\n            ClientConnectionsAction::SetConnectionState(state) => {\n                if let Entry::Occupied(mut entry) = maybe_client_entry\n                    && entry.get().connection_state != state\n                {\n                    entry.get_mut().connection_state = state;\n\n                    updated = true;\n                }\n            }\n        }\n\n        if updated {\n            self.session_config.client_connections = client_connections;\n\n            if let Some(session_path) = &self.session_path {\n                save_session(&self.session_config, session_path).ok();\n            }\n            alvr_events::send_event(EventType::Session(Box::new(self.session_config.clone())));\n        }\n    }\n\n    pub fn client_hostnames(&self) -> Vec<String> {\n        self.session_config\n            .client_connections\n            .keys()\n            .cloned()\n            .collect()\n    }\n\n    // Run at the start of dashboard or server\n    pub fn clean_client_list(&mut self) {\n        let connections = self.client_list().clone();\n        for (hostname, connection) in connections {\n            if connection.trusted {\n                self.update_client_connections(\n                    hostname,\n                    ClientConnectionsAction::SetConnectionState(ConnectionState::Disconnected),\n                )\n            } else {\n                self.update_client_connections(hostname, ClientConnectionsAction::RemoveEntry);\n            }\n        }\n\n        for hostname in self.client_hostnames() {\n            self.update_client_connections(\n                hostname.clone(),\n                ClientConnectionsAction::UpdateCurrentIp(None),\n            );\n        }\n    }\n}\n\nimpl Debug for ServerSessionManager {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{:?}\", self.session_path)\n    }\n}\n"
  },
  {
    "path": "alvr/server_io/src/openvr_drivers.rs",
    "content": "use crate::openvrpaths;\nuse alvr_common::{\n    ToAny,\n    anyhow::{Result, bail},\n};\nuse serde_json as json;\nuse std::{\n    collections::{HashMap, HashSet},\n    fs,\n    path::PathBuf,\n};\n\npub fn get_registered_drivers() -> Result<Vec<PathBuf>> {\n    Ok(openvrpaths::from_openvr_paths(\n        openvrpaths::load_openvr_paths_json()?\n            .get_mut(\"external_drivers\")\n            .to_any()?,\n    ))\n}\n\npub fn driver_registration(driver_paths: &[PathBuf], register: bool) -> Result<()> {\n    let mut openvr_paths_json = openvrpaths::load_openvr_paths_json()?;\n    let paths_json_ref = openvr_paths_json.get_mut(\"external_drivers\").to_any()?;\n\n    let mut paths: HashSet<_> = openvrpaths::from_openvr_paths(paths_json_ref)\n        .into_iter()\n        .collect();\n\n    if register {\n        paths.extend(driver_paths.iter().cloned());\n    } else {\n        for path in driver_paths {\n            paths.remove(path);\n        }\n    }\n\n    // write into openvr_paths_json, the other fields are preserved\n    *paths_json_ref =\n        openvrpaths::to_openvr_paths(paths.into_iter().collect::<Vec<_>>().as_slice());\n\n    openvrpaths::save_openvr_paths_json(&openvr_paths_json)\n}\n\npub fn get_driver_dir_from_registered() -> Result<PathBuf> {\n    for dir in get_registered_drivers()? {\n        let maybe_driver_name = || -> Result<_> {\n            let manifest_string = fs::read_to_string(dir.join(\"driver.vrdrivermanifest\"))?;\n            let mut manifest_map =\n                json::from_str::<HashMap<String, json::Value>>(&manifest_string)?;\n\n            manifest_map.remove(\"name\").to_any()\n        }();\n\n        if let Ok(json::Value::String(str)) = maybe_driver_name\n            && str == \"alvr_server\"\n        {\n            return Ok(dir);\n        }\n    }\n\n    bail!(\"ALVR driver path not registered\")\n}\n"
  },
  {
    "path": "alvr/server_io/src/openvrpaths.rs",
    "content": "use alvr_common::{\n    ToAny,\n    anyhow::{Result, bail},\n};\nuse encoding_rs_io::DecodeReaderBytes;\nuse serde_json as json;\nuse std::{\n    fs::{self, File},\n    io::Read,\n    path::PathBuf,\n};\n\nfn openvr_source_file_path() -> Result<PathBuf> {\n    let path = if cfg!(windows) {\n        dirs::cache_dir()\n    } else {\n        dirs::config_dir()\n    }\n    .to_any()?\n    .join(\"openvr/openvrpaths.vrpath\");\n\n    if path.exists() {\n        Ok(path)\n    } else {\n        bail!(\"{} does not exist\", path.to_string_lossy())\n    }\n}\n\npub fn steamvr_settings_file_path() -> Result<PathBuf> {\n    let path = if cfg!(windows) {\n        // N.B. if ever implementing this: given Steam can be installed on another\n        // drive, etc., this should probably start by looking at Windows registry keys.\n        bail!(\"Not implemented for Windows.\") // Original motive for implementation had little reason for Windows.\n    } else {\n        dirs::data_dir()\n    }\n    .to_any()?\n    .join(\"Steam/config/steamvr.vrsettings\");\n\n    if path.exists() {\n        Ok(path)\n    } else {\n        bail!(\"{} does not exist\", path.to_string_lossy())\n    }\n}\n\npub fn load_openvr_paths_json() -> Result<json::Value> {\n    let file = File::open(openvr_source_file_path()?)?;\n\n    let mut file_content_decoded = String::new();\n    DecodeReaderBytes::new(&file).read_to_string(&mut file_content_decoded)?;\n\n    let value = json::from_str(&file_content_decoded)?;\n\n    Ok(value)\n}\n\npub fn save_openvr_paths_json(openvr_paths: &json::Value) -> Result<()> {\n    let file_content = json::to_string_pretty(openvr_paths)?;\n\n    fs::write(openvr_source_file_path()?, file_content)?;\n\n    Ok(())\n}\n\npub fn from_openvr_paths(paths: &json::Value) -> Vec<std::path::PathBuf> {\n    let Some(paths_vec) = paths.as_array() else {\n        return vec![];\n    };\n\n    paths_vec\n        .iter()\n        .filter_map(json::Value::as_str)\n        .map(|s| PathBuf::from(s.replace(r\"\\\\\", r\"\\\")))\n        .collect()\n}\n\npub fn to_openvr_paths(paths: &[PathBuf]) -> json::Value {\n    let paths_vec = paths\n        .iter()\n        .map(|p| p.to_string_lossy().into())\n        .map(json::Value::String) // backslashes gets duplicated here\n        .collect::<Vec<_>>();\n\n    json::Value::Array(paths_vec)\n}\n\nfn get_single_openvr_path(path_type: &str) -> Result<PathBuf> {\n    let openvr_paths_json = load_openvr_paths_json()?;\n    let paths_json = openvr_paths_json.get(path_type).to_any()?;\n    from_openvr_paths(paths_json).first().cloned().to_any()\n}\n\npub fn steamvr_root_dir() -> Result<PathBuf> {\n    get_single_openvr_path(\"runtime\")\n}\n"
  },
  {
    "path": "alvr/server_openvr/Cargo.toml",
    "content": "[package]\nname = \"alvr_server_openvr\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors = [\"alvr-org\", \"Valve Corporation\"]\nlicense = \"MIT\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[features]\ngpl = [] # Enable for FFmpeg support on Windows. Always enabled on Linux\n\n[dependencies]\nalvr_common.workspace = true\nalvr_filesystem.workspace = true\nalvr_packets.workspace = true\nalvr_server_core.workspace = true\nalvr_server_io.workspace = true\nalvr_session.workspace = true\n\nserde_json = \"1\"\nsysinfo = \"0.37\"\n\n[build-dependencies]\nalvr_filesystem = { path = \"../filesystem\" }\nbindgen = \"0.72\"\ncc = { version = \"1\", features = [\"parallel\"] }\nwalkdir = \"2\"\n\n[target.'cfg(target_os = \"linux\")'.build-dependencies]\npkg-config = \"0.3\"\n"
  },
  {
    "path": "alvr/server_openvr/LICENSE",
    "content": "Copyright (c) 2018-2019 polygraphene\nCopyright (c) 2020-2024 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "alvr/server_openvr/LICENSE-Valve",
    "content": "Copyright (c) 2015, Valve Corporation\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation and/or\nother materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors\nmay be used to endorse or promote products derived from this software without\nspecific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "alvr/server_openvr/build.rs",
    "content": "use std::{env, path::PathBuf};\n\nfn get_ffmpeg_path() -> PathBuf {\n    let ffmpeg_path = alvr_filesystem::deps_dir()\n        .join(if cfg!(target_os = \"linux\") {\n            \"linux\"\n        } else {\n            \"windows\"\n        })\n        .join(\"ffmpeg\");\n\n    if cfg!(target_os = \"linux\") {\n        ffmpeg_path.join(\"alvr_build\")\n    } else {\n        ffmpeg_path\n    }\n}\n\n#[cfg(all(target_os = \"linux\", feature = \"gpl\"))]\nfn get_linux_x264_path() -> PathBuf {\n    alvr_filesystem::deps_dir().join(\"linux/x264/alvr_build\")\n}\n\nfn main() {\n    let platform_name = env::var(\"CARGO_CFG_TARGET_OS\").unwrap();\n    let out_dir = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n\n    let platform_subpath = match platform_name.as_str() {\n        \"windows\" => \"cpp/platform/win32\",\n        \"linux\" => \"cpp/platform/linux\",\n        \"macos\" => \"cpp/platform/macos\",\n        _ => panic!(),\n    };\n\n    let common_iter = walkdir::WalkDir::new(\"cpp\")\n        .into_iter()\n        .filter_entry(|entry| {\n            entry.file_name() != \"tools\"\n                && entry.file_name() != \"platform\"\n                && (platform_name != \"macos\" || entry.file_name() != \"amf\")\n                && (platform_name != \"linux\" || entry.file_name() != \"amf\")\n        });\n\n    let platform_iter = walkdir::WalkDir::new(platform_subpath).into_iter();\n\n    let cpp_paths = common_iter\n        .chain(platform_iter)\n        .filter_map(|maybe_entry| maybe_entry.ok())\n        .map(|entry| entry.into_path())\n        .collect::<Vec<_>>();\n\n    let source_files_paths = cpp_paths.iter().filter(|path| {\n        path.extension()\n            .filter(|ext| {\n                let ext_str = ext.to_string_lossy();\n                ext_str == \"c\" || ext_str == \"cpp\"\n            })\n            .is_some()\n    });\n\n    let mut build = cc::Build::new();\n    build\n        .cpp(true)\n        .std(\"c++17\")\n        .files(source_files_paths)\n        .include(alvr_filesystem::workspace_dir().join(\"openvr/headers\"))\n        .include(\"cpp\");\n\n    if platform_name == \"windows\" {\n        build\n            .debug(false) // This is because we cannot link to msvcrtd (see below)\n            .flag(\"/permissive-\")\n            .define(\"NOMINMAX\", None)\n            .define(\"_WINSOCKAPI_\", None)\n            .define(\"_MBCS\", None)\n            .define(\"_MT\", None);\n    } else if platform_name == \"macos\" {\n        build.define(\"__APPLE__\", None);\n    }\n\n    #[cfg(debug_assertions)]\n    build.define(\"ALVR_DEBUG_LOG\", None);\n\n    let gpl_or_linux = cfg!(feature = \"gpl\") || cfg!(target_os = \"linux\");\n\n    if gpl_or_linux {\n        let ffmpeg_path = get_ffmpeg_path();\n\n        assert!(ffmpeg_path.join(\"include\").exists());\n        build.include(ffmpeg_path.join(\"include\"));\n    }\n\n    #[cfg(all(target_os = \"linux\", feature = \"gpl\"))]\n    {\n        let x264_path = get_linux_x264_path();\n\n        assert!(x264_path.join(\"include\").exists());\n        build.include(x264_path.join(\"include\"));\n    }\n\n    #[cfg(feature = \"gpl\")]\n    build.define(\"ALVR_GPL\", None);\n\n    #[cfg(target_os = \"windows\")]\n    {\n        let vpl_path = alvr_filesystem::deps_dir().join(\"windows/libvpl/alvr_build\");\n        let vpl_include_path = vpl_path.join(\"include\");\n        let vpl_lib_path = vpl_path.join(\"lib\");\n\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            vpl_lib_path.to_string_lossy()\n        );\n\n        build.define(\"ONEVPL_EXPERIMENTAL\", None);\n        build.include(vpl_include_path);\n        println!(\"cargo:rustc-link-lib=vpl\");\n    }\n\n    build.compile(\"bindings\");\n\n    #[cfg(all(target_os = \"linux\", feature = \"gpl\"))]\n    {\n        let x264_path = get_linux_x264_path();\n        let x264_lib_path = x264_path.join(\"lib\");\n\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            x264_lib_path.to_string_lossy()\n        );\n\n        let x264_pkg_path = x264_lib_path.join(\"pkgconfig\");\n        assert!(x264_pkg_path.exists());\n\n        let x264_pkg_path = x264_pkg_path.to_string_lossy().to_string();\n        unsafe {\n            env::set_var(\n                \"PKG_CONFIG_PATH\",\n                env::var(\"PKG_CONFIG_PATH\").map_or(x264_pkg_path.clone(), |old| {\n                    format!(\"{x264_pkg_path}:{old}\")\n                }),\n            )\n        };\n        println!(\"cargo:rustc-link-lib=static=x264\");\n\n        pkg_config::Config::new()\n            .statik(true)\n            .probe(\"x264\")\n            .unwrap();\n    }\n\n    // ffmpeg\n    if gpl_or_linux {\n        let ffmpeg_path = get_ffmpeg_path();\n        let ffmpeg_lib_path = ffmpeg_path.join(\"lib\");\n\n        assert!(ffmpeg_lib_path.exists());\n\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            ffmpeg_lib_path.to_string_lossy()\n        );\n\n        #[cfg(target_os = \"linux\")]\n        {\n            let ffmpeg_pkg_path = ffmpeg_lib_path.join(\"pkgconfig\");\n            assert!(ffmpeg_pkg_path.exists());\n\n            let ffmpeg_pkg_path = ffmpeg_pkg_path.to_string_lossy().to_string();\n            unsafe {\n                env::set_var(\n                    \"PKG_CONFIG_PATH\",\n                    env::var(\"PKG_CONFIG_PATH\").map_or(ffmpeg_pkg_path.clone(), |old| {\n                        format!(\"{ffmpeg_pkg_path}:{old}\")\n                    }),\n                )\n            };\n\n            let pkg = pkg_config::Config::new().statik(true).to_owned();\n\n            for lib in [\"libavutil\", \"libavfilter\", \"libavcodec\"] {\n                pkg.probe(lib).unwrap();\n            }\n        }\n        #[cfg(windows)]\n        for lib in [\"avutil\", \"avfilter\", \"avcodec\", \"swscale\"] {\n            println!(\"cargo:rustc-link-lib={lib}\");\n        }\n    }\n\n    bindgen::builder()\n        .clang_arg(\"-xc++\")\n        .header(\"cpp/alvr_server/bindings.h\")\n        .derive_default(true)\n        .generate()\n        .unwrap()\n        .write_to_file(out_dir.join(\"bindings.rs\"))\n        .unwrap();\n\n    if platform_name == \"linux\" {\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            alvr_filesystem::workspace_dir()\n                .join(\"openvr/lib/linux64\")\n                .to_string_lossy()\n        );\n        println!(\"cargo:rustc-link-lib=openvr_api\");\n    } else if platform_name == \"windows\" {\n        println!(\n            \"cargo:rustc-link-search=native={}\",\n            alvr_filesystem::workspace_dir()\n                .join(\"openvr/lib/win64\")\n                .to_string_lossy()\n        );\n        println!(\"cargo:rustc-link-lib=openvr_api\");\n    }\n\n    #[cfg(target_os = \"linux\")]\n    {\n        pkg_config::Config::new().probe(\"vulkan\").unwrap();\n\n        #[cfg(not(feature = \"gpl\"))]\n        {\n            pkg_config::Config::new().probe(\"x264\").unwrap();\n        }\n\n        // fail build if there are undefined symbols in final library\n        println!(\"cargo:rustc-cdylib-link-arg=-Wl,--no-undefined\");\n    }\n\n    for path in cpp_paths {\n        println!(\"cargo:rerun-if-changed={}\", path.to_string_lossy());\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/ALVR-common/common-utils.cpp",
    "content": "#include \"common-utils.h\"\n\n#include <codecvt>\n#include <locale>\n\nstd::wstring ToWstring(const std::string& src) {\n    // TODO: src is really UTF-8?\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\n    return converter.from_bytes(src);\n}\n\nstd::string ToUTF8(const std::wstring& src) {\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\n    return converter.to_bytes(src);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/ALVR-common/common-utils.h",
    "content": "#pragma once\n\n#include <string>\n\nstd::wstring ToWstring(const std::string& src);\nstd::string ToUTF8(const std::wstring& src);\n"
  },
  {
    "path": "alvr/server_openvr/cpp/ALVR-common/exception.cpp",
    "content": "#include \"exception.h\"\n#include \"common-utils.h\"\n#include <stdarg.h>\n#include <wchar.h>\n\nException FormatExceptionV(const char* format, va_list args) {\n    char buf[1024];\n    vsprintf(buf, format, args);\n    return Exception(buf);\n}\n\nException FormatException(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    Exception e = FormatExceptionV(format, args);\n    va_end(args);\n\n    return e;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/ALVR-common/exception.h",
    "content": "#pragma once\n\n#include <stdexcept>\n#include <string>\n\nclass Exception : public std::exception {\npublic:\n    Exception(std::string what)\n        : m_what(what) { }\n    Exception() { }\n\n    const char* what() const noexcept override { return m_what.c_str(); }\n\nprivate:\n    std::string m_what;\n};\n\nException FormatExceptionV(const char* format, va_list args);\nException FormatException(const char* format, ...);\n"
  },
  {
    "path": "alvr/server_openvr/cpp/ALVR-common/packet_types.h",
    "content": "#ifndef ALVRCLIENT_PACKETTYPES_H\n#define ALVRCLIENT_PACKETTYPES_H\n#include \"../alvr_server/bindings.h\"\n#include <assert.h>\n#include <stdint.h>\n\nenum ALVR_CODEC {\n    ALVR_CODEC_H264 = 0,\n    ALVR_CODEC_HEVC = 1,\n    ALVR_CODEC_AV1 = 2,\n};\n\nenum ALVR_H264_PROFILE {\n    ALVR_H264_PROFILE_HIGH = 0,\n    ALVR_H264_PROFILE_MAIN = 1,\n    ALVR_H264_PROFILE_BASELINE = 2,\n};\n\nenum ALVR_RATE_CONTROL_METHOD {\n    ALVR_CBR = 0,\n    ALVR_VBR = 1,\n};\n\nenum ALVR_ENTROPY_CODING {\n    ALVR_CABAC = 0,\n    ALVR_CAVLC = 1,\n};\n\nenum ALVR_ENCODER_QUALITY_PRESET { ALVR_QUALITY = 0, ALVR_BALANCED = 1, ALVR_SPEED = 2 };\n\nenum ALVR_INPUT {\n    ALVR_INPUT_FINGER_INDEX,\n    ALVR_INPUT_FINGER_MIDDLE,\n    ALVR_INPUT_FINGER_RING,\n    ALVR_INPUT_FINGER_PINKY,\n};\n#define ALVR_BUTTON_FLAG(input) (1ULL << input)\n\n#endif // ALVRCLIENT_PACKETTYPES_H\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/ChaperoneUpdater.cpp",
    "content": "#include \"ALVR-common/packet_types.h\"\n#include \"Logger.h\"\n#include \"bindings.h\"\n#include <memory>\n#include <mutex>\n\n#ifndef __APPLE__\n// Workaround symbol clash in openvr.h / openvr_driver.h\nnamespace alvr_chaperone {\n#include <openvr.h>\n}\nusing namespace alvr_chaperone;\n#endif\n\nstd::mutex chaperone_mutex;\n\nbool isOpenvrInit = false;\n\nvoid InitOpenvrClient() {\n    Debug(\"InitOpenvrClient\");\n\n#ifndef __APPLE__\n    std::unique_lock<std::mutex> lock(chaperone_mutex);\n\n    if (isOpenvrInit) {\n        return;\n    }\n\n    vr::EVRInitError error;\n    // Background needed for VRCompositor()->GetTrackingSpace()\n    vr::VR_Init(&error, vr::VRApplication_Background);\n\n    if (error != vr::VRInitError_None) {\n        Warn(\"Failed to init OpenVR client! Error: %d\", error);\n        return;\n    }\n    isOpenvrInit = true;\n#endif\n}\n\nvoid ShutdownOpenvrClient() {\n    Debug(\"ShutdownOpenvrClient\");\n\n#ifndef __APPLE__\n    std::unique_lock<std::mutex> lock(chaperone_mutex);\n\n    if (!isOpenvrInit) {\n        return;\n    }\n\n    isOpenvrInit = false;\n    vr::VR_Shutdown();\n#endif\n}\n\nbool IsOpenvrClientReady() { return isOpenvrInit; }\n\nvoid _SetChaperoneArea(float areaWidth, float areaHeight) {\n    Debug(\"SetChaperoneArea\");\n\n#ifndef __APPLE__\n    std::unique_lock<std::mutex> lock(chaperone_mutex);\n\n    const vr::HmdMatrix34_t MATRIX_IDENTITY\n        = { { { 1.0, 0.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 } } };\n\n    float perimeterPoints[4][2];\n\n    perimeterPoints[0][0] = -1.0f * areaWidth;\n    perimeterPoints[0][1] = -1.0f * areaHeight;\n    perimeterPoints[1][0] = -1.0f * areaWidth;\n    perimeterPoints[1][1] = 1.0f * areaHeight;\n    perimeterPoints[2][0] = 1.0f * areaWidth;\n    perimeterPoints[2][1] = 1.0f * areaHeight;\n    perimeterPoints[3][0] = 1.0f * areaWidth;\n    perimeterPoints[3][1] = -1.0f * areaHeight;\n\n    auto setup = vr::VRChaperoneSetup();\n\n    if (setup != nullptr) {\n        vr::VRChaperoneSetup()->SetWorkingPerimeter(\n            reinterpret_cast<vr::HmdVector2_t*>(perimeterPoints), 4\n        );\n        vr::VRChaperoneSetup()->SetWorkingStandingZeroPoseToRawTrackingPose(&MATRIX_IDENTITY);\n        vr::VRChaperoneSetup()->SetWorkingSeatedZeroPoseToRawTrackingPose(&MATRIX_IDENTITY);\n        vr::VRChaperoneSetup()->SetWorkingPlayAreaSize(areaWidth, areaHeight);\n        vr::VRChaperoneSetup()->CommitWorkingCopy(vr::EChaperoneConfigFile_Live);\n    }\n\n    auto settings = vr::VRSettings();\n\n    if (settings != nullptr) {\n        // Hide SteamVR Chaperone\n        vr::VRSettings()->SetFloat(\n            vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_FadeDistance_Float, 0.0f\n        );\n    }\n\n#endif\n}\n\n#ifdef __linux__\nstd::unique_ptr<vr::HmdMatrix34_t> GetInvZeroPose() {\n    Debug(\"GetInvZeroPose\");\n\n    std::unique_lock<std::mutex> lock(chaperone_mutex);\n    if (!isOpenvrInit) {\n        return nullptr;\n    }\n    auto mat = std::make_unique<vr::HmdMatrix34_t>();\n    // revert pulls live into working copy\n    vr::VRChaperoneSetup()->RevertWorkingCopy();\n    auto compositor = vr::VRCompositor();\n    if (compositor == nullptr) {\n        return nullptr;\n    }\n    if (compositor->GetTrackingSpace() == vr::TrackingUniverseStanding) {\n        vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(mat.get());\n    } else {\n        vr::VRChaperoneSetup()->GetWorkingSeatedZeroPoseToRawTrackingPose(mat.get());\n    }\n    return mat;\n}\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Controller.cpp",
    "content": "#include \"Controller.h\"\n#include \"Logger.h\"\n#include \"Paths.h\"\n#include \"Settings.h\"\n#include \"Utils.h\"\n#include \"include/openvr_math.h\"\n#include <algorithm>\n#include <cstring>\n#include <string_view>\n\nController::Controller(uint64_t deviceID, vr::EVRSkeletalTrackingLevel skeletonLevel)\n    : TrackedDevice(\n          deviceID,\n          Settings::Instance().m_controllerIsTracker ? vr::TrackedDeviceClass_GenericTracker\n                                                     : vr::TrackedDeviceClass_Controller\n      )\n    , m_skeletonLevel(skeletonLevel) {\n    Debug(\"Controller::constructor deviceID=%llu\", deviceID);\n}\n\nbool Controller::activate() {\n    Debug(\"Controller::Activate deviceID=%llu\", this->device_id);\n\n    auto vr_driver_input = vr::VRDriverInput();\n\n    SetOpenvrProps((void*)this, this->device_id);\n\n    RegisterButtons((void*)this, this->device_id);\n\n    vr_driver_input->CreateHapticComponent(this->prop_container, \"/output/haptic\", &m_compHaptic);\n\n    vr_driver_input->CreateScalarComponent(\n        this->prop_container,\n        \"/input/finger/index\",\n        &m_buttonHandles[ALVR_INPUT_FINGER_INDEX],\n        vr::VRScalarType_Absolute,\n        vr::VRScalarUnits_NormalizedOneSided\n    );\n    vr_driver_input->CreateScalarComponent(\n        this->prop_container,\n        \"/input/finger/middle\",\n        &m_buttonHandles[ALVR_INPUT_FINGER_MIDDLE],\n        vr::VRScalarType_Absolute,\n        vr::VRScalarUnits_NormalizedOneSided\n    );\n    vr_driver_input->CreateScalarComponent(\n        this->prop_container,\n        \"/input/finger/ring\",\n        &m_buttonHandles[ALVR_INPUT_FINGER_RING],\n        vr::VRScalarType_Absolute,\n        vr::VRScalarUnits_NormalizedOneSided\n    );\n    vr_driver_input->CreateScalarComponent(\n        this->prop_container,\n        \"/input/finger/pinky\",\n        &m_buttonHandles[ALVR_INPUT_FINGER_PINKY],\n        vr::VRScalarType_Absolute,\n        vr::VRScalarUnits_NormalizedOneSided\n    );\n\n    if (this->device_id == HAND_LEFT_ID || this->device_id == HAND_TRACKER_LEFT_ID) {\n        vr_driver_input->CreateSkeletonComponent(\n            this->prop_container,\n            \"/input/skeleton/left\",\n            \"/skeleton/hand/left\",\n            \"/pose/raw\",\n            m_skeletonLevel,\n            nullptr,\n            0U,\n            &m_compSkeleton\n        );\n    } else {\n        vr_driver_input->CreateSkeletonComponent(\n            this->prop_container,\n            \"/input/skeleton/right\",\n            \"/skeleton/hand/right\",\n            \"/pose/raw\",\n            m_skeletonLevel,\n            nullptr,\n            0U,\n            &m_compSkeleton\n        );\n    }\n\n    // NB: here we set some initial values for the hand skeleton to fix the frozen hand bug\n    {\n        vr::VRBoneTransform_t boneTransforms[SKELETON_BONE_COUNT];\n        GetBoneTransform(false, boneTransforms);\n\n        vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithController,\n            boneTransforms,\n            SKELETON_BONE_COUNT\n        );\n        vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithoutController,\n            boneTransforms,\n            SKELETON_BONE_COUNT\n        );\n    }\n\n    return true;\n}\n\nvr::VRInputComponentHandle_t Controller::getHapticComponent() { return m_compHaptic; }\n\nvoid Controller::RegisterButton(uint64_t id) {\n    Debug(\"Controller::RegisterButton deviceID=%llu\", this->device_id);\n\n    ButtonInfo buttonInfo;\n    if (device_id == HAND_LEFT_ID) {\n        buttonInfo = LEFT_CONTROLLER_BUTTON_MAPPING[id];\n    } else {\n        buttonInfo = RIGHT_CONTROLLER_BUTTON_MAPPING[id];\n    }\n\n    if (buttonInfo.type == ButtonType::Binary) {\n        for (auto path : buttonInfo.steamvr_paths) {\n            vr::VRDriverInput()->CreateBooleanComponent(\n                this->prop_container, path, &m_buttonHandles[PathStringToHash(path)]\n            );\n        }\n    } else {\n        auto scalarType = buttonInfo.type == ButtonType::ScalarOneSided\n            ? vr::VRScalarUnits_NormalizedOneSided\n            : vr::VRScalarUnits_NormalizedTwoSided;\n\n        for (auto path : buttonInfo.steamvr_paths) {\n            vr::VRDriverInput()->CreateScalarComponent(\n                this->prop_container,\n                path,\n                &m_buttonHandles[PathStringToHash(path)],\n                vr::VRScalarType_Absolute,\n                scalarType\n            );\n        }\n    }\n}\n\nvoid Controller::SetButton(uint64_t id, FfiButtonValue value) {\n    Debug(\"Controller::SetButton deviceID=%llu buttonID=%llu\", this->device_id, id);\n\n    if (!this->last_pose.poseIsValid) {\n        return;\n    }\n\n    for (auto id : ALVR_TO_STEAMVR_PATH_IDS[id]) {\n        if (value.type == BUTTON_TYPE_BINARY) {\n            vr::VRDriverInput()->UpdateBooleanComponent(\n                m_buttonHandles[id], (bool)value.binary, 0.0\n            );\n        } else {\n            vr::VRDriverInput()->UpdateScalarComponent(m_buttonHandles[id], value.scalar, 0.0);\n        }\n    }\n\n    // todo: remove when moving inferred controller hand skeleton to rust\n    if (id == LEFT_A_TOUCH_ID || id == LEFT_B_TOUCH_ID || id == LEFT_X_TOUCH_ID\n        || id == LEFT_Y_TOUCH_ID || id == LEFT_TRACKPAD_TOUCH_ID || id == LEFT_THUMBSTICK_TOUCH_ID\n        || id == LEFT_THUMBREST_TOUCH_ID || id == RIGHT_A_TOUCH_ID || id == RIGHT_B_TOUCH_ID\n        || id == RIGHT_TRACKPAD_TOUCH_ID || id == RIGHT_THUMBSTICK_TOUCH_ID\n        || id == RIGHT_THUMBREST_TOUCH_ID) {\n        m_currentThumbTouch = value.binary;\n    } else if (id == LEFT_TRIGGER_TOUCH_ID || id == RIGHT_TRIGGER_TOUCH_ID) {\n        m_currentTriggerTouch = value.binary;\n    } else if (id == LEFT_TRIGGER_VALUE_ID || id == RIGHT_TRIGGER_VALUE_ID) {\n        m_triggerValue = value.scalar;\n    } else if (id == LEFT_SQUEEZE_VALUE_ID || id == RIGHT_SQUEEZE_VALUE_ID) {\n        m_gripValue = value.scalar;\n    }\n}\n\nbool Controller::OnPoseUpdate(uint64_t targetTimestampNs, float predictionS, FfiHandData handData) {\n    if (this->object_id == vr::k_unTrackedDeviceIndexInvalid) {\n        return false;\n    }\n\n    auto controllerMotion = handData.controllerMotion;\n    auto handSkeleton = handData.handSkeleton;\n\n    bool enabledAsHandTracker = handData.isHandTracker\n        && (device_id == HAND_TRACKER_LEFT_ID || device_id == HAND_TRACKER_RIGHT_ID);\n    bool enabledAsController\n        = !handData.isHandTracker && (device_id == HAND_LEFT_ID || device_id == HAND_RIGHT_ID);\n    bool enabled = (controllerMotion != nullptr || handSkeleton != nullptr)\n        && (enabledAsHandTracker || enabledAsController);\n\n    Debug(\n        \"%s %s: enabled: %d, ctrl: %d, hand: %d\",\n        (device_id == HAND_TRACKER_LEFT_ID || device_id == HAND_TRACKER_RIGHT_ID) ? \"hand tracker\"\n                                                                                  : \"controller\",\n        (device_id == HAND_TRACKER_LEFT_ID || device_id == HAND_LEFT_ID) ? \"left\" : \"right\",\n        enabled,\n        handData.controllerMotion != nullptr,\n        handData.handSkeleton != nullptr\n    );\n\n    auto vr_driver_input = vr::VRDriverInput();\n\n    auto pose = vr::DriverPose_t {};\n    pose.poseIsValid = enabled;\n    pose.deviceIsConnected = enabled;\n    pose.result = enabled ? vr::TrackingResult_Running_OK : vr::TrackingResult_Uninitialized;\n\n    pose.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0);\n    pose.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0);\n\n    if (controllerMotion != nullptr) {\n        auto m = controllerMotion;\n\n        pose.qRotation = HmdQuaternion_Init(\n            m->pose.orientation.w,\n            m->pose.orientation.x,\n            m->pose.orientation.y,\n            m->pose.orientation.z\n        );\n\n        pose.vecPosition[0] = m->pose.position[0];\n        pose.vecPosition[1] = m->pose.position[1];\n        pose.vecPosition[2] = m->pose.position[2];\n\n        pose.vecVelocity[0] = m->linearVelocity[0];\n        pose.vecVelocity[1] = m->linearVelocity[1];\n        pose.vecVelocity[2] = m->linearVelocity[2];\n\n        pose.vecAngularVelocity[0] = m->angularVelocity[0];\n        pose.vecAngularVelocity[1] = m->angularVelocity[1];\n        pose.vecAngularVelocity[2] = m->angularVelocity[2];\n    } else if (handSkeleton != nullptr) {\n        auto r = handSkeleton->jointRotations[0];\n        pose.qRotation = HmdQuaternion_Init(r.w, r.x, r.y, r.z);\n\n        auto p = handSkeleton->jointPositions[0];\n        pose.vecPosition[0] = p[0];\n        pose.vecPosition[1] = p[1];\n        pose.vecPosition[2] = p[2];\n\n        // If possible, use the last stored m_pose and timestamp\n        // to calculate the velocities of the current pose.\n        double linearVelocity[3] = { 0.0, 0.0, 0.0 };\n        vr::HmdVector3d_t angularVelocity = { 0.0, 0.0, 0.0 };\n\n        if (handData.predictHandSkeleton && this->last_pose.poseIsValid) {\n            double dt = ((double)targetTimestampNs - (double)m_poseTargetTimestampNs) / NS_PER_S;\n\n            if (dt > 0.0) {\n                linearVelocity[0] = (pose.vecPosition[0] - this->last_pose.vecPosition[0]) / dt;\n                linearVelocity[1] = (pose.vecPosition[1] - this->last_pose.vecPosition[1]) / dt;\n                linearVelocity[2] = (pose.vecPosition[2] - this->last_pose.vecPosition[2]) / dt;\n                angularVelocity\n                    = AngularVelocityBetweenQuats(this->last_pose.qRotation, pose.qRotation, dt);\n            }\n        }\n\n        pose.vecVelocity[0] = linearVelocity[0];\n        pose.vecVelocity[1] = linearVelocity[1];\n        pose.vecVelocity[2] = linearVelocity[2];\n\n        pose.vecAngularVelocity[0] = angularVelocity.v[0];\n        pose.vecAngularVelocity[1] = angularVelocity.v[1];\n        pose.vecAngularVelocity[2] = angularVelocity.v[2];\n    }\n\n    pose.poseTimeOffset = predictionS;\n\n    this->submit_pose(pose);\n\n    m_poseTargetTimestampNs = targetTimestampNs;\n\n    // Early return to skip updating the skeleton\n    if (!enabled) {\n        return false;\n    } else if (handSkeleton != nullptr) {\n        vr::VRBoneTransform_t boneTransform[SKELETON_BONE_COUNT] = {};\n\n        boneTransform[0].orientation.w = 1.0;\n        boneTransform[0].orientation.x = 0.0;\n        boneTransform[0].orientation.y = 0.0;\n        boneTransform[0].orientation.z = 0.0;\n        boneTransform[0].position.v[0] = 0.0;\n        boneTransform[0].position.v[1] = 0.0;\n        boneTransform[0].position.v[2] = 0.0;\n        boneTransform[0].position.v[3] = 1.0;\n\n        // NB: start from index 1 to skip the root bone\n        for (int j = 1; j < 31; j++) {\n            boneTransform[j].orientation.w = handSkeleton->jointRotations[j].w;\n            boneTransform[j].orientation.x = handSkeleton->jointRotations[j].x;\n            boneTransform[j].orientation.y = handSkeleton->jointRotations[j].y;\n            boneTransform[j].orientation.z = handSkeleton->jointRotations[j].z;\n            boneTransform[j].position.v[0] = handSkeleton->jointPositions[j][0];\n            boneTransform[j].position.v[1] = handSkeleton->jointPositions[j][1];\n            boneTransform[j].position.v[2] = handSkeleton->jointPositions[j][2];\n            boneTransform[j].position.v[3] = 1.0;\n        }\n\n        vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithController,\n            boneTransform,\n            SKELETON_BONE_COUNT\n        );\n        vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithoutController,\n            boneTransform,\n            SKELETON_BONE_COUNT\n        );\n\n        float rotThumb = (handSkeleton->jointRotations[2].z + handSkeleton->jointRotations[2].y\n                          + handSkeleton->jointRotations[3].z + handSkeleton->jointRotations[3].y\n                          + handSkeleton->jointRotations[4].z + handSkeleton->jointRotations[4].y)\n            * 0.67f;\n        float rotIndex = (handSkeleton->jointRotations[7].z + handSkeleton->jointRotations[8].z\n                          + handSkeleton->jointRotations[9].z)\n            * 0.67f;\n        float rotMiddle = (handSkeleton->jointRotations[12].z + handSkeleton->jointRotations[13].z\n                           + handSkeleton->jointRotations[14].z)\n            * 0.67f;\n        float rotRing = (handSkeleton->jointRotations[17].z + handSkeleton->jointRotations[18].z\n                         + handSkeleton->jointRotations[19].z)\n            * 0.67f;\n        float rotPinky = (handSkeleton->jointRotations[22].z + handSkeleton->jointRotations[23].z\n                          + handSkeleton->jointRotations[24].z)\n            * 0.67f;\n\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_INDEX], rotIndex, 0.0\n        );\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_MIDDLE], rotMiddle, 0.0\n        );\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_RING], rotRing, 0.0\n        );\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_PINKY], rotPinky, 0.0\n        );\n    } else if (controllerMotion != nullptr) {\n        if (m_lastThumbTouch != m_currentThumbTouch) {\n            m_thumbTouchAnimationProgress += 1.f / ANIMATION_FRAME_COUNT;\n            if (m_thumbTouchAnimationProgress > 1.f) {\n                m_thumbTouchAnimationProgress = 0;\n                m_lastThumbTouch = m_currentThumbTouch;\n            }\n        } else {\n            m_thumbTouchAnimationProgress = 0;\n        }\n\n        if (m_lastTriggerTouch != m_currentTriggerTouch) {\n            m_indexTouchAnimationProgress += 1.f / ANIMATION_FRAME_COUNT;\n            if (m_indexTouchAnimationProgress > 1.f) {\n                m_indexTouchAnimationProgress = 0;\n                m_lastTriggerTouch = m_currentTriggerTouch;\n            }\n        } else {\n            m_indexTouchAnimationProgress = 0;\n        }\n\n        float indexCurl = 0.0;\n        if (m_triggerValue > 0.0) {\n            indexCurl = 0.5 + m_triggerValue * 0.5;\n        } else if (m_lastTriggerTouch == 0) {\n            indexCurl = m_indexTouchAnimationProgress * 0.5;\n        } else {\n            indexCurl = 0.5 - m_indexTouchAnimationProgress * 0.5;\n        }\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_INDEX], indexCurl, 0.0\n        );\n\n        vr_driver_input->UpdateScalarComponent(\n            m_buttonHandles[ALVR_INPUT_FINGER_MIDDLE], m_gripValue, 0.0\n        );\n\n        // Ring and pinky fingers are not tracked. Infer a more natural pose.\n        if (m_currentThumbTouch) {\n            vr_driver_input->UpdateScalarComponent(m_buttonHandles[ALVR_INPUT_FINGER_RING], 1, 0.0);\n            vr_driver_input->UpdateScalarComponent(\n                m_buttonHandles[ALVR_INPUT_FINGER_PINKY], 1, 0.0\n            );\n        } else {\n            vr_driver_input->UpdateScalarComponent(\n                m_buttonHandles[ALVR_INPUT_FINGER_RING], m_gripValue, 0.0\n            );\n            vr_driver_input->UpdateScalarComponent(\n                m_buttonHandles[ALVR_INPUT_FINGER_PINKY], m_gripValue, 0.0\n            );\n        }\n\n        vr::VRBoneTransform_t boneTransforms[SKELETON_BONE_COUNT];\n\n        // Perform whatever logic is necessary to convert your device's input into a\n        // skeletal pose, first to create a pose \"With Controller\", that is as close to the\n        // pose of the user's real hand as possible\n        GetBoneTransform(true, boneTransforms);\n\n        // Then update the WithController pose on the component with those transforms\n        vr::EVRInputError err = vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithController,\n            boneTransforms,\n            SKELETON_BONE_COUNT\n        );\n        if (err != vr::VRInputError_None) {\n            // Handle failure case\n            Error(\"UpdateSkeletonComponentfailed.  Error: %i\\n\", err);\n        }\n\n        GetBoneTransform(false, boneTransforms);\n\n        // Then update the WithoutController pose on the component\n        err = vr_driver_input->UpdateSkeletonComponent(\n            m_compSkeleton,\n            vr::VRSkeletalMotionRange_WithoutController,\n            boneTransforms,\n            SKELETON_BONE_COUNT\n        );\n        if (err != vr::VRInputError_None) {\n            // Handle failure case\n            Error(\"UpdateSkeletonComponentfailed.  Error: %i\\n\", err);\n        }\n    }\n\n    return false;\n}\n\nvoid GetThumbBoneTransform(\n    bool withController, bool isLeftHand, bool touch, vr::VRBoneTransform_t outBoneTransform[]\n) {\n    if (isLeftHand) {\n        if (touch) {\n            if (withController) {\n                outBoneTransform[2] = { { -0.017303f, 0.032567f, 0.025281f, 1.f },\n                                        { 0.317609f, 0.528344f, 0.213134f, 0.757991f } };\n                outBoneTransform[3] = { { 0.040406f, 0.000000f, -0.000000f, 1.f },\n                                        { 0.991742f, 0.085317f, 0.019416f, 0.093765f } };\n                outBoneTransform[4] = { { 0.032517f, -0.000000f, 0.000000f, 1.f },\n                                        { 0.959385f, -0.012202f, -0.031055f, 0.280120f } };\n            } else {\n                outBoneTransform[2] = { { -0.016426f, 0.030866f, 0.025118f, 1.f },\n                                        { 0.403850f, 0.595704f, 0.082451f, 0.689380f } };\n                outBoneTransform[3] = { { 0.040406f, 0.000000f, -0.000000f, 1.f },\n                                        { 0.989655f, -0.090426f, 0.028457f, 0.107691f } };\n                outBoneTransform[4] = { { 0.032517f, 0.000000f, 0.000000f, 1.f },\n                                        { 0.988590f, 0.143978f, 0.041520f, 0.015363f } };\n            }\n        } else {\n            outBoneTransform[2] = { { -0.012083f, 0.028070f, 0.025050f, 1 },\n                                    { 0.464112f, 0.567418f, 0.272106f, 0.623374f } };\n            outBoneTransform[3] = { { 0.040406f, 0.000000f, -0.000000f, 1 },\n                                    { 0.994838f, 0.082939f, 0.019454f, 0.055130f } };\n            outBoneTransform[4] = { { 0.032517f, 0.000000f, 0.000000f, 1 },\n                                    { 0.974793f, -0.003213f, 0.021867f, -0.222015f } };\n        }\n\n        outBoneTransform[5] = { { 0.030464f, -0.000000f, -0.000000f, 1 },\n                                { 1.000000f, -0.000000f, 0.000000f, 0.000000f } };\n    } else {\n        if (touch) {\n            if (withController) {\n                outBoneTransform[2] = { { 0.017303f, 0.032567f, 0.025281f, 1 },\n                                        { 0.528344f, -0.317609f, 0.757991f, -0.213134f } };\n                outBoneTransform[3] = { { -0.040406f, -0.000000f, 0.000000f, 1 },\n                                        { 0.991742f, 0.085317f, 0.019416f, 0.093765f } };\n                outBoneTransform[4] = { { -0.032517f, 0.000000f, -0.000000f, 1 },\n                                        { 0.959385f, -0.012202f, -0.031055f, 0.280120f } };\n            } else {\n                outBoneTransform[2] = { { 0.016426f, 0.030866f, 0.025118f, 1 },\n                                        { 0.595704f, -0.403850f, 0.689380f, -0.082451f } };\n                outBoneTransform[3] = { { -0.040406f, -0.000000f, 0.000000f, 1 },\n                                        { 0.989655f, -0.090426f, 0.028457f, 0.107691f } };\n                outBoneTransform[4] = { { -0.032517f, -0.000000f, -0.000000f, 1 },\n                                        { 0.988590f, 0.143978f, 0.041520f, 0.015363f } };\n            }\n        } else {\n            outBoneTransform[2] = { { 0.012330f, 0.028661f, 0.025049f, 1 },\n                                    { 0.571059f, -0.451277f, 0.630056f, -0.270685f } };\n            outBoneTransform[3] = { { -0.040406f, -0.000000f, 0.000000f, 1 },\n                                    { 0.994565f, 0.078280f, 0.018282f, 0.066177f } };\n            outBoneTransform[4] = { { -0.032517f, -0.000000f, -0.000000f, 1 },\n                                    { 0.977658f, -0.003039f, 0.020722f, -0.209156f } };\n        }\n\n        outBoneTransform[5] = { { -0.030464f, 0.000000f, 0.000000f, 1 },\n                                { 1.000000f, -0.000000f, 0.000000f, 0.000000f } };\n    }\n}\n\nvoid GetTriggerBoneTransform(\n    bool withController,\n    bool isLeftHand,\n    bool touch,\n    float click,\n    vr::VRBoneTransform_t outBoneTransform[]\n) {\n    if (click) {\n        if (withController) {\n            if (isLeftHand) {\n                outBoneTransform[6] = { { -0.003925f, 0.027171f, 0.014640f, 1 },\n                                        { 0.666448f, 0.430031f, -0.455947f, 0.403772f } };\n                outBoneTransform[7] = { { 0.076015f, -0.005124f, 0.000239f, 1 },\n                                        { -0.956011f, -0.000025f, 0.158355f, -0.246913f } };\n                outBoneTransform[8] = { { 0.043930f, -0.000000f, -0.000000f, 1 },\n                                        { -0.944138f, -0.043351f, 0.014947f, -0.326345f } };\n                outBoneTransform[9] = { { 0.028695f, 0.000000f, 0.000000f, 1 },\n                                        { -0.912149f, 0.003626f, 0.039888f, -0.407898f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                         { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.847397f, -0.257141f, -0.139135f, 0.443213f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.874907f, 0.009875f, 0.026584f, 0.483460f } };\n                outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                         { 0.894578f, -0.036774f, -0.050597f, 0.442513f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                         { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n                outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                         { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n                outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                         { 0.968615f, -0.064538f, -0.046586f, 0.235477f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                         { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n                outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                         { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n                outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                         { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[26] = { { 0.006629f, 0.026690f, 0.061870f, 1 },\n                                         { 0.805084f, -0.018369f, 0.584788f, -0.097597f } };\n                outBoneTransform[27] = { { -0.007882f, -0.040478f, 0.039337f, 1 },\n                                         { -0.322494f, 0.932092f, 0.121861f, 0.111140f } };\n                outBoneTransform[28] = { { 0.017136f, -0.032633f, 0.080682f, 1 },\n                                         { -0.169466f, 0.800083f, 0.571006f, 0.071415f } };\n                outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                         { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n                outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                         { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n            } else {\n                outBoneTransform[6] = { { -0.003925f, 0.027171f, 0.014640f, 1 },\n                                        { 0.666448f, 0.430031f, -0.455947f, 0.403772f } };\n                outBoneTransform[7] = { { 0.076015f, -0.005124f, 0.000239f, 1 },\n                                        { -0.956011f, -0.000025f, 0.158355f, -0.246913f } };\n                outBoneTransform[8] = { { 0.043930f, -0.000000f, -0.000000f, 1 },\n                                        { -0.944138f, -0.043351f, 0.014947f, -0.326345f } };\n                outBoneTransform[9] = { { 0.028695f, 0.000000f, 0.000000f, 1 },\n                                        { -0.912149f, 0.003626f, 0.039888f, -0.407898f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                         { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.847397f, -0.257141f, -0.139135f, 0.443213f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.874907f, 0.009875f, 0.026584f, 0.483460f } };\n                outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                         { 0.894578f, -0.036774f, -0.050597f, 0.442513f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                         { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n                outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                         { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n                outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                         { 0.968615f, -0.064538f, -0.046586f, 0.235477f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                         { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n                outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                         { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n                outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                         { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[26] = { { 0.006629f, 0.026690f, 0.061870f, 1 },\n                                         { 0.805084f, -0.018369f, 0.584788f, -0.097597f } };\n                outBoneTransform[27] = { { -0.007882f, -0.040478f, 0.039337f, 1 },\n                                         { -0.322494f, 0.932092f, 0.121861f, 0.111140f } };\n                outBoneTransform[28] = { { 0.017136f, -0.032633f, 0.080682f, 1 },\n                                         { -0.169466f, 0.800083f, 0.571006f, 0.071415f } };\n                outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                         { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n                outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                         { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n            }\n        } else {\n            if (isLeftHand) {\n                outBoneTransform[6] = { { 0.003802f, 0.021514f, 0.012803f, 1 },\n                                        { 0.617314f, 0.395175f, -0.510874f, 0.449185f } };\n                outBoneTransform[7] = { { 0.074204f, -0.005002f, 0.000234f, 1 },\n                                        { 0.737291f, -0.032006f, -0.115013f, 0.664944f } };\n                outBoneTransform[8] = { { 0.043287f, -0.000000f, -0.000000f, 1 },\n                                        { 0.611381f, 0.003287f, 0.003823f, 0.791320f } };\n                outBoneTransform[9] = { { 0.028275f, 0.000000f, 0.000000f, 1 },\n                                        { 0.745389f, -0.000684f, -0.000945f, 0.666629f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.004885f, 0.006885f, 0.016480f, 1 },\n                                         { 0.522678f, 0.527374f, -0.469333f, 0.477923f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.826071f, -0.121321f, 0.017267f, 0.550082f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.956676f, 0.013210f, 0.009330f, 0.290704f } };\n                outBoneTransform[14] = { { 0.033266f, 0.000000f, 0.000000f, 1 },\n                                         { 0.979740f, -0.001605f, -0.019412f, 0.199323f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.001696f, -0.006648f, 0.016418f, 1 },\n                                         { 0.509620f, 0.540794f, -0.504891f, 0.439220f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.955009f, -0.065344f, -0.063228f, 0.282294f } };\n                outBoneTransform[18] = { { 0.040577f, 0.000000f, 0.000000f, 1 },\n                                         { 0.953823f, -0.000972f, 0.000697f, 0.300366f } };\n                outBoneTransform[19] = { { 0.028698f, -0.000000f, -0.000000f, 1 },\n                                         { 0.977627f, -0.001163f, -0.011433f, 0.210033f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[21] = { { -0.001792f, -0.019041f, 0.015254f, 1 },\n                                         { 0.518602f, 0.511152f, -0.596086f, 0.338315f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.978584f, -0.045398f, -0.103083f, 0.172297f } };\n                outBoneTransform[23] = { { 0.030154f, 0.000000f, 0.000000f, 1 },\n                                         { 0.970479f, -0.000068f, -0.002025f, 0.241175f } };\n                outBoneTransform[24] = { { 0.018187f, 0.000000f, 0.000000f, 1 },\n                                         { 0.997053f, -0.000687f, -0.052009f, -0.056395f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[26] = { { -0.005193f, 0.054191f, 0.060030f, 1 },\n                                         { 0.747374f, 0.182388f, 0.599615f, 0.220518f } };\n                outBoneTransform[27] = { { 0.000171f, 0.016473f, 0.096515f, 1 },\n                                         { -0.006456f, 0.022747f, -0.932927f, -0.359287f } };\n                outBoneTransform[28] = { { -0.038019f, -0.074839f, 0.046941f, 1 },\n                                         { -0.199973f, 0.698334f, -0.635627f, -0.261380f } };\n                outBoneTransform[29] = { { -0.036836f, -0.089774f, 0.081969f, 1 },\n                                         { -0.191006f, 0.756582f, -0.607429f, -0.148761f } };\n                outBoneTransform[30] = { { -0.030241f, -0.086049f, 0.119881f, 1 },\n                                         { -0.019037f, 0.779368f, -0.612017f, -0.132881f } };\n            } else {\n                outBoneTransform[6] = { { -0.003802f, 0.021514f, 0.012803f, 1 },\n                                        { 0.395174f, -0.617314f, 0.449185f, 0.510874f } };\n                outBoneTransform[7] = { { -0.074204f, 0.005002f, -0.000234f, 1 },\n                                        { 0.737291f, -0.032006f, -0.115013f, 0.664944f } };\n                outBoneTransform[8] = { { -0.043287f, 0.000000f, 0.000000f, 1 },\n                                        { 0.611381f, 0.003287f, 0.003823f, 0.791320f } };\n                outBoneTransform[9] = { { -0.028275f, -0.000000f, -0.000000f, 1 },\n                                        { 0.745389f, -0.000684f, -0.000945f, 0.666629f } };\n                outBoneTransform[10] = { { -0.022821f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { -0.004885f, 0.006885f, 0.016480f, 1 },\n                                         { 0.527233f, -0.522513f, 0.478085f, 0.469510f } };\n                outBoneTransform[12] = { { -0.070953f, -0.000779f, -0.000997f, 1 },\n                                         { 0.826317f, -0.120120f, 0.019005f, 0.549918f } };\n                outBoneTransform[13] = { { -0.043108f, -0.000000f, -0.000000f, 1 },\n                                         { 0.958363f, 0.013484f, 0.007380f, 0.285138f } };\n                outBoneTransform[14] = { { -0.033266f, -0.000000f, -0.000000f, 1 },\n                                         { 0.977901f, -0.001431f, -0.018078f, 0.208279f } };\n                outBoneTransform[15] = { { -0.025892f, 0.000000f, -0.000000f, 1 },\n                                         { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { -0.001696f, -0.006648f, 0.016418f, 1 },\n                                         { 0.541481f, -0.508179f, 0.441001f, 0.504054f } };\n                outBoneTransform[17] = { { -0.065876f, -0.001786f, -0.000693f, 1 },\n                                         { 0.953780f, -0.064506f, -0.058812f, 0.287548f } };\n                outBoneTransform[18] = { { -0.040577f, -0.000000f, -0.000000f, 1 },\n                                         { 0.954761f, -0.000983f, 0.000698f, 0.297372f } };\n                outBoneTransform[19] = { { -0.028698f, 0.000000f, 0.000000f, 1 },\n                                         { 0.976924f, -0.001344f, -0.010281f, 0.213335f } };\n                outBoneTransform[20] = { { -0.022430f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[21] = { { 0.001792f, -0.019041f, 0.015254f, 1 },\n                                         { 0.510569f, -0.514906f, 0.341115f, 0.598191f } };\n                outBoneTransform[22] = { { -0.062878f, -0.002844f, -0.000332f, 1 },\n                                         { 0.979195f, -0.043879f, -0.095103f, 0.173800f } };\n                outBoneTransform[23] = { { -0.030154f, -0.000000f, -0.000000f, 1 },\n                                         { 0.971387f, -0.000102f, -0.002019f, 0.237494f } };\n                outBoneTransform[24] = { { -0.018187f, -0.000000f, -0.000000f, 1 },\n                                         { 0.997961f, 0.000800f, -0.051911f, -0.037114f } };\n                outBoneTransform[25] = { { -0.018018f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[26] = { { 0.004392f, 0.055515f, 0.060253f, 1 },\n                                         { 0.745924f, 0.156756f, -0.597950f, -0.247953f } };\n                outBoneTransform[27] = { { -0.000171f, 0.016473f, 0.096515f, 1 },\n                                         { -0.006456f, 0.022747f, 0.932927f, 0.359287f } };\n                outBoneTransform[28] = { { 0.038119f, -0.074730f, 0.046338f, 1 },\n                                         { -0.207931f, 0.699835f, 0.632631f, 0.258406f } };\n                outBoneTransform[29] = { { 0.035492f, -0.089519f, 0.081636f, 1 },\n                                         { -0.197555f, 0.760574f, 0.601098f, 0.145535f } };\n                outBoneTransform[30] = { { 0.029073f, -0.085957f, 0.119561f, 1 },\n                                         { -0.031423f, 0.791013f, 0.597190f, 0.129133f } };\n            }\n        }\n    } else if (touch) {\n        if (withController) {\n            if (isLeftHand) {\n                outBoneTransform[6] = { { -0.003925f, 0.027171f, 0.014640f, 1 },\n                                        { 0.666448f, 0.430031f, -0.455947f, 0.403772f } };\n                outBoneTransform[7] = { { 0.074204f, -0.005002f, 0.000234f, 1 },\n                                        { -0.951843f, 0.009717f, 0.158611f, -0.262188f } };\n                outBoneTransform[8] = { { 0.043930f, -0.000000f, -0.000000f, 1 },\n                                        { -0.973045f, -0.044676f, 0.010341f, -0.226012f } };\n                outBoneTransform[9] = { { 0.028695f, 0.000000f, 0.000000f, 1 },\n                                        { -0.935253f, -0.002881f, 0.023037f, -0.353217f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                         { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.847397f, -0.257141f, -0.139135f, 0.443213f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.874907f, 0.009875f, 0.026584f, 0.483460f } };\n                outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                         { 0.894578f, -0.036774f, -0.050597f, 0.442513f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                         { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n                outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                         { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n                outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                         { 0.968615f, -0.064538f, -0.046586f, 0.235477f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                         { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n                outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                         { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n                outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                         { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[26] = { { 0.006629f, 0.026690f, 0.061870f, 1 },\n                                         { 0.805084f, -0.018369f, 0.584788f, -0.097597f } };\n                outBoneTransform[27] = { { -0.009005f, -0.041708f, 0.037992f, 1 },\n                                         { -0.338860f, 0.939952f, -0.007564f, 0.040082f } };\n                outBoneTransform[28] = { { 0.017136f, -0.032633f, 0.080682f, 1 },\n                                         { -0.169466f, 0.800083f, 0.571006f, 0.071415f } };\n                outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                         { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n                outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                         { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n            } else {\n                outBoneTransform[6] = { { -0.003925f, 0.027171f, 0.014640f, 1 },\n                                        { 0.666448f, 0.430031f, -0.455947f, 0.403772f } };\n                outBoneTransform[7] = { { 0.074204f, -0.005002f, 0.000234f, 1 },\n                                        { -0.951843f, 0.009717f, 0.158611f, -0.262188f } };\n                outBoneTransform[8] = { { 0.043930f, -0.000000f, -0.000000f, 1 },\n                                        { -0.973045f, -0.044676f, 0.010341f, -0.226012f } };\n                outBoneTransform[9] = { { 0.028695f, 0.000000f, 0.000000f, 1 },\n                                        { -0.935253f, -0.002881f, 0.023037f, -0.353217f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                         { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.847397f, -0.257141f, -0.139135f, 0.443213f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.874907f, 0.009875f, 0.026584f, 0.483460f } };\n                outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                         { 0.894578f, -0.036774f, -0.050597f, 0.442513f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                         { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n                outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                         { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n                outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                         { 0.968615f, -0.064538f, -0.046586f, 0.235477f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                         { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n                outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                         { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n                outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                         { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[26] = { { 0.006629f, 0.026690f, 0.061870f, 1 },\n                                         { 0.805084f, -0.018369f, 0.584788f, -0.097597f } };\n                outBoneTransform[27] = { { -0.009005f, -0.041708f, 0.037992f, 1 },\n                                         { -0.338860f, 0.939952f, -0.007564f, 0.040082f } };\n                outBoneTransform[28] = { { 0.017136f, -0.032633f, 0.080682f, 1 },\n                                         { -0.169466f, 0.800083f, 0.571006f, 0.071415f } };\n                outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                         { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n                outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                         { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n            }\n        } else {\n            if (isLeftHand) {\n                outBoneTransform[6] = { { 0.002693f, 0.023387f, 0.013573f, 1 },\n                                        { 0.626743f, 0.404630f, -0.499840f, 0.440032f } };\n                outBoneTransform[7] = { { 0.074204f, -0.005002f, 0.000234f, 1 },\n                                        { 0.869067f, -0.019031f, -0.093524f, 0.485400f } };\n                outBoneTransform[8] = { { 0.043512f, -0.000000f, -0.000000f, 1 },\n                                        { 0.834068f, 0.020722f, 0.003930f, 0.551259f } };\n                outBoneTransform[9] = { { 0.028422f, 0.000000f, 0.000000f, 1 },\n                                        { 0.890556f, 0.000289f, -0.009290f, 0.454779f } };\n                outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { 0.003937f, 0.006967f, 0.016424f, 1 },\n                                         { 0.531603f, 0.532690f, -0.459598f, 0.471602f } };\n                outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                         { 0.906933f, -0.142169f, -0.015445f, 0.396261f } };\n                outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                         { 0.975787f, 0.014996f, 0.010867f, 0.217936f } };\n                outBoneTransform[14] = { { 0.033266f, 0.000000f, 0.000000f, 1 },\n                                         { 0.992777f, -0.002096f, -0.021403f, 0.118029f } };\n                outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                         { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { 0.001282f, -0.006612f, 0.016394f, 1 },\n                                         { 0.513688f, 0.543325f, -0.502550f, 0.434011f } };\n                outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                         { 0.971280f, -0.068108f, -0.073480f, 0.215818f } };\n                outBoneTransform[18] = { { 0.040619f, 0.000000f, 0.000000f, 1 },\n                                         { 0.976566f, -0.001379f, 0.000441f, 0.215216f } };\n                outBoneTransform[19] = { { 0.028715f, -0.000000f, -0.000000f, 1 },\n                                         { 0.987232f, -0.000977f, -0.011919f, 0.158838f } };\n                outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[21] = { { -0.002032f, -0.019020f, 0.015240f, 1 },\n                                         { 0.521784f, 0.511917f, -0.594340f, 0.335325f } };\n                outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                         { 0.982925f, -0.053050f, -0.108004f, 0.139206f } };\n                outBoneTransform[23] = { { 0.030177f, 0.000000f, 0.000000f, 1 },\n                                         { 0.979798f, 0.000394f, -0.001374f, 0.199982f } };\n                outBoneTransform[24] = { { 0.018187f, 0.000000f, 0.000000f, 1 },\n                                         { 0.997410f, -0.000172f, -0.051977f, -0.049724f } };\n                outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[26] = { { -0.004857f, 0.053377f, 0.060017f, 1 },\n                                         { 0.751040f, 0.174397f, 0.601473f, 0.209178f } };\n                outBoneTransform[27] = { { -0.013234f, -0.004327f, 0.069740f, 1 },\n                                         { -0.119277f, 0.262590f, -0.888979f, -0.355718f } };\n                outBoneTransform[28] = { { -0.037500f, -0.074514f, 0.046899f, 1 },\n                                         { -0.204942f, 0.706005f, -0.626220f, -0.259623f } };\n                outBoneTransform[29] = { { -0.036251f, -0.089302f, 0.081732f, 1 },\n                                         { -0.194045f, 0.764033f, -0.596592f, -0.150590f } };\n                outBoneTransform[30] = { { -0.029633f, -0.085595f, 0.119439f, 1 },\n                                         { -0.025015f, 0.787219f, -0.601140f, -0.135243f } };\n            } else {\n                outBoneTransform[6] = { { -0.002693f, 0.023387f, 0.013573f, 1 },\n                                        { 0.404698f, -0.626951f, 0.439894f, 0.499645f } };\n                outBoneTransform[7] = { { -0.074204f, 0.005002f, -0.000234f, 1 },\n                                        { 0.870303f, -0.017421f, -0.092515f, 0.483436f } };\n                outBoneTransform[8] = { { -0.043512f, 0.000000f, 0.000000f, 1 },\n                                        { 0.835972f, 0.018944f, 0.003312f, 0.548436f } };\n                outBoneTransform[9] = { { -0.028422f, -0.000000f, -0.000000f, 1 },\n                                        { 0.890326f, 0.000173f, -0.008504f, 0.455244f } };\n                outBoneTransform[10] = { { -0.022821f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n                outBoneTransform[11] = { { -0.003937f, 0.006967f, 0.016424f, 1 },\n                                         { 0.532293f, -0.531137f, 0.472074f, 0.460113f } };\n                outBoneTransform[12] = { { -0.070953f, -0.000779f, -0.000997f, 1 },\n                                         { 0.908154f, -0.139967f, -0.013210f, 0.394323f } };\n                outBoneTransform[13] = { { -0.043108f, -0.000000f, -0.000000f, 1 },\n                                         { 0.977887f, 0.015350f, 0.008912f, 0.208378f } };\n                outBoneTransform[14] = { { -0.033266f, -0.000000f, -0.000000f, 1 },\n                                         { 0.992487f, -0.002006f, -0.020888f, 0.120540f } };\n                outBoneTransform[15] = { { -0.025892f, 0.000000f, -0.000000f, 1 },\n                                         { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n                outBoneTransform[16] = { { -0.001282f, -0.006612f, 0.016394f, 1 },\n                                         { 0.544460f, -0.511334f, 0.436935f, 0.501187f } };\n                outBoneTransform[17] = { { -0.065876f, -0.001786f, -0.000693f, 1 },\n                                         { 0.971233f, -0.064561f, -0.071188f, 0.217877f } };\n                outBoneTransform[18] = { { -0.040619f, -0.000000f, -0.000000f, 1 },\n                                         { 0.978211f, -0.001419f, 0.000451f, 0.207607f } };\n                outBoneTransform[19] = { { -0.028715f, 0.000000f, 0.000000f, 1 },\n                                         { 0.987488f, -0.001166f, -0.010852f, 0.157314f } };\n                outBoneTransform[20] = { { -0.022430f, 0.000000f, -0.000000f, 1 },\n                                         { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n                outBoneTransform[21] = { { 0.002032f, -0.019020f, 0.015240f, 1 },\n                                         { 0.513640f, -0.518192f, 0.337332f, 0.594860f } };\n                outBoneTransform[22] = { { -0.062878f, -0.002844f, -0.000332f, 1 },\n                                         { 0.983501f, -0.050059f, -0.104491f, 0.138930f } };\n                outBoneTransform[23] = { { -0.030177f, -0.000000f, -0.000000f, 1 },\n                                         { 0.981170f, 0.000501f, -0.001363f, 0.193138f } };\n                outBoneTransform[24] = { { -0.018187f, -0.000000f, -0.000000f, 1 },\n                                         { 0.997801f, 0.000487f, -0.051933f, -0.041173f } };\n                outBoneTransform[25] = { { -0.018018f, -0.000000f, 0.000000f, 1 },\n                                         { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n                outBoneTransform[26] = { { 0.004574f, 0.055518f, 0.060226f, 1 },\n                                         { 0.745334f, 0.161961f, -0.597782f, -0.246784f } };\n                outBoneTransform[27] = { { 0.013831f, -0.004360f, 0.069547f, 1 },\n                                         { -0.117443f, 0.257604f, 0.890065f, 0.357255f } };\n                outBoneTransform[28] = { { 0.038220f, -0.074817f, 0.046428f, 1 },\n                                         { -0.205767f, 0.697939f, 0.635107f, 0.259191f } };\n                outBoneTransform[29] = { { 0.035802f, -0.089658f, 0.081733f, 1 },\n                                         { -0.196007f, 0.758396f, 0.604341f, 0.145564f } };\n                outBoneTransform[30] = { { 0.029364f, -0.086069f, 0.119701f, 1 },\n                                         { -0.028444f, 0.787767f, 0.601616f, 0.129123f } };\n            }\n        }\n    } else {\n        // no touch\n        if (isLeftHand) {\n            outBoneTransform[6] = { { 0.000632f, 0.026866f, 0.015002f, 1 },\n                                    { 0.644251f, 0.421979f, -0.478202f, 0.422133f } };\n            outBoneTransform[7] = { { 0.074204f, -0.005002f, 0.000234f, 1 },\n                                    { 0.995332f, 0.007007f, -0.039124f, 0.087949f } };\n            outBoneTransform[8] = { { 0.043930f, -0.000000f, -0.000000f, 1 },\n                                    { 0.997891f, 0.045808f, 0.002142f, -0.045943f } };\n            outBoneTransform[9] = { { 0.028695f, 0.000000f, 0.000000f, 1 },\n                                    { 0.999649f, 0.001850f, -0.022782f, -0.013409f } };\n            outBoneTransform[10] = { { 0.022821f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n            outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                     { 0.546723f, 0.541277f, -0.442520f, 0.460749f } };\n            outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                     { 0.980294f, -0.167261f, -0.078959f, 0.069368f } };\n            outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                     { 0.997947f, 0.018493f, 0.013192f, 0.059886f } };\n            outBoneTransform[14] = { { 0.033266f, 0.000000f, 0.000000f, 1 },\n                                     { 0.997394f, -0.003328f, -0.028225f, -0.066315f } };\n            outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                     { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                     { 0.516692f, 0.550144f, -0.495548f, 0.429888f } };\n            outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                     { 0.990420f, -0.058696f, -0.101820f, 0.072495f } };\n            outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                     { 0.999545f, -0.002240f, 0.000004f, 0.030081f } };\n            outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                     { 0.999102f, -0.000721f, -0.012693f, 0.040420f } };\n            outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n            outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                     { 0.526918f, 0.523940f, -0.584025f, 0.326740f } };\n            outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                     { 0.986609f, -0.059615f, -0.135163f, 0.069132f } };\n            outBoneTransform[23] = { { 0.030220f, 0.000000f, 0.000000f, 1 },\n                                     { 0.994317f, 0.001896f, -0.000132f, 0.106446f } };\n            outBoneTransform[24] = { { 0.018187f, 0.000000f, 0.000000f, 1 },\n                                     { 0.995931f, -0.002010f, -0.052079f, -0.073526f } };\n            outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n            outBoneTransform[26] = { { -0.006059f, 0.056285f, 0.060064f, 1 },\n                                     { 0.737238f, 0.202745f, 0.594267f, 0.249441f } };\n            outBoneTransform[27] = { { -0.040416f, -0.043018f, 0.019345f, 1 },\n                                     { -0.290330f, 0.623527f, -0.663809f, -0.293734f } };\n            outBoneTransform[28] = { { -0.039354f, -0.075674f, 0.047048f, 1 },\n                                     { -0.187047f, 0.678062f, -0.659285f, -0.265683f } };\n            outBoneTransform[29] = { { -0.038340f, -0.090987f, 0.082579f, 1 },\n                                     { -0.183037f, 0.736793f, -0.634757f, -0.143936f } };\n            outBoneTransform[30] = { { -0.031806f, -0.087214f, 0.121015f, 1 },\n                                     { -0.003659f, 0.758407f, -0.639342f, -0.126678f } };\n        } else {\n            outBoneTransform[6] = { { -0.000632f, 0.026866f, 0.015002f, 1 },\n                                    { 0.421833f, -0.643793f, 0.422458f, 0.478661f } };\n            outBoneTransform[7] = { { -0.074204f, 0.005002f, -0.000234f, 1 },\n                                    { 0.994784f, 0.007053f, -0.041286f, 0.093009f } };\n            outBoneTransform[8] = { { -0.043930f, 0.000000f, 0.000000f, 1 },\n                                    { 0.998404f, 0.045905f, 0.002780f, -0.032767f } };\n            outBoneTransform[9] = { { -0.028695f, -0.000000f, -0.000000f, 1 },\n                                    { 0.999704f, 0.001955f, -0.022774f, -0.008282f } };\n            outBoneTransform[10] = { { -0.022821f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, -0.000000f, 0.000000f } };\n            outBoneTransform[11] = { { -0.002177f, 0.007120f, 0.016319f, 1 },\n                                     { 0.541874f, -0.547427f, 0.459996f, 0.441701f } };\n            outBoneTransform[12] = { { -0.070953f, -0.000779f, -0.000997f, 1 },\n                                     { 0.979837f, -0.168061f, -0.075910f, 0.076899f } };\n            outBoneTransform[13] = { { -0.043108f, -0.000000f, -0.000000f, 1 },\n                                     { 0.997271f, 0.018278f, 0.013375f, 0.070266f } };\n            outBoneTransform[14] = { { -0.033266f, -0.000000f, -0.000000f, 1 },\n                                     { 0.998402f, -0.003143f, -0.026423f, -0.049849f } };\n            outBoneTransform[15] = { { -0.025892f, 0.000000f, -0.000000f, 1 },\n                                     { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { -0.000513f, -0.006545f, 0.016348f, 1 },\n                                     { 0.548983f, -0.519068f, 0.426914f, 0.496920f } };\n            outBoneTransform[17] = { { -0.065876f, -0.001786f, -0.000693f, 1 },\n                                     { 0.989791f, -0.065882f, -0.096417f, 0.081716f } };\n            outBoneTransform[18] = { { -0.040697f, -0.000000f, -0.000000f, 1 },\n                                     { 0.999102f, -0.002168f, -0.000020f, 0.042317f } };\n            outBoneTransform[19] = { { -0.028747f, 0.000000f, 0.000000f, 1 },\n                                     { 0.998584f, -0.000674f, -0.012714f, 0.051653f } };\n            outBoneTransform[20] = { { -0.022430f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n            outBoneTransform[21] = { { 0.002478f, -0.018981f, 0.015214f, 1 },\n                                     { 0.518597f, -0.527304f, 0.328264f, 0.587580f } };\n            outBoneTransform[22] = { { -0.062878f, -0.002844f, -0.000332f, 1 },\n                                     { 0.987294f, -0.063356f, -0.125964f, 0.073274f } };\n            outBoneTransform[23] = { { -0.030220f, -0.000000f, -0.000000f, 1 },\n                                     { 0.993413f, 0.001573f, -0.000147f, 0.114578f } };\n            outBoneTransform[24] = { { -0.018187f, -0.000000f, -0.000000f, 1 },\n                                     { 0.997047f, -0.000695f, -0.052009f, -0.056495f } };\n            outBoneTransform[25] = { { -0.018018f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n            outBoneTransform[26] = { { 0.005198f, 0.054204f, 0.060030f, 1 },\n                                     { 0.747318f, 0.182508f, -0.599586f, -0.220688f } };\n            outBoneTransform[27] = { { 0.038779f, -0.042973f, 0.019824f, 1 },\n                                     { -0.297445f, 0.639373f, 0.648910f, 0.285734f } };\n            outBoneTransform[28] = { { 0.038027f, -0.074844f, 0.046941f, 1 },\n                                     { -0.199898f, 0.698218f, 0.635767f, 0.261406f } };\n            outBoneTransform[29] = { { 0.036845f, -0.089781f, 0.081973f, 1 },\n                                     { -0.190960f, 0.756469f, 0.607591f, 0.148733f } };\n            outBoneTransform[30] = { { 0.030251f, -0.086056f, 0.119887f, 1 },\n                                     { -0.018948f, 0.779249f, 0.612180f, 0.132846f } };\n        }\n    }\n}\n\nvoid GetGripClickBoneTransform(\n    bool withController, bool isLeftHand, vr::VRBoneTransform_t outBoneTransform[]\n) {\n    if (withController) {\n        if (isLeftHand) {\n            outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                     { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n            outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                     { -0.831727f, 0.270927f, 0.175647f, -0.451638f } };\n            outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                     { -0.854886f, -0.008231f, -0.028107f, -0.517990f } };\n            outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                     { -0.825759f, 0.085208f, 0.086456f, -0.550805f } };\n            outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                     { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                     { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n            outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                     { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n            outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                     { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n            outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                     { 0.968615f, -0.064537f, -0.046586f, 0.235477f } };\n            outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n            outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                     { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n            outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                     { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n            outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                     { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n            outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                     { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n            outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n\n            outBoneTransform[28] = { { 0.016642f, -0.029992f, 0.083200f, 1 },\n                                     { -0.094577f, 0.694550f, 0.702845f, 0.121100f } };\n            outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                     { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n            outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                     { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n        } else {\n            outBoneTransform[11] = { { 0.002177f, 0.007120f, 0.016319f, 1 },\n                                     { 0.529359f, 0.540512f, -0.463783f, 0.461011f } };\n            outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                     { -0.831727f, 0.270927f, 0.175647f, -0.451638f } };\n            outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                     { -0.854886f, -0.008231f, -0.028107f, -0.517990f } };\n            outBoneTransform[14] = { { 0.033266f, -0.000000f, 0.000000f, 1 },\n                                     { -0.825759f, 0.085208f, 0.086456f, -0.550805f } };\n            outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                     { 0.999195f, -0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { 0.000513f, -0.006545f, 0.016348f, 1 },\n                                     { 0.500244f, 0.530784f, -0.516215f, 0.448939f } };\n            outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                     { 0.831617f, -0.242931f, -0.139695f, 0.479461f } };\n            outBoneTransform[18] = { { 0.040697f, 0.000000f, 0.000000f, 1 },\n                                     { 0.769163f, -0.001746f, 0.001363f, 0.639049f } };\n            outBoneTransform[19] = { { 0.028747f, -0.000000f, -0.000000f, 1 },\n                                     { 0.968615f, -0.064537f, -0.046586f, 0.235477f } };\n            outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, -0.000000f, -0.000000f } };\n            outBoneTransform[21] = { { -0.002478f, -0.018981f, 0.015214f, 1 },\n                                     { 0.474671f, 0.434670f, -0.653212f, 0.398827f } };\n            outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                     { 0.798788f, -0.199577f, -0.094418f, 0.559636f } };\n            outBoneTransform[23] = { { 0.030220f, 0.000002f, -0.000000f, 1 },\n                                     { 0.853087f, 0.001644f, -0.000913f, 0.521765f } };\n            outBoneTransform[24] = { { 0.018187f, -0.000002f, 0.000000f, 1 },\n                                     { 0.974249f, 0.052491f, 0.003591f, 0.219249f } };\n            outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n\n            outBoneTransform[28] = { { 0.016642f, -0.029992f, 0.083200f, 1 },\n                                     { -0.094577f, 0.694550f, 0.702845f, 0.121100f } };\n            outBoneTransform[29] = { { 0.011144f, -0.028727f, 0.108366f, 1 },\n                                     { -0.076328f, 0.788280f, 0.605097f, 0.081527f } };\n            outBoneTransform[30] = { { 0.011333f, -0.026044f, 0.128585f, 1 },\n                                     { -0.144791f, 0.737451f, 0.656958f, -0.060069f } };\n        }\n\n    } else {\n        if (isLeftHand) {\n            outBoneTransform[11] = { { 0.005787f, 0.006806f, 0.016534f, 1 },\n                                     { 0.514203f, 0.522315f, -0.478348f, 0.483700f } };\n            outBoneTransform[12] = { { 0.070953f, 0.000779f, 0.000997f, 1 },\n                                     { 0.723653f, -0.097901f, 0.048546f, 0.681458f } };\n            outBoneTransform[13] = { { 0.043108f, 0.000000f, 0.000000f, 1 },\n                                     { 0.637464f, -0.002366f, -0.002831f, 0.770472f } };\n            outBoneTransform[14] = { { 0.033266f, 0.000000f, 0.000000f, 1 },\n                                     { 0.658008f, 0.002610f, 0.003196f, 0.753000f } };\n            outBoneTransform[15] = { { 0.025892f, -0.000000f, 0.000000f, 1 },\n                                     { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { 0.004123f, -0.006858f, 0.016563f, 1 },\n                                     { 0.489609f, 0.523374f, -0.520644f, 0.463997f } };\n            outBoneTransform[17] = { { 0.065876f, 0.001786f, 0.000693f, 1 },\n                                     { 0.759970f, -0.055609f, 0.011571f, 0.647471f } };\n            outBoneTransform[18] = { { 0.040331f, 0.000000f, 0.000000f, 1 },\n                                     { 0.664315f, 0.001595f, 0.001967f, 0.747449f } };\n            outBoneTransform[19] = { { 0.028489f, -0.000000f, -0.000000f, 1 },\n                                     { 0.626957f, -0.002784f, -0.003234f, 0.779042f } };\n            outBoneTransform[20] = { { 0.022430f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n            outBoneTransform[21] = { { 0.001131f, -0.019295f, 0.015429f, 1 },\n                                     { 0.479766f, 0.477833f, -0.630198f, 0.379934f } };\n            outBoneTransform[22] = { { 0.062878f, 0.002844f, 0.000332f, 1 },\n                                     { 0.827001f, 0.034282f, 0.003440f, 0.561144f } };\n            outBoneTransform[23] = { { 0.029874f, 0.000000f, 0.000000f, 1 },\n                                     { 0.702185f, -0.006716f, -0.009289f, 0.711903f } };\n            outBoneTransform[24] = { { 0.017979f, 0.000000f, 0.000000f, 1 },\n                                     { 0.676853f, 0.007956f, 0.009917f, 0.736009f } };\n            outBoneTransform[25] = { { 0.018018f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n\n            outBoneTransform[28] = { { 0.000448f, 0.001536f, 0.116543f, 1 },\n                                     { -0.039357f, 0.105143f, -0.928833f, -0.353079f } };\n            outBoneTransform[29] = { { 0.003949f, -0.014869f, 0.130608f, 1 },\n                                     { -0.055071f, 0.068695f, -0.944016f, -0.317933f } };\n            outBoneTransform[30] = { { 0.003263f, -0.034685f, 0.139926f, 1 },\n                                     { 0.019690f, -0.100741f, -0.957331f, -0.270149f } };\n        } else {\n            outBoneTransform[11] = { { -0.005787f, 0.006806f, 0.016534f, 1 },\n                                     { 0.522315f, -0.514203f, 0.483700f, 0.478348f } };\n            outBoneTransform[12] = { { -0.070953f, -0.000779f, -0.000997f, 1 },\n                                     { 0.723653f, -0.097901f, 0.048546f, 0.681458f } };\n            outBoneTransform[13] = { { -0.043108f, -0.000000f, -0.000000f, 1 },\n                                     { 0.637464f, -0.002366f, -0.002831f, 0.770472f } };\n            outBoneTransform[14] = { { -0.033266f, -0.000000f, -0.000000f, 1 },\n                                     { 0.658008f, 0.002610f, 0.003196f, 0.753000f } };\n            outBoneTransform[15] = { { -0.025892f, 0.000000f, -0.000000f, 1 },\n                                     { 0.999195f, 0.000000f, 0.000000f, 0.040126f } };\n            outBoneTransform[16] = { { -0.004123f, -0.006858f, 0.016563f, 1 },\n                                     { 0.523374f, -0.489609f, 0.463997f, 0.520644f } };\n            outBoneTransform[17] = { { -0.065876f, -0.001786f, -0.000693f, 1 },\n                                     { 0.759970f, -0.055609f, 0.011571f, 0.647471f } };\n            outBoneTransform[18] = { { -0.040331f, -0.000000f, -0.000000f, 1 },\n                                     { 0.664315f, 0.001595f, 0.001967f, 0.747449f } };\n            outBoneTransform[19] = { { -0.028489f, 0.000000f, 0.000000f, 1 },\n                                     { 0.626957f, -0.002784f, -0.003234f, 0.779042f } };\n            outBoneTransform[20] = { { -0.022430f, 0.000000f, -0.000000f, 1 },\n                                     { 1.000000f, 0.000000f, 0.000000f, 0.000000f } };\n            outBoneTransform[21] = { { -0.001131f, -0.019295f, 0.015429f, 1 },\n                                     { 0.477833f, -0.479766f, 0.379935f, 0.630198f } };\n            outBoneTransform[22] = { { -0.062878f, -0.002844f, -0.000332f, 1 },\n                                     { 0.827001f, 0.034282f, 0.003440f, 0.561144f } };\n            outBoneTransform[23] = { { -0.029874f, -0.000000f, -0.000000f, 1 },\n                                     { 0.702185f, -0.006716f, -0.009289f, 0.711903f } };\n            outBoneTransform[24] = { { -0.017979f, -0.000000f, -0.000000f, 1 },\n                                     { 0.676853f, 0.007956f, 0.009917f, 0.736009f } };\n            outBoneTransform[25] = { { -0.018018f, -0.000000f, 0.000000f, 1 },\n                                     { 1.000000f, -0.000000f, -0.000000f, -0.000000f } };\n\n            outBoneTransform[28] = { { -0.000448f, 0.001536f, 0.116543f, 1 },\n                                     { -0.039357f, 0.105143f, 0.928833f, 0.353079f } };\n            outBoneTransform[29] = { { -0.003949f, -0.014869f, 0.130608f, 1 },\n                                     { -0.055071f, 0.068695f, 0.944016f, 0.317933f } };\n            outBoneTransform[30] = { { -0.003263f, -0.034685f, 0.139926f, 1 },\n                                     { 0.019690f, -0.100741f, 0.957331f, 0.270149f } };\n        }\n    }\n}\n\nvoid Controller::GetBoneTransform(bool withController, vr::VRBoneTransform_t outBoneTransform[]) {\n    auto isLeftHand = device_id == HAND_LEFT_ID;\n\n    vr::VRBoneTransform_t boneTransform1[SKELETON_BONE_COUNT];\n    vr::VRBoneTransform_t boneTransform2[SKELETON_BONE_COUNT];\n\n    // root and wrist\n    outBoneTransform[0] = { { 0.000000f, 0.000000f, 0.000000f, 1 },\n                            { 1.000000f, -0.000000f, -0.000000f, 0.000000f } };\n    if (isLeftHand) {\n        outBoneTransform[1] = { { -0.034038f, 0.036503f, 0.164722f, 1 },\n                                { -0.055147f, -0.078608f, -0.920279f, 0.379296f } };\n    } else {\n        outBoneTransform[1] = { { 0.034038f, 0.036503f, 0.164722f, 1 },\n                                { -0.055147f, -0.078608f, 0.920279f, -0.379296f } };\n    }\n\n    // thumb\n    GetThumbBoneTransform(withController, isLeftHand, m_lastThumbTouch, boneTransform1);\n    GetThumbBoneTransform(withController, isLeftHand, m_currentThumbTouch, boneTransform2);\n    for (int boneIdx = 2; boneIdx < 6; boneIdx++) {\n        outBoneTransform[boneIdx].position = Lerp(\n            boneTransform1[boneIdx].position,\n            boneTransform2[boneIdx].position,\n            m_thumbTouchAnimationProgress\n        );\n        outBoneTransform[boneIdx].orientation = Slerp(\n            boneTransform1[boneIdx].orientation,\n            boneTransform2[boneIdx].orientation,\n            m_thumbTouchAnimationProgress\n        );\n    }\n\n    // trigger (index to pinky)\n    if (m_triggerValue > 0) {\n        GetTriggerBoneTransform(withController, isLeftHand, true, false, boneTransform1);\n        GetTriggerBoneTransform(withController, isLeftHand, true, true, boneTransform2);\n        for (int boneIdx = 6; boneIdx < SKELETON_BONE_COUNT; boneIdx++) {\n            outBoneTransform[boneIdx].position = Lerp(\n                boneTransform1[boneIdx].position, boneTransform2[boneIdx].position, m_triggerValue\n            );\n            outBoneTransform[boneIdx].orientation = Slerp(\n                boneTransform1[boneIdx].orientation,\n                boneTransform2[boneIdx].orientation,\n                m_triggerValue\n            );\n        }\n    } else {\n        GetTriggerBoneTransform(\n            withController, isLeftHand, m_lastTriggerTouch, false, boneTransform1\n        );\n        GetTriggerBoneTransform(\n            withController, isLeftHand, m_currentTriggerTouch, false, boneTransform2\n        );\n        for (int boneIdx = 6; boneIdx < SKELETON_BONE_COUNT; boneIdx++) {\n            outBoneTransform[boneIdx].position = Lerp(\n                boneTransform1[boneIdx].position,\n                boneTransform2[boneIdx].position,\n                m_indexTouchAnimationProgress\n            );\n            outBoneTransform[boneIdx].orientation = Slerp(\n                boneTransform1[boneIdx].orientation,\n                boneTransform2[boneIdx].orientation,\n                m_indexTouchAnimationProgress\n            );\n        }\n    }\n\n    // grip (middle to pinky)\n    if (m_gripValue > 0) {\n        GetGripClickBoneTransform(withController, isLeftHand, boneTransform2);\n        for (int boneIdx = 11; boneIdx < 26; boneIdx++) {\n            outBoneTransform[boneIdx].position = Lerp(\n                outBoneTransform[boneIdx].position, boneTransform2[boneIdx].position, m_gripValue\n            );\n            outBoneTransform[boneIdx].orientation = Slerp(\n                outBoneTransform[boneIdx].orientation,\n                boneTransform2[boneIdx].orientation,\n                m_gripValue\n            );\n        }\n        for (int boneIdx = 28; boneIdx < SKELETON_BONE_COUNT; boneIdx++) {\n            outBoneTransform[boneIdx].position = Lerp(\n                outBoneTransform[boneIdx].position, boneTransform2[boneIdx].position, m_gripValue\n            );\n            outBoneTransform[boneIdx].orientation = Slerp(\n                outBoneTransform[boneIdx].orientation,\n                boneTransform2[boneIdx].orientation,\n                m_gripValue\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Controller.h",
    "content": "#pragma once\n\n#include \"ALVR-common/packet_types.h\"\n#include \"TrackedDevice.h\"\n#include \"openvr_driver_wrap.h\"\n#include <map>\n\nclass Controller : public TrackedDevice {\npublic:\n    Controller(uint64_t deviceID, vr::EVRSkeletalTrackingLevel skeletonLevel);\n    virtual ~Controller() {};\n    void RegisterButton(uint64_t id);\n    void SetButton(uint64_t id, FfiButtonValue value);\n    bool OnPoseUpdate(uint64_t targetTimestampNs, float predictionS, FfiHandData handData);\n\nprivate:\n    static const int SKELETON_BONE_COUNT = 31;\n    static const int ANIMATION_FRAME_COUNT = 15;\n\n    std::map<uint64_t, vr::VRInputComponentHandle_t> m_buttonHandles;\n\n    vr::VRInputComponentHandle_t m_compHaptic;\n    vr::VRInputComponentHandle_t m_compSkeleton = vr::k_ulInvalidInputComponentHandle;\n    vr::EVRSkeletalTrackingLevel m_skeletonLevel;\n\n    uint64_t m_poseTargetTimestampNs;\n\n    // These variables are used for controller hand animation\n    // todo: move to rust\n    float m_thumbTouchAnimationProgress = 0;\n    float m_indexTouchAnimationProgress = 0;\n    bool m_currentThumbTouch = false;\n    bool m_lastThumbTouch = false;\n    bool m_currentTriggerTouch = false;\n    bool m_lastTriggerTouch = false;\n    float m_triggerValue = 0;\n    float m_gripValue = 0;\n\n    vr::VRInputComponentHandle_t getHapticComponent();\n    void GetBoneTransform(bool withController, vr::VRBoneTransform_t outBoneTransform[]);\n\n    // TrackedDevice\n    bool activate() final;\n    void* get_component([[maybe_unused]] const char* component_name_and_version) final {\n        return nullptr;\n    }\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/FakeViveTracker.cpp",
    "content": "#include \"FakeViveTracker.h\"\n\n#include \"Logger.h\"\n#include \"Paths.h\"\n#include \"Settings.h\"\n#include \"Utils.h\"\n#include \"bindings.h\"\n#include <cassert>\n\nFakeViveTracker::FakeViveTracker(uint64_t deviceID)\n    : TrackedDevice(deviceID, vr::TrackedDeviceClass_GenericTracker) { }\n\nbool FakeViveTracker::activate() {\n    Debug(\"FakeViveTracker::Activate\");\n\n    auto vr_properties = vr::VRProperties();\n\n    // Normally a vive tracker emulator would (logically) always set the tracking system to\n    // \"lighthouse\" but in order to do space calibration with existing tools such as OpenVR Space\n    // calibrator and be able to calibrate to/from ALVR HMD (and the proxy tracker) space to/from a\n    // native HMD/tracked device which is already using \"lighthouse\" as the tracking system the\n    // proxy tracker needs to be in a different tracking system to treat them differently and\n    // prevent those tools doing the same space transform to the proxy tracker.\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_TrackingSystemName_String, \"ALVRTrackerCustom\"\n    ); //\"lighthouse\");\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_ModelNumber_String, \"Vive Tracker Pro MV\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_SerialNumber_String, this->get_serial_number().c_str()\n    ); // Changed\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_RenderModelName_String, \"{htc}vr_tracker_vive_1_0\"\n    );\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_WillDriftInYaw_Bool, false);\n    vr_properties->SetStringProperty(this->prop_container, vr::Prop_ManufacturerName_String, \"HTC\");\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_TrackingFirmwareVersion_String,\n        \"1541800000 RUNNER-WATCHMAN$runner-watchman@runner-watchman 2018-01-01 FPGA 512(2.56/0/0) \"\n        \"BL 0 VRC 1541800000 Radio 1518800000\"\n    ); // Changed\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_HardwareRevision_String,\n        \"product 128 rev 2.5.6 lot 2000/0/0 0\"\n    ); // Changed\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_ConnectedWirelessDongle_String, \"D0000BE000\"\n    ); // Changed\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_DeviceIsWireless_Bool, true);\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_DeviceIsCharging_Bool, false);\n    vr_properties->SetFloatProperty(\n        this->prop_container, vr::Prop_DeviceBatteryPercentage_Float, 1.f\n    ); // Always charged\n\n    vr::HmdMatrix34_t l_transform\n        = { { { -1.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, -1.f, 0.f }, { 0.f, -1.f, 0.f, 0.f } } };\n    vr_properties->SetProperty(\n        this->prop_container,\n        vr::Prop_StatusDisplayTransform_Matrix34,\n        &l_transform,\n        sizeof(vr::HmdMatrix34_t),\n        vr::k_unHmdMatrix34PropertyTag\n    );\n\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_Firmware_UpdateAvailable_Bool, false\n    );\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_Firmware_ManualUpdate_Bool, false\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_Firmware_ManualUpdateURL_String,\n        \"https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware\"\n    );\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_HardwareRevision_Uint64, 2214720000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_FirmwareVersion_Uint64, 1541800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_FPGAVersion_Uint64, 512\n    ); // Changed\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_VRCVersion_Uint64, 1514800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_RadioVersion_Uint64, 1518800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        this->prop_container, vr::Prop_DongleVersion_Uint64, 8933539758\n    ); // Changed, based on vr::Prop_ConnectedWirelessDongle_String above\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_DeviceProvidesBatteryStatus_Bool, true\n    );\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_DeviceCanPowerOff_Bool, true);\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_Firmware_ProgrammingTarget_String,\n        this->get_serial_number().c_str()\n    );\n    vr_properties->SetInt32Property(\n        this->prop_container, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker\n    );\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_Firmware_ForceUpdateRequired_Bool, false\n    );\n    vr_properties->SetStringProperty(this->prop_container, vr::Prop_ResourceRoot_String, \"htc\");\n\n    const char* name;\n    if (this->device_id == BODY_CHEST_ID) {\n        name = \"ALVR/tracker/chest\";\n    } else if (this->device_id == BODY_HIPS_ID) {\n        name = \"ALVR/tracker/waist\";\n    } else if (this->device_id == BODY_LEFT_FOOT_ID) {\n        name = \"ALVR/tracker/left_foot\";\n    } else if (this->device_id == BODY_RIGHT_FOOT_ID) {\n        name = \"ALVR/tracker/right_foot\";\n    } else if (this->device_id == BODY_LEFT_KNEE_ID) {\n        name = \"ALVR/tracker/left_knee\";\n    } else if (this->device_id == BODY_RIGHT_KNEE_ID) {\n        name = \"ALVR/tracker/right_knee\";\n    } else if (this->device_id == BODY_LEFT_ELBOW_ID) {\n        name = \"ALVR/tracker/left_elbow\";\n    } else if (this->device_id == BODY_RIGHT_ELBOW_ID) {\n        name = \"ALVR/tracker/right_elbow\";\n    } else {\n        name = \"ALVR/tracker/unknown\";\n    }\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_RegisteredDeviceType_String, name\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_InputProfilePath_String,\n        \"{htc}/input/vive_tracker_profile.json\"\n    );\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_Identifiable_Bool, false);\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_Firmware_RemindUpdate_Bool, false\n    );\n    vr_properties->SetInt32Property(\n        this->prop_container, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_Invalid\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container, vr::Prop_ControllerType_String, \"vive_tracker_waist\"\n    );\n    vr_properties->SetInt32Property(\n        this->prop_container, vr::Prop_ControllerHandSelectionPriority_Int32, -1\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceOff_String,\n        \"{htc}/icons/tracker_status_off.png\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceSearching_String,\n        \"{htc}/icons/tracker_status_searching.gif\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceSearchingAlert_String,\n        \"{htc}/icons/tracker_status_searching_alert.gif\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceReady_String,\n        \"{htc}/icons/tracker_status_ready.png\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceReadyAlert_String,\n        \"{htc}/icons/tracker_status_ready_alert.png\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceNotReady_String,\n        \"{htc}/icons/tracker_status_error.png\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceStandby_String,\n        \"{htc}/icons/tracker_status_standby.png\"\n    );\n    vr_properties->SetStringProperty(\n        this->prop_container,\n        vr::Prop_NamedIconPathDeviceAlertLow_String,\n        \"{htc}/icons/tracker_status_ready_low.png\"\n    );\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_HasDisplayComponent_Bool, false);\n    vr_properties->SetBoolProperty(this->prop_container, vr::Prop_HasCameraComponent_Bool, false);\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_HasDriverDirectModeComponent_Bool, false\n    );\n    vr_properties->SetBoolProperty(\n        this->prop_container, vr::Prop_HasVirtualDisplayComponent_Bool, false\n    );\n    return true;\n}\n\nvoid FakeViveTracker::OnPoseUpdated(uint64_t targetTimestampNs, const FfiDeviceMotion* motion) {\n    if (this->object_id == vr::k_unTrackedDeviceIndexInvalid) {\n        return;\n    }\n\n    bool tracked = motion != nullptr;\n\n    auto pose = vr::DriverPose_t {};\n    pose.poseIsValid = tracked;\n    pose.deviceIsConnected = tracked;\n    pose.result = tracked ? vr::TrackingResult_Running_OK : vr::TrackingResult_Uninitialized;\n\n    pose.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0);\n    pose.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0);\n\n    if (motion != nullptr) {\n        pose.qRotation = HmdQuaternion_Init(\n            motion->pose.orientation.w,\n            motion->pose.orientation.x,\n            motion->pose.orientation.y,\n            motion->pose.orientation.z\n        );\n\n        pose.vecPosition[0] = motion->pose.position[0];\n        pose.vecPosition[1] = motion->pose.position[1];\n        pose.vecPosition[2] = motion->pose.position[2];\n    }\n\n    this->submit_pose(pose);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/FakeViveTracker.h",
    "content": "#pragma once\n\n#include \"TrackedDevice.h\"\n#include \"bindings.h\"\n#include \"openvr_driver_wrap.h\"\n\nclass FakeViveTracker : public TrackedDevice {\npublic:\n    FakeViveTracker(uint64_t deviceID);\n    void OnPoseUpdated(uint64_t targetTimestampNs, const FfiDeviceMotion* motion);\n\nprivate:\n    // TrackedDevice\n    bool activate() final;\n    void* get_component(const char*) final { return nullptr; }\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/HMD.cpp",
    "content": "#include \"HMD.h\"\n\n#include \"Controller.h\"\n#include \"Logger.h\"\n#include \"Paths.h\"\n#include \"PoseHistory.h\"\n#include \"Settings.h\"\n#include \"Utils.h\"\n#include \"ViveTrackerProxy.h\"\n#include \"bindings.h\"\n\n#ifdef _WIN32\n#include \"platform/win32/CEncoder.h\"\n#elif __APPLE__\n#include \"platform/macos/CEncoder.h\"\n#else\n#include \"platform/linux/CEncoder.h\"\n#endif\n\nHmd::Hmd()\n    : TrackedDevice(\n          HEAD_ID,\n          Settings::Instance().m_TrackingRefOnly ? vr::TrackedDeviceClass_TrackingReference\n                                                 : vr::TrackedDeviceClass_HMD\n      )\n    , m_baseComponentsInitialized(false)\n    , m_streamComponentsInitialized(false) {\n    Debug(\"Hmd::constructor\");\n\n    auto dummy_fov = FfiFov { -1.0, 1.0, 1.0, -1.0 };\n    auto dummy_pose = FfiPose { { 0, 0, 0, 1 }, { 0, 0, 0 } };\n    auto dummy_view_params = FfiViewParams { dummy_pose, dummy_fov };\n\n    this->view_params[0] = dummy_view_params;\n    this->view_params[1] = dummy_view_params;\n\n    m_poseHistory = std::make_shared<PoseHistory>();\n\n    if (Settings::Instance().m_enableViveTrackerProxy) {\n        m_viveTrackerProxy = std::make_unique<ViveTrackerProxy>(*this);\n        if (!vr::VRServerDriverHost()->TrackedDeviceAdded(\n                m_viveTrackerProxy->GetSerialNumber(),\n                vr::TrackedDeviceClass_GenericTracker,\n                m_viveTrackerProxy.get()\n            )) {\n            Warn(\"Failed to register Vive tracker\");\n        }\n    }\n}\n\nHmd::~Hmd() {\n    Debug(\"Hmd::destructor\");\n\n    if (m_encoder) {\n        Debug(\"Hmd::~Hmd(): Stopping encoder...\\n\");\n        m_encoder->Stop();\n        m_encoder.reset();\n    }\n\n#ifdef _WIN32\n    if (m_D3DRender) {\n        m_D3DRender->Shutdown();\n        m_D3DRender.reset();\n    }\n#endif\n}\n\nbool Hmd::activate() {\n    Debug(\"Hmd::Activate\");\n\n    auto vr_properties = vr::VRProperties();\n\n    SetOpenvrProps((void*)this, this->device_id);\n\n    vr_properties->SetFloatProperty(\n        this->prop_container,\n        vr::Prop_DisplayFrequency_Float,\n        static_cast<float>(Settings::Instance().m_refreshRate)\n    );\n\n    vr::VRDriverInput()->CreateBooleanComponent(this->prop_container, \"/proximity\", &m_proximity);\n\n#ifdef _WIN32\n    float originalIPD\n        = vr::VRSettings()->GetFloat(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_IPD_Float);\n    vr::VRSettings()->SetFloat(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_IPD_Float, 0.063);\n#endif\n\n    HmdMatrix_SetIdentity(&m_eyeToHeadLeft);\n    HmdMatrix_SetIdentity(&m_eyeToHeadRight);\n\n// Disable async reprojection on Linux. Windows interface uses IVRDriverDirectModeComponent\n// which never applies reprojection\n// Also Disable async reprojection on vulkan\n#ifndef _WIN32\n    vr::VRSettings()->SetBool(\n        vr::k_pch_SteamVR_Section,\n        vr::k_pch_SteamVR_EnableLinuxVulkanAsync_Bool,\n        Settings::Instance().m_enableLinuxVulkanAsyncCompute\n    );\n    vr::VRSettings()->SetBool(\n        vr::k_pch_SteamVR_Section,\n        vr::k_pch_SteamVR_DisableAsyncReprojection_Bool,\n        !Settings::Instance().m_enableLinuxAsyncReprojection\n    );\n#endif\n\n    if (!m_baseComponentsInitialized) {\n        m_baseComponentsInitialized = true;\n\n        if (this->device_class == vr::TrackedDeviceClass_HMD) {\n#ifdef _WIN32\n            m_D3DRender = std::make_shared<CD3DRender>();\n\n            // Use the same adapter as vrcompositor uses. If another adapter is used, vrcompositor\n            // says \"failed to open shared texture\" and then crashes. It seems vrcompositor selects\n            // always(?) first adapter. vrcompositor may use Intel iGPU when user sets it as primary\n            // adapter. I don't know what happens on laptop which support optimus.\n            // Prop_GraphicsAdapterLuid_Uint64 is only for redirect display and is ignored on direct\n            // mode driver. So we can't specify an adapter for vrcompositor. m_nAdapterIndex is set\n            // 0 on the dashboard.\n            if (!m_D3DRender->Initialize(Settings::Instance().m_nAdapterIndex)) {\n                Error(\n                    \"Could not create graphics device for adapter %d.  Requires a minimum of two \"\n                    \"graphics cards.\\n\",\n                    Settings::Instance().m_nAdapterIndex\n                );\n                return false;\n            }\n\n            int32_t nDisplayAdapterIndex;\n            if (!m_D3DRender->GetAdapterInfo(&nDisplayAdapterIndex, m_adapterName)) {\n                Error(\"Failed to get primary adapter info!\\n\");\n                return false;\n            }\n\n            Info(\"Using %ls as primary graphics adapter.\\n\", m_adapterName.c_str());\n            Info(\"OSVer: %ls\\n\", GetWindowsOSVersion().c_str());\n\n            m_directModeComponent\n                = std::make_shared<OvrDirectModeComponent>(m_D3DRender, m_poseHistory);\n#endif\n        }\n\n        DriverReadyIdle(this->device_class == vr::TrackedDeviceClass_HMD);\n    }\n\n    if (this->device_class == vr::TrackedDeviceClass_HMD) {\n        vr::VREvent_Data_t eventData;\n        eventData.ipd = { 0.063 };\n        vr::VRServerDriverHost()->VendorSpecificEvent(\n            this->object_id, vr::VREvent_IpdChanged, eventData, 0\n        );\n    }\n\n    return true;\n}\n\nvoid* Hmd::get_component(const char* component_name_and_version) {\n    Debug(\"Hmd::GetComponent %s\", component_name_and_version);\n\n    // NB: \"this\" pointer needs to be statically cast to point to the correct vtable\n\n    auto name_and_vers = std::string(component_name_and_version);\n    if (name_and_vers == vr::IVRDisplayComponent_Version) {\n        return (vr::IVRDisplayComponent*)this;\n    }\n\n#ifdef _WIN32\n    if (name_and_vers == vr::IVRDriverDirectModeComponent_Version) {\n        return m_directModeComponent.get();\n    }\n#endif\n\n    return nullptr;\n}\n\nvoid Hmd::OnPoseUpdated(uint64_t targetTimestampNs, FfiDeviceMotion motion) {\n    Debug(\"Hmd::OnPoseUpdated\");\n\n    if (this->object_id == vr::k_unTrackedDeviceIndexInvalid) {\n        return;\n    }\n    auto pose = vr::DriverPose_t {};\n    pose.poseIsValid = true;\n    pose.result = vr::TrackingResult_Running_OK;\n    pose.deviceIsConnected = true;\n\n    pose.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0);\n    pose.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0);\n\n    pose.qRotation = HmdQuaternion_Init(\n        motion.pose.orientation.w,\n        motion.pose.orientation.x,\n        motion.pose.orientation.y,\n        motion.pose.orientation.z\n    );\n\n    pose.vecPosition[0] = motion.pose.position[0];\n    pose.vecPosition[1] = motion.pose.position[1];\n    pose.vecPosition[2] = motion.pose.position[2];\n\n    this->submit_pose(pose);\n\n    m_poseHistory->OnPoseUpdated(targetTimestampNs, motion);\n\n    if (m_viveTrackerProxy)\n        m_viveTrackerProxy->update();\n\n#if !defined(_WIN32) && !defined(__APPLE__)\n    // This has to be set after initialization is done, because something in vrcompositor is\n    // setting it to 90Hz in the meantime\n    if (!m_refreshRateSet && m_encoder && m_encoder->IsConnected()) {\n        m_refreshRateSet = true;\n        vr::VRProperties()->SetFloatProperty(\n            this->prop_container,\n            vr::Prop_DisplayFrequency_Float,\n            static_cast<float>(Settings::Instance().m_refreshRate)\n        );\n    }\n#endif\n}\n\nvoid Hmd::StartStreaming() {\n    Debug(\"Hmd::StartStreaming\");\n\n    vr::VRDriverInput()->UpdateBooleanComponent(m_proximity, true, 0.0);\n\n    if (m_streamComponentsInitialized) {\n        return;\n    }\n\n    // Spin up a separate thread to handle the overlapped encoding/transmit step.\n    if (this->device_class == vr::TrackedDeviceClass_HMD) {\n#ifdef _WIN32\n        m_encoder = std::make_shared<CEncoder>();\n        try {\n            m_encoder->Initialize(m_D3DRender);\n        } catch (Exception e) {\n            Error(\n                \"Your GPU does not meet the requirements for video encoding. %s %s\\n%s %s\\n\",\n                \"If you get this error after changing some settings, you can revert them by\",\n                \"deleting the file \\\"session.json\\\" in the installation folder.\",\n                \"Failed to initialize CEncoder:\",\n                e.what()\n            );\n        }\n        m_encoder->Start();\n\n        m_directModeComponent->SetEncoder(m_encoder);\n\n#elif __APPLE__\n        m_encoder = std::make_shared<CEncoder>();\n#else\n        m_encoder = std::make_shared<CEncoder>(m_poseHistory);\n        m_encoder->Start();\n#endif\n        m_encoder->OnStreamStart();\n    }\n\n    m_streamComponentsInitialized = true;\n}\n\nvoid Hmd::StopStreaming() {\n    Debug(\"Hmd::StopStreaming\");\n\n    vr::VRDriverInput()->UpdateBooleanComponent(m_proximity, false, 0.0);\n}\n\nvoid Hmd::SetViewParams(const FfiViewParams params[2]) {\n    Debug(\"Hmd::SetViewParams\");\n\n    this->view_params[0] = params[0];\n    this->view_params[1] = params[1];\n\n    // The OpenXR spec defines the HMD position as the midpoint\n    // between the eyes, so conversion to this is handled by the\n    // client.\n    auto left_transform = pose_to_mat(params[0].pose);\n    auto right_transform = pose_to_mat(params[1].pose);\n    vr::VRServerDriverHost()->SetDisplayEyeToHead(object_id, left_transform, right_transform);\n\n    auto left_proj = fov_to_tangents(params[0].fov);\n    auto right_proj = fov_to_tangents(params[1].fov);\n    vr::VRServerDriverHost()->SetDisplayProjectionRaw(object_id, left_proj, right_proj);\n\n#ifdef _WIN32\n    if (m_encoder) {\n        m_encoder->SetViewParams(left_proj, left_transform, right_proj, right_transform);\n    }\n#endif\n\n    // todo: check if this is still needed\n    vr::VRServerDriverHost()->VendorSpecificEvent(\n        object_id, vr::VREvent_LensDistortionChanged, {}, 0\n    );\n}\n\nvoid Hmd::SetProximityState(bool headsetIsWorn) {\n    vr::VRDriverInput()->UpdateBooleanComponent(m_proximity, headsetIsWorn, 0.0);\n}\n\nvoid Hmd::GetWindowBounds(int32_t* pnX, int32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight) {\n    Debug(\n        \"Hmd::GetWindowBounds %dx%d - %dx%d\\n\",\n        0,\n        0,\n        Settings::Instance().m_renderWidth,\n        Settings::Instance().m_renderHeight\n    );\n\n    *pnX = 0;\n    *pnY = 0;\n    *pnWidth = Settings::Instance().m_renderWidth;\n    *pnHeight = Settings::Instance().m_renderHeight;\n}\n\nbool Hmd::IsDisplayRealDisplay() {\n#ifdef _WIN32\n    return false;\n#else\n    return true;\n#endif\n}\n\nvoid Hmd::GetRecommendedRenderTargetSize(uint32_t* pnWidth, uint32_t* pnHeight) {\n    *pnWidth = Settings::Instance().m_recommendedTargetWidth / 2;\n    *pnHeight = Settings::Instance().m_recommendedTargetHeight;\n    Debug(\"Hmd::GetRecommendedRenderTargetSize %dx%d\\n\", *pnWidth, *pnHeight);\n}\n\nvoid Hmd::GetEyeOutputViewport(\n    vr::EVREye eEye, uint32_t* pnX, uint32_t* pnY, uint32_t* pnWidth, uint32_t* pnHeight\n) {\n    *pnY = 0;\n    *pnWidth = Settings::Instance().m_renderWidth / 2;\n    *pnHeight = Settings::Instance().m_renderHeight;\n\n    if (eEye == vr::Eye_Left) {\n        *pnX = 0;\n    } else {\n        *pnX = Settings::Instance().m_renderWidth / 2;\n    }\n\n    Debug(\"Hmd::GetEyeOutputViewport Eye=%d %dx%d %dx%d\\n\", eEye, *pnX, *pnY, *pnWidth, *pnHeight);\n}\n\nvoid Hmd::GetProjectionRaw(vr::EVREye eye, float* left, float* right, float* top, float* bottom) {\n    auto proj = fov_to_tangents(this->view_params[eye].fov);\n    *left = proj.vTopLeft.v[0];\n    *right = proj.vBottomRight.v[0];\n    *top = proj.vTopLeft.v[1];\n    *bottom = proj.vBottomRight.v[1];\n\n    Debug(\"Hmd::GetProjectionRaw Eye=%d %f %f %f %f\\n\", eye, *left, *right, *top, *bottom);\n}\n\nvr::DistortionCoordinates_t Hmd::ComputeDistortion(vr::EVREye, float u, float v) {\n    return { { u, v }, { u, v }, { u, v } };\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/HMD.h",
    "content": "#pragma once\n\n#include \"ALVR-common/packet_types.h\"\n#include \"TrackedDevice.h\"\n#include \"openvr_driver_wrap.h\"\n#include <memory>\n#ifdef _WIN32\n#include \"platform/win32/OvrDirectModeComponent.h\"\n#endif\n\nclass Controller;\nclass Controller;\nclass ViveTrackerProxy;\n\nclass CEncoder;\n#ifdef _WIN32\nclass CD3DRender;\n#endif\nclass PoseHistory;\n\nclass Hmd : public TrackedDevice, vr::IVRDisplayComponent {\npublic:\n    std::shared_ptr<PoseHistory> m_poseHistory;\n    std::shared_ptr<CEncoder> m_encoder;\n\n    Hmd();\n    virtual ~Hmd();\n    void OnPoseUpdated(uint64_t targetTimestampNs, FfiDeviceMotion motion);\n    void StartStreaming();\n    void StopStreaming();\n    void SetViewParams(const FfiViewParams params[2]);\n    void SetProximityState(bool headsetIsWorn);\n\nprivate:\n    vr::VRInputComponentHandle_t m_proximity;\n\n    FfiViewParams view_params[2];\n\n    bool m_baseComponentsInitialized;\n    bool m_streamComponentsInitialized;\n\n    vr::HmdMatrix34_t m_eyeToHeadLeft;\n    vr::HmdMatrix34_t m_eyeToHeadRight;\n    vr::HmdRect2_t m_eyeFoVLeft;\n    vr::HmdRect2_t m_eyeFoVRight;\n\n    std::wstring m_adapterName;\n\n#ifdef _WIN32\n    std::shared_ptr<CD3DRender> m_D3DRender;\n#endif\n\n#ifdef _WIN32\n    std::shared_ptr<OvrDirectModeComponent> m_directModeComponent;\n#endif\n\n    std::shared_ptr<ViveTrackerProxy> m_viveTrackerProxy;\n\n#ifndef _WIN32\n    bool m_refreshRateSet = false;\n#endif\n\n    // TrackedDevice\n    virtual bool activate() final;\n    virtual void* get_component(const char* component_name_and_version) final;\n\n    // IVRDisplayComponent\n    virtual void GetWindowBounds(int32_t* x, int32_t* y, uint32_t* width, uint32_t* height);\n    virtual bool IsDisplayOnDesktop() { return false; }\n    virtual bool IsDisplayRealDisplay();\n    virtual void GetRecommendedRenderTargetSize(uint32_t* width, uint32_t* height);\n    virtual void GetEyeOutputViewport(\n        vr::EVREye eye, uint32_t* x, uint32_t* y, uint32_t* width, uint32_t* height\n    );\n    virtual void\n    GetProjectionRaw(vr::EVREye eEye, float* pfLeft, float* pfRight, float* pfTop, float* pfBottom);\n    virtual vr::DistortionCoordinates_t ComputeDistortion(vr::EVREye eEye, float fU, float fV);\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/IDRScheduler.cpp",
    "content": "#include \"IDRScheduler.h\"\n\n#include \"Utils.h\"\n#include <mutex>\n\nIDRScheduler::IDRScheduler() { }\n\nIDRScheduler::~IDRScheduler() { }\n\nvoid IDRScheduler::OnStreamStart() {\n    m_minIDRFrameInterval = Settings::Instance().m_minimumIdrIntervalMs * 1000;\n    m_scheduled = false;\n    InsertIDR();\n}\n\nvoid IDRScheduler::InsertIDR() {\n    std::unique_lock lock(m_mutex);\n\n    m_insertIDRTime = GetTimestampUs() - MIN_IDR_FRAME_INTERVAL * 2;\n    m_scheduled = true;\n}\n\nbool IDRScheduler::CheckIDRInsertion() {\n    std::unique_lock lock(m_mutex);\n\n    if (m_scheduled) {\n        if (m_insertIDRTime <= GetTimestampUs()) {\n            m_scheduled = false;\n            return true;\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/IDRScheduler.h",
    "content": "#pragma once\n\n#include \"Settings.h\"\n#include <mutex>\n#include <stdint.h>\n\nclass IDRScheduler {\npublic:\n    IDRScheduler();\n    ~IDRScheduler();\n\n    void OnStreamStart();\n    void InsertIDR();\n\n    bool CheckIDRInsertion();\n\nprivate:\n    static const int MIN_IDR_FRAME_INTERVAL = 100 * 1000; // 100-milliseconds\n    uint64_t m_insertIDRTime = 0;\n    bool m_scheduled = false;\n    std::mutex m_mutex;\n    uint64_t m_minIDRFrameInterval = MIN_IDR_FRAME_INTERVAL;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Logger.cpp",
    "content": "#include \"Logger.h\"\n\n#include <cstdarg>\n\n#include \"bindings.h\"\n#include \"driverlog.h\"\n\nvoid _log(const char* format, va_list args, void (*logFn)(const char*), bool driverLog = false) {\n    char buf[1024];\n    int count = vsnprintf(buf, sizeof(buf), format, args);\n    if (count > (int)sizeof(buf))\n        count = (int)sizeof(buf);\n    if (count > 0 && buf[count - 1] == '\\n')\n        buf[count - 1] = '\\0';\n\n    logFn(buf);\n\n    if (driverLog)\n        DriverLog(buf);\n}\n\nException MakeException(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    Exception e = FormatExceptionV(format, args);\n    va_end(args);\n\n    return e;\n}\n\nvoid Error(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    _log(format, args, LogError, true);\n    va_end(args);\n}\n\nvoid Warn(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    _log(format, args, LogWarn, true);\n    va_end(args);\n}\n\nvoid Info(const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n    // Don't log to SteamVR/writing to file for info level, this is mostly statistics info\n    _log(format, args, LogInfo);\n    va_end(args);\n}\n\nvoid Debug(const char* format, ...) {\n#ifdef ALVR_DEBUG_LOG\n    va_list args;\n    va_start(args, format);\n    _log(format, args, LogDebug);\n    va_end(args);\n#else\n    (void)format;\n#endif\n}\n\nvoid LogPeriod(const char* tag, const char* format, ...) {\n    va_list args;\n    va_start(args, format);\n\n    char buf[1024];\n    int count = vsnprintf(buf, sizeof(buf), format, args);\n    if (count > (int)sizeof(buf))\n        count = (int)sizeof(buf);\n    if (count > 0 && buf[count - 1] == '\\n')\n        buf[count - 1] = '\\0';\n\n    LogPeriodically(tag, buf);\n\n    va_end(args);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Logger.h",
    "content": "#pragma once\n\n#include \"ALVR-common/exception.h\"\n\nException MakeException(const char* format, ...);\n\nvoid Error(const char* format, ...);\nvoid Warn(const char* format, ...);\nvoid Info(const char* format, ...);\nvoid Debug(const char* format, ...);\nvoid LogPeriod(const char* tag, const char* format, ...);\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/NalParsing.cpp",
    "content": "\n#include \"Logger.h\"\n#include \"Settings.h\"\n#include \"Utils.h\"\n#include \"bindings.h\"\n#include <mutex>\n#include <string.h>\n\nstatic const char NAL_PREFIX_3B[] = { 0x00, 0x00, 0x01 };\nstatic const char NAL_PREFIX_4B[] = { 0x00, 0x00, 0x00, 0x01 };\n\nstatic const unsigned char H264_NAL_TYPE_SPS = 7;\nstatic const unsigned char HEVC_NAL_TYPE_VPS = 32;\n\nstatic const unsigned char H264_NAL_TYPE_AUD = 9;\nstatic const unsigned char HEVC_NAL_TYPE_AUD = 35;\n\nint8_t getNalPrefixSize(unsigned char* buf) {\n    if (memcmp(buf, NAL_PREFIX_3B, sizeof(NAL_PREFIX_3B)) == 0) {\n        return sizeof(NAL_PREFIX_3B);\n    } else if (memcmp(buf, NAL_PREFIX_4B, sizeof(NAL_PREFIX_4B)) == 0) {\n        return sizeof(NAL_PREFIX_4B);\n    } else {\n        return -1;\n    }\n}\n\n/*\nSends the (VPS + )SPS + PPS video configuration headers from H.264 or H.265 stream as a sequence of\nNALs. (VPS + )SPS + PPS have short size (8bytes + 28bytes in some environment), so we can assume\nSPS + PPS is contained in first fragment.\n*/\nvoid sendHeaders(int codec, unsigned char*& buf, int& len, int nalNum) {\n    unsigned char* cursor = buf;\n    int headersLen = 0;\n    int foundHeaders = -1; // Offset by 1 header to find the length until the next header\n\n    while (headersLen <= len) {\n        if (headersLen + sizeof(NAL_PREFIX_4B) > (unsigned)len) {\n            cursor++;\n            headersLen++;\n            continue;\n        }\n        int8_t prefixSize = getNalPrefixSize(cursor);\n        if (prefixSize == -1) {\n            cursor++;\n            headersLen++;\n            continue;\n        }\n        foundHeaders++;\n        if (foundHeaders == nalNum) {\n            break;\n        }\n        headersLen += prefixSize;\n        cursor += prefixSize;\n    }\n\n    if (foundHeaders != nalNum) {\n        return;\n    }\n\n    SetVideoConfigNals((const unsigned char*)buf, headersLen, codec);\n\n    // move the cursor forward excluding config NALs\n    buf = cursor;\n    len -= headersLen;\n}\n\nvoid processH264Nals(unsigned char*& buf, int& len) {\n    unsigned char prefixSize = getNalPrefixSize(buf);\n    unsigned char nalType = buf[prefixSize] & 0x1F;\n\n    if (nalType == H264_NAL_TYPE_AUD && len > prefixSize * 2 + 2) {\n        buf += prefixSize + 2;\n        len -= prefixSize + 2;\n        prefixSize = getNalPrefixSize(buf);\n        nalType = buf[prefixSize] & 0x1F;\n    }\n    if (nalType == H264_NAL_TYPE_SPS) {\n        sendHeaders(ALVR_CODEC_H264, buf, len, 2); // 2 headers SPS and PPS\n    }\n}\n\nvoid processHevcNals(unsigned char*& buf, int& len) {\n    unsigned char prefixSize = getNalPrefixSize(buf);\n    unsigned char nalType = (buf[prefixSize] >> 1) & 0x3F;\n\n    if (nalType == HEVC_NAL_TYPE_AUD && len > prefixSize * 2 + 3) {\n        buf += prefixSize + 3;\n        len -= prefixSize + 3;\n        prefixSize = getNalPrefixSize(buf);\n        nalType = (buf[prefixSize] >> 1) & 0x3F;\n    }\n    if (nalType == HEVC_NAL_TYPE_VPS) {\n        sendHeaders(ALVR_CODEC_HEVC, buf, len, 3); // 3 headers VPS, SPS and PPS\n    }\n}\n\nvoid ParseFrameNals(\n    int codec, unsigned char* buf, int len, unsigned long long targetTimestampNs, bool isIdr\n) {\n    static bool av1GotFrame = false;\n\n    if ((unsigned)len < sizeof(NAL_PREFIX_4B)) {\n        return;\n    }\n\n    if (codec == ALVR_CODEC_H264) {\n        processH264Nals(buf, len);\n    } else if (codec == ALVR_CODEC_HEVC) {\n        processHevcNals(buf, len);\n    } else if (codec == ALVR_CODEC_AV1 && !av1GotFrame) {\n        av1GotFrame = true;\n        SetVideoConfigNals(0, 0, codec);\n    }\n\n    VideoSend(targetTimestampNs, buf, len, isIdr);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Paths.cpp",
    "content": "#include \"Paths.h\"\n#include \"bindings.h\"\n\nuint64_t HEAD_ID;\nuint64_t HAND_LEFT_ID;\nuint64_t HAND_RIGHT_ID;\nuint64_t HAND_TRACKER_LEFT_ID;\nuint64_t HAND_TRACKER_RIGHT_ID;\nuint64_t BODY_CHEST_ID;\nuint64_t BODY_HIPS_ID;\nuint64_t BODY_LEFT_ELBOW_ID;\nuint64_t BODY_RIGHT_ELBOW_ID;\nuint64_t BODY_LEFT_KNEE_ID;\nuint64_t BODY_LEFT_FOOT_ID;\nuint64_t BODY_RIGHT_KNEE_ID;\nuint64_t BODY_RIGHT_FOOT_ID;\n\nuint64_t LEFT_A_TOUCH_ID;\nuint64_t LEFT_B_TOUCH_ID;\nuint64_t LEFT_X_TOUCH_ID;\nuint64_t LEFT_Y_TOUCH_ID;\nuint64_t LEFT_TRACKPAD_TOUCH_ID;\nuint64_t LEFT_THUMBSTICK_TOUCH_ID;\nuint64_t LEFT_THUMBREST_TOUCH_ID;\nuint64_t LEFT_TRIGGER_TOUCH_ID;\nuint64_t LEFT_TRIGGER_VALUE_ID;\nuint64_t LEFT_SQUEEZE_VALUE_ID;\nuint64_t RIGHT_A_TOUCH_ID;\nuint64_t RIGHT_B_TOUCH_ID;\nuint64_t RIGHT_TRACKPAD_TOUCH_ID;\nuint64_t RIGHT_THUMBSTICK_TOUCH_ID;\nuint64_t RIGHT_THUMBREST_TOUCH_ID;\nuint64_t RIGHT_TRIGGER_TOUCH_ID;\nuint64_t RIGHT_TRIGGER_VALUE_ID;\nuint64_t RIGHT_SQUEEZE_VALUE_ID;\n\nstd::set<uint64_t> BODY_IDS;\nstd::map<uint64_t, ButtonInfo> LEFT_CONTROLLER_BUTTON_MAPPING;\nstd::map<uint64_t, ButtonInfo> RIGHT_CONTROLLER_BUTTON_MAPPING;\nstd::map<uint64_t, std::vector<uint64_t>> ALVR_TO_STEAMVR_PATH_IDS;\n\nvoid init_paths() {\n    HEAD_ID = PathStringToHash(\"/user/head\");\n    HAND_LEFT_ID = PathStringToHash(\"/user/hand/left\");\n    HAND_RIGHT_ID = PathStringToHash(\"/user/hand/right\");\n    HAND_TRACKER_LEFT_ID = PathStringToHash(\"/user/hand_tracker/left\");\n    HAND_TRACKER_RIGHT_ID = PathStringToHash(\"/user/hand_tracker/right\");\n    BODY_CHEST_ID = PathStringToHash(\"/user/body/chest\");\n    BODY_HIPS_ID = PathStringToHash(\"/user/body/waist\");\n    BODY_LEFT_ELBOW_ID = PathStringToHash(\"/user/body/left_elbow\");\n    BODY_RIGHT_ELBOW_ID = PathStringToHash(\"/user/body/right_elbow\");\n    BODY_LEFT_KNEE_ID = PathStringToHash(\"/user/body/left_knee\");\n    BODY_LEFT_FOOT_ID = PathStringToHash(\"/user/body/left_foot\");\n    BODY_RIGHT_KNEE_ID = PathStringToHash(\"/user/body/right_knee\");\n    BODY_RIGHT_FOOT_ID = PathStringToHash(\"/user/body/right_foot\");\n\n    LEFT_A_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/a/touch\");\n    LEFT_B_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/b/touch\");\n    LEFT_X_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/x/touch\");\n    LEFT_Y_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/y/touch\");\n    LEFT_TRACKPAD_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/trackpad/touch\");\n    LEFT_THUMBSTICK_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/thumbstick/touch\");\n    LEFT_THUMBREST_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/thumbrest/touch\");\n    LEFT_TRIGGER_TOUCH_ID = PathStringToHash(\"/user/hand/left/input/trigger/touch\");\n    LEFT_TRIGGER_VALUE_ID = PathStringToHash(\"/user/hand/left/input/trigger/value\");\n    LEFT_SQUEEZE_VALUE_ID = PathStringToHash(\"/user/hand/left/input/squeeze/value\");\n    RIGHT_A_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/a/touch\");\n    RIGHT_B_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/b/touch\");\n    RIGHT_TRACKPAD_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/trackpad/touch\");\n    RIGHT_THUMBSTICK_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/thumbstick/touch\");\n    RIGHT_THUMBREST_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/thumbrest/touch\");\n    RIGHT_TRIGGER_TOUCH_ID = PathStringToHash(\"/user/hand/right/input/trigger/touch\");\n    RIGHT_TRIGGER_VALUE_ID = PathStringToHash(\"/user/hand/right/input/trigger/value\");\n    RIGHT_SQUEEZE_VALUE_ID = PathStringToHash(\"/user/hand/right/input/squeeze/value\");\n\n    BODY_IDS.insert(BODY_CHEST_ID);\n    BODY_IDS.insert(BODY_HIPS_ID);\n    BODY_IDS.insert(BODY_LEFT_ELBOW_ID);\n    BODY_IDS.insert(BODY_RIGHT_ELBOW_ID);\n    BODY_IDS.insert(BODY_LEFT_KNEE_ID);\n    BODY_IDS.insert(BODY_LEFT_FOOT_ID);\n    BODY_IDS.insert(BODY_RIGHT_KNEE_ID);\n    BODY_IDS.insert(BODY_RIGHT_FOOT_ID);\n\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/system/click\"),\n                                            { { \"/input/system/click\", \"/input/left_ps/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/system/touch\"),\n                                            { { \"/input/system/touch\", \"/input/left_ps/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/menu/click\"),\n          { { \"/input/system/click\", \"/input/application_menu/click\", \"/input/create/click\" },\n            ButtonType::Binary } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/menu/touch\"),\n          { { \"/input/system/touch\", \"/input/application_menu/touch\", \"/input/create/touch\" },\n            ButtonType::Binary } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/a/click\"),\n                                            { { \"/input/a/click\", \"/input/cross/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/a/touch\"),\n                                            { { \"/input/a/touch\", \"/input/cross/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/b/click\"),\n                                            { { \"/input/b/click\", \"/input/circle/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/b/touch\"),\n                                            { { \"/input/b/touch\", \"/input/circle/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/x/click\"),\n                                            { { \"/input/x/click\", \"/input/square/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/x/touch\"),\n                                            { { \"/input/x/touch\", \"/input/square/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/y/click\"),\n                                            { { \"/input/y/click\", \"/input/triangle/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/y/touch\"),\n                                            { { \"/input/y/touch\", \"/input/triangle/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/squeeze/click\"),\n                                            { { \"/input/grip/click\", \"/input/l1/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/squeeze/touch\"),\n                                            { { \"/input/grip/touch\", \"/input/l1/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/squeeze/value\"),\n                                            { { \"/input/grip/value\", \"/input/l1/value\" },\n                                              ButtonType::ScalarOneSided } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/squeeze/force\"),\n                                            { { \"/input/grip/force\" },\n                                              ButtonType::ScalarOneSided } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trigger/click\"),\n                                            { { \"/input/trigger/click\", \"/input/l2/click\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trigger/value\"),\n                                            { { \"/input/trigger/value\", \"/input/l2/value\" },\n                                              ButtonType::ScalarOneSided } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trigger/touch\"),\n                                            { { \"/input/trigger/touch\", \"/input/l2/touch\" },\n                                              ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/thumbstick/x\"),\n          { { \"/input/joystick/x\", \"/input/thumbstick/x\", \"/input/left_stick/x\" },\n            ButtonType::ScalarTwoSided } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/thumbstick/y\"),\n          { { \"/input/joystick/y\", \"/input/thumbstick/y\", \"/input/left_stick/y\" },\n            ButtonType::ScalarTwoSided } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/thumbstick/click\"),\n          { { \"/input/joystick/click\", \"/input/thumbstick/click\", \"/input/left_stick/click\" },\n            ButtonType::Binary } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/thumbstick/touch\"),\n          { { \"/input/joystick/touch\", \"/input/thumbstick/touch\", \"/input/left_stick/touch\" },\n            ButtonType::Binary } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trackpad/x\"),\n                                            { { \"/input/trackpad/x\" },\n                                              ButtonType::ScalarTwoSided } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trackpad/y\"),\n                                            { { \"/input/trackpad/y\" },\n                                              ButtonType::ScalarTwoSided } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trackpad/click\"\n                                            ),\n                                            { { \"/input/trackpad/click\" }, ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/left/input/trackpad/force\"),\n          { { \"/input/trackpad/force\" }, ButtonType::ScalarOneSided } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/trackpad/touch\"\n                                            ),\n                                            { { \"/input/trackpad/touch\" }, ButtonType::Binary } });\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/left/input/thumbrest/touch\"\n                                            ),\n                                            { { \"/input/thumbrest/touch\" }, ButtonType::Binary } });\n\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/system/click\"),\n          { { \"/input/system/click\", \"/input/right_ps/click\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/system/touch\"),\n          { { \"/input/system/touch\", \"/input/right_ps/touch\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/menu/click\"),\n          { { \"/input/system/click\", \"/input/application_menu/click\", \"/input/options/click\" },\n            ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/menu/touch\"),\n          { { \"/input/system/touch\", \"/input/application_menu/touch\", \"/input/options/touch\" },\n            ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/a/click\"),\n                                             { { \"/input/a/click\", \"/input/cross/click\" },\n                                               ButtonType::Binary } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/a/touch\"),\n                                             { { \"/input/a/touch\", \"/input/cross/touch\" },\n                                               ButtonType::Binary } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/b/click\"),\n                                             { { \"/input/b/click\", \"/input/circle/click\" },\n                                               ButtonType::Binary } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/b/touch\"),\n                                             { { \"/input/b/touch\", \"/input/circle/touch\" },\n                                               ButtonType::Binary } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/squeeze/click\"),\n          { { \"/input/grip/click\", \"/input/r1/click\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/squeeze/touch\"),\n          { { \"/input/grip/touch\", \"/input/r1/touch\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/squeeze/value\"),\n          { { \"/input/grip/value\", \"/input/r1/value\" }, ButtonType::ScalarOneSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/squeeze/force\"),\n          { { \"/input/grip/force\" }, ButtonType::ScalarOneSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trigger/click\"),\n          { { \"/input/trigger/click\", \"/input/r2/click\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trigger/value\"),\n          { { \"/input/trigger/value\", \"/input/r2/value\" }, ButtonType::ScalarOneSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trigger/touch\"),\n          { { \"/input/trigger/touch\", \"/input/r2/touch\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/thumbstick/x\"),\n          { { \"/input/joystick/x\", \"/input/thumbstick/x\", \"/input/right_stick/x\" },\n            ButtonType::ScalarTwoSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/thumbstick/y\"),\n          { { \"/input/joystick/y\", \"/input/thumbstick/y\", \"/input/right_stick/y\" },\n            ButtonType::ScalarTwoSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/thumbstick/click\"),\n          { { \"/input/joystick/click\", \"/input/thumbstick/click\", \"/input/right_stick/click\" },\n            ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/thumbstick/touch\"),\n          { { \"/input/joystick/touch\", \"/input/thumbstick/touch\", \"/input/right_stick/touch\" },\n            ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/trackpad/x\"),\n                                             { { \"/input/trackpad/x\" },\n                                               ButtonType::ScalarTwoSided } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert({ PathStringToHash(\"/user/hand/right/input/trackpad/y\"),\n                                             { { \"/input/trackpad/y\" },\n                                               ButtonType::ScalarTwoSided } });\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trackpad/click\"),\n          { { \"/input/trackpad/click\" }, ButtonType::Binary } }\n    );\n    LEFT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trackpad/force\"),\n          { { \"/input/trackpad/force\" }, ButtonType::ScalarOneSided } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/trackpad/touch\"),\n          { { \"/input/trackpad/touch\" }, ButtonType::Binary } }\n    );\n    RIGHT_CONTROLLER_BUTTON_MAPPING.insert(\n        { PathStringToHash(\"/user/hand/right/input/thumbrest/touch\"),\n          { { \"/input/thumbrest/touch\" }, ButtonType::Binary } }\n    );\n\n    for (auto hand : { LEFT_CONTROLLER_BUTTON_MAPPING, RIGHT_CONTROLLER_BUTTON_MAPPING }) {\n        for (auto info : hand) {\n            std::vector<uint64_t> ids;\n            for (auto path : info.second.steamvr_paths) {\n                ids.push_back(PathStringToHash(path));\n            }\n            ALVR_TO_STEAMVR_PATH_IDS.insert({ info.first, ids });\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Paths.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <map>\n#include <set>\n#include <vector>\n\n#include \"openvr_driver_wrap.h\"\n\nextern uint64_t HEAD_ID;\nextern uint64_t HAND_LEFT_ID;\nextern uint64_t HAND_RIGHT_ID;\nextern uint64_t HAND_TRACKER_LEFT_ID;\nextern uint64_t HAND_TRACKER_RIGHT_ID;\nextern uint64_t BODY_CHEST_ID;\nextern uint64_t BODY_HIPS_ID;\nextern uint64_t BODY_LEFT_ELBOW_ID;\nextern uint64_t BODY_RIGHT_ELBOW_ID;\nextern uint64_t BODY_LEFT_KNEE_ID;\nextern uint64_t BODY_LEFT_FOOT_ID;\nextern uint64_t BODY_RIGHT_KNEE_ID;\nextern uint64_t BODY_RIGHT_FOOT_ID;\n\n// These values are needed to determine the hand skeleton when holding a controller.\n// todo: move inferred hand skeleton to rust\nextern uint64_t LEFT_A_TOUCH_ID;\nextern uint64_t LEFT_B_TOUCH_ID;\nextern uint64_t LEFT_X_TOUCH_ID;\nextern uint64_t LEFT_Y_TOUCH_ID;\nextern uint64_t LEFT_TRACKPAD_TOUCH_ID;\nextern uint64_t LEFT_THUMBSTICK_TOUCH_ID;\nextern uint64_t LEFT_THUMBREST_TOUCH_ID;\nextern uint64_t LEFT_TRIGGER_TOUCH_ID;\nextern uint64_t LEFT_TRIGGER_VALUE_ID;\nextern uint64_t LEFT_SQUEEZE_TOUCH_ID;\nextern uint64_t LEFT_SQUEEZE_VALUE_ID;\nextern uint64_t RIGHT_A_TOUCH_ID;\nextern uint64_t RIGHT_B_TOUCH_ID;\nextern uint64_t RIGHT_TRACKPAD_TOUCH_ID;\nextern uint64_t RIGHT_THUMBSTICK_TOUCH_ID;\nextern uint64_t RIGHT_THUMBREST_TOUCH_ID;\nextern uint64_t RIGHT_TRIGGER_TOUCH_ID;\nextern uint64_t RIGHT_TRIGGER_VALUE_ID;\nextern uint64_t RIGHT_SQUEEZE_TOUCH_ID;\nextern uint64_t RIGHT_SQUEEZE_VALUE_ID;\n\nenum class ButtonType {\n    Binary,\n    ScalarOneSided,\n    ScalarTwoSided,\n};\n\nstruct ButtonInfo {\n    std::vector<const char*> steamvr_paths;\n    ButtonType type;\n};\n\nextern std::set<uint64_t> BODY_IDS;\nextern std::map<uint64_t, ButtonInfo> LEFT_CONTROLLER_BUTTON_MAPPING;\nextern std::map<uint64_t, ButtonInfo> RIGHT_CONTROLLER_BUTTON_MAPPING;\nextern std::map<uint64_t, std::vector<uint64_t>> ALVR_TO_STEAMVR_PATH_IDS;\n\nvoid init_paths();\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/PoseHistory.cpp",
    "content": "#include \"PoseHistory.h\"\n#include \"Logger.h\"\n#include \"Utils.h\"\n#include \"include/openvr_math.h\"\n#include <mutex>\n#include <optional>\n\nvoid PoseHistory::OnPoseUpdated(uint64_t targetTimestampNs, FfiDeviceMotion motion) {\n    // Put pose history buffer\n    TrackingHistoryFrame history;\n    history.targetTimestampNs = targetTimestampNs;\n    history.motion = motion;\n\n    HmdMatrix_QuatToMat(\n        motion.pose.orientation.w,\n        motion.pose.orientation.x,\n        motion.pose.orientation.y,\n        motion.pose.orientation.z,\n        &history.rotationMatrix\n    );\n\n    std::unique_lock<std::mutex> lock(m_mutex);\n    if (!m_transformIdentity) {\n        vr::HmdMatrix34_t rotation = vrmath::matMul33(m_transform, history.rotationMatrix);\n        history.rotationMatrix = rotation;\n    }\n\n    if (m_poseBuffer.size() == 0) {\n        m_poseBuffer.push_back(history);\n    } else {\n        if (m_poseBuffer.back().targetTimestampNs != targetTimestampNs) {\n            // New track info\n            m_poseBuffer.push_back(history);\n        }\n    }\n    // The value should match with the client's MAXIMUM_TRACKING_FRAMES in ovr_context.cpp\n    if (m_poseBuffer.size() > 120 * 3) {\n        m_poseBuffer.pop_front();\n    }\n}\n\nstd::optional<PoseHistory::TrackingHistoryFrame>\nPoseHistory::GetBestPoseMatch(const vr::HmdMatrix34_t& pose) const {\n    std::unique_lock<std::mutex> lock(m_mutex);\n    float minDiff = 100000;\n    auto minIt = m_poseBuffer.begin();\n    for (auto it = m_poseBuffer.begin(); it != m_poseBuffer.end(); ++it) {\n        float distance = 0;\n        // Rotation matrix composes a part of ViewMatrix of TrackingInfo.\n        // Be carefull of transpose.\n        // And bottom side and right side of matrix should not be compared, because pPose does not\n        // contain that part of matrix.\n        for (int i = 0; i < 3; i++) {\n            for (int j = 0; j < 3; j++) {\n                distance += pow(it->rotationMatrix.m[j][i] - pose.m[j][i], 2);\n            }\n        }\n        if (minDiff > distance) {\n            minIt = it;\n            minDiff = distance;\n        }\n    }\n    if (minIt != m_poseBuffer.end()) {\n        return *minIt;\n    }\n\n    Debug(\"PoseHistory::GetBestPoseMatch: No pose matched.\");\n    return {};\n}\n\nstd::optional<PoseHistory::TrackingHistoryFrame> PoseHistory::GetPoseAt(uint64_t timestampNs\n) const {\n    std::unique_lock<std::mutex> lock(m_mutex);\n    for (auto it = m_poseBuffer.rbegin(), end = m_poseBuffer.rend(); it != end; ++it) {\n        if (it->targetTimestampNs == timestampNs)\n            return *it;\n    }\n\n    Debug(\"PoseHistory::GetPoseAt: No pose matched.\");\n    return {};\n}\n\nvoid PoseHistory::SetTransform(const vr::HmdMatrix34_t& transform) {\n    std::unique_lock<std::mutex> lock(m_mutex);\n    m_transform = transform;\n\n    for (int i = 0; i < 3; ++i) {\n        for (int j = 0; j < 3; ++j) {\n            if (transform.m[i][j] != (i == j ? 1 : 0)) {\n                m_transformIdentity = false;\n                return;\n            }\n        }\n    }\n    m_transformIdentity = true;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/PoseHistory.h",
    "content": "#pragma once\n\n#include \"ALVR-common/packet_types.h\"\n#include \"openvr_driver_wrap.h\"\n\n#include <list>\n#include <mutex>\n#include <optional>\n\nclass PoseHistory {\npublic:\n    struct TrackingHistoryFrame {\n        uint64_t targetTimestampNs;\n        FfiDeviceMotion motion;\n        vr::HmdMatrix34_t rotationMatrix;\n    };\n\n    void OnPoseUpdated(uint64_t targetTimestampNs, FfiDeviceMotion motion);\n\n    std::optional<TrackingHistoryFrame> GetBestPoseMatch(const vr::HmdMatrix34_t& pose) const;\n    // Return the most recent pose known at the given timestamp\n    std::optional<TrackingHistoryFrame> GetPoseAt(uint64_t timestampNs) const;\n\n    void SetTransform(const vr::HmdMatrix34_t& transform);\n\nprivate:\n    mutable std::mutex m_mutex;\n    std::list<TrackingHistoryFrame> m_poseBuffer;\n    vr::HmdMatrix34_t m_transform\n        = { { { 1.0, 0.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 } } };\n    bool m_transformIdentity = true;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Settings.cpp",
    "content": "#include \"Settings.h\"\n#include \"Logger.h\"\n#include \"bindings.h\"\n#include <cstdlib>\n#include <filesystem>\n#include <fstream>\n#include <streambuf>\n#include <string>\n\n#define PICOJSON_USE_INT64\n#include \"include/picojson.h\"\n\nusing namespace std;\n\nextern uint64_t g_DriverTestMode;\n\nSettings Settings::m_Instance;\n\nSettings::Settings()\n    : m_loaded(false) { }\n\nSettings::~Settings() { }\n\nvoid Settings::Load() {\n    try {\n        auto sessionFile = std::ifstream(g_sessionPath);\n\n        auto json = std::string(\n            std::istreambuf_iterator<char>(sessionFile), std::istreambuf_iterator<char>()\n        );\n\n        picojson::value v;\n        std::string err = picojson::parse(v, json);\n        if (!err.empty()) {\n            Error(\"Error on parsing session config (%s): %hs\\n\", g_sessionPath, err.c_str());\n            return;\n        }\n\n        auto config = v.get(\"openvr_config\");\n\n        m_refreshRate = (int)config.get(\"refresh_rate\").get<int64_t>();\n        m_renderWidth = config.get(\"eye_resolution_width\").get<int64_t>() * 2;\n        m_renderHeight = config.get(\"eye_resolution_height\").get<int64_t>();\n        m_recommendedTargetWidth = config.get(\"target_eye_resolution_width\").get<int64_t>() * 2;\n        m_recommendedTargetHeight = config.get(\"target_eye_resolution_height\").get<int64_t>();\n        m_nAdapterIndex = (int32_t)config.get(\"adapter_index\").get<int64_t>();\n        m_captureFrameDir = config.get(\"capture_frame_dir\").get<std::string>();\n\n        m_enableFoveatedEncoding = config.get(\"enable_foveated_encoding\").get<bool>();\n        m_foveationCenterSizeX = (float)config.get(\"foveation_center_size_x\").get<double>();\n        m_foveationCenterSizeY = (float)config.get(\"foveation_center_size_y\").get<double>();\n        m_foveationCenterShiftX = (float)config.get(\"foveation_center_shift_x\").get<double>();\n        m_foveationCenterShiftY = (float)config.get(\"foveation_center_shift_y\").get<double>();\n        m_foveationEdgeRatioX = (float)config.get(\"foveation_edge_ratio_x\").get<double>();\n        m_foveationEdgeRatioY = (float)config.get(\"foveation_edge_ratio_y\").get<double>();\n\n        m_enableColorCorrection = config.get(\"enable_color_correction\").get<bool>();\n        m_brightness = (float)config.get(\"brightness\").get<double>();\n        m_contrast = (float)config.get(\"contrast\").get<double>();\n        m_saturation = (float)config.get(\"saturation\").get<double>();\n        m_gamma = (float)config.get(\"gamma\").get<double>();\n        m_sharpening = (float)config.get(\"sharpening\").get<double>();\n\n        m_codec = (int32_t)config.get(\"codec\").get<int64_t>();\n        m_h264Profile = (int32_t)config.get(\"h264_profile\").get<int64_t>();\n        m_rateControlMode = (uint32_t)config.get(\"rate_control_mode\").get<int64_t>();\n        m_fillerData = config.get(\"filler_data\").get<bool>();\n        m_entropyCoding = (uint32_t)config.get(\"entropy_coding\").get<int64_t>();\n        m_use10bitEncoder = config.get(\"use_10bit_encoder\").get<bool>();\n        m_encodingGamma = config.get(\"encoding_gamma\").get<double>();\n        m_enableHdr = config.get(\"enable_hdr\").get<bool>();\n        m_forceHdrSrgbCorrection = config.get(\"force_hdr_srgb_correction\").get<bool>();\n        m_clampHdrExtendedRange = config.get(\"clamp_hdr_extended_range\").get<bool>();\n        m_enableAmfPreAnalysis = config.get(\"enable_amf_pre_analysis\").get<bool>();\n        m_enableVbaq = config.get(\"enable_vbaq\").get<bool>();\n        m_enableAmfHmqb = config.get(\"enable_amf_hmqb\").get<bool>();\n        m_useAmfPreproc = config.get(\"use_amf_preproc\").get<bool>();\n        m_amfPreProcSigma = (uint32_t)config.get(\"amf_preproc_sigma\").get<int64_t>();\n        m_amfPreProcTor = (uint32_t)config.get(\"amf_preproc_tor\").get<int64_t>();\n        m_encoderQualityPreset = (uint32_t)config.get(\"encoder_quality_preset\").get<int64_t>();\n        m_amdBitrateCorruptionFix = (bool)config.get(\"amd_bitrate_corruption_fix\").get<bool>();\n        m_nvencQualityPreset = (uint32_t)config.get(\"nvenc_quality_preset\").get<int64_t>();\n        m_force_sw_encoding = config.get(\"force_sw_encoding\").get<bool>();\n        m_swThreadCount = (int32_t)config.get(\"sw_thread_count\").get<int64_t>();\n\n        m_nvencTuningPreset = (uint32_t)config.get(\"nvenc_tuning_preset\").get<int64_t>();\n        m_nvencMultiPass = (uint32_t)config.get(\"nvenc_multi_pass\").get<int64_t>();\n        m_nvencAdaptiveQuantizationMode\n            = (uint32_t)config.get(\"nvenc_adaptive_quantization_mode\").get<int64_t>();\n        m_nvencLowDelayKeyFrameScale = config.get(\"nvenc_low_delay_key_frame_scale\").get<int64_t>();\n        m_nvencRefreshRate = config.get(\"nvenc_refresh_rate\").get<int64_t>();\n        m_nvencEnableIntraRefresh = config.get(\"enable_intra_refresh\").get<bool>();\n        m_nvencIntraRefreshPeriod = config.get(\"intra_refresh_period\").get<int64_t>();\n        m_nvencIntraRefreshCount = config.get(\"intra_refresh_count\").get<int64_t>();\n        m_nvencMaxNumRefFrames = config.get(\"max_num_ref_frames\").get<int64_t>();\n        m_nvencGopLength = config.get(\"gop_length\").get<int64_t>();\n        m_nvencPFrameStrategy = config.get(\"p_frame_strategy\").get<int64_t>();\n        m_nvencRateControlMode = config.get(\"nvenc_rate_control_mode\").get<int64_t>();\n        m_nvencRcBufferSize = config.get(\"rc_buffer_size\").get<int64_t>();\n        m_nvencRcInitialDelay = config.get(\"rc_initial_delay\").get<int64_t>();\n        m_nvencRcMaxBitrate = config.get(\"rc_max_bitrate\").get<int64_t>();\n        m_nvencRcAverageBitrate = config.get(\"rc_average_bitrate\").get<int64_t>();\n        m_nvencEnableWeightedPrediction\n            = config.get(\"nvenc_enable_weighted_prediction\").get<bool>();\n\n        m_minimumIdrIntervalMs = config.get(\"minimum_idr_interval_ms\").get<int64_t>();\n\n        m_enableViveTrackerProxy = config.get(\"enable_vive_tracker_proxy\").get<bool>();\n        m_TrackingRefOnly = config.get(\"tracking_ref_only\").get<bool>();\n        m_enableLinuxVulkanAsyncCompute = config.get(\"linux_async_compute\").get<bool>();\n        m_enableLinuxAsyncReprojection = config.get(\"linux_async_reprojection\").get<bool>();\n\n        m_enableControllers = config.get(\"controllers_enabled\").get<bool>();\n        m_controllerIsTracker = config.get(\"controller_is_tracker\").get<bool>();\n\n        m_enableBodyTrackingFakeVive = config.get(\"body_tracking_vive_enabled\").get<bool>();\n        m_bodyTrackingHasLegs = config.get(\"body_tracking_has_legs\").get<bool>();\n\n        m_useSeparateHandTrackers = config.get(\"use_separate_hand_trackers\").get<bool>();\n\n        Info(\"Render Target: %d %d\\n\", m_renderWidth, m_renderHeight);\n        Info(\"Refresh Rate: %d\\n\", m_refreshRate);\n        m_loaded = true;\n    } catch (std::exception& e) {\n        Error(\"Exception on parsing session config (%s): %hs\\n\", g_sessionPath, e.what());\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Settings.h",
    "content": "#pragma once\n\n#include \"ALVR-common/packet_types.h\"\n#include <string>\n\nclass Settings {\n    static Settings m_Instance;\n    bool m_loaded;\n\n    Settings();\n    virtual ~Settings();\n\npublic:\n    void Load();\n    static Settings& Instance() { return m_Instance; }\n\n    bool IsLoaded() { return m_loaded; }\n\n    int m_refreshRate;\n    uint32_t m_renderWidth;\n    uint32_t m_renderHeight;\n    int32_t m_recommendedTargetWidth;\n    int32_t m_recommendedTargetHeight;\n    int32_t m_nAdapterIndex;\n    std::string m_captureFrameDir;\n\n    bool m_enableFoveatedEncoding;\n    float m_foveationCenterSizeX;\n    float m_foveationCenterSizeY;\n    float m_foveationCenterShiftX;\n    float m_foveationCenterShiftY;\n    float m_foveationEdgeRatioX;\n    float m_foveationEdgeRatioY;\n\n    bool m_enableColorCorrection;\n    float m_brightness;\n    float m_contrast;\n    float m_saturation;\n    float m_gamma;\n    float m_sharpening;\n\n    int m_codec;\n    int m_h264Profile;\n    bool m_use10bitEncoder;\n    double m_encodingGamma;\n    bool m_enableHdr;\n    bool m_forceHdrSrgbCorrection;\n    bool m_clampHdrExtendedRange;\n    bool m_enableAmfPreAnalysis;\n    bool m_enableVbaq;\n    bool m_enableAmfHmqb;\n    bool m_useAmfPreproc;\n    uint32_t m_amfPreProcSigma;\n    uint32_t m_amfPreProcTor;\n    uint32_t m_encoderQualityPreset;\n    bool m_amdBitrateCorruptionFix;\n    uint32_t m_nvencQualityPreset;\n    uint32_t m_rateControlMode;\n    bool m_fillerData;\n    uint32_t m_entropyCoding;\n    bool m_force_sw_encoding;\n    uint32_t m_swThreadCount;\n\n    uint32_t m_nvencTuningPreset;\n    uint32_t m_nvencMultiPass;\n    uint32_t m_nvencAdaptiveQuantizationMode;\n    int64_t m_nvencLowDelayKeyFrameScale;\n    int64_t m_nvencRefreshRate;\n    bool m_nvencEnableIntraRefresh;\n    int64_t m_nvencIntraRefreshPeriod;\n    int64_t m_nvencIntraRefreshCount;\n    int64_t m_nvencMaxNumRefFrames;\n    int64_t m_nvencGopLength;\n    int64_t m_nvencPFrameStrategy;\n    int64_t m_nvencRateControlMode;\n    int64_t m_nvencRcBufferSize;\n    int64_t m_nvencRcInitialDelay;\n    int64_t m_nvencRcMaxBitrate;\n    int64_t m_nvencRcAverageBitrate;\n    bool m_nvencEnableWeightedPrediction;\n\n    uint64_t m_minimumIdrIntervalMs;\n\n    bool m_enableViveTrackerProxy = false;\n    bool m_TrackingRefOnly = false;\n    bool m_enableLinuxVulkanAsyncCompute;\n    bool m_enableLinuxAsyncReprojection;\n\n    bool m_enableControllers;\n    int m_controllerIsTracker = false;\n    int m_enableBodyTrackingFakeVive = false;\n    int m_bodyTrackingHasLegs = false;\n    bool m_useSeparateHandTrackers = false;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/TrackedDevice.cpp",
    "content": "#include \"TrackedDevice.h\"\n#include \"Logger.h\"\n#include \"Utils.h\"\n#include <chrono>\n#include <thread>\n\nTrackedDevice::TrackedDevice(uint64_t device_id, vr::ETrackedDeviceClass device_class)\n    : device_id(device_id)\n    , device_class(device_class) {\n    this->last_pose = vr::DriverPose_t {};\n    this->last_pose.poseIsValid = false;\n    this->last_pose.deviceIsConnected = false;\n    this->last_pose.result = vr::TrackingResult_Uninitialized;\n\n    this->last_pose.qDriverFromHeadRotation = HmdQuaternion_Init(1, 0, 0, 0);\n    this->last_pose.qWorldFromDriverRotation = HmdQuaternion_Init(1, 0, 0, 0);\n    this->last_pose.qRotation = HmdQuaternion_Init(1, 0, 0, 0);\n}\n\nstd::string TrackedDevice::get_serial_number() {\n    auto size = GetSerialNumber(this->device_id, nullptr);\n\n    auto buffer = std::vector<char>(size);\n    GetSerialNumber(this->device_id, &buffer[0]);\n\n    return std::string(&buffer[0]);\n}\n\nvoid TrackedDevice::set_prop(FfiOpenvrProperty prop) {\n    if (this->object_id == vr::k_unTrackedDeviceIndexInvalid) {\n        return;\n    }\n\n    auto key = (vr::ETrackedDeviceProperty)prop.key;\n\n    auto props = vr::VRProperties();\n\n    vr::ETrackedPropertyError result;\n\n    if (prop.type == FfiOpenvrPropertyType::Bool) {\n        result = props->SetBoolProperty(this->prop_container, key, prop.value.bool_);\n    } else if (prop.type == FfiOpenvrPropertyType::Float) {\n        result = props->SetFloatProperty(this->prop_container, key, prop.value.float_);\n    } else if (prop.type == FfiOpenvrPropertyType::Int32) {\n        result = props->SetInt32Property(this->prop_container, key, prop.value.int32);\n    } else if (prop.type == FfiOpenvrPropertyType::Uint64) {\n        result = props->SetUint64Property(this->prop_container, key, prop.value.uint64);\n    } else if (prop.type == FfiOpenvrPropertyType::Vector3) {\n        auto vec3 = vr::HmdVector3_t {};\n        vec3.v[0] = prop.value.vector3[0];\n        vec3.v[1] = prop.value.vector3[1];\n        vec3.v[2] = prop.value.vector3[2];\n        result = props->SetVec3Property(this->prop_container, key, vec3);\n    } else if (prop.type == FfiOpenvrPropertyType::Double) {\n        result = props->SetDoubleProperty(this->prop_container, key, prop.value.double_);\n    } else if (prop.type == FfiOpenvrPropertyType::String) {\n        result = props->SetStringProperty(this->prop_container, key, prop.value.string);\n    } else {\n        Error(\"Unreachable\");\n        result = vr::TrackedProp_Success;\n    }\n\n    if (result != vr::TrackedProp_Success) {\n        Error(\n            \"Error setting property %d: %s\",\n            key,\n            vr::VRPropertiesRaw()->GetPropErrorNameFromEnum(result)\n        );\n    }\n\n    auto event_data = vr::VREvent_Data_t {};\n    event_data.property.container = this->prop_container;\n    event_data.property.prop = key;\n    vr::VRServerDriverHost()->VendorSpecificEvent(\n        this->object_id, vr::VREvent_PropertyChanged, event_data, 0.\n    );\n}\n\nvoid TrackedDevice::submit_pose(vr::DriverPose_t pose) {\n    this->last_pose = pose;\n    vr::VRServerDriverHost()->TrackedDevicePoseUpdated(\n        this->object_id, pose, sizeof(vr::DriverPose_t)\n    );\n}\n\nbool TrackedDevice::register_device(bool await_activation) {\n    if (!vr::VRServerDriverHost()->TrackedDeviceAdded(\n            this->get_serial_number().c_str(),\n            this->device_class,\n            (vr::ITrackedDeviceServerDriver*)this\n        )) {\n        Error(\"Failed to register device (%s)\", this->get_serial_number().c_str());\n\n        return false;\n    }\n\n    if (await_activation) {\n        auto lock = std::unique_lock<std::mutex>(this->activation_mutex);\n        this->activation_condvar.wait_for(lock, std::chrono::seconds(1), [this] {\n            return this->activation_state != ActivationState::Pending;\n        });\n\n        return this->activation_state == ActivationState::Success;\n    } else {\n        return true;\n    }\n}\n\nvr::EVRInitError TrackedDevice::Activate(vr::TrackedDeviceIndex_t object_id) {\n    this->object_id = object_id;\n    this->prop_container = vr::VRProperties()->TrackedDeviceToPropertyContainer(this->object_id);\n\n    {\n        auto guard = std::lock_guard<std::mutex>(this->activation_mutex);\n\n        if (this->activate()) {\n            this->activation_state = ActivationState::Success;\n        } else {\n            this->activation_state = ActivationState::Failure;\n        }\n    }\n    this->activation_condvar.notify_one();\n\n    return this->activation_state == ActivationState::Success ? vr::VRInitError_None\n                                                              : vr::VRInitError_Driver_Failed;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/TrackedDevice.h",
    "content": "#pragma once\n\n#include \"bindings.h\"\n#include \"openvr_driver_wrap.h\"\n#include <condition_variable>\n#include <map>\n#include <mutex>\n#include <optional>\n\nenum class ActivationState {\n    Pending,\n    Success,\n    Failure,\n};\n\nclass TrackedDevice : vr::ITrackedDeviceServerDriver {\npublic:\n    vr::TrackedDeviceIndex_t object_id = vr::k_unTrackedDeviceIndexInvalid;\n    vr::PropertyContainerHandle_t prop_container = vr::k_ulInvalidPropertyContainer;\n    vr::DriverPose_t last_pose;\n\n    bool register_device(bool await_activation);\n    void set_prop(FfiOpenvrProperty prop);\n\nprotected:\n    uint64_t device_id;\n    vr::ETrackedDeviceClass device_class;\n\n    TrackedDevice(uint64_t device_id, vr::ETrackedDeviceClass device_class);\n    std::string get_serial_number();\n    void submit_pose(vr::DriverPose_t pose);\n    virtual bool activate() = 0;\n    virtual void* get_component(const char*) = 0;\n\nprivate:\n    ActivationState activation_state = ActivationState::Pending;\n    std::mutex activation_mutex = {};\n    std::condition_variable activation_condvar = {};\n\n    // ITrackedDeviceServerDriver\n    vr::EVRInitError Activate(vr::TrackedDeviceIndex_t object_id) final;\n    void Deactivate() final {\n        this->device_id = vr::k_unTrackedDeviceIndexInvalid;\n        this->prop_container = vr::k_ulInvalidPropertyContainer;\n    }\n    void EnterStandby() final { }\n    void* GetComponent(const char* component_name_and_version) final {\n        return get_component(component_name_and_version);\n    }\n    void DebugRequest(const char*, char* buffer, uint32_t buffer_size) final {\n        if (buffer_size >= 1)\n            buffer[0] = 0;\n    }\n    vr::DriverPose_t GetPose() final { return this->last_pose; }\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/Utils.h",
    "content": "#pragma once\n\n#include <chrono>\n#ifdef _WIN32\n#pragma warning(disable : 4005)\n#include <winsock2.h>\n#pragma warning(default : 4005)\n#include <d3d11.h>\n#include <delayimp.h>\n#include <stdint.h>\n#include <string>\n#include <vector>\n#include <windows.h>\n#include <wininet.h>\n#include <ws2tcpip.h>\n#define _USE_MATH_DEFINES\n#include <versionhelpers.h>\n#else\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <string.h>\n#endif\n\n#include <math.h>\n\n#include \"ALVR-common/packet_types.h\"\n#include \"openvr_driver_wrap.h\"\n\nconst float DEG_TO_RAD = (float)(M_PI / 180.);\nconst double NS_PER_S = 1000000000.0;\n\n// Get elapsed time in us from Unix Epoch\ninline uint64_t GetTimestampUs() {\n    auto duration = std::chrono::system_clock::now().time_since_epoch();\n    return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();\n}\n\n#ifdef _WIN32\ninline std::wstring GetErrorStr(HRESULT hr) {\n    wchar_t* s = NULL;\n    std::wstring ret;\n    FormatMessageW(\n        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,\n        NULL,\n        hr,\n        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\n        (LPWSTR)&s,\n        0,\n        NULL\n    );\n    ret = s;\n    LocalFree(s);\n\n    if (ret.size() >= 1 && ret[ret.size() - 1] == L'\\n') {\n        ret.erase(ret.size() - 1, 1);\n    }\n    if (ret.size() >= 1 && ret[ret.size() - 1] == L'\\r') {\n        ret.erase(ret.size() - 1, 1);\n    }\n    return ret;\n}\n#endif\n\ninline vr::HmdQuaternion_t HmdQuaternion_Init(double w, double x, double y, double z) {\n    vr::HmdQuaternion_t quat;\n    quat.w = w;\n    quat.x = x;\n    quat.y = y;\n    quat.z = z;\n    return quat;\n}\n\ninline vr::HmdRect2_t fov_to_tangents(FfiFov fov) {\n    auto proj_bounds = vr::HmdRect2_t {};\n    proj_bounds.vTopLeft.v[0] = tanf(fov.left);\n    proj_bounds.vBottomRight.v[0] = tanf(fov.right);\n    proj_bounds.vTopLeft.v[1] = tanf(fov.down);\n    proj_bounds.vBottomRight.v[1] = tanf(fov.up);\n\n    return proj_bounds;\n}\n\ninline vr::HmdMatrix34_t pose_to_mat(FfiPose pose) {\n    FfiQuat o = pose.orientation;\n\n    vr::HmdMatrix34_t mat = {};\n\n    mat.m[0][0] = 1.0f - 2.0f * (o.y * o.y + o.z * o.z);\n    mat.m[0][1] = 2.0f * (o.x * o.y - o.w * o.z);\n    mat.m[0][2] = 2.0f * (o.x * o.z + o.w * o.y);\n    mat.m[1][0] = 2.0f * (o.x * o.y + o.w * o.z);\n    mat.m[1][1] = 1.0f - 2.0f * (o.x * o.x + o.z * o.z);\n    mat.m[1][2] = 2.0f * (o.y * o.z - o.w * o.x);\n    mat.m[2][0] = 2.0f * (o.x * o.z - o.w * o.y);\n    mat.m[2][1] = 2.0f * (o.y * o.z + o.w * o.x);\n    mat.m[2][2] = 1.0f - 2.0f * (o.x * o.x + o.y * o.y);\n\n    mat.m[0][3] = pose.position[0];\n    mat.m[1][3] = pose.position[1];\n    mat.m[2][3] = pose.position[2];\n\n    return mat;\n}\n\ninline void HmdMatrix_SetIdentity(vr::HmdMatrix34_t* pMatrix) {\n    pMatrix->m[0][0] = 1.f;\n    pMatrix->m[0][1] = 0.f;\n    pMatrix->m[0][2] = 0.f;\n    pMatrix->m[0][3] = 0.f;\n    pMatrix->m[1][0] = 0.f;\n    pMatrix->m[1][1] = 1.f;\n    pMatrix->m[1][2] = 0.f;\n    pMatrix->m[1][3] = 0.f;\n    pMatrix->m[2][0] = 0.f;\n    pMatrix->m[2][1] = 0.f;\n    pMatrix->m[2][2] = 1.f;\n    pMatrix->m[2][3] = 0.f;\n}\n\ninline void\nHmdMatrix_QuatToMat(double w, double x, double y, double z, vr::HmdMatrix34_t* pMatrix) {\n    pMatrix->m[0][0] = (float)(1.0f - 2.0f * y * y - 2.0f * z * z);\n    pMatrix->m[0][1] = (float)(2.0f * x * y - 2.0f * z * w);\n    pMatrix->m[0][2] = (float)(2.0f * x * z + 2.0f * y * w);\n    pMatrix->m[0][3] = (float)(0.0f);\n    pMatrix->m[1][0] = (float)(2.0f * x * y + 2.0f * z * w);\n    pMatrix->m[1][1] = (float)(1.0f - 2.0f * x * x - 2.0f * z * z);\n    pMatrix->m[1][2] = (float)(2.0f * y * z - 2.0f * x * w);\n    pMatrix->m[1][3] = (float)(0.0f);\n    pMatrix->m[2][0] = (float)(2.0f * x * z - 2.0f * y * w);\n    pMatrix->m[2][1] = (float)(2.0f * y * z + 2.0f * x * w);\n    pMatrix->m[2][2] = (float)(1.0f - 2.0f * x * x - 2.0f * y * y);\n    pMatrix->m[2][3] = (float)(0.0f);\n}\n\ninline vr::HmdQuaternion_t EulerAngleToQuaternion(const double* yaw_pitch_roll) {\n    vr::HmdQuaternion_t q;\n    // Abbreviations for the various angular functions\n    double cy = cos(yaw_pitch_roll[0] * 0.5);\n    double sy = sin(yaw_pitch_roll[0] * 0.5);\n    double cr = cos(yaw_pitch_roll[2] * 0.5);\n    double sr = sin(yaw_pitch_roll[2] * 0.5);\n    double cp = cos(yaw_pitch_roll[1] * 0.5);\n    double sp = sin(yaw_pitch_roll[1] * 0.5);\n\n    q.w = cy * cr * cp + sy * sr * sp;\n    q.x = cy * sr * cp - sy * cr * sp;\n    q.y = cy * cr * sp + sy * sr * cp;\n    q.z = sy * cr * cp - cy * sr * sp;\n    return q;\n}\n\ninline vr::HmdVector4_t Lerp(vr::HmdVector4_t& v1, vr::HmdVector4_t& v2, double lambda) {\n    vr::HmdVector4_t res;\n    res.v[0] = (float)((1 - lambda) * v1.v[0] + lambda * v2.v[0]);\n    res.v[1] = (float)((1 - lambda) * v1.v[1] + lambda * v2.v[1]);\n    res.v[2] = (float)((1 - lambda) * v1.v[2] + lambda * v2.v[2]);\n    res.v[3] = 1;\n\n    return res;\n}\n\ninline vr::HmdQuaternionf_t\nSlerp(vr::HmdQuaternionf_t& q1, vr::HmdQuaternionf_t& q2, double lambda) {\n    if (q1.w != q2.w || q1.x != q2.x || q1.y != q2.y || q1.z != q2.z) {\n        float dotproduct = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;\n        float theta, st, sut, sout, coeff1, coeff2;\n\n        // algorithm adapted from Shoemake's paper\n\n        theta = (float)acos(dotproduct);\n        if (theta < 0.0)\n            theta = -theta;\n\n        st = (float)sin(theta);\n        sut = (float)sin(lambda * theta);\n        sout = (float)sin((1 - lambda) * theta);\n        coeff1 = sout / st;\n        coeff2 = sut / st;\n\n        vr::HmdQuaternionf_t res;\n        res.w = coeff1 * q1.w + coeff2 * q2.w;\n        res.x = coeff1 * q1.x + coeff2 * q2.x;\n        res.y = coeff1 * q1.y + coeff2 * q2.y;\n        res.z = coeff1 * q1.z + coeff2 * q2.z;\n\n        float norm = res.w * res.w + res.x * res.x + res.y * res.y + res.z * res.z;\n        res.w /= norm;\n        res.x /= norm;\n        res.y /= norm;\n        res.z /= norm;\n\n        return res;\n    } else {\n        return q1;\n    }\n}\n\n// Sourced from https://mariogc.com/post/angular-velocity-quaternions/\ninline vr::HmdVector3d_t AngularVelocityBetweenQuats(\n    const vr::HmdQuaternion_t& q1, const vr::HmdQuaternion_t& q2, double dt\n) {\n    double r = (2.0f / dt);\n    return { (q1.w * q2.x - q1.x * q2.w - q1.y * q2.z + q1.z * q2.y) * r,\n             (q1.w * q2.y + q1.x * q2.z - q1.y * q2.w - q1.z * q2.x) * r,\n             (q1.w * q2.z - q1.x * q2.y + q1.y * q2.x - q1.z * q2.w) * r };\n}\n\n#ifdef _WIN32\ntypedef void(WINAPI* RtlGetVersion_FUNC)(OSVERSIONINFOEXW*);\n\ninline std::wstring GetWindowsOSVersion() {\n    HMODULE hModule;\n    OSVERSIONINFOEXW ver;\n\n    hModule = LoadLibraryW(L\"ntdll.dll\");\n    if (hModule == NULL) {\n        return L\"Unknown\";\n    }\n    RtlGetVersion_FUNC RtlGetVersion = (RtlGetVersion_FUNC)GetProcAddress(hModule, \"RtlGetVersion\");\n    if (RtlGetVersion == NULL) {\n        FreeLibrary(hModule);\n        return L\"Unknown\";\n    }\n    memset(&ver, 0, sizeof(ver));\n    ver.dwOSVersionInfoSize = sizeof(ver);\n    RtlGetVersion(&ver);\n\n    FreeLibrary(hModule);\n\n    wchar_t buf[1000];\n    _snwprintf_s(\n        buf,\n        sizeof(buf) / sizeof(buf[0]),\n        L\"MajorVersion=%d MinorVersion=%d Build=%d\",\n        ver.dwMajorVersion,\n        ver.dwMinorVersion,\n        ver.dwBuildNumber\n    );\n    return buf;\n}\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/ViveTrackerProxy.cpp",
    "content": "#include \"ViveTrackerProxy.h\"\n#include \"HMD.h\"\n#include \"Settings.h\"\n\n#include <cassert>\n\nViveTrackerProxy::ViveTrackerProxy(Hmd& owner)\n    : m_unObjectId(vr::k_unTrackedDeviceIndexInvalid)\n    , m_HMDOwner(&owner) { }\n\nvr::DriverPose_t ViveTrackerProxy::GetPose() {\n    assert(m_HMDOwner != nullptr);\n    return m_HMDOwner->last_pose;\n}\n\nvr::EVRInitError ViveTrackerProxy::Activate(vr::TrackedDeviceIndex_t unObjectId) {\n    auto vr_properties = vr::VRProperties();\n\n    m_unObjectId = unObjectId;\n    assert(m_unObjectId != vr::k_unTrackedDeviceIndexInvalid);\n    const auto propertyContainer = vr_properties->TrackedDeviceToPropertyContainer(m_unObjectId);\n\n    // Normally a vive tracker emulator would (logically) always set the tracking system to\n    // \"lighthouse\" but in order to do space calibration with existing tools such as OpenVR Space\n    // calibrator and be able to calibrate to/from ALVR HMD (and the proxy tracker) space to/from a\n    // native HMD/tracked device which is already using \"lighthouse\" as the tracking system the\n    // proxy tracker needs to be in a different tracking system to treat them differently and\n    // prevent those tools doing the same space transform to the proxy tracker.\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_TrackingSystemName_String, \"HeadTrackerCustom\"\n    ); //\"lighthouse\");\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_ModelNumber_String, \"Vive Tracker Pro MV\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_SerialNumber_String, GetSerialNumber()\n    ); // Changed\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_RenderModelName_String, \"{htc}vr_tracker_vive_1_0\"\n    );\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_WillDriftInYaw_Bool, false);\n    vr_properties->SetStringProperty(propertyContainer, vr::Prop_ManufacturerName_String, \"HTC\");\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_TrackingFirmwareVersion_String,\n        \"1541800000 RUNNER-WATCHMAN$runner-watchman@runner-watchman 2018-01-01 FPGA 512(2.56/0/0) \"\n        \"BL 0 VRC 1541800000 Radio 1518800000\"\n    ); // Changed\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_HardwareRevision_String, \"product 128 rev 2.5.6 lot 2000/0/0 0\"\n    ); // Changed\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_ConnectedWirelessDongle_String, \"D0000BE000\"\n    ); // Changed\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_DeviceIsWireless_Bool, true);\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_DeviceIsCharging_Bool, false);\n    vr_properties->SetFloatProperty(\n        propertyContainer, vr::Prop_DeviceBatteryPercentage_Float, 1.f\n    ); // Always charged\n\n    vr::HmdMatrix34_t l_transform\n        = { { { -1.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, -1.f, 0.f }, { 0.f, -1.f, 0.f, 0.f } } };\n    vr_properties->SetProperty(\n        propertyContainer,\n        vr::Prop_StatusDisplayTransform_Matrix34,\n        &l_transform,\n        sizeof(vr::HmdMatrix34_t),\n        vr::k_unHmdMatrix34PropertyTag\n    );\n\n    vr_properties->SetBoolProperty(\n        propertyContainer, vr::Prop_Firmware_UpdateAvailable_Bool, false\n    );\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_Firmware_ManualUpdate_Bool, false);\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_Firmware_ManualUpdateURL_String,\n        \"https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware\"\n    );\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_HardwareRevision_Uint64, 2214720000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_FirmwareVersion_Uint64, 1541800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_FPGAVersion_Uint64, 512\n    ); // Changed\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_VRCVersion_Uint64, 1514800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_RadioVersion_Uint64, 1518800000\n    ); // Changed\n    vr_properties->SetUint64Property(\n        propertyContainer, vr::Prop_DongleVersion_Uint64, 8933539758\n    ); // Changed, based on vr::Prop_ConnectedWirelessDongle_String above\n    vr_properties->SetBoolProperty(\n        propertyContainer, vr::Prop_DeviceProvidesBatteryStatus_Bool, true\n    );\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_DeviceCanPowerOff_Bool, true);\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_Firmware_ProgrammingTarget_String, GetSerialNumber()\n    );\n    vr_properties->SetInt32Property(\n        propertyContainer, vr::Prop_DeviceClass_Int32, vr::TrackedDeviceClass_GenericTracker\n    );\n    vr_properties->SetBoolProperty(\n        propertyContainer, vr::Prop_Firmware_ForceUpdateRequired_Bool, false\n    );\n    vr_properties->SetStringProperty(propertyContainer, vr::Prop_ResourceRoot_String, \"htc\");\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_RegisteredDeviceType_String, \"ALVR/tracker/hmd_proxy\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_InputProfilePath_String, \"{htc}/input/vive_tracker_profile.json\"\n    );\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_Identifiable_Bool, false);\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_Firmware_RemindUpdate_Bool, false);\n    vr_properties->SetInt32Property(\n        propertyContainer, vr::Prop_ControllerRoleHint_Int32, vr::TrackedControllerRole_Invalid\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer, vr::Prop_ControllerType_String, \"vive_tracker_waist\"\n    );\n    vr_properties->SetInt32Property(\n        propertyContainer, vr::Prop_ControllerHandSelectionPriority_Int32, -1\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceOff_String,\n        \"{htc}/icons/tracker_status_off.png\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceSearching_String,\n        \"{htc}/icons/tracker_status_searching.gif\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceSearchingAlert_String,\n        \"{htc}/icons/tracker_status_searching_alert.gif\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceReady_String,\n        \"{htc}/icons/tracker_status_ready.png\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceReadyAlert_String,\n        \"{htc}/icons/tracker_status_ready_alert.png\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceNotReady_String,\n        \"{htc}/icons/tracker_status_error.png\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceStandby_String,\n        \"{htc}/icons/tracker_status_standby.png\"\n    );\n    vr_properties->SetStringProperty(\n        propertyContainer,\n        vr::Prop_NamedIconPathDeviceAlertLow_String,\n        \"{htc}/icons/tracker_status_ready_low.png\"\n    );\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_HasDisplayComponent_Bool, false);\n    vr_properties->SetBoolProperty(propertyContainer, vr::Prop_HasCameraComponent_Bool, false);\n    vr_properties->SetBoolProperty(\n        propertyContainer, vr::Prop_HasDriverDirectModeComponent_Bool, false\n    );\n    vr_properties->SetBoolProperty(\n        propertyContainer, vr::Prop_HasVirtualDisplayComponent_Bool, false\n    );\n    return vr::VRInitError_None;\n}\n\nvoid ViveTrackerProxy::update() {\n    auto newPose = GetPose();\n    vr::VRServerDriverHost()->TrackedDevicePoseUpdated(\n        m_unObjectId, newPose, sizeof(vr::DriverPose_t)\n    );\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/ViveTrackerProxy.h",
    "content": "#pragma once\n\n#include \"openvr_driver_wrap.h\"\n\nclass Hmd;\n\nclass ViveTrackerProxy final : public vr::ITrackedDeviceServerDriver {\n    vr::TrackedDeviceIndex_t m_unObjectId;\n    Hmd* m_HMDOwner;\n\npublic:\n    ViveTrackerProxy(Hmd& owner);\n\n    ViveTrackerProxy(const ViveTrackerProxy&) = delete;\n    ViveTrackerProxy& operator=(const ViveTrackerProxy&) = delete;\n\n    constexpr inline const char* GetSerialNumber() const { return \"ALVR HMD Tracker Proxy\"; }\n\n    virtual vr::EVRInitError Activate(vr::TrackedDeviceIndex_t unObjectId) override;\n\n    virtual inline void Deactivate() override { m_unObjectId = vr::k_unTrackedDeviceIndexInvalid; }\n\n    virtual inline void EnterStandby() override { }\n    virtual inline void* GetComponent(const char* /*pchComponentNameAndVersion*/) override {\n        // override this to add a component to a driver\n        return nullptr;\n    }\n\n    virtual inline void DebugRequest(\n        const char* /*pchRequest*/, char* pchResponseBuffer, uint32_t unResponseBufferSize\n    ) override {\n        if (unResponseBufferSize >= 1)\n            pchResponseBuffer[0] = 0;\n    }\n\n    virtual vr::DriverPose_t GetPose() override;\n\n    void update();\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/alvr_server.cpp",
    "content": "#ifdef _WIN32\n#include \"platform/win32/CEncoder.h\"\n#include <windows.h>\n#elif __APPLE__\n#include \"platform/macos/CEncoder.h\"\n#else\n#include \"platform/linux/CEncoder.h\"\n#endif\n#include \"Controller.h\"\n#include \"FakeViveTracker.h\"\n#include \"HMD.h\"\n#include \"Logger.h\"\n#include \"Paths.h\"\n#include \"PoseHistory.h\"\n#include \"Settings.h\"\n#include \"TrackedDevice.h\"\n#include \"bindings.h\"\n#include \"driverlog.h\"\n#include \"openvr_driver_wrap.h\"\n#include <algorithm>\n#include <cmath>\n#include <cstring>\n#include <map>\n#include <optional>\n\n#ifdef __linux__\n#include \"include/openvr_math.h\"\nstd::unique_ptr<vr::HmdMatrix34_t> GetInvZeroPose();\n\nstd::unique_ptr<vr::HmdMatrix34_t> GetRawZeroPose() {\n    auto invZeroPose = GetInvZeroPose();\n    if (invZeroPose == nullptr) {\n        return nullptr;\n    }\n    return std::make_unique<vr::HmdMatrix34_t>(vrmath::matInv33(*invZeroPose));\n}\n\nbool IsOpenvrClientReady();\n#endif\nvoid _SetChaperoneArea(float areaWidth, float areaHeight);\n\nvr::EVREventType VendorEvent_ALVRDriverResync\n    = (vr::EVREventType)(vr::VREvent_VendorSpecific_Reserved_Start + ((vr::EVREventType)0xC0));\n\nstatic void load_debug_privilege(void) {\n#ifdef _WIN32\n    const DWORD flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY;\n    TOKEN_PRIVILEGES tp;\n    HANDLE token;\n    LUID val;\n\n    if (!OpenProcessToken(GetCurrentProcess(), flags, &token)) {\n        return;\n    }\n\n    if (!!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &val)) {\n        tp.PrivilegeCount = 1;\n        tp.Privileges[0].Luid = val;\n        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n        AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL);\n    }\n\n    if (!!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) {\n        tp.PrivilegeCount = 1;\n        tp.Privileges[0].Luid = val;\n        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n        if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {\n            Warn(\"[GPU PRIO FIX] Could not set privilege to increase GPU priority\\n\");\n        }\n    }\n\n    Debug(\"[GPU PRIO FIX] Succeeded to set some sort of priority.\\n\");\n\n    CloseHandle(token);\n#endif\n}\n\nclass DriverProvider : public vr::IServerTrackedDeviceProvider {\npublic:\n    bool early_hmd_initialization = false;\n\n    std::unique_ptr<Hmd> hmd;\n    std::unique_ptr<Controller> left_controller, right_controller;\n    std::unique_ptr<Controller> left_hand_tracker, right_hand_tracker;\n    std::vector<std::unique_ptr<FakeViveTracker>> generic_trackers;\n    bool devices_initialized = false;\n    bool shutdown_called = false;\n\n    std::map<uint64_t, TrackedDevice*> tracked_devices;\n\n    virtual vr::EVRInitError Init(vr::IVRDriverContext* pContext) override {\n        Debug(\"DriverProvider::Init\");\n\n        VR_INIT_SERVER_DRIVER_CONTEXT(pContext);\n        InitDriverLog(vr::VRDriverLog());\n\n        if (this->early_hmd_initialization) {\n            auto hmd = new Hmd();\n            // Note: we disable awaiting for Acivate() call. That will only be called after\n            // IServerTrackedDeviceProvider::Init() (this function) returns.\n            hmd->register_device(false);\n            this->hmd = std::unique_ptr<Hmd>(hmd);\n            this->tracked_devices.insert({ HEAD_ID, this->hmd.get() });\n        }\n\n        return vr::VRInitError_None;\n    }\n    virtual void Cleanup() override {\n        Debug(\"DriverProvider::Cleanup\");\n\n        this->left_hand_tracker.reset();\n        this->right_hand_tracker.reset();\n        this->left_controller.reset();\n        this->right_controller.reset();\n        this->hmd.reset();\n        // this->generic_trackers.clear();\n\n        CleanupDriverLog();\n\n        VR_CLEANUP_SERVER_DRIVER_CONTEXT();\n    }\n    virtual const char* const* GetInterfaceVersions() override { return vr::k_InterfaceVersions; }\n    virtual const char* GetTrackedDeviceDriverVersion() {\n        return vr::ITrackedDeviceServerDriver_Version;\n    }\n    virtual void RunFrame() override {\n        vr::VREvent_t event;\n        while (vr::VRServerDriverHost()->PollNextEvent(&event, sizeof(vr::VREvent_t))) {\n            if (event.eventType == vr::VREvent_Input_HapticVibration) {\n                Debug(\"DriverProvider: Received HapticVibration event\");\n\n                vr::VREvent_HapticVibration_t haptics = event.data.hapticVibration;\n\n                uint64_t id = 0;\n                if (this->left_controller\n                    && haptics.containerHandle == this->left_controller->prop_container) {\n                    id = HAND_LEFT_ID;\n                } else if (this->right_controller\n                           && haptics.containerHandle == this->right_controller->prop_container) {\n                    id = HAND_RIGHT_ID;\n                }\n\n                HapticsSend(id, haptics.fDurationSeconds, haptics.fFrequency, haptics.fAmplitude);\n            }\n#ifdef __linux__\n            else if (event.eventType == vr::VREvent_ChaperoneUniverseHasChanged\n                     || event.eventType == vr::VREvent_ChaperoneRoomSetupFinished\n                     || event.eventType == vr::VREvent_ChaperoneFlushCache\n                     || event.eventType == vr::VREvent_ChaperoneSettingsHaveChanged\n                     || event.eventType == vr::VREvent_SeatedZeroPoseReset\n                     || event.eventType == vr::VREvent_StandingZeroPoseReset\n                     || event.eventType == vr::VREvent_SceneApplicationChanged\n                     || event.eventType == VendorEvent_ALVRDriverResync) {\n                if (hmd && hmd->m_poseHistory) {\n                    auto rawZeroPose = GetRawZeroPose();\n                    if (rawZeroPose != nullptr) {\n                        hmd->m_poseHistory->SetTransform(*rawZeroPose);\n                    }\n                }\n            }\n#endif\n        }\n        if (vr::VRServerDriverHost()->IsExiting() && !shutdown_called) {\n            Debug(\"DriverProvider: Received shutdown event\");\n\n            shutdown_called = true;\n            ShutdownRuntime();\n        }\n    }\n    virtual bool ShouldBlockStandbyMode() override { return false; }\n    virtual void EnterStandby() override { Debug(\"DriverProvider::EnterStandby\"); }\n    virtual void LeaveStandby() override { Debug(\"DriverProvider::LeaveStandby\"); }\n} g_driver_provider;\n\n// bindigs for Rust\n\nconst unsigned char* FRAME_RENDER_VS_CSO_PTR;\nunsigned int FRAME_RENDER_VS_CSO_LEN;\nconst unsigned char* FRAME_RENDER_PS_CSO_PTR;\nunsigned int FRAME_RENDER_PS_CSO_LEN;\nconst unsigned char* QUAD_SHADER_CSO_PTR;\nunsigned int QUAD_SHADER_CSO_LEN;\nconst unsigned char* COMPRESS_AXIS_ALIGNED_CSO_PTR;\nunsigned int COMPRESS_AXIS_ALIGNED_CSO_LEN;\nconst unsigned char* COLOR_CORRECTION_CSO_PTR;\nunsigned int COLOR_CORRECTION_CSO_LEN;\nconst unsigned char* RGBTOYUV420_CSO_PTR;\nunsigned int RGBTOYUV420_CSO_LEN;\n\nconst unsigned char* QUAD_SHADER_COMP_SPV_PTR;\nunsigned int QUAD_SHADER_COMP_SPV_LEN;\nconst unsigned char* COLOR_SHADER_COMP_SPV_PTR;\nunsigned int COLOR_SHADER_COMP_SPV_LEN;\nconst unsigned char* FFR_SHADER_COMP_SPV_PTR;\nunsigned int FFR_SHADER_COMP_SPV_LEN;\nconst unsigned char* RGBTOYUV420_SHADER_COMP_SPV_PTR;\nunsigned int RGBTOYUV420_SHADER_COMP_SPV_LEN;\n\nconst char* g_sessionPath;\nconst char* g_driverRootDir;\n\nvoid (*LogError)(const char* stringPtr);\nvoid (*LogWarn)(const char* stringPtr);\nvoid (*LogInfo)(const char* stringPtr);\nvoid (*LogDebug)(const char* stringPtr);\nvoid (*LogEncoder)(const char* stringPtr);\nvoid (*LogPeriodically)(const char* tag, const char* stringPtr);\nvoid (*DriverReadyIdle)(bool setDefaultChaprone);\nvoid (*SetVideoConfigNals)(const unsigned char* configBuffer, int len, int codec);\nvoid (*VideoSend)(unsigned long long targetTimestampNs, unsigned char* buf, int len, bool isIdr);\nvoid (*HapticsSend)(unsigned long long path, float duration_s, float frequency, float amplitude);\nvoid (*ShutdownRuntime)();\nunsigned long long (*PathStringToHash)(const char* path);\nvoid (*ReportPresent)(unsigned long long timestamp_ns, unsigned long long offset_ns);\nvoid (*ReportComposed)(unsigned long long timestamp_ns, unsigned long long offset_ns);\nFfiDynamicEncoderParams (*GetDynamicEncoderParams)();\nunsigned long long (*GetSerialNumber)(unsigned long long deviceID, char* outString);\nvoid (*SetOpenvrProps)(void* instancePtr, unsigned long long deviceID);\nvoid (*RegisterButtons)(void* instancePtr, unsigned long long deviceID);\nvoid (*WaitForVSync)();\n\nvoid CppInit(bool earlyHmdInitialization) {\n    g_driver_provider.early_hmd_initialization = earlyHmdInitialization;\n\n    HookCrashHandler();\n\n    // Initialize path constants\n    init_paths();\n\n    Settings::Instance().Load();\n\n    load_debug_privilege();\n}\n\nvoid* CppOpenvrEntryPoint(const char* interface_name, int* return_code) {\n    if (std::string(interface_name) == vr::IServerTrackedDeviceProvider_Version) {\n        *return_code = vr::VRInitError_None;\n        return &g_driver_provider;\n    } else {\n        *return_code = vr::VRInitError_Init_InterfaceNotFound;\n        return nullptr;\n    }\n}\n\nbool InitializeStreaming() {\n    Settings::Instance().Load();\n\n    if (!g_driver_provider.devices_initialized) {\n        if (!g_driver_provider.early_hmd_initialization) {\n            auto hmd = new Hmd();\n            if (!hmd->register_device(false)) {\n                Error(\"Failed to register HMD\");\n                return false;\n            }\n            g_driver_provider.hmd = std::unique_ptr<Hmd>(hmd);\n            g_driver_provider.tracked_devices.insert({ HEAD_ID, g_driver_provider.hmd.get() });\n        }\n\n        // Note: for controllers, hands and trackers don't bail out if registration fails\n        if (Settings::Instance().m_enableControllers) {\n            auto controllerSkeletonLevel = Settings::Instance().m_useSeparateHandTrackers\n                ? vr::VRSkeletalTracking_Estimated\n                : vr::VRSkeletalTracking_Partial;\n\n            auto left_controller = new Controller(HAND_LEFT_ID, controllerSkeletonLevel);\n            if (left_controller->register_device(true)) {\n                g_driver_provider.left_controller = std::unique_ptr<Controller>(left_controller);\n                g_driver_provider.tracked_devices.insert(\n                    { HAND_LEFT_ID, g_driver_provider.left_controller.get() }\n                );\n            }\n\n            auto right_controller = new Controller(HAND_RIGHT_ID, controllerSkeletonLevel);\n            if (right_controller->register_device(true)) {\n                g_driver_provider.right_controller = std::unique_ptr<Controller>(right_controller);\n                g_driver_provider.tracked_devices.insert(\n                    { HAND_RIGHT_ID, g_driver_provider.right_controller.get() }\n                );\n            }\n\n            if (Settings::Instance().m_useSeparateHandTrackers) {\n                auto left_hand_tracker\n                    = new Controller(HAND_TRACKER_LEFT_ID, vr::VRSkeletalTracking_Full);\n                if (left_hand_tracker->register_device(true)) {\n                    g_driver_provider.left_hand_tracker\n                        = std::unique_ptr<Controller>(left_hand_tracker);\n                    g_driver_provider.tracked_devices.insert(\n                        { HAND_TRACKER_LEFT_ID, g_driver_provider.left_hand_tracker.get() }\n                    );\n                }\n\n                auto right_hand_tracker\n                    = new Controller(HAND_TRACKER_RIGHT_ID, vr::VRSkeletalTracking_Full);\n                if (right_hand_tracker->register_device(true)) {\n                    g_driver_provider.right_hand_tracker\n                        = std::unique_ptr<Controller>(right_hand_tracker);\n                    g_driver_provider.tracked_devices.insert(\n                        { HAND_TRACKER_RIGHT_ID, g_driver_provider.right_hand_tracker.get() }\n                    );\n                }\n            }\n        }\n\n        if (Settings::Instance().m_enableBodyTrackingFakeVive) {\n            auto chestTracker = std::make_unique<FakeViveTracker>(BODY_CHEST_ID);\n            if (chestTracker->register_device(true)) {\n                g_driver_provider.tracked_devices.insert({ BODY_CHEST_ID, chestTracker.get() });\n                g_driver_provider.generic_trackers.push_back(std::move(chestTracker));\n            }\n\n            auto waistTracker = std::make_unique<FakeViveTracker>(BODY_HIPS_ID);\n            if (waistTracker->register_device(true)) {\n                g_driver_provider.tracked_devices.insert({ BODY_HIPS_ID, waistTracker.get() });\n                g_driver_provider.generic_trackers.push_back(std::move(waistTracker));\n            }\n\n            auto leftElbowTracker = std::make_unique<FakeViveTracker>(BODY_LEFT_ELBOW_ID);\n            if (leftElbowTracker->register_device(true)) {\n                g_driver_provider.tracked_devices.insert({ BODY_LEFT_ELBOW_ID,\n                                                           leftElbowTracker.get() });\n                g_driver_provider.generic_trackers.push_back(std::move(leftElbowTracker));\n            }\n\n            auto rightElbowTracker = std::make_unique<FakeViveTracker>(BODY_RIGHT_ELBOW_ID);\n            if (rightElbowTracker->register_device(true)) {\n                g_driver_provider.tracked_devices.insert({ BODY_RIGHT_ELBOW_ID,\n                                                           rightElbowTracker.get() });\n                g_driver_provider.generic_trackers.push_back(std::move(rightElbowTracker));\n            }\n\n            if (Settings::Instance().m_bodyTrackingHasLegs) {\n                auto leftKneeTracker = std::make_unique<FakeViveTracker>(BODY_LEFT_KNEE_ID);\n                if (leftKneeTracker->register_device(true)) {\n                    g_driver_provider.tracked_devices.insert({ BODY_LEFT_KNEE_ID,\n                                                               leftKneeTracker.get() });\n                    g_driver_provider.generic_trackers.push_back(std::move(leftKneeTracker));\n                }\n\n                auto leftFootTracker = std::make_unique<FakeViveTracker>(BODY_LEFT_FOOT_ID);\n                if (leftFootTracker->register_device(true)) {\n                    g_driver_provider.tracked_devices.insert({ BODY_LEFT_FOOT_ID,\n                                                               leftFootTracker.get() });\n                    g_driver_provider.generic_trackers.push_back(std::move(leftFootTracker));\n                }\n\n                auto rightKneeTracker = std::make_unique<FakeViveTracker>(BODY_RIGHT_KNEE_ID);\n                if (rightKneeTracker->register_device(true)) {\n                    g_driver_provider.tracked_devices.insert({ BODY_RIGHT_KNEE_ID,\n                                                               rightKneeTracker.get() });\n                    g_driver_provider.generic_trackers.push_back(std::move(rightKneeTracker));\n                }\n\n                auto rightFootTracker = std::make_unique<FakeViveTracker>(BODY_RIGHT_FOOT_ID);\n                if (rightFootTracker->register_device(true)) {\n                    g_driver_provider.tracked_devices.insert({ BODY_RIGHT_FOOT_ID,\n                                                               rightFootTracker.get() });\n                    g_driver_provider.generic_trackers.push_back(std::move(rightFootTracker));\n                }\n            }\n        }\n\n        g_driver_provider.devices_initialized = true;\n    }\n\n    if (g_driver_provider.hmd) {\n        g_driver_provider.hmd->StartStreaming();\n    }\n\n    return true;\n}\n\nvoid DeinitializeStreaming() {\n    if (g_driver_provider.hmd) {\n        g_driver_provider.hmd->StopStreaming();\n    }\n}\n\nvoid SendVSync() { vr::VRServerDriverHost()->VsyncEvent(0.0); }\n\nvoid RequestIDR() {\n    if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) {\n        g_driver_provider.hmd->m_encoder->InsertIDR();\n    }\n}\n\nvoid SetTracking(\n    unsigned long long targetTimestampNs,\n    float controllerPoseTimeOffsetS,\n    FfiDeviceMotion headMotion,\n    FfiHandData leftHandData,\n    FfiHandData rightHandData,\n    const FfiDeviceMotion* bodyTrackerMotions,\n    int bodyTrackerMotionCount\n) {\n    if (g_driver_provider.hmd) {\n        g_driver_provider.hmd->OnPoseUpdated(targetTimestampNs, headMotion);\n    }\n\n    if (g_driver_provider.left_hand_tracker) {\n        g_driver_provider.left_hand_tracker->OnPoseUpdate(\n            targetTimestampNs, controllerPoseTimeOffsetS, leftHandData\n        );\n    }\n\n    if (g_driver_provider.left_controller) {\n        g_driver_provider.left_controller->OnPoseUpdate(\n            targetTimestampNs, controllerPoseTimeOffsetS, leftHandData\n        );\n    }\n\n    if (g_driver_provider.right_hand_tracker) {\n        g_driver_provider.right_hand_tracker->OnPoseUpdate(\n            targetTimestampNs, controllerPoseTimeOffsetS, rightHandData\n        );\n    }\n\n    if (g_driver_provider.right_controller) {\n        g_driver_provider.right_controller->OnPoseUpdate(\n            targetTimestampNs, controllerPoseTimeOffsetS, rightHandData\n        );\n    }\n\n    if (Settings::Instance().m_enableBodyTrackingFakeVive) {\n        std::map<uint64_t, FfiDeviceMotion> motionsMap;\n        for (int i = 0; i < bodyTrackerMotionCount; i++) {\n            auto m = bodyTrackerMotions[i];\n            motionsMap.insert({ m.deviceID, m });\n        }\n\n        for (auto id : BODY_IDS) {\n            auto it = g_driver_provider.tracked_devices.find(id);\n            if (it != g_driver_provider.tracked_devices.end()) {\n                auto* maybeTracker = (FakeViveTracker*)it->second;\n                auto res = motionsMap.find(id);\n                auto* maybeMotion = res != motionsMap.end() ? &res->second : nullptr;\n\n                maybeTracker->OnPoseUpdated(targetTimestampNs, maybeMotion);\n            }\n        }\n    }\n}\n\nvoid RequestDriverResync() {\n    if (g_driver_provider.hmd) {\n        vr::VRServerDriverHost()->VendorSpecificEvent(\n            g_driver_provider.hmd->object_id, VendorEvent_ALVRDriverResync, {}, 0\n        );\n    }\n}\n\nvoid ShutdownSteamvr() {\n    if (g_driver_provider.hmd) {\n        vr::VRServerDriverHost()->VendorSpecificEvent(\n            g_driver_provider.hmd->object_id, vr::VREvent_DriverRequestedQuit, {}, 0\n        );\n    }\n}\n\nvoid SetOpenvrProperty(void* instancePtr, FfiOpenvrProperty prop) {\n    ((TrackedDevice*)instancePtr)->set_prop(prop);\n}\n\nvoid SetOpenvrPropByDeviceID(unsigned long long deviceID, FfiOpenvrProperty prop) {\n    auto device_it = g_driver_provider.tracked_devices.find(deviceID);\n\n    if (device_it != g_driver_provider.tracked_devices.end()) {\n        device_it->second->set_prop(prop);\n    }\n}\n\nvoid RegisterButton(void* instancePtr, unsigned long long buttonID) {\n    // Todo: move RegisterButton to generic TrackedDevice interface\n    ((Controller*)instancePtr)->RegisterButton(buttonID);\n}\n\nvoid SetLocalViewParams(const FfiViewParams params[2]) {\n    if (g_driver_provider.hmd) {\n        g_driver_provider.hmd->SetViewParams(params);\n    }\n}\n\nvoid SetBattery(unsigned long long deviceID, float gauge_value, bool is_plugged) {\n    auto device_it = g_driver_provider.tracked_devices.find(deviceID);\n\n    if (device_it != g_driver_provider.tracked_devices.end()) {\n        vr::VRProperties()->SetFloatProperty(\n            device_it->second->prop_container, vr::Prop_DeviceBatteryPercentage_Float, gauge_value\n        );\n        vr::VRProperties()->SetBoolProperty(\n            device_it->second->prop_container, vr::Prop_DeviceIsCharging_Bool, is_plugged\n        );\n    }\n}\n\nvoid SetButton(unsigned long long buttonID, FfiButtonValue value) {\n    if (LEFT_CONTROLLER_BUTTON_MAPPING.find(buttonID) != LEFT_CONTROLLER_BUTTON_MAPPING.end()) {\n        if (g_driver_provider.left_controller) {\n            g_driver_provider.left_controller->SetButton(buttonID, value);\n        }\n        if (g_driver_provider.left_hand_tracker) {\n            g_driver_provider.left_hand_tracker->SetButton(buttonID, value);\n        }\n    } else if (RIGHT_CONTROLLER_BUTTON_MAPPING.find(buttonID)\n               != RIGHT_CONTROLLER_BUTTON_MAPPING.end()) {\n        if (g_driver_provider.right_controller) {\n            g_driver_provider.right_controller->SetButton(buttonID, value);\n        }\n        if (g_driver_provider.right_hand_tracker) {\n            g_driver_provider.right_hand_tracker->SetButton(buttonID, value);\n        }\n    }\n}\n\nvoid SetProximityState(bool headset_is_worn) {\n    if (g_driver_provider.hmd) {\n        g_driver_provider.hmd->SetProximityState(headset_is_worn);\n    }\n}\n\nvoid SetChaperoneArea(float areaWidth, float areaHeight) {\n    _SetChaperoneArea(areaWidth, areaHeight);\n}\n\nvoid CaptureFrame() {\n#ifndef __APPLE__\n    if (g_driver_provider.hmd && g_driver_provider.hmd->m_encoder) {\n        g_driver_provider.hmd->m_encoder->CaptureFrame();\n    }\n#endif\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/bindings.h",
    "content": "#pragma once\n\nstruct FfiFov {\n    float left;\n    float right;\n    float up;\n    float down;\n};\n\nstruct FfiQuat {\n    float x;\n    float y;\n    float z;\n    float w;\n};\n\nstruct FfiPose {\n    FfiQuat orientation;\n    float position[3];\n};\n\nstruct FfiDeviceMotion {\n    unsigned long long deviceID;\n    FfiPose pose;\n    float linearVelocity[3];\n    float angularVelocity[3];\n};\n\nstruct FfiViewParams {\n    FfiPose pose;\n    FfiFov fov;\n};\n\nstruct FfiHandSkeleton {\n    float jointPositions[31][3];\n    FfiQuat jointRotations[31];\n};\n\nstruct FfiHandData {\n    const FfiDeviceMotion* controllerMotion;\n    const FfiHandSkeleton* handSkeleton;\n    bool isHandTracker;\n    bool predictHandSkeleton;\n};\n\nenum FfiOpenvrPropertyType {\n    Bool,\n    Float,\n    Int32,\n    Uint64,\n    Vector3,\n    Double,\n    String,\n};\n\nunion FfiOpenvrPropertyValue {\n    unsigned int bool_;\n    float float_;\n    int int32;\n    unsigned long long uint64;\n    float vector3[3];\n    double double_;\n    char string[256];\n};\n\nstruct FfiOpenvrProperty {\n    unsigned int key;\n    FfiOpenvrPropertyType type;\n    FfiOpenvrPropertyValue value;\n};\n\nenum FfiButtonType {\n    BUTTON_TYPE_BINARY,\n    BUTTON_TYPE_SCALAR,\n};\n\nstruct FfiButtonValue {\n    FfiButtonType type;\n    union {\n        unsigned int binary;\n        float scalar;\n    };\n};\n\nstruct FfiDynamicEncoderParams {\n    unsigned int updated;\n    unsigned long long bitrate_bps;\n    float framerate;\n};\n\nextern \"C\" const unsigned char* FRAME_RENDER_VS_CSO_PTR;\nextern \"C\" unsigned int FRAME_RENDER_VS_CSO_LEN;\nextern \"C\" const unsigned char* FRAME_RENDER_PS_CSO_PTR;\nextern \"C\" unsigned int FRAME_RENDER_PS_CSO_LEN;\nextern \"C\" const unsigned char* QUAD_SHADER_CSO_PTR;\nextern \"C\" unsigned int QUAD_SHADER_CSO_LEN;\nextern \"C\" const unsigned char* COMPRESS_AXIS_ALIGNED_CSO_PTR;\nextern \"C\" unsigned int COMPRESS_AXIS_ALIGNED_CSO_LEN;\nextern \"C\" const unsigned char* COLOR_CORRECTION_CSO_PTR;\nextern \"C\" unsigned int COLOR_CORRECTION_CSO_LEN;\nextern \"C\" const unsigned char* RGBTOYUV420_CSO_PTR;\nextern \"C\" unsigned int RGBTOYUV420_CSO_LEN;\n\nextern \"C\" const unsigned char* QUAD_SHADER_COMP_SPV_PTR;\nextern \"C\" unsigned int QUAD_SHADER_COMP_SPV_LEN;\nextern \"C\" const unsigned char* COLOR_SHADER_COMP_SPV_PTR;\nextern \"C\" unsigned int COLOR_SHADER_COMP_SPV_LEN;\nextern \"C\" const unsigned char* FFR_SHADER_COMP_SPV_PTR;\nextern \"C\" unsigned int FFR_SHADER_COMP_SPV_LEN;\nextern \"C\" const unsigned char* RGBTOYUV420_SHADER_COMP_SPV_PTR;\nextern \"C\" unsigned int RGBTOYUV420_SHADER_COMP_SPV_LEN;\n\nextern \"C\" const char* g_sessionPath;\nextern \"C\" const char* g_driverRootDir;\n\nextern \"C\" void (*LogError)(const char* stringPtr);\nextern \"C\" void (*LogWarn)(const char* stringPtr);\nextern \"C\" void (*LogInfo)(const char* stringPtr);\nextern \"C\" void (*LogDebug)(const char* stringPtr);\nextern \"C\" void (*LogEncoder)(const char* stringPtr);\nextern \"C\" void (*LogPeriodically)(const char* tag, const char* stringPtr);\nextern \"C\" void (*DriverReadyIdle)(bool setDefaultChaprone);\nextern \"C\" void (*SetVideoConfigNals)(const unsigned char* configBuffer, int len, int codec);\nextern \"C\" void (*VideoSend)(\n    unsigned long long targetTimestampNs, unsigned char* buf, int len, bool isIdr\n);\nextern \"C\" void (*HapticsSend)(\n    unsigned long long path, float duration_s, float frequency, float amplitude\n);\nextern \"C\" void (*ShutdownRuntime)();\nextern \"C\" unsigned long long (*PathStringToHash)(const char* path);\nextern \"C\" void (*ReportPresent)(unsigned long long timestamp_ns, unsigned long long offset_ns);\nextern \"C\" void (*ReportComposed)(unsigned long long timestamp_ns, unsigned long long offset_ns);\nextern \"C\" FfiDynamicEncoderParams (*GetDynamicEncoderParams)();\nextern \"C\" unsigned long long (*GetSerialNumber)(unsigned long long deviceID, char* outString);\nextern \"C\" void (*SetOpenvrProps)(void* instancePtr, unsigned long long deviceID);\nextern \"C\" void (*RegisterButtons)(void* instancePtr, unsigned long long deviceID);\nextern \"C\" void (*WaitForVSync)();\n\nextern \"C\" void CppInit(bool earlyHmdInitialization);\nextern \"C\" void* CppOpenvrEntryPoint(const char* pInterfaceName, int* pReturnCode);\nextern \"C\" bool InitializeStreaming();\nextern \"C\" void DeinitializeStreaming();\nextern \"C\" void SendVSync();\nextern \"C\" void RequestIDR();\nextern \"C\" void SetTracking(\n    unsigned long long targetTimestampNs,\n    float controllerPoseTimeOffsetS,\n    FfiDeviceMotion headMotion,\n    FfiHandData leftHandData,\n    FfiHandData rightHandData,\n    const FfiDeviceMotion* bodyTrackerMotions,\n    int bodyTrackerMotionCount\n);\nextern \"C\" void RequestDriverResync();\nextern \"C\" void ShutdownSteamvr();\n\nextern \"C\" void SetOpenvrProperty(void* instancePtr, FfiOpenvrProperty prop);\nextern \"C\" void SetOpenvrPropByDeviceID(unsigned long long deviceID, FfiOpenvrProperty prop);\nextern \"C\" void RegisterButton(void* instancePtr, unsigned long long buttonID);\nextern \"C\" void SetLocalViewParams(const FfiViewParams params[2]);\nextern \"C\" void SetBattery(unsigned long long deviceID, float gauge_value, bool is_plugged);\nextern \"C\" void SetButton(unsigned long long buttonID, FfiButtonValue value);\nextern \"C\" void SetProximityState(bool headset_is_worn);\n\nextern \"C\" void InitOpenvrClient();\nextern \"C\" void ShutdownOpenvrClient();\nextern \"C\" void SetChaperoneArea(float areaWidth, float areaHeight);\n\nextern \"C\" void CaptureFrame();\n\n// NalParsing.cpp\nvoid ParseFrameNals(\n    int codec, unsigned char* buf, int len, unsigned long long targetTimestampNs, bool isIdr\n);\n\n// CrashHandler.cpp\nvoid HookCrashHandler();\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/driverlog.cpp",
    "content": "//========= Copyright Valve Corporation ============//\n\n#include \"driverlog.h\"\n\n#include <stdarg.h>\n#include <stdio.h>\n\nstatic vr::IVRDriverLog* s_pLogFile = NULL;\n\n#if !defined(WIN32)\n#define vsnprintf_s vsnprintf\n#endif\n\nbool InitDriverLog(vr::IVRDriverLog* pDriverLog) {\n    if (s_pLogFile)\n        return false;\n    s_pLogFile = pDriverLog;\n    return s_pLogFile != NULL;\n}\n\nvoid CleanupDriverLog() { s_pLogFile = NULL; }\n\nvoid DriverLogVarArgs(const char* pMsgFormat, va_list args) {\n    char buf[1024];\n    vsnprintf_s(buf, sizeof(buf), pMsgFormat, args);\n\n    if (s_pLogFile)\n        s_pLogFile->Log(buf);\n}\n\nvoid DriverLog(const char* pMsgFormat, ...) {\n    va_list args;\n    va_start(args, pMsgFormat);\n\n    DriverLogVarArgs(pMsgFormat, args);\n\n    va_end(args);\n}\n\nvoid DebugDriverLog(const char* pMsgFormat, ...) {\n#ifdef _DEBUG\n    va_list args;\n    va_start(args, pMsgFormat);\n\n    DriverLogVarArgs(pMsgFormat, args);\n\n    va_end(args);\n#else\n    (void)pMsgFormat;\n#endif\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/driverlog.h",
    "content": "//========= Copyright Valve Corporation ============//\n\n#ifndef DRIVERLOG_H\n#define DRIVERLOG_H\n\n#pragma once\n\n#include \"openvr_driver_wrap.h\"\n#include <string>\n\nextern void DriverLog(const char* pchFormat, ...);\nextern void DriverLogVarArgs(const char* pMsgFormat, va_list args);\n\n// --------------------------------------------------------------------------\n// Purpose: Write to the log file only in debug builds\n// --------------------------------------------------------------------------\nextern void DebugDriverLog(const char* pchFormat, ...);\n\nextern bool InitDriverLog(vr::IVRDriverLog* pDriverLog);\nextern void CleanupDriverLog();\n\n#endif // DRIVERLOG_H\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/include/openvr_math.h",
    "content": "#pragma once\n\n#include <cmath>\n#include <memory>\n\ninline vr::HmdQuaternion_t operator+(const vr::HmdQuaternion_t& lhs, const vr::HmdQuaternion_t& rhs) {\n\treturn {\n\t\tlhs.w + rhs.w,\n\t\tlhs.x + rhs.x,\n\t\tlhs.y + rhs.y,\n\t\tlhs.z + rhs.z\n\t};\n}\n\n\ninline vr::HmdQuaternion_t operator-(const vr::HmdQuaternion_t& lhs, const vr::HmdQuaternion_t& rhs) {\n\treturn{\n\t\tlhs.w - rhs.w,\n\t\tlhs.x - rhs.x,\n\t\tlhs.y - rhs.y,\n\t\tlhs.z - rhs.z\n\t};\n}\n\n\ninline vr::HmdQuaternion_t operator*(const vr::HmdQuaternion_t& lhs, const vr::HmdQuaternion_t& rhs) {\n\treturn {\n\t\t(lhs.w * rhs.w) - (lhs.x * rhs.x) - (lhs.y * rhs.y) - (lhs.z * rhs.z),\n\t\t(lhs.w * rhs.x) + (lhs.x * rhs.w) + (lhs.y * rhs.z) - (lhs.z * rhs.y),\n\t\t(lhs.w * rhs.y) + (lhs.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),\n\t\t(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x)\n\t};\n}\n\n\ninline vr::HmdVector3d_t operator+(const vr::HmdVector3d_t& lhs, const vr::HmdVector3d_t& rhs) {\n\treturn {\n\t\tlhs.v[0] + rhs.v[0],\n\t\tlhs.v[1] + rhs.v[1],\n\t\tlhs.v[2] + rhs.v[2]\n\t};\n}\n\ninline vr::HmdVector3d_t operator+(const vr::HmdVector3d_t& lhs, const double(&rhs)[3]) {\n\treturn{\n\t\tlhs.v[0] + rhs[0],\n\t\tlhs.v[1] + rhs[1],\n\t\tlhs.v[2] + rhs[2]\n\t};\n}\n\ninline vr::HmdVector3d_t operator-(const vr::HmdVector3d_t& lhs, const vr::HmdVector3d_t& rhs) {\n\treturn{\n\t\tlhs.v[0] - rhs.v[0],\n\t\tlhs.v[1] - rhs.v[1],\n\t\tlhs.v[2] - rhs.v[2]\n\t};\n}\n\ninline vr::HmdVector3d_t operator-(const vr::HmdVector3d_t& lhs, const double (&rhs)[3]) {\n\treturn{\n\t\tlhs.v[0] - rhs[0],\n\t\tlhs.v[1] - rhs[1],\n\t\tlhs.v[2] - rhs[2]\n\t};\n}\n\n\ninline vr::HmdVector3d_t operator*(const vr::HmdVector3d_t& lhs, const double rhs) {\n\treturn{\n\t\tlhs.v[0] * rhs,\n\t\tlhs.v[1] * rhs,\n\t\tlhs.v[2] * rhs\n\t};\n}\n\n\ninline vr::HmdVector3d_t operator/(const vr::HmdVector3d_t& lhs, const double rhs) {\n\treturn{\n\t\tlhs.v[0] / rhs,\n\t\tlhs.v[1] / rhs,\n\t\tlhs.v[2] / rhs\n\t};\n}\n\n\nnamespace vrmath {\n\n\ttemplate<typename T> int signum(T v) {\n\t\treturn (v > (T)0) ? 1 : ((v < (T)0) ? -1 : 0);\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromRotationAxis(double rot, double ux, double uy, double uz) {\n\t\tauto ha = rot / 2;\n\t\treturn{\n\t\t\tstd::cos(ha),\n\t\t\tux * std::sin(ha),\n\t\t\tuy * std::sin(ha),\n\t\t\tuz * std::sin(ha)\n\t\t};\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromRotationX(double rot) {\n\t\tauto ha = rot / 2;\n\t\treturn{\n\t\t\tstd::cos(ha),\n\t\t\tstd::sin(ha),\n\t\t\t0.0f,\n\t\t\t0.0f\n\t\t};\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromRotationY(double rot) {\n\t\tauto ha = rot / 2;\n\t\treturn{\n\t\t\tstd::cos(ha),\n\t\t\t0.0f,\n\t\t\tstd::sin(ha),\n\t\t\t0.0f\n\t\t};\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromRotationZ(double rot) {\n\t\tauto ha = rot / 2;\n\t\treturn{\n\t\t\tstd::cos(ha),\n\t\t\t0.0f,\n\t\t\t0.0f,\n\t\t\tstd::sin(ha)\n\t\t};\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromYawPitchRoll(double yaw, double pitch, double roll) {\n\t\treturn quaternionFromRotationY(yaw) * quaternionFromRotationX(pitch) * quaternionFromRotationZ(roll);\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionFromRotationMatrix(const vr::HmdMatrix34_t& mat) {\n\t\tauto a = mat.m;\n\t\tvr::HmdQuaternion_t q;\n\t\tdouble trace = a[0][0] + a[1][1] + a[2][2];\n\t\tif (trace > 0) {\n\t\t\tdouble s = 0.5 / sqrt(trace + 1.0);\n\t\t\tq.w = 0.25 / s;\n\t\t\tq.x = (a[1][2] - a[2][1]) * s;\n\t\t\tq.y = (a[2][0] - a[0][2]) * s;\n\t\t\tq.z = (a[0][1] - a[1][0]) * s;\n\t\t} else {\n\t\t\tif (a[0][0] > a[1][1] && a[0][0] > a[2][2]) {\n\t\t\t\tdouble s = 2.0 * sqrt(1.0 + a[0][0] - a[1][1] - a[2][2]);\n\t\t\t\tq.w = (a[1][2] - a[2][1]) / s;\n\t\t\t\tq.x = 0.25 * s;\n\t\t\t\tq.y = (a[1][0] + a[0][1]) / s;\n\t\t\t\tq.z = (a[2][0] + a[0][2]) / s;\n\t\t\t} else if (a[1][1] > a[2][2]) {\n\t\t\t\tdouble s = 2.0 * sqrt(1.0 + a[1][1] - a[0][0] - a[2][2]);\n\t\t\t\tq.w = (a[2][0] - a[0][2]) / s;\n\t\t\t\tq.x = (a[1][0] + a[0][1]) / s;\n\t\t\t\tq.y = 0.25 * s;\n\t\t\t\tq.z = (a[2][1] + a[1][2]) / s;\n\t\t\t} else {\n\t\t\t\tdouble s = 2.0 * sqrt(1.0 + a[2][2] - a[0][0] - a[1][1]);\n\t\t\t\tq.w = (a[0][1] - a[1][0]) / s;\n\t\t\t\tq.x = (a[2][0] + a[0][2]) / s;\n\t\t\t\tq.y = (a[2][1] + a[1][2]) / s;\n\t\t\t\tq.z = 0.25 * s;\n\t\t\t}\n\t\t}\n\t\tq.x = -q.x;\n\t\tq.y = -q.y;\n\t\tq.z = -q.z;\n\t\treturn q;\n\t}\n\n\tinline vr::HmdQuaternion_t quaternionConjugate(const vr::HmdQuaternion_t& quat) {\n\t\treturn {\n\t\t\tquat.w,\n\t\t\t-quat.x,\n\t\t\t-quat.y,\n\t\t\t-quat.z,\n\t\t};\n\t}\n\n\tinline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const vr::HmdVector3d_t& vector, bool reverse = false) {\n\t\tif (reverse) {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector.v[0], vector.v[1] , vector.v[2] };\n\t\t\tauto pout = vrmath::quaternionConjugate(quat) * pin * quat;\n\t\t\treturn {pout.x, pout.y, pout.z};\n\t\t} else {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector.v[0], vector.v[1] , vector.v[2] };\n\t\t\tauto pout = quat * pin * vrmath::quaternionConjugate(quat);\n\t\t\treturn { pout.x, pout.y, pout.z };\n\t\t}\n\t}\n\n\tinline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const vr::HmdQuaternion_t& quatInv, const vr::HmdVector3d_t& vector, bool reverse = false) {\n\t\tif (reverse) {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector.v[0], vector.v[1] , vector.v[2] };\n\t\t\tauto pout = quatInv * pin * quat;\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t} else {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector.v[0], vector.v[1] , vector.v[2] };\n\t\t\tauto pout = quat * pin * quatInv;\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t}\n\t}\n\n\tinline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const double (&vector)[3], bool reverse = false) {\n\t\tif (reverse) {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector[0], vector[1] , vector[2] };\n\t\t\tauto pout = vrmath::quaternionConjugate(quat) * pin * quat;\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t} else {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector[0], vector[1] , vector[2] };\n\t\t\tauto pout = quat * pin * vrmath::quaternionConjugate(quat);\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t}\n\t}\n\n\tinline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const vr::HmdQuaternion_t& quatInv, const double(&vector)[3], bool reverse = false) {\n\t\tif (reverse) {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector[0], vector[1] , vector[2] };\n\t\t\tauto pout = quatInv * pin * quat;\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t} else {\n\t\t\tvr::HmdQuaternion_t pin = { 0.0, vector[0], vector[1] , vector[2] };\n\t\t\tauto pout = quat * pin * quatInv;\n\t\t\treturn{ pout.x, pout.y, pout.z };\n\t\t}\n\t}\n\n\tinline vr::HmdMatrix34_t matMul33(const vr::HmdMatrix34_t& a, const vr::HmdMatrix34_t& b) {\n\t\tvr::HmdMatrix34_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tfor (unsigned j = 0; j < 3; j++) {\n\t\t\t\tresult.m[i][j] = 0.0f;\n\t\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\t\tresult.m[i][j] += a.m[i][k] * b.m[k][j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tinline vr::HmdVector3_t matMul33(const vr::HmdMatrix34_t& a, const vr::HmdVector3_t& b) {\n\t\tvr::HmdVector3_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tresult.v[i] = 0.0f;\n\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\tresult.v[i] += a.m[i][k] * b.v[k];\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n\n\tinline vr::HmdVector3d_t matMul33(const vr::HmdMatrix34_t& a, const vr::HmdVector3d_t& b) {\n\t\tvr::HmdVector3d_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tresult.v[i] = 0.0f;\n\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\tresult.v[i] += a.m[i][k] * b.v[k];\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n\n\tinline vr::HmdVector3_t matMul33(const vr::HmdVector3_t& a, const vr::HmdMatrix34_t& b) {\n\t\tvr::HmdVector3_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tresult.v[i] = 0.0f;\n\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\tresult.v[i] += a.v[k] * b.m[k][i];\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n\n\tinline vr::HmdVector3d_t matMul33(const vr::HmdVector3d_t& a, const vr::HmdMatrix34_t& b) {\n\t\tvr::HmdVector3d_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tresult.v[i] = 0.0f;\n\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\tresult.v[i] += a.v[k] * b.m[k][i];\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n\n\tinline vr::HmdMatrix34_t transposeMul33(const vr::HmdMatrix34_t& a) {\n\t\tvr::HmdMatrix34_t result;\n\t\tfor (unsigned i = 0; i < 3; i++) {\n\t\t\tfor (unsigned k = 0; k < 3; k++) {\n\t\t\t\tresult.m[i][k] = a.m[k][i];\n\t\t\t}\n\t\t}\n\t\tresult.m[0][3] = a.m[0][3];\n\t\tresult.m[1][3] = a.m[1][3];\n\t\tresult.m[2][3] = a.m[2][3];\n\t\treturn result;\n\t}\n\n  inline vr::HmdMatrix34_t matInv33(vr::HmdMatrix34_t matrix) {\n    vr::HmdMatrix34_t result;\n    float cofac00 = matrix.m[1][1] * matrix.m[2][2] - matrix.m[1][2] * matrix.m[2][1];\n    float cofac10 = matrix.m[1][2] * matrix.m[2][0] - matrix.m[1][0] * matrix.m[2][2];\n    float cofac20 = matrix.m[1][0] * matrix.m[2][1] - matrix.m[1][1] * matrix.m[2][0];\n\n    float det = matrix.m[0][0] * cofac00 + matrix.m[0][1] * cofac10 + matrix.m[0][2] * cofac20;\n\n    if (det == 0) {\n        vr::HmdMatrix34_t result = { { { 1.0, 0.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0, 0.0 }, { 0.0, 0.0, 1.0, 0.0 } } };\n        return result;\n    }\n\n    float invDet = 1.0f / det;\n\n    float cofac01 = matrix.m[0][2] * matrix.m[2][1] - matrix.m[0][1] * matrix.m[2][2];\n    float cofac02 = matrix.m[0][1] * matrix.m[1][2] - matrix.m[0][2] * matrix.m[1][1];\n    float cofac11 = matrix.m[0][0] * matrix.m[2][2] - matrix.m[0][2] * matrix.m[2][0];\n    float cofac12 = matrix.m[0][2] * matrix.m[1][0] - matrix.m[0][0] * matrix.m[1][2];\n    float cofac21 = matrix.m[0][1] * matrix.m[2][0] - matrix.m[0][0] * matrix.m[2][1];\n    float cofac22 = matrix.m[0][0] * matrix.m[1][1] - matrix.m[0][1] * matrix.m[1][0];\n\n    result.m[0][0] = invDet * cofac00;\n    result.m[0][1] = invDet * cofac01;\n    result.m[0][2] = invDet * cofac02;\n    result.m[0][3] = 0.0f;\n\n    result.m[1][0] = invDet * cofac10;\n    result.m[1][1] = invDet * cofac11;\n    result.m[1][2] = invDet * cofac12;\n    result.m[1][3] = 0.0f;\n\n    result.m[2][0] = invDet * cofac20;\n    result.m[2][1] = invDet * cofac21;\n    result.m[2][2] = invDet * cofac22;\n    result.m[2][3] = 0.0f;\n\n    return result;\n  }\n}\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/include/picojson.h",
    "content": "/*\n * Copyright 2009-2010 Cybozu Labs, Inc.\n * Copyright 2011-2014 Kazuho Oku\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice,\n *    this list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n#ifndef picojson_h\n#define picojson_h\n\n#include <algorithm>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <cstddef>\n#include <iostream>\n#include <iterator>\n#include <limits>\n#include <map>\n#include <stdexcept>\n#include <string>\n#include <vector>\n#include <utility>\n\n// for isnan/isinf\n#if __cplusplus >= 201103L\n#include <cmath>\n#else\nextern \"C\" {\n#ifdef _MSC_VER\n#include <float.h>\n#elif defined(__INTEL_COMPILER)\n#include <mathimf.h>\n#else\n#include <math.h>\n#endif\n}\n#endif\n\n#ifndef PICOJSON_USE_RVALUE_REFERENCE\n#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600)\n#define PICOJSON_USE_RVALUE_REFERENCE 1\n#else\n#define PICOJSON_USE_RVALUE_REFERENCE 0\n#endif\n#endif // PICOJSON_USE_RVALUE_REFERENCE\n\n#ifndef PICOJSON_NOEXCEPT\n#if PICOJSON_USE_RVALUE_REFERENCE\n#define PICOJSON_NOEXCEPT noexcept\n#else\n#define PICOJSON_NOEXCEPT throw()\n#endif\n#endif\n\n// experimental support for int64_t (see README.mkdn for detail)\n#ifdef PICOJSON_USE_INT64\n#define __STDC_FORMAT_MACROS\n#include <errno.h>\n#include <inttypes.h>\n#endif\n\n// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0\n#ifndef PICOJSON_USE_LOCALE\n#define PICOJSON_USE_LOCALE 1\n#endif\n#if PICOJSON_USE_LOCALE\nextern \"C\" {\n#include <locale.h>\n}\n#endif\n\n#ifndef PICOJSON_ASSERT\n#define PICOJSON_ASSERT(e)                                                                                                         \\\n  do {                                                                                                                             \\\n    if (!(e))                                                                                                                      \\\n      throw std::runtime_error(#e);                                                                                                \\\n  } while (0)\n#endif\n\n#ifdef _MSC_VER\n#define SNPRINTF _snprintf_s\n#pragma warning(push)\n#pragma warning(disable : 4244) // conversion from int to char\n#pragma warning(disable : 4127) // conditional expression is constant\n#pragma warning(disable : 4702) // unreachable code\n#else\n#define SNPRINTF snprintf\n#endif\n\nnamespace picojson {\n\nenum {\n  null_type,\n  boolean_type,\n  number_type,\n  string_type,\n  array_type,\n  object_type\n#ifdef PICOJSON_USE_INT64\n  ,\n  int64_type\n#endif\n};\n\nenum { INDENT_WIDTH = 2 };\n\nstruct null {};\n\nclass value {\npublic:\n  typedef std::vector<value> array;\n  typedef std::map<std::string, value> object;\n  union _storage {\n    bool boolean_;\n    double number_;\n#ifdef PICOJSON_USE_INT64\n    int64_t int64_;\n#endif\n    std::string *string_;\n    array *array_;\n    object *object_;\n  };\n\nprotected:\n  int type_;\n  _storage u_;\n\npublic:\n  value();\n  value(int type, bool);\n  explicit value(bool b);\n#ifdef PICOJSON_USE_INT64\n  explicit value(int64_t i);\n#endif\n  explicit value(double n);\n  explicit value(const std::string &s);\n  explicit value(const array &a);\n  explicit value(const object &o);\n#if PICOJSON_USE_RVALUE_REFERENCE\n  explicit value(std::string &&s);\n  explicit value(array &&a);\n  explicit value(object &&o);\n#endif\n  explicit value(const char *s);\n  value(const char *s, size_t len);\n  ~value();\n  value(const value &x);\n  value &operator=(const value &x);\n#if PICOJSON_USE_RVALUE_REFERENCE\n  value(value &&x) PICOJSON_NOEXCEPT;\n  value &operator=(value &&x) PICOJSON_NOEXCEPT;\n#endif\n  void swap(value &x) PICOJSON_NOEXCEPT;\n  template <typename T> bool is() const;\n  template <typename T> const T &get() const;\n  template <typename T> T &get();\n  template <typename T> void set(const T &);\n#if PICOJSON_USE_RVALUE_REFERENCE\n  template <typename T> void set(T &&);\n#endif\n  bool evaluate_as_boolean() const;\n  const value &get(const size_t idx) const;\n  const value &get(const std::string &key) const;\n  value &get(const size_t idx);\n  value &get(const std::string &key);\n\n  bool contains(const size_t idx) const;\n  bool contains(const std::string &key) const;\n  std::string to_str() const;\n  template <typename Iter> void serialize(Iter os, bool prettify = false) const;\n  std::string serialize(bool prettify = false) const;\n\nprivate:\n  template <typename T> value(const T *); // intentionally defined to block implicit conversion of pointer to bool\n  template <typename Iter> static void _indent(Iter os, int indent);\n  template <typename Iter> void _serialize(Iter os, int indent) const;\n  std::string _serialize(int indent) const;\n  void clear();\n};\n\ntypedef value::array array;\ntypedef value::object object;\n\ninline value::value() : type_(null_type), u_() {\n}\n\ninline value::value(int type, bool) : type_(type), u_() {\n  switch (type) {\n#define INIT(p, v)                                                                                                                 \\\n  case p##type:                                                                                                                    \\\n    u_.p = v;                                                                                                                      \\\n    break\n    INIT(boolean_, false);\n    INIT(number_, 0.0);\n#ifdef PICOJSON_USE_INT64\n    INIT(int64_, 0);\n#endif\n    INIT(string_, new std::string());\n    INIT(array_, new array());\n    INIT(object_, new object());\n#undef INIT\n  default:\n    break;\n  }\n}\n\ninline value::value(bool b) : type_(boolean_type), u_() {\n  u_.boolean_ = b;\n}\n\n#ifdef PICOJSON_USE_INT64\ninline value::value(int64_t i) : type_(int64_type), u_() {\n  u_.int64_ = i;\n}\n#endif\n\ninline value::value(double n) : type_(number_type), u_() {\n  if (\n#ifdef _MSC_VER\n      !_finite(n)\n#elif __cplusplus >= 201103L || !(defined(isnan) && defined(isinf))\n      std::isnan(n) || std::isinf(n)\n#else\n      isnan(n) || isinf(n)\n#endif\n          ) {\n    throw std::overflow_error(\"\");\n  }\n  u_.number_ = n;\n}\n\ninline value::value(const std::string &s) : type_(string_type), u_() {\n  u_.string_ = new std::string(s);\n}\n\ninline value::value(const array &a) : type_(array_type), u_() {\n  u_.array_ = new array(a);\n}\n\ninline value::value(const object &o) : type_(object_type), u_() {\n  u_.object_ = new object(o);\n}\n\n#if PICOJSON_USE_RVALUE_REFERENCE\ninline value::value(std::string &&s) : type_(string_type), u_() {\n  u_.string_ = new std::string(std::move(s));\n}\n\ninline value::value(array &&a) : type_(array_type), u_() {\n  u_.array_ = new array(std::move(a));\n}\n\ninline value::value(object &&o) : type_(object_type), u_() {\n  u_.object_ = new object(std::move(o));\n}\n#endif\n\ninline value::value(const char *s) : type_(string_type), u_() {\n  u_.string_ = new std::string(s);\n}\n\ninline value::value(const char *s, size_t len) : type_(string_type), u_() {\n  u_.string_ = new std::string(s, len);\n}\n\ninline void value::clear() {\n  switch (type_) {\n#define DEINIT(p)                                                                                                                  \\\n  case p##type:                                                                                                                    \\\n    delete u_.p;                                                                                                                   \\\n    break\n    DEINIT(string_);\n    DEINIT(array_);\n    DEINIT(object_);\n#undef DEINIT\n  default:\n    break;\n  }\n}\n\ninline value::~value() {\n  clear();\n}\n\ninline value::value(const value &x) : type_(x.type_), u_() {\n  switch (type_) {\n#define INIT(p, v)                                                                                                                 \\\n  case p##type:                                                                                                                    \\\n    u_.p = v;                                                                                                                      \\\n    break\n    INIT(string_, new std::string(*x.u_.string_));\n    INIT(array_, new array(*x.u_.array_));\n    INIT(object_, new object(*x.u_.object_));\n#undef INIT\n  default:\n    u_ = x.u_;\n    break;\n  }\n}\n\ninline value &value::operator=(const value &x) {\n  if (this != &x) {\n    value t(x);\n    swap(t);\n  }\n  return *this;\n}\n\n#if PICOJSON_USE_RVALUE_REFERENCE\ninline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() {\n  swap(x);\n}\ninline value &value::operator=(value &&x) PICOJSON_NOEXCEPT {\n  swap(x);\n  return *this;\n}\n#endif\ninline void value::swap(value &x) PICOJSON_NOEXCEPT {\n  std::swap(type_, x.type_);\n  std::swap(u_, x.u_);\n}\n\n#define IS(ctype, jtype)                                                                                                           \\\n  template <> inline bool value::is<ctype>() const {                                                                               \\\n    return type_ == jtype##_type;                                                                                                  \\\n  }\nIS(null, null)\nIS(bool, boolean)\n#ifdef PICOJSON_USE_INT64\nIS(int64_t, int64)\n#endif\nIS(std::string, string)\nIS(array, array)\nIS(object, object)\n#undef IS\ntemplate <> inline bool value::is<double>() const {\n  return type_ == number_type\n#ifdef PICOJSON_USE_INT64\n         || type_ == int64_type\n#endif\n      ;\n}\n\n#define GET(ctype, var)                                                                                                            \\\n  template <> inline const ctype &value::get<ctype>() const {                                                                      \\\n    PICOJSON_ASSERT(\"type mismatch! call is<type>() before get<type>()\" && is<ctype>());                                           \\\n    return var;                                                                                                                    \\\n  }                                                                                                                                \\\n  template <> inline ctype &value::get<ctype>() {                                                                                  \\\n    PICOJSON_ASSERT(\"type mismatch! call is<type>() before get<type>()\" && is<ctype>());                                           \\\n    return var;                                                                                                                    \\\n  }\nGET(bool, u_.boolean_)\nGET(std::string, *u_.string_)\nGET(array, *u_.array_)\nGET(object, *u_.object_)\n#ifdef PICOJSON_USE_INT64\nGET(double,\n    (type_ == int64_type && (const_cast<value *>(this)->type_ = number_type, (const_cast<value *>(this)->u_.number_ = u_.int64_)),\n     u_.number_))\nGET(int64_t, u_.int64_)\n#else\nGET(double, u_.number_)\n#endif\n#undef GET\n\n#define SET(ctype, jtype, setter)                                                                                                  \\\n  template <> inline void value::set<ctype>(const ctype &_val) {                                                                   \\\n    clear();                                                                                                                       \\\n    type_ = jtype##_type;                                                                                                          \\\n    setter                                                                                                                         \\\n  }\nSET(bool, boolean, u_.boolean_ = _val;)\nSET(std::string, string, u_.string_ = new std::string(_val);)\nSET(array, array, u_.array_ = new array(_val);)\nSET(object, object, u_.object_ = new object(_val);)\nSET(double, number, u_.number_ = _val;)\n#ifdef PICOJSON_USE_INT64\nSET(int64_t, int64, u_.int64_ = _val;)\n#endif\n#undef SET\n\n#if PICOJSON_USE_RVALUE_REFERENCE\n#define MOVESET(ctype, jtype, setter)                                                                                              \\\n  template <> inline void value::set<ctype>(ctype && _val) {                                                                       \\\n    clear();                                                                                                                       \\\n    type_ = jtype##_type;                                                                                                          \\\n    setter                                                                                                                         \\\n  }\nMOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)\nMOVESET(array, array, u_.array_ = new array(std::move(_val));)\nMOVESET(object, object, u_.object_ = new object(std::move(_val));)\n#undef MOVESET\n#endif\n\ninline bool value::evaluate_as_boolean() const {\n  switch (type_) {\n  case null_type:\n    return false;\n  case boolean_type:\n    return u_.boolean_;\n  case number_type:\n    return u_.number_ != 0;\n#ifdef PICOJSON_USE_INT64\n  case int64_type:\n    return u_.int64_ != 0;\n#endif\n  case string_type:\n    return !u_.string_->empty();\n  default:\n    return true;\n  }\n}\n\ninline const value &value::get(const size_t idx) const {\n  static value s_null;\n  PICOJSON_ASSERT(is<array>());\n  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;\n}\n\ninline value &value::get(const size_t idx) {\n  static value s_null;\n  PICOJSON_ASSERT(is<array>());\n  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;\n}\n\ninline const value &value::get(const std::string &key) const {\n  static value s_null;\n  PICOJSON_ASSERT(is<object>());\n  object::const_iterator i = u_.object_->find(key);\n  return i != u_.object_->end() ? i->second : s_null;\n}\n\ninline value &value::get(const std::string &key) {\n  static value s_null;\n  PICOJSON_ASSERT(is<object>());\n  object::iterator i = u_.object_->find(key);\n  return i != u_.object_->end() ? i->second : s_null;\n}\n\ninline bool value::contains(const size_t idx) const {\n  PICOJSON_ASSERT(is<array>());\n  return idx < u_.array_->size();\n}\n\ninline bool value::contains(const std::string &key) const {\n  PICOJSON_ASSERT(is<object>());\n  object::const_iterator i = u_.object_->find(key);\n  return i != u_.object_->end();\n}\n\ninline std::string value::to_str() const {\n  switch (type_) {\n  case null_type:\n    return \"null\";\n  case boolean_type:\n    return u_.boolean_ ? \"true\" : \"false\";\n#ifdef PICOJSON_USE_INT64\n  case int64_type: {\n    char buf[sizeof(\"-9223372036854775808\")];\n    SNPRINTF(buf, sizeof(buf), \"%\" PRId64, u_.int64_);\n    return buf;\n  }\n#endif\n  case number_type: {\n    char buf[256];\n    double tmp;\n    SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? \"%.f\" : \"%.17g\", u_.number_);\n#if PICOJSON_USE_LOCALE\n    char *decimal_point = localeconv()->decimal_point;\n    if (strcmp(decimal_point, \".\") != 0) {\n      size_t decimal_point_len = strlen(decimal_point);\n      for (char *p = buf; *p != '\\0'; ++p) {\n        if (strncmp(p, decimal_point, decimal_point_len) == 0) {\n          return std::string(buf, p) + \".\" + (p + decimal_point_len);\n        }\n      }\n    }\n#endif\n    return buf;\n  }\n  case string_type:\n    return *u_.string_;\n  case array_type:\n    return \"array\";\n  case object_type:\n    return \"object\";\n  default:\n    PICOJSON_ASSERT(0);\n#ifdef _MSC_VER\n    __assume(0);\n#endif\n  }\n  return std::string();\n}\n\ntemplate <typename Iter> void copy(const std::string &s, Iter oi) {\n  std::copy(s.begin(), s.end(), oi);\n}\n\ntemplate <typename Iter> struct serialize_str_char {\n  Iter oi;\n  void operator()(char c) {\n    switch (c) {\n#define MAP(val, sym)                                                                                                              \\\n  case val:                                                                                                                        \\\n    copy(sym, oi);                                                                                                                 \\\n    break\n      MAP('\"', \"\\\\\\\"\");\n      MAP('\\\\', \"\\\\\\\\\");\n      MAP('/', \"\\\\/\");\n      MAP('\\b', \"\\\\b\");\n      MAP('\\f', \"\\\\f\");\n      MAP('\\n', \"\\\\n\");\n      MAP('\\r', \"\\\\r\");\n      MAP('\\t', \"\\\\t\");\n#undef MAP\n    default:\n      if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {\n        char buf[7];\n        SNPRINTF(buf, sizeof(buf), \"\\\\u%04x\", c & 0xff);\n        copy(buf, buf + 6, oi);\n      } else {\n        *oi++ = c;\n      }\n      break;\n    }\n  }\n};\n\ntemplate <typename Iter> void serialize_str(const std::string &s, Iter oi) {\n  *oi++ = '\"';\n  serialize_str_char<Iter> process_char = {oi};\n  std::for_each(s.begin(), s.end(), process_char);\n  *oi++ = '\"';\n}\n\ntemplate <typename Iter> void value::serialize(Iter oi, bool prettify) const {\n  return _serialize(oi, prettify ? 0 : -1);\n}\n\ninline std::string value::serialize(bool prettify) const {\n  return _serialize(prettify ? 0 : -1);\n}\n\ntemplate <typename Iter> void value::_indent(Iter oi, int indent) {\n  *oi++ = '\\n';\n  for (int i = 0; i < indent * INDENT_WIDTH; ++i) {\n    *oi++ = ' ';\n  }\n}\n\ntemplate <typename Iter> void value::_serialize(Iter oi, int indent) const {\n  switch (type_) {\n  case string_type:\n    serialize_str(*u_.string_, oi);\n    break;\n  case array_type: {\n    *oi++ = '[';\n    if (indent != -1) {\n      ++indent;\n    }\n    for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) {\n      if (i != u_.array_->begin()) {\n        *oi++ = ',';\n      }\n      if (indent != -1) {\n        _indent(oi, indent);\n      }\n      i->_serialize(oi, indent);\n    }\n    if (indent != -1) {\n      --indent;\n      if (!u_.array_->empty()) {\n        _indent(oi, indent);\n      }\n    }\n    *oi++ = ']';\n    break;\n  }\n  case object_type: {\n    *oi++ = '{';\n    if (indent != -1) {\n      ++indent;\n    }\n    for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) {\n      if (i != u_.object_->begin()) {\n        *oi++ = ',';\n      }\n      if (indent != -1) {\n        _indent(oi, indent);\n      }\n      serialize_str(i->first, oi);\n      *oi++ = ':';\n      if (indent != -1) {\n        *oi++ = ' ';\n      }\n      i->second._serialize(oi, indent);\n    }\n    if (indent != -1) {\n      --indent;\n      if (!u_.object_->empty()) {\n        _indent(oi, indent);\n      }\n    }\n    *oi++ = '}';\n    break;\n  }\n  default:\n    copy(to_str(), oi);\n    break;\n  }\n  if (indent == 0) {\n    *oi++ = '\\n';\n  }\n}\n\ninline std::string value::_serialize(int indent) const {\n  std::string s;\n  _serialize(std::back_inserter(s), indent);\n  return s;\n}\n\ntemplate <typename Iter> class input {\nprotected:\n  Iter cur_, end_;\n  bool consumed_;\n  int line_;\n\npublic:\n  input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) {\n  }\n  int getc() {\n    if (consumed_) {\n      if (*cur_ == '\\n') {\n        ++line_;\n      }\n      ++cur_;\n    }\n    if (cur_ == end_) {\n      consumed_ = false;\n      return -1;\n    }\n    consumed_ = true;\n    return *cur_ & 0xff;\n  }\n  void ungetc() {\n    consumed_ = false;\n  }\n  Iter cur() const {\n    if (consumed_) {\n      input<Iter> *self = const_cast<input<Iter> *>(this);\n      self->consumed_ = false;\n      ++self->cur_;\n    }\n    return cur_;\n  }\n  int line() const {\n    return line_;\n  }\n  void skip_ws() {\n    while (1) {\n      int ch = getc();\n      if (!(ch == ' ' || ch == '\\t' || ch == '\\n' || ch == '\\r')) {\n        ungetc();\n        break;\n      }\n    }\n  }\n  bool expect(const int expected) {\n    skip_ws();\n    if (getc() != expected) {\n      ungetc();\n      return false;\n    }\n    return true;\n  }\n  bool match(const std::string &pattern) {\n    for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {\n      if (getc() != *pi) {\n        ungetc();\n        return false;\n      }\n    }\n    return true;\n  }\n};\n\ntemplate <typename Iter> inline int _parse_quadhex(input<Iter> &in) {\n  int uni_ch = 0, hex;\n  for (int i = 0; i < 4; i++) {\n    if ((hex = in.getc()) == -1) {\n      return -1;\n    }\n    if ('0' <= hex && hex <= '9') {\n      hex -= '0';\n    } else if ('A' <= hex && hex <= 'F') {\n      hex -= 'A' - 0xa;\n    } else if ('a' <= hex && hex <= 'f') {\n      hex -= 'a' - 0xa;\n    } else {\n      in.ungetc();\n      return -1;\n    }\n    uni_ch = uni_ch * 16 + hex;\n  }\n  return uni_ch;\n}\n\ntemplate <typename String, typename Iter> inline bool _parse_codepoint(String &out, input<Iter> &in) {\n  int uni_ch;\n  if ((uni_ch = _parse_quadhex(in)) == -1) {\n    return false;\n  }\n  if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {\n    if (0xdc00 <= uni_ch) {\n      // a second 16-bit of a surrogate pair appeared\n      return false;\n    }\n    // first 16-bit of surrogate pair, get the next one\n    if (in.getc() != '\\\\' || in.getc() != 'u') {\n      in.ungetc();\n      return false;\n    }\n    int second = _parse_quadhex(in);\n    if (!(0xdc00 <= second && second <= 0xdfff)) {\n      return false;\n    }\n    uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);\n    uni_ch += 0x10000;\n  }\n  if (uni_ch < 0x80) {\n    out.push_back(static_cast<char>(uni_ch));\n  } else {\n    if (uni_ch < 0x800) {\n      out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6)));\n    } else {\n      if (uni_ch < 0x10000) {\n        out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12)));\n      } else {\n        out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18)));\n        out.push_back(static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f)));\n      }\n      out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f)));\n    }\n    out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f)));\n  }\n  return true;\n}\n\ntemplate <typename String, typename Iter> inline bool _parse_string(String &out, input<Iter> &in) {\n  while (1) {\n    int ch = in.getc();\n    if (ch < ' ') {\n      in.ungetc();\n      return false;\n    } else if (ch == '\"') {\n      return true;\n    } else if (ch == '\\\\') {\n      if ((ch = in.getc()) == -1) {\n        return false;\n      }\n      switch (ch) {\n#define MAP(sym, val)                                                                                                              \\\n  case sym:                                                                                                                        \\\n    out.push_back(val);                                                                                                            \\\n    break\n        MAP('\"', '\\\"');\n        MAP('\\\\', '\\\\');\n        MAP('/', '/');\n        MAP('b', '\\b');\n        MAP('f', '\\f');\n        MAP('n', '\\n');\n        MAP('r', '\\r');\n        MAP('t', '\\t');\n#undef MAP\n      case 'u':\n        if (!_parse_codepoint(out, in)) {\n          return false;\n        }\n        break;\n      default:\n        return false;\n      }\n    } else {\n      out.push_back(static_cast<char>(ch));\n    }\n  }\n  return false;\n}\n\ntemplate <typename Context, typename Iter> inline bool _parse_array(Context &ctx, input<Iter> &in) {\n  if (!ctx.parse_array_start()) {\n    return false;\n  }\n  size_t idx = 0;\n  if (in.expect(']')) {\n    return ctx.parse_array_stop(idx);\n  }\n  do {\n    if (!ctx.parse_array_item(in, idx)) {\n      return false;\n    }\n    idx++;\n  } while (in.expect(','));\n  return in.expect(']') && ctx.parse_array_stop(idx);\n}\n\ntemplate <typename Context, typename Iter> inline bool _parse_object(Context &ctx, input<Iter> &in) {\n  if (!ctx.parse_object_start()) {\n    return false;\n  }\n  if (in.expect('}')) {\n    return true;\n  }\n  do {\n    std::string key;\n    if (!in.expect('\"') || !_parse_string(key, in) || !in.expect(':')) {\n      return false;\n    }\n    if (!ctx.parse_object_item(in, key)) {\n      return false;\n    }\n  } while (in.expect(','));\n  return in.expect('}');\n}\n\ntemplate <typename Iter> inline std::string _parse_number(input<Iter> &in) {\n  std::string num_str;\n  while (1) {\n    int ch = in.getc();\n    if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {\n      num_str.push_back(static_cast<char>(ch));\n    } else if (ch == '.') {\n#if PICOJSON_USE_LOCALE\n      num_str += localeconv()->decimal_point;\n#else\n      num_str.push_back('.');\n#endif\n    } else {\n      in.ungetc();\n      break;\n    }\n  }\n  return num_str;\n}\n\ntemplate <typename Context, typename Iter> inline bool _parse(Context &ctx, input<Iter> &in) {\n  in.skip_ws();\n  int ch = in.getc();\n  switch (ch) {\n#define IS(ch, text, op)                                                                                                           \\\n  case ch:                                                                                                                         \\\n    if (in.match(text) && op) {                                                                                                    \\\n      return true;                                                                                                                 \\\n    } else {                                                                                                                       \\\n      return false;                                                                                                                \\\n    }\n    IS('n', \"ull\", ctx.set_null());\n    IS('f', \"alse\", ctx.set_bool(false));\n    IS('t', \"rue\", ctx.set_bool(true));\n#undef IS\n  case '\"':\n    return ctx.parse_string(in);\n  case '[':\n    return _parse_array(ctx, in);\n  case '{':\n    return _parse_object(ctx, in);\n  default:\n    if (('0' <= ch && ch <= '9') || ch == '-') {\n      double f;\n      char *endp;\n      in.ungetc();\n      std::string num_str(_parse_number(in));\n      if (num_str.empty()) {\n        return false;\n      }\n#ifdef PICOJSON_USE_INT64\n      {\n        errno = 0;\n        intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);\n        if (errno == 0 && std::numeric_limits<int64_t>::min() <= ival && ival <= std::numeric_limits<int64_t>::max() &&\n            endp == num_str.c_str() + num_str.size()) {\n          ctx.set_int64(ival);\n          return true;\n        }\n      }\n#endif\n      f = strtod(num_str.c_str(), &endp);\n      if (endp == num_str.c_str() + num_str.size()) {\n        ctx.set_number(f);\n        return true;\n      }\n      return false;\n    }\n    break;\n  }\n  in.ungetc();\n  return false;\n}\n\nclass deny_parse_context {\npublic:\n  bool set_null() {\n    return false;\n  }\n  bool set_bool(bool) {\n    return false;\n  }\n#ifdef PICOJSON_USE_INT64\n  bool set_int64(int64_t) {\n    return false;\n  }\n#endif\n  bool set_number(double) {\n    return false;\n  }\n  template <typename Iter> bool parse_string(input<Iter> &) {\n    return false;\n  }\n  bool parse_array_start() {\n    return false;\n  }\n  template <typename Iter> bool parse_array_item(input<Iter> &, size_t) {\n    return false;\n  }\n  bool parse_array_stop(size_t) {\n    return false;\n  }\n  bool parse_object_start() {\n    return false;\n  }\n  template <typename Iter> bool parse_object_item(input<Iter> &, const std::string &) {\n    return false;\n  }\n};\n\nclass default_parse_context {\nprotected:\n  value *out_;\n\npublic:\n  default_parse_context(value *out) : out_(out) {\n  }\n  bool set_null() {\n    *out_ = value();\n    return true;\n  }\n  bool set_bool(bool b) {\n    *out_ = value(b);\n    return true;\n  }\n#ifdef PICOJSON_USE_INT64\n  bool set_int64(int64_t i) {\n    *out_ = value(i);\n    return true;\n  }\n#endif\n  bool set_number(double f) {\n    *out_ = value(f);\n    return true;\n  }\n  template <typename Iter> bool parse_string(input<Iter> &in) {\n    *out_ = value(string_type, false);\n    return _parse_string(out_->get<std::string>(), in);\n  }\n  bool parse_array_start() {\n    *out_ = value(array_type, false);\n    return true;\n  }\n  template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {\n    array &a = out_->get<array>();\n    a.push_back(value());\n    default_parse_context ctx(&a.back());\n    return _parse(ctx, in);\n  }\n  bool parse_array_stop(size_t) {\n    return true;\n  }\n  bool parse_object_start() {\n    *out_ = value(object_type, false);\n    return true;\n  }\n  template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &key) {\n    object &o = out_->get<object>();\n    default_parse_context ctx(&o[key]);\n    return _parse(ctx, in);\n  }\n\nprivate:\n  default_parse_context(const default_parse_context &);\n  default_parse_context &operator=(const default_parse_context &);\n};\n\nclass null_parse_context {\npublic:\n  struct dummy_str {\n    void push_back(int) {\n    }\n  };\n\npublic:\n  null_parse_context() {\n  }\n  bool set_null() {\n    return true;\n  }\n  bool set_bool(bool) {\n    return true;\n  }\n#ifdef PICOJSON_USE_INT64\n  bool set_int64(int64_t) {\n    return true;\n  }\n#endif\n  bool set_number(double) {\n    return true;\n  }\n  template <typename Iter> bool parse_string(input<Iter> &in) {\n    dummy_str s;\n    return _parse_string(s, in);\n  }\n  bool parse_array_start() {\n    return true;\n  }\n  template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {\n    return _parse(*this, in);\n  }\n  bool parse_array_stop(size_t) {\n    return true;\n  }\n  bool parse_object_start() {\n    return true;\n  }\n  template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &) {\n    return _parse(*this, in);\n  }\n\nprivate:\n  null_parse_context(const null_parse_context &);\n  null_parse_context &operator=(const null_parse_context &);\n};\n\n// obsolete, use the version below\ntemplate <typename Iter> inline std::string parse(value &out, Iter &pos, const Iter &last) {\n  std::string err;\n  pos = parse(out, pos, last, &err);\n  return err;\n}\n\ntemplate <typename Context, typename Iter> inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) {\n  input<Iter> in(first, last);\n  if (!_parse(ctx, in) && err != NULL) {\n    char buf[64];\n    SNPRINTF(buf, sizeof(buf), \"syntax error at line %d near: \", in.line());\n    *err = buf;\n    while (1) {\n      int ch = in.getc();\n      if (ch == -1 || ch == '\\n') {\n        break;\n      } else if (ch >= ' ') {\n        err->push_back(static_cast<char>(ch));\n      }\n    }\n  }\n  return in.cur();\n}\n\ntemplate <typename Iter> inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) {\n  default_parse_context ctx(&out);\n  return _parse(ctx, first, last, err);\n}\n\ninline std::string parse(value &out, const std::string &s) {\n  std::string err;\n  parse(out, s.begin(), s.end(), &err);\n  return err;\n}\n\ninline std::string parse(value &out, std::istream &is) {\n  std::string err;\n  parse(out, std::istreambuf_iterator<char>(is.rdbuf()), std::istreambuf_iterator<char>(), &err);\n  return err;\n}\n\ntemplate <typename T> struct last_error_t { static std::string s; };\ntemplate <typename T> std::string last_error_t<T>::s;\n\ninline void set_last_error(const std::string &s) {\n  last_error_t<bool>::s = s;\n}\n\ninline const std::string &get_last_error() {\n  return last_error_t<bool>::s;\n}\n\ninline bool operator==(const value &x, const value &y) {\n  if (x.is<null>())\n    return y.is<null>();\n#define PICOJSON_CMP(type)                                                                                                         \\\n  if (x.is<type>())                                                                                                                \\\n  return y.is<type>() && x.get<type>() == y.get<type>()\n  PICOJSON_CMP(bool);\n  PICOJSON_CMP(double);\n  PICOJSON_CMP(std::string);\n  PICOJSON_CMP(array);\n  PICOJSON_CMP(object);\n#undef PICOJSON_CMP\n  PICOJSON_ASSERT(0);\n#ifdef _MSC_VER\n  __assume(0);\n#endif\n  return false;\n}\n\ninline bool operator!=(const value &x, const value &y) {\n  return !(x == y);\n}\n}\n\n#if !PICOJSON_USE_RVALUE_REFERENCE\nnamespace std {\ntemplate <> inline void swap(picojson::value &x, picojson::value &y) {\n  x.swap(y);\n}\n}\n#endif\n\ninline std::istream &operator>>(std::istream &is, picojson::value &x) {\n  picojson::set_last_error(std::string());\n  const std::string err(picojson::parse(x, is));\n  if (!err.empty()) {\n    picojson::set_last_error(err);\n    is.setstate(std::ios::failbit);\n  }\n  return is;\n}\n\ninline std::ostream &operator<<(std::ostream &os, const picojson::value &x) {\n  x.serialize(std::ostream_iterator<char>(os));\n  return os;\n}\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/nvEncodeAPI.h",
    "content": "/*\n * This copyright notice applies to this header file only:\n *\n * Copyright (c) 2010-2022 NVIDIA Corporation\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the software, and to permit persons to whom the\n * software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/**\n * \\file nvEncodeAPI.h\n *   NVIDIA GPUs - beginning with the Kepler generation - contain a hardware-based encoder\n *   (referred to as NVENC) which provides fully-accelerated hardware-based video encoding.\n *   NvEncodeAPI provides the interface for NVIDIA video encoder (NVENC).\n * \\date 2011-2022\n *  This file contains the interface constants, structure definitions and function prototypes.\n */\n\n#ifndef _NV_ENCODEAPI_H_\n#define _NV_ENCODEAPI_H_\n\n#include <stdlib.h>\n\n#ifdef _WIN32\n#include <windows.h>\n#endif\n\n#ifdef _MSC_VER\n#ifndef _STDINT\ntypedef __int32 int32_t;\ntypedef unsigned __int32 uint32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\ntypedef signed char int8_t;\ntypedef unsigned char uint8_t;\ntypedef short int16_t;\ntypedef unsigned short uint16_t;\n#endif\n#else\n#include <stdint.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * \\addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures\n * @{\n */\n\n#ifdef _WIN32\n#define NVENCAPI __stdcall\ntypedef RECT NVENC_RECT;\n#else\n#define NVENCAPI\n// =========================================================================================\n#ifndef GUID_DEFINED\n#define GUID_DEFINED\n/*!\n * \\struct GUID\n * Abstracts the GUID structure for non-windows platforms.\n */\n// =========================================================================================\ntypedef struct _GUID {\n    uint32_t Data1; /**< [in]: Specifies the first 8 hexadecimal digits of the GUID. */\n    uint16_t Data2; /**< [in]: Specifies the first group of 4 hexadecimal digits. */\n    uint16_t Data3; /**< [in]: Specifies the second group of 4 hexadecimal digits. */\n    uint8_t Data4[8]; /**< [in]: Array of 8 bytes. The first 2 bytes contain the third group of 4\n                         hexadecimal digits. The remaining 6 bytes contain the final 12 hexadecimal\n                         digits.                       */\n} GUID, *LPGUID;\n#endif // GUID\n\n/**\n * \\struct _NVENC_RECT\n * Defines a Rectangle. Used in ::NV_ENC_PREPROCESS_FRAME.\n */\ntypedef struct _NVENC_RECT {\n    uint32_t left; /**< [in]: X coordinate of the upper left corner of rectangular area to be\n                      specified.       */\n    uint32_t top; /**< [in]: Y coordinate of the upper left corner of the rectangular area to be\n                     specified.   */\n    uint32_t right; /**< [in]: X coordinate of the bottom right corner of the rectangular area to be\n                       specified. */\n    uint32_t bottom; /**< [in]: Y coordinate of the bottom right corner of the rectangular area to\n                        be specified. */\n} NVENC_RECT;\n\n#endif // _WIN32\n\n/** @} */ /* End of GUID and NVENC_RECT structure grouping*/\n\ntypedef void* NV_ENC_INPUT_PTR; /**< NVENCODE API input buffer                              */\ntypedef void* NV_ENC_OUTPUT_PTR; /**< NVENCODE API output buffer*/\ntypedef void* NV_ENC_REGISTERED_PTR; /**< A Resource that has been registered with NVENCODE API*/\ntypedef void* NV_ENC_CUSTREAM_PTR; /**< Pointer to CUstream*/\n\n#define NVENCAPI_MAJOR_VERSION 12\n#define NVENCAPI_MINOR_VERSION 0\n\n#define NVENCAPI_VERSION (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24))\n\n/**\n * Macro to generate per-structure version for use with API.\n */\n#define NVENCAPI_STRUCT_VERSION(ver) ((uint32_t)NVENCAPI_VERSION | ((ver) << 16) | (0x7 << 28))\n\n#define NVENC_INFINITE_GOPLENGTH 0xffffffff\n\n#define NV_MAX_SEQ_HDR_LEN (512)\n\n#ifdef __GNUC__\n#define NV_ENC_DEPRECATED                                                                          \\\n    __attribute__((deprecated(\"WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK VERSION\")))\n#elif defined(_MSC_VER)\n#define NV_ENC_DEPRECATED                                                                          \\\n    __declspec(deprecated(\"WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK VERSION\"))\n#endif\n\n// =========================================================================================\n// Encode Codec GUIDS supported by the NvEncodeAPI interface.\n// =========================================================================================\n\n// {6BC82762-4E63-4ca4-AA85-1E50F321F6BF}\nstatic const GUID NV_ENC_CODEC_H264_GUID\n    = { 0x6bc82762, 0x4e63, 0x4ca4, { 0xaa, 0x85, 0x1e, 0x50, 0xf3, 0x21, 0xf6, 0xbf } };\n\n// {790CDC88-4522-4d7b-9425-BDA9975F7603}\nstatic const GUID NV_ENC_CODEC_HEVC_GUID\n    = { 0x790cdc88, 0x4522, 0x4d7b, { 0x94, 0x25, 0xbd, 0xa9, 0x97, 0x5f, 0x76, 0x3 } };\n\n// {0A352289-0AA7-4759-862D-5D15CD16D254}\nstatic const GUID NV_ENC_CODEC_AV1_GUID\n    = { 0x0a352289, 0x0aa7, 0x4759, { 0x86, 0x2d, 0x5d, 0x15, 0xcd, 0x16, 0xd2, 0x54 } };\n\n// =========================================================================================\n// *   Encode Profile GUIDS supported by the NvEncodeAPI interface.\n// =========================================================================================\n\n// {BFD6F8E7-233C-4341-8B3E-4818523803F4}\nstatic const GUID NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID\n    = { 0xbfd6f8e7, 0x233c, 0x4341, { 0x8b, 0x3e, 0x48, 0x18, 0x52, 0x38, 0x3, 0xf4 } };\n\n// {0727BCAA-78C4-4c83-8C2F-EF3DFF267C6A}\nstatic const GUID NV_ENC_H264_PROFILE_BASELINE_GUID\n    = { 0x727bcaa, 0x78c4, 0x4c83, { 0x8c, 0x2f, 0xef, 0x3d, 0xff, 0x26, 0x7c, 0x6a } };\n\n// {60B5C1D4-67FE-4790-94D5-C4726D7B6E6D}\nstatic const GUID NV_ENC_H264_PROFILE_MAIN_GUID\n    = { 0x60b5c1d4, 0x67fe, 0x4790, { 0x94, 0xd5, 0xc4, 0x72, 0x6d, 0x7b, 0x6e, 0x6d } };\n\n// {E7CBC309-4F7A-4b89-AF2A-D537C92BE310}\nstatic const GUID NV_ENC_H264_PROFILE_HIGH_GUID\n    = { 0xe7cbc309, 0x4f7a, 0x4b89, { 0xaf, 0x2a, 0xd5, 0x37, 0xc9, 0x2b, 0xe3, 0x10 } };\n\n// {7AC663CB-A598-4960-B844-339B261A7D52}\nstatic const GUID NV_ENC_H264_PROFILE_HIGH_444_GUID\n    = { 0x7ac663cb, 0xa598, 0x4960, { 0xb8, 0x44, 0x33, 0x9b, 0x26, 0x1a, 0x7d, 0x52 } };\n\n// {40847BF5-33F7-4601-9084-E8FE3C1DB8B7}\nstatic const GUID NV_ENC_H264_PROFILE_STEREO_GUID\n    = { 0x40847bf5, 0x33f7, 0x4601, { 0x90, 0x84, 0xe8, 0xfe, 0x3c, 0x1d, 0xb8, 0xb7 } };\n\n// {B405AFAC-F32B-417B-89C4-9ABEED3E5978}\nstatic const GUID NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID\n    = { 0xb405afac, 0xf32b, 0x417b, { 0x89, 0xc4, 0x9a, 0xbe, 0xed, 0x3e, 0x59, 0x78 } };\n\n// {AEC1BD87-E85B-48f2-84C3-98BCA6285072}\nstatic const GUID NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID\n    = { 0xaec1bd87, 0xe85b, 0x48f2, { 0x84, 0xc3, 0x98, 0xbc, 0xa6, 0x28, 0x50, 0x72 } };\n\n// {B514C39A-B55B-40fa-878F-F1253B4DFDEC}\nstatic const GUID NV_ENC_HEVC_PROFILE_MAIN_GUID\n    = { 0xb514c39a, 0xb55b, 0x40fa, { 0x87, 0x8f, 0xf1, 0x25, 0x3b, 0x4d, 0xfd, 0xec } };\n\n// {fa4d2b6c-3a5b-411a-8018-0a3f5e3c9be5}\nstatic const GUID NV_ENC_HEVC_PROFILE_MAIN10_GUID\n    = { 0xfa4d2b6c, 0x3a5b, 0x411a, { 0x80, 0x18, 0x0a, 0x3f, 0x5e, 0x3c, 0x9b, 0xe5 } };\n\n// For HEVC Main 444 8 bit and HEVC Main 444 10 bit profiles only\n// {51ec32b5-1b4c-453c-9cbd-b616bd621341}\nstatic const GUID NV_ENC_HEVC_PROFILE_FREXT_GUID\n    = { 0x51ec32b5, 0x1b4c, 0x453c, { 0x9c, 0xbd, 0xb6, 0x16, 0xbd, 0x62, 0x13, 0x41 } };\n\n// {5f2a39f5-f14e-4f95-9a9e-b76d568fcf97}\nstatic const GUID NV_ENC_AV1_PROFILE_MAIN_GUID\n    = { 0x5f2a39f5, 0xf14e, 0x4f95, { 0x9a, 0x9e, 0xb7, 0x6d, 0x56, 0x8f, 0xcf, 0x97 } };\n\n// =========================================================================================\n// *   Preset GUIDS supported by the NvEncodeAPI interface.\n// =========================================================================================\n// {B2DFB705-4EBD-4C49-9B5F-24A777D3E587}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_DEFAULT_GUID\n    = { 0xb2dfb705, 0x4ebd, 0x4c49, { 0x9b, 0x5f, 0x24, 0xa7, 0x77, 0xd3, 0xe5, 0x87 } };\n\n// {60E4C59F-E846-4484-A56D-CD45BE9FDDF6}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HP_GUID\n    = { 0x60e4c59f, 0xe846, 0x4484, { 0xa5, 0x6d, 0xcd, 0x45, 0xbe, 0x9f, 0xdd, 0xf6 } };\n\n// {34DBA71D-A77B-4B8F-9C3E-B6D5DA24C012}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HQ_GUID\n    = { 0x34dba71d, 0xa77b, 0x4b8f, { 0x9c, 0x3e, 0xb6, 0xd5, 0xda, 0x24, 0xc0, 0x12 } };\n\n// {82E3E450-BDBB-4e40-989C-82A90DF9EF32}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_BD_GUID\n    = { 0x82e3e450, 0xbdbb, 0x4e40, { 0x98, 0x9c, 0x82, 0xa9, 0xd, 0xf9, 0xef, 0x32 } };\n\n// {49DF21C5-6DFA-4feb-9787-6ACC9EFFB726}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID\n    = { 0x49df21c5, 0x6dfa, 0x4feb, { 0x97, 0x87, 0x6a, 0xcc, 0x9e, 0xff, 0xb7, 0x26 } };\n\n// {C5F733B9-EA97-4cf9-BEC2-BF78A74FD105}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HQ_GUID\n    = { 0xc5f733b9, 0xea97, 0x4cf9, { 0xbe, 0xc2, 0xbf, 0x78, 0xa7, 0x4f, 0xd1, 0x5 } };\n\n// {67082A44-4BAD-48FA-98EA-93056D150A58}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HP_GUID\n    = { 0x67082a44, 0x4bad, 0x48fa, { 0x98, 0xea, 0x93, 0x5, 0x6d, 0x15, 0xa, 0x58 } };\n\n// {D5BFB716-C604-44e7-9BB8-DEA5510FC3AC}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID\n    = { 0xd5bfb716, 0xc604, 0x44e7, { 0x9b, 0xb8, 0xde, 0xa5, 0x51, 0xf, 0xc3, 0xac } };\n\n// {149998E7-2364-411d-82EF-179888093409}\nNV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_HP_GUID\n    = { 0x149998e7, 0x2364, 0x411d, { 0x82, 0xef, 0x17, 0x98, 0x88, 0x9, 0x34, 0x9 } };\n\n// Performance degrades and quality improves as we move from P1 to P7. Presets P3 to P7 for H264 and\n// Presets P2 to P7 for HEVC have B frames enabled by default for HIGH_QUALITY and LOSSLESS tuning\n// info, and will not work with Weighted Prediction enabled. In case Weighted Prediction is\n// required, disable B frames by setting frameIntervalP = 1 {FC0A8D3E-45F8-4CF8-80C7-298871590EBF}\nstatic const GUID NV_ENC_PRESET_P1_GUID\n    = { 0xfc0a8d3e, 0x45f8, 0x4cf8, { 0x80, 0xc7, 0x29, 0x88, 0x71, 0x59, 0xe, 0xbf } };\n\n// {F581CFB8-88D6-4381-93F0-DF13F9C27DAB}\nstatic const GUID NV_ENC_PRESET_P2_GUID\n    = { 0xf581cfb8, 0x88d6, 0x4381, { 0x93, 0xf0, 0xdf, 0x13, 0xf9, 0xc2, 0x7d, 0xab } };\n\n// {36850110-3A07-441F-94D5-3670631F91F6}\nstatic const GUID NV_ENC_PRESET_P3_GUID\n    = { 0x36850110, 0x3a07, 0x441f, { 0x94, 0xd5, 0x36, 0x70, 0x63, 0x1f, 0x91, 0xf6 } };\n\n// {90A7B826-DF06-4862-B9D2-CD6D73A08681}\nstatic const GUID NV_ENC_PRESET_P4_GUID\n    = { 0x90a7b826, 0xdf06, 0x4862, { 0xb9, 0xd2, 0xcd, 0x6d, 0x73, 0xa0, 0x86, 0x81 } };\n\n// {21C6E6B4-297A-4CBA-998F-B6CBDE72ADE3}\nstatic const GUID NV_ENC_PRESET_P5_GUID\n    = { 0x21c6e6b4, 0x297a, 0x4cba, { 0x99, 0x8f, 0xb6, 0xcb, 0xde, 0x72, 0xad, 0xe3 } };\n\n// {8E75C279-6299-4AB6-8302-0B215A335CF5}\nstatic const GUID NV_ENC_PRESET_P6_GUID\n    = { 0x8e75c279, 0x6299, 0x4ab6, { 0x83, 0x2, 0xb, 0x21, 0x5a, 0x33, 0x5c, 0xf5 } };\n\n// {84848C12-6F71-4C13-931B-53E283F57974}\nstatic const GUID NV_ENC_PRESET_P7_GUID\n    = { 0x84848c12, 0x6f71, 0x4c13, { 0x93, 0x1b, 0x53, 0xe2, 0x83, 0xf5, 0x79, 0x74 } };\n\n/**\n * \\addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures\n * @{\n */\n\n/**\n * Input frame encode modes\n */\ntypedef enum _NV_ENC_PARAMS_FRAME_FIELD_MODE {\n    NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME = 0x01, /**< Frame mode */\n    NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD = 0x02, /**< Field mode */\n    NV_ENC_PARAMS_FRAME_FIELD_MODE_MBAFF = 0x03 /**< MB adaptive frame/field */\n} NV_ENC_PARAMS_FRAME_FIELD_MODE;\n\n/**\n * Rate Control Modes\n */\ntypedef enum _NV_ENC_PARAMS_RC_MODE {\n    NV_ENC_PARAMS_RC_CONSTQP = 0x0, /**< Constant QP mode */\n    NV_ENC_PARAMS_RC_VBR = 0x1, /**< Variable bitrate mode */\n    NV_ENC_PARAMS_RC_CBR = 0x2, /**< Constant bitrate mode */\n    NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ\n    = 0x8, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION /\n              NV_ENC_TWO_PASS_FULL_RESOLUTION + lowDelayKeyFrameScale=1 */\n    NV_ENC_PARAMS_RC_CBR_HQ\n    = 0x10, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION /\n               NV_ENC_TWO_PASS_FULL_RESOLUTION */\n    NV_ENC_PARAMS_RC_VBR_HQ\n    = 0x20 /**< Deprecated, use NV_ENC_PARAMS_RC_VBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION /\n              NV_ENC_TWO_PASS_FULL_RESOLUTION */\n} NV_ENC_PARAMS_RC_MODE;\n\n/**\n * Multi Pass encoding\n */\ntypedef enum _NV_ENC_MULTI_PASS {\n    NV_ENC_MULTI_PASS_DISABLED = 0x0, /**< Single Pass */\n    NV_ENC_TWO_PASS_QUARTER_RESOLUTION\n    = 0x1, /**< Two Pass encoding is enabled where first Pass is quarter resolution */\n    NV_ENC_TWO_PASS_FULL_RESOLUTION\n    = 0x2, /**< Two Pass encoding is enabled where first Pass is full resolution */\n} NV_ENC_MULTI_PASS;\n\n/**\n * Emphasis Levels\n */\ntypedef enum _NV_ENC_EMPHASIS_MAP_LEVEL {\n    NV_ENC_EMPHASIS_MAP_LEVEL_0 = 0x0, /**< Emphasis Map Level 0, for zero Delta QP value */\n    NV_ENC_EMPHASIS_MAP_LEVEL_1 = 0x1, /**< Emphasis Map Level 1, for very low Delta QP value */\n    NV_ENC_EMPHASIS_MAP_LEVEL_2 = 0x2, /**< Emphasis Map Level 2, for low Delta QP value */\n    NV_ENC_EMPHASIS_MAP_LEVEL_3 = 0x3, /**< Emphasis Map Level 3, for medium Delta QP value */\n    NV_ENC_EMPHASIS_MAP_LEVEL_4 = 0x4, /**< Emphasis Map Level 4, for high Delta QP value */\n    NV_ENC_EMPHASIS_MAP_LEVEL_5 = 0x5 /**< Emphasis Map Level 5, for very high Delta QP value */\n} NV_ENC_EMPHASIS_MAP_LEVEL;\n\n/**\n * QP MAP MODE\n */\ntypedef enum _NV_ENC_QP_MAP_MODE {\n    NV_ENC_QP_MAP_DISABLED = 0x0, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap have no effect. */\n    NV_ENC_QP_MAP_EMPHASIS = 0x1, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as\n                                     Emphasis level. Currently this is only supported for H264 */\n    NV_ENC_QP_MAP_DELTA\n    = 0x2, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP delta map. */\n    NV_ENC_QP_MAP = 0x3, /**< Currently This is not supported. Value in\n                            NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP value.   */\n} NV_ENC_QP_MAP_MODE;\n\n#define NV_ENC_PARAMS_RC_VBR_MINQP (NV_ENC_PARAMS_RC_MODE)0x4 /**< Deprecated */\n#define NV_ENC_PARAMS_RC_2_PASS_QUALITY NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ /**< Deprecated */\n#define NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP NV_ENC_PARAMS_RC_CBR_HQ /**< Deprecated */\n#define NV_ENC_PARAMS_RC_2_PASS_VBR NV_ENC_PARAMS_RC_VBR_HQ /**< Deprecated */\n#define NV_ENC_PARAMS_RC_CBR2 NV_ENC_PARAMS_RC_CBR /**< Deprecated */\n\n/**\n * Input picture structure\n */\ntypedef enum _NV_ENC_PIC_STRUCT {\n    NV_ENC_PIC_STRUCT_FRAME = 0x01, /**< Progressive frame */\n    NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM = 0x02, /**< Field encoding top field first */\n    NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP = 0x03 /**< Field encoding bottom field first */\n} NV_ENC_PIC_STRUCT;\n\n/**\n * Display picture structure\n * Currently, this enum is only used for deciding the number of clock timestamp sets in Picture\n * Timing SEI / Time Code SEI Otherwise, this has no impact on encoder behavior\n */\ntypedef enum _NV_ENC_DISPLAY_PIC_STRUCT {\n    NV_ENC_PIC_STRUCT_DISPLAY_FRAME = 0x00, /**< Field encoding top field first */\n    NV_ENC_PIC_STRUCT_DISPLAY_FIELD_TOP_BOTTOM = 0x01, /**< Field encoding top field first */\n    NV_ENC_PIC_STRUCT_DISPLAY_FIELD_BOTTOM_TOP = 0x02, /**< Field encoding bottom field first */\n    NV_ENC_PIC_STRUCT_DISPLAY_FRAME_DOUBLING = 0x03, /**< Frame doubling */\n    NV_ENC_PIC_STRUCT_DISPLAY_FRAME_TRIPLING = 0x04 /**< Field tripling */\n} NV_ENC_DISPLAY_PIC_STRUCT;\n\n/**\n * Input picture type\n */\ntypedef enum _NV_ENC_PIC_TYPE {\n    NV_ENC_PIC_TYPE_P = 0x0, /**< Forward predicted */\n    NV_ENC_PIC_TYPE_B = 0x01, /**< Bi-directionally predicted picture */\n    NV_ENC_PIC_TYPE_I = 0x02, /**< Intra predicted picture */\n    NV_ENC_PIC_TYPE_IDR = 0x03, /**< IDR picture */\n    NV_ENC_PIC_TYPE_BI = 0x04, /**< Bi-directionally predicted with only Intra MBs */\n    NV_ENC_PIC_TYPE_SKIPPED = 0x05, /**< Picture is skipped */\n    NV_ENC_PIC_TYPE_INTRA_REFRESH = 0x06, /**< First picture in intra refresh cycle */\n    NV_ENC_PIC_TYPE_NONREF_P = 0x07, /**< Non reference P picture */\n    NV_ENC_PIC_TYPE_UNKNOWN = 0xFF /**< Picture type unknown */\n} NV_ENC_PIC_TYPE;\n\n/**\n * Motion vector precisions\n */\ntypedef enum _NV_ENC_MV_PRECISION {\n    NV_ENC_MV_PRECISION_DEFAULT\n    = 0x0, /**< Driver selects Quarter-Pel motion vector precision by default */\n    NV_ENC_MV_PRECISION_FULL_PEL = 0x01, /**< Full-Pel motion vector precision */\n    NV_ENC_MV_PRECISION_HALF_PEL = 0x02, /**< Half-Pel motion vector precision */\n    NV_ENC_MV_PRECISION_QUARTER_PEL = 0x03 /**< Quarter-Pel motion vector precision */\n} NV_ENC_MV_PRECISION;\n\n/**\n * Input buffer formats\n */\ntypedef enum _NV_ENC_BUFFER_FORMAT {\n    NV_ENC_BUFFER_FORMAT_UNDEFINED = 0x00000000, /**< Undefined buffer format */\n\n    NV_ENC_BUFFER_FORMAT_NV12\n    = 0x00000001, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */\n    NV_ENC_BUFFER_FORMAT_YV12 = 0x00000010, /**< Planar YUV [Y plane followed by V and U planes] */\n    NV_ENC_BUFFER_FORMAT_IYUV = 0x00000100, /**< Planar YUV [Y plane followed by U and V planes] */\n    NV_ENC_BUFFER_FORMAT_YUV444\n    = 0x00001000, /**< Planar YUV [Y plane followed by U and V planes] */\n    NV_ENC_BUFFER_FORMAT_YUV420_10BIT\n    = 0x00010000, /**< 10 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Each pixel\n                     of size 2 bytes. Most Significant 10 bits contain pixel data. */\n    NV_ENC_BUFFER_FORMAT_YUV444_10BIT\n    = 0x00100000, /**< 10 bit Planar YUV444 [Y plane followed by U and V planes]. Each pixel of size\n                     2 bytes. Most Significant 10 bits contain pixel data.  */\n    NV_ENC_BUFFER_FORMAT_ARGB\n    = 0x01000000, /**< 8 bit Packed A8R8G8B8. This is a word-ordered format\n                       where a pixel is represented by a 32-bit word with B\n                       in the lowest 8 bits, G in the next 8 bits, R in the\n                       8 bits after that and A in the highest 8 bits. */\n    NV_ENC_BUFFER_FORMAT_ARGB10\n    = 0x02000000, /**< 10 bit Packed A2R10G10B10. This is a word-ordered format\n                       where a pixel is represented by a 32-bit word with B\n                       in the lowest 10 bits, G in the next 10 bits, R in the\n                       10 bits after that and A in the highest 2 bits. */\n    NV_ENC_BUFFER_FORMAT_AYUV\n    = 0x04000000, /**< 8 bit Packed A8Y8U8V8. This is a word-ordered format\n                       where a pixel is represented by a 32-bit word with V\n                       in the lowest 8 bits, U in the next 8 bits, Y in the\n                       8 bits after that and A in the highest 8 bits. */\n    NV_ENC_BUFFER_FORMAT_ABGR\n    = 0x10000000, /**< 8 bit Packed A8B8G8R8. This is a word-ordered format\n                       where a pixel is represented by a 32-bit word with R\n                       in the lowest 8 bits, G in the next 8 bits, B in the\n                       8 bits after that and A in the highest 8 bits. */\n    NV_ENC_BUFFER_FORMAT_ABGR10\n    = 0x20000000, /**< 10 bit Packed A2B10G10R10. This is a word-ordered format\n                       where a pixel is represented by a 32-bit word with R\n                       in the lowest 10 bits, G in the next 10 bits, B in the\n                       10 bits after that and A in the highest 2 bits. */\n    NV_ENC_BUFFER_FORMAT_U8\n    = 0x40000000, /**< Buffer format representing one-dimensional buffer.\n                       This format should be used only when registering the\n                       resource as output buffer, which will be used to write\n                       the encoded bit stream or H.264 ME only mode output. */\n} NV_ENC_BUFFER_FORMAT;\n\n#define NV_ENC_BUFFER_FORMAT_NV12_PL NV_ENC_BUFFER_FORMAT_NV12\n#define NV_ENC_BUFFER_FORMAT_YV12_PL NV_ENC_BUFFER_FORMAT_YV12\n#define NV_ENC_BUFFER_FORMAT_IYUV_PL NV_ENC_BUFFER_FORMAT_IYUV\n#define NV_ENC_BUFFER_FORMAT_YUV444_PL NV_ENC_BUFFER_FORMAT_YUV444\n\n/**\n * Encoding levels\n */\ntypedef enum _NV_ENC_LEVEL {\n    NV_ENC_LEVEL_AUTOSELECT = 0,\n\n    NV_ENC_LEVEL_H264_1 = 10,\n    NV_ENC_LEVEL_H264_1b = 9,\n    NV_ENC_LEVEL_H264_11 = 11,\n    NV_ENC_LEVEL_H264_12 = 12,\n    NV_ENC_LEVEL_H264_13 = 13,\n    NV_ENC_LEVEL_H264_2 = 20,\n    NV_ENC_LEVEL_H264_21 = 21,\n    NV_ENC_LEVEL_H264_22 = 22,\n    NV_ENC_LEVEL_H264_3 = 30,\n    NV_ENC_LEVEL_H264_31 = 31,\n    NV_ENC_LEVEL_H264_32 = 32,\n    NV_ENC_LEVEL_H264_4 = 40,\n    NV_ENC_LEVEL_H264_41 = 41,\n    NV_ENC_LEVEL_H264_42 = 42,\n    NV_ENC_LEVEL_H264_5 = 50,\n    NV_ENC_LEVEL_H264_51 = 51,\n    NV_ENC_LEVEL_H264_52 = 52,\n    NV_ENC_LEVEL_H264_60 = 60,\n    NV_ENC_LEVEL_H264_61 = 61,\n    NV_ENC_LEVEL_H264_62 = 62,\n\n    NV_ENC_LEVEL_HEVC_1 = 30,\n    NV_ENC_LEVEL_HEVC_2 = 60,\n    NV_ENC_LEVEL_HEVC_21 = 63,\n    NV_ENC_LEVEL_HEVC_3 = 90,\n    NV_ENC_LEVEL_HEVC_31 = 93,\n    NV_ENC_LEVEL_HEVC_4 = 120,\n    NV_ENC_LEVEL_HEVC_41 = 123,\n    NV_ENC_LEVEL_HEVC_5 = 150,\n    NV_ENC_LEVEL_HEVC_51 = 153,\n    NV_ENC_LEVEL_HEVC_52 = 156,\n    NV_ENC_LEVEL_HEVC_6 = 180,\n    NV_ENC_LEVEL_HEVC_61 = 183,\n    NV_ENC_LEVEL_HEVC_62 = 186,\n\n    NV_ENC_TIER_HEVC_MAIN = 0,\n    NV_ENC_TIER_HEVC_HIGH = 1,\n\n    NV_ENC_LEVEL_AV1_2 = 0,\n    NV_ENC_LEVEL_AV1_21 = 1,\n    NV_ENC_LEVEL_AV1_22 = 2,\n    NV_ENC_LEVEL_AV1_23 = 3,\n    NV_ENC_LEVEL_AV1_3 = 4,\n    NV_ENC_LEVEL_AV1_31 = 5,\n    NV_ENC_LEVEL_AV1_32 = 6,\n    NV_ENC_LEVEL_AV1_33 = 7,\n    NV_ENC_LEVEL_AV1_4 = 8,\n    NV_ENC_LEVEL_AV1_41 = 9,\n    NV_ENC_LEVEL_AV1_42 = 10,\n    NV_ENC_LEVEL_AV1_43 = 11,\n    NV_ENC_LEVEL_AV1_5 = 12,\n    NV_ENC_LEVEL_AV1_51 = 13,\n    NV_ENC_LEVEL_AV1_52 = 14,\n    NV_ENC_LEVEL_AV1_53 = 15,\n    NV_ENC_LEVEL_AV1_6 = 16,\n    NV_ENC_LEVEL_AV1_61 = 17,\n    NV_ENC_LEVEL_AV1_62 = 18,\n    NV_ENC_LEVEL_AV1_63 = 19,\n    NV_ENC_LEVEL_AV1_7 = 20,\n    NV_ENC_LEVEL_AV1_71 = 21,\n    NV_ENC_LEVEL_AV1_72 = 22,\n    NV_ENC_LEVEL_AV1_73 = 23,\n    NV_ENC_LEVEL_AV1_AUTOSELECT,\n\n    NV_ENC_TIER_AV1_0 = 0,\n    NV_ENC_TIER_AV1_1 = 1\n} NV_ENC_LEVEL;\n\n/**\n * Error Codes\n */\ntypedef enum _NVENCSTATUS {\n    /**\n     * This indicates that API call returned with no errors.\n     */\n    NV_ENC_SUCCESS,\n\n    /**\n     * This indicates that no encode capable devices were detected.\n     */\n    NV_ENC_ERR_NO_ENCODE_DEVICE,\n\n    /**\n     * This indicates that devices pass by the client is not supported.\n     */\n    NV_ENC_ERR_UNSUPPORTED_DEVICE,\n\n    /**\n     * This indicates that the encoder device supplied by the client is not\n     * valid.\n     */\n    NV_ENC_ERR_INVALID_ENCODERDEVICE,\n\n    /**\n     * This indicates that device passed to the API call is invalid.\n     */\n    NV_ENC_ERR_INVALID_DEVICE,\n\n    /**\n     * This indicates that device passed to the API call is no longer available and\n     * needs to be reinitialized. The clients need to destroy the current encoder\n     * session by freeing the allocated input output buffers and destroying the device\n     * and create a new encoding session.\n     */\n    NV_ENC_ERR_DEVICE_NOT_EXIST,\n\n    /**\n     * This indicates that one or more of the pointers passed to the API call\n     * is invalid.\n     */\n    NV_ENC_ERR_INVALID_PTR,\n\n    /**\n     * This indicates that completion event passed in ::NvEncEncodePicture() call\n     * is invalid.\n     */\n    NV_ENC_ERR_INVALID_EVENT,\n\n    /**\n     * This indicates that one or more of the parameter passed to the API call\n     * is invalid.\n     */\n    NV_ENC_ERR_INVALID_PARAM,\n\n    /**\n     * This indicates that an API call was made in wrong sequence/order.\n     */\n    NV_ENC_ERR_INVALID_CALL,\n\n    /**\n     * This indicates that the API call failed because it was unable to allocate\n     * enough memory to perform the requested operation.\n     */\n    NV_ENC_ERR_OUT_OF_MEMORY,\n\n    /**\n     * This indicates that the encoder has not been initialized with\n     * ::NvEncInitializeEncoder() or that initialization has failed.\n     * The client cannot allocate input or output buffers or do any encoding\n     * related operation before successfully initializing the encoder.\n     */\n    NV_ENC_ERR_ENCODER_NOT_INITIALIZED,\n\n    /**\n     * This indicates that an unsupported parameter was passed by the client.\n     */\n    NV_ENC_ERR_UNSUPPORTED_PARAM,\n\n    /**\n     * This indicates that the ::NvEncLockBitstream() failed to lock the output\n     * buffer. This happens when the client makes a non blocking lock call to\n     * access the output bitstream by passing NV_ENC_LOCK_BITSTREAM::doNotWait flag.\n     * This is not a fatal error and client should retry the same operation after\n     * few milliseconds.\n     */\n    NV_ENC_ERR_LOCK_BUSY,\n\n    /**\n     * This indicates that the size of the user buffer passed by the client is\n     * insufficient for the requested operation.\n     */\n    NV_ENC_ERR_NOT_ENOUGH_BUFFER,\n\n    /**\n     * This indicates that an invalid struct version was used by the client.\n     */\n    NV_ENC_ERR_INVALID_VERSION,\n\n    /**\n     * This indicates that ::NvEncMapInputResource() API failed to map the client\n     * provided input resource.\n     */\n    NV_ENC_ERR_MAP_FAILED,\n\n    /**\n     * This indicates encode driver requires more input buffers to produce an output\n     * bitstream. If this error is returned from ::NvEncEncodePicture() API, this\n     * is not a fatal error. If the client is encoding with B frames then,\n     * ::NvEncEncodePicture() API might be buffering the input frame for re-ordering.\n     *\n     * A client operating in synchronous mode cannot call ::NvEncLockBitstream()\n     * API on the output bitstream buffer if ::NvEncEncodePicture() returned the\n     * ::NV_ENC_ERR_NEED_MORE_INPUT error code.\n     * The client must continue providing input frames until encode driver returns\n     * ::NV_ENC_SUCCESS. After receiving ::NV_ENC_SUCCESS status the client can call\n     * ::NvEncLockBitstream() API on the output buffers in the same order in which\n     * it has called ::NvEncEncodePicture().\n     */\n    NV_ENC_ERR_NEED_MORE_INPUT,\n\n    /**\n     * This indicates that the HW encoder is busy encoding and is unable to encode\n     * the input. The client should call ::NvEncEncodePicture() again after few\n     * milliseconds.\n     */\n    NV_ENC_ERR_ENCODER_BUSY,\n\n    /**\n     * This indicates that the completion event passed in ::NvEncEncodePicture()\n     * API has not been registered with encoder driver using ::NvEncRegisterAsyncEvent().\n     */\n    NV_ENC_ERR_EVENT_NOT_REGISTERD,\n\n    /**\n     * This indicates that an unknown internal error has occurred.\n     */\n    NV_ENC_ERR_GENERIC,\n\n    /**\n     * This indicates that the client is attempting to use a feature\n     * that is not available for the license type for the current system.\n     */\n    NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY,\n\n    /**\n     * This indicates that the client is attempting to use a feature\n     * that is not implemented for the current version.\n     */\n    NV_ENC_ERR_UNIMPLEMENTED,\n\n    /**\n     * This indicates that the ::NvEncRegisterResource API failed to register the resource.\n     */\n    NV_ENC_ERR_RESOURCE_REGISTER_FAILED,\n\n    /**\n     * This indicates that the client is attempting to unregister a resource\n     * that has not been successfully registered.\n     */\n    NV_ENC_ERR_RESOURCE_NOT_REGISTERED,\n\n    /**\n     * This indicates that the client is attempting to unmap a resource\n     * that has not been successfully mapped.\n     */\n    NV_ENC_ERR_RESOURCE_NOT_MAPPED,\n\n} NVENCSTATUS;\n\n/**\n * Encode Picture encode flags.\n */\ntypedef enum _NV_ENC_PIC_FLAGS {\n    NV_ENC_PIC_FLAG_FORCEINTRA = 0x1, /**< Encode the current picture as an Intra picture */\n    NV_ENC_PIC_FLAG_FORCEIDR\n    = 0x2, /**< Encode the current picture as an IDR picture.\n                This flag is only valid when Picture type decision is taken by the Encoder\n                [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */\n    NV_ENC_PIC_FLAG_OUTPUT_SPSPPS\n    = 0x4, /**< Write the sequence and picture header in encoded bitstream of the current picture */\n    NV_ENC_PIC_FLAG_EOS = 0x8, /**< Indicates end of the input stream */\n} NV_ENC_PIC_FLAGS;\n\n/**\n * Memory heap to allocate input and output buffers.\n */\ntypedef enum _NV_ENC_MEMORY_HEAP {\n    NV_ENC_MEMORY_HEAP_AUTOSELECT\n    = 0, /**< Memory heap to be decided by the encoder driver based on the usage */\n    NV_ENC_MEMORY_HEAP_VID = 1, /**< Memory heap is in local video memory */\n    NV_ENC_MEMORY_HEAP_SYSMEM_CACHED = 2, /**< Memory heap is in cached system memory */\n    NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED = 3 /**< Memory heap is in uncached system memory */\n} NV_ENC_MEMORY_HEAP;\n\n/**\n * B-frame used as reference modes\n */\ntypedef enum _NV_ENC_BFRAME_REF_MODE {\n    NV_ENC_BFRAME_REF_MODE_DISABLED = 0x0, /**< B frame is not used for reference */\n    NV_ENC_BFRAME_REF_MODE_EACH = 0x1, /**< Each B-frame will be used for reference */\n    NV_ENC_BFRAME_REF_MODE_MIDDLE\n    = 0x2, /**< Only(Number of B-frame)/2 th B-frame will be used for reference */\n} NV_ENC_BFRAME_REF_MODE;\n\n/**\n * H.264 entropy coding modes.\n */\ntypedef enum _NV_ENC_H264_ENTROPY_CODING_MODE {\n    NV_ENC_H264_ENTROPY_CODING_MODE_AUTOSELECT\n    = 0x0, /**< Entropy coding mode is auto selected by the encoder driver */\n    NV_ENC_H264_ENTROPY_CODING_MODE_CABAC = 0x1, /**< Entropy coding mode is CABAC */\n    NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC = 0x2 /**< Entropy coding mode is CAVLC */\n} NV_ENC_H264_ENTROPY_CODING_MODE;\n\n/**\n * H.264 specific BDirect modes\n */\ntypedef enum _NV_ENC_H264_BDIRECT_MODE {\n    NV_ENC_H264_BDIRECT_MODE_AUTOSELECT\n    = 0x0, /**< BDirect mode is auto selected by the encoder driver */\n    NV_ENC_H264_BDIRECT_MODE_DISABLE = 0x1, /**< Disable BDirect mode */\n    NV_ENC_H264_BDIRECT_MODE_TEMPORAL = 0x2, /**< Temporal BDirect mode */\n    NV_ENC_H264_BDIRECT_MODE_SPATIAL = 0x3 /**< Spatial BDirect mode */\n} NV_ENC_H264_BDIRECT_MODE;\n\n/**\n * H.264 specific FMO usage\n */\ntypedef enum _NV_ENC_H264_FMO_MODE {\n    NV_ENC_H264_FMO_AUTOSELECT = 0x0, /**< FMO usage is auto selected by the encoder driver */\n    NV_ENC_H264_FMO_ENABLE = 0x1, /**< Enable FMO */\n    NV_ENC_H264_FMO_DISABLE = 0x2, /**< Disable FMO */\n} NV_ENC_H264_FMO_MODE;\n\n/**\n * H.264 specific Adaptive Transform modes\n */\ntypedef enum _NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE {\n    NV_ENC_H264_ADAPTIVE_TRANSFORM_AUTOSELECT\n    = 0x0, /**< Adaptive Transform 8x8 mode is auto selected by the encoder driver*/\n    NV_ENC_H264_ADAPTIVE_TRANSFORM_DISABLE = 0x1, /**< Adaptive Transform 8x8 mode disabled */\n    NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE = 0x2, /**< Adaptive Transform 8x8 mode should be used */\n} NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE;\n\n/**\n * Stereo frame packing modes.\n */\ntypedef enum _NV_ENC_STEREO_PACKING_MODE {\n    NV_ENC_STEREO_PACKING_MODE_NONE = 0x0, /**< No Stereo packing required */\n    NV_ENC_STEREO_PACKING_MODE_CHECKERBOARD\n    = 0x1, /**< Checkerboard mode for packing stereo frames */\n    NV_ENC_STEREO_PACKING_MODE_COLINTERLEAVE\n    = 0x2, /**< Column Interleave mode for packing stereo frames */\n    NV_ENC_STEREO_PACKING_MODE_ROWINTERLEAVE\n    = 0x3, /**< Row Interleave mode for packing stereo frames */\n    NV_ENC_STEREO_PACKING_MODE_SIDEBYSIDE = 0x4, /**< Side-by-side mode for packing stereo frames */\n    NV_ENC_STEREO_PACKING_MODE_TOPBOTTOM = 0x5, /**< Top-Bottom mode for packing stereo frames */\n    NV_ENC_STEREO_PACKING_MODE_FRAMESEQ\n    = 0x6 /**< Frame Sequential mode for packing stereo frames */\n} NV_ENC_STEREO_PACKING_MODE;\n\n/**\n *  Input Resource type\n */\ntypedef enum _NV_ENC_INPUT_RESOURCE_TYPE {\n    NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX = 0x0, /**< input resource type is a directx9 surface*/\n    NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR\n    = 0x1, /**< input resource type is a cuda device pointer surface*/\n    NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY\n    = 0x2, /**< input resource type is a cuda array surface.\n                This array must be a 2D array and the CUDA_ARRAY3D_SURFACE_LDST\n                flag must have been specified when creating it. */\n    NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX = 0x3 /**< input resource type is an OpenGL texture */\n} NV_ENC_INPUT_RESOURCE_TYPE;\n\n/**\n *  Buffer usage\n */\ntypedef enum _NV_ENC_BUFFER_USAGE {\n    NV_ENC_INPUT_IMAGE = 0x0, /**< Registered surface will be used for input image */\n    NV_ENC_OUTPUT_MOTION_VECTOR\n    = 0x1, /**< Registered surface will be used for output of H.264 ME only mode.\n                This buffer usage type is not supported for HEVC ME only mode. */\n    NV_ENC_OUTPUT_BITSTREAM\n    = 0x2, /**< Registered surface will be used for output bitstream in encoding */\n} NV_ENC_BUFFER_USAGE;\n\n/**\n *  Encoder Device type\n */\ntypedef enum _NV_ENC_DEVICE_TYPE {\n    NV_ENC_DEVICE_TYPE_DIRECTX = 0x0, /**< encode device type is a directx9 device */\n    NV_ENC_DEVICE_TYPE_CUDA = 0x1, /**< encode device type is a cuda device */\n    NV_ENC_DEVICE_TYPE_OPENGL = 0x2 /**< encode device type is an OpenGL device.\n                                         Use of this device type is supported only on Linux */\n} NV_ENC_DEVICE_TYPE;\n\n/**\n * Number of reference frames\n */\ntypedef enum _NV_ENC_NUM_REF_FRAMES {\n    NV_ENC_NUM_REF_FRAMES_AUTOSELECT\n    = 0x0, /**< Number of reference frames is auto selected by the encoder driver */\n    NV_ENC_NUM_REF_FRAMES_1 = 0x1, /**< Number of reference frames equal to 1 */\n    NV_ENC_NUM_REF_FRAMES_2 = 0x2, /**< Number of reference frames equal to 2 */\n    NV_ENC_NUM_REF_FRAMES_3 = 0x3, /**< Number of reference frames equal to 3 */\n    NV_ENC_NUM_REF_FRAMES_4 = 0x4, /**< Number of reference frames equal to 4 */\n    NV_ENC_NUM_REF_FRAMES_5 = 0x5, /**< Number of reference frames equal to 5 */\n    NV_ENC_NUM_REF_FRAMES_6 = 0x6, /**< Number of reference frames equal to 6 */\n    NV_ENC_NUM_REF_FRAMES_7 = 0x7 /**< Number of reference frames equal to 7 */\n} NV_ENC_NUM_REF_FRAMES;\n\n/**\n * Encoder capabilities enumeration.\n */\ntypedef enum _NV_ENC_CAPS {\n    /**\n     * Maximum number of B-Frames supported.\n     */\n    NV_ENC_CAPS_NUM_MAX_BFRAMES,\n\n    /**\n     * Rate control modes supported.\n     * \\n The API return value is a bitmask of the values in NV_ENC_PARAMS_RC_MODE.\n     */\n    NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,\n\n    /**\n     * Indicates HW support for field mode encoding.\n     * \\n 0 : Interlaced mode encoding is not supported.\n     * \\n 1 : Interlaced field mode encoding is supported.\n     * \\n 2 : Interlaced frame encoding and field mode encoding are both supported.\n     */\n    NV_ENC_CAPS_SUPPORT_FIELD_ENCODING,\n\n    /**\n     * Indicates HW support for monochrome mode encoding.\n     * \\n 0 : Monochrome mode not supported.\n     * \\n 1 : Monochrome mode supported.\n     */\n    NV_ENC_CAPS_SUPPORT_MONOCHROME,\n\n    /**\n     * Indicates HW support for FMO.\n     * \\n 0 : FMO not supported.\n     * \\n 1 : FMO supported.\n     */\n    NV_ENC_CAPS_SUPPORT_FMO,\n\n    /**\n     * Indicates HW capability for Quarter pel motion estimation.\n     * \\n 0 : Quarter-Pel Motion Estimation not supported.\n     * \\n 1 : Quarter-Pel Motion Estimation supported.\n     */\n    NV_ENC_CAPS_SUPPORT_QPELMV,\n\n    /**\n     * H.264 specific. Indicates HW support for BDirect modes.\n     * \\n 0 : BDirect mode encoding not supported.\n     * \\n 1 : BDirect mode encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_BDIRECT_MODE,\n\n    /**\n     * H264 specific. Indicates HW support for CABAC entropy coding mode.\n     * \\n 0 : CABAC entropy coding not supported.\n     * \\n 1 : CABAC entropy coding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_CABAC,\n\n    /**\n     * Indicates HW support for Adaptive Transform.\n     * \\n 0 : Adaptive Transform not supported.\n     * \\n 1 : Adaptive Transform supported.\n     */\n    NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,\n\n    /**\n     * Indicates HW support for Multi View Coding.\n     * \\n 0 : Multi View Coding not supported.\n     * \\n 1 : Multi View Coding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_STEREO_MVC,\n\n    /**\n     * Indicates HW support for encoding Temporal layers.\n     * \\n 0 : Encoding Temporal layers not supported.\n     * \\n 1 : Encoding Temporal layers supported.\n     */\n    NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS,\n\n    /**\n     * Indicates HW support for Hierarchical P frames.\n     * \\n 0 : Hierarchical P frames not supported.\n     * \\n 1 : Hierarchical P frames supported.\n     */\n    NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,\n\n    /**\n     * Indicates HW support for Hierarchical B frames.\n     * \\n 0 : Hierarchical B frames not supported.\n     * \\n 1 : Hierarchical B frames supported.\n     */\n    NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,\n\n    /**\n     * Maximum Encoding level supported (See ::NV_ENC_LEVEL for details).\n     */\n    NV_ENC_CAPS_LEVEL_MAX,\n\n    /**\n     * Minimum Encoding level supported (See ::NV_ENC_LEVEL for details).\n     */\n    NV_ENC_CAPS_LEVEL_MIN,\n\n    /**\n     * Indicates HW support for separate colour plane encoding.\n     * \\n 0 : Separate colour plane encoding not supported.\n     * \\n 1 : Separate colour plane encoding supported.\n     */\n    NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,\n\n    /**\n     * Maximum output width supported.\n     */\n    NV_ENC_CAPS_WIDTH_MAX,\n\n    /**\n     * Maximum output height supported.\n     */\n    NV_ENC_CAPS_HEIGHT_MAX,\n\n    /**\n     * Indicates Temporal Scalability Support.\n     * \\n 0 : Temporal SVC encoding not supported.\n     * \\n 1 : Temporal SVC encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC,\n\n    /**\n     * Indicates Dynamic Encode Resolution Change Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Dynamic Encode Resolution Change not supported.\n     * \\n 1 : Dynamic Encode Resolution Change supported.\n     */\n    NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE,\n\n    /**\n     * Indicates Dynamic Encode Bitrate Change Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Dynamic Encode bitrate change not supported.\n     * \\n 1 : Dynamic Encode bitrate change supported.\n     */\n    NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,\n\n    /**\n     * Indicates Forcing Constant QP On The Fly Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Forcing constant QP on the fly not supported.\n     * \\n 1 : Forcing constant QP on the fly supported.\n     */\n    NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,\n\n    /**\n     * Indicates Dynamic rate control mode Change Support.\n     * \\n 0 : Dynamic rate control mode change not supported.\n     * \\n 1 : Dynamic rate control mode change supported.\n     */\n    NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,\n\n    /**\n     * Indicates Subframe readback support for slice-based encoding. If this feature is supported,\n     * it can be enabled by setting enableSubFrameWrite = 1. \\n 0 : Subframe readback not supported.\n     * \\n 1 : Subframe readback supported.\n     */\n    NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,\n\n    /**\n     * Indicates Constrained Encoding mode support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Constrained encoding mode not supported.\n     * \\n 1 : Constrained encoding mode supported.\n     * If this mode is supported client can enable this during initialization.\n     * Client can then force a picture to be coded as constrained picture where\n     * in-loop filtering is disabled across slice boundaries and prediction vectors for inter\n     * macroblocks in each slice will be restricted to the slice region.\n     */\n    NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,\n\n    /**\n     * Indicates Intra Refresh Mode Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Intra Refresh Mode not supported.\n     * \\n 1 : Intra Refresh Mode supported.\n     */\n    NV_ENC_CAPS_SUPPORT_INTRA_REFRESH,\n\n    /**\n     * Indicates Custom VBV Buffer Size support. It can be used for capping frame size.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Custom VBV buffer size specification from client, not supported.\n     * \\n 1 : Custom VBV buffer size specification from client, supported.\n     */\n    NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,\n\n    /**\n     * Indicates Dynamic Slice Mode Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Dynamic Slice Mode not supported.\n     * \\n 1 : Dynamic Slice Mode supported.\n     */\n    NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,\n\n    /**\n     * Indicates Reference Picture Invalidation Support.\n     * Support added from NvEncodeAPI version 2.0.\n     * \\n 0 : Reference Picture Invalidation not supported.\n     * \\n 1 : Reference Picture Invalidation supported.\n     */\n    NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,\n\n    /**\n     * Indicates support for Pre-Processing.\n     * The API return value is a bitmask of the values defined in ::NV_ENC_PREPROC_FLAGS\n     */\n    NV_ENC_CAPS_PREPROC_SUPPORT,\n\n    /**\n     * Indicates support Async mode.\n     * \\n 0 : Async Encode mode not supported.\n     * \\n 1 : Async Encode mode supported.\n     */\n    NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,\n\n    /**\n     * Maximum MBs per frame supported.\n     */\n    NV_ENC_CAPS_MB_NUM_MAX,\n\n    /**\n     * Maximum aggregate throughput in MBs per sec.\n     */\n    NV_ENC_CAPS_MB_PER_SEC_MAX,\n\n    /**\n     * Indicates HW support for YUV444 mode encoding.\n     * \\n 0 : YUV444 mode encoding not supported.\n     * \\n 1 : YUV444 mode encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_YUV444_ENCODE,\n\n    /**\n     * Indicates HW support for lossless encoding.\n     * \\n 0 : lossless encoding not supported.\n     * \\n 1 : lossless encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE,\n\n    /**\n     * Indicates HW support for Sample Adaptive Offset.\n     * \\n 0 : SAO not supported.\n     * \\n 1 : SAO encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_SAO,\n\n    /**\n     * Indicates HW support for Motion Estimation Only Mode.\n     * \\n 0 : MEOnly Mode not supported.\n     * \\n 1 : MEOnly Mode supported for I and P frames.\n     * \\n 2 : MEOnly Mode supported for I, P and B frames.\n     */\n    NV_ENC_CAPS_SUPPORT_MEONLY_MODE,\n\n    /**\n     * Indicates HW support for lookahead encoding (enableLookahead=1).\n     * \\n 0 : Lookahead not supported.\n     * \\n 1 : Lookahead supported.\n     */\n    NV_ENC_CAPS_SUPPORT_LOOKAHEAD,\n\n    /**\n     * Indicates HW support for temporal AQ encoding (enableTemporalAQ=1).\n     * \\n 0 : Temporal AQ not supported.\n     * \\n 1 : Temporal AQ supported.\n     */\n    NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ,\n    /**\n     * Indicates HW support for 10 bit encoding.\n     * \\n 0 : 10 bit encoding not supported.\n     * \\n 1 : 10 bit encoding supported.\n     */\n    NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,\n    /**\n     * Maximum number of Long Term Reference frames supported\n     */\n    NV_ENC_CAPS_NUM_MAX_LTR_FRAMES,\n\n    /**\n     * Indicates HW support for Weighted Prediction.\n     * \\n 0 : Weighted Prediction not supported.\n     * \\n 1 : Weighted Prediction supported.\n     */\n    NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,\n\n    /**\n     * On managed (vGPU) platforms (Windows only), this API, in conjunction with other GRID\n     * Management APIs, can be used to estimate the residual capacity of the hardware encoder on the\n     * GPU as a percentage of the total available encoder capacity. This API can be called at any\n     * time; i.e. during the encode session or before opening the encode session. If the available\n     * encoder capacity is returned as zero, applications may choose to switch to software encoding\n     * and continue to call this API (e.g. polling once per second) until capacity becomes\n     * available.\n     *\n     * On bare metal (non-virtualized GPU) and linux platforms, this API always returns 100.\n     */\n    NV_ENC_CAPS_DYNAMIC_QUERY_ENCODER_CAPACITY,\n\n    /**\n     * Indicates B as reference support.\n     * \\n 0 : B as reference is not supported.\n     * \\n 1 : each B-Frame as reference is supported.\n     * \\n 2 : only Middle B-frame as reference is supported.\n     */\n    NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE,\n\n    /**\n     * Indicates HW support for Emphasis Level Map based delta QP computation.\n     * \\n 0 : Emphasis Level Map based delta QP not supported.\n     * \\n 1 : Emphasis Level Map based delta QP is supported.\n     */\n    NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,\n\n    /**\n     * Minimum input width supported.\n     */\n    NV_ENC_CAPS_WIDTH_MIN,\n\n    /**\n     * Minimum input height supported.\n     */\n    NV_ENC_CAPS_HEIGHT_MIN,\n\n    /**\n     * Indicates HW support for multiple reference frames.\n     */\n    NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,\n\n    /**\n     * Indicates HW support for HEVC with alpha encoding.\n     * \\n 0 : HEVC with alpha encoding not supported.\n     * \\n 1 : HEVC with alpha encoding is supported.\n     */\n    NV_ENC_CAPS_SUPPORT_ALPHA_LAYER_ENCODING,\n\n    /**\n     * Indicates number of Encoding engines present on GPU.\n     */\n    NV_ENC_CAPS_NUM_ENCODER_ENGINES,\n\n    /**\n     * Indicates single slice intra refresh support.\n     */\n    NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH,\n\n    /**\n     * Reserved - Not to be used by clients.\n     */\n    NV_ENC_CAPS_EXPOSED_COUNT\n\n} NV_ENC_CAPS;\n\n/**\n *  HEVC CU SIZE\n */\ntypedef enum _NV_ENC_HEVC_CUSIZE {\n    NV_ENC_HEVC_CUSIZE_AUTOSELECT = 0,\n    NV_ENC_HEVC_CUSIZE_8x8 = 1,\n    NV_ENC_HEVC_CUSIZE_16x16 = 2,\n    NV_ENC_HEVC_CUSIZE_32x32 = 3,\n    NV_ENC_HEVC_CUSIZE_64x64 = 4,\n} NV_ENC_HEVC_CUSIZE;\n\n/**\n *  AV1 PART SIZE\n */\ntypedef enum _NV_ENC_AV1_PART_SIZE {\n    NV_ENC_AV1_PART_SIZE_AUTOSELECT = 0,\n    NV_ENC_AV1_PART_SIZE_4x4 = 1,\n    NV_ENC_AV1_PART_SIZE_8x8 = 2,\n    NV_ENC_AV1_PART_SIZE_16x16 = 3,\n    NV_ENC_AV1_PART_SIZE_32x32 = 4,\n    NV_ENC_AV1_PART_SIZE_64x64 = 5,\n} NV_ENC_AV1_PART_SIZE;\n\n/**\n *  Enums related to fields in VUI parameters.\n */\ntypedef enum _NV_ENC_VUI_VIDEO_FORMAT {\n    NV_ENC_VUI_VIDEO_FORMAT_COMPONENT = 0,\n    NV_ENC_VUI_VIDEO_FORMAT_PAL = 1,\n    NV_ENC_VUI_VIDEO_FORMAT_NTSC = 2,\n    NV_ENC_VUI_VIDEO_FORMAT_SECAM = 3,\n    NV_ENC_VUI_VIDEO_FORMAT_MAC = 4,\n    NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED = 5,\n} NV_ENC_VUI_VIDEO_FORMAT;\n\ntypedef enum _NV_ENC_VUI_COLOR_PRIMARIES {\n    NV_ENC_VUI_COLOR_PRIMARIES_UNDEFINED = 0,\n    NV_ENC_VUI_COLOR_PRIMARIES_BT709 = 1,\n    NV_ENC_VUI_COLOR_PRIMARIES_UNSPECIFIED = 2,\n    NV_ENC_VUI_COLOR_PRIMARIES_RESERVED = 3,\n    NV_ENC_VUI_COLOR_PRIMARIES_BT470M = 4,\n    NV_ENC_VUI_COLOR_PRIMARIES_BT470BG = 5,\n    NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M = 6,\n    NV_ENC_VUI_COLOR_PRIMARIES_SMPTE240M = 7,\n    NV_ENC_VUI_COLOR_PRIMARIES_FILM = 8,\n    NV_ENC_VUI_COLOR_PRIMARIES_BT2020 = 9,\n    NV_ENC_VUI_COLOR_PRIMARIES_SMPTE428 = 10,\n    NV_ENC_VUI_COLOR_PRIMARIES_SMPTE431 = 11,\n    NV_ENC_VUI_COLOR_PRIMARIES_SMPTE432 = 12,\n    NV_ENC_VUI_COLOR_PRIMARIES_JEDEC_P22 = 22,\n} NV_ENC_VUI_COLOR_PRIMARIES;\n\ntypedef enum _NV_ENC_VUI_TRANSFER_CHARACTERISTIC {\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNDEFINED = 0,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 = 1,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNSPECIFIED = 2,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_RESERVED = 3,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470M = 4,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470BG = 5,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M = 6,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE240M = 7,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LINEAR = 8,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG = 9,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG_SQRT = 10,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_IEC61966_2_4 = 11,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT1361_ECG = 12,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB = 13,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10 = 14,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_12 = 15,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084 = 16,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE428 = 17,\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC_ARIB_STD_B67 = 18,\n} NV_ENC_VUI_TRANSFER_CHARACTERISTIC;\n\ntypedef enum _NV_ENC_VUI_MATRIX_COEFFS {\n    NV_ENC_VUI_MATRIX_COEFFS_RGB = 0,\n    NV_ENC_VUI_MATRIX_COEFFS_BT709 = 1,\n    NV_ENC_VUI_MATRIX_COEFFS_UNSPECIFIED = 2,\n    NV_ENC_VUI_MATRIX_COEFFS_RESERVED = 3,\n    NV_ENC_VUI_MATRIX_COEFFS_FCC = 4,\n    NV_ENC_VUI_MATRIX_COEFFS_BT470BG = 5,\n    NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M = 6,\n    NV_ENC_VUI_MATRIX_COEFFS_SMPTE240M = 7,\n    NV_ENC_VUI_MATRIX_COEFFS_YCGCO = 8,\n    NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL = 9,\n    NV_ENC_VUI_MATRIX_COEFFS_BT2020_CL = 10,\n    NV_ENC_VUI_MATRIX_COEFFS_SMPTE2085 = 11,\n} NV_ENC_VUI_MATRIX_COEFFS;\n\n/**\n * Input struct for querying Encoding capabilities.\n */\ntypedef struct _NV_ENC_CAPS_PARAM {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CAPS_PARAM_VER */\n    NV_ENC_CAPS capsToQuery; /**< [in]: Specifies the encode capability to be queried. Client should\n                                pass a member for ::NV_ENC_CAPS enum. */\n    uint32_t reserved[62]; /**< [in]: Reserved and must be set to 0 */\n} NV_ENC_CAPS_PARAM;\n\n/** NV_ENC_CAPS_PARAM struct version. */\n#define NV_ENC_CAPS_PARAM_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Encoder Output parameters\n */\ntypedef struct _NV_ENC_ENCODE_OUT_PARAMS {\n    uint32_t version; /**< [out]: Struct version. */\n    uint32_t bitstreamSizeInBytes; /**< [out]: Encoded bitstream size in bytes */\n    uint32_t reserved[62]; /**< [out]: Reserved and must be set to 0 */\n} NV_ENC_ENCODE_OUT_PARAMS;\n\n/** NV_ENC_ENCODE_OUT_PARAMS struct version. */\n#define NV_ENC_ENCODE_OUT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Creation parameters for input buffer.\n */\ntypedef struct _NV_ENC_CREATE_INPUT_BUFFER {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CREATE_INPUT_BUFFER_VER */\n    uint32_t width; /**< [in]: Input frame width */\n    uint32_t height; /**< [in]: Input frame height */\n    NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */\n    NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Input buffer format */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0 */\n    NV_ENC_INPUT_PTR inputBuffer; /**< [out]: Pointer to input buffer */\n    void* pSysMemBuffer; /**< [in]: Pointer to existing system memory buffer */\n    uint32_t reserved1[57]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[63]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CREATE_INPUT_BUFFER;\n\n/** NV_ENC_CREATE_INPUT_BUFFER struct version. */\n#define NV_ENC_CREATE_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Creation parameters for output bitstream buffer.\n */\ntypedef struct _NV_ENC_CREATE_BITSTREAM_BUFFER {\n    uint32_t\n        version; /**< [in]: Struct version. Must be set to ::NV_ENC_CREATE_BITSTREAM_BUFFER_VER */\n    uint32_t size; /**< [in]: Deprecated. Do not use */\n    NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0 */\n    NV_ENC_OUTPUT_PTR bitstreamBuffer; /**< [out]: Pointer to the output bitstream buffer */\n    void* bitstreamBufferPtr; /**< [out]: Reserved and should not be used */\n    uint32_t reserved1[58]; /**< [in]: Reserved and should be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and should be set to NULL */\n} NV_ENC_CREATE_BITSTREAM_BUFFER;\n\n/** NV_ENC_CREATE_BITSTREAM_BUFFER struct version. */\n#define NV_ENC_CREATE_BITSTREAM_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Structs needed for ME only mode.\n */\ntypedef struct _NV_ENC_MVECTOR {\n    int16_t mvx; /**< the x component of MV in quarter-pel units */\n    int16_t mvy; /**< the y component of MV in quarter-pel units */\n} NV_ENC_MVECTOR;\n\n/**\n * Motion vector structure per macroblock for H264 motion estimation.\n */\ntypedef struct _NV_ENC_H264_MV_DATA {\n    NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors for 8x8 partition */\n    uint8_t mbType; /**< 0 (I), 1 (P), 2 (IPCM), 3 (B) */\n    uint8_t\n        partitionType; /**< Specifies the block partition type. 0:16x16, 1:8x8, 2:16x8, 3:8x16 */\n    uint16_t reserved; /**< reserved padding for alignment */\n    uint32_t mbCost;\n} NV_ENC_H264_MV_DATA;\n\n/**\n * Motion vector structure per CU for HEVC motion estimation.\n */\ntypedef struct _NV_ENC_HEVC_MV_DATA {\n    NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors within a CU */\n    uint8_t cuType; /**< 0 (I), 1(P) */\n    uint8_t cuSize; /**< 0: 8x8, 1: 16x16, 2: 32x32, 3: 64x64 */\n    uint8_t partitionMode; /**< The CU partition mode\n                                0 (2Nx2N), 1 (2NxN), 2(Nx2N), 3 (NxN),\n                                4 (2NxnU), 5 (2NxnD), 6(nLx2N), 7 (nRx2N) */\n    uint8_t lastCUInCTB; /**< Marker to separate CUs in the current CTB from CUs in the next CTB */\n} NV_ENC_HEVC_MV_DATA;\n\n/**\n * Creation parameters for output motion vector buffer for ME only mode.\n */\ntypedef struct _NV_ENC_CREATE_MV_BUFFER {\n    uint32_t version; /**< [in]: Struct version. Must be set to NV_ENC_CREATE_MV_BUFFER_VER */\n    NV_ENC_OUTPUT_PTR mvBuffer; /**< [out]: Pointer to the output motion vector buffer */\n    uint32_t reserved1[255]; /**< [in]: Reserved and should be set to 0 */\n    void* reserved2[63]; /**< [in]: Reserved and should be set to NULL */\n} NV_ENC_CREATE_MV_BUFFER;\n\n/** NV_ENC_CREATE_MV_BUFFER struct version*/\n#define NV_ENC_CREATE_MV_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * QP value for frames\n */\ntypedef struct _NV_ENC_QP {\n    uint32_t\n        qpInterP; /**< [in]: Specifies QP value for P-frame. Even though this field is uint32_t for\n                     legacy reasons, the client should treat this as a signed parameter(int32_t) for\n                     cases in which negative QP values are to be specified. */\n    uint32_t\n        qpInterB; /**< [in]: Specifies QP value for B-frame. Even though this field is uint32_t for\n                     legacy reasons, the client should treat this as a signed parameter(int32_t) for\n                     cases in which negative QP values are to be specified. */\n    uint32_t\n        qpIntra; /**< [in]: Specifies QP value for Intra Frame. Even though this field is uint32_t\n                    for legacy reasons, the client should treat this as a signed parameter(int32_t)\n                    for cases in which negative QP values are to be specified. */\n} NV_ENC_QP;\n\n/**\n * Rate Control Configuration Parameters\n */\ntypedef struct _NV_ENC_RC_PARAMS {\n    uint32_t version;\n    NV_ENC_PARAMS_RC_MODE\n        rateControlMode; /**< [in]: Specifies the rate control mode. Check support for various rate\n                            control modes using ::NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES caps. */\n    NV_ENC_QP constQP; /**< [in]: Specifies the initial QP to be used for encoding, these values\n                          would be used for all frames if in Constant QP mode. */\n    uint32_t\n        averageBitRate; /**< [in]: Specifies the average bitrate(in bits/sec) used for encoding. */\n    uint32_t maxBitRate; /**< [in]: Specifies the maximum bitrate for the encoded output. This is\n                            used for VBR and ignored for CBR mode. */\n    uint32_t vbvBufferSize; /**< [in]: Specifies the VBV(HRD) buffer size. in bits. Set 0 to use the\n                               default VBV  buffer size. */\n    uint32_t vbvInitialDelay; /**< [in]: Specifies the VBV(HRD) initial delay in bits. Set 0 to use\n                                 the default VBV  initial delay .*/\n    uint32_t enableMinQP : 1; /**< [in]: Set this to 1 if minimum QP used for rate control. */\n    uint32_t enableMaxQP : 1; /**< [in]: Set this to 1 if maximum QP used for rate control. */\n    uint32_t enableInitialRCQP : 1; /**< [in]: Set this to 1 if user supplied initial QP is used for\n                                       rate control. */\n    uint32_t enableAQ : 1; /**< [in]: Set this to 1 to enable adaptive quantization (Spatial). */\n    uint32_t reservedBitField1 : 1; /**< [in]: Reserved bitfields and must be set to 0. */\n    uint32_t enableLookahead : 1; /**< [in]: Set this to 1 to enable lookahead with depth\n                                     <lookaheadDepth> (if lookahead is enabled, input frames must\n                                     remain available to the encoder until encode completion) */\n    uint32_t disableIadapt : 1; /**< [in]: Set this to 1 to disable adaptive I-frame insertion at\n                                   scene cuts (only has an effect when lookahead is enabled) */\n    uint32_t disableBadapt : 1; /**< [in]: Set this to 1 to disable adaptive B-frame decision (only\n                                   has an effect when lookahead is enabled) */\n    uint32_t enableTemporalAQ : 1; /**< [in]: Set this to 1 to enable temporal AQ */\n    uint32_t zeroReorderDelay : 1; /**< [in]: Set this to 1 to indicate zero latency operation (no\n                                      reordering delay, num_reorder_frames=0) */\n    uint32_t enableNonRefP : 1; /**< [in]: Set this to 1 to enable automatic insertion of\n                                   non-reference P-frames (no effect if enablePTD=0) */\n    uint32_t\n        strictGOPTarget : 1; /**< [in]: Set this to 1 to minimize GOP-to-GOP rate fluctuations */\n    uint32_t aqStrength : 4; /**< [in]: When AQ (Spatial) is enabled (i.e.\n                                NV_ENC_RC_PARAMS::enableAQ is set), this field is used to specify AQ\n                                strength. AQ strength scale is from 1 (low) - 15 (aggressive). If\n                                not set, strength is auto selected by driver. */\n    uint32_t reservedBitFields : 16; /**< [in]: Reserved bitfields and must be set to 0 */\n    NV_ENC_QP minQP; /**< [in]: Specifies the minimum QP used for rate control. Client must set\n                        NV_ENC_CONFIG::enableMinQP to 1. */\n    NV_ENC_QP maxQP; /**< [in]: Specifies the maximum QP used for rate control. Client must set\n                        NV_ENC_CONFIG::enableMaxQP to 1. */\n    NV_ENC_QP initialRCQP; /**< [in]: Specifies the initial QP used for rate control. Client must\n                              set NV_ENC_CONFIG::enableInitialRCQP to 1. */\n    uint32_t temporallayerIdxMask; /**< [in]: Specifies the temporal layers (as a bitmask) whose QPs\n                                      have changed. Valid max bitmask is\n                                      [2^NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS - 1]. Applicable only\n                                      for constant QP mode (NV_ENC_RC_PARAMS::rateControlMode =\n                                      NV_ENC_PARAMS_RC_CONSTQP). */\n    uint8_t temporalLayerQP[8]; /**< [in]: Specifies the temporal layer QPs used for rate control.\n                                   Temporal layer index is used as the array index. Applicable only\n                                   for constant QP mode (NV_ENC_RC_PARAMS::rateControlMode =\n                                   NV_ENC_PARAMS_RC_CONSTQP). */\n    uint8_t targetQuality; /**< [in]: Target CQ (Constant Quality) level for VBR mode (range 0-51\n                              with 0-automatic)  */\n    uint8_t targetQualityLSB; /**< [in]: Fractional part of target quality (as 8.8 fixed point\n                                 format) */\n    uint16_t lookaheadDepth; /**< [in]: Maximum depth of lookahead with range 0-(31 - number of B\n                                frames). lookaheadDepth is only used if enableLookahead=1.*/\n    uint8_t lowDelayKeyFrameScale; /**< [in]: Specifies the ratio of I frame bits to P frame bits in\n                                      case of single frame VBV and CBR rate control mode, is set to\n                                      2 by default for low latency tuning info and 1 by default for\n                                      ultra low latency tuning info  */\n    int8_t yDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_y_dc' in AV1.*/\n    int8_t uDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_u_dc' in AV1.*/\n    int8_t vDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_v_dc' in AV1 (for future use\n                                only - deltaQ_v_dc is currently always internally set to same value\n                                as deltaQ_u_dc). */\n    NV_ENC_QP_MAP_MODE\n        qpMapMode; /**< [in]: This flag is used to interpret values in array specified by\n                      NV_ENC_PIC_PARAMS::qpDeltaMap. Set this to NV_ENC_QP_MAP_EMPHASIS to treat\n                      values specified by NV_ENC_PIC_PARAMS::qpDeltaMap as Emphasis Level Map.\n                              Emphasis Level can be assigned any value specified in enum\n                      NV_ENC_EMPHASIS_MAP_LEVEL. Emphasis Level Map is used to specify regions to be\n                      encoded at varying levels of quality. The hardware encoder adjusts the\n                      quantization within the image as per the provided emphasis map, by adjusting\n                      the quantization parameter (QP) assigned to each macroblock. This adjustment\n                      is commonly called \"Delta QP\". The adjustment depends on the absolute QP\n                      decided by the rate control algorithm, and is applied after the rate control\n                      has decided each macroblock's QP. Since the Delta QP overrides rate control,\n                      enabling Emphasis Level Map may violate bitrate and VBV buffer size\n                      constraints. Emphasis Level Map is useful in situations where client has a\n                      priori knowledge of the image complexity (e.g. via use of NVFBC's\n                      Classification feature) and encoding those high-complexity areas at higher\n                      quality (lower QP) is important, even at the possible cost of violating\n                      bitrate/VBV buffer size constraints This feature is not supported when AQ(\n                      Spatial/Temporal) is enabled. This feature is only supported for H264 codec\n                      currently.\n\n                              Set this to NV_ENC_QP_MAP_DELTA to treat values specified by\n                      NV_ENC_PIC_PARAMS::qpDeltaMap as QP Delta. This specifies QP modifier to be\n                      applied on top of the QP chosen by rate control\n\n                              Set this to NV_ENC_QP_MAP_DISABLED to ignore\n                      NV_ENC_PIC_PARAMS::qpDeltaMap values. In this case, qpDeltaMap should be set\n                      to NULL.\n\n                              Other values are reserved for future use.*/\n    NV_ENC_MULTI_PASS multiPass; /**< [in]: This flag is used to enable multi-pass encoding for a\n                                    given ::NV_ENC_PARAMS_RC_MODE. This flag is not valid for H264\n                                    and HEVC MEOnly mode */\n    uint32_t alphaLayerBitrateRatio; /**< [in]: Specifies the ratio in which bitrate should be split\n                                        between base and alpha layer. A value 'x' for this field\n                                        will split the target bitrate in a ratio of x : 1 between\n                                        base and alpha layer. The default split ratio is 15.*/\n    int8_t cbQPIndexOffset; /**< [in]: Specifies the value of 'chroma_qp_index_offset' in H264 /\n                               'pps_cb_qp_offset' in HEVC / 'deltaQ_u_ac' in AV1.*/\n    int8_t crQPIndexOffset; /**< [in]: Specifies the value of 'second_chroma_qp_index_offset' in\n                               H264 / 'pps_cr_qp_offset' in HEVC / 'deltaQ_v_ac' in AV1 (for future\n                               use only - deltaQ_v_ac is currently always internally set to same\n                               value as deltaQ_u_ac). */\n    uint16_t reserved2;\n    uint32_t reserved[4];\n} NV_ENC_RC_PARAMS;\n\n/** macro for constructing the version field of ::_NV_ENC_RC_PARAMS */\n#define NV_ENC_RC_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)\n\n#define MAX_NUM_CLOCK_TS 3\n\n/**\n * Clock Timestamp set parameters\n * For H264, this structure is used to populate Picture Timing SEI when\n * NV_ENC_CONFIG_H264::enableTimeCode is set to 1. For HEVC, this structure is used to populate Time\n * Code SEI when NV_ENC_CONFIG_HEVC::enableTimeCodeSEI is set to 1. For more details, refer to Annex\n * D of ITU-T Specification.\n */\n\ntypedef struct _NV_ENC_CLOCK_TIMESTAMP_SET {\n    uint32_t countingType : 1; /**< [in] Specifies the 'counting_type' */\n    uint32_t discontinuityFlag : 1; /**< [in] Specifies the 'discontinuity_flag' */\n    uint32_t cntDroppedFrames : 1; /**< [in] Specifies the 'cnt_dropped_flag' */\n    uint32_t nFrames : 8; /**< [in] Specifies the value of 'n_frames' */\n    uint32_t secondsValue : 6; /**< [in] Specifies the 'seconds_value' */\n    uint32_t minutesValue : 6; /**< [in] Specifies the 'minutes_value' */\n    uint32_t hoursValue : 5; /**< [in] Specifies the 'hours_value' */\n    uint32_t reserved2 : 4; /**< [in] Reserved and must be set to 0 */\n    uint32_t timeOffset; /**< [in] Specifies the 'time_offset_value' */\n} NV_ENC_CLOCK_TIMESTAMP_SET;\n\ntypedef struct _NV_ENC_TIME_CODE {\n    NV_ENC_DISPLAY_PIC_STRUCT displayPicStruct; /**< [in] Display picStruct */\n    NV_ENC_CLOCK_TIMESTAMP_SET clockTimestamp[MAX_NUM_CLOCK_TS]; /**< [in] Clock Timestamp set */\n} NV_ENC_TIME_CODE;\n\n/**\n * \\struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS\n * H264 Video Usability Info parameters\n */\ntypedef struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS {\n    uint32_t overscanInfoPresentFlag; /**< [in]: If set to 1 , it specifies that the overscanInfo is\n                                         present */\n    uint32_t overscanInfo; /**< [in]: Specifies the overscan info(as defined in Annex E of the ITU-T\n                              Specification). */\n    uint32_t videoSignalTypePresentFlag; /**< [in]: If set to 1, it specifies  that the videoFormat,\n                                            videoFullRangeFlag and colourDescriptionPresentFlag are\n                                            present. */\n    NV_ENC_VUI_VIDEO_FORMAT videoFormat; /**< [in]: Specifies the source video format(as defined in\n                                            Annex E of the ITU-T Specification).*/\n    uint32_t videoFullRangeFlag; /**< [in]: Specifies the output range of the luma and chroma\n                                    samples(as defined in Annex E of the ITU-T Specification). */\n    uint32_t\n        colourDescriptionPresentFlag; /**< [in]: If set to 1, it specifies that the colourPrimaries,\n                                         transferCharacteristics and colourMatrix are present. */\n    NV_ENC_VUI_COLOR_PRIMARIES\n        colourPrimaries; /**< [in]: Specifies color primaries for converting to RGB(as defined in\n                            Annex E of the ITU-T Specification) */\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC\n        transferCharacteristics; /**< [in]: Specifies the opto-electronic transfer characteristics\n                                    to use (as defined in Annex E of the ITU-T Specification) */\n    NV_ENC_VUI_MATRIX_COEFFS colourMatrix; /**< [in]: Specifies the matrix coefficients used in\n                                              deriving the luma and chroma from the RGB primaries\n                                              (as defined in Annex E of the ITU-T Specification). */\n    uint32_t chromaSampleLocationFlag; /**< [in]: If set to 1 , it specifies that the\n                                          chromaSampleLocationTop and chromaSampleLocationBot are\n                                          present.*/\n    uint32_t chromaSampleLocationTop; /**< [in]: Specifies the chroma sample location for top\n                                         field(as defined in Annex E of the ITU-T Specification) */\n    uint32_t chromaSampleLocationBot; /**< [in]: Specifies the chroma sample location for bottom\n                                         field(as defined in Annex E of the ITU-T Specification) */\n    uint32_t bitstreamRestrictionFlag; /**< [in]: If set to 1, it specifies the bitstream\n                                          restriction parameters are present in the bitstream.*/\n    uint32_t timingInfoPresentFlag; /**< [in]: If set to 1, it specifies that the timingInfo is\n                                       present and the 'numUnitInTicks' and 'timeScale' fields are\n                                       specified by the application. */\n    /**< [in]: If not set, the timingInfo may still be present with timing related fields calculated\n     * internally basedon the frame rate specified by the application. */\n    uint32_t numUnitInTicks; /**< [in]: Specifies the number of time units of the clock(as defined\n                                in Annex E of the ITU-T Specification). */\n    uint32_t timeScale; /**< [in]: Specifies the frquency of the clock(as defined in Annex E of the\n                           ITU-T Specification). */\n    uint32_t reserved[12]; /**< [in]: Reserved and must be set to 0 */\n} NV_ENC_CONFIG_H264_VUI_PARAMETERS;\n\ntypedef NV_ENC_CONFIG_H264_VUI_PARAMETERS NV_ENC_CONFIG_HEVC_VUI_PARAMETERS;\n\n/**\n * \\struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE\n * External motion vector hint counts per block type.\n * H264 and AV1 support multiple hint while HEVC supports one hint for each valid candidate.\n */\ntypedef struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE {\n    uint32_t numCandsPerBlk16x16 : 4; /**< [in]: Supported for H264, HEVC. It Specifies the number\n                                         of candidates per 16x16 block. */\n    uint32_t numCandsPerBlk16x8 : 4; /**< [in]: Supported for H264 only. Specifies the number of\n                                        candidates per 16x8 block. */\n    uint32_t numCandsPerBlk8x16 : 4; /**< [in]: Supported for H264 only. Specifies the number of\n                                        candidates per 8x16 block. */\n    uint32_t numCandsPerBlk8x8 : 4; /**< [in]: Supported for H264, HEVC. Specifies the number of\n                                       candidates per 8x8 block. */\n    uint32_t numCandsPerSb : 8; /**< [in]: Supported for AV1 only. Specifies the number of\n                                   candidates per SB. */\n    uint32_t reserved : 8; /**< [in]: Reserved for padding. */\n    uint32_t reserved1[3]; /**< [in]: Reserved for future use. */\n} NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE;\n\n/**\n * \\struct _NVENC_EXTERNAL_ME_HINT\n * External Motion Vector hint structure for H264 and HEVC.\n */\ntypedef struct _NVENC_EXTERNAL_ME_HINT {\n    int32_t mvx : 12; /**< [in]: Specifies the x component of integer pixel MV (relative to current\n                         MB) S12.0. */\n    int32_t mvy : 10; /**< [in]: Specifies the y component of integer pixel MV (relative to current\n                         MB) S10.0 .*/\n    int32_t\n        refidx : 5; /**< [in]: Specifies the reference index (31=invalid). Current we support only 1\n                       reference frame per direction for external hints, so \\p refidx must be 0. */\n    int32_t dir : 1; /**< [in]: Specifies the direction of motion estimation . 0=L0 1=L1.*/\n    int32_t partType : 2; /**< [in]: Specifies the block partition type.0=16x16 1=16x8 2=8x16 3=8x8\n                             (blocks in partition must be consecutive).*/\n    int32_t lastofPart : 1; /**< [in]: Set to 1 for the last MV of (sub) partition  */\n    int32_t lastOfMB : 1; /**< [in]: Set to 1 for the last MV of macroblock. */\n} NVENC_EXTERNAL_ME_HINT;\n\n/**\n * \\struct _NVENC_EXTERNAL_ME_SB_HINT\n * External Motion Vector SB hint structure for AV1\n */\ntypedef struct _NVENC_EXTERNAL_ME_SB_HINT {\n    int16_t refidx : 5; /**< [in]: Specifies the reference index (31=invalid) */\n    int16_t direction : 1; /**< [in]: Specifies the direction of motion estimation . 0=L0 1=L1.*/\n    int16_t bi : 1; /**< [in]: Specifies reference mode 0=single mv, 1=compound mv */\n    int16_t partition_type : 3; /**< [in]: Specifies the partition type: 0: 2NX2N, 1:2NxN, 2:Nx2N.\n                                   reserved 3bits for future modes */\n    int16_t\n        x8 : 3; /**< [in]: Specifies the current partition's top left x position in 8 pixel unit */\n    int16_t last_of_cu : 1; /**< [in]: Set to 1 for the last MV current CU */\n    int16_t last_of_sb : 1; /**< [in]: Set to 1 for the last MV of current SB */\n    int16_t reserved0 : 1; /**< [in]: Reserved and must be set to 0 */\n    int16_t mvx : 14; /**< [in]: Specifies the x component of integer pixel MV (relative to current\n                         MB) S12.2. */\n    int16_t cu_size : 2; /**< [in]: Specifies the CU size: 0: 8x8, 1: 16x16, 2:32x32, 3:64x64 */\n    int16_t mvy : 12; /**< [in]: Specifies the y component of integer pixel MV (relative to current\n                         MB) S10.2 .*/\n    int16_t\n        y8 : 3; /**< [in]: Specifies the current partition's top left y position in 8 pixel unit */\n    int16_t reserved1 : 1; /**< [in]: Reserved and must be set to 0 */\n} NVENC_EXTERNAL_ME_SB_HINT;\n\n/**\n * \\struct _NV_ENC_CONFIG_H264\n * H264 encoder configuration parameters\n */\ntypedef struct _NV_ENC_CONFIG_H264 {\n    uint32_t enableTemporalSVC : 1; /**< [in]: Set to 1 to enable SVC temporal*/\n    uint32_t enableStereoMVC : 1; /**< [in]: Set to 1 to enable stereo MVC*/\n    uint32_t hierarchicalPFrames : 1; /**< [in]: Set to 1 to enable hierarchical P Frames */\n    uint32_t hierarchicalBFrames : 1; /**< [in]: Set to 1 to enable hierarchical B Frames */\n    uint32_t outputBufferingPeriodSEI : 1; /**< [in]: Set to 1 to write SEI buffering period syntax\n                                              in the bitstream */\n    uint32_t outputPictureTimingSEI : 1; /**< [in]: Set to 1 to write SEI picture timing syntax in\n                                            the bitstream. */\n    uint32_t\n        outputAUD : 1; /**< [in]: Set to 1 to write access unit delimiter syntax in bitstream */\n    uint32_t disableSPSPPS : 1; /**< [in]: Set to 1 to disable writing of Sequence and Picture\n                                   parameter info in bitstream */\n    uint32_t outputFramePackingSEI : 1; /**< [in]: Set to 1 to enable writing of frame packing\n                                           arrangement SEI messages to bitstream */\n    uint32_t outputRecoveryPointSEI : 1; /**< [in]: Set to 1 to enable writing of recovery point SEI\n                                            message */\n    uint32_t enableIntraRefresh : 1; /**< [in]: Set to 1 to enable gradual decoder refresh or intra\n                                        refresh. If the GOP structure uses B frames this will be\n                                        ignored */\n    uint32_t\n        enableConstrainedEncoding : 1; /**< [in]: Set this to 1 to enable constrainedFrame encoding\n                                          where each slice in the constrained picture is independent\n                                          of other slices. Constrained encoding works only with\n                                          rectangular slices. Check support for constrained encoding\n                                          using ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING caps. */\n    uint32_t repeatSPSPPS : 1; /**< [in]: Set to 1 to enable writing of Sequence and Picture\n                                  parameter for every IDR frame */\n    uint32_t enableVFR : 1; /**< [in]: Setting enableVFR=1 currently only sets the\n                               fixed_frame_rate_flag=0 in the VUI but otherwise has no impact on the\n                               encoder behavior. For more details please refer to E.1 VUI syntax of\n                               H.264 standard. Note, however, that NVENC does not support VFR\n                               encoding and rate control. */\n    uint32_t\n        enableLTR : 1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) frame support. LTR\n                          can be used in two modes: \"LTR Trust\" mode and \"LTR Per Picture\" mode. LTR\n                          Trust mode: In this mode, ltrNumFrames pictures after IDR are\n                          automatically marked as LTR. This mode is enabled by setting ltrTrustMode\n                          = 1. Use of LTR Trust mode is strongly discouraged as this mode may be\n                          deprecated in future. LTR Per Picture mode: In this mode, client can\n                          control whether the current picture should be marked as LTR. Enable this\n                          mode by setting ltrTrustMode = 0 and ltrMarkFrame = 1 for the picture to\n                          be marked as LTR. This is the preferred mode for using LTR. Note that LTRs\n                          are not supported if encoding session is configured with B-frames */\n    uint32_t\n        qpPrimeYZeroTransformBypassFlag : 1; /**< [in]: To enable lossless encode set this to 1, set\n                                                QP to 0 and RC_mode to NV_ENC_PARAMS_RC_CONSTQP and\n                                                profile to HIGH_444_PREDICTIVE_PROFILE. Check\n                                                support for lossless encoding using\n                                                ::NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE caps.  */\n    uint32_t\n        useConstrainedIntraPred : 1; /**< [in]: Set 1 to enable constrained intra prediction. */\n    uint32_t\n        enableFillerDataInsertion : 1; /**< [in]: Set to 1 to enable insertion of filler data in the\n                                          bitstream. This flag will take effect only when one of the\n                                          CBR rate control modes (NV_ENC_PARAMS_RC_CBR,\n                                          NV_ENC_PARAMS_RC_CBR_HQ, NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ)\n                                          is in use and both NV_ENC_INITIALIZE_PARAMS::frameRateNum\n                                          and NV_ENC_INITIALIZE_PARAMS::frameRateDen are set to\n                                          non-zero values. Setting this field when\n                                                  NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is\n                                          also set is currently not supported and will make\n                                          ::NvEncInitializeEncoder() return an error. */\n    uint32_t disableSVCPrefixNalu : 1; /**< [in]: Set to 1 to disable writing of SVC Prefix NALU\n                                          preceding each slice in bitstream. Applicable only when\n                                          temporal SVC is enabled\n                                          (NV_ENC_CONFIG_H264::enableTemporalSVC = 1). */\n    uint32_t enableScalabilityInfoSEI : 1; /**< [in]: Set to 1 to enable writing of Scalability\n                                              Information SEI message preceding each IDR picture in\n                                              bitstream Applicable only when temporal SVC is enabled\n                                              (NV_ENC_CONFIG_H264::enableTemporalSVC = 1). */\n    uint32_t\n        singleSliceIntraRefresh : 1; /**< [in]: Set to 1 to maintain single slice in frames during\n                                        intra refresh. Check support for single slice intra refresh\n                                        using ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps. This\n                                        flag will be ignored if the value returned for\n                                        ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps is false. */\n    uint32_t enableTimeCode : 1; /**< [in]: Set to 1 to enable writing of clock timestamp sets in\n                                    picture timing SEI.  Note that this flag will be ignored for\n                                    D3D12 interface. */\n    uint32_t reservedBitFields : 10; /**< [in]: Reserved bitfields and must be set to 0 */\n    uint32_t level; /**< [in]: Specifies the encoding level. Client is recommended to set this to\n                       NV_ENC_LEVEL_AUTOSELECT in order to enable the NvEncodeAPI interface to\n                       select the correct level. */\n    uint32_t idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made equal to\n                           gopLength in NV_ENC_CONFIG.Low latency application client can set IDR\n                           interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not inserted\n                           automatically. */\n    uint32_t separateColourPlaneFlag; /**< [in]: Set to 1 to enable 4:4:4 separate colour planes */\n    uint32_t disableDeblockingFilterIDC; /**< [in]: Specifies the deblocking filter mode.\n                                            Permissible value range: [0,2]. This flag corresponds to\n                                            the flag disable_deblocking_filter_idc specified in\n                                            section 7.4.3 of H.264 specification, which specifies\n                                            whether the operation of the deblocking filter shall be\n                                            disabled across some block edges of the slice and\n                                            specifies for which edges the filtering is disabled. See\n                                            section 7.4.3 of H.264 specification for more details.*/\n    uint32_t numTemporalLayers; /**< [in]: Specifies number of temporal layers to be used for\n                                   hierarchical coding / temporal SVC. Valid value range is\n                                   [1,::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS] */\n    uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */\n    uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */\n    NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE\n        adaptiveTransformMode; /**< [in]: Specifies the AdaptiveTransform Mode. Check support for\n                                  AdaptiveTransform mode using\n                                  ::NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM caps. */\n    NV_ENC_H264_FMO_MODE fmoMode; /**< [in]: Specified the FMO Mode. Check support for FMO using\n                                     ::NV_ENC_CAPS_SUPPORT_FMO caps. */\n    NV_ENC_H264_BDIRECT_MODE\n        bdirectMode; /**< [in]: Specifies the BDirect mode. Check support for BDirect mode using\n                        ::NV_ENC_CAPS_SUPPORT_BDIRECT_MODE caps.*/\n    NV_ENC_H264_ENTROPY_CODING_MODE\n        entropyCodingMode; /**< [in]: Specifies the entropy coding mode. Check support for CABAC\n                              mode using ::NV_ENC_CAPS_SUPPORT_CABAC caps. */\n    NV_ENC_STEREO_PACKING_MODE stereoMode; /**< [in]: Specifies the stereo frame packing mode which\n                                              is to be signaled in frame packing arrangement SEI */\n    uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh\n                                    if enableIntrarefresh is set. Requires enableIntraRefresh to be\n                                    set. Will be disabled if NV_ENC_CONFIG::gopLength is not set to\n                                    NVENC_INFINITE_GOPLENGTH. */\n    uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames\n                                 for periodic intra refresh. This value should be smaller than\n                                 intraRefreshPeriod */\n    uint32_t\n        maxNumRefFrames; /**< [in]: Specifies the DPB size used for encoding. Setting it to 0 will\n                            let driver use the default DPB size. The low latency application which\n                            wants to invalidate reference frame as an error resilience tool is\n                            recommended to use a large DPB size so that the encoder can keep old\n                            reference frames which can be used if recent frames are invalidated. */\n    uint32_t\n        sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in\n                      which the picture is divided into slices sliceMode = 0 MB based slices,\n                      sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode\n                      = 3 numSlices in Picture. When forceIntraRefreshWithFrameCnt is set it will\n                      have priority over sliceMode setting When sliceMode == 0 and sliceModeData ==\n                      0 whole picture will be coded with one slice */\n    uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For:\n                                       sliceMode = 0, sliceModeData specifies # of MBs in each slice\n                               (except last slice) sliceMode = 1, sliceModeData specifies maximum #\n                               of bytes in each slice (except last slice) sliceMode = 2,\n                               sliceModeData specifies # of MB rows in each slice (except last\n                               slice) sliceMode = 3, sliceModeData specifies number of slices in the\n                               picture. Driver will divide picture into slices optimally */\n    NV_ENC_CONFIG_H264_VUI_PARAMETERS\n        h264VUIParameters; /**< [in]: Specifies the H264 video usability info parameters */\n    uint32_t ltrNumFrames; /**< [in]: Specifies the number of LTR frames. This parameter has\n                              different meaning in two LTR modes. In \"LTR Trust\" mode (ltrTrustMode\n                              = 1), encoder will mark the first ltrNumFrames base layer reference\n                              frames within each IDR interval as LTR. In \"LTR Per Picture\" mode\n                              (ltrTrustMode = 0 and ltrMarkFrame = 1), ltrNumFrames specifies\n                              maximum number of LTR frames in DPB. */\n    uint32_t ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See comments near\n                              NV_ENC_CONFIG_H264::enableLTR for description of the two modes. Set to\n                              1 to use \"LTR Trust\" mode of LTR operation. Clients are discouraged to\n                              use \"LTR Trust\" mode as this mode may be deprecated in future\n                              releases. Set to 0 when using \"LTR Per Picture\" mode of LTR operation.\n                            */\n    uint32_t chromaFormatIDC; /**< [in]: Specifies the chroma format. Should be set to 1 for yuv420\n                                 input, 3 for yuv444 input. Check support for YUV444 encoding using\n                                 ::NV_ENC_CAPS_SUPPORT_YUV444_ENCODE caps.*/\n    uint32_t maxTemporalLayers; /**< [in]: Specifies the max temporal layer used for temporal SVC /\n                                   hierarchical coding. Defaut value of this field is\n                                   NV_ENC_CAPS::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS. Note that the\n                                   value NV_ENC_CONFIG_H264::maxNumRefFrames should be greater than\n                                   or equal to (NV_ENC_CONFIG_H264::maxTemporalLayers - 2) * 2, for\n                                   NV_ENC_CONFIG_H264::maxTemporalLayers >= 2.*/\n    NV_ENC_BFRAME_REF_MODE\n        useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check support for\n                            useBFramesAsRef mode using ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/\n    NV_ENC_NUM_REF_FRAMES\n        numRefL0; /**< [in]: Specifies max number of reference frames in reference picture list L0,\n                     that can be used by hardware for prediction of a frame. Check support for\n                     numRefL0 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */\n    NV_ENC_NUM_REF_FRAMES\n        numRefL1; /**< [in]: Specifies max number of reference frames in reference picture list L1,\n                     that can be used by hardware for prediction of a frame. Check support for\n                     numRefL1 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */\n\n    uint32_t reserved1[267]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG_H264;\n\n/**\n * \\struct _NV_ENC_CONFIG_HEVC\n * HEVC encoder configuration parameters to be set during initialization.\n */\ntypedef struct _NV_ENC_CONFIG_HEVC {\n    uint32_t level; /**< [in]: Specifies the level of the encoded bitstream.*/\n    uint32_t tier; /**< [in]: Specifies the level tier of the encoded bitstream.*/\n    NV_ENC_HEVC_CUSIZE minCUSize; /**< [in]: Specifies the minimum size of luma coding unit.*/\n    NV_ENC_HEVC_CUSIZE\n        maxCUSize; /**< [in]: Specifies the maximum size of luma coding unit. Currently NVENC SDK\n                      only supports maxCUSize equal to NV_ENC_HEVC_CUSIZE_32x32.*/\n    uint32_t\n        useConstrainedIntraPred : 1; /**< [in]: Set 1 to enable constrained intra prediction. */\n    uint32_t disableDeblockAcrossSliceBoundary : 1; /**< [in]: Set 1 to disable in loop filtering\n                                                       across slice boundary.*/\n    uint32_t outputBufferingPeriodSEI : 1; /**< [in]: Set 1 to write SEI buffering period syntax in\n                                              the bitstream */\n    uint32_t outputPictureTimingSEI : 1; /**< [in]: Set 1 to write SEI picture timing syntax in the\n                                            bitstream */\n    uint32_t outputAUD : 1; /**< [in]: Set 1 to write Access Unit Delimiter syntax. */\n    uint32_t\n        enableLTR : 1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) frame support. LTR\n                          can be used in two modes: \"LTR Trust\" mode and \"LTR Per Picture\" mode. LTR\n                          Trust mode: In this mode, ltrNumFrames pictures after IDR are\n                          automatically marked as LTR. This mode is enabled by setting ltrTrustMode\n                          = 1. Use of LTR Trust mode is strongly discouraged as this mode may be\n                          deprecated in future releases. LTR Per Picture mode: In this mode, client\n                          can control whether the current picture should be marked as LTR. Enable\n                          this mode by setting ltrTrustMode = 0 and ltrMarkFrame = 1 for the picture\n                          to be marked as LTR. This is the preferred mode for using LTR. Note that\n                          LTRs are not supported if encoding session is configured with B-frames */\n    uint32_t disableSPSPPS : 1; /**< [in]: Set 1 to disable VPS, SPS and PPS signaling in the\n                                   bitstream. */\n    uint32_t repeatSPSPPS : 1; /**< [in]: Set 1 to output VPS,SPS and PPS for every IDR frame.*/\n    uint32_t\n        enableIntraRefresh : 1; /**< [in]: Set 1 to enable gradual decoder refresh or intra refresh.\n                                   If the GOP structure uses B frames this will be ignored */\n    uint32_t chromaFormatIDC : 2; /**< [in]: Specifies the chroma format. Should be set to 1 for\n                                     yuv420 input, 3 for yuv444 input.*/\n    uint32_t pixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth minus 8. Should be set to\n                                         0 for 8 bit input, 2 for 10 bit input.*/\n    uint32_t\n        enableFillerDataInsertion : 1; /**< [in]: Set to 1 to enable insertion of filler data in the\n                                          bitstream. This flag will take effect only when one of the\n                                          CBR rate control modes (NV_ENC_PARAMS_RC_CBR,\n                                          NV_ENC_PARAMS_RC_CBR_HQ, NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ)\n                                          is in use and both NV_ENC_INITIALIZE_PARAMS::frameRateNum\n                                          and NV_ENC_INITIALIZE_PARAMS::frameRateDen are set to\n                                          non-zero values. Setting this field when\n                                                  NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is\n                                          also set is currently not supported and will make\n                                          ::NvEncInitializeEncoder() return an error. */\n    uint32_t\n        enableConstrainedEncoding : 1; /**< [in]: Set this to 1 to enable constrainedFrame encoding\n                                          where each slice in the constrained picture is independent\n                                          of other slices. Constrained encoding works only with\n                                          rectangular slices. Check support for constrained encoding\n                                          using ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING caps. */\n    uint32_t enableAlphaLayerEncoding : 1; /**< [in]: Set this to 1 to enable HEVC encode with alpha\n                                              layer. */\n    uint32_t\n        singleSliceIntraRefresh : 1; /**< [in]: Set this to 1 to maintain single slice frames during\n                                        intra refresh. Check support for single slice intra refresh\n                                        using ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps. This\n                                        flag will be ignored if the value returned for\n                                        ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps is false. */\n    uint32_t outputRecoveryPointSEI : 1; /**< [in]: Set to 1 to enable writing of recovery point SEI\n                                            message */\n    uint32_t outputTimeCodeSEI : 1; /**< [in]: Set 1 to write SEI time code syntax in the bitstream.\n                                       Note that this flag will be ignored for D3D12 interface.*/\n    uint32_t reserved : 12; /**< [in]: Reserved bitfields.*/\n    uint32_t idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made equal to\n                           gopLength in NV_ENC_CONFIG. Low latency application client can set IDR\n                           interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not inserted\n                           automatically. */\n    uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh\n                                 if enableIntrarefresh is set. Requires enableIntraRefresh to be\n                                 set. Will be disabled if NV_ENC_CONFIG::gopLength is not set to\n                                 NVENC_INFINITE_GOPLENGTH. */\n    uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames\n                                 for periodic intra refresh. This value should be smaller than\n                                 intraRefreshPeriod */\n    uint32_t maxNumRefFramesInDPB; /**< [in]: Specifies the maximum number of references frames in\n                                      the DPB.*/\n    uint32_t ltrNumFrames; /**< [in]: This parameter has different meaning in two LTR modes.\n                                      In \"LTR Trust\" mode (ltrTrustMode = 1), encoder will mark the\n                              first ltrNumFrames base layer reference frames within each IDR\n                              interval as LTR. In \"LTR Per Picture\" mode (ltrTrustMode = 0 and\n                              ltrMarkFrame = 1), ltrNumFrames specifies maximum number of LTR frames\n                              in DPB. These ltrNumFrames acts as a guidance to the encoder and are\n                              not necessarily honored. To achieve a right balance between the\n                              encoding quality and keeping LTR frames in the DPB queue, the encoder\n                              can internally limit the number of LTR frames. The number of LTR\n                              frames actually used depends upon the encoding preset being used;\n                              Faster encoding presets will use fewer LTR frames.*/\n    uint32_t vpsId; /**< [in]: Specifies the VPS id of the video parameter set */\n    uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */\n    uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */\n    uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the\n                           way in which the picture is divided into slices sliceMode = 0 CTU based\n                           slices, sliceMode = 1 Byte based slices, sliceMode = 2 CTU row based\n                           slices, sliceMode = 3, numSlices in Picture When sliceMode == 0 and\n                           sliceModeData == 0 whole picture will be coded with one slice */\n    uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For:\n                                        sliceMode = 0, sliceModeData specifies # of CTUs in each\n                               slice (except last slice) sliceMode = 1, sliceModeData specifies\n                               maximum # of bytes in each slice (except last slice) sliceMode = 2,\n                               sliceModeData specifies # of CTU rows in each slice (except last\n                               slice) sliceMode = 3, sliceModeData specifies number of slices in the\n                               picture. Driver will divide picture into slices optimally */\n    uint32_t maxTemporalLayersMinus1; /**< [in]: Specifies the max temporal layer used for\n                                         hierarchical coding. */\n    NV_ENC_CONFIG_HEVC_VUI_PARAMETERS\n        hevcVUIParameters; /**< [in]: Specifies the HEVC video usability info parameters */\n    uint32_t ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See comments near\n                              NV_ENC_CONFIG_HEVC::enableLTR for description of the two modes. Set to\n                              1 to use \"LTR Trust\" mode of LTR operation. Clients are discouraged to\n                              use \"LTR Trust\" mode as this mode may be deprecated in future\n                              releases. Set to 0 when using \"LTR Per Picture\" mode of LTR operation.\n                            */\n    NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode.\n                                               Check support for useBFramesAsRef mode using\n                                               ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/\n    NV_ENC_NUM_REF_FRAMES\n        numRefL0; /**< [in]: Specifies max number of reference frames in reference picture list L0,\n                     that can be used by hardware for prediction of a frame. Check support for\n                     numRefL0 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */\n    NV_ENC_NUM_REF_FRAMES\n        numRefL1; /**< [in]: Specifies max number of reference frames in reference picture list L1,\n                     that can be used by hardware for prediction of a frame. Check support for\n                     numRefL1 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */\n    uint32_t reserved1[214]; /**< [in]: Reserved and must be set to 0.*/\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG_HEVC;\n\n#define NV_MAX_TILE_COLS_AV1 64\n#define NV_MAX_TILE_ROWS_AV1 64\n\n/**\n * \\struct _NV_ENC_FILM_GRAIN_PARAMS_AV1\n * AV1 Film Grain Parameters structure\n */\n\ntypedef struct _NV_ENC_FILM_GRAIN_PARAMS_AV1 {\n    uint32_t applyGrain : 1; /**< [in]: Set to 1 to specify film grain should be added to frame */\n    uint32_t chromaScalingFromLuma : 1; /**< [in]: Set to 1 to specify the chroma scaling is\n                                           inferred from luma scaling */\n    uint32_t overlapFlag : 1; /**< [in]: Set to 1 to indicate that overlap between film grain blocks\n                                 should be applied*/\n    uint32_t clipToRestrictedRange : 1; /**< [in]: Set to 1 to clip values to restricted (studio)\n                                           range after adding film grain  */\n    uint32_t grainScalingMinus8 : 2; /**< [in]: Represents the shift - 8 applied to the values of\n                                        the chroma component */\n    uint32_t arCoeffLag : 2; /**< [in]: Specifies the number of auto-regressive coefficients for\n                                luma and chroma */\n    uint32_t numYPoints : 4; /**< [in]: Specifies the number of points for the piecewise linear\n                                scaling function of the luma component */\n    uint32_t numCbPoints : 4; /**< [in]: Specifies the number of points for the piecewise linear\n                                 scaling function of the cb component */\n    uint32_t numCrPoints : 4; /**< [in]: Specifies the number of points for the piecewise linear\n                                 scaling function of the cr component */\n    uint32_t arCoeffShiftMinus6 : 2; /**< [in]: specifies the range of the auto-regressive\n                                        coefficients */\n    uint32_t grainScaleShift : 2; /**< [in]: Specifies how much the Gaussian random numbers should\n                                     be scaled down during the grain synthesi process  */\n    uint32_t reserved1 : 8; /**< [in]: Reserved bits field - should be set to 0 */\n    uint8_t pointYValue[14]; /**< [in]: pointYValue[i]: x coordinate for i-th point of luma\n                                piecewise linear scaling function. Values on a scale of 0...255 */\n    uint8_t pointYScaling[14]; /**< [in]: pointYScaling[i]: i-th point output value of luma\n                                  piecewise linear scaling function */\n    uint8_t pointCbValue[10]; /**< [in]: pointCbValue[i]: x coordinate for i-th point of cb\n                                 piecewise linear scaling function. Values on a scale of 0...255 */\n    uint8_t pointCbScaling[10]; /**< [in]: pointCbScaling[i]: i-th point output value of cb\n                                   piecewise linear scaling function */\n    uint8_t pointCrValue[10]; /**< [in]: pointCrValue[i]: x coordinate for i-th point of cr\n                                 piecewise linear scaling function. Values on a scale of 0...255 */\n    uint8_t pointCrScaling[10]; /**< [in]: pointCrScaling[i]: i-th point output value of cr\n                                   piecewise linear scaling function */\n    uint8_t arCoeffsYPlus128[24]; /**< [in]: Specifies auto-regressive coefficients used for the Y\n                                     plane */\n    uint8_t arCoeffsCbPlus128[25]; /**< [in]: Specifies auto-regressive coefficients used for the U\n                                      plane */\n    uint8_t arCoeffsCrPlus128[25]; /**< [in]: Specifies auto-regressive coefficients used for the V\n                                      plane */\n    uint8_t reserved2[2]; /**< [in]: Reserved bytes -  should be set to 0 */\n    uint8_t cbMult; /**< [in]: Represents a multiplier for the cb component used in derivation of\n                       the input index to the cb component scaling function */\n    uint8_t cbLumaMult; /**< [in]: represents a multiplier for the average luma component used in\n                           derivation of the input index to the cb component scaling function. */\n    uint16_t cbOffset; /**< [in]: Represents an offset used in derivation of the input index to the\n                          cb component scaling function */\n    uint8_t crMult; /**< [in]: Represents a multiplier for the cr component used in derivation of\n                       the input index to the cr component scaling function */\n    uint8_t crLumaMult; /**< [in]: represents a multiplier for the average luma component used in\n                           derivation of the input index to the cr component scaling function. */\n    uint16_t crOffset; /**< [in]: Represents an offset used in derivation of the input index to the\n                          cr component scaling function */\n} NV_ENC_FILM_GRAIN_PARAMS_AV1;\n\n/**\n * \\struct _NV_ENC_CONFIG_AV1\n * AV1 encoder configuration parameters to be set during initialization.\n */\ntypedef struct _NV_ENC_CONFIG_AV1 {\n    uint32_t level; /**< [in]: Specifies the level of the encoded bitstream.*/\n    uint32_t tier; /**< [in]: Specifies the level tier of the encoded bitstream.*/\n    NV_ENC_AV1_PART_SIZE\n        minPartSize; /**< [in]: Specifies the minimum size of luma coding block partition.*/\n    NV_ENC_AV1_PART_SIZE\n        maxPartSize; /**< [in]: Specifies the maximum size of luma coding block partition.*/\n    uint32_t outputAnnexBFormat : 1; /**< [in]: Set 1 to use Annex B format for bitstream output.*/\n    uint32_t\n        enableTimingInfo : 1; /**< [in]: Set 1 to write Timing Info into sequence/frame headers */\n    uint32_t enableDecoderModelInfo : 1; /**< [in]: Set 1 to write Decoder Model Info into\n                                            sequence/frame headers */\n    uint32_t enableFrameIdNumbers : 1; /**< [in]: Set 1 to write Frame id numbers in  bitstream */\n    uint32_t disableSeqHdr : 1; /**< [in]: Set 1 to disable Sequence Header signaling in the\n                                   bitstream. */\n    uint32_t repeatSeqHdr : 1; /**< [in]: Set 1 to output Sequence Header for every Key frame.*/\n    uint32_t\n        enableIntraRefresh : 1; /**< [in]: Set 1 to enable gradual decoder refresh or intra refresh.\n                                   If the GOP structure uses B frames this will be ignored */\n    uint32_t chromaFormatIDC : 2; /**< [in]: Specifies the chroma format. Should be set to 1 for\n                                     yuv420 input (yuv444 input currently not supported).*/\n    uint32_t enableBitstreamPadding : 1; /**< [in]: Set 1 to enable bitstream padding. */\n    uint32_t\n        enableCustomTileConfig : 1; /**< [in]: Set 1 to enable custom tile configuration:\n                                       numTileColumns and numTileRows must have non zero values and\n                                       tileWidths and tileHeights must point to a valid address  */\n    uint32_t enableFilmGrainParams : 1; /**< [in]: Set 1 to enable custom film grain parameters:\n                                           filmGrainParams must point to a valid address  */\n    uint32_t\n        inputPixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth minus 8 of video input.\n                                         Should be set to 0 for 8 bit input, 2 for 10 bit input.*/\n    uint32_t\n        pixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth minus 8 of encoded video.\n                                    Should be set to 0 for 8 bit, 2 for 10 bit. HW will do the\n                                    bitdepth conversion internally from inputPixelBitDepthMinus8 ->\n                                    pixelBitDepthMinus8 if bit dpeths differ Support for 8 bit input\n                                    to 10 bit encode conversion only */\n    uint32_t reserved : 14; /**< [in]: Reserved bitfields.*/\n    uint32_t idrPeriod; /**< [in]: Specifies the IDR/Key frame interval. If not set, this is made\n                           equal to gopLength in NV_ENC_CONFIG.Low latency application client can\n                           set IDR interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not\n                           inserted automatically. */\n    uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh\n                                    if enableIntrarefresh is set. Requires enableIntraRefresh to be\n                                    set. Will be disabled if NV_ENC_CONFIG::gopLength is not set to\n                                    NVENC_INFINITE_GOPLENGTH. */\n    uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames\n                                 for periodic intra refresh. This value should be smaller than\n                                 intraRefreshPeriod */\n    uint32_t maxNumRefFramesInDPB; /**< [in]: Specifies the maximum number of references frames in\n                                      the DPB.*/\n    uint32_t\n        numTileColumns; /**< [in]: This parameter in conjunction with the flag\n                           enableCustomTileConfig and the array tileWidths[] specifies the way in\n                           which the picture is divided into tile columns. When\n                           enableCustomTileConfig == 0, the picture will be uniformly divided into\n                           numTileColumns tile columns. If numTileColumns is not a power of 2, it\n                           will be rounded down to the next power of 2 value. If numTileColumns ==\n                           0, the picture will be coded with the smallest number of vertical tiles\n                           as allowed by standard. When enableCustomTileConfig == 1, numTileColumns\n                           must be > 0 and <= NV_MAX_TILE_COLS_AV1 and tileWidths must point to a\n                           valid array of numTileColumns entries. Entry i specifies the width in\n                           64x64 CTU unit of tile colum i. The sum of all the entries should be\n                           equal to the picture width in 64x64 CTU units. */\n    uint32_t\n        numTileRows; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig\n                        and the array tileHeights[] specifies the way in which the picture is\n                        divided into tiles rows When enableCustomTileConfig == 0, the picture will\n                        be uniformly divided into numTileRows tile rows. If numTileRows is not a\n                        power of 2, it will be rounded down to the next power of 2 value. If\n                        numTileRows == 0, the picture will be coded with the smallest number of\n                        horizontal tiles as allowed by standard. When enableCustomTileConfig == 1,\n                        numTileRows must be > 0 and <= NV_MAX_TILE_ROWS_AV1 and tileHeights must\n                        point to a valid array of numTileRows entries. Entry i specifies the height\n                        in 64x64 CTU unit of tile row i. The sum of all the entries should be equal\n                        to the picture hieght in 64x64 CTU units. */\n    uint32_t*\n        tileWidths; /**< [in]: If enableCustomTileConfig == 1, tileWidths[i] specifies the width of\n                       tile column i in 64x64 CTU unit, with 0 <= i <= numTileColumns -1. */\n    uint32_t*\n        tileHeights; /**< [in]: If enableCustomTileConfig == 1, tileHeights[i] specifies the height\n                        of tile row i in 64x64 CTU unit, with 0 <= i <= numTileRows -1. */\n    uint32_t maxTemporalLayersMinus1; /**< [in]: Specifies the max temporal layer used for\n                                         hierarchical coding. */\n    NV_ENC_VUI_COLOR_PRIMARIES\n        colorPrimaries; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */\n    NV_ENC_VUI_TRANSFER_CHARACTERISTIC\n        transferCharacteristics; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */\n    NV_ENC_VUI_MATRIX_COEFFS\n        matrixCoefficients; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */\n    uint32_t colorRange; /**< [in]: 0: studio swing representation - 1: full swing representation */\n    uint32_t\n        chromaSamplePosition; /**< [in]: 0: unknown\n                                         1: Horizontally collocated with luma (0,0) sample, between\n                                 two vertical samples 2: Co-located with luma (0,0) sample */\n    NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode.\n                                               Check support for useBFramesAsRef mode using\n                                               ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/\n    NV_ENC_FILM_GRAIN_PARAMS_AV1*\n        filmGrainParams; /**< [in]: If enableFilmGrainParams == 1, filmGrainParams must point to a\n                            valid NV_ENC_FILM_GRAIN_PARAMS_AV1 structure */\n    NV_ENC_NUM_REF_FRAMES\n        numFwdRefs; /**< [in]: Specifies max number of forward reference frame used for prediction\n                       of a frame. It must be in range 1-4 (Last, Last2, last3 and Golden). It's a\n                       suggestive value not necessarily be honored always. */\n    NV_ENC_NUM_REF_FRAMES\n        numBwdRefs; /**< [in]: Specifies max number of L1 list reference frame used for prediction\n                       of a frame. It must be in range 1-3 (Backward, Altref2, Altref). It's a\n                       suggestive value not necessarily be honored always. */\n    uint32_t reserved1[235]; /**< [in]: Reserved and must be set to 0.*/\n    void* reserved2[62]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG_AV1;\n\n/**\n * \\struct _NV_ENC_CONFIG_H264_MEONLY\n * H264 encoder configuration parameters for ME only Mode\n *\n */\ntypedef struct _NV_ENC_CONFIG_H264_MEONLY {\n    uint32_t disablePartition16x16 : 1; /**< [in]: Disable Motion Estimation on 16x16 blocks*/\n    uint32_t disablePartition8x16 : 1; /**< [in]: Disable Motion Estimation on 8x16 blocks*/\n    uint32_t disablePartition16x8 : 1; /**< [in]: Disable Motion Estimation on 16x8 blocks*/\n    uint32_t disablePartition8x8 : 1; /**< [in]: Disable Motion Estimation on 8x8 blocks*/\n    uint32_t disableIntraSearch : 1; /**< [in]: Disable Intra search during Motion Estimation*/\n    uint32_t bStereoEnable : 1; /**< [in]: Enable Stereo Mode for Motion Estimation where each view\n                                   is independently executed*/\n    uint32_t reserved : 26; /**< [in]: Reserved and must be set to 0 */\n    uint32_t reserved1[255]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG_H264_MEONLY;\n\n/**\n * \\struct _NV_ENC_CONFIG_HEVC_MEONLY\n * HEVC encoder configuration parameters for ME only Mode\n *\n */\ntypedef struct _NV_ENC_CONFIG_HEVC_MEONLY {\n    uint32_t reserved[256]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved1[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG_HEVC_MEONLY;\n\n/**\n * \\struct _NV_ENC_CODEC_CONFIG\n * Codec-specific encoder configuration parameters to be set during initialization.\n */\ntypedef union _NV_ENC_CODEC_CONFIG {\n    NV_ENC_CONFIG_H264 h264Config; /**< [in]: Specifies the H.264-specific encoder configuration. */\n    NV_ENC_CONFIG_HEVC hevcConfig; /**< [in]: Specifies the HEVC-specific encoder configuration. */\n    NV_ENC_CONFIG_AV1 av1Config; /**< [in]: Specifies the AV1-specific encoder configuration. */\n    NV_ENC_CONFIG_H264_MEONLY\n        h264MeOnlyConfig; /**< [in]: Specifies the H.264-specific ME only encoder configuration. */\n    NV_ENC_CONFIG_HEVC_MEONLY\n        hevcMeOnlyConfig; /**< [in]: Specifies the HEVC-specific ME only encoder configuration. */\n    uint32_t reserved[320]; /**< [in]: Reserved and must be set to 0 */\n} NV_ENC_CODEC_CONFIG;\n\n/**\n * \\struct _NV_ENC_CONFIG\n * Encoder configuration parameters to be set during initialization.\n */\ntypedef struct _NV_ENC_CONFIG {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CONFIG_VER. */\n    GUID profileGUID; /**< [in]: Specifies the codec profile GUID. If client specifies \\p\n                         NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID the NvEncodeAPI interface will select\n                         the appropriate codec profile. */\n    uint32_t gopLength; /**< [in]: Specifies the number of pictures in one GOP. Low latency\n                           application client can set goplength to NVENC_INFINITE_GOPLENGTH so that\n                           keyframes are not inserted automatically. */\n    int32_t frameIntervalP; /**< [in]: Specifies the GOP pattern as follows: \\p frameIntervalP = 0:\n                               I, 1: IPP, 2: IBP, 3: IBBP  If goplength is set to\n                               NVENC_INFINITE_GOPLENGTH \\p frameIntervalP should be set to 1. */\n    uint32_t monoChromeEncoding; /**< [in]: Set this to 1 to enable monochrome encoding for this\n                                    session. */\n    NV_ENC_PARAMS_FRAME_FIELD_MODE\n        frameFieldMode; /**< [in]: Specifies the frame/field mode.\n                                   Check support for field encoding using\n                           ::NV_ENC_CAPS_SUPPORT_FIELD_ENCODING caps. Using a frameFieldMode other\n                           than NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME for RGB input is not supported.\n                         */\n    NV_ENC_MV_PRECISION\n        mvPrecision; /**< [in]: Specifies the desired motion vector prediction precision. */\n    NV_ENC_RC_PARAMS rcParams; /**< [in]: Specifies the rate control parameters for the current\n                                  encoding session. */\n    NV_ENC_CODEC_CONFIG encodeCodecConfig; /**< [in]: Specifies the codec specific config parameters\n                                              through this union. */\n    uint32_t reserved[278]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_CONFIG;\n\n/** macro for constructing the version field of ::_NV_ENC_CONFIG */\n#define NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(8) | (1 << 31))\n\n/**\n *  Tuning information of NVENC encoding (TuningInfo is not applicable to H264 and HEVC MEOnly\n * mode).\n */\ntypedef enum NV_ENC_TUNING_INFO {\n    NV_ENC_TUNING_INFO_UNDEFINED = 0, /**< Undefined tuningInfo. Invalid value for encoding. */\n    NV_ENC_TUNING_INFO_HIGH_QUALITY = 1, /**< Tune presets for latency tolerant encoding.*/\n    NV_ENC_TUNING_INFO_LOW_LATENCY = 2, /**< Tune presets for low latency streaming.*/\n    NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY = 3, /**< Tune presets for ultra low latency streaming.*/\n    NV_ENC_TUNING_INFO_LOSSLESS = 4, /**< Tune presets for lossless encoding.*/\n    NV_ENC_TUNING_INFO_COUNT /**< Count number of tuningInfos. Invalid value. */\n} NV_ENC_TUNING_INFO;\n\n/**\n * \\struct _NV_ENC_INITIALIZE_PARAMS\n * Encode Session Initialization parameters.\n */\ntypedef struct _NV_ENC_INITIALIZE_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_INITIALIZE_PARAMS_VER. */\n    GUID encodeGUID; /**< [in]: Specifies the Encode GUID for which the encoder is being created.\n                        ::NvEncInitializeEncoder() API will fail if this is not set, or set to\n                        unsupported value. */\n    GUID presetGUID; /**< [in]: Specifies the preset for encoding. If the preset GUID is set then ,\n                        the preset configuration will be applied before any other parameter. */\n    uint32_t encodeWidth; /**< [in]: Specifies the encode width. If not set\n                             ::NvEncInitializeEncoder() API will fail. */\n    uint32_t encodeHeight; /**< [in]: Specifies the encode height. If not set\n                              ::NvEncInitializeEncoder() API will fail. */\n    uint32_t darWidth; /**< [in]: Specifies the display aspect ratio width (H264/HEVC) or the render\n                          width (AV1). */\n    uint32_t darHeight; /**< [in]: Specifies the display aspect ratio height (H264/HEVC) or the\n                           render height (AV1). */\n    uint32_t frameRateNum; /**< [in]: Specifies the numerator for frame rate used for encoding in\n                              frames per second ( Frame rate = frameRateNum / frameRateDen ). */\n    uint32_t frameRateDen; /**< [in]: Specifies the denominator for frame rate used for encoding in\n                              frames per second ( Frame rate = frameRateNum / frameRateDen ). */\n    uint32_t enableEncodeAsync; /**< [in]: Set this to 1 to enable asynchronous mode and is expected\n                                   to use events to get picture completion notification. */\n    uint32_t enablePTD; /**< [in]: Set this to 1 to enable the Picture Type Decision is be taken by\n                           the NvEncodeAPI interface. */\n    uint32_t reportSliceOffsets : 1; /**< [in]: Set this to 1 to enable reporting slice offsets in\n                                        ::_NV_ENC_LOCK_BITSTREAM.\n                                        NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync must be set to 0\n                                        to use this feature. Client must set this to 0 if\n                                        NV_ENC_CONFIG_H264::sliceMode is 1 on Kepler GPUs */\n    uint32_t\n        enableSubFrameWrite : 1; /**< [in]: Set this to 1 to write out available bitstream to memory\n                                    at subframe intervals. If enableSubFrameWrite = 1, then the\n                                    hardware encoder returns data as soon as a slice (H264/HEVC) or\n                                    tile (AV1) has completed encoding. This results in better\n                                    encoding latency, but the downside is that the application has\n                                    to keep polling via a call to nvEncLockBitstream API\n                                    continuously to see if any encoded slice/tile data is available.\n                                            Use this mode if you feel that the marginal reduction in\n                                    latency from sub-frame encoding is worth the increase in\n                                    complexity due to CPU-based polling. */\n    uint32_t enableExternalMEHints : 1; /**< [in]: Set to 1 to enable external ME hints for the\n                                           current frame. For NV_ENC_INITIALIZE_PARAMS::enablePTD=1\n                                           with B frames, programming L1 hints is optional for B\n                                           frames since Client doesn't know internal GOP structure.\n                                                   NV_ENC_PIC_PARAMS::meHintRefPicDist should\n                                           preferably be set with enablePTD=1. */\n    uint32_t enableMEOnlyMode : 1; /**< [in]: Set to 1 to enable ME Only Mode .*/\n    uint32_t\n        enableWeightedPrediction : 1; /**< [in]: Set this to 1 to enable weighted prediction. Not\n                                         supported if encode session is configured for B-Frames\n                                         (i.e. NV_ENC_CONFIG::frameIntervalP > 1 or preset >=P3 when\n                                         tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or\n                                                 tuningInfo = ::NV_ENC_TUNING_INFO_LOSSLESS. This is\n                                         because preset >=p3 internally enables B frames when\n                                         tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or\n                                         ::NV_ENC_TUNING_INFO_LOSSLESS). */\n    uint32_t enableOutputInVidmem : 1; /**< [in]: Set this to 1 to enable output of NVENC in video\n                                          memory buffer created by application. This feature is not\n                                          supported for HEVC ME only mode. */\n    uint32_t reservedBitFields : 26; /**< [in]: Reserved bitfields and must be set to 0 */\n    uint32_t privDataSize; /**< [in]: Reserved private data buffer size and must be set to 0 */\n    void* privData; /**< [in]: Reserved private data buffer and must be set to NULL */\n    NV_ENC_CONFIG*\n        encodeConfig; /**< [in]: Specifies the advanced codec specific structure. If client has sent\n                         a valid codec config structure, it will override parameters set by the\n                         NV_ENC_INITIALIZE_PARAMS::presetGUID parameter. If set to NULL the\n                         NvEncodeAPI interface will use the NV_ENC_INITIALIZE_PARAMS::presetGUID to\n                         set the codec specific parameters. Client can also optionally query the\n                         NvEncodeAPI interface to get codec specific parameters for a presetGUID\n                         using ::NvEncGetEncodePresetConfig() API. It can then modify (if required)\n                         some of the codec config parameters and send down a custom config structure\n                         as part of ::_NV_ENC_INITIALIZE_PARAMS. Even in this case client is\n                         recommended to pass the same preset guid it has used in\n                         ::NvEncGetEncodePresetConfig() API to query the config structure; as\n                         NV_ENC_INITIALIZE_PARAMS::presetGUID. This will not override the custom\n                         config structure but will be used to determine other Encoder HW specific\n                         parameters not exposed in the API. */\n    uint32_t maxEncodeWidth; /**< [in]: Maximum encode width to be used for current Encode session.\n                                        Client should allocate output buffers according to this\n                                dimension for dynamic resolution change. If set to 0, Encoder will\n                                not allow dynamic resolution change. */\n    uint32_t maxEncodeHeight; /**< [in]: Maximum encode height to be allowed for current Encode\n                                 session. Client should allocate output buffers according to this\n                                 dimension for dynamic resolution change. If set to 0, Encode will\n                                 not allow dynamic resolution change. */\n    NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE maxMEHintCountsPerBlock\n        [2]; /**< [in]: If Client wants to pass external motion vectors in\n                NV_ENC_PIC_PARAMS::meExternalHints buffer it must specify the maximum number of hint\n                candidates per block per direction for the encode session. The\n                NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] is for L0 predictors and\n                NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] is for L1 predictors. This\n                client must also set NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints to 1. */\n    NV_ENC_TUNING_INFO tuningInfo; /**< [in]: Tuning Info of NVENC encoding(TuningInfo is not\n                                      applicable to H264 and HEVC meonly mode). */\n    NV_ENC_BUFFER_FORMAT\n        bufferFormat; /**< [in]: Input buffer format. Used only when DX12 interface type is used */\n    uint32_t reserved[287]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_INITIALIZE_PARAMS;\n\n/** macro for constructing the version field of ::_NV_ENC_INITIALIZE_PARAMS */\n#define NV_ENC_INITIALIZE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(5) | (1 << 31))\n\n/**\n * \\struct _NV_ENC_RECONFIGURE_PARAMS\n * Encode Session Reconfigured parameters.\n */\ntypedef struct _NV_ENC_RECONFIGURE_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_RECONFIGURE_PARAMS_VER. */\n    NV_ENC_INITIALIZE_PARAMS\n        reInitEncodeParams; /**< [in]: Encoder session re-initialization parameters.\n                                       If reInitEncodeParams.encodeConfig is NULL and\n                                       reInitEncodeParams.presetGUID is the same as the preset\n                                       GUID specified on the call to NvEncInitializeEncoder(),\n                                       EncodeAPI will continue to use the existing encode\n                                       configuration.\n                                       If reInitEncodeParams.encodeConfig is NULL and\n                                       reInitEncodeParams.presetGUID is different from the preset\n                                       GUID specified on the call to NvEncInitializeEncoder(),\n                                       EncodeAPI will try to use the default configuration for\n                                       the preset specified by reInitEncodeParams.presetGUID.\n                                       In this case, reconfiguration may fail if the new\n                                       configuration is incompatible with the existing\n                                       configuration (e.g. the new configuration results in\n                                       a change in the GOP structure). */\n    uint32_t resetEncoder : 1; /**< [in]: This resets the rate control states and other internal\n                                  encoder states. This should be used only with an IDR frame. If\n                                  NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1, encoder will\n                                  force the frame type to IDR */\n    uint32_t forceIDR : 1; /**< [in]: Encode the current picture as an IDR picture. This flag is\n                              only valid when Picture type decision is taken by the Encoder\n                                      [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */\n    uint32_t reserved : 30;\n\n} NV_ENC_RECONFIGURE_PARAMS;\n\n/** macro for constructing the version field of ::_NV_ENC_RECONFIGURE_PARAMS */\n#define NV_ENC_RECONFIGURE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(1) | (1 << 31))\n\n/**\n * \\struct _NV_ENC_PRESET_CONFIG\n * Encoder preset config\n */\ntypedef struct _NV_ENC_PRESET_CONFIG {\n    uint32_t version; /**< [in]:  Struct version. Must be set to ::NV_ENC_PRESET_CONFIG_VER. */\n    NV_ENC_CONFIG\n        presetCfg; /**< [out]: preset config returned by the Nvidia Video Encoder interface. */\n    uint32_t reserved1[255]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_PRESET_CONFIG;\n\n/** macro for constructing the version field of ::_NV_ENC_PRESET_CONFIG */\n#define NV_ENC_PRESET_CONFIG_VER (NVENCAPI_STRUCT_VERSION(4) | (1 << 31))\n\n/**\n * \\struct _NV_ENC_PIC_PARAMS_MVC\n * MVC-specific parameters to be sent on a per-frame basis.\n */\ntypedef struct _NV_ENC_PIC_PARAMS_MVC {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_PIC_PARAMS_MVC_VER. */\n    uint32_t viewID; /**< [in]: Specifies the view ID associated with the current input view. */\n    uint32_t\n        temporalID; /**< [in]: Specifies the temporal ID associated with the current input view. */\n    uint32_t priorityID; /**< [in]: Specifies the priority ID associated with the current input\n                            view. Reserved and ignored by the NvEncodeAPI interface. */\n    uint32_t reserved1[12]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved2[8]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_PIC_PARAMS_MVC;\n\n/** macro for constructing the version field of ::_NV_ENC_PIC_PARAMS_MVC */\n#define NV_ENC_PIC_PARAMS_MVC_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\union _NV_ENC_PIC_PARAMS_H264_EXT\n * H264 extension  picture parameters\n */\ntypedef union _NV_ENC_PIC_PARAMS_H264_EXT {\n    NV_ENC_PIC_PARAMS_MVC mvcPicParams; /**< [in]: Specifies the MVC picture parameters. */\n    uint32_t reserved1[32]; /**< [in]: Reserved and must be set to 0.        */\n} NV_ENC_PIC_PARAMS_H264_EXT;\n\n/**\n * \\struct _NV_ENC_SEI_PAYLOAD\n *  User SEI message\n */\ntypedef struct _NV_ENC_SEI_PAYLOAD {\n    uint32_t payloadSize; /**< [in] SEI payload size in bytes. SEI payload must be byte aligned, as\n                             described in Annex D */\n    uint32_t payloadType; /**< [in] SEI payload types and syntax can be found in Annex D of the\n                             H.264 Specification. */\n    uint8_t* payload; /**< [in] pointer to user data */\n} NV_ENC_SEI_PAYLOAD;\n\n#define NV_ENC_H264_SEI_PAYLOAD NV_ENC_SEI_PAYLOAD\n\n/**\n * \\struct _NV_ENC_PIC_PARAMS_H264\n * H264 specific enc pic params. sent on a per frame basis.\n */\ntypedef struct _NV_ENC_PIC_PARAMS_H264 {\n    uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be\n                                  set if client is handling the picture type decision. */\n    uint32_t reserved3; /**< [in]: Reserved and must be set to 0 */\n    uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if\n                            NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t colourPlaneId; /**< [in]: Specifies the colour plane ID associated with the current\n                               input. */\n    uint32_t\n        forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to\n                                          intraRefreshFrameCnt. When outputRecoveryPointSEI is set\n                                          this is value is used for recovery_frame_cnt in recovery\n                                          point SEI message forceIntraRefreshWithFrameCnt cannot be\n                                          used if B frames are used in the GOP structure specified\n                                        */\n    uint32_t constrainedFrame : 1; /**< [in]: Set to 1 if client wants to encode this frame with\n                                      each slice completely independent of other slices in the\n                                      frame. NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding\n                                      should be set to 1 */\n    uint32_t sliceModeDataUpdate : 1; /**< [in]: Set to 1 if client wants to change the\n                                         sliceModeData field to specify new sliceSize Parameter When\n                                         forceIntraRefreshWithFrameCnt is set it will have priority\n                                         over sliceMode setting */\n    uint32_t ltrMarkFrame : 1; /**< [in]: Set to 1 if client wants to mark this frame as LTR */\n    uint32_t ltrUseFrames : 1; /**< [in]: Set to 1 if client allows encoding this frame using the\n                                  LTR frames specified in ltrFrameBitmap */\n    uint32_t reservedBitFields : 28; /**< [in]: Reserved bit fields and must be set to 0 */\n    uint8_t* sliceTypeData; /**< [in]: Deprecated. */\n    uint32_t sliceTypeArrayCnt; /**< [in]: Deprecated. */\n    uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in\n                                    seiPayloadArray array. */\n    NV_ENC_SEI_PAYLOAD*\n        seiPayloadArray; /**< [in]: Array of SEI payloads which will be inserted for this frame. */\n    uint32_t\n        sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in\n                      which the picture is divided into slices sliceMode = 0 MB based slices,\n                      sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode\n                      = 3, numSlices in Picture When forceIntraRefreshWithFrameCnt is set it will\n                      have priority over sliceMode setting When sliceMode == 0 and sliceModeData ==\n                      0 whole picture will be coded with one slice */\n    uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For:\n                                       sliceMode = 0, sliceModeData specifies # of MBs in each slice\n                               (except last slice) sliceMode = 1, sliceModeData specifies maximum #\n                               of bytes in each slice (except last slice) sliceMode = 2,\n                               sliceModeData specifies # of MB rows in each slice (except last\n                               slice) sliceMode = 3, sliceModeData specifies number of slices in the\n                               picture. Driver will divide picture into slices optimally */\n    uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term referenceframe index to use for\n                                 marking this frame as LTR.*/\n    uint32_t ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame indices to\n                                   use when encoding this frame. */\n    uint32_t\n        ltrUsageMode; /**< [in]: Not supported. Reserved for future use and must be set to 0. */\n    uint32_t\n        forceIntraSliceCount; /**< [in]: Specifies the number of slices to be forced to Intra in the\n                                 current picture. This option along with forceIntraSliceIdx[] array\n                                 needs to be used with sliceMode = 3 only */\n    uint32_t* forceIntraSliceIdx; /**< [in]: Slice indices to be forced to intra in the current\n                                     picture. Each slice index should be <= num_slices_in_picture\n                                     -1. Index starts from 0 for first slice. The number of entries\n                                     in this array should be equal to forceIntraSliceCount */\n    NV_ENC_PIC_PARAMS_H264_EXT h264ExtPicParams; /**< [in]: Specifies the H264 extension config\n                                                    parameters using this config. */\n    NV_ENC_TIME_CODE\n        timeCode; /**< [in]: Specifies the clock timestamp sets used in picture timing SEI.\n                     Applicable only when NV_ENC_CONFIG_H264::enableTimeCode is set to 1. */\n    uint32_t reserved[203]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved2[61]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_PIC_PARAMS_H264;\n\n/**\n * \\struct _NV_ENC_PIC_PARAMS_HEVC\n * HEVC specific enc pic params. sent on a per frame basis.\n */\ntypedef struct _NV_ENC_PIC_PARAMS_HEVC {\n    uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be\n                                  set if client is handling the picture type decision. */\n    uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if\n                            NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t temporalId; /**< [in]: Specifies the temporal id of the picture */\n    uint32_t\n        forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to\n                                          intraRefreshFrameCnt. When outputRecoveryPointSEI is set\n                                          this is value is used for recovery_frame_cnt in recovery\n                                          point SEI message forceIntraRefreshWithFrameCnt cannot be\n                                          used if B frames are used in the GOP structure specified\n                                        */\n    uint32_t constrainedFrame : 1; /**< [in]: Set to 1 if client wants to encode this frame with\n                                      each slice completely independent of other slices in the\n                                      frame. NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding\n                                      should be set to 1 */\n    uint32_t sliceModeDataUpdate : 1; /**< [in]: Set to 1 if client wants to change the\n                                         sliceModeData field to specify new sliceSize Parameter When\n                                         forceIntraRefreshWithFrameCnt is set it will have priority\n                                         over sliceMode setting */\n    uint32_t ltrMarkFrame : 1; /**< [in]: Set to 1 if client wants to mark this frame as LTR */\n    uint32_t ltrUseFrames : 1; /**< [in]: Set to 1 if client allows encoding this frame using the\n                                  LTR frames specified in ltrFrameBitmap */\n    uint32_t reservedBitFields : 28; /**< [in]: Reserved bit fields and must be set to 0 */\n    uint8_t* sliceTypeData; /**< [in]: Array which specifies the slice type used to force intra\n                               slice for a particular slice. Currently supported only for\n                               NV_ENC_CONFIG_H264::sliceMode == 3. Client should allocate array of\n                               size sliceModeData where sliceModeData is specified in field of\n                               ::_NV_ENC_CONFIG_H264 Array element with index n corresponds to nth\n                               slice. To force a particular slice to intra client should set\n                               corresponding array element to NV_ENC_SLICE_TYPE_I all other array\n                               elements should be set to NV_ENC_SLICE_TYPE_DEFAULT */\n    uint32_t sliceTypeArrayCnt; /**< [in]: Client should set this to the number of elements\n                                   allocated in sliceTypeData array. If sliceTypeData is NULL then\n                                   this should be set to 0 */\n    uint32_t\n        sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in\n                      which the picture is divided into slices sliceMode = 0 CTU based slices,\n                      sliceMode = 1 Byte based slices, sliceMode = 2 CTU row based slices, sliceMode\n                      = 3, numSlices in Picture When forceIntraRefreshWithFrameCnt is set it will\n                      have priority over sliceMode setting When sliceMode == 0 and sliceModeData ==\n                      0 whole picture will be coded with one slice */\n    uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For:\n                                       sliceMode = 0, sliceModeData specifies # of CTUs in each\n                               slice (except last slice) sliceMode = 1, sliceModeData specifies\n                               maximum # of bytes in each slice (except last slice) sliceMode = 2,\n                               sliceModeData specifies # of CTU rows in each slice (except last\n                               slice) sliceMode = 3, sliceModeData specifies number of slices in the\n                               picture. Driver will divide picture into slices optimally */\n    uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term reference frame index to use for\n                                 marking this frame as LTR.*/\n    uint32_t ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame indices to\n                                   use when encoding this frame. */\n    uint32_t\n        ltrUsageMode; /**< [in]: Not supported. Reserved for future use and must be set to 0. */\n    uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in\n                                    seiPayloadArray array. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0. */\n    NV_ENC_SEI_PAYLOAD*\n        seiPayloadArray; /**< [in]: Array of SEI payloads which will be inserted for this frame. */\n    NV_ENC_TIME_CODE\n        timeCode; /**< [in]: Specifies the clock timestamp sets used in time code SEI. Applicable\n                     only when NV_ENC_CONFIG_HEVC::enableTimeCodeSEI is set to 1. */\n    uint32_t reserved2[237]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_PIC_PARAMS_HEVC;\n\n#define NV_ENC_AV1_OBU_PAYLOAD NV_ENC_SEI_PAYLOAD\n\n/**\n * \\struct _NV_ENC_PIC_PARAMS_AV1\n * AV1 specific enc pic params. sent on a per frame basis.\n */\ntypedef struct _NV_ENC_PIC_PARAMS_AV1 {\n    uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be\n                                  set if client is handling the picture type decision. */\n    uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if\n                            NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t temporalId; /**< [in]: Specifies the temporal id of the picture */\n    uint32_t\n        forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to\n                                          intraRefreshFrameCnt. forceIntraRefreshWithFrameCnt cannot\n                                          be used if B frames are used in the GOP structure\n                                          specified */\n    uint32_t goldenFrameFlag : 1; /**< [in]: Encode frame as Golden Frame. This is ignored if\n                                     NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t arfFrameFlag : 1; /**< [in]: Encode frame as Alternate Reference Frame. This is ignored\n                                  if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t arf2FrameFlag : 1; /**< [in]: Encode frame as Alternate Reference 2 Frame. This is\n                                   ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t bwdFrameFlag : 1; /**< [in]: Encode frame as Backward Reference Frame. This is ignored\n                                  if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t overlayFrameFlag : 1; /**< [in]: Encode frame as overlay frame. A previously encoded\n                                      frame with the same displayPOCSyntax value should be present\n                                      in reference frame buffer. This is ignored if\n                                      NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t\n        showExistingFrameFlag : 1; /**< [in]: When ovelayFrameFlag is set to 1, this flag controls\n                                      the value of the show_existing_frame syntax element associated\n                                      with the overlay frame. This flag is added to the interface as\n                                      a placeholder. Its value is ignored for now and always assumed\n                                      to be set to 1. This is ignored if\n                                      NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */\n    uint32_t errorResilientModeFlag : 1; /**< [in]: encode frame independently from previously\n                                            encoded frames */\n\n    uint32_t tileConfigUpdate : 1; /**< [in]: Set to 1 if client wants to overwrite the default tile\n                                      configuration with the tile parameters specified below When\n                                      forceIntraRefreshWithFrameCnt is set it will have priority\n                                      over tileConfigUpdate setting */\n    uint32_t\n        enableCustomTileConfig : 1; /**< [in]: Set 1 to enable custom tile configuration:\n                                       numTileColumns and numTileRows must have non zero values and\n                                       tileWidths and tileHeights must point to a valid address  */\n    uint32_t\n        filmGrainParamsUpdate : 1; /**< [in]: Set to 1 if client wants to update previous film grain\n                                      parameters: filmGrainParams must point to a valid address and\n                                      encoder must have been configured with film grain enabled  */\n    uint32_t reservedBitFields : 22; /**< [in]: Reserved bitfields and must be set to 0 */\n    uint32_t\n        numTileColumns; /**< [in]: This parameter in conjunction with the flag\n                           enableCustomTileConfig and the array tileWidths[] specifies the way in\n                           which the picture is divided into tile columns. When\n                           enableCustomTileConfig == 0, the picture will be uniformly divided into\n                           numTileColumns tile columns. If numTileColumns is not a power of 2, it\n                           will be rounded down to the next power of 2 value. If numTileColumns ==\n                           0, the picture will be coded with the smallest number of vertical tiles\n                           as allowed by standard. When enableCustomTileConfig == 1, numTileColumns\n                           must be > 0 and <= NV_MAX_TILE_COLS_AV1 and tileWidths must point to a\n                           valid array of numTileColumns entries. Entry i specifies the width in\n                           64x64 CTU unit of tile colum i. The sum of all the entries should be\n                           equal to the picture width in 64x64 CTU units. */\n    uint32_t\n        numTileRows; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig\n                        and the array tileHeights[] specifies the way in which the picture is\n                        divided into tiles rows When enableCustomTileConfig == 0, the picture will\n                        be uniformly divided into numTileRows tile rows. If numTileRows is not a\n                        power of 2, it will be rounded down to the next power of 2 value. If\n                        numTileRows == 0, the picture will be coded with the smallest number of\n                        horizontal tiles as allowed by standard. When enableCustomTileConfig == 1,\n                        numTileRows must be > 0 and <= NV_MAX_TILE_ROWS_AV1 and tileHeights must\n                        point to a valid array of numTileRows entries. Entry i specifies the height\n                        in 64x64 CTU unit of tile row i. The sum of all the entries should be equal\n                        to the picture hieght in 64x64 CTU units. */\n    uint32_t*\n        tileWidths; /**< [in]: If enableCustomTileConfig == 1, tileWidths[i] specifies the width of\n                       tile column i in 64x64 CTU unit, with 0 <= i <= numTileColumns -1. */\n    uint32_t*\n        tileHeights; /**< [in]: If enableCustomTileConfig == 1, tileHeights[i] specifies the height\n                        of tile row i in 64x64 CTU unit, with 0 <= i <= numTileRows -1. */\n    uint32_t obuPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in\n                                    obuPayloadArray array. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0. */\n    NV_ENC_AV1_OBU_PAYLOAD*\n        obuPayloadArray; /**< [in]: Array of OBU payloads which will be inserted for this frame. */\n    NV_ENC_FILM_GRAIN_PARAMS_AV1*\n        filmGrainParams; /**< [in]: If filmGrainParamsUpdate == 1, filmGrainParams must point to a\n                            valid NV_ENC_FILM_GRAIN_PARAMS_AV1 structure */\n    uint32_t reserved2[247]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_PIC_PARAMS_AV1;\n\n/**\n * Codec specific per-picture encoding parameters.\n */\ntypedef union _NV_ENC_CODEC_PIC_PARAMS {\n    NV_ENC_PIC_PARAMS_H264 h264PicParams; /**< [in]: H264 encode picture params. */\n    NV_ENC_PIC_PARAMS_HEVC hevcPicParams; /**< [in]: HEVC encode picture params. */\n    NV_ENC_PIC_PARAMS_AV1 av1PicParams; /**< [in]: AV1 encode picture params. */\n    uint32_t reserved[256]; /**< [in]: Reserved and must be set to 0. */\n} NV_ENC_CODEC_PIC_PARAMS;\n\n/**\n * \\struct _NV_ENC_PIC_PARAMS\n * Encoding parameters that need to be sent on a per frame basis.\n */\ntypedef struct _NV_ENC_PIC_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_PIC_PARAMS_VER. */\n    uint32_t inputWidth; /**< [in]: Specifies the input frame width */\n    uint32_t inputHeight; /**< [in]: Specifies the input frame height */\n    uint32_t inputPitch; /**< [in]: Specifies the input buffer pitch. If pitch value is not known,\n                            set this to inputWidth. */\n    uint32_t encodePicFlags; /**< [in]: Specifies bit-wise OR of encode picture flags. See\n                                ::NV_ENC_PIC_FLAGS enum. */\n    uint32_t frameIdx; /**< [in]: Specifies the frame index associated with the input frame\n                          [optional]. */\n    uint64_t\n        inputTimeStamp; /**< [in]: Specifies opaque data which is associated with the encoded frame,\n                           but not actually encoded in the output bitstream. This opaque data can be\n                           used later to uniquely refer to the corresponding encoded frame. For\n                           example, it can be used for identifying the frame to be invalidated in\n                           the reference picture buffer, if lost at the client. */\n    uint64_t inputDuration; /**< [in]: Specifies duration of the input picture */\n    NV_ENC_INPUT_PTR inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must use a\n                                     pointer obtained from ::NvEncCreateInputBuffer() or\n                                     ::NvEncMapInputResource() APIs.*/\n    NV_ENC_OUTPUT_PTR\n        outputBitstream; /**< [in]: Specifies the output buffer pointer.\n                                    If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 0,\n                            specifies the pointer to output buffer. Client should use a pointer\n                            obtained from ::NvEncCreateBitstreamBuffer() API. If\n                            NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 1, client\n                            should allocate buffer in video memory for NV_ENC_ENCODE_OUT_PARAMS\n                            struct and encoded bitstream data. Client should use a pointer obtained\n                            from ::NvEncMapInputResource() API, when mapping this output buffer and\n                            assign it to NV_ENC_PIC_PARAMS::outputBitstream. First 256 bytes of this\n                            buffer should be interpreted as NV_ENC_ENCODE_OUT_PARAMS struct followed\n                            by encoded bitstream data. Recommended size for output buffer is sum of\n                            size of NV_ENC_ENCODE_OUT_PARAMS struct and twice the input frame size\n                            for lower resolution eg. CIF and 1.5 times the input frame size for\n                            higher resolutions. If encoded bitstream size is greater than the\n                            allocated buffer size for encoded bitstream, then the output buffer will\n                            have encoded bitstream data equal to buffer size. All CUDA operations on\n                            this buffer must use the default stream. */\n    void* completionEvent; /**< [in]: Specifies an event to be signaled on completion of encoding of\n                              this Frame [only if operating in Asynchronous mode]. Each output\n                              buffer should be associated with a distinct event pointer. */\n    NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Specifies the input buffer format. */\n    NV_ENC_PIC_STRUCT pictureStruct; /**< [in]: Specifies structure of the input picture. */\n    NV_ENC_PIC_TYPE\n        pictureType; /**< [in]: Specifies input picture type. Client required to be set explicitly\n                        by the client if the client has not set NV_ENC_INITALIZE_PARAMS::enablePTD\n                        to 1 while calling NvInitializeEncoder. */\n    NV_ENC_CODEC_PIC_PARAMS\n        codecPicParams; /**< [in]: Specifies the codec specific per-picture encoding parameters. */\n    NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE\n        meHintCountsPerBlock[2]; /**< [in]: For H264 and Hevc, specifies the number of hint\n                                    candidates per block per direction for the current frame.\n                                    meHintCountsPerBlock[0] is for L0 predictors and\n                                    meHintCountsPerBlock[1] is for L1 predictors. The candidate\n                                    count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] must never\n                                    exceed NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx]\n                                    provided during encoder initialization. */\n    NVENC_EXTERNAL_ME_HINT*\n        meExternalHints; /**< [in]: For H264 and Hevc, Specifies the pointer to ME external hints\n                            for the current frame. The size of ME hint buffer should be equal to\n                            number of macroblocks * the total number of candidates per macroblock.\n                                    The total number of candidates per MB per direction =\n                            1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 +\n                            2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 +\n                            2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8\n                                    + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For frames using\n                            bidirectional ME , the total number of candidates for single macroblock\n                            is sum of total number of candidates per MB for each direction (L0 and\n                            L1) */\n    uint32_t reserved1[6]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[2]; /**< [in]: Reserved and must be set to NULL */\n    int8_t* qpDeltaMap; /**< [in]: Specifies the pointer to signed byte array containing value per\n                           MB for H264, per CTB for HEVC and per SB for AV1 in raster scan order for\n                           the current picture, which will be interpreted depending on\n                           NV_ENC_RC_PARAMS::qpMapMode. If NV_ENC_RC_PARAMS::qpMapMode is\n                           NV_ENC_QP_MAP_DELTA, qpDeltaMap specifies QP modifier per MB for H264,\n                           per CTB for HEVC and per SB for AV1. This QP modifier will be applied on\n                           top of the QP chosen by rate control. If NV_ENC_RC_PARAMS::qpMapMode is\n                           NV_ENC_QP_MAP_EMPHASIS, qpDeltaMap specifies Emphasis Level Map per MB\n                           for H264. This level value along with QP chosen by rate control is used\n                           to compute the QP modifier, which in turn is applied on top of QP chosen\n                           by rate control. If NV_ENC_RC_PARAMS::qpMapMode is\n                           NV_ENC_QP_MAP_DISABLED, value in qpDeltaMap will be ignored.*/\n    uint32_t\n        qpDeltaMapSize; /**< [in]: Specifies the size in bytes of qpDeltaMap surface allocated by\n                           client and pointed to by NV_ENC_PIC_PARAMS::qpDeltaMap. Surface (array)\n                           should be picWidthInMbs * picHeightInMbs for H264, picWidthInCtbs *\n                           picHeightInCtbs for HEVC and picWidthInSbs * picHeightInSbs for AV1 */\n    uint32_t reservedBitFields; /**< [in]: Reserved bitfields and must be set to 0 */\n    uint16_t meHintRefPicDist[2]; /**< [in]: Specifies temporal distance for reference picture\n                                     (NVENC_EXTERNAL_ME_HINT::refidx = 0) used during external ME\n                                     with NV_ENC_INITALIZE_PARAMS::enablePTD = 1 .\n                                     meHintRefPicDist[0] is for L0 hints and meHintRefPicDist[1] is\n                                     for L1 hints. If not set, will internally infer distance of 1.\n                                     Ignored for NV_ENC_INITALIZE_PARAMS::enablePTD = 0 */\n    NV_ENC_INPUT_PTR\n        alphaBuffer; /**< [in]: Specifies the input alpha buffer pointer. Client must use a pointer\n                        obtained from ::NvEncCreateInputBuffer() or ::NvEncMapInputResource() APIs.\n                                Applicable only when encoding hevc with alpha layer is enabled. */\n    NVENC_EXTERNAL_ME_SB_HINT*\n        meExternalSbHints; /**< [in]: For AV1,Specifies the pointer to ME external SB hints for the\n                              current frame. The size of ME hint buffer should be equal to\n                              meSbHintsCount. */\n    uint32_t\n        meSbHintsCount; /**< [in]: For AV1, specifies the total number of external ME SB hint\n                           candidates for the frame NV_ENC_PIC_PARAMS::meSbHintsCount must never\n                           exceed the total number of SBs in frame * the max number of candidates\n                           per SB provided during encoder initialization. The max number of\n                           candidates per SB is maxMeHintCountsPerBlock[0].numCandsPerSb +\n                           maxMeHintCountsPerBlock[1].numCandsPerSb */\n    uint32_t reserved3[285]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved4[58]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_PIC_PARAMS;\n\n/** Macro for constructing the version field of ::_NV_ENC_PIC_PARAMS */\n#define NV_ENC_PIC_PARAMS_VER (NVENCAPI_STRUCT_VERSION(6) | (1 << 31))\n\n/**\n * \\struct _NV_ENC_MEONLY_PARAMS\n * MEOnly parameters that need to be sent on a per motion estimation basis.\n * NV_ENC_MEONLY_PARAMS::meExternalHints is supported for H264 only.\n */\ntypedef struct _NV_ENC_MEONLY_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to NV_ENC_MEONLY_PARAMS_VER.*/\n    uint32_t inputWidth; /**< [in]: Specifies the input frame width */\n    uint32_t inputHeight; /**< [in]: Specifies the input frame height */\n    NV_ENC_INPUT_PTR\n        inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must use a pointer\n                        obtained from NvEncCreateInputBuffer() or NvEncMapInputResource() APIs. */\n    NV_ENC_INPUT_PTR referenceFrame; /**< [in]: Specifies the reference frame pointer */\n    NV_ENC_OUTPUT_PTR\n        mvBuffer; /**< [in]: Specifies the output buffer pointer.\n                             If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 0,\n                     specifies the pointer to motion vector data buffer allocated by\n                     NvEncCreateMVBuffer. Client must lock mvBuffer using ::NvEncLockBitstream() API\n                     to get the motion vector data. If\n                     NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 1, client should\n                     allocate buffer in video memory for storing the motion vector data. The size of\n                     this buffer must be equal to total number of macroblocks multiplied by size of\n                     NV_ENC_H264_MV_DATA struct. Client should use a pointer obtained from\n                     ::NvEncMapInputResource() API, when mapping this output buffer and assign it to\n                     NV_ENC_MEONLY_PARAMS::mvBuffer. All CUDA operations on this buffer must use the\n                     default stream. */\n    NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Specifies the input buffer format. */\n    void* completionEvent; /**< [in]: Specifies an event to be signaled on completion of motion\n                              estimation of this Frame [only if operating in Asynchronous mode].\n                                      Each output buffer should be associated with a distinct event\n                              pointer. */\n    uint32_t viewID; /**< [in]: Specifies left or right viewID if\n                        NV_ENC_CONFIG_H264_MEONLY::bStereoEnable is set. viewID can be 0,1 if\n                        bStereoEnable is set, 0 otherwise. */\n    NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE\n    meHintCountsPerBlock[2]; /**< [in]: Specifies the number of hint candidates per block for the\n                                current frame. meHintCountsPerBlock[0] is for L0 predictors. The\n                                candidate count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] must\n                                never exceed NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx]\n                                provided during encoder initialization. */\n    NVENC_EXTERNAL_ME_HINT*\n        meExternalHints; /**< [in]: Specifies the pointer to ME external hints for the current\n                            frame. The size of ME hint buffer should be equal to number of\n                            macroblocks * the total number of candidates per macroblock. The total\n                            number of candidates per MB per direction =\n                            1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 +\n                            2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 +\n                            2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8\n                                     + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For frames\n                            using bidirectional ME , the total number of candidates for single\n                            macroblock is sum of total number of candidates per MB for each\n                            direction (L0 and L1) */\n    uint32_t reserved1[243]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[59]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_MEONLY_PARAMS;\n\n/** NV_ENC_MEONLY_PARAMS struct version*/\n#define NV_ENC_MEONLY_PARAMS_VER NVENCAPI_STRUCT_VERSION(3)\n\n/**\n * \\struct _NV_ENC_LOCK_BITSTREAM\n * Bitstream buffer lock parameters.\n */\ntypedef struct _NV_ENC_LOCK_BITSTREAM {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_LOCK_BITSTREAM_VER. */\n    uint32_t doNotWait : 1; /**< [in]: If this flag is set, the NvEncodeAPI interface will return\n                               buffer pointer even if operation is not completed. If not set, the\n                               call will block until operation completes. */\n    uint32_t ltrFrame : 1; /**< [out]: Flag indicating this frame is marked as LTR frame */\n    uint32_t getRCStats : 1; /**< [in]: If this flag is set then lockBitstream call will add\n                                additional intra-inter MB count and average MVX, MVY */\n    uint32_t reservedBitFields : 29; /**< [in]: Reserved bit fields and must be set to 0 */\n    void* outputBitstream; /**< [in]: Pointer to the bitstream buffer being locked. */\n    uint32_t* sliceOffsets; /**< [in, out]: Array which receives the slice (H264/HEVC) or tile (AV1)\n                               offsets. This is not supported if NV_ENC_CONFIG_H264::sliceMode is 1\n                               on Kepler GPUs. Array size must be equal to size of frame in MBs. */\n    uint32_t frameIdx; /**< [out]: Frame no. for which the bitstream is being retrieved. */\n    uint32_t hwEncodeStatus; /**< [out]: The NvEncodeAPI interface status for the locked picture. */\n    uint32_t numSlices; /**< [out]: Number of slices (H264/HEVC) or tiles (AV1) in the encoded\n                           picture. Will be reported only if\n                           NV_ENC_INITIALIZE_PARAMS::reportSliceOffsets set to 1. */\n    uint32_t\n        bitstreamSizeInBytes; /**< [out]: Actual number of bytes generated and copied to the memory\n                                 pointed by bitstreamBufferPtr. When HEVC alpha layer encoding is\n                                 enabled, this field reports the total encoded size in bytes i.e it\n                                 is the encoded size of the base plus the alpha layer. For AV1 when\n                                 enablePTD is set, this field reports the total encoded size in\n                                 bytes of all the encoded frames packed into the current output\n                                 surface i.e. show frame plus all preceding no-show frames */\n    uint64_t\n        outputTimeStamp; /**< [out]: Presentation timestamp associated with the encoded output. */\n    uint64_t\n        outputDuration; /**< [out]: Presentation duration associates with the encoded output. */\n    void* bitstreamBufferPtr; /**< [out]: Pointer to the generated output bitstream.\n                                          For MEOnly mode _NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr\n                                 should be typecast to NV_ENC_H264_MV_DATA/NV_ENC_HEVC_MV_DATA\n                                 pointer respectively for H264/HEVC  */\n    NV_ENC_PIC_TYPE pictureType; /**< [out]: Picture type of the encoded picture. */\n    NV_ENC_PIC_STRUCT pictureStruct; /**< [out]: Structure of the generated output picture. */\n    uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */\n    uint32_t frameSatd; /**< [out]: Total SATD cost for whole frame. */\n    uint32_t ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */\n    uint32_t ltrFrameBitmap; /**< [out]: Bitmap of LTR frames indices which were used for encoding\n                                this frame. Value of 0 if no LTR frames were used. */\n    uint32_t\n        temporalId; /**< [out]: TemporalId value of the frame when using temporalSVC encoding */\n    uint32_t reserved[12]; /**< [in]: Reserved and must be set to 0 */\n    uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the encoded frame. For HEVC,\n                              Number of Intra CTBs in the encoded frame. For AV1, Number of Intra\n                              SBs in the encoded show frame. Supported only if\n                              _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */\n    uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the encoded frame, includes\n                              skip MBs. For HEVC, Number of Inter CTBs in the encoded frame. For\n                              AV1, Number of Inter SBs in the encoded show frame. Supported only if\n                              _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */\n    int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the encoded frame.\n                           Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */\n    int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the encoded frame.\n                           Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */\n    uint32_t alphaLayerSizeInBytes; /**< [out]: Number of bytes generated for the alpha layer in the\n                                       encoded output. Applicable only when HEVC with alpha encoding\n                                       is enabled. */\n\n    uint32_t reserved1[218]; /**< [in]: Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_LOCK_BITSTREAM;\n\n/** Macro for constructing the version field of ::_NV_ENC_LOCK_BITSTREAM */\n#define NV_ENC_LOCK_BITSTREAM_VER NVENCAPI_STRUCT_VERSION(2)\n\n/**\n * \\struct _NV_ENC_LOCK_INPUT_BUFFER\n * Uncompressed Input Buffer lock parameters.\n */\ntypedef struct _NV_ENC_LOCK_INPUT_BUFFER {\n    uint32_t version; /**< [in]:  Struct version. Must be set to ::NV_ENC_LOCK_INPUT_BUFFER_VER. */\n    uint32_t doNotWait : 1; /**< [in]:  Set to 1 to make ::NvEncLockInputBuffer() a unblocking call.\n                               If the encoding is not completed, driver will return\n                               ::NV_ENC_ERR_ENCODER_BUSY error code. */\n    uint32_t reservedBitFields : 31; /**< [in]:  Reserved bitfields and must be set to 0 */\n    NV_ENC_INPUT_PTR inputBuffer; /**< [in]:  Pointer to the input buffer to be locked, client\n                                     should pass the pointer obtained from\n                                     ::NvEncCreateInputBuffer() or ::NvEncMapInputResource API. */\n    void* bufferDataPtr; /**< [out]: Pointed to the locked input buffer data. Client can only access\n                            input buffer using the \\p bufferDataPtr. */\n    uint32_t pitch; /**< [out]: Pitch of the locked input buffer. */\n    uint32_t reserved1[251]; /**< [in]:  Reserved and must be set to 0  */\n    void* reserved2[64]; /**< [in]:  Reserved and must be set to NULL  */\n} NV_ENC_LOCK_INPUT_BUFFER;\n\n/** Macro for constructing the version field of ::_NV_ENC_LOCK_INPUT_BUFFER */\n#define NV_ENC_LOCK_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\struct _NV_ENC_MAP_INPUT_RESOURCE\n * Map an input resource to a Nvidia Encoder Input Buffer\n */\ntypedef struct _NV_ENC_MAP_INPUT_RESOURCE {\n    uint32_t version; /**< [in]:  Struct version. Must be set to ::NV_ENC_MAP_INPUT_RESOURCE_VER. */\n    uint32_t subResourceIndex; /**< [in]:  Deprecated. Do not use. */\n    void* inputResource; /**< [in]:  Deprecated. Do not use. */\n    NV_ENC_REGISTERED_PTR registeredResource; /**< [in]:  The Registered resource handle obtained by\n                                                 calling NvEncRegisterInputResource. */\n    NV_ENC_INPUT_PTR\n        mappedResource; /**< [out]: Mapped pointer corresponding to the registeredResource. This\n                           pointer must be used in NV_ENC_PIC_PARAMS::inputBuffer parameter in\n                           ::NvEncEncodePicture() API. */\n    NV_ENC_BUFFER_FORMAT\n        mappedBufferFmt; /**< [out]: Buffer format of the outputResource. This buffer format must be\n                            used in NV_ENC_PIC_PARAMS::bufferFmt if client using the above mapped\n                            resource pointer. */\n    uint32_t reserved1[251]; /**< [in]:  Reserved and must be set to 0. */\n    void* reserved2[63]; /**< [in]:  Reserved and must be set to NULL */\n} NV_ENC_MAP_INPUT_RESOURCE;\n\n/** Macro for constructing the version field of ::_NV_ENC_MAP_INPUT_RESOURCE */\n#define NV_ENC_MAP_INPUT_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4)\n\n/**\n * \\struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX\n * NV_ENC_REGISTER_RESOURCE::resourceToRegister must be a pointer to a variable of this type,\n * when NV_ENC_REGISTER_RESOURCE::resourceType is NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX\n */\ntypedef struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX {\n    uint32_t texture; /**< [in]: The name of the texture to be used. */\n    uint32_t target; /**< [in]: Accepted values are GL_TEXTURE_RECTANGLE and GL_TEXTURE_2D. */\n} NV_ENC_INPUT_RESOURCE_OPENGL_TEX;\n\n/** \\struct NV_ENC_FENCE_POINT_D3D12\n * Fence and fence value for synchronization.\n */\ntypedef struct _NV_ENC_FENCE_POINT_D3D12 {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_FENCE_POINT_D3D12_VER. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0. */\n    void*\n        pFence; /**< [in]: Pointer to ID3D12Fence. This fence object is used for synchronization. */\n    uint64_t waitValue; /**< [in]: Fence value to reach or exceed before the GPU operation. */\n    uint64_t signalValue; /**< [in]: Fence value to set the fence to, after the GPU operation. */\n    uint32_t bWait : 1; /**< [in]: Wait on 'waitValue' if bWait is set to 1, before starting GPU\n                           operation. */\n    uint32_t bSignal : 1; /**< [in]: Signal on 'signalValue' if bSignal is set to 1, after GPU\n                             operation is complete. */\n    uint32_t reservedBitField : 30; /**< [in]: Reserved and must be set to 0. */\n    uint32_t reserved1[7]; /**< [in]: Reserved and must be set to 0. */\n} NV_ENC_FENCE_POINT_D3D12;\n\n#define NV_ENC_FENCE_POINT_D3D12_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\struct _NV_ENC_INPUT_RESOURCE_D3D12\n * NV_ENC_PIC_PARAMS::inputBuffer and NV_ENC_PIC_PARAMS::alphaBuffer must be a pointer to a struct\n * of this type, when D3D12 interface is used\n */\ntypedef struct _NV_ENC_INPUT_RESOURCE_D3D12 {\n    uint32_t\n        version; /**< [in]: Struct version. Must be set to ::NV_ENC_INPUT_RESOURCE_D3D12_VER. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0. */\n    NV_ENC_INPUT_PTR pInputBuffer; /**< [in]: Specifies the input surface pointer. Client must use a\n                                      pointer obtained from NvEncMapInputResource() in\n                                      NV_ENC_MAP_INPUT_RESOURCE::mappedResource when mapping the\n                                      input surface. */\n    NV_ENC_FENCE_POINT_D3D12 inputFencePoint; /**< [in]: Specifies the fence and corresponding fence\n                                                 values to do GPU wait and signal. */\n    uint32_t reserved1[16]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved2[16]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_INPUT_RESOURCE_D3D12;\n\n#define NV_ENC_INPUT_RESOURCE_D3D12_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\struct _NV_ENC_OUTPUT_RESOURCE_D3D12\n * NV_ENC_PIC_PARAMS::outputBitstream and NV_ENC_LOCK_BITSTREAM::outputBitstream must be a pointer\n * to a struct of this type, when D3D12 interface is used\n */\ntypedef struct _NV_ENC_OUTPUT_RESOURCE_D3D12 {\n    uint32_t\n        version; /**< [in]: Struct version. Must be set to ::NV_ENC_OUTPUT_RESOURCE_D3D12_VER. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0. */\n    NV_ENC_INPUT_PTR pOutputBuffer; /**< [in]: Specifies the output buffer pointer. Client must use\n                                       a pointer obtained from NvEncMapInputResource() in\n                                       NV_ENC_MAP_INPUT_RESOURCE::mappedResource when mapping output\n                                       bitstream buffer */\n    NV_ENC_FENCE_POINT_D3D12 outputFencePoint; /**< [in]: Specifies the fence and corresponding\n                                                  fence values to do GPU wait and signal.*/\n    uint32_t reserved1[16]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved2[16]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_OUTPUT_RESOURCE_D3D12;\n\n#define NV_ENC_OUTPUT_RESOURCE_D3D12_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\struct _NV_ENC_REGISTER_RESOURCE\n * Register a resource for future use with the Nvidia Video Encoder Interface.\n */\ntypedef struct _NV_ENC_REGISTER_RESOURCE {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_REGISTER_RESOURCE_VER. */\n    NV_ENC_INPUT_RESOURCE_TYPE resourceType; /**< [in]: Specifies the type of resource to be\n                                                registered. Supported values are\n                                                        ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX,\n                                                        ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR,\n                                                        ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX */\n    uint32_t width; /**< [in]: Input frame width. */\n    uint32_t height; /**< [in]: Input frame height. */\n    uint32_t pitch; /**< [in]: Input buffer pitch.\n                               For ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX resources, set this to 0.\n                               For ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR resources, set this to\n                                 the pitch as obtained from cuMemAllocPitch(), or to the width in\n                                 bytes (if this resource was created by using cuMemAlloc()). This\n                                 value must be a multiple of 4.\n                               For ::NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY resources, set this to the\n                                 width of the allocation in bytes (i.e.\n                                 CUDA_ARRAY3D_DESCRIPTOR::Width *\n                       CUDA_ARRAY3D_DESCRIPTOR::NumChannels). For\n                       ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX resources, set this to the texture\n                       width multiplied by the number of components in the texture format. */\n    uint32_t subResourceIndex; /**< [in]: Subresource Index of the DirectX resource to be\n                                  registered. Should be set to 0 for other interfaces. */\n    void* resourceToRegister; /**< [in]: Handle to the resource that is being registered. */\n    NV_ENC_REGISTERED_PTR\n        registeredResource; /**< [out]: Registered resource handle. This should be used in future\n                               interactions with the Nvidia Video Encoder Interface. */\n    NV_ENC_BUFFER_FORMAT bufferFormat; /**< [in]: Buffer format of resource to be registered. */\n    NV_ENC_BUFFER_USAGE bufferUsage; /**< [in]: Usage of resource to be registered. */\n    NV_ENC_FENCE_POINT_D3D12*\n        pInputFencePoint; /**< [in]: Specifies the input fence and corresponding fence values to do\n                             GPU wait and signal. To be used only when\n                             NV_ENC_REGISTER_RESOURCE::resourceToRegister represents D3D12 surface\n                             and NV_ENC_BUFFER_USAGE::bufferUsage is NV_ENC_INPUT_IMAGE. The fence\n                             NV_ENC_FENCE_POINT_D3D12::pFence and\n                             NV_ENC_FENCE_POINT_D3D12::waitValue will be used to do GPU wait before\n                             starting GPU operation, if NV_ENC_FENCE_POINT_D3D12::bWait is set. The\n                             fence NV_ENC_FENCE_POINT_D3D12::pFence and\n                             NV_ENC_FENCE_POINT_D3D12::signalValue will be used to do GPU signal\n                                     when GPU operation finishes, if\n                             NV_ENC_FENCE_POINT_D3D12::bSignal is set. */\n    uint32_t reserved1[247]; /**< [in]: Reserved and must be set to 0. */\n    void* reserved2[61]; /**< [in]: Reserved and must be set to NULL. */\n} NV_ENC_REGISTER_RESOURCE;\n\n/** Macro for constructing the version field of ::_NV_ENC_REGISTER_RESOURCE */\n#define NV_ENC_REGISTER_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4)\n\n/**\n * \\struct _NV_ENC_STAT\n * Encode Stats structure.\n */\ntypedef struct _NV_ENC_STAT {\n    uint32_t version; /**< [in]:  Struct version. Must be set to ::NV_ENC_STAT_VER. */\n    uint32_t reserved; /**< [in]:  Reserved and must be set to 0 */\n    NV_ENC_OUTPUT_PTR outputBitStream; /**< [out]: Specifies the pointer to output bitstream. */\n    uint32_t bitStreamSize; /**< [out]: Size of generated bitstream in bytes. */\n    uint32_t picType; /**< [out]: Picture type of encoded picture. See ::NV_ENC_PIC_TYPE. */\n    uint32_t lastValidByteOffset; /**< [out]: Offset of last valid bytes of completed bitstream */\n    uint32_t sliceOffsets[16]; /**< [out]: Offsets of each slice */\n    uint32_t picIdx; /**< [out]: Picture number */\n    uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */\n    uint32_t ltrFrame : 1; /**< [out]: Flag indicating this frame is marked as LTR frame */\n    uint32_t reservedBitFields : 31; /**< [in]:  Reserved bit fields and must be set to 0 */\n    uint32_t ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */\n    uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the encoded frame. For HEVC,\n                              Number of Intra CTBs in the encoded frame. */\n    uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the encoded frame, includes\n                              skip MBs. For HEVC, Number of Inter CTBs in the encoded frame. */\n    int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the encoded frame. */\n    int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the encoded frame. */\n    uint32_t reserved1[226]; /**< [in]:  Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]:  Reserved and must be set to NULL */\n} NV_ENC_STAT;\n\n/** Macro for constructing the version field of ::_NV_ENC_STAT */\n#define NV_ENC_STAT_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * \\struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD\n * Sequence and picture paramaters payload.\n */\ntypedef struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD {\n    uint32_t version; /**< [in]:  Struct version. Must be set to ::NV_ENC_INITIALIZE_PARAMS_VER. */\n    uint32_t\n        inBufferSize; /**< [in]:  Specifies the size of the spsppsBuffer provided by the client */\n    uint32_t spsId; /**< [in]:  Specifies the SPS id to be used in sequence header. Default value is\n                       0.  */\n    uint32_t\n        ppsId; /**< [in]:  Specifies the PPS id to be used in picture header. Default value is 0. */\n    void* spsppsBuffer; /**< [in]:  Specifies bitstream header pointer of size\n                           NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. It is the client's\n                           responsibility to manage this memory. */\n    uint32_t* outSPSPPSPayloadSize; /**< [out]: Size of the sequence and picture header in bytes. */\n    uint32_t reserved[250]; /**< [in]:  Reserved and must be set to 0 */\n    void* reserved2[64]; /**< [in]:  Reserved and must be set to NULL */\n} NV_ENC_SEQUENCE_PARAM_PAYLOAD;\n\n/** Macro for constructing the version field of ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD */\n#define NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Event registration/unregistration parameters.\n */\ntypedef struct _NV_ENC_EVENT_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_EVENT_PARAMS_VER. */\n    uint32_t reserved; /**< [in]: Reserved and must be set to 0 */\n    void* completionEvent; /**< [in]: Handle to event to be registered/unregistered with the\n                              NvEncodeAPI interface. */\n    uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0    */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_EVENT_PARAMS;\n\n/** Macro for constructing the version field of ::_NV_ENC_EVENT_PARAMS */\n#define NV_ENC_EVENT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)\n\n/**\n * Encoder Session Creation parameters\n */\ntypedef struct _NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS {\n    uint32_t version; /**< [in]: Struct version. Must be set to\n                         ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER. */\n    NV_ENC_DEVICE_TYPE deviceType; /**< [in]: Specified the device Type */\n    void* device; /**< [in]: Pointer to client device. */\n    void* reserved; /**< [in]: Reserved and must be set to 0. */\n    uint32_t apiVersion; /**< [in]: API version. Should be set to NVENCAPI_VERSION. */\n    uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0    */\n    void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */\n} NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS;\n/** Macro for constructing the version field of ::_NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS */\n#define NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)\n\n/** @} */ /* END ENCODER_STRUCTURE */\n\n/**\n * \\addtogroup ENCODE_FUNC NvEncodeAPI Functions\n * @{\n */\n\n// NvEncOpenEncodeSession\n/**\n * \\brief Opens an encoding session.\n *\n * Deprecated.\n *\n * \\return\n * ::NV_ENC_ERR_INVALID_CALL\\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncOpenEncodeSession(void* device, uint32_t deviceType, void** encoder);\n\n// NvEncGetEncodeGuidCount\n/**\n * \\brief Retrieves the number of supported encode GUIDs.\n *\n * The function returns the number of codec GUIDs supported by the NvEncodeAPI\n * interface.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [out] encodeGUIDCount\n *   Number of supported encode GUIDs.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodeGUIDCount(void* encoder, uint32_t* encodeGUIDCount);\n\n// NvEncGetEncodeGUIDs\n/**\n * \\brief Retrieves an array of supported encoder codec GUIDs.\n *\n * The function returns an array of codec GUIDs supported by the NvEncodeAPI interface.\n * The client must allocate an array where the NvEncodeAPI interface can\n * fill the supported GUIDs and pass the pointer in \\p *GUIDs parameter.\n * The size of the array can be determined by using ::NvEncGetEncodeGUIDCount() API.\n * The Nvidia Encoding interface returns the number of codec GUIDs it has actually\n * filled in the GUID array in the \\p GUIDCount parameter.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] guidArraySize\n *   Number of GUIDs to retrieved. Should be set to the number retrieved using\n *   ::NvEncGetEncodeGUIDCount.\n * \\param [out] GUIDs\n *   Array of supported Encode GUIDs.\n * \\param [out] GUIDCount\n *   Number of supported Encode GUIDs.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncGetEncodeGUIDs(void* encoder, GUID* GUIDs, uint32_t guidArraySize, uint32_t* GUIDCount);\n\n// NvEncGetEncodeProfileGuidCount\n/**\n * \\brief Retrieves the number of supported profile GUIDs.\n *\n * The function returns the number of profile GUIDs supported for a given codec.\n * The client must first enumerate the codec GUIDs supported by the NvEncodeAPI\n * interface. After determining the codec GUID, it can query the NvEncodeAPI\n * interface to determine the number of profile GUIDs supported for a particular\n * codec GUID.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   The codec GUID for which the profile GUIDs are being enumerated.\n * \\param [out] encodeProfileGUIDCount\n *   Number of encode profiles supported for the given encodeGUID.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncGetEncodeProfileGUIDCount(void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount);\n\n// NvEncGetEncodeProfileGUIDs\n/**\n * \\brief Retrieves an array of supported encode profile GUIDs.\n *\n * The function returns an array of supported profile GUIDs for a particular\n * codec GUID. The client must allocate an array where the NvEncodeAPI interface\n * can populate the profile GUIDs. The client can determine the array size using\n * ::NvEncGetEncodeProfileGUIDCount() API. The client must also validiate that the\n * NvEncodeAPI interface supports the GUID the client wants to pass as \\p encodeGUID\n * parameter.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   The encode GUID whose profile GUIDs are being enumerated.\n * \\param [in] guidArraySize\n *   Number of GUIDs to be retrieved. Should be set to the number retrieved using\n *   ::NvEncGetEncodeProfileGUIDCount.\n * \\param [out] profileGUIDs\n *   Array of supported Encode Profile GUIDs\n * \\param [out] GUIDCount\n *   Number of valid encode profile GUIDs in \\p profileGUIDs array.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodeProfileGUIDs(\n    void* encoder, GUID encodeGUID, GUID* profileGUIDs, uint32_t guidArraySize, uint32_t* GUIDCount\n);\n\n// NvEncGetInputFormatCount\n/**\n * \\brief Retrieve the number of supported Input formats.\n *\n * The function returns the number of supported input formats. The client must\n * query the NvEncodeAPI interface to determine the supported input formats\n * before creating the input surfaces.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the number of supported input formats\n *   is to be retrieved.\n * \\param [out] inputFmtCount\n *   Number of input formats supported for specified Encode GUID.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI\nNvEncGetInputFormatCount(void* encoder, GUID encodeGUID, uint32_t* inputFmtCount);\n\n// NvEncGetInputFormats\n/**\n * \\brief Retrieves an array of supported Input formats\n *\n * Returns an array of supported input formats  The client must use the input\n * format to create input surface using ::NvEncCreateInputBuffer() API.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the number of supported input formats\n *   is to be retrieved.\n *\\param [in] inputFmtArraySize\n *   Size input format count array passed in \\p inputFmts.\n *\\param [out] inputFmts\n *   Array of input formats supported for this Encode GUID.\n *\\param [out] inputFmtCount\n *   The number of valid input format types returned by the NvEncodeAPI\n *   interface in \\p inputFmts array.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetInputFormats(\n    void* encoder,\n    GUID encodeGUID,\n    NV_ENC_BUFFER_FORMAT* inputFmts,\n    uint32_t inputFmtArraySize,\n    uint32_t* inputFmtCount\n);\n\n// NvEncGetEncodeCaps\n/**\n * \\brief Retrieves the capability value for a specified encoder attribute.\n *\n * The function returns the capability value for a given encoder attribute. The\n * client must validate the encodeGUID using ::NvEncGetEncodeGUIDs() API before\n * calling this function. The encoder attribute being queried are enumerated in\n * ::NV_ENC_CAPS_PARAM enum.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the capability attribute is to be retrieved.\n * \\param [in] capsParam\n *   Used to specify attribute being queried. Refer ::NV_ENC_CAPS_PARAM for  more\n * details.\n * \\param [out] capsVal\n *   The value corresponding to the capability attribute being queried.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI\nNvEncGetEncodeCaps(void* encoder, GUID encodeGUID, NV_ENC_CAPS_PARAM* capsParam, int* capsVal);\n\n// NvEncGetEncodePresetCount\n/**\n * \\brief Retrieves the number of supported preset GUIDs.\n *\n * The function returns the number of preset GUIDs available for a given codec.\n * The client must validate the codec GUID using ::NvEncGetEncodeGUIDs() API\n * before calling this function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the number of supported presets is to\n *   be retrieved.\n * \\param [out] encodePresetGUIDCount\n *   Receives the number of supported preset GUIDs.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncGetEncodePresetCount(void* encoder, GUID encodeGUID, uint32_t* encodePresetGUIDCount);\n\n// NvEncGetEncodePresetGUIDs\n/**\n * \\brief Receives an array of supported encoder preset GUIDs.\n *\n * The function returns an array of encode preset GUIDs available for a given codec.\n * The client can directly use one of the preset GUIDs based upon the use case\n * or target device. The preset GUID chosen can be directly used in\n * NV_ENC_INITIALIZE_PARAMS::presetGUID parameter to ::NvEncEncodePicture() API.\n * Alternately client can  also use the preset GUID to retrieve the encoding config\n * parameters being used by NvEncodeAPI interface for that given preset, using\n * ::NvEncGetEncodePresetConfig() API. It can then modify preset config parameters\n * as per its use case and send it to NvEncodeAPI interface as part of\n * NV_ENC_INITIALIZE_PARAMS::encodeConfig parameter for NvEncInitializeEncoder()\n * API.\n *\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the list of supported presets is to be\n *   retrieved.\n * \\param [in] guidArraySize\n *   Size of array of preset GUIDs passed in \\p preset GUIDs\n * \\param [out] presetGUIDs\n *   Array of supported Encode preset GUIDs from the NvEncodeAPI interface\n *   to client.\n * \\param [out] encodePresetGUIDCount\n *   Receives the number of preset GUIDs returned by the NvEncodeAPI\n *   interface.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodePresetGUIDs(\n    void* encoder,\n    GUID encodeGUID,\n    GUID* presetGUIDs,\n    uint32_t guidArraySize,\n    uint32_t* encodePresetGUIDCount\n);\n\n// NvEncGetEncodePresetConfig\n/**\n * \\brief Returns a preset config structure supported for given preset GUID.\n *\n * The function returns a preset config structure for a given preset GUID.\n * NvEncGetEncodePresetConfig() API is not applicable to AV1.\n * Before using this function the client must enumerate the preset GUIDs available for\n * a given codec. The preset config structure can be modified by the client depending\n * upon its use case and can be then used to initialize the encoder using\n * ::NvEncInitializeEncoder() API. The client can use this function only if it\n * wants to modify the NvEncodeAPI preset configuration, otherwise it can\n * directly use the preset GUID.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the list of supported presets is to be\n *   retrieved.\n * \\param [in] presetGUID\n *   Preset GUID, corresponding to which the Encoding configurations is to be\n *   retrieved.\n * \\param [out] presetConfig\n *   The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for\n *    more details.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodePresetConfig(\n    void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_PRESET_CONFIG* presetConfig\n);\n\n// NvEncGetEncodePresetConfigEx\n/**\n * \\brief Returns a preset config structure supported for given preset GUID.\n *\n * The function returns a preset config structure for a given preset GUID and tuning info.\n * NvEncGetEncodePresetConfigEx() API is not applicable to H264 and HEVC meonly mode.\n * Before using this function the client must enumerate the preset GUIDs available for\n * a given codec. The preset config structure can be modified by the client depending\n * upon its use case and can be then used to initialize the encoder using\n * ::NvEncInitializeEncoder() API. The client can use this function only if it\n * wants to modify the NvEncodeAPI preset configuration, otherwise it can\n * directly use the preset GUID.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encodeGUID\n *   Encode GUID, corresponding to which the list of supported presets is to be\n *   retrieved.\n * \\param [in] presetGUID\n *   Preset GUID, corresponding to which the Encoding configurations is to be\n *   retrieved.\n * \\param [in] tuningInfo\n *   tuning info, corresponding to which the Encoding configurations is to be\n *   retrieved.\n * \\param [out] presetConfig\n *   The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for\n *    more details.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodePresetConfigEx(\n    void* encoder,\n    GUID encodeGUID,\n    GUID presetGUID,\n    NV_ENC_TUNING_INFO tuningInfo,\n    NV_ENC_PRESET_CONFIG* presetConfig\n);\n\n// NvEncInitializeEncoder\n/**\n * \\brief Initialize the encoder.\n *\n * This API must be used to initialize the encoder. The initialization parameter\n * is passed using \\p *createEncodeParams  The client must send the following\n * fields of the _NV_ENC_INITIALIZE_PARAMS structure with a valid value.\n * - NV_ENC_INITIALIZE_PARAMS::encodeGUID\n * - NV_ENC_INITIALIZE_PARAMS::encodeWidth\n * - NV_ENC_INITIALIZE_PARAMS::encodeHeight\n *\n * The client can pass a preset GUID directly to the NvEncodeAPI interface using\n * NV_ENC_INITIALIZE_PARAMS::presetGUID field. If the client doesn't pass\n * NV_ENC_INITIALIZE_PARAMS::encodeConfig structure, the codec specific parameters\n * will be selected based on the preset GUID. The preset GUID must have been\n * validated by the client using ::NvEncGetEncodePresetGUIDs() API.\n * If the client passes a custom ::_NV_ENC_CONFIG structure through\n * NV_ENC_INITIALIZE_PARAMS::encodeConfig , it will override the codec specific parameters\n * based on the preset GUID. It is recommended that even if the client passes a custom config,\n * it should also send a preset GUID. In this case, the preset GUID passed by the client\n * will not override any of the custom config parameters programmed by the client,\n * it is only used as a hint by the NvEncodeAPI interface to determine certain encoder parameters\n * which are not exposed to the client.\n *\n * There are two modes of operation for the encoder namely:\n * - Asynchronous mode\n * - Synchronous mode\n *\n * The client can select asynchronous or synchronous mode by setting the \\p\n * enableEncodeAsync field in ::_NV_ENC_INITIALIZE_PARAMS to 1 or 0 respectively.\n *\\par Asynchronous mode of operation:\n * The Asynchronous mode can be enabled by setting NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1.\n * The client operating in asynchronous mode must allocate completion event object\n * for each output buffer and pass the completion event object in the\n * ::NvEncEncodePicture() API. The client can create another thread and wait on\n * the event object to be signaled by NvEncodeAPI interface on completion of the\n * encoding process for the output frame. This should unblock the main thread from\n * submitting work to the encoder. When the event is signaled the client can call\n * NvEncodeAPI interfaces to copy the bitstream data using ::NvEncLockBitstream()\n * API. This is the preferred mode of operation.\n *\n * NOTE: Asynchronous mode is not supported on Linux.\n *\n *\\par Synchronous mode of operation:\n * The client can select synchronous mode by setting NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to\n *0. The client working in synchronous mode can work in a single threaded or multi threaded mode.\n *The client need not allocate any event objects. The client can only lock the bitstream data after\n *NvEncodeAPI interface has returned\n * ::NV_ENC_SUCCESS from encode picture. The NvEncodeAPI interface can return\n * ::NV_ENC_ERR_NEED_MORE_INPUT error code from ::NvEncEncodePicture() API. The\n * client must not lock the output buffer in such case but should send the next\n * frame for encoding. The client must keep on calling ::NvEncEncodePicture() API\n * until it returns ::NV_ENC_SUCCESS. \\n\n * The client must always lock the bitstream data in order in which it has submitted.\n * This is true for both asynchronous and synchronous mode.\n *\n *\\par Picture type decision:\n * If the client is taking the picture type decision and it must disable the picture\n * type decision module in NvEncodeAPI by setting NV_ENC_INITIALIZE_PARAMS::enablePTD\n * to 0. In this case the client is  required to send the picture in encoding\n * order to NvEncodeAPI by doing the re-ordering for B frames. \\n\n * If the client doesn't want to take the picture type decision it can enable\n * picture type decision module in the NvEncodeAPI interface by setting\n * NV_ENC_INITIALIZE_PARAMS::enablePTD to 1 and send the input pictures in display\n * order.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] createEncodeParams\n *   Refer ::_NV_ENC_INITIALIZE_PARAMS for details.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncInitializeEncoder(void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams);\n\n// NvEncCreateInputBuffer\n/**\n * \\brief Allocates Input buffer.\n *\n * This function is used to allocate an input buffer. The client must enumerate\n * the input buffer format before allocating the input buffer resources. The\n * NV_ENC_INPUT_PTR returned by the NvEncodeAPI interface in the\n * NV_ENC_CREATE_INPUT_BUFFER::inputBuffer field can be directly used in\n * ::NvEncEncodePicture() API. The number of input buffers to be allocated by the\n * client must be at least 4 more than the number of B frames being used for encoding.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] createInputBufferParams\n *  Pointer to the ::NV_ENC_CREATE_INPUT_BUFFER structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncCreateInputBuffer(void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams);\n\n// NvEncDestroyInputBuffer\n/**\n * \\brief Release an input buffers.\n *\n * This function is used to free an input buffer. If the client has allocated\n * any input buffer using ::NvEncCreateInputBuffer() API, it must free those\n * input buffers by calling this function. The client must release the input\n * buffers before destroying the encoder using ::NvEncDestroyEncoder() API.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] inputBuffer\n *   Pointer to the input buffer to be released.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncDestroyInputBuffer(void* encoder, NV_ENC_INPUT_PTR inputBuffer);\n\n// NvEncSetIOCudaStreams\n/**\n * \\brief Set input and output CUDA stream for specified encoder attribute.\n *\n * Encoding may involve CUDA pre-processing on the input and post-processing on encoded output.\n * This function is used to set input and output CUDA streams to pipeline the CUDA pre-processing\n * and post-processing tasks. Clients should call this function before the call to\n * NvEncUnlockInputBuffer(). If this function is not called, the default CUDA stream is used for\n * input and output processing. After a successful call to this function, the streams specified\n * in that call will replace the previously-used streams.\n * This API is supported for NVCUVID interface only.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] inputStream\n *   Pointer to CUstream which is used to process ::NV_ENC_PIC_PARAMS::inputFrame for encode.\n *   In case of ME-only mode, inputStream is used to process ::NV_ENC_MEONLY_PARAMS::inputBuffer and\n *   ::NV_ENC_MEONLY_PARAMS::referenceFrame\n * \\param [in] outputStream\n *  Pointer to CUstream which is used to process ::NV_ENC_PIC_PARAMS::outputBuffer for encode.\n *  In case of ME-only mode, outputStream is used to process ::NV_ENC_MEONLY_PARAMS::mvBuffer\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI NvEncSetIOCudaStreams(\n    void* encoder, NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream\n);\n\n// NvEncCreateBitstreamBuffer\n/**\n * \\brief Allocates an output bitstream buffer\n *\n * This function is used to allocate an output bitstream buffer and returns a\n * NV_ENC_OUTPUT_PTR to bitstream  buffer to the client in the\n * NV_ENC_CREATE_BITSTREAM_BUFFER::bitstreamBuffer field.\n * The client can only call this function after the encoder session has been\n * initialized using ::NvEncInitializeEncoder() API. The minimum number of output\n * buffers allocated by the client must be at least 4 more than the number of B\n * B frames being used for encoding. The client can only access the output\n * bitstream data by locking the \\p bitstreamBuffer using the ::NvEncLockBitstream()\n * function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] createBitstreamBufferParams\n *   Pointer ::NV_ENC_CREATE_BITSTREAM_BUFFER for details.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncCreateBitstreamBuffer(\n    void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams\n);\n\n// NvEncDestroyBitstreamBuffer\n/**\n * \\brief Release a bitstream buffer.\n *\n * This function is used to release the output bitstream buffer allocated using\n * the ::NvEncCreateBitstreamBuffer() function. The client must release the output\n * bitstreamBuffer using this function before destroying the encoder session.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] bitstreamBuffer\n *   Pointer to the bitstream buffer being released.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncDestroyBitstreamBuffer(void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer);\n\n// NvEncEncodePicture\n/**\n * \\brief Submit an input picture for encoding.\n *\n * This function is used to submit an input picture buffer for encoding. The\n * encoding parameters are passed using \\p *encodePicParams which is a pointer\n * to the ::_NV_ENC_PIC_PARAMS structure.\n *\n * If the client has set NV_ENC_INITIALIZE_PARAMS::enablePTD to 0, then it must\n * send a valid value for the following fields.\n * - NV_ENC_PIC_PARAMS::pictureType\n * - NV_ENC_PIC_PARAMS_H264::displayPOCSyntax (H264 only)\n * - NV_ENC_PIC_PARAMS_H264::frameNumSyntax(H264 only)\n * - NV_ENC_PIC_PARAMS_H264::refPicFlag(H264 only)\n *\n *\\par MVC Encoding:\n * For MVC encoding the client must call encode picture API for each view separately\n * and must pass valid view id in NV_ENC_PIC_PARAMS_MVC::viewID field. Currently\n * NvEncodeAPI only support stereo MVC so client must send viewID as 0 for base\n * view and view ID as 1 for dependent view.\n *\n *\\par Asynchronous Encoding\n * If the client has enabled asynchronous mode of encoding by setting\n * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1 in the ::NvEncInitializeEncoder()\n * API ,then the client must send a valid NV_ENC_PIC_PARAMS::completionEvent.\n * Incase of asynchronous mode of operation, client can queue the ::NvEncEncodePicture()\n * API commands from the main thread and then queue output buffers to be processed\n * to a secondary worker thread. Before the locking the output buffers in the\n * secondary thread , the client must wait on NV_ENC_PIC_PARAMS::completionEvent\n * it has queued in ::NvEncEncodePicture() API call. The client must always process\n * completion event and the output buffer in the same order in which they have been\n * submitted for encoding. The NvEncodeAPI interface is responsible for any\n * re-ordering required for B frames and will always ensure that encoded bitstream\n * data is written in the same order in which output buffer is submitted.\n * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code for\n * some ::NvEncEncodePicture() API calls but the client must not treat it as a fatal error.\n * The NvEncodeAPI interface might not be able to submit an input picture buffer for encoding\n * immediately due to re-ordering for B frames.\n *\\code\n  The below example shows how  asynchronous encoding in case of 1 B frames\n  ------------------------------------------------------------------------\n  Suppose the client allocated 4 input buffers(I1,I2..), 4 output buffers(O1,O2..)\n  and 4 completion events(E1, E2, ...). The NvEncodeAPI interface will need to\n  keep a copy of the input buffers for re-ordering and it allocates following\n  internal buffers (NvI1, NvI2...). These internal buffers are managed by NvEncodeAPI\n  and the client is not responsible for the allocating or freeing the memory of\n  the internal buffers.\n\n  a) The client main thread will queue the following encode frame calls.\n  Note the picture type is unknown to the client, the decision is being taken by\n  NvEncodeAPI interface. The client should pass ::_NV_ENC_PIC_PARAMS parameter\n  consisting of allocated input buffer, output buffer and output events in successive\n  ::NvEncEncodePicture() API calls along with other required encode picture params.\n  For example:\n  1st EncodePicture parameters - (I1, O1, E1)\n  2nd EncodePicture parameters - (I2, O2, E2)\n  3rd EncodePicture parameters - (I3, O3, E3)\n\n  b) NvEncodeAPI SW will receive the following encode Commands from the client.\n  The left side shows input from client in the form (Input buffer, Output Buffer,\n  Output Event). The right hand side shows a possible picture type decision take by\n  the NvEncodeAPI interface.\n  (I1, O1, E1)    ---P1 Frame\n  (I2, O2, E2)    ---B2 Frame\n  (I3, O3, E3)    ---P3 Frame\n\n  c) NvEncodeAPI interface will make a copy of the input buffers to its internal\n   buffers for re-ordering. These copies are done as part of nvEncEncodePicture\n   function call from the client and NvEncodeAPI interface is responsible for\n   synchronization of copy operation with the actual encoding operation.\n   I1 --> NvI1\n   I2 --> NvI2\n   I3 --> NvI3\n\n   d) The NvEncodeAPI encodes I1 as P frame and submits I1 to encoder HW and returns\n ::NV_ENC_SUCCESS. The NvEncodeAPI tries to encode I2 as B frame and fails with\n ::NV_ENC_ERR_NEED_MORE_INPUT error code. The error is not fatal and it notifies client that I2 is\n not submitted to encoder immediately. The NvEncodeAPI encodes I3 as P frame and submits I3 for\n encoding which will be used as  backward reference frame for I2. The NvEncodeAPI then submits I2\n for encoding and returns ::NV_ENC_SUCESS. Both the submission are part of the same\n ::NvEncEncodePicture() function call.\n\n  e) After returning from ::NvEncEncodePicture() call , the client must queue the output\n   bitstream  processing work to the secondary thread. The output bitstream processing\n   for asynchronous mode consist of first waiting on completion event(E1, E2..)\n   and then locking the output bitstream buffer(O1, O2..) for reading the encoded\n   data. The work queued to the secondary thread by the client is in the following order\n   (I1, O1, E1)\n   (I2, O2, E2)\n   (I3, O3, E3)\n   Note they are in the same order in which client calls ::NvEncEncodePicture() API\n   in \\p step a).\n\n  f) NvEncodeAPI interface  will do the re-ordering such that Encoder HW will receive\n  the following encode commands:\n  (NvI1, O1, E1)   ---P1 Frame\n  (NvI3, O2, E2)   ---P3 Frame\n  (NvI2, O3, E3)   ---B2 frame\n\n  g) After the encoding operations are completed, the events will be signaled\n  by NvEncodeAPI interface in the following order :\n  (O1, E1) ---P1 Frame ,output bitstream copied to O1 and event E1 signaled.\n  (O2, E2) ---P3 Frame ,output bitstream copied to O2 and event E2 signaled.\n  (O3, E3) ---B2 Frame ,output bitstream copied to O3 and event E3 signaled.\n\n  h) The client must lock the bitstream data using ::NvEncLockBitstream() API in\n   the order O1,O2,O3  to read the encoded data, after waiting for the events\n   to be signaled in the same order i.e E1, E2 and E3.The output processing is\n   done in the secondary thread in the following order:\n   Waits on E1, copies encoded bitstream from O1\n   Waits on E2, copies encoded bitstream from O2\n   Waits on E3, copies encoded bitstream from O3\n\n  -Note the client will receive the events signaling and output buffer in the\n   same order in which they have submitted for encoding.\n  -Note the LockBitstream will have picture type field which will notify the\n   output picture type to the clients.\n  -Note the input, output buffer and the output completion event are free to be\n   reused once NvEncodeAPI interfaced has signaled the event and the client has\n   copied the data from the output buffer.\n\n * \\endcode\n *\n *\\par Synchronous Encoding\n * The client can enable synchronous mode of encoding by setting\n * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 0 in ::NvEncInitializeEncoder() API.\n * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code for\n * some ::NvEncEncodePicture() API calls when NV_ENC_INITIALIZE_PARAMS::enablePTD\n * is set to 1, but the client must not treat it as a fatal error. The NvEncodeAPI\n * interface might not be able to submit an input picture buffer for encoding\n * immediately due to re-ordering for B frames. The NvEncodeAPI interface cannot\n * submit the input picture which is decided to be encoded as B frame as it waits\n * for backward reference from  temporally subsequent frames. This input picture\n * is buffered internally and waits for more input picture to arrive. The client\n * must not call ::NvEncLockBitstream() API on the output buffers whose\n * ::NvEncEncodePicture() API returns ::NV_ENC_ERR_NEED_MORE_INPUT. The client must\n * wait for the NvEncodeAPI interface to return ::NV_ENC_SUCCESS before locking the\n * output bitstreams to read the encoded bitstream data. The following example\n * explains the scenario with synchronous encoding with 2 B frames.\n *\\code\n The below example shows how  synchronous encoding works in case of 1 B frames\n -----------------------------------------------------------------------------\n Suppose the client allocated 4 input buffers(I1,I2..), 4 output buffers(O1,O2..)\n and 4 completion events(E1, E2, ...). The NvEncodeAPI interface will need to\n keep a copy of the input buffers for re-ordering and it allocates following\n internal buffers (NvI1, NvI2...). These internal buffers are managed by NvEncodeAPI\n and the client is not responsible for the allocating or freeing the memory of\n the internal buffers.\n\n The client calls ::NvEncEncodePicture() API with input buffer I1 and output buffer O1.\n The NvEncodeAPI decides to encode I1 as P frame and submits it to encoder\n HW and returns ::NV_ENC_SUCCESS.\n The client can now read the encoded data by locking the output O1 by calling\n NvEncLockBitstream API.\n\n The client calls ::NvEncEncodePicture() API with input buffer I2 and output buffer O2.\n The NvEncodeAPI decides to encode I2 as B frame and buffers I2 by copying it\n to internal buffer and returns ::NV_ENC_ERR_NEED_MORE_INPUT.\n The error is not fatal and it notifies client that it cannot read the encoded\n data by locking the output O2 by calling ::NvEncLockBitstream() API without submitting\n more work to the NvEncodeAPI interface.\n\n The client calls ::NvEncEncodePicture() with input buffer I3 and output buffer O3.\n The NvEncodeAPI decides to encode I3 as P frame and it first submits I3 for\n encoding which will be used as backward reference frame for I2.\n The NvEncodeAPI then submits I2 for encoding and returns ::NV_ENC_SUCESS. Both\n the submission are part of the same ::NvEncEncodePicture() function call.\n The client can now read the encoded data for both the frames by locking the output\n O2 followed by  O3 ,by calling ::NvEncLockBitstream() API.\n\n The client must always lock the output in the same order in which it has submitted\n to receive the encoded bitstream in correct encoding order.\n\n * \\endcode\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] encodePicParams\n *   Pointer to the ::_NV_ENC_PIC_PARAMS structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_ENCODER_BUSY \\n\n * ::NV_ENC_ERR_NEED_MORE_INPUT \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncEncodePicture(void* encoder, NV_ENC_PIC_PARAMS* encodePicParams);\n\n// NvEncLockBitstream\n/**\n * \\brief Lock output bitstream buffer\n *\n * This function is used to lock the bitstream buffer to read the encoded data.\n * The client can only access the encoded data by calling this function.\n * The pointer to client accessible encoded data is returned in the\n * NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr field. The size of the encoded data\n * in the output buffer is returned in the NV_ENC_LOCK_BITSTREAM::bitstreamSizeInBytes\n * The NvEncodeAPI interface also returns the output picture type and picture structure\n * of the encoded frame in NV_ENC_LOCK_BITSTREAM::pictureType and\n * NV_ENC_LOCK_BITSTREAM::pictureStruct fields respectively. If the client has\n * set NV_ENC_LOCK_BITSTREAM::doNotWait to 1, the function might return\n * ::NV_ENC_ERR_LOCK_BUSY if client is operating in synchronous mode. This is not\n * a fatal failure if NV_ENC_LOCK_BITSTREAM::doNotWait is set to 1. In the above case the client can\n * retry the function after few milliseconds.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] lockBitstreamBufferParams\n *   Pointer to the ::_NV_ENC_LOCK_BITSTREAM structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_LOCK_BUSY \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncLockBitstream(void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams);\n\n// NvEncUnlockBitstream\n/**\n * \\brief Unlock the output bitstream buffer\n *\n * This function is used to unlock the output bitstream buffer after the client\n * has read the encoded data from output buffer. The client must call this function\n * to unlock the output buffer which it has previously locked using ::NvEncLockBitstream()\n * function. Using a locked bitstream buffer in ::NvEncEncodePicture() API will cause\n * the function to fail.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] bitstreamBuffer\n *   bitstream buffer pointer being unlocked\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncUnlockBitstream(void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer);\n\n// NvLockInputBuffer\n/**\n * \\brief Locks an input buffer\n *\n * This function is used to lock the input buffer to load the uncompressed YUV\n * pixel data into input buffer memory. The client must pass the NV_ENC_INPUT_PTR\n * it had previously allocated using ::NvEncCreateInputBuffer()in the\n * NV_ENC_LOCK_INPUT_BUFFER::inputBuffer field.\n * The NvEncodeAPI interface returns pointer to client accessible input buffer\n * memory in NV_ENC_LOCK_INPUT_BUFFER::bufferDataPtr field.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] lockInputBufferParams\n *   Pointer to the ::_NV_ENC_LOCK_INPUT_BUFFER structure\n *\n * \\return\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_LOCK_BUSY \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncLockInputBuffer(void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams);\n\n// NvUnlockInputBuffer\n/**\n * \\brief Unlocks the input buffer\n *\n * This function is used to unlock the input buffer memory previously locked for\n * uploading YUV pixel data. The input buffer must be unlocked before being used\n * again for encoding, otherwise NvEncodeAPI will fail the ::NvEncEncodePicture()\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] inputBuffer\n *   Pointer to the input buffer that is being unlocked.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n *\n */\nNVENCSTATUS NVENCAPI NvEncUnlockInputBuffer(void* encoder, NV_ENC_INPUT_PTR inputBuffer);\n\n// NvEncGetEncodeStats\n/**\n * \\brief Get encoding statistics.\n *\n * This function is used to retrieve the encoding statistics.\n * This API is not supported when encode device type is CUDA.\n * Note that this API will be removed in future Video Codec SDK release.\n * Clients should use NvEncLockBitstream() API to retrieve the encoding statistics.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] encodeStats\n *   Pointer to the ::_NV_ENC_STAT structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetEncodeStats(void* encoder, NV_ENC_STAT* encodeStats);\n\n// NvEncGetSequenceParams\n/**\n * \\brief Get encoded sequence and picture header.\n *\n * This function can be used to retrieve the sequence and picture header out of\n * band. The client must call this function only after the encoder has been\n * initialized using ::NvEncInitializeEncoder() function. The client must\n * allocate the memory where the NvEncodeAPI interface can copy the bitstream\n * header and pass the pointer to the memory in NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer.\n * The size of buffer is passed in the field  NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize.\n * The NvEncodeAPI interface will copy the bitstream header payload and returns\n * the actual size of the bitstream header in the field\n * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize.\n * The client must call  ::NvEncGetSequenceParams() function from the same thread which is\n * being used to call ::NvEncEncodePicture() function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] sequenceParamPayload\n *   Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncGetSequenceParams(void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload);\n\n// NvEncGetSequenceParamEx\n/**\n * \\brief Get sequence and picture header.\n *\n * This function can be used to retrieve the sequence and picture header out of band, even when\n * encoder has not been initialized using ::NvEncInitializeEncoder() function.\n * The client must allocate the memory where the NvEncodeAPI interface can copy the bitstream\n * header and pass the pointer to the memory in NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer.\n * The size of buffer is passed in the field  NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize.\n * If encoder has not been initialized using ::NvEncInitializeEncoder() function, client must\n * send NV_ENC_INITIALIZE_PARAMS as input. The NV_ENC_INITIALIZE_PARAMS passed must be same as the\n * one which will be used for initializing encoder using ::NvEncInitializeEncoder() function later.\n * If encoder is already initialized using ::NvEncInitializeEncoder() function, the provided\n * NV_ENC_INITIALIZE_PARAMS structure is ignored. The NvEncodeAPI interface will copy the bitstream\n * header payload and returns the actual size of the bitstream header in the field\n * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize. The client must call\n * ::NvEncGetSequenceParamsEx() function from the same thread which is being used to call\n * ::NvEncEncodePicture() function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] encInitParams\n *   Pointer to the _NV_ENC_INITIALIZE_PARAMS structure.\n * \\param [in,out] sequenceParamPayload\n *   Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncGetSequenceParamEx(\n    void* encoder,\n    NV_ENC_INITIALIZE_PARAMS* encInitParams,\n    NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload\n);\n\n// NvEncRegisterAsyncEvent\n/**\n * \\brief Register event for notification to encoding completion.\n *\n * This function is used to register the completion event with NvEncodeAPI\n * interface. The event is required when the client has configured the encoder to\n * work in asynchronous mode. In this mode the client needs to send a completion\n * event with every output buffer. The NvEncodeAPI interface will signal the\n * completion of the encoding process using this event. Only after the event is\n * signaled the client can get the encoded data using ::NvEncLockBitstream() function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] eventParams\n *   Pointer to the ::_NV_ENC_EVENT_PARAMS structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncRegisterAsyncEvent(void* encoder, NV_ENC_EVENT_PARAMS* eventParams);\n\n// NvEncUnregisterAsyncEvent\n/**\n * \\brief Unregister completion event.\n *\n * This function is used to unregister completion event which has been previously\n * registered using ::NvEncRegisterAsyncEvent() function. The client must unregister\n * all events before destroying the encoder using ::NvEncDestroyEncoder() function.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] eventParams\n *   Pointer to the ::_NV_ENC_EVENT_PARAMS structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncUnregisterAsyncEvent(void* encoder, NV_ENC_EVENT_PARAMS* eventParams);\n\n// NvEncMapInputResource\n/**\n * \\brief Map an externally created input resource pointer for encoding.\n *\n * Maps an externally allocated input resource [using and returns a NV_ENC_INPUT_PTR\n * which can be used for encoding in the ::NvEncEncodePicture() function. The\n * mapped resource is returned in the field NV_ENC_MAP_INPUT_RESOURCE::outputResourcePtr.\n * The NvEncodeAPI interface also returns the buffer format of the mapped resource\n * in the field NV_ENC_MAP_INPUT_RESOURCE::outbufferFmt.\n * This function provides synchronization guarantee that any graphics work submitted\n * on the input buffer is completed before the buffer is used for encoding. This is\n * also true for compute (i.e. CUDA) work, provided that the previous workload using\n * the input resource was submitted to the default stream.\n * The client should not access any input buffer while they are mapped by the encoder.\n * For D3D12 interface type, this function does not provide synchronization guarantee.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] mapInputResParams\n *   Pointer to the ::_NV_ENC_MAP_INPUT_RESOURCE structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \\n\n * ::NV_ENC_ERR_MAP_FAILED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncMapInputResource(void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams);\n\n// NvEncUnmapInputResource\n/**\n * \\brief  UnMaps a NV_ENC_INPUT_PTR  which was mapped for encoding\n *\n *\n * UnMaps an input buffer which was previously mapped using ::NvEncMapInputResource()\n * API. The mapping created using ::NvEncMapInputResource() should be invalidated\n * using this API before the external resource is destroyed by the client. The client\n * must unmap the buffer after ::NvEncLockBitstream() API returns successfully for encode\n * work submitted using the mapped input buffer.\n *\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] mappedInputBuffer\n *   Pointer to the NV_ENC_INPUT_PTR\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \\n\n * ::NV_ENC_ERR_RESOURCE_NOT_MAPPED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncUnmapInputResource(void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer);\n\n// NvEncDestroyEncoder\n/**\n * \\brief Destroy Encoding Session\n *\n * Destroys the encoder session previously created using ::NvEncOpenEncodeSession()\n * function. The client must flush the encoder before freeing any resources. In order\n * to flush the encoder the client must pass a NULL encode picture packet and either\n * wait for the ::NvEncEncodePicture() function to return in synchronous mode or wait\n * for the flush event to be signaled by the encoder in asynchronous mode.\n * The client must free all the input and output resources created using the\n * NvEncodeAPI interface before destroying the encoder. If the client is operating\n * in asynchronous mode, it must also unregister the completion events previously\n * registered.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncDestroyEncoder(void* encoder);\n\n// NvEncInvalidateRefFrames\n/**\n * \\brief Invalidate reference frames\n *\n * Invalidates reference frame based on the time stamp provided by the client.\n * The encoder marks any reference frames or any frames which have been reconstructed\n * using the corrupt frame as invalid for motion estimation and uses older reference\n * frames for motion estimation. The encoder forces the current frame to be encoded\n * as an intra frame if no reference frames are left after invalidation process.\n * This is useful for low latency application for error resiliency. The client\n * is recommended to set NV_ENC_CONFIG_H264::maxNumRefFrames to a large value so\n * that encoder can keep a backup of older reference frames in the DPB and can use them\n * for motion estimation when the newer reference frames have been invalidated.\n * This API can be called multiple times.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] invalidRefFrameTimeStamp\n *   Timestamp of the invalid reference frames which needs to be invalidated.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI NvEncInvalidateRefFrames(void* encoder, uint64_t invalidRefFrameTimeStamp);\n\n// NvEncOpenEncodeSessionEx\n/**\n * \\brief Opens an encoding session.\n *\n * Opens an encoding session and returns a pointer to the encoder interface in\n * the \\p **encoder parameter. The client should start encoding process by calling\n * this API first.\n * The client must pass a pointer to IDirect3DDevice9 device or CUDA context in the \\p *device\n * parameter. For the OpenGL interface, \\p device must be NULL. An OpenGL context must be current\n * when calling all NvEncodeAPI functions. If the creation of encoder session fails, the client must\n * call ::NvEncDestroyEncoder API before exiting.\n *\n * \\param [in] openSessionExParams\n *    Pointer to a ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS structure.\n * \\param [out] encoder\n *    Encode Session pointer to the NvEncodeAPI interface.\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_NO_ENCODE_DEVICE \\n\n * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \\n\n * ::NV_ENC_ERR_INVALID_DEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncOpenEncodeSessionEx(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS* openSessionExParams, void** encoder);\n\n// NvEncRegisterResource\n/**\n * \\brief Registers a resource with the Nvidia Video Encoder Interface.\n *\n * Registers a resource with the Nvidia Video Encoder Interface for book keeping.\n * The client is expected to pass the registered resource handle as well, while calling\n * ::NvEncMapInputResource API.\n *\n * \\param [in] encoder\n *   Pointer to the NVEncodeAPI interface.\n *\n * \\param [in] registerResParams\n *   Pointer to a ::_NV_ENC_REGISTER_RESOURCE structure\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_RESOURCE_REGISTER_FAILED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n * ::NV_ENC_ERR_UNIMPLEMENTED \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncRegisterResource(void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams);\n\n// NvEncUnregisterResource\n/**\n * \\brief Unregisters a resource previously registered with the Nvidia Video Encoder Interface.\n *\n * Unregisters a resource previously registered with the Nvidia Video Encoder Interface.\n * The client is expected to unregister any resource that it has registered with the\n * Nvidia Video Encoder Interface before destroying the resource.\n *\n * \\param [in] encoder\n *   Pointer to the NVEncodeAPI interface.\n *\n * \\param [in] registeredResource\n *   The registered resource pointer that was returned in ::NvEncRegisterResource.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n * ::NV_ENC_ERR_UNIMPLEMENTED \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncUnregisterResource(void* encoder, NV_ENC_REGISTERED_PTR registeredResource);\n\n// NvEncReconfigureEncoder\n/**\n * \\brief Reconfigure an existing encoding session.\n *\n * Reconfigure an existing encoding session.\n * The client should call this API to change/reconfigure the parameter passed during\n * NvEncInitializeEncoder API call.\n * Currently Reconfiguration of following are not supported.\n * Change in GOP structure.\n * Change in sync-Async mode.\n * Change in MaxWidth & MaxHeight.\n * Change in PTD mode.\n *\n * Resolution change is possible only if maxEncodeWidth & maxEncodeHeight of\n * NV_ENC_INITIALIZE_PARAMS is set while creating encoder session.\n *\n * \\param [in] encoder\n *   Pointer to the NVEncodeAPI interface.\n *\n * \\param [in] reInitEncodeParams\n *    Pointer to a ::NV_ENC_RECONFIGURE_PARAMS structure.\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_NO_ENCODE_DEVICE \\n\n * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \\n\n * ::NV_ENC_ERR_INVALID_DEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_GENERIC \\n\n *\n */\nNVENCSTATUS NVENCAPI\nNvEncReconfigureEncoder(void* encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams);\n\n// NvEncCreateMVBuffer\n/**\n * \\brief Allocates output MV buffer for ME only mode.\n *\n * This function is used to allocate an output MV buffer. The size of the mvBuffer is\n * dependent on the frame height and width of the last ::NvEncCreateInputBuffer() call.\n * The NV_ENC_OUTPUT_PTR returned by the NvEncodeAPI interface in the\n * ::NV_ENC_CREATE_MV_BUFFER::mvBuffer field should be used in\n * ::NvEncRunMotionEstimationOnly() API.\n * Client must lock ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using ::NvEncLockBitstream() API to get the\n * motion vector data.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in,out] createMVBufferParams\n *  Pointer to the ::NV_ENC_CREATE_MV_BUFFER structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI\nNvEncCreateMVBuffer(void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams);\n\n// NvEncDestroyMVBuffer\n/**\n * \\brief Release an output MV buffer for ME only mode.\n *\n * This function is used to release the output MV buffer allocated using\n * the ::NvEncCreateMVBuffer() function. The client must release the output\n * mvBuffer using this function before destroying the encoder session.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] mvBuffer\n *   Pointer to the mvBuffer being released.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI NvEncDestroyMVBuffer(void* encoder, NV_ENC_OUTPUT_PTR mvBuffer);\n\n// NvEncRunMotionEstimationOnly\n/**\n * \\brief Submit an input picture and reference frame for motion estimation in ME only mode.\n *\n * This function is used to submit the input frame and reference frame for motion\n * estimation. The ME parameters are passed using *meOnlyParams which is a pointer\n * to ::_NV_ENC_MEONLY_PARAMS structure.\n * Client must lock ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using ::NvEncLockBitstream() API to get the\n * motion vector data. to get motion vector data.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n * \\param [in] meOnlyParams\n *   Pointer to the ::_NV_ENC_MEONLY_PARAMS structure.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \\n\n * ::NV_ENC_ERR_DEVICE_NOT_EXIST \\n\n * ::NV_ENC_ERR_UNSUPPORTED_PARAM \\n\n * ::NV_ENC_ERR_OUT_OF_MEMORY \\n\n * ::NV_ENC_ERR_INVALID_PARAM \\n\n * ::NV_ENC_ERR_INVALID_VERSION \\n\n * ::NV_ENC_ERR_NEED_MORE_INPUT \\n\n * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \\n\n * ::NV_ENC_ERR_GENERIC \\n\n */\nNVENCSTATUS NVENCAPI\nNvEncRunMotionEstimationOnly(void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams);\n\n// NvEncodeAPIGetMaxSupportedVersion\n/**\n * \\brief Get the largest NvEncodeAPI version supported by the driver.\n *\n * This function can be used by clients to determine if the driver supports\n * the NvEncodeAPI header the application was compiled with.\n *\n * \\param [out] version\n *   Pointer to the requested value. The 4 least significant bits in the returned\n *   indicate the minor version and the rest of the bits indicate the major\n *   version of the largest supported version.\n *\n * \\return\n * ::NV_ENC_SUCCESS \\n\n * ::NV_ENC_ERR_INVALID_PTR \\n\n */\nNVENCSTATUS NVENCAPI NvEncodeAPIGetMaxSupportedVersion(uint32_t* version);\n\n// NvEncGetLastErrorString\n/**\n * \\brief Get the description of the last error reported by the API.\n *\n * This function returns a null-terminated string that can be used by clients to better understand\n * the reason for failure of a previous API call.\n *\n * \\param [in] encoder\n *   Pointer to the NvEncodeAPI interface.\n *\n * \\return\n *   Pointer to buffer containing the details of the last error encountered by the API.\n */\nconst char* NVENCAPI NvEncGetLastErrorString(void* encoder);\n\n/// \\cond API PFN\n/*\n *  Defines API function pointers\n */\ntypedef NVENCSTATUS(NVENCAPI* PNVENCOPENENCODESESSION)(\n    void* device, uint32_t deviceType, void** encoder\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEGUIDCOUNT)(void* encoder, uint32_t* encodeGUIDCount);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEGUIDS)(\n    void* encoder, GUID* GUIDs, uint32_t guidArraySize, uint32_t* GUIDCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPROFILEGUIDCOUNT)(\n    void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPROFILEGUIDS)(\n    void* encoder, GUID encodeGUID, GUID* profileGUIDs, uint32_t guidArraySize, uint32_t* GUIDCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETINPUTFORMATCOUNT)(\n    void* encoder, GUID encodeGUID, uint32_t* inputFmtCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETINPUTFORMATS)(\n    void* encoder,\n    GUID encodeGUID,\n    NV_ENC_BUFFER_FORMAT* inputFmts,\n    uint32_t inputFmtArraySize,\n    uint32_t* inputFmtCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODECAPS)(\n    void* encoder, GUID encodeGUID, NV_ENC_CAPS_PARAM* capsParam, int* capsVal\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCOUNT)(\n    void* encoder, GUID encodeGUID, uint32_t* encodePresetGUIDCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETGUIDS)(\n    void* encoder,\n    GUID encodeGUID,\n    GUID* presetGUIDs,\n    uint32_t guidArraySize,\n    uint32_t* encodePresetGUIDCount\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCONFIG)(\n    void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_PRESET_CONFIG* presetConfig\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODEPRESETCONFIGEX)(\n    void* encoder,\n    GUID encodeGUID,\n    GUID presetGUID,\n    NV_ENC_TUNING_INFO tuningInfo,\n    NV_ENC_PRESET_CONFIG* presetConfig\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCINITIALIZEENCODER)(\n    void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCCREATEINPUTBUFFER)(\n    void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYINPUTBUFFER)(\n    void* encoder, NV_ENC_INPUT_PTR inputBuffer\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCCREATEBITSTREAMBUFFER)(\n    void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYBITSTREAMBUFFER)(\n    void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCENCODEPICTURE)(\n    void* encoder, NV_ENC_PIC_PARAMS* encodePicParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCLOCKBITSTREAM)(\n    void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCUNLOCKBITSTREAM)(\n    void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCLOCKINPUTBUFFER)(\n    void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCUNLOCKINPUTBUFFER)(void* encoder, NV_ENC_INPUT_PTR inputBuffer);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETENCODESTATS)(void* encoder, NV_ENC_STAT* encodeStats);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETSEQUENCEPARAMS)(\n    void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCREGISTERASYNCEVENT)(\n    void* encoder, NV_ENC_EVENT_PARAMS* eventParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCUNREGISTERASYNCEVENT)(\n    void* encoder, NV_ENC_EVENT_PARAMS* eventParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCMAPINPUTRESOURCE)(\n    void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCUNMAPINPUTRESOURCE)(\n    void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYENCODER)(void* encoder);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCINVALIDATEREFFRAMES)(\n    void* encoder, uint64_t invalidRefFrameTimeStamp\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCOPENENCODESESSIONEX)(\n    NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS* openSessionExParams, void** encoder\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCREGISTERRESOURCE)(\n    void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCUNREGISTERRESOURCE)(\n    void* encoder, NV_ENC_REGISTERED_PTR registeredRes\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCRECONFIGUREENCODER)(\n    void* encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams\n);\n\ntypedef NVENCSTATUS(NVENCAPI* PNVENCCREATEMVBUFFER)(\n    void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCDESTROYMVBUFFER)(void* encoder, NV_ENC_OUTPUT_PTR mvBuffer);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCRUNMOTIONESTIMATIONONLY)(\n    void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams\n);\ntypedef const char*(NVENCAPI* PNVENCGETLASTERROR)(void* encoder);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCSETIOCUDASTREAMS)(\n    void* encoder, NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream\n);\ntypedef NVENCSTATUS(NVENCAPI* PNVENCGETSEQUENCEPARAMEX)(\n    void* encoder,\n    NV_ENC_INITIALIZE_PARAMS* encInitParams,\n    NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload\n);\n\n/// \\endcond\n\n/** @} */ /* END ENCODE_FUNC */\n\n/**\n * \\ingroup ENCODER_STRUCTURE\n * NV_ENCODE_API_FUNCTION_LIST\n */\ntypedef struct _NV_ENCODE_API_FUNCTION_LIST {\n    uint32_t version; /**< [in]: Client should pass NV_ENCODE_API_FUNCTION_LIST_VER. */\n    uint32_t reserved; /**< [in]: Reserved and should be set to 0. */\n    PNVENCOPENENCODESESSION\n        nvEncOpenEncodeSession; /**< [out]: Client should access ::NvEncOpenEncodeSession() API\n                                   through this pointer.        */\n    PNVENCGETENCODEGUIDCOUNT\n        nvEncGetEncodeGUIDCount; /**< [out]: Client should access ::NvEncGetEncodeGUIDCount() API\n                                    through this pointer.       */\n    PNVENCGETENCODEPRESETCOUNT nvEncGetEncodeProfileGUIDCount; /**< [out]: Client should access\n                                                                  ::NvEncGetEncodeProfileGUIDCount()\n                                                                  API through this pointer.*/\n    PNVENCGETENCODEPRESETGUIDS\n        nvEncGetEncodeProfileGUIDs; /**< [out]: Client should access ::NvEncGetEncodeProfileGUIDs()\n                                       API through this pointer.    */\n    PNVENCGETENCODEGUIDS\n        nvEncGetEncodeGUIDs; /**< [out]: Client should access ::NvEncGetEncodeGUIDs() API through\n                                this pointer.           */\n    PNVENCGETINPUTFORMATCOUNT\n        nvEncGetInputFormatCount; /**< [out]: Client should access ::NvEncGetInputFormatCount() API\n                                     through this pointer.      */\n    PNVENCGETINPUTFORMATS\n        nvEncGetInputFormats; /**< [out]: Client should access ::NvEncGetInputFormats() API through\n                                 this pointer.          */\n    PNVENCGETENCODECAPS nvEncGetEncodeCaps; /**< [out]: Client should access ::NvEncGetEncodeCaps()\n                                               API through this pointer.            */\n    PNVENCGETENCODEPRESETCOUNT\n        nvEncGetEncodePresetCount; /**< [out]: Client should access ::NvEncGetEncodePresetCount()\n                                      API through this pointer.     */\n    PNVENCGETENCODEPRESETGUIDS\n        nvEncGetEncodePresetGUIDs; /**< [out]: Client should access ::NvEncGetEncodePresetGUIDs()\n                                      API through this pointer.     */\n    PNVENCGETENCODEPRESETCONFIG\n        nvEncGetEncodePresetConfig; /**< [out]: Client should access ::NvEncGetEncodePresetConfig()\n                                       API through this pointer.    */\n    PNVENCINITIALIZEENCODER\n        nvEncInitializeEncoder; /**< [out]: Client should access ::NvEncInitializeEncoder() API\n                                   through this pointer.        */\n    PNVENCCREATEINPUTBUFFER\n        nvEncCreateInputBuffer; /**< [out]: Client should access ::NvEncCreateInputBuffer() API\n                                   through this pointer.        */\n    PNVENCDESTROYINPUTBUFFER\n        nvEncDestroyInputBuffer; /**< [out]: Client should access ::NvEncDestroyInputBuffer() API\n                                    through this pointer.       */\n    PNVENCCREATEBITSTREAMBUFFER\n        nvEncCreateBitstreamBuffer; /**< [out]: Client should access ::NvEncCreateBitstreamBuffer()\n                                       API through this pointer.    */\n    PNVENCDESTROYBITSTREAMBUFFER\n        nvEncDestroyBitstreamBuffer; /**< [out]: Client should access\n                                        ::NvEncDestroyBitstreamBuffer() API through this pointer. */\n    PNVENCENCODEPICTURE nvEncEncodePicture; /**< [out]: Client should access ::NvEncEncodePicture()\n                                               API through this pointer.            */\n    PNVENCLOCKBITSTREAM nvEncLockBitstream; /**< [out]: Client should access ::NvEncLockBitstream()\n                                               API through this pointer.            */\n    PNVENCUNLOCKBITSTREAM\n        nvEncUnlockBitstream; /**< [out]: Client should access ::NvEncUnlockBitstream() API through\n                                 this pointer.          */\n    PNVENCLOCKINPUTBUFFER\n        nvEncLockInputBuffer; /**< [out]: Client should access ::NvEncLockInputBuffer() API through\n                                 this pointer.          */\n    PNVENCUNLOCKINPUTBUFFER\n        nvEncUnlockInputBuffer; /**< [out]: Client should access ::NvEncUnlockInputBuffer() API\n                                   through this pointer.        */\n    PNVENCGETENCODESTATS\n        nvEncGetEncodeStats; /**< [out]: Client should access ::NvEncGetEncodeStats() API through\n                                this pointer.           */\n    PNVENCGETSEQUENCEPARAMS\n        nvEncGetSequenceParams; /**< [out]: Client should access ::NvEncGetSequenceParams() API\n                                   through this pointer.        */\n    PNVENCREGISTERASYNCEVENT\n        nvEncRegisterAsyncEvent; /**< [out]: Client should access ::NvEncRegisterAsyncEvent() API\n                                    through this pointer.       */\n    PNVENCUNREGISTERASYNCEVENT\n        nvEncUnregisterAsyncEvent; /**< [out]: Client should access ::NvEncUnregisterAsyncEvent()\n                                      API through this pointer.     */\n    PNVENCMAPINPUTRESOURCE\n        nvEncMapInputResource; /**< [out]: Client should access ::NvEncMapInputResource() API\n                                  through this pointer.         */\n    PNVENCUNMAPINPUTRESOURCE\n        nvEncUnmapInputResource; /**< [out]: Client should access ::NvEncUnmapInputResource() API\n                                    through this pointer.       */\n    PNVENCDESTROYENCODER\n        nvEncDestroyEncoder; /**< [out]: Client should access ::NvEncDestroyEncoder() API through\n                                this pointer.           */\n    PNVENCINVALIDATEREFFRAMES\n        nvEncInvalidateRefFrames; /**< [out]: Client should access ::NvEncInvalidateRefFrames() API\n                                     through this pointer.      */\n    PNVENCOPENENCODESESSIONEX\n        nvEncOpenEncodeSessionEx; /**< [out]: Client should access ::NvEncOpenEncodeSession() API\n                                     through this pointer.        */\n    PNVENCREGISTERRESOURCE\n        nvEncRegisterResource; /**< [out]: Client should access ::NvEncRegisterResource() API\n                                  through this pointer.         */\n    PNVENCUNREGISTERRESOURCE\n        nvEncUnregisterResource; /**< [out]: Client should access ::NvEncUnregisterResource() API\n                                    through this pointer.       */\n    PNVENCRECONFIGUREENCODER\n        nvEncReconfigureEncoder; /**< [out]: Client should access ::NvEncReconfigureEncoder() API\n                                    through this pointer.       */\n    void* reserved1;\n    PNVENCCREATEMVBUFFER nvEncCreateMVBuffer; /**< [out]: Client should access ::NvEncCreateMVBuffer\n                                                 API through this pointer.             */\n    PNVENCDESTROYMVBUFFER\n        nvEncDestroyMVBuffer; /**< [out]: Client should access ::NvEncDestroyMVBuffer API through\n                                 this pointer.            */\n    PNVENCRUNMOTIONESTIMATIONONLY\n        nvEncRunMotionEstimationOnly; /**< [out]: Client should access\n                                         ::NvEncRunMotionEstimationOnly API through this pointer. */\n    PNVENCGETLASTERROR\n        nvEncGetLastErrorString; /**< [out]: Client should access ::nvEncGetLastErrorString API\n                                    through this pointer.         */\n    PNVENCSETIOCUDASTREAMS\n        nvEncSetIOCudaStreams; /**< [out]: Client should access ::nvEncSetIOCudaStreams API through\n                                  this pointer.           */\n    PNVENCGETENCODEPRESETCONFIGEX nvEncGetEncodePresetConfigEx; /**< [out]: Client should access\n                                                                   ::NvEncGetEncodePresetConfigEx()\n                                                                   API through this pointer.  */\n    PNVENCGETSEQUENCEPARAMEX\n        nvEncGetSequenceParamEx; /**< [out]: Client should access ::NvEncGetSequenceParamEx() API\n                                    through this pointer.       */\n    void* reserved2[277]; /**< [in]:  Reserved and must be set to NULL */\n} NV_ENCODE_API_FUNCTION_LIST;\n\n/** Macro for constructing the version field of ::_NV_ENCODEAPI_FUNCTION_LIST. */\n#define NV_ENCODE_API_FUNCTION_LIST_VER NVENCAPI_STRUCT_VERSION(2)\n\n// NvEncodeAPICreateInstance\n/**\n * \\ingroup ENCODE_FUNC\n * Entry Point to the NvEncodeAPI interface.\n *\n * Creates an instance of the NvEncodeAPI interface, and populates the\n * pFunctionList with function pointers to the API routines implemented by the\n * NvEncodeAPI interface.\n *\n * \\param [out] functionList\n *\n * \\return\n * ::NV_ENC_SUCCESS\n * ::NV_ENC_ERR_INVALID_PTR\n */\nNVENCSTATUS NVENCAPI NvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST* functionList);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/openvr_driver_wrap.h",
    "content": "#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-parameter\"\n#include <openvr_driver.h>\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/ColorCorrectionPixelShader.hlsl",
    "content": "\ncbuffer ColorCorrectionParams {\n\tfloat renderWidth;\n\tfloat renderHeight;\n\tfloat brightness;\n\tfloat contrast;\n\tfloat saturation;\n\tfloat gamma;\n\tfloat sharpening;\n\tfloat _align;\n};\n\nconst static float DX = 1. / renderWidth;\nconst static float DY = 1. / renderHeight;\nconst static float sharpenNeighbourWeight = -sharpening / 8.;\n\nTexture2D<float4> sourceTexture;\n\nSamplerState bilinearSampler {\n\tFilter = MIN_MAG_LINEAR_MIP_POINT;\n\tAddressU = CLAMP;\n\tAddressV = CLAMP;\n};\n\nfloat3 GetSharpenNeighborComponent(float2 uv, float xoff, float yoff) {\n\treturn sourceTexture.Sample(bilinearSampler, uv + float2(xoff, yoff)).rgb * sharpenNeighbourWeight;\n}\n\nfloat3 blendLighten(float3 base, float3 blend) {\n    return float3(max(base.r,blend.r),max(base.g,blend.g),max(base.b,blend.b));\n}\n\n// https://forum.unity.com/threads/hue-saturation-brightness-contrast-shader.260649/\nfloat4 main(float2 uv : TEXCOORD0) : SV_Target{\n\t// sharpening\n\tfloat3 pixel = sourceTexture.Sample(bilinearSampler, uv).rgb * (sharpening + 1.);\n\tpixel += GetSharpenNeighborComponent(uv, -DX, -DY);\n\tpixel += GetSharpenNeighborComponent(uv, 0, -DY);\n\tpixel += GetSharpenNeighborComponent(uv, +DX, -DY);\n\tpixel += GetSharpenNeighborComponent(uv, +DX, 0);\n\tpixel += GetSharpenNeighborComponent(uv, +DX, +DY);\n\tpixel += GetSharpenNeighborComponent(uv, 0, +DY);\n\tpixel += GetSharpenNeighborComponent(uv, -DX, +DY);\n\tpixel += GetSharpenNeighborComponent(uv, -DX, 0);\n\n\tpixel += brightness;                                                                            // brightness\n\tpixel = (pixel - 0.5) * contrast + 0.5f;                                                        // contast\n    pixel = blendLighten(lerp(dot(pixel, float3(0.299, 0.587, 0.114)), pixel, saturation), pixel);  // saturation + lighten only\n\n\tpixel = clamp(pixel, 0, 1);\n\tpixel = pow(pixel, 1. / gamma);                                                                 // gamma\n\n\treturn float4(pixel, 1);\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/CompressAxisAlignedPixelShader.hlsl",
    "content": "// Compress to rectangular slices\n\n#include \"FoveatedRendering.hlsli\"\n\n\n\nTexture2D<float4> compositionTexture;\n\nSamplerState trilinearSampler {\n\tFilter = MIN_MAG_MIP_LINEAR;\n\t//AddressU = Wrap;\n\t//AddressV = Wrap;\n};\n\nfloat4 main(float2 uv : TEXCOORD0) : SV_Target {\n\tbool isRightEye = uv.x > 0.5;\n\tfloat2 eyeUV = TextureToEyeUV(uv, isRightEye) / eyeSizeRatio;\n\n\tfloat2 c0 = (1. - centerSize) / 2.;\n\tfloat2 c1 = (edgeRatio - 1.) * c0 * (centerShift + 1.) / edgeRatio;\n\tfloat2 c2 = (edgeRatio - 1.) * centerSize + 1.;\n\n\tfloat2 loBound = c0 * (centerShift + 1.) / c2;\n\tfloat2 hiBound = c0 * (centerShift - 1.) / c2 + 1.;\n\tfloat2 underBound = float2(eyeUV.x < loBound.x, eyeUV.y < loBound.y);\n\tfloat2 inBound = float2(loBound.x < eyeUV.x && eyeUV.x < hiBound.x,\n\t\t\t\t\t\t\tloBound.y < eyeUV.y && eyeUV.y < hiBound.y);\n\tfloat2 overBound = float2(eyeUV.x > hiBound.x, eyeUV.y > hiBound.y);\n\n\tfloat2 center = eyeUV * c2 / edgeRatio + c1;\n\tfloat2 d2 = eyeUV * c2;\n\tfloat2 d3 = (eyeUV - 1.) * c2 + 1.;\n\tfloat2 g1 = eyeUV / loBound;\n\tfloat2 g2 = (1. - eyeUV) / (1. - hiBound);\n\n\tfloat2 leftEdge = g1 * center + (1. - g1) * d2;\n\tfloat2 rightEdge = g2 * center + (1. - g2) * d3;\n\n\tfloat2 compressedUV = underBound * leftEdge + inBound * center + overBound * rightEdge;\n\n\treturn compositionTexture.Sample(trilinearSampler, EyeToTextureUV(compressedUV, isRightEye));\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/FoveatedRendering.hlsli",
    "content": "cbuffer FoveationVars {\n\tuint2 targetResolution;\n\tuint2 optimizedResolution;\n\tfloat2 eyeSizeRatio;\n\tfloat2 centerSize;\n\tfloat2 centerShift;\n\tfloat2 edgeRatio;\n};\n\nfloat2 TextureToEyeUV(float2 textureUV, bool isRightEye) {\n\t// flip distortion horizontally for right eye\n\t// left: x * 2; right: (1 - x) * 2\n\treturn float2((textureUV.x + float(isRightEye) * (1. - 2. * textureUV.x)) * 2., textureUV.y);\n}\n\nfloat2 EyeToTextureUV(float2 eyeUV, bool isRightEye) {\n\t// saturate is used to avoid color bleeding between the two sides of the texture or with the black border when filtering\n\t//float2 clampedUV = saturate(eyeUV);\n\t// left: x / 2; right 1 - (x / 2)\n\t//return float2(clampedUV.x / 2. + float(isRightEye) * (1. - clampedUV.x), clampedUV.y);\n\treturn float2(eyeUV.x * .5 + float(isRightEye) * (1. - eyeUV.x), eyeUV.y);\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/FrameRender.fx",
    "content": "cbuffer FrameRenderParams {\n\tfloat encodingGamma;\n\tfloat _padding0;\n\tfloat _padding1;\n\tfloat _padding2;\n};\n\nTexture2D txLeft : register(t0);\nTexture2D txRight : register(t1);\nSamplerState samLinear : register(s0);\n\nstruct VS_INPUT\n{\n\tfloat4 Pos : POSITION;\n\tfloat2 Tex : TEXCOORD;\n\tuint    View : VIEW;\n};\n\nstruct PS_INPUT\n{\n\tfloat4 Pos : SV_POSITION;\n\tfloat2 Tex : TEXCOORD;\n\tuint    View : VIEW;\n};\n\n#define SRGB_GAMMA_TO_NONLINEAR (1.0 / 2.4)\n#define SRGB_GAMMA_TO_LINEAR (2.4)\n\nfloat4 EncodingLinearToNonlinearRGB(float4 color, float gamma)\n{\n\tfloat4 nonlinearColor;\n\n\tnonlinearColor.r = (color.r <= 0.0) ? color.r : pow(color.r, gamma);\n\tnonlinearColor.g = (color.g <= 0.0) ? color.g : pow(color.g, gamma);\n\tnonlinearColor.b = (color.b <= 0.0) ? color.b : pow(color.b, gamma);\n\tnonlinearColor.a = (color.a <= 0.0) ? color.a : pow(color.a, gamma);\n\n\treturn nonlinearColor;\n}\n\nfloat4 LinearToNonlinearRGB(float4 color, float gamma)\n{\n\tfloat4 nonlinearColor;\n\n\tnonlinearColor.r = (color.r <= 0.0031308) ? (color.r * 12.92) : (1.055 * pow(color.r, gamma) - 0.055);\n\tnonlinearColor.g = (color.g <= 0.0031308) ? (color.g * 12.92) : (1.055 * pow(color.g, gamma) - 0.055);\n\tnonlinearColor.b = (color.b <= 0.0031308) ? (color.b * 12.92) : (1.055 * pow(color.b, gamma) - 0.055);\n\tnonlinearColor.a = (color.a <= 0.0031308) ? (color.a * 12.92) : (1.055 * pow(color.a, gamma) - 0.055);\n\n\treturn nonlinearColor;\n}\n\nfloat4 NonlinearToLinearRGB(float4 color, float gamma)\n{\n\tfloat4 linearColor;\n\n\tlinearColor.r = (color.r <= 0.04045) ? (color.r / 12.92) : pow((color.r + 0.055) / 1.055, gamma);\n\tlinearColor.g = (color.g <= 0.04045) ? (color.g / 12.92) : pow((color.g + 0.055) / 1.055, gamma);\n\tlinearColor.b = (color.b <= 0.04045) ? (color.b / 12.92) : pow((color.b + 0.055) / 1.055, gamma);\n\tlinearColor.a = (color.a <= 0.04045) ? (color.a / 12.92) : pow((color.a + 0.055) / 1.055, gamma);\n\n    return linearColor;\n}\n\nPS_INPUT VS(VS_INPUT input)\n{\n\tPS_INPUT output = (PS_INPUT)0;\n\toutput.Pos = input.Pos;\n\toutput.Tex = input.Tex;\n\toutput.View = input.View;\n\n\treturn output;\n}\nfloat4 PS(PS_INPUT input) : SV_Target\n{\n\tfloat4 color = float4(1.0, 0.0, 0.0, 1.0);\n\tuint correctionType = (input.View >> 1) & 0xF;\n\tuint shouldClamp = (input.View >> 5);\n\n\tif ((input.View & 1) == 1) {\n\t\tcolor = txRight.Sample(samLinear, input.Tex);\n\t}\n\telse {\n\t\tcolor = txLeft.Sample(samLinear, input.Tex);\n\t}\n\t\n\tif (shouldClamp == (uint)1) {\n\t\tcolor = clamp(color, 0.0, 1.0);\n\t}\n\n\tcolor = EncodingLinearToNonlinearRGB(color, encodingGamma);\n\n\tif (correctionType == (uint)1) { // Left View sRGB\n\t\tcolor = LinearToNonlinearRGB(color, SRGB_GAMMA_TO_NONLINEAR);\n\t}\n\telse if (correctionType == (uint)2) { // Left View non-HDR linear\n\t\tcolor = NonlinearToLinearRGB(color, SRGB_GAMMA_TO_LINEAR);\n\t}\n\tif (shouldClamp == (uint)2) {\n\t\tcolor = clamp(color, 0.0, 1.0);\n\t}\n\n\treturn color;\n};"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/FrameRenderPS.hlsl",
    "content": "#include \"FrameRender.fx\"\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/FrameRenderVS.hlsl",
    "content": "#include \"FrameRender.fx\"\n"
  },
  {
    "path": "alvr/server_openvr/cpp/alvr_server/shader/rgbtoyuv420.hlsl",
    "content": "cbuffer YUVParams {\n\tfloat4 offset;\n\tfloat4 yCoeff;\n\tfloat4 uCoeff;\n\tfloat4 vCoeff;\n\n\tfloat renderWidth;\n\tfloat renderHeight;\n\tfloat _padding0;\n\tfloat _padding1;\n};\n\nTexture2D<float4> sourceTexture;\n\nstruct PS_OUTPUT\n{\n\tfloat4 plane_Y: SV_Target0;\n\tfloat4 plane_UV: SV_Target1;\n};\n\nSamplerState bilinearSampler {\n\tFilter = MIN_MAG_LINEAR_MIP_POINT;\n\tAddressU = CLAMP;\n\tAddressV = CLAMP;\n};\n\nPS_OUTPUT main(float2 uv : TEXCOORD0) {\n\tPS_OUTPUT output;\n\n\tuint2 uvTexels = uint2(uv * float2(renderWidth, renderHeight));\n\n\t// Y @ 1x for YUV420\n\tfloat3 point1 = sourceTexture.Sample(bilinearSampler, uv).rgb;\n\tfloat y = dot(point1, yCoeff.rgb) + offset.x;\n\n\t// UV @ 1/2x for YUV420\n\tfloat2 image_uv = float2((uvTexels.x * 2) % renderWidth / renderWidth, (uvTexels.y * 2) % renderHeight / renderHeight);\n\tfloat3 point2 = sourceTexture.Sample(bilinearSampler, image_uv).rgb;\n\tfloat  u = dot(point2, uCoeff.rgb) + offset.y;\n\tfloat  v = dot(point2, vCoeff.rgb) + offset.z;\n\n\toutput.plane_Y = float4(y, 0, 0, 1);\n\toutput.plane_UV = float4(u, v, 0, 1);\n\n\treturn output;\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/CEncoder.cpp",
    "content": "#include \"CEncoder.h\"\n\n#include <chrono>\n#include <exception>\n#include <fstream>\n#include <iostream>\n#include <memory>\n#include <poll.h>\n#include <sstream>\n#include <stdexcept>\n#include <stdlib.h>\n#include <string>\n#include <sys/mman.h>\n#include <sys/poll.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include \"ALVR-common/packet_types.h\"\n#include \"EncodePipeline.h\"\n#include \"FrameRender.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/PoseHistory.h\"\n#include \"alvr_server/Settings.h\"\n#include \"ffmpeg_helper.h\"\n#include \"protocol.h\"\n\nextern \"C\" {\n#include <libavutil/avutil.h>\n}\n\nCEncoder::CEncoder(std::shared_ptr<PoseHistory> poseHistory)\n    : m_poseHistory(poseHistory) { }\n\nCEncoder::~CEncoder() { Stop(); }\n\nnamespace {\nvoid read_exactly(pollfd pollfds, char* out, size_t size, std::atomic_bool& exiting) {\n    while (not exiting and size != 0) {\n        int timeout = 1; // poll api doesn't fit perfectly(100 mircoseconds) poll uses milliseconds\n                         // we do the best we can(1000 mircoseconds)\n        pollfds.events = POLLIN;\n        int count = poll(&pollfds, 1, timeout);\n        if (count < 0) {\n            throw MakeException(\"poll failed: %s\", strerror(errno));\n        } else if (count == 1) {\n            int s = read(pollfds.fd, out, size);\n            if (s == -1) {\n                throw MakeException(\"read failed: %s\", strerror(errno));\n            }\n            out += s;\n            size -= s;\n        }\n    }\n}\n\nvoid read_latest(pollfd pollfds, char* out, size_t size, std::atomic_bool& exiting) {\n    read_exactly(pollfds, out, size, exiting);\n    while (not exiting) {\n        int timeout = 0; // poll api fixes the original perfectly(0 microseconds)\n        pollfds.events = POLLIN;\n        int count = poll(&pollfds, 1, timeout);\n        if (count == 0)\n            return;\n        read_exactly(pollfds, out, size, exiting);\n    }\n}\n\nint accept_timeout(pollfd socket, std::atomic_bool& exiting) {\n    while (not exiting) {\n        int timeout = 15; // poll api also fits the original perfectly(15000 microseconds)\n        socket.events = POLLIN;\n        int count = poll(&socket, 1, timeout);\n        if (count < 0) {\n            throw MakeException(\"poll failed: %s\", strerror(errno));\n        } else if (count == 1) {\n            return accept4(socket.fd, NULL, NULL, SOCK_CLOEXEC);\n        }\n    }\n    return -1;\n}\n\nvoid av_logfn(void*, int level, const char* data, va_list va) {\n    if (level >\n#ifdef DEBUG\n        AV_LOG_DEBUG)\n#else\n        AV_LOG_INFO)\n#endif\n        return;\n\n    char buf[256];\n    vsnprintf(buf, sizeof(buf), data, va);\n\n    if (level <= AV_LOG_ERROR)\n        Error(\"Encoder: %s\", buf);\n    else\n        Info(\"Encoder: %s\", buf);\n}\n\n} // namespace\n\nvoid CEncoder::GetFds(int client, int (*received_fds)[6]) {\n    struct msghdr msg;\n    struct cmsghdr* cmsg;\n    union {\n        struct cmsghdr cm;\n        u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024];\n    } control_un;\n    struct iovec iov[1];\n    char data[1];\n    int ret;\n\n    msg.msg_control = &control_un;\n    msg.msg_controllen = sizeof(control_un);\n    msg.msg_flags = 0;\n    msg.msg_name = NULL;\n    msg.msg_namelen = 0;\n    iov[0].iov_base = data;\n    iov[0].iov_len = 1;\n    msg.msg_iov = iov;\n    msg.msg_iovlen = 1;\n\n    ret = recvmsg(client, &msg, 0);\n    if (ret == -1) {\n        throw MakeException(\"recvmsg failed: %s\", strerror(errno));\n    }\n\n    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {\n            memcpy(received_fds, CMSG_DATA(cmsg), sizeof(*received_fds));\n            break;\n        }\n    }\n\n    if (cmsg == NULL) {\n        throw MakeException(\"cmsg is NULL\");\n    }\n}\n\nvoid CEncoder::Run() {\n    Info(\"CEncoder::Run\\n\");\n    m_socketPath = getenv(\"XDG_RUNTIME_DIR\");\n    m_socketPath += \"/alvr-ipc\";\n\n    int ret;\n    // we don't really care about what happends with unlink, it's just incase we crashed before this\n    // run\n    ret = unlink(m_socketPath.c_str());\n\n    m_socket.fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);\n    struct sockaddr_un name;\n    if (m_socket.fd == -1) {\n        perror(\"socket\");\n        exit(1);\n    }\n\n    memset(&name, 0, sizeof(name));\n    name.sun_family = AF_UNIX;\n    strncpy(name.sun_path, m_socketPath.c_str(), sizeof(name.sun_path) - 1);\n\n    ret = bind(m_socket.fd, (const struct sockaddr*)&name, sizeof(name));\n    if (ret == -1) {\n        perror(\"bind\");\n        exit(1);\n    }\n\n    ret = listen(m_socket.fd, 1024);\n    if (ret == -1) {\n        perror(\"listen\");\n        exit(1);\n    }\n\n    Info(\"CEncoder Listening\\n\");\n    struct pollfd client;\n    client.fd = accept_timeout(m_socket, m_exiting);\n    if (m_exiting)\n        return;\n    init_packet init;\n    client.events = POLLIN;\n    read_exactly(client, (char*)&init, sizeof(init), m_exiting);\n    if (m_exiting)\n        return;\n\n    // check that pointer types are null, other values would not make sense over a socket\n    assert(init.image_create_info.queueFamilyIndexCount == 0);\n    assert(init.image_create_info.pNext == NULL);\n\n    char ifbuf[256];\n    char ifbuf2[256];\n    sprintf(ifbuf, \"/proc/%d/cmdline\", (int)init.source_pid);\n    std::ifstream ifscmdl(ifbuf);\n    ifscmdl >> ifbuf2;\n    Info(\"CEncoder client connected, pid %d, cmdline %s\\n\", (int)init.source_pid, ifbuf2);\n\n    try {\n        GetFds(client.fd, &m_fds);\n\n        m_connected = true;\n\n        fprintf(stderr, \"\\n\\nWe are initalizing Vulkan in CEncoder thread\\n\\n\\n\");\n\n        av_log_set_callback(av_logfn);\n\n        alvr::VkContext vk_ctx(init.device_uuid.data(), {});\n\n        FrameRender render(vk_ctx, init, m_fds);\n        auto output = render.CreateOutput();\n\n        alvr::VkFrame frame(\n            vk_ctx, output.image, output.imageInfo, output.size, output.memory, output.drm\n        );\n        auto encode_pipeline = alvr::EncodePipeline::Create(\n            &render,\n            vk_ctx,\n            frame,\n            output.imageInfo,\n            render.GetEncodingWidth(),\n            render.GetEncodingHeight()\n        );\n\n        bool valid_timestamps = true;\n\n        fprintf(stderr, \"CEncoder starting to read present packets\");\n        present_packet frame_info;\n        while (not m_exiting) {\n            read_latest(client, (char*)&frame_info, sizeof(frame_info), m_exiting);\n\n            encode_pipeline->SetParams(GetDynamicEncoderParams());\n\n            auto pose = m_poseHistory->GetBestPoseMatch((const vr::HmdMatrix34_t&)frame_info.pose);\n            if (!pose) {\n                continue;\n            }\n\n            if (m_captureFrame) {\n                m_captureFrame = false;\n                render.CaptureInputFrame(\n                    Settings::Instance().m_captureFrameDir + \"/alvr_frame_input.ppm\"\n                );\n                render.CaptureOutputFrame(\n                    Settings::Instance().m_captureFrameDir + \"/alvr_frame_output.ppm\"\n                );\n            }\n\n            render.Render(frame_info.image, frame_info.semaphore_value);\n\n            if (!valid_timestamps) {\n                ReportPresent(pose->targetTimestampNs, 0);\n                ReportComposed(pose->targetTimestampNs, 0);\n            }\n\n            encode_pipeline->PushFrame(pose->targetTimestampNs, m_scheduler.CheckIDRInsertion());\n\n            static_assert(sizeof(frame_info.pose) == sizeof(vr::HmdMatrix34_t&));\n\n            alvr::FramePacket packet;\n            if (!encode_pipeline->GetEncoded(packet)) {\n                Error(\"Failed to get encoded data!\");\n                continue;\n            }\n\n            if (valid_timestamps) {\n                auto render_timestamps = render.GetTimestamps();\n                auto encode_timestamp = encode_pipeline->GetTimestamp();\n\n                uint64_t present_offset = render_timestamps.now - render_timestamps.renderBegin;\n                uint64_t composed_offset = 0;\n\n                valid_timestamps = render_timestamps.now != 0;\n\n                if (encode_timestamp.gpu) {\n                    composed_offset = render_timestamps.now - encode_timestamp.gpu;\n                } else if (encode_timestamp.cpu) {\n                    auto now = std::chrono::duration_cast<std::chrono::nanoseconds>(\n                                   std::chrono::steady_clock::now().time_since_epoch()\n                    )\n                                   .count();\n                    composed_offset = now - encode_timestamp.cpu;\n                } else {\n                    composed_offset = render_timestamps.now - render_timestamps.renderComplete;\n                }\n\n                if (present_offset < composed_offset) {\n                    present_offset = composed_offset;\n                }\n\n                ReportPresent(pose->targetTimestampNs, present_offset);\n                ReportComposed(pose->targetTimestampNs, composed_offset);\n            }\n\n            ParseFrameNals(\n                encode_pipeline->GetCodec(), packet.data, packet.size, packet.pts, packet.isIDR\n            );\n        }\n    } catch (std::exception& e) {\n        std::stringstream err;\n        err << \"error in encoder thread: \" << e.what();\n        Error(err.str().c_str());\n    }\n\n    client.events = POLLHUP;\n    close(client.fd);\n}\n\nvoid CEncoder::Stop() {\n    m_exiting = true;\n    m_socket.events = POLLHUP;\n    close(m_socket.fd);\n    unlink(m_socketPath.c_str());\n}\n\nvoid CEncoder::OnStreamStart() { m_scheduler.OnStreamStart(); }\n\nvoid CEncoder::InsertIDR() { m_scheduler.InsertIDR(); }\n\nvoid CEncoder::CaptureFrame() { m_captureFrame = true; }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/CEncoder.h",
    "content": "#pragma once\n\n#include \"alvr_server/IDRScheduler.h\"\n#include \"shared/threadtools.h\"\n#include <atomic>\n#include <memory>\n#include <poll.h>\n#include <sys/types.h>\n\nclass PoseHistory;\n\nclass CEncoder : public CThread {\npublic:\n    CEncoder(std::shared_ptr<PoseHistory> poseHistory);\n    ~CEncoder();\n    bool Init() override { return true; }\n    void Run() override;\n\n    void Stop();\n    void OnStreamStart();\n    void InsertIDR();\n    bool IsConnected() { return m_connected; }\n    void CaptureFrame();\n\nprivate:\n    void GetFds(int client, int (*fds)[6]);\n    std::shared_ptr<PoseHistory> m_poseHistory;\n    std::atomic_bool m_exiting { false };\n    IDRScheduler m_scheduler;\n    pollfd m_socket;\n    std::string m_socketPath;\n    int m_fds[6];\n    bool m_connected = false;\n    std::atomic_bool m_captureFrame = false;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/CrashHandler.cpp",
    "content": "#include \"../../alvr_server/bindings.h\"\n\nvoid HookCrashHandler() { }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipeline.cpp",
    "content": "#include \"EncodePipeline.h\"\n\n#include \"EncodePipelineNvEnc.h\"\n#include \"EncodePipelineSW.h\"\n#include \"EncodePipelineVAAPI.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"ffmpeg_helper.h\"\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n}\n\nvoid alvr::EncodePipeline::SetParams(FfiDynamicEncoderParams params) {\n    if (params.updated) {\n        encoder_ctx->bit_rate = params.bitrate_bps / params.framerate * 60.0;\n        encoder_ctx->framerate = AVRational { 60, 1 };\n        encoder_ctx->rc_buffer_size = encoder_ctx->bit_rate / 60.0 * 1.1;\n        encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;\n        encoder_ctx->rc_initial_buffer_occupancy = encoder_ctx->rc_buffer_size / 4 * 3;\n    }\n}\n\nstd::unique_ptr<alvr::EncodePipeline> alvr::EncodePipeline::Create(\n    Renderer* render,\n    VkContext& vk_ctx,\n    VkFrame& input_frame,\n    VkImageCreateInfo& image_create_info,\n    uint32_t width,\n    uint32_t height\n) {\n    if (Settings::Instance().m_force_sw_encoding == false) {\n        if (vk_ctx.nvidia) {\n            try {\n                auto nvenc = std::make_unique<alvr::EncodePipelineNvEnc>(\n                    render, vk_ctx, input_frame, image_create_info, width, height\n                );\n                Info(\"Using NvEnc encoder\");\n                return nvenc;\n            } catch (std::exception& e) {\n                Error(\n                    \"Failed to create NvEnc encoder: %s\\nPlease make sure you have installed CUDA \"\n                    \"runtime.\",\n                    e.what()\n                );\n            }\n        } else {\n            try {\n                auto vaapi = std::make_unique<alvr::EncodePipelineVAAPI>(\n                    render, vk_ctx, input_frame, width, height\n                );\n                Info(\"Using VAAPI encoder\");\n                return vaapi;\n            } catch (std::exception& e) {\n                Error(\n                    \"Failed to create VAAPI encoder: %s\\nPlease make sure you have installed VAAPI \"\n                    \"runtime.\",\n                    e.what()\n                );\n            }\n        }\n    }\n    auto sw = std::make_unique<alvr::EncodePipelineSW>(render, width, height);\n    Info(\"Using SW encoder\");\n    return sw;\n}\n\nalvr::EncodePipeline::~EncodePipeline() { avcodec_free_context(&encoder_ctx); }\n\nbool alvr::EncodePipeline::GetEncoded(FramePacket& packet) {\n    av_packet_free(&encoder_packet);\n    encoder_packet = av_packet_alloc();\n    int err = avcodec_receive_packet(encoder_ctx, encoder_packet);\n    if (err != 0) {\n        av_packet_free(&encoder_packet);\n        if (err == AVERROR(EAGAIN)) {\n            return false;\n        }\n        throw alvr::AvException(\"failed to encode\", err);\n    }\n    packet.data = encoder_packet->data;\n    packet.size = encoder_packet->size;\n    packet.pts = encoder_packet->pts;\n    packet.isIDR = (encoder_packet->flags & AV_PKT_FLAG_KEY) != 0;\n    return true;\n}\n\nint alvr::EncodePipeline::GetCodec() { return Settings::Instance().m_codec; }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipeline.h",
    "content": "#pragma once\n#include \"alvr_server/bindings.h\"\n#include <cstdint>\n#include <memory>\n#include <vector>\n#include <vulkan/vulkan_core.h>\n\nextern \"C\" struct AVCodecContext;\nextern \"C\" struct AVPacket;\n\nclass Renderer;\n\nnamespace alvr {\n\nclass VkFrame;\nclass VkFrameCtx;\nclass VkContext;\n\nstruct FramePacket {\n    uint8_t* data;\n    int size;\n    uint64_t pts;\n    bool isIDR;\n};\n\nclass EncodePipeline {\npublic:\n    struct Timestamp {\n        uint64_t gpu = 0;\n        uint64_t cpu = 0;\n    };\n\n    virtual ~EncodePipeline();\n\n    virtual void PushFrame(uint64_t targetTimestampNs, bool idr) = 0;\n    virtual bool GetEncoded(FramePacket& data);\n    virtual Timestamp GetTimestamp() { return timestamp; }\n    virtual int GetCodec();\n\n    virtual void SetParams(FfiDynamicEncoderParams params);\n    static std::unique_ptr<EncodePipeline> Create(\n        Renderer* render,\n        VkContext& vk_ctx,\n        VkFrame& input_frame,\n        VkImageCreateInfo& image_create_info,\n        uint32_t width,\n        uint32_t height\n    );\n\nprotected:\n    AVCodecContext* encoder_ctx = nullptr; // shall be initialized by child class\n    AVPacket* encoder_packet = NULL;\n    Timestamp timestamp = {};\n};\n\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.cpp",
    "content": "#include \"EncodePipelineNvEnc.h\"\n#include \"ALVR-common/packet_types.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"ffmpeg_helper.h\"\n#include <chrono>\n#include <memory>\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavutil/opt.h>\n}\n\nnamespace {\n\nconst char* encoder(ALVR_CODEC codec) {\n    switch (codec) {\n    case ALVR_CODEC_H264:\n        return \"h264_nvenc\";\n    case ALVR_CODEC_HEVC:\n        return \"hevc_nvenc\";\n    case ALVR_CODEC_AV1:\n        return \"av1_nvenc\";\n    }\n    throw std::runtime_error(\"invalid codec \" + std::to_string(codec));\n}\n\nvoid set_hwframe_ctx(AVCodecContext* ctx, AVBufferRef* hw_device_ctx) {\n    AVBufferRef* hw_frames_ref;\n    AVHWFramesContext* frames_ctx = NULL;\n    int err = 0;\n\n    if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {\n        throw std::runtime_error(\"Failed to create CUDA frame context.\");\n    }\n    frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data);\n    frames_ctx->format = AV_PIX_FMT_CUDA;\n    /**\n     * We will recieve a frame from HW as AV_PIX_FMT_VULKAN which will converted to AV_PIX_FMT_BGRA\n     * as SW format when we get it from HW.\n     * But NVEnc support only BGR0 format and we easy can just to force it\n     * Because:\n     * AV_PIX_FMT_BGRA - 28  ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...\n     * AV_PIX_FMT_BGR0 - 123 ///< packed BGR 8:8:8,    32bpp, BGRXBGRX...   X=unused/undefined\n     *\n     * We just to ignore the alpha channel and it's done\n     */\n    frames_ctx->sw_format = AV_PIX_FMT_BGR0;\n    frames_ctx->width = ctx->width;\n    frames_ctx->height = ctx->height;\n    if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {\n        av_buffer_unref(&hw_frames_ref);\n        throw alvr::AvException(\"Failed to initialize CUDA frame context:\", err);\n    }\n    ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref);\n    if (!ctx->hw_frames_ctx)\n        err = AVERROR(ENOMEM);\n\n    av_buffer_unref(&hw_frames_ref);\n}\n\n} // namespace\nalvr::EncodePipelineNvEnc::EncodePipelineNvEnc(\n    Renderer* render,\n    VkContext& vk_ctx,\n    VkFrame& input_frame,\n    VkImageCreateInfo& image_create_info,\n    uint32_t width,\n    uint32_t height\n) {\n    r = render;\n    vk_frame_ctx = std::make_unique<alvr::VkFrameCtx>(vk_ctx, image_create_info);\n\n    auto input_frame_ctx = (AVHWFramesContext*)vk_frame_ctx->ctx->data;\n    assert(input_frame_ctx->sw_format == AV_PIX_FMT_BGRA);\n\n    int err;\n    vk_frame = input_frame.make_av_frame(*vk_frame_ctx);\n\n    err = av_hwdevice_ctx_create_derived(&hw_ctx, AV_HWDEVICE_TYPE_CUDA, vk_ctx.ctx, 0);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to create a CUDA device:\", err);\n    }\n\n    const auto& settings = Settings::Instance();\n\n    auto codec_id = ALVR_CODEC(settings.m_codec);\n    const char* encoder_name = encoder(codec_id);\n    const AVCodec* codec = avcodec_find_encoder_by_name(encoder_name);\n    if (codec == nullptr) {\n        throw std::runtime_error(std::string(\"Failed to find encoder \") + encoder_name);\n    }\n\n    encoder_ctx = avcodec_alloc_context3(codec);\n    if (not encoder_ctx) {\n        throw std::runtime_error(\"failed to allocate NvEnc encoder\");\n    }\n\n    switch (codec_id) {\n    case ALVR_CODEC_H264:\n        switch (settings.m_entropyCoding) {\n        case ALVR_CABAC:\n            av_opt_set(encoder_ctx->priv_data, \"coder\", \"ac\", 0);\n            break;\n        case ALVR_CAVLC:\n            av_opt_set(encoder_ctx->priv_data, \"coder\", \"vlc\", 0);\n            break;\n        }\n        break;\n    case ALVR_CODEC_HEVC:\n        break;\n    case ALVR_CODEC_AV1:\n        break;\n    }\n\n    switch (settings.m_rateControlMode) {\n    case ALVR_CBR:\n        av_opt_set(encoder_ctx->priv_data, \"rc\", \"cbr\", 0);\n        break;\n    case ALVR_VBR:\n        av_opt_set(encoder_ctx->priv_data, \"rc\", \"vbr\", 0);\n        break;\n    }\n\n    if (codec_id == ALVR_CODEC_H264) {\n        switch (settings.m_h264Profile) {\n        case ALVR_H264_PROFILE_BASELINE:\n            av_opt_set(encoder_ctx->priv_data, \"profile\", \"baseline\", 0);\n            break;\n        case ALVR_H264_PROFILE_MAIN:\n            av_opt_set(encoder_ctx->priv_data, \"profile\", \"main\", 0);\n            break;\n        default:\n        case ALVR_H264_PROFILE_HIGH:\n            av_opt_set(encoder_ctx->priv_data, \"profile\", \"high\", 0);\n            break;\n        }\n    }\n\n    char preset[] = \"p0\";\n    // replace 0 with preset number\n    preset[1] += settings.m_nvencQualityPreset;\n    av_opt_set(encoder_ctx->priv_data, \"preset\", preset, 0);\n\n    if (settings.m_nvencAdaptiveQuantizationMode == 1) {\n        av_opt_set_int(encoder_ctx->priv_data, \"spatial_aq\", 1, 0);\n    } else if (settings.m_nvencAdaptiveQuantizationMode == 2) {\n        av_opt_set_int(encoder_ctx->priv_data, \"temporal_aq\", 1, 0);\n    }\n\n    if (settings.m_nvencEnableWeightedPrediction) {\n        av_opt_set_int(encoder_ctx->priv_data, \"weighted_pred\", 1, 0);\n    }\n\n    av_opt_set_int(encoder_ctx->priv_data, \"tune\", settings.m_nvencTuningPreset, 0);\n    av_opt_set_int(encoder_ctx->priv_data, \"zerolatency\", 1, 0);\n    // Delay isn't actually a delay instead its how many surfaces to encode at a time\n    av_opt_set_int(encoder_ctx->priv_data, \"delay\", 1, 0);\n    av_opt_set_int(encoder_ctx->priv_data, \"forced-idr\", 1, 0);\n    // work around ffmpeg default not working for older NVIDIA cards\n    av_opt_set_int(encoder_ctx->priv_data, \"b_ref_mode\", 0, 0);\n\n    encoder_ctx->pix_fmt = AV_PIX_FMT_CUDA;\n    encoder_ctx->width = width;\n    encoder_ctx->height = height;\n    encoder_ctx->time_base = { 1, (int)1e9 };\n    encoder_ctx->framerate = AVRational { settings.m_refreshRate, 1 };\n    encoder_ctx->sample_aspect_ratio = AVRational { 1, 1 };\n    encoder_ctx->max_b_frames = 0;\n    encoder_ctx->gop_size = INT16_MAX;\n    encoder_ctx->color_range = AVCOL_RANGE_JPEG;\n    auto params = FfiDynamicEncoderParams {};\n    params.updated = true;\n    params.bitrate_bps = 30'000'000;\n    params.framerate = 60.0;\n    SetParams(params);\n\n    set_hwframe_ctx(encoder_ctx, hw_ctx);\n\n    err = avcodec_open2(encoder_ctx, codec, NULL);\n    if (err < 0) {\n        throw alvr::AvException(\"Cannot open video encoder codec:\", err);\n    }\n\n    hw_frame = av_frame_alloc();\n}\n\nalvr::EncodePipelineNvEnc::~EncodePipelineNvEnc() {\n    av_buffer_unref(&hw_ctx);\n    av_frame_free(&hw_frame);\n}\n\nvoid alvr::EncodePipelineNvEnc::PushFrame(uint64_t targetTimestampNs, bool idr) {\n    AVVkFrame* vkf = reinterpret_cast<AVVkFrame*>(vk_frame->data[0]);\n    vkf->sem_value[0]++;\n\n    VkTimelineSemaphoreSubmitInfo timelineInfo = {};\n    timelineInfo.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;\n    timelineInfo.signalSemaphoreValueCount = 1;\n    timelineInfo.pSignalSemaphoreValues = &vkf->sem_value[0];\n\n    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.pNext = &timelineInfo;\n    submitInfo.waitSemaphoreCount = 1;\n    submitInfo.pWaitSemaphores = &r->GetOutput().semaphore;\n    submitInfo.pWaitDstStageMask = &waitStage;\n    submitInfo.signalSemaphoreCount = 1;\n    submitInfo.pSignalSemaphores = &vkf->sem[0];\n    VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, nullptr));\n\n    int err = av_hwframe_get_buffer(encoder_ctx->hw_frames_ctx, hw_frame, 0);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to allocate CUDA frame\", err);\n    }\n    err = av_hwframe_transfer_data(hw_frame, vk_frame.get(), 0);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to transfer Vulkan image to CUDA frame\", err);\n    }\n\n    hw_frame->pict_type = idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;\n    hw_frame->pts = targetTimestampNs;\n\n    if ((err = avcodec_send_frame(encoder_ctx, hw_frame)) < 0) {\n        throw alvr::AvException(\"avcodec_send_frame failed:\", err);\n    }\n\n    av_frame_unref(hw_frame);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineNvEnc.h",
    "content": "#pragma once\n\n#include \"EncodePipeline.h\"\n#include <functional>\n#include <memory>\n\nextern \"C\" struct AVBufferRef;\nextern \"C\" struct AVCodecContext;\nextern \"C\" struct AVFrame;\n\nclass Renderer;\n\nnamespace alvr {\n\nclass EncodePipelineNvEnc : public EncodePipeline {\npublic:\n    ~EncodePipelineNvEnc();\n    EncodePipelineNvEnc(\n        Renderer* render,\n        VkContext& vk_ctx,\n        VkFrame& input_frame,\n        VkImageCreateInfo& image_create_info,\n        uint32_t width,\n        uint32_t height\n    );\n\n    void PushFrame(uint64_t targetTimestampNs, bool idr) override;\n\nprivate:\n    Renderer* r = nullptr;\n    std::unique_ptr<alvr::VkFrameCtx> vk_frame_ctx;\n    AVBufferRef* hw_ctx = nullptr;\n    std::unique_ptr<AVFrame, std::function<void(AVFrame*)>> vk_frame;\n    AVFrame* hw_frame = nullptr;\n};\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.cpp",
    "content": "#include \"EncodePipelineSW.h\"\n\n#include <chrono>\n\n#include \"FormatConverter.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n\nnamespace {\n\nvoid x264_log(void*, int level, const char* fmt, va_list args) {\n    char buf[256];\n    vsnprintf(buf, sizeof(buf), fmt, args);\n    switch (level) {\n    case X264_LOG_ERROR:\n        Error(\"x264: %s\", buf);\n        break;\n    case X264_LOG_WARNING:\n        Warn(\"x264: %s\", buf);\n        break;\n    case X264_LOG_INFO:\n        Info(\"x264: %s\", buf);\n        break;\n    case X264_LOG_DEBUG:\n        Debug(\"x264: %s\", buf);\n        break;\n    default:\n        break;\n    }\n}\n\n}\n\nalvr::EncodePipelineSW::EncodePipelineSW(Renderer* render, uint32_t width, uint32_t height) {\n    const auto& settings = Settings::Instance();\n\n    x264_param_default_preset(&param, \"ultrafast\", \"zerolatency\");\n\n    param.pf_log = x264_log;\n    param.i_log_level = X264_LOG_INFO;\n\n    param.b_aud = 0;\n    param.b_cabac = settings.m_entropyCoding == ALVR_CABAC;\n    param.b_sliced_threads = true;\n    param.i_threads = settings.m_swThreadCount;\n    param.i_width = width;\n    param.i_height = height;\n    param.rc.i_rc_method = X264_RC_ABR;\n\n    switch (settings.m_h264Profile) {\n    case ALVR_H264_PROFILE_BASELINE:\n        x264_param_apply_profile(&param, \"baseline\");\n        break;\n    case ALVR_H264_PROFILE_MAIN:\n        x264_param_apply_profile(&param, \"main\");\n        break;\n    default:\n    case ALVR_H264_PROFILE_HIGH:\n        x264_param_apply_profile(&param, \"high\");\n        break;\n    }\n\n    auto params = FfiDynamicEncoderParams {};\n    params.updated = true;\n    params.bitrate_bps = 30'000'000;\n    params.framerate = Settings::Instance().m_refreshRate;\n    SetParams(params);\n\n    enc = x264_encoder_open(&param);\n    if (!enc) {\n        throw std::runtime_error(\"Failed to open encoder\");\n    }\n\n    x264_picture_init(&picture);\n    picture.img.i_csp = X264_CSP_I420;\n    picture.img.i_plane = 3;\n\n    x264_picture_init(&picture_out);\n\n    rgbtoyuv = new RgbToYuv420(\n        render,\n        render->GetOutput().image,\n        render->GetOutput().imageInfo,\n        render->GetOutput().semaphore\n    );\n}\n\nalvr::EncodePipelineSW::~EncodePipelineSW() {\n    if (rgbtoyuv) {\n        delete rgbtoyuv;\n    }\n    if (enc) {\n        x264_encoder_close(enc);\n    }\n}\n\nvoid alvr::EncodePipelineSW::PushFrame(uint64_t targetTimestampNs, bool idr) {\n    rgbtoyuv->Convert(picture.img.plane, picture.img.i_stride);\n    rgbtoyuv->Sync();\n    timestamp.cpu = std::chrono::duration_cast<std::chrono::nanoseconds>(\n                        std::chrono::steady_clock::now().time_since_epoch()\n    )\n                        .count();\n\n    picture.i_type = idr ? X264_TYPE_IDR : X264_TYPE_AUTO;\n    pts = picture.i_pts = targetTimestampNs;\n    is_idr = idr;\n\n    int nnal = 0;\n    nal_size = x264_encoder_encode(enc, &nal, &nnal, &picture, &picture_out);\n    if (nal_size < 0) {\n        throw std::runtime_error(\"x264 encoder_encode failed\");\n    }\n}\n\nbool alvr::EncodePipelineSW::GetEncoded(FramePacket& packet) {\n    if (!nal) {\n        return false;\n    }\n    packet.size = nal_size;\n    packet.data = nal[0].p_payload;\n    packet.pts = pts;\n    packet.isIDR = is_idr;\n    return packet.size > 0;\n}\n\nvoid alvr::EncodePipelineSW::SetParams(FfiDynamicEncoderParams params) {\n    if (!params.updated) {\n        return;\n    }\n    // x264 doesn't work well with adaptive bitrate/fps\n    param.i_fps_num = Settings::Instance().m_refreshRate;\n    param.i_fps_den = 1;\n    param.rc.i_bitrate\n        = params.bitrate_bps / 1'000 * 1.4; // needs higher value to hit target bitrate\n    param.rc.i_vbv_buffer_size = param.rc.i_bitrate / param.i_fps_num * 1.1;\n    param.rc.i_vbv_max_bitrate = param.rc.i_bitrate;\n    param.rc.f_vbv_buffer_init = 0.75;\n    if (enc) {\n        x264_encoder_reconfig(enc, &param);\n    }\n}\n\nint alvr::EncodePipelineSW::GetCodec() { return ALVR_CODEC_H264; }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineSW.h",
    "content": "#pragma once\n\n#include \"EncodePipeline.h\"\n\n#include <x264.h>\n\nclass FormatConverter;\n\nnamespace alvr {\n\nclass EncodePipelineSW : public EncodePipeline {\npublic:\n    ~EncodePipelineSW();\n    EncodePipelineSW(Renderer* render, uint32_t width, uint32_t height);\n\n    void PushFrame(uint64_t targetTimestampNs, bool idr) override;\n    bool GetEncoded(FramePacket& packet) override;\n    void SetParams(FfiDynamicEncoderParams params) override;\n    int GetCodec() override;\n\nprivate:\n    x264_t* enc = nullptr;\n    x264_param_t param;\n    x264_picture_t picture;\n    x264_picture_t picture_out;\n    x264_nal_t* nal = nullptr;\n    int nal_size = 0;\n    int64_t pts = 0;\n    bool is_idr = false;\n    FormatConverter* rgbtoyuv = nullptr;\n};\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.cpp",
    "content": "#include \"EncodePipelineVAAPI.h\"\n#include \"ALVR-common/packet_types.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"ffmpeg_helper.h\"\n#include <chrono>\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavfilter/avfilter.h>\n#include <libavfilter/buffersink.h>\n#include <libavfilter/buffersrc.h>\n#include <libavutil/hwcontext.h>\n#include <libavutil/opt.h>\n}\n\nnamespace {\n\nconst char* encoder(ALVR_CODEC codec) {\n    switch (codec) {\n    case ALVR_CODEC_H264:\n        return \"h264_vaapi\";\n    case ALVR_CODEC_HEVC:\n        return \"hevc_vaapi\";\n    case ALVR_CODEC_AV1:\n        return \"av1_vaapi\";\n    }\n    throw std::runtime_error(\"invalid codec \" + std::to_string(codec));\n}\n\nvoid set_hwframe_ctx(AVCodecContext* ctx, AVBufferRef* hw_device_ctx) {\n    AVBufferRef* hw_frames_ref;\n    AVHWFramesContext* frames_ctx = NULL;\n    int err = 0;\n\n    if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {\n        throw std::runtime_error(\"Failed to create VAAPI frame context.\");\n    }\n    frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data);\n    frames_ctx->format = AV_PIX_FMT_VAAPI;\n    frames_ctx->sw_format = (Settings::Instance().m_codec == ALVR_CODEC_HEVC\n                             || Settings::Instance().m_codec == ALVR_CODEC_AV1)\n            && Settings::Instance().m_use10bitEncoder\n        ? AV_PIX_FMT_P010\n        : AV_PIX_FMT_NV12;\n    frames_ctx->width = ctx->width;\n    frames_ctx->height = ctx->height;\n    frames_ctx->initial_pool_size = 3;\n    if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {\n        av_buffer_unref(&hw_frames_ref);\n        throw alvr::AvException(\"Failed to initialize VAAPI frame context:\", err);\n    }\n    ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref);\n    if (!ctx->hw_frames_ctx)\n        err = AVERROR(ENOMEM);\n\n    av_buffer_unref(&hw_frames_ref);\n}\n\n// Map the vulkan frames to corresponding vaapi frames\nAVFrame*\nmap_frame(AVBufferRef* hw_frames_ref, AVBufferRef* drm_device_ctx, alvr::VkFrame& input_frame) {\n    auto frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data);\n\n    AVFrame* mapped_frame = av_frame_alloc();\n    mapped_frame->format = AV_PIX_FMT_VAAPI;\n    mapped_frame->hw_frames_ctx = av_buffer_ref(hw_frames_ref);\n\n    AVBufferRef* drm_frames_ref = NULL;\n    if (!(drm_frames_ref = av_hwframe_ctx_alloc(drm_device_ctx))) {\n        throw std::runtime_error(\"Failed to create vulkan frame context.\");\n    }\n    AVHWFramesContext* drm_frames_ctx = (AVHWFramesContext*)(drm_frames_ref->data);\n    drm_frames_ctx->format = AV_PIX_FMT_DRM_PRIME;\n    drm_frames_ctx->sw_format = frames_ctx->sw_format;\n    drm_frames_ctx->width = frames_ctx->width;\n    drm_frames_ctx->height = frames_ctx->height;\n    drm_frames_ctx->initial_pool_size = 0;\n    int err;\n    if ((err = av_hwframe_ctx_init(drm_frames_ref)) < 0) {\n        av_buffer_unref(&drm_frames_ref);\n        throw alvr::AvException(\"Failed to initialize DRM frame context:\", err);\n    }\n\n    AVFrame* vk_frame = av_frame_alloc();\n    vk_frame->width = frames_ctx->width;\n    vk_frame->height = frames_ctx->height;\n    vk_frame->hw_frames_ctx = drm_frames_ref;\n    vk_frame->data[0] = (uint8_t*)(AVDRMFrameDescriptor*)input_frame;\n    vk_frame->format = AV_PIX_FMT_DRM_PRIME;\n    vk_frame->buf[0] = av_buffer_alloc(1);\n    av_hwframe_map(mapped_frame, vk_frame, AV_HWFRAME_MAP_READ);\n    av_frame_free(&vk_frame);\n\n    av_buffer_unref(&hw_frames_ref);\n\n    return mapped_frame;\n}\n\n// Import VA surface\nAVFrame* import_frame(AVBufferRef* hw_frames_ref, DrmImage& drm) {\n    AVFrame* va_frame = av_frame_alloc();\n    int err = av_hwframe_get_buffer(hw_frames_ref, va_frame, 0);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to get hwframe buffer:\", err);\n    }\n\n    AVFrame* mapped_frame = av_frame_alloc();\n    mapped_frame->format = AV_PIX_FMT_DRM_PRIME;\n    err = av_hwframe_map(mapped_frame, va_frame, AV_HWFRAME_MAP_WRITE);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to export va frame:\", err);\n    }\n\n    auto desc = reinterpret_cast<AVDRMFrameDescriptor*>(mapped_frame->data[0]);\n    drm.fd = desc->objects[0].fd;\n    drm.format = desc->layers[0].format;\n    drm.modifier = desc->objects[0].format_modifier;\n    drm.planes = desc->layers[0].nb_planes;\n    for (uint32_t i = 0; i < drm.planes; ++i) {\n        drm.strides[0] = desc->layers[0].planes[i].pitch;\n        drm.offsets[0] = desc->layers[0].planes[i].offset;\n    }\n\n    return va_frame;\n}\n\n}\n\nalvr::EncodePipelineVAAPI::EncodePipelineVAAPI(\n    Renderer* render, VkContext& vk_ctx, VkFrame& input_frame, uint32_t width, uint32_t height\n)\n    : r(render) {\n    /* VAAPI Encoding pipeline\n     * The encoding pipeline has 3 frame types:\n     * - input vulkan frames, only used to initialize the mapped frames\n     * - mapped frames, one per input frame, same format, and point to the same memory on the device\n     * - encoder frame, with a format compatible with the encoder, created by the filter\n     * Each frame type has a corresponding hardware frame context, the vulkan one is provided\n     *\n     * The pipeline is simply made of a scale_vaapi object, that does the conversion between formats\n     * and the encoder that takes the converted frame and produces packets.\n     */\n    int err = av_hwdevice_ctx_create(\n        &hw_ctx, AV_HWDEVICE_TYPE_VAAPI, vk_ctx.devicePath.c_str(), NULL, 0\n    );\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to create a VAAPI device:\", err);\n    }\n\n    drm_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);\n    AVHWDeviceContext* hwctx = (AVHWDeviceContext*)drm_ctx->data;\n    AVDRMDeviceContext* drmctx = (AVDRMDeviceContext*)hwctx->hwctx;\n    drmctx->fd = -1;\n    err = av_hwdevice_ctx_init(drm_ctx);\n    if (err < 0) {\n        throw alvr::AvException(\"Failed to create DRM device:\", err);\n    }\n\n    const auto& settings = Settings::Instance();\n\n    auto codec_id = ALVR_CODEC(settings.m_codec);\n    const char* encoder_name = encoder(codec_id);\n    const AVCodec* codec = avcodec_find_encoder_by_name(encoder_name);\n    if (codec == nullptr) {\n        throw std::runtime_error(std::string(\"Failed to find encoder \") + encoder_name);\n    }\n\n    encoder_ctx = avcodec_alloc_context3(codec);\n    if (not encoder_ctx) {\n        throw std::runtime_error(\"failed to allocate VAAPI encoder\");\n    }\n\n    encoder_ctx->gop_size = INT_MAX;\n\n    switch (codec_id) {\n    case ALVR_CODEC_H264:\n        switch (settings.m_h264Profile) {\n        case ALVR_H264_PROFILE_BASELINE:\n            encoder_ctx->profile = FF_PROFILE_H264_BASELINE;\n            break;\n        case ALVR_H264_PROFILE_MAIN:\n            encoder_ctx->profile = FF_PROFILE_H264_MAIN;\n            break;\n        default:\n        case ALVR_H264_PROFILE_HIGH:\n            encoder_ctx->profile = FF_PROFILE_H264_HIGH;\n            break;\n        }\n\n        switch (settings.m_entropyCoding) {\n        case ALVR_CABAC:\n            av_opt_set(encoder_ctx->priv_data, \"coder\", \"ac\", 0);\n            break;\n        case ALVR_CAVLC:\n            av_opt_set(encoder_ctx->priv_data, \"coder\", \"vlc\", 0);\n            break;\n        }\n\n        break;\n    case ALVR_CODEC_HEVC:\n        encoder_ctx->profile = Settings::Instance().m_use10bitEncoder ? FF_PROFILE_HEVC_MAIN_10\n                                                                      : FF_PROFILE_HEVC_MAIN;\n        encoder_ctx->gop_size = INT16_MAX;\n        break;\n    case ALVR_CODEC_AV1:\n        encoder_ctx->profile = FF_PROFILE_AV1_MAIN;\n        break;\n    }\n\n    switch (settings.m_rateControlMode) {\n    case ALVR_VBR:\n        av_opt_set(encoder_ctx->priv_data, \"rc_mode\", \"VBR\", 0);\n        break;\n    case ALVR_CBR:\n    default:\n        av_opt_set(encoder_ctx->priv_data, \"rc_mode\", \"CBR\", 0);\n        break;\n    }\n\n    av_opt_set_int(encoder_ctx->priv_data, \"filler_data\", settings.m_fillerData, 0);\n\n    encoder_ctx->width = width;\n    encoder_ctx->height = height;\n    encoder_ctx->time_base = { 1, (int)1e9 };\n    encoder_ctx->sample_aspect_ratio = AVRational { 1, 1 };\n    encoder_ctx->pix_fmt = AV_PIX_FMT_VAAPI;\n    encoder_ctx->max_b_frames = 0;\n    encoder_ctx->color_range = AVCOL_RANGE_JPEG;\n\n    auto params = FfiDynamicEncoderParams {};\n    params.updated = true;\n    params.bitrate_bps = 30'000'000;\n    params.framerate = settings.m_refreshRate;\n    SetParams(params);\n\n    vlVaQualityBits quality = {};\n    quality.vbaq_mode\n        = Settings::Instance()\n              .m_enableVbaq; // No noticable performance difference and should improve subjective\n                             // quality by allocating more bits to smooth areas\n    switch (settings.m_encoderQualityPreset) {\n    case ALVR_QUALITY:\n        if (vk_ctx.amd) {\n            quality.preset_mode = PRESET_MODE_QUALITY;\n            encoder_ctx->compression_level\n                = quality.quality; // (QUALITY preset, no pre-encoding, vbaq)\n        } else if (vk_ctx.intel) {\n            encoder_ctx->compression_level = 1;\n        }\n        break;\n    case ALVR_BALANCED:\n        if (vk_ctx.amd) {\n            quality.preset_mode = PRESET_MODE_BALANCE;\n            encoder_ctx->compression_level\n                = quality.quality; // (BALANCE preset, no pre-encoding, vbaq)\n        } else if (vk_ctx.intel) {\n            encoder_ctx->compression_level = 4;\n        }\n        break;\n    case ALVR_SPEED:\n    default:\n        if (vk_ctx.amd) {\n            quality.preset_mode = PRESET_MODE_SPEED;\n            encoder_ctx->compression_level\n                = quality.quality; // (speed preset, no pre-encoding, vbaq)\n        } else if (vk_ctx.intel) {\n            encoder_ctx->compression_level = 7;\n        }\n        break;\n    }\n\n    av_opt_set_int(encoder_ctx->priv_data, \"async_depth\", 1, 0);\n\n    set_hwframe_ctx(encoder_ctx, hw_ctx);\n\n    err = avcodec_open2(encoder_ctx, codec, NULL);\n    if (err < 0) {\n        throw alvr::AvException(\"Cannot open video encoder codec:\", err);\n    }\n\n    AVBufferRef* hw_frames_ref;\n    if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_ctx))) {\n        throw std::runtime_error(\"Failed to create VAAPI frame context.\");\n    }\n    auto frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data);\n    frames_ctx->format = AV_PIX_FMT_VAAPI;\n    frames_ctx->sw_format = input_frame.avFormat();\n    frames_ctx->width = input_frame.imageInfo().extent.width;\n    frames_ctx->height = input_frame.imageInfo().extent.height;\n    frames_ctx->initial_pool_size = 1;\n    if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {\n        av_buffer_unref(&hw_frames_ref);\n        throw alvr::AvException(\"Failed to initialize VAAPI frame context:\", err);\n    }\n\n    encoder_frame = av_frame_alloc();\n    if (vk_ctx.intel || getenv(\"ALVR_VAAPI_IMPORT_SURFACE\")) {\n        Info(\"Importing VA surface\");\n        DrmImage drm;\n        mapped_frame = import_frame(hw_frames_ref, drm);\n        r->ImportOutput(drm);\n    } else {\n        mapped_frame = map_frame(hw_frames_ref, drm_ctx, input_frame);\n    }\n\n    filter_graph = avfilter_graph_alloc();\n\n    AVFilterInOut* outputs = avfilter_inout_alloc();\n    AVFilterInOut* inputs = avfilter_inout_alloc();\n\n    std::stringstream buffer_filter_args;\n    buffer_filter_args << \"video_size=\" << mapped_frame->width << \"x\" << mapped_frame->height;\n    buffer_filter_args << \":pix_fmt=\" << mapped_frame->format;\n    buffer_filter_args << \":time_base=\" << encoder_ctx->time_base.num << \"/\"\n                       << encoder_ctx->time_base.den;\n    if ((err = avfilter_graph_create_filter(\n             &filter_in,\n             avfilter_get_by_name(\"buffer\"),\n             \"in\",\n             buffer_filter_args.str().c_str(),\n             NULL,\n             filter_graph\n         ))) {\n        throw alvr::AvException(\"filter_in creation failed:\", err);\n    }\n    AVBufferSrcParameters* par = av_buffersrc_parameters_alloc();\n    memset(par, 0, sizeof(*par));\n    par->format = AV_PIX_FMT_NONE;\n    par->hw_frames_ctx = av_buffer_ref(mapped_frame->hw_frames_ctx);\n    av_buffersrc_parameters_set(filter_in, par);\n    av_free(par);\n\n    if ((err = avfilter_graph_create_filter(\n             &filter_out, avfilter_get_by_name(\"buffersink\"), \"out\", NULL, NULL, filter_graph\n         ))) {\n        throw alvr::AvException(\"filter_out creation failed:\", err);\n    }\n\n    outputs->name = av_strdup(\"in\");\n    outputs->filter_ctx = filter_in;\n    outputs->pad_idx = 0;\n    outputs->next = NULL;\n\n    inputs->name = av_strdup(\"out\");\n    inputs->filter_ctx = filter_out;\n    inputs->pad_idx = 0;\n    inputs->next = NULL;\n\n    std::string filters = \"scale_vaapi=out_range=full:format=\";\n    if ((Settings::Instance().m_codec == ALVR_CODEC_HEVC\n         || Settings::Instance().m_codec == ALVR_CODEC_AV1)\n        && Settings::Instance().m_use10bitEncoder) {\n        filters += \"p010\";\n    } else {\n        filters += \"nv12\";\n    }\n    if ((err = avfilter_graph_parse_ptr(filter_graph, filters.c_str(), &inputs, &outputs, NULL))\n        < 0) {\n        throw alvr::AvException(\"avfilter_graph_parse_ptr failed:\", err);\n    }\n\n    avfilter_inout_free(&outputs);\n    avfilter_inout_free(&inputs);\n\n    for (unsigned i = 0; i < filter_graph->nb_filters; ++i) {\n        filter_graph->filters[i]->hw_device_ctx = av_buffer_ref(hw_ctx);\n    }\n\n    if ((err = avfilter_graph_config(filter_graph, NULL))) {\n        throw alvr::AvException(\"avfilter_graph_config failed:\", err);\n    }\n}\n\nalvr::EncodePipelineVAAPI::~EncodePipelineVAAPI() {\n    // Commented because freeing it here causes a gpu reset, it should be cleaned up away\n    // avcodec_free_context(&encoder_ctx);\n    // avfilter_graph_free(&filter_graph);\n    // av_frame_free(&mapped_frame);\n    // av_frame_free(&encoder_frame);\n    // av_buffer_unref(&hw_ctx);\n    // av_buffer_unref(&drm_ctx);\n}\n\nvoid alvr::EncodePipelineVAAPI::PushFrame(uint64_t targetTimestampNs, bool idr) {\n    r->Sync();\n    timestamp.cpu = std::chrono::duration_cast<std::chrono::nanoseconds>(\n                        std::chrono::steady_clock::now().time_since_epoch()\n    )\n                        .count();\n    int err = av_buffersrc_add_frame_flags(\n        filter_in, mapped_frame, AV_BUFFERSRC_FLAG_PUSH | AV_BUFFERSRC_FLAG_KEEP_REF\n    );\n    if (err != 0) {\n        throw alvr::AvException(\"av_buffersrc_add_frame failed\", err);\n    }\n    err = av_buffersink_get_frame(filter_out, encoder_frame);\n    if (err != 0) {\n        throw alvr::AvException(\"av_buffersink_get_frame failed\", err);\n    }\n\n    encoder_frame->pict_type = idr ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;\n    encoder_frame->pts = targetTimestampNs;\n\n    if ((err = avcodec_send_frame(encoder_ctx, encoder_frame)) < 0) {\n        throw alvr::AvException(\"avcodec_send_frame failed: \", err);\n    }\n    av_frame_unref(encoder_frame);\n}\n\nvoid alvr::EncodePipelineVAAPI::SetParams(FfiDynamicEncoderParams params) {\n    if (!params.updated) {\n        return;\n    }\n    encoder_ctx->bit_rate = params.bitrate_bps;\n    encoder_ctx->framerate = AVRational { int(params.framerate * 1000), 1000 };\n    encoder_ctx->rc_buffer_size = encoder_ctx->bit_rate / params.framerate;\n    encoder_ctx->rc_max_rate = encoder_ctx->bit_rate;\n    encoder_ctx->rc_initial_buffer_occupancy = encoder_ctx->rc_buffer_size;\n\n    if (Settings::Instance().m_amdBitrateCorruptionFix) {\n        RequestIDR();\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/EncodePipelineVAAPI.h",
    "content": "#pragma once\n\n#include \"EncodePipeline.h\"\n\nextern \"C\" struct AVBufferRef;\nextern \"C\" struct AVCodecContext;\nextern \"C\" struct AVFilterContext;\nextern \"C\" struct AVFilterGraph;\nextern \"C\" struct AVFrame;\n\nclass Renderer;\n\nnamespace alvr {\n\n#define PRESET_MODE_SPEED (0)\n#define PRESET_MODE_BALANCE (1)\n#define PRESET_MODE_QUALITY (2)\n\nenum EncoderQualityPreset { QUALITY = 0, BALANCED = 1, SPEED = 2 };\n\nclass EncodePipelineVAAPI : public EncodePipeline {\npublic:\n    ~EncodePipelineVAAPI();\n    EncodePipelineVAAPI(\n        Renderer* render, VkContext& vk_ctx, VkFrame& input_frame, uint32_t width, uint32_t height\n    );\n\n    void PushFrame(uint64_t targetTimestampNs, bool idr) override;\n    void SetParams(FfiDynamicEncoderParams params) override;\n\nprivate:\n    Renderer* r = nullptr;\n    AVBufferRef* hw_ctx = nullptr;\n    AVBufferRef* drm_ctx = nullptr;\n    AVFrame* mapped_frame = nullptr;\n    AVFrame* encoder_frame = nullptr;\n    AVFilterGraph* filter_graph = nullptr;\n    AVFilterContext* filter_in = nullptr;\n    AVFilterContext* filter_out = nullptr;\n\n    union vlVaQualityBits {\n        unsigned int quality;\n        struct {\n            unsigned int valid_setting : 1;\n            unsigned int preset_mode : 2;\n            unsigned int pre_encode_mode : 1;\n            unsigned int vbaq_mode : 1;\n            unsigned int reservered : 27;\n        };\n    };\n};\n;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/FormatConverter.cpp",
    "content": "#include \"FormatConverter.h\"\n#include \"alvr_server/bindings.h\"\n\nFormatConverter::FormatConverter(Renderer* render)\n    : r(render) { }\n\nFormatConverter::~FormatConverter() {\n    for (const OutputImage& image : m_images) {\n        vkUnmapMemory(r->m_dev, image.memory);\n        vkDestroyImageView(r->m_dev, image.view, nullptr);\n        vkDestroyImage(r->m_dev, image.image, nullptr);\n        vkFreeMemory(r->m_dev, image.memory, nullptr);\n    }\n\n    vkDestroySemaphore(r->m_dev, m_output.semaphore, nullptr);\n\n    vkDestroyQueryPool(r->m_dev, m_queryPool, nullptr);\n    vkDestroyDescriptorSetLayout(r->m_dev, m_descriptorLayout, nullptr);\n    vkDestroyImageView(r->m_dev, m_view, nullptr);\n    vkDestroyShaderModule(r->m_dev, m_shader, nullptr);\n    vkDestroyPipeline(r->m_dev, m_pipeline, nullptr);\n    vkDestroyPipelineLayout(r->m_dev, m_pipelineLayout, nullptr);\n}\n\nvoid FormatConverter::init(\n    VkImage image,\n    VkImageCreateInfo imageCreateInfo,\n    VkSemaphore semaphore,\n    int count,\n    const unsigned char* shaderData,\n    unsigned shaderLen\n) {\n    m_images.resize(count);\n    m_semaphore = semaphore;\n\n    // Timestamp query\n    VkQueryPoolCreateInfo queryPoolInfo = {};\n    queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;\n    queryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;\n    queryPoolInfo.queryCount = 1;\n    VK_CHECK(vkCreateQueryPool(r->m_dev, &queryPoolInfo, nullptr, &m_queryPool));\n\n    // Command buffer\n    VkCommandBufferAllocateInfo commandBufferInfo = {};\n    commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n    commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n    commandBufferInfo.commandPool = r->m_commandPool;\n    commandBufferInfo.commandBufferCount = 1;\n    VK_CHECK(vkAllocateCommandBuffers(r->m_dev, &commandBufferInfo, &m_commandBuffer));\n\n    // Descriptors\n    VkDescriptorSetLayoutBinding descriptorBindings[2];\n    descriptorBindings[0] = {};\n    descriptorBindings[0].binding = 0;\n    descriptorBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;\n    descriptorBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorBindings[0].descriptorCount = 1;\n    descriptorBindings[1] = {};\n    descriptorBindings[1].binding = 1;\n    descriptorBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;\n    descriptorBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorBindings[1].descriptorCount = count;\n\n    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {};\n    descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n    descriptorSetLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;\n    descriptorSetLayoutInfo.bindingCount = 2;\n    descriptorSetLayoutInfo.pBindings = descriptorBindings;\n    VK_CHECK(vkCreateDescriptorSetLayout(\n        r->m_dev, &descriptorSetLayoutInfo, nullptr, &m_descriptorLayout\n    ));\n\n    // Input image\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = imageCreateInfo.format;\n    viewInfo.image = image;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VK_CHECK(vkCreateImageView(r->m_dev, &viewInfo, nullptr, &m_view));\n\n    // Output images\n    for (int i = 0; i < count; ++i) {\n        VkImageCreateInfo imageInfo = {};\n        imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n        imageInfo.imageType = VK_IMAGE_TYPE_2D;\n        imageInfo.format = VK_FORMAT_R8_UNORM;\n        imageInfo.extent.width = imageCreateInfo.extent.width;\n        imageInfo.extent.height = imageCreateInfo.extent.height;\n        imageInfo.extent.depth = 1;\n        imageInfo.arrayLayers = 1;\n        imageInfo.mipLevels = 1;\n        imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n        imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;\n        imageInfo.tiling = VK_IMAGE_TILING_LINEAR;\n        imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n        VK_CHECK(vkCreateImage(r->m_dev, &imageInfo, nullptr, &m_images[i].image));\n\n        VkMemoryRequirements memReqs;\n        VkMemoryAllocateInfo memAllocInfo {};\n        memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        vkGetImageMemoryRequirements(r->m_dev, m_images[i].image, &memReqs);\n        memAllocInfo.allocationSize = memReqs.size;\n\n        VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n            | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;\n        memAllocInfo.memoryTypeIndex = r->memoryTypeIndex(memType, memReqs.memoryTypeBits);\n        VK_CHECK(vkAllocateMemory(r->m_dev, &memAllocInfo, nullptr, &m_images[i].memory));\n        VK_CHECK(vkBindImageMemory(r->m_dev, m_images[i].image, m_images[i].memory, 0));\n\n        VkImageViewCreateInfo viewInfo = {};\n        viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n        viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n        viewInfo.format = imageInfo.format;\n        viewInfo.image = m_images[i].image;\n        viewInfo.subresourceRange = {};\n        viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        viewInfo.subresourceRange.baseMipLevel = 0;\n        viewInfo.subresourceRange.levelCount = 1;\n        viewInfo.subresourceRange.baseArrayLayer = 0;\n        viewInfo.subresourceRange.layerCount = 1;\n        viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n        viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n        viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n        viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n        VK_CHECK(vkCreateImageView(r->m_dev, &viewInfo, nullptr, &m_images[i].view));\n\n        VkImageMemoryBarrier imageBarrier = {};\n        imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n        imageBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;\n        imageBarrier.image = m_images[i].image;\n        imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageBarrier.subresourceRange.layerCount = 1;\n        imageBarrier.subresourceRange.levelCount = 1;\n        imageBarrier.srcAccessMask = 0;\n        imageBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;\n\n        r->commandBufferBegin();\n        vkCmdPipelineBarrier(\n            r->m_commandBuffer,\n            VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,\n            VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,\n            0,\n            0,\n            nullptr,\n            0,\n            nullptr,\n            1,\n            &imageBarrier\n        );\n        r->commandBufferSubmit();\n\n        VkImageSubresource subresource = {};\n        subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        VkSubresourceLayout layout;\n        vkGetImageSubresourceLayout(r->m_dev, m_images[i].image, &subresource, &layout);\n\n        m_images[i].linesize = layout.rowPitch;\n        VK_CHECK(vkMapMemory(\n            r->m_dev,\n            m_images[i].memory,\n            0,\n            VK_WHOLE_SIZE,\n            0,\n            reinterpret_cast<void**>(&m_images[i].mapped)\n        ));\n    }\n\n    VkSemaphoreCreateInfo semInfo = {};\n    semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n    VK_CHECK(vkCreateSemaphore(r->m_dev, &semInfo, nullptr, &m_output.semaphore));\n\n    // Shader\n    VkShaderModuleCreateInfo moduleInfo = {};\n    moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    moduleInfo.codeSize = shaderLen;\n    moduleInfo.pCode = (uint32_t*)shaderData;\n    VK_CHECK(vkCreateShaderModule(r->m_dev, &moduleInfo, nullptr, &m_shader));\n\n    // Pipeline\n    VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};\n    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n    pipelineLayoutInfo.setLayoutCount = 1;\n    pipelineLayoutInfo.pSetLayouts = &m_descriptorLayout;\n    VK_CHECK(vkCreatePipelineLayout(r->m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout));\n\n    VkPipelineShaderStageCreateInfo stageInfo = {};\n    stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;\n    stageInfo.pName = \"main\";\n    stageInfo.module = m_shader;\n\n    VkComputePipelineCreateInfo pipelineInfo = {};\n    pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;\n    pipelineInfo.layout = m_pipelineLayout;\n    pipelineInfo.stage = stageInfo;\n    VK_CHECK(vkCreateComputePipelines(r->m_dev, nullptr, 1, &pipelineInfo, nullptr, &m_pipeline));\n\n    m_groupCountX = (imageCreateInfo.extent.width + 7) / 8;\n    m_groupCountY = (imageCreateInfo.extent.height + 7) / 8;\n}\n\nvoid FormatConverter::Convert(uint8_t** data, int* linesize) {\n    VkCommandBufferBeginInfo commandBufferBegin = {};\n    commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin));\n\n    vkCmdResetQueryPool(m_commandBuffer, m_queryPool, 0, 1);\n\n    vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);\n\n    std::vector<VkWriteDescriptorSet> descriptorWriteSets;\n\n    VkDescriptorImageInfo descriptorImageInfoIn = {};\n    descriptorImageInfoIn.imageView = m_view;\n    descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n    VkWriteDescriptorSet descriptorWriteSet = {};\n    descriptorWriteSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n    descriptorWriteSet.descriptorCount = 1;\n    descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorWriteSet.pImageInfo = &descriptorImageInfoIn;\n    descriptorWriteSet.dstBinding = 0;\n    descriptorWriteSets.push_back(descriptorWriteSet);\n\n    VkDescriptorImageInfo descriptorImageInfoOuts[3] = {};\n    for (size_t i = 0; i < m_images.size(); ++i) {\n        descriptorImageInfoOuts[i].imageView = m_images[i].view;\n        descriptorImageInfoOuts[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n        descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n        descriptorWriteSet.pImageInfo = &descriptorImageInfoOuts[i];\n        descriptorWriteSet.dstBinding = 1;\n        descriptorWriteSet.dstArrayElement = i;\n        descriptorWriteSets.push_back(descriptorWriteSet);\n    }\n\n    r->d.vkCmdPushDescriptorSetKHR(\n        m_commandBuffer,\n        VK_PIPELINE_BIND_POINT_COMPUTE,\n        m_pipelineLayout,\n        0,\n        descriptorWriteSets.size(),\n        descriptorWriteSets.data()\n    );\n\n    vkCmdDispatch(m_commandBuffer, m_groupCountX, m_groupCountY, 1);\n\n    vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, 0);\n\n    vkEndCommandBuffer(m_commandBuffer);\n\n    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.waitSemaphoreCount = 1;\n    submitInfo.pWaitSemaphores = &m_semaphore;\n    submitInfo.pWaitDstStageMask = &waitStage;\n    submitInfo.signalSemaphoreCount = 1;\n    submitInfo.pSignalSemaphores = &m_output.semaphore;\n    submitInfo.commandBufferCount = 1;\n    submitInfo.pCommandBuffers = &m_commandBuffer;\n    VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, nullptr));\n\n    for (size_t i = 0; i < m_images.size(); ++i) {\n        data[i] = m_images[i].mapped;\n        linesize[i] = m_images[i].linesize;\n    }\n}\n\nvoid FormatConverter::Sync() {\n    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.waitSemaphoreCount = 1;\n    submitInfo.pWaitSemaphores = &m_output.semaphore;\n    submitInfo.pWaitDstStageMask = &waitStage;\n    VK_CHECK(vkQueueSubmit(r->m_queue, 1, &submitInfo, r->m_fence));\n\n    VK_CHECK(vkWaitForFences(r->m_dev, 1, &r->m_fence, VK_TRUE, UINT64_MAX));\n    VK_CHECK(vkResetFences(r->m_dev, 1, &r->m_fence));\n}\n\nuint64_t FormatConverter::GetTimestamp() {\n    uint64_t query;\n    VK_CHECK(vkGetQueryPoolResults(\n        r->m_dev,\n        m_queryPool,\n        0,\n        1,\n        sizeof(uint64_t),\n        &query,\n        sizeof(uint64_t),\n        VK_QUERY_RESULT_64_BIT\n    ));\n    return query * r->m_timestampPeriod;\n}\n\nRgbToYuv420::RgbToYuv420(\n    Renderer* render, VkImage image, VkImageCreateInfo imageInfo, VkSemaphore semaphore\n)\n    : FormatConverter(render) {\n    init(\n        image,\n        imageInfo,\n        semaphore,\n        3,\n        RGBTOYUV420_SHADER_COMP_SPV_PTR,\n        RGBTOYUV420_SHADER_COMP_SPV_LEN\n    );\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/FormatConverter.h",
    "content": "#pragma once\n\n#include \"Renderer.h\"\n\nclass FormatConverter {\npublic:\n    struct Output {\n        VkSemaphore semaphore = VK_NULL_HANDLE;\n    };\n\n    virtual ~FormatConverter();\n\n    Output GetOutput();\n\n    void Convert(uint8_t** data, int* linesize);\n\n    void Sync();\n\n    uint64_t GetTimestamp();\n\nprotected:\n    struct OutputImage {\n        VkImage image = VK_NULL_HANDLE;\n        VkDeviceMemory memory = VK_NULL_HANDLE;\n        VkImageView view = VK_NULL_HANDLE;\n        VkSemaphore semaphore = VK_NULL_HANDLE;\n        VkDeviceSize linesize = 0;\n        uint8_t* mapped = nullptr;\n    };\n\n    explicit FormatConverter(Renderer* render);\n    void init(\n        VkImage image,\n        VkImageCreateInfo imageCreateInfo,\n        VkSemaphore semaphore,\n        int count,\n        const unsigned char* shaderData,\n        unsigned shaderLen\n    );\n\n    Renderer* r;\n    VkQueryPool m_queryPool = VK_NULL_HANDLE;\n    VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE;\n    VkDescriptorSetLayout m_descriptorLayout = VK_NULL_HANDLE;\n    VkImageView m_view = VK_NULL_HANDLE;\n    VkSemaphore m_semaphore = VK_NULL_HANDLE;\n    VkShaderModule m_shader = VK_NULL_HANDLE;\n    VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;\n    VkPipeline m_pipeline = VK_NULL_HANDLE;\n    uint32_t m_groupCountX = 0;\n    uint32_t m_groupCountY = 0;\n    std::vector<OutputImage> m_images;\n    Output m_output;\n};\n\nclass RgbToYuv420 : public FormatConverter {\npublic:\n    explicit RgbToYuv420(\n        Renderer* render, VkImage image, VkImageCreateInfo imageInfo, VkSemaphore semaphore\n    );\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/FrameRender.cpp",
    "content": "#include \"FrameRender.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/bindings.h\"\n\n#include <filesystem>\n#include <fstream>\n\nFrameRender::FrameRender(alvr::VkContext& ctx, init_packet& init, int fds[])\n    : Renderer(\n          ctx.get_vk_instance(),\n          ctx.get_vk_device(),\n          ctx.get_vk_phys_device(),\n          ctx.get_vk_queue_family_index(),\n          ctx.get_vk_device_extensions()\n      ) {\n    m_quadShaderSize = QUAD_SHADER_COMP_SPV_LEN;\n    m_quadShaderCode = reinterpret_cast<const uint32_t*>(QUAD_SHADER_COMP_SPV_PTR);\n\n    Startup(\n        init.image_create_info.extent.width,\n        init.image_create_info.extent.height,\n        init.image_create_info.format\n    );\n\n    for (size_t i = 0; i < 3; ++i) {\n        AddImage(init.image_create_info, init.mem_index, fds[2 * i], fds[2 * i + 1]);\n    }\n\n    m_width = Settings::Instance().m_renderWidth;\n    m_height = Settings::Instance().m_renderHeight;\n\n    Info(\"FrameRender: Input size %ux%u\", m_width, m_height);\n\n    if (Settings::Instance().m_force_sw_encoding) {\n        m_handle = ExternalHandle::None;\n    } else if (ctx.amd || ctx.intel) {\n        m_handle = ExternalHandle::DmaBuf;\n    } else if (ctx.nvidia) {\n        m_handle = ExternalHandle::OpaqueFd;\n    }\n\n    setupCustomShaders(\"pre\");\n\n    if (Settings::Instance().m_enableColorCorrection) {\n        setupColorCorrection();\n    }\n\n    if (Settings::Instance().m_enableFoveatedEncoding) {\n        setupFoveatedRendering();\n    }\n\n    setupCustomShaders(\"post\");\n\n    if (m_pipelines.empty()) {\n        RenderPipeline* pipeline = new RenderPipeline(this);\n        pipeline->SetShader(QUAD_SHADER_COMP_SPV_PTR, QUAD_SHADER_COMP_SPV_LEN);\n        m_pipelines.push_back(pipeline);\n        AddPipeline(pipeline);\n    }\n\n    Info(\"FrameRender: Output size %ux%u\", m_width, m_height);\n}\n\nFrameRender::~FrameRender() {\n    for (RenderPipeline* pipeline : m_pipelines) {\n        delete pipeline;\n    }\n}\n\nFrameRender::Output FrameRender::CreateOutput() {\n    Renderer::CreateOutput(m_width, m_height, m_handle);\n    return GetOutput();\n}\n\nuint32_t FrameRender::GetEncodingWidth() const { return m_width; }\n\nuint32_t FrameRender::GetEncodingHeight() const { return m_height; }\n\nvoid FrameRender::setupColorCorrection() {\n    std::vector<VkSpecializationMapEntry> entries;\n\n#define ENTRY(x, v)                                                                                \\\n    m_colorCorrectionConstants.x = v;                                                              \\\n    entries.push_back(                                                                             \\\n        { (uint32_t)entries.size(), offsetof(ColorCorrection, x), sizeof(ColorCorrection::x) }     \\\n    );\n\n    ENTRY(renderWidth, m_width);\n    ENTRY(renderHeight, m_height);\n    ENTRY(brightness, Settings::Instance().m_brightness);\n    ENTRY(contrast, Settings::Instance().m_contrast + 1.f);\n    ENTRY(saturation, Settings::Instance().m_saturation + 1.f);\n    ENTRY(gamma, Settings::Instance().m_gamma);\n    ENTRY(sharpening, Settings::Instance().m_sharpening);\n#undef ENTRY\n\n    RenderPipeline* pipeline = new RenderPipeline(this);\n    pipeline->SetShader(COLOR_SHADER_COMP_SPV_PTR, COLOR_SHADER_COMP_SPV_LEN);\n    pipeline->SetConstants(&m_colorCorrectionConstants, std::move(entries));\n    m_pipelines.push_back(pipeline);\n    AddPipeline(pipeline);\n}\n\nvoid FrameRender::setupFoveatedRendering() {\n    float targetEyeWidth = (float)m_width / 2;\n    float targetEyeHeight = (float)m_height;\n\n    float centerSizeX = (float)Settings::Instance().m_foveationCenterSizeX;\n    float centerSizeY = (float)Settings::Instance().m_foveationCenterSizeY;\n    float centerShiftX = (float)Settings::Instance().m_foveationCenterShiftX;\n    float centerShiftY = (float)Settings::Instance().m_foveationCenterShiftY;\n    float edgeRatioX = (float)Settings::Instance().m_foveationEdgeRatioX;\n    float edgeRatioY = (float)Settings::Instance().m_foveationEdgeRatioY;\n\n    float edgeSizeX = targetEyeWidth - centerSizeX * targetEyeWidth;\n    float edgeSizeY = targetEyeHeight - centerSizeY * targetEyeHeight;\n\n    float centerSizeXAligned\n        = 1. - ceil(edgeSizeX / (edgeRatioX * 2.)) * (edgeRatioX * 2.) / targetEyeWidth;\n    float centerSizeYAligned\n        = 1. - ceil(edgeSizeY / (edgeRatioY * 2.)) * (edgeRatioY * 2.) / targetEyeHeight;\n\n    float edgeSizeXAligned = targetEyeWidth - centerSizeXAligned * targetEyeWidth;\n    float edgeSizeYAligned = targetEyeHeight - centerSizeYAligned * targetEyeHeight;\n\n    float centerShiftXAligned = ceil(centerShiftX * edgeSizeXAligned / (edgeRatioX * 2.))\n        * (edgeRatioX * 2.) / edgeSizeXAligned;\n    float centerShiftYAligned = ceil(centerShiftY * edgeSizeYAligned / (edgeRatioY * 2.))\n        * (edgeRatioY * 2.) / edgeSizeYAligned;\n\n    float foveationScaleX = (centerSizeXAligned + (1. - centerSizeXAligned) / edgeRatioX);\n    float foveationScaleY = (centerSizeYAligned + (1. - centerSizeYAligned) / edgeRatioY);\n\n    float optimizedEyeWidth = foveationScaleX * targetEyeWidth;\n    float optimizedEyeHeight = foveationScaleY * targetEyeHeight;\n\n    // round the frame dimensions to a number of pixel multiple of 32 for the encoder\n    auto optimizedEyeWidthAligned = (uint32_t)ceil(optimizedEyeWidth / 32.f) * 32;\n    auto optimizedEyeHeightAligned = (uint32_t)ceil(optimizedEyeHeight / 32.f) * 32;\n\n    float eyeWidthRatioAligned = optimizedEyeWidth / optimizedEyeWidthAligned;\n    float eyeHeightRatioAligned = optimizedEyeHeight / optimizedEyeHeightAligned;\n\n    m_width = optimizedEyeWidthAligned * 2;\n    m_height = optimizedEyeHeightAligned;\n\n    std::vector<VkSpecializationMapEntry> entries;\n\n#define ENTRY(x, v)                                                                                \\\n    m_foveatedRenderingConstants.x = v;                                                            \\\n    entries.push_back(                                                                             \\\n        { (uint32_t)entries.size(), offsetof(FoveationVars, x), sizeof(FoveationVars::x) }         \\\n    );\n\n    ENTRY(eyeWidthRatio, eyeWidthRatioAligned);\n    ENTRY(eyeHeightRatio, eyeHeightRatioAligned);\n    ENTRY(centerSizeX, centerSizeXAligned);\n    ENTRY(centerSizeY, centerSizeYAligned);\n    ENTRY(centerShiftX, centerShiftXAligned);\n    ENTRY(centerShiftY, centerShiftYAligned);\n    ENTRY(edgeRatioX, edgeRatioX);\n    ENTRY(edgeRatioY, edgeRatioY);\n#undef ENTRY\n\n    RenderPipeline* pipeline = new RenderPipeline(this);\n    pipeline->SetShader(FFR_SHADER_COMP_SPV_PTR, FFR_SHADER_COMP_SPV_LEN);\n    pipeline->SetConstants(&m_foveatedRenderingConstants, std::move(entries));\n    m_pipelines.push_back(pipeline);\n    AddPipeline(pipeline);\n}\n\nvoid FrameRender::setupCustomShaders(const std::string& stage) {\n    try {\n        const std::filesystem::path shadersDir\n            = std::filesystem::path(g_sessionPath).replace_filename(\"shaders\");\n        for (const auto& entry :\n             std::filesystem::directory_iterator(shadersDir / std::filesystem::path(stage))) {\n            std::ifstream fs(entry.path(), std::ios::binary | std::ios::in);\n            uint32_t magic = 0;\n            fs.read((char*)&magic, sizeof(uint32_t));\n            if (magic != 0x07230203) {\n                Warn(\"FrameRender: Shader file %s is not a SPIR-V file\", entry.path().c_str());\n                continue;\n            }\n            Info(\n                \"FrameRender: Adding [%s] shader %s\", stage.c_str(), entry.path().filename().c_str()\n            );\n            RenderPipeline* pipeline = new RenderPipeline(this);\n            pipeline->SetShader(entry.path().c_str());\n            m_pipelines.push_back(pipeline);\n            AddPipeline(pipeline);\n        }\n    } catch (...) { }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/FrameRender.h",
    "content": "#pragma once\n\n#include \"Renderer.h\"\n#include \"ffmpeg_helper.h\"\n#include \"protocol.h\"\n\nclass FrameRender : public Renderer {\npublic:\n    explicit FrameRender(alvr::VkContext& ctx, init_packet& init, int fds[]);\n    ~FrameRender();\n\n    Output CreateOutput();\n    uint32_t GetEncodingWidth() const;\n    uint32_t GetEncodingHeight() const;\n\nprivate:\n    struct ColorCorrection {\n        float renderWidth;\n        float renderHeight;\n        float brightness;\n        float contrast;\n        float saturation;\n        float gamma;\n        float sharpening;\n    };\n\n    struct FoveationVars {\n        float eyeWidthRatio;\n        float eyeHeightRatio;\n        float centerSizeX;\n        float centerSizeY;\n        float centerShiftX;\n        float centerShiftY;\n        float edgeRatioX;\n        float edgeRatioY;\n    };\n\n    void setupColorCorrection();\n    void setupFoveatedRendering();\n    void setupCustomShaders(const std::string& stage);\n\n    uint32_t m_width;\n    uint32_t m_height;\n    ExternalHandle m_handle = ExternalHandle::None;\n    ColorCorrection m_colorCorrectionConstants;\n    FoveationVars m_foveatedRenderingConstants;\n    std::vector<RenderPipeline*> m_pipelines;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/Renderer.cpp",
    "content": "#include \"Renderer.h\"\n\n#include <algorithm>\n#include <array>\n#include <cassert>\n#include <cstring>\n#include <fstream>\n#include <iostream>\n\n#ifndef DRM_FORMAT_INVALID\n#define DRM_FORMAT_INVALID 0\n#define fourcc_code(a, b, c, d)                                                                    \\\n    ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))\n#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4')\n#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4')\n#define fourcc_mod_code(vendor, val) ((((uint64_t)vendor) << 56) | ((val) & 0x00ffffffffffffffULL))\n#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1))\n#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(0, 0)\n#define DRM_FORMAT_MOD_VENDOR_AMD 0x02\n#define AMD_FMT_MOD_DCC_SHIFT 13\n#define AMD_FMT_MOD_DCC_MASK 0x1\n#define IS_AMD_FMT_MOD(val) (((val) >> 56) == DRM_FORMAT_MOD_VENDOR_AMD)\n#define AMD_FMT_MOD_GET(field, value)                                                              \\\n    (((value) >> AMD_FMT_MOD_##field##_SHIFT) & AMD_FMT_MOD_##field##_MASK)\n#endif\n\nstruct Vertex {\n    float position[2];\n};\n\nstatic uint32_t to_drm_format(VkFormat format) {\n    switch (format) {\n    case VK_FORMAT_B8G8R8A8_UNORM:\n        return DRM_FORMAT_ARGB8888;\n    case VK_FORMAT_R8G8B8A8_UNORM:\n        return DRM_FORMAT_ABGR8888;\n    default:\n        std::cerr << \"Unsupported format \" << format << std::endl;\n        return DRM_FORMAT_INVALID;\n    }\n}\n\nstatic bool filter_modifier(uint64_t modifier) {\n    if (IS_AMD_FMT_MOD(modifier)) {\n        // DCC not supported as encode input\n        if (AMD_FMT_MOD_GET(DCC, modifier)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nRenderer::Renderer(\n    const VkInstance& inst,\n    const VkDevice& dev,\n    const VkPhysicalDevice& physDev,\n    uint32_t queueIdx,\n    const std::vector<const char*>& devExtensions\n)\n    : m_inst(inst)\n    , m_dev(dev)\n    , m_physDev(physDev)\n    , m_queueFamilyIndex(queueIdx) {\n    auto checkExtension = [devExtensions](const char* name) {\n        return std::find_if(\n                   devExtensions.begin(),\n                   devExtensions.end(),\n                   [name](const char* ext) { return strcmp(ext, name) == 0; }\n               )\n            != devExtensions.end();\n    };\n    d.haveDmaBuf = checkExtension(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);\n    d.haveDrmModifiers = checkExtension(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);\n    d.haveCalibratedTimestamps = checkExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME);\n\n    if (!checkExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) {\n        throw std::runtime_error(\"Vulkan: Required extension \" VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME\n                                 \" not available\");\n    }\n\n#define VK_LOAD_PFN(name) d.name = (PFN_##name)vkGetInstanceProcAddr(m_inst, #name)\n    VK_LOAD_PFN(vkImportSemaphoreFdKHR);\n    VK_LOAD_PFN(vkGetMemoryFdKHR);\n    VK_LOAD_PFN(vkGetMemoryFdPropertiesKHR);\n    VK_LOAD_PFN(vkGetImageDrmFormatModifierPropertiesEXT);\n    VK_LOAD_PFN(vkGetCalibratedTimestampsEXT);\n    VK_LOAD_PFN(vkCmdPushDescriptorSetKHR);\n#undef VK_LOAD_PFN\n\n    VkPhysicalDeviceProperties props = {};\n    vkGetPhysicalDeviceProperties(m_physDev, &props);\n    m_timestampPeriod = props.limits.timestampPeriod;\n}\n\nRenderer::~Renderer() {\n    vkDeviceWaitIdle(m_dev);\n\n    for (const InputImage& image : m_images) {\n        vkDestroyImageView(m_dev, image.view, nullptr);\n        vkDestroyImage(m_dev, image.image, nullptr);\n        vkFreeMemory(m_dev, image.memory, nullptr);\n        vkDestroySemaphore(m_dev, image.semaphore, nullptr);\n    }\n\n    for (const StagingImage& image : m_stagingImages) {\n        vkDestroyImageView(m_dev, image.view, nullptr);\n        vkDestroyImage(m_dev, image.image, nullptr);\n        vkFreeMemory(m_dev, image.memory, nullptr);\n    }\n\n    vkDestroyImageView(m_dev, m_output.view, nullptr);\n    vkDestroyImage(m_dev, m_output.image, nullptr);\n    vkFreeMemory(m_dev, m_output.memory, nullptr);\n    vkDestroySemaphore(m_dev, m_output.semaphore, nullptr);\n\n    vkDestroyQueryPool(m_dev, m_queryPool, nullptr);\n    vkDestroyCommandPool(m_dev, m_commandPool, nullptr);\n    vkDestroySampler(m_dev, m_sampler, nullptr);\n    vkDestroyDescriptorSetLayout(m_dev, m_descriptorLayout, nullptr);\n    vkDestroyFence(m_dev, m_fence, nullptr);\n}\n\nvoid Renderer::Startup(uint32_t width, uint32_t height, VkFormat format) {\n    m_format = format;\n    m_imageSize.width = width;\n    m_imageSize.height = height;\n\n    vkGetDeviceQueue(m_dev, m_queueFamilyIndex, 0, &m_queue);\n\n    // Timestamp query\n    VkQueryPoolCreateInfo queryPoolInfo = {};\n    queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;\n    queryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;\n    queryPoolInfo.queryCount = 2;\n    VK_CHECK(vkCreateQueryPool(m_dev, &queryPoolInfo, nullptr, &m_queryPool));\n\n    // Command buffer\n    VkCommandPoolCreateInfo cmdPoolInfo = {};\n    cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n    cmdPoolInfo.queueFamilyIndex = m_queueFamilyIndex;\n    cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;\n    VK_CHECK(vkCreateCommandPool(m_dev, &cmdPoolInfo, nullptr, &m_commandPool));\n\n    VkCommandBufferAllocateInfo commandBufferInfo = {};\n    commandBufferInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n    commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n    commandBufferInfo.commandPool = m_commandPool;\n    commandBufferInfo.commandBufferCount = 1;\n    VK_CHECK(vkAllocateCommandBuffers(m_dev, &commandBufferInfo, &m_commandBuffer));\n\n    // Sampler\n    VkSamplerCreateInfo samplerInfo = {};\n    samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n    samplerInfo.magFilter = VK_FILTER_LINEAR;\n    samplerInfo.minFilter = VK_FILTER_LINEAR;\n    samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;\n    samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n    samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n    samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n    samplerInfo.anisotropyEnable = VK_TRUE;\n    samplerInfo.maxAnisotropy = 16.0f;\n    samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;\n    VK_CHECK(vkCreateSampler(m_dev, &samplerInfo, nullptr, &m_sampler));\n\n    // Descriptors\n    VkDescriptorSetLayoutBinding descriptorBindings[2] = {};\n    descriptorBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    descriptorBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;\n    descriptorBindings[0].descriptorCount = 1;\n    descriptorBindings[0].pImmutableSamplers = &m_sampler;\n    descriptorBindings[0].binding = 0;\n    descriptorBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;\n    descriptorBindings[1].descriptorCount = 1;\n    descriptorBindings[1].binding = 1;\n\n    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = {};\n    descriptorSetLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n    descriptorSetLayoutInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;\n    descriptorSetLayoutInfo.bindingCount = 2;\n    descriptorSetLayoutInfo.pBindings = descriptorBindings;\n    VK_CHECK(\n        vkCreateDescriptorSetLayout(m_dev, &descriptorSetLayoutInfo, nullptr, &m_descriptorLayout)\n    );\n\n    // Fence\n    VkFenceCreateInfo fenceInfo = {};\n    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;\n    VK_CHECK(vkCreateFence(m_dev, &fenceInfo, nullptr, &m_fence));\n}\n\nvoid Renderer::AddImage(\n    VkImageCreateInfo imageInfo, size_t memoryIndex, int imageFd, int semaphoreFd\n) {\n    VkExternalMemoryImageCreateInfo extMemImageInfo = {};\n    extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n    extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n    imageInfo.pNext = &extMemImageInfo;\n    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    VkImage image;\n    VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &image));\n\n    VkMemoryRequirements req;\n    vkGetImageMemoryRequirements(m_dev, image, &req);\n\n    VkMemoryDedicatedAllocateInfo dedicatedMemInfo = {};\n    dedicatedMemInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;\n    dedicatedMemInfo.image = image;\n\n    VkImportMemoryFdInfoKHR importMemInfo = {};\n    importMemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;\n    importMemInfo.pNext = &dedicatedMemInfo;\n    importMemInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n    importMemInfo.fd = imageFd;\n\n    VkMemoryAllocateInfo memAllocInfo = {};\n    memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    memAllocInfo.pNext = &importMemInfo;\n    memAllocInfo.allocationSize = req.size;\n    memAllocInfo.memoryTypeIndex = memoryIndex;\n\n    VkDeviceMemory mem;\n    VK_CHECK(vkAllocateMemory(m_dev, &memAllocInfo, nullptr, &mem));\n    VK_CHECK(vkBindImageMemory(m_dev, image, mem, 0));\n\n    VkSemaphoreTypeCreateInfo timelineInfo = {};\n    timelineInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;\n    timelineInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n\n    VkSemaphoreCreateInfo semInfo = {};\n    semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n    semInfo.pNext = &timelineInfo;\n    VkSemaphore semaphore;\n    VK_CHECK(vkCreateSemaphore(m_dev, &semInfo, nullptr, &semaphore));\n\n    VkImportSemaphoreFdInfoKHR impSemInfo = {};\n    impSemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;\n    impSemInfo.semaphore = semaphore;\n    impSemInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n    impSemInfo.fd = semaphoreFd;\n    VK_CHECK(d.vkImportSemaphoreFdKHR(m_dev, &impSemInfo));\n\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = imageInfo.format;\n    viewInfo.image = image;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VkImageView view;\n    VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &view));\n\n    m_images.push_back({ image, VK_IMAGE_LAYOUT_UNDEFINED, mem, semaphore, view });\n}\n\nvoid Renderer::AddPipeline(RenderPipeline* pipeline) {\n    pipeline->Build();\n    m_pipelines.push_back(pipeline);\n\n    if (m_pipelines.size() > 1 && m_stagingImages.size() < 2) {\n        addStagingImage(m_imageSize.width, m_imageSize.height);\n    }\n}\n\nvoid Renderer::CreateOutput(uint32_t width, uint32_t height, ExternalHandle handle) {\n    m_output.imageInfo = {};\n    m_output.imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n    m_output.imageInfo.imageType = VK_IMAGE_TYPE_2D;\n    m_output.imageInfo.format = m_format;\n    m_output.imageInfo.extent.width = width;\n    m_output.imageInfo.extent.height = height;\n    m_output.imageInfo.extent.depth = 1;\n    m_output.imageInfo.mipLevels = 1;\n    m_output.imageInfo.arrayLayers = 1;\n    m_output.imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;\n    m_output.imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;\n    m_output.imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n    m_output.imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    std::vector<VkDrmFormatModifierPropertiesEXT> modifierProps;\n\n    VkExternalMemoryImageCreateInfo extMemImageInfo = {};\n    extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n\n    if (d.haveDrmModifiers && handle == ExternalHandle::DmaBuf) {\n        VkImageDrmFormatModifierListCreateInfoEXT modifierListInfo = {};\n        modifierListInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT;\n\n        m_output.imageInfo.pNext = &modifierListInfo;\n        m_output.imageInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;\n\n        VkDrmFormatModifierPropertiesListEXT modifierPropsList = {};\n        modifierPropsList.sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT;\n\n        VkFormatProperties2 formatProps = {};\n        formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;\n        formatProps.pNext = &modifierPropsList;\n        vkGetPhysicalDeviceFormatProperties2(m_physDev, m_output.imageInfo.format, &formatProps);\n\n        modifierProps.resize(modifierPropsList.drmFormatModifierCount);\n        modifierPropsList.pDrmFormatModifierProperties = modifierProps.data();\n        vkGetPhysicalDeviceFormatProperties2(m_physDev, m_output.imageInfo.format, &formatProps);\n\n        std::vector<uint64_t> imageModifiers;\n        std::cout << \"Available modifiers:\" << std::endl;\n        for (const VkDrmFormatModifierPropertiesEXT& prop : modifierProps) {\n            std::cout << \"modifier: \" << prop.drmFormatModifier\n                      << \" planes: \" << prop.drmFormatModifierPlaneCount << std::endl;\n            if (!filter_modifier(prop.drmFormatModifier)) {\n                std::cout << \" filtered\" << std::endl;\n                continue;\n            }\n\n            VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = {};\n            modInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT;\n            modInfo.drmFormatModifier = prop.drmFormatModifier;\n            modInfo.sharingMode = m_output.imageInfo.sharingMode;\n            modInfo.queueFamilyIndexCount = m_output.imageInfo.queueFamilyIndexCount;\n            modInfo.pQueueFamilyIndices = m_output.imageInfo.pQueueFamilyIndices;\n\n            VkPhysicalDeviceImageFormatInfo2 formatInfo = {};\n            formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;\n            formatInfo.pNext = &modInfo;\n            formatInfo.format = m_output.imageInfo.format;\n            formatInfo.type = m_output.imageInfo.imageType;\n            formatInfo.tiling = m_output.imageInfo.tiling;\n            formatInfo.usage = m_output.imageInfo.usage;\n            formatInfo.flags = m_output.imageInfo.flags;\n\n            VkImageFormatProperties2 imageFormatProps = {};\n            imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;\n            imageFormatProps.pNext = NULL;\n\n            VkResult r = vkGetPhysicalDeviceImageFormatProperties2(\n                m_physDev, &formatInfo, &imageFormatProps\n            );\n            if (r == VK_SUCCESS) {\n                imageModifiers.push_back(prop.drmFormatModifier);\n            }\n        }\n        modifierListInfo.drmFormatModifierCount = imageModifiers.size();\n        modifierListInfo.pDrmFormatModifiers = imageModifiers.data();\n\n        extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;\n        modifierListInfo.pNext = &extMemImageInfo;\n\n        VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image));\n    } else if (d.haveDmaBuf && handle == ExternalHandle::DmaBuf) {\n        extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;\n        m_output.imageInfo.pNext = &extMemImageInfo;\n\n        m_output.imageInfo.tiling = VK_IMAGE_TILING_LINEAR;\n        VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image));\n    } else if (handle == ExternalHandle::OpaqueFd) {\n        extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n        m_output.imageInfo.pNext = &extMemImageInfo;\n\n        m_output.imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n        VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image));\n    } else {\n        m_output.imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n        VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, nullptr, &m_output.image));\n    }\n\n    VkMemoryDedicatedRequirements mdr = {};\n    mdr.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;\n\n    VkMemoryRequirements2 memoryReqs = {};\n    memoryReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;\n    memoryReqs.pNext = &mdr;\n\n    VkImageMemoryRequirementsInfo2 memoryReqsInfo = {};\n    memoryReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;\n    memoryReqsInfo.image = m_output.image;\n    vkGetImageMemoryRequirements2(m_dev, &memoryReqsInfo, &memoryReqs);\n    m_output.size = memoryReqs.memoryRequirements.size;\n\n    VkExportMemoryAllocateInfo memory_export_info = {};\n    memory_export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;\n    memory_export_info.handleTypes = extMemImageInfo.handleTypes;\n\n    VkMemoryDedicatedAllocateInfo memory_dedicated_info = {};\n    memory_dedicated_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;\n    memory_dedicated_info.image = m_output.image;\n    if (handle != ExternalHandle::None) {\n        memory_dedicated_info.pNext = &memory_export_info;\n    }\n\n    VkMemoryAllocateInfo memi = {};\n    memi.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    memi.pNext = &memory_dedicated_info;\n    memi.allocationSize = memoryReqs.memoryRequirements.size;\n    memi.memoryTypeIndex = memoryTypeIndex(\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryRequirements.memoryTypeBits\n    );\n    VK_CHECK(vkAllocateMemory(m_dev, &memi, nullptr, &m_output.memory));\n\n    VkBindImageMemoryInfo bimi = {};\n    bimi.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;\n    bimi.image = m_output.image;\n    bimi.memory = m_output.memory;\n    bimi.memoryOffset = 0;\n    VK_CHECK(vkBindImageMemory2(m_dev, 1, &bimi));\n\n    // DRM export\n    if (d.haveDmaBuf) {\n        VkMemoryGetFdInfoKHR memoryGetFdInfo = {};\n        memoryGetFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;\n        memoryGetFdInfo.memory = m_output.memory;\n        memoryGetFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;\n        VkResult res = d.vkGetMemoryFdKHR(m_dev, &memoryGetFdInfo, &m_output.drm.fd);\n        if (res != VK_SUCCESS) {\n            std::cout << \"vkGetMemoryFdKHR \" << result_to_str(res) << std::endl;\n        } else {\n            if (d.haveDrmModifiers) {\n                VkImageDrmFormatModifierPropertiesEXT imageDrmProps = {};\n                imageDrmProps.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT;\n                d.vkGetImageDrmFormatModifierPropertiesEXT(m_dev, m_output.image, &imageDrmProps);\n                if (res != VK_SUCCESS) {\n                    std::cout << \"vkGetImageDrmFormatModifierPropertiesEXT \" << result_to_str(res)\n                              << std::endl;\n                } else {\n                    m_output.drm.modifier = imageDrmProps.drmFormatModifier;\n                    for (VkDrmFormatModifierPropertiesEXT prop : modifierProps) {\n                        if (prop.drmFormatModifier == m_output.drm.modifier) {\n                            m_output.drm.planes = prop.drmFormatModifierPlaneCount;\n                        }\n                    }\n                }\n            } else {\n                m_output.drm.modifier = DRM_FORMAT_MOD_INVALID;\n                m_output.drm.planes = 1;\n            }\n\n            for (uint32_t i = 0; i < m_output.drm.planes; i++) {\n                VkImageSubresource subresource = {};\n                if (d.haveDrmModifiers) {\n                    subresource.aspectMask = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT << i;\n                } else {\n                    subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n                }\n                VkSubresourceLayout layout;\n                vkGetImageSubresourceLayout(m_dev, m_output.image, &subresource, &layout);\n                m_output.drm.strides[i] = layout.rowPitch;\n                m_output.drm.offsets[i] = layout.offset;\n            }\n        }\n        m_output.drm.format = to_drm_format(m_output.imageInfo.format);\n    }\n\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = m_output.imageInfo.format;\n    viewInfo.image = m_output.image;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &m_output.view));\n\n    VkSemaphoreCreateInfo semInfo = {};\n    semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n    VK_CHECK(vkCreateSemaphore(m_dev, &semInfo, nullptr, &m_output.semaphore));\n}\n\nvoid Renderer::ImportOutput(const DrmImage& drm) {\n    vkDestroyImageView(m_dev, m_output.view, nullptr);\n    vkDestroyImage(m_dev, m_output.image, nullptr);\n    vkFreeMemory(m_dev, m_output.memory, nullptr);\n\n    m_output.drm = drm;\n    m_output.imageInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;\n\n    VkExternalMemoryImageCreateInfo extMemImageInfo = {};\n    extMemImageInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n    extMemImageInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;\n    m_output.imageInfo.pNext = &extMemImageInfo;\n\n    VkSubresourceLayout layouts[4] = {};\n    for (uint32_t i = 0; i < drm.planes; ++i) {\n        layouts[i].offset = drm.offsets[i];\n        layouts[i].rowPitch = drm.strides[i];\n    }\n    VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = {};\n    modifierInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;\n    modifierInfo.drmFormatModifier = drm.modifier;\n    modifierInfo.drmFormatModifierPlaneCount = drm.planes;\n    modifierInfo.pPlaneLayouts = layouts;\n    extMemImageInfo.pNext = &modifierInfo;\n\n    VK_CHECK(vkCreateImage(m_dev, &m_output.imageInfo, NULL, &m_output.image));\n\n    VkMemoryFdPropertiesKHR fdProps = {};\n    fdProps.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;\n    VK_CHECK(d.vkGetMemoryFdPropertiesKHR(\n        m_dev, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, drm.fd, &fdProps\n    ));\n\n    VkImageMemoryRequirementsInfo2 memoryReqsInfo = {};\n    memoryReqsInfo.image = m_output.image;\n    memoryReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2;\n\n    VkMemoryRequirements2 memoryReqs = {};\n    memoryReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;\n    vkGetImageMemoryRequirements2(m_dev, &memoryReqsInfo, &memoryReqs);\n\n    VkMemoryAllocateInfo memoryAllocInfo = {};\n    memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    memoryAllocInfo.allocationSize = memoryReqs.memoryRequirements.size;\n    memoryAllocInfo.memoryTypeIndex = memoryTypeIndex(\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryRequirements.memoryTypeBits\n    );\n\n    VkImportMemoryFdInfoKHR importMemInfo = {};\n    importMemInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;\n    importMemInfo.fd = drm.fd;\n    importMemInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;\n    memoryAllocInfo.pNext = &importMemInfo;\n\n    VkMemoryDedicatedAllocateInfo dedicatedMemInfo = {};\n    dedicatedMemInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;\n    dedicatedMemInfo.image = m_output.image;\n    importMemInfo.pNext = &dedicatedMemInfo;\n\n    VK_CHECK(vkAllocateMemory(m_dev, &memoryAllocInfo, NULL, &m_output.memory));\n\n    VkBindImageMemoryInfo bindInfo = {};\n    bindInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;\n    bindInfo.image = m_output.image;\n    bindInfo.memory = m_output.memory;\n    bindInfo.memoryOffset = 0;\n    VK_CHECK(vkBindImageMemory2(m_dev, 1, &bindInfo));\n\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = m_output.imageInfo.format;\n    viewInfo.image = m_output.image;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &m_output.view));\n}\n\nvoid Renderer::Render(uint32_t index, uint64_t waitValue) {\n    if (!m_inputImageCapture.empty()) {\n        VkSemaphoreWaitInfo waitInfo = {};\n        waitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO;\n        waitInfo.semaphoreCount = 1;\n        waitInfo.pSemaphores = &m_images[index].semaphore;\n        waitInfo.pValues = &waitValue;\n        VK_CHECK(vkWaitSemaphores(m_dev, &waitInfo, UINT64_MAX));\n\n        dumpImage(\n            m_images[index].image,\n            m_images[index].view,\n            m_images[index].layout,\n            m_imageSize.width,\n            m_imageSize.height,\n            m_inputImageCapture\n        );\n        m_inputImageCapture.clear();\n    }\n\n    VkCommandBufferBeginInfo commandBufferBegin = {};\n    commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin));\n\n    vkCmdResetQueryPool(m_commandBuffer, m_queryPool, 0, 2);\n    vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, m_queryPool, 0);\n\n    for (size_t i = 0; i < m_pipelines.size(); ++i) {\n        VkRect2D rect = {};\n        VkImage in = VK_NULL_HANDLE;\n        VkImageView inView = VK_NULL_HANDLE;\n        VkImageLayout* inLayout = nullptr;\n        VkImage out = VK_NULL_HANDLE;\n        VkImageView outView = VK_NULL_HANDLE;\n        VkImageLayout* outLayout = nullptr;\n        if (i == 0) {\n            auto& img = m_images[index];\n            in = img.image;\n            inView = img.view;\n            inLayout = &img.layout;\n        } else {\n            auto& img = m_stagingImages[(i - 1) % m_stagingImages.size()];\n            in = img.image;\n            inView = img.view;\n            inLayout = &img.layout;\n        }\n        if (i == m_pipelines.size() - 1) {\n            out = m_output.image;\n            outView = m_output.view;\n            outLayout = &m_output.layout;\n            rect.extent.width = m_output.imageInfo.extent.width;\n            rect.extent.height = m_output.imageInfo.extent.height;\n        } else {\n            auto& img = m_stagingImages[i % m_stagingImages.size()];\n            out = img.image;\n            outView = img.view;\n            outLayout = &img.layout;\n            rect.extent = m_imageSize;\n        }\n        VkImageMemoryBarrier imageBarrier = {};\n        imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageBarrier.subresourceRange.layerCount = 1;\n        imageBarrier.subresourceRange.levelCount = 1;\n        std::vector<VkImageMemoryBarrier> imageBarriers;\n        if (*inLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {\n            imageBarrier.image = in;\n            imageBarrier.oldLayout = *inLayout;\n            *inLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n            imageBarrier.newLayout = *inLayout;\n            imageBarrier.srcAccessMask = 0;\n            imageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n            imageBarriers.push_back(imageBarrier);\n        }\n        if (*outLayout != VK_IMAGE_LAYOUT_GENERAL) {\n            imageBarrier.image = out;\n            imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n            *outLayout = VK_IMAGE_LAYOUT_GENERAL;\n            imageBarrier.newLayout = *outLayout;\n            imageBarrier.srcAccessMask = 0;\n            imageBarrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;\n            imageBarriers.push_back(imageBarrier);\n        }\n        if (imageBarriers.size()) {\n            vkCmdPipelineBarrier(\n                m_commandBuffer,\n                VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,\n                VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,\n                0,\n                0,\n                nullptr,\n                0,\n                nullptr,\n                imageBarriers.size(),\n                imageBarriers.data()\n            );\n        }\n        m_pipelines[i]->Render(inView, outView, rect);\n    }\n\n    vkCmdWriteTimestamp(m_commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, 1);\n\n    VK_CHECK(vkEndCommandBuffer(m_commandBuffer));\n\n    VkTimelineSemaphoreSubmitInfo timelineInfo = {};\n    timelineInfo.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;\n    timelineInfo.waitSemaphoreValueCount = 1;\n    timelineInfo.pWaitSemaphoreValues = &waitValue;\n\n    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.pNext = &timelineInfo;\n    submitInfo.waitSemaphoreCount = 1;\n    submitInfo.pWaitSemaphores = &m_images[index].semaphore;\n    submitInfo.pWaitDstStageMask = &waitStage;\n    submitInfo.signalSemaphoreCount = 1;\n    submitInfo.pSignalSemaphores = &m_output.semaphore;\n    submitInfo.commandBufferCount = 1;\n    submitInfo.pCommandBuffers = &m_commandBuffer;\n    VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, nullptr));\n}\n\nvoid Renderer::Sync() {\n    VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.waitSemaphoreCount = 1;\n    submitInfo.pWaitSemaphores = &m_output.semaphore;\n    submitInfo.pWaitDstStageMask = &waitStage;\n    VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, m_fence));\n\n    VK_CHECK(vkWaitForFences(m_dev, 1, &m_fence, VK_TRUE, UINT64_MAX));\n    VK_CHECK(vkResetFences(m_dev, 1, &m_fence));\n}\n\nRenderer::Output& Renderer::GetOutput() { return m_output; }\n\nRenderer::Timestamps Renderer::GetTimestamps() {\n    if (!d.haveCalibratedTimestamps) {\n        return { 0, 0, 0 };\n    }\n\n    uint64_t queries[2];\n    VK_CHECK(vkGetQueryPoolResults(\n        m_dev,\n        m_queryPool,\n        0,\n        2,\n        2 * sizeof(uint64_t),\n        queries,\n        sizeof(uint64_t),\n        VK_QUERY_RESULT_64_BIT\n    ));\n    queries[0] *= m_timestampPeriod;\n    queries[1] *= m_timestampPeriod;\n\n    VkCalibratedTimestampInfoEXT timestampInfo = {};\n    timestampInfo.sType = VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT;\n    timestampInfo.timeDomain = VK_TIME_DOMAIN_DEVICE_EXT;\n    uint64_t deviation;\n    uint64_t timestamp;\n    VK_CHECK(d.vkGetCalibratedTimestampsEXT(m_dev, 1, &timestampInfo, &timestamp, &deviation));\n    timestamp *= m_timestampPeriod;\n\n    if (!m_outputImageCapture.empty()) {\n        dumpImage(\n            m_output.image,\n            m_output.view,\n            m_output.layout,\n            m_output.imageInfo.extent.width,\n            m_output.imageInfo.extent.height,\n            m_outputImageCapture\n        );\n        m_outputImageCapture.clear();\n    }\n\n    return { timestamp, queries[0], queries[1] };\n}\n\nvoid Renderer::CaptureInputFrame(const std::string& filename) { m_inputImageCapture = filename; }\n\nvoid Renderer::CaptureOutputFrame(const std::string& filename) { m_outputImageCapture = filename; }\n\nstd::string Renderer::result_to_str(VkResult result) {\n    switch (result) {\n#define VAL(x)                                                                                     \\\n    case x:                                                                                        \\\n        return #x\n        VAL(VK_SUCCESS);\n        VAL(VK_NOT_READY);\n        VAL(VK_TIMEOUT);\n        VAL(VK_EVENT_SET);\n        VAL(VK_EVENT_RESET);\n        VAL(VK_INCOMPLETE);\n        VAL(VK_ERROR_OUT_OF_HOST_MEMORY);\n        VAL(VK_ERROR_OUT_OF_DEVICE_MEMORY);\n        VAL(VK_ERROR_INITIALIZATION_FAILED);\n        VAL(VK_ERROR_DEVICE_LOST);\n        VAL(VK_ERROR_MEMORY_MAP_FAILED);\n        VAL(VK_ERROR_LAYER_NOT_PRESENT);\n        VAL(VK_ERROR_EXTENSION_NOT_PRESENT);\n        VAL(VK_ERROR_FEATURE_NOT_PRESENT);\n        VAL(VK_ERROR_INCOMPATIBLE_DRIVER);\n        VAL(VK_ERROR_TOO_MANY_OBJECTS);\n        VAL(VK_ERROR_FORMAT_NOT_SUPPORTED);\n        VAL(VK_ERROR_FRAGMENTED_POOL);\n        VAL(VK_ERROR_OUT_OF_POOL_MEMORY);\n        VAL(VK_ERROR_INVALID_EXTERNAL_HANDLE);\n        VAL(VK_ERROR_SURFACE_LOST_KHR);\n        VAL(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);\n        VAL(VK_SUBOPTIMAL_KHR);\n        VAL(VK_ERROR_OUT_OF_DATE_KHR);\n        VAL(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);\n        VAL(VK_ERROR_VALIDATION_FAILED_EXT);\n        VAL(VK_ERROR_INVALID_SHADER_NV);\n        VAL(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);\n        VAL(VK_ERROR_NOT_PERMITTED_EXT);\n        VAL(VK_RESULT_MAX_ENUM);\n#undef VAL\n    default:\n        return \"Unknown VkResult\";\n    }\n}\n\nvoid Renderer::commandBufferBegin() {\n    VkCommandBufferBeginInfo commandBufferBegin = {};\n    commandBufferBegin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    VK_CHECK(vkBeginCommandBuffer(m_commandBuffer, &commandBufferBegin));\n}\n\nvoid Renderer::commandBufferSubmit() {\n    VK_CHECK(vkEndCommandBuffer(m_commandBuffer));\n\n    VkSubmitInfo submitInfo = {};\n    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submitInfo.commandBufferCount = 1;\n    submitInfo.pCommandBuffers = &m_commandBuffer;\n    VkFenceCreateInfo fenceInfo = {};\n    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;\n    VkFence fence;\n    VK_CHECK(vkCreateFence(m_dev, &fenceInfo, nullptr, &fence));\n    VK_CHECK(vkQueueSubmit(m_queue, 1, &submitInfo, fence));\n    VK_CHECK(vkWaitForFences(m_dev, 1, &fence, VK_TRUE, UINT64_MAX));\n    vkDestroyFence(m_dev, fence, nullptr);\n}\n\nvoid Renderer::addStagingImage(uint32_t width, uint32_t height) {\n    VkImageCreateInfo imageInfo = {};\n    imageInfo = {};\n    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n    imageInfo.imageType = VK_IMAGE_TYPE_2D;\n    imageInfo.format = m_format;\n    imageInfo.extent.width = width;\n    imageInfo.extent.height = height;\n    imageInfo.extent.depth = 1;\n    imageInfo.mipLevels = 1;\n    imageInfo.arrayLayers = 1;\n    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n    imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;\n    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    VkImage image;\n    VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &image));\n\n    VkMemoryRequirements memoryReqs;\n    vkGetImageMemoryRequirements(m_dev, image, &memoryReqs);\n    VkMemoryAllocateInfo memoryAllocInfo = {};\n    memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    memoryAllocInfo.allocationSize = memoryReqs.size;\n    memoryAllocInfo.memoryTypeIndex\n        = memoryTypeIndex(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, memoryReqs.memoryTypeBits);\n    VkDeviceMemory memory;\n    VK_CHECK(vkAllocateMemory(m_dev, &memoryAllocInfo, nullptr, &memory));\n    VK_CHECK(vkBindImageMemory(m_dev, image, memory, 0));\n\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = imageInfo.format;\n    viewInfo.image = image;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VkImageView view;\n    VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &view));\n\n    m_stagingImages.push_back({ image, VK_IMAGE_LAYOUT_UNDEFINED, memory, view });\n}\n\nvoid Renderer::dumpImage(\n    VkImage image,\n    VkImageView imageView,\n    VkImageLayout imageLayout,\n    uint32_t width,\n    uint32_t height,\n    const std::string& filename\n) {\n    VkImageCreateInfo imageInfo = {};\n    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n    imageInfo.imageType = VK_IMAGE_TYPE_2D;\n    imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;\n    imageInfo.extent.width = width;\n    imageInfo.extent.height = height;\n    imageInfo.extent.depth = 1;\n    imageInfo.arrayLayers = 1;\n    imageInfo.mipLevels = 1;\n    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;\n    imageInfo.tiling = VK_IMAGE_TILING_LINEAR;\n    imageInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;\n    VkImage dstImage;\n    VK_CHECK(vkCreateImage(m_dev, &imageInfo, nullptr, &dstImage));\n\n    VkMemoryRequirements memReqs;\n    VkMemoryAllocateInfo memAllocInfo {};\n    memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    vkGetImageMemoryRequirements(m_dev, dstImage, &memReqs);\n    memAllocInfo.allocationSize = memReqs.size;\n    memAllocInfo.memoryTypeIndex = memoryTypeIndex(\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT\n            | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n        memReqs.memoryTypeBits\n    );\n    VkDeviceMemory dstMemory;\n    VK_CHECK(vkAllocateMemory(m_dev, &memAllocInfo, nullptr, &dstMemory));\n    VK_CHECK(vkBindImageMemory(m_dev, dstImage, dstMemory, 0));\n\n    VkImageViewCreateInfo viewInfo = {};\n    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    viewInfo.format = imageInfo.format;\n    viewInfo.image = dstImage;\n    viewInfo.subresourceRange = {};\n    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    viewInfo.subresourceRange.baseMipLevel = 0;\n    viewInfo.subresourceRange.levelCount = 1;\n    viewInfo.subresourceRange.baseArrayLayer = 0;\n    viewInfo.subresourceRange.layerCount = 1;\n    viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    VkImageView dstView;\n    VK_CHECK(vkCreateImageView(m_dev, &viewInfo, nullptr, &dstView));\n\n    std::array<VkImageMemoryBarrier, 2> imageBarrierIn;\n    imageBarrierIn[0] = {};\n    imageBarrierIn[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n    imageBarrierIn[0].oldLayout = imageLayout;\n    imageBarrierIn[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    imageBarrierIn[0].image = image;\n    imageBarrierIn[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    imageBarrierIn[0].subresourceRange.layerCount = 1;\n    imageBarrierIn[0].subresourceRange.levelCount = 1;\n    imageBarrierIn[0].srcAccessMask = 0;\n    imageBarrierIn[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n    imageBarrierIn[1] = {};\n    imageBarrierIn[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n    imageBarrierIn[1].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    imageBarrierIn[1].newLayout = VK_IMAGE_LAYOUT_GENERAL;\n    imageBarrierIn[1].image = dstImage;\n    imageBarrierIn[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    imageBarrierIn[1].subresourceRange.layerCount = 1;\n    imageBarrierIn[1].subresourceRange.levelCount = 1;\n    imageBarrierIn[1].srcAccessMask = 0;\n    imageBarrierIn[1].dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;\n\n    // Shader\n    VkShaderModuleCreateInfo moduleInfo = {};\n    moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    moduleInfo.codeSize = m_quadShaderSize;\n    moduleInfo.pCode = m_quadShaderCode;\n    VkShaderModule shader;\n    VK_CHECK(vkCreateShaderModule(m_dev, &moduleInfo, nullptr, &shader));\n\n    // Pipeline\n    VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};\n    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n    pipelineLayoutInfo.setLayoutCount = 1;\n    pipelineLayoutInfo.pSetLayouts = &m_descriptorLayout;\n    VkPipelineLayout pipelineLayout;\n    VK_CHECK(vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &pipelineLayout));\n\n    VkPipelineShaderStageCreateInfo stageInfo = {};\n    stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;\n    stageInfo.pName = \"main\";\n    stageInfo.module = shader;\n\n    VkComputePipelineCreateInfo pipelineInfo = {};\n    pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;\n    pipelineInfo.layout = pipelineLayout;\n    pipelineInfo.stage = stageInfo;\n    VkPipeline pipeline;\n    VK_CHECK(vkCreateComputePipelines(m_dev, nullptr, 1, &pipelineInfo, nullptr, &pipeline));\n\n    std::array<VkImageMemoryBarrier, 2> imageBarrierOut;\n    imageBarrierOut[0] = {};\n    imageBarrierOut[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n    imageBarrierOut[0].oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    imageBarrierOut[0].newLayout = imageLayout;\n    imageBarrierOut[0].image = image;\n    imageBarrierOut[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    imageBarrierOut[0].subresourceRange.layerCount = 1;\n    imageBarrierOut[0].subresourceRange.levelCount = 1;\n    imageBarrierOut[0].srcAccessMask = 0;\n    imageBarrierOut[0].dstAccessMask = 0;\n    imageBarrierOut[1] = {};\n    imageBarrierOut[1].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n    imageBarrierOut[1].oldLayout = VK_IMAGE_LAYOUT_GENERAL;\n    imageBarrierOut[1].newLayout = VK_IMAGE_LAYOUT_GENERAL;\n    imageBarrierOut[1].image = dstImage;\n    imageBarrierOut[1].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    imageBarrierOut[1].subresourceRange.layerCount = 1;\n    imageBarrierOut[1].subresourceRange.levelCount = 1;\n    imageBarrierOut[1].srcAccessMask = 0;\n    imageBarrierOut[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;\n\n    std::vector<VkWriteDescriptorSet> descriptorWriteSets;\n\n    VkDescriptorImageInfo descriptorImageInfoIn = {};\n    descriptorImageInfoIn.imageView = imageView;\n    descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\n    VkDescriptorImageInfo descriptorImageInfoOut = {};\n    descriptorImageInfoOut.imageView = dstView;\n    descriptorImageInfoOut.imageLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n    VkWriteDescriptorSet descriptorWriteSet = {};\n    descriptorWriteSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n    descriptorWriteSet.descriptorCount = 1;\n    descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    descriptorWriteSet.pImageInfo = &descriptorImageInfoIn;\n    descriptorWriteSet.dstBinding = 0;\n    descriptorWriteSets.push_back(descriptorWriteSet);\n\n    descriptorWriteSet.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorWriteSet.pImageInfo = &descriptorImageInfoOut;\n    descriptorWriteSet.dstBinding = 1;\n    descriptorWriteSets.push_back(descriptorWriteSet);\n\n    commandBufferBegin();\n    vkCmdPipelineBarrier(\n        m_commandBuffer,\n        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,\n        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,\n        0,\n        0,\n        nullptr,\n        0,\n        nullptr,\n        imageBarrierIn.size(),\n        imageBarrierIn.data()\n    );\n    vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);\n    d.vkCmdPushDescriptorSetKHR(\n        m_commandBuffer,\n        VK_PIPELINE_BIND_POINT_COMPUTE,\n        pipelineLayout,\n        0,\n        descriptorWriteSets.size(),\n        descriptorWriteSets.data()\n    );\n    vkCmdDispatch(\n        m_commandBuffer, (imageInfo.extent.width + 7) / 8, (imageInfo.extent.height + 7) / 8, 1\n    );\n    vkCmdPipelineBarrier(\n        m_commandBuffer,\n        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,\n        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,\n        0,\n        0,\n        nullptr,\n        0,\n        nullptr,\n        imageBarrierOut.size(),\n        imageBarrierOut.data()\n    );\n    commandBufferSubmit();\n\n    VkImageSubresource subresource = {};\n    subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    VkSubresourceLayout layout;\n    vkGetImageSubresourceLayout(m_dev, dstImage, &subresource, &layout);\n\n    const char* imageData;\n    VK_CHECK(vkMapMemory(m_dev, dstMemory, 0, VK_WHOLE_SIZE, 0, (void**)&imageData));\n    imageData += layout.offset;\n\n    std::ofstream file(filename, std::ios::out | std::ios::binary);\n\n    // PPM header\n    file << \"P6\\n\" << width << \"\\n\" << height << \"\\n\" << 255 << \"\\n\";\n\n    // PPM binary pixel data\n    for (uint32_t y = 0; y < height; y++) {\n        uint32_t* row = (uint32_t*)imageData;\n        for (uint32_t x = 0; x < width; x++) {\n            file.write((char*)row++, 3);\n        }\n        imageData += layout.rowPitch;\n    }\n    file.close();\n\n    std::cout << \"Image saved to \\\"\" << filename << \"\\\"\" << std::endl;\n\n    vkUnmapMemory(m_dev, dstMemory);\n    vkFreeMemory(m_dev, dstMemory, nullptr);\n    vkDestroyImage(m_dev, dstImage, nullptr);\n    vkDestroyImageView(m_dev, dstView, nullptr);\n    vkDestroyShaderModule(m_dev, shader, nullptr);\n    vkDestroyPipeline(m_dev, pipeline, nullptr);\n    vkDestroyPipelineLayout(m_dev, pipelineLayout, nullptr);\n}\n\nuint32_t Renderer::memoryTypeIndex(VkMemoryPropertyFlags properties, uint32_t typeBits) const {\n    VkPhysicalDeviceMemoryProperties prop;\n    vkGetPhysicalDeviceMemoryProperties(m_physDev, &prop);\n    for (uint32_t i = 0; i < prop.memoryTypeCount; i++) {\n        if ((prop.memoryTypes[i].propertyFlags & properties) == properties && typeBits & (1 << i)) {\n            return i;\n        }\n    }\n    return 0xFFFFFFFF;\n}\n\n// RenderPipeline\nRenderPipeline::RenderPipeline(Renderer* render)\n    : r(render) { }\n\nRenderPipeline::~RenderPipeline() {\n    vkDestroyShaderModule(r->m_dev, m_shader, nullptr);\n    vkDestroyPipeline(r->m_dev, m_pipeline, nullptr);\n    vkDestroyPipelineLayout(r->m_dev, m_pipelineLayout, nullptr);\n}\n\nvoid RenderPipeline::SetShader(const char* filename) {\n    std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate);\n    if (!is.is_open()) {\n        std::cerr << \"Failed to open shader file: \" << filename << std::endl;\n        return;\n    }\n    size_t size = is.tellg();\n    is.seekg(0, std::ios::beg);\n    std::vector<char> data(size);\n    is.read(data.data(), size);\n    SetShader((unsigned char*)data.data(), size);\n}\n\nvoid RenderPipeline::SetShader(const unsigned char* data, unsigned len) {\n    VkShaderModuleCreateInfo moduleInfo = {};\n    moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    moduleInfo.codeSize = len;\n    moduleInfo.pCode = (uint32_t*)data;\n    VK_CHECK(vkCreateShaderModule(r->m_dev, &moduleInfo, nullptr, &m_shader));\n}\n\nvoid RenderPipeline::Build() {\n    VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};\n    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n    pipelineLayoutInfo.setLayoutCount = 1;\n    pipelineLayoutInfo.pSetLayouts = &r->m_descriptorLayout;\n    VK_CHECK(vkCreatePipelineLayout(r->m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout));\n\n    VkSpecializationInfo specInfo = {};\n    specInfo.mapEntryCount = m_constantEntries.size();\n    specInfo.pMapEntries = m_constantEntries.data();\n    specInfo.dataSize = m_constantSize;\n    specInfo.pData = m_constant;\n\n    VkPipelineShaderStageCreateInfo stageInfo = {};\n    stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;\n    stageInfo.pName = \"main\";\n    stageInfo.module = m_shader;\n    if (m_constant) {\n        stageInfo.pSpecializationInfo = &specInfo;\n    }\n\n    VkComputePipelineCreateInfo pipelineInfo = {};\n    pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;\n    pipelineInfo.layout = m_pipelineLayout;\n    pipelineInfo.stage = stageInfo;\n    VK_CHECK(vkCreateComputePipelines(r->m_dev, nullptr, 1, &pipelineInfo, nullptr, &m_pipeline));\n}\n\nvoid RenderPipeline::Render(VkImageView in, VkImageView out, VkRect2D outSize) {\n    vkCmdBindPipeline(r->m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);\n\n    VkDescriptorImageInfo descriptorImageInfoIn = {};\n    descriptorImageInfoIn.imageView = in;\n    descriptorImageInfoIn.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\n    VkDescriptorImageInfo descriptorImageInfoOut = {};\n    descriptorImageInfoOut.imageView = out;\n    descriptorImageInfoOut.imageLayout = VK_IMAGE_LAYOUT_GENERAL;\n\n    VkWriteDescriptorSet descriptorWriteSets[2] = {};\n    descriptorWriteSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n    descriptorWriteSets[0].descriptorCount = 1;\n    descriptorWriteSets[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    descriptorWriteSets[0].pImageInfo = &descriptorImageInfoIn;\n    descriptorWriteSets[0].dstBinding = 0;\n    descriptorWriteSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n    descriptorWriteSets[1].descriptorCount = 1;\n    descriptorWriteSets[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;\n    descriptorWriteSets[1].pImageInfo = &descriptorImageInfoOut;\n    descriptorWriteSets[1].dstBinding = 1;\n    r->d.vkCmdPushDescriptorSetKHR(\n        r->m_commandBuffer,\n        VK_PIPELINE_BIND_POINT_COMPUTE,\n        m_pipelineLayout,\n        0,\n        2,\n        descriptorWriteSets\n    );\n\n    vkCmdDispatch(\n        r->m_commandBuffer, (outSize.extent.width + 7) / 8, (outSize.extent.height + 7) / 8, 1\n    );\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/Renderer.h",
    "content": "#pragma once\n\n#include <array>\n#include <iostream>\n#include <string>\n#include <vector>\n#include <vulkan/vulkan.h>\n\n#define VK_CHECK(f)                                                                                \\\n    {                                                                                              \\\n        VkResult res = (f);                                                                        \\\n        if (res != VK_SUCCESS) {                                                                   \\\n            std::cerr << Renderer::result_to_str(res) << \"at\" << __FILE__ << \":\" << __LINE__       \\\n                      << std::endl;                                                                \\\n            throw std::runtime_error(                                                              \\\n                \"Vulkan: \" + Renderer::result_to_str(res) + \"at \" __FILE__ \":\"                     \\\n                + std::to_string(__LINE__)                                                         \\\n            );                                                                                     \\\n        }                                                                                          \\\n    }\n\nstruct DrmImage {\n    int fd = -1;\n    uint32_t format = 0;\n    uint64_t modifier = 0;\n    uint32_t planes = 0;\n    std::array<uint32_t, 4> strides;\n    std::array<uint32_t, 4> offsets;\n};\n\nclass RenderPipeline;\n\nclass Renderer {\npublic:\n    enum class ExternalHandle { None, DmaBuf, OpaqueFd };\n\n    struct Output {\n        VkImage image = VK_NULL_HANDLE;\n        VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n        VkImageCreateInfo imageInfo;\n        VkDeviceSize size = 0;\n        VkDeviceMemory memory = VK_NULL_HANDLE;\n        VkSemaphore semaphore = VK_NULL_HANDLE;\n        // ---\n        VkImageView view = VK_NULL_HANDLE;\n        // ---\n        DrmImage drm;\n    };\n\n    struct Timestamps {\n        uint64_t now;\n        uint64_t renderBegin;\n        uint64_t renderComplete;\n    };\n\n    explicit Renderer(\n        const VkInstance& inst,\n        const VkDevice& dev,\n        const VkPhysicalDevice& physDev,\n        uint32_t queueIdx,\n        const std::vector<const char*>& devExtensions\n    );\n    virtual ~Renderer();\n\n    void Startup(uint32_t width, uint32_t height, VkFormat format);\n\n    void AddImage(VkImageCreateInfo imageInfo, size_t memoryIndex, int imageFd, int semaphoreFd);\n\n    void AddPipeline(RenderPipeline* pipeline);\n\n    void CreateOutput(uint32_t width, uint32_t height, ExternalHandle handle);\n    void ImportOutput(const DrmImage& drm);\n\n    void Render(uint32_t index, uint64_t waitValue);\n\n    void Sync();\n\n    Output& GetOutput();\n    Timestamps GetTimestamps();\n\n    void CaptureInputFrame(const std::string& filename);\n    void CaptureOutputFrame(const std::string& filename);\n\n    static std::string result_to_str(VkResult result);\n\n    // private:\n    struct InputImage {\n        VkImage image = VK_NULL_HANDLE;\n        VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n        VkDeviceMemory memory = VK_NULL_HANDLE;\n        VkSemaphore semaphore = VK_NULL_HANDLE;\n        VkImageView view = VK_NULL_HANDLE;\n    };\n\n    struct StagingImage {\n        VkImage image = VK_NULL_HANDLE;\n        VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;\n        VkDeviceMemory memory = VK_NULL_HANDLE;\n        VkImageView view = VK_NULL_HANDLE;\n    };\n\n    void commandBufferBegin();\n    void commandBufferSubmit();\n    void addStagingImage(uint32_t width, uint32_t height);\n    void dumpImage(\n        VkImage image,\n        VkImageView imageView,\n        VkImageLayout imageLayout,\n        uint32_t width,\n        uint32_t height,\n        const std::string& filename\n    );\n    uint32_t memoryTypeIndex(VkMemoryPropertyFlags properties, uint32_t typeBits) const;\n\n    struct {\n        PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;\n        PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR = nullptr;\n        PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR = nullptr;\n        PFN_vkGetImageDrmFormatModifierPropertiesEXT vkGetImageDrmFormatModifierPropertiesEXT\n            = nullptr;\n        PFN_vkGetCalibratedTimestampsEXT vkGetCalibratedTimestampsEXT = nullptr;\n        PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR = nullptr;\n        bool haveDmaBuf = false;\n        bool haveDrmModifiers = false;\n        bool haveCalibratedTimestamps = false;\n    } d;\n\n    Output m_output;\n    std::vector<InputImage> m_images;\n    std::vector<StagingImage> m_stagingImages;\n    std::vector<RenderPipeline*> m_pipelines;\n\n    VkInstance m_inst = VK_NULL_HANDLE;\n    VkDevice m_dev = VK_NULL_HANDLE;\n    VkPhysicalDevice m_physDev = VK_NULL_HANDLE;\n    VkQueue m_queue = VK_NULL_HANDLE;\n    uint32_t m_queueFamilyIndex = 0;\n    VkFormat m_format = VK_FORMAT_UNDEFINED;\n    VkExtent2D m_imageSize = { 0, 0 };\n    VkQueryPool m_queryPool = VK_NULL_HANDLE;\n    VkCommandPool m_commandPool = VK_NULL_HANDLE;\n    VkSampler m_sampler = VK_NULL_HANDLE;\n    VkDescriptorSetLayout m_descriptorLayout = VK_NULL_HANDLE;\n    VkCommandBuffer m_commandBuffer = VK_NULL_HANDLE;\n    VkFence m_fence = VK_NULL_HANDLE;\n    double m_timestampPeriod = 0;\n\n    size_t m_quadShaderSize = 0;\n    const uint32_t* m_quadShaderCode = nullptr;\n\n    std::string m_inputImageCapture;\n    std::string m_outputImageCapture;\n};\n\nclass RenderPipeline {\npublic:\n    explicit RenderPipeline(Renderer* render);\n    virtual ~RenderPipeline();\n\n    void SetShader(const char* filename);\n    void SetShader(const unsigned char* data, unsigned len);\n\n    template <typename T>\n    void SetConstants(const T* data, std::vector<VkSpecializationMapEntry>&& entries) {\n        m_constant = static_cast<const void*>(data);\n        m_constantSize = sizeof(T);\n        m_constantEntries = std::move(entries);\n    }\n\nprivate:\n    void Build();\n    void Render(VkImageView in, VkImageView out, VkRect2D outSize);\n\n    Renderer* r;\n    VkShaderModule m_shader = VK_NULL_HANDLE;\n    const void* m_constant = nullptr;\n    uint32_t m_constantSize = 0;\n    std::vector<VkSpecializationMapEntry> m_constantEntries;\n    VkPipeline m_pipeline = VK_NULL_HANDLE;\n    VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;\n\n    friend class Renderer;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.cpp",
    "content": "#include \"ffmpeg_helper.h\"\n\n#include <chrono>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <unistd.h>\n\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/bindings.h\"\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavfilter/avfilter.h>\n#include <libavutil/avutil.h>\n}\n\nnamespace {\n// it seems that ffmpeg does not provide this mapping\nAVPixelFormat vk_format_to_av_format(vk::Format vk_fmt) {\n    for (int f = AV_PIX_FMT_NONE; f < AV_PIX_FMT_NB; ++f) {\n        auto current_fmt = av_vkfmt_from_pixfmt(AVPixelFormat(f));\n        if (current_fmt and *current_fmt == (VkFormat)vk_fmt)\n            return AVPixelFormat(f);\n    }\n    throw std::runtime_error(\"unsupported vulkan pixel format \" + std::to_string((VkFormat)vk_fmt));\n}\n}\n\nstd::string alvr::AvException::makemsg(const std::string& msg, int averror) {\n    char av_msg[AV_ERROR_MAX_STRING_SIZE];\n    av_strerror(averror, av_msg, sizeof(av_msg));\n    return msg + \" \" + av_msg;\n}\n\nalvr::VkContext::VkContext(\n    const uint8_t* deviceUUID, const std::vector<const char*>& requiredDeviceExtensions\n) {\n    std::vector<const char*> instance_extensions = {\n        VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,\n        VK_KHR_SURFACE_EXTENSION_NAME,\n    };\n\n    std::vector<const char*> device_extensions = {\n        VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,\n        VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,\n        VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,\n        VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,\n        VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,\n        VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,\n        VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,\n        VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,\n        VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME,\n        VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME,\n    };\n    device_extensions.insert(\n        device_extensions.end(), requiredDeviceExtensions.begin(), requiredDeviceExtensions.end()\n    );\n\n    uint32_t instanceExtensionCount = 0;\n    vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr);\n    std::vector<VkExtensionProperties> instanceExts(instanceExtensionCount);\n    vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, instanceExts.data());\n    for (const char* name : instance_extensions) {\n        auto it = std::find_if(\n            instanceExts.begin(),\n            instanceExts.end(),\n            [name](VkExtensionProperties e) { return strcmp(e.extensionName, name) == 0; }\n        );\n        if (it != instanceExts.end()) {\n            instanceExtensions.push_back(name);\n        }\n    }\n\n    VkApplicationInfo appInfo = {};\n    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n    appInfo.pApplicationName = \"ALVR\";\n    appInfo.apiVersion = VK_API_VERSION_1_2;\n\n    VkInstanceCreateInfo instanceInfo = {};\n    instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n    instanceInfo.pApplicationInfo = &appInfo;\n\n#ifdef DEBUG\n    const char* validationLayers[] = { \"VK_LAYER_KHRONOS_validation\" };\n    instanceInfo.ppEnabledLayerNames = validationLayers;\n    instanceInfo.enabledLayerCount = 1;\n#endif\n\n    instanceInfo.enabledExtensionCount = instanceExtensions.size();\n    instanceInfo.ppEnabledExtensionNames = instanceExtensions.data();\n    VK_CHECK(vkCreateInstance(&instanceInfo, nullptr, &instance));\n\n    uint32_t deviceCount = 0;\n    VK_CHECK(vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr));\n    std::vector<VkPhysicalDevice> physicalDevices(deviceCount);\n    VK_CHECK(vkEnumeratePhysicalDevices(instance, &deviceCount, physicalDevices.data()));\n    for (VkPhysicalDevice dev : physicalDevices) {\n        VkPhysicalDeviceVulkan11Properties props11 = {};\n        props11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;\n\n        VkPhysicalDeviceProperties2 props = {};\n        props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;\n        props.pNext = &props11;\n        vkGetPhysicalDeviceProperties2(dev, &props);\n        if (memcmp(props11.deviceUUID, deviceUUID, VK_UUID_SIZE) == 0) {\n            physicalDevice = dev;\n            break;\n        }\n    }\n    if (!physicalDevice && !physicalDevices.empty()) {\n        Warn(\"Falling back to first device\");\n        physicalDevice = physicalDevices[0];\n    }\n    if (!physicalDevice) {\n        throw std::runtime_error(\"Failed to find vulkan device.\");\n    }\n\n    VkPhysicalDeviceDrmPropertiesEXT drmProps = {};\n    drmProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT;\n\n    VkPhysicalDeviceProperties2 deviceProps = {};\n    deviceProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;\n    deviceProps.pNext = &drmProps;\n    vkGetPhysicalDeviceProperties2(physicalDevice, &deviceProps);\n\n    amd = deviceProps.properties.vendorID == 0x1002;\n    intel = deviceProps.properties.vendorID == 0x8086;\n    nvidia = deviceProps.properties.vendorID == 0x10de;\n    Info(\"Using Vulkan device %s\", deviceProps.properties.deviceName);\n\n    uint32_t deviceExtensionCount = 0;\n    VK_CHECK(vkEnumerateDeviceExtensionProperties(\n        physicalDevice, nullptr, &deviceExtensionCount, nullptr\n    ));\n    std::vector<VkExtensionProperties> deviceExts(deviceExtensionCount);\n    VK_CHECK(vkEnumerateDeviceExtensionProperties(\n        physicalDevice, nullptr, &deviceExtensionCount, deviceExts.data()\n    ));\n    for (const char* name : device_extensions) {\n        auto it\n            = std::find_if(deviceExts.begin(), deviceExts.end(), [name](VkExtensionProperties e) {\n                  return strcmp(e.extensionName, name) == 0;\n              });\n        if (it != deviceExts.end()) {\n            deviceExtensions.push_back(name);\n        }\n    }\n\n    float queuePriority = 1.0;\n    std::vector<VkDeviceQueueCreateInfo> queueInfos;\n\n    uint32_t queueFamilyCount;\n    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);\n    std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);\n    vkGetPhysicalDeviceQueueFamilyProperties(\n        physicalDevice, &queueFamilyCount, queueFamilyProperties.data()\n    );\n    for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i) {\n        const bool graphics = queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;\n        const bool compute = queueFamilyProperties[i].queueFlags & VK_QUEUE_COMPUTE_BIT;\n        if (compute && (queueFamilyIndex == VK_QUEUE_FAMILY_IGNORED || !graphics)) {\n            queueFamilyIndex = i;\n        }\n        VkDeviceQueueCreateInfo queueInfo = {};\n        queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n        queueInfo.queueFamilyIndex = i;\n        queueInfo.queueCount = 1;\n        queueInfo.pQueuePriorities = &queuePriority;\n        queueInfos.push_back(queueInfo);\n    }\n\n    VkPhysicalDeviceVulkan12Features features12 = {};\n    features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;\n    features12.timelineSemaphore = true;\n\n    VkPhysicalDeviceFeatures2 features = {};\n    features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;\n    features.pNext = &features12;\n    features.features.samplerAnisotropy = VK_TRUE;\n\n    VkDeviceCreateInfo deviceInfo = {};\n    deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;\n    deviceInfo.pNext = &features;\n    deviceInfo.queueCreateInfoCount = queueInfos.size();\n    deviceInfo.pQueueCreateInfos = queueInfos.data();\n    deviceInfo.enabledExtensionCount = deviceExtensions.size();\n    deviceInfo.ppEnabledExtensionNames = deviceExtensions.data();\n    VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device));\n\n    for (int i = 128; i < 136; ++i) {\n        auto path = \"/dev/dri/renderD\" + std::to_string(i);\n        int fd = open(path.c_str(), O_RDONLY);\n        if (fd == -1) {\n            continue;\n        }\n        struct stat s = {};\n        int ret = fstat(fd, &s);\n        close(fd);\n        if (ret != 0) {\n            continue;\n        }\n        dev_t primaryDev = makedev(drmProps.primaryMajor, drmProps.primaryMinor);\n        dev_t renderDev = makedev(drmProps.renderMajor, drmProps.renderMinor);\n        if (primaryDev == s.st_rdev || renderDev == s.st_rdev) {\n            devicePath = path;\n            break;\n        }\n    }\n    if (devicePath.empty()) {\n        devicePath = \"/dev/dri/renderD128\";\n    }\n    Info(\"Using device path %s\", devicePath.c_str());\n\n    ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN);\n    AVHWDeviceContext* hwctx = (AVHWDeviceContext*)ctx->data;\n    AVVulkanDeviceContext* vkctx = (AVVulkanDeviceContext*)hwctx->hwctx;\n\n    vkctx->alloc = nullptr;\n    vkctx->inst = instance;\n    vkctx->phys_dev = physicalDevice;\n    vkctx->act_dev = device;\n    vkctx->device_features = features;\n    vkctx->queue_family_index = queueFamilyIndex;\n    vkctx->nb_graphics_queues = 1;\n    vkctx->queue_family_tx_index = queueFamilyIndex;\n    vkctx->nb_tx_queues = 1;\n    vkctx->queue_family_comp_index = queueFamilyIndex;\n    vkctx->nb_comp_queues = 1;\n    vkctx->get_proc_addr = vkGetInstanceProcAddr;\n    vkctx->queue_family_encode_index = -1;\n    vkctx->nb_encode_queues = 0;\n    vkctx->queue_family_decode_index = -1;\n    vkctx->nb_decode_queues = 0;\n\n    char** inst_extensions = (char**)malloc(sizeof(char*) * instanceExtensions.size());\n    for (uint32_t i = 0; i < instanceExtensions.size(); ++i) {\n        inst_extensions[i] = strdup(instanceExtensions[i]);\n    }\n    vkctx->enabled_inst_extensions = inst_extensions;\n    vkctx->nb_enabled_inst_extensions = instanceExtensions.size();\n\n    char** dev_extensions = (char**)malloc(sizeof(char*) * deviceExtensions.size());\n    for (uint32_t i = 0; i < deviceExtensions.size(); ++i) {\n        dev_extensions[i] = strdup(deviceExtensions[i]);\n    }\n    vkctx->enabled_dev_extensions = dev_extensions;\n    vkctx->nb_enabled_dev_extensions = deviceExtensions.size();\n\n    int ret = av_hwdevice_ctx_init(ctx);\n    if (ret)\n        throw AvException(\"failed to initialize ffmpeg\", ret);\n}\n\nalvr::VkContext::~VkContext() {\n    av_buffer_unref(&ctx);\n    vkDestroyDevice(device, nullptr);\n    vkDestroyInstance(instance, nullptr);\n}\n\nalvr::VkFrameCtx::VkFrameCtx(VkContext& vkContext, vk::ImageCreateInfo image_create_info) {\n    AVHWFramesContext* frames_ctx = NULL;\n    int err = 0;\n\n    if (!(ctx = av_hwframe_ctx_alloc(vkContext.ctx))) {\n        throw std::runtime_error(\"Failed to create vulkan frame context.\");\n    }\n    frames_ctx = (AVHWFramesContext*)(ctx->data);\n    frames_ctx->format = AV_PIX_FMT_VULKAN;\n    frames_ctx->sw_format = vk_format_to_av_format(image_create_info.format);\n    frames_ctx->width = image_create_info.extent.width;\n    frames_ctx->height = image_create_info.extent.height;\n    frames_ctx->initial_pool_size = 0;\n    if ((err = av_hwframe_ctx_init(ctx)) < 0) {\n        av_buffer_unref(&ctx);\n        throw alvr::AvException(\"Failed to initialize vulkan frame context:\", err);\n    }\n}\n\nalvr::VkFrameCtx::~VkFrameCtx() { av_buffer_unref(&ctx); }\n\nalvr::VkFrame::VkFrame(\n    const VkContext& vk_ctx,\n    VkImage image,\n    VkImageCreateInfo image_info,\n    VkDeviceSize size,\n    VkDeviceMemory memory,\n    DrmImage drm\n)\n    : vkimage(image)\n    , vkimageinfo(image_info) {\n    device = vk_ctx.get_vk_device();\n    avformat = vk_format_to_av_format(vk::Format(image_info.format));\n\n    av_drmframe = (AVDRMFrameDescriptor*)malloc(sizeof(AVDRMFrameDescriptor));\n    av_drmframe->nb_objects = 1;\n    av_drmframe->objects[0].fd = drm.fd;\n    av_drmframe->objects[0].size = size;\n    av_drmframe->objects[0].format_modifier = drm.modifier;\n    av_drmframe->nb_layers = 1;\n    av_drmframe->layers[0].format = drm.format;\n    av_drmframe->layers[0].nb_planes = drm.planes;\n    for (uint32_t i = 0; i < drm.planes; ++i) {\n        av_drmframe->layers[0].planes[i].object_index = 0;\n        av_drmframe->layers[0].planes[i].pitch = drm.strides[i];\n        av_drmframe->layers[0].planes[i].offset = drm.offsets[i];\n    }\n\n    av_vkframe = av_vk_frame_alloc();\n    av_vkframe->img[0] = image;\n    av_vkframe->tiling = image_info.tiling;\n    av_vkframe->mem[0] = memory;\n    av_vkframe->size[0] = size;\n    av_vkframe->layout[0] = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    VkExportSemaphoreCreateInfo exportInfo = {};\n    exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;\n    exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    VkSemaphoreTypeCreateInfo timelineInfo = {};\n    timelineInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;\n    timelineInfo.pNext = &exportInfo;\n    timelineInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n\n    VkSemaphoreCreateInfo semInfo = {};\n    semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n    semInfo.pNext = &timelineInfo;\n    vkCreateSemaphore(device, &semInfo, nullptr, &av_vkframe->sem[0]);\n}\n\nalvr::VkFrame::~VkFrame() {\n    free(av_drmframe);\n    if (av_vkframe) {\n        vkDestroySemaphore(device, av_vkframe->sem[0], nullptr);\n        av_free(av_vkframe);\n    }\n}\n\nstd::unique_ptr<AVFrame, std::function<void(AVFrame*)>>\nalvr::VkFrame::make_av_frame(VkFrameCtx& frame_ctx) {\n    std::unique_ptr<AVFrame, std::function<void(AVFrame*)>> frame {\n        av_frame_alloc(), [](AVFrame* p) { av_frame_free(&p); }\n    };\n    frame->width = vkimageinfo.extent.width;\n    frame->height = vkimageinfo.extent.height;\n    frame->hw_frames_ctx = av_buffer_ref(frame_ctx.ctx);\n    frame->data[0] = (uint8_t*)av_vkframe;\n    frame->format = AV_PIX_FMT_VULKAN;\n    frame->buf[0] = av_buffer_alloc(1);\n    frame->pts = std::chrono::steady_clock::now().time_since_epoch().count();\n\n    return frame;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/ffmpeg_helper.h",
    "content": "#pragma once\n\n#include <functional>\n#include <memory>\n#include <vulkan/vulkan.hpp>\n\nextern \"C\" {\n#include <stdint.h>\n\n#include <libavcodec/avcodec.h>\n\n#include <libavfilter/avfilter.h>\n#include <libavfilter/buffersink.h>\n#include <libavfilter/buffersrc.h>\n\n#include <libavutil/avutil.h>\n#include <libavutil/dict.h>\n#include <libavutil/hwcontext.h>\n#include <libavutil/hwcontext_drm.h>\n#include <libavutil/hwcontext_vulkan.h>\n#include <libavutil/opt.h>\n}\n\n#include \"Renderer.h\"\n\nnamespace alvr {\n\n// Utility class to build an exception from an ffmpeg return code.\n// Messages are rarely useful however.\nclass AvException : public std::runtime_error {\npublic:\n    AvException(std::string msg, int averror)\n        : std::runtime_error { makemsg(msg, averror) } { }\n\nprivate:\n    static std::string makemsg(const std::string& msg, int averror);\n};\n\nclass VkContext {\npublic:\n    VkContext(const uint8_t* deviceUUID, const std::vector<const char*>& requiredDeviceExtensions);\n    ~VkContext();\n    VkDevice get_vk_device() const { return device; }\n    VkInstance get_vk_instance() const { return instance; }\n    VkPhysicalDevice get_vk_phys_device() const { return physicalDevice; }\n    uint32_t get_vk_queue_family_index() const { return queueFamilyIndex; }\n    std::vector<const char*> get_vk_instance_extensions() const { return instanceExtensions; }\n    std::vector<const char*> get_vk_device_extensions() const { return deviceExtensions; }\n\n    AVBufferRef* ctx = nullptr;\n    VkInstance instance = VK_NULL_HANDLE;\n    VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;\n    VkDevice device = VK_NULL_HANDLE;\n    uint32_t queueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n    std::vector<const char*> instanceExtensions;\n    std::vector<const char*> deviceExtensions;\n    bool amd = false;\n    bool intel = false;\n    bool nvidia = false;\n    std::string devicePath;\n};\n\nclass VkFrameCtx {\npublic:\n    VkFrameCtx(VkContext& vkContext, vk::ImageCreateInfo image_create_info);\n    ~VkFrameCtx();\n\n    AVBufferRef* ctx;\n};\n\nclass VkFrame {\npublic:\n    VkFrame(\n        const VkContext& vk_ctx,\n        VkImage image,\n        VkImageCreateInfo image_info,\n        VkDeviceSize size,\n        VkDeviceMemory memory,\n        DrmImage drm\n    );\n    ~VkFrame();\n    VkImage image() { return vkimage; }\n    VkImageCreateInfo imageInfo() { return vkimageinfo; }\n    VkFormat format() { return vkimageinfo.format; }\n    AVPixelFormat avFormat() { return avformat; }\n    operator AVVkFrame*() const { return av_vkframe; }\n    operator AVDRMFrameDescriptor*() const { return av_drmframe; }\n    std::unique_ptr<AVFrame, std::function<void(AVFrame*)>> make_av_frame(VkFrameCtx& frame_ctx);\n\nprivate:\n    AVVkFrame* av_vkframe = nullptr;\n    AVDRMFrameDescriptor* av_drmframe = nullptr;\n    vk::Device device;\n    VkImage vkimage;\n    VkImageCreateInfo vkimageinfo;\n    AVPixelFormat avformat;\n};\n\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/protocol.h",
    "content": "#pragma once\n\n#include <array>\n#include <atomic>\n#include <condition_variable>\n#include <cstdint>\n#include <cstdlib>\n#include <mutex>\n#include <vulkan/vulkan.h>\n\nstruct present_packet {\n    uint32_t image;\n    uint32_t frame;\n    uint64_t semaphore_value;\n    float pose[3][4];\n};\n\nstruct init_packet {\n    uint32_t num_images;\n    std::array<uint8_t, VK_UUID_SIZE> device_uuid;\n    VkImageCreateInfo image_create_info;\n    size_t mem_index;\n    pid_t source_pid;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/shader/color.comp",
    "content": "#version 450\n\nlayout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\nlayout (binding = 0) uniform sampler2D in_img;\nlayout (binding = 1, rgba8) uniform writeonly image2D out_img;\n\nlayout (constant_id = 0) const float renderWidth = 0.;\nlayout (constant_id = 1) const float renderHeight = 0.;\nlayout (constant_id = 2) const float brightness = 0.;\nlayout (constant_id = 3) const float contrast = 0.;\nlayout (constant_id = 4) const float saturation = 0.;\nlayout (constant_id = 5) const float gamma = 0.;\nlayout (constant_id = 6) const float sharpening = 0.;\n\nvec3 GetSharpenNeighborComponent(vec2 uv, float xoff, float yoff)\n{\n    const float sharpenNeighbourWeight = -sharpening / 8.;\n    return texture(in_img, uv + vec2(xoff, yoff)).rgb * sharpenNeighbourWeight;\n}\n\nvec3 blendLighten(vec3 base, vec3 blend)\n{\n    return vec3(max(base.r, blend.r), max(base.g, blend.g), max(base.b, blend.b));\n}\n\n// https://forum.unity.com/threads/hue-saturation-brightness-contrast-shader.260649/\nvoid main()\n{\n    ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n    vec2 uv = (vec2(pos) + 0.5f) / imageSize(out_img);\n\n    const float DX = 1. / renderWidth;\n    const float DY = 1. / renderHeight;\n\n    // sharpening\n    vec3 pixel = texture(in_img, uv).rgb * (sharpening + 1.);\n    pixel += GetSharpenNeighborComponent(uv, -DX, -DY);\n    pixel += GetSharpenNeighborComponent(uv, 0, -DY);\n    pixel += GetSharpenNeighborComponent(uv, +DX, -DY);\n    pixel += GetSharpenNeighborComponent(uv, +DX, 0);\n    pixel += GetSharpenNeighborComponent(uv, +DX, +DY);\n    pixel += GetSharpenNeighborComponent(uv, 0, +DY);\n    pixel += GetSharpenNeighborComponent(uv, -DX, +DY);\n    pixel += GetSharpenNeighborComponent(uv, -DX, 0);\n\n    pixel += brightness; // brightness\n    pixel = (pixel - 0.5) * contrast + 0.5f; // contast\n    pixel = blendLighten(mix(vec3(dot(pixel, vec3(0.299, 0.587, 0.114))), pixel, vec3(saturation)), pixel); // saturation + lighten only\n\n    pixel = clamp(pixel, 0., 1.);\n    pixel = pow(pixel, vec3(1. / gamma)); // gamma\n\n    imageStore(out_img, pos, vec4(pixel, 1.));\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/shader/ffr.comp",
    "content": "#version 450\n\nlayout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\nlayout (binding = 0) uniform sampler2D in_img;\nlayout (binding = 1, rgba8) uniform writeonly image2D out_img;\n\nlayout (constant_id = 0) const float eyeSizeRatioX = 0.;\nlayout (constant_id = 1) const float eyeSizeRatioY = 0.;\nlayout (constant_id = 2) const float centerSizeX = 0.;\nlayout (constant_id = 3) const float centerSizeY = 0.;\nlayout (constant_id = 4) const float centerShiftX = 0.;\nlayout (constant_id = 5) const float centerShiftY = 0.;\nlayout (constant_id = 6) const float edgeRatioX = 0.;\nlayout (constant_id = 7) const float edgeRatioY = 0.;\n\nconst vec2 eyeSizeRatio = vec2(eyeSizeRatioX, eyeSizeRatioY);\nconst vec2 centerSize = vec2(centerSizeX, centerSizeY);\nconst vec2 centerShift = vec2(centerShiftX, centerShiftY);\nconst vec2 edgeRatio = vec2(edgeRatioX, edgeRatioY);\n\nvec2 TextureToEyeUV(vec2 textureUV, bool isRightEye)\n{\n    // flip distortion horizontally for right eye\n    // left: x * 2; right: (1 - x) * 2\n    return vec2((textureUV.x + float(isRightEye) * (1. - 2. * textureUV.x)) * 2., textureUV.y);\n}\n\nvec2 EyeToTextureUV(vec2 eyeUV, bool isRightEye)\n{\n    // saturate is used to avoid color bleeding between the two sides of the texture or with the\n    // black border when filtering\n    // vec2 clampedUV = saturate(eyeUV);\n    // left: x / 2; right 1 - (x / 2)\n    // return vec2(clampedUV.x / 2. + float(isRightEye) * (1. - clampedUV.x), clampedUV.y);\n    return vec2(eyeUV.x * .5 + float(isRightEye) * (1. - eyeUV.x), eyeUV.y);\n}\n\nvoid main()\n{\n    ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n    vec2 uv = (vec2(pos) + 0.5f) / imageSize(out_img);\n\n    bool isRightEye = uv.x > 0.5;\n    vec2 eyeUV = TextureToEyeUV(uv, isRightEye) / eyeSizeRatio;\n\n    vec2 c0 = (1. - centerSize) * .5;\n    vec2 c1 = (edgeRatio - 1.) * c0 * (centerShift + 1.) / edgeRatio;\n    vec2 c2 = (edgeRatio - 1.) * centerSize + 1.;\n\n    vec2 loBound = c0 * (centerShift + 1.) / c2;\n    vec2 hiBound = c0 * (centerShift - 1.) / c2 + 1.;\n    vec2 underBound = vec2(eyeUV.x < loBound.x, eyeUV.y < loBound.y);\n    vec2 inBound = vec2(loBound.x < eyeUV.x && eyeUV.x < hiBound.x,\n                        loBound.y < eyeUV.y && eyeUV.y < hiBound.y);\n    vec2 overBound = vec2(eyeUV.x > hiBound.x, eyeUV.y > hiBound.y);\n\n    vec2 center = eyeUV * c2 / edgeRatio + c1;\n\n    vec2 d2 = eyeUV * c2;\n    vec2 d3 = (eyeUV - 1.) * c2 + 1.;\n    vec2 g1 = eyeUV / loBound;\n    vec2 g2 = (1. - eyeUV) / (1. - hiBound);\n\n    vec2 leftEdge = g1 * center + (1. - g1) * d2;\n    vec2 rightEdge = g2 * center + (1. - g2) * d3;\n\n    vec2 compressedUV = underBound * leftEdge + inBound * center + overBound * rightEdge;\n\n    imageStore(out_img, pos, texture(in_img, EyeToTextureUV(compressedUV, isRightEye)));\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/shader/quad.comp",
    "content": "#version 450\n\nlayout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\nlayout (binding = 0) uniform sampler2D in_img;\nlayout (binding = 1, rgba8) uniform writeonly image2D out_img;\n\nvoid main()\n{\n    ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n    vec2 npos = (vec2(pos) + 0.5f) / imageSize(out_img);\n    vec4 res = texture(in_img, npos);\n    imageStore(out_img, pos, res);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/linux/shader/rgbtoyuv420.comp",
    "content": "#version 450\n\nlayout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\nlayout (binding = 0, rgba8) uniform readonly image2D in_img;\nlayout (binding = 1, r8) uniform writeonly image2D out_img[3];\n\n/* FFmpeg/libavfilter/vf_scale_vulkan.c */\n\nvoid main()\n{\n    const mat4 yuv_matrix = mat4(\n        0.0, 1.0, 0.0, 0.0,\n        0.0, -0.5, 0.5, 0.0,\n        0.5, -0.5, 0, 0.0,\n        0.0, 0.0, 0.0, 1.0\n    );\n\n    ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n    vec4 res = imageLoad(in_img, pos);\n\n    res *= yuv_matrix;\n    res *= vec4(219.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0, 1.0);\n    res += vec4(16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 0.0);\n\n    imageStore(out_img[0], pos, vec4(res.r, 0.0, 0.0, 0.0));\n    pos /= ivec2(2);\n    imageStore(out_img[1], pos, vec4(res.g, 0.0, 0.0, 0.0));\n    imageStore(out_img[2], pos, vec4(res.b, 0.0, 0.0, 0.0));\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/macos/CEncoder.h",
    "content": "#pragma once\n\n#include \"shared/threadtools.h\"\n\nclass CEncoder : public CThread {\npublic:\n    CEncoder() { }\n    ~CEncoder() { }\n    bool Init() override { return true; }\n    void Run() override { }\n\n    void Stop() { }\n    void OnStreamStart() { }\n    void InsertIDR() { }\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/macos/CrashHandler.cpp",
    "content": "#include \"../../alvr_server/bindings.h\"\n\nvoid HookCrashHandler() { }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/CEncoder.cpp",
    "content": "#include \"CEncoder.h\"\n\nCEncoder::CEncoder()\n    : m_bExiting(false)\n    , m_targetTimestampNs(0) {\n    m_encodeFinished.Set();\n}\n\nCEncoder::~CEncoder() {\n    if (m_videoEncoder) {\n        m_videoEncoder->Shutdown();\n        m_videoEncoder.reset();\n    }\n}\n\nvoid CEncoder::Initialize(std::shared_ptr<CD3DRender> d3dRender) {\n    m_FrameRender = std::make_shared<FrameRender>(d3dRender);\n    m_FrameRender->Startup();\n    uint32_t encoderWidth, encoderHeight;\n    m_FrameRender->GetEncodingResolution(&encoderWidth, &encoderHeight);\n\n    Exception vplException;\n    Exception vceException;\n    Exception nvencException;\n#ifdef ALVR_GPL\n    Exception swException;\n\n    if (Settings::Instance().m_force_sw_encoding) {\n        try {\n            Debug(\"Try to use VideoEncoderSW.\\n\");\n            m_videoEncoder\n                = std::make_shared<VideoEncoderSW>(d3dRender, encoderWidth, encoderHeight);\n            m_videoEncoder->Initialize();\n            return;\n        } catch (Exception e) {\n            swException = e;\n        }\n    }\n#endif\n\n    try {\n        Debug(\"Try to use VideoEncoderAMF.\\n\");\n        m_videoEncoder = std::make_shared<VideoEncoderAMF>(d3dRender, encoderWidth, encoderHeight);\n        m_videoEncoder->Initialize();\n        return;\n    } catch (Exception e) {\n        vceException = e;\n    }\n    try {\n        Debug(\"Try to use VideoEncoderNVENC.\\n\");\n        m_videoEncoder\n            = std::make_shared<VideoEncoderNVENC>(d3dRender, encoderWidth, encoderHeight);\n        m_videoEncoder->Initialize();\n        return;\n    } catch (Exception e) {\n        nvencException = e;\n    }\n    try {\n        Debug(\"Try to use VideoEncoderVPL.\\n\");\n        m_videoEncoder = std::make_shared<VideoEncoderVPL>(d3dRender, encoderWidth, encoderHeight);\n        m_videoEncoder->Initialize();\n        return;\n    } catch (Exception e) {\n        vplException = e;\n    }\n#ifdef ALVR_GPL\n    try {\n        Debug(\"Try to use VideoEncoderSW.\\n\");\n        m_videoEncoder = std::make_shared<VideoEncoderSW>(d3dRender, encoderWidth, encoderHeight);\n        m_videoEncoder->Initialize();\n        return;\n    } catch (Exception e) {\n        swException = e;\n    }\n    throw MakeException(\n        \"All VideoEncoder are not available. VCE: %s, NVENC: %s, VPL: %s, SW: %s\",\n        vceException.what(),\n        nvencException.what(),\n        vplException.what(),\n        swException.what()\n    );\n#else\n    throw MakeException(\n        \"All VideoEncoder are not available. VCE: %s, NVENC: %s, VPL: %s\",\n        vceException.what(),\n        nvencException.what(),\n        vplException.what()\n    );\n#endif\n}\n\nvoid CEncoder::SetViewParams(\n    vr::HmdRect2_t projLeft,\n    vr::HmdMatrix34_t eyeToHeadLeft,\n    vr::HmdRect2_t projRight,\n    vr::HmdMatrix34_t eyeToHeadRight\n) {\n    m_FrameRender->SetViewParams(projLeft, eyeToHeadLeft, projRight, eyeToHeadRight);\n}\n\nbool CEncoder::CopyToStaging(\n    ID3D11Texture2D* pTexture[][2],\n    vr::VRTextureBounds_t bounds[][2],\n    vr::HmdMatrix34_t poses[],\n    int layerCount,\n    bool recentering,\n    uint64_t presentationTime,\n    uint64_t targetTimestampNs,\n    const std::string& message,\n    const std::string& debugText\n) {\n    m_presentationTime = presentationTime;\n    m_targetTimestampNs = targetTimestampNs;\n    m_FrameRender->Startup();\n\n    m_FrameRender->RenderFrame(\n        pTexture, bounds, poses, layerCount, recentering, message, debugText\n    );\n    return true;\n}\n\nvoid CEncoder::Run() {\n    Debug(\"CEncoder: Start thread. Id=%d\\n\", GetCurrentThreadId());\n    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_MOST_URGENT);\n\n    while (!m_bExiting) {\n        m_newFrameReady.Wait();\n        if (m_bExiting)\n            break;\n\n        if (m_FrameRender->GetTexture()) {\n            m_videoEncoder->Transmit(\n                m_FrameRender->GetTexture().Get(),\n                m_presentationTime,\n                m_targetTimestampNs,\n                m_scheduler.CheckIDRInsertion()\n            );\n        }\n\n        m_encodeFinished.Set();\n    }\n}\n\nvoid CEncoder::Stop() {\n    m_bExiting = true;\n    m_newFrameReady.Set();\n    Join();\n    m_FrameRender.reset();\n}\n\nvoid CEncoder::NewFrameReady() {\n    m_encodeFinished.Reset();\n    m_newFrameReady.Set();\n}\n\nvoid CEncoder::WaitForEncode() { m_encodeFinished.Wait(); }\n\nvoid CEncoder::OnStreamStart() { m_scheduler.OnStreamStart(); }\n\nvoid CEncoder::InsertIDR() { m_scheduler.InsertIDR(); }\n\nvoid CEncoder::CaptureFrame() { }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/CEncoder.h",
    "content": "#pragma once\n#include \"shared/d3drender.h\"\n\n#include \"shared/threadtools.h\"\n\n#include \"FrameRender.h\"\n#include \"VideoEncoder.h\"\n#include \"VideoEncoderAMF.h\"\n#include \"VideoEncoderNVENC.h\"\n#include \"VideoEncoderVPL.h\"\n#include \"alvr_server/Utils.h\"\n#include <d3d11.h>\n#include <d3d11_1.h>\n#include <map>\n#include <wincodec.h>\n#include <wincodecsdk.h>\n#include <wrl.h>\n#ifdef ALVR_GPL\n#include \"VideoEncoderSW.h\"\n#endif\n#include \"alvr_server/IDRScheduler.h\"\n\nusing Microsoft::WRL::ComPtr;\n\n//----------------------------------------------------------------------------\n// Blocks on reading backbuffer from gpu, so WaitForPresent can return\n// as soon as we know rendering made it this frame.  This step of the pipeline\n// should run about 3ms per frame.\n//----------------------------------------------------------------------------\nclass CEncoder : public CThread {\npublic:\n    CEncoder();\n    ~CEncoder();\n\n    void Initialize(std::shared_ptr<CD3DRender> d3dRender);\n\n    void SetViewParams(\n        vr::HmdRect2_t projLeft,\n        vr::HmdMatrix34_t eyeToHeadLeft,\n        vr::HmdRect2_t projRight,\n        vr::HmdMatrix34_t eyeToHeadRight\n    );\n\n    bool CopyToStaging(\n        ID3D11Texture2D* pTexture[][2],\n        vr::VRTextureBounds_t bounds[][2],\n        vr::HmdMatrix34_t poses[],\n        int layerCount,\n        bool recentering,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        const std::string& message,\n        const std::string& debugText\n    );\n\n    virtual void Run();\n\n    virtual void Stop();\n\n    void NewFrameReady();\n\n    void WaitForEncode();\n\n    void OnStreamStart();\n\n    void InsertIDR();\n\n    void CaptureFrame();\n\nprivate:\n    CThreadEvent m_newFrameReady, m_encodeFinished;\n    std::shared_ptr<VideoEncoder> m_videoEncoder;\n    bool m_bExiting;\n    uint64_t m_presentationTime;\n    uint64_t m_targetTimestampNs;\n\n    std::shared_ptr<FrameRender> m_FrameRender;\n\n    IDRScheduler m_scheduler;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/CrashHandler.cpp",
    "content": "#include \"../../alvr_server/bindings.h\"\n\n#include \"../../alvr_server/Logger.h\"\n#include \"../../shared/backward.hpp\"\n#include <ostream>\n#include <windows.h>\n\nstatic LONG WINAPI handler(PEXCEPTION_POINTERS ptrs) {\n    backward::StackTrace stacktrace;\n    backward::Printer printer;\n    std::ostringstream stream;\n\n    stacktrace.load_from(ptrs->ExceptionRecord->ExceptionAddress);\n    printer.print(stacktrace, stream);\n    std::string str = stream.str();\n    Error(\"Unhandled exception: %X\\n%s\", ptrs->ExceptionRecord->ExceptionCode, str.c_str());\n\n    Sleep(2000);\n\n    return EXCEPTION_EXECUTE_HANDLER;\n}\n\nvoid HookCrashHandler() { SetUnhandledExceptionFilter(handler); }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/FFR.cpp",
    "content": "#include \"FFR.h\"\n\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/Utils.h\"\n#include \"alvr_server/bindings.h\"\n\nusing Microsoft::WRL::ComPtr;\nusing namespace d3d_render_utils;\n\nnamespace {\n\nstruct FoveationVars {\n    uint32_t targetEyeWidth;\n    uint32_t targetEyeHeight;\n    uint32_t optimizedEyeWidth;\n    uint32_t optimizedEyeHeight;\n\n    float eyeWidthRatio;\n    float eyeHeightRatio;\n\n    float centerSizeX;\n    float centerSizeY;\n    float centerShiftX;\n    float centerShiftY;\n    float edgeRatioX;\n    float edgeRatioY;\n};\n\nFoveationVars CalculateFoveationVars() {\n    float targetEyeWidth = (float)Settings::Instance().m_renderWidth / 2;\n    float targetEyeHeight = (float)Settings::Instance().m_renderHeight;\n\n    float centerSizeX = (float)Settings::Instance().m_foveationCenterSizeX;\n    float centerSizeY = (float)Settings::Instance().m_foveationCenterSizeY;\n    float centerShiftX = (float)Settings::Instance().m_foveationCenterShiftX;\n    float centerShiftY = (float)Settings::Instance().m_foveationCenterShiftY;\n    float edgeRatioX = (float)Settings::Instance().m_foveationEdgeRatioX;\n    float edgeRatioY = (float)Settings::Instance().m_foveationEdgeRatioY;\n\n    float edgeSizeX = targetEyeWidth - centerSizeX * targetEyeWidth;\n    float edgeSizeY = targetEyeHeight - centerSizeY * targetEyeHeight;\n\n    float centerSizeXAligned\n        = 1. - ceil(edgeSizeX / (edgeRatioX * 2.)) * (edgeRatioX * 2.) / targetEyeWidth;\n    float centerSizeYAligned\n        = 1. - ceil(edgeSizeY / (edgeRatioY * 2.)) * (edgeRatioY * 2.) / targetEyeHeight;\n\n    float edgeSizeXAligned = targetEyeWidth - centerSizeXAligned * targetEyeWidth;\n    float edgeSizeYAligned = targetEyeHeight - centerSizeYAligned * targetEyeHeight;\n\n    float centerShiftXAligned = ceil(centerShiftX * edgeSizeXAligned / (edgeRatioX * 2.))\n        * (edgeRatioX * 2.) / edgeSizeXAligned;\n    float centerShiftYAligned = ceil(centerShiftY * edgeSizeYAligned / (edgeRatioY * 2.))\n        * (edgeRatioY * 2.) / edgeSizeYAligned;\n\n    float foveationScaleX = (centerSizeXAligned + (1. - centerSizeXAligned) / edgeRatioX);\n    float foveationScaleY = (centerSizeYAligned + (1. - centerSizeYAligned) / edgeRatioY);\n\n    float optimizedEyeWidth = foveationScaleX * targetEyeWidth;\n    float optimizedEyeHeight = foveationScaleY * targetEyeHeight;\n\n    // round the frame dimensions to a number of pixel multiple of 32 for the encoder\n    auto optimizedEyeWidthAligned = (uint32_t)ceil(optimizedEyeWidth / 32.f) * 32;\n    auto optimizedEyeHeightAligned = (uint32_t)ceil(optimizedEyeHeight / 32.f) * 32;\n\n    float eyeWidthRatioAligned = optimizedEyeWidth / optimizedEyeWidthAligned;\n    float eyeHeightRatioAligned = optimizedEyeHeight / optimizedEyeHeightAligned;\n\n    return { (uint32_t)targetEyeWidth,\n             (uint32_t)targetEyeHeight,\n             optimizedEyeWidthAligned,\n             optimizedEyeHeightAligned,\n             eyeWidthRatioAligned,\n             eyeHeightRatioAligned,\n             centerSizeXAligned,\n             centerSizeYAligned,\n             centerShiftXAligned,\n             centerShiftYAligned,\n             edgeRatioX,\n             edgeRatioY };\n}\n}\n\nvoid FFR::GetOptimizedResolution(uint32_t* width, uint32_t* height) {\n    auto fovVars = CalculateFoveationVars();\n    *width = fovVars.optimizedEyeWidth * 2;\n    *height = fovVars.optimizedEyeHeight;\n}\n\nFFR::FFR(ID3D11Device* device)\n    : mDevice(device) { }\n\nvoid FFR::Initialize(ID3D11Texture2D* compositionTexture) {\n    auto fovVars = CalculateFoveationVars();\n    ComPtr<ID3D11Buffer> foveatedRenderingBuffer = CreateBuffer(mDevice.Get(), fovVars);\n\n    std::vector<uint8_t> quadShaderCSO(\n        QUAD_SHADER_CSO_PTR, QUAD_SHADER_CSO_PTR + QUAD_SHADER_CSO_LEN\n    );\n    mQuadVertexShader = CreateVertexShader(mDevice.Get(), quadShaderCSO);\n\n    mOptimizedTexture = CreateTexture(\n        mDevice.Get(),\n        fovVars.optimizedEyeWidth * 2,\n        fovVars.optimizedEyeHeight,\n        Settings::Instance().m_enableHdr ? DXGI_FORMAT_R16G16B16A16_FLOAT\n                                         : DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n    );\n\n    if (Settings::Instance().m_enableFoveatedEncoding) {\n        std::vector<uint8_t> compressAxisAlignedShaderCSO(\n            COMPRESS_AXIS_ALIGNED_CSO_PTR,\n            COMPRESS_AXIS_ALIGNED_CSO_PTR + COMPRESS_AXIS_ALIGNED_CSO_LEN\n        );\n        auto compressAxisAlignedPipeline = RenderPipeline(mDevice.Get());\n        compressAxisAlignedPipeline.Initialize(\n            { compositionTexture },\n            mQuadVertexShader.Get(),\n            compressAxisAlignedShaderCSO,\n            mOptimizedTexture.Get(),\n            foveatedRenderingBuffer.Get()\n        );\n\n        mPipelines.push_back(compressAxisAlignedPipeline);\n    } else {\n        mOptimizedTexture = compositionTexture;\n    }\n}\n\nvoid FFR::Render() {\n    for (auto& p : mPipelines) {\n        p.Render();\n    }\n}\n\nID3D11Texture2D* FFR::GetOutputTexture() { return mOptimizedTexture.Get(); }\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/FFR.h",
    "content": "#pragma once\n\n#include \"d3d-render-utils/RenderPipeline.h\"\n\nclass FFR {\npublic:\n    FFR(ID3D11Device* device);\n    void Initialize(ID3D11Texture2D* compositionTexture);\n    void Render();\n    void GetOptimizedResolution(uint32_t* width, uint32_t* height);\n    ID3D11Texture2D* GetOutputTexture();\n\nprivate:\n    Microsoft::WRL::ComPtr<ID3D11Device> mDevice;\n    Microsoft::WRL::ComPtr<ID3D11Texture2D> mOptimizedTexture;\n    Microsoft::WRL::ComPtr<ID3D11VertexShader> mQuadVertexShader;\n\n    std::vector<d3d_render_utils::RenderPipeline> mPipelines;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/FrameRender.cpp",
    "content": "#include \"FrameRender.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/Utils.h\"\n#include \"alvr_server/bindings.h\"\n\nextern uint64_t g_DriverTestMode;\n\nusing namespace d3d_render_utils;\n\nstatic const DirectX::XMFLOAT4X4 _identityMat = DirectX::XMFLOAT4X4(\n    1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f\n);\n\nstatic DirectX::XMMATRIX HmdMatrix_AsDxMat(const vr::HmdMatrix34_t& m) {\n    // I think the negative Y basis is a handedness thing?\n    DirectX::XMFLOAT4X4 f = DirectX::XMFLOAT4X4(\n        m.m[0][0],\n        m.m[1][0],\n        m.m[2][0],\n        0.0f,\n        -m.m[0][1],\n        -m.m[1][1],\n        -m.m[2][1],\n        0.0f,\n        m.m[0][2],\n        m.m[1][2],\n        m.m[2][2],\n        0.0f,\n        m.m[0][3],\n        m.m[1][3],\n        m.m[2][3],\n        1.0f\n    );\n    return DirectX::XMLoadFloat4x4(&f);\n}\n\nstatic DirectX::XMMATRIX HmdMatrix_AsDxMatOrientOnly(vr::HmdMatrix34_t& m) {\n    // I think the negative Y basis is a handedness thing?\n    DirectX::XMFLOAT4X4 f = DirectX::XMFLOAT4X4(\n        m.m[0][0],\n        m.m[1][0],\n        m.m[2][0],\n        0.0f,\n        -m.m[0][1],\n        -m.m[1][1],\n        -m.m[2][1],\n        0.0f,\n        m.m[0][2],\n        m.m[1][2],\n        m.m[2][2],\n        0.0f,\n        0.0f,\n        0.0f,\n        0.0f,\n        1.0f\n    );\n    return DirectX::XMLoadFloat4x4(&f);\n}\n\nstatic DirectX::XMMATRIX HmdMatrix_AsDxMatPosOnly(const vr::HmdMatrix34_t& m) {\n    DirectX::XMFLOAT4X4 f = DirectX::XMFLOAT4X4(\n        1.0f,\n        0.0f,\n        0.0f,\n        0.0f,\n        0.0f,\n        1.0f,\n        0.0f,\n        0.0f,\n        0.0f,\n        0.0f,\n        1.0f,\n        0.0f,\n        m.m[0][3],\n        m.m[1][3],\n        m.m[2][3],\n        1.0f\n    );\n    return DirectX::XMLoadFloat4x4(&f);\n}\n\nFrameRender::FrameRender(std::shared_ptr<CD3DRender> pD3DRender)\n    : m_pD3DRender(pD3DRender) {\n    // Set safe defaults for tangents and eye-to-HMD\n    HmdMatrix_SetIdentity(&m_eyeToHead[0]);\n    HmdMatrix_SetIdentity(&m_eyeToHead[1]);\n    m_viewProj[0] = { -1.0f, 1.0f, 1.0f, -1.0f };\n    m_viewProj[1] = { -1.0f, 1.0f, 1.0f, -1.0f };\n\n    FrameRender::SetGpuPriority(m_pD3DRender->GetDevice());\n}\n\nFrameRender::~FrameRender() { }\n\nbool FrameRender::Startup() {\n    if (m_pStagingTexture) {\n        return true;\n    }\n\n    //\n    // Create staging texture\n    // This is input texture of Video Encoder and is render target of both eyes.\n    //\n\n    D3D11_TEXTURE2D_DESC compositionTextureDesc;\n    ZeroMemory(&compositionTextureDesc, sizeof(compositionTextureDesc));\n    compositionTextureDesc.Width = Settings::Instance().m_renderWidth;\n    compositionTextureDesc.Height = Settings::Instance().m_renderHeight;\n    compositionTextureDesc.Format = Settings::Instance().m_enableHdr\n        ? DXGI_FORMAT_R16G16B16A16_FLOAT\n        : DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;\n    compositionTextureDesc.MipLevels = 1;\n    compositionTextureDesc.ArraySize = 1;\n    compositionTextureDesc.SampleDesc.Count = 1;\n    compositionTextureDesc.Usage = D3D11_USAGE_DEFAULT;\n    compositionTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;\n\n    ComPtr<ID3D11Texture2D> compositionTexture;\n\n    if (FAILED(m_pD3DRender->GetDevice()->CreateTexture2D(\n            &compositionTextureDesc, NULL, &compositionTexture\n        ))) {\n        Error(\"Failed to create staging texture!\\n\");\n        return false;\n    }\n\n    HRESULT hr = m_pD3DRender->GetDevice()->CreateRenderTargetView(\n        compositionTexture.Get(), NULL, &m_pRenderTargetView\n    );\n    if (FAILED(hr)) {\n        Error(\"CreateRenderTargetView %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;\n    depthStencilDesc.DepthEnable = FALSE;\n    depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;\n    depthStencilDesc.DepthFunc = D3D11_COMPARISON_ALWAYS;\n    depthStencilDesc.StencilEnable = FALSE;\n    depthStencilDesc.StencilReadMask = 0xFF;\n    depthStencilDesc.StencilWriteMask = 0xFF;\n\n    // Stencil operations if pixel is front-facing\n    depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;\n    depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;\n    depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;\n    depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;\n\n    // Stencil operations if pixel is back-facing\n    depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;\n    depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;\n    depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;\n    depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;\n\n    m_pD3DRender->GetDevice()->CreateDepthStencilState(&depthStencilDesc, &m_depthStencilState);\n\n    // Left eye viewport\n\n    m_viewportL.Width = (float)Settings::Instance().m_renderWidth / 2.0;\n    m_viewportL.Height = (float)Settings::Instance().m_renderHeight;\n    m_viewportL.MinDepth = 0.0f;\n    m_viewportL.MaxDepth = 1.0f;\n    m_viewportL.TopLeftX = 0;\n    m_viewportL.TopLeftY = 0;\n\n    // Right eye viewport\n    m_viewportR.Width = (float)Settings::Instance().m_renderWidth / 2.0;\n    m_viewportR.Height = (float)Settings::Instance().m_renderHeight;\n    m_viewportR.MinDepth = 0.0f;\n    m_viewportR.MaxDepth = 1.0f;\n    m_viewportR.TopLeftX = (float)Settings::Instance().m_renderWidth / 2.0;\n    m_viewportR.TopLeftY = 0;\n\n    // Final composition viewport\n    m_viewport.Width = (float)Settings::Instance().m_renderWidth;\n    m_viewport.Height = (float)Settings::Instance().m_renderHeight;\n    m_viewport.MinDepth = 0.0f;\n    m_viewport.MaxDepth = 1.0f;\n    m_viewport.TopLeftX = 0;\n    m_viewport.TopLeftY = 0;\n\n    // Left eye scissor\n    m_scissorL.bottom = 0.0f;\n    m_scissorL.left = 0.0f;\n    m_scissorL.right = (float)Settings::Instance().m_renderWidth / 2.0f;\n    m_scissorL.top = (float)Settings::Instance().m_renderHeight;\n\n    // Right eye scissor\n    m_scissorR.bottom = 0.0f;\n    m_scissorR.left = (float)Settings::Instance().m_renderWidth / 2.0f;\n    m_scissorR.right = (float)Settings::Instance().m_renderWidth;\n    m_scissorR.top = (float)Settings::Instance().m_renderHeight;\n\n    // Final composition scissor\n    m_scissor.bottom = 0.0f;\n    m_scissor.left = 0.0f;\n    m_scissor.right = (float)Settings::Instance().m_renderWidth;\n    m_scissor.top = (float)Settings::Instance().m_renderHeight;\n\n    //\n    // Compile shaders\n    //\n\n    std::vector<uint8_t> vshader(\n        FRAME_RENDER_VS_CSO_PTR, FRAME_RENDER_VS_CSO_PTR + FRAME_RENDER_VS_CSO_LEN\n    );\n    hr = m_pD3DRender->GetDevice()->CreateVertexShader(\n        (const DWORD*)&vshader[0], vshader.size(), NULL, &m_pVertexShader\n    );\n    if (FAILED(hr)) {\n        Error(\"CreateVertexShader %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    std::vector<uint8_t> pshader(\n        FRAME_RENDER_PS_CSO_PTR, FRAME_RENDER_PS_CSO_PTR + FRAME_RENDER_PS_CSO_LEN\n    );\n    hr = m_pD3DRender->GetDevice()->CreatePixelShader(\n        (const DWORD*)&pshader[0], pshader.size(), NULL, &m_pPixelShader\n    );\n    if (FAILED(hr)) {\n        Error(\"CreatePixelShader %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    //\n    // Create input layout\n    //\n\n    // Define the input layout\n    D3D11_INPUT_ELEMENT_DESC layout[] = {\n        { \"POSITION\", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },\n        { \"TEXCOORD\", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 },\n        { \"VIEW\", 0, DXGI_FORMAT_R32_UINT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },\n    };\n    UINT numElements = ARRAYSIZE(layout);\n\n    // Create the input layout\n    hr = m_pD3DRender->GetDevice()->CreateInputLayout(\n        layout, numElements, &vshader[0], vshader.size(), &m_pVertexLayout\n    );\n    if (FAILED(hr)) {\n        Error(\"CreateInputLayout %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    // Set the input layout\n    m_pD3DRender->GetContext()->IASetInputLayout(m_pVertexLayout.Get());\n\n    //\n    // Create frame render CBuffer\n    //\n    struct FrameRenderBuffer {\n        float encodingGamma;\n        float _align0;\n        float _align1;\n        float _align2;\n    };\n    FrameRenderBuffer frameRenderStruct\n        = { (float)(1.0 / Settings::Instance().m_encodingGamma), 0.0f, 0.0f, 0.0f };\n    m_pFrameRenderCBuffer = CreateBuffer(m_pD3DRender->GetDevice(), frameRenderStruct);\n\n    //\n    // Create vertex buffer\n    //\n\n    // Src texture has various geometry and we should use the part of the textures.\n    // That part are defined by uv-coordinates of \"bounds\" passed to\n    // IVRDriverDirectModeComponent::SubmitLayer. So we should update uv-coordinates for every\n    // frames and layers.\n    D3D11_BUFFER_DESC bd;\n    ZeroMemory(&bd, sizeof(bd));\n    bd.Usage = D3D11_USAGE_DYNAMIC;\n    bd.ByteWidth = sizeof(SimpleVertex) * 8;\n    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;\n    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\n\n    hr = m_pD3DRender->GetDevice()->CreateBuffer(&bd, NULL, &m_pVertexBuffer);\n    if (FAILED(hr)) {\n        Error(\"CreateBuffer 1 %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    // Set vertex buffer\n    UINT stride = sizeof(SimpleVertex);\n    UINT offset = 0;\n    m_pD3DRender->GetContext()->IASetVertexBuffers(\n        0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset\n    );\n\n    //\n    // Create index buffer\n    //\n\n    WORD indices[] = { 0, 1, 2, 0, 3, 1,\n\n                       4, 5, 6, 4, 7, 5 };\n\n    bd.Usage = D3D11_USAGE_DEFAULT;\n    bd.ByteWidth = sizeof(indices);\n    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;\n    bd.CPUAccessFlags = 0;\n\n    D3D11_SUBRESOURCE_DATA InitData;\n    ZeroMemory(&InitData, sizeof(InitData));\n    InitData.pSysMem = indices;\n\n    hr = m_pD3DRender->GetDevice()->CreateBuffer(&bd, &InitData, &m_pIndexBuffer);\n    if (FAILED(hr)) {\n        Error(\"CreateBuffer 2 %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    // Set index buffer\n    m_pD3DRender->GetContext()->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);\n\n    // Set primitive topology\n    m_pD3DRender->GetContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);\n\n    // Create the sample state\n    D3D11_SAMPLER_DESC sampDesc;\n    ZeroMemory(&sampDesc, sizeof(sampDesc));\n    sampDesc.Filter = D3D11_FILTER_ANISOTROPIC;\n    sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;\n    sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;\n    sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;\n    sampDesc.MaxAnisotropy = D3D11_REQ_MAXANISOTROPY;\n    sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;\n    sampDesc.MinLOD = 0;\n    sampDesc.MaxLOD = D3D11_FLOAT32_MAX;\n    hr = m_pD3DRender->GetDevice()->CreateSamplerState(&sampDesc, &m_pSamplerLinear);\n    if (FAILED(hr)) {\n        Error(\"CreateSamplerState %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    //\n    // Create alpha blend state\n    // We need alpha blending to support layer.\n    //\n\n    // BlendState for first layer.\n    // Some VR apps (like SteamVR Home beta) submit the texture that alpha is zero on all pixels.\n    // So we need to ignore alpha of first layer.\n    D3D11_BLEND_DESC BlendDesc;\n    ZeroMemory(&BlendDesc, sizeof(BlendDesc));\n    BlendDesc.AlphaToCoverageEnable = FALSE;\n    BlendDesc.IndependentBlendEnable = FALSE;\n    for (int i = 0; i < 8; i++) {\n        BlendDesc.RenderTarget[i].BlendEnable = TRUE;\n        BlendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_ONE;\n        BlendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_ZERO;\n        BlendDesc.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;\n        BlendDesc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_ONE;\n        BlendDesc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ZERO;\n        BlendDesc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;\n        BlendDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED\n            | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE;\n    }\n\n    hr = m_pD3DRender->GetDevice()->CreateBlendState(&BlendDesc, &m_pBlendStateFirst);\n    if (FAILED(hr)) {\n        Error(\"CreateBlendState %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    // BleandState for other layers than first.\n    BlendDesc.AlphaToCoverageEnable = FALSE;\n    BlendDesc.IndependentBlendEnable = FALSE;\n    for (int i = 0; i < 8; i++) {\n        BlendDesc.RenderTarget[i].BlendEnable = TRUE;\n        BlendDesc.RenderTarget[i].SrcBlend = D3D11_BLEND_SRC_ALPHA;\n        BlendDesc.RenderTarget[i].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;\n        BlendDesc.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;\n        BlendDesc.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_ONE;\n        BlendDesc.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ZERO;\n        BlendDesc.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;\n        BlendDesc.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;\n    }\n\n    hr = m_pD3DRender->GetDevice()->CreateBlendState(&BlendDesc, &m_pBlendState);\n    if (FAILED(hr)) {\n        Error(\"CreateBlendState %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n        return false;\n    }\n\n    m_pStagingTexture = compositionTexture;\n\n    std::vector<uint8_t> quadShaderCSO(\n        QUAD_SHADER_CSO_PTR, QUAD_SHADER_CSO_PTR + QUAD_SHADER_CSO_LEN\n    );\n    ComPtr<ID3D11VertexShader> quadVertexShader\n        = CreateVertexShader(m_pD3DRender->GetDevice(), quadShaderCSO);\n\n    enableColorCorrection = Settings::Instance().m_enableColorCorrection;\n    if (enableColorCorrection) {\n        std::vector<uint8_t> colorCorrectionShaderCSO(\n            COLOR_CORRECTION_CSO_PTR, COLOR_CORRECTION_CSO_PTR + COLOR_CORRECTION_CSO_LEN\n        );\n\n        ComPtr<ID3D11Texture2D> colorCorrectedTexture = CreateTexture(\n            m_pD3DRender->GetDevice(),\n            Settings::Instance().m_renderWidth,\n            Settings::Instance().m_renderHeight,\n            Settings::Instance().m_enableHdr ? DXGI_FORMAT_R16G16B16A16_FLOAT\n                                             : DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n        );\n\n        struct ColorCorrection {\n            float renderWidth;\n            float renderHeight;\n            float brightness;\n            float contrast;\n            float saturation;\n            float gamma;\n            float sharpening;\n            float _align;\n        };\n        ColorCorrection colorCorrectionStruct = {\n            (float)Settings::Instance().m_renderWidth, (float)Settings::Instance().m_renderHeight,\n            Settings::Instance().m_brightness,         Settings::Instance().m_contrast + 1.f,\n            Settings::Instance().m_saturation + 1.f,   Settings::Instance().m_gamma,\n            Settings::Instance().m_sharpening\n        };\n        ComPtr<ID3D11Buffer> colorCorrectionBuffer\n            = CreateBuffer(m_pD3DRender->GetDevice(), colorCorrectionStruct);\n\n        m_colorCorrectionPipeline = std::make_unique<RenderPipeline>(m_pD3DRender->GetDevice());\n        m_colorCorrectionPipeline->Initialize(\n            { m_pStagingTexture.Get() },\n            quadVertexShader.Get(),\n            colorCorrectionShaderCSO,\n            colorCorrectedTexture.Get(),\n            colorCorrectionBuffer.Get()\n        );\n\n        m_pStagingTexture = colorCorrectedTexture;\n    }\n\n    enableFFE = Settings::Instance().m_enableFoveatedEncoding;\n    if (enableFFE) {\n        m_ffr = std::make_unique<FFR>(m_pD3DRender->GetDevice());\n        m_ffr->Initialize(m_pStagingTexture.Get());\n\n        m_pStagingTexture = m_ffr->GetOutputTexture();\n    }\n\n    if (Settings::Instance().m_enableHdr) {\n        std::vector<uint8_t> yuv420ShaderCSO(\n            RGBTOYUV420_CSO_PTR, RGBTOYUV420_CSO_PTR + RGBTOYUV420_CSO_LEN\n        );\n        uint32_t texWidth, texHeight;\n        GetEncodingResolution(&texWidth, &texHeight);\n\n        ComPtr<ID3D11Texture2D> yuvTexture = CreateTexture(\n            m_pD3DRender->GetDevice(),\n            texWidth,\n            texHeight,\n            Settings::Instance().m_use10bitEncoder ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12\n        );\n\n        struct YUVParams {\n            float offset[4];\n            float yCoeff[4];\n            float uCoeff[4];\n            float vCoeff[4];\n\n            float renderWidth;\n            float renderHeight;\n            float _padding0;\n            float _padding1;\n        };\n\n        // Bless this page for ending my stint of plugging in random values\n        // from other projects:\n        // https://kdashg.github.io/misc/colors/from-coeffs.html\n        YUVParams paramStruct_bt2020_8bit_full\n            = { { 0.0000000f, 0.5019608f, 0.5019608f, 0.0f }, // offset\n                { 0.2627000f, 0.6780000f, 0.0593000f, 0.0f }, // yCoeff\n                { -0.1390825f, -0.3589567f, 0.4980392f, 0.0f }, // uCoeff\n                { 0.4980392f, -0.4579826f, -0.0400566f, 0.0f }, // vCoeff\n                (float)texWidth,\n                (float)texHeight,\n                0.0,\n                0.0 };\n\n        YUVParams paramStruct_bt2020_10bit_full\n            = { { 0.0000000f, 0.5004888f, 0.5004888f, 0.0f }, // offset\n                { 0.2627000f, 0.6780000f, 0.0593000f, 0.0f }, // yCoeff\n                { -0.1394936f, -0.3600177f, 0.4995112f, 0.0f }, // uCoeff\n                { 0.4995112f, -0.4593363f, -0.0401750f, 0.0f }, // vCoeff\n                (float)texWidth,\n                (float)texHeight,\n                0.0,\n                0.0 };\n\n        YUVParams paramStruct_bt2020_8bit_limited\n            = { { 0.0627451f, 0.5019608f, 0.5019608f, 0.0f }, // offset\n                { 0.2256129f, 0.5822824f, 0.0509282f, 0.0f }, // yCoeff\n                { -0.1226554f, -0.3165603f, 0.4392157f, 0.0f }, // uCoeff\n                { 0.4392157f, -0.4038902f, -0.0353255f, 0.0f }, // vCoeff\n                (float)texWidth,\n                (float)texHeight,\n                0.0,\n                0.0 };\n\n        YUVParams paramStruct_bt2020_10bit_limited\n            = { { 0.0625611f, 0.5004888f, 0.5004888f, 0.0f }, // offset\n                { 0.2249513f, 0.5805748f, 0.0507789f, 0.0f }, // yCoeff\n                { -0.1222957f, -0.3156319f, 0.4379277f, 0.0f }, // uCoeff\n                { 0.4379277f, -0.4027058f, -0.0352219f, 0.0f }, // vCoeff\n                (float)texWidth,\n                (float)texHeight,\n                0.0,\n                0.0 };\n\n        YUVParams& paramStruct = paramStruct_bt2020_8bit_full;\n        if (Settings::Instance().m_use10bitEncoder) {\n            paramStruct = paramStruct_bt2020_10bit_full;\n        } else {\n            paramStruct = paramStruct_bt2020_8bit_full;\n        }\n\n        ComPtr<ID3D11Buffer> paramBuffer = CreateBuffer(m_pD3DRender->GetDevice(), paramStruct);\n\n        m_yuvPipeline = std::make_unique<RenderPipelineYUV>(m_pD3DRender->GetDevice());\n        m_yuvPipeline->Initialize(\n            { m_pStagingTexture.Get() },\n            quadVertexShader.Get(),\n            yuv420ShaderCSO,\n            yuvTexture.Get(),\n            paramBuffer.Get()\n        );\n\n        m_pStagingTexture = yuvTexture;\n    }\n\n    Debug(\"Staging Texture created\\n\");\n\n    return true;\n}\n\nvoid FrameRender::SetViewParams(\n    vr::HmdRect2_t projLeft,\n    vr::HmdMatrix34_t eyeToHeadLeft,\n    vr::HmdRect2_t projRight,\n    vr::HmdMatrix34_t eyeToHeadRight\n) {\n    m_viewProj[0] = projLeft;\n    m_eyeToHead[0] = eyeToHeadLeft;\n    m_viewProj[1] = projRight;\n    m_eyeToHead[1] = eyeToHeadRight;\n}\n\nbool FrameRender::RenderFrame(\n    ID3D11Texture2D* pTexture[][2],\n    vr::VRTextureBounds_t bounds[][2],\n    vr::HmdMatrix34_t poses[],\n    int layerCount,\n    bool recentering,\n    const std::string& message,\n    const std::string& debugText\n) {\n    // Set render target\n    m_pD3DRender->GetContext()->OMSetRenderTargets(1, m_pRenderTargetView.GetAddressOf(), NULL);\n\n    m_pD3DRender->GetContext()->OMSetDepthStencilState(m_depthStencilState.Get(), 0);\n\n    // Clear the back buffer\n    m_pD3DRender->GetContext()->ClearRenderTargetView(\n        m_pRenderTargetView.Get(), DirectX::Colors::MidnightBlue\n    );\n\n    // Overlay recentering texture on top of all layers.\n    int recenterLayer = -1;\n    if (recentering) {\n        recenterLayer = layerCount;\n        layerCount++;\n    }\n\n    // Set up our projection, HMD, and HMD-to-eye transforms once\n    const auto nearZ = 0.001f;\n    const auto farZ = 1.0f;\n    DirectX::XMMATRIX projectionMatL = DirectX::XMMatrixPerspectiveOffCenterRH(\n        m_viewProj[0].vTopLeft.v[0] * nearZ,\n        m_viewProj[0].vBottomRight.v[0] * nearZ,\n        -m_viewProj[0].vTopLeft.v[1] * nearZ,\n        -m_viewProj[0].vBottomRight.v[1] * nearZ,\n        nearZ,\n        farZ\n    );\n    DirectX::XMMATRIX projectionMatR = DirectX::XMMatrixPerspectiveOffCenterRH(\n        m_viewProj[1].vTopLeft.v[0] * nearZ,\n        m_viewProj[1].vBottomRight.v[0] * nearZ,\n        -m_viewProj[1].vTopLeft.v[1] * nearZ,\n        -m_viewProj[1].vBottomRight.v[1] * nearZ,\n        nearZ,\n        farZ\n    );\n    DirectX::XMMATRIX hmdToEyeMatL\n        = DirectX::XMMatrixInverse(nullptr, HmdMatrix_AsDxMatPosOnly(m_eyeToHead[0]));\n    DirectX::XMMATRIX hmdToEyeMatR\n        = DirectX::XMMatrixInverse(nullptr, HmdMatrix_AsDxMatPosOnly(m_eyeToHead[1]));\n    DirectX::XMMATRIX hmdPoseForTargetTs\n        = HmdMatrix_AsDxMatOrientOnly(poses[0]); // Set to HmdMatrix_AsDxMat to debug the rendering\n\n    // I think the negative Y basis is a handedness thing?\n    DirectX::XMMATRIX identityMat = DirectX::XMLoadFloat4x4(&_identityMat);\n\n    for (int i = 0; i < layerCount; i++) {\n        ID3D11Texture2D* textures[2];\n        vr::VRTextureBounds_t bound[2];\n\n        if (i == recenterLayer) {\n            textures[0] = (ID3D11Texture2D*)m_recenterTexture.Get();\n            textures[1] = (ID3D11Texture2D*)m_recenterTexture.Get();\n            bound[0].uMin = bound[0].vMin = bound[1].uMin = bound[1].vMin = 0.0f;\n            bound[0].uMax = bound[0].vMax = bound[1].uMax = bound[1].vMax = 1.0f;\n        } else {\n            textures[0] = pTexture[i][0];\n            textures[1] = pTexture[i][1];\n            bound[0] = bounds[i][0];\n            bound[1] = bounds[i][1];\n        }\n        if (textures[0] == NULL || textures[1] == NULL) {\n            Debug(\n                \"Ignore NULL layer. layer=%d/%d%s%s\\n\",\n                i,\n                layerCount,\n                recentering ? L\" (recentering)\" : L\"\",\n                !message.empty() ? L\" (message)\" : L\"\"\n            );\n            continue;\n        }\n\n        D3D11_TEXTURE2D_DESC srcDesc;\n        textures[0]->GetDesc(&srcDesc);\n\n        D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};\n        SRVDesc.Format = srcDesc.Format;\n        SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;\n        SRVDesc.Texture2D.MostDetailedMip = 0;\n        SRVDesc.Texture2D.MipLevels = 1;\n\n        ComPtr<ID3D11ShaderResourceView> pShaderResourceView[2];\n\n        HRESULT hr = m_pD3DRender->GetDevice()->CreateShaderResourceView(\n            textures[0], &SRVDesc, pShaderResourceView[0].ReleaseAndGetAddressOf()\n        );\n        if (FAILED(hr)) {\n            Error(\"CreateShaderResourceView %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n            return false;\n        }\n        hr = m_pD3DRender->GetDevice()->CreateShaderResourceView(\n            textures[1], &SRVDesc, pShaderResourceView[1].ReleaseAndGetAddressOf()\n        );\n        if (FAILED(hr)) {\n            Error(\"CreateShaderResourceView %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n            return false;\n        }\n\n        if (i == 0) {\n            m_pD3DRender->GetContext()->OMSetBlendState(m_pBlendStateFirst.Get(), NULL, 0xffffffff);\n        } else {\n            m_pD3DRender->GetContext()->OMSetBlendState(m_pBlendState.Get(), NULL, 0xffffffff);\n        }\n\n        uint32_t inputColorAdjust = 0;\n        if (Settings::Instance().m_enableHdr) {\n            if (SRVDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n                || SRVDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB\n                || SRVDesc.Format == DXGI_FORMAT_B8G8R8X8_UNORM_SRGB) {\n                inputColorAdjust = 1; // do sRGB manually\n            }\n            if (Settings::Instance().m_forceHdrSrgbCorrection) {\n                inputColorAdjust = 1;\n            }\n            if (Settings::Instance().m_clampHdrExtendedRange) {\n                inputColorAdjust |= 0x10; // Clamp values to 0.0 to 1.0\n            }\n        } else {\n            if (SRVDesc.Format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB\n                && SRVDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM_SRGB\n                && SRVDesc.Format != DXGI_FORMAT_B8G8R8X8_UNORM_SRGB) {\n                inputColorAdjust = 2; // undo sRGB?\n\n                if (Settings::Instance().m_forceHdrSrgbCorrection) {\n                    inputColorAdjust = 0;\n                }\n            }\n\n            if (Settings::Instance().m_clampHdrExtendedRange) {\n                inputColorAdjust |= 0x10; // Clamp values to 0.0 to 1.0\n            }\n        }\n\n        //\n        // Update uv-coordinates in vertex buffer according to bounds.\n        //\n\n        DirectX::XMMATRIX framePose\n            = (i == recenterLayer) ? identityMat : HmdMatrix_AsDxMatOrientOnly(poses[i]);\n        DirectX::XMMATRIX framePoseInv = DirectX::XMMatrixInverse(nullptr, framePose);\n\n        // framePose is the position of the layer in space, ie an identity matrix\n        // would place the quad perpendicular in the floor at 0,0,0\n        DirectX::XMMATRIX viewMatDiff\n            = DirectX::XMMatrixInverse(nullptr, hmdPoseForTargetTs * framePoseInv);\n\n        DirectX::XMMATRIX transformMatL = viewMatDiff * hmdToEyeMatL * projectionMatL;\n        DirectX::XMMATRIX transformMatR = viewMatDiff * hmdToEyeMatR * projectionMatR;\n\n        if (i == recenterLayer) {\n            transformMatL = identityMat;\n            transformMatR = identityMat;\n        }\n\n        const auto depth = 700.0f;\n        const auto m = 1.0f;\n        DirectX::XMFLOAT4 vertsL[4];\n        DirectX::XMFLOAT4 vertsR[4];\n\n        DirectX::XMVECTORF32 vertsL_VF32[4]\n            = { { { { -1.0f * -m_viewProj[0].vTopLeft.v[0] * depth * m,\n                      1.0f * -m_viewProj[0].vTopLeft.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { 1.0f * m_viewProj[0].vBottomRight.v[0] * depth * m,\n                      -1.0f * m_viewProj[0].vBottomRight.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { 1.0f * m_viewProj[0].vBottomRight.v[0] * depth * m,\n                      1.0f * -m_viewProj[0].vTopLeft.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { -1.0f * -m_viewProj[0].vTopLeft.v[0] * depth * m,\n                      -1.0f * m_viewProj[0].vBottomRight.v[1] * depth * m,\n                      -depth,\n                      1.0f } } } };\n\n        for (int i = 0; i < 4; i++) {\n            DirectX::XMStoreFloat4(\n                &vertsL[i], DirectX::XMVector3Transform(vertsL_VF32[i], transformMatL)\n            );\n        }\n\n        DirectX::XMVECTORF32 vertsR_VF32[4]\n            = { { { { -1.0f * -m_viewProj[1].vTopLeft.v[0] * depth * m,\n                      1.0f * -m_viewProj[1].vTopLeft.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { 1.0f * m_viewProj[1].vBottomRight.v[0] * depth * m,\n                      -1.0f * m_viewProj[1].vBottomRight.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { 1.0f * m_viewProj[1].vBottomRight.v[0] * depth * m,\n                      1.0f * -m_viewProj[1].vTopLeft.v[1] * depth * m,\n                      -depth,\n                      1.0f } } },\n                { { { -1.0f * -m_viewProj[1].vTopLeft.v[0] * depth * m,\n                      -1.0f * m_viewProj[1].vBottomRight.v[1] * depth * m,\n                      -depth,\n                      1.0f } } } };\n\n        for (int i = 0; i < 4; i++) {\n            DirectX::XMStoreFloat4(\n                &vertsR[i], DirectX::XMVector3Transform(vertsR_VF32[i], transformMatR)\n            );\n        }\n\n        // We discard the z value because we never want any clipping,\n        // but we do want the w value for perspective correction.\n        SimpleVertex vertices[] = {\n            // Left View\n            { DirectX::XMFLOAT4(vertsL[0].x, vertsL[0].y, 0.5, vertsL[0].w),\n              DirectX::XMFLOAT2(bound[0].uMin, bound[0].vMax),\n              0 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsL[1].x, vertsL[1].y, 0.5, vertsL[1].w),\n              DirectX::XMFLOAT2(bound[0].uMax, bound[0].vMin),\n              0 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsL[2].x, vertsL[2].y, 0.5, vertsL[2].w),\n              DirectX::XMFLOAT2(bound[0].uMax, bound[0].vMax),\n              0 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsL[3].x, vertsL[3].y, 0.5, vertsL[3].w),\n              DirectX::XMFLOAT2(bound[0].uMin, bound[0].vMin),\n              0 + (inputColorAdjust * 2) },\n            // Right View\n            { DirectX::XMFLOAT4(vertsR[0].x, vertsR[0].y, 0.5, vertsR[0].w),\n              DirectX::XMFLOAT2(bound[1].uMin, bound[1].vMax),\n              1 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsR[1].x, vertsR[1].y, 0.5, vertsR[1].w),\n              DirectX::XMFLOAT2(bound[1].uMax, bound[1].vMin),\n              1 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsR[2].x, vertsR[2].y, 0.5, vertsR[2].w),\n              DirectX::XMFLOAT2(bound[1].uMax, bound[1].vMax),\n              1 + (inputColorAdjust * 2) },\n            { DirectX::XMFLOAT4(vertsR[3].x, vertsR[3].y, 0.5, vertsR[3].w),\n              DirectX::XMFLOAT2(bound[1].uMin, bound[1].vMin),\n              1 + (inputColorAdjust * 2) },\n        };\n\n        D3D11_MAPPED_SUBRESOURCE mapped = { 0 };\n        hr = m_pD3DRender->GetContext()->Map(\n            m_pVertexBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped\n        );\n        if (FAILED(hr)) {\n            Error(\"Map %p %ls\\n\", hr, GetErrorStr(hr).c_str());\n            return false;\n        }\n        memcpy(mapped.pData, vertices, sizeof(vertices));\n\n        m_pD3DRender->GetContext()->Unmap(m_pVertexBuffer.Get(), 0);\n\n        // Set the input layout\n        m_pD3DRender->GetContext()->IASetInputLayout(m_pVertexLayout.Get());\n\n        //\n        // Set buffers\n        //\n\n        UINT stride = sizeof(SimpleVertex);\n        UINT offset = 0;\n        m_pD3DRender->GetContext()->IASetVertexBuffers(\n            0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset\n        );\n\n        m_pD3DRender->GetContext()->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);\n        m_pD3DRender->GetContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);\n        m_pD3DRender->GetContext()->PSSetConstantBuffers(\n            0, 1, m_pFrameRenderCBuffer.GetAddressOf()\n        );\n\n        //\n        // Set shaders\n        //\n\n        m_pD3DRender->GetContext()->VSSetShader(m_pVertexShader.Get(), nullptr, 0);\n        m_pD3DRender->GetContext()->PSSetShader(m_pPixelShader.Get(), nullptr, 0);\n\n        ID3D11ShaderResourceView* shaderResourceView[2]\n            = { pShaderResourceView[0].Get(), pShaderResourceView[1].Get() };\n        m_pD3DRender->GetContext()->PSSetShaderResources(0, 2, shaderResourceView);\n\n        m_pD3DRender->GetContext()->PSSetSamplers(0, 1, m_pSamplerLinear.GetAddressOf());\n\n        //\n        // Draw\n        //\n\n        // Left eye\n        m_pD3DRender->GetContext()->RSSetViewports(1, &m_viewportL);\n        m_pD3DRender->GetContext()->RSSetScissorRects(1, &m_scissorL);\n        m_pD3DRender->GetContext()->DrawIndexed(VERTEX_INDEX_COUNT / 2, 0, 0);\n\n        // Right eye\n        m_pD3DRender->GetContext()->RSSetViewports(1, &m_viewportR);\n        m_pD3DRender->GetContext()->RSSetScissorRects(1, &m_scissorR);\n        m_pD3DRender->GetContext()->DrawIndexed(\n            VERTEX_INDEX_COUNT / 2, (VERTEX_INDEX_COUNT / 2), 0\n        );\n    }\n\n    // Restore full viewport/scissor rect for the rest\n    m_pD3DRender->GetContext()->RSSetViewports(1, &m_viewport);\n    m_pD3DRender->GetContext()->RSSetScissorRects(1, &m_scissor);\n\n    if (enableColorCorrection) {\n        m_colorCorrectionPipeline->Render();\n    }\n\n    if (enableFFE) {\n        m_ffr->Render();\n    }\n\n    if (Settings::Instance().m_enableHdr) {\n        m_yuvPipeline->Render();\n    }\n\n    m_pD3DRender->GetContext()->Flush();\n\n    return true;\n}\n\nComPtr<ID3D11Texture2D> FrameRender::GetTexture() { return m_pStagingTexture; }\n\nvoid FrameRender::GetEncodingResolution(uint32_t* width, uint32_t* height) {\n    if (enableFFE) {\n        m_ffr->GetOptimizedResolution(width, height);\n    } else {\n        *width = Settings::Instance().m_renderWidth;\n        *height = Settings::Instance().m_renderHeight;\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/FrameRender.h",
    "content": "#pragma once\n\n#include <memory>\n#include <stdint.h>\n#include <string>\n\n#include <d3d11.h>\n#include <d3dcompiler.h>\n#include <directxcolors.h>\n#include <directxmath.h>\n#include <wrl.h>\n\n#include <cinttypes>\n#include <dxgi.h>\n#include <unknwn.h>\n#include <windows.h>\n\n#include \"FFR.h\"\n#include \"alvr_server/openvr_driver_wrap.h\"\n#include \"d3d-render-utils/RenderPipelineYUV.h\"\n#include \"shared/d3drender.h\"\n\n#define GPU_PRIORITY_VAL 7\n\nusing Microsoft::WRL::ComPtr;\n\ntemplate <class T> class ComQIPtr : public ComPtr<T> {\n\npublic:\n    inline ComQIPtr(IUnknown* unk) {\n        this->ptr_ = nullptr;\n        unk->QueryInterface(__uuidof(T), (void**)this->GetAddressOf());\n    }\n\n    inline ComPtr<T>& operator=(IUnknown* unk) {\n        ComPtr<T>::Clear();\n        unk->QueryInterface(__uuidof(T), (void**)this->GetAddressOf());\n        return *this;\n    }\n};\n\nclass FrameRender {\npublic:\n    FrameRender(std::shared_ptr<CD3DRender> pD3DRender);\n    virtual ~FrameRender();\n\n    bool Startup();\n    void SetViewParams(\n        vr::HmdRect2_t projLeft,\n        vr::HmdMatrix34_t eyeToHeadLeft,\n        vr::HmdRect2_t projRight,\n        vr::HmdMatrix34_t eyeToHeadRight\n    );\n    bool RenderFrame(\n        ID3D11Texture2D* pTexture[][2],\n        vr::VRTextureBounds_t bounds[][2],\n        vr::HmdMatrix34_t poses[],\n        int layerCount,\n        bool recentering,\n        const std::string& message,\n        const std::string& debugText\n    );\n    void GetEncodingResolution(uint32_t* width, uint32_t* height);\n\n    ComPtr<ID3D11Texture2D> GetTexture();\n\nprivate:\n    std::shared_ptr<CD3DRender> m_pD3DRender;\n    ComPtr<ID3D11Texture2D> m_pStagingTexture;\n\n    ComPtr<ID3D11VertexShader> m_pVertexShader;\n    ComPtr<ID3D11PixelShader> m_pPixelShader;\n\n    ComPtr<ID3D11InputLayout> m_pVertexLayout;\n    ComPtr<ID3D11Buffer> m_pVertexBuffer;\n    ComPtr<ID3D11Buffer> m_pIndexBuffer;\n    ComPtr<ID3D11Buffer> m_pFrameRenderCBuffer;\n\n    ComPtr<ID3D11SamplerState> m_pSamplerLinear;\n\n    ComPtr<ID3D11RenderTargetView> m_pRenderTargetView;\n    ComPtr<ID3D11DepthStencilState> m_depthStencilState;\n\n    D3D11_VIEWPORT m_viewportL, m_viewportR, m_viewport;\n    D3D11_RECT m_scissorL, m_scissorR, m_scissor;\n\n    ComPtr<ID3D11BlendState> m_pBlendStateFirst;\n    ComPtr<ID3D11BlendState> m_pBlendState;\n\n    ComPtr<ID3D11Resource> m_recenterTexture;\n    ComPtr<ID3D11ShaderResourceView> m_recenterResourceView;\n    ComPtr<ID3D11Resource> m_messageBGTexture;\n    ComPtr<ID3D11ShaderResourceView> m_messageBGResourceView;\n\n    vr::HmdRect2_t m_viewProj[2];\n    vr::HmdMatrix34_t m_eyeToHead[2];\n\n    struct SimpleVertex {\n        DirectX::XMFLOAT4 Pos;\n        DirectX::XMFLOAT2 Tex;\n        uint32_t View;\n    };\n    // Parameter for Draw method. 2-triangles for both eyes.\n    static const int VERTEX_INDEX_COUNT = 12;\n\n    std::unique_ptr<d3d_render_utils::RenderPipeline> m_colorCorrectionPipeline;\n    bool enableColorCorrection;\n\n    std::unique_ptr<FFR> m_ffr;\n    bool enableFFE;\n\n    std::unique_ptr<d3d_render_utils::RenderPipelineYUV> m_yuvPipeline;\n\n    static bool SetGpuPriority(ID3D11Device* device) {\n        typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS {\n            D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE,\n            D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL,\n            D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL,\n            D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL,\n            D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH,\n            D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME\n        } D3DKMT_SCHEDULINGPRIORITYCLASS;\n\n        ComQIPtr<IDXGIDevice> dxgiDevice(device);\n        if (!dxgiDevice) {\n            Info(\"[GPU PRIO FIX] Failed to get IDXGIDevice\\n\");\n            return false;\n        }\n\n        HMODULE gdi32 = GetModuleHandleW(L\"GDI32\");\n        if (!gdi32) {\n            Info(\"[GPU PRIO FIX] Failed to get GDI32\\n\");\n            return false;\n        }\n\n        NTSTATUS(WINAPI * d3dkmt_spspc)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS);\n        d3dkmt_spspc = (decltype(d3dkmt_spspc)\n        )GetProcAddress(gdi32, \"D3DKMTSetProcessSchedulingPriorityClass\");\n        if (!d3dkmt_spspc) {\n            Info(\"[GPU PRIO FIX] Failed to get d3dkmt_spspc\\n\");\n            return false;\n        }\n\n        NTSTATUS status\n            = d3dkmt_spspc(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME);\n        if (status\n            == 0xc0000022) { // STATUS_ACCESS_DENIED, see http://deusexmachina.uk/ntstatus.html\n            Info(\n                \"[GPU PRIO FIX] Failed to set process (%d) priority class, please run ALVR as \"\n                \"Administrator.\\n\",\n                GetCurrentProcess()\n            );\n            return false;\n        } else if (status != 0) {\n            Info(\n                \"[GPU PRIO FIX] Failed to set process (%d) priority class: %u\\n\",\n                GetCurrentProcess(),\n                status\n            );\n            return false;\n        }\n\n        HRESULT hr = dxgiDevice->SetGPUThreadPriority(GPU_PRIORITY_VAL);\n        if (FAILED(hr)) {\n            Info(\"[GPU PRIO FIX] SetGPUThreadPriority failed\\n\");\n            return false;\n        }\n\n        Debug(\"[GPU PRIO FIX] D3D11 GPU priority setup success\\n\");\n        return true;\n    }\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/NvCodecUtils.h",
    "content": "/*\n* Copyright 2017-2022 NVIDIA Corporation.  All rights reserved.\n*\n* Please refer to the NVIDIA end user license agreement (EULA) associated\n* with this source code for terms and conditions that govern your use of\n* this software. Any use, reproduction, disclosure, or distribution of\n* this software and related documentation outside the terms of the EULA\n* is strictly prohibited.\n*\n*/\n\n//---------------------------------------------------------------------------\n//! \\file NvCodecUtils.h\n//! \\brief Miscellaneous classes and error checking functions.\n//!\n//! Used by Transcode/Encode samples apps for reading input files, mutithreading, performance measurement or colorspace conversion while decoding.\n//---------------------------------------------------------------------------\n\n#pragma once\n#include <iomanip>\n#include <chrono>\n#include <sys/stat.h>\n#include <assert.h>\n#include <stdint.h>\n#include <string.h>\n#include <iostream>\n#include <fstream>\n#include <ios>\n#include <sstream>\n#include <thread>\n#include <list>\n#include <vector>\n#include <condition_variable>\n\n#ifdef __cuda_cuda_h__\ninline bool check(CUresult e, int iLine, const char *szFile) {\n    if (e != CUDA_SUCCESS) {\n        const char *szErrName = NULL;\n        cuGetErrorName(e, &szErrName);\n        //LOG(FATAL) << \"CUDA driver API error \" << szErrName << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n#endif\n\n#ifdef __CUDA_RUNTIME_H__\ninline bool check(cudaError_t e, int iLine, const char *szFile) {\n    if (e != cudaSuccess) {\n        //LOG(FATAL) << \"CUDA runtime API error \" << cudaGetErrorName(e) << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n#endif\n\n#ifdef _NV_ENCODEAPI_H_\ninline bool check(NVENCSTATUS e, int iLine, const char *szFile) {\n    const char *aszErrName[] = {\n        \"NV_ENC_SUCCESS\",\n        \"NV_ENC_ERR_NO_ENCODE_DEVICE\",\n        \"NV_ENC_ERR_UNSUPPORTED_DEVICE\",\n        \"NV_ENC_ERR_INVALID_ENCODERDEVICE\",\n        \"NV_ENC_ERR_INVALID_DEVICE\",\n        \"NV_ENC_ERR_DEVICE_NOT_EXIST\",\n        \"NV_ENC_ERR_INVALID_PTR\",\n        \"NV_ENC_ERR_INVALID_EVENT\",\n        \"NV_ENC_ERR_INVALID_PARAM\",\n        \"NV_ENC_ERR_INVALID_CALL\",\n        \"NV_ENC_ERR_OUT_OF_MEMORY\",\n        \"NV_ENC_ERR_ENCODER_NOT_INITIALIZED\",\n        \"NV_ENC_ERR_UNSUPPORTED_PARAM\",\n        \"NV_ENC_ERR_LOCK_BUSY\",\n        \"NV_ENC_ERR_NOT_ENOUGH_BUFFER\",\n        \"NV_ENC_ERR_INVALID_VERSION\",\n        \"NV_ENC_ERR_MAP_FAILED\",\n        \"NV_ENC_ERR_NEED_MORE_INPUT\",\n        \"NV_ENC_ERR_ENCODER_BUSY\",\n        \"NV_ENC_ERR_EVENT_NOT_REGISTERD\",\n        \"NV_ENC_ERR_GENERIC\",\n        \"NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY\",\n        \"NV_ENC_ERR_UNIMPLEMENTED\",\n        \"NV_ENC_ERR_RESOURCE_REGISTER_FAILED\",\n        \"NV_ENC_ERR_RESOURCE_NOT_REGISTERED\",\n        \"NV_ENC_ERR_RESOURCE_NOT_MAPPED\",\n    };\n    if (e != NV_ENC_SUCCESS) {\n        //LOG(FATAL) << \"NVENC error \" << aszErrName[e] << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n#endif\n\n#ifdef _WINERROR_\ninline bool check(HRESULT e, int iLine, const char *szFile) {\n    if (e != S_OK) {\n        std::stringstream stream;\n        stream << std::hex << std::uppercase << e;\n        //LOG(FATAL) << \"HRESULT error 0x\" << stream.str() << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n#endif\n\n#if defined(__gl_h_) || defined(__GL_H__)\ninline bool check(GLenum e, int iLine, const char *szFile) {\n    if (e != 0) {\n        //LOG(ERROR) << \"GLenum error \" << e << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n#endif\n\ninline bool check(int e, int iLine, const char *szFile) {\n    if (e < 0) {\n        //LOG(ERROR) << \"General error \" << e << \" at line \" << iLine << \" in file \" << szFile;\n        return false;\n    }\n    return true;\n}\n\n#define ck(call) check(call, __LINE__, __FILE__)\n#define MAKE_FOURCC( ch0, ch1, ch2, ch3 )                               \\\n                ( (uint32_t)(uint8_t)(ch0) | ( (uint32_t)(uint8_t)(ch1) << 8 ) |    \\\n                ( (uint32_t)(uint8_t)(ch2) << 16 ) | ( (uint32_t)(uint8_t)(ch3) << 24 ) )\n\n/**\n* @brief Wrapper class around std::thread\n*/\nclass NvThread\n{\npublic:\n    NvThread() = default;\n    NvThread(const NvThread&) = delete;\n    NvThread& operator=(const NvThread& other) = delete;\n\n    NvThread(std::thread&& thread) : t(std::move(thread))\n    {\n\n    }\n\n    NvThread(NvThread&& thread) : t(std::move(thread.t))\n    {\n\n    }\n\n    NvThread& operator=(NvThread&& other)\n    {\n        t = std::move(other.t);\n        return *this;\n    }\n\n    ~NvThread()\n    {\n        join();\n    }\n\n    void join()\n    {\n        if (t.joinable())\n        {\n            t.join();\n        }\n    }\nprivate:\n    std::thread t;\n};\n\n#ifndef _WIN32\n#define _stricmp strcasecmp\n#define _stat64 stat64\n#endif\n\n/**\n* @brief Utility class to allocate buffer memory. Helps avoid I/O during the encode/decode loop in case of performance tests.\n*/\nclass BufferedFileReader {\npublic:\n    /**\n    * @brief Constructor function to allocate appropriate memory and copy file contents into it\n    */\n    BufferedFileReader(const char *szFileName, bool bPartial = false) {\n        struct _stat64 st;\n\n        if (_stat64(szFileName, &st) != 0) {\n            return;\n        }\n        \n        nSize = st.st_size;\n        while (nSize) {\n            try {\n                pBuf = new uint8_t[(size_t)nSize];\n                if (nSize != st.st_size) {\n                    //LOG(WARNING) << \"File is too large - only \" << std::setprecision(4) << 100.0 * nSize / st.st_size << \"% is loaded\"; \n                }\n                break;\n            } catch(std::bad_alloc) {\n                if (!bPartial) {\n                    //LOG(ERROR) << \"Failed to allocate memory in BufferedReader\";\n                    return;\n                }\n                nSize = (uint32_t)(nSize * 0.9);\n            }\n        }\n\n        std::ifstream fpIn(szFileName, std::ifstream::in | std::ifstream::binary);\n        if (!fpIn)\n        {\n            //LOG(ERROR) << \"Unable to open input file: \" << szFileName;\n            return;\n        }\n\n        std::streamsize nRead = fpIn.read(reinterpret_cast<char*>(pBuf), nSize).gcount();\n        fpIn.close();\n\n        assert(nRead == nSize);\n    }\n    ~BufferedFileReader() {\n        if (pBuf) {\n            delete[] pBuf;\n        }\n    }\n    bool GetBuffer(uint8_t **ppBuf, uint64_t *pnSize) {\n        if (!pBuf) {\n            return false;\n        }\n\n        *ppBuf = pBuf;\n        *pnSize = nSize;\n        return true;\n    }\n\nprivate:\n    uint8_t *pBuf = NULL;\n    uint64_t nSize = 0;\n};\n\n/**\n* @brief Template class to facilitate color space conversion\n*/\ntemplate<typename T>\nclass YuvConverter {\npublic:\n    YuvConverter(int nWidth, int nHeight) : nWidth(nWidth), nHeight(nHeight) {\n        pQuad = new T[((nWidth + 1) / 2) * ((nHeight + 1) / 2)];\n    }\n    ~YuvConverter() {\n        delete[] pQuad;\n    }\n    void PlanarToUVInterleaved(T *pFrame, int nPitch = 0) {\n        if (nPitch == 0) {\n            nPitch = nWidth;\n        }\n\n        // sizes of source surface plane\n        int nSizePlaneY = nPitch * nHeight;\n        int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2);\n        int nSizePlaneV = nSizePlaneU;\n\n        T *puv = pFrame + nSizePlaneY;\n        if (nPitch == nWidth) {\n            memcpy(pQuad, puv, nSizePlaneU * sizeof(T));\n        } else {\n            for (int i = 0; i < (nHeight + 1) / 2; i++) {\n                memcpy(pQuad + ((nWidth + 1) / 2) * i, puv + ((nPitch + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T));\n            }\n        }\n        T *pv = puv + nSizePlaneU;\n        for (int y = 0; y < (nHeight + 1) / 2; y++) {\n            for (int x = 0; x < (nWidth + 1) / 2; x++) {\n                puv[y * nPitch + x * 2] = pQuad[y * ((nWidth + 1) / 2) + x];\n                puv[y * nPitch + x * 2 + 1] = pv[y * ((nPitch + 1) / 2) + x];\n            }\n        }\n    }\n    void UVInterleavedToPlanar(T *pFrame, int nPitch = 0) {\n        if (nPitch == 0) {\n            nPitch = nWidth;\n        }\n\n        // sizes of source surface plane\n        int nSizePlaneY = nPitch * nHeight;\n        int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2);\n        int nSizePlaneV = nSizePlaneU;\n\n        T *puv = pFrame + nSizePlaneY,\n            *pu = puv, \n            *pv = puv + nSizePlaneU;\n\n        // split chroma from interleave to planar\n        for (int y = 0; y < (nHeight + 1) / 2; y++) {\n            for (int x = 0; x < (nWidth + 1) / 2; x++) {\n                pu[y * ((nPitch + 1) / 2) + x] = puv[y * nPitch + x * 2];\n                pQuad[y * ((nWidth + 1) / 2) + x] = puv[y * nPitch + x * 2 + 1];\n            }\n        }\n        if (nPitch == nWidth) {\n            memcpy(pv, pQuad, nSizePlaneV * sizeof(T));\n        } else {\n            for (int i = 0; i < (nHeight + 1) / 2; i++) {\n                memcpy(pv + ((nPitch + 1) / 2) * i, pQuad + ((nWidth + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T));\n            }\n        }\n    }\n\nprivate:\n    T *pQuad;\n    int nWidth, nHeight;\n};\n\n/**\n* @brief Class for writing IVF format header for AV1 codec\n*/\nclass IVFUtils {\npublic:\n    void WriteFileHeader(std::vector<uint8_t> &vPacket, uint32_t nFourCC, uint32_t nWidth, uint32_t nHeight, uint32_t nFrameRateNum, uint32_t nFrameRateDen, uint32_t nFrameCnt)\n    {\n        char header[32];\n\n        header[0] = 'D';\n        header[1] = 'K';\n        header[2] = 'I';\n        header[3] = 'F';\n        mem_put_le16(header + 4, 0);                    // version\n        mem_put_le16(header + 6, 32);                   // header size\n        mem_put_le32(header + 8, nFourCC);              // fourcc\n        mem_put_le16(header + 12, nWidth);              // width\n        mem_put_le16(header + 14, nHeight);             // height\n        mem_put_le32(header + 16, nFrameRateNum);       // rate\n        mem_put_le32(header + 20, nFrameRateDen);       // scale\n        mem_put_le32(header + 24, nFrameCnt);           // length\n        mem_put_le32(header + 28, 0);                   // unused\n\n        vPacket.insert(vPacket.end(), &header[0], &header[32]);\n    }\n    \n    void WriteFrameHeader(std::vector<uint8_t> &vPacket,  size_t nFrameSize, int64_t pts)\n    {\n        char header[12];\n        mem_put_le32(header, (int)nFrameSize);\n        mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF));\n        mem_put_le32(header + 8, (int)(pts >> 32));\n        \n        vPacket.insert(vPacket.end(), &header[0], &header[12]);\n    }\n    \nprivate:\n    static inline void mem_put_le32(void *vmem, int val)\n    {\n        unsigned char *mem = (unsigned char *)vmem;\n        mem[0] = (unsigned char)((val >>  0) & 0xff);\n        mem[1] = (unsigned char)((val >>  8) & 0xff);\n        mem[2] = (unsigned char)((val >> 16) & 0xff);\n        mem[3] = (unsigned char)((val >> 24) & 0xff);\n    }\n\n    static inline void mem_put_le16(void *vmem, int val)\n    {\n        unsigned char *mem = (unsigned char *)vmem;\n        mem[0] = (unsigned char)((val >>  0) & 0xff);\n        mem[1] = (unsigned char)((val >>  8) & 0xff);\n    }\n\n};\n    \n/**\n* @brief Utility class to measure elapsed time in seconds between the block of executed code\n*/\nclass StopWatch {\npublic:\n    void Start() {\n        t0 = std::chrono::high_resolution_clock::now();\n    }\n    double Stop() {\n        return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch() - t0.time_since_epoch()).count() / 1.0e9;\n    }\n\nprivate:\n    std::chrono::high_resolution_clock::time_point t0;\n};\n\ntemplate<typename T>\nclass ConcurrentQueue\n{\n    public:\n\n    ConcurrentQueue() {}\n    ConcurrentQueue(size_t size) : maxSize(size) {}\n    ConcurrentQueue(const ConcurrentQueue&) = delete;\n    ConcurrentQueue& operator=(const ConcurrentQueue&) = delete;\n\n    void setSize(size_t s) {\n        maxSize = s;\n    }\n\n    void push_back(const T& value) {\n        // Do not use a std::lock_guard here. We will need to explicitly\n        // unlock before notify_one as the other waiting thread will\n        // automatically try to acquire mutex once it wakes up\n        // (which will happen on notify_one)\n        std::unique_lock<std::mutex> lock(m_mutex);\n        auto wasEmpty = m_List.empty();\n\n        while (full()) {\n            m_cond.wait(lock);\n        }\n\n        m_List.push_back(value);\n        if (wasEmpty && !m_List.empty()) {\n            lock.unlock();\n            m_cond.notify_one();\n        }\n    }\n\n    T pop_front() {\n        std::unique_lock<std::mutex> lock(m_mutex);\n\n        while (m_List.empty()) {\n            m_cond.wait(lock);\n        }\n        auto wasFull = full();\n        T data = std::move(m_List.front());\n        m_List.pop_front();\n\n        if (wasFull && !full()) {\n            lock.unlock();\n            m_cond.notify_one();\n        }\n\n        return data;\n    }\n\n    T front() {\n        std::unique_lock<std::mutex> lock(m_mutex);\n\n        while (m_List.empty()) {\n            m_cond.wait(lock);\n        }\n\n        return m_List.front();\n    }\n\n    size_t size() {\n        std::unique_lock<std::mutex> lock(m_mutex);\n        return m_List.size();\n    }\n\n    bool empty() {\n        std::unique_lock<std::mutex> lock(m_mutex);\n        return m_List.empty();\n    }\n    void clear() {\n        std::unique_lock<std::mutex> lock(m_mutex);\n        m_List.clear();\n    }\n\nprivate:\n    bool full() {\n        if (m_List.size() == maxSize)\n            return true;\n        return false;\n    }\n\nprivate:\n    std::list<T> m_List;\n    std::mutex m_mutex;\n    std::condition_variable m_cond;\n    size_t maxSize;\n};\n\ninline void CheckInputFile(const char *szInFilePath) {\n    std::ifstream fpIn(szInFilePath, std::ios::in | std::ios::binary);\n    if (fpIn.fail()) {\n        std::ostringstream err;\n        err << \"Unable to open input file: \" << szInFilePath << std::endl;\n        throw std::invalid_argument(err.str());\n    }\n}\n\ninline void ValidateResolution(int nWidth, int nHeight) {\n    \n    if (nWidth <= 0 || nHeight <= 0) {\n        std::ostringstream err;\n        err << \"Please specify positive non zero resolution as -s WxH. Current resolution is \" << nWidth << \"x\" << nHeight << std::endl;\n        throw std::invalid_argument(err.str());\n    }\n}\n\ntemplate <class COLOR32>\nvoid Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);\ntemplate <class COLOR64>\nvoid Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);\n\ntemplate <class COLOR32>\nvoid P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);\ntemplate <class COLOR64>\nvoid P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);\n\ntemplate <class COLOR32>\nvoid YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);\ntemplate <class COLOR64>\nvoid YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);\n\ntemplate <class COLOR32>\nvoid YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);\ntemplate <class COLOR64>\nvoid YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);\n\ntemplate <class COLOR32>\nvoid Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0);\ntemplate <class COLOR32>\nvoid P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4);\n\ntemplate <class COLOR32>\nvoid YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0);\ntemplate <class COLOR32>\nvoid YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4);\n\nvoid Bgra64ToP016(uint8_t *dpBgra, int nBgraPitch, uint8_t *dpP016, int nP016Pitch, int nWidth, int nHeight, int iMatrix = 4);\n\nvoid ConvertUInt8ToUInt16(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, int nDestPitch, int nWidth, int nHeight);\nvoid ConvertUInt16ToUInt8(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, int nDestPitch, int nWidth, int nHeight);\n\nvoid ResizeNv12(unsigned char *dpDstNv12, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcNv12, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstNv12UV = nullptr);\nvoid ResizeP016(unsigned char *dpDstP016, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcP016, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstP016UV = nullptr);\n\nvoid ScaleYUV420(unsigned char *dpDstY, unsigned char* dpDstU, unsigned char* dpDstV, int nDstPitch, int nDstChromaPitch, int nDstWidth, int nDstHeight,\n    unsigned char *dpSrcY, unsigned char* dpSrcU, unsigned char* dpSrcV, int nSrcPitch, int nSrcChromaPitch, int nSrcWidth, int nSrcHeight, bool bSemiplanar);\n\n#ifdef __cuda_cuda_h__\nvoid ComputeCRC(uint8_t *pBuffer, uint32_t *crcValue, CUstream_st *outputCUStream);\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/NvEncoder.cpp",
    "content": "/*\n* Copyright 2017-2022 NVIDIA Corporation.  All rights reserved.\n*\n* Please refer to the NVIDIA end user license agreement (EULA) associated\n* with this source code for terms and conditions that govern your use of\n* this software. Any use, reproduction, disclosure, or distribution of\n* this software and related documentation outside the terms of the EULA\n* is strictly prohibited.\n*\n*/\n\n#include \"NvEncoder.h\"\n\n#ifndef _WIN32\n#include <cstring>\nstatic inline bool operator==(const GUID &guid1, const GUID &guid2) {\n    return !memcmp(&guid1, &guid2, sizeof(GUID));\n}\n\nstatic inline bool operator!=(const GUID &guid1, const GUID &guid2) {\n    return !(guid1 == guid2);\n}\n#endif\n\nNvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void *pDevice, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat,\n                            uint32_t nExtraOutputDelay, bool bMotionEstimationOnly, bool bOutputInVideoMemory, bool bDX12Encode, bool bUseIVFContainer) :\n    m_pDevice(pDevice), \n    m_eDeviceType(eDeviceType),\n    m_nWidth(nWidth),\n    m_nHeight(nHeight),\n    m_nMaxEncodeWidth(nWidth),\n    m_nMaxEncodeHeight(nHeight),\n    m_eBufferFormat(eBufferFormat), \n    m_bMotionEstimationOnly(bMotionEstimationOnly), \n    m_bOutputInVideoMemory(bOutputInVideoMemory),\n    m_bIsDX12Encode(bDX12Encode),\n    m_bUseIVFContainer(bUseIVFContainer),\n    m_nExtraOutputDelay(nExtraOutputDelay), \n    m_hEncoder(nullptr)\n{\n    LoadNvEncApi();\n\n    if (!m_nvenc.nvEncOpenEncodeSession) \n    {\n        m_nEncoderBuffer = 0;\n        NVENC_THROW_ERROR(\"EncodeAPI not found\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n    }\n\n    NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER };\n    encodeSessionExParams.device = m_pDevice;\n    encodeSessionExParams.deviceType = m_eDeviceType;\n    encodeSessionExParams.apiVersion = NVENCAPI_VERSION;\n    void *hEncoder = NULL;\n    NVENC_API_CALL(m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder));\n    m_hEncoder = hEncoder;\n}\n\nvoid NvEncoder::LoadNvEncApi()\n{\n#if defined(_WIN32)\n#if defined(_WIN64)\n    HMODULE hModule = LoadLibrary(TEXT(\"nvEncodeAPI64.dll\"));\n#else\n    HMODULE hModule = LoadLibrary(TEXT(\"nvEncodeAPI.dll\"));\n#endif\n#else\n    void *hModule = dlopen(\"libnvidia-encode.so.1\", RTLD_LAZY);\n#endif\n\n    if (hModule == NULL)\n    {\n        NVENC_THROW_ERROR(\"NVENC library file is not found. Please ensure NV driver is installed\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n    }\n\n    m_hModule = hModule;\n\n    typedef NVENCSTATUS(NVENCAPI *NvEncodeAPIGetMaxSupportedVersion_Type)(uint32_t*);\n#if defined(_WIN32)\n    NvEncodeAPIGetMaxSupportedVersion_Type NvEncodeAPIGetMaxSupportedVersion = (NvEncodeAPIGetMaxSupportedVersion_Type)GetProcAddress(hModule, \"NvEncodeAPIGetMaxSupportedVersion\");\n#else\n    NvEncodeAPIGetMaxSupportedVersion_Type NvEncodeAPIGetMaxSupportedVersion = (NvEncodeAPIGetMaxSupportedVersion_Type)dlsym(hModule, \"NvEncodeAPIGetMaxSupportedVersion\");\n#endif\n\n    uint32_t version = 0;\n    uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION;\n    NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version));\n\n    if (currentVersion > version)\n    {\n        NVENC_THROW_ERROR(\"Current Driver Version does not support this NvEncodeAPI version, please upgrade driver\", NV_ENC_ERR_INVALID_VERSION);\n    }\n\n    typedef NVENCSTATUS(NVENCAPI *NvEncodeAPICreateInstance_Type)(NV_ENCODE_API_FUNCTION_LIST*);\n#if defined(_WIN32)\n    NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)GetProcAddress(hModule, \"NvEncodeAPICreateInstance\");\n#else\n    NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance = (NvEncodeAPICreateInstance_Type)dlsym(hModule, \"NvEncodeAPICreateInstance\");\n#endif\n\n    if (!NvEncodeAPICreateInstance)\n    {\n        NVENC_THROW_ERROR(\"Cannot find NvEncodeAPICreateInstance() entry in NVENC library\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n    }\n    m_nvenc = { NV_ENCODE_API_FUNCTION_LIST_VER };\n    NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc));\n}\n\nNvEncoder::~NvEncoder()\n{\n    DestroyHWEncoder();\n\n    if (m_hModule)\n    {\n#if defined(_WIN32)\n        FreeLibrary((HMODULE)m_hModule);\n#else\n        dlclose(m_hModule);\n#endif\n        m_hModule = nullptr;\n    }\n}\n\nvoid NvEncoder::CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo)\n{\n    if (!m_hEncoder)\n    {\n        NVENC_THROW_ERROR(\"Encoder Initialization failed\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n        return;\n    }\n\n    if (pIntializeParams == nullptr || pIntializeParams->encodeConfig == nullptr)\n    {\n        NVENC_THROW_ERROR(\"pInitializeParams and pInitializeParams->encodeConfig can't be NULL\", NV_ENC_ERR_INVALID_PTR);\n    }\n\n    memset(pIntializeParams->encodeConfig, 0, sizeof(NV_ENC_CONFIG));\n    auto pEncodeConfig = pIntializeParams->encodeConfig;\n    memset(pIntializeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS));\n    pIntializeParams->encodeConfig = pEncodeConfig;\n\n\n    pIntializeParams->encodeConfig->version = NV_ENC_CONFIG_VER;\n    pIntializeParams->version = NV_ENC_INITIALIZE_PARAMS_VER;\n\n    pIntializeParams->encodeGUID = codecGuid;\n    pIntializeParams->presetGUID = presetGuid;\n    pIntializeParams->encodeWidth = m_nWidth;\n    pIntializeParams->encodeHeight = m_nHeight;\n    pIntializeParams->darWidth = m_nWidth;\n    pIntializeParams->darHeight = m_nHeight;\n    pIntializeParams->frameRateNum = 30;\n    pIntializeParams->frameRateDen = 1;\n    pIntializeParams->enablePTD = 1;\n    pIntializeParams->reportSliceOffsets = 0;\n    pIntializeParams->enableSubFrameWrite = 0;\n    pIntializeParams->maxEncodeWidth = m_nWidth;\n    pIntializeParams->maxEncodeHeight = m_nHeight;\n    pIntializeParams->enableMEOnlyMode = m_bMotionEstimationOnly;\n    pIntializeParams->enableOutputInVidmem = m_bOutputInVideoMemory;\n#if defined(_WIN32)\n    if (!m_bOutputInVideoMemory)\n    {\n        pIntializeParams->enableEncodeAsync = GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT);\n    }\n#endif\n\n    NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } };\n    m_nvenc.nvEncGetEncodePresetConfig(m_hEncoder, codecGuid, presetGuid, &presetConfig);\n    memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG));\n    pIntializeParams->encodeConfig->frameIntervalP = 1;\n    pIntializeParams->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH;\n\n    pIntializeParams->encodeConfig->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;\n\n    if (!m_bMotionEstimationOnly)\n    {\n        pIntializeParams->tuningInfo = tuningInfo;\n        NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } };\n        m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, tuningInfo, &presetConfig);\n        memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG));\n    }\n    else\n    {\n        m_encodeConfig.version = NV_ENC_CONFIG_VER;\n        m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;\n        m_encodeConfig.rcParams.constQP = { 28, 31, 25 };\n    }\n    \n    if (pIntializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID)\n    {\n        if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)\n        {\n            pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC = 3;\n        }\n        pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.idrPeriod = pIntializeParams->encodeConfig->gopLength;\n    }\n    else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID)\n    {\n        pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 =\n            (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT ) ? 2 : 0;\n        if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)\n        {\n            pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC = 3;\n        }\n        pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = pIntializeParams->encodeConfig->gopLength;\n    }\n    else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_AV1_GUID)\n    {\n        pIntializeParams->encodeConfig->encodeCodecConfig.av1Config.pixelBitDepthMinus8 = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? 2 : 0;\n\t\tpIntializeParams->encodeConfig->encodeCodecConfig.av1Config.inputPixelBitDepthMinus8 = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? 2 : 0;\n        pIntializeParams->encodeConfig->encodeCodecConfig.av1Config.chromaFormatIDC = 1;\n        pIntializeParams->encodeConfig->encodeCodecConfig.av1Config.idrPeriod = pIntializeParams->encodeConfig->gopLength;\n        if (m_bOutputInVideoMemory)\n        {\n            pIntializeParams->encodeConfig->frameIntervalP = 1;\n        }\n    }\n\n    if (m_bIsDX12Encode)\n    {\n        pIntializeParams->bufferFormat = m_eBufferFormat;\n    }\n    \n    return;\n}\n\nvoid NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncoderParams)\n{\n    if (!m_hEncoder)\n    {\n        NVENC_THROW_ERROR(\"Encoder Initialization failed\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n    }\n\n    if (!pEncoderParams)\n    {\n        NVENC_THROW_ERROR(\"Invalid NV_ENC_INITIALIZE_PARAMS ptr\", NV_ENC_ERR_INVALID_PTR);\n    }\n\n    if (pEncoderParams->encodeWidth == 0 || pEncoderParams->encodeHeight == 0)\n    {\n        NVENC_THROW_ERROR(\"Invalid encoder width and height\", NV_ENC_ERR_INVALID_PARAM);\n    }\n\n    if (pEncoderParams->encodeGUID != NV_ENC_CODEC_H264_GUID && pEncoderParams->encodeGUID != NV_ENC_CODEC_HEVC_GUID && pEncoderParams->encodeGUID != NV_ENC_CODEC_AV1_GUID)\n    {\n        NVENC_THROW_ERROR(\"Invalid codec guid\", NV_ENC_ERR_INVALID_PARAM);\n    }\n\n    if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID)\n    {\n        if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)\n        {\n            NVENC_THROW_ERROR(\"10-bit format isn't supported by H264 encoder\", NV_ENC_ERR_INVALID_PARAM);\n        }\n    }\n\n    if (pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID)\n    {\n        if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)\n        {\n            NVENC_THROW_ERROR(\"YUV444 format isn't supported by AV1 encoder\", NV_ENC_ERR_INVALID_PARAM);\n        }\n    }\n\n    // set other necessary params if not set yet\n    if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID)\n    {\n        if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444) &&\n            (pEncoderParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC != 3))\n        {\n            NVENC_THROW_ERROR(\"Invalid ChromaFormatIDC\", NV_ENC_ERR_INVALID_PARAM);\n        }\n    }\n\n    if (pEncoderParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID)\n    {\n        bool yuv10BitFormat = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) ? true : false;\n        if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 != 2)\n        {\n            NVENC_THROW_ERROR(\"Invalid PixelBitdepth\", NV_ENC_ERR_INVALID_PARAM);\n        }\n\n        if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) &&\n            (pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC != 3))\n        {\n            NVENC_THROW_ERROR(\"Invalid ChromaFormatIDC\", NV_ENC_ERR_INVALID_PARAM);\n        }\n    }\n\n    if (pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID)\n    {\n        bool yuv10BitFormat = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? true : false;\n        if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig.av1Config.pixelBitDepthMinus8 != 2)\n        {\n            NVENC_THROW_ERROR(\"Invalid PixelBitdepth\", NV_ENC_ERR_INVALID_PARAM);\n        }\n\n        if (pEncoderParams->encodeConfig->encodeCodecConfig.av1Config.chromaFormatIDC != 1)\n        {\n            NVENC_THROW_ERROR(\"Invalid ChromaFormatIDC\", NV_ENC_ERR_INVALID_PARAM);\n        }\n\n        if (m_bOutputInVideoMemory && pEncoderParams->encodeConfig->frameIntervalP > 1)\n        {\n            NVENC_THROW_ERROR(\"Alt Ref frames not supported for AV1 in case of OutputInVideoMemory\", NV_ENC_ERR_INVALID_PARAM);\n        }\n    }\n\n    memcpy(&m_initializeParams, pEncoderParams, sizeof(m_initializeParams));\n    m_initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER;\n\n    if (pEncoderParams->encodeConfig)\n    {\n        memcpy(&m_encodeConfig, pEncoderParams->encodeConfig, sizeof(m_encodeConfig));\n        m_encodeConfig.version = NV_ENC_CONFIG_VER;\n    }\n    else\n    {\n        NV_ENC_PRESET_CONFIG presetConfig = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } };\n        if (!m_bMotionEstimationOnly)\n        {\n            m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, pEncoderParams->tuningInfo, &presetConfig);\n            memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG));\n            if (m_bOutputInVideoMemory && pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID)\n            {\n                m_encodeConfig.frameIntervalP = 1;\n            }\n        }\n        else\n        {\n            m_encodeConfig.version = NV_ENC_CONFIG_VER;\n            m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;\n            m_encodeConfig.rcParams.constQP = { 28, 31, 25 };\n        }\n    }\n    m_initializeParams.encodeConfig = &m_encodeConfig;\n\n    NVENC_API_CALL(m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams));\n\n    m_bEncoderInitialized = true;\n    m_nWidth = m_initializeParams.encodeWidth;\n    m_nHeight = m_initializeParams.encodeHeight;\n    m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth;\n    m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight;\n\n    m_nEncoderBuffer = m_encodeConfig.frameIntervalP + m_encodeConfig.rcParams.lookaheadDepth + m_nExtraOutputDelay;\n    m_nOutputDelay = m_nEncoderBuffer - 1;\n\n    if (!m_bOutputInVideoMemory)\n    {\n        m_vpCompletionEvent.resize(m_nEncoderBuffer, nullptr);\n    }\n\n#if defined(_WIN32)\n    for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) \n    {\n        m_vpCompletionEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);\n        if (!m_bIsDX12Encode)\n        {\n            NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER };\n            eventParams.completionEvent = m_vpCompletionEvent[i];\n            m_nvenc.nvEncRegisterAsyncEvent(m_hEncoder, &eventParams);\n        }\n    }\n#endif\n\n    m_vMappedInputBuffers.resize(m_nEncoderBuffer, nullptr);\n\n    if (m_bMotionEstimationOnly)\n    {\n        m_vMappedRefBuffers.resize(m_nEncoderBuffer, nullptr);\n\n        if (!m_bOutputInVideoMemory)\n        {\n            InitializeMVOutputBuffer();\n        }\n    }\n    else\n    {\n        if (!m_bOutputInVideoMemory && !m_bIsDX12Encode)\n        {\n            m_vBitstreamOutputBuffer.resize(m_nEncoderBuffer, nullptr);\n            InitializeBitstreamBuffer();\n        }\n    }\n\n    AllocateInputBuffers(m_nEncoderBuffer);\n}\n\nvoid NvEncoder::DestroyEncoder()\n{\n    if (!m_hEncoder)\n    {\n        return;\n    }\n\n    ReleaseInputBuffers();\n\n    DestroyHWEncoder();\n}\n\nvoid NvEncoder::DestroyHWEncoder()\n{\n    if (!m_hEncoder)\n    {\n        return;\n    }\n\n#if defined(_WIN32)\n    for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++)\n    {\n        if (m_vpCompletionEvent[i])\n        {\n            if (!m_bIsDX12Encode)\n            {\n                NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER };\n                eventParams.completionEvent = m_vpCompletionEvent[i];\n                m_nvenc.nvEncUnregisterAsyncEvent(m_hEncoder, &eventParams);\n            }\n            CloseHandle(m_vpCompletionEvent[i]);\n        }\n    }\n    m_vpCompletionEvent.clear();\n#endif\n\n    if (m_bMotionEstimationOnly)\n    {\n        DestroyMVOutputBuffer();\n    }\n    else\n    {\n        if (!m_bIsDX12Encode)\n            DestroyBitstreamBuffer();\n    }\n\n    m_nvenc.nvEncDestroyEncoder(m_hEncoder);\n\n    m_hEncoder = nullptr;\n\n    m_bEncoderInitialized = false;\n}\n\nconst NvEncInputFrame* NvEncoder::GetNextInputFrame()\n{\n    int i = m_iToSend % m_nEncoderBuffer;\n    return &m_vInputFrames[i];\n}\n\nconst NvEncInputFrame* NvEncoder::GetNextReferenceFrame()\n{\n    int i = m_iToSend % m_nEncoderBuffer;\n    return &m_vReferenceFrames[i];\n}\n\nvoid NvEncoder::MapResources(uint32_t bfrIdx)\n{\n    NV_ENC_MAP_INPUT_RESOURCE mapInputResource = { NV_ENC_MAP_INPUT_RESOURCE_VER };\n\n    mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx];\n    NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource));\n    m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource;\n\n    if (m_bMotionEstimationOnly)\n    {\n        mapInputResource.registeredResource = m_vRegisteredResourcesForReference[bfrIdx];\n        NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource));\n        m_vMappedRefBuffers[bfrIdx] = mapInputResource.mappedResource;\n    }\n}\n\nvoid NvEncoder::EncodeFrame(std::vector<std::vector<uint8_t>> &vPacket, NV_ENC_PIC_PARAMS *pPicParams)\n{\n    vPacket.clear();\n    if (!IsHWEncoderInitialized())\n    {\n        NVENC_THROW_ERROR(\"Encoder device not found\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n    }\n\n    int bfrIdx = m_iToSend % m_nEncoderBuffer;\n\n    MapResources(bfrIdx);\n\n    NVENCSTATUS nvStatus = DoEncode(m_vMappedInputBuffers[bfrIdx], m_vBitstreamOutputBuffer[bfrIdx], pPicParams);\n\n    if (nvStatus == NV_ENC_SUCCESS || nvStatus == NV_ENC_ERR_NEED_MORE_INPUT)\n    {\n        m_iToSend++;\n        GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true);\n    }\n    else\n    {\n        NVENC_THROW_ERROR(\"nvEncEncodePicture API failed\", nvStatus);\n    }\n}\n\nvoid NvEncoder::RunMotionEstimation(std::vector<uint8_t> &mvData)\n{\n    if (!m_hEncoder)\n    {\n        NVENC_THROW_ERROR(\"Encoder Initialization failed\", NV_ENC_ERR_NO_ENCODE_DEVICE);\n        return;\n    }\n\n    const uint32_t bfrIdx = m_iToSend % m_nEncoderBuffer;\n\n    MapResources(bfrIdx);\n\n    NVENCSTATUS nvStatus = DoMotionEstimation(m_vMappedInputBuffers[bfrIdx], m_vMappedRefBuffers[bfrIdx], m_vMVDataOutputBuffer[bfrIdx]);\n\n    if (nvStatus == NV_ENC_SUCCESS)\n    {\n        m_iToSend++;\n        std::vector<std::vector<uint8_t>> vPacket;\n        GetEncodedPacket(m_vMVDataOutputBuffer, vPacket, true);\n        if (vPacket.size() != 1)\n        {\n            NVENC_THROW_ERROR(\"GetEncodedPacket() doesn't return one (and only one) MVData\", NV_ENC_ERR_GENERIC);\n        }\n        mvData = vPacket[0];\n    }\n    else\n    {\n        NVENC_THROW_ERROR(\"nvEncEncodePicture API failed\", nvStatus);\n    }\n}\n\n\nvoid NvEncoder::GetSequenceParams(std::vector<uint8_t> &seqParams)\n{\n    uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less\n    memset(spsppsData, 0, sizeof(spsppsData));\n    NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = { NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER };\n    uint32_t spsppsSize = 0;\n\n    payload.spsppsBuffer = spsppsData;\n    payload.inBufferSize = sizeof(spsppsData);\n    payload.outSPSPPSPayloadSize = &spsppsSize;\n    NVENC_API_CALL(m_nvenc.nvEncGetSequenceParams(m_hEncoder, &payload));\n    seqParams.clear();\n    seqParams.insert(seqParams.end(), &spsppsData[0], &spsppsData[spsppsSize]);\n}\n\nNVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS *pPicParams)\n{\n    NV_ENC_PIC_PARAMS picParams = {};\n    if (pPicParams)\n    {\n        picParams = *pPicParams;\n    }\n    picParams.version = NV_ENC_PIC_PARAMS_VER;\n    picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;\n    picParams.inputBuffer = inputBuffer;\n    picParams.bufferFmt = GetPixelFormat();\n    picParams.inputWidth = GetEncodeWidth();\n    picParams.inputHeight = GetEncodeHeight();\n    picParams.outputBitstream = outputBuffer;\n    picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer);\n    NVENCSTATUS nvStatus = m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams);\n\n    return nvStatus; \n}\n\nvoid NvEncoder::SendEOS()\n{\n    NV_ENC_PIC_PARAMS picParams = { NV_ENC_PIC_PARAMS_VER };\n    picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS;\n    picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer);\n    NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams));\n}\n\nvoid NvEncoder::EndEncode(std::vector<std::vector<uint8_t>> &vPacket)\n{\n    vPacket.clear();\n    if (!IsHWEncoderInitialized())\n    {\n        NVENC_THROW_ERROR(\"Encoder device not initialized\", NV_ENC_ERR_ENCODER_NOT_INITIALIZED);\n    }\n\n    SendEOS();\n\n    GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, false);\n}\n\nvoid NvEncoder::GetEncodedPacket(std::vector<NV_ENC_OUTPUT_PTR> &vOutputBuffer, std::vector<std::vector<uint8_t>> &vPacket, bool bOutputDelay)\n{\n    unsigned i = 0;\n    int iEnd = bOutputDelay ? m_iToSend - m_nOutputDelay : m_iToSend;\n    for (; m_iGot < iEnd; m_iGot++)\n    {\n        WaitForCompletionEvent(m_iGot % m_nEncoderBuffer);\n        NV_ENC_LOCK_BITSTREAM lockBitstreamData = { NV_ENC_LOCK_BITSTREAM_VER };\n        lockBitstreamData.outputBitstream = vOutputBuffer[m_iGot % m_nEncoderBuffer];\n        lockBitstreamData.doNotWait = false;\n        NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData));\n  \n        uint8_t *pData = (uint8_t *)lockBitstreamData.bitstreamBufferPtr;\n        if (vPacket.size() < i + 1)\n        {\n            vPacket.push_back(std::vector<uint8_t>());\n        }\n        vPacket[i].clear();\n       \n        if ((m_initializeParams.encodeGUID == NV_ENC_CODEC_AV1_GUID) && (m_bUseIVFContainer))\n        {\n            if (m_bWriteIVFFileHeader)\n            {\n                m_IVFUtils.WriteFileHeader(vPacket[i], MAKE_FOURCC('A', 'V', '0', '1'), m_initializeParams.encodeWidth, m_initializeParams.encodeHeight, m_initializeParams.frameRateNum, m_initializeParams.frameRateDen, 0xFFFF);\n                m_bWriteIVFFileHeader = false;\n            }\n\n            m_IVFUtils.WriteFrameHeader(vPacket[i], lockBitstreamData.bitstreamSizeInBytes, lockBitstreamData.outputTimeStamp);\n        }\n        vPacket[i].insert(vPacket[i].end(), &pData[0], &pData[lockBitstreamData.bitstreamSizeInBytes]);\n        \n        i++;\n\n        NVENC_API_CALL(m_nvenc.nvEncUnlockBitstream(m_hEncoder, lockBitstreamData.outputBitstream));\n\n        if (m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer])\n        {\n            NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer]));\n            m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer] = nullptr;\n        }\n\n        if (m_bMotionEstimationOnly && m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer])\n        {\n            NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer]));\n            m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer] = nullptr;\n        }\n    }\n}\n\nbool NvEncoder::Reconfigure(const NV_ENC_RECONFIGURE_PARAMS *pReconfigureParams)\n{\n    NVENC_API_CALL(m_nvenc.nvEncReconfigureEncoder(m_hEncoder, const_cast<NV_ENC_RECONFIGURE_PARAMS*>(pReconfigureParams)));\n\n    memcpy(&m_initializeParams, &(pReconfigureParams->reInitEncodeParams), sizeof(m_initializeParams));\n    if (pReconfigureParams->reInitEncodeParams.encodeConfig)\n    {\n        memcpy(&m_encodeConfig, pReconfigureParams->reInitEncodeParams.encodeConfig, sizeof(m_encodeConfig));\n    }\n\n    m_nWidth = m_initializeParams.encodeWidth;\n    m_nHeight = m_initializeParams.encodeHeight;\n    m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth;\n    m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight;\n\n    return true;\n}\n\nNV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void *pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType,\n    int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage, \n    NV_ENC_FENCE_POINT_D3D12* pInputFencePoint)\n{\n    NV_ENC_REGISTER_RESOURCE registerResource = { NV_ENC_REGISTER_RESOURCE_VER };\n    registerResource.resourceType = eResourceType;\n    registerResource.resourceToRegister = pBuffer;\n    registerResource.width = width;\n    registerResource.height = height;\n    registerResource.pitch = pitch;\n    registerResource.bufferFormat = bufferFormat;\n    registerResource.bufferUsage = bufferUsage;\n    registerResource.pInputFencePoint = pInputFencePoint;\n    NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, &registerResource));\n\n    return registerResource.registeredResource;\n}\n\nvoid NvEncoder::RegisterInputResources(std::vector<void*> inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType,\n                                         int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame)\n{\n    for (uint32_t i = 0; i < inputframes.size(); ++i)\n    {\n        NV_ENC_REGISTERED_PTR registeredPtr = RegisterResource(inputframes[i], eResourceType, width, height, pitch, bufferFormat, NV_ENC_INPUT_IMAGE);\n        \n        std::vector<uint32_t> _chromaOffsets;\n        NvEncoder::GetChromaSubPlaneOffsets(bufferFormat, pitch, height, _chromaOffsets);\n        NvEncInputFrame inputframe = {};\n        inputframe.inputPtr = (void *)inputframes[i];\n        inputframe.chromaOffsets[0] = 0;\n        inputframe.chromaOffsets[1] = 0;\n        for (uint32_t ch = 0; ch < _chromaOffsets.size(); ch++)\n        {\n            inputframe.chromaOffsets[ch] = _chromaOffsets[ch];\n        }\n        inputframe.numChromaPlanes = NvEncoder::GetNumChromaPlanes(bufferFormat);\n        inputframe.pitch = pitch;\n        inputframe.chromaPitch = NvEncoder::GetChromaPitch(bufferFormat, pitch);\n        inputframe.bufferFormat = bufferFormat;\n        inputframe.resourceType = eResourceType;\n\n        if (bReferenceFrame)\n        {\n            m_vRegisteredResourcesForReference.push_back(registeredPtr);\n            m_vReferenceFrames.push_back(inputframe);\n        }\n        else\n        {\n            m_vRegisteredResources.push_back(registeredPtr);\n            m_vInputFrames.push_back(inputframe);\n        }\n    }\n}\n\nvoid NvEncoder::FlushEncoder()\n{\n    if (!m_bMotionEstimationOnly && !m_bOutputInVideoMemory)\n    {\n        // Incase of error it is possible for buffers still mapped to encoder.\n        // flush the encoder queue and then unmapped it if any surface is still mapped\n        try\n        {\n            std::vector<std::vector<uint8_t>> vPacket;\n            EndEncode(vPacket);\n        }\n        catch (...)\n        {\n\n        }\n    }\n}\n\nvoid NvEncoder::UnregisterInputResources()\n{\n    FlushEncoder();\n    \n    if (m_bMotionEstimationOnly)\n    {\n        for (uint32_t i = 0; i < m_vMappedRefBuffers.size(); ++i)\n        {\n            if (m_vMappedRefBuffers[i])\n            {\n                m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedRefBuffers[i]);\n            }\n        }\n    }\n    m_vMappedRefBuffers.clear();\n\n    for (uint32_t i = 0; i < m_vMappedInputBuffers.size(); ++i)\n    {\n        if (m_vMappedInputBuffers[i])\n        {\n            m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[i]);\n        }\n    }\n    m_vMappedInputBuffers.clear();\n\n    for (uint32_t i = 0; i < m_vRegisteredResources.size(); ++i)\n    {\n        if (m_vRegisteredResources[i])\n        {\n            m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResources[i]);\n        }\n    }\n    m_vRegisteredResources.clear();\n\n\n    for (uint32_t i = 0; i < m_vRegisteredResourcesForReference.size(); ++i)\n    {\n        if (m_vRegisteredResourcesForReference[i])\n        {\n            m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResourcesForReference[i]);\n        }\n    }\n    m_vRegisteredResourcesForReference.clear();\n\n}\n\n\nvoid NvEncoder::WaitForCompletionEvent(int iEvent)\n{\n#if defined(_WIN32)\n    // Check if we are in async mode. If not, don't wait for event;\n    NV_ENC_CONFIG sEncodeConfig = { 0 };\n    NV_ENC_INITIALIZE_PARAMS sInitializeParams = { 0 };\n    sInitializeParams.encodeConfig = &sEncodeConfig;\n    GetInitializeParams(&sInitializeParams);\n\n    if (0U == sInitializeParams.enableEncodeAsync)\n    {\n        return;\n    }\n#ifdef DEBUG\n    WaitForSingleObject(m_vpCompletionEvent[iEvent], INFINITE);\n#else\n    // wait for 20s which is infinite on terms of gpu time\n    if (WaitForSingleObject(m_vpCompletionEvent[iEvent], 20000) == WAIT_FAILED)\n    {\n        NVENC_THROW_ERROR(\"Failed to encode frame\", NV_ENC_ERR_GENERIC);\n    }\n#endif\n#endif\n}\n\nuint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width)\n{\n    switch (bufferFormat) {\n    case NV_ENC_BUFFER_FORMAT_NV12:\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n        return width;\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return width * 2;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return width * 4;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return 0;\n    }\n}\n\nuint32_t NvEncoder::GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat)\n{\n    switch (bufferFormat) \n    {\n    case NV_ENC_BUFFER_FORMAT_NV12:\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        return 1;\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return 2;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return 0;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return -1;\n    }\n}\n\nuint32_t NvEncoder::GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat,const uint32_t lumaPitch)\n{\n    switch (bufferFormat)\n    {\n    case NV_ENC_BUFFER_FORMAT_NV12:\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return lumaPitch;\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n        return (lumaPitch + 1)/2;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return 0;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return -1;\n    }\n}\n\nvoid NvEncoder::GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, const uint32_t height, std::vector<uint32_t>& chromaOffsets)\n{\n    chromaOffsets.clear();\n    switch (bufferFormat)\n    {\n    case NV_ENC_BUFFER_FORMAT_NV12:\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        chromaOffsets.push_back(pitch * height);\n        return;\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n        chromaOffsets.push_back(pitch * height);\n        chromaOffsets.push_back(chromaOffsets[0] + (NvEncoder::GetChromaPitch(bufferFormat, pitch) * GetChromaHeight(bufferFormat, height)));\n        return;\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        chromaOffsets.push_back(pitch * height);\n        chromaOffsets.push_back(chromaOffsets[0] + (pitch * height));\n        return;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return;\n    }\n}\n\nuint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight)\n{\n    switch (bufferFormat)\n    {\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n    case NV_ENC_BUFFER_FORMAT_NV12:\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        return (lumaHeight + 1)/2;\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return lumaHeight;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return 0;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return 0;\n    }\n}\n\nuint32_t NvEncoder::GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth)\n{\n    switch (bufferFormat)\n    {\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n        return (lumaWidth + 1) / 2;\n    case NV_ENC_BUFFER_FORMAT_NV12:\n        return lumaWidth;\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        return 2 * lumaWidth;\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n        return lumaWidth;\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return 2 * lumaWidth;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return 0;\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return 0;\n    }\n}\n\n\nint NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery)\n{\n    if (!m_hEncoder)\n    {\n        return 0;\n    }\n    NV_ENC_CAPS_PARAM capsParam = { NV_ENC_CAPS_PARAM_VER };\n    capsParam.capsToQuery = capsToQuery;\n    int v;\n    m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v);\n    return v;\n}\n\nint NvEncoder::GetFrameSize() const\n{\n    switch (GetPixelFormat())\n    {\n    case NV_ENC_BUFFER_FORMAT_YV12:\n    case NV_ENC_BUFFER_FORMAT_IYUV:\n    case NV_ENC_BUFFER_FORMAT_NV12:\n        return GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2);\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        return 2 * GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2);\n    case NV_ENC_BUFFER_FORMAT_YUV444:\n        return GetEncodeWidth() * GetEncodeHeight() * 3;\n    case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:\n        return 2 * GetEncodeWidth() * GetEncodeHeight() * 3;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n    case NV_ENC_BUFFER_FORMAT_ARGB10:\n    case NV_ENC_BUFFER_FORMAT_AYUV:\n    case NV_ENC_BUFFER_FORMAT_ABGR:\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n        return 4 * GetEncodeWidth() * GetEncodeHeight();\n    default:\n        NVENC_THROW_ERROR(\"Invalid Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n        return 0;\n    }\n}\n\nvoid NvEncoder::GetInitializeParams(NV_ENC_INITIALIZE_PARAMS *pInitializeParams)\n{\n    if (!pInitializeParams || !pInitializeParams->encodeConfig)\n    {\n        NVENC_THROW_ERROR(\"Both pInitializeParams and pInitializeParams->encodeConfig can't be NULL\", NV_ENC_ERR_INVALID_PTR);\n    }\n    NV_ENC_CONFIG *pEncodeConfig = pInitializeParams->encodeConfig;\n    *pEncodeConfig = m_encodeConfig;\n    *pInitializeParams = m_initializeParams;\n    pInitializeParams->encodeConfig = pEncodeConfig;\n}\n\nvoid NvEncoder::InitializeBitstreamBuffer()\n{\n    for (int i = 0; i < m_nEncoderBuffer; i++)\n    {\n        NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER };\n        NVENC_API_CALL(m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer));\n        m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer;\n    }\n}\n\nvoid NvEncoder::DestroyBitstreamBuffer()\n{\n    for (uint32_t i = 0; i < m_vBitstreamOutputBuffer.size(); i++)\n    {\n        if (m_vBitstreamOutputBuffer[i])\n        {\n            m_nvenc.nvEncDestroyBitstreamBuffer(m_hEncoder, m_vBitstreamOutputBuffer[i]);\n        }\n    }\n\n    m_vBitstreamOutputBuffer.clear();\n}\n\nvoid NvEncoder::InitializeMVOutputBuffer()\n{\n    for (int i = 0; i < m_nEncoderBuffer; i++)\n    {\n        NV_ENC_CREATE_MV_BUFFER createMVBuffer = { NV_ENC_CREATE_MV_BUFFER_VER };\n        NVENC_API_CALL(m_nvenc.nvEncCreateMVBuffer(m_hEncoder, &createMVBuffer));\n        m_vMVDataOutputBuffer.push_back(createMVBuffer.mvBuffer);\n    }\n}\n\nvoid NvEncoder::DestroyMVOutputBuffer()\n{\n    for (uint32_t i = 0; i < m_vMVDataOutputBuffer.size(); i++)\n    {\n        if (m_vMVDataOutputBuffer[i])\n        {\n            m_nvenc.nvEncDestroyMVBuffer(m_hEncoder, m_vMVDataOutputBuffer[i]);\n        }\n    }\n\n    m_vMVDataOutputBuffer.clear();\n}\n\nNVENCSTATUS NvEncoder::DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_INPUT_PTR inputBufferForReference, NV_ENC_OUTPUT_PTR outputBuffer)\n{\n    NV_ENC_MEONLY_PARAMS meParams = { NV_ENC_MEONLY_PARAMS_VER };\n    meParams.inputBuffer = inputBuffer;\n    meParams.referenceFrame = inputBufferForReference;\n    meParams.inputWidth = GetEncodeWidth();\n    meParams.inputHeight = GetEncodeHeight();\n    meParams.mvBuffer = outputBuffer;\n    meParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer);\n    NVENCSTATUS nvStatus = m_nvenc.nvEncRunMotionEstimationOnly(m_hEncoder, &meParams);\n    \n    return nvStatus;\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/NvEncoder.h",
    "content": "/*\n* Copyright 2017-2022 NVIDIA Corporation.  All rights reserved.\n*\n* Please refer to the NVIDIA end user license agreement (EULA) associated\n* with this source code for terms and conditions that govern your use of\n* this software. Any use, reproduction, disclosure, or distribution of\n* this software and related documentation outside the terms of the EULA\n* is strictly prohibited.\n*\n*/\n\n#pragma once\n\n#include <vector>\n#include \"alvr_server/nvEncodeAPI.h\"\n#include <stdint.h>\n#include <mutex>\n#include <string>\n#include <iostream>\n#include <sstream>\n#include <string.h>\n#include \"NvCodecUtils.h\"\n\n/**\n* @brief Exception class for error reporting from NvEncodeAPI calls.\n*/\nclass NVENCException : public std::exception\n{\npublic:\n    NVENCException(const std::string& errorStr, const NVENCSTATUS errorCode)\n        : m_errorString(errorStr), m_errorCode(errorCode) {}\n\n    virtual ~NVENCException() throw() {}\n    virtual const char* what() const throw() { return m_errorString.c_str(); }\n    NVENCSTATUS  getErrorCode() const { return m_errorCode; }\n    const std::string& getErrorString() const { return m_errorString; }\n    static NVENCException makeNVENCException(const std::string& errorStr, const NVENCSTATUS errorCode,\n        const std::string& functionName, const std::string& fileName, int lineNo);\nprivate:\n    std::string m_errorString;\n    NVENCSTATUS m_errorCode;\n};\n\ninline NVENCException NVENCException::makeNVENCException(const std::string& errorStr, const NVENCSTATUS errorCode, const std::string& functionName,\n    const std::string& fileName, int lineNo)\n{\n    std::ostringstream errorLog;\n    errorLog << functionName << \" : \" << errorStr << \" at \" << fileName << \":\" << lineNo << std::endl;\n    NVENCException exception(errorLog.str(), errorCode);\n    return exception;\n}\n\n#define NVENC_THROW_ERROR( errorStr, errorCode )                                                         \\\n    do                                                                                                   \\\n    {                                                                                                    \\\n        throw NVENCException::makeNVENCException(errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \\\n    } while (0)\n\n\n#define NVENC_API_CALL( nvencAPI )                                                                                 \\\n    do                                                                                                             \\\n    {                                                                                                              \\\n        NVENCSTATUS errorCode = nvencAPI;                                                                          \\\n        if( errorCode != NV_ENC_SUCCESS)                                                                           \\\n        {                                                                                                          \\\n            std::ostringstream errorLog;                                                                           \\\n            errorLog << #nvencAPI << \" returned error \" << errorCode;                                              \\\n            throw NVENCException::makeNVENCException(errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \\\n        }                                                                                                          \\\n    } while (0)\n\nstruct NvEncInputFrame\n{\n    void* inputPtr = nullptr;\n    uint32_t chromaOffsets[2];\n    uint32_t numChromaPlanes;\n    uint32_t pitch;\n    uint32_t chromaPitch;\n    NV_ENC_BUFFER_FORMAT bufferFormat;\n    NV_ENC_INPUT_RESOURCE_TYPE resourceType;\n};\n\n/**\n* @brief Shared base class for different encoder interfaces.\n*/\nclass NvEncoder\n{\npublic:\n    /**\n    *  @brief This function is used to initialize the encoder session.\n    *  Application must call this function to initialize the encoder, before\n    *  starting to encode any frames.\n    */\n    virtual void CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncodeParams);\n\n    /**\n    *  @brief  This function is used to destroy the encoder session.\n    *  Application must call this function to destroy the encoder session and\n    *  clean up any allocated resources. The application must call EndEncode()\n    *  function to get any queued encoded frames before calling DestroyEncoder().\n    */\n    virtual void DestroyEncoder();\n\n    /**\n    *  @brief  This function is used to reconfigure an existing encoder session.\n    *  Application can use this function to dynamically change the bitrate,\n    *  resolution and other QOS parameters. If the application changes the\n    *  resolution, it must set NV_ENC_RECONFIGURE_PARAMS::forceIDR.\n    */\n    bool Reconfigure(const NV_ENC_RECONFIGURE_PARAMS *pReconfigureParams);\n\n    /**\n    *  @brief  This function is used to get the next available input buffer.\n    *  Applications must call this function to obtain a pointer to the next\n    *  input buffer. The application must copy the uncompressed data to the\n    *  input buffer and then call EncodeFrame() function to encode it.\n    */\n    const NvEncInputFrame* GetNextInputFrame();\n\n\n    /**\n    *  @brief  This function is used to encode a frame.\n    *  Applications must call EncodeFrame() function to encode the uncompressed\n    *  data, which has been copied to an input buffer obtained from the\n    *  GetNextInputFrame() function.\n    */\n    virtual void EncodeFrame(std::vector<std::vector<uint8_t>> &vPacket, NV_ENC_PIC_PARAMS *pPicParams = nullptr);\n\n    /**\n    *  @brief  This function to flush the encoder queue.\n    *  The encoder might be queuing frames for B picture encoding or lookahead;\n    *  the application must call EndEncode() to get all the queued encoded frames\n    *  from the encoder. The application must call this function before destroying\n    *  an encoder session.\n    */\n    virtual void EndEncode(std::vector<std::vector<uint8_t>> &vPacket);\n\n    /**\n    *  @brief  This function is used to query hardware encoder capabilities.\n    *  Applications can call this function to query capabilities like maximum encode\n    *  dimensions, support for lookahead or the ME-only mode etc.\n    */\n    int GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery);\n\n    /**\n    *  @brief  This function is used to get the current device on which encoder is running.\n    */\n    void *GetDevice() const { return m_pDevice; }\n\n    /**\n    *  @brief  This function is used to get the current device type which encoder is running.\n    */\n    NV_ENC_DEVICE_TYPE GetDeviceType() const { return m_eDeviceType; }\n\n    /**\n    *  @brief  This function is used to get the current encode width.\n    *  The encode width can be modified by Reconfigure() function.\n    */\n    int GetEncodeWidth() const { return m_nWidth; }\n\n    /**\n    *  @brief  This function is used to get the current encode height.\n    *  The encode height can be modified by Reconfigure() function.\n    */\n    int GetEncodeHeight() const { return m_nHeight; }\n\n    /**\n    *   @brief  This function is used to get the current frame size based on pixel format.\n    */\n    int GetFrameSize() const;\n\n    /**\n    *  @brief  This function is used to initialize config parameters based on\n    *          given codec and preset guids.\n    *  The application can call this function to get the default configuration\n    *  for a certain preset. The application can either use these parameters\n    *  directly or override them with application-specific settings before\n    *  using them in CreateEncoder() function.\n    */\n    void CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo = NV_ENC_TUNING_INFO_UNDEFINED);\n\n    /**\n    *  @brief  This function is used to get the current initialization parameters,\n    *          which had been used to configure the encoder session.\n    *  The initialization parameters are modified if the application calls\n    *  Reconfigure() function.\n    */\n    void GetInitializeParams(NV_ENC_INITIALIZE_PARAMS *pInitializeParams);\n\n    /**\n    *  @brief  This function is used to run motion estimation\n    *  This is used to run motion estimation on a a pair of frames. The\n    *  application must copy the reference frame data to the buffer obtained\n    *  by calling GetNextReferenceFrame(), and copy the input frame data to\n    *  the buffer obtained by calling GetNextInputFrame() before calling the\n    *  RunMotionEstimation() function.\n    */\n    void RunMotionEstimation(std::vector<uint8_t> &mvData);\n\n    /**\n    *  @brief This function is used to get an available reference frame.\n    *  Application must call this function to get a pointer to reference buffer,\n    *  to be used in the subsequent RunMotionEstimation() function.\n    */\n    const NvEncInputFrame* GetNextReferenceFrame();\n\n    /**\n    *  @brief This function is used to get sequence and picture parameter headers.\n    *  Application can call this function after encoder is initialized to get SPS and PPS\n    *  nalus for the current encoder instance. The sequence header data might change when\n    *  application calls Reconfigure() function.\n    */\n    void GetSequenceParams(std::vector<uint8_t> &seqParams);\n\n    /**\n    *  @brief  NvEncoder class virtual destructor.\n    */\n    virtual ~NvEncoder();\n\npublic:\n    /**\n    *  @brief This a static function to get chroma offsets for YUV planar formats.\n    */\n    static void GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch,\n                                        const uint32_t height, std::vector<uint32_t>& chromaOffsets);\n    /**\n    *  @brief This a static function to get the chroma plane pitch for YUV planar formats.\n    */\n    static uint32_t GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaPitch);\n\n    /**\n    *  @brief This a static function to get the number of chroma planes for YUV planar formats.\n    */\n    static uint32_t GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat);\n\n    /**\n    *  @brief This a static function to get the chroma plane width in bytes for YUV planar formats.\n    */\n    static uint32_t GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth);\n\n    /**\n    *  @brief This a static function to get the chroma planes height in bytes for YUV planar formats.\n    */\n    static uint32_t GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight);\n\n\n    /**\n    *  @brief This a static function to get the width in bytes for the frame.\n    *  For YUV planar format this is the width in bytes of the luma plane.\n    */\n    static uint32_t GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width);\n\n    /**\n    *  @brief This function returns the number of allocated buffers.\n    */\n    uint32_t GetEncoderBufferCount() const { return m_nEncoderBuffer; }\nprotected:\n\n    /**\n    *  @brief  NvEncoder class constructor.\n    *  NvEncoder class constructor cannot be called directly by the application.\n    */\n    NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void *pDevice, uint32_t nWidth, uint32_t nHeight,\n        NV_ENC_BUFFER_FORMAT eBufferFormat, uint32_t nOutputDelay, bool bMotionEstimationOnly, bool bOutputInVideoMemory = false, bool bDX12Encode = false,\n        bool bUseIVFContainer = true);\n\n    /**\n    *  @brief This function is used to check if hardware encoder is properly initialized.\n    */\n    bool IsHWEncoderInitialized() const { return m_hEncoder != NULL && m_bEncoderInitialized; }\n\n    /**\n    *  @brief This function is used to register CUDA, D3D or OpenGL input buffers with NvEncodeAPI.\n    *  This is non public function and is called by derived class for allocating\n    *  and registering input buffers.\n    */\n    void RegisterInputResources(std::vector<void*> inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType,\n        int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame = false);\n\n    /**\n    *  @brief This function is used to unregister resources which had been previously registered for encoding\n    *         using RegisterInputResources() function.\n    */\n    void UnregisterInputResources();\n\n    /**\n    *  @brief This function is used to register CUDA, D3D or OpenGL input or output buffers with NvEncodeAPI.\n    */\n    NV_ENC_REGISTERED_PTR RegisterResource(void *pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType,\n        int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage = NV_ENC_INPUT_IMAGE,\n        NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL);\n\n    /**\n    *  @brief This function returns maximum width used to open the encoder session.\n    *  All encode input buffers are allocated using maximum dimensions.\n    */\n    uint32_t GetMaxEncodeWidth() const { return m_nMaxEncodeWidth; }\n\n    /**\n    *  @brief This function returns maximum height used to open the encoder session.\n    *  All encode input buffers are allocated using maximum dimensions.\n    */\n    uint32_t GetMaxEncodeHeight() const { return m_nMaxEncodeHeight; }\n\n    /**\n    *  @brief This function returns the completion event.\n    */\n    void* GetCompletionEvent(uint32_t eventIdx) { return (m_vpCompletionEvent.size() == m_nEncoderBuffer) ? m_vpCompletionEvent[eventIdx] : nullptr; }\n\n    /**\n    *  @brief This function returns the current pixel format.\n    */\n    NV_ENC_BUFFER_FORMAT GetPixelFormat() const { return m_eBufferFormat; }\n\n    /**\n    *  @brief This function is used to submit the encode commands to the  \n    *         NVENC hardware.\n    */\n    NVENCSTATUS DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS *pPicParams);\n\n    /**\n    *  @brief This function is used to submit the encode commands to the \n    *         NVENC hardware for ME only mode.\n    */\n    NVENCSTATUS DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_INPUT_PTR inputBufferForReference, NV_ENC_OUTPUT_PTR outputBuffer);\n\n    /**\n    *  @brief This function is used to map the input buffers to NvEncodeAPI.\n    */\n    void MapResources(uint32_t bfrIdx);\n\n    /**\n    *  @brief This function is used to wait for completion of encode command.\n    */\n    void WaitForCompletionEvent(int iEvent);\n\n    /**\n    *  @brief This function is used to send EOS to HW encoder.\n    */\n    void SendEOS();\n\nprivate:\n    /**\n    *  @brief This is a private function which is used to check if there is any\n              buffering done by encoder.\n    *  The encoder generally buffers data to encode B frames or for lookahead\n    *  or pipelining.\n    */\n    bool IsZeroDelay() { return m_nOutputDelay == 0; }\n\n    /**\n    *  @brief This is a private function which is used to load the encode api shared library.\n    */\n    void LoadNvEncApi();\n\n    /**\n    *  @brief This is a private function which is used to get the output packets\n    *         from the encoder HW.\n    *  This is called by DoEncode() function. If there is buffering enabled,\n    *  this may return without any output data.\n    */\n    void GetEncodedPacket(std::vector<NV_ENC_OUTPUT_PTR> &vOutputBuffer, std::vector<std::vector<uint8_t>> &vPacket, bool bOutputDelay);\n\n    /**\n    *  @brief This is a private function which is used to initialize the bitstream buffers.\n    *  This is only used in the encoding mode.\n    */\n    void InitializeBitstreamBuffer();\n\n    /**\n    *  @brief This is a private function which is used to destroy the bitstream buffers.\n    *  This is only used in the encoding mode.\n    */\n    void DestroyBitstreamBuffer();\n\n    /**\n    *  @brief This is a private function which is used to initialize MV output buffers.\n    *  This is only used in ME-only Mode.\n    */\n    void InitializeMVOutputBuffer();\n\n    /**\n    *  @brief This is a private function which is used to destroy MV output buffers.\n    *  This is only used in ME-only Mode.\n    */\n    void DestroyMVOutputBuffer();\n\n    /**\n    *  @brief This is a private function which is used to destroy HW encoder.\n    */\n    void DestroyHWEncoder();\n\n    /**\n    *  @brief This function is used to flush the encoder queue.\n    */\n    void FlushEncoder();\n\nprivate:\n    /**\n    *  @brief This is a pure virtual function which is used to allocate input buffers.\n    *  The derived classes must implement this function.\n    */\n    virtual void AllocateInputBuffers(int32_t numInputBuffers) = 0;\n\n    /**\n    *  @brief This is a pure virtual function which is used to destroy input buffers.\n    *  The derived classes must implement this function.\n    */\n    virtual void ReleaseInputBuffers() = 0;\n\nprotected:\n    bool m_bMotionEstimationOnly = false;\n    bool m_bOutputInVideoMemory = false;\n    bool m_bIsDX12Encode = false;\n    void *m_hEncoder = nullptr;\n    NV_ENCODE_API_FUNCTION_LIST m_nvenc;\n    NV_ENC_INITIALIZE_PARAMS m_initializeParams = {};\n    std::vector<NvEncInputFrame> m_vInputFrames;\n    std::vector<NV_ENC_REGISTERED_PTR> m_vRegisteredResources;\n    std::vector<NvEncInputFrame> m_vReferenceFrames;\n    std::vector<NV_ENC_REGISTERED_PTR> m_vRegisteredResourcesForReference;\n    std::vector<NV_ENC_INPUT_PTR> m_vMappedInputBuffers;\n    std::vector<NV_ENC_INPUT_PTR> m_vMappedRefBuffers;\n    std::vector<void *> m_vpCompletionEvent;\n\n    int32_t m_iToSend = 0;\n    int32_t m_iGot = 0;\n    int32_t m_nEncoderBuffer = 0;\n    int32_t m_nOutputDelay = 0;\n    IVFUtils m_IVFUtils;\n    bool m_bWriteIVFFileHeader = true;\n    bool m_bUseIVFContainer = true;\n\nprivate:\n    uint32_t m_nWidth;\n    uint32_t m_nHeight;\n    NV_ENC_BUFFER_FORMAT m_eBufferFormat;\n    void *m_pDevice;\n    NV_ENC_DEVICE_TYPE m_eDeviceType;\n    NV_ENC_CONFIG m_encodeConfig = {};\n    bool m_bEncoderInitialized = false;\n    uint32_t m_nExtraOutputDelay = 3; // To ensure encode and graphics can work in parallel, m_nExtraOutputDelay should be set to at least 1\n    std::vector<NV_ENC_OUTPUT_PTR> m_vBitstreamOutputBuffer;\n    std::vector<NV_ENC_OUTPUT_PTR> m_vMVDataOutputBuffer;\n    uint32_t m_nMaxEncodeWidth = 0;\n    uint32_t m_nMaxEncodeHeight = 0;\n    void* m_hModule = nullptr;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.cpp",
    "content": "/*\n* Copyright 2017-2022 NVIDIA Corporation.  All rights reserved.\n*\n* Please refer to the NVIDIA end user license agreement (EULA) associated\n* with this source code for terms and conditions that govern your use of\n* this software. Any use, reproduction, disclosure, or distribution of\n* this software and related documentation outside the terms of the EULA\n* is strictly prohibited.\n*\n*/\n\n\n#ifndef _WIN32\n#include <dlfcn.h>\n#endif\n#include \"NvEncoderD3D11.h\"\n\n#ifndef MAKEFOURCC\n#define MAKEFOURCC(a,b,c,d) (((unsigned int)a) | (((unsigned int)b)<< 8) | (((unsigned int)c)<<16) | (((unsigned int)d)<<24) )\n#endif\n\nDXGI_FORMAT GetD3D11Format(NV_ENC_BUFFER_FORMAT eBufferFormat)\n{\n    switch (eBufferFormat)\n    {\n    case NV_ENC_BUFFER_FORMAT_NV12:\n        return DXGI_FORMAT_NV12;\n    case NV_ENC_BUFFER_FORMAT_ARGB:\n        return DXGI_FORMAT_B8G8R8A8_UNORM;\n\tcase NV_ENC_BUFFER_FORMAT_ABGR:\n\t\treturn DXGI_FORMAT_R8G8B8A8_UNORM;\n    case NV_ENC_BUFFER_FORMAT_ABGR10:\n\t\treturn DXGI_FORMAT_R8G8B8A8_UNORM;\n    case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:\n        return DXGI_FORMAT_P010;\n    default:\n        return DXGI_FORMAT_UNKNOWN;\n    }\n}\n\nNvEncoderD3D11::NvEncoderD3D11(ID3D11Device* pD3D11Device, uint32_t nWidth, uint32_t nHeight,\n    NV_ENC_BUFFER_FORMAT eBufferFormat,  uint32_t nExtraOutputDelay, bool bMotionEstimationOnly, bool bOutputInVideoMemory) :\n    NvEncoder(NV_ENC_DEVICE_TYPE_DIRECTX, pD3D11Device, nWidth, nHeight, eBufferFormat, nExtraOutputDelay, bMotionEstimationOnly, bOutputInVideoMemory)\n{\n    if (!pD3D11Device)\n    {\n        NVENC_THROW_ERROR(\"Bad d3d11device ptr\", NV_ENC_ERR_INVALID_PTR);\n        return;\n    }\n\n    if (GetD3D11Format(GetPixelFormat()) == DXGI_FORMAT_UNKNOWN)\n    {\n        NVENC_THROW_ERROR(\"Unsupported Buffer format\", NV_ENC_ERR_INVALID_PARAM);\n    }\n\n    if (!m_hEncoder)\n    {\n        NVENC_THROW_ERROR(\"Encoder Initialization failed\", NV_ENC_ERR_INVALID_DEVICE);\n    }\n\n    m_pD3D11Device = pD3D11Device;\n    m_pD3D11Device->AddRef();\n    m_pD3D11Device->GetImmediateContext(&m_pD3D11DeviceContext);\n}\n\nNvEncoderD3D11::~NvEncoderD3D11() \n{\n    ReleaseD3D11Resources();\n}\n\nvoid NvEncoderD3D11::AllocateInputBuffers(int32_t numInputBuffers)\n{\n    if (!IsHWEncoderInitialized())\n    {\n        NVENC_THROW_ERROR(\"Encoder intialization failed\", NV_ENC_ERR_ENCODER_NOT_INITIALIZED);\n    }\n\n    // for MEOnly mode we need to allocate seperate set of buffers for reference frame\n    int numCount = m_bMotionEstimationOnly ? 2 : 1;\n    for (int count = 0; count < numCount; count++)\n    {\n        std::vector<void*> inputFrames;\n        for (int i = 0; i < numInputBuffers; i++)\n        {\n            ID3D11Texture2D *pInputTextures = NULL;\n            D3D11_TEXTURE2D_DESC desc;\n            ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));\n            desc.Width = GetMaxEncodeWidth();\n            desc.Height = GetMaxEncodeHeight();\n            desc.MipLevels = 1;\n            desc.ArraySize = 1;\n            desc.Format = GetD3D11Format(GetPixelFormat());\n            desc.SampleDesc.Count = 1;\n            desc.Usage = D3D11_USAGE_DEFAULT;\n            desc.BindFlags = D3D11_BIND_RENDER_TARGET;\n            desc.CPUAccessFlags = 0;\n            if (m_pD3D11Device->CreateTexture2D(&desc, NULL, &pInputTextures) != S_OK)\n            {\n                NVENC_THROW_ERROR(\"Failed to create d3d11textures\", NV_ENC_ERR_OUT_OF_MEMORY);\n            }\n            inputFrames.push_back(pInputTextures);\n        }\n        RegisterInputResources(inputFrames, NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX, \n            GetMaxEncodeWidth(), GetMaxEncodeHeight(), 0, GetPixelFormat(), count == 1 ? true : false);\n    }\n}\n\nvoid NvEncoderD3D11::ReleaseInputBuffers()\n{\n    ReleaseD3D11Resources();\n}\n\nvoid NvEncoderD3D11::ReleaseD3D11Resources()\n{\n    if (!m_hEncoder)\n    {\n        return;\n    }\n\n    UnregisterInputResources();\n\n    for (uint32_t i = 0; i < m_vInputFrames.size(); ++i)\n    {\n        if (m_vInputFrames[i].inputPtr)\n        {\n            reinterpret_cast<ID3D11Texture2D*>(m_vInputFrames[i].inputPtr)->Release();\n        }\n    }\n    m_vInputFrames.clear();\n\n    for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i)\n    {\n        if (m_vReferenceFrames[i].inputPtr)\n        {\n            reinterpret_cast<ID3D11Texture2D*>(m_vReferenceFrames[i].inputPtr)->Release();\n        }\n    }\n    m_vReferenceFrames.clear();\n\n    if (m_pD3D11DeviceContext)\n    {\n        m_pD3D11DeviceContext->Release();\n        m_pD3D11DeviceContext = nullptr;\n    }\n\n    if (m_pD3D11Device)\n    {\n        m_pD3D11Device->Release();\n        m_pD3D11Device = nullptr;\n    }\n}\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/NvEncoderD3D11.h",
    "content": "/*\n* Copyright 2017-2022 NVIDIA Corporation.  All rights reserved.\n*\n* Please refer to the NVIDIA end user license agreement (EULA) associated\n* with this source code for terms and conditions that govern your use of\n* this software. Any use, reproduction, disclosure, or distribution of\n* this software and related documentation outside the terms of the EULA\n* is strictly prohibited.\n*\n*/\n\n#pragma once\n\n#include <vector>\n#include <stdint.h>\n#include <mutex>\n#include <unordered_map>\n#include <d3d11.h>\n#include \"NvEncoder.h\"\n\nclass NvEncoderD3D11 : public NvEncoder\n{\npublic:\n    NvEncoderD3D11(ID3D11Device* pD3D11Device, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, \n        uint32_t nExtraOutputDelay = 3, bool bMotionEstimationOnly = false,  bool bOPInVideoMemory = false);\n    virtual ~NvEncoderD3D11();\n\nprotected:\n    /**\n    *  @brief This function is used to release the input buffers allocated for encoding.\n    *  This function is an override of virtual function NvEncoder::ReleaseInputBuffers().\n    */\n    virtual void ReleaseInputBuffers() override;\n\nprivate:\n    /**\n    *  @brief This function is used to allocate input buffers for encoding.\n    *  This function is an override of virtual function NvEncoder::AllocateInputBuffers().\n    *  This function creates ID3D11Texture2D textures which is used to accept input data.\n    *  To obtain handle to input buffers application must call NvEncoder::GetNextInputFrame()\n    */\n    virtual void AllocateInputBuffers(int32_t numInputBuffers) override;\n\nprivate:\n    /**\n    *  @brief This is a private function to release ID3D11Texture2D textures used for encoding.\n    */\n    void ReleaseD3D11Resources();\n\nprotected:\n    ID3D11Device *m_pD3D11Device = nullptr;\n\nprivate:\n    ID3D11DeviceContext* m_pD3D11DeviceContext = nullptr;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.cpp",
    "content": "#include \"OvrDirectModeComponent.h\"\n\nOvrDirectModeComponent::OvrDirectModeComponent(\n    std::shared_ptr<CD3DRender> pD3DRender, std::shared_ptr<PoseHistory> poseHistory\n)\n    : m_pD3DRender(pD3DRender)\n    , m_poseHistory(poseHistory)\n    , m_submitLayer(0) { }\n\nvoid OvrDirectModeComponent::SetEncoder(std::shared_ptr<CEncoder> pEncoder) {\n    m_pEncoder = pEncoder;\n}\n\n/** Specific to Oculus compositor support, textures supplied must be created using this method. */\nvoid OvrDirectModeComponent::CreateSwapTextureSet(\n    uint32_t unPid,\n    const SwapTextureSetDesc_t* pSwapTextureSetDesc,\n    SwapTextureSet_t* pOutSwapTextureSet\n) {\n    Debug(\n        \"OvrDirectModeComponent::CreateSwapTextureSet pid=%d Format=%d %dx%d SampleCount=%d\",\n        unPid,\n        pSwapTextureSetDesc->nFormat,\n        pSwapTextureSetDesc->nWidth,\n        pSwapTextureSetDesc->nHeight,\n        pSwapTextureSetDesc->nSampleCount\n    );\n\n    // HRESULT hr = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL,\n    // 0, D3D11_SDK_VERSION, &pDevice, &eFeatureLevel, &pContext);\n\n    D3D11_TEXTURE2D_DESC SharedTextureDesc = {};\n    DXGI_FORMAT format = (DXGI_FORMAT)pSwapTextureSetDesc->nFormat;\n    SharedTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;\n    if (format == DXGI_FORMAT_R32G8X24_TYPELESS || format == DXGI_FORMAT_R32_TYPELESS) {\n        SharedTextureDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\n    }\n    SharedTextureDesc.ArraySize = 1;\n    SharedTextureDesc.MipLevels = 1;\n    SharedTextureDesc.SampleDesc.Count\n        = pSwapTextureSetDesc->nSampleCount == 0 ? 1 : pSwapTextureSetDesc->nSampleCount;\n    SharedTextureDesc.SampleDesc.Quality = 0;\n    SharedTextureDesc.Usage = D3D11_USAGE_DEFAULT;\n    SharedTextureDesc.Format = format;\n\n    // Some(or all?) applications request larger texture than we specified in\n    // GetRecommendedRenderTargetSize. But, we must create textures in requested size to prevent\n    // cropped output. And then we must shrink texture to H.264 movie size.\n    SharedTextureDesc.Width = pSwapTextureSetDesc->nWidth;\n    SharedTextureDesc.Height = pSwapTextureSetDesc->nHeight;\n\n    // SharedTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX |\n    // D3D11_RESOURCE_MISC_SHARED_NTHANDLE;\n    SharedTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;\n\n    ProcessResource* processResource = new ProcessResource();\n    processResource->pid = unPid;\n\n    for (int i = 0; i < 3; i++) {\n        HRESULT hr = m_pD3DRender->GetDevice()->CreateTexture2D(\n            &SharedTextureDesc, NULL, &processResource->textures[i]\n        );\n        // LogDriver(\"texture%d %p res:%d %s\", i, texture[i], hr, GetDxErrorStr(hr).c_str());\n        if (FAILED(hr)) {\n            Error(\"CreateSwapTextureSet CreateTexture2D %p %ls\", hr, GetErrorStr(hr).c_str());\n            delete processResource;\n            break;\n        }\n\n        IDXGIResource* pResource;\n        hr = processResource->textures[i]->QueryInterface(\n            __uuidof(IDXGIResource), (void**)&pResource\n        );\n        if (FAILED(hr)) {\n            Error(\"CreateSwapTextureSet QueryInterface %p %ls\", hr, GetErrorStr(hr).c_str());\n            delete processResource;\n            break;\n        }\n        // LogDriver(\"QueryInterface %p res:%d %s\", pResource, hr, GetDxErrorStr(hr).c_str());\n\n        hr = pResource->GetSharedHandle(&processResource->sharedHandles[i]);\n        if (FAILED(hr)) {\n            Error(\"CreateSwapTextureSet GetSharedHandle %p %ls\", hr, GetErrorStr(hr).c_str());\n            delete processResource;\n            pResource->Release();\n            break;\n        }\n        // LogDriver(\"GetSharedHandle %p res:%d %s\", processResource->sharedHandles[i], hr,\n        // GetDxErrorStr(hr).c_str());\n\n        m_handleMap.insert(\n            std::make_pair(processResource->sharedHandles[i], std::make_pair(processResource, i))\n        );\n\n        pOutSwapTextureSet->rSharedTextureHandles[i]\n            = (vr::SharedTextureHandle_t)processResource->sharedHandles[i];\n\n        pResource->Release();\n\n        Debug(\"Created Texture %d %p\", i, processResource->sharedHandles[i]);\n    }\n    // m_processMap.insert(std::pair<uint32_t, ProcessResource *>(unPid, processResource));\n}\n\n/** Used to textures created using CreateSwapTextureSet.  Only one of the set's handles needs to be\n * used to destroy the entire set. */\nvoid OvrDirectModeComponent::DestroySwapTextureSet(vr::SharedTextureHandle_t sharedTextureHandle) {\n    Debug(\"OvrDirectModeComponent::DestroySwapTextureSet %p\", sharedTextureHandle);\n\n    auto it = m_handleMap.find((HANDLE)sharedTextureHandle);\n    if (it != m_handleMap.end()) {\n        // Release all reference (a bit forcible)\n        ProcessResource* p = it->second.first;\n        m_handleMap.erase(p->sharedHandles[0]);\n        m_handleMap.erase(p->sharedHandles[1]);\n        m_handleMap.erase(p->sharedHandles[2]);\n        delete p;\n    } else {\n        Debug(\"Requested to destroy not managing texture. handle:%p\", sharedTextureHandle);\n    }\n}\n\n/** Used to purge all texture sets for a given process. */\nvoid OvrDirectModeComponent::DestroyAllSwapTextureSets(uint32_t unPid) {\n    Debug(\"OvrDirectModeComponent::DestroyAllSwapTextureSets pid=%d\", unPid);\n\n    for (auto it = m_handleMap.begin(); it != m_handleMap.end();) {\n        if (it->second.first->pid == unPid) {\n            if (it->second.second == 0) {\n                delete it->second.first;\n            }\n            m_handleMap.erase(it++);\n        } else {\n            ++it;\n        }\n    }\n}\n\n/** After Present returns, calls this to get the next index to use for rendering. */\nvoid OvrDirectModeComponent::GetNextSwapTextureSetIndex(\n    vr::SharedTextureHandle_t sharedTextureHandles[2], uint32_t (*pIndices)[2]\n) {\n    Debug(\"OvrDirectModeComponent::GetNextSwapTextureSetIndex\");\n\n    (*pIndices)[0]++;\n    (*pIndices)[0] %= 3;\n    (*pIndices)[1]++;\n    (*pIndices)[1] %= 3;\n}\n\n/** Call once per layer to draw for this frame.  One shared texture handle per eye.  Textures must\n * be created using CreateSwapTextureSet and should be alternated per frame.  Call Present once all\n * layers have been submitted. */\nvoid OvrDirectModeComponent::SubmitLayer(const SubmitLayerPerEye_t (&perEye)[2]) {\n    Debug(\"OvrDirectModeComponent::SubmitLayer\");\n\n    m_presentMutex.lock();\n\n    // mHmdPose is the same pose for both eyes, getting the eye view pose\n    //  requires some records keeping, unfortunately (m_eyeToHead)\n    auto pPose = &perEye[0].mHmdPose;\n\n    if (m_submitLayer == 0) {\n        // Detect FrameIndex of submitted frame by pPose.\n        // This is important part to achieve smooth headtracking.\n        // We search for history of TrackingInfo and find the TrackingInfo which have nearest matrix\n        // value.\n\n        auto pose = m_poseHistory->GetBestPoseMatch(*pPose);\n        if (pose) {\n            // found the frameIndex\n            m_prevTargetTimestampNs = m_targetTimestampNs;\n            m_targetTimestampNs = pose->targetTimestampNs;\n\n            m_prevFramePoseRotation = m_framePoseRotation;\n            m_framePoseRotation.x = pose->motion.pose.orientation.x;\n            m_framePoseRotation.y = pose->motion.pose.orientation.y;\n            m_framePoseRotation.z = pose->motion.pose.orientation.z;\n            m_framePoseRotation.w = pose->motion.pose.orientation.w;\n        } else {\n            m_targetTimestampNs = 0;\n            m_framePoseRotation = HmdQuaternion_Init(0.0, 0.0, 0.0, 0.0);\n        }\n    }\n    if (m_submitLayer < MAX_LAYERS) {\n        m_submitLayers[m_submitLayer][0] = perEye[0];\n        m_submitLayers[m_submitLayer][1] = perEye[1];\n        m_submitLayer++;\n    } else {\n        Warn(\"Too many layers submitted!\");\n    }\n\n    // CopyTexture();\n\n    m_presentMutex.unlock();\n}\n\n/** Submits queued layers for display. */\nvoid OvrDirectModeComponent::Present(vr::SharedTextureHandle_t syncTexture) {\n    Debug(\"OvrDirectModeComponent::Present\");\n\n    m_presentMutex.lock();\n\n    ReportPresent(m_targetTimestampNs, 0);\n\n    bool useMutex = true;\n\n    IDXGIKeyedMutex* pKeyedMutex = NULL;\n\n    uint32_t layerCount = m_submitLayer;\n    m_submitLayer = 0;\n\n    if (m_prevTargetTimestampNs == m_targetTimestampNs) {\n        Debug(\"Discard duplicated frame. FrameIndex=%llu (Ignoring)\", m_targetTimestampNs);\n        // return;\n    }\n\n    ID3D11Texture2D* pSyncTexture = m_pD3DRender->GetSharedTexture((HANDLE)syncTexture);\n    if (!pSyncTexture) {\n        Warn(\"[VDispDvr] SyncTexture is NULL!\");\n        m_presentMutex.unlock();\n        return;\n    }\n\n    if (useMutex) {\n        // Access to shared texture must be wrapped in AcquireSync/ReleaseSync\n        // to ensure the compositor has finished rendering to it before it gets used.\n        // This enforces scheduling of work on the gpu between processes.\n        if (SUCCEEDED(pSyncTexture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&pKeyedMutex)\n            )) {\n            // TODO: Reasonable timeout and timeout handling\n            HRESULT hr = pKeyedMutex->AcquireSync(0, 10);\n            if (hr != S_OK) {\n                Debug(\n                    \"[VDispDvr] ACQUIRESYNC FAILED!!! hr=%d %p %ls\", hr, hr, GetErrorStr(hr).c_str()\n                );\n                pKeyedMutex->Release();\n                m_presentMutex.unlock();\n                return;\n            }\n        }\n    }\n\n    CopyTexture(layerCount);\n\n    if (useMutex) {\n        if (pKeyedMutex) {\n            pKeyedMutex->ReleaseSync(0);\n            pKeyedMutex->Release();\n        }\n    }\n\n    ReportComposed(m_targetTimestampNs, 0);\n\n    if (m_pEncoder) {\n        m_pEncoder->NewFrameReady();\n    }\n\n    m_presentMutex.unlock();\n}\n\nvoid OvrDirectModeComponent::PostPresent() {\n    Debug(\"OvrDirectModeComponent::PostPresent\");\n\n    WaitForVSync();\n}\n\nvoid OvrDirectModeComponent::CopyTexture(uint32_t layerCount) {\n\n    uint64_t presentationTime = GetTimestampUs();\n\n    ID3D11Texture2D* pTexture[MAX_LAYERS][2];\n    ComPtr<ID3D11Texture2D> Texture[MAX_LAYERS][2];\n    vr::VRTextureBounds_t bounds[MAX_LAYERS][2];\n    vr::HmdMatrix34_t poses[MAX_LAYERS];\n\n    for (uint32_t i = 0; i < layerCount; i++) {\n        // Find left eye texture.\n        HANDLE leftEyeTexture = (HANDLE)m_submitLayers[i][0].hTexture;\n        auto it = m_handleMap.find(leftEyeTexture);\n        if (it == m_handleMap.end()) {\n            // Ignore this layer.\n            Debug(\n                \"Submitted texture is not found on HandleMap. eye=right layer=%d/%d Texture \"\n                \"Handle=%p\",\n                i,\n                layerCount,\n                leftEyeTexture\n            );\n        } else {\n            Texture[i][0] = it->second.first->textures[it->second.second];\n            D3D11_TEXTURE2D_DESC desc;\n            Texture[i][0]->GetDesc(&desc);\n\n            // Find right eye texture.\n            HANDLE rightEyeTexture = (HANDLE)m_submitLayers[i][1].hTexture;\n            it = m_handleMap.find(rightEyeTexture);\n            if (it == m_handleMap.end()) {\n                // Ignore this layer\n                Debug(\n                    \"Submitted texture is not found on HandleMap. eye=left layer=%d/%d Texture \"\n                    \"Handle=%p\",\n                    i,\n                    layerCount,\n                    rightEyeTexture\n                );\n                Texture[i][0].Reset();\n            } else {\n                Texture[i][1] = it->second.first->textures[it->second.second];\n            }\n        }\n\n        pTexture[i][0] = Texture[i][0].Get();\n        pTexture[i][1] = Texture[i][1].Get();\n        bounds[i][0] = m_submitLayers[i][0].bounds;\n        bounds[i][1] = m_submitLayers[i][1].bounds;\n        poses[i] = m_submitLayers[i][0].mHmdPose;\n    }\n\n    // This can go away, but is useful to see it as a separate packet on the gpu in traces.\n    m_pD3DRender->GetContext()->Flush();\n\n    if (m_pEncoder) {\n        // Wait for the encoder to be ready.  This is important because the encoder thread\n        // blocks on transmit which uses our shared d3d context (which is not thread safe).\n        m_pEncoder->WaitForEncode();\n\n        std::string debugText;\n\n        uint64_t submitFrameIndex = m_targetTimestampNs;\n\n        // Copy entire texture to staging so we can read the pixels to send to remote device.\n        m_pEncoder->CopyToStaging(\n            pTexture,\n            bounds,\n            poses,\n            layerCount,\n            false,\n            presentationTime,\n            submitFrameIndex,\n            \"\",\n            debugText\n        );\n\n        m_pD3DRender->GetContext()->Flush();\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/OvrDirectModeComponent.h",
    "content": "#pragma once\n#include \"CEncoder.h\"\n#include \"alvr_server/PoseHistory.h\"\n#include \"alvr_server/Utils.h\"\n#include \"alvr_server/openvr_driver_wrap.h\"\n\n#include \"alvr_server/Settings.h\"\n\n#include <mutex>\n\nclass OvrDirectModeComponent : public vr::IVRDriverDirectModeComponent {\npublic:\n    OvrDirectModeComponent(\n        std::shared_ptr<CD3DRender> pD3DRender, std::shared_ptr<PoseHistory> poseHistory\n    );\n\n    void SetEncoder(std::shared_ptr<CEncoder> pEncoder);\n\n    /** Specific to Oculus compositor support, textures supplied must be created using this method.\n     */\n    virtual void CreateSwapTextureSet(\n        uint32_t unPid,\n        const SwapTextureSetDesc_t* pSwapTextureSetDesc,\n        SwapTextureSet_t* pOutSwapTextureSet\n    );\n\n    /** Used to textures created using CreateSwapTextureSet.  Only one of the set's handles needs to\n     * be used to destroy the entire set. */\n    virtual void DestroySwapTextureSet(vr::SharedTextureHandle_t sharedTextureHandle);\n\n    /** Used to purge all texture sets for a given process. */\n    virtual void DestroyAllSwapTextureSets(uint32_t unPid);\n\n    /** After Present returns, calls this to get the next index to use for rendering. */\n    virtual void GetNextSwapTextureSetIndex(\n        vr::SharedTextureHandle_t sharedTextureHandles[2], uint32_t (*pIndices)[2]\n    );\n\n    /** Call once per layer to draw for this frame.  One shared texture handle per eye.  Textures\n     * must be created using CreateSwapTextureSet and should be alternated per frame.  Call Present\n     * once all layers have been submitted. */\n    virtual void SubmitLayer(const SubmitLayerPerEye_t (&perEye)[2]);\n\n    /** Submits queued layers for display. */\n    virtual void Present(vr::SharedTextureHandle_t syncTexture);\n\n    /** Called after Present to allow driver to take more time until vsync after they've\n     * successfully acquired the sync texture in Present.*/\n    virtual void PostPresent();\n\n    void CopyTexture(uint32_t layerCount);\n\nprivate:\n    std::shared_ptr<CD3DRender> m_pD3DRender;\n    std::shared_ptr<CEncoder> m_pEncoder;\n    std::shared_ptr<PoseHistory> m_poseHistory;\n\n    // Resource for each process\n    struct ProcessResource {\n        ComPtr<ID3D11Texture2D> textures[3];\n        HANDLE sharedHandles[3];\n        uint32_t pid;\n    };\n    std::map<HANDLE, std::pair<ProcessResource*, int>> m_handleMap;\n\n    static const int MAX_LAYERS = 10;\n    int m_submitLayer;\n    SubmitLayerPerEye_t m_submitLayers[MAX_LAYERS][2];\n    vr::HmdQuaternion_t m_prevFramePoseRotation;\n    vr::HmdQuaternion_t m_framePoseRotation;\n    uint64_t m_targetTimestampNs;\n    uint64_t m_prevTargetTimestampNs;\n\n    std::mutex m_presentMutex;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoder.cpp",
    "content": "#include \"VideoEncoder.h\"\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoder.h",
    "content": "#pragma once\n\n#include \"NvEncoderD3D11.h\"\n#include \"shared/d3drender.h\"\n#include <functional>\n#include <memory>\n\nclass VideoEncoder {\npublic:\n    virtual void Initialize() = 0;\n    virtual void Shutdown() = 0;\n\n    virtual void Transmit(\n        ID3D11Texture2D* pTexture,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        bool insertIDR\n    ) = 0;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderAMF.cpp",
    "content": "#include \"VideoEncoderAMF.h\"\n\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n\n#define AMF_THROW_IF(expr)                                                                         \\\n    {                                                                                              \\\n        AMF_RESULT res = expr;                                                                     \\\n        if (res != AMF_OK) {                                                                       \\\n            throw MakeException(\"AMF Error %d. %s\", res, L#expr);                                  \\\n        }                                                                                          \\\n    }\n\nconst wchar_t* VideoEncoderAMF::START_TIME_PROPERTY = L\"StartTimeProperty\";\nconst wchar_t* VideoEncoderAMF::FRAME_INDEX_PROPERTY = L\"FrameIndexProperty\";\n\nAMFPipe::AMFPipe(amf::AMFComponentPtr src, AMFDataReceiver receiver)\n    : m_amfComponentSrc(src)\n    , m_receiver(receiver) { }\n\nAMFPipe::~AMFPipe() {\n    Debug(\"AMFPipe::~AMFPipe()  m_amfComponentSrc->Drain\\n\");\n    m_amfComponentSrc->Drain();\n}\n\nvoid AMFPipe::doPassthrough(bool hasQueryTimeout, uint32_t timerResolution) {\n    amf::AMFDataPtr data = nullptr;\n    if (hasQueryTimeout) {\n        AMF_RESULT res = m_amfComponentSrc->QueryOutput(&data);\n        if (res == AMF_OK && data) {\n            m_receiver(data);\n        } else {\n            Debug(\"Failed to get AMF component data. Last status: %d.\\n\", res);\n        }\n    } else {\n        uint16_t timeout = 1000; // 1s timeout\n        AMF_RESULT res = m_amfComponentSrc->QueryOutput(&data);\n\n        timeBeginPeriod(timerResolution);\n        while (!data && --timeout != 0) {\n            amf_sleep(1);\n            res = m_amfComponentSrc->QueryOutput(&data);\n        }\n        timeEndPeriod(timerResolution);\n\n        if (data) {\n            m_receiver(data);\n        } else {\n            Debug(\"Failed to get AMF component data. Last status: %d.\\n\", res);\n        }\n    }\n}\n\nAMFSolidPipe::AMFSolidPipe(amf::AMFComponentPtr src, amf::AMFComponentPtr dst)\n    : AMFPipe(src, std::bind(&AMFSolidPipe::Passthrough, this, std::placeholders::_1))\n    , m_amfComponentDst(dst) { }\n\nvoid AMFSolidPipe::Passthrough(AMFDataPtr data) {\n    auto res = m_amfComponentDst->SubmitInput(data);\n    switch (res) {\n    case AMF_OK:\n        break;\n    case AMF_INPUT_FULL:\n        Debug(\"m_amfComponentDst->SubmitInput returns AMF_INPUT_FULL.\\n\");\n        break;\n    case AMF_NEED_MORE_INPUT:\n        Debug(\"m_amfComponentDst->SubmitInput returns AMF_NEED_MORE_INPUT.\\n\");\n        break;\n    default:\n        Debug(\"m_amfComponentDst->SubmitInput returns code %d.\\n\", res);\n        break;\n    }\n}\n\nAMFPipeline::AMFPipeline()\n    : m_pipes() {\n    TIMECAPS tc;\n    m_timerResolution = timeGetDevCaps(&tc, sizeof(tc)) == TIMERR_NOERROR ? tc.wPeriodMin : 1;\n}\n\nAMFPipeline::~AMFPipeline() {\n    for (auto& pipe : m_pipes) {\n        delete pipe;\n    }\n}\n\nvoid AMFPipeline::Connect(AMFPipePtr pipe) { m_pipes.emplace_back(pipe); }\n\nvoid AMFPipeline::Run(bool hasQueryTimeout) {\n    for (auto& pipe : m_pipes) {\n        pipe->doPassthrough(hasQueryTimeout, m_timerResolution);\n    }\n}\n\n//\n// VideoEncoderAMF\n//\n\nVideoEncoderAMF::VideoEncoderAMF(std::shared_ptr<CD3DRender> d3dRender, int width, int height)\n    : m_d3dRender(d3dRender)\n    , m_codec(Settings::Instance().m_codec)\n    , m_refreshRate(Settings::Instance().m_refreshRate)\n    , m_renderWidth(width)\n    , m_renderHeight(height)\n    , m_bitrateInMBits(30)\n    , m_surfaceFormat(amf::AMF_SURFACE_RGBA)\n    , m_use10bit(Settings::Instance().m_use10bitEncoder)\n    , m_hasQueryTimeout(false) {\n    if (Settings::Instance().m_enableHdr) {\n        // Bypass preprocessor and converters for HDR, since it will already be YUV\n        m_surfaceFormat = m_use10bit ? amf::AMF_SURFACE_P010 : amf::AMF_SURFACE_NV12;\n    }\n}\n\nVideoEncoderAMF::~VideoEncoderAMF() { }\n\namf::AMFComponentPtr VideoEncoderAMF::MakeEncoder(\n    amf::AMF_SURFACE_FORMAT inputFormat,\n    int width,\n    int height,\n    int codec,\n    int refreshRate,\n    int bitrateInMbits\n) {\n    const wchar_t* pCodec;\n\n    amf_int32 frameRateIn = refreshRate;\n    amf_int64 bitRateIn = bitrateInMbits * 1'000'000L; // in bits\n\n    switch (codec) {\n    case ALVR_CODEC_H264:\n        if (m_use10bit) {\n            throw MakeException(\"H.264 10-bit encoding is not supported\");\n        }\n        pCodec = AMFVideoEncoderVCE_AVC;\n        break;\n    case ALVR_CODEC_HEVC:\n        pCodec = AMFVideoEncoder_HEVC;\n        break;\n    case ALVR_CODEC_AV1:\n        pCodec = AMFVideoEncoder_AV1;\n        break;\n    default:\n        throw MakeException(\"Unsupported video encoding %d\", codec);\n    }\n\n    amf::AMFComponentPtr amfEncoder;\n    // Create encoder component.\n    AMF_THROW_IF(g_AMFFactory.GetFactory()->CreateComponent(m_amfContext, pCodec, &amfEncoder));\n\n    switch (codec) {\n    case ALVR_CODEC_H264: {\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_USAGE, AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY);\n        switch (Settings::Instance().m_h264Profile) {\n        case ALVR_H264_PROFILE_BASELINE:\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE, AMF_VIDEO_ENCODER_PROFILE_BASELINE);\n            break;\n        case ALVR_H264_PROFILE_MAIN:\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE, AMF_VIDEO_ENCODER_PROFILE_MAIN);\n            break;\n        default:\n        case ALVR_H264_PROFILE_HIGH:\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE, AMF_VIDEO_ENCODER_PROFILE_HIGH);\n            break;\n        }\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PROFILE_LEVEL, 42);\n        switch (Settings::Instance().m_rateControlMode) {\n        case ALVR_CBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR\n            );\n            // Required for CBR to work correctly\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_FILLER_DATA_ENABLE, Settings::Instance().m_fillerData\n            );\n            break;\n        case ALVR_VBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD,\n                AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR\n            );\n            break;\n        }\n\n        switch (Settings::Instance().m_entropyCoding) {\n        case ALVR_CABAC:\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_CABAC_ENABLE, AMF_VIDEO_ENCODER_CABAC);\n            break;\n        case ALVR_CAVLC:\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_CABAC_ENABLE, AMF_VIDEO_ENCODER_CALV);\n            break;\n        }\n\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FRAMESIZE, ::AMFConstructSize(width, height));\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, ::AMFConstructRate(frameRateIn, 1));\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_B_PIC_PATTERN, 0);\n\n        switch (Settings::Instance().m_encoderQualityPreset) {\n        case ALVR_QUALITY:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY\n            );\n            break;\n        case ALVR_BALANCED:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED\n            );\n            break;\n        case ALVR_SPEED:\n        default:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_QUALITY_PRESET, AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED\n            );\n            break;\n        }\n\n        amf::AMFCapsPtr caps;\n        if (amfEncoder->GetCaps(&caps) == AMF_OK) {\n            caps->GetProperty(AMF_VIDEO_ENCODER_CAP_PRE_ANALYSIS, &m_hasPreAnalysis);\n            caps->GetProperty(AMF_VIDEO_ENCODER_CAPS_QUERY_TIMEOUT_SUPPORT, &m_hasQueryTimeout);\n        }\n\n        if (Settings::Instance().m_enableAmfPreAnalysis) {\n            if (!Settings::Instance().m_useAmfPreproc || Settings::Instance().m_use10bitEncoder) {\n                Warn(\"Pre-analysis could not be enabled because \\\"Use preproc\\\" is not enabled or \"\n                     \"\\\"Reduce color banding\\\" is enabled.\");\n            } else if (m_hasPreAnalysis) {\n                Warn(\"Enabling h264 pre-analysis. You may experience higher latency when this is \"\n                     \"enabled.\");\n                amfEncoder->SetProperty(\n                    AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE,\n                    Settings::Instance().m_enableAmfPreAnalysis\n                );\n            } else {\n                Warn(\"Pre-analysis could not be enabled because your GPU does not support it for \"\n                     \"h264 encoding.\");\n            }\n        }\n\n        // Enable Full Range\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, true);\n\n        // Use BT.2020 for HDR encoding, BT.709 otherwise.\n        if (Settings::Instance().m_enableHdr) {\n            // Also specify the input formats for HDR to prevent incorrect color conversions\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_INPUT_COLOR_PROFILE, AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_INPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_INPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020\n            );\n\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020\n            );\n        } else {\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT709\n            );\n        }\n\n        // No noticable performance difference and should improve subjective quality by allocating\n        // more bits to smooth areas\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_ENABLE_VBAQ, Settings::Instance().m_enableVbaq);\n\n        // May impact performance but improves quality in high-motion areas\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HIGH_MOTION_QUALITY_BOOST_ENABLE, Settings::Instance().m_enableAmfHmqb\n        );\n\n        // Turns Off IDR/I Frames\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_IDR_PERIOD, 0);\n\n        // Disable AUD to produce the same stream format as VideoEncoderNVENC.\n        // FIXME: This option doesn't work in 22.10.3, but works in versions prior 22.5.1\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_INSERT_AUD, false);\n\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE, bitRateIn / frameRateIn * 1.1);\n\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_MAX_NUM_REFRAMES, 0);\n\n        if (m_hasQueryTimeout) {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_QUERY_TIMEOUT, 1000); // 1s timeout\n        }\n    }\n    case ALVR_CODEC_HEVC: {\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_USAGE, AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY\n        );\n        switch (Settings::Instance().m_rateControlMode) {\n        case ALVR_CBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD,\n                AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR\n            );\n            // Required for CBR to work correctly\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_FILLER_DATA_ENABLE, Settings::Instance().m_fillerData\n            );\n            break;\n        case ALVR_VBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD,\n                AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR\n            );\n            break;\n        }\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_FRAMESIZE, ::AMFConstructSize(width, height)\n        );\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_FRAMERATE, ::AMFConstructRate(frameRateIn, 1)\n        );\n\n        switch (Settings::Instance().m_encoderQualityPreset) {\n        case ALVR_QUALITY:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY\n            );\n            break;\n        case ALVR_BALANCED:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET,\n                AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED\n            );\n            break;\n        case ALVR_SPEED:\n        default:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED\n            );\n            break;\n        }\n\n        if (m_use10bit) {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_10);\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_PROFILE, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10\n            );\n        } else {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_8);\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_PROFILE, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN\n            );\n        }\n\n        amf::AMFCapsPtr caps;\n        if (amfEncoder->GetCaps(&caps) == AMF_OK) {\n            caps->GetProperty(AMF_VIDEO_ENCODER_HEVC_CAP_PRE_ANALYSIS, &m_hasPreAnalysis);\n            caps->GetProperty(\n                AMF_VIDEO_ENCODER_CAPS_HEVC_QUERY_TIMEOUT_SUPPORT, &m_hasQueryTimeout\n            );\n        }\n\n        if (Settings::Instance().m_enableAmfPreAnalysis) {\n            if (!Settings::Instance().m_useAmfPreproc || Settings::Instance().m_use10bitEncoder) {\n                Warn(\"Pre-analysis could not be enabled because \\\"Use preproc\\\" is not enabled or \"\n                     \"\\\"Reduce color banding\\\" is enabled.\");\n            } else if (m_hasPreAnalysis) {\n                Warn(\"Enabling HEVC pre-analysis. You may experience higher latency when this is \"\n                     \"enabled.\");\n                amfEncoder->SetProperty(\n                    AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE,\n                    Settings::Instance().m_enableAmfPreAnalysis\n                );\n            } else {\n                Warn(\"Pre-analysis could not be enabled because your GPU does not support it for \"\n                     \"HEVC encoding.\");\n            }\n        }\n\n        // Enable Full Range\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL\n        );\n\n        // Use BT.2020 for HDR encoding, BT.709 otherwise.\n        if (Settings::Instance().m_enableHdr) {\n            // Also specify the input formats for HDR to prevent incorrect color conversions\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_INPUT_COLOR_PROFILE,\n                AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_INPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_INPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020\n            );\n\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE,\n                AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT2020\n            );\n        } else {\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE,\n                AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709\n            );\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC,\n                AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22\n            ); // sRGB\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, AMF_COLOR_PRIMARIES_BT709\n            );\n        }\n\n        // No noticable performance difference and should improve subjective quality by allocating\n        // more bits to smooth areas\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, Settings::Instance().m_enableVbaq\n        );\n\n        // May impact performance but improves quality in high-motion areas\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_HIGH_MOTION_QUALITY_BOOST_ENABLE,\n            Settings::Instance().m_enableAmfHmqb\n        );\n\n        // Turns Off IDR/I Frames\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, 0);\n        // Set infinite GOP length\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, 0);\n\n        // Disable AUD to produce the same stream format as VideoEncoderNVENC.\n        // FIXME: This option doesn't work in 22.10.3, but works in versions prior 22.5.1\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, false);\n\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_HEVC_VBV_BUFFER_SIZE, bitRateIn / frameRateIn * 1.1\n        );\n\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES, 0);\n\n        if (m_hasQueryTimeout) {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, 1000); // 1s timeout\n        }\n    }\n    case ALVR_CODEC_AV1: {\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_AV1_USAGE, AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY\n        );\n        switch (Settings::Instance().m_rateControlMode) {\n        case ALVR_CBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD,\n                AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR\n            );\n            // Required for CBR to work correctly\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_FILLER_DATA, Settings::Instance().m_fillerData\n            );\n            break;\n        case ALVR_VBR:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD,\n                AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR\n            );\n            break;\n        }\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_TARGET_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_PEAK_BITRATE, bitRateIn);\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_FRAMESIZE, ::AMFConstructSize(width, height));\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_AV1_FRAMERATE, ::AMFConstructRate(frameRateIn, 1)\n        );\n\n        switch (Settings::Instance().m_encoderQualityPreset) {\n        case ALVR_QUALITY:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY\n            );\n            break;\n        case ALVR_BALANCED:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED\n            );\n            break;\n        case ALVR_SPEED:\n        default:\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED\n            );\n            break;\n        }\n\n        if (m_use10bit) {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_10);\n            // There's no separate profile for 10-bit for AV1 (as of AMF v1.4.33). Assumedly MAIN\n            // works fine for both.\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_PROFILE, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN\n            );\n        } else {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_COLOR_BIT_DEPTH, AMF_COLOR_BIT_DEPTH_8);\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_PROFILE, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN\n            );\n        }\n\n        // There is no VBAQ option for AV1. Instead it has CAQ (Content adaptive quantization)\n        if (Settings::Instance().m_enableVbaq) {\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_AQ_MODE, AMF_VIDEO_ENCODER_AV1_AQ_MODE_CAQ\n            );\n        } else {\n            amfEncoder->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_AQ_MODE, AMF_VIDEO_ENCODER_AV1_AQ_MODE_NONE\n            );\n        }\n\n        amf::AMFCapsPtr caps;\n        if (amfEncoder->GetCaps(&caps) == AMF_OK) {\n            caps->GetProperty(AMF_VIDEO_ENCODER_AV1_CAP_PRE_ANALYSIS, &m_hasPreAnalysis);\n        }\n\n        if (Settings::Instance().m_enableAmfPreAnalysis) {\n            if (!Settings::Instance().m_useAmfPreproc || Settings::Instance().m_use10bitEncoder) {\n                Warn(\"Pre-analysis could not be enabled because \\\"Use preproc\\\" is not enabled or \"\n                     \"\\\"Reduce color banding\\\" is enabled.\");\n            } else if (m_hasPreAnalysis) {\n                Warn(\"Enabling AV1 pre-analysis. You may experience higher latency when this is \"\n                     \"enabled.\");\n                amfEncoder->SetProperty(\n                    AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE,\n                    Settings::Instance().m_enableAmfPreAnalysis\n                );\n            } else {\n                Warn(\"Pre-analysis could not be enabled because your GPU does not support it for \"\n                     \"AV1 encoding.\");\n            }\n        }\n\n        // Enable Full Range\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PROFILE, AMF_VIDEO_CONVERTER_COLOR_PROFILE_JPEG\n        );\n\n        // May impact performance but improves quality in high-motion areas\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_AV1_HIGH_MOTION_QUALITY_BOOST, Settings::Instance().m_enableAmfHmqb\n        );\n\n        // Set infinite GOP length\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_GOP_SIZE, 0);\n\n        amfEncoder->SetProperty(\n            AMF_VIDEO_ENCODER_AV1_VBV_BUFFER_SIZE, bitRateIn / frameRateIn * 1.2\n        );\n\n        amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_MAX_NUM_REFRAMES, 0);\n\n        // AV1 assumed always has support for query timeout.\n        m_hasQueryTimeout = true;\n\n        if (m_hasQueryTimeout) {\n            amfEncoder->SetProperty(AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT, 1000); // 1s timeout\n        }\n    }\n    }\n\n    Debug(\"Configured %s.\\n\", pCodec);\n    AMF_THROW_IF(amfEncoder->Init(inputFormat, width, height));\n\n    Debug(\"Initialized %s.\\n\", pCodec);\n\n    return amfEncoder;\n}\n\namf::AMFComponentPtr VideoEncoderAMF::MakeConverter(\n    amf::AMF_SURFACE_FORMAT inputFormat, int width, int height, amf::AMF_SURFACE_FORMAT outputFormat\n) {\n    amf::AMFComponentPtr amfConverter;\n    AMF_THROW_IF(\n        g_AMFFactory.GetFactory()->CreateComponent(m_amfContext, AMFVideoConverter, &amfConverter)\n    );\n\n    AMF_THROW_IF(amfConverter->SetProperty(AMF_VIDEO_CONVERTER_MEMORY_TYPE, amf::AMF_MEMORY_DX11));\n    AMF_THROW_IF(amfConverter->SetProperty(AMF_VIDEO_CONVERTER_OUTPUT_FORMAT, outputFormat));\n    AMF_THROW_IF(amfConverter->SetProperty(\n        AMF_VIDEO_CONVERTER_OUTPUT_SIZE, ::AMFConstructSize(width, height)\n    ));\n\n    AMF_THROW_IF(amfConverter->Init(inputFormat, width, height));\n\n    Debug(\"Initialized %s.\\n\", AMFVideoConverter);\n    return amfConverter;\n}\n\namf::AMFComponentPtr\nVideoEncoderAMF::MakePreprocessor(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height) {\n    amf::AMFComponentPtr amfPreprocessor;\n    AMF_THROW_IF(\n        g_AMFFactory.GetFactory()->CreateComponent(m_amfContext, AMFPreProcessing, &amfPreprocessor)\n    );\n\n    AMF_THROW_IF(amfPreprocessor->SetProperty(AMF_PP_ENGINE_TYPE, amf::AMF_MEMORY_DX11));\n    AMF_THROW_IF(amfPreprocessor->SetProperty(\n        AMF_PP_ADAPTIVE_FILTER_STRENGTH, Settings::Instance().m_amfPreProcSigma\n    ));\n    AMF_THROW_IF(amfPreprocessor->SetProperty(\n        AMF_PP_ADAPTIVE_FILTER_SENSITIVITY, Settings::Instance().m_amfPreProcTor\n    ));\n\n    AMF_THROW_IF(amfPreprocessor->Init(inputFormat, width, height));\n\n    Debug(\"Initialized %s.\\n\", AMFPreProcessing);\n    return amfPreprocessor;\n}\n\nvoid VideoEncoderAMF::Initialize() {\n    Debug(\"Initializing VideoEncoderAMF.\\n\");\n    AMF_THROW_IF(g_AMFFactory.Init());\n\n    AMF_THROW_IF(g_AMFFactory.GetFactory()->CreateContext(&m_amfContext));\n    AMF_THROW_IF(m_amfContext->InitDX11(m_d3dRender->GetDevice()));\n\n    amf::AMF_SURFACE_FORMAT inFormat = m_surfaceFormat;\n    if (Settings::Instance().m_enableHdr) {\n        // Bypass preprocessor and converters for HDR, since it will already be YUV\n        ;\n    } else if (m_use10bit) {\n        inFormat = amf::AMF_SURFACE_R10G10B10A2;\n        m_amfComponents.emplace_back(\n            MakeConverter(m_surfaceFormat, m_renderWidth, m_renderHeight, inFormat)\n        );\n    } else {\n        if (Settings::Instance().m_useAmfPreproc) {\n            inFormat = amf::AMF_SURFACE_NV12;\n            m_amfComponents.emplace_back(\n                MakeConverter(m_surfaceFormat, m_renderWidth, m_renderHeight, inFormat)\n            );\n            m_amfComponents.emplace_back(MakePreprocessor(inFormat, m_renderWidth, m_renderHeight));\n        }\n    }\n    m_amfComponents.emplace_back(MakeEncoder(\n        inFormat, m_renderWidth, m_renderHeight, m_codec, m_refreshRate, m_bitrateInMBits\n    ));\n\n    m_pipeline = new AMFPipeline();\n    for (int i = 0; i < m_amfComponents.size() - 1; i++) {\n        m_pipeline->Connect(new AMFSolidPipe(m_amfComponents[i], m_amfComponents[i + 1]));\n    }\n\n    m_pipeline->Connect(new AMFPipe(\n        m_amfComponents.back(), std::bind(&VideoEncoderAMF::Receive, this, std::placeholders::_1)\n    ));\n\n    Debug(\"Successfully initialized VideoEncoderAMF.\\n\");\n}\n\nvoid VideoEncoderAMF::Shutdown() {\n    Debug(\"Shutting down VideoEncoderAMF.\\n\");\n\n    delete m_pipeline;\n\n    for (auto& component : m_amfComponents) {\n        component->Release();\n        delete component;\n    }\n\n    m_amfContext->Terminate();\n    m_amfContext = NULL;\n\n    g_AMFFactory.Terminate();\n\n    if (fpOut) {\n        fpOut.close();\n    }\n    Debug(\"Successfully shutdown VideoEncoderAMF.\\n\");\n}\n\nvoid VideoEncoderAMF::Transmit(\n    ID3D11Texture2D* pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR\n) {\n    amf::AMFSurfacePtr surface;\n    // Surface is cached by AMF.\n\n    auto params = GetDynamicEncoderParams();\n    if (params.updated) {\n        amf_int64 bitRateIn = params.bitrate_bps / params.framerate * m_refreshRate; // in bps\n\n        const amf_int64 maxRate = 1'000'000'000;\n        if (bitRateIn > maxRate) {\n            // Don't warn if we're within 1% (10 mbps) of the max bitrate because that case gets\n            // triggered by rounding errors with the max supported rate\n            if (bitRateIn > maxRate + maxRate / 100) {\n                Warn(\"Set bitrate over max supported by AMF, clamping to 1000mbps\");\n            }\n            bitRateIn = maxRate;\n        }\n\n        if (m_codec == ALVR_CODEC_H264) {\n            m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, bitRateIn);\n            m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, bitRateIn);\n            m_amfComponents.back()->SetProperty(\n                AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE, bitRateIn / m_refreshRate * 1.1\n            );\n        } else {\n            m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, bitRateIn);\n            m_amfComponents.back()->SetProperty(AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, bitRateIn);\n            m_amfComponents.back()->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_VBV_BUFFER_SIZE, bitRateIn / m_refreshRate * 1.1\n            );\n        }\n\n        if (Settings::Instance().m_amdBitrateCorruptionFix) {\n            RequestIDR();\n        }\n    }\n\n    AMF_THROW_IF(m_amfContext->AllocSurface(\n        amf::AMF_MEMORY_DX11, m_surfaceFormat, m_renderWidth, m_renderHeight, &surface\n    ));\n    ID3D11Texture2D* textureDX11 = (ID3D11Texture2D*)surface->GetPlaneAt(0)->GetNative(\n    ); // no reference counting - do not Release()\n    m_d3dRender->GetContext()->CopyResource(textureDX11, pTexture);\n\n    amf_pts start_time = amf_high_precision_clock();\n    surface->SetProperty(START_TIME_PROPERTY, start_time);\n    surface->SetProperty(FRAME_INDEX_PROPERTY, targetTimestampNs);\n\n    ApplyFrameProperties(surface, insertIDR);\n\n    m_amfComponents.front()->SubmitInput(surface);\n    m_pipeline->Run(m_hasQueryTimeout);\n}\n\nvoid VideoEncoderAMF::Receive(AMFDataPtr data) {\n    amf_pts current_time = amf_high_precision_clock();\n    amf_pts start_time = 0;\n    uint64_t targetTimestampNs;\n    data->GetProperty(START_TIME_PROPERTY, &start_time);\n    data->GetProperty(FRAME_INDEX_PROPERTY, &targetTimestampNs);\n\n    amf::AMFBufferPtr buffer(data); // query for buffer interface\n\n    char* p = reinterpret_cast<char*>(buffer->GetNative());\n    int length = static_cast<int>(buffer->GetSize());\n\n    if (fpOut) {\n        fpOut.write(p, length);\n    }\n\n    uint64_t type;\n    bool isIdr;\n    if (m_codec == ALVR_CODEC_H264) {\n        data->GetProperty(AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &type);\n        isIdr = type == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR;\n    } else {\n        data->GetProperty(AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &type);\n        isIdr = type == AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR;\n    }\n\n    ParseFrameNals(m_codec, reinterpret_cast<uint8_t*>(p), length, targetTimestampNs, isIdr);\n}\n\nvoid VideoEncoderAMF::ApplyFrameProperties(const amf::AMFSurfacePtr& surface, bool insertIDR) {\n    switch (m_codec) {\n    case ALVR_CODEC_H264:\n        // FIXME: This option doesn't work in drivers 22.3.1 - 22.5.1, but works in 22.10.3\n        surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_AUD, false);\n        if (insertIDR) {\n            Debug(\"Inserting IDR frame for H.264.\\n\");\n            surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_SPS, true);\n            surface->SetProperty(AMF_VIDEO_ENCODER_INSERT_PPS, true);\n            surface->SetProperty(\n                AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_PICTURE_TYPE_IDR\n            );\n        }\n        break;\n    case ALVR_CODEC_HEVC:\n        // FIXME: This option works with 22.10.3, but may not work with older drivers\n        surface->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, false);\n        if (insertIDR) {\n            Debug(\"Inserting IDR frame for H.265.\\n\");\n            // Insert VPS,SPS,PPS\n            // These options don't work properly on older AMD driver (Radeon Software 17.7, AMF\n            // Runtime 1.4.4) Fixed in 18.9.2 & 1.4.9\n            surface->SetProperty(AMF_VIDEO_ENCODER_HEVC_INSERT_HEADER, true);\n            surface->SetProperty(\n                AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_IDR\n            );\n        }\n        break;\n    case ALVR_CODEC_AV1:\n        if (insertIDR) {\n            Debug(\"Inserting IDR frame for AV1.\\n\");\n            surface->SetProperty(AMF_VIDEO_ENCODER_AV1_FORCE_INSERT_SEQUENCE_HEADER, true);\n            surface->SetProperty(\n                AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE, AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_KEY\n            );\n        }\n        break;\n    default:\n        throw MakeException(\"Invalid video codec\");\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderAMF.h",
    "content": "#pragma once\n#include \"VideoEncoder.h\"\n\n#include \"../../shared/amf/public/common/AMFFactory.h\"\n#include \"../../shared/amf/public/common/AMFSTL.h\"\n#include \"../../shared/amf/public/common/Thread.h\"\n#include \"../../shared/amf/public/include/components/PreProcessing.h\"\n#include \"../../shared/amf/public/include/components/VideoConverter.h\"\n#include \"../../shared/amf/public/include/components/VideoEncoderAV1.h\"\n#include \"../../shared/amf/public/include/components/VideoEncoderHEVC.h\"\n#include \"../../shared/amf/public/include/components/VideoEncoderVCE.h\"\n\ntypedef amf::AMFData* AMFDataPtr;\ntypedef std::function<void(AMFDataPtr)> AMFDataReceiver;\n\nclass AMFPipeline;\n\nclass AMFPipe {\npublic:\n    AMFPipe(amf::AMFComponentPtr src, AMFDataReceiver receiver);\n    virtual ~AMFPipe();\n\n    void doPassthrough(bool hasQueryTimeout, uint32_t timerResolution);\n\nprotected:\n    amf::AMFComponentPtr m_amfComponentSrc;\n    AMFDataReceiver m_receiver;\n};\n\ntypedef AMFPipe* AMFPipePtr;\n\nclass AMFSolidPipe : public AMFPipe {\npublic:\n    AMFSolidPipe(amf::AMFComponentPtr src, amf::AMFComponentPtr dst);\n\nprotected:\n    void Passthrough(AMFDataPtr);\n\n    amf::AMFComponentPtr m_amfComponentDst;\n};\n\nclass AMFPipeline {\npublic:\n    AMFPipeline();\n    ~AMFPipeline();\n\n    void Connect(AMFPipePtr pipe);\n    void Run(bool hasQueryTimeout);\n\nprotected:\n    uint32_t m_timerResolution;\n\n    std::vector<AMFPipePtr> m_pipes;\n};\n\ntypedef AMFPipeline* AMFPipelinePtr;\n\n// Video encoder for AMD VCE and VCN.\nclass VideoEncoderAMF : public VideoEncoder {\npublic:\n    VideoEncoderAMF(std::shared_ptr<CD3DRender> pD3DRender, int width, int height);\n    ~VideoEncoderAMF();\n\n    void Initialize();\n    void Shutdown();\n\n    void Transmit(\n        ID3D11Texture2D* pTexture,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        bool insertIDR\n    );\n    void Receive(AMFDataPtr data);\n\nprivate:\n    static const wchar_t* START_TIME_PROPERTY;\n    static const wchar_t* FRAME_INDEX_PROPERTY;\n\n    amf::AMFComponentPtr MakeConverter(\n        amf::AMF_SURFACE_FORMAT inputFormat,\n        int width,\n        int height,\n        amf::AMF_SURFACE_FORMAT outputFormat\n    );\n    amf::AMFComponentPtr\n    MakePreprocessor(amf::AMF_SURFACE_FORMAT inputFormat, int width, int height);\n    amf::AMFComponentPtr MakeEncoder(\n        amf::AMF_SURFACE_FORMAT inputFormat,\n        int width,\n        int height,\n        int codec,\n        int refreshRate,\n        int bitrateInMbits\n    );\n    amf::AMFContextPtr m_amfContext;\n    AMFPipelinePtr m_pipeline;\n    std::vector<amf::AMFComponentPtr> m_amfComponents;\n\n    std::ofstream fpOut;\n\n    std::shared_ptr<CD3DRender> m_d3dRender;\n\n    bool m_use10bit;\n    amf::AMF_SURFACE_FORMAT m_surfaceFormat;\n\n    int m_codec;\n    int m_refreshRate;\n    int m_renderWidth;\n    int m_renderHeight;\n    int m_bitrateInMBits;\n\n    bool m_hasQueryTimeout;\n    bool m_hasPreAnalysis;\n\n    void ApplyFrameProperties(const amf::AMFSurfacePtr& surface, bool insertIDR);\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderNVENC.cpp",
    "content": "#include \"VideoEncoderNVENC.h\"\n#include \"NvCodecUtils.h\"\n\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/Utils.h\"\n\nVideoEncoderNVENC::VideoEncoderNVENC(std::shared_ptr<CD3DRender> pD3DRender, int width, int height)\n    : m_pD3DRender(pD3DRender)\n    , m_codec(Settings::Instance().m_codec)\n    , m_refreshRate(Settings::Instance().m_refreshRate)\n    , m_renderWidth(width)\n    , m_renderHeight(height)\n    , m_bitrateInMBits(30) { }\n\nVideoEncoderNVENC::~VideoEncoderNVENC() { }\n\nvoid VideoEncoderNVENC::Initialize() {\n    //\n    // Initialize Encoder\n    //\n\n    NV_ENC_BUFFER_FORMAT format\n        = Settings::Instance().m_enableHdr ? NV_ENC_BUFFER_FORMAT_NV12 : NV_ENC_BUFFER_FORMAT_ABGR;\n\n    if (Settings::Instance().m_use10bitEncoder) {\n        format = Settings::Instance().m_enableHdr ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT\n                                                  : NV_ENC_BUFFER_FORMAT_ABGR10;\n    }\n\n    Debug(\n        \"Initializing CNvEncoder. Width=%d Height=%d Format=%d\\n\",\n        m_renderWidth,\n        m_renderHeight,\n        format\n    );\n\n    try {\n        m_NvNecoder = std::make_shared<NvEncoderD3D11>(\n            m_pD3DRender->GetDevice(), m_renderWidth, m_renderHeight, format, 0\n        );\n    } catch (NVENCException e) {\n        throw MakeException(\n            \"NvEnc NvEncoderD3D11 failed. Code=%d %hs\\n\", e.getErrorCode(), e.what()\n        );\n    }\n\n    NV_ENC_INITIALIZE_PARAMS initializeParams = { NV_ENC_INITIALIZE_PARAMS_VER };\n    NV_ENC_CONFIG encodeConfig = { NV_ENC_CONFIG_VER };\n    initializeParams.encodeConfig = &encodeConfig;\n\n    FillEncodeConfig(\n        initializeParams,\n        m_refreshRate,\n        m_renderWidth,\n        m_renderHeight,\n        m_bitrateInMBits * 1'000'000L\n    );\n    try {\n        m_NvNecoder->CreateEncoder(&initializeParams);\n    } catch (NVENCException e) {\n        if (e.getErrorCode() == NV_ENC_ERR_INVALID_PARAM) {\n            throw MakeException(\n                \"This GPU does not support H.265 encoding. (NvEncoderCuda NV_ENC_ERR_INVALID_PARAM)\"\n            );\n        }\n        throw MakeException(\"NvEnc CreateEncoder failed. Code=%d %hs\", e.getErrorCode(), e.what());\n    }\n\n    Debug(\"CNvEncoder is successfully initialized.\\n\");\n}\n\nvoid VideoEncoderNVENC::Shutdown() {\n    std::vector<std::vector<uint8_t>> vPacket;\n    if (m_NvNecoder)\n        m_NvNecoder->EndEncode(vPacket);\n\n    for (std::vector<uint8_t>& packet : vPacket) {\n        if (fpOut) {\n            fpOut.write(reinterpret_cast<char*>(packet.data()), packet.size());\n        }\n    }\n    if (m_NvNecoder) {\n        m_NvNecoder->DestroyEncoder();\n        m_NvNecoder.reset();\n    }\n\n    Debug(\"CNvEncoder::Shutdown\\n\");\n\n    if (fpOut) {\n        fpOut.close();\n    }\n}\n\nvoid VideoEncoderNVENC::Transmit(\n    ID3D11Texture2D* pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR\n) {\n    auto params = GetDynamicEncoderParams();\n    if (params.updated) {\n        m_bitrateInMBits = params.bitrate_bps / 1'000'000;\n        NV_ENC_INITIALIZE_PARAMS initializeParams = { NV_ENC_INITIALIZE_PARAMS_VER };\n        NV_ENC_CONFIG encodeConfig = { NV_ENC_CONFIG_VER };\n        initializeParams.encodeConfig = &encodeConfig;\n        FillEncodeConfig(\n            initializeParams,\n            params.framerate,\n            m_renderWidth,\n            m_renderHeight,\n            m_bitrateInMBits * 1'000'000L\n        );\n        NV_ENC_RECONFIGURE_PARAMS reconfigureParams = { NV_ENC_RECONFIGURE_PARAMS_VER };\n        reconfigureParams.reInitEncodeParams = initializeParams;\n        m_NvNecoder->Reconfigure(&reconfigureParams);\n    }\n\n    std::vector<std::vector<uint8_t>> vPacket;\n\n    const NvEncInputFrame* encoderInputFrame = m_NvNecoder->GetNextInputFrame();\n\n    ID3D11Texture2D* pInputTexture\n        = reinterpret_cast<ID3D11Texture2D*>(encoderInputFrame->inputPtr);\n    m_pD3DRender->GetContext()->CopyResource(pInputTexture, pTexture);\n\n    NV_ENC_PIC_PARAMS picParams = {};\n    if (insertIDR) {\n        Debug(\"Inserting IDR frame.\\n\");\n        picParams.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR;\n    }\n    m_NvNecoder->EncodeFrame(vPacket, &picParams);\n\n    for (std::vector<uint8_t>& packet : vPacket) {\n        uint8_t* buf = packet.data();\n        int len = (int)packet.size();\n\n        // NVENC's AV1 encoding includes a bunch of IVF wrapping,\n        // so we need to strip it down to just the OBUs\n        if (m_codec == ALVR_CODEC_AV1) {\n            const uint8_t ivf_magic[4] = { 0x44, 0x4B, 0x49, 0x46 };\n            if (len >= 4 && !memcmp(buf, ivf_magic, 4)) {\n                buf += 32;\n                len -= 32;\n            }\n            if (len <= 12) {\n                continue;\n            }\n            buf += 12; // skip past the IVF packet size header thing\n            len -= 12;\n        }\n\n        if (len <= 0) {\n            continue;\n        }\n\n        if (fpOut) {\n            fpOut.write(reinterpret_cast<char*>(buf), len);\n        }\n\n        ParseFrameNals(m_codec, buf, len, targetTimestampNs, insertIDR);\n    }\n}\n\nvoid VideoEncoderNVENC::FillEncodeConfig(\n    NV_ENC_INITIALIZE_PARAMS& initializeParams,\n    int refreshRate,\n    int renderWidth,\n    int renderHeight,\n    uint64_t bitrate_bps\n) {\n    auto& encodeConfig = *initializeParams.encodeConfig;\n\n    GUID encoderGUID;\n    switch (m_codec) {\n    case ALVR_CODEC_H264:\n        encoderGUID = NV_ENC_CODEC_H264_GUID;\n        break;\n    case ALVR_CODEC_HEVC:\n        encoderGUID = NV_ENC_CODEC_HEVC_GUID;\n        break;\n    case ALVR_CODEC_AV1:\n        encoderGUID = NV_ENC_CODEC_AV1_GUID;\n        break;\n    }\n\n    GUID qualityPreset;\n    // See recommended NVENC settings for low-latency encoding.\n    // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvenc-video-encoder-api-prog-guide/#recommended-nvenc-settings\n    switch (Settings::Instance().m_nvencQualityPreset) {\n    case 7:\n        qualityPreset = NV_ENC_PRESET_P7_GUID;\n        break;\n    case 6:\n        qualityPreset = NV_ENC_PRESET_P6_GUID;\n        break;\n    case 5:\n        qualityPreset = NV_ENC_PRESET_P5_GUID;\n        break;\n    case 4:\n        qualityPreset = NV_ENC_PRESET_P4_GUID;\n        break;\n    case 3:\n        qualityPreset = NV_ENC_PRESET_P3_GUID;\n        break;\n    case 2:\n        qualityPreset = NV_ENC_PRESET_P2_GUID;\n        break;\n    case 1:\n    default:\n        qualityPreset = NV_ENC_PRESET_P1_GUID;\n        break;\n    }\n\n    NV_ENC_TUNING_INFO tuningPreset\n        = static_cast<NV_ENC_TUNING_INFO>(Settings::Instance().m_nvencTuningPreset);\n\n    m_NvNecoder->CreateDefaultEncoderParams(\n        &initializeParams, encoderGUID, qualityPreset, tuningPreset\n    );\n\n    initializeParams.encodeWidth = initializeParams.darWidth = renderWidth;\n    initializeParams.encodeHeight = initializeParams.darHeight = renderHeight;\n    initializeParams.frameRateNum = refreshRate;\n    initializeParams.frameRateDen = 1;\n\n    if (Settings::Instance().m_nvencRefreshRate != -1) {\n        initializeParams.frameRateNum = Settings::Instance().m_nvencRefreshRate;\n    }\n\n    initializeParams.enableWeightedPrediction\n        = Settings::Instance().m_nvencEnableWeightedPrediction;\n\n    // 16 is recommended when using reference frame invalidation. But it has caused bad visual\n    // quality. Now, use 0 (use default).\n    uint32_t maxNumRefFrames = 0;\n    uint32_t gopLength = NVENC_INFINITE_GOPLENGTH;\n\n    if (Settings::Instance().m_nvencMaxNumRefFrames != -1) {\n        maxNumRefFrames = Settings::Instance().m_nvencMaxNumRefFrames;\n    }\n    if (Settings::Instance().m_nvencGopLength != -1) {\n        gopLength = Settings::Instance().m_nvencGopLength;\n    }\n\n    switch (m_codec) {\n    case ALVR_CODEC_H264: {\n        auto& config = encodeConfig.encodeCodecConfig.h264Config;\n        config.repeatSPSPPS = 1;\n        config.enableIntraRefresh = Settings::Instance().m_nvencEnableIntraRefresh;\n\n        if (Settings::Instance().m_nvencIntraRefreshPeriod != -1) {\n            config.intraRefreshPeriod = Settings::Instance().m_nvencIntraRefreshPeriod;\n        }\n        if (Settings::Instance().m_nvencIntraRefreshCount != -1) {\n            config.intraRefreshCnt = Settings::Instance().m_nvencIntraRefreshCount;\n        }\n\n        switch (Settings::Instance().m_entropyCoding) {\n        case ALVR_CABAC:\n            config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC;\n            break;\n        case ALVR_CAVLC:\n            config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC;\n            break;\n        }\n\n        config.maxNumRefFrames = maxNumRefFrames;\n        config.idrPeriod = gopLength;\n\n        if (Settings::Instance().m_fillerData) {\n            config.enableFillerDataInsertion = Settings::Instance().m_rateControlMode == ALVR_CBR;\n        }\n\n        config.h264VUIParameters.videoSignalTypePresentFlag = 1;\n        config.h264VUIParameters.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED;\n        config.h264VUIParameters.videoFullRangeFlag = 1;\n        config.h264VUIParameters.colourDescriptionPresentFlag = 1;\n        if (Settings::Instance().m_enableHdr) {\n            config.h264VUIParameters.colourPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020;\n            config.h264VUIParameters.transferCharacteristics\n                = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.h264VUIParameters.colourMatrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL;\n        } else {\n            config.h264VUIParameters.colourPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709;\n            config.h264VUIParameters.transferCharacteristics\n                = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.h264VUIParameters.colourMatrix = NV_ENC_VUI_MATRIX_COEFFS_BT709;\n        }\n    } break;\n    case ALVR_CODEC_HEVC: {\n        auto& config = encodeConfig.encodeCodecConfig.hevcConfig;\n        config.repeatSPSPPS = 1;\n        config.enableIntraRefresh = Settings::Instance().m_nvencEnableIntraRefresh;\n\n        if (Settings::Instance().m_nvencIntraRefreshPeriod != -1) {\n            config.intraRefreshPeriod = Settings::Instance().m_nvencIntraRefreshPeriod;\n        }\n        if (Settings::Instance().m_nvencIntraRefreshCount != -1) {\n            config.intraRefreshCnt = Settings::Instance().m_nvencIntraRefreshCount;\n        }\n\n        config.maxNumRefFramesInDPB = maxNumRefFrames;\n        config.idrPeriod = gopLength;\n\n        if (Settings::Instance().m_use10bitEncoder) {\n            encodeConfig.encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2;\n        }\n\n        if (Settings::Instance().m_fillerData) {\n            config.enableFillerDataInsertion = Settings::Instance().m_rateControlMode == ALVR_CBR;\n        }\n\n        config.hevcVUIParameters.videoSignalTypePresentFlag = 1;\n        config.hevcVUIParameters.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED;\n        config.hevcVUIParameters.videoFullRangeFlag = 1;\n        config.hevcVUIParameters.colourDescriptionPresentFlag = 1;\n        if (Settings::Instance().m_enableHdr) {\n            config.hevcVUIParameters.colourPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020;\n            config.hevcVUIParameters.transferCharacteristics\n                = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.hevcVUIParameters.colourMatrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL;\n        } else {\n            config.hevcVUIParameters.colourPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709;\n            config.hevcVUIParameters.transferCharacteristics\n                = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.hevcVUIParameters.colourMatrix = NV_ENC_VUI_MATRIX_COEFFS_BT709;\n        }\n    } break;\n    case ALVR_CODEC_AV1: {\n        auto& config = encodeConfig.encodeCodecConfig.av1Config;\n        config.repeatSeqHdr = 1;\n        config.enableIntraRefresh = Settings::Instance().m_nvencEnableIntraRefresh;\n\n        if (Settings::Instance().m_nvencIntraRefreshPeriod != -1) {\n            config.intraRefreshPeriod = Settings::Instance().m_nvencIntraRefreshPeriod;\n        }\n        if (Settings::Instance().m_nvencIntraRefreshCount != -1) {\n            config.intraRefreshCnt = Settings::Instance().m_nvencIntraRefreshCount;\n        }\n\n        config.maxNumRefFramesInDPB = maxNumRefFrames;\n        config.idrPeriod = gopLength;\n\n        if (Settings::Instance().m_use10bitEncoder) {\n            config.pixelBitDepthMinus8 = 2;\n        }\n\n        if (Settings::Instance().m_fillerData) {\n            config.enableBitstreamPadding = Settings::Instance().m_rateControlMode == ALVR_CBR;\n        }\n\n        config.chromaFormatIDC = 1; // 4:2:0, 4:4:4 currently not supported\n        config.colorRange = 1;\n        if (Settings::Instance().m_enableHdr) {\n            config.colorPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020;\n            config.transferCharacteristics = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.matrixCoefficients = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL;\n        } else {\n            config.colorPrimaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709;\n            config.transferCharacteristics = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB;\n            config.matrixCoefficients = NV_ENC_VUI_MATRIX_COEFFS_BT709;\n        }\n    } break;\n    }\n\n    // Disable automatic IDR insertion by NVENC. We need to manually insert IDR when packet is\n    // dropped if don't use reference frame invalidation.\n    encodeConfig.gopLength = gopLength;\n    encodeConfig.frameIntervalP = 1;\n\n    if (Settings::Instance().m_nvencPFrameStrategy != -1) {\n        encodeConfig.frameIntervalP = Settings::Instance().m_nvencPFrameStrategy;\n    }\n\n    switch (Settings::Instance().m_rateControlMode) {\n    case ALVR_CBR:\n        encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;\n        break;\n    case ALVR_VBR:\n        encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR;\n        break;\n    }\n    encodeConfig.rcParams.multiPass\n        = static_cast<NV_ENC_MULTI_PASS>(Settings::Instance().m_nvencMultiPass);\n    encodeConfig.rcParams.lowDelayKeyFrameScale = 1;\n\n    if (Settings::Instance().m_nvencLowDelayKeyFrameScale != -1) {\n        encodeConfig.rcParams.lowDelayKeyFrameScale\n            = Settings::Instance().m_nvencLowDelayKeyFrameScale;\n    }\n\n    uint32_t maxFrameSize = static_cast<uint32_t>(bitrate_bps / refreshRate);\n    Debug(\"VideoEncoderNVENC: maxFrameSize=%d bits\\n\", maxFrameSize);\n    encodeConfig.rcParams.vbvBufferSize = maxFrameSize * 1.1;\n    encodeConfig.rcParams.vbvInitialDelay = maxFrameSize * 1.1;\n    encodeConfig.rcParams.maxBitRate = static_cast<uint32_t>(bitrate_bps);\n    encodeConfig.rcParams.averageBitRate = static_cast<uint32_t>(bitrate_bps);\n    if (Settings::Instance().m_nvencAdaptiveQuantizationMode == SpatialAQ) {\n        encodeConfig.rcParams.enableAQ = 1;\n    } else if (Settings::Instance().m_nvencAdaptiveQuantizationMode == TemporalAQ) {\n        encodeConfig.rcParams.enableTemporalAQ = 1;\n    }\n\n    if (Settings::Instance().m_nvencRateControlMode != -1) {\n        encodeConfig.rcParams.rateControlMode\n            = (NV_ENC_PARAMS_RC_MODE)Settings::Instance().m_nvencRateControlMode;\n    }\n    if (Settings::Instance().m_nvencRcBufferSize != -1) {\n        encodeConfig.rcParams.vbvBufferSize = Settings::Instance().m_nvencRcBufferSize;\n    }\n    if (Settings::Instance().m_nvencRcInitialDelay != -1) {\n        encodeConfig.rcParams.vbvInitialDelay = Settings::Instance().m_nvencRcInitialDelay;\n    }\n    if (Settings::Instance().m_nvencRcMaxBitrate != -1) {\n        encodeConfig.rcParams.maxBitRate = Settings::Instance().m_nvencRcMaxBitrate;\n    }\n    if (Settings::Instance().m_nvencRcAverageBitrate != -1) {\n        encodeConfig.rcParams.averageBitRate = Settings::Instance().m_nvencRcAverageBitrate;\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderNVENC.h",
    "content": "#pragma once\n\n#include \"NvEncoderD3D11.h\"\n#include \"VideoEncoder.h\"\n#include \"shared/d3drender.h\"\n#include <memory>\n\nenum AdaptiveQuantizationMode { SpatialAQ = 1, TemporalAQ = 2 };\n\n// Video encoder for NVIDIA NvEnc.\nclass VideoEncoderNVENC : public VideoEncoder {\npublic:\n    VideoEncoderNVENC(std::shared_ptr<CD3DRender> pD3DRender, int width, int height);\n    ~VideoEncoderNVENC();\n\n    void Initialize();\n    void Shutdown();\n\n    void Transmit(\n        ID3D11Texture2D* pTexture,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        bool insertIDR\n    );\n\nprivate:\n    void FillEncodeConfig(\n        NV_ENC_INITIALIZE_PARAMS& initializeParams,\n        int refreshRate,\n        int renderWidth,\n        int renderHeight,\n        uint64_t bitrate_bps\n    );\n\n    std::ofstream fpOut;\n    std::shared_ptr<NvEncoder> m_NvNecoder;\n\n    std::shared_ptr<CD3DRender> m_pD3DRender;\n\n    int m_codec;\n    int m_refreshRate;\n    int m_renderWidth;\n    int m_renderHeight;\n    int m_bitrateInMBits;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderSW.cpp",
    "content": "#ifdef ALVR_GPL\n\n#include \"VideoEncoderSW.h\"\n\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/Utils.h\"\n\n#include <algorithm>\n#include <array>\n#include <iostream>\n#include <string>\n\nVideoEncoderSW::VideoEncoderSW(std::shared_ptr<CD3DRender> d3dRender, int width, int height)\n    : m_d3dRender(d3dRender)\n    , m_codec(ALVR_CODEC_H264)\n    , m_refreshRate(Settings::Instance().m_refreshRate)\n    , m_renderWidth(width)\n    , m_renderHeight(height)\n    , m_bitrateInMBits(30) {\n    // #ifdef ALVR_DEBUG_LOG\n    //     av_log_set_level(AV_LOG_DEBUG);\n    //     av_log_set_callback(LibVALog);\n    //     Debug(\"Set FFMPEG/LibAV to debug logging\");\n    // #endif\n}\n\nVideoEncoderSW::~VideoEncoderSW() { }\n\nvoid VideoEncoderSW::LibVALog(void* v, int level, const char* data, va_list va) {\n    const char* prefix = \"[libav]: \";\n    std::stringstream sstream;\n    sstream << prefix << data;\n    vprintf(sstream.str().c_str(), va);\n}\n\nvoid VideoEncoderSW::Initialize() {\n    int err;\n    Debug(\"Initializing VideoEncoderSW.\\n\");\n\n    const auto& settings = Settings::Instance();\n\n    // Query codec\n    AVCodecID codecId = ToFFMPEGCodec(m_codec);\n    if (!codecId)\n        throw MakeException(\"Invalid requested codec %d\", m_codec);\n\n    const AVCodec* codec = avcodec_find_encoder(codecId);\n    if (codec == NULL)\n        throw MakeException(\"Could not find codec id %d\", codecId);\n\n    // Initialize CodecContext\n    m_codecContext = avcodec_alloc_context3(codec);\n    if (m_codecContext == NULL)\n        throw MakeException(\"Failed to allocate encoder id %d\", codecId);\n\n    // Set codec settings\n    AVDictionary* opt = NULL;\n    av_dict_set(&opt, \"preset\", \"ultrafast\", 0);\n    av_dict_set(&opt, \"tune\", \"zerolatency\", 0);\n\n    switch (settings.m_h264Profile) {\n    case ALVR_H264_PROFILE_BASELINE:\n        m_codecContext->profile = FF_PROFILE_H264_BASELINE;\n        break;\n    case ALVR_H264_PROFILE_MAIN:\n        m_codecContext->profile = FF_PROFILE_H264_MAIN;\n        break;\n    default:\n    case ALVR_H264_PROFILE_HIGH:\n        m_codecContext->profile = FF_PROFILE_H264_HIGH;\n        break;\n    }\n    switch (settings.m_entropyCoding) {\n    case ALVR_CABAC:\n        av_dict_set(&opt, \"coder\", \"ac\", 0);\n        break;\n    case ALVR_CAVLC:\n        av_dict_set(&opt, \"coder\", \"vlc\", 0);\n        break;\n    }\n\n    m_codecContext->width = m_renderWidth;\n    m_codecContext->height = m_renderHeight;\n    m_codecContext->time_base = AVRational { 1, (int)(1e9) };\n    m_codecContext->framerate = AVRational { settings.m_refreshRate, 1 };\n    m_codecContext->sample_aspect_ratio = AVRational { 1, 1 };\n    m_codecContext->pix_fmt\n        = settings.m_use10bitEncoder ? AV_PIX_FMT_YUV420P10 : AV_PIX_FMT_YUV420P;\n    m_codecContext->color_range = AVCOL_RANGE_JPEG;\n    if (settings.m_enableHdr) {\n        m_codecContext->color_primaries = AVCOL_PRI_BT2020;\n        m_codecContext->color_trc = AVCOL_TRC_GAMMA22;\n        m_codecContext->colorspace = AVCOL_SPC_BT2020_NCL;\n    } else {\n        m_codecContext->color_primaries = AVCOL_PRI_BT709;\n        m_codecContext->color_trc = AVCOL_TRC_GAMMA22;\n        m_codecContext->colorspace = AVCOL_SPC_BT709;\n    }\n    m_codecContext->max_b_frames = 0;\n    m_codecContext->gop_size = 0;\n    m_codecContext->bit_rate = m_bitrateInMBits * 1'000'000L;\n    m_codecContext->rc_buffer_size = m_codecContext->bit_rate / settings.m_refreshRate * 1.1;\n    switch (settings.m_rateControlMode) {\n    case ALVR_CBR:\n        if (settings.m_fillerData) {\n            av_dict_set(&opt, \"nal-hrd\", \"cbr\", 0);\n        }\n        break;\n    case ALVR_VBR:\n        av_dict_set(&opt, \"nal-hrd\", \"vbr\", 0);\n        break;\n    }\n    m_codecContext->rc_max_rate = m_codecContext->bit_rate;\n    m_codecContext->thread_count = settings.m_swThreadCount;\n\n    if ((err = avcodec_open2(m_codecContext, codec, &opt)))\n        throw MakeException(\"Cannot open video encoder codec: %d\", err);\n\n    // Config transfer/encode frames\n    m_transferredFrame = av_frame_alloc();\n    m_transferredFrame->buf[0] = av_buffer_alloc(1);\n    m_encoderFrame = av_frame_alloc();\n    m_encoderFrame->width = m_codecContext->width;\n    m_encoderFrame->height = m_codecContext->height;\n    m_encoderFrame->format = m_codecContext->pix_fmt;\n    if ((err = av_frame_get_buffer(m_encoderFrame, 0)))\n        throw MakeException(\"Error when allocating encoder frame: %d\", err);\n\n    Debug(\"Successfully initialized VideoEncoderSW\");\n}\n\nvoid VideoEncoderSW::Shutdown() {\n    Debug(\"Shutting down VideoEncoderSW.\\n\");\n\n    av_frame_free(&m_transferredFrame);\n    av_frame_free(&m_encoderFrame);\n\n    avcodec_free_context(&m_codecContext);\n    sws_freeContext(m_scalerContext);\n    m_scalerContext = nullptr;\n\n    Debug(\"Successfully shutdown VideoEncoderSW.\\n\");\n}\n\nvoid VideoEncoderSW::Transmit(\n    ID3D11Texture2D* pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR\n) {\n    // Handle bitrate changes\n    auto params = GetDynamicEncoderParams();\n    if (params.updated) {\n        m_codecContext->bit_rate = params.bitrate_bps;\n        m_codecContext->framerate = AVRational { (int)params.framerate, 1 };\n        m_codecContext->rc_buffer_size = m_codecContext->bit_rate / params.framerate * 1.1;\n        m_codecContext->rc_max_rate = m_codecContext->bit_rate;\n    }\n\n    // Setup staging texture if not defined yet; we can only define it here as we now have the\n    // texture's size\n    if (!m_stagingTex) {\n        HRESULT hr = SetupStagingTexture(pTexture);\n        if (FAILED(hr)) {\n            Error(\"Failed to create staging texture: %p %ls\", hr, GetErrorStr(hr).c_str());\n            return;\n        }\n        Debug(\"Success in creating staging texture\");\n    }\n\n    // Copy texture and map it to memory\n    /// SteamVR crashes if the swapchain textures are set to staging, which is needed to be read by\n    /// the CPU. Unless there's another solution we have to copy the texture every time, which is\n    /// gonna be another performance hit.\n    HRESULT hr = CopyTexture(pTexture);\n    if (FAILED(hr)) {\n        Error(\"Failed to copy texture to staging: %p %ls\", hr, GetErrorStr(hr).c_str());\n        return;\n    }\n    // Debug(\"Success in mapping staging texture\");\n\n    AVPixelFormat inputFormat = AV_PIX_FMT_RGBA;\n    if (Settings::Instance().m_enableHdr) {\n        inputFormat\n            = Settings::Instance().m_use10bitEncoder ? AV_PIX_FMT_YUV420P10 : AV_PIX_FMT_YUV420P;\n    }\n\n    // Setup software scaler if not defined yet; we can only define it here as we now have the\n    // texture's size\n    if (!m_scalerContext) {\n        m_scalerContext = sws_getContext(\n            m_stagingTexDesc.Width,\n            m_stagingTexDesc.Height,\n            inputFormat,\n            m_codecContext->width,\n            m_codecContext->height,\n            m_codecContext->pix_fmt,\n            SWS_BILINEAR,\n            NULL,\n            NULL,\n            NULL\n        );\n        if (!m_scalerContext) {\n            Error(\"Couldn't initialize SWScaler.\");\n            m_d3dRender->GetContext()->Unmap(m_stagingTex.Get(), 0);\n            return;\n        }\n        Debug(\"Successfully initialized SWScaler.\");\n    }\n\n    // We got the texture, populate tansferredFrame with data\n    m_transferredFrame->width = m_stagingTexDesc.Width;\n    m_transferredFrame->height = m_stagingTexDesc.Height;\n    m_transferredFrame->data[0] = (uint8_t*)m_stagingTexMap.pData;\n    m_transferredFrame->linesize[0] = m_stagingTexMap.RowPitch;\n    m_transferredFrame->format = inputFormat;\n    m_transferredFrame->pts = targetTimestampNs;\n\n    // Use SWScaler for scaling\n    if (sws_scale(\n            m_scalerContext,\n            m_transferredFrame->data,\n            m_transferredFrame->linesize,\n            0,\n            m_transferredFrame->height,\n            m_encoderFrame->data,\n            m_encoderFrame->linesize\n        )\n        == 0) {\n        Error(\"SWScale failed.\");\n        m_d3dRender->GetContext()->Unmap(m_stagingTex.Get(), 0);\n        return;\n    }\n    // Debug(\"SWScale succeeded.\");\n\n    // Send frame for encoding\n    m_encoderFrame->pict_type = insertIDR ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;\n    m_encoderFrame->pts = targetTimestampNs;\n\n    int err;\n    if ((err = avcodec_send_frame(m_codecContext, m_encoderFrame)) < 0) {\n        Error(\"Encoding frame failed: err code %d\", err);\n        m_d3dRender->GetContext()->Unmap(m_stagingTex.Get(), 0);\n        return;\n    }\n    // Debug(\"Send frame succeeded.\");\n\n    // Retrieve frames from encoding and send them until buffer is emptied\n    while (true) {\n        AVPacket* packet = av_packet_alloc();\n        err = avcodec_receive_packet(m_codecContext, packet);\n        if (err != 0) {\n            av_packet_free(&packet);\n            break;\n        }\n        // Send encoded frame to client\n        bool isIdr = (packet->flags & AV_PKT_FLAG_KEY) != 0;\n        ParseFrameNals(m_codec, packet->data, packet->size, packet->pts, isIdr);\n        // Debug(\"Sent encoded packet to client\");\n        av_packet_free(&packet);\n    }\n    if (err == AVERROR(EINVAL)) {\n        Error(\"Received encoded frame failed: err code %d\", err);\n    }\n\n    // Unmap the copied texture and delete it\n    m_d3dRender->GetContext()->Unmap(m_stagingTex.Get(), 0);\n}\n\nHRESULT VideoEncoderSW::SetupStagingTexture(ID3D11Texture2D* pTexture) {\n    D3D11_TEXTURE2D_DESC desc;\n    pTexture->GetDesc(&desc);\n    m_stagingTexDesc.Width = desc.Width;\n    m_stagingTexDesc.Height = desc.Height;\n    m_stagingTexDesc.MipLevels = desc.MipLevels;\n    m_stagingTexDesc.ArraySize = desc.ArraySize;\n    m_stagingTexDesc.Format = desc.Format;\n    m_stagingTexDesc.SampleDesc = desc.SampleDesc;\n    m_stagingTexDesc.Usage = D3D11_USAGE_STAGING;\n    m_stagingTexDesc.BindFlags = 0;\n    m_stagingTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n    m_stagingTexDesc.MiscFlags = 0;\n\n    return m_d3dRender->GetDevice()->CreateTexture2D(&m_stagingTexDesc, nullptr, &m_stagingTex);\n}\n\nHRESULT VideoEncoderSW::CopyTexture(ID3D11Texture2D* pTexture) {\n    m_d3dRender->GetContext()->CopyResource(m_stagingTex.Get(), pTexture);\n    return m_d3dRender->GetContext()->Map(\n        m_stagingTex.Get(), 0, D3D11_MAP_READ, 0, &m_stagingTexMap\n    );\n}\n\nAVCodecID VideoEncoderSW::ToFFMPEGCodec(ALVR_CODEC codec) {\n    switch (codec) {\n    case ALVR_CODEC_H264:\n        return AV_CODEC_ID_H264;\n    case ALVR_CODEC_HEVC:\n        return AV_CODEC_ID_HEVC;\n    case ALVR_CODEC_AV1:\n        Warn(\"AV1 is not supported. Using HEVC instead.\");\n        return AV_CODEC_ID_HEVC;\n    default:\n        return AV_CODEC_ID_NONE;\n    }\n}\n\n#endif // ALVR_GPL\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderSW.h",
    "content": "#ifdef ALVR_GPL\n\n#pragma once\n\n#include <wrl.h>\n\n#include \"ALVR-common/packet_types.h\"\n#include \"VideoEncoder.h\"\n#include \"shared/d3drender.h\"\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavformat/avformat.h>\n#include <libavutil/avutil.h>\n#include <libswscale/swscale.h>\n}\n\nusing Microsoft::WRL::ComPtr;\n\n// Software video encoder using FFMPEG\nclass VideoEncoderSW : public VideoEncoder {\npublic:\n    VideoEncoderSW(std::shared_ptr<CD3DRender> pD3DRender, int width, int height);\n    ~VideoEncoderSW();\n\n    void Initialize();\n    void Shutdown();\n\n    static void LibVALog(void*, int level, const char* data, va_list va);\n\n    AVCodecID ToFFMPEGCodec(ALVR_CODEC codec);\n\n    void Transmit(\n        ID3D11Texture2D* pTexture,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        bool insertIDR\n    );\n    HRESULT SetupStagingTexture(ID3D11Texture2D* pTexture);\n    HRESULT CopyTexture(ID3D11Texture2D* pTexture);\n\nprivate:\n    std::shared_ptr<CD3DRender> m_d3dRender;\n\n    AVCodecContext* m_codecContext;\n    AVFrame *m_transferredFrame, *m_encoderFrame;\n    SwsContext* m_scalerContext = nullptr;\n\n    ComPtr<ID3D11Texture2D> m_stagingTex;\n    D3D11_TEXTURE2D_DESC m_stagingTexDesc;\n    D3D11_MAPPED_SUBRESOURCE m_stagingTexMap;\n\n    ALVR_CODEC m_codec;\n    int m_refreshRate;\n    int m_renderWidth;\n    int m_renderHeight;\n    int m_bitrateInMBits;\n};\n\n#endif // ALVR_GPL\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderVPL.cpp",
    "content": "#include \"VideoEncoderVPL.h\"\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Settings.h\"\n#include \"alvr_server/Utils.h\"\n\n#define VPLVERSION(major, minor) (major << 16 | minor)\n#define MAJOR_API_VERSION_REQUIRED 2\n#define MINOR_API_VERSION_REQUIRED 9\n#define WAIT_100_MILLISECONDS 100\n#define ALIGN16(value) (((value + 15) >> 4) << 4)\n#define ERROR_THROW(msg, ...)                                                                      \\\n    {                                                                                              \\\n        Error(\"VPL: \" msg \"\\n\", __VA_ARGS__);                                                      \\\n        throw MakeException(\"VPL: \" msg, __VA_ARGS__);                                             \\\n    }\n\n#define VPL_LOG(fn, msg, ...) fn(\"VPL: \" msg \"\\n\", __VA_ARGS__)\n#define VPL_DEBUG(msg, ...) VPL_LOG(Debug, msg, __VA_ARGS__)\n#define VPL_WARN(msg, ...) VPL_LOG(Warn, msg, __VA_ARGS__)\n#define VPL_INFO(msg, ...) VPL_LOG(Info, msg, __VA_ARGS__)\n\n#define VERIFY(expr, msg)                                                                          \\\n    if (!(expr))                                                                                   \\\n    ERROR_THROW(\"%s. %s\", msg, #expr)\n#define VPL_VERIFY(expr)                                                                           \\\n    {                                                                                              \\\n        mfxStatus res = expr;                                                                      \\\n        if (res != MFX_ERR_NONE)                                                                   \\\n            ERROR_THROW(\"\\\"%s\\\" failed with %d\", #expr, res);                                      \\\n    }\n\nVideoEncoderVPL::VideoEncoderVPL(std::shared_ptr<CD3DRender> pD3DRender, int width, int height)\n    : m_pD3DRender(pD3DRender)\n    , m_renderWidth(width)\n    , m_renderHeight(height)\n    , m_bitrateInMBits(30) {\n    VPL_DEBUG(\"constructed\");\n}\n\nVideoEncoderVPL::~VideoEncoderVPL() { VPL_DEBUG(\"destructed\"); }\n\nvoid VideoEncoderVPL::Initialize() {\n    VPL_DEBUG(\"initialize\");\n\n    ChooseParams();\n    InitTransferTex();\n    InitVpl();\n    InitVplEncode();\n\n    // Prepare output bitstream\n    m_vplBitstream.MaxLength = m_renderWidth * m_renderHeight * 8;\n    m_vplBitstream.Data = (mfxU8*)calloc(m_vplBitstream.MaxLength, sizeof(mfxU8));\n}\n\nvoid VideoEncoderVPL::Shutdown() {\n    VPL_DEBUG(\"shutdown\");\n\n    MFXVideoENCODE_Close(m_vplSession);\n    MFXClose(m_vplSession);\n\n    if (m_vplBitstream.Data)\n        free(m_vplBitstream.Data);\n\n    if (m_vplLoader)\n        MFXUnload(m_vplLoader);\n}\n\nvoid VideoEncoderVPL::Transmit(\n    ID3D11Texture2D* pTexture, uint64_t presentationTime, uint64_t targetTimestampNs, bool insertIDR\n) {\n    // VPL_DEBUG(\"transmit\");\n\n    auto dynParams = GetDynamicEncoderParams();\n    if (dynParams.updated) {\n        m_vplEncodeParams.mfx.TargetKbps = dynParams.bitrate_bps / 1000;\n        MFXVideoENCODE_Reset(m_vplSession, &m_vplEncodeParams);\n    }\n\n    auto encSurface = VplImportTexture(pTexture);\n\n    mfxEncodeCtrl encodeCtrl = {};\n    encodeCtrl.FrameType = insertIDR ? MFX_FRAMETYPE_IDR : 0;\n\n    mfxStatus sts = MFX_ERR_NONE;\n    mfxSyncPoint syncp = {};\n    bool isEncGoing = true;\n    bool isDraining = false;\n\n    while (isEncGoing) {\n        isDraining = encSurface == nullptr;\n        sts = MFXVideoENCODE_EncodeFrameAsync(\n            m_vplSession, &encodeCtrl, encSurface, &m_vplBitstream, &syncp\n        );\n\n        if (encSurface) {\n            VPL_VERIFY(encSurface->FrameInterface->Release(encSurface));\n            encSurface = nullptr;\n        }\n\n        switch (sts) {\n        case MFX_ERR_NONE:\n            // MFX_ERR_NONE and syncp indicate output is available\n            if (syncp) {\n                // Encode output is not available on CPU until sync operation completes\n                do {\n                    sts = MFXVideoCORE_SyncOperation(m_vplSession, syncp, WAIT_100_MILLISECONDS);\n                    if (MFX_ERR_NONE == sts) {\n                        ParseFrameNals(\n                            m_codec,\n                            reinterpret_cast<uint8_t*>(\n                                m_vplBitstream.Data + m_vplBitstream.DataOffset\n                            ),\n                            m_vplBitstream.DataLength,\n                            targetTimestampNs,\n                            insertIDR\n                        );\n                        m_vplBitstream.DataLength = 0;\n                    }\n                } while (sts == MFX_WRN_IN_EXECUTION);\n            }\n            break;\n\n        case MFX_ERR_NOT_ENOUGH_BUFFER:\n            ERROR_THROW(\"not enough buffer\");\n\n        case MFX_ERR_MORE_DATA:\n            if (isDraining == true)\n                isEncGoing = false;\n            break;\n\n        case MFX_ERR_DEVICE_LOST:\n            ERROR_THROW(\"device lost\");\n\n        case MFX_WRN_DEVICE_BUSY:\n            VPL_DEBUG(\"device busy\");\n            break;\n\n        default:\n            ERROR_THROW(\"unknown encoding status %d\", sts);\n        }\n    }\n}\n\nvoid VideoEncoderVPL::InitTransferTex() {\n    D3D11_TEXTURE2D_DESC transferTexDesc = { UINT(m_renderWidth),\n                                             UINT(m_renderHeight),\n                                             1,\n                                             1,\n                                             m_dxColorFormat,\n                                             { 1, 0 },\n                                             D3D11_USAGE_DEFAULT,\n                                             D3D11_BIND_SHADER_RESOURCE,\n                                             0,\n                                             D3D11_RESOURCE_MISC_SHARED };\n\n    HRESULT hr\n        = m_pD3DRender->GetDevice()->CreateTexture2D(&transferTexDesc, nullptr, &m_transferTex);\n    if (FAILED(hr))\n        ERROR_THROW(\"failed to create transfer texture HR=%p %ls\", hr, GetErrorStr(hr).c_str());\n}\n\nvoid VideoEncoderVPL::InitVpl() {\n    m_vplLoader = MFXLoad();\n    VERIFY(m_vplLoader != nullptr, \"MFXLoad failed -- is implementation in path?\");\n\n    CheckVPLConfig();\n\n    VPL_VERIFY(MFXCreateSession(m_vplLoader, 0, &m_vplSession));\n    VPL_VERIFY(\n        MFXVideoCORE_SetHandle(m_vplSession, MFX_HANDLE_D3D11_DEVICE, m_pD3DRender->GetDevice())\n    );\n\n    LogImplementationInfo();\n\n    // Get interface for ImportFrameSurface\n    VPL_VERIFY(MFXGetMemoryInterface(m_vplSession, &m_vplMemoryInterface));\n    VERIFY(m_vplMemoryInterface != nullptr, \"MFXGetMemoryInterface failed\");\n}\n\nvoid VideoEncoderVPL::InitVplEncode() {\n    m_vplEncodeParams.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;\n    m_vplEncodeParams.mfx.LowPower = MFX_CODINGOPTION_ON;\n    m_vplEncodeParams.AsyncDepth = 1;\n    m_vplEncodeParams.mfx.CodecId = m_vplCodec;\n    m_vplEncodeParams.mfx.CodecProfile = m_vplCodecProfile;\n    m_vplEncodeParams.mfx.TargetUsage = m_vplQualityPreset;\n    m_vplEncodeParams.mfx.TargetKbps = m_bitrateInMBits * 1000;\n    m_vplEncodeParams.mfx.RateControlMethod = m_vplRateControlMode;\n    m_vplEncodeParams.mfx.FrameInfo.FrameRateExtN = m_refreshRate;\n    m_vplEncodeParams.mfx.FrameInfo.FrameRateExtD = 1;\n    m_vplEncodeParams.mfx.FrameInfo.FourCC = m_vplColorFormat;\n    m_vplEncodeParams.mfx.FrameInfo.ChromaFormat = m_vplChromaFormat;\n    m_vplEncodeParams.mfx.FrameInfo.CropW = m_renderWidth;\n    m_vplEncodeParams.mfx.FrameInfo.CropH = m_renderHeight;\n    m_vplEncodeParams.mfx.FrameInfo.Width = ALIGN16(m_renderWidth);\n    m_vplEncodeParams.mfx.FrameInfo.Height = ALIGN16(m_renderHeight);\n\n    mfxStatus sts = MFXVideoENCODE_Query(m_vplSession, &m_vplEncodeParams, &m_vplEncodeParams);\n    switch (sts) {\n    case MFX_WRN_INCOMPATIBLE_VIDEO_PARAM:\n        VPL_WARN(\"incompatible video params, auto-correcting\");\n        break;\n\n    case MFX_WRN_PARTIAL_ACCELERATION:\n        VPL_WARN(\"partial acceleration\");\n        break;\n\n    case MFX_ERR_UNSUPPORTED:\n        ERROR_THROW(\"query unsupported\");\n    }\n\n    // Initialize ENCODE\n    VPL_VERIFY(MFXVideoENCODE_Init(m_vplSession, &m_vplEncodeParams));\n}\n\nmfxFrameSurface1* VideoEncoderVPL::VplImportTexture(ID3D11Texture2D* texture) {\n    m_pD3DRender->GetContext()->CopyResource(m_transferTex.p, texture);\n\n    mfxSurfaceD3D11Tex2D extSurfD3D11 = {};\n    extSurfD3D11.SurfaceInterface.Header.SurfaceType = MFX_SURFACE_TYPE_D3D11_TEX2D;\n    extSurfD3D11.SurfaceInterface.Header.SurfaceFlags\n        = MFX_SURFACE_FLAG_IMPORT_SHARED | MFX_SURFACE_FLAG_IMPORT_COPY;\n    extSurfD3D11.SurfaceInterface.Header.StructSize = sizeof(mfxSurfaceD3D11Tex2D);\n    extSurfD3D11.texture2D = m_transferTex.p;\n\n    mfxFrameSurface1* encSurface = nullptr;\n    VPL_VERIFY(m_vplMemoryInterface->ImportFrameSurface(\n        m_vplMemoryInterface,\n        MFX_SURFACE_COMPONENT_ENCODE,\n        &extSurfD3D11.SurfaceInterface.Header,\n        &encSurface\n    ));\n\n    return encSurface;\n}\n\nvoid VideoEncoderVPL::ChooseParams() {\n    Settings& s = Settings::Instance();\n\n    m_refreshRate = s.m_refreshRate;\n    m_codec = s.m_codec;\n\n    // h264 encoding is currently broken due to an encoding\n    // error when forcing the idr frame type:\n    //\n    // mfxEncodeCtrl encodeCtrl = {};\n    // encodeCtrl.FrameType = MFX_FRAMETYPE_IDR;\n    //\n    // Results in error -15 (MFX_ERR_INVALID_VIDEO_PARAM)\n    // when encoding a frame.\n    if (m_codec == ALVR_CODEC_H264) {\n        VPL_WARN(\"h264 codec currently unsupported, forcing HEVC\");\n        m_codec = ALVR_CODEC_HEVC;\n    }\n\n    if (s.m_enableHdr) {\n        if (s.m_use10bitEncoder) {\n            m_dxColorFormat = DXGI_FORMAT_P010;\n            m_vplColorFormat = MFX_FOURCC_P010;\n            m_vplChromaFormat = MFX_CHROMAFORMAT_YUV420;\n        } else {\n            m_dxColorFormat = DXGI_FORMAT_NV12;\n            m_vplColorFormat = MFX_FOURCC_NV12;\n            m_vplChromaFormat = MFX_CHROMAFORMAT_YUV420;\n        }\n    } else {\n        m_dxColorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;\n        m_vplColorFormat = MFX_FOURCC_BGR4;\n        m_vplChromaFormat = MFX_CHROMAFORMAT_YUV444;\n    }\n\n    switch (m_codec) {\n    case ALVR_CODEC_H264:\n        m_vplCodec = MFX_CODEC_AVC;\n        break;\n    case ALVR_CODEC_HEVC:\n        m_vplCodec = MFX_CODEC_HEVC;\n        break;\n    case ALVR_CODEC_AV1:\n        m_vplCodec = MFX_CODEC_AV1;\n        break;\n    default:\n        ERROR_THROW(\"unsupported video encoding %d\", s.m_codec);\n    }\n\n    m_vplCodecProfile = MFX_PROFILE_UNKNOWN;\n    if (m_codec == ALVR_CODEC_H264) {\n        switch (s.m_h264Profile) {\n        case ALVR_H264_PROFILE_BASELINE:\n            m_vplCodecProfile = MFX_PROFILE_AVC_BASELINE;\n            break;\n        case ALVR_H264_PROFILE_MAIN:\n            m_vplCodecProfile = MFX_PROFILE_AVC_MAIN;\n            break;\n        case ALVR_H264_PROFILE_HIGH:\n            m_vplCodecProfile = MFX_PROFILE_AVC_HIGH;\n            break;\n        default:\n            ERROR_THROW(\"unsupported h264 profile %d\", s.m_h264Profile);\n        }\n    }\n\n    if (s.m_use10bitEncoder) {\n        switch (m_codec) {\n        case ALVR_CODEC_H264:\n            m_vplCodecProfile = MFX_PROFILE_AVC_HIGH10;\n            break;\n        case ALVR_CODEC_HEVC:\n            m_vplCodecProfile = MFX_PROFILE_HEVC_MAIN10;\n            break;\n        }\n    }\n\n    switch (s.m_encoderQualityPreset) {\n    case ALVR_QUALITY:\n        m_vplQualityPreset = MFX_TARGETUSAGE_BEST_QUALITY;\n        break;\n    case ALVR_BALANCED:\n        m_vplQualityPreset = MFX_TARGETUSAGE_BALANCED;\n        break;\n    case ALVR_SPEED:\n        m_vplQualityPreset = MFX_TARGETUSAGE_BEST_SPEED;\n        break;\n    default:\n        ERROR_THROW(\"invalid encoder quality preset\");\n    }\n\n    switch (s.m_rateControlMode) {\n    case ALVR_CBR:\n        m_vplRateControlMode = MFX_RATECONTROL_CBR;\n        break;\n    case ALVR_VBR:\n        m_vplRateControlMode = MFX_RATECONTROL_VBR;\n        break;\n    default:\n        ERROR_THROW(\"invalid rate control mode\");\n    }\n}\n\nvoid VideoEncoderVPL::CheckVPLConfig() {\n    mfxConfig cfg[5];\n    mfxVariant cfgVal[5];\n\n    // Implementation used must be the hardware implementation\n    cfg[0] = MFXCreateConfig(m_vplLoader);\n    VERIFY(cfg[0] != NULL, \"MFXCreateConfig failed\");\n    cfgVal[0].Type = MFX_VARIANT_TYPE_U32;\n    cfgVal[0].Data.U32 = MFX_IMPL_TYPE_HARDWARE;\n    VPL_VERIFY(MFXSetConfigFilterProperty(cfg[0], (mfxU8*)\"mfxImplDescription.Impl\", cfgVal[0]));\n\n    // Implementation used must provide API version 2.9 or newer\n    cfg[1] = MFXCreateConfig(m_vplLoader);\n    VERIFY(NULL != cfg[1], \"MFXCreateConfig failed\")\n    cfgVal[1].Type = MFX_VARIANT_TYPE_U32;\n    cfgVal[1].Data.U32 = VPLVERSION(2, 9);\n    VPL_VERIFY(MFXSetConfigFilterProperty(\n        cfg[1], (mfxU8*)\"mfxImplDescription.ApiVersion.Version\", cfgVal[1]\n    ));\n\n    // Implementation used must be D3D11 acceleration mode\n    cfg[2] = MFXCreateConfig(m_vplLoader);\n    VERIFY(NULL != cfg[2], \"MFXCreateConfig failed\")\n    cfgVal[2].Type = MFX_VARIANT_TYPE_U32;\n    cfgVal[2].Data.U32 = MFX_ACCEL_MODE_VIA_D3D11;\n    VPL_VERIFY(\n        MFXSetConfigFilterProperty(cfg[2], (mfxU8*)\"mfxImplDescription.AccelerationMode\", cfgVal[2])\n    );\n\n    // Implementation used must be D3D11 surface sharing mode\n    // Applying the 3 associated parameters (logical AND operation) using a single mfxConfig\n    cfg[3] = MFXCreateConfig(m_vplLoader);\n    VERIFY(NULL != cfg[3], \"MFXCreateConfig failed\")\n    cfgVal[3].Type = MFX_VARIANT_TYPE_U32;\n    cfgVal[3].Data.U32 = MFX_SURFACE_TYPE_D3D11_TEX2D;\n    VPL_VERIFY(MFXSetConfigFilterProperty(\n        cfg[3], (mfxU8*)\"mfxSurfaceTypesSupported.surftype.SurfaceType\", cfgVal[3]\n    ));\n\n    cfgVal[3].Data.U32 = MFX_SURFACE_COMPONENT_ENCODE;\n    VPL_VERIFY(MFXSetConfigFilterProperty(\n        cfg[3], (mfxU8*)\"mfxSurfaceTypesSupported.surftype.surfcomp.SurfaceComponent\", cfgVal[3]\n    ));\n\n    cfgVal[3].Data.U32 = MFX_SURFACE_FLAG_IMPORT_COPY;\n    VPL_VERIFY(MFXSetConfigFilterProperty(\n        cfg[3], (mfxU8*)\"mfxSurfaceTypesSupported.surftype.surfcomp.SurfaceFlags\", cfgVal[3]\n    ));\n\n    // Implementation must provide correct codec\n    cfg[4] = MFXCreateConfig(m_vplLoader);\n    VERIFY(NULL != cfg[4], \"MFXCreateConfig failed\")\n    cfgVal[4].Type = MFX_VARIANT_TYPE_U32;\n    cfgVal[4].Data.U32 = m_vplCodec;\n    VPL_VERIFY(MFXSetConfigFilterProperty(\n        cfg[4], (mfxU8*)\"mfxImplDescription.mfxEncoderDescription.encoder.CodecID\", cfgVal[4]\n    ));\n}\n\nvoid VideoEncoderVPL::LogImplementationInfo() {\n    mfxImplDescription* idesc = nullptr;\n    mfxStatus sts;\n    // Loads info about implementation at specified list location\n    sts = MFXEnumImplementations(m_vplLoader, 0, MFX_IMPLCAPS_IMPLDESCSTRUCTURE, (mfxHDL*)&idesc);\n    if (!idesc || (sts != MFX_ERR_NONE))\n        return;\n\n    const char* accel_mode;\n    switch (idesc->AccelerationMode) {\n    case MFX_ACCEL_MODE_NA:\n        accel_mode = \"na\";\n        break;\n    case MFX_ACCEL_MODE_VIA_D3D9:\n        accel_mode = \"d3d9\";\n        break;\n    case MFX_ACCEL_MODE_VIA_D3D11:\n        accel_mode = \"d3d11\";\n        break;\n    case MFX_ACCEL_MODE_VIA_VAAPI:\n        accel_mode = \"vaapi\";\n        break;\n    case MFX_ACCEL_MODE_VIA_VAAPI_DRM_MODESET:\n        accel_mode = \"vaapi_drm_modeset\";\n        break;\n    case MFX_ACCEL_MODE_VIA_VAAPI_GLX:\n        accel_mode = \"vaapi_glx\";\n        break;\n    case MFX_ACCEL_MODE_VIA_VAAPI_X11:\n        accel_mode = \"vaapi_x11\";\n        break;\n    case MFX_ACCEL_MODE_VIA_VAAPI_WAYLAND:\n        accel_mode = \"vaapi_wayland\";\n        break;\n    case MFX_ACCEL_MODE_VIA_HDDLUNITE:\n        accel_mode = \"hddlunite\";\n        break;\n    default:\n        accel_mode = \"unknown\";\n        break;\n    }\n\n    VPL_INFO(\n        \"using api version %hu.%hu on device %s\",\n        idesc->ApiVersion.Major,\n        idesc->ApiVersion.Minor,\n        idesc->Dev.DeviceID\n    );\n    VPL_DEBUG(\"api version: %hu.%hu\", idesc->ApiVersion.Major, idesc->ApiVersion.Minor);\n    VPL_DEBUG(\"impl type: hw\");\n    VPL_DEBUG(\"accel mode: %s\", accel_mode);\n    VPL_DEBUG(\"device id: %s\", idesc->Dev.DeviceID);\n    MFXDispReleaseImplDescription(m_vplLoader, idesc);\n\n#if (MFX_VERSION >= 2004)\n    // Show implementation path, added in 2.4 API\n    mfxHDL implPath = nullptr;\n    sts = MFXEnumImplementations(m_vplLoader, 0, MFX_IMPLCAPS_IMPLPATH, &implPath);\n    if (!implPath || (sts != MFX_ERR_NONE))\n        return;\n\n    VPL_DEBUG(\"path: %s\", reinterpret_cast<mfxChar*>(implPath));\n    MFXDispReleaseImplDescription(m_vplLoader, implPath);\n#endif\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/VideoEncoderVPL.h",
    "content": "#pragma once\n\n#include \"VideoEncoder.h\"\n#include \"shared/d3drender.h\"\n#include <atlbase.h>\n#include <d3d11.h>\n#include <dxgi.h>\n#include <vector>\n\n#include \"vpl/mfx.h\"\n#include \"vpl/mfxjpeg.h\"\n#include \"vpl/mfxmemory.h\"\n#include \"vpl/mfxstructures.h\"\n#include \"vpl/mfxvideo.h\"\n#if (MFX_VERSION >= 2000)\n#include \"vpl/mfxdispatcher.h\"\n#endif\n\nclass VideoEncoderVPL : public VideoEncoder {\npublic:\n    VideoEncoderVPL(std::shared_ptr<CD3DRender> pD3DRender, int width, int height);\n    ~VideoEncoderVPL();\n\n    void Initialize();\n    void Shutdown();\n\n    void Transmit(\n        ID3D11Texture2D* pTexture,\n        uint64_t presentationTime,\n        uint64_t targetTimestampNs,\n        bool insertIDR\n    );\n\nprivate:\n    void CheckVPLConfig();\n    void ChooseParams();\n    void InitTransferTex();\n    void InitVpl();\n    void InitVplEncode();\n    mfxFrameSurface1* VplImportTexture(ID3D11Texture2D* texture);\n    void LogImplementationInfo();\n\n    std::shared_ptr<CD3DRender> m_pD3DRender;\n    int m_codec;\n    int m_renderWidth;\n    int m_renderHeight;\n    int m_refreshRate;\n    int m_bitrateInMBits;\n\n    mfxU32 m_vplCodec;\n    mfxU32 m_vplCodecProfile;\n    mfxU32 m_vplColorFormat;\n    mfxU32 m_vplChromaFormat;\n    mfxU32 m_vplQualityPreset;\n    mfxU32 m_vplRateControlMode;\n    DXGI_FORMAT m_dxColorFormat;\n    mfxVideoParam m_vplEncodeParams = {};\n\n    mfxLoader m_vplLoader = nullptr;\n    mfxSession m_vplSession = nullptr;\n    mfxBitstream m_vplBitstream = {};\n    mfxMemoryInterface* m_vplMemoryInterface = nullptr;\n    CComPtr<ID3D11Texture2D> m_transferTex;\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/QuadVertexShader.hlsl",
    "content": "struct PixelType {\n\tfloat2 uv : TEXCOORD0; // TEXCOORD0 must be first if I don't want to define \"position\" in the pixel shader\n\tfloat4 position : SV_Position;\n};\n\n//https://gamedev.stackexchange.com/questions/98283/how-do-i-draw-a-full-screen-quad-in-directx-11\nPixelType main(uint vertexID : SV_VertexID) {\n\tPixelType pix;\n\tpix.uv = float2(vertexID & 1, vertexID >> 1);\n\tpix.position = float4((pix.uv.x - 0.5f) * 2, -(pix.uv.y - 0.5f) * 2, 0, 1);\n\treturn pix;\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderPipeline.cpp",
    "content": "#include \"RenderPipeline.h\"\n\nusing namespace std::string_literals;\n\nnamespace d3d_render_utils {\n\nRenderPipeline::RenderPipeline(ID3D11Device* device) {\n    mDevice = device;\n    mDevice->GetImmediateContext(&mImmediateContext);\n}\n\nvoid RenderPipeline::Initialize(\n    std::vector<ID3D11Texture2D*> inputTextures,\n    ID3D11VertexShader* quadVertexShader,\n    ID3D11PixelShader* pixelShader,\n    ID3D11Texture2D* renderTarget,\n    ID3D11Buffer* shaderBuffer,\n    bool enableAlphaBlend,\n    bool overrideAlpha\n) {\n    mInputTextureViews.clear();\n    for (auto tex : inputTextures) {\n        ID3D11ShaderResourceView* resourceView;\n        OK_OR_THROW(\n            mDevice->CreateShaderResourceView(tex, nullptr, &resourceView),\n            \"Failed to create input texture resosurce view.\"\n        );\n        mInputTextureViews.push_back(resourceView);\n    }\n\n    OK_OR_THROW(\n        mDevice->CreateShaderResourceView(renderTarget, nullptr, &mRenderTargetResourceView),\n        \"Failed to create render target resosurce view.\"\n    );\n\n    OK_OR_THROW(\n        mDevice->CreateRenderTargetView(renderTarget, nullptr, &mRenderTargetView),\n        \"Failed to create ID3D11RenderTargetView.\"\n    );\n\n    D3D11_TEXTURE2D_DESC renderTargetDesc;\n    renderTarget->GetDesc(&renderTargetDesc);\n\n    mViewport.Width = (FLOAT)renderTargetDesc.Width;\n    mViewport.Height = (FLOAT)renderTargetDesc.Height;\n    mViewport.MinDepth = 0.0f;\n    mViewport.MaxDepth = 1.0f;\n    mViewport.TopLeftX = 0.0f;\n    mViewport.TopLeftY = 0.0f;\n\n    mGenerateMipmaps = renderTargetDesc.MipLevels != 1;\n\n    mShaderBuffer = shaderBuffer;\n    mVertexShader = quadVertexShader;\n    mPixelShader = pixelShader;\n\n    D3D11_BLEND_DESC blendDesc = { 0 };\n    blendDesc.RenderTarget[0].BlendEnable = enableAlphaBlend;\n    blendDesc.RenderTarget[0].SrcBlend = (overrideAlpha ? D3D11_BLEND_ONE : D3D11_BLEND_SRC_ALPHA);\n    blendDesc.RenderTarget[0].DestBlend\n        = (overrideAlpha ? D3D11_BLEND_ZERO : D3D11_BLEND_INV_SRC_ALPHA);\n    blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;\n    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;\n    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;\n    blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;\n    blendDesc.RenderTarget[0].RenderTargetWriteMask\n        = (overrideAlpha ? D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN\n                   | D3D11_COLOR_WRITE_ENABLE_BLUE\n                         : D3D11_COLOR_WRITE_ENABLE_ALL);\n    OK_OR_THROW(\n        mDevice->CreateBlendState(&blendDesc, &mBlendState), \"Failed to create blend state.\"\n    );\n}\n\nvoid RenderPipeline::Initialize(\n    std::vector<ID3D11Texture2D*> inputTextures,\n    ID3D11VertexShader* quadVertexShader,\n    std::vector<uint8_t>& pixelShaderCSO,\n    ID3D11Texture2D* renderTarget,\n    ID3D11Buffer* shaderBuffer,\n    bool enableAlphaBlend,\n    bool overrideAlpha\n) {\n    auto pixelShader = CreatePixelShader(mDevice.Get(), pixelShaderCSO);\n    Initialize(\n        inputTextures,\n        quadVertexShader,\n        pixelShader,\n        renderTarget,\n        shaderBuffer,\n        enableAlphaBlend,\n        overrideAlpha\n    );\n}\n\nvoid RenderPipeline::Render(ID3D11DeviceContext* otherContext) {\n    ID3D11DeviceContext* context = otherContext != nullptr ? otherContext : mImmediateContext.Get();\n\n    context->OMSetRenderTargets(1, mRenderTargetView.GetAddressOf(), nullptr);\n    context->RSSetViewports(1, &mViewport);\n\n    context->OMSetBlendState(mBlendState.Get(), nullptr, 0xffffffff);\n\n    if (mShaderBuffer != nullptr) {\n        context->PSSetConstantBuffers(0, 1, mShaderBuffer.GetAddressOf());\n    }\n\n    std::vector<ID3D11ShaderResourceView*> inputTextureViewPtrs;\n    for (auto texView : mInputTextureViews) {\n        inputTextureViewPtrs.push_back(texView.Get());\n    }\n    context->PSSetShaderResources(0, (UINT)mInputTextureViews.size(), &inputTextureViewPtrs[0]);\n\n    context->VSSetShader(mVertexShader.Get(), nullptr, 0);\n    context->PSSetShader(mPixelShader.Get(), nullptr, 0);\n\n    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);\n    context->Draw(4, 0);\n\n    if (mGenerateMipmaps) {\n        context->GenerateMips(mRenderTargetResourceView.Get());\n    }\n}\n\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderPipeline.h",
    "content": "#pragma once\n\n#include \"RenderUtils.h\"\n\nnamespace d3d_render_utils {\n\nclass RenderPipeline {\npublic:\n    RenderPipeline(ID3D11Device* device);\n\n    void Initialize(\n        std::vector<ID3D11Texture2D*> inputTextures,\n        ID3D11VertexShader* quadVertexShader,\n        std::vector<uint8_t>& pixelShaderCSO,\n        ID3D11Texture2D* renderTarget,\n        ID3D11Buffer* shaderBuffer = nullptr,\n        bool enableAlphaBlend = false,\n        bool overrideAlpha = false\n    );\n    void Initialize(\n        std::vector<ID3D11Texture2D*> inputTextures,\n        ID3D11VertexShader* quadVertexShader,\n        ID3D11PixelShader* pixelShader,\n        ID3D11Texture2D* renderTarget,\n        ID3D11Buffer* shaderBuffer = nullptr,\n        bool enableAlphaBlend = false,\n        bool overrideAlpha = false\n    );\n\n    void Render(ID3D11DeviceContext* otherContext = nullptr);\n\nprivate:\n    D3D11_VIEWPORT mViewport;\n    bool mGenerateMipmaps;\n\n    Microsoft::WRL::ComPtr<ID3D11Device> mDevice;\n    Microsoft::WRL::ComPtr<ID3D11DeviceContext> mImmediateContext;\n    std::vector<Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>> mInputTextureViews;\n    Microsoft::WRL::ComPtr<ID3D11RasterizerState> mRasterizerState;\n    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> mRenderTargetView;\n    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> mRenderTargetResourceView;\n    Microsoft::WRL::ComPtr<ID3D11Buffer> mShaderBuffer;\n    Microsoft::WRL::ComPtr<ID3D11VertexShader> mVertexShader;\n    Microsoft::WRL::ComPtr<ID3D11PixelShader> mPixelShader;\n    Microsoft::WRL::ComPtr<ID3D11BlendState> mBlendState;\n};\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderPipelineYUV.cpp",
    "content": "#include \"RenderPipelineYUV.h\"\n\nusing namespace std::string_literals;\n\nnamespace d3d_render_utils {\n\nRenderPipelineYUV::RenderPipelineYUV(ID3D11Device* device) {\n    mDevice = device;\n    mDevice->GetImmediateContext(&mImmediateContext);\n}\n\nvoid RenderPipelineYUV::Initialize(\n    std::vector<ID3D11Texture2D*> inputTextures,\n    ID3D11VertexShader* quadVertexShader,\n    ID3D11PixelShader* pixelShader,\n    ID3D11Texture2D* renderTarget,\n    ID3D11Buffer* shaderBuffer\n) {\n    mInputTextureViews.clear();\n    for (auto tex : inputTextures) {\n        ID3D11ShaderResourceView* resourceView;\n        OK_OR_THROW(\n            mDevice->CreateShaderResourceView(tex, nullptr, &resourceView),\n            \"Failed to create input texture resosurce view.\"\n        );\n        mInputTextureViews.push_back(resourceView);\n    }\n\n    D3D11_TEXTURE2D_DESC renderTargetDesc;\n    renderTarget->GetDesc(&renderTargetDesc);\n\n    DXGI_FORMAT uvFormat = renderTargetDesc.Format == DXGI_FORMAT_NV12 ? DXGI_FORMAT_R8G8_UNORM\n                                                                       : DXGI_FORMAT_R16G16_UNORM;\n    DXGI_FORMAT yFormat = renderTargetDesc.Format == DXGI_FORMAT_NV12 ? DXGI_FORMAT_R8_UNORM\n                                                                      : DXGI_FORMAT_R16_UNORM;\n\n    // Create SRV for luminance (Y) plane\n    D3D11_SHADER_RESOURCE_VIEW_DESC srvDescY = {};\n    srvDescY.Format = yFormat;\n    srvDescY.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;\n    srvDescY.Texture2D.MostDetailedMip = 0;\n    srvDescY.Texture2D.MipLevels = 1;\n    ID3D11ShaderResourceView* pSRVY;\n    OK_OR_THROW(\n        mDevice->CreateShaderResourceView(renderTarget, &srvDescY, &mRenderTargetResourceViewY),\n        \"Failed to create render target resosurce view.\"\n    );\n\n    // Create SRV for chrominance (UV) planes\n    D3D11_SHADER_RESOURCE_VIEW_DESC srvDescUV = {};\n    srvDescUV.Format = uvFormat;\n    srvDescUV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;\n    srvDescUV.Texture2D.MostDetailedMip = 0;\n    srvDescUV.Texture2D.MipLevels = 1;\n    ID3D11ShaderResourceView* pSRVUV;\n    OK_OR_THROW(\n        mDevice->CreateShaderResourceView(renderTarget, &srvDescUV, &mRenderTargetResourceViewUV),\n        \"Failed to create render target resosurce view.\"\n    );\n\n    // Create luminance (Y) render target view\n    D3D11_RENDER_TARGET_VIEW_DESC rtvDescLuminance = {};\n    rtvDescLuminance.Format = yFormat;\n    rtvDescLuminance.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;\n    rtvDescLuminance.Texture2D.MipSlice = 0;\n    ID3D11RenderTargetView* pRTVLuminance;\n    OK_OR_THROW(\n        mDevice->CreateRenderTargetView(renderTarget, &rtvDescLuminance, &mRenderTargetViewY),\n        \"Failed to create ID3D11RenderTargetView.\"\n    );\n\n    // Create chrominance (UV) render target view\n    D3D11_RENDER_TARGET_VIEW_DESC rtvDescChrominance = {};\n    rtvDescChrominance.Format = uvFormat;\n    rtvDescChrominance.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;\n    rtvDescChrominance.Texture2D.MipSlice = 0;\n    ID3D11RenderTargetView* pRTVChrominance;\n    OK_OR_THROW(\n        mDevice->CreateRenderTargetView(renderTarget, &rtvDescChrominance, &mRenderTargetViewUV),\n        \"Failed to create ID3D11RenderTargetView.\"\n    );\n\n    mViewportY.Width = (FLOAT)renderTargetDesc.Width;\n    mViewportY.Height = (FLOAT)renderTargetDesc.Height;\n    mViewportY.MinDepth = 0.0f;\n    mViewportY.MaxDepth = 1.0f;\n    mViewportY.TopLeftX = 0.0f;\n    mViewportY.TopLeftY = 0.0f;\n\n    mViewportUV.Width = (FLOAT)renderTargetDesc.Width;\n    mViewportUV.Height = (FLOAT)renderTargetDesc.Height;\n    mViewportUV.MinDepth = 0.0f;\n    mViewportUV.MaxDepth = 1.0f;\n    mViewportUV.TopLeftX = 0.0f;\n    mViewportUV.TopLeftY = 0.0f;\n\n    mGenerateMipmaps = renderTargetDesc.MipLevels != 1;\n\n    mShaderBuffer = shaderBuffer;\n    mVertexShader = quadVertexShader;\n    mPixelShader = pixelShader;\n}\n\nvoid RenderPipelineYUV::Initialize(\n    std::vector<ID3D11Texture2D*> inputTextures,\n    ID3D11VertexShader* quadVertexShader,\n    std::vector<uint8_t>& pixelShaderCSO,\n    ID3D11Texture2D* renderTarget,\n    ID3D11Buffer* shaderBuffer\n) {\n    auto pixelShader = CreatePixelShader(mDevice.Get(), pixelShaderCSO);\n    Initialize(inputTextures, quadVertexShader, pixelShader, renderTarget, shaderBuffer);\n}\n\nvoid RenderPipelineYUV::Render(ID3D11DeviceContext* otherContext) {\n    ID3D11DeviceContext* context = otherContext != nullptr ? otherContext : mImmediateContext.Get();\n\n    ID3D11RenderTargetView* aRenderTargets[]\n        = { mRenderTargetViewY.Get(), mRenderTargetViewUV.Get() };\n    D3D11_VIEWPORT aViewports[] = { mViewportY, mViewportUV };\n\n    ID3D11RenderTargetView* aRenderTargetsHack[] = { mRenderTargetViewY.Get() };\n    D3D11_VIEWPORT aViewportsHack[] = { mViewportY };\n\n    context->OMSetRenderTargets(2, aRenderTargets, nullptr);\n    context->RSSetViewports(2, aViewports);\n\n    if (mShaderBuffer != nullptr) {\n        context->PSSetConstantBuffers(0, 1, mShaderBuffer.GetAddressOf());\n    }\n\n    std::vector<ID3D11ShaderResourceView*> inputTextureViewPtrs;\n    for (auto texView : mInputTextureViews) {\n        inputTextureViewPtrs.push_back(texView.Get());\n    }\n    context->PSSetShaderResources(0, (UINT)mInputTextureViews.size(), &inputTextureViewPtrs[0]);\n\n    context->VSSetShader(mVertexShader.Get(), nullptr, 0);\n    context->PSSetShader(mPixelShader.Get(), nullptr, 0);\n\n    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);\n    context->Draw(4, 0);\n\n    // HACK: Whyyyyyy does the Y channel only render the top-left corner\n    // with both render targets enabled????\n    context->OMSetRenderTargets(1, aRenderTargetsHack, nullptr);\n    context->RSSetViewports(1, aViewportsHack);\n\n    context->Draw(4, 0);\n\n    if (mGenerateMipmaps) {\n        context->GenerateMips(mRenderTargetResourceViewY.Get());\n        context->GenerateMips(mRenderTargetResourceViewUV.Get());\n    }\n}\n\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderPipelineYUV.h",
    "content": "#pragma once\n\n#include \"RenderUtils.h\"\n\nnamespace d3d_render_utils {\n\nclass RenderPipelineYUV {\npublic:\n    RenderPipelineYUV(ID3D11Device* device);\n\n    void Initialize(\n        std::vector<ID3D11Texture2D*> inputTextures,\n        ID3D11VertexShader* quadVertexShader,\n        std::vector<uint8_t>& pixelShaderCSO,\n        ID3D11Texture2D* renderTarget,\n        ID3D11Buffer* shaderBuffer = nullptr\n    );\n    void Initialize(\n        std::vector<ID3D11Texture2D*> inputTextures,\n        ID3D11VertexShader* quadVertexShader,\n        ID3D11PixelShader* pixelShader,\n        ID3D11Texture2D* renderTarget,\n        ID3D11Buffer* shaderBuffer = nullptr\n    );\n\n    void Render(ID3D11DeviceContext* otherContext = nullptr);\n\nprivate:\n    D3D11_VIEWPORT mViewportY;\n    D3D11_VIEWPORT mViewportUV;\n    bool mGenerateMipmaps;\n\n    Microsoft::WRL::ComPtr<ID3D11Device> mDevice;\n    Microsoft::WRL::ComPtr<ID3D11DeviceContext> mImmediateContext;\n    std::vector<Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>> mInputTextureViews;\n    Microsoft::WRL::ComPtr<ID3D11RasterizerState> mRasterizerState;\n    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> mRenderTargetViewY;\n    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> mRenderTargetViewUV;\n    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> mRenderTargetResourceViewY;\n    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> mRenderTargetResourceViewUV;\n    Microsoft::WRL::ComPtr<ID3D11Buffer> mShaderBuffer;\n    Microsoft::WRL::ComPtr<ID3D11VertexShader> mVertexShader;\n    Microsoft::WRL::ComPtr<ID3D11PixelShader> mPixelShader;\n    Microsoft::WRL::ComPtr<ID3D11BlendState> mBlendState;\n};\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.cpp",
    "content": "#include \"RenderUtils.h\"\n\n#include <d3d11_4.h>\n\nusing namespace std::string_literals;\nusing Microsoft::WRL::ComPtr;\n\nnamespace d3d_render_utils {\n\nvoid GetAdapterInfo(ID3D11Device* d3dDevice, int32_t& adapterIndex, std::wstring& adapterName) {\n    ComPtr<IDXGIDevice> dxgiDevice;\n    OK_OR_THROW(QUERY(d3dDevice, &dxgiDevice), \"Failed to query DXGI device.\");\n\n    ComPtr<IDXGIAdapter> adapter;\n    OK_OR_THROW(\n        dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&adapter),\n        \"Failed to get DXGI adapter.\"\n    );\n\n    ComPtr<IDXGIFactory> factory;\n    OK_OR_THROW(\n        adapter->GetParent(__uuidof(IDXGIFactory), (void**)&factory), \"Failed to get DXGI factory.\"\n    );\n\n    DXGI_ADAPTER_DESC adapterDesc;\n    adapter->GetDesc(&adapterDesc);\n\n    ComPtr<IDXGIAdapter> enumeratedAdapter;\n    for (UINT idx = 0; factory->EnumAdapters(idx, &enumeratedAdapter) != DXGI_ERROR_NOT_FOUND;\n         idx++) {\n        DXGI_ADAPTER_DESC enumeratedDesc;\n        enumeratedAdapter->GetDesc(&enumeratedDesc);\n\n        if (enumeratedDesc.AdapterLuid.HighPart == adapterDesc.AdapterLuid.HighPart\n            && enumeratedDesc.AdapterLuid.LowPart == adapterDesc.AdapterLuid.LowPart) {\n            adapterIndex = idx;\n            adapterName = adapterDesc.Description;\n            return;\n        }\n    }\n\n    throw MakeException(\"No valid adapter found.\");\n}\n\nID3D11Device* CreateDevice(IDXGIAdapter* dxgiAdapter) {\n    UINT creationFlags = 0;\n#if _DEBUG\n    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;\n#endif\n\n    D3D_FEATURE_LEVEL featureLevel;\n\n    ID3D11Device* device;\n    ComPtr<ID3D11DeviceContext> context;\n    OK_OR_THROW(\n        D3D11CreateDevice(\n            dxgiAdapter,\n            dxgiAdapter != nullptr ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE,\n            nullptr,\n            creationFlags,\n            nullptr,\n            0,\n            D3D11_SDK_VERSION,\n            &device,\n            &featureLevel,\n            &context\n        ),\n        \"Failed to create D3D11 device!\"\n    );\n\n    if (featureLevel < D3D_FEATURE_LEVEL_11_0) {\n        throw MakeException(\"DX11 level hardware required!\");\n    }\n\n    // todo: check if needed:\n    ComPtr<ID3D11Multithread> multithread;\n    if (SUCCEEDED(QUERY(context, &multithread))) {\n        multithread->SetMultithreadProtected(true);\n    } else {\n        Debug(\"Failed to get ID3D11Multithread interface. Ignore.\\n\");\n    }\n\n    return device;\n}\n\nID3D11Device* CreateDevice(uint32_t adapterIndex) {\n    ComPtr<IDXGIFactory1> factory;\n    OK_OR_THROW(\n        CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory),\n        \"Failed to create DXGIFactory1!\"\n    );\n\n    ComPtr<IDXGIAdapter> adapter;\n    OK_OR_THROW(factory->EnumAdapters(adapterIndex, &adapter), \"Failed to create DXGIAdapter!\");\n\n    return CreateDevice(adapter.Get());\n}\n\nID3D11Texture2D* CreateTexture(\n    ID3D11Device* device,\n    uint32_t width,\n    uint32_t height,\n    DXGI_FORMAT format,\n    bool mipmaps,\n    bool shared,\n    UINT sampleCount\n) {\n    D3D11_TEXTURE2D_DESC desc = { 0 };\n    desc.Width = width;\n    desc.Height = height;\n    desc.Format = format;\n    desc.SampleDesc.Count = sampleCount;\n    desc.MipLevels = mipmaps ? 0 : 1;\n    desc.MiscFlags = (shared ? D3D11_RESOURCE_MISC_SHARED : 0)\n        | (mipmaps ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0);\n    // D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX | D3D11_RESOURCE_MISC_SHARED_NTHANDLE\n\n    desc.ArraySize = 1;\n    desc.SampleDesc.Quality = 0;\n    desc.Usage = D3D11_USAGE_DEFAULT;\n    desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;\n    desc.CPUAccessFlags = 0;\n\n    ID3D11Texture2D* texture;\n    OK_OR_THROW(device->CreateTexture2D(&desc, nullptr, &texture), \"Failed to create texture.\");\n    return texture;\n}\n\nID3D11Buffer*\n_CreateBuffer(ID3D11Device* device, const void* bufferData, size_t bufferSize, D3D11_USAGE usage) {\n    D3D11_BUFFER_DESC bufferDesc = { 0 };\n    bufferDesc.Usage = usage;\n    bufferDesc.ByteWidth = (UINT)bufferSize;\n    bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;\n    bufferDesc.StructureByteStride = 0;\n\n    D3D11_SUBRESOURCE_DATA dataDesc = { 0 };\n    dataDesc.pSysMem = bufferData;\n\n    ID3D11Buffer* buffer;\n    OK_OR_THROW(\n        device->CreateBuffer(&bufferDesc, bufferData != nullptr ? &dataDesc : nullptr, &buffer),\n        \"Failed to create D3D11 buffer.\"\n    );\n    return buffer;\n}\n\nvoid UpdateBuffer(ID3D11DeviceContext* context, ID3D11Buffer* buffer, const void* bufferData) {\n    context->UpdateSubresource(buffer, 0, nullptr, bufferData, 0, 0);\n}\n\nID3D11VertexShader*\nCreateVertexShader(ID3D11Device* device, std::vector<uint8_t>& vertexShaderCSO) {\n    ID3D11VertexShader* vertexShader;\n    OK_OR_THROW(\n        device->CreateVertexShader(\n            &vertexShaderCSO[0], vertexShaderCSO.size(), nullptr, &vertexShader\n        ),\n        \"Failed to create vertex shader.\"\n    );\n    return vertexShader;\n}\n\nID3D11PixelShader* CreatePixelShader(ID3D11Device* device, std::vector<uint8_t>& pixelShaderCSO) {\n    ID3D11PixelShader* pixelShader;\n    OK_OR_THROW(\n        device->CreatePixelShader(&pixelShaderCSO[0], pixelShaderCSO.size(), nullptr, &pixelShader),\n        \"Failed to create pixel shader.\"\n    );\n    return pixelShader;\n}\n\nID3D11Texture2D* GetTextureFromHandle(ID3D11Device* device, HANDLE handle) {\n    ID3D11Texture2D* texture;\n    OK_OR_THROW(\n        device->OpenSharedResource(handle, __uuidof(ID3D11Texture2D), (void**)&texture),\n        \"[VDispDvr] SyncTexture is NULL!\"\n    );\n    return texture;\n}\n\nHANDLE GetHandleFromTexture(ID3D11Texture2D* texture) {\n    auto exceptMsg = \"Failed to get handle from shared texture\";\n\n    ComPtr<IDXGIResource> resource;\n    OK_OR_THROW(QUERY(texture, &resource), exceptMsg);\n\n    HANDLE handle;\n    OK_OR_THROW(resource->GetSharedHandle(&handle), exceptMsg);\n\n    return handle;\n}\n\nvoid KeyedMutexSync(\n    ID3D11Device* device, HANDLE handle, uint64_t timeout, std::function<void()> callback\n) {\n    ComPtr<ID3D11Texture2D> syncTexture = GetTextureFromHandle(device, handle);\n\n    ComPtr<IDXGIKeyedMutex> keyedMutex;\n    OK_OR_THROW(QUERY(syncTexture, &keyedMutex), \"Failed to query mutex\");\n\n    // TODO: Reasonable timeout and timeout handling\n    OK_OR_THROW(keyedMutex->AcquireSync(0, (DWORD)timeout), \"[VDispDvr] ACQUIRESYNC FAILED!!!\");\n\n    callback();\n\n    keyedMutex->ReleaseSync(0);\n}\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/d3d-render-utils/RenderUtils.h",
    "content": "#pragma once\n\n#pragma comment(lib, \"dxgi.lib\")\n#pragma comment(lib, \"d3d11.lib\")\n\n#include <exception>\n#include <functional>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\n#include <d3d11.h>\n#include <wrl.h>\n\n#include \"alvr_server/Logger.h\"\n#include \"alvr_server/Utils.h\"\n\n#define OK_OR_THROW(dxcall, msg)                                                                   \\\n    {                                                                                              \\\n        HRESULT hr = dxcall;                                                                       \\\n        if (FAILED(hr))                                                                            \\\n            throw MakeException(\"%ls HR=%p %ls\", msg, hr, GetErrorStr(hr).c_str());                \\\n    }\n#define QUERY(from, ppd3d) from->QueryInterface(__uuidof(*(ppd3d)), (void**)(ppd3d))\n\nnamespace d3d_render_utils {\n\nvoid GetAdapterInfo(ID3D11Device* d3dDevice, int32_t& adapterIndex, std::wstring& adapterName);\n\nID3D11Device* CreateDevice(IDXGIAdapter* dxgiAdapter = nullptr);\nID3D11Device* CreateDevice(uint32_t adapterIndex);\n\nID3D11Texture2D* CreateTexture(\n    ID3D11Device* device,\n    uint32_t width,\n    uint32_t height,\n    DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM,\n    bool mipmaps = false,\n    bool shared = false,\n    UINT sampleCount = 1\n);\n\nID3D11Buffer*\n_CreateBuffer(ID3D11Device* device, const void* bufferData, size_t bufferSize, D3D11_USAGE usage);\n\ntemplate <typename T>\nID3D11Buffer*\nCreateBuffer(ID3D11Device* device, const T& bufferData, D3D11_USAGE usage = D3D11_USAGE_IMMUTABLE) {\n    return _CreateBuffer(device, &bufferData, sizeof(T), usage);\n}\n\nvoid UpdateBuffer(ID3D11DeviceContext* context, ID3D11Buffer* buffer, const void* bufferData);\n\nID3D11VertexShader* CreateVertexShader(ID3D11Device* device, std::vector<uint8_t>& vertexShaderCSO);\nID3D11PixelShader* CreatePixelShader(ID3D11Device* device, std::vector<uint8_t>& pixelShaderCSO);\n\nID3D11Texture2D* GetTextureFromHandle(ID3D11Device* device, HANDLE handle);\nHANDLE GetHandleFromTexture(ID3D11Texture2D* texture);\n\nvoid KeyedMutexSync(\n    ID3D11Device* device,\n    HANDLE sharedTextureHandle,\n    uint64_t timeout,\n    std::function<void()> acquiredSyncCallback\n);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/shared/d3drender.cpp",
    "content": "//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================\n#include \"d3drender.h\"\n#include <d3d11_4.h>\n#include <evntprov.h>\n\n#pragma comment( lib, \"dxgi.lib\" )\n#pragma comment( lib, \"d3d11.lib\" )\n#pragma comment( lib, \"rpcrt4.lib\" )\n\n#define Log( ... )\n\nnamespace\n{\n\tinline bool operator==( const LUID &A, const LUID &B )\n\t{\n\t\treturn A.HighPart == B.HighPart && A.LowPart == B.LowPart;\n\t}\n\n\tbool FindDXGIOutput( IDXGIFactory *pFactory, int32_t nWidth, int32_t nHeight, IDXGIAdapter **pOutAdapter, IDXGIOutput **pOutOutput, int32_t *pOutX, int32_t *pOutY )\n\t{\n\t\tIDXGIAdapter *pDXGIAdapter;\n\t\tfor ( UINT nAdapterIndex = 0; pFactory->EnumAdapters( nAdapterIndex, &pDXGIAdapter ) != DXGI_ERROR_NOT_FOUND; nAdapterIndex++ )\n\t\t{\n\t\t\tIDXGIOutput *pDXGIOutput;\n\t\t\tfor ( UINT nOutputIndex = 0; pDXGIAdapter->EnumOutputs( nOutputIndex, &pDXGIOutput ) != DXGI_ERROR_NOT_FOUND; nOutputIndex++ )\n\t\t\t{\n\t\t\t\tDXGI_OUTPUT_DESC desc;\n\t\t\t\tpDXGIOutput->GetDesc( &desc );\n\n\t\t\t\t//if ( desc.DesktopCoordinates.right - desc.DesktopCoordinates.left == nWidth &&\n\t\t\t\t//\tdesc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top == nHeight )\n\t\t\t\t//{\n\t\t\t\t\t*pOutAdapter = pDXGIAdapter;\n\t\t\t\t\t*pOutOutput = pDXGIOutput;\n\t\t\t\t\t*pOutX = desc.DesktopCoordinates.left;\n\t\t\t\t\t*pOutY = desc.DesktopCoordinates.top;\n\t\t\t\t\treturn true;\n\t\t\t\t//}\n\t\t\t\tpDXGIOutput->Release();\n\t\t\t}\n\t\t\tpDXGIAdapter->Release();\n\t\t}\n\t\treturn false;\n\t}\n\n\tbool CreateDevice( IDXGIAdapter *pDXGIAdapter, ID3D11Device **pD3D11Device, ID3D11DeviceContext **pD3D11Context )\n\t{\n\t\tUINT creationFlags = 0;\n#if _DEBUG\n\t\tcreationFlags |= D3D11_CREATE_DEVICE_DEBUG;\n#endif\n\t\tD3D_FEATURE_LEVEL eFeatureLevel;\n\n\t\tHRESULT hRes = D3D11CreateDevice( pDXGIAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, creationFlags, NULL, 0, D3D11_SDK_VERSION, pD3D11Device, &eFeatureLevel, pD3D11Context );\n#if _DEBUG\n\t\t// CreateDevice fails on Win10 in debug if the Win10 SDK isn't installed.\n\t\tif ( pD3D11Device == NULL )\n\t\t{\n\t\t\thRes = D3D11CreateDevice( pDXGIAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, pD3D11Device, &eFeatureLevel, pD3D11Context );\n\t\t}\n#endif\n\t\tif ( FAILED( hRes ) )\n\t\t{\n\t\t\tLog( \"Failed to create D3D11 device! (err=%u)\", hRes );\n\t\t\treturn false;\n\t\t}\n\n\t\tif ( eFeatureLevel < D3D_FEATURE_LEVEL_11_0 )\n\t\t{\n\t\t\tLog( \"DX11 level hardware required!\" );\n\t\t\treturn false;\n\t\t}\n\n\t\tID3D11Multithread *D3D11Multithread = NULL;\n\t\tHRESULT hr = (*pD3D11Context)->QueryInterface(__uuidof(ID3D11Multithread), (void **)&D3D11Multithread);\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tLog(\"Successfully get ID3D11Multithread interface. We set SetMultithreadProtected(TRUE)\");\n\t\t\tD3D11Multithread->SetMultithreadProtected(TRUE);\n\t\t\tD3D11Multithread->Release();\n\t\t}\n\t\telse {\n\t\t\tLog(\"Failed to get ID3D11Multithread interface. Ignore.\");\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tclass CEventHelper\n\t{\n\tpublic:\n\t\tCEventHelper()\n\t\t{\n\t\t\tUuidFromStringA( ( RPC_CSTR ) \"8c8f13b1-60eb-4b6a-a433-de86104115ac\", &guid );\n\t\t\tEventRegister( &guid, nullptr, nullptr, &handle );\n\t\t}\n\n\t\tREGHANDLE handle;\n\t\tGUID guid;\n\t};\n\n\tCEventHelper s_eventHelper;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid EventWriteString( const wchar_t* pwchEvent )\n{\n\t::EventWriteString( s_eventHelper.handle, 0, 0, pwchEvent );\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCD3DRender::CD3DRender()\n\t: m_pDXGIFactory( NULL )\n\t, m_pDXGIOutput( NULL )\n\t, m_pDXGISwapChain( NULL )\n\t, m_pD3D11Device( NULL )\n\t, m_pD3D11Context( NULL )\n\t, m_nDisplayWidth( 0 )\n\t, m_nDisplayHeight( 0 )\n\t, m_nDisplayX( 0 )\n\t, m_nDisplayY( 0 )\n{\n\t// Initialize DXGI\n\t{\n\t\t// Need to use DXGI 1.1 for shared texture support.\n\t\tIDXGIFactory1 *pDXGIFactory1;\n\t\tif ( FAILED( CreateDXGIFactory1( __uuidof( IDXGIFactory1 ), ( void ** )&pDXGIFactory1 ) ) )\n\t\t{\n\t\t\tLog( \"Failed to create DXGIFactory1!\" );\n\t\t\treturn;\n\t\t}\n\t\telse if ( FAILED( pDXGIFactory1->QueryInterface( __uuidof( IDXGIFactory ), ( void ** )&m_pDXGIFactory ) ) )\n\t\t{\n\t\t\tpDXGIFactory1->Release();\n\t\t\tLog( \"Failed to get DXGIFactory interface!\" );\n\t\t\treturn;\n\t\t}\n\t\tpDXGIFactory1->Release();\n\t}\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCD3DRender::~CD3DRender()\n{\n\tSAFE_RELEASE( m_pDXGIFactory );\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::GetDisplayPos( int32_t *pDisplayX, int32_t *pDisplayY )\n{\n\t*pDisplayX = m_nDisplayX;\n\t*pDisplayY = m_nDisplayY;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::GetDisplaySize( uint32_t *pDisplayWidth, uint32_t *pDisplayHeight )\n{\n\t*pDisplayWidth = m_nDisplayWidth;\n\t*pDisplayHeight = m_nDisplayHeight;\n}\n\n//--------------------------------------------------------------------------------------------------\n// Purpose: Return the DXGI index and name of the adapter currently in use.\n//--------------------------------------------------------------------------------------------------\nbool CD3DRender::GetAdapterInfo( int32_t *pAdapterIndex, std::wstring &adapterName )\n{\n\tif ( m_pD3D11Device == NULL )\n\t\treturn false;\n\n\tbool bSuccess = false;\n\n\tIDXGIDevice *pDXGIDevice;\n\tif ( SUCCEEDED( m_pD3D11Device->QueryInterface( __uuidof( IDXGIDevice ), ( void ** )&pDXGIDevice ) ) )\n\t{\n\t\tIDXGIAdapter *pDXGIAdapter;\n\t\tif ( SUCCEEDED( pDXGIDevice->GetParent( __uuidof( IDXGIAdapter ), ( void ** )&pDXGIAdapter ) ) )\n\t\t{\n\t\t\tDXGI_ADAPTER_DESC adapterDesc;\n\t\t\tpDXGIAdapter->GetDesc( &adapterDesc );\n\n\t\t\tIDXGIFactory *pDXGIFactory;\n\t\t\tif ( SUCCEEDED( pDXGIAdapter->GetParent( __uuidof( IDXGIFactory ), ( void ** )&pDXGIFactory ) ) )\n\t\t\t{\n\t\t\t\tIDXGIAdapter *pEnumeratedAdapter;\n\t\t\t\tfor ( UINT nAdapterIndex = 0; pDXGIFactory->EnumAdapters( nAdapterIndex, &pEnumeratedAdapter ) != DXGI_ERROR_NOT_FOUND; nAdapterIndex++ )\n\t\t\t\t{\n\t\t\t\t\tDXGI_ADAPTER_DESC enumeratedDesc;\n\t\t\t\t\tpEnumeratedAdapter->GetDesc( &enumeratedDesc );\n\t\t\t\t\tpEnumeratedAdapter->Release();\n\n\t\t\t\t\tif ( enumeratedDesc.AdapterLuid == adapterDesc.AdapterLuid )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( pAdapterIndex )\n\t\t\t\t\t\t\t*pAdapterIndex = nAdapterIndex;\n\n\t\t\t\t\t\tadapterName = adapterDesc.Description;\n\n\t\t\t\t\t\tbSuccess = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpDXGIFactory->Release();\n\t\t\t}\n\t\t\tpDXGIAdapter->Release();\n\t\t}\n\t\tpDXGIDevice->Release();\n\t}\n\n\treturn bSuccess;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nID3D11Texture2D *CD3DRender::GetSharedTexture( HANDLE hSharedTexture )\n{\n\tif ( !hSharedTexture )\n\t\treturn NULL;\n\n\tfor ( SharedTextures_t::iterator it = m_SharedTextureCache.begin();\n\t\tit != m_SharedTextureCache.end(); ++it )\n\t{\n\t\tif ( it->m_hSharedTexture == hSharedTexture )\n\t\t{\n\t\t\treturn it->m_pTexture;\n\t\t}\n\t}\n\n\tID3D11Texture2D *pTexture;\n\tif ( SUCCEEDED( m_pD3D11Device->OpenSharedResource(\n\t\thSharedTexture, __uuidof( ID3D11Texture2D ), ( void ** )&pTexture ) ) )\n\t{\n\t\tSharedTextureEntry_t entry { hSharedTexture, pTexture };\n\t\tm_SharedTextureCache.push_back( entry );\n\t\treturn pTexture;\n\t}\n\n\treturn NULL;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CD3DRender::Initialize( uint32_t nAdapterIndex )\n{\n\tShutdown();\n\n\tif ( m_pDXGIFactory == NULL )\n\t\treturn false;\n\n\tIDXGIAdapter *pDXGIAdapter;\n\tif ( FAILED( m_pDXGIFactory->EnumAdapters( nAdapterIndex, &pDXGIAdapter ) ) )\n\t\treturn false;\n\n\tbool bSuccess = CreateDevice( pDXGIAdapter, &m_pD3D11Device, &m_pD3D11Context );\n\n\tpDXGIAdapter->Release();\n\n\treturn bSuccess;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CD3DRender::Initialize( uint32_t nDisplayWidth, uint32_t nDisplayHeight )\n{\n\tShutdown();\n\n\tif ( m_pDXGIFactory == NULL )\n\t\treturn false;\n\n\tm_nDisplayWidth = nDisplayWidth;\n\tm_nDisplayHeight = nDisplayHeight;\n\n\tIDXGIAdapter *pDXGIAdapter;\n\tif ( !FindDXGIOutput( m_pDXGIFactory, m_nDisplayWidth, m_nDisplayHeight, &pDXGIAdapter, &m_pDXGIOutput, &m_nDisplayX, &m_nDisplayY ) )\n\t\treturn false;\n\n\tbool bSuccess = CreateDevice( pDXGIAdapter, &m_pD3D11Device, &m_pD3D11Context );\n\n\tpDXGIAdapter->Release();\n\n\treturn bSuccess;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::Shutdown()\n{\n\tSetFullscreen( FALSE );\n\tSAFE_RELEASE( m_pD3D11Context );\n\tSAFE_RELEASE( m_pD3D11Device );\n\tSAFE_RELEASE( m_pDXGISwapChain );\n\tSAFE_RELEASE( m_pDXGIOutput );\n\tm_nDisplayWidth = 0;\n\tm_nDisplayHeight = 0;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CD3DRender::CreateSwapChain( HWND hWnd, const DXGI_RATIONAL &refreshRate )\n{\n\tif ( !m_pD3D11Device )\n\t\treturn false;\n\tif ( !m_pDXGIOutput )\n\t\treturn false;\n\tif ( !m_pDXGIFactory )\n\t\treturn false;\n\n\t// Determine which video mode to use\n\n\tDXGI_MODE_DESC modeDesc;\n\tZeroMemory( &modeDesc, sizeof( modeDesc ) );\n\n\tmodeDesc.Width = m_nDisplayWidth;\n\tmodeDesc.Height = m_nDisplayHeight;\n\tmodeDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\n\tmodeDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;\n\tmodeDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;\n\tmodeDesc.RefreshRate = refreshRate;\n\n\tDXGI_MODE_DESC modeOut = modeDesc;\n\n\tif ( FAILED( m_pDXGIOutput->FindClosestMatchingMode( &modeDesc, &modeOut, m_pD3D11Device ) ) )\n\t{\n\t\tLog( \"Failed to find closest matching mode!\" );\n\t\treturn false;\n\t}\n\n\t// Create a fullscreen swap chain for the window\n\n\tDXGI_SWAP_CHAIN_DESC swapChainDesc;\n\tZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );\n\tswapChainDesc.BufferCount = 2;\n\tswapChainDesc.BufferDesc = modeOut;\n\tswapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;\n\tswapChainDesc.OutputWindow = hWnd;\n\tswapChainDesc.SampleDesc.Count = 1;\n\tswapChainDesc.SampleDesc.Quality = 0;\n\tswapChainDesc.Windowed = TRUE;\n\tswapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;\n\tswapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;\n\n\tif ( FAILED( m_pDXGIFactory->CreateSwapChain( m_pD3D11Device, &swapChainDesc, &m_pDXGISwapChain ) ) )\n\t{\n\t\tLog( \"Failed to create swap chain!\" );\n\t\treturn false;\n\t}\n\n\tm_pDXGIFactory->MakeWindowAssociation( swapChainDesc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER );\n\n\t//!! Probably don't need this as long as we Flush after Present.\n\tIDXGIDevice1 *pDXGIDevice1;\n\tif ( SUCCEEDED( m_pD3D11Device->QueryInterface( __uuidof( IDXGIDevice1 ), ( void ** )&pDXGIDevice1 ) ) )\n\t{\n\t\tpDXGIDevice1->SetGPUThreadPriority( 7 );\n\t\tpDXGIDevice1->SetMaximumFrameLatency( 1 );\n\t\tpDXGIDevice1->Release();\n\t}\n\n\treturn true;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::SetFullscreen( BOOL bFullscreen )\n{\n\tif ( m_pDXGISwapChain && m_pDXGIOutput )\n\t{\n\t\t// Bail if no change is necessary.\n\t\tBOOL bState;\n\t\tif ( SUCCEEDED( m_pDXGISwapChain->GetFullscreenState( &bState, NULL ) ) )\n\t\t{\n\t\t\tif ( bState == bFullscreen )\n\t\t\t\treturn;\n\t\t}\n\n\t\t// Bail if not ready to go fullscreen yet.\n\t\tif ( bFullscreen )\n\t\t{\n\t\t\tif ( m_pDXGISwapChain->Present( 0, DXGI_PRESENT_TEST ) != S_OK )\n\t\t\t\treturn;\n\t\t}\n\n\t\tif ( m_pDXGISwapChain->SetFullscreenState( bFullscreen, bFullscreen ? m_pDXGIOutput : NULL ) == S_OK )\n\t\t{\n\t\t\tUpdateBuffers(); // WM_SIZE doesn't get sent since window was created at proper size\n\t\t}\n\t}\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::UpdateBuffers()\n{\n\tif ( m_pDXGISwapChain )\n\t{\n\t\tDXGI_SWAP_CHAIN_DESC swapChainDesc;\n\t\tif ( SUCCEEDED( m_pDXGISwapChain->GetDesc( &swapChainDesc ) ) )\n\t\t{\n\t\t\tm_pD3D11Context->ClearState(); //OMSetRenderTargets(0, NULL, NULL);\n\t\t\tm_pDXGISwapChain->ResizeBuffers( 0, ( UINT )m_nDisplayWidth, ( UINT )m_nDisplayHeight, swapChainDesc.BufferDesc.Format, swapChainDesc.Flags );\n\t\t}\n\t}\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CD3DRender::GetAdapterLuid( int32_t nAdapterIndex, uint64_t *pAdapterLuid )\n{\n\tbool bSuccess = false;\n\n\tif ( m_pDXGIFactory != NULL )\n\t{\n\t\tIDXGIAdapter *pDXGIAdapter;\n\t\tif ( SUCCEEDED( m_pDXGIFactory->EnumAdapters( nAdapterIndex, &pDXGIAdapter ) ) )\n\t\t{\n\t\t\tDXGI_ADAPTER_DESC adapterDesc;\n\t\t\tpDXGIAdapter->GetDesc( &adapterDesc );\n\t\t\tpDXGIAdapter->Release();\n\n\t\t\t*pAdapterLuid = *( uint64_t * )&adapterDesc.AdapterLuid;\n\t\t\tbSuccess = true;\n\t\t}\n\t}\n\n\treturn bSuccess;\n}\n\n#define MIN(a, b) ((a) < (b) ? (a) : (b))\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CD3DRender::CopyTextureData( BYTE *pDst, uint32_t nDstRowPitch,\n\tconst BYTE *pSrc, uint32_t nSrcRowPitch,\n\tuint32_t nWidth, uint32_t nHeight, uint32_t nPitch )\n{\n\tif ( nDstRowPitch == nSrcRowPitch )\n\t{\n\t\tmemcpy( pDst, pSrc, nSrcRowPitch * nHeight );\n\t}\n\telse\n\t{\n\t\tuint32_t nMinRowPitch = MIN(nDstRowPitch, nSrcRowPitch);\n\t\tfor ( uint32_t i = 0; i < nHeight; i++ )\n\t\t{\n\t\t\tmemcpy( pDst, pSrc, nMinRowPitch );\n\t\t\tpDst += nDstRowPitch;\n\t\t\tpSrc += nSrcRowPitch;\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/platform/win32/shared/d3drender.h",
    "content": "//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================\n//\n// Helper class for working with D3D.\n//\n//==================================================================================================\n#pragma once\n\n#include <d3d11.h>\n#include <stdint.h>\n#include <vector>\n#include <string>\n\nvoid EventWriteString( const wchar_t* pwchEvent ); // gpuview event\n\n#define SAFE_RELEASE( x ) if ( x ) { ( x )->Release(); ( x ) = NULL; }\n\nclass CD3DRender\n{\npublic:\n\tCD3DRender();\n\t~CD3DRender();\n\n\tbool Initialize( uint32_t nDisplayWidth, uint32_t nDisplayHeight );\n\tbool Initialize( uint32_t nAdapterIndex );\n\tvoid Shutdown();\n\n\tvoid GetDisplayPos( int32_t *pDisplayX, int32_t *pDisplayY );\n\tvoid GetDisplaySize( uint32_t *pDisplayWidth, uint32_t *pDisplayHeight );\n\tbool GetAdapterInfo( int32_t *pAdapterIndex, std::wstring &adapterName );\n\tID3D11Texture2D *GetSharedTexture( HANDLE hSharedTexture );\n\n\tbool CreateSwapChain( HWND hWnd, const DXGI_RATIONAL &refreshRate );\n\tvoid SetFullscreen( BOOL bFullscreen );\n\tvoid UpdateBuffers();\n\n\tID3D11Device *GetDevice() { return m_pD3D11Device; }\n\tID3D11DeviceContext *GetContext() { return m_pD3D11Context; }\n\tIDXGISwapChain *GetSwapChain() { return m_pDXGISwapChain; }\n\n\tbool GetAdapterLuid( int32_t nAdapterIndex, uint64_t *pAdapterLuid );\n\n\tstatic void CopyTextureData( BYTE *pDst, uint32_t nDstRowPitch,\n\t\tconst BYTE *pSrc, uint32_t nSrcRowPitch,\n\t\tuint32_t nWidth, uint32_t nHeight, uint32_t nPitch );\n\nprivate:\n\tIDXGIFactory *m_pDXGIFactory;\n\tIDXGIOutput *m_pDXGIOutput;\n\tIDXGISwapChain *m_pDXGISwapChain;\n\tID3D11Device *m_pD3D11Device;\n\tID3D11DeviceContext *m_pD3D11Context;\n\tuint32_t m_nDisplayWidth, m_nDisplayHeight;\n\tint32_t m_nDisplayX, m_nDisplayY;\n\n\tstruct SharedTextureEntry_t\n\t{\n\t\tHANDLE m_hSharedTexture;\n\t\tID3D11Texture2D *m_pTexture;\n\t};\n\ttypedef std::vector< SharedTextureEntry_t > SharedTextures_t;\n\tSharedTextures_t m_SharedTextureCache;\n};\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/AMFFactory.cpp",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"AMFFactory.h\"\n#include \"Thread.h\"\n\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n    #pragma clang diagnostic ignored \"-Wglobal-constructors\"\n#endif\n\nAMFFactoryHelper g_AMFFactory;\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n#ifdef AMF_CORE_STATIC\nextern \"C\"\n{\n    extern AMF_CORE_LINK AMF_RESULT AMF_CDECL_CALL AMFInit(amf_uint64 version, amf::AMFFactory **ppFactory);\n}\n#endif\n\n//-------------------------------------------------------------------------------------------------\nAMFFactoryHelper::AMFFactoryHelper() :\nm_hDLLHandle(NULL),\nm_pFactory(NULL),\nm_pDebug(NULL),\nm_pTrace(NULL),\nm_AMFRuntimeVersion(0),\nm_iRefCount(0)\n{\n}\n//-------------------------------------------------------------------------------------------------\nAMFFactoryHelper::~AMFFactoryHelper()\n{\n    Terminate();\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMFFactoryHelper::Init(const wchar_t* dllName)\n{\n    dllName;\n\n#ifndef AMF_CORE_STATIC\n    if (m_hDLLHandle != NULL)\n    {\n        amf_atomic_inc(&m_iRefCount);\n        return AMF_OK;\n    }\n\n    const wchar_t* dllName_ = dllName == NULL ? AMF_DLL_NAME : dllName;\n#if defined (_WIN32) || defined (__APPLE__)\n    m_hDLLHandle = amf_load_library(dllName_);\n#else\n    m_hDLLHandle = amf_load_library1(dllName_, false); //load with local flags\n#endif\n    if(m_hDLLHandle == NULL)\n    {\n        return AMF_FAIL;\n    }\n\n    AMFInit_Fn initFun = (AMFInit_Fn)::amf_get_proc_address(m_hDLLHandle, AMF_INIT_FUNCTION_NAME);\n    if(initFun == NULL)\n    {\n        return AMF_FAIL;\n    }\n    AMF_RESULT res = initFun(AMF_FULL_VERSION, &m_pFactory);\n    if(res != AMF_OK)\n    {\n        return res;\n    }\n    AMFQueryVersion_Fn versionFun = (AMFQueryVersion_Fn)::amf_get_proc_address(m_hDLLHandle, AMF_QUERY_VERSION_FUNCTION_NAME);\n    if(versionFun == NULL)\n    {\n        return AMF_FAIL;\n    }\n    res = versionFun(&m_AMFRuntimeVersion);\n    if(res != AMF_OK)\n    {\n        return res;\n    }\n#else\n    AMF_RESULT res = AMFInit(AMF_FULL_VERSION, &m_pFactory);\n    if (res != AMF_OK)\n    {\n        return res;\n    }\n    m_AMFRuntimeVersion = AMF_FULL_VERSION;\n#endif\n    m_pFactory->GetTrace(&m_pTrace);\n    m_pFactory->GetDebug(&m_pDebug);\n\n    amf_atomic_inc(&m_iRefCount);\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMFFactoryHelper::Terminate()\n{\n    if(m_hDLLHandle != NULL)\n    {\n        amf_atomic_dec(&m_iRefCount);\n        if(m_iRefCount == 0)\n        {\n            amf_free_library(m_hDLLHandle);\n            m_hDLLHandle = NULL;\n            m_pFactory= NULL;\n            m_pDebug = NULL;\n            m_pTrace = NULL;\n        }\n    }\n\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\namf::AMFFactory* AMFFactoryHelper::GetFactory()\n{\n    return m_pFactory;\n}\n//-------------------------------------------------------------------------------------------------\namf::AMFDebug* AMFFactoryHelper::GetDebug()\n{\n    return m_pDebug;\n}\n//-------------------------------------------------------------------------------------------------\namf::AMFTrace* AMFFactoryHelper::GetTrace()\n{\n    return m_pTrace;\n}\n//-------------------------------------------------------------------------------------------------\namf_uint64 AMFFactoryHelper::AMFQueryVersion()\n{\n    return m_AMFRuntimeVersion;\n}\n\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT  AMFFactoryHelper::LoadExternalComponent(amf::AMFContext* pContext, const wchar_t* dll, const char* function, void* reserved, amf::AMFComponent** ppComponent)\n{\n    // check passed in parameters\n    if (!pContext || !dll || !function)\n    {\n        return AMF_INVALID_ARG;\n    }\n\n    // check if DLL has already been loaded\n    amf_handle  hDll = NULL;\n    for (std::vector<ComponentHolder>::iterator it = m_extComponents.begin(); it != m_extComponents.end(); ++it)\n    {\n#if defined(_WIN32)\n         if (wcsicmp(it->m_DLL.c_str(), dll) == 0) // ignore case on Windows\n#elif defined(__linux) // Linux\n        if (wcscmp(it->m_DLL.c_str(), dll) == 0) // case sensitive on Linux\n#endif\n        {\n            if (it->m_hDLLHandle != NULL)\n            {\n                hDll = it->m_hDLLHandle;\n                amf_atomic_inc(&it->m_iRefCount);\n                break;\n            }\n\n            return AMF_UNEXPECTED;\n        }\n    }\n    // DLL wasn't loaded before so load it now and\n    // add it to the internal list\n    if (hDll == NULL)\n    {\n        ComponentHolder component;\n        component.m_iRefCount = 0;\n        component.m_hDLLHandle = NULL;\n        component.m_DLL = dll;\n\n#if defined(_WIN32) || defined(__APPLE__)\n        hDll = amf_load_library(dll);\n#else\n        hDll = amf_load_library1(dll, false); //global flag set to true\n#endif\n        if (hDll == NULL)\n            return AMF_FAIL;\n\n        // since LoadLibrary succeeded add the information\n        // into the internal list so we can properly free\n        // the DLL later on, even if we fail to get the\n        // required information from it...\n        component.m_hDLLHandle = hDll;\n        amf_atomic_inc(&component.m_iRefCount);\n        m_extComponents.push_back(component);\n    }\n\n    // look for function we want in the dll we just loaded\n    typedef AMF_RESULT(AMF_CDECL_CALL *AMFCreateComponentFunc)(amf::AMFContext*, void* reserved, amf::AMFComponent**);\n    AMFCreateComponentFunc  initFn = (AMFCreateComponentFunc)::amf_get_proc_address(hDll, function);\n    if (initFn == NULL)\n        return AMF_FAIL;\n\n    return initFn(pContext, reserved, ppComponent);\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT  AMFFactoryHelper::UnLoadExternalComponent(const wchar_t* dll)\n{\n    if (!dll)\n    {\n        return AMF_INVALID_ARG;\n    }\n    for (std::vector<ComponentHolder>::iterator it = m_extComponents.begin(); it != m_extComponents.end(); ++it)\n    {\n#if defined(_WIN32)\n         if (wcsicmp(it->m_DLL.c_str(), dll) == 0) // ignore case on Windows\n#elif defined(__linux) // Linux\n        if (wcscmp(it->m_DLL.c_str(), dll) == 0) // case sensitive on Linux\n#endif\n        {\n            if (it->m_hDLLHandle == NULL)\n            {\n                return AMF_UNEXPECTED;\n            }\n            amf_atomic_dec(&it->m_iRefCount);\n            if (it->m_iRefCount == 0)\n            {\n                amf_free_library(it->m_hDLLHandle);\n                m_extComponents.erase(it);\n            }\n            break;\n        }\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/AMFFactory.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_AMFFactory_h\n#define AMF_AMFFactory_h\n\n#pragma once\n\n#include \"../include/core/Factory.h\"\n#include <string>\n#include <vector>\n\n\nclass AMFFactoryHelper\n{\npublic:\n    AMFFactoryHelper();\n    virtual ~AMFFactoryHelper();\n\n    AMF_RESULT  Init(const wchar_t* dllName = NULL);\n    AMF_RESULT  Terminate();\n\n    AMF_RESULT  LoadExternalComponent(amf::AMFContext* pContext, const wchar_t* dll, const char* function, void* reserved, amf::AMFComponent** ppComponent);\n    AMF_RESULT  UnLoadExternalComponent(const wchar_t* dll);\n\n    amf::AMFFactory* GetFactory();\n    amf::AMFDebug* GetDebug();\n    amf::AMFTrace* GetTrace();\n\n    amf_uint64 AMFQueryVersion();\n\n    amf_handle    GetAMFDLLHandle() { return m_hDLLHandle; }\nprotected:\n    struct ComponentHolder\n    {\n        amf_handle      m_hDLLHandle;\n        amf_long        m_iRefCount;\n        std::wstring    m_DLL;\n\n        ComponentHolder()\n        {\n            m_hDLLHandle = NULL;\n            m_iRefCount = 0;\n        }\n    };\n\n    amf_handle          m_hDLLHandle;\n    amf::AMFFactory*    m_pFactory;\n    amf::AMFDebug*      m_pDebug;\n    amf::AMFTrace*      m_pTrace;\n    amf_uint64          m_AMFRuntimeVersion;\n\n    amf_long            m_iRefCount;\n\n    std::vector<ComponentHolder>  m_extComponents;\n};\n\nextern ::AMFFactoryHelper g_AMFFactory;\n#endif // AMF_AMFFactory_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/AMFMath.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#pragma once\n\n#include <cmath>\n\nnamespace amf\n{\n    // right-handed system\n    // +y is up\n    // +x is to the right\n    // -z is forward\n\n    const float AMF_PI         = 3.141592654f;\n    const float AMF_1DIV2PI    = 0.159154943f;\n    const float AMF_2PI        = 6.283185307f;\n    const float AMF_PIDIV2     = 1.570796327f;\n\n    const uint32_t AMF_PERMUTE_0X        = 0;\n    const uint32_t AMF_PERMUTE_0Y        = 1;\n    const uint32_t AMF_PERMUTE_0Z        = 2;\n    const uint32_t AMF_PERMUTE_0W        = 3;\n    const uint32_t AMF_PERMUTE_1X        = 4;\n    const uint32_t AMF_PERMUTE_1Y        = 5;\n    const uint32_t AMF_PERMUTE_1Z        = 6;\n    const uint32_t AMF_PERMUTE_1W        = 7;\n\n    const uint32_t AMF_SWIZZLE_X         = 0;\n    const uint32_t AMF_SWIZZLE_Y         = 1;\n    const uint32_t AMF_SWIZZLE_Z         = 2;\n    const uint32_t AMF_SWIZZLE_W         = 3;\n\n    //---------------------------------------------------------------------------------------------\n    class VectorPOD\n    {\n    public:\n        float x;\n        float y;\n        float z;\n        float w;\n\n//        Vector():x(0),y(0),z(0),w(0){}\n//        Vector(float _x, float _y, float _z, float _w ):x(_x),y(_y),z(_z),w(_w){}\n\n        void Assign(float _x, float _y, float _z, float _w)\n        {\n            x= _x; y = _y; z = _z; w = _w;\n        }\n\n        inline VectorPOD& operator-=(const VectorPOD& other)\n        {\n            x -=other.x; y -=other.y; z -=other.z; w -=other.w;\n            return *this;\n        }\n        inline VectorPOD operator-(const VectorPOD& other) const\n        {\n            VectorPOD vector;\n            vector.x = x - other.x;\n            vector.y = y - other.y;\n            vector.z = z - other.z;\n            vector.w = w - other.w;\n            return vector;\n        }\n        inline VectorPOD& operator+=(const VectorPOD& other)\n        {\n            x +=other.x;\n            y +=other.y;\n            z +=other.z;\n            w +=other.w;\n            return *this;\n        }\n        inline VectorPOD operator+(const VectorPOD& other)  const\n        {\n            VectorPOD vector;\n            vector.x = x + other.x;\n            vector.y = y + other.y;\n            vector.z = z + other.z;\n            vector.w = w + other.w;\n            return vector;\n        }\n\n        inline VectorPOD operator*(const VectorPOD& other)  const\n        {\n            VectorPOD vector;\n            vector.x = x * other.x;\n            vector.y = y * other.y;\n            vector.z = z * other.z;\n            vector.w = w * other.w;\n            return vector;\n        }\n        inline VectorPOD operator*=(const VectorPOD& other)\n        {\n            x*=other.x;\n            y*=other.y;\n            z*=other.z;\n            w*=other.w;\n            return *this;\n        }\n\n        inline VectorPOD Swizzle(uint32_t E0, uint32_t E1, uint32_t E2, uint32_t E3) const\n        {\n            const uint32_t *aPtr = (const uint32_t* )(this);\n\n            VectorPOD Result;\n            uint32_t *pWork = (uint32_t*)(&Result);\n\n            pWork[0] = aPtr[E0];\n            pWork[1] = aPtr[E1];\n            pWork[2] = aPtr[E2];\n            pWork[3] = aPtr[E3];\n\n            return Result;\n        }\n\n        /*\n        inline VectorPOD& operator=(const VectorPOD& other)\n        {\n            Assign(other.x, other.y, other.z, other.w);\n            return *this;\n        }\n        */\n        inline bool operator==(const VectorPOD& other) const\n        {\n            return x == other.x && y == other.y && z == other.z && w == other.w;\n        }\n        inline bool operator!=(const VectorPOD& other) const { return !operator==(other); }\n        inline VectorPOD Dot3(const VectorPOD& vec) const\n        {\n            float fValue = x * vec.x + y * vec.y + z * vec.z;\n            VectorPOD Result;\n            Result.Assign(fValue, fValue, fValue, fValue);\n            return Result;\n        }\n        inline VectorPOD Dot4(const VectorPOD& vec) const\n        {\n            float fValue = x * vec.x + y * vec.y + z * vec.z + w * vec.w;\n            VectorPOD Result;\n            Result.Assign(fValue, fValue, fValue, fValue);\n            return Result;\n        }\n\n        inline VectorPOD LengthSq3()  const\n        {\n            return Dot3(*this);\n        }\n\n        inline VectorPOD LengthSq4()  const\n        {\n            return Dot4(*this);\n        }\n\n        inline VectorPOD Sqrt()  const\n        {\n            VectorPOD Result;\n            Result.x = sqrtf(x);\n            Result.y = sqrtf(y);\n            Result.z = sqrtf(z);\n            Result.w = sqrtf(w);\n            return Result;\n        }\n        inline VectorPOD Length3()  const\n        {\n            VectorPOD Result;\n            Result = LengthSq3();\n            Result = Result.Sqrt();\n            return Result;\n        }\n        inline VectorPOD Length4()  const\n        {\n            VectorPOD Result;\n            Result = LengthSq4();\n            Result = Result.Sqrt();\n            return Result;\n        }\n        inline VectorPOD Normalize3()  const\n        {\n            float fLength;\n            VectorPOD vResult;\n\n            vResult = Length3();\n            fLength = vResult.x;\n\n            // Prevent divide by zero\n            if (fLength > 0)\n            {\n                fLength = 1.0f / fLength;\n            }\n\n            vResult.x = x * fLength;\n            vResult.y = y * fLength;\n            vResult.z = z * fLength;\n            vResult.w = w * fLength;\n            return vResult;\n        }\n\n        inline VectorPOD Cross3(const VectorPOD& vec) const\n        {\n            VectorPOD vResult;\n            vResult.Assign(\n                (y * vec.z) - (z * vec.y),\n                (z * vec.x) - (x * vec.z),\n                (x * vec.y) - (y * vec.x),\n                0.0f);\n            return vResult;\n        }\n        inline VectorPOD Negate() const\n        {\n            VectorPOD Result;\n            Result.x = -x;\n            Result.y = -y;\n            Result.z = -z;\n            Result.w = -w;\n            return Result;\n        }\n\n\t\tinline VectorPOD operator-() const\n\t\t{\n\t\t\treturn Negate();\n\t\t}\n\n        inline VectorPOD MergeXY(const VectorPOD& vec) const\n        {\n            VectorPOD Result;\n            Result.x = x;\n            Result.y = vec.x;\n            Result.z = y;\n            Result.w = vec.y;\n            return Result;\n        }\n        inline VectorPOD MergeZW(const VectorPOD& vec) const\n        {\n            VectorPOD Result;\n            Result.x = z;\n            Result.y = vec.z;\n            Result.z = w;\n            Result.w = vec.w;\n            return Result;\n        }\n        inline VectorPOD VectorPermute(const VectorPOD& vec, uint32_t PermuteX, uint32_t PermuteY, uint32_t PermuteZ, uint32_t PermuteW ) const\n        {\n            const uint32_t *aPtr[2];\n            aPtr[0] = (const uint32_t* )(this);\n            aPtr[1] = (const uint32_t* )(&vec);\n\n            VectorPOD Result;\n            uint32_t *pWork = (uint32_t*)(&Result);\n\n            const uint32_t i0 = PermuteX & 3;\n            const uint32_t vi0 = PermuteX >> 2;\n            pWork[0] = aPtr[vi0][i0];\n\n            const uint32_t i1 = PermuteY & 3;\n            const uint32_t vi1 = PermuteY >> 2;\n            pWork[1] = aPtr[vi1][i1];\n\n            const uint32_t i2 = PermuteZ & 3;\n            const uint32_t vi2 = PermuteZ >> 2;\n            pWork[2] = aPtr[vi2][i2];\n\n            const uint32_t i3 = PermuteW & 3;\n            const uint32_t vi3 = PermuteW >> 2;\n            pWork[3] = aPtr[vi3][i3];\n\n            return Result;\n        }\n        inline VectorPOD Reciprocal()\n        {\n            VectorPOD Result;\n            Result.x = 1.f / x;\n            Result.y = 1.f / y;\n            Result.z = 1.f / z;\n            Result.w = 1.f / w;\n            return Result;\n        }\n    };\n\n    class Vector : public VectorPOD\n    {\n    public:\n        Vector()\n        {\n            x = 0;\n            y = 0;\n            z = 0;\n            w = 0;\n        }\n        Vector(float _x, float _y, float _z, float _w )\n        {\n            x = _x;\n            y = _y;\n            z = _z;\n            w = _w;\n        }\n        Vector(const VectorPOD& other)\n        {\n            operator=(other);\n        }\n        Vector& operator=(const VectorPOD& other)\n        {\n            x = other.x;\n            y = other.y;\n            z = other.z;\n            w = other.w;\n            return *this;\n        }\n        //---------------------------------------------------------------------------------------------\n\n    };\n\n\n    //---------------------------------------------------------------------------------------------\n    class Quaternion : public Vector\n    {\n    public:\n        Quaternion(){}\n        Quaternion(const Quaternion& other){Assign(other.x, other.y, other.z, other.w);}\n        Quaternion(float pitch, float yaw, float roll) {FromEuler(pitch, yaw, roll);}\n        Quaternion(float _x, float _y, float _z, float _w) {Assign(_x, _y, _z, _w);}\n\n        inline void FromEuler(float pitch, float yaw, float roll)\n        {\n            float cy = cosf(yaw * 0.5f);\n            float sy = sinf(yaw * 0.5f);\n            float cr = cosf(roll * 0.5f);\n            float sr = sinf(roll * 0.5f);\n            float cp = cosf(pitch * 0.5f);\n            float sp = sinf(pitch * 0.5f);\n\n            w = cp * cr * cy + sp * sr * sy;\n            x = cp * sr * cy - sp * cr * sy;\n            y = cp * cr * sy + sp * sr * cy;\n            z = sp * cr * cy - cp * sr * sy;\n\n        }\n\n\n        inline Quaternion& operator=(const Quaternion& other)\n        {\n            Assign(other.x, other.y, other.z, other.w);\n            return *this;\n        }\n\n        inline bool operator==(const Quaternion& other) const\n        {\n            return x == other.x && y == other.y && z == other.z && w == other.w;\n        }\n\n        inline bool operator!=(const Quaternion& other) const { return !operator==(other); }\n\n\n        inline Quaternion operator*(const Quaternion& other) const\n        {\n            return Quaternion(\n            other.w * x + other.x * w + other.y * z - other.z * y,\n            other.w * y - other.x * z + other.y * w + other.z * x,\n            other.w * z + other.x * y - other.y * x + other.z * w,\n            other.w * w - other.x * x - other.y * y - other.z * z\n            );\n        }\n\n        inline const Quaternion& RotateBy(const Quaternion& rotator)\n        {\n            *this = rotator * (*this);\n            return *this;\n        }\n\n        inline Vector ToEulerAngles() const\n        {\n            float yaw, pitch, roll;\n#if 0\n        // roll (x-axis rotation)\n            float sinr = 2.0f * (w * x + y * z);\n            float cosr = 1.0f - 2.0f * (x * x + y * y);\n            roll = atan2f(sinr, cosr);\n\n            // pitch (y-axis rotation)\n            float sinp = 2.0f * (w * y - z * x);\n            if (fabsf(sinp) >= 1)\n                pitch = copysignf(AMF_PIDIV2, sinp); // use 90 degrees if out of range\n            else\n                pitch = asinf(sinp);\n\n            // yaw (z-axis rotation)\n            float siny = 2.0f * (w * z + x * y);\n            float cosy = 1.0f - 2.0f * (y * y + z * z);\n            yaw = atan2f(siny, cosy);\n\n#else\n            float sqw = w*w;\n            float sqx = x*x;\n            float sqy = y*y;\n            float sqz = z*z;\n            float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor\n            float test = x*y + z*w;\n            if (test > 0.499f * unit) { // singularity at north pole\n                yaw = 2.0f * atan2(x, w);\n                pitch = AMF_PIDIV2;\n                roll = 0;\n            }else if (test < -0.499f*unit) { // singularity at south pole\n                yaw = -2.0f * atan2(x, w);\n                pitch = -AMF_PIDIV2;\n                roll = 0;\n            }\n            else\n            {\n                yaw = atan2(2.0f * (y*w - x*z), sqx - sqy - sqz + sqw);\n                pitch = asin(2.0f * test / unit);\n                roll = atan2(2.0f * (x*w - y * z), -sqx + sqy - sqz + sqw);\n            }\n#endif\n            return Vector(pitch, yaw, roll, 0);\n        }\n        inline Quaternion& operator-=(const Quaternion& other)\n        {\n            x -= other.x; y -= other.y; z -= other.z; w -= other.w;\n            return *this;\n        }\n        inline Quaternion operator-(const Quaternion& other) const\n        {\n            Quaternion vector;\n            vector.x = x - other.x;\n            vector.y = y - other.y;\n            vector.z = z - other.z;\n            vector.w = w - other.w;\n            return vector;\n        }\n        inline Quaternion& operator+=(const Quaternion& other)\n        {\n            x += other.x;\n            y += other.y;\n            z += other.z;\n            w += other.w;\n            return *this;\n        }\n        inline Quaternion operator+(const Quaternion& other)  const\n        {\n            Quaternion vector;\n            vector.x = x + other.x;\n            vector.y = y + other.y;\n            vector.z = z + other.z;\n            vector.w = w + other.w;\n            return vector;\n        }\n        inline Quaternion Conjugate()  const\n        {\n            Quaternion result(-x, -y, -z, w);\n            return result;\n        }\n        inline Vector DistanceAngles(const Quaternion& newValue) const\n        {\n            Vector diff;\n            amf::Quaternion diffQ = newValue * Conjugate();\n            float len = diffQ.Length4().x;\n\n            if (len <= 0.0005f)\n            {\n                diff = amf::Vector(2.0f * diffQ.x, 2.0f * diffQ.y, 2.0f * diffQ.z, 0);\n            }\n            else\n            {\n                float angle = 2.0f * atan2(len, diffQ.w);\n                diff = amf::Vector(diffQ.x * angle / len, diffQ.y * angle / len, diffQ.z * angle / len, 0);\n            }\n            return diff;\n        }\n    };\n    inline void ScalarSinCos(float* pSin, float* pCos, float  Value)\n    {\n        // Map Value to y in [-pi,pi], x = 2*pi*quotient + remainder.\n        float quotient = AMF_1DIV2PI *Value;\n        if (Value >= 0.0f)\n        {\n            quotient = (float)((int)(quotient + 0.5f));\n        }\n        else\n        {\n            quotient = (float)((int)(quotient - 0.5f));\n        }\n        float y = Value - AMF_2PI * quotient;\n\n        // Map y to [-pi/2,pi/2] with sin(y) = sin(Value).\n        float sign;\n        if (y > AMF_PIDIV2)\n        {\n            y = AMF_PI - y;\n            sign = -1.0f;\n        }\n        else if (y < -AMF_PIDIV2)\n        {\n            y = -AMF_PI - y;\n            sign = -1.0f;\n        }\n        else\n        {\n            sign = +1.0f;\n        }\n\n        float y2 = y * y;\n\n        // 11-degree minimax approximation\n        *pSin = ( ( ( ( (-2.3889859e-08f * y2 + 2.7525562e-06f) * y2 - 0.00019840874f ) * y2 + 0.0083333310f ) * y2 - 0.16666667f ) * y2 + 1.0f ) * y;\n\n        // 10-degree minimax approximation\n        float p = ( ( ( ( -2.6051615e-07f * y2 + 2.4760495e-05f ) * y2 - 0.0013888378f ) * y2 + 0.041666638f ) * y2 - 0.5f ) * y2 + 1.0f;\n        *pCos = sign*p;\n    }\n\n    //---------------------------------------------------------------------------------------------\n    class Matrix\n    {\n    public:\n        union\n        {\n            float       m[4][4];\n            VectorPOD   r[4];\n            float       k[16];\n        };\n\n        Matrix() {Identity();}\n\n        Matrix(float *_m) { memcpy(m, _m, sizeof(m));}\n        Matrix(float i0, float i1, float i2, float i3, float i4, float i5, float i6, float i7,\n               float i8, float i9, float i10, float i11, float i12, float i13, float i14, float i15)\n                {\n                    k[0] = i0;   k[1] = i1;   k[2] = i2;   k[3] = i3;\n                    k[4] = i4;   k[5] = i5;   k[6] = i6;   k[7] = i7;\n                    k[8] = i8;   k[9] = i9;   k[10] = i10; k[11] = i11;\n                    k[12] = i12; k[13] = i13; k[14] = i14; k[15] = i15;\n                }\n\n        inline void Identity()\n        {\n            k[0] = k[5] = k[10] = k[15] = 1.0f;\n            k[1] = k[2] = k[3] = k[4] = k[6] = k[7] = k[8] = k[9] = k[11] = k[12] = k[13] = k[14] = 0.0f;\n\n        }\n        inline Matrix &operator=(const Matrix & other)\n        {\n            memcpy(m, other.m, sizeof(m));\n            return *this;\n        }\n\n        inline Matrix operator*(const Matrix& n) const\n        {\n            return Matrix(\n                     k[0]*n.k[0]  + k[4]*n.k[1]  + k[8]*n.k[2]  + k[12]*n.k[3],   k[1]*n.k[0]  + k[5]*n.k[1]  + k[9]*n.k[2]  + k[13]*n.k[3],   k[2]*n.k[0]  + k[6]*n.k[1]  + k[10]*n.k[2]  + k[14]*n.k[3],   k[3]*n.k[0]  + k[7]*n.k[1]  + k[11]*n.k[2]  + k[15]*n.k[3],\n                     k[0]*n.k[4]  + k[4]*n.k[5]  + k[8]*n.k[6]  + k[12]*n.k[7],   k[1]*n.k[4]  + k[5]*n.k[5]  + k[9]*n.k[6]  + k[13]*n.k[7],   k[2]*n.k[4]  + k[6]*n.k[5]  + k[10]*n.k[6]  + k[14]*n.k[7],   k[3]*n.k[4]  + k[7]*n.k[5]  + k[11]*n.k[6]  + k[15]*n.k[7],\n                     k[0]*n.k[8]  + k[4]*n.k[9]  + k[8]*n.k[10] + k[12]*n.k[11],  k[1]*n.k[8]  + k[5]*n.k[9]  + k[9]*n.k[10] + k[13]*n.k[11],  k[2]*n.k[8]  + k[6]*n.k[9]  + k[10]*n.k[10] + k[14]*n.k[11],  k[3]*n.k[8]  + k[7]*n.k[9]  + k[11]*n.k[10] + k[15]*n.k[11],\n                     k[0]*n.k[12] + k[4]*n.k[13] + k[8]*n.k[14] + k[12]*n.k[15],  k[1]*n.k[12] + k[5]*n.k[13] + k[9]*n.k[14] + k[13]*n.k[15],  k[2]*n.k[12] + k[6]*n.k[13] + k[10]*n.k[14] + k[14]*n.k[15],  k[3]*n.k[12] + k[7]*n.k[13] + k[11]*n.k[14] + k[15]*n.k[15]);\n        }\n        inline Matrix operator*=(const Matrix& other)\n        {\n            *this = *this * other;\n            return *this;\n        }\n\n        inline bool operator==(const Matrix& other) const\n        {\n            return memcmp(this, &other, sizeof(*this)) == 0;\n        }\n        inline bool operator!=(const Matrix& other) const\n        {\n            return memcmp(this, &other, sizeof(*this)) != 0;\n        }\n\n        inline Vector operator*(const Vector& v) const\n        {\n            Vector Z(v.z, v.z, v.z, v.z);\n            Vector Y(v.y, v.y, v.y, v.y);\n            Vector X(v.x, v.x, v.x, v.x);\n\n            Vector ret;\n            ret = Z * r[2] + r[3];\n            ret = Y * r[1] + ret;\n            ret = X * r[0] + ret;\n\n            return ret;\n        }\n\n        void MatrixAffineTransformation(const Vector &Scaling, const Vector &RotationOrigin, const Vector &RotationQuaternion, const Vector &Translation)\n        {\n            // M = MScaling * Inverse(MRotationOrigin) * MRotation * MRotationOrigin * MTranslation;\n\n            MatrixScalingFromVector(Scaling);\n            Vector VRotationOrigin (RotationOrigin.x,RotationOrigin.y,RotationOrigin.z, 0);\n            Matrix MRotation;\n            MRotation.MatrixRotationQuaternion(RotationQuaternion);\n            Vector VTranslation (Translation.x, Translation.y, Translation.z, 0);\n\n            r[3] -= VRotationOrigin;\n            *this *= MRotation;\n            r[3] += VRotationOrigin;\n            r[3] += VTranslation;\n        }\n        inline void MatrixScalingFromVector(const Vector& Scale)\n        {\n            m[0][0] = Scale.x;\n            m[1][1] = Scale.y;\n            m[2][2] = Scale.z;\n            m[3][3] = 1.0f;\n\n        }\n        void MatrixRotationQuaternion(const Vector& Quaternion)\n        {\n            static const Vector Constant1110 = {1.0f, 1.0f, 1.0f, 0.0f};\n\n            Vector Q0 = Quaternion + Quaternion;\n            Vector Q1 = Quaternion * Q0;\n\n            Vector V0 = Q1.VectorPermute(Constant1110, AMF_PERMUTE_0Y, AMF_PERMUTE_0X, AMF_PERMUTE_0X, AMF_PERMUTE_1W);\n            Vector V1 = Q1.VectorPermute(Constant1110, AMF_PERMUTE_0Z, AMF_PERMUTE_0Z, AMF_PERMUTE_0Y, AMF_PERMUTE_1W);\n            Vector R0 = Constant1110 - V0;\n            R0 = R0 - V1;\n\n            V0 = Quaternion.Swizzle(AMF_SWIZZLE_X, AMF_SWIZZLE_X, AMF_SWIZZLE_Y, AMF_SWIZZLE_W);\n            V1 = Q0.Swizzle(AMF_SWIZZLE_Z, AMF_SWIZZLE_Y, AMF_SWIZZLE_Z, AMF_SWIZZLE_W);\n            V0 = V0 * V1;\n\n            V1 = Vector(Quaternion.w, Quaternion.w, Quaternion.w, Quaternion.w);\n            Vector V2 = Q0.Swizzle(AMF_SWIZZLE_Y, AMF_SWIZZLE_Z, AMF_SWIZZLE_X, AMF_SWIZZLE_W);\n            V1 = V1 * V2;\n\n            Vector R1 = V0 + V1;\n            Vector R2 = V0 - V1;\n\n            V0 = R1.VectorPermute(R2, AMF_PERMUTE_0Y, AMF_PERMUTE_1X, AMF_PERMUTE_1Y, AMF_PERMUTE_0Z);\n            V1 = R1.VectorPermute(R2, AMF_PERMUTE_0X, AMF_PERMUTE_1Z, AMF_PERMUTE_0X, AMF_PERMUTE_1Z);\n\n            r[0] = R0.VectorPermute(V0, AMF_PERMUTE_0X, AMF_PERMUTE_1X, AMF_PERMUTE_1Y, AMF_PERMUTE_0W);\n            r[1] = R0.VectorPermute(V0, AMF_PERMUTE_1Z, AMF_PERMUTE_0Y, AMF_PERMUTE_1W, AMF_PERMUTE_0W);\n            r[2] = R0.VectorPermute(V1, AMF_PERMUTE_1X, AMF_PERMUTE_1Y, AMF_PERMUTE_0Z, AMF_PERMUTE_0W);\n            r[3] = Vector(0.0f, 0.0f, 0.0f, 1.0f);\n        }\n        inline void LookToLH(const Vector& EyePosition, const Vector& EyeDirection, const Vector& UpDirection)\n        {\n            Vector R2 = EyeDirection.Normalize3();\n\n            Vector R0 = UpDirection.Cross3(R2);\n            R0 = R0.Normalize3();\n\n            Vector R1 = R2.Cross3(R0);\n\n            Vector NegEyePosition = EyePosition.Negate();\n\n            Vector D0 = R0.Dot3(NegEyePosition);\n            Vector D1 = R1.Dot3(NegEyePosition);\n            Vector D2 = R2.Dot3(NegEyePosition);\n\n            Matrix M;\n            M.r[0] = Vector(R0.x, R0.y, R0.z, D0.w);\n            M.r[1] = Vector(R1.x, R1.y, R1.z, D1.w);\n            M.r[2] = Vector(R2.x, R2.y, R2.z, D2.w);\n            M.r[3] = Vector(0.0f, 0.0f, 0.0f, 1.0f);\n\n            *this = M.Transpose();\n        }\n        inline Matrix Transpose() const\n        {\n\n            // Original matrix:\n            //\n            //     m00m01m02m03\n            //     m10m11m12m13\n            //     m20m21m22m23\n            //     m30m31m32m33\n\n            Matrix P;\n            P.r[0] = r[0].MergeXY(r[2]); // m00m20m01m21\n            P.r[1] = r[1].MergeXY(r[3]); // m10m30m11m31\n            P.r[2] = r[0].MergeZW(r[2]); // m02m22m03m23\n            P.r[3] = r[1].MergeZW(r[3]); // m12m32m13m33\n\n            Matrix MT;\n            MT.r[0] = P.r[0].MergeXY(P.r[1]); // m00m10m20m30\n            MT.r[1] = P.r[0].MergeZW(P.r[1]); // m01m11m21m31\n            MT.r[2] = P.r[2].MergeXY(P.r[3]); // m02m12m22m32\n            MT.r[3] = P.r[2].MergeZW(P.r[3]); // m03m13m23m33\n            return MT;\n        }\n        inline void LookAtLH(Vector& EyePosition, Vector& FocusPosition, Vector& UpDirection)\n        {\n            Vector EyeDirection = FocusPosition - EyePosition;\n            LookToLH(EyePosition, EyeDirection, UpDirection);\n        }\n        inline void PerspectiveFovLH(float FovAngleY, float AspectRatio, float NearZ, float FarZ)\n        {\n            float    SinFov;\n            float    CosFov;\n            ScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY);\n\n            float Height = CosFov / SinFov;\n            float Width = Height / AspectRatio;\n            float fRange = FarZ / (FarZ-NearZ);\n\n            m[0][0] = Width;\n            m[0][1] = 0.0f;\n            m[0][2] = 0.0f;\n            m[0][3] = 0.0f;\n\n            m[1][0] = 0.0f;\n            m[1][1] = Height;\n            m[1][2] = 0.0f;\n            m[1][3] = 0.0f;\n\n            m[2][0] = 0.0f;\n            m[2][1] = 0.0f;\n            m[2][2] = fRange;\n            m[2][3] = 1.0f;\n\n            m[3][0] = 0.0f;\n            m[3][1] = 0.0f;\n            m[3][2] = -fRange * NearZ;\n            m[3][3] = 0.0f;\n        }\n        inline void RotationRollPitchYaw(float Pitch, float Yaw, float Roll)\n        {\n            Quaternion Q;\n            Q.FromEuler( Pitch, Yaw, Roll);\n            MatrixAffineTransformation(Vector(1.f, 1.f, 1.f, 0.f), Vector(), Q, Vector());\n        }\n        inline Vector Determinant()\n        {\n            static const Vector Sign (1.0f, -1.0f, 1.0f, -1.0f);\n\n            Vector V0(r[2].y, r[2].x, r[2].x, r[2].x);\n            Vector V1(r[3].z, r[3].z, r[3].y, r[3].y);\n            Vector V2(r[2].y, r[2].x, r[2].x, r[2].x);\n            Vector V3(r[3].w, r[3].w, r[3].w, r[3].z);\n            Vector V4(r[2].z, r[2].z, r[2].y, r[2].y);\n            Vector V5(r[3].w, r[3].w, r[3].w, r[3].z);\n\n            Vector P0 = V0 * V1;\n            Vector P1 = V2 * V3;\n            Vector P2 = V4 * V5;\n\n            V0 = Vector(r[2].z, r[2].z, r[2].y, r[2].y);\n            V1 = Vector(r[3].y, r[3].x, r[3].x, r[3].x);\n            V2 = Vector(r[2].w, r[2].w, r[2].w, r[2].z);\n            V3 = Vector(r[3].y, r[3].x, r[3].x, r[3].x);\n            V4 = Vector(r[2].w, r[2].w, r[2].w, r[2].z);\n            V5 = Vector(r[3].z, r[3].z, r[3].y, r[3].y);\n\n            P0 -= V0 * V1;\n            P1 -= V2 * V3;\n            P2 -= V4 * V5;\n\n            V0 = Vector(r[1].w, r[1].w, r[1].w, r[1].z);\n            V1 = Vector(r[1].z, r[1].z, r[1].y, r[1].y);\n            V2 = Vector(r[1].y, r[1].x, r[1].x, r[1].x);\n\n\n            Vector S = r[0] * Sign;\n            Vector R = V0 * P0;\n            R -= V1 * P1;\n            R += V2 * P2;\n\n            return S.Dot4(R);\n        }\n#define XM3RANKDECOMPOSE(a, b, c, x, y, z)      \\\n    if((x) < (y))                   \\\n    {                               \\\n        if((y) < (z))               \\\n        {                           \\\n            (a) = 2;                \\\n            (b) = 1;                \\\n            (c) = 0;                \\\n        }                           \\\n        else                        \\\n        {                           \\\n            (a) = 1;                \\\n                                    \\\n            if((x) < (z))           \\\n            {                       \\\n                (b) = 2;            \\\n                (c) = 0;            \\\n            }                       \\\n            else                    \\\n            {                       \\\n                (b) = 0;            \\\n                (c) = 2;            \\\n            }                       \\\n        }                           \\\n    }                               \\\n    else                            \\\n    {                               \\\n        if((x) < (z))               \\\n        {                           \\\n            (a) = 2;                \\\n            (b) = 0;                \\\n            (c) = 1;                \\\n        }                           \\\n        else                        \\\n        {                           \\\n            (a) = 0;                \\\n                                    \\\n            if((y) < (z))           \\\n            {                       \\\n                (b) = 2;            \\\n                (c) = 1;            \\\n            }                       \\\n            else                    \\\n            {                       \\\n                (b) = 1;            \\\n                (c) = 2;            \\\n            }                       \\\n        }                           \\\n    }\n\n#define XM3_DECOMP_EPSILON 0.0001f\n\n        inline amf::Quaternion ConvertMatrixToQuat()\n        {\n            amf::Quaternion q;\n            float r22 = m[2][2];\n            if (r22 <= 0.f)  // x^2 + y^2 >= z^2 + w^2\n            {\n                float dif10 = m[1][1] - m[0][0];\n                float omr22 = 1.f - r22;\n                if (dif10 <= 0.f)  // x^2 >= y^2\n                {\n                    float fourXSqr = omr22 - dif10;\n                    float inv4x = 0.5f / sqrtf(fourXSqr);\n                    q.x = fourXSqr*inv4x;\n                    q.y = (m[0][1] + m[1][0])*inv4x;\n                    q.z = (m[0][2] + m[2][0])*inv4x;\n                    q.w = (m[1][2] - m[2][1])*inv4x;\n                }\n                else  // y^2 >= x^2\n                {\n                    float fourYSqr = omr22 + dif10;\n                    float inv4y = 0.5f / sqrtf(fourYSqr);\n                    q.x = (m[0][1] + m[1][0])*inv4y;\n                    q.y = fourYSqr*inv4y;\n                    q.z = (m[1][2] + m[2][1])*inv4y;\n                    q.w = (m[2][0] - m[0][2])*inv4y;\n                }\n            }\n            else  // z^2 + w^2 >= x^2 + y^2\n            {\n                float sum10 = m[1][1] + m[0][0];\n                float opr22 = 1.f + r22;\n                if (sum10 <= 0.f)  // z^2 >= w^2\n                {\n                    float fourZSqr = opr22 - sum10;\n                    float inv4z = 0.5f / sqrtf(fourZSqr);\n                    q.x = (m[0][2] + m[2][0])*inv4z;\n                    q.y = (m[1][2] + m[2][1])*inv4z;\n                    q.z = fourZSqr*inv4z;\n                    q.w = (m[0][1] - m[1][0])*inv4z;\n                }\n                else  // w^2 >= z^2\n                {\n                    float fourWSqr = opr22 + sum10;\n                    float inv4w = 0.5f / sqrtf(fourWSqr);\n                    q.x = (m[1][2] - m[2][1])*inv4w;\n                    q.y = (m[2][0] - m[0][2])*inv4w;\n                    q.z = (m[0][1] - m[1][0])*inv4w;\n                    q.w = fourWSqr*inv4w;\n                }\n            }\n            return q;\n        }\n        inline bool DecomposeMatrix(amf::Quaternion &q, amf::Vector &p, amf::Vector &s)\n        {\n            static amf::Vector amfXMIdentityR0( 1.0f, 0.0f, 0.0f, 0.0f );\n            static amf::Vector amfXMIdentityR1( 0.0f, 1.0f, 0.0f, 0.0f );\n            static amf::Vector amfXMIdentityR2( 0.0f, 0.0f, 1.0f, 0.0f );\n            static const amf::VectorPOD *pvCanonicalBasis[3] = {\n                    &amfXMIdentityR0,\n                    &amfXMIdentityR1,\n                    &amfXMIdentityR2\n            };\n\n//    p.Assign(-m.m[0][3], -m.m[1][3], -m.m[2][3], 0);\n            p = r[3];\n\n            amf::VectorPOD *ppvBasis[3];\n            amf::Matrix matTemp;\n            ppvBasis[0] = &matTemp.r[0];\n            ppvBasis[1] = &matTemp.r[1];\n            ppvBasis[2] = &matTemp.r[2];\n\n            matTemp.r[0] = r[0];\n            matTemp.r[1] = r[1];\n            matTemp.r[2] = r[2];\n            matTemp.r[3] = amf::Vector(0.0f, 0.0f, 0.0f, 1.0f );\n\n\n            float *pfScales = (float*)&s;\n\n            size_t a, b, c;\n            pfScales[0] = ppvBasis[0][0].Length3().x;\n            pfScales[1] = ppvBasis[1][0].Length3().x;\n            pfScales[2] = ppvBasis[2][0].Length3().x;\n            pfScales[3] = 0.f;\n\n            XM3RANKDECOMPOSE(a, b, c, pfScales[0], pfScales[1], pfScales[2])\n\n            if(pfScales[a] < XM3_DECOMP_EPSILON)\n            {\n                ppvBasis[a][0] = pvCanonicalBasis[a][0];\n            }\n            ppvBasis[a][0] = ppvBasis[a][0].Normalize3();\n\n            if(pfScales[b] < XM3_DECOMP_EPSILON)\n            {\n                size_t aa, bb, cc;\n                float fAbsX, fAbsY, fAbsZ;\n\n                fAbsX = fabsf(ppvBasis[a][0].x);\n                fAbsY = fabsf(ppvBasis[a][0].y);\n                fAbsZ = fabsf(ppvBasis[a][0].z);\n\n                XM3RANKDECOMPOSE(aa, bb, cc, fAbsX, fAbsY, fAbsZ)\n\n                ppvBasis[b][0] = ppvBasis[a][0].Cross3(pvCanonicalBasis[cc][0]);\n            }\n\n            ppvBasis[b][0] = ppvBasis[b][0].Normalize3();\n\n            if(pfScales[c] < XM3_DECOMP_EPSILON)\n            {\n                ppvBasis[c][0] = ppvBasis[a][0].Cross3(ppvBasis[b][0]);\n            }\n\n            ppvBasis[c][0] = ppvBasis[c][0].Normalize3();\n\n\n            float fDet = matTemp.Determinant().x;\n\n            // use Kramer's rule to check for handedness of coordinate system\n            if(fDet < 0.0f)\n            {\n                // switch coordinate system by negating the scale and inverting the basis vector on the x-axis\n                pfScales[a] = -pfScales[a];\n                ppvBasis[a][0] = ppvBasis[a][0].Negate();\n\n                fDet = -fDet;\n            }\n\n            fDet -= 1.0f;\n            fDet *= fDet;\n\n            if(XM3_DECOMP_EPSILON < fDet)\n            {\n                // Non-SRT matrix encountered\n                return false;\n            }\n\n            q = matTemp.ConvertMatrixToQuat();\n            return true;\n        }\n        inline Matrix Inverse(Vector *pDeterminant)\n        {\n\n            float A2323 = m[2][2] * m[3][3] - m[2][3] * m[3][2];\n            float A1323 = m[2][1] * m[3][3] - m[2][3] * m[3][1];\n            float A1223 = m[2][1] * m[3][2] - m[2][2] * m[3][1];\n            float A0323 = m[2][0] * m[3][3] - m[2][3] * m[3][0];\n            float A0223 = m[2][0] * m[3][2] - m[2][2] * m[3][0];\n            float A0123 = m[2][0] * m[3][1] - m[2][1] * m[3][0];\n            float A2313 = m[1][2] * m[3][3] - m[1][3] * m[3][2];\n            float A1313 = m[1][1] * m[3][3] - m[1][3] * m[3][1];\n            float A1213 = m[1][1] * m[3][2] - m[1][2] * m[3][1];\n            float A2312 = m[1][2] * m[2][3] - m[1][3] * m[2][2];\n            float A1312 = m[1][1] * m[2][3] - m[1][3] * m[2][1];\n            float A1212 = m[1][1] * m[2][2] - m[1][2] * m[2][1];\n            float A0313 = m[1][0] * m[3][3] - m[1][3] * m[3][0];\n            float A0213 = m[1][0] * m[3][2] - m[1][2] * m[3][0];\n            float A0312 = m[1][0] * m[2][3] - m[1][3] * m[2][0];\n            float A0212 = m[1][0] * m[2][2] - m[1][2] * m[2][0];\n            float A0113 = m[1][0] * m[3][1] - m[1][1] * m[3][0];\n            float A0112 = m[1][0] * m[2][1] - m[1][1] * m[2][0];\n\n            float det =\n              m[0][0] * (m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223)\n            - m[0][1] * (m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223)\n            + m[0][2] * (m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123)\n            - m[0][3] * (m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123);\n            det = 1.0f / det;\n            Matrix ret;\n            ret.m[0][0] = det *  (m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223);\n            ret.m[0][1] = det * -(m[0][1] * A2323 - m[0][2] * A1323 + m[0][3] * A1223);\n            ret.m[0][2] = det *  (m[0][1] * A2313 - m[0][2] * A1313 + m[0][3] * A1213);\n            ret.m[0][3] = det * -(m[0][1] * A2312 - m[0][2] * A1312 + m[0][3] * A1212);\n            ret.m[1][0] = det * -(m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223);\n            ret.m[1][1] = det *  (m[0][0] * A2323 - m[0][2] * A0323 + m[0][3] * A0223);\n            ret.m[1][2] = det * -(m[0][0] * A2313 - m[0][2] * A0313 + m[0][3] * A0213);\n            ret.m[1][3] = det *  (m[0][0] * A2312 - m[0][2] * A0312 + m[0][3] * A0212);\n            ret.m[2][0] = det *  (m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123);\n            ret.m[2][1] = det * -(m[0][0] * A1323 - m[0][1] * A0323 + m[0][3] * A0123);\n            ret.m[2][2] = det *  (m[0][0] * A1313 - m[0][1] * A0313 + m[0][3] * A0113);\n            ret.m[2][3] = det * -(m[0][0] * A1312 - m[0][1] * A0312 + m[0][3] * A0112);\n            ret.m[3][0] = det * -(m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123);\n            ret.m[3][1] = det *  (m[0][0] * A1223 - m[0][1] * A0223 + m[0][2] * A0123);\n            ret.m[3][2] = det * -(m[0][0] * A1213 - m[0][1] * A0213 + m[0][2] * A0113);\n            ret.m[3][3] = det *  (m[0][0] * A1212 - m[0][1] * A0212 + m[0][2] * A0112);\n\n            if (pDeterminant != nullptr)\n            {\n                *pDeterminant = Vector(det,det,det,det);\n            }\n            return ret;\n        }\n\n    };\n\n    class Pose\n    {\n    public:\n        typedef enum ValidityFlagBits\n        {\n            // validity flags\n            PF_NONE                     = 0x0000,\n            PF_ORIENTATION              = 0x0001,\n            PF_POSITION                 = 0x0002,\n            PF_ORIENTATION_VELOCITY     = 0x0004,\n            PF_POSITION_VELOCITY        = 0x0008,\n            PF_ORIENTATION_ACCELERATION = 0x0010,\n            PF_POSITION_ACCELERATION    = 0x0020,\n            PF_COORDINATES              = PF_ORIENTATION | PF_POSITION,\n            PF_VELOCITY                 = PF_ORIENTATION_VELOCITY | PF_POSITION_VELOCITY,\n            PF_ACCELERATION             = PF_ORIENTATION_ACCELERATION | PF_POSITION_ACCELERATION,\n        } ValidityFlagBits;\n        typedef uint32_t ValidityFlags;\n\n        Pose() : m_ValidityFlags(PF_NONE){}\n        Pose(const Pose &other) { *this = other; }\n\n        Pose(const amf::Quaternion& orientation, const amf::Vector& position)\n        {\n            Set(orientation, position);\n        }\n        Pose(const amf::Quaternion& orientation, const amf::Vector& position,\n            const amf::Vector& orientationVelocity, const amf::Vector& positionVelocity)\n        {\n            Set(orientation, position, orientationVelocity, positionVelocity);\n        }\n        Pose(const amf::Quaternion& orientation, const amf::Vector& position,\n            const amf::Vector& orientationVelocity, const amf::Vector& positionVelocity,\n            const amf::Vector& orientationAcceleration, const amf::Vector& positionAcceleration)\n        {\n            Set(orientation, position, orientationVelocity, positionVelocity, orientationAcceleration, positionAcceleration);\n        }\n        void Set(const amf::Quaternion& orientation, const amf::Vector& position)\n        {\n            m_Orientation = orientation;\n            m_Position = position;\n            m_ValidityFlags = PF_COORDINATES;\n        }\n        void Set(const amf::Quaternion& orientation, const amf::Vector& position,\n            const amf::Vector& orientationVelocity, const amf::Vector& positionVelocity)\n        {\n            m_Orientation = orientation;\n            m_Position = position;\n            m_OrientationVelocity = orientationVelocity;\n            m_PositionVelocity = positionVelocity;\n            m_ValidityFlags = PF_COORDINATES | PF_VELOCITY;\n        }\n\n        void Set(const amf::Quaternion& orientation, const amf::Vector& position,\n            const amf::Vector& orientationVelocity, const amf::Vector& positionVelocity,\n            const amf::Vector& orientationAcceleration, const amf::Vector& positionAcceleration)\n        {\n            m_Orientation = orientation;\n            m_Position = position;\n            m_OrientationVelocity = orientationVelocity;\n            m_PositionVelocity = positionVelocity;\n            m_OrientationAcceleration = orientationAcceleration;\n            m_PositionAcceleration = positionAcceleration;\n            m_ValidityFlags = PF_COORDINATES | PF_VELOCITY | PF_ACCELERATION;\n        }\n\n\n        inline const amf::Quaternion&   GetOrientation() const { return m_Orientation; }\n        inline const amf::Vector&   GetPosition() const { return m_Position; }\n        inline const amf::Vector&   GetOrientationVelocity() const { return m_OrientationVelocity; }\n        inline const amf::Vector&   GetPositionVelocity() const { return m_PositionVelocity; }\n        inline const amf::Vector&   GetOrientationAcceleration() const { return m_OrientationAcceleration; }\n        inline const amf::Vector&   GetPositionAcceleration() const { return m_PositionAcceleration; }\n        inline ValidityFlags        GetValidityFlags() const { return m_ValidityFlags; }\n\n        inline void   SetOrientation(const amf::Quaternion& orienation)\n        {\n            m_Orientation = orienation;\n            m_ValidityFlags |= PF_ORIENTATION;\n        }\n        inline void       SetPosition(const amf::Vector& position)\n        {\n            m_Position = position;\n            m_ValidityFlags |= PF_POSITION;\n        }\n        inline void       SetOrientationVelocity(const amf::Vector& orientationVelocity)\n        {\n            m_OrientationVelocity = orientationVelocity;\n            m_ValidityFlags |= PF_ORIENTATION_VELOCITY;\n        }\n        inline void       SetPositionVelocity(const amf::Vector& positionVelocity)\n        {\n            m_PositionVelocity = positionVelocity;\n            m_ValidityFlags |= PF_POSITION_VELOCITY;\n        }\n        inline void       SetOrientationAcceleration(const amf::Vector& orientationAcceleration)\n        {\n            m_OrientationAcceleration = orientationAcceleration;\n            m_ValidityFlags |= PF_ORIENTATION_ACCELERATION;\n        }\n        inline void       SetPositionAcceleration(const amf::Vector& positionAcceleration)\n        {\n            m_PositionAcceleration = positionAcceleration;\n            m_ValidityFlags |= PF_POSITION_ACCELERATION;\n        }\n\n    protected:\n        amf::Quaternion                 m_Orientation;\n        amf::Vector                     m_Position;\n        amf::Vector                     m_OrientationVelocity;\n        amf::Vector                     m_PositionVelocity;\n        amf::Vector                     m_OrientationAcceleration;\n        amf::Vector                     m_PositionAcceleration;\n        ValidityFlags                   m_ValidityFlags;\n    };\n\n\t//-------------------------------------------------------------------------------------------------\n\ttemplate <typename T>\n\tclass AlphaFilter\n\t{\n\tpublic:\n\t\tAlphaFilter(T alpha) :\n\t\t\tm_Alpha(alpha),\n\t\t\tm_FilteredValue(0)\n\t\t{\n\t\t}\n\n\t\tT Apply(T value)\n\t\t{\n\t\t\tm_FilteredValue = m_FilteredValue + m_Alpha * (value - m_FilteredValue);\n\t\t\treturn m_FilteredValue;\n\t\t}\n\n\tprivate:\n\t\tT m_Alpha;\n\t\tT m_FilteredValue;\n\t};\n\n\t//-------------------------------------------------------------------------------------------------\n\ttemplate <typename T>\n\tclass AlphaBetaFilter\n\t{\n\tpublic:\n\t\tAlphaBetaFilter(T alpha, T beta) :\n\t\t\tm_Alpha(alpha),\n\t\t\tm_Beta(beta),\n\t\t\tm_Value(0),\n\t\t\tm_PrevValue(0),\n\t\t\tm_Velocity(0)\n\t\t{\n\t\t}\n\n\t\tT Apply(T value, T dt)\n\t\t{\n\t\t\tm_PrevValue = m_Value;\n\n\t\t\tm_Value += m_Velocity * dt;\n\t\t\tT rk = value - m_Value;\n\t\t\tm_Value += m_Alpha * rk;\n\t\t\tm_Velocity += (m_Beta * rk) / dt;\n\n\t\t\treturn m_Value;\n\t\t}\n\n\t\tinline T GetVelocity() const { return m_Velocity; }\n\n\tprivate:\n\t\tT\tm_Alpha,\n\t\t\tm_Beta;\n\t\tT\tm_Value,\n\t\t\tm_PrevValue,\n\t\t\tm_Velocity;\n\t};\n\n\t//-------------------------------------------------------------------------------------------------\n\ttemplate <typename T>\n\tclass ThresholdFilter\n\t{\n\tpublic:\n\t\tThresholdFilter(T threshold) :\n\t\t\tm_Threshold(threshold)\n\t\t{\n\t\t}\n\n\t\tT Apply(T value) const\n\t\t{\n\t\t\tT result = value;\n\t\t\tif (std::abs(value) < m_Threshold)\n\t\t\t{\n\t\t\t\tresult = T(0);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\tprivate:\n\t\tT\tm_Threshold;\n\t};\n\n\t//-------------------------------------------------------------------------------------------------\n\tclass Derivative\n\t{\n\tpublic:\n\t\tDerivative() {}\n\n\t\tinline static float Calculate(float newVal, float oldVal, float dt)\n\t\t{\n\t\t\treturn (newVal - oldVal) / dt;\n\t\t}\n\n\t\tinline static float Calculate(float dx, float dt)\n\t\t{\n\t\t\treturn dx / dt;\n\t\t}\n\n\t\tstatic amf::Vector Calculate(const amf::Vector& newVal, const amf::Vector& oldVal, float dt)\n\t\t{\n\t\t\tamf::Vector result;\n\t\t\tresult.w = Calculate(newVal.w, oldVal.w, dt);\n\t\t\tresult.x = Calculate(newVal.x, oldVal.x, dt);\n\t\t\tresult.y = Calculate(newVal.y, oldVal.y, dt);\n\t\t\tresult.z = Calculate(newVal.z, oldVal.z, dt);\n\t\t\treturn result;\n\t\t}\n\n\t\tstatic amf::Vector Calculate(const amf::Vector& dx, float dt)\n\t\t{\n\t\t\tamf::Vector result;\n\t\t\tresult.w = Calculate(dx.w, dt);\n\t\t\tresult.x = Calculate(dx.x, dt);\n\t\t\tresult.y = Calculate(dx.y, dt);\n\t\t\tresult.z = Calculate(dx.z, dt);\n\t\t\treturn result;\n\t\t}\n\t};\n\n\n\n    //---------------------------------------------------------------------------------------------\n} // namespace amf"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/AMFSTL.cpp",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"AMFSTL.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <locale>\n#include <locale.h>\n#include <fstream>\n#include <string>\n#include <wchar.h>\n#include <stdarg.h>\n#if defined(__ANDROID__)\n    #include <codecvt>\n#endif\n\n#if !defined(__APPLE__) && !defined(_WIN32)\n#include <malloc.h>\n#endif\n\n#pragma warning(disable: 4996)\n\n#if defined(__linux) || defined(__APPLE__)\nextern \"C\"\n{\n    extern int vscwprintf(const wchar_t* p_fmt, va_list p_args);\n    extern int vscprintf(const char* p_fmt, va_list p_args);\n}\n#endif\n\n#ifdef _MSC_VER\n    #define snprintf _snprintf\n    #define vscprintf _vscprintf\n    #define vscwprintf _vscwprintf  //  Count chars without writing to string\n    #define vswprintf _vsnwprintf\n#endif\n\n\nusing namespace amf;\n\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n    #pragma clang diagnostic ignored \"-Wglobal-constructors\"\n#endif\n\nstatic const amf_string AMF_FORBIDDEN_SYMBOLS = \":? %,;@&=+$<>#\\\"\";\nstatic const amf_string AMF_FORBIDDEN_SYMBOLS_QUERY = \":? %,;@+$<>#\\\"\";\n\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n\n//MM: in question:  unwise      = \"{\" | \"}\" | \"|\" | \"\\\" | \"^\" | \"[\" | \"]\" | \"`\"\n\n//----------------------------------------------------------------------------------------\n// string conversaion\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_from_unicode_to_utf8(const amf_wstring& str)\n{\n    amf_string result;\n    if(0 == str.size())\n    {\n        return result;\n    }\n#if defined(_WIN32)\n    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);\n#endif\n\n\n    const wchar_t* pwBuff = str.c_str();\n\n#if defined(_WIN32)\n    int Utf8BuffSize = ::WideCharToMultiByte(CP_UTF8, 0, pwBuff, -1, NULL, 0, NULL, NULL);\n    if(0 == Utf8BuffSize)\n    {\n        return result;\n    }\n    Utf8BuffSize += 8; // get some extra space\n    result.resize(Utf8BuffSize);\n    Utf8BuffSize = ::WideCharToMultiByte(CP_UTF8, 0, pwBuff, -1, &result[0], Utf8BuffSize, NULL, NULL);\n    Utf8BuffSize--;\n#elif defined(__ANDROID__)\n    char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n    int Utf8BuffSize = str.length();\n    if(0 == Utf8BuffSize)\n    {\n        return result;\n    }\n    Utf8BuffSize += 8; // get some extra space\n    result.resize(Utf8BuffSize);\n\n    mbstate_t mbs;\n    mbrlen(NULL, 0, &mbs);\n\n    Utf8BuffSize = 0;\n    for( int i = 0; i < str.length(); i++)\n    {\n        //MM TODO Android - not implemented\n        //int written = wcrtomb(&result[Utf8BuffSize], pwBuff[i], &mbs);\n        result[Utf8BuffSize] = (char)(pwBuff[i]);\n        int written = 1;\n        // temp replacement\n        Utf8BuffSize += written;\n    }\n    setlocale(LC_CTYPE, old_locale);\n\n#else\n    char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n    int Utf8BuffSize = wcstombs(NULL, pwBuff, 0);\n    if(0 == Utf8BuffSize)\n    {\n        return result;\n    }\n    Utf8BuffSize += 8; // get some extra space\n    result.resize(Utf8BuffSize);\n    Utf8BuffSize = wcstombs(&result[0], pwBuff, Utf8BuffSize);\n\n    setlocale(LC_CTYPE, old_locale);\n#endif\n    result.resize(Utf8BuffSize);\n\n\n    return result;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_from_utf8_to_unicode(const amf_string& str)\n{\n    amf_wstring result;\n    if(0 == str.size())\n    {\n        return result;\n    }\n#if defined(_WIN32)\n    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);\n#endif\n\n\n    const char* pUtf8Buff = str.c_str();\n\n#if defined(_WIN32)\n    int UnicodeBuffSize = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8Buff, -1, NULL, 0);\n    if(0 == UnicodeBuffSize)\n    {\n        return result;\n    }\n    UnicodeBuffSize += 8; // get some extra space\n    result.resize(UnicodeBuffSize);\n    UnicodeBuffSize = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8Buff, -1, &result[0], UnicodeBuffSize);\n    UnicodeBuffSize--;\n\n#elif defined(__ANDROID__)\n    //MM on android mbstowcs cannot be used to define length\n    char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n\n    mbstate_t mbs;\n    mbrlen(NULL, 0, &mbs);\n    int len = str.length();\n    const char* pt = pUtf8Buff;\n    int UnicodeBuffSize = 0;\n    while(len > 0)\n    {\n        size_t length = mbrlen (pt, len, &mbs); //MM TODO Android always return 1\n        if((length == 0) || (length > len))\n        {\n            break;\n        }\n        UnicodeBuffSize++;\n        len -= length;\n        pt += length;\n    }\n    UnicodeBuffSize += 8; // get some extra space\n    result.resize(UnicodeBuffSize);\n\n    mbrlen (NULL, 0, &mbs);\n    len = str.length();\n    pt = pUtf8Buff;\n    UnicodeBuffSize = 0;\n    while(len > 0)\n    {\n        size_t length = mbrlen (pt, len, &mbs);\n        if((length == 0) || (length > len))\n        {\n            break;\n        }\n        mbrtowc(&result[UnicodeBuffSize], pt, length, &mbs);     //MM TODO Android always return 1 char\n        UnicodeBuffSize++;\n        len -= length;\n        pt += length;\n    }\n    setlocale(LC_CTYPE, old_locale);\n\n #else\n    char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n    int UnicodeBuffSize = mbstowcs(NULL, pUtf8Buff, 0);\n    if(0 == UnicodeBuffSize)\n    {\n        return result;\n    }\n    UnicodeBuffSize += 8; // get some extra space\n    result.resize(UnicodeBuffSize);\n    UnicodeBuffSize = mbstowcs(&result[0], pUtf8Buff, UnicodeBuffSize);\n    setlocale(LC_CTYPE, old_locale);\n#endif\n    result.resize(UnicodeBuffSize);\n\n\n    return result;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_from_unicode_to_multibyte(const amf_wstring& str)\n{\n    amf_string result;\n    if(0 == str.size())\n    {\n        return result;\n    }\n\n    const wchar_t* pwBuff = str.c_str();\n\n#if defined(__ANDROID__)\n    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;\n    result.assign(converter.to_bytes(pwBuff).c_str());\n/*\n    int Utf8BuffSize = str.length();\n    if(0 == Utf8BuffSize)\n    {\n        return result;\n    }\n    Utf8BuffSize += 8; // get some extra space\n    result.resize(Utf8BuffSize);\n\n    mbstate_t mbs;\n    mbrlen(NULL, 0, &mbs);\n\n    Utf8BuffSize = 0;\n    for( int i = 0; i < str.length(); i++)\n    {\n        //MM TODO Android - not implemented\n        //int written = wcrtomb(&result[Utf8BuffSize], pwBuff[i], &mbs);\n        result[Utf8BuffSize] = (char)(pwBuff[i]);\n        int written = 1;\n        // temp replacement\n        Utf8BuffSize += written;\n    }\n    result.resize(Utf8BuffSize);\n*/\n#else\n    amf_size Utf8BuffSize = wcstombs(NULL, pwBuff, 0);\n    if(static_cast<std::size_t>(-1) == Utf8BuffSize)\n    {\n        return result;\n    }\n\n    Utf8BuffSize += 8; // get some extra space\n    result.resize(Utf8BuffSize);\n    Utf8BuffSize = wcstombs(&result[0], pwBuff, Utf8BuffSize);\n    result.resize(Utf8BuffSize);\n#endif\n    return result;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_from_multibyte_to_unicode(const amf_string& str)\n{\n    amf_wstring result;\n    if(0 == str.size())\n    {\n        return result;\n    }\n\n    const char* pUtf8Buff = str.c_str();\n\n\n#if defined(__ANDROID__)\n    //MM on android mbstowcs cannot be used to define length\n    mbstate_t mbs;\n    mbrlen(NULL, 0, &mbs);\n    int len = str.length();\n    const char* pt = pUtf8Buff;\n    int UnicodeBuffSize = 0;\n    while(len > 0)\n    {\n        size_t length = mbrlen (pt, len, &mbs); //MM TODO Android always return 1\n        if((length == 0) || (length > len))\n        {\n            break;\n        }\n        UnicodeBuffSize++;\n        len -= length;\n        pt += length;\n    }\n    UnicodeBuffSize += 8; // get some extra space\n    result.resize(UnicodeBuffSize);\n\n    mbrlen (NULL, 0, &mbs);\n    len = str.length();\n    pt = pUtf8Buff;\n    UnicodeBuffSize = 0;\n    while(len > 0)\n    {\n        size_t length = mbrlen (pt, len, &mbs);\n        if((length == 0) || (length > len))\n        {\n            break;\n        }\n        mbrtowc(&result[UnicodeBuffSize], pt, length, &mbs);     //MM TODO Android always return 1 char\n        UnicodeBuffSize++;\n        len -= length;\n        pt += length;\n    }\n #else\n    amf_size UnicodeBuffSize = mbstowcs(NULL, pUtf8Buff, 0);\n    if(0 == UnicodeBuffSize)\n    {\n        return result;\n    }\n\n    UnicodeBuffSize += 8; // get some extra space\n    result.resize(UnicodeBuffSize);\n    UnicodeBuffSize = mbstowcs(&result[0], pUtf8Buff, UnicodeBuffSize);\n#endif\n    result.resize(UnicodeBuffSize);\n    return result;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_from_string_to_hex_string(const amf_string& str)\n{\n    amf_string ret;\n    char buf[10];\n    for(int i = 0; i < (int)str.length(); i++)\n    {\n        sprintf(buf, \"%02X\", (unsigned char)str[i]);\n        ret += buf;\n    }\n\n    return ret;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_from_hex_string_to_string(const amf_string& str)\n{\n    amf_string ret;\n    char buf[3] = {\n        0, 0, 0\n    };\n    for(int i = 0; i < (int)str.length(); i += 2)\n    {\n        buf[0] = str[i];\n        buf[1] = str[i + 1];\n        int tmp = 0;\n        sscanf(buf, \"%2X\", &tmp);\n        ret += (char)tmp;\n    }\n    return ret;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_string_to_lower(const amf_string& str)\n{\n    std::locale loc;\n    amf_string out = str.c_str();\n    size_t iLen = out.length();\n    for(size_t i = 0; i < iLen; i++)\n    {\n        out[i] = std::tolower (out[i], loc);\n    }\n    return out;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_string_to_lower(const amf_wstring& str)\n{\n    std::locale loc;\n    amf_wstring out = str.c_str();\n    size_t iLen = out.length();\n    for(size_t i = 0; i < iLen; i++)\n    {\n        out[i] = std::tolower (out[i], loc);\n    }\n    return out;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_string_to_upper(const amf_string& str)\n{\n    std::locale loc;\n    amf_string out = str.c_str();\n    size_t iLen = out.length();\n    for(size_t i = 0; i < iLen; i++)\n    {\n        out[i] = std::toupper (out[i], loc);\n    }\n    return out;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_string_to_upper(const amf_wstring& str)\n{\n    std::locale loc;\n    amf_wstring out = str.c_str();\n    size_t iLen = out.length();\n    for(size_t i = 0; i < iLen; i++)\n    {\n        out[i] = std::toupper (out[i], loc);\n    }\n    return out;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_convert_path_to_os_accepted_path(const amf_wstring& path)\n{\n    amf_wstring result = path;\n    amf_wstring::size_type pos = 0;\n    while(pos != amf_string::npos)\n    {\n        pos = result.find(L'/', pos);\n        if(pos == amf_wstring::npos)\n        {\n            break;\n        }\n        result[pos] = PATH_SEPARATOR_WCHAR;\n        pos++;\n    }\n    return result;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_convert_path_to_url_accepted_path(const amf_wstring& path)\n{\n    amf_wstring result = path;\n    amf_wstring::size_type pos = 0;\n    while(pos != amf_string::npos)\n    {\n        pos = result.find(L'\\\\', pos);\n        if(pos == amf_wstring::npos)\n        {\n            break;\n        }\n        result[pos] = L'/';\n        pos++;\n    }\n    return result;\n}\n//----------------------------------------------------------------------------------------\n//------------------------------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_from_unicode_to_url_utf8(const amf_wstring& data, bool bQuery) // converts to UTF8 and replace fobidden symbols\n{\n    amf_string converted = amf_from_unicode_to_utf8(amf_convert_path_to_url_accepted_path(data));\n    // convert all necessary symbols to hex\n    amf_string Result;\n\n    amf_size num = converted.length();\n    char buf[20];\n    for(amf_size i = 0; i < num; i++)\n    {\n        if((converted[i] <= 0x20) || (converted[i] >= 0x7F) ||\n           (bQuery && ( AMF_FORBIDDEN_SYMBOLS.find(converted[i]) != amf_string::npos) ) ||\n           (!bQuery && ( AMF_FORBIDDEN_SYMBOLS_QUERY.find(converted[i]) != amf_string::npos) ))\n        {\n            snprintf(buf, sizeof(buf), \"%%%02X\", (unsigned int)(unsigned char)converted[i]);\n        }\n        else\n        {\n            buf[0] = converted[i];\n            buf[1] = 0;\n        }\n        Result += buf;\n    }\n    return Result;\n}\n//------------------------------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_from_url_utf8_to_unicode(const amf_string& data)\n{\n    amf_string Result;\n    amf_string::size_type pos = 0;\n    while(pos != amf_string::npos)\n    {\n        amf_string::size_type old_pos = pos;\n        pos = data.find('%', pos);\n        if(pos == amf_string::npos)\n        {\n            Result += data.substr(old_pos);\n            break;\n        }\n        if(pos - old_pos > 0)\n        {\n            Result += data.substr(old_pos, pos - old_pos);\n        }\n        char buf[5] = {\n            '0', 'x', 0, 0, 0\n        };\n        buf[2] = data[pos + 1];\n        buf[3] = data[pos + 2];\n        char* ret = NULL;\n\n        Result += (char)strtol(buf, &ret, 16);\n        pos += 3;\n    }\n\n    amf_wstring converted = amf_from_utf8_to_unicode(Result);\n\n    return converted;\n}\n//----------------------------------------------------------------------------------------------\namf_size AMF_STD_CALL amf::amf_string_ci_find(const amf_wstring& left, const amf_wstring& right, amf_size off)\n{\n    amf_wstring _left = amf_string_to_lower(left);\n    amf_wstring _right = amf_string_to_lower(right);\n    return _left.find(_right, off);\n}\n//----------------------------------------------------------------------------------------------\namf_size AMF_STD_CALL amf::amf_string_ci_rfind(const amf_wstring& left, const amf_wstring& right, amf_size off)\n{\n    amf_wstring _left = amf_string_to_lower(left);\n    amf_wstring _right = amf_string_to_lower(right);\n    return _left.rfind(_right, off);\n}\n//----------------------------------------------------------------------------------------------\namf_int AMF_STD_CALL amf::amf_string_ci_compare(const amf_wstring& left, const amf_wstring& right)\n{\n    amf_wstring _left = amf_string_to_lower(left);\n    amf_wstring _right = amf_string_to_lower(right);\n    return _left.compare(_right);\n}\n//----------------------------------------------------------------------------------------------\namf_int AMF_STD_CALL amf::amf_string_ci_compare(const amf_string& left, const amf_string& right)\n{\n    amf_string _left = amf_string_to_lower(left);\n    amf_string _right = amf_string_to_lower(right);\n    return _left.compare(_right);\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_string_format(const wchar_t* format, ...)\n{\n    va_list arglist;\n    va_start(arglist, format);\n    amf_wstring text = amf_string_formatVA(format, arglist);\n    va_end(arglist);\n\n    return text;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_string_format(const char* format, ...)\n{\n    va_list arglist;\n    va_start(arglist, format);\n    amf_string text = amf_string_formatVA(format, arglist);\n    va_end(arglist);\n\n    return text;\n}\n//----------------------------------------------------------------------------------------\namf_wstring AMF_STD_CALL amf::amf_string_formatVA(const wchar_t* format, va_list args)\n{\n#if (defined(__linux) || defined(__APPLE__)) && (!defined(__ANDROID__))\n    //replace %s with %ls\n    amf_wstring text(format);\n    amf_wstring textReplaced;\n    textReplaced.reserve(text.length() * 2);\n    bool percentFlag = false;\n    for(amf_wstring::iterator i = text.begin(); i != text.end(); ++i)\n    {\n        if(percentFlag && (*i == L's'))\n        {\n            textReplaced.push_back(L'l');\n            textReplaced.push_back(L's');\n        }\n        else if(percentFlag && (*i == L'S'))\n        {\n            textReplaced.push_back(L's');\n        }\n        else\n        {\n            textReplaced.push_back(*i);\n        }\n        percentFlag = (*i != L'%') ? false : !percentFlag;\n    }\n\n    format = textReplaced.c_str();\n#endif //#if defined(__linux)\n    va_list argcopy;\n#ifdef _WIN32\n    argcopy = args;\n#else\n    va_copy(argcopy, args);\n#endif\n    int size = vscwprintf(format, argcopy);\n\n    va_end(argcopy);\n\n\n    std::vector<wchar_t> buf(size + 1);\n    wchar_t* pBuf = &buf[0];\n    vswprintf(pBuf, size + 1, format, args);\n    return pBuf;\n}\n//----------------------------------------------------------------------------------------\namf_string AMF_STD_CALL amf::amf_string_formatVA(const char* format, va_list args)\n{\n    va_list argcopy;\n\n#ifdef _WIN32\n    argcopy = args;\n#else\n    va_copy(argcopy, args);\n#endif\n    int size = vscprintf(format, args);\n\n    va_end(argcopy);\n\n    std::vector<char> buf(size + 1);\n    char* pBuf = &buf[0];\n    vsnprintf(pBuf, size + 1, format, args);\n    return pBuf;\n}\n#if (defined(__linux) || defined(__APPLE__)) && !defined(__ANDROID__)\nint vscprintf(const char* format, va_list argptr)\n{\n    char* p_tmp_buf;\n    size_t tmp_buf_size;\n    FILE* fd = open_memstream(&p_tmp_buf, &tmp_buf_size);\n    if(fd == 0)\n    {\n        return -1;\n    }\n    va_list arg_copy;\n    va_copy(arg_copy, argptr);\n    vfprintf(fd, format, arg_copy);\n    va_end(arg_copy);\n    fclose(fd);\n    free(p_tmp_buf);\n    return tmp_buf_size;\n}\n\nint vscwprintf(const wchar_t* format, va_list argptr)\n{\n    wchar_t* p_tmp_buf;\n    size_t tmp_buf_size;\n    FILE* fd = open_wmemstream(&p_tmp_buf, &tmp_buf_size);\n    if(fd == 0)\n    {\n        return -1;\n    }\n    va_list arg_copy;\n    va_copy(arg_copy, argptr);\n    vfwprintf(fd, format, argptr);\n    va_end(arg_copy);\n    fclose(fd);\n    free(p_tmp_buf);\n    return tmp_buf_size;\n}\n#endif\n\n//----------------------------------------------------------------------------------------\nvoid* AMF_STD_CALL amf_alloc(size_t count)\n{\n    return malloc(count);\n}\n//----------------------------------------------------------------------------------------\nvoid AMF_STD_CALL amf_free(void* ptr)\n{\n    free(ptr);\n}\n//----------------------------------------------------------------------------------------\nvoid* AMF_STD_CALL amf_aligned_alloc(size_t count, size_t alignment)\n{\n#if defined(_WIN32)\n    return _aligned_malloc(count, alignment);\n#elif defined (__APPLE__)\n    void* p = nullptr;\n    posix_memalign(&p, alignment, count);\n    return p;\n#elif defined(__linux)\n    return memalign(alignment, count);\n#endif\n}\n//----------------------------------------------------------------------------------------\nvoid AMF_STD_CALL amf_aligned_free(void* ptr)\n{\n#if defined(_WIN32)\n    return _aligned_free(ptr);\n#else\n    return free(ptr);\n#endif\n}\n//----------------------------------------------------------------------------------------\n#if defined (__ANDROID__)\ntemplate <typename CHAR_T>\nstatic bool isOneOf(CHAR_T p_ch, const CHAR_T* p_set)\n{\n    for (const CHAR_T* current = p_set; *current != 0; ++current)\n    {\n        if (*current == p_ch)\n            return true;\n    }\n    return false;\n}\n\nstatic void processWidthAndPrecision(amf_string& p_fmt, va_list& p_args)\n{\n    for (size_t i = 0; i < p_fmt.length(); i++)\n    {\n        if (p_fmt[i] == '*')\n        {\n            int value = va_arg(p_args, int);\n            char valueString[64];\n            sprintf(valueString, \"%d\", value);\n            p_fmt.replace(i, 1, valueString);\n        }\n    }\n}\n\ntypedef size_t(*outputStreamDelegateW)(void* p_context, size_t p_offset, const wchar_t* p_stringToAdd, size_t p_length);\nstatic size_t amf_wprintfCore(outputStreamDelegateW p_outDelegate, void* p_context, const wchar_t* p_fmt, va_list p_args)\n{\n    static const wchar_t formatSpecifiers[] = L\"cCdiouxXeEfgGaAnpsSZ\";\n    bool inFormat = false;\n    const wchar_t* beginCurrentFormat = NULL;\n    amf_wstring::size_type formatLength = 0;\n    amf_wstring currentFormat;\n    amf_wstring currentArgumentString;\n    size_t totalCount = 0;\n\n    for (const wchar_t* fmt = p_fmt; *fmt != L'\\0'; ++fmt)\n    {\n        if (*fmt == L'%')\n        {\n            inFormat = !inFormat;\n            if (inFormat)    //    Beginning of a format substring - fmt points at the opening %\n            {\n                beginCurrentFormat = fmt;    //    Save the pointer to the current format substring\n                formatLength = 0;\n            }\n            else    //    This was a percent character %% - don't bother\n            {\n                beginCurrentFormat = NULL;\n            }\n            currentFormat.clear();\n        }\n        if (inFormat)\n        {\n            ++formatLength;\n\n            if (isOneOf<wchar_t>(*fmt, formatSpecifiers))\n            {    //    end of the format specifier\n                inFormat = false;\n                currentFormat.assign(beginCurrentFormat, formatLength);    //    currentFormat now contains a modified format string for the current parameter\n                amf_string currentFormatMB = amf_from_unicode_to_multibyte(currentFormat.c_str());\n                //processWidthAndPrecision(currentFormatMB, &p_args[0]);    //    This would extract additional arguments for width and precision and replace * with their values\n                for (size_t i = 0; i < currentFormatMB.length(); i++)\n                {\n                    if (currentFormatMB[i] == '*')\n                    {\n                        int value = va_arg(p_args, int);\n                        char valueString[64];\n                        sprintf(valueString, \"%d\", value);\n                        currentFormatMB.replace(i, 1, valueString);\n                    }\n                }\n\n                switch (*fmt)\n                {\n                case L'c':\n                {\n                    wchar_t ch;\n                    switch (*(fmt - 1))\n                    {\n                    case L'h':\n                        ch = static_cast<wchar_t>(va_arg(p_args, int));\n                        break;\n                    case L'l':\n                    case L'w':\n                        ch = va_arg(p_args, unsigned int);\n                        break;\n                    default:\n                        ch = va_arg(p_args, unsigned int);    //    In a wchar_t version of printf %c means wchar_t\n                    }\n                    currentArgumentString = ch;\n                }\n                break;\n                case L'C':\n                {\n                    wchar_t ch;\n                    switch (*(fmt - 1))\n                    {\n                    case L'h':\n                        ch = static_cast<wchar_t>(va_arg(p_args, int));\n                        break;\n                    case L'l':\n                    case L'w':\n                        ch = va_arg(p_args, unsigned int);\n                        break;\n                    default:\n                        ch = static_cast<wchar_t>(va_arg(p_args, int));    //    In a wchar_t version of printf %C means char\n                    }\n                    currentArgumentString = ch;\n                }\n                break;\n                case L's':\n                {\n                    const void* str = va_arg(p_args, const void*);\n                    if (str != NULL)\n                    {\n                        const wchar_t* str_wchar = nullptr;\n                        switch (*(fmt - 1))\n                        {\n                        case L'h':\n                            currentArgumentString = amf_from_utf8_to_unicode(reinterpret_cast<const char*>(str));\n                            str_wchar = currentArgumentString.c_str();\n                            break;\n                        case L'l':\n                        case L'w':\n                            currentArgumentString = str_wchar = reinterpret_cast<const wchar_t*>(str);\n                            break;\n                        default:\n                            currentArgumentString = str_wchar = reinterpret_cast<const wchar_t*>(str);\n                        }\n                    }\n                    else\n                    {\n                        currentArgumentString = L\"(null)\";\n                    }\n                }\n                break;\n                case L'S':\n                {\n                    const void* str = va_arg(p_args, const void*);\n                    if (str != NULL)\n                    {\n                        switch (*(fmt - 1))\n                        {\n                        case (wchar_t)'h':\n                            currentArgumentString = amf_from_utf8_to_unicode(reinterpret_cast<const char*>(str));\n                            break;\n                        case L'l':\n                        case L'w':\n                            currentArgumentString = reinterpret_cast<const wchar_t*>(str);\n                            break;\n                        default:\n                            currentArgumentString = amf_from_utf8_to_unicode(reinterpret_cast<const char*>(str));\n                        }\n                    }\n                    else\n                    {\n                        currentArgumentString = L\"(null)\";\n                    }\n                }\n                break;\n                //    All integer formats\n                case L'i':\n                case L'd':\n                case L'u':\n                case L'o':\n                case L'x':\n                case L'X':\n                {\n                    char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                    switch (*(fmt - 1))\n                    {\n                    case L'l':\n                        if (*(fmt - 2) == L'l')    //    long long\n                        {\n                            sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long long));\n                        }\n                        else\n                        {\n                            sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long));\n                        }\n                        break;\n                    case L'h':\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, int));\n                        break;\n#ifdef _WIN32\n                    case L'I':    //    I is Microsoft-specific\n#else\n                    case L'z':    //    z and t are C99-specific, but seem to be unsupported in VC\n                    case L't':\n#endif\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, size_t));\n                        break;\n                    default:\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, int));\n                        break;\n                    }\n                    currentArgumentString = amf_from_utf8_to_unicode(tempBuffer);\n                }\n                break;\n                //    All floating point formats\n                case L'e':\n                case L'E':\n                case L'f':\n                case L'g':\n                case L'G':\n                case L'a':\n                case L'A':\n                {\n                    char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                    switch (*(fmt - 1))\n                    {\n                    case L'l':\n                    case L'L':\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long double));\n                        break;\n                    default:\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, double));\n                        break;\n                    }\n                    currentArgumentString = amf_from_utf8_to_unicode(tempBuffer);\n                }\n                break;\n                //    Pointer\n                case L'p':\n                {\n                    char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                    sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, const void*));\n                    currentArgumentString = amf_from_utf8_to_unicode(tempBuffer);\n                }\n                break;\n                case L'n':\n                {\n                    int* dest = va_arg(p_args, int*);\n                    *dest = static_cast<int>(totalCount);\n                    currentArgumentString.clear();\n                }\n                break;\n                }\n                size_t length = currentArgumentString.length();\n                if (p_outDelegate != NULL)    // If destination buffer is NULL, just count the characters\n                {\n                    p_outDelegate(p_context, totalCount, currentArgumentString.c_str(), length);\n                }\n                totalCount += length;\n            }    //    if (isOneOf(*fmt, formatSpecifiers))\n        }    // if (inFormat)\n        else\n        {    //    Just copy the character into the output buffer\n            if (p_outDelegate != NULL)    // If destination buffer is NULL, just count the characters\n            {\n                p_outDelegate(p_context, totalCount, fmt, 1);\n            }\n            ++totalCount;\n        }\n    }\n    return totalCount;\n}\n\ntypedef size_t(*outputStreamDelegate)(void* p_context, size_t p_offset, const char* p_stringToAdd, size_t p_length);\nstatic size_t amf_printfCore(outputStreamDelegate p_outDelegate, void* p_context, const char* p_fmt, va_list p_args)\n{\n    static const char formatSpecifiers[] = \"cCdiouxXeEfgGaAnpsSZ\";\n    bool inFormat = false;\n    const char* beginCurrentFormat = NULL;\n    amf_string::size_type formatLength = 0;\n    amf_string currentFormat;\n    amf_string currentArgumentString;\n    size_t totalCount = 0;\n\n    for (const char* fmt = p_fmt; *fmt != '\\0'; ++fmt)\n    {\n        if (*fmt == '%')\n        {\n            inFormat = !inFormat;\n            if (inFormat)    //    Beginning of a format substring - fmt points at the opening %\n            {\n                beginCurrentFormat = fmt;    //    Save the pointer to the current format substring\n                formatLength = 0;\n            }\n            else    //    This was a percent character %% - don't bother\n            {\n                beginCurrentFormat = NULL;\n            }\n            currentFormat.clear();\n        }\n        if (inFormat)\n        {\n            ++formatLength;\n\n            if (isOneOf<char>(*fmt, formatSpecifiers))\n            {    //    end of the format specifier\n                inFormat = false;\n                currentFormat.assign(beginCurrentFormat, formatLength);    //    currentFormat now contains a modified format string for the current parameter\n                amf_string currentFormatMB = currentFormat.c_str();\n                //processWidthAndPrecision(currentFormatMB, &p_args[0]);    //    This would extract additional arguments for width and precision and replace * with their values\n                for (size_t i = 0; i < currentFormatMB.length(); i++)\n                {\n                    if (currentFormatMB[i] == '*')\n                    {\n                        int value = va_arg(p_args, int);\n                        char valueString[64];\n                        sprintf(valueString, \"%d\", value);\n                        currentFormatMB.replace(i, 1, valueString);\n                    }\n                }\n\n                switch (*fmt)\n                {\n                    case 'c':\n                    {\n                        char ch;\n                        switch (*(fmt - 1))\n                        {\n                            case 'h':\n                                ch = static_cast<char>(va_arg(p_args, int));\n                                break;\n                            case 'l':\n                            case 'w':\n                                ch = va_arg(p_args, unsigned int);\n                                break;\n                            default:\n                                ch = va_arg(p_args, unsigned int);    //    In a wchar_t version of printf %c means wchar_t\n                        }\n                        currentArgumentString = ch;\n                    }\n                        break;\n                    case 'C':\n                    {\n                        char ch;\n                        switch (*(fmt - 1))\n                        {\n                            case 'h':\n                                ch = static_cast<char>(va_arg(p_args, int));\n                                break;\n                            case 'l':\n                            case 'w':\n                                ch = va_arg(p_args, unsigned int);\n                                break;\n                            default:\n                                ch = static_cast<char>(va_arg(p_args, int));    //    In a wchar_t version of printf %C means char\n                        }\n                        currentArgumentString = ch;\n                    }\n                        break;\n                    case 's':\n                    {\n                        const void* str = va_arg(p_args, const void*);\n                        if (str != NULL)\n                        {\n                            switch (*(fmt - 1))\n                            {\n                                case 'h':\n                                    currentArgumentString = reinterpret_cast<const char*>(str);\n                                    break;\n                                case 'l':\n                                case 'w':\n                                    currentArgumentString = amf_from_unicode_to_utf8(reinterpret_cast<const wchar_t*>(str));\n                                    break;\n                                default:\n                                    currentArgumentString = reinterpret_cast<const char*>(str);\n                            }\n                        }\n                        else\n                        {\n                            currentArgumentString = \"(null)\";\n                        }\n                    }\n                        break;\n                    case L'S':\n                    {\n                        const void* str = va_arg(p_args, const void*);\n                        if (str != NULL)\n                        {\n                            switch (*(fmt - 1))\n                            {\n                                case 'h':\n                                    currentArgumentString = reinterpret_cast<const char*>(str);\n                                    break;\n                                case 'l':\n                                case 'w':\n                                    currentArgumentString = amf_from_unicode_to_utf8(reinterpret_cast<const wchar_t*>(str));\n                                    break;\n                                default:\n                                    currentArgumentString = amf_from_unicode_to_utf8(reinterpret_cast<const wchar_t*>(str));\n                            }\n                        }\n                        else\n                        {\n                            currentArgumentString = \"(null)\";\n                        }\n                    }\n                        break;\n                        //    All integer formats\n                    case L'i':\n                    case L'd':\n                    case L'u':\n                    case L'o':\n                    case L'x':\n                    case L'X':\n                    {\n                        char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                        switch (*(fmt - 1))\n                        {\n                            case L'l':\n                                if (*(fmt - 1) == L'l')    //    long long\n                                {\n                                    sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long long));\n                                }\n                                else\n                                {\n                                    sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long));\n                                }\n                                break;\n                            case L'h':\n                                sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, int));\n                                break;\n#ifdef _WIN32\n                                case L'I':    //    I is Microsoft-specific\n#else\n                            case L'z':    //    z and t are C99-specific, but seem to be unsupported in VC\n                            case L't':\n#endif\n                                sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, size_t));\n                                break;\n                            default:\n                                sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, int));\n                                break;\n                        }\n                        currentArgumentString = tempBuffer;\n                    }\n                        break;\n                        //    All floating point formats\n                    case L'e':\n                    case L'E':\n                    case L'f':\n                    case L'g':\n                    case L'G':\n                    case L'a':\n                    case L'A':\n                    {\n                        char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                        switch (*(fmt - 1))\n                        {\n                            case L'l':\n                            case L'L':\n                                sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, long double));\n                                break;\n                            default:\n                                sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, double));\n                                break;\n                        }\n                        currentArgumentString = tempBuffer;\n                    }\n                        break;\n                        //    Pointer\n                    case L'p':\n                    {\n                        char tempBuffer[64];    //    64 bytes should be enough for any numeric format\n                        sprintf(tempBuffer, currentFormatMB.c_str(), va_arg(p_args, const void*));\n                        currentArgumentString = tempBuffer;\n                    }\n                        break;\n                    case L'n':\n                    {\n                        int* dest = va_arg(p_args, int*);\n                        *dest = static_cast<int>(totalCount);\n                        currentArgumentString.clear();\n                    }\n                        break;\n                }\n                size_t length = currentArgumentString.length();\n                if (p_outDelegate != NULL)    // If destination buffer is NULL, just count the characters\n                {\n                    p_outDelegate(p_context, totalCount, currentArgumentString.c_str(), length);\n                }\n                totalCount += length;\n            }    //    if (isOneOf(*fmt, formatSpecifiers))\n        }    // if (inFormat)\n        else\n        {    //    Just copy the character into the output buffer\n            if (p_outDelegate != NULL)    // If destination buffer is NULL, just count the characters\n            {\n                p_outDelegate(p_context, totalCount, fmt, 1);\n            }\n            ++totalCount;\n        }\n    }\n    return totalCount;\n}\n\n\ntypedef struct {\n    wchar_t*    m_Buf;\n    size_t        m_Size;\n} MemBufferContextW;\n\ntypedef struct {\n    char*    m_Buf;\n    size_t        m_Size;\n} MemBufferContext;\n\nstatic size_t writeToMem(void* p_context, size_t p_offset, const wchar_t* p_stringToAdd, size_t p_length)\n{\n    wchar_t* buf = &(reinterpret_cast<MemBufferContextW*>(p_context)->m_Buf[p_offset]);\n    size_t bufSize = reinterpret_cast<MemBufferContextW*>(p_context)->m_Size - 1;    //    -1 to accommodate a trailing '\\0'\n    for (int i = 0; i < p_length && i < bufSize; i++)\n    {\n        *buf++ = p_stringToAdd[i];\n    }\n    return p_length;\n}\n\nstatic size_t writeToFile(void* p_context, size_t, const wchar_t* p_stringToAdd, size_t p_length)\n{\n    return fwrite(p_stringToAdd, sizeof(wchar_t), p_length, reinterpret_cast<FILE*>(p_context));\n}\n\nextern \"C\"\n{\n    int vswprintf(wchar_t* p_buf, size_t p_size, const wchar_t* p_fmt, va_list p_args)\n    {\n        MemBufferContextW context = { p_buf, p_size };\n        int bytesWritten = (int)amf_wprintfCore(writeToMem, &context, p_fmt, p_args);\n        p_buf[bytesWritten] = L'\\0';\n        return bytesWritten;\n    }\n\n    int wsprintf(wchar_t* p_buf, const wchar_t* p_fmt, ...)\n    {\n        va_list argptr;\n        va_start(argptr, p_fmt);\n        return vswprintf(p_buf, static_cast<size_t>(-1), p_fmt, argptr);\n    }\n\n    int swprintf(wchar_t* p_buf, size_t p_size, const wchar_t* p_fmt, ...)\n    {\n        va_list argptr;\n        va_start(argptr, p_fmt);\n        return vswprintf(p_buf, p_size, p_fmt, argptr);\n    }\n\n    int vfwprintf(FILE* p_stream, const wchar_t* p_fmt, va_list p_args)\n    {\n        return (int)amf_wprintfCore(writeToFile, p_stream, p_fmt, p_args);\n    }\n\n    int fwprintf(FILE* p_stream, const wchar_t* p_fmt, ...)\n    {\n        va_list argptr;\n        va_start(argptr, p_fmt);\n        return vfwprintf(p_stream, p_fmt, argptr);\n    }\n\n#if !defined(__APPLE__)\n    int vscwprintf(const wchar_t* p_fmt, va_list p_args)\n    {\n        return (int)amf_wprintfCore(NULL, NULL, p_fmt, p_args);\n    }\n\n    int vscprintf(const char* p_fmt, va_list p_args)\n    {\n        return (int)amf_printfCore(NULL, NULL, p_fmt, p_args);\n    }\n#endif\n}\n#endif\n\n//--------------------------------------------------------------------------------\n// Mac doens't have _wcsicmp(0 - poor man implementation\n//--------------------------------------------------------------------------------\n#ifdef __APPLE__\nextern \"C\"\n{\n    int _wcsicmp(const wchar_t* s1, const wchar_t* s2)\n    {\n        amf_wstring low_s1 = s1;\n        amf_wstring low_s2 = s2;\n        std::transform(low_s1.begin(), low_s1.end(), low_s1.begin(), ::tolower);\n        std::transform(low_s2.begin(), low_s2.end(), low_s2.begin(), ::tolower);\n\n        return wcscmp(low_s1.c_str(), low_s2.c_str());\n    }\n}\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/AMFSTL.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_AMFSTL_h\n#define AMF_AMFSTL_h\n\n#pragma once\n\n#if defined(__GNUC__)\n    //disable gcc warinings on STL code\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Weffc++\"\n    #include <memory>  //default stl allocator\n#else\n\n    #include <xmemory>  //default stl allocator\n#endif\n\n#include <algorithm>\n#include <string>\n#include <vector>\n#include <list>\n#include <deque>\n#include <queue>\n#include <map>\n#include <set>\n\n#include \"../include/core/Interface.h\"\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif\n    // allocator\n    void* AMF_STD_CALL amf_alloc(amf_size count);\n    void AMF_STD_CALL amf_free(void* ptr);\n    void* AMF_STD_CALL amf_aligned_alloc(size_t count, size_t alignment);\n    void AMF_STD_CALL amf_aligned_free(void* ptr);\n#if defined(__cplusplus)\n}\n#endif\n\nnamespace amf\n{\n#pragma warning(push)\n\n#pragma warning(disable: 4996)    // was declared deprecated\n    //-------------------------------------------------------------------------------------------------\n    // STL allocator redefined - will allocate all memory in \"C\" runtime of Common.DLL\n    //-------------------------------------------------------------------------------------------------\n    template<class _Ty>\n    class amf_allocator : public std::allocator<_Ty>\n    {\n    public:\n        amf_allocator() : std::allocator<_Ty>()\n        {}\n        amf_allocator(const amf_allocator<_Ty>& rhs) : std::allocator<_Ty>(rhs)\n        {}\n        template<class _Other> amf_allocator(const amf_allocator<_Other>& rhs) : std::allocator<_Ty>(rhs)\n        {}\n        template<class _Other> struct rebind // convert an allocator<_Ty> to an allocator <_Other>\n        {\n            typedef amf_allocator<_Other> other;\n        };\n        void deallocate(_Ty* const _Ptr, const size_t _Count)\n        {\n            _Count;\n            amf_free((void*)_Ptr);\n        }\n        _Ty* allocate(const size_t _Count, const void* = static_cast<const void*>(0))\n        { // allocate array of _Count el ements\n            return static_cast<_Ty*>(amf_alloc(_Count * sizeof(_Ty)));\n        }\n    };\n\n\n\n\n    //-------------------------------------------------------------------------------------------------\n    // STL container templates with changed memory allocation\n    //-------------------------------------------------------------------------------------------------\n    template<class _Ty>\n    class amf_vector\n        : public std::vector<_Ty, amf_allocator<_Ty> >\n    {\n    public:\n    typedef std::vector<_Ty, amf_allocator<_Ty> > _base;\n\n    amf_vector() : _base() {}\n    explicit amf_vector(size_t _Count) : _base(_Count) {} //MM GCC has strange compile error. to get around replaced size_type with size_t\n    amf_vector(size_t _Count, const _Ty& _Val) : _base(_Count,_Val) {}\n    };\n\n    template<class _Ty>\n    class amf_list\n        : public std::list<_Ty, amf_allocator<_Ty> >\n    {};\n\n    template<class _Ty>\n    class amf_deque\n        : public std::deque<_Ty, amf_allocator<_Ty> >\n    {};\n\n    template<class _Ty>\n    class amf_queue\n        : public std::queue<_Ty, amf_deque<_Ty> >\n    {};\n\n    template<class _Kty, class _Ty, class _Pr = std::less<_Kty> >\n    class amf_map\n        : public std::map<_Kty, _Ty, _Pr, amf_allocator<std::pair<const _Kty, _Ty>> >\n    {};\n\n    template<class _Kty, class _Pr = std::less<_Kty> >\n    class amf_set\n        : public std::set<_Kty, _Pr, amf_allocator<_Kty> >\n    {};\n\n    template<class _Ty>\n    class amf_limited_deque\n        : public amf_deque<_Ty> // circular queue of pointers to blocks\n    {\n    public:\n        typedef amf_deque<_Ty> _base;\n        amf_limited_deque(size_t size_limit) : _base(), _size_limit(size_limit)\n        {    // construct empty deque\n        }\n        size_t size_limit()\n        {\n            return _size_limit;\n        }\n\n        void set_size_limit(size_t size_limit)\n        {\n            _size_limit = size_limit;\n            while(_base::size() > _size_limit)\n            {\n                _base::pop_front();\n            }\n        }\n\n        _Ty push_front(const _Ty& _Val)\n        {    // insert element at beginning\n            _Ty ret;\n            if(_size_limit > 0)\n            {\n                _base::push_front(_Val);\n                if(_base::size() > _size_limit)\n                {\n                    ret = _base::back();\n                    _base::pop_back();\n                }\n            }\n            return ret;\n        }\n        void push_front_ex(const _Ty& _Val)\n        {    // insert element at beginning\n            _base::push_front(_Val);\n        }\n\n        _Ty push_back(const _Ty& _Val)\n        {    // insert element at beginning\n            _Ty ret;\n            if(_size_limit > 0)\n            {\n                _base::push_back(_Val);\n                if(_base::size() > _size_limit)\n                {\n                    ret = _base::front();\n                    _base::pop_front();\n                }\n            }\n            return ret;\n        }\n\n    protected:\n        size_t _size_limit;\n    };\n#pragma warning(pop)\n    //---------------------------------------------------------------\n#if defined(__GNUC__)\n    //disable gcc warinings on STL code\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Weffc++\"\n#endif\n\n    template<class _Interf>\n    class AMFInterfacePtr_TAdapted : public AMFInterfacePtr_T<_Interf>\n    {\n    public:\n        AMFInterfacePtr_TAdapted* operator&()\n        {\n            return this;\n        }\n\n        AMFInterfacePtr_TAdapted()\n            : AMFInterfacePtr_T<_Interf>()\n        {}\n\n        AMFInterfacePtr_TAdapted(_Interf* pOther)\n            : AMFInterfacePtr_T<_Interf>(pOther)\n        {}\n\n        AMFInterfacePtr_TAdapted(const AMFInterfacePtr_T<_Interf>& other)\n            : AMFInterfacePtr_T<_Interf>(other)\n        {}\n    };\n\n    template<class _Interf>\n    class amf_vector<AMFInterfacePtr_T<_Interf> >\n        : public std::vector<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >\n    {\n    public:\n        typedef AMFInterfacePtr_T<_Interf>& reference;\n        typedef std::vector<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > > baseclass;\n        reference operator[](size_t n)\n        {\n            return baseclass::operator[](n);\n        }\n    };\n\n    template<class _Interf>\n    class amf_deque<AMFInterfacePtr_T<_Interf> >\n        : public std::deque<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >\n    {};\n\n    template<class _Interf>\n    class amf_list<AMFInterfacePtr_T<_Interf> >\n        : public std::list<AMFInterfacePtr_TAdapted<_Interf>, amf_allocator<AMFInterfacePtr_TAdapted<_Interf> > >\n    {};\n#if defined(__GNUC__)\n    // restore gcc warnings\n    #pragma GCC diagnostic pop\n#endif\n}\n//-------------------------------------------------------------------------------------------------\n// string classes\n//-------------------------------------------------------------------------------------------------\n\ntypedef std::basic_string<char, std::char_traits<char>, amf::amf_allocator<char> > amf_string;\ntypedef std::basic_string<wchar_t, std::char_traits<wchar_t>, amf::amf_allocator<wchar_t> > amf_wstring;\n\ntemplate <class TAmfString>\nstd::size_t amf_string_hash(TAmfString const& s) noexcept\n{\n#if defined(_WIN64) || defined(__x86_64__)\n    constexpr size_t fnvOffsetBasis = 14695981039346656037ULL;\n    constexpr size_t fnvPrime = 1099511628211ULL;\n#else // defined(_WIN64) || defined(__x86_64__)\n    constexpr size_t fnvOffsetBasis = 2166136261U;\n    constexpr size_t fnvPrime = 16777619U;\n#endif // defined(_WIN64) || defined(__x86_64__)\n\n    const unsigned char* const pStr = reinterpret_cast<const unsigned char*>(s.c_str());\n    const size_t count = s.size() * sizeof(typename TAmfString::value_type);\n    size_t value = fnvOffsetBasis;\n    for (size_t i = 0; i < count; ++i)\n    {\n        value ^= static_cast<size_t>(pStr[i]);\n        value *= fnvPrime;\n    }\n    return value;\n}\n\ntemplate<>\nstruct std::hash<amf_wstring>\n{\n    std::size_t operator()(amf_wstring const& s) const noexcept\n    {\n        return amf_string_hash<amf_wstring>(s);\n    }\n};\n\ntemplate<>\nstruct std::hash<amf_string>\n{\n    std::size_t operator()(amf_string const& s) const noexcept\n    {\n        return amf_string_hash<amf_string>(s);\n    }\n};\n\nnamespace amf\n{\n    //-------------------------------------------------------------------------------------------------\n    // string conversion\n    //-------------------------------------------------------------------------------------------------\n    amf_string AMF_STD_CALL amf_from_unicode_to_utf8(const amf_wstring& str);\n    amf_wstring AMF_STD_CALL amf_from_utf8_to_unicode(const amf_string& str);\n    amf_string AMF_STD_CALL amf_from_unicode_to_multibyte(const amf_wstring& str);\n    amf_wstring AMF_STD_CALL amf_from_multibyte_to_unicode(const amf_string& str);\n    amf_string AMF_STD_CALL amf_from_string_to_hex_string(const amf_string& str);\n    amf_string AMF_STD_CALL amf_from_hex_string_to_string(const amf_string& str);\n\n    amf_string AMF_STD_CALL amf_string_to_lower(const amf_string& str);\n    amf_wstring AMF_STD_CALL amf_string_to_lower(const amf_wstring& str);\n    amf_string AMF_STD_CALL amf_string_to_upper(const amf_string& str);\n    amf_wstring AMF_STD_CALL amf_string_to_upper(const amf_wstring& str);\n\n    amf_string AMF_STD_CALL amf_from_unicode_to_url_utf8(const amf_wstring& data, bool bQuery = false); // converts to UTF8 and replace fobidden symbols\n    amf_wstring AMF_STD_CALL amf_from_url_utf8_to_unicode(const amf_string& data);\n\n    amf_wstring AMF_STD_CALL amf_convert_path_to_os_accepted_path(const amf_wstring& path);\n    amf_wstring AMF_STD_CALL amf_convert_path_to_url_accepted_path(const amf_wstring& path);\n\n    //-------------------------------------------------------------------------------------------------\n    // string helpers\n    //-------------------------------------------------------------------------------------------------\n    amf_wstring AMF_STD_CALL amf_string_format(const wchar_t* format, ...);\n    amf_string AMF_STD_CALL amf_string_format(const char* format, ...);\n\n    amf_wstring AMF_STD_CALL amf_string_formatVA(const wchar_t* format, va_list args);\n    amf_string AMF_STD_CALL amf_string_formatVA(const char* format, va_list args);\n\n    amf_int AMF_STD_CALL amf_string_ci_compare(const amf_wstring& left, const amf_wstring& right);\n    amf_int AMF_STD_CALL amf_string_ci_compare(const amf_string& left, const amf_string& right);\n\n    amf_size AMF_STD_CALL amf_string_ci_find(const amf_wstring& left, const amf_wstring& right, amf_size off = 0);\n    amf_size AMF_STD_CALL amf_string_ci_rfind(const amf_wstring& left, const amf_wstring& right, amf_size off = amf_wstring::npos);\n    //-------------------------------------------------------------------------------------------------\n} // namespace amf\n\n\n\n\n#if defined(__GNUC__)\n    // restore gcc warnings\n    #pragma GCC diagnostic pop\n#endif\n\n#endif // AMF_AMFSTL_h\n\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/ByteArray.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_ByteArray_h\n#define AMF_ByteArray_h\n\n\n#pragma once\n#include \"../include/core/Platform.h\"\n#define    INIT_ARRAY_SIZE 1024\n#define    ARRAY_MAX_SIZE (1LL << 60LL) // extremely large maximum size\n//------------------------------------------------------------------------\nclass AMFByteArray\n{\nprotected:\n    amf_uint8        *m_pData;\n    amf_size         m_iSize;\n    amf_size         m_iMaxSize;\npublic:\n    AMFByteArray() : m_pData(0), m_iSize(0), m_iMaxSize(0)\n    {\n    }\n    AMFByteArray(const AMFByteArray &other) : m_pData(0), m_iSize(0), m_iMaxSize(0)\n    {\n        *this = other;\n    }\n    AMFByteArray(amf_size num) : m_pData(0), m_iSize(0), m_iMaxSize(0)\n    {\n        SetSize(num);\n    }\n    virtual ~AMFByteArray()\n    {\n        if (m_pData != 0)\n        {\n            delete[] m_pData;\n        }\n    }\n    void  SetSize(amf_size num)\n    {\n        if (num == m_iSize)\n        {\n            return;\n        }\n        if (num < m_iSize)\n        {\n            memset(m_pData + num, 0, m_iMaxSize - num);\n        }\n        else if (num > m_iMaxSize)\n        {\n            // This is done to prevent the following error from surfacing\n            // for the pNewData allocation on some compilers:\n            //     -Werror=alloc-size-larger-than=\n            amf_size newSize = (num / INIT_ARRAY_SIZE) * INIT_ARRAY_SIZE + INIT_ARRAY_SIZE;\n            if (newSize > ARRAY_MAX_SIZE)\n            {\n                return;\n            }\n            m_iMaxSize = newSize;\n\n            amf_uint8 *pNewData = new amf_uint8[m_iMaxSize];\n            memset(pNewData, 0, m_iMaxSize);\n            if (m_pData != NULL)\n            {\n                memcpy(pNewData, m_pData, m_iSize);\n                delete[] m_pData;\n            }\n            m_pData = pNewData;\n        }\n        m_iSize = num;\n    }\n    void Copy(const AMFByteArray &old)\n    {\n        if (m_iMaxSize < old.m_iSize)\n        {\n            m_iMaxSize = old.m_iMaxSize;\n            if (m_pData != NULL)\n            {\n                delete[] m_pData;\n            }\n            m_pData = new amf_uint8[m_iMaxSize];\n            memset(m_pData, 0, m_iMaxSize);\n        }\n        memcpy(m_pData, old.m_pData, old.m_iSize);\n        m_iSize = old.m_iSize;\n    }\n    amf_uint8    operator[] (amf_size iPos) const\n    {\n        return m_pData[iPos];\n    }\n    amf_uint8&    operator[] (amf_size iPos)\n    {\n        return m_pData[iPos];\n    }\n    AMFByteArray&    operator=(const AMFByteArray &other)\n    {\n        SetSize(other.GetSize());\n        if (GetSize() > 0)\n        {\n            memcpy(GetData(), other.GetData(), GetSize());\n        }\n        return *this;\n    }\n    amf_uint8 *GetData() const { return m_pData; }\n    amf_size GetSize() const { return m_iSize; }\n};\n#endif // AMF_ByteArray_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/CPUCaps.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n//\n// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include <iostream>\n#include <vector>\n#include <bitset>\n#include <array>\n#include <string>\n#include <cstring>\n\n#if defined(_WIN32)\n#include <intrin.h>\n#else\n#include <stdint.h>\n#endif\n\nclass InstructionSet\n{\n\t// forward declarations\n\tclass InstructionSet_Internal;\n\npublic:\n\t// getters\n\tstatic std::string Vendor(void) { return CPU_Rep.vendor_; }\n\tstatic std::string Brand(void) { return CPU_Rep.brand_; }\n\n\tstatic bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; }\n\tstatic bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; }\n\tstatic bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; }\n\tstatic bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; }\n\tstatic bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; }\n\tstatic bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; }\n\tstatic bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; }\n\tstatic bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; }\n\tstatic bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; }\n\tstatic bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; }\n\tstatic bool AES(void) { return CPU_Rep.f_1_ECX_[25]; }\n\tstatic bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; }\n\tstatic bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; }\n\tstatic bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; }\n\tstatic bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; }\n\tstatic bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; }\n\n\tstatic bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; }\n\tstatic bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; }\n\tstatic bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; }\n\tstatic bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; }\n\tstatic bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; }\n\tstatic bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; }\n\tstatic bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; }\n\tstatic bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; }\n\tstatic bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; }\n\n\tstatic bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; }\n\tstatic bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; }\n\tstatic bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; }\n\tstatic bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; }\n\tstatic bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; }\n\tstatic bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; }\n\tstatic bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; }\n\tstatic bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; }\n\tstatic bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; }\n\tstatic bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; }\n\tstatic bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; }\n\tstatic bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; }\n\tstatic bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; }\n\tstatic bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; }\n\tstatic bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; }\n\tstatic bool AVX512BW(void) { return CPU_Rep.f_7_EBX_[30]; }\n\tstatic bool AVX512VL(void) { return CPU_Rep.f_7_EBX_[31]; }\n\n\tstatic bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; }\n\n\tstatic bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; }\n\tstatic bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; }\n\tstatic bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; }\n\tstatic bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; }\n\tstatic bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; }\n\tstatic bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; }\n\n\tstatic bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; }\n\tstatic bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; }\n\tstatic bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; }\n\tstatic bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; }\n\tstatic bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }\n\nprivate:\n\tstatic const InstructionSet_Internal CPU_Rep;\n\n\tclass InstructionSet_Internal\n\t{\n\tprotected:\n\t\tvoid GetCpuID\n\t\t(\n\t\t\tint32_t registers[4], //out\n\t\t\tint32_t functionID,\n\t\t\tint32_t subfunctionID   = 0\n\t\t)\n\t\t{\n\t\t#ifdef _WIN32\n\t\t\tif(!subfunctionID)\n\t\t\t{\n\t\t\t\t__cpuid((int *)registers, (int)functionID);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t__cpuidex((int *)registers, (int)functionID, subfunctionID);\n\t\t\t}\n\t\t#else\n\n\t\t\tasm volatile\n\t\t\t(\n\t\t\t\t\"cpuid\":\n\t\t\t\t\"=a\" (registers[0]),\n\t\t\t\t\"=b\" (registers[1]),\n\t\t\t\t\"=c\" (registers[2]),\n\t\t\t\t\"=d\" (registers[3]):\n\t\t\t\t\"a\" (functionID),\n\t\t\t\t\"c\" (subfunctionID)\n\t\t\t);\n\n\t\t#endif\n\t\t}\n\tpublic:\n\t\tInstructionSet_Internal()\n\t\t\t: nIds_( 0 ),\n\t\t\tnExIds_( 0 ),\n\t\t\tisIntel_( false ),\n\t\t\tisAMD_( false ),\n\t\t\tf_1_ECX_( 0 ),\n\t\t\tf_1_EDX_( 0 ),\n\t\t\tf_7_EBX_( 0 ),\n\t\t\tf_7_ECX_( 0 ),\n\t\t\tf_81_ECX_( 0 ),\n\t\t\tf_81_EDX_( 0 )\n\t\t{\n\t\t\t//int cpuInfo[4] = {-1};\n\t\t\tstd::array<int, 4> cpui;\n\n\t\t\t// Calling __cpuid with 0x0 as the function_id argument\n\t\t\t// gets the number of the highest valid function ID.\n\n\t\t\t//todo: verify\n\t\t\t//__cpuid(cpui.data(), 0);\n\t\t\tGetCpuID(cpui.data(), 0);\n\n\t\t\tnIds_ = cpui[0];\n\n\t\t\tfor (int i = 0; i <= nIds_; ++i)\n\t\t\t{\n\t\t\t\t//todo: verify\n\t\t\t\t//__cpuidex(cpui.data(), i, 0);\n\t\t\t\tGetCpuID(cpui.data(), i, 0);\n\n\t\t\t\tdata_.push_back(cpui);\n\t\t\t}\n\n\t\t\t// Capture vendor string\n\t\t\tchar vendor[0x20];\n\t\t\tstd::memset(vendor, 0, sizeof(vendor));\n\t\t\t*reinterpret_cast<int*>(vendor) = data_[0][1];\n\t\t\t*reinterpret_cast<int*>(vendor + 4) = data_[0][3];\n\t\t\t*reinterpret_cast<int*>(vendor + 8) = data_[0][2];\n\t\t\tvendor_ = vendor;\n\t\t\tif (vendor_ == \"GenuineIntel\")\n\t\t\t{\n\t\t\t\tisIntel_ = true;\n\t\t\t}\n\t\t\telse if (vendor_ == \"AuthenticAMD\")\n\t\t\t{\n\t\t\t\tisAMD_ = true;\n\t\t\t}\n\n\t\t\t// load bitset with flags for function 0x00000001\n\t\t\tif (nIds_ >= 1)\n\t\t\t{\n\t\t\t\tf_1_ECX_ = data_[1][2];\n\t\t\t\tf_1_EDX_ = data_[1][3];\n\t\t\t}\n\n\t\t\t// load bitset with flags for function 0x00000007\n\t\t\tif (nIds_ >= 7)\n\t\t\t{\n\t\t\t\tf_7_EBX_ = data_[7][1];\n\t\t\t\tf_7_ECX_ = data_[7][2];\n\t\t\t}\n\n\t\t\t// Calling __cpuid with 0x80000000 as the function_id argument\n\t\t\t// gets the number of the highest valid extended ID.\n\t\t\t//todo: verify\n\t\t\t//__cpuid(cpui.data(), 0x80000000);\n\t\t\tGetCpuID(cpui.data(), 0x80000000);\n\n\t\t\tnExIds_ = cpui[0];\n\n\t\t\tchar brand[0x40];\n\t\t\tmemset(brand, 0, sizeof(brand));\n\n\t\t\tfor (int i = 0x80000000; i <= nExIds_; ++i)\n\t\t\t{\n\t\t\t\t//todo: verify\n\t\t\t\t//__cpuidex(cpui.data(), i, 0);\n\t\t\t\tGetCpuID(cpui.data(), i, 0);\n\n\t\t\t\textdata_.push_back(cpui);\n\t\t\t}\n\n\t\t\t// load bitset with flags for function 0x80000001\n\t\t\tif (nExIds_ >= 0x80000001)\n\t\t\t{\n\t\t\t\tf_81_ECX_ = extdata_[1][2];\n\t\t\t\tf_81_EDX_ = extdata_[1][3];\n\t\t\t}\n\n\t\t\t// Interpret CPU brand string if reported\n\t\t\tif (nExIds_ >= 0x80000004)\n\t\t\t{\n\t\t\t\tmemcpy(brand, extdata_[2].data(), sizeof(cpui));\n\t\t\t\tmemcpy(brand + 16, extdata_[3].data(), sizeof(cpui));\n\t\t\t\tmemcpy(brand + 32, extdata_[4].data(), sizeof(cpui));\n\t\t\t\tbrand_ = brand;\n\t\t\t}\n\t\t};\n\n\t\tvirtual ~InstructionSet_Internal()\n\t\t{\n\t\t\tint i = 0;\n\t\t\t++i;\n\t\t}\n\n\t\tint nIds_;\n\t\tint nExIds_;\n\t\tstd::string vendor_;\n\t\tstd::string brand_;\n\t\tbool isIntel_;\n\t\tbool isAMD_;\n\t\tstd::bitset<32> f_1_ECX_;\n\t\tstd::bitset<32> f_1_EDX_;\n\t\tstd::bitset<32> f_7_EBX_;\n\t\tstd::bitset<32> f_7_ECX_;\n\t\tstd::bitset<32> f_81_ECX_;\n\t\tstd::bitset<32> f_81_EDX_;\n\t\tstd::vector<std::array<int, 4>> data_;\n\t\tstd::vector<std::array<int, 4>> extdata_;\n\t};\n};\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/CurrentTimeImpl.cpp",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"CurrentTimeImpl.h\"\n\nnamespace amf\n{\n\n\t//-------------------------------------------------------------------------------------------------\n\tAMFCurrentTimeImpl::AMFCurrentTimeImpl()\n\t\t: m_timeOfFirstCall(-1)\n\t{\n\t}\n\n\t//-------------------------------------------------------------------------------------------------\n\tAMFCurrentTimeImpl::~AMFCurrentTimeImpl()\n\t{\n\t\tm_timeOfFirstCall = -1;\n\t}\n\n\t//-------------------------------------------------------------------------------------------------\n\tamf_pts AMF_STD_CALL AMFCurrentTimeImpl::Get()\n\t{\n\t\tamf::AMFLock lock(&m_sync);\n\n\t\t// We want pts time to start at 0 and subsequent\n\t\t// times to be relative to that\n\t\tif (m_timeOfFirstCall < 0)\n\t\t{\n\t\t\tm_timeOfFirstCall = amf_high_precision_clock();\n\t\t\treturn 0;\n\t\t}\n\t\treturn (amf_high_precision_clock() - m_timeOfFirstCall); // In nanoseconds\n\t}\n\n\t//-------------------------------------------------------------------------------------------------\n\tvoid AMF_STD_CALL AMFCurrentTimeImpl::Reset()\n\t{\n\t\tm_timeOfFirstCall = -1;\n\t}\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/CurrentTimeImpl.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_CurrentTimeImpl_h\n#define AMF_CurrentTimeImpl_h\n\n#include \"../include/core/CurrentTime.h\"\n#include \"InterfaceImpl.h\"\n#include \"Thread.h\"\n\nnamespace amf\n{\n\nclass AMFCurrentTimeImpl : public AMFInterfaceImpl<AMFCurrentTime>\n{\npublic:\n\tAMFCurrentTimeImpl();\n\t~AMFCurrentTimeImpl();\n\n\tAMF_BEGIN_INTERFACE_MAP\n\t\tAMF_INTERFACE_ENTRY(AMFCurrentTime)\n\tAMF_END_INTERFACE_MAP\n\n\tvirtual amf_pts AMF_STD_CALL Get();\n\n\tvirtual void AMF_STD_CALL Reset();\n\nprivate:\n\tamf_pts\t\t\t\t\t\t\t\t\tm_timeOfFirstCall;\n\tmutable AMFCriticalSection\t\t\t\tm_sync;\n};\n\n//----------------------------------------------------------------------------------------------\n// smart pointer\n//----------------------------------------------------------------------------------------------\ntypedef AMFInterfacePtr_T<AMFCurrentTime> AMFCurrentTimePtr;\n//----------------------------------------------------------------------------------------------}\n}\n#endif // AMF_CurrentTimeImpl_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStream.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n ***************************************************************************************************\n * @file  DataStream.h\n * @brief AMFDataStream declaration\n ***************************************************************************************************\n */\n#ifndef AMF_DataStream_h\n#define AMF_DataStream_h\n#pragma once\n\n#include \"../include/core/Interface.h\"\n\nnamespace amf\n{\n    // currently supports only\n    // file://\n    // memory://\n\n    // eventually can be extended with:\n    // rtsp://\n    // rtmp://\n    // http://\n    // etc\n\n    //----------------------------------------------------------------------------------------------\n    enum AMF_STREAM_OPEN\n    {\n        AMFSO_READ              = 0,\n        AMFSO_WRITE             = 1,\n        AMFSO_READ_WRITE        = 2,\n        AMFSO_APPEND            = 3,\n    };\n    //----------------------------------------------------------------------------------------------\n    enum AMF_FILE_SHARE\n    {\n        AMFFS_EXCLUSIVE         = 0,\n        AMFFS_SHARE_READ        = 1,\n        AMFFS_SHARE_WRITE       = 2,\n        AMFFS_SHARE_READ_WRITE  = 3,\n    };\n    //----------------------------------------------------------------------------------------------\n    enum AMF_SEEK_ORIGIN\n    {\n        AMF_SEEK_BEGIN          = 0,\n        AMF_SEEK_CURRENT        = 1,\n        AMF_SEEK_END            = 2,\n    };\n    //----------------------------------------------------------------------------------------------\n    // AMFDataStream interface\n    //----------------------------------------------------------------------------------------------\n    class AMF_NO_VTABLE AMFDataStream : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xdb08fe70, 0xb743, 0x4c26, 0xb2, 0x77, 0xa5, 0xc8, 0xe8, 0x14, 0xda, 0x4)\n\n        // interface\n        virtual AMF_RESULT          AMF_STD_CALL Open(const wchar_t* pFileUrl, AMF_STREAM_OPEN eOpenType, AMF_FILE_SHARE eShareType) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Close() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Read(void* pData, amf_size iSize, amf_size* pRead) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Write(const void* pData, amf_size iSize, amf_size* pWritten) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Seek(AMF_SEEK_ORIGIN eOrigin, amf_int64 iPosition, amf_int64* pNewPosition) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPosition(amf_int64* pPosition) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetSize(amf_int64* pSize) = 0;\n        virtual bool                AMF_STD_CALL IsSeekable() = 0;\n\n        static AMF_RESULT          AMF_STD_CALL OpenDataStream(const wchar_t* pFileUrl, AMF_STREAM_OPEN eOpenType, AMF_FILE_SHARE eShareType, AMFDataStream** str);\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFDataStream> AMFDataStreamPtr;\n    //----------------------------------------------------------------------------------------------\n    \n} //namespace amf\n\n#endif // AMF_DataStream_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStreamFactory.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"DataStream.h\"\n#include \"DataStreamMemory.h\"\n#include \"DataStreamFile.h\"\n#include \"TraceAdapter.h\"\n#include <string>\n\nusing namespace amf;\n\n\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL amf::AMFDataStream::OpenDataStream(const wchar_t* pFileUrl, AMF_STREAM_OPEN eOpenType, AMF_FILE_SHARE eShareType, AMFDataStream** str)\n{\n    AMF_RETURN_IF_FALSE(pFileUrl != NULL, AMF_INVALID_ARG);\n\n    AMF_RESULT res = AMF_NOT_SUPPORTED;\n    std::wstring url(pFileUrl);\n\n    std::wstring protocol;\n    std::wstring path;\n    std::wstring::size_type found_pos = url.find(L\"://\", 0);\n    if(found_pos != std::wstring::npos)\n    {\n        protocol = url.substr(0, found_pos);\n        path = url.substr(found_pos + 3);\n    }\n    else\n    {\n        protocol = L\"file\";\n        path = url;\n    }\n    AMFDataStreamPtr ptr = NULL;\n    if(protocol == L\"file\")\n    {\n        ptr = new AMFDataStreamFileImpl;\n        res = AMF_OK;\n    }\n    if(protocol == L\"memory\")\n    {\n        ptr = new AMFDataStreamMemoryImpl();\n        res = AMF_OK;\n    }\n    if( res == AMF_OK )\n    {\n        res = ptr->Open(path.c_str(), eOpenType, eShareType);\n        if( res != AMF_OK )\n        {\n            return res;\n        }\n        *str = ptr.Detach();\n        return AMF_OK;\n    }\n    return res;\n}\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStreamFile.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"TraceAdapter.h\"\n#include \"DataStreamFile.h\"\n\n#pragma warning(disable: 4996)\n#if defined(_WIN32)\n#include <io.h>\n#endif\n\n#include <fcntl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n\n#if defined(_WIN32)\n    #define amf_close _close\n    #define amf_read _read\n    #define amf_write _write\n    #define amf_seek64 _lseeki64\n#elif defined(__linux)// Linux\n    #include <unistd.h>\n    #define amf_close        close\n    #define amf_read         read\n    #define amf_write        write\n    #define amf_seek64       lseek64\n#elif defined(__APPLE__)\n    #include <unistd.h>\n    #define amf_close        close\n    #define amf_read         read\n    #define amf_write        write\n    #define amf_seek64       lseek\n#endif\n\nusing namespace amf;\n\n#define AMF_FACILITY    L\"AMFDataStreamFileImpl\"\n\n#define AMF_FILE_PROTOCOL L\"file\"\n\n//-------------------------------------------------------------------------------------------------\nAMFDataStreamFileImpl::AMFDataStreamFileImpl()\n    : m_iFileDescriptor(-1), m_Path()\n{}\n//-------------------------------------------------------------------------------------------------\nAMFDataStreamFileImpl::~AMFDataStreamFileImpl()\n{\n    Close();\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::Close()\n{\n    AMF_RESULT err = AMF_OK;\n    if(m_iFileDescriptor != -1)\n    {\n        const int status = amf_close(m_iFileDescriptor);\n        if(status != 0)\n        {\n            err = AMF_FAIL;\n        }\n        m_iFileDescriptor = -1;\n    }\n    return err;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::Read(void* pData, amf_size iSize, amf_size* pRead)\n{\n    AMF_RETURN_IF_FALSE(m_iFileDescriptor != -1, AMF_FILE_NOT_OPEN, L\"Read() - File not open\");\n    AMF_RESULT err = AMF_OK;\n\n    int ready = amf_read(m_iFileDescriptor, pData, (amf_uint)iSize);\n\n    if(pRead != NULL)\n    {\n        *pRead = ready;\n    }\n    if(ready == 0)  // eof\n    {\n        err = AMF_EOF;\n    }\n    else if(ready == -1)\n    {\n        err = AMF_FAIL;\n    }\n    return err;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::Write(const void* pData, amf_size iSize, amf_size* pWritten)\n{\n    AMF_RETURN_IF_FALSE(m_iFileDescriptor != -1, AMF_FILE_NOT_OPEN, L\"Write() - File not Open\");\n    AMF_RESULT err = AMF_OK;\n    amf_uint32 written = amf_write(m_iFileDescriptor, pData, (amf_uint)iSize);\n\n    if(pWritten != NULL)\n    {\n        *pWritten = written;\n    }\n    if(written != iSize) // check errors\n    {\n        err = AMF_FAIL;\n    }\n    return err;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::Seek(AMF_SEEK_ORIGIN eOrigin, amf_int64 iPosition, amf_int64* pNewPosition)\n{\n    AMF_RETURN_IF_FALSE(m_iFileDescriptor != -1, AMF_FILE_NOT_OPEN, L\"Seek() - File not Open\");\n\n    int org = 0;\n\n    switch(eOrigin)\n    {\n    case AMF_SEEK_BEGIN:\n        org = SEEK_SET;\n        break;\n\n    case AMF_SEEK_CURRENT:\n        org = SEEK_CUR;\n        break;\n\n    case AMF_SEEK_END:\n        org = SEEK_END;\n        break;\n    }\n    amf_int64 new_pos = 0;\n\n    new_pos = amf_seek64(m_iFileDescriptor, iPosition, org);\n    if(new_pos == -1L) // check errors\n    {\n        return AMF_FAIL;\n    }\n    if(pNewPosition != NULL)\n    {\n        *pNewPosition = new_pos;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::GetPosition(amf_int64* pPosition)\n{\n    AMF_RETURN_IF_FALSE(pPosition != NULL, AMF_INVALID_POINTER);\n    AMF_RETURN_IF_FALSE(m_iFileDescriptor != -1, AMF_FILE_NOT_OPEN, L\"GetPosition() - File not Open\");\n    *pPosition = amf_seek64(m_iFileDescriptor, 0, SEEK_CUR);\n    if(*pPosition == -1L)\n    {\n        return AMF_FAIL;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::GetSize(amf_int64* pSize)\n{\n    AMF_RETURN_IF_FALSE(pSize != NULL, AMF_INVALID_POINTER);\n    AMF_RETURN_IF_FALSE(m_iFileDescriptor != -1, AMF_FILE_NOT_OPEN, L\"GetSize() - File not open\");\n\n    amf_int64 cur_pos = amf_seek64(m_iFileDescriptor, 0, SEEK_CUR);\n    *pSize = amf_seek64(m_iFileDescriptor, 0, SEEK_END);\n    amf_seek64(m_iFileDescriptor, cur_pos, SEEK_SET);\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nbool AMF_STD_CALL AMFDataStreamFileImpl::IsSeekable()\n{\n    return true;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamFileImpl::Open(const wchar_t* pFilePath, AMF_STREAM_OPEN eOpenType, AMF_FILE_SHARE eShareType)\n{\n    if(m_iFileDescriptor != -1)\n    {\n        Close();\n    }\n    AMF_RETURN_IF_FALSE(pFilePath != NULL, AMF_INVALID_ARG);\n\n    m_Path = pFilePath;\n\n\n#if defined(_WIN32)\n    int access = _O_BINARY;\n#else\n    int access = 0;\n#endif\n\n    switch(eOpenType)\n    {\n    case AMFSO_READ:\n        access |= O_RDONLY;\n        break;\n\n    case AMFSO_WRITE:\n        access |= O_CREAT | O_TRUNC | O_WRONLY;\n        break;\n\n    case AMFSO_READ_WRITE:\n        access |= O_CREAT | O_TRUNC | O_RDWR;\n        break;\n\n    case AMFSO_APPEND:\n        access |= O_CREAT | O_APPEND | O_RDWR;\n        break;\n    }\n\n#ifdef _WIN32\n    int shflag = 0;\n    switch(eShareType)\n    {\n    case AMFFS_EXCLUSIVE:\n        shflag = _SH_DENYRW;\n        break;\n\n    case AMFFS_SHARE_READ:\n        shflag = _SH_DENYWR;\n        break;\n\n    case AMFFS_SHARE_WRITE:\n        shflag = _SH_DENYRD;\n        break;\n\n    case AMFFS_SHARE_READ_WRITE:\n        shflag = _SH_DENYNO;\n        break;\n    }\n#endif\n\n#ifdef O_BINARY\n    access |= O_BINARY;\n#endif\n\n#ifdef _WIN32\n    m_iFileDescriptor = _wsopen(m_Path.c_str(), access, shflag, 0666);\n#else\n    amf_string str = amf_from_unicode_to_utf8(m_Path);\n    m_iFileDescriptor = open(str.c_str(), access, 0666);\n#endif\n\n    if(m_iFileDescriptor == -1)\n    {\n        return AMF_FAIL;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStreamFile.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_DataStreamFile_h\n#define AMF_DataStreamFile_h\n\n#pragma once\n\n#include \"DataStream.h\"\n#include \"InterfaceImpl.h\"\n#include \"AMFSTL.h\"\n#include <string>\n\nnamespace amf\n{\n    class AMFDataStreamFileImpl : public AMFInterfaceImpl<AMFDataStream>\n    {\n    public:\n        AMFDataStreamFileImpl();\n        virtual ~AMFDataStreamFileImpl();\n        // interface\n        virtual AMF_RESULT AMF_STD_CALL Close();\n        virtual AMF_RESULT AMF_STD_CALL Read(void* pData, amf_size iSize, amf_size* pRead);\n        virtual AMF_RESULT AMF_STD_CALL Write(const void* pData, amf_size iSize, amf_size* pWritten);\n        virtual AMF_RESULT AMF_STD_CALL Seek(AMF_SEEK_ORIGIN eOrigin, amf_int64 iPosition, amf_int64* pNewPosition);\n        virtual AMF_RESULT AMF_STD_CALL GetPosition(amf_int64* pPosition);\n        virtual AMF_RESULT AMF_STD_CALL GetSize(amf_int64* pSize);\n        virtual bool       AMF_STD_CALL IsSeekable();\n\n        // local\n        // aways pass full URL just in case\n        virtual AMF_RESULT AMF_STD_CALL Open(const wchar_t* pFilePath, AMF_STREAM_OPEN eOpenType, AMF_FILE_SHARE eShareType);\n    protected:\n        int m_iFileDescriptor;\n        amf_wstring m_Path;\n    };\n} //namespace amf\n#endif // AMF_DataStreamFile_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStreamMemory.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"Thread.h\"\n#include \"TraceAdapter.h\"\n#include \"DataStreamMemory.h\"\n\nusing namespace amf;\n\n#define AMF_FACILITY    L\"AMFDataStreamMemoryImpl\"\n\n//-------------------------------------------------------------------------------------------------\nAMFDataStreamMemoryImpl::AMFDataStreamMemoryImpl()\n    : m_pMemory(NULL),\n    m_uiMemorySize(0),\n    m_uiAllocatedSize(0),\n    m_pos(0)\n{}\n//-------------------------------------------------------------------------------------------------\nAMFDataStreamMemoryImpl::~AMFDataStreamMemoryImpl()\n{\n    Close();\n}\n//-------------------------------------------------------------------------------------------------\n// interface\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::Close()\n{\n    if(m_pMemory != NULL)\n    {\n        amf_virtual_free(m_pMemory);\n    }\n    m_pMemory = NULL,\n    m_uiMemorySize = 0,\n    m_uiAllocatedSize = 0,\n    m_pos = 0;\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMFDataStreamMemoryImpl::Realloc(amf_size iSize)\n{\n    if(iSize > m_uiMemorySize)\n    {\n        amf_uint8* pNewMemory = (amf_uint8*)amf_virtual_alloc(iSize);\n        if(pNewMemory == NULL)\n        {\n            return AMF_OUT_OF_MEMORY;\n        }\n        m_uiAllocatedSize = iSize;\n        if(m_pMemory != NULL)\n        {\n            memcpy(pNewMemory, m_pMemory, m_uiMemorySize);\n            amf_virtual_free(m_pMemory);\n        }\n\n        m_pMemory = pNewMemory;\n    }\n    m_uiMemorySize = iSize;\n    if(m_pos > m_uiMemorySize)\n    {\n        m_pos = m_uiMemorySize;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::Read(void* pData, amf_size iSize, amf_size* pRead)\n{\n    AMF_RETURN_IF_FALSE(pData != NULL, AMF_INVALID_POINTER, L\"Read() - pData==NULL\");\n    AMF_RETURN_IF_FALSE(m_pMemory != NULL, AMF_NOT_INITIALIZED, L\"Read() - Stream is not allocated\");\n\n    amf_size toRead = AMF_MIN(iSize, m_uiMemorySize - m_pos);\n    memcpy(pData, m_pMemory + m_pos, toRead);\n    m_pos += toRead;\n    if(pRead != NULL)\n    {\n        *pRead = toRead;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::Write(const void* pData, amf_size iSize, amf_size* pWritten)\n{\n    AMF_RETURN_IF_FALSE(pData != NULL, AMF_INVALID_POINTER, L\"Write() - pData==NULL\");\n    AMF_RETURN_IF_FAILED(Realloc(m_pos + iSize), L\"Write() - Stream is not allocated\");\n\n    amf_size toWrite = AMF_MIN(iSize, m_uiMemorySize - m_pos);\n    memcpy(m_pMemory + m_pos, pData, toWrite);\n    m_pos += toWrite;\n    if(pWritten != NULL)\n    {\n        *pWritten = toWrite;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::Seek(AMF_SEEK_ORIGIN eOrigin, amf_int64 iPosition, amf_int64* pNewPosition)\n{\n    switch(eOrigin)\n    {\n    case AMF_SEEK_BEGIN:\n        m_pos = (amf_size)iPosition;\n        break;\n\n    case AMF_SEEK_CURRENT:\n        m_pos += (amf_size)iPosition;\n        break;\n\n    case AMF_SEEK_END:\n        m_pos = m_uiMemorySize - (amf_size)iPosition;\n        break;\n    }\n\n    if(m_pos > m_uiMemorySize)\n    {\n        m_pos = m_uiMemorySize;\n    }\n    if(pNewPosition != NULL)\n    {\n        *pNewPosition = m_pos;\n    }\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::GetPosition(amf_int64* pPosition)\n{\n    AMF_RETURN_IF_FALSE(pPosition != NULL, AMF_INVALID_POINTER, L\"GetPosition() - pPosition==NULL\");\n    *pPosition = m_pos;\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_STD_CALL AMFDataStreamMemoryImpl::GetSize(amf_int64* pSize)\n{\n    AMF_RETURN_IF_FALSE(pSize != NULL, AMF_INVALID_POINTER, L\"GetPosition() - pSize==NULL\");\n    *pSize = m_uiMemorySize;\n    return AMF_OK;\n}\n//-------------------------------------------------------------------------------------------------\nbool AMF_STD_CALL AMFDataStreamMemoryImpl::IsSeekable()\n{\n    return true;\n}\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/DataStreamMemory.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_DataStreamMemory_h\n#define AMF_DataStreamMemory_h\n\n#pragma once\n\n#include \"DataStream.h\"\n#include \"InterfaceImpl.h\"\n\nnamespace amf\n{\n    class AMFDataStreamMemoryImpl : public AMFInterfaceImpl<AMFDataStream>\n    {\n    public:\n        AMFDataStreamMemoryImpl();\n        virtual ~AMFDataStreamMemoryImpl();\n        // interface\n        virtual AMF_RESULT AMF_STD_CALL Open(const wchar_t* /*pFileUrl*/, AMF_STREAM_OPEN /*eOpenType*/, AMF_FILE_SHARE /*eShareType*/)\n        {\n            //pFileUrl;\n            //eOpenType;\n            //eShareType;\n            return AMF_OK;\n        }\n        virtual AMF_RESULT AMF_STD_CALL Close();\n        virtual AMF_RESULT AMF_STD_CALL Read(void* pData, amf_size iSize, amf_size* pRead);\n        virtual AMF_RESULT AMF_STD_CALL Write(const void* pData, amf_size iSize, amf_size* pWritten);\n        virtual AMF_RESULT AMF_STD_CALL Seek(AMF_SEEK_ORIGIN eOrigin, amf_int64 iPosition, amf_int64* pNewPosition);\n        virtual AMF_RESULT AMF_STD_CALL GetPosition(amf_int64* pPosition);\n        virtual AMF_RESULT AMF_STD_CALL GetSize(amf_int64* pSize);\n        virtual bool       AMF_STD_CALL IsSeekable();\n\n    protected:\n        AMF_RESULT Realloc(amf_size iSize);\n\n        amf_uint8* m_pMemory;\n        amf_size m_uiMemorySize;\n        amf_size m_uiAllocatedSize;\n        amf_size m_pos;\n    private:\n        AMFDataStreamMemoryImpl(const AMFDataStreamMemoryImpl&);\n        AMFDataStreamMemoryImpl& operator=(const AMFDataStreamMemoryImpl&);\n    };\n} //namespace amf\n\n#endif // AMF_DataStreamMemory_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/IOCapsImpl.cpp",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"IOCapsImpl.h\"\n\nnamespace amf\n{\n    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    AMFIOCapsImpl::SurfaceFormat::SurfaceFormat() :\n        m_Format(AMF_SURFACE_UNKNOWN),\n        m_Native(false)\n    {\n    }\n\n    AMFIOCapsImpl::SurfaceFormat::SurfaceFormat(AMF_SURFACE_FORMAT format, amf_bool native) :\n        m_Format(format),\n        m_Native(native)\n    {\n    }\n\n    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n    AMFIOCapsImpl::MemoryType::MemoryType() :\n        m_Type(AMF_MEMORY_UNKNOWN),\n        m_Native(false)\n    {\n    }\n\n    AMFIOCapsImpl::MemoryType::MemoryType(AMF_MEMORY_TYPE type, amf_bool native) :\n        m_Type(type),\n        m_Native(native)\n    {\n    }\n\n\n    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n    AMFIOCapsImpl::AMFIOCapsImpl() :\n        m_MinWidth(-1),\n        m_MaxWidth(-1),\n        m_MinHeight(-1),\n        m_MaxHeight(-1),\n        m_VertAlign(-1),\n        m_InterlacedSupported(false)\n    {\n    }\n\n    AMFIOCapsImpl::AMFIOCapsImpl(amf_int32 minWidth, amf_int32 maxWidth,\n                      amf_int32 minHeight, amf_int32 maxHeight,\n                      amf_int32 vertAlign, amf_bool interlacedSupport,\n                      amf_int32 numOfNativeFormats, const AMF_SURFACE_FORMAT* nativeFormats,\n                      amf_int32 numOfNonNativeFormats, const AMF_SURFACE_FORMAT* nonNativeFormats,\n                      amf_int32 numOfNativeMemTypes, const AMF_MEMORY_TYPE* nativeMemTypes,\n                      amf_int32 numOfNonNativeMemTypes, const AMF_MEMORY_TYPE* nonNativeMemTypes)\n    {\n        m_MinWidth = minWidth;\n        m_MaxWidth = maxWidth;\n        m_MinHeight = minHeight;\n        m_MaxHeight = maxHeight;\n        m_VertAlign = vertAlign;\n        m_InterlacedSupported = interlacedSupport;\n        PopulateSurfaceFormats(numOfNativeFormats, nativeFormats, true);\n        PopulateSurfaceFormats(numOfNonNativeFormats, nonNativeFormats, false);\n        PopulateMemoryTypes(numOfNativeMemTypes, nativeMemTypes, true);\n        PopulateMemoryTypes(numOfNonNativeMemTypes, nonNativeMemTypes, false);\n    }\n\n    void AMFIOCapsImpl::PopulateSurfaceFormats(amf_int32 numOfFormats, const AMF_SURFACE_FORMAT* formats, amf_bool native)\n    {\n        if (formats != NULL)\n        {\n            for (amf_int32 i = 0; i < numOfFormats; i++)\n            {\n                bool found = false;\n                for(amf_size exists_idx = 0; exists_idx < m_SurfaceFormats.size(); exists_idx++)\n                {\n                    if(m_SurfaceFormats[exists_idx].GetFormat() == formats[i])\n                    {\n                        found = true;\n                    }\n                }\n                if(!found)\n                {\n                    m_SurfaceFormats.push_back(SurfaceFormat(formats[i], native));\n                }\n            }\n        }\n    }\n\n    void AMFIOCapsImpl::PopulateMemoryTypes(amf_int32 numOfTypes, const AMF_MEMORY_TYPE* memTypes, amf_bool native)\n    {\n        if (memTypes != NULL)\n        {\n            for (amf_int32 i = 0; i < numOfTypes; i++)\n            {\n                bool found = false;\n                for(amf_size exists_idx = 0; exists_idx < m_MemoryTypes.size(); exists_idx++)\n                {\n                    if(m_MemoryTypes[exists_idx].GetType() == memTypes[i])\n                    {\n                        found = true;\n                    }\n                }\n                if(!found)\n                {\n                    m_MemoryTypes.push_back(MemoryType(memTypes[i], native));\n                }\n            }\n        }\n    }\n\n    //  Get supported resolution ranges in pixels/lines:\n    void AMF_STD_CALL AMFIOCapsImpl::GetWidthRange(amf_int32* minWidth, amf_int32* maxWidth) const\n    {\n        if (minWidth != NULL)\n        {\n            *minWidth = m_MinWidth;\n        }\n        if (maxWidth != NULL)\n        {\n            *maxWidth = m_MaxWidth;\n        }\n    }\n\n    void AMF_STD_CALL AMFIOCapsImpl::GetHeightRange(amf_int32* minHeight, amf_int32* maxHeight) const\n    {\n        if (minHeight != NULL)\n        {\n            *minHeight = m_MinHeight;\n        }\n        if (maxHeight != NULL)\n        {\n            *maxHeight = m_MaxHeight;\n        }\n    }\n\n    //  Get memory alignment in lines:\n    //  Vertical aligmnent should be multiples of this number\n    amf_int32 AMF_STD_CALL AMFIOCapsImpl::GetVertAlign() const\n    {\n        return m_VertAlign;\n    }\n\n    //  Enumerate supported surface pixel formats:\n    amf_int32 AMF_STD_CALL AMFIOCapsImpl::GetNumOfFormats() const\n    {\n        return (amf_int32)m_SurfaceFormats.size();\n    }\n\n    AMF_RESULT AMF_STD_CALL AMFIOCapsImpl::GetFormatAt(amf_int32 index, AMF_SURFACE_FORMAT* format, bool* native) const\n    {\n        if (index >= 0 && index < static_cast<amf_int32>(m_SurfaceFormats.size()))\n        {\n            SurfaceFormat curFormat(m_SurfaceFormats.at(index));\n            if (format != NULL)\n            {\n                *format = curFormat.GetFormat();\n            }\n            if (native != NULL)\n            {\n                *native = curFormat.IsNative();\n            }\n            return AMF_OK;\n        }\n        else\n        {\n            return AMF_INVALID_ARG;\n        }\n    }\n\n    //  Enumerate supported surface formats:\n    amf_int32 AMF_STD_CALL AMFIOCapsImpl::GetNumOfMemoryTypes() const\n    {\n        return (amf_int32)m_MemoryTypes.size();\n    }\n\n    AMF_RESULT AMF_STD_CALL AMFIOCapsImpl::GetMemoryTypeAt(amf_int32 index, AMF_MEMORY_TYPE* memType, bool* native) const\n    {\n        if (index >= 0 && index < static_cast<amf_int32>(m_MemoryTypes.size()))\n        {\n            MemoryType curType(m_MemoryTypes.at(index));\n            if (memType != NULL)\n            {\n                *memType = curType.GetType();\n            }\n            if (native != NULL)\n            {\n                *native = curType.IsNative();\n            }\n            return AMF_OK;\n        }\n        else\n        {\n            return AMF_INVALID_ARG;\n        }\n    }\n\n    //  interlaced support:\n    amf_bool AMF_STD_CALL AMFIOCapsImpl::IsInterlacedSupported() const\n    {\n        return m_InterlacedSupported;\n    }\n\n    void AMFIOCapsImpl::SetResolution(amf_int32 minWidth, amf_int32 maxWidth, amf_int32 minHeight, amf_int32 maxHeight)\n    {\n        m_MinWidth = minWidth;\n        m_MaxWidth = maxWidth;\n        m_MinHeight = minHeight;\n        m_MaxHeight = maxHeight;\n    }\n\n    void AMFIOCapsImpl::SetVertAlign(amf_int32 vertAlign)\n    {\n        m_VertAlign = vertAlign;\n    }\n\n    void AMFIOCapsImpl::SetInterlacedSupport(amf_bool interlaced)\n    {\n        m_InterlacedSupported = interlaced;\n    }\n\n}"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/IOCapsImpl.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_IOCapsImpl_h\n#define AMF_IOCapsImpl_h\n\n#pragma once\n\n#include \"InterfaceImpl.h\"\n#include \"../include/components/ComponentCaps.h\"\n#include <vector>\n\nnamespace amf\n{\n    class AMFIOCapsImpl : public AMFInterfaceImpl<AMFIOCaps>\n    {\n    protected:\n        class SurfaceFormat\n        {\n        public:\n            typedef std::vector<SurfaceFormat>  Collection;\n        public:\n            SurfaceFormat();\n            SurfaceFormat(AMF_SURFACE_FORMAT format, amf_bool native);\n\n            inline AMF_SURFACE_FORMAT GetFormat() const throw() { return m_Format; }\n            inline amf_bool IsNative() const throw() { return m_Native; }\n        private:\n            AMF_SURFACE_FORMAT  m_Format;\n            amf_bool    m_Native;\n        };\n\n        class MemoryType\n        {\n        public:\n            typedef std::vector<MemoryType>  Collection;\n        public:\n            MemoryType();\n            MemoryType(AMF_MEMORY_TYPE type, amf_bool native);\n\n            inline AMF_MEMORY_TYPE GetType() const throw() { return m_Type; }\n            inline amf_bool IsNative() const throw() { return m_Native; }\n        private:\n            AMF_MEMORY_TYPE m_Type;\n            amf_bool    m_Native;\n        };\n\n        struct Resolution\n        {\n            amf_int32   m_Width;\n            amf_int32   m_Height;\n        };\n\n    protected:\n        AMFIOCapsImpl();\n        AMFIOCapsImpl(amf_int32 minWidth, amf_int32 maxWidth, \n                      amf_int32 minHeight, amf_int32 maxHeight,\n                      amf_int32 vertAlign, amf_bool interlacedSupport,\n                      amf_int32 numOfNativeFormats, const AMF_SURFACE_FORMAT* nativeFormats,\n                      amf_int32 numOfNonNativeFormats, const AMF_SURFACE_FORMAT* nonNativeFormats,\n                      amf_int32 numOfNativeMemTypes, const AMF_MEMORY_TYPE* nativeMemTypes,\n                      amf_int32 numOfNonNativeMemTypes, const AMF_MEMORY_TYPE* nonNativeMemTypes);\n\n    public:\n        //  Get supported resolution ranges in pixels/lines:\n        virtual void AMF_STD_CALL GetWidthRange(amf_int32* minWidth, amf_int32* maxWidth) const;\n        virtual void AMF_STD_CALL GetHeightRange(amf_int32* minHeight, amf_int32* maxHeight) const;\n\n        //  Get memory alignment in lines:\n        //  Vertical aligmnent should be multiples of this number\n        virtual amf_int32 AMF_STD_CALL GetVertAlign() const;\n        \n        //  Enumerate supported surface pixel formats:\n        virtual amf_int32 AMF_STD_CALL GetNumOfFormats() const;\n        virtual  AMF_RESULT AMF_STD_CALL GetFormatAt(amf_int32 index, AMF_SURFACE_FORMAT* format, amf_bool* native) const;\n\n        //  Enumerate supported surface formats:\n        virtual amf_int32 AMF_STD_CALL GetNumOfMemoryTypes() const;\n        virtual AMF_RESULT AMF_STD_CALL GetMemoryTypeAt(amf_int32 index, AMF_MEMORY_TYPE* memType, amf_bool* native) const;\n\n        //  interlaced support:\n        virtual amf_bool AMF_STD_CALL IsInterlacedSupported() const;\n\n    protected:\n        void SetResolution(amf_int32 minWidth, amf_int32 maxWidth, amf_int32 minHeight, amf_int32 maxHeight);\n        void SetVertAlign(amf_int32 alignment);\n        void SetInterlacedSupport(amf_bool interlaced);\n        void PopulateSurfaceFormats(amf_int32 numOfFormats, const AMF_SURFACE_FORMAT* formats, amf_bool native);\n        void PopulateMemoryTypes(amf_int32 numOfTypes, const AMF_MEMORY_TYPE* memTypes, amf_bool native);\n\n\n    protected:\n        amf_int32   m_MinWidth;\n        amf_int32   m_MaxWidth;\n        amf_int32   m_MinHeight;\n        amf_int32   m_MaxHeight;\n        amf_int32   m_VertAlign;\n        amf_bool    m_InterlacedSupported;\n        SurfaceFormat::Collection   m_SurfaceFormats;\n        MemoryType::Collection      m_MemoryTypes;\n    };\n}\n#endif // AMF_IOCapsImpl_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/InterfaceImpl.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_InterfaceImpl_h\n#define AMF_InterfaceImpl_h\n\n#pragma once\n\n#include \"../include/core/Interface.h\"\n#include \"Thread.h\"\n\n#pragma warning(disable : 4511)\nnamespace amf\n{\n    #define AMF_BEGIN_INTERFACE_MAP  \\\n        virtual AMF_RESULT AMF_STD_CALL QueryInterface(const amf::AMFGuid & interfaceID, void** ppInterface) \\\n        {  \\\n            AMF_RESULT err = AMF_NO_INTERFACE; \\\n\n\n    #define AMF_INTERFACE_ENTRY(T)  \\\n        if(AMFCompareGUIDs(interfaceID, T::IID())) \\\n        { \\\n            *ppInterface = (void*)static_cast<T*>(this); \\\n            this->Acquire(); \\\n            err = AMF_OK; \\\n        } \\\n        else \\\n\n    #define AMF_INTERFACE_ENTRY_THIS(T, _TI)  \\\n        if(AMFCompareGUIDs(interfaceID, T::IID())) \\\n        { \\\n            *ppInterface = (void*)static_cast<T*>(static_cast<_TI*>(this)); \\\n            this->Acquire(); \\\n            err = AMF_OK; \\\n        } \\\n        else \\\n\n    #define AMF_INTERFACE_MULTI_ENTRY(T)  \\\n        if(AMFCompareGUIDs(interfaceID, T::IID())) \\\n        { \\\n            *ppInterface = (void*)static_cast<T*>(this); \\\n            AcquireInternal(); \\\n            err = AMF_OK; \\\n        } \\\n        else \\\n\n    #define AMF_INTERFACE_CHAIN_ENTRY(T)  \\\n        if(static_cast<T&>(*this).T::QueryInterface(interfaceID, ppInterface) == AMF_OK) \\\n        {err = AMF_OK;} \\\n        else \\\n\n    //good as an example but we should not use aggregate pattern without big reason - very hard to debug\n    #define AMF_INTERFACE_AGREGATED_ENTRY(T, _Ptr)  \\\n        if(AMFCompareGUIDs(interfaceID, T::IID())) \\\n        { \\\n            T* ptr = static_cast<T*>(_Ptr); \\\n            *ppInterface = (void*)ptr; \\\n            ptr->Acquire(); \\\n            err = AMF_OK; \\\n        } \\\n        else \\\n\n    #define AMF_INTERFACE_CHAIN_AGREGATED_ENTRY(T, _Ptr)  \\\n        if(err = static_cast<T*>(_Ptr)->QueryInterface(interfaceID, ppInterface)) { \\\n        } \\\n        else \\\n\n    #define AMF_END_INTERFACE_MAP \\\n        {} \\\n        return err; \\\n        } \\\n\n\n    //---------------------------------------------------------------\n    class AMFInterfaceBase\n    {\n    protected:\n        amf_long m_refCount;\n        virtual ~AMFInterfaceBase()\n#if __GNUC__ == 11 //WORKAROUND for gcc-11 bug\n        __attribute__ ((noinline))\n#endif\n        {}\n    public:\n        AMFInterfaceBase() : m_refCount(0)\n        {}\n        virtual amf_long AMF_STD_CALL AcquireInternal()\n        {\n            amf_long newVal = amf_atomic_inc(&m_refCount);\n            return newVal;\n        }\n        virtual amf_long AMF_STD_CALL ReleaseInternal()\n        {\n            amf_long newVal = amf_atomic_dec(&m_refCount);\n            if(newVal == 0)\n            {\n                delete this;\n            }\n            return newVal;\n        }\n        virtual amf_long AMF_STD_CALL RefCountInternal()\n        {\n            return m_refCount;\n        }\n    };\n    //---------------------------------------------------------------\n    template<class _Base , typename _Param1 = int, typename _Param2 = int, typename _Param3 = int>\n    class AMFInterfaceImpl : public _Base, public AMFInterfaceBase\n    {\n    protected:\n        virtual ~AMFInterfaceImpl()\n        {}\n    public:\n        AMFInterfaceImpl(_Param1 param1, _Param2 param2, _Param3 param3) : _Base(param1, param2, param3)\n        {}\n        AMFInterfaceImpl(_Param1 param1, _Param2 param2) : _Base(param1, param2)\n        {}\n        AMFInterfaceImpl(_Param1 param1) : _Base(param1)\n        {}\n        AMFInterfaceImpl()\n        {}\n        virtual amf_long AMF_STD_CALL Acquire()\n        {\n            return AMFInterfaceBase::AcquireInternal();\n        }\n        virtual amf_long AMF_STD_CALL Release()\n        {\n            return AMFInterfaceBase::ReleaseInternal();\n        }\n        virtual amf_long AMF_STD_CALL RefCount()\n        {\n            return AMFInterfaceBase::RefCountInternal();\n        }\n\n        AMF_BEGIN_INTERFACE_MAP\n            AMF_INTERFACE_ENTRY(AMFInterface)\n            AMF_INTERFACE_ENTRY(_Base)\n        AMF_END_INTERFACE_MAP\n    };\n\n    //---------------------------------------------------------------\n    template<class _Base, class _BaseInterface, typename _Param1 = int, typename _Param2 = int, typename _Param3 = int, typename _Param4 = int, typename _Param5 = int, typename _Param6 = int>\n    class AMFInterfaceMultiImpl : public _Base\n    {\n    protected:\n        virtual ~AMFInterfaceMultiImpl()\n        {}\n    public:\n        AMFInterfaceMultiImpl(_Param1 param1, _Param2 param2, _Param3 param3, _Param4 param4, _Param5 param5, _Param6 param6) : _Base(param1, param2, param3, param4, param5, param6)\n        {}\n        AMFInterfaceMultiImpl(_Param1 param1, _Param2 param2, _Param3 param3, _Param4 param4, _Param5 param5) : _Base(param1, param2, param3, param4, param5)\n        {}\n        AMFInterfaceMultiImpl(_Param1 param1, _Param2 param2, _Param3 param3, _Param4 param4) : _Base(param1, param2, param3, param4)\n        {}\n        AMFInterfaceMultiImpl(_Param1 param1, _Param2 param2, _Param3 param3) : _Base(param1, param2, param3)\n        {}\n        AMFInterfaceMultiImpl(_Param1 param1, _Param2 param2) : _Base(param1, param2)\n        {}\n        AMFInterfaceMultiImpl(_Param1 param1) : _Base(param1)\n        {}\n        AMFInterfaceMultiImpl()\n        {}\n        virtual amf_long AMF_STD_CALL Acquire()\n        {\n            return AMFInterfaceBase::AcquireInternal();\n        }\n        virtual amf_long AMF_STD_CALL Release()\n        {\n            return AMFInterfaceBase::ReleaseInternal();\n        }\n        virtual amf_long AMF_STD_CALL RefCount()\n        {\n            return AMFInterfaceBase::RefCountInternal();\n        }\n\n        AMF_BEGIN_INTERFACE_MAP\n            AMF_INTERFACE_ENTRY_THIS(AMFInterface, _BaseInterface)\n            AMF_INTERFACE_CHAIN_ENTRY(_Base)\n        AMF_END_INTERFACE_MAP\n    };\n\n\n} // namespace amf\n#endif // AMF_InterfaceImpl_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/ObservableImpl.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n ***************************************************************************************************\n * @file  ObservableImpl.h\n * @brief AMFObservableImpl common template declaration\n ***************************************************************************************************\n */\n#ifndef AMF_ObservableImpl_h\n#define AMF_ObservableImpl_h\n#pragma once\n\n#include \"Thread.h\"\n#include <list>\n\nnamespace amf\n{\n    template<typename Observer>\n    class AMFObservableImpl\n    {\n    private:\n        typedef std::list<Observer*> ObserversList;\n        ObserversList m_observers;\n    public:\n        AMFObservableImpl() : m_observers()\n        {}\n        virtual ~AMFObservableImpl()\n        {\n            assert(m_observers.size() == 0);\n        }\n        virtual void AMF_STD_CALL AddObserver(Observer* pObserver)\n        {\n            if (pObserver == nullptr)\n            {\n                return;\n            }\n\n            amf_bool found = false;\n            AMFLock lock(&m_sc);\n            \n            for (typename ObserversList::iterator it = m_observers.begin(); it != m_observers.end(); it++)\n            {\n                if (*it == pObserver)\n                {\n                    found = true;\n                    break;\n                }\n            }\n            if (found == false)\n            {\n                m_observers.push_back(pObserver);\n            }\n        }\n\n        virtual void AMF_STD_CALL RemoveObserver(Observer* pObserver)\n        {\n            AMFLock lock(&m_sc);\n            m_observers.remove(pObserver);\n        }\n\n    protected:\n        void AMF_STD_CALL ClearObservers()\n        {\n            AMFLock lock(&m_sc);\n            m_observers.clear();\n        }\n\n        void AMF_STD_CALL NotifyObservers(void  (AMF_STD_CALL Observer::* pEvent)())\n        {\n            ObserversList tempList;\n            {\n                AMFLock lock(&m_sc);\n                tempList = m_observers;\n            }\n            for (typename ObserversList::iterator it = tempList.begin(); it != tempList.end(); ++it)\n            {\n                Observer* pObserver = *it;\n                (pObserver->*pEvent)();\n            }\n        }\n\n        template<typename TArg0>\n        void AMF_STD_CALL NotifyObservers(void (AMF_STD_CALL Observer::* pEvent)(TArg0), TArg0 arg0)\n        {\n            ObserversList tempList;\n            {\n                AMFLock lock(&m_sc);\n                tempList = m_observers;\n            }\n            for (typename ObserversList::iterator it = tempList.begin(); it != tempList.end(); ++it)\n            {\n                Observer* pObserver = *it;\n                (pObserver->*pEvent)(arg0);\n            }\n        }\n        template<typename TArg0, typename TArg1>\n        void AMF_STD_CALL NotifyObservers(void (AMF_STD_CALL Observer::* pEvent)(TArg0, TArg1), TArg0 arg0, TArg1 arg1)\n        {\n            ObserversList tempList;\n            {\n                AMFLock lock(&m_sc);\n                tempList = m_observers;\n            }\n            for (typename ObserversList::iterator it = tempList.begin(); it != tempList.end(); it++)\n            {\n                Observer* pObserver = *it;\n                (pObserver->*pEvent)(arg0, arg1);\n            }\n        }\n    private:\n        AMFCriticalSection m_sc;\n    };\n}\n#endif //AMF_ObservableImpl_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/PropertyStorageExImpl.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include <climits>\n#include \"PropertyStorageExImpl.h\"\n#include \"PropertyStorageImpl.h\"\n#include \"TraceAdapter.h\"\n\n#pragma warning(disable: 4996)\n\nusing namespace amf;\n\n#define AMF_FACILITY L\"AMFPropertyStorageExImpl\"\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n    #pragma clang diagnostic ignored \"-Wglobal-constructors\"\n#endif\n\namf::AMFCriticalSection amf::ms_csAMFPropertyStorageExImplMaps;\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n//-------------------------------------------------------------------------------------------------\nAMF_RESULT amf::CastVariantToAMFProperty(amf::AMFVariantStruct* pDest, const amf::AMFVariantStruct* pSrc, amf::AMF_VARIANT_TYPE eType,\n        amf::AMF_PROPERTY_CONTENT_TYPE /*contentType*/,\n        const amf::AMFEnumDescriptionEntry* pEnumDescription)\n{\n    AMF_RETURN_IF_INVALID_POINTER(pDest);\n\n    AMF_RESULT err = AMF_OK;\n    switch (eType)\n    {\n    case AMF_VARIANT_INTERFACE:\n        if (pSrc->type == eType)\n        {\n            err = AMFVariantCopy(pDest, pSrc);\n        }\n        else\n        {\n            pDest->type = AMF_VARIANT_INTERFACE;\n            pDest->pInterface = nullptr;\n        }\n        break;\n\n    case AMF_VARIANT_INT64:\n    {\n        if(pEnumDescription)\n        {\n            const AMFEnumDescriptionEntry* pEnumDescriptionCache = pEnumDescription;\n            err = AMFVariantChangeType(pDest, pSrc, AMF_VARIANT_INT64);\n            bool found = false;\n            if(err == AMF_OK)\n            {\n                //mean numeric came. validating\n                while(pEnumDescriptionCache->name)\n                {\n                    if(pEnumDescriptionCache->value == AMFVariantGetInt64(pDest))\n                    {\n                        AMFVariantAssignInt64(pDest, pEnumDescriptionCache->value);\n                        found = true;\n                        break;\n                    }\n                    pEnumDescriptionCache++;\n                }\n                err = found ? AMF_OK : AMF_INVALID_ARG;\n            }\n            if(!found)\n            {\n                pEnumDescriptionCache = pEnumDescription;\n                err = AMFVariantChangeType(pDest, pSrc, AMF_VARIANT_WSTRING);\n                if(err == AMF_OK)\n                {\n                    //string came. validating and assigning numeric\n                    found = false;\n                    while(pEnumDescriptionCache->name)\n                    {\n                        if(amf_wstring(pEnumDescriptionCache->name) == AMFVariantGetWString(pDest))\n                        {\n                            AMFVariantAssignInt64(pDest, pEnumDescriptionCache->value);\n                            found = true;\n                            break;\n                        }\n                        pEnumDescriptionCache++;\n                    }\n                    err = found ? AMF_OK : AMF_INVALID_ARG;\n                }\n            }\n        }\n        else\n        {\n            err = AMFVariantChangeType(pDest, pSrc, AMF_VARIANT_INT64);\n        }\n    }\n    break;\n\n    default:\n        err = AMFVariantChangeType(pDest, pSrc, eType);\n    break;\n    }\n    return err;\n}\n//-------------------------------------------------------------------------------------------------\nAMFPropertyInfoImpl::AMFPropertyInfoImpl(const wchar_t* name, const wchar_t* desc, AMF_VARIANT_TYPE type, AMF_PROPERTY_CONTENT_TYPE contentType,\n        AMFVariantStruct defaultValue, AMFVariantStruct minValue, AMFVariantStruct maxValue, bool allowChangeInRuntime,\n        const AMFEnumDescriptionEntry* pEnumDescription) : m_name(), m_desc()\n{\n    AMF_PROPERTY_ACCESS_TYPE accessTypeTmp = allowChangeInRuntime ? AMF_PROPERTY_ACCESS_FULL : AMF_PROPERTY_ACCESS_READ_WRITE;\n    Init(name, desc, type, contentType, defaultValue, minValue, maxValue, accessTypeTmp, pEnumDescription);\n}\n//-------------------------------------------------------------------------------------------------\nAMFPropertyInfoImpl::AMFPropertyInfoImpl(const wchar_t* name, const wchar_t* desc, AMF_VARIANT_TYPE type, AMF_PROPERTY_CONTENT_TYPE contentType,\n        AMFVariantStruct defaultValue, AMFVariantStruct minValue, AMFVariantStruct maxValue, AMF_PROPERTY_ACCESS_TYPE accessType,\n        const AMFEnumDescriptionEntry* pEnumDescription) : m_name(), m_desc()\n{\n    Init(name, desc, type, contentType, defaultValue, minValue, maxValue, accessType, pEnumDescription);\n}\n//-------------------------------------------------------------------------------------------------\nAMFPropertyInfoImpl::AMFPropertyInfoImpl() : m_name(), m_desc()\n{\n    AMFVariantInit(&this->defaultValue);\n    AMFVariantInit(&this->minValue);\n    AMFVariantInit(&this->maxValue);\n\n    name = L\"\";\n    desc = L\"\";\n    type = AMF_VARIANT_EMPTY;\n    contentType = AMF_PROPERTY_CONTENT_TYPE(-1);\n    accessType = AMF_PROPERTY_ACCESS_FULL;\n}\n//-------------------------------------------------------------------------------------------------\nvoid AMFPropertyInfoImpl::Init(const wchar_t* name_, const wchar_t* desc_, AMF_VARIANT_TYPE type_, AMF_PROPERTY_CONTENT_TYPE contentType_,\n        AMFVariantStruct defaultValue_, AMFVariantStruct minValue_, AMFVariantStruct maxValue_, AMF_PROPERTY_ACCESS_TYPE accessType_,\n        const AMFEnumDescriptionEntry* pEnumDescription_)\n{\n    m_name = name_;\n    name = m_name.c_str();\n\n    m_desc = desc_;\n    desc = m_desc.c_str();\n\n    type = type_;\n    contentType = contentType_;\n    accessType = accessType_;\n    AMFVariantInit(&defaultValue);\n    AMFVariantInit(&minValue);\n    AMFVariantInit(&maxValue);\n    pEnumDescription = pEnumDescription_;\n\n    switch(type)\n    {\n    case AMF_VARIANT_BOOL:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignBool(&defaultValue, false);\n        }\n    }\n    break;\n    case AMF_VARIANT_RECT:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignRect(&defaultValue, AMFConstructRect(0, 0, 0, 0));\n        }\n    }\n    break;\n    case AMF_VARIANT_SIZE:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignSize(&defaultValue, AMFConstructSize(0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignSize(&minValue, AMFConstructSize(INT_MIN, INT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignSize(&maxValue, AMFConstructSize(INT_MAX, INT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_POINT:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignPoint(&defaultValue, AMFConstructPoint(0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignPoint(&minValue, AMFConstructPoint(INT_MIN, INT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignPoint(&maxValue, AMFConstructPoint(INT_MAX, INT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_RATE:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignRate(&defaultValue, AMFConstructRate(0, 0));\n        }\n        if (CastVariantToAMFProperty(&this->minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignRate(&this->minValue, AMFConstructRate(0, 1));\n        }\n        if (CastVariantToAMFProperty(&this->maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignRate(&this->maxValue, AMFConstructRate(INT_MAX, INT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_RATIO:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignRatio(&defaultValue, AMFConstructRatio(0, 0));\n        }\n    }\n    break;\n    case AMF_VARIANT_COLOR:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignColor(&defaultValue, AMFConstructColor(0, 0, 0, 255));\n        }\n    }\n    break;\n\n    case AMF_VARIANT_INT64:\n    {\n        if(pEnumDescription)\n        {\n            if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n            {\n                AMFVariantAssignInt64(&defaultValue, pEnumDescription->value);\n            }\n        }\n        else //AMF_PROPERTY_CONTENT_DEFAULT\n        {\n            if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n            {\n                AMFVariantAssignInt64(&defaultValue, 0);\n            }\n            if(CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n            {\n                AMFVariantAssignInt64(&minValue, INT_MIN);\n            }\n            if(CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n            {\n                AMFVariantAssignInt64(&maxValue, INT_MAX);\n            }\n        }\n    }\n    break;\n\n    case AMF_VARIANT_DOUBLE:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignDouble(&defaultValue, 0);\n        }\n        if(CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignDouble(&minValue, DBL_MIN);\n        }\n        if(CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignDouble(&maxValue, DBL_MAX);\n        }\n    }\n    break;\n\n    case AMF_VARIANT_STRING:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignString(&maxValue, \"\");\n        }\n    }\n    break;\n\n    case AMF_VARIANT_WSTRING:\n    {\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignWString(&maxValue, L\"\");\n        }\n    }\n    break;\n\n    case AMF_VARIANT_INTERFACE:\n        if(CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignWString(&maxValue, L\"\");\n        }\n        break;\n    case AMF_VARIANT_FLOAT:\n    {\n        if (CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloat(&defaultValue, 0);\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloat(&minValue, FLT_MIN);\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloat(&maxValue, FLT_MAX);\n        }\n    }\n    break;\n    case AMF_VARIANT_FLOAT_SIZE:\n    {\n        if (CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatSize(&defaultValue, AMFConstructFloatSize(0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatSize(&minValue, AMFConstructFloatSize(FLT_MIN, FLT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatSize(&maxValue, AMFConstructFloatSize(FLT_MAX, FLT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_FLOAT_POINT2D:\n    {\n        if (CastVariantToAMFProperty(&defaultValue, &defaultValue, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint2D(&defaultValue, AMFConstructFloatPoint2D(0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint2D(&minValue, AMFConstructFloatPoint2D(FLT_MIN, FLT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint2D(&maxValue, AMFConstructFloatPoint2D(FLT_MAX, FLT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_FLOAT_POINT3D:\n    {\n        if (CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint3D(&defaultValue, AMFConstructFloatPoint3D(0, 0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint3D(&minValue, AMFConstructFloatPoint3D(FLT_MIN, FLT_MIN, FLT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatPoint3D(&maxValue, AMFConstructFloatPoint3D(FLT_MAX, FLT_MAX, FLT_MAX));\n        }\n    }\n    break;\n    case AMF_VARIANT_FLOAT_VECTOR4D:\n    {\n        if (CastVariantToAMFProperty(&defaultValue, &defaultValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatVector4D(&defaultValue, AMFConstructFloatVector4D(0, 0, 0, 0));\n        }\n        if (CastVariantToAMFProperty(&minValue, &minValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatVector4D(&minValue, AMFConstructFloatVector4D(FLT_MIN, FLT_MIN, FLT_MIN, FLT_MIN));\n        }\n        if (CastVariantToAMFProperty(&maxValue, &maxValue_, type, contentType, pEnumDescription) != AMF_OK)\n        {\n            AMFVariantAssignFloatVector4D(&maxValue, AMFConstructFloatVector4D(FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX));\n        }\n    }\n    break;\n    default:\n        break;\n    }\n\n    value = defaultValue;\n}\n\nAMFPropertyInfoImpl::AMFPropertyInfoImpl(const AMFPropertyInfoImpl& propertyInfo) : AMFPropertyInfo(), m_name(), m_desc()\n{\n    Init(propertyInfo.name, propertyInfo.desc, propertyInfo.type, propertyInfo.contentType, propertyInfo.defaultValue, propertyInfo.minValue, propertyInfo.maxValue, propertyInfo.accessType, propertyInfo.pEnumDescription);\n}\n//-------------------------------------------------------------------------------------------------\nAMFPropertyInfoImpl& AMFPropertyInfoImpl::operator=(const AMFPropertyInfoImpl& propertyInfo)\n{\n    // store name and desc inside instance in m_sName and m_sDesc recpectively;\n    // m_pName and m_pDesc are pointed to our local copies\n    this->m_name = propertyInfo.name;\n    this->m_desc = propertyInfo.desc;\n    this->name = m_name.c_str();\n    this->desc = m_desc.c_str();\n\n    this->type = propertyInfo.type;\n    this->contentType = propertyInfo.contentType;\n    this->accessType = propertyInfo.accessType;\n    AMFVariantCopy(&this->defaultValue, &propertyInfo.defaultValue);\n    AMFVariantCopy(&this->minValue, &propertyInfo.minValue);\n    AMFVariantCopy(&this->maxValue, &propertyInfo.maxValue);\n    this->pEnumDescription = propertyInfo.pEnumDescription;\n\n    this->value = propertyInfo.value;\n    this->userModified = propertyInfo.userModified;\n\n    return *this;\n}\n//-------------------------------------------------------------------------------------------------\nAMFPropertyInfoImpl::~AMFPropertyInfoImpl()\n{\n    AMFVariantClear(&this->defaultValue);\n    AMFVariantClear(&this->minValue);\n    AMFVariantClear(&this->maxValue);\n}\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/PropertyStorageExImpl.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n///-------------------------------------------------------------------------\n///  @file   PropertyStorageExImpl.h\n///  @brief  AMFPropertyStorageExImpl header\n///-------------------------------------------------------------------------\n#ifndef AMF_PropertyStorageExImpl_h\n#define AMF_PropertyStorageExImpl_h\n#pragma once\n\n#include \"../include/core/PropertyStorageEx.h\"\n#include \"Thread.h\"\n#include \"InterfaceImpl.h\"\n#include \"ObservableImpl.h\"\n#include \"TraceAdapter.h\"\n#include <limits.h>\n#include <float.h>\n#include <memory>\n\nnamespace amf\n{\n\n    AMF_RESULT CastVariantToAMFProperty(AMFVariantStruct* pDest, const AMFVariantStruct* pSrc, AMF_VARIANT_TYPE eType,\n        AMF_PROPERTY_CONTENT_TYPE contentType,\n        const AMFEnumDescriptionEntry* pEnumDescription = 0);\n\n    //---------------------------------------------------------------------------------------------\n    class AMFPropertyInfoImpl : public AMFPropertyInfo\n    {\n    private:\n        amf_wstring m_name;\n        amf_wstring m_desc;\n\n        void Init(const wchar_t* name, const wchar_t* desc, AMF_VARIANT_TYPE type, AMF_PROPERTY_CONTENT_TYPE contentType,\n            AMFVariantStruct defaultValue, AMFVariantStruct minValue, AMFVariantStruct maxValue, AMF_PROPERTY_ACCESS_TYPE accessType,\n            const AMFEnumDescriptionEntry* pEnumDescription);\n\n    public:\n        AMFVariant  value;\n        amf_bool    userModified = false;\n\n    public:\n        AMFPropertyInfoImpl(const wchar_t* name, const wchar_t* desc, AMF_VARIANT_TYPE type, AMF_PROPERTY_CONTENT_TYPE contentType,\n            AMFVariantStruct defaultValue, AMFVariantStruct minValue, AMFVariantStruct maxValue, bool allowChangeInRuntime,\n            const AMFEnumDescriptionEntry* pEnumDescription);\n        AMFPropertyInfoImpl(const wchar_t* name, const wchar_t* desc, AMF_VARIANT_TYPE type, AMF_PROPERTY_CONTENT_TYPE contentType,\n            AMFVariantStruct defaultValue, AMFVariantStruct minValue, AMFVariantStruct maxValue, AMF_PROPERTY_ACCESS_TYPE accessType,\n            const AMFEnumDescriptionEntry* pEnumDescription);\n        AMFPropertyInfoImpl();\n\n        AMFPropertyInfoImpl(const AMFPropertyInfoImpl& propertyInfo);\n        AMFPropertyInfoImpl& operator=(const AMFPropertyInfoImpl& propertyInfo);\n\n        virtual ~AMFPropertyInfoImpl();\n\n        virtual void  OnPropertyChanged() { }\n    };\n\n    typedef amf_map<amf_wstring, std::shared_ptr<AMFPropertyInfoImpl> >  PropertyInfoMap;\n\n    //---------------------------------------------------------------------------------------------\n    template<typename _TBase> class AMFPropertyStorageExImpl :\n        public _TBase,\n        public AMFObservableImpl<AMFPropertyStorageObserver>\n    {\n    protected:\n        PropertyInfoMap   m_PropertiesInfo;\n\n    public:\n        AMFPropertyStorageExImpl()\n        {\n        }\n\n        virtual ~AMFPropertyStorageExImpl()\n        {\n        }\n\n\n        // interface access\n        AMF_BEGIN_INTERFACE_MAP\n            AMF_INTERFACE_ENTRY(AMFPropertyStorage)\n            AMF_INTERFACE_ENTRY(AMFPropertyStorageEx)\n        AMF_END_INTERFACE_MAP\n\n\n        using _TBase::GetProperty;\n        using _TBase::SetProperty;\n\n        // interface\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL Clear()\n        {\n            ResetDefaultValues();\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL AddTo(AMFPropertyStorage* pDest, bool overwrite, bool /*deep*/) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pDest);\n\n            if (pDest != this)\n            {\n                for (PropertyInfoMap::const_iterator it = m_PropertiesInfo.begin(); it != m_PropertiesInfo.end(); it++)\n                {\n                    if (!overwrite && pDest->HasProperty(it->first.c_str()))\n                    {\n                        continue;\n                    }\n\n                    AMF_RESULT err = pDest->SetProperty(it->first.c_str(), it->second->value);\n                    if (err != AMF_INVALID_ARG) // not validated - skip it\n                    {\n                        AMF_RETURN_IF_FAILED(err, L\"AddTo() - failed to copy property=%s\", it->first.c_str());\n                    }\n                }\n            }\n\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL CopyTo(AMFPropertyStorage* pDest, bool deep) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pDest);\n\n            if (pDest != this)\n            {\n                pDest->Clear();\n                return AddTo(pDest, true, deep);\n            }\n\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL SetProperty(const wchar_t* name, AMFVariantStruct value)\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n\n            const AMFPropertyInfo* pParamInfo = NULL;\n            AMF_RESULT err = GetPropertyInfo(name, &pParamInfo);\n            if (err != AMF_OK)\n            {\n                return err;\n            }\n\n            if (pParamInfo && !pParamInfo->AllowedWrite())\n            {\n                return AMF_ACCESS_DENIED;\n            }\n            return SetPrivateProperty(name, value);\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetProperty(const wchar_t* name, AMFVariantStruct* pValue) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n            AMF_RETURN_IF_INVALID_POINTER(pValue);\n\n            const AMFPropertyInfo* pParamInfo = NULL;\n            AMF_RESULT err = GetPropertyInfo(name, &pParamInfo);\n            if (err != AMF_OK)\n            {\n                return err;\n            }\n\n            if (pParamInfo && !pParamInfo->AllowedRead())\n            {\n                return AMF_ACCESS_DENIED;\n            }\n            return GetPrivateProperty(name, pValue);\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual bool        AMF_STD_CALL HasProperty(const wchar_t* name) const\n        {\n            const AMFPropertyInfo* pParamInfo = NULL;\n            AMF_RESULT err = GetPropertyInfo(name, &pParamInfo);\n            return (err != AMF_OK) ? false : true;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual amf_size    AMF_STD_CALL GetPropertyCount() const\n        {\n            return m_PropertiesInfo.size();\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetPropertyAt(amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n            AMF_RETURN_IF_INVALID_POINTER(pValue);\n            AMF_RETURN_IF_FALSE(nameSize != 0, AMF_INVALID_ARG);\n            AMF_RETURN_IF_FALSE(index < m_PropertiesInfo.size(), AMF_INVALID_ARG);\n\n            PropertyInfoMap::const_iterator found = m_PropertiesInfo.begin();\n            for (amf_size i = 0; i < index; i++)\n            {\n                found++;\n            }\n\n            size_t copySize = AMF_MIN(nameSize-1, found->first.length());\n            memcpy(name, found->first.c_str(), copySize * sizeof(wchar_t));\n            name[copySize] = 0;\n            AMFVariantCopy(pValue, &found->second->value);\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual amf_size    AMF_STD_CALL GetPropertiesInfoCount() const\n        {\n            return m_PropertiesInfo.size();\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetPropertyInfo(amf_size szInd, const AMFPropertyInfo** ppParamInfo) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(ppParamInfo);\n            AMF_RETURN_IF_FALSE(szInd < m_PropertiesInfo.size(), AMF_INVALID_ARG);\n\n            PropertyInfoMap::const_iterator it = m_PropertiesInfo.begin();\n            for (; szInd > 0; --szInd)\n            {\n                it++;\n            }\n\n            *ppParamInfo = it->second.get();\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetPropertyInfo(const wchar_t* name, const AMFPropertyInfo** ppParamInfo) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n            AMF_RETURN_IF_INVALID_POINTER(ppParamInfo);\n\n            PropertyInfoMap::const_iterator it = m_PropertiesInfo.find(name);\n            if (it != m_PropertiesInfo.end())\n            {\n                *ppParamInfo = it->second.get();\n                return AMF_OK;\n            }\n\n            return AMF_NOT_FOUND;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL ValidateProperty(const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n            AMF_RETURN_IF_INVALID_POINTER(pOutValidated);\n\n            AMF_RESULT err = AMF_OK;\n            const AMFPropertyInfo* pParamInfo = NULL;\n\n            AMF_RETURN_IF_FAILED(GetPropertyInfo(name, &pParamInfo), L\"Property=%s\", name);\n            AMF_RETURN_IF_FAILED(CastVariantToAMFProperty(pOutValidated, &value, pParamInfo->type, pParamInfo->contentType, pParamInfo->pEnumDescription), L\"Property=%s\", name);\n\n            switch(pParamInfo->type)\n            {\n            case AMF_VARIANT_INT64:\n                if((pParamInfo->minValue.type != AMF_VARIANT_EMPTY && AMFVariantGetInt64(pOutValidated) < AMFVariantGetInt64(&pParamInfo->minValue)) ||\n                    (pParamInfo->maxValue.type != AMF_VARIANT_EMPTY && AMFVariantGetInt64(pOutValidated) > AMFVariantGetInt64(&pParamInfo->maxValue)) )\n                {\n                    err = AMF_OUT_OF_RANGE;\n                }\n                break;\n\n            case AMF_VARIANT_DOUBLE:\n                if((AMFVariantGetDouble(pOutValidated) < AMFVariantGetDouble(&pParamInfo->minValue)) ||\n                   (AMFVariantGetDouble(pOutValidated) > AMFVariantGetDouble(&pParamInfo->maxValue)) )\n                {\n                    err = AMF_OUT_OF_RANGE;\n                }\n                break;\n            case AMF_VARIANT_FLOAT:\n                if ((AMFVariantGetFloat(pOutValidated) < AMFVariantGetFloat(&pParamInfo->minValue)) ||\n                    (AMFVariantGetFloat(pOutValidated) > AMFVariantGetFloat(&pParamInfo->maxValue)))\n                {\n                    err = AMF_OUT_OF_RANGE;\n                }\n                break;\n            case AMF_VARIANT_RATE:\n                {\n                    // NOTE: denominator can't be 0\n                    const AMFRate& validatedSize = AMFVariantGetRate(pOutValidated);\n                          AMFRate  minSize       = AMFConstructRate(0, 1);\n                          AMFRate  maxSize       = AMFConstructRate(INT_MAX, INT_MAX);\n                    if (pParamInfo->minValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        minSize = AMFVariantGetRate(&pParamInfo->minValue);\n                    }\n                    if (pParamInfo->maxValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        maxSize = AMFVariantGetRate(&pParamInfo->maxValue);\n                    }\n                    if (validatedSize.num < minSize.num || validatedSize.num > maxSize.num ||\n                        validatedSize.den < minSize.den || validatedSize.den > maxSize.den)\n                    {\n                        err = AMF_OUT_OF_RANGE;\n                    }\n                }\n                break;\n            case AMF_VARIANT_SIZE:\n                {\n                    AMFSize validatedSize = AMFVariantGetSize(pOutValidated);\n                    AMFSize minSize = AMFConstructSize(0, 0);\n                    AMFSize maxSize = AMFConstructSize(INT_MAX, INT_MAX);\n                    if (pParamInfo->minValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        minSize = AMFVariantGetSize(&pParamInfo->minValue);\n                    }\n                    if (pParamInfo->maxValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        maxSize = AMFVariantGetSize(&pParamInfo->maxValue);\n                    }\n                    if (validatedSize.width < minSize.width || validatedSize.height < minSize.height ||\n                        validatedSize.width > maxSize.width || validatedSize.height > maxSize.height)\n                    {\n                        err = AMF_OUT_OF_RANGE;\n                    }\n                }\n                break;\n            case AMF_VARIANT_FLOAT_SIZE:\n                {\n                    AMFFloatSize validatedSize = AMFVariantGetFloatSize(pOutValidated);\n                    AMFFloatSize minSize = AMFConstructFloatSize(0, 0);\n                    AMFFloatSize maxSize = AMFConstructFloatSize(FLT_MIN, FLT_MAX);\n                    if (pParamInfo->minValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        minSize = AMFVariantGetFloatSize(&pParamInfo->minValue);\n                    }\n                    if (pParamInfo->maxValue.type != AMF_VARIANT_EMPTY)\n                    {\n                        maxSize = AMFVariantGetFloatSize(&pParamInfo->maxValue);\n                    }\n                    if (validatedSize.width < minSize.width || validatedSize.height < minSize.height ||\n                        validatedSize.width > maxSize.width || validatedSize.height > maxSize.height)\n                    {\n                        err = AMF_OUT_OF_RANGE;\n                    }\n                }\n                break;\n            default:    //  GK: Clang issues a warning when not every value of an enum is handled in a switch-case\n                break;\n            }\n            return err;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL OnPropertyChanged(const wchar_t* /*name*/){ }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL AddObserver(AMFPropertyStorageObserver* pObserver) { AMFObservableImpl<AMFPropertyStorageObserver>::AddObserver(pObserver); }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL RemoveObserver(AMFPropertyStorageObserver* pObserver) { AMFObservableImpl<AMFPropertyStorageObserver>::RemoveObserver(pObserver); }\n        //-------------------------------------------------------------------------------------------------\n    protected:\n        //-------------------------------------------------------------------------------------------------\n        AMF_RESULT SetAccessType(const wchar_t* name, AMF_PROPERTY_ACCESS_TYPE accessType)\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n\n            PropertyInfoMap::iterator found = m_PropertiesInfo.find(name);\n            AMF_RETURN_IF_FALSE(found != m_PropertiesInfo.end(), AMF_NOT_FOUND);\n\n            if (found->second->accessType == accessType)\n            {\n                return AMF_OK;\n            }\n\n            found->second->accessType = accessType;\n            OnPropertyChanged(name);\n            NotifyObservers<const wchar_t*>(&AMFPropertyStorageObserver::OnPropertyChanged, name);\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        AMF_RESULT SetPrivateProperty(const wchar_t* name, AMFVariantStruct value)\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n\n            AMFVariant validatedValue;\n            AMF_RESULT validateResult = ValidateProperty(name, value, &validatedValue);\n            if (validateResult != AMF_OK)\n            {\n                return validateResult;\n            }\n\n            PropertyInfoMap::iterator found = m_PropertiesInfo.find(name);\n            if (found == m_PropertiesInfo.end())\n            {\n                return AMF_NOT_FOUND;\n            }\n\n            if (found->second->value == validatedValue)\n            {\n                return AMF_OK;\n            }\n\n            found->second->value = validatedValue;\n            found->second->OnPropertyChanged();\n            OnPropertyChanged(name);\n            NotifyObservers<const wchar_t*>(&AMFPropertyStorageObserver::OnPropertyChanged, name);\n\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        AMF_RESULT GetPrivateProperty(const wchar_t* name, AMFVariantStruct* pValue) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(name);\n            AMF_RETURN_IF_INVALID_POINTER(pValue);\n\n            PropertyInfoMap::const_iterator found = m_PropertiesInfo.find(name);\n            if (found != m_PropertiesInfo.end())\n            {\n                AMFVariantCopy(pValue, &found->second->value);\n                return AMF_OK;\n            }\n\n            // NOTE: needed for internal components that don't automatically\n            //       expose their properties in the main map...\n            const AMFPropertyInfo* pParamInfo;\n            if (GetPropertyInfo(name, &pParamInfo) == AMF_OK)\n            {\n                AMFVariantCopy(pValue, &pParamInfo->defaultValue);\n                return AMF_OK;\n            }\n\n            return AMF_NOT_FOUND;\n        }\n        //-------------------------------------------------------------------------------------------------\n        template<typename _T>\n        AMF_RESULT          AMF_STD_CALL SetPrivateProperty(const wchar_t* name, const _T& value)\n        {\n            AMF_RESULT err = SetPrivateProperty(name, static_cast<const AMFVariantStruct&>(AMFVariant(value)));\n            return err;\n        }\n        //-------------------------------------------------------------------------------------------------\n        template<typename _T>\n        AMF_RESULT          AMF_STD_CALL GetPrivateProperty(const wchar_t* name, _T* pValue) const\n        {\n            AMFVariant var;\n            AMF_RESULT err = GetPrivateProperty(name, static_cast<AMFVariantStruct*>(&var));\n            if(err == AMF_OK)\n            {\n                *pValue = static_cast<_T>(var);\n            }\n            return err;\n        }\n        //-------------------------------------------------------------------------------------------------\n        bool HasPrivateProperty(const wchar_t* name) const\n        {\n            return m_PropertiesInfo.find(name) != m_PropertiesInfo.end();\n        }\n        //-------------------------------------------------------------------------------------------------\n        bool  IsRuntimeChange(const wchar_t* name) const\n        {\n            PropertyInfoMap::const_iterator it = m_PropertiesInfo.find(name);\n            return (it != m_PropertiesInfo.end()) ? it->second->AllowedChangeInRuntime() : false;\n        }\n        //-------------------------------------------------------------------------------------------------\n        void  ResetDefaultValues()\n        {\n            // copy defaults to property storage\n            for (PropertyInfoMap::iterator it = m_PropertiesInfo.begin(); it != m_PropertiesInfo.end(); ++it)\n            {\n                AMFPropertyInfoImpl*  info = it->second.get();\n\n                info->value = info->defaultValue;\n                info->userModified = false;\n            }\n        }\n        //-------------------------------------------------------------------------------------------------\n\n    private:\n        AMFPropertyStorageExImpl(const AMFPropertyStorageExImpl&);\n        AMFPropertyStorageExImpl& operator=(const AMFPropertyStorageExImpl&);\n    };\n    extern AMFCriticalSection ms_csAMFPropertyStorageExImplMaps;\n    //---------------------------------------------------------------------------------------------\n\n\n#define AMFPrimitivePropertyInfoMapBegin \\\n        { \\\n            amf::AMFPropertyInfoImpl* s_PropertiesInfo[] = \\\n            { \n\n#define AMFPrimitivePropertyInfoMapEnd \\\n            }; \\\n            for (amf_size i = 0; i < sizeof(s_PropertiesInfo) / sizeof(s_PropertiesInfo[0]); ++i) \\\n            { \\\n                amf::AMFPropertyInfoImpl* pPropInfo = s_PropertiesInfo[i]; \\\n                m_PropertiesInfo[pPropInfo->name].reset(pPropInfo); \\\n            } \\\n    } \n\n\n    #define AMFPropertyInfoBool(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_BOOL, 0, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoEnum(_name, _desc, _defaultValue, pEnumDescription, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_INT64, 0, amf::AMFVariant(amf_int64(_defaultValue)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, pEnumDescription)\n\n    #define AMFPropertyInfoInt64(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_INT64, 0, amf::AMFVariant(amf_int64(_defaultValue)), \\\n                                     amf::AMFVariant(amf_int64(_minValue)), amf::AMFVariant(amf_int64(_maxValue)), _AccessType, 0)\n\n    #define AMFPropertyInfoDouble(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_DOUBLE, 0, amf::AMFVariant(amf_double(_defaultValue)), \\\n                                     amf::AMFVariant(amf_double(_minValue)), amf::AMFVariant(amf_double(_maxValue)), _AccessType, 0)\n\n    #define AMFPropertyInfoFloat(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_FLOAT, 0, amf::AMFVariant(amf_float(_defaultValue)), \\\n                                     amf::AMFVariant(amf_float(_minValue)), amf::AMFVariant(amf_float(_maxValue)), _AccessType, 0)\n\n\n    #define AMFPropertyInfoRect(_name, _desc, defaultLeft, defaultTop, defaultRight, defaultBottom, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_RECT, 0, amf::AMFVariant(AMFConstructRect(defaultLeft, defaultTop, defaultRight, defaultBottom)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoPoint(_name, _desc, defaultX, defaultY, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_POINT, 0, amf::AMFVariant(AMFConstructPoint(defaultX, defaultY)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoSize(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_SIZE, 0, amf::AMFVariant(AMFSize(_defaultValue)), \\\n                                     amf::AMFVariant(AMFSize(_minValue)), amf::AMFVariant(AMFSize(_maxValue)), _AccessType, 0)\n\n    #define AMFPropertyInfoFloatSize(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_FLOAT_SIZE, 0, amf::AMFVariant(AMFFloatSize(_defaultValue)), \\\n                                     amf::AMFVariant(AMFFloatSize(_minValue)), amf::AMFVariant(AMFFloatSize(_maxValue)), _AccessType, 0)\n\n    #define AMFPropertyInfoRate(_name, _desc, defaultNum, defaultDen, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_RATE, 0, amf::AMFVariant(AMFConstructRate(defaultNum, defaultDen)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoRateEx(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_RATE, 0, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(_minValue), amf::AMFVariant(_maxValue), _AccessType, 0)\n\n    #define AMFPropertyInfoRatio(_name, _desc, defaultNum, defaultDen, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_RATIO, 0, amf::AMFVariant(AMFConstructRatio(defaultNum, defaultDen)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoColor(_name, _desc, defaultR, defaultG, defaultB, defaultA, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_COLOR, 0, amf::AMFVariant(AMFConstructColor(defaultR, defaultG, defaultB, defaultA)), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n\n    #define AMFPropertyInfoString(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_STRING, 0, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoWString(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_WSTRING, 0, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoInterface(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_INTERFACE, 0, amf::AMFVariant(amf::AMFInterfacePtr(_defaultValue)), \\\n                                     amf::AMFVariant(amf::AMFInterfacePtr()), amf::AMFVariant(amf::AMFInterfacePtr()), _AccessType, 0)\n\n\n    #define AMFPropertyInfoXML(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_STRING, AMF_PROPERTY_CONTENT_XML, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoPath(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_WSTRING, AMF_PROPERTY_CONTENT_FILE_OPEN_PATH, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoSavePath(_name, _desc, _defaultValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_WSTRING, AMF_PROPERTY_CONTENT_FILE_SAVE_PATH, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(), amf::AMFVariant(), _AccessType, 0)\n\n    #define AMFPropertyInfoFloatVector4D(_name, _desc, _defaultValue, _minValue, _maxValue, _AccessType) \\\n        new amf::AMFPropertyInfoImpl(_name, _desc, amf::AMF_VARIANT_FLOAT_VECTOR4D, 0, amf::AMFVariant(_defaultValue), \\\n                                     amf::AMFVariant(_minValue), amf::AMFVariant(_maxValue), _AccessType, 0)\n\n} // namespace amf\n\n#endif // #ifndef AMF_PropertyStorageExImpl_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/PropertyStorageImpl.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n///-------------------------------------------------------------------------\n///  @file   PropertyStorageImpl.h\n///  @brief  AMFPropertyStorageImpl header\n///-------------------------------------------------------------------------\n#ifndef AMF_PropertyStorageImpl_h\n#define AMF_PropertyStorageImpl_h\n#pragma once\n\n#include \"../include/core/PropertyStorage.h\"\n#include \"Thread.h\"\n#include \"InterfaceImpl.h\"\n#include \"ObservableImpl.h\"\n#include \"TraceAdapter.h\"\n\nnamespace amf\n{\n    //---------------------------------------------------------------------------------------------\n    template<typename _TBase> class AMFPropertyStorageImpl :\n        public _TBase, \n        public AMFObservableImpl<AMFPropertyStorageObserver>\n    {\n    public:\n        //-------------------------------------------------------------------------------------------------\n        AMFPropertyStorageImpl() : m_PropertyValues()\n        {\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual ~AMFPropertyStorageImpl()\n        {\n        }\n        //-------------------------------------------------------------------------------------------------\n        // interface access\n        AMF_BEGIN_INTERFACE_MAP\n            AMF_INTERFACE_ENTRY(AMFPropertyStorage)\n        AMF_END_INTERFACE_MAP\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL SetProperty(const wchar_t* pName, AMFVariantStruct value)\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pName);\n\n            m_PropertyValues[pName] = value;\n            OnPropertyChanged(pName);\n            NotifyObservers<const wchar_t*>(&AMFPropertyStorageObserver::OnPropertyChanged, pName);\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetProperty(const wchar_t* pName, AMFVariantStruct* pValue) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pName);\n            AMF_RETURN_IF_INVALID_POINTER(pValue);\n\n            amf_wstring name(pName);\n            amf_map<amf_wstring, AMFVariant>::const_iterator found = m_PropertyValues.find(name);\n            if(found != m_PropertyValues.end())\n            {\n                AMFVariantCopy(pValue, &found->second);\n                return AMF_OK;\n            }\n            return AMF_NOT_FOUND;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual bool        AMF_STD_CALL HasProperty(const wchar_t* pName) const\n        {\n            AMF_ASSERT(pName != NULL);\n            return m_PropertyValues.find(pName) != m_PropertyValues.end();\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual amf_size    AMF_STD_CALL GetPropertyCount() const\n        {\n            return m_PropertyValues.size();\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL GetPropertyAt(amf_size index, wchar_t* pName, amf_size nameSize, AMFVariantStruct* pValue) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pName);\n            AMF_RETURN_IF_INVALID_POINTER(pValue);\n            AMF_RETURN_IF_FALSE(nameSize != 0, AMF_INVALID_ARG);\n            amf_map<amf_wstring, AMFVariant>::const_iterator found = m_PropertyValues.begin();\n            if(found == m_PropertyValues.end())\n            {\n                return AMF_INVALID_ARG;\n            }\n            for( amf_size i = 0; i < index; i++)\n            {\n                found++;\n                if(found == m_PropertyValues.end())\n                {\n                    return AMF_INVALID_ARG;\n                }\n            }\n            size_t copySize = AMF_MIN(nameSize-1, found->first.length());\n            memcpy(pName, found->first.c_str(), copySize * sizeof(wchar_t));\n            pName[copySize] = 0;\n            AMFVariantCopy(pValue, &found->second);\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL Clear()\n        {\n            m_PropertyValues.clear();\n            return AMF_OK;\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL AddTo(AMFPropertyStorage* pDest, bool overwrite, bool /*deep*/) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pDest);\n            AMF_RESULT err = AMF_OK;\n            amf_map<amf_wstring, AMFVariant>::const_iterator it = m_PropertyValues.begin();\n\n            for(; it != m_PropertyValues.end(); it++)\n            {\n                if(!HasProperty(it->first.c_str())) // ignore properties which aren't accessible\n                {\n                    continue;\n                }\n\n                if(!overwrite)\n                {\n                    if(pDest->HasProperty(it->first.c_str()))\n                    {\n                        continue;\n                    }\n                }\n                {\n                    err = pDest->SetProperty(it->first.c_str(), it->second);\n                }\n                if(err == AMF_ACCESS_DENIED)\n                {\n                    continue;\n                }\n                AMF_RETURN_IF_FAILED(err, L\"AddTo() - failed to copy property=%s\", it->first.c_str());\n            }\n            return AMF_OK;\n        }        \n        //-------------------------------------------------------------------------------------------------\n        virtual AMF_RESULT  AMF_STD_CALL CopyTo(AMFPropertyStorage* pDest, bool deep) const\n        {\n            AMF_RETURN_IF_INVALID_POINTER(pDest);\n            if(pDest != this)\n            {\n                pDest->Clear();\n                return AddTo(pDest, true, deep);\n            }\n            else\n            {\n                return AMF_OK;\n            }\n        }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL OnPropertyChanged(const wchar_t* /*name*/) { }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL AddObserver(AMFPropertyStorageObserver* pObserver) { AMFObservableImpl<AMFPropertyStorageObserver>::AddObserver(pObserver); }\n        //-------------------------------------------------------------------------------------------------\n        virtual void        AMF_STD_CALL RemoveObserver(AMFPropertyStorageObserver* pObserver) { AMFObservableImpl<AMFPropertyStorageObserver>::RemoveObserver(pObserver); }\n        //-------------------------------------------------------------------------------------------------\n    protected:\n        //-------------------------------------------------------------------------------------------------\n        amf_map<amf_wstring, AMFVariant> m_PropertyValues;\n    };\n    //---------------------------------------------------------------------------------------------\n    //---------------------------------------------------------------------------------------------\n}\n#endif // AMF_PropertyStorageImpl_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/Thread.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#if defined(_WIN32)\n#include <process.h>\n#else\n#include <pthread.h>\n#endif\n#include \"Thread.h\"\n\n#if defined(METRO_APP)\n    #include <ppl.h>\n    #include <ppltasks.h>\n#endif\n\n\n\nnamespace amf\n{\n    //----------------------------------------------------------------------------\n    AMFEvent::AMFEvent(bool bInitiallyOwned, bool bManualReset, const wchar_t* pName) : m_hSyncObject()\n    {\n        m_hSyncObject = amf_create_event(bInitiallyOwned, bManualReset, pName);\n    }\n    //----------------------------------------------------------------------------\n    AMFEvent::~AMFEvent()\n    {\n        amf_delete_event(m_hSyncObject);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFEvent::Lock(amf_ulong ulTimeout)\n    {\n        return amf_wait_for_event(m_hSyncObject, ulTimeout);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFEvent::LockTimeout(amf_ulong ulTimeout)\n    {\n        return amf_wait_for_event_timeout(m_hSyncObject, ulTimeout);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFEvent::Unlock()\n    {\n        return true;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFEvent::SetEvent()\n    {\n        return amf_set_event(m_hSyncObject);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFEvent::ResetEvent()\n    {\n        return amf_reset_event(m_hSyncObject);\n    }\n    //----------------------------------------------------------------------------\n    //----------------------------------------------------------------------------\n    AMFMutex::AMFMutex(bool bInitiallyOwned, const wchar_t* pName\n                   #if defined(_WIN32)\n                       , bool bOpenExistent\n                   #endif\n                       ):m_hSyncObject()\n    {\n    #if defined(_WIN32)\n        if(bOpenExistent)\n        {\n            m_hSyncObject = amf_open_mutex(pName);\n        }\n        else\n    #else\n    //#pragma message AMF_TODO(\"Open mutex!!! missing functionality in Linux!!!\")\n    #endif\n        {\n            m_hSyncObject = amf_create_mutex(bInitiallyOwned, pName);\n        }\n    }\n    //----------------------------------------------------------------------------\n    AMFMutex::~AMFMutex()\n    {\n        if(m_hSyncObject)\n        {\n            amf_delete_mutex(m_hSyncObject);\n        }\n    }\n    //----------------------------------------------------------------------------\n    bool AMFMutex::Lock(amf_ulong ulTimeout)\n    {\n        if(m_hSyncObject)\n        {\n            return amf_wait_for_mutex(m_hSyncObject, ulTimeout);\n        }\n        else\n        {\n            return false;\n        }\n    }\n    //----------------------------------------------------------------------------\n    bool AMFMutex::Unlock()\n    {\n        if(m_hSyncObject)\n        {\n            return amf_release_mutex(m_hSyncObject);\n        }\n        else\n        {\n            return false;\n        }\n    }\n    //----------------------------------------------------------------------------\n    bool AMFMutex::IsValid()\n    {\n        return m_hSyncObject != NULL;\n    }\n    //----------------------------------------------------------------------------\n    //----------------------------------------------------------------------------\n    AMFCriticalSection::AMFCriticalSection() : m_Sect()\n    {\n        m_Sect = amf_create_critical_section();\n    }\n    //----------------------------------------------------------------------------\n    AMFCriticalSection::~AMFCriticalSection()\n    {\n        amf_delete_critical_section(m_Sect);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFCriticalSection::Lock(amf_ulong ulTimeout)\n    {\n        return (ulTimeout != AMF_INFINITE) ? amf_wait_critical_section(m_Sect, ulTimeout)\n                                           : amf_enter_critical_section(m_Sect);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFCriticalSection::Unlock()\n    {\n        return amf_leave_critical_section(m_Sect);\n    }\n    //----------------------------------------------------------------------------\n    AMFSemaphore::AMFSemaphore(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName)\n        : m_hSemaphore(NULL)\n    {\n        Create(iInitCount, iMaxCount, pName);\n    }\n    //----------------------------------------------------------------------------\n    AMFSemaphore::~AMFSemaphore()\n    {\n        amf_delete_semaphore(m_hSemaphore);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFSemaphore::Create(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName)\n    {\n        if(m_hSemaphore != NULL)  // delete old one\n        {\n            amf_delete_semaphore(m_hSemaphore);\n            m_hSemaphore = NULL;\n        }\n        if(iMaxCount > 0)\n        {\n            m_hSemaphore = amf_create_semaphore(iInitCount, iMaxCount, pName);\n        }\n        return true;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFSemaphore::Lock(amf_ulong ulTimeout)\n    {\n        return amf_wait_for_semaphore(m_hSemaphore, ulTimeout);\n    }\n    //----------------------------------------------------------------------------\n    bool AMFSemaphore::Unlock()\n    {\n        amf_long iOldCount = 0;\n        return amf_release_semaphore(m_hSemaphore, 1, &iOldCount);\n    }\n    //----------------------------------------------------------------------------\n    AMFLock::AMFLock(AMFSyncBase* pBase, amf_ulong ulTimeout)\n        : m_pBase(pBase),\n        m_bLocked()\n    {\n        m_bLocked = Lock(ulTimeout);\n    }\n    //----------------------------------------------------------------------------\n    AMFLock::~AMFLock()\n    {\n        if (IsLocked() == true)\n        {\n            Unlock();\n        }\n    }\n    //----------------------------------------------------------------------------\n    bool AMFLock::Lock(amf_ulong ulTimeout)\n    {\n        if(m_pBase == NULL)\n        {\n            return false;\n        }\n        m_bLocked = m_pBase->Lock(ulTimeout);\n        return m_bLocked;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFLock::Unlock()\n    {\n        if(m_pBase == NULL)\n        {\n            return false;\n        }\n        const bool  unlockSucceeded = m_pBase->Unlock();\n        m_bLocked = m_bLocked && (unlockSucceeded == false);\n        return unlockSucceeded;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFLock::IsLocked()\n    {\n        return m_bLocked;\n    }\n    //----------------------------------------------------------------------------\n\n#if defined(METRO_APP)\n    using namespace Platform;\n    using namespace Windows::Foundation;\n    using namespace Windows::UI::Xaml;\n    using namespace Windows::UI::Xaml::Controls;\n    using namespace Windows::UI::Xaml::Navigation;\n        class AMFThreadObj\n        {\n            Windows::Foundation::IAsyncAction^      m_AsyncAction;\n            AMFEvent                                m_StopEvent;\n            AMFThread*                              m_pOwner;\n        public:\n            AMFThreadObj(AMFThread* owner);\n            virtual ~AMFThreadObj();\n\n            virtual bool Start();\n            virtual bool RequestStop();\n            virtual bool WaitForStop();\n            virtual bool StopRequested();\n\n            // this is executed in the thread and overloaded by implementor\n            virtual void Run() { m_pOwner->Run(); }\n            virtual bool Init(){ return m_pOwner->Init(); }\n            virtual bool Terminate(){ return m_pOwner->Terminate();}\n        };\n\n\n    AMFThreadObj::AMFThreadObj(AMFThread* owner)\n        : m_StopEvent(true, true), m_pOwner(owner)\n    {}\n\n    AMFThreadObj::~AMFThreadObj()\n    {}\n\n    bool AMFThreadObj::Start()\n    {\n        auto workItemDelegate = [this](IAsyncAction ^ workItem)\n        {\n            if( !this->Init() )\n            {\n                return;\n            }\n\n            this->Run();\n            this->Terminate();\n\n            this->m_AsyncAction = nullptr;\n            if( this->StopRequested() )\n            {\n                this->m_StopEvent.SetEvent();\n            }\n\n        };\n\n        Windows::System::Threading::WorkItemPriority WorkPriority;\n        WorkPriority = Windows::System::Threading::WorkItemPriority::Normal;\n\n        auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler(workItemDelegate);\n        m_AsyncAction = Windows::System::Threading::ThreadPool::RunAsync(workItemHandler, WorkPriority);\n\n        return true;\n    }\n\n    bool AMFThreadObj::RequestStop()\n    {\n        if( m_AsyncAction == nullptr )\n        {\n            return true;\n        }\n\n        m_StopEvent.ResetEvent();\n        return true;\n    }\n\n    bool AMFThreadObj::WaitForStop()\n    {\n        if( m_AsyncAction == nullptr )\n        {\n            return true;\n        }\n\n        return m_StopEvent.Lock();\n    }\n\n    bool AMFThreadObj::StopRequested()\n    {\n        return !m_StopEvent.Lock(0);\n    }\n    bool AMFThreadObj::IsRunning()\n    {\n        return m_AsyncAction != nullptr;\n    }\n\n    void amf::ExitThread()\n    {}\n\n    //#endif//#if defined(METRO_APP)\n    //#if defined(_WIN32)\n#elif defined(_WIN32)   // _WIN32 and METRO_APP defines are not mutually exclusive\n    class AMFThreadObj\n    {\n        AMFThread*      m_pOwner;\n        uintptr_t       m_pThread;\n        AMFEvent        m_StopEvent;\n        AMFCriticalSection m_Lock;\n    public:\n        // this is called by owner\n        AMFThreadObj(AMFThread* owner);\n        virtual ~AMFThreadObj();\n\n        virtual bool Start();\n        virtual bool RequestStop();\n        virtual bool WaitForStop();\n        virtual bool StopRequested();\n        virtual bool IsRunning();\n\n\n    protected:\n        static void AMF_CDECL_CALL AMFThreadProc(void* pThis);\n\n        // this is executed in the thread and overloaded by implementor\n        virtual void Run()\n        {\n            m_pOwner->Run();\n        }\n        virtual bool Init()\n        {\n            return m_pOwner->Init();\n        }\n        virtual bool Terminate()\n        {\n            return m_pOwner->Terminate();\n        }\n    };\n    //----------------------------------------------------------------------------\n    AMFThreadObj::AMFThreadObj(AMFThread* owner) :\n        m_pOwner(owner),\n        m_pThread(uintptr_t(-1)),\n        m_StopEvent(true, true)\n    {}\n    //----------------------------------------------------------------------------\n    AMFThreadObj::~AMFThreadObj()\n    {\n        //    RequestStop();\n        //    WaitForStop();\n    }\n    //----------------------------------------------------------------------------\n    void AMF_CDECL_CALL AMFThreadObj::AMFThreadProc(void* pThis)\n    {\n        AMFThreadObj* pT = (AMFThreadObj*)pThis;\n        if(!pT->Init())\n        {\n            return;\n        }\n        pT->Run();\n        pT->Terminate();\n\n        pT->m_pThread = uintptr_t(-1);\n        if(pT->StopRequested())\n        {\n            pT->m_StopEvent.SetEvent(); // signal to stop that we just finished\n        }\n    }\n    //----------------------------------------------------------------------------\n    bool AMFThreadObj::Start()\n    {\n        if(m_pThread != (uintptr_t)-1L)\n        {\n            return true;\n        }\n        AMFLock lock(&m_Lock);\n        m_pThread = _beginthread(AMFThreadProc, 0, (void* )this);\n\n        return m_pThread != (uintptr_t)-1L;\n    }\n\n    //----------------------------------------------------------------------------\n    bool AMFThreadObj::RequestStop()\n    {\n        if(m_pThread == (uintptr_t)-1L)\n        {\n            return true;\n        }\n\n        m_StopEvent.ResetEvent();\n        return true;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFThreadObj::WaitForStop()\n    {\n        AMFLock lock(&m_Lock);\n        if(m_pThread == (uintptr_t)-1L)\n        {\n            return true;\n        }\n        bool stopped = m_StopEvent.Lock();\n        \n        m_pThread = (uintptr_t)-1L;\n        return stopped;\n    }\n    //----------------------------------------------------------------------------\n    bool AMFThreadObj::StopRequested()\n    {\n        return !m_StopEvent.Lock(0);\n    }\n    bool AMFThreadObj::IsRunning()\n    {\n        return m_pThread != (uintptr_t)-1L;\n    }\n    //----------------------------------------------------------------------------\n    void ExitThread()\n    {\n        _endthread();\n    }\n\n#endif //#if defined(_WIN32)\n#if defined(__linux) || defined(__APPLE__)\n    class AMFThreadObj\n    {\n    public:\n        AMFThreadObj(AMFThread* owner);\n        virtual ~AMFThreadObj();\n\n        virtual bool Start();\n        virtual bool RequestStop();\n        virtual bool WaitForStop();\n        virtual bool StopRequested();\n        virtual bool IsRunning();\n\n        // this is executed in the thread and overloaded by implementor\n        virtual void Run() { m_pOwner->Run(); }\n        virtual bool Init(){ return m_pOwner->Init(); }\n        virtual bool Terminate(){ return m_pOwner->Terminate();}\n\n    private:\n        AMFThread*      m_pOwner;\n        pthread_t m_hThread;\n        bool m_bStopRequested;\n        bool m_bRunning;\n        bool m_bInternalRunning; //used to detect thread auto-exit case and make join in Start\n        AMFCriticalSection m_Lock;\n\n        AMFThreadObj(const AMFThreadObj&);\n        AMFThreadObj& operator=(const AMFThreadObj&);\n        static void* AMF_CDECL_CALL AMFThreadProc(void* pThis);\n\n    };\n\n    AMFThreadObj::AMFThreadObj(AMFThread* owner)\n        : m_pOwner(owner),\n        m_bStopRequested(false),\n        m_bRunning(false),\n        m_bInternalRunning(false)\n    {\n    }\n\n    AMFThreadObj::~AMFThreadObj()\n    {\n        RequestStop();\n        WaitForStop();\n    }\n\n    void* AMF_CDECL_CALL AMFThreadObj::AMFThreadProc(void* pThis)\n    {\n        AMFThreadObj* pT = (AMFThreadObj*)pThis;\n        if(!pT->Init())\n        {\n            return 0;\n        }\n\n        pT->Run();\n        pT->Terminate();\n        pT->m_bStopRequested = false;\n        pT->m_bInternalRunning = false;\n        return 0;\n    }\n\n    bool AMFThreadObj::Start()\n    {\n        bool result = true;\n        if(m_bRunning == true && m_bInternalRunning == false)\n        {\n            pthread_join(m_hThread, 0);\n            m_bRunning = false;\n            m_bStopRequested = false;\n        }\n\n        if (IsRunning() == false)\n        {\n            WaitForStop();\n\n            AMFLock lock(&m_Lock);\n            if (pthread_create(&m_hThread, 0, AMFThreadProc, (void*)this) == 0)\n            {\n                m_bRunning = true;\n                m_bInternalRunning = true;\n            }\n            else\n            {\n                result = false;\n            }\n        }\n        return result;\n    }\n\n    bool AMFThreadObj::RequestStop()\n    {\n        AMFLock lock(&m_Lock);\n        if (IsRunning() == false)\n        {\n            return true;\n        }\n      \n        m_bStopRequested = true;\n        return true;\n    }\n\n    bool AMFThreadObj::WaitForStop()\n    {\n        AMFLock lock(&m_Lock);\n\n        if (IsRunning() == true)\n        {\n            pthread_join(m_hThread, 0);\n            m_bRunning = false;\n        }\n            \n        m_bStopRequested = false;\n        return true;\n    }\n\n    bool AMFThreadObj::StopRequested()\n    {\n        return m_bStopRequested;\n    }\n\n    bool AMFThreadObj::IsRunning()\n    {\n        return m_bRunning && m_bInternalRunning;\n    }\n\n    void ExitThread()\n    {\n        pthread_exit(0);\n    }\n\n#endif //#if defined(__linux)\n\n    AMFThread::AMFThread() : m_thread()\n    {\n        m_thread = new AMFThreadObj(this);\n    }\n\n    AMFThread::~AMFThread()\n    {\n        delete m_thread;\n    }\n\n    bool AMFThread::Start()\n    {\n        return m_thread->Start();\n    }\n\n    bool AMFThread::RequestStop()\n    {\n        return m_thread->RequestStop();\n    }\n\n    bool AMFThread::WaitForStop()\n    {\n        return m_thread->WaitForStop();\n    }\n\n    bool AMFThread::StopRequested()\n    {\n        return m_thread->StopRequested();\n    }\n    bool AMFThread::IsRunning() const\n    {\n        return m_thread->IsRunning();\n    }\n} //namespace\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/Thread.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Thread_h\n#define AMF_Thread_h\n#pragma once\n\n#include <cassert>\n#include <list>\n#include <vector>\n\n#include \"../include/core/Platform.h\"\n\n#ifndef _WIN32\n#include <pthread.h>\n#endif\n\nextern \"C\"\n{\n    // threads\n    #define AMF_INFINITE        (0xFFFFFFFF) // Infinite ulTimeout\n\n    // threads: atomic\n    amf_long    AMF_CDECL_CALL amf_atomic_inc(amf_long* X);\n    amf_long    AMF_CDECL_CALL amf_atomic_dec(amf_long* X);\n\n    // threads: critical section\n    amf_handle  AMF_CDECL_CALL amf_create_critical_section();\n    bool        AMF_CDECL_CALL amf_delete_critical_section(amf_handle cs);\n    bool        AMF_CDECL_CALL amf_enter_critical_section(amf_handle cs);\n    bool        AMF_CDECL_CALL amf_wait_critical_section(amf_handle cs, amf_ulong ulTimeout);\n    bool        AMF_CDECL_CALL amf_leave_critical_section(amf_handle cs);\n    // threads: event\n    amf_handle  AMF_CDECL_CALL amf_create_event(bool bInitiallyOwned, bool bManualReset, const wchar_t* pName);\n    bool        AMF_CDECL_CALL amf_delete_event(amf_handle hevent);\n    bool        AMF_CDECL_CALL amf_set_event(amf_handle hevent);\n    bool        AMF_CDECL_CALL amf_reset_event(amf_handle hevent);\n    bool        AMF_CDECL_CALL amf_wait_for_event(amf_handle hevent, amf_ulong ulTimeout);\n    bool        AMF_CDECL_CALL amf_wait_for_event_timeout(amf_handle hevent, amf_ulong ulTimeout);\n\n    // threads: mutex\n    amf_handle  AMF_CDECL_CALL amf_create_mutex(bool bInitiallyOwned, const wchar_t* pName);\n#if defined(_WIN32)\n    amf_handle  AMF_CDECL_CALL amf_open_mutex(const wchar_t* pName);\n#endif\n    bool        AMF_CDECL_CALL amf_delete_mutex(amf_handle hmutex);\n    bool        AMF_CDECL_CALL amf_wait_for_mutex(amf_handle hmutex, amf_ulong ulTimeout);\n    bool        AMF_CDECL_CALL amf_release_mutex(amf_handle hmutex);\n\n    // threads: semaphore\n    amf_handle  AMF_CDECL_CALL amf_create_semaphore(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName);\n    bool        AMF_CDECL_CALL amf_delete_semaphore(amf_handle hsemaphore);\n    bool        AMF_CDECL_CALL amf_wait_for_semaphore(amf_handle hsemaphore, amf_ulong ulTimeout);\n    bool        AMF_CDECL_CALL amf_release_semaphore(amf_handle hsemaphore, amf_long iCount, amf_long* iOldCount);\n\n    // threads: delay\n    void        AMF_CDECL_CALL amf_sleep(amf_ulong delay);\n    amf_pts     AMF_CDECL_CALL amf_high_precision_clock();    // in 100 of nanosec\n\n    void        AMF_CDECL_CALL amf_increase_timer_precision();\n    void        AMF_CDECL_CALL amf_restore_timer_precision();\n\n    amf_handle  AMF_CDECL_CALL amf_load_library(const wchar_t* filename);\n    amf_handle  AMF_CDECL_CALL amf_load_library1(const wchar_t* filename, bool bGlobal);\n\n    void*       AMF_CDECL_CALL amf_get_proc_address(amf_handle module, const char* procName);\n    int         AMF_CDECL_CALL amf_free_library(amf_handle module);\n\n#if defined(__APPLE__)\n    amf_uint64 AMF_STD_CALL get_current_thread_id();\n#else\n    amf_uint32 AMF_STD_CALL get_current_thread_id();\n#endif\n\n#if !defined(METRO_APP)\n    // virtual memory\n    void*       AMF_CDECL_CALL amf_virtual_alloc(amf_size size);\n    void        AMF_CDECL_CALL amf_virtual_free(void* ptr);\n#else\n    #define amf_virtual_alloc amf_alloc\n    #define amf_virtual_free amf_free\n#endif\n\n    // cpu\n#if defined(_WIN32) || (__linux__)\n    amf_int32   AMF_STD_CALL  amf_get_cpu_cores();\n#endif\n\n}\n\nnamespace amf\n{\n    //----------------------------------------------------------------\n    class AMF_NO_VTABLE AMFSyncBase\n    {\n    public:\n        virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE) = 0;\n        virtual bool Unlock() = 0;\n    };\n    //----------------------------------------------------------------\n    class AMFEvent : public AMFSyncBase\n    {\n    private:\n        amf_handle m_hSyncObject;\n\n        AMFEvent(const AMFEvent&);\n        AMFEvent& operator=(const AMFEvent&);\n\n    public:\n        AMFEvent(bool bInitiallyOwned = false, bool bManualReset = false, const wchar_t* pName = NULL);\n        virtual ~AMFEvent();\n\n        virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE);\n        virtual bool LockTimeout(amf_ulong ulTimeout = AMF_INFINITE);\n        virtual bool Unlock();\n        bool SetEvent();\n        bool ResetEvent();\n        amf_handle GetNative() { return m_hSyncObject; }\n    };\n    //----------------------------------------------------------------\n    class AMFMutex : public AMFSyncBase\n    {\n    private:\n        amf_handle m_hSyncObject;\n\n        AMFMutex(const AMFMutex&);\n        AMFMutex& operator=(const AMFMutex&);\n\n    public:\n        AMFMutex(bool bInitiallyOwned = false, const wchar_t* pName = NULL\n        #if defined(_WIN32)\n            , bool bOpenExistent = false\n        #endif\n            );\n        virtual ~AMFMutex();\n\n        virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE);\n        virtual bool Unlock();\n        bool IsValid();\n    };\n    //----------------------------------------------------------------\n    class AMFCriticalSection : public AMFSyncBase\n    {\n    private:\n        amf_handle m_Sect;\n\n        AMFCriticalSection(const AMFCriticalSection&);\n        AMFCriticalSection& operator=(const AMFCriticalSection&);\n\n    public:\n        AMFCriticalSection();\n        virtual ~AMFCriticalSection();\n\n        virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE);\n        virtual bool Unlock();\n    };\n    //----------------------------------------------------------------\n    class AMFSemaphore : public AMFSyncBase\n    {\n    private:\n        amf_handle m_hSemaphore;\n\n        AMFSemaphore(const AMFSemaphore&);\n        AMFSemaphore& operator=(const AMFSemaphore&);\n\n    public:\n        AMFSemaphore(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName = NULL);\n        virtual ~AMFSemaphore();\n\n        virtual bool Create(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName = NULL);\n        virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE);\n        virtual bool Unlock();\n    };\n    //----------------------------------------------------------------\n    class AMFLock\n    {\n    private:\n        AMFSyncBase* m_pBase;\n        bool m_bLocked;\n\n        AMFLock(const AMFLock&);\n        AMFLock& operator=(const AMFLock&);\n\n    public:\n        AMFLock(AMFSyncBase* pBase, amf_ulong ulTimeout = AMF_INFINITE);\n        ~AMFLock();\n\n        bool Lock(amf_ulong ulTimeout = AMF_INFINITE);\n        bool Unlock();\n        bool IsLocked();\n    };\n    //----------------------------------------------------------------\n    class AMFReadWriteSync\n    {\n    private:\n        struct ReadWriteResources\n        {\n            // max threads reading concurrently\n            const int m_maxReadThreads;\n            AMFSemaphore m_readSemaphore;\n            AMFCriticalSection m_writeCriticalSection;\n            ReadWriteResources() : \n            m_maxReadThreads(10),\n                m_readSemaphore(m_maxReadThreads, m_maxReadThreads),\n                m_writeCriticalSection()\n            { }\n        };\n        class ReadSync : public AMFSyncBase\n        {\n        private:\n            ReadSync(const ReadSync&);\n            ReadSync& operator=(const ReadSync&);\n\n            ReadWriteResources& m_resources;\n        public:\n            ReadSync(ReadWriteResources& resources) : m_resources(resources)\n            { }\n            virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE)\n            {\n                return m_resources.m_readSemaphore.Lock(ulTimeout);\n            }\n            virtual bool Unlock()\n            {\n                return m_resources.m_readSemaphore.Unlock();\n            }\n        };\n        class WriteSync : public AMFSyncBase\n        {\n        private:\n            WriteSync(const WriteSync&);\n            WriteSync& operator=(const WriteSync&);\n\n            ReadWriteResources& m_resources;\n        public:\n            WriteSync(ReadWriteResources& resources) : m_resources(resources)\n            { }\n            /// waits passed timeout for other writers; wait readers for infinite\n            virtual bool Lock(amf_ulong ulTimeout = AMF_INFINITE)\n            {\n                if(!m_resources.m_writeCriticalSection.Lock(ulTimeout))\n                {\n                    return false;\n                }\n                for(int i = 0; i < m_resources.m_maxReadThreads; i++)\n                {\n                    m_resources.m_readSemaphore.Lock();\n                }\n                return true;\n            }\n            virtual bool Unlock()\n            {\n                // there is windows function to release N times by one call - could be optimize later\n                for(int i = 0; i < m_resources.m_maxReadThreads; i++)\n                {\n                    m_resources.m_readSemaphore.Unlock();\n                }\n                return m_resources.m_writeCriticalSection.Unlock();\n            }\n        };\n    private:\n        ReadWriteResources m_resources;\n        ReadSync m_readSync;\n        WriteSync m_writeSync;\n    public:\n        AMFReadWriteSync() :\n            m_resources(),\n            m_readSync(m_resources),\n            m_writeSync(m_resources)\n        { }\n\n        AMFSyncBase* GetReadSync()\n        {\n            return &m_readSync;\n        }\n        AMFSyncBase* GetWriteSync()\n        {\n            return &m_writeSync;\n        }\n    };\n    //----------------------------------------------------------------\n    class AMFThreadObj;\n    class AMFThread\n    {\n        friend class AMFThreadObj;\n    public:\n        AMFThread();\n        virtual ~AMFThread();\n\n        virtual bool Start();\n        virtual bool RequestStop();\n        virtual bool WaitForStop();\n        virtual bool StopRequested();\n        virtual bool IsRunning() const;\n\n    protected:\n        // this is executed in the thread and overloaded by implementor\n        virtual void Run() = 0;\n        virtual bool Init()\n        {\n            return true;\n        }\n        virtual bool Terminate()\n        {\n            return true;\n        }\n    private:\n        AMFThreadObj* m_thread;\n\n        AMFThread(const AMFThread&);\n        AMFThread& operator=(const AMFThread&);\n    };\n\n    void ExitThread();\n    //----------------------------------------------------------------\n    template<typename T>\n    class AMFQueue\n    {\n    protected:\n        class ItemData\n        {\n        public:\n            T data;\n            amf_ulong ulID;\n            amf_long ulPriority;\n            ItemData() : data(), ulID(), ulPriority(){}\n        };\n        typedef std::list< ItemData > QueueList;\n\n        QueueList m_Queue;\n        AMFCriticalSection m_cSect;\n        AMFEvent m_SomethingInQueueEvent;\n        AMFSemaphore m_QueueSizeSem;\n        amf_int32 m_iQueueSize;\n\n        bool InternalGet(amf_ulong& ulID, T& item)\n        {\n            AMFLock lock(&m_cSect);\n            if(!m_Queue.empty())  // something to get\n            {\n                ItemData& itemdata = m_Queue.front();\n                ulID = itemdata.ulID;\n                item = itemdata.data;\n                m_Queue.pop_front();\n                m_QueueSizeSem.Unlock();\n                if(m_Queue.empty())\n                {\n                    m_SomethingInQueueEvent.ResetEvent();\n                }\n                return true;\n            }\n            return false;\n        }\n    public:\n        AMFQueue(amf_int32 iQueueSize = 0)\n            : m_Queue(),\n            m_cSect(),\n            m_SomethingInQueueEvent(false, false),\n            m_QueueSizeSem(iQueueSize, iQueueSize > 0 ? iQueueSize + 1 : 0),\n            m_iQueueSize(iQueueSize) {}\n        virtual ~AMFQueue(){}\n\n        virtual bool SetQueueSize(amf_int32 iQueueSize)\n        {\n            bool success = m_QueueSizeSem.Create(iQueueSize, iQueueSize > 0 ? iQueueSize + 1 : 0);\n            if(success)\n            {\n                m_iQueueSize = iQueueSize;\n            }\n            return success;\n        }\n        virtual amf_int32 GetQueueSize()\n        {\n            return m_iQueueSize;\n        }\n        virtual bool Add(amf_ulong ulID, const T& item, amf_long ulPriority = 0, amf_ulong ulTimeout = AMF_INFINITE)\n        {\n            if(m_QueueSizeSem.Lock(ulTimeout) == false)\n            {\n                return false;\n            }\n            {\n                AMFLock lock(&m_cSect);\n\n\n                ItemData itemdata;\n                itemdata.ulID = ulID;\n                itemdata.data = item;\n                itemdata.ulPriority = ulPriority;\n\n                typename QueueList::iterator iter = m_Queue.end();\n\n                for(; iter != m_Queue.begin(); )\n                {\n                    iter--;\n                    if(ulPriority <= (iter->ulPriority))\n                    {\n                        iter++;\n                        break;\n                    }\n                }\n                m_Queue.insert(iter, itemdata);\n                m_SomethingInQueueEvent.SetEvent(); // this will set all waiting threads - some of them get data, some of them not\n            }\n            return true;\n        }\n\n        virtual bool Get(amf_ulong& ulID, T& item, amf_ulong ulTimeout)\n        {\n            if(InternalGet(ulID, item))  // try right away\n            {\n                return true;\n            }\n            // wait for queue\n            if(m_SomethingInQueueEvent.Lock(ulTimeout))\n            {\n                return InternalGet(ulID, item);\n            }\n            return false;\n        }\n        virtual void Clear()\n        {\n            bool bValue = true;\n            while(bValue)\n            {\n                amf_ulong ulID;\n                T item;\n                bValue = InternalGet(ulID, item);\n            }\n        }\n        virtual amf_size GetSize()\n        {\n            AMFLock lock(&m_cSect);\n            return m_Queue.size();\n        }\n    };\n    //----------------------------------------------------------------\n    template<class inT, class outT>\n    class AMFQueueThread : public AMFThread\n    {\n    private:\n        AMFQueueThread(const AMFQueueThread&);\n        AMFQueueThread& operator=(const AMFQueueThread&);\n\n    protected:\n        AMFQueue<inT>* m_pInQueue;\n        AMFQueue<outT>* m_pOutQueue;\n        AMFMutex m_mutexInProcess;  ///< This mutex shows other threads that the thread function allocates\n        ///< some objects on stack and it is unsafe state. To manipulate objects owned by descendant classes\n        ///< client must lock this mutex by calling BlockProcessing member function. When client finished its work\n        ///< corresponding UnblockProcessing member function call must be done.\n\n        bool m_blockProcessingRequested;\n        AMFCriticalSection m_csBlockingRequest;\n    public:\n        AMFQueueThread(AMFQueue<inT>* pInQueue,\n            AMFQueue<outT>* pOutQueue) : m_pInQueue(pInQueue), m_pOutQueue(pOutQueue), m_mutexInProcess(),\n            m_blockProcessingRequested(false), m_csBlockingRequest()\n        {}\n        virtual bool Process(amf_ulong& ulID, inT& inData, outT& outData) = 0;\n        virtual void BlockProcessing()\n        {\n            AMFLock lock(&m_csBlockingRequest);\n            m_blockProcessingRequested = true;\n            m_mutexInProcess.Lock();\n        }\n        virtual void UnblockProcessing()\n        {\n            AMFLock lock(&m_csBlockingRequest);\n            m_mutexInProcess.Unlock();\n            m_blockProcessingRequested = false;\n        }\n        virtual bool IsPaused()\n        {\n            return false;\n        }\n        virtual void OnHaveOutput() {}\n        virtual void OnIdle() {}\n\n        virtual void Run()\n        {\n            bool bStop = false;\n            while(!bStop)\n            {\n                {\n                    AMFLock lock(&m_mutexInProcess);\n                    inT inData;\n                    amf_ulong ulID = 0;\n                    bool callProcess = true;\n                    if(m_pInQueue != NULL)\n                    {\n                        amf_ulong waitTimeout = 5;\n                        bool validInput = m_pInQueue->Get(ulID, inData, waitTimeout); // Pulse to check Stop from time to time\n                        if(StopRequested())\n                        {\n                            bStop = true;\n                        }\n                        if(!validInput)\n                        {\n                            callProcess = false;\n                        }\n                    }\n                    if(!bStop && callProcess)\n                    {\n                        outT outData;\n                        bool validOutput = Process(ulID, inData, outData);\n                        if(StopRequested())\n                        {\n                            bStop = true;\n                        }\n                        if(!bStop  && (m_pOutQueue != NULL) && validOutput)\n                        {\n                            m_pOutQueue->Add(ulID, outData);\n                            OnHaveOutput();\n                        }\n                    }\n                    else\n                    {\n                        OnIdle();\n                    }\n                }\n                if(StopRequested())\n                {\n                    bStop = true;\n                }\n#if defined(__linux) || defined(__APPLE__)\n                ///< HACK\n                ///< This amf_sleep(0) is required to emulate windows mutex behavior.\n                ///< In Windows release mutext causes some other waiting thread is receiving ownership of mutex.\n                ///< In Linux it is not true.\n                ///< Without sleep AMFLock destructor releases mutex but immediately on next cycle AMFLock constructor tries to lock\n                ///< the mutex and now system have two threads waiting for the mutex.\n                ///< Using some random logic system decides who will be unlocked.\n                ///< This thread may win during several seconds it looks like pipeline is hang.\n                ///< amf_sleep call causes waiting thread is becoming unlocked.\n                if(m_blockProcessingRequested)\n                {\n                    amf_sleep(0);\n                }\n#endif\n            }\n        }\n    };\n    //----------------------------------------------------------------\n    template<class inT, class outT, class _Thread, class ThreadParam>\n    class AMFQueueThreadPipeline\n    {\n    private:\n        AMFQueueThreadPipeline(const AMFQueueThreadPipeline&);\n        AMFQueueThreadPipeline& operator=(const AMFQueueThreadPipeline&);\n\n    public:\n        AMFQueue<inT>* m_pInQueue;\n        AMFQueue<outT>* m_pOutQueue;\n        std::vector<_Thread*>    m_ThreadPool;\n\n        AMFQueueThreadPipeline(AMFQueue<inT>* pInQueue, AMFQueue<outT>* pOutQueue)\n            : m_pInQueue(pInQueue),\n            m_pOutQueue(pOutQueue),\n            m_ThreadPool()\n        {}\n        virtual ~AMFQueueThreadPipeline()\n        {\n            Stop();\n        }\n        void Start(int iNumberOfThreads, ThreadParam param)\n        {\n            if((long)m_ThreadPool.size() >= iNumberOfThreads)\n            {\n                Stop();  //temporary to remove stopped threads. need callback from thread to clean pool\n                //return;\n            }\n            size_t initialSize = m_ThreadPool.size();\n            for(size_t i = initialSize; i < (size_t)iNumberOfThreads; i++)\n            {\n                _Thread* pThread = new _Thread(m_pInQueue, m_pOutQueue, param);\n                m_ThreadPool.push_back(pThread);\n                pThread->Start();\n            }\n        }\n        void RequestStop()\n        {\n            long num = (long)m_ThreadPool.size();\n            for(long i = 0; i < num; i++)\n            {\n                m_ThreadPool[i]->RequestStop();\n            }\n        }\n        void BlockProcessing()\n        {\n            long num = (long)m_ThreadPool.size();\n            for(long i = 0; i < num; i++)\n            {\n                m_ThreadPool[i]->BlockProcessing();\n            }\n        }\n        void UnblockProcessing()\n        {\n            long num = (long)m_ThreadPool.size();\n            for(long i = 0; i < num; i++)\n            {\n                m_ThreadPool[i]->UnblockProcessing();\n            }\n        }\n        void WaitForStop()\n        {\n            long num = (long)m_ThreadPool.size();\n            for(long i = 0; i < num; i++)\n            {\n                _Thread* pThread = m_ThreadPool[i];\n                pThread->WaitForStop();\n                delete pThread;\n            }\n            m_ThreadPool.clear();\n        }\n        void Stop()\n        {\n            RequestStop();\n            WaitForStop();\n        }\n    };\n    //----------------------------------------------------------------\n    class AMFPreciseWaiter\n    {\n    public:\n        AMFPreciseWaiter() : m_WaitEvent(), m_bCancel(false)\n        {}\n        virtual ~AMFPreciseWaiter()\n        {}\n        amf_pts Wait(amf_pts waittime)\n        {\n            if (waittime < 0)\n            {\n                return 0;\n            }\n            m_bCancel = false;\n            amf_pts start = amf_high_precision_clock();\n            amf_pts waited = 0;\n            int count = 0; \n            while(!m_bCancel)\n            {\n                count++;\n                if(!m_WaitEvent.LockTimeout(1))\n                {\n                    break;\n                }\n                waited = amf_high_precision_clock() - start;\n                if(waited >= waittime)\n                {\n                    break;\n                }\n            }\n            return waited;\n        }\n        amf_pts WaitEx(amf_pts waittime)\n        {\n            m_bCancel = false;\n            amf_pts start = amf_high_precision_clock();\n            amf_pts waited = 0;\n            int count = 0;\n            while (!m_bCancel && waited < waittime)\n            {\n                if (waittime - waited < 2 * AMF_SECOND / 1000)// last 2 ms burn CPU for precision\n                {\n                    for (int i = 0; i < 1000; i++)\n                    {\n                        count++;\n#ifdef _WIN32\n                        YieldProcessor();\n#endif\n                    }\n\n                }\n                else if (!m_WaitEvent.LockTimeout(1))\n                {\n                    \tbreak;\n                }\n\n                waited = amf_high_precision_clock() - start;\n            }\n            return waited;\n        }\n        void Cancel()\n        {\n            m_bCancel = true;\n        }\n    protected:\n        AMFEvent m_WaitEvent;\n        bool m_bCancel;\n    };\n    //----------------------------------------------------------------\n} // namespace amf\n#endif // AMF_Thread_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/TraceAdapter.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#include \"../include/core/Factory.h\"\n#include \"Thread.h\"\n#include \"TraceAdapter.h\"\n\n#pragma warning(disable: 4251)\n#pragma warning(disable: 4996)\n\nusing namespace amf;\n\n#if defined(AMF_CORE_STATIC) || defined(AMF_RUNTIME) || defined(AMF_LITE)\nextern \"C\"\n{\n    extern AMF_CORE_LINK AMF_RESULT AMF_CDECL_CALL AMFInit(amf_uint64 version, amf::AMFFactory **ppFactory);\n}\n#else \n #include \"AMFFactory.h\"\n#endif\n\n//------------------------------------------------------------------------------------------------\nstatic AMFTrace *s_pTrace = NULL;\n//------------------------------------------------------------------------------------------------\nstatic AMFTrace *GetTrace()\n{\n    if (s_pTrace == NULL)\n    {\n#if defined(AMF_CORE_STATIC) || defined(AMF_RUNTIME) || defined(AMF_LITE)\n        AMFFactory *pFactory = NULL;\n        AMFInit(AMF_FULL_VERSION, &pFactory);\n        pFactory->GetTrace(&s_pTrace);\n#else\n        s_pTrace = g_AMFFactory.GetTrace();\n        if (s_pTrace == nullptr) \n        {\n            g_AMFFactory.Init(); // last resort, should not happen\n            s_pTrace = g_AMFFactory.GetTrace();\n            g_AMFFactory.Terminate();\n        }\n#endif\n    }\n    return s_pTrace;\n}\n//------------------------------------------------------------------------------------------------\nstatic AMFDebug *s_pDebug = NULL;\n//------------------------------------------------------------------------------------------------\nstatic AMFDebug *GetDebug()\n{\n    if (s_pDebug == NULL)\n    {\n#if defined(AMF_CORE_STATIC) || defined(AMF_RUNTIME) || defined(AMF_LITE)\n        AMFFactory *pFactory = NULL;\n        AMFInit(AMF_FULL_VERSION, &pFactory);\n        pFactory->GetDebug(&s_pDebug);\n#else\n        s_pDebug = g_AMFFactory.GetDebug();\n        if (s_pDebug == nullptr)\n        {\n            g_AMFFactory.Init(); // last resort, should not happen\n            s_pDebug = g_AMFFactory.GetDebug();\n            g_AMFFactory.Terminate();\n        }\n#endif\n    }\n    return s_pDebug;\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFSetCustomDebugger(AMFDebug *pDebugger)\n{\n    s_pDebug = pDebugger;\n    return AMF_OK;\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFSetCustomTracer(AMFTrace *pTracer)\n{\n    s_pTrace = pTracer;\n    return AMF_OK;\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFTraceEnableAsync(bool enable)\n{\n    return GetTrace()->TraceEnableAsync(enable);\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFTraceFlush()\n{\n    return GetTrace()->TraceFlush();\n}\n//------------------------------------------------------------------------------------------------\nvoid AMF_CDECL_CALL amf::AMFTraceW(const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope,\n            amf_int32 countArgs, const wchar_t* format, ...) // if countArgs <= 0 -> no args, formatting could be optimized then\n{\n    if(countArgs <= 0)\n    {\n        GetTrace()->Trace(src_path, line, level, scope, format, NULL);\n    }\n    else\n    {\n        va_list vl;\n        va_start(vl, format);\n\n        GetTrace()->Trace(src_path, line, level, scope, format, &vl);\n\n        va_end(vl);\n    }\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFTraceSetPath(const wchar_t* path)\n{\n    return GetTrace()->SetPath(path);\n}\n//------------------------------------------------------------------------------------------------\nAMF_RESULT AMF_CDECL_CALL amf::AMFTraceGetPath(wchar_t* path, amf_size* pSize)\n{\n    return GetTrace()->GetPath(path, pSize);\n}\n//------------------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf::AMFTraceEnableWriter(const wchar_t* writerID, bool enable)\n{\n    return GetTrace()->EnableWriter(writerID, enable);\n}\n//------------------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf::AMFTraceWriterEnabled(const wchar_t* writerID)\n{\n    return GetTrace()->WriterEnabled(writerID);\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceSetGlobalLevel(amf_int32 level)\n{\n    return GetTrace()->SetGlobalLevel(level);\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceGetGlobalLevel()\n{\n    return GetTrace()->GetGlobalLevel();\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceSetWriterLevel(const wchar_t* writerID, amf_int32 level)\n{\n    return GetTrace()->SetWriterLevel(writerID, level);\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceGetWriterLevel(const wchar_t* writerID)\n{\n    return GetTrace()->GetWriterLevel(writerID);\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceSetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope, amf_int32 level)\n{\n    return GetTrace()->SetWriterLevelForScope(writerID, scope, level);\n}\n//------------------------------------------------------------------------------------------------\namf_int32 AMF_CDECL_CALL amf::AMFTraceGetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope)\n{\n    return GetTrace()->GetWriterLevelForScope(writerID, scope);\n}\n//------------------------------------------------------------------------------------------------\nvoid AMF_CDECL_CALL amf::AMFTraceRegisterWriter(const wchar_t* writerID, AMFTraceWriter* pWriter)\n{\n    GetTrace()->RegisterWriter(writerID, pWriter, true);\n}\n\nvoid AMF_CDECL_CALL amf::AMFTraceUnregisterWriter(const wchar_t* writerID)\n{\n    GetTrace()->UnregisterWriter(writerID);\n}\n\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wexit-time-destructors\"\n    #pragma clang diagnostic ignored \"-Wglobal-constructors\"\n#endif\n\nvoid AMF_CDECL_CALL amf::AMFTraceEnterScope()\n{\n    GetTrace()->Indent(1);\n}\n\namf_uint32 AMF_CDECL_CALL AMFTraceGetScopeDepth()\n{\n    return GetTrace()->GetIndentation();\n}\n\nvoid AMF_CDECL_CALL amf::AMFTraceExitScope()\n{\n    GetTrace()->Indent(-1);\n}\n\nvoid AMF_CDECL_CALL  amf::AMFAssertsEnable(bool enable)\n{\n    GetDebug()->AssertsEnable(enable);\n}\nbool AMF_CDECL_CALL  amf::AMFAssertsEnabled()\n{\n    return GetDebug()->AssertsEnabled();\n}\namf_wstring AMF_CDECL_CALL  amf::AMFFormatResult(AMF_RESULT result) \n{ \n    return amf::amf_string_format(L\"AMF_ERROR %d : %s: \", result, GetTrace()->GetResultText(result)); \n}\n\nconst wchar_t* AMF_STD_CALL amf::AMFGetResultText(AMF_RESULT res)\n{\n    return GetTrace()->GetResultText(res);\n}\nconst wchar_t* AMF_STD_CALL amf::AMFSurfaceGetFormatName(const AMF_SURFACE_FORMAT eSurfaceFormat)\n{\n    return GetTrace()->SurfaceGetFormatName(eSurfaceFormat);\n}\nAMF_SURFACE_FORMAT AMF_STD_CALL amf::AMFSurfaceGetFormatByName(const wchar_t* pwName)\n{\n    return GetTrace()->SurfaceGetFormatByName(pwName);\n}\nconst wchar_t* AMF_STD_CALL amf::AMFGetMemoryTypeName(const AMF_MEMORY_TYPE memoryType)\n{\n    return GetTrace()->GetMemoryTypeName(memoryType);\n}\n\nAMF_MEMORY_TYPE AMF_STD_CALL amf::AMFGetMemoryTypeByName(const wchar_t* name)\n{\n    return GetTrace()->GetMemoryTypeByName(name);\n}\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/TraceAdapter.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n///-------------------------------------------------------------------------\n///  @file   TraceAdapter.h\n///  @brief  AMFTrace interface\n///-------------------------------------------------------------------------\n#ifndef AMF_TraceAdapter_h\n#define AMF_TraceAdapter_h\n#pragma once\n\n#include \"../include/core/Debug.h\"\n#include \"../include/core/Trace.h\"\n#include \"../include/core/Result.h\"\n#include \"../common/AMFFactory.h\"\n#include \"AMFSTL.h\"\n\n#ifndef WIN32\n#include <stdarg.h>\n#endif\n#include <assert.h>\n\n//-----------------------------------\n// Visual Studio memory leak report\n#if defined(WIN32) && defined(_DEBUG) && defined(CRTDBG)\n\n#include <crtdbg.h>\n\n#if !defined(METRO_APP)\n\n#ifdef _DEBUG\n#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)\n#define new DEBUG_NEW\n#endif\n#endif\n#endif\n//-----------------------------------\n\n#if defined(_DEBUG) && defined(__linux)\n#include <sys/ptrace.h>\n#include <signal.h>\n#endif\n\nnamespace amf\n{\n/**\n*******************************************************************************\n*   AMFTraceEnableAsync\n*\n*   @brief\n*       Enable or disable async mode\n*\n*  There are 2 modes trace can work in:\n*  Synchronous - every Trace call immediately goes to writers: console, windows, file, ...\n*  Asynchronous - trace message go to thread local queues; separate thread passes them to writes\n*  Asynchronous mode offers no synchronization between working threads which are writing traces\n*  and high performance.\n*  Asynchronous mode is not enabled always as that dedicated thread (started in Media SDK module) cannot be\n*  terminated safely. See msdn ExitProcess description: it terminates all threads without notifications.\n*  ExitProcess is called after exit from main() -> before module static variables destroyed and before atexit\n*  notifiers are called -> no way to finish trace dedicated thread.\n*\n*  Therefore here is direct enable of asynchronous mode.\n*  AMFTraceEnableAsync(true) increases internal asynchronous counter by 1; AMFTraceEnableAsync(false) decreases by 1\n*  when counter becomes > 0 mode - switches to async; when becomes 0 - switches to sync\n*\n*  Tracer must be switched to sync mode before quit application, otherwise async writing thread will be force terminated by OS (at lease Windows)\n*  See MSDN ExitProcess article for details.\n*******************************************************************************\n*/\nextern \"C\"\n{\nAMF_RESULT AMF_CDECL_CALL AMFTraceEnableAsync(bool enable);\n\n/**\n*******************************************************************************\n*   AMFDebugSetDebugger\n*\n*   @brief\n*       it is used to set a local debugger, or set NULL to remove\n*\n*******************************************************************************\n*/\nAMF_RESULT AMF_CDECL_CALL AMFSetCustomDebugger(AMFDebug *pDebugger);\n\n/**\n*******************************************************************************\n*   AMFTraceSetTracer\n*\n*   @brief\n*       it is used to set a local tracer, or set NULL to remove\n*\n*******************************************************************************\n*/\nAMF_RESULT AMF_CDECL_CALL AMFSetCustomTracer(AMFTrace *pTrace);\n\n/**\n*******************************************************************************\n*   AMFTraceFlush\n*\n*   @brief\n*       Enforce trace writers flush\n*\n*******************************************************************************\n*/\nAMF_RESULT AMF_CDECL_CALL AMFTraceFlush();\n\n/**\n*******************************************************************************\n*   EXPAND\n*\n*   @brief\n*       Auxilary Macro used to evaluate __VA_ARGS__ from 1 macro argument into list of them\n*\n*   It is needed for COUNT_ARGS macro\n*\n*******************************************************************************\n*/\n#define EXPAND(x) x\n\n/**\n*******************************************************************************\n*   GET_TENTH_ARG\n*\n*   @brief\n*       Auxilary Macro for COUNT_ARGS macro\n*\n*******************************************************************************\n*/\n#define GET_TENTH_ARG(a, b, c, d, e, f, g, h, i, j, name, ...) name\n\n/**\n*******************************************************************************\n*   COUNT_ARGS\n*\n*   @brief\n*       Macro returns number of arguments actually passed into it\n*\n*   COUNT_ARGS macro works ok for 1..10 arguments\n*   It is needed to distinguish macro call with optional parameters and without them\n*******************************************************************************\n*/\n#define COUNT_ARGS(...) EXPAND(GET_TENTH_ARG(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))\n\n/**\n*******************************************************************************\n*   AMFTraceW\n*\n*   @brief\n*       General trace function with all possible parameters\n*******************************************************************************\n*/\nvoid AMF_CDECL_CALL AMFTraceW(const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope,\n        amf_int32 countArgs, const wchar_t* format, ...);\n\n/**\n*******************************************************************************\n*   AMF_UNICODE\n*\n*   @brief\n*       Macro to convert string constant into wide char string constant\n*\n*   Auxilary AMF_UNICODE_ macro is needed as otherwise it is not possible to use AMF_UNICODE(__FILE__)\n*   Microsoft macro _T also uses 2 passes to accomplish that\n*******************************************************************************\n*/\n#define AMF_UNICODE(s) AMF_UNICODE_(s)\n#define AMF_UNICODE_(s) L ## s\n\n/**\n*******************************************************************************\n*   AMFTrace\n*\n*   @brief\n*       Most general macro for trace, incapsulates passing source file and line\n*******************************************************************************\n*/\n#define AMFTrace(level, scope, /*format, */...) amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, level, scope, COUNT_ARGS(__VA_ARGS__) - 1, __VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMFTraceError\n*\n*   @brief\n*       Shortened macro to trace exactly error.\n*\n*   Similar macroses are: AMFTraceWarning, AMFTraceInfo, AMFTraceDebug\n*******************************************************************************\n*/\n#define AMFTraceError(scope, /*format, */...)   amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, AMF_TRACE_ERROR, scope, COUNT_ARGS(__VA_ARGS__) - 1, __VA_ARGS__)\n#define AMFTraceWarning(scope, /*format, */...) amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, AMF_TRACE_WARNING, scope, COUNT_ARGS(__VA_ARGS__) - 1, __VA_ARGS__)\n#define AMFTraceInfo(scope, /*format, */...)    amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, AMF_TRACE_INFO, scope, COUNT_ARGS(__VA_ARGS__) - 1, __VA_ARGS__)\n#define AMFTraceDebug(scope, /*format, */...)   amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, AMF_TRACE_DEBUG, scope, COUNT_ARGS(__VA_ARGS__) - 1, __VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMFDebugHitEvent\n*\n*   @brief\n*       Designed to determine how many are specific events take place\n*******************************************************************************\n*/\nvoid      AMF_CDECL_CALL AMFDebugHitEvent(const wchar_t* scope, const wchar_t* eventName);\n/**\n*******************************************************************************\n*   AMFDebugGetEventsCount\n*\n*   @brief\n*       Designed to acquire counter of events reported by call AMFDebugHitEvent\n*******************************************************************************\n*/\namf_int64 AMF_CDECL_CALL AMFDebugGetEventsCount(const wchar_t* scope, const wchar_t* eventName);\n\n/**\n*******************************************************************************\n*   AMFAssertsEnabled\n*\n*   @brief\n*       Returns bool values indicating if asserts were enabled or not\n*******************************************************************************\n*/\nbool AMF_CDECL_CALL AMFAssertsEnabled();\n\n/**\n*******************************************************************************\n*   AMFTraceEnterScope\n*\n*   @brief\n*       Increase trace indentation value by 1\n*\n*   Indentation value is thread specific\n*******************************************************************************\n*/\nvoid AMF_CDECL_CALL AMFTraceEnterScope();\n/**\n*******************************************************************************\n*   AMFTraceExitScope\n*\n*   @brief\n*       Decrease trace indentation value by 1\n*\n*   Indentation value is thread specific\n*******************************************************************************\n*/\nvoid AMF_CDECL_CALL AMFTraceExitScope();\n\n/**\n*******************************************************************************\n*   AMF_FACILITY\n*\n*   @brief\n*       Default value for AMF_FACILITY, this NULL leads to generate facility from source file name\n*\n*   This AMF_FACILITY could be overloaded locally with #define AMF_FACILITY L\"LocalScope\"\n*******************************************************************************\n*/\nstatic const wchar_t* AMF_FACILITY = NULL;\n} //extern \"C\"\n\n/**\n*******************************************************************************\n*   AMFDebugBreak\n*\n*   @brief\n*       Macro for switching to debug of application\n*******************************************************************************\n*/\n#if defined(_DEBUG)\n#if defined(_WIN32)\n#define AMFDebugBreak  {if(amf::AMFAssertsEnabled()) {__debugbreak();} \\\n}                                                                                //{  }\n#elif defined(__linux)\n//    #define AMFDebugBreak ((void)0)\n#define AMFDebugBreak  {if(amf::AMFAssertsEnabled() && ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {raise(SIGTRAP);} \\\n}//{  }\n#elif defined(__APPLE__)\n#define AMFDebugBreak  {if(amf::AMFAssertsEnabled()) {assert(0);} \\\n}\n#endif\n#else\n#define AMFDebugBreak\n#endif\n\n/**\n*******************************************************************************\n*   __FormatMessage\n*\n*   @brief\n*       Auxilary function to select from 2 messages and preformat message if any arguments are specified\n*******************************************************************************\n*/\ninline amf_wstring __FormatMessage(int /*argsCount*/, const wchar_t* expression)\n{\n    return amf_wstring(expression); // the only expression is provided - return this one\n}\n\ninline amf_wstring __FormatMessage(int argsCount, const wchar_t* /*expression*/, const wchar_t* message, ...)\n{\n    // this version of __FormatMessage for case when descriptive message is provided with optional args\n    if(argsCount <= 0)\n    {\n        return amf_wstring(message);\n    }\n    else\n    {\n        va_list arglist;\n        va_start(arglist, message);\n        amf_wstring result = amf::amf_string_formatVA(message, arglist);\n        va_end(arglist);\n        return result;\n    }\n}\n\n/**\n*******************************************************************************\n*   AMF_FIRST_VALUE\n*\n*   @brief\n*       Auxilary macro: extracts first argument from the list\n*******************************************************************************\n*/\n#define AMF_FIRST_VALUE(x, ...) x\n\n/**\n*******************************************************************************\n*   AMF_BASE_RETURN\n*\n*   @brief\n*       Base generic macro: checks expression for success, if failed: trace error, debug break and return an error\n*\n*       return_result is a parameter to return to upper level, could be hard-coded or\n*           specified exp_res what means pass inner level error\n*******************************************************************************\n*/\n#define AMF_BASE_RETURN(exp, exp_type, check_func, format_prefix, level, scope, return_result/*(could be exp_res)*/, /* optional message args*/ ...) \\\n    { \\\n        exp_type exp_res = (exp_type)(exp); \\\n        if(!check_func(exp_res)) \\\n        { \\\n            amf_wstring message = format_prefix(exp_res) + amf::__FormatMessage(COUNT_ARGS(__VA_ARGS__) - 2, __VA_ARGS__); \\\n            EXPAND(amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, level, scope, 0, message.c_str()) ); \\\n            AMFDebugBreak; \\\n            return return_result; \\\n        } \\\n    }\n\n/**\n*******************************************************************************\n*   AMF_BASE_ASSERT\n*\n*   @brief\n*       Base generic macro: checks expression for success, if failed: trace error, debug break\n*******************************************************************************\n*/\n#define AMF_BASE_ASSERT(exp, exp_type, check_func, format_prefix, level, scope, return_result/*(could be exp_res)*/, /*optional message, optional message args*/ ...) \\\n    { \\\n        exp_type exp_res = (exp_type)(exp); \\\n        if(!check_func(exp_res)) \\\n        { \\\n            amf_wstring message = format_prefix(exp_res) + amf::__FormatMessage(COUNT_ARGS(__VA_ARGS__) - 2, __VA_ARGS__); \\\n            EXPAND(amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, level, scope, 0, message.c_str()) ); \\\n            AMFDebugBreak; \\\n        } \\\n    }\n\n/**\n*******************************************************************************\n*   AMF_BASE_CALL\n*\n*   @brief\n*       Macro supporting cascade call function returning AMF_RESULT from another\n*\n*       return_result is a parameter to return to upper level, could be hard-coded or\n*           specified exp_res what means pass inner level error\n*******************************************************************************\n*/\n#define AMF_BASE_CALL(exp, exp_type, check_func, format_prefix, level, scope, return_result/*(could be exp_res)*/, /*optional message, optional message args*/ ...) \\\n    { \\\n        amf_wstring function_name = amf::__FormatMessage(COUNT_ARGS(__VA_ARGS__) - 2, __VA_ARGS__); \\\n        amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, AMF_TRACE_DEBUG, scope, 0, function_name.c_str()); \\\n        amf::AMFTraceEnterScope(); \\\n        exp_type exp_res = (exp_type)(exp); \\\n        amf::AMFTraceExitScope(); \\\n        if(!check_func(exp_res)) \\\n        { \\\n            amf_wstring message = format_prefix(exp_res) + function_name; \\\n            EXPAND(amf::AMFTraceW(AMF_UNICODE(__FILE__), __LINE__, level, scope, 0, message.c_str()) ); \\\n            AMFDebugBreak; \\\n            return return_result; \\\n        } \\\n    }\n\n/**\n*******************************************************************************\n*   AMFCheckExpression\n*\n*   @brief\n*       Checks if result succeeds\n*******************************************************************************\n*/\ninline bool AMFCheckExpression(int result) { return result != 0; }\n/**\n*******************************************************************************\n*   AMFFormatAssert\n*\n*   @brief\n*       Returns default assertion message\n*******************************************************************************\n*/\ninline amf_wstring AMFFormatAssert(int result) { return result ? amf_wstring() : amf_wstring(L\"Assertion failed:\"); }\n\n/**\n*******************************************************************************\n*   AMFOpenCLSucceeded\n*\n*   @brief\n*       Checks cl_status for success\n*******************************************************************************\n*/\ninline bool AMFOpenCLSucceeded(int result) { return result == 0; }\n/**\n*******************************************************************************\n*   AMFFormatOpenCLError\n*\n*   @brief\n*       Formats open CL error\n*******************************************************************************\n*/\ninline amf_wstring AMFFormatOpenCLError(int result)  { return amf::amf_string_format(L\"OpenCL failed, error = %d:\", result); }\n/**\n*******************************************************************************\n*   AMFResultIsOK\n*\n*   @brief\n*       Checks if AMF_RESULT is OK\n*******************************************************************************\n*/\ninline bool AMFResultIsOK(AMF_RESULT result) { return result == AMF_OK; }\n/**\n*******************************************************************************\n*   AMFSucceeded\n*\n*   @brief\n*       Checks if AMF_RESULT is succeeded\n*******************************************************************************\n*/\ninline bool AMFSucceeded(AMF_RESULT result) { return result == AMF_OK || result == AMF_REPEAT; }\n/**\n*******************************************************************************\n*   AMFFormatResult\n*\n*   @brief\n*       Formats AMF_RESULT into descriptive string\n*******************************************************************************\n*/\namf_wstring AMF_CDECL_CALL  AMFFormatResult(AMF_RESULT result);\n\n/**\n*******************************************************************************\n*   AMFHResultSucceded\n*\n*   @brief\n*       Checks if HRESULT succeeded\n*******************************************************************************\n*/\ninline bool AMFHResultSucceded(HRESULT result) { return SUCCEEDED(result); }\n/**\n*******************************************************************************\n*   AMFFormatHResult\n*\n*   @brief\n*       Formats HRESULT into descriptive string\n*******************************************************************************\n*/\ninline amf_wstring AMFFormatHResult(HRESULT result)  { return amf::amf_string_format(L\"COM failed, HR = %0X:\", result); }\n\n/**\n*******************************************************************************\n*   AMFVkResultSucceeded\n*\n*   @brief\n*       Checks if VkResult succeeded\n*******************************************************************************\n*/\ninline bool AMFVkResultSucceeded(int result) { return result == 0; }\n\n/**\n*******************************************************************************\n*   AMFFormatVkResult\n*\n*   @brief\n*       Formats VkResult into descriptive string\n*******************************************************************************\n*/\ninline amf_wstring AMFFormatVkResult(int result) { return amf::amf_string_format(L\"Vulkan failed, VkResult = %d:\", result); }\n\n/**\n*******************************************************************************\n*   AMF_CALL\n*\n*   @brief\n*       Macro to call AMF_RESULT returning function from AMF_RESULT returning function\n*\n*   It does:\n*       1) Trace (level == debug) function name (or message if specified)\n*       2) Indent trace\n*       3) Call function\n*       4) Unindent trace\n*       5) Checks its result\n*       6) If not OK trace error, switch to debugger (if asserts enabled) and return that error code to upper level\n*\n*   Use cases:\n*       A) AMF_CALL(Init(\"Name\"));      // trace expression itself\n*       B) AMF_CALL(Init(\"Name\"), L\"Initialize resources\");  // trace desciptive message\n*       C) AMF_CALL(Init(name), L\"Initialize resources with %s\", name);   // trace descriptive message with aditional arguments from runtime\n*******************************************************************************\n*/\n#define AMF_CALL(exp, ... /*optional format, args*/) AMF_BASE_CALL(exp, AMF_RESULT, amf::AMFResultIsOK, amf::AMFFormatResult, AMF_TRACE_ERROR, AMF_FACILITY, exp_res, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMF_ASSERT_OK\n*\n*   @brief\n*       Checks expression == AMF_OK, otherwise trace error and debug break\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define AMF_ASSERT_OK(exp, ... /*optional format, args*/) AMF_BASE_ASSERT(exp, AMF_RESULT, amf::AMFResultIsOK, amf::AMFFormatResult, AMF_TRACE_ERROR, AMF_FACILITY, AMF_FAIL, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMF_ASSERT\n*\n*   @brief\n*       Checks expression != 0, otherwise trace error and debug break\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define AMF_ASSERT(exp, ...) AMF_BASE_ASSERT(exp, int, amf::AMFCheckExpression, amf::AMFFormatAssert, AMF_TRACE_ERROR, AMF_FACILITY, AMF_FAIL, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMF_RETURN_IF_FAILED\n*\n*   @brief\n*       Checks expression != 0, otherwise trace error, debug break and return that error to upper level\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define AMF_RETURN_IF_FAILED(exp, ...) AMF_BASE_RETURN(exp, AMF_RESULT, amf::AMFResultIsOK, amf::AMFFormatResult, AMF_TRACE_ERROR, AMF_FACILITY, exp_res, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   ASSERT_RETURN_IF_CL_FAILED\n*\n*   @brief\n*       Checks cl error is ok, otherwise trace error, debug break and return that error to upper level\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define ASSERT_RETURN_IF_CL_FAILED(exp, /*optional format, args,*/...) AMF_BASE_RETURN(exp, int, amf::AMFOpenCLSucceeded, amf::AMFFormatOpenCLError, AMF_TRACE_ERROR, AMF_FACILITY, AMF_OPENCL_FAILED, L###exp, ##__VA_ARGS__)\n#define AMF_RETURN_IF_CL_FAILED(exp, /*optional format, args,*/...) AMF_BASE_RETURN(exp, int, amf::AMFOpenCLSucceeded, amf::AMFFormatOpenCLError, AMF_TRACE_ERROR, AMF_FACILITY, AMF_OPENCL_FAILED, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   ASSERT_RETURN_IF_HR_FAILED\n*\n*   @brief\n*       Obsolete macro: Checks HRESULT if succeeded, otherwise trace error, debug break and return specified error to upper level\n*\n*       Other macroses below are also obsolete\n*******************************************************************************\n*/\n#define ASSERT_RETURN_IF_HR_FAILED(exp, reterr, /*optional format, args,*/...) AMF_BASE_RETURN(exp, HRESULT, amf::AMFHResultSucceded, amf::AMFFormatHResult, AMF_TRACE_ERROR, AMF_FACILITY, reterr, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   ASSERT_RETURN_IF_VK_FAILED\n*\n*   @brief\n*       Checks VkResult if succeeded, otherwise trace error, debug break and return specified error to upper level\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define ASSERT_RETURN_IF_VK_FAILED(exp, reterr, /*optional format, args,*/...) AMF_BASE_RETURN(exp, int, amf::AMFVkResultSucceeded, amf::AMFFormatVkResult, AMF_TRACE_ERROR, AMF_FACILITY, reterr, L###exp, ##__VA_ARGS__)\n\n\n/**\n*******************************************************************************\n*   AMF_RETURN_IF_FALSE\n*\n*   @brief\n*       Checks expression != 0, otherwise trace error, debug break and return that error to upper level\n*\n*       Could be used: A) with just expression B) with optinal descriptive message C) message + args for printf\n*******************************************************************************\n*/\n#define AMF_RETURN_IF_FALSE(exp, ret_value, /*optional message,*/ ...) AMF_BASE_RETURN(exp, int, amf::AMFCheckExpression, amf::AMFFormatAssert, AMF_TRACE_ERROR, AMF_FACILITY, ret_value, L###exp, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMF_RETURN_IF_INVALID_POINTER\n*\n*   @brief\n*       Checks ptr != NULL, otherwise trace error, debug break and return that error to upper level\n*\n*******************************************************************************\n*/\n#define AMF_RETURN_IF_INVALID_POINTER(ptr, /*optional message,*/ ...) AMF_BASE_RETURN(ptr != NULL, int, amf::AMFCheckExpression, amf::AMFFormatAssert, AMF_TRACE_ERROR, AMF_FACILITY, AMF_INVALID_POINTER, L\"invalid pointer : \" L###ptr, ##__VA_ARGS__)\n\n/**\n*******************************************************************************\n*   AMFTestEventObserver\n*\n*   @brief\n*       Interface to subscribe on test events\n*******************************************************************************\n*/\n\nextern \"C\"\n{\n\n    /**\n    *******************************************************************************\n    *   AMFTraceSetPath\n    *\n    *   @brief\n    *       Set Trace path\n    *\n    *       Returns AMF_OK if succeeded\n    *******************************************************************************\n    */\n    AMF_RESULT AMF_CDECL_CALL  AMFTraceSetPath(const wchar_t* path);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceGetPath\n    *\n    *   @brief\n    *       Get Trace path\n    *\n    *       Returns AMF_OK if succeeded\n    *******************************************************************************\n    */\n    AMF_RESULT AMF_CDECL_CALL  AMFTraceGetPath(\n        wchar_t* path, ///< [out] buffer able to hold *pSize symbols; path is copied there, at least part fitting the buffer, always terminator is copied\n        amf_size* pSize ///< [in, out] size of buffer, returned needed size of buffer including zero terminator\n    );\n\n    /**\n    *******************************************************************************\n    *   AMFTraceEnableWriter\n    *\n    *   @brief\n    *       Disable trace to registered writer\n    *\n    *       Returns previous state\n    *******************************************************************************\n    */\n    bool AMF_CDECL_CALL  AMFTraceEnableWriter(const wchar_t* writerID, bool enable);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceWriterEnabled\n    *\n    *   @brief\n    *       Return flag if writer enabled\n    *******************************************************************************\n    */\n    bool AMF_CDECL_CALL  AMFTraceWriterEnabled(const wchar_t* writerID);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceSetGlobalLevel\n    *\n    *   @brief\n    *       Sets trace level for writer and scope\n    *\n    *       Returns previous setting\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceSetGlobalLevel(amf_int32 level);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceGetGlobalLevel\n    *\n    *   @brief\n    *       Returns global level\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceGetGlobalLevel();\n\n    /**\n    *******************************************************************************\n    *   AMFTraceSetWriterLevel\n    *\n    *   @brief\n    *       Sets trace level for writer\n    *\n    *       Returns previous setting\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceSetWriterLevel(const wchar_t* writerID, amf_int32 level);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceGetWriterLevel\n    *\n    *   @brief\n    *       Gets trace level for writer\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceGetWriterLevel(const wchar_t* writerID);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceSetWriterLevelForScope\n    *\n    *   @brief\n    *       Sets trace level for writer and scope\n    *\n    *       Returns previous setting\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceSetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope, amf_int32 level);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceGetWriterLevelForScope\n    *\n    *   @brief\n    *       Gets trace level for writer and scope\n    *******************************************************************************\n    */\n    amf_int32 AMF_CDECL_CALL  AMFTraceGetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceRegisterWriter\n    *\n    *   @brief\n    *       Register custom trace writer\n    *\n    *******************************************************************************\n    */\n    void AMF_CDECL_CALL  AMFTraceRegisterWriter(const wchar_t* writerID, AMFTraceWriter* pWriter);\n\n    /**\n    *******************************************************************************\n    *   AMFTraceUnregisterWriter\n    *\n    *   @brief\n    *       Register custom trace writer\n    *\n    *******************************************************************************\n    */\n    void AMF_CDECL_CALL  AMFTraceUnregisterWriter(const wchar_t* writerID);\n    /*\n    *******************************************************************************\n    *   AMFAssertsEnable\n    *\n    *   @brief\n    *       Enable asserts in checks\n    *\n    *******************************************************************************\n    */\n    void AMF_CDECL_CALL  AMFAssertsEnable(bool enable);\n\n    /**\n    *******************************************************************************\n    *   AMFAssertsEnabled\n    *\n    *   @brief\n    *       Returns true if asserts in checks enabled\n    *\n    *******************************************************************************\n    */\n    bool AMF_CDECL_CALL  AMFAssertsEnabled();\n\n    const wchar_t* AMF_STD_CALL AMFGetResultText(AMF_RESULT res);\n    const wchar_t* AMF_STD_CALL AMFSurfaceGetFormatName(const AMF_SURFACE_FORMAT eSurfaceFormat);\n    AMF_SURFACE_FORMAT AMF_STD_CALL AMFSurfaceGetFormatByName(const wchar_t* pwName);\n    const wchar_t*  AMF_STD_CALL AMFGetMemoryTypeName(const AMF_MEMORY_TYPE memoryType);\n    AMF_MEMORY_TYPE AMF_STD_CALL AMFGetMemoryTypeByName(const wchar_t* name);\n} //extern \"C\"\n} // namespace amf\n#endif // AMF_TraceAdapter_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/common/Windows/ThreadWindows.cpp",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n\n#include \"../Thread.h\"\n\n#ifdef _WIN32\n\n#include <timeapi.h>\n#include <windows.h>\n#include <memory>\n//----------------------------------------------------------------------------------------\n// threading\n//----------------------------------------------------------------------------------------\namf_long AMF_CDECL_CALL amf_atomic_inc(amf_long* X)\n{\n    return InterlockedIncrement((long*)X);\n}\n//----------------------------------------------------------------------------------------\namf_long AMF_CDECL_CALL amf_atomic_dec(amf_long* X)\n{\n    return InterlockedDecrement((long*)X);\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_create_critical_section()\n{\n    CRITICAL_SECTION* cs = new CRITICAL_SECTION;\n#if defined(METRO_APP)\n    ::InitializeCriticalSectionEx(cs, 0, CRITICAL_SECTION_NO_DEBUG_INFO);\n#else\n    ::InitializeCriticalSection(cs);\n#endif\n    return (amf_handle)cs; // in Win32 - no errors\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_delete_critical_section(amf_handle cs)\n{\n    if(cs == NULL)\n    {\n        return false;\n    }\n    ::DeleteCriticalSection((CRITICAL_SECTION*)cs);\n    delete (CRITICAL_SECTION*)cs;\n    return true; // in Win32 - no errors\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_enter_critical_section(amf_handle cs)\n{\n    if(cs == NULL)\n    {\n        return false;\n    }\n    ::EnterCriticalSection((CRITICAL_SECTION*)cs);\n    return true; // in Win32 - no errors\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_wait_critical_section(amf_handle cs, amf_ulong ulTimeout)\n{\n    if(cs == NULL)\n    {\n        return false;\n    }\n    while (true)\n    {\n        const BOOL success = ::TryEnterCriticalSection((CRITICAL_SECTION*)cs);\n        if (success == TRUE)\n        {\n            return true; // in Win32 - no errors\n        }\n        if (ulTimeout == 0)\n        {\n            return false;\n        }\n\n        amf_sleep(1);\n        ulTimeout--;\n    }\n\n    return false;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_leave_critical_section(amf_handle cs)\n{\n    if(cs == NULL)\n    {\n        return false;\n    }\n    ::LeaveCriticalSection((CRITICAL_SECTION*)cs);\n    return true; // in Win32 - no errors\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_create_event(bool bInitiallyOwned, bool bManualReset, const wchar_t* pName)\n{\n#if defined(METRO_APP)\n    DWORD flags = ((bManualReset) ? CREATE_EVENT_MANUAL_RESET : 0) |\n        ((bInitiallyOwned) ? CREATE_EVENT_INITIAL_SET : 0);\n\n    return ::CreateEventEx(NULL, pName, flags, STANDARD_RIGHTS_ALL | EVENT_MODIFY_STATE);\n\n#else\n    return ::CreateEventW(NULL, bManualReset == true, bInitiallyOwned == true, pName);\n\n#endif\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_delete_event(amf_handle hevent)\n{\n    if(hevent == NULL)\n    {\n        return false;\n    }\n    return ::CloseHandle(hevent) != FALSE;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_set_event(amf_handle hevent)\n{\n    if(hevent == NULL)\n    {\n        return false;\n    }\n    return ::SetEvent(hevent) != FALSE;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_reset_event(amf_handle hevent)\n{\n    if(hevent == NULL)\n    {\n        return false;\n    }\n    return ::ResetEvent(hevent) != FALSE;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_wait_for_event(amf_handle hevent, amf_ulong ulTimeout)\n{\n    if(hevent == NULL)\n    {\n        return false;\n    }\n#if defined(METRO_APP)\n    return ::WaitForSingleObjectEx(hevent, ulTimeout, FALSE) == WAIT_OBJECT_0;\n\n#else\n    return ::WaitForSingleObject(hevent, ulTimeout) == WAIT_OBJECT_0;\n\n#endif\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_wait_for_event_timeout(amf_handle hevent, amf_ulong ulTimeout)\n{\n    if(hevent == NULL)\n    {\n        return false;\n    }\n    DWORD ret;\n#if defined(METRO_APP)\n    ret = ::WaitForSingleObjectEx(hevent, ulTimeout, FALSE);\n#else\n    ret = ::WaitForSingleObject(hevent, ulTimeout);\n#endif\n    return ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT;\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_create_mutex(bool bInitiallyOwned, const wchar_t* pName)\n{\n#if defined(METRO_APP)\n    DWORD flags = (bInitiallyOwned) ? CREATE_MUTEX_INITIAL_OWNER : 0;\n    return ::CreateMutexEx(NULL, pName, flags, STANDARD_RIGHTS_ALL);\n\n#else\n    return ::CreateMutexW(NULL, bInitiallyOwned == true, pName);\n\n#endif\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_open_mutex(const wchar_t* pName)\n{\n    return ::OpenMutexW(MUTEX_ALL_ACCESS, FALSE, pName);\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_delete_mutex(amf_handle hmutex)\n{\n    if(hmutex == NULL)\n    {\n        return false;\n    }\n    return ::CloseHandle(hmutex) != FALSE;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_wait_for_mutex(amf_handle hmutex, amf_ulong ulTimeout)\n{\n    if(hmutex == NULL)\n    {\n        return false;\n    }\n#if defined(METRO_APP)\n    return ::WaitForSingleObjectEx(hmutex, ulTimeout, FALSE) == WAIT_OBJECT_0;\n\n#else\n    return ::WaitForSingleObject(hmutex, ulTimeout) == WAIT_OBJECT_0;\n\n#endif\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_release_mutex(amf_handle hmutex)\n{\n    if(hmutex == NULL)\n    {\n        return false;\n    }\n    return ::ReleaseMutex(hmutex) != FALSE;\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_create_semaphore(amf_long iInitCount, amf_long iMaxCount, const wchar_t* pName)\n{\n    if(iMaxCount == NULL || iInitCount > iMaxCount)\n    {\n        return NULL;\n    }\n#if defined(METRO_APP)\n    return ::CreateSemaphoreEx(NULL, iInitCount, iMaxCount, pName, 0, STANDARD_RIGHTS_ALL | SEMAPHORE_MODIFY_STATE);\n\n#else\n    return ::CreateSemaphoreW(NULL, iInitCount, iMaxCount, pName);\n\n#endif\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_delete_semaphore(amf_handle hsemaphore)\n{\n    if(hsemaphore == NULL)\n    {\n        return false;\n    }\n    return ::CloseHandle(hsemaphore) != FALSE;\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_wait_for_semaphore(amf_handle hsemaphore, amf_ulong timeout)\n{\n    if(hsemaphore == NULL)\n    {\n        return true;\n    }\n#if defined(METRO_APP)\n    return ::WaitForSingleObjectEx(hsemaphore, timeout, false) == WAIT_OBJECT_0;\n\n#else\n    return ::WaitForSingleObject(hsemaphore, timeout) == WAIT_OBJECT_0;\n\n#endif\n}\n//----------------------------------------------------------------------------------------\nbool AMF_CDECL_CALL amf_release_semaphore(amf_handle hsemaphore, amf_long iCount, amf_long* iOldCount)\n{\n    if(hsemaphore == NULL)\n    {\n        return false;\n    }\n    return ::ReleaseSemaphore(hsemaphore, iCount, iOldCount) != FALSE;\n}\n//------------------------------------------------------------------------------\nvoid AMF_CDECL_CALL amf_sleep(amf_ulong delay)\n{\n#if defined(METRO_APP)\n    Concurrency::wait(delay);\n#else\n    Sleep(delay);\n#endif\n}\n//----------------------------------------------------------------------------------------\namf_pts AMF_CDECL_CALL amf_high_precision_clock()\n{\n    static int state = 0;\n    static LARGE_INTEGER Frequency;\n    static LARGE_INTEGER StartCount;\n    static amf_pts offset = 0;\n\n    if(state == 0)\n    {\n        if(QueryPerformanceFrequency(&Frequency))\n        {\n            state = 1;\n            QueryPerformanceCounter(&StartCount);\n        }\n        else\n        {\n            state = 2;\n        }\n    }\n    if(state == 1)\n    {\n        LARGE_INTEGER PerformanceCount;\n        if(QueryPerformanceCounter(&PerformanceCount))\n        {\n            amf_pts elapsed = static_cast<amf_pts>((PerformanceCount.QuadPart - StartCount.QuadPart) * 10000000LL / Frequency.QuadPart);\n\n            // periodically reset StartCount in order to avoid overflow\n            if (elapsed > (3600LL * AMF_SECOND))\n            {\n                offset += elapsed;\n                StartCount = PerformanceCount;\n\n                return offset;\n            }\n            else\n            {\n                return offset + elapsed;\n            }\n        }\n    }\n#if defined(METRO_APP)\n    return GetTickCount64() * 10;\n\n#else\n    return GetTickCount() * 10;\n\n#endif\n}\n//-------------------------------------------------------------------------------------------------\n#pragma comment (lib, \"winmm.lib\")\nstatic amf_uint32 timerPrecision = 1;\n\nvoid AMF_CDECL_CALL amf_increase_timer_precision()\n{\n#if !defined(METRO_APP)\n    while (timeBeginPeriod(timerPrecision) == TIMERR_NOCANDO)\n    {\n        ++timerPrecision;\n    }\n/*\n    typedef NTSTATUS (CALLBACK * NTSETTIMERRESOLUTION)(IN ULONG DesiredTime,IN BOOLEAN SetResolution,OUT PULONG ActualTime);\n    typedef NTSTATUS (CALLBACK * NTQUERYTIMERRESOLUTION)(OUT PULONG MaximumTime,OUT PULONG MinimumTime,OUT PULONG CurrentTime);\n\n    HINSTANCE hNtDll = LoadLibrary(L\"NTDLL.dll\");\n    if(hNtDll != NULL)\n    {\n        ULONG MinimumResolution=0;\n        ULONG MaximumResolution=0;\n        ULONG ActualResolution=0;\n\n        NTQUERYTIMERRESOLUTION NtQueryTimerResolution = (NTQUERYTIMERRESOLUTION)GetProcAddress(hNtDll, \"NtQueryTimerResolution\");\n        NTSETTIMERRESOLUTION NtSetTimerResolution = (NTSETTIMERRESOLUTION)GetProcAddress(hNtDll, \"NtSetTimerResolution\");\n\n        if(NtQueryTimerResolution != NULL && NtSetTimerResolution != NULL)\n        {\n            NtQueryTimerResolution (&MinimumResolution, &MaximumResolution, &ActualResolution);\n            if(MaximumResolution != 0)\n            {\n                NtSetTimerResolution (MaximumResolution, TRUE, &ActualResolution);\n                NtQueryTimerResolution (&MinimumResolution, &MaximumResolution, &ActualResolution);\n\n                // if call NtQueryTimerResolution() again it will return the same values but precision is actually increased\n            }\n        }\n        FreeLibrary(hNtDll);\n    }\n*/\n#endif\n}\nvoid AMF_CDECL_CALL amf_restore_timer_precision()\n{\n#if !defined(METRO_APP)\n    timeEndPeriod(timerPrecision);\n#endif\n}\n//----------------------------------------------------------------------------------------\namf_handle  AMF_CDECL_CALL amf_load_library1(const wchar_t* filename, bool /*bGlobal*/)\n{\n    return amf_load_library(filename);\n}\n//----------------------------------------------------------------------------------------\namf_handle AMF_CDECL_CALL amf_load_library(const wchar_t* filename)\n{\n#if defined(METRO_APP)\n    return LoadPackagedLibrary(filename, 0);\n#else\n\treturn ::LoadLibraryExW(filename, NULL, LOAD_LIBRARY_SEARCH_USER_DIRS |\n\t\tLOAD_LIBRARY_SEARCH_APPLICATION_DIR |\n\t\tLOAD_LIBRARY_SEARCH_DEFAULT_DIRS |\n\t\tLOAD_LIBRARY_SEARCH_SYSTEM32);\n#endif\n}\n//----------------------------------------------------------------------------------------\nvoid* AMF_CDECL_CALL amf_get_proc_address(amf_handle module, const char* procName)\n{\n    return (void*)::GetProcAddress((HMODULE)module, procName);\n}\n//----------------------------------------------------------------------------------------\nint AMF_CDECL_CALL amf_free_library(amf_handle module)\n{\n    return ::FreeLibrary((HMODULE)module)==TRUE;\n}\n#if !defined(METRO_APP)\n//----------------------------------------------------------------------------------------\n// memory\n//----------------------------------------------------------------------------------------\nvoid* AMF_CDECL_CALL amf_virtual_alloc(size_t size)\n{\n    return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);\n}\n//----------------------------------------------------------------------------------------\nvoid AMF_CDECL_CALL amf_virtual_free(void* ptr)\n{\n    VirtualFree(ptr, NULL, MEM_RELEASE);\n}\n#endif //#if !defined(METRO_APP)\n//----------------------------------------------------------------------------------------\n// cpu\n//----------------------------------------------------------------------------------------\namf_int32 AMF_STD_CALL amf_get_cpu_cores()\n{\n    //query the number of CPU HW cores\n    DWORD len = 0;\n    GetLogicalProcessorInformation(NULL, &len);\n\n    amf_uint32 count = len / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);\n    std::unique_ptr<SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]>  pBuffer(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[count]);\n    if (pBuffer)\n    {\n        GetLogicalProcessorInformation(pBuffer.get(), &len);\n        count = len / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);\n        amf_int32 iCores = 0;\n        for (amf_uint32 idx = 0; idx < count; idx++)\n        {\n            if (pBuffer[idx].Relationship == RelationProcessorCore)\n            {\n                iCores++;\n            }\n        }\n        return iCores;\n    }\n\n    return 1;\n}\n//----------------------------------------------------------------------------------------\n//----------------------------------------------------------------------------------------\n#endif // _WIN32"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/Ambisonic2SRenderer.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// interface declaration;  Ambisonic to Stereo Renderer\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_Ambisonic2SRenderer_h\n#define AMF_Ambisonic2SRenderer_h\n#pragma once\n\n#include \"public/include/components/Component.h\"\n\n#define AMFAmbisonic2SRendererHW L\"AMFAmbisonic2SRenderer\"\n\nenum AMF_AMBISONIC2SRENDERER_MODE_ENUM \n{\n    AMF_AMBISONIC2SRENDERER_MODE_SIMPLE            = 0,\n    AMF_AMBISONIC2SRENDERER_MODE_HRTF_AMD0         = 1,\n    AMF_AMBISONIC2SRENDERER_MODE_HRTF_MIT1         = 2,\n};\n\n\n// static properties \n#define AMF_AMBISONIC2SRENDERER_IN_AUDIO_SAMPLE_RATE        L\"InSampleRate\"         // amf_int64 (default = 0)\n#define AMF_AMBISONIC2SRENDERER_IN_AUDIO_CHANNELS           L\"InChannels\"           // amf_int64 (only = 4)\n#define AMF_AMBISONIC2SRENDERER_IN_AUDIO_SAMPLE_FORMAT      L\"InSampleFormat\"       // amf_int64(AMF_AUDIO_FORMAT) (default = AMFAF_FLTP)\n\n#define AMF_AMBISONIC2SRENDERER_OUT_AUDIO_CHANNELS          L\"OutChannels\"          // amf_int64 (only = 2 - stereo)\n#define AMF_AMBISONIC2SRENDERER_OUT_AUDIO_SAMPLE_FORMAT     L\"OutSampleFormat\"      // amf_int64(AMF_AUDIO_FORMAT) (only = AMFAF_FLTP)\n#define AMF_AMBISONIC2SRENDERER_OUT_AUDIO_CHANNEL_LAYOUT    L\"OutChannelLayout\"     // amf_int64 (only = 3 - defalut stereo L R)\n\n#define AMF_AMBISONIC2SRENDERER_MODE                        L\"StereoMode\"               //TODO: AMF_AMBISONIC2SRENDERER_MODE_ENUM(default=AMF_AMBISONIC2SRENDERER_MODE_HRTF)\n\n\n// dynamic properties\n#define AMF_AMBISONIC2SRENDERER_W                           L\"w\"                        //amf_int64 (default=0)\n#define AMF_AMBISONIC2SRENDERER_X                           L\"x\"                        //amf_int64 (default=1)\n#define AMF_AMBISONIC2SRENDERER_Y                           L\"y\"                        //amf_int64 (default=2)\n#define AMF_AMBISONIC2SRENDERER_Z                           L\"z\"                        //amf_int64 (default=3)\n\n#define AMF_AMBISONIC2SRENDERER_THETA                       L\"Theta\"                    //double (default=0.0)\n#define AMF_AMBISONIC2SRENDERER_PHI                         L\"Phi\"                      //double (default=0.0)\n#define AMF_AMBISONIC2SRENDERER_RHO                         L\"Rho\"                      //double (default=0.0)\n\nextern \"C\"\n{\n    AMF_RESULT AMF_CDECL_CALL AMFCreateComponentAmbisonic(amf::AMFContext* pContext, void* reserved, amf::AMFComponent** ppComponent);\n}\n#endif //#ifndef AMF_Ambisonic2SRenderer_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/AudioCapture.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// Audio session  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_AudioCapture_h\n#define AMF_AudioCapture_h\n\n#pragma once\n\n#include \"Component.h\"\n\n// Set to capture from either a microphone or desktop\n#define AUDIOCAPTURE_SOURCE                L\"AudioCaptureSource\"           // amf_bool true for microphone, false for desktop;\n\n// In the case of capturing a microphone, the AUDIOCAPTURE_DEVICE_ACTIVE property\n// can be set to -1 so that the active input devices are looked up. If the initialization\n// is successful then the AUDIOCAPTURE_DEVICE_NAME and AUDIOCAPTURE_DEVICE_COUNT\n// properties will be set.\n#define AUDIOCAPTURE_DEVICE_ACTIVE          L\"AudioCaptureDeviceActive\"   // amf_int64\n#define AUDIOCAPTURE_DEVICE_COUNT           L\"AudioCaptureDeviceCount\"    // amf_int64\n#define AUDIOCAPTURE_DEVICE_NAME            L\"AudioCaptureDeviceName\"     // String\n\n// Codec used for audio capture\n#define AUDIOCAPTURE_CODEC                  L\"AudioCaptureCodec\"           // amf_int64, AV_CODEC_ID_PCM_F32LE\n// Sample rate used for audio capture\n#define AUDIOCAPTURE_SAMPLERATE             L\"AudioCaptureSampleRate\"      // amf_int64, 44100 in samples\n// Sample count used for audio capture\n#define AUDIOCAPTURE_SAMPLES                L\"AudioCaptureSampleCount\"     // amf_int64, 1024\n// Bitrate used for audio capture\n#define AUDIOCAPTURE_BITRATE                L\"AudioCaptureBitRate\"         // amf_int64, in bits\n// Channel count used for audio capture\n#define AUDIOCAPTURE_CHANNELS               L\"AudioCaptureChannelCount\"    // amf_int64, 2\n// Channel layout used for audio capture\n#define AUDIOCAPTURE_CHANNEL_LAYOUT         L\"AudioCaptureChannelLayout\"   // amf_int64, AMF_AUDIO_CHANNEL_LAYOUT\n// Format used for audio capture\n#define AUDIOCAPTURE_FORMAT                 L\"AudioCaptureFormat\"          // amf_int64, AMFAF_U8\n// Block alignment\n#define AUDIOCAPTURE_BLOCKALIGN             L\"AudioCaptureBlockAlign\"      // amf_int64, bytes\n// Audio frame size\n#define AUDIOCAPTURE_FRAMESIZE              L\"AudioCaptureFrameSize\"       // amf_int64, bytes\n// Audio low latency state\n#define AUDIOCAPTURE_LOWLATENCY             L\"AudioCaptureLowLatency\"      // amf_int64;\n\n// Optional interface that provides current time\n#define AUDIOCAPTURE_CURRENT_TIME_INTERFACE\tL\"CurrentTimeInterface\"        // interface to current time object\n\nextern \"C\"\n{\n\t// Component that allows the recording of inputs such as microphones or the audio that is being\n\t// rendered.  The direction that is captured is controlled by the AUDIOCAPTURE_CAPTURE property\n\t//\n\tAMF_RESULT AMF_CDECL_CALL AMFCreateComponentAudioCapture(amf::AMFContext* pContext, amf::AMFComponent** ppComponent);\n}\n\n#endif // #ifndef AMF_AudioCapture_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/Capture.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// Capture interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef __Capture_h__\n#define __Capture_h__\n#pragma once\n\n#include \"../../../public/include/components/Component.h\"\n\ntypedef enum AMF_CAPTURE_DEVICE_TYPE_ENUM\n{\n    AMF_CAPTURE_DEVICE_UNKNOWN              = 0,\n    AMF_CAPTURE_DEVICE_MEDIAFOUNDATION      = 1,\n    AMF_CAPTURE_DEVICE_WASAPI               = 2,\n    AMF_CAPTURE_DEVICE_SDI                  = 3,\n    AMF_CAPTURE_DEVICE_SCREEN_DUPLICATION   = 4,\n} AMF_CAPTURE_DEVICE_TYPE_ENUM;\n\n// device properties\n#define AMF_CAPTURE_DEVICE_TYPE                     L\"DeviceType\"               // amf_int64( AMF_CAPTURE_DEVICE_TYPE_ENUM )\n#define AMF_CAPTURE_DEVICE_NAME                     L\"DeviceName\"               // wchar_t* : name of the device\n\n\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n\n    //----------------------------------------------------------------------------------------------\n    // AMFCaptureDevice interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFCaptureDevice : public AMFComponentEx\n    {\n    public:\n        AMF_DECLARE_IID (0x5bfd1b17, 0x9f2a, 0x43c4, 0x9c, 0xdd, 0x2c, 0x3, 0x88, 0x43, 0xb5, 0xf3)\n\n        virtual AMF_RESULT          AMF_STD_CALL Start() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Stop() = 0;\n\n        // TODO add callback interface for disconnected / lost / changed device notification\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFCaptureDevice> AMFCaptureDevicePtr;\n    //----------------------------------------------------------------------------------------------\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFCaptureDevice, 0x5bfd1b17, 0x9f2a, 0x43c4, 0x9c, 0xdd, 0x2c, 0x3, 0x88, 0x43, 0xb5, 0xf3)\n\n    typedef struct AMFCaptureDeviceVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFCaptureDevice* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFCaptureDevice* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFCaptureDevice* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFCaptureDevice* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFCaptureDevice* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFCaptureDevice* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFCaptureDevice* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFCaptureDevice* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFCaptureDevice* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFCaptureDevice* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFCaptureDevice* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFCaptureDevice* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFCaptureDevice* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFCaptureDevice* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n        // AMFComponent interface\n\n        AMF_RESULT          (AMF_STD_CALL *Init)(AMFCaptureDevice* pThis, AMF_SURFACE_FORMAT format,amf_int32 width,amf_int32 height);\n        AMF_RESULT          (AMF_STD_CALL *ReInit)(AMFCaptureDevice* pThis, amf_int32 width,amf_int32 height);\n        AMF_RESULT          (AMF_STD_CALL *Terminate)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *Drain)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *Flush)(AMFCaptureDevice* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *SubmitInput)(AMFCaptureDevice* pThis, AMFData* pData);\n        AMF_RESULT          (AMF_STD_CALL *QueryOutput)(AMFCaptureDevice* pThis, AMFData** ppData);\n        AMFContext*         (AMF_STD_CALL *GetContext)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *SetOutputDataAllocatorCB)(AMFCaptureDevice* pThis, AMFDataAllocatorCB* callback);\n\n        AMF_RESULT          (AMF_STD_CALL *GetCaps)(AMFCaptureDevice* pThis, AMFCaps** ppCaps);\n        AMF_RESULT          (AMF_STD_CALL *Optimize)(AMFCaptureDevice* pThis, AMFComponentOptimizationCallback* pCallback);\n\n        // AMFComponentEx interface\n\n        amf_int32           (AMF_STD_CALL *GetInputCount)(AMFCaptureDevice* pThis);\n        amf_int32           (AMF_STD_CALL *GetOutputCount)(AMFCaptureDevice* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *GetInput)(AMFCaptureDevice* pThis, amf_int32 index, AMFInput** ppInput);\n        AMF_RESULT          (AMF_STD_CALL *GetOutput)(AMFCaptureDevice* pThis, amf_int32 index, AMFOutput** ppOutput);\n\n        // AMFCaptureDevice interface\n\n        AMF_RESULT          (AMF_STD_CALL *Start)(AMFCaptureDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *Stop)(AMFCaptureDevice* pThis);\n\n    } AMFCaptureVtbl;\n\n    struct AMFCapture\n    {\n        const AMFCaptureVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFCaptureManager interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFCaptureManager : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID ( 0xf64d2f0d, 0xad16, 0x4ce7, 0x80, 0x5f, 0xa1, 0xe7, 0x3b, 0x0, 0xf4, 0x28)\n\n        virtual AMF_RESULT          AMF_STD_CALL Update() = 0;\n        virtual amf_int32           AMF_STD_CALL GetDeviceCount() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetDevice(amf_int32 index,AMFCaptureDevice **pDevice) = 0;\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFCaptureManager> AMFCaptureManagerPtr;\n    //----------------------------------------------------------------------------------------------\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFCaptureManager, 0xf64d2f0d, 0xad16, 0x4ce7, 0x80, 0x5f, 0xa1, 0xe7, 0x3b, 0x0, 0xf4, 0x28)\n\n    typedef struct AMFCaptureManagerVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFCaptureManager* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFCaptureManager* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFCaptureManager* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n\n        // AMFCaptureManager interface\n        AMF_RESULT          (AMF_STD_CALL *Update)((AMFCaptureManager* pThis);\n        amf_int32           (AMF_STD_CALL *GetDeviceCount)(AMFCaptureManager* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetDevice)(AMFCaptureManager* pThis, amf_int32 index,AMFCaptureDevice **pDevice);\n\n    } AMFCaptureManagerVtbl;\n\n    struct AMFCaptureManager\n    {\n        const AMFCaptureManagerVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n} // namespace\n#endif\n\nextern \"C\"\n{\n    AMF_RESULT AMF_CDECL_CALL AMFCreateCaptureManager(amf::AMFContext* pContext, amf::AMFCaptureManager** ppManager);\n}\n\n#endif // __Capture_h__"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/ChromaKey.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n/**\n ***************************************************************************************************\n * @file  ChromaKey.h\n * @brief AMFChromaKey interface declaration\n ***************************************************************************************************\n */\n#ifndef __AMFChromaKey_h__\n#define __AMFChromaKey_h__\n#pragma once\n\n#include \"public/include/components/Component.h\"\n\n#define AMFChromaKey                  L\"AMFChromaKey\"\n\n// static properties \n#define AMF_CHROMAKEY_COLOR           L\"ChromaKeyColor\"         // amf_uint64 (default=0x992A1E), YUV Green key Color\n#define AMF_CHROMAKEY_COLOR_EX        L\"ChromaKeyColorEX\"       // amf_uint64 (default=0), YUV Green key Color, secondary\n#define AMF_CHROMAKEY_RANGE_MIN       L\"ChromaKeyRangeMin\"      // amf_uint64 (default=20)   color tolerance low, 0~255\n#define AMF_CHROMAKEY_RANGE_MAX       L\"ChromaKeyRangeMax\"      // amf_uint64 (default=22)   color tolerance high, 0~255\n#define AMF_CHROMAKEY_RANGE_EXT       L\"ChromaKeyRangeExt\"      // amf_uint64 (default=40)   color tolerance extended, 0~255\n#define AMF_CHROMAKEY_SPILL_MODE      L\"ChromaKeySpillMode\"     // amf_uint64 (default=0)    spill suppression mode\n#define AMF_CHROMAKEY_RANGE_SPILL     L\"ChromaKeyRangeSpill\"    // amf_uint64 (default=5)    spill suppression threshold\n#define AMF_CHROMAKEY_LUMA_LOW        L\"ChromaKeyLumaLow\"       // amf_uint64 (default=16)   minimum luma value for processing\n#define AMF_CHROMAKEY_INPUT_COUNT     L\"InputCount\"             // amf_uint64 (default=2)    number of inputs\n#define AMF_CHROMAKEY_COLOR_POS       L\"KeyColorPos\"            // amf_uint64 (default=0)    key color position from the surface\n#define AMF_CHROMAKEY_OUT_FORMAT      L\"ChromaKeyOutFormat\"     // amf_uint64 (default=RGBA) output format\n#define AMF_CHROMAKEY_MEMORY_TYPE     L\"ChromaKeyMemoryType\"    // amf_uint64 (default=DX11) mmeory type\n#define AMF_CHROMAKEY_COLOR_ADJ       L\"ChromaKeyColorAdj\"      // amf_uint64 (default=0)    endble color adjustment\n#define AMF_CHROMAKEY_COLOR_ADJ_THRE  L\"ChromaKeyColorAdjThre\"  // amf_uint64 (default=0)    color adjustment threshold\n#define AMF_CHROMAKEY_COLOR_ADJ_THRE2 L\"ChromaKeyColorAdjThre2\" // amf_uint64 (default=0)    color adjustment threshold\n#define AMF_CHROMAKEY_BYPASS          L\"ChromaKeyBypass\"        // amf_uint64 (default=0)    disable chromakey\n#define AMF_CHROMAKEY_EDGE            L\"ChromaKeyEdge\"          // amf_uint64 (default=0)    endble edge detection\n#define AMF_CHROMAKEY_BOKEH           L\"ChromaKeyBokeh\"         // amf_uint64 (default=0)    endble background bokeh\n#define AMF_CHROMAKEY_BOKEH_RADIUS    L\"ChromaKeyBokehRadius\"   // amf_uint64 (default=7)    background bokeh radius\n#define AMF_CHROMAKEY_DEBUG           L\"ChromaKeyDebug\"         // amf_uint64 (default=0)    endble debug mode\n\n#define AMF_CHROMAKEY_POSX            L\"ChromaKeyPosX\"          // amf_uint64 (default=0)    positionX\n#define AMF_CHROMAKEY_POSY            L\"ChromaKeyPosY\"          // amf_uint64 (default=0)    positionY\n\nextern \"C\"\n{\n    AMF_RESULT AMF_CDECL_CALL AMFCreateComponentChromaKey(amf::AMFContext* pContext, amf::AMFComponentEx** ppComponent);\n}\n#endif //#ifndef __AMFChromaKey_h__\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/ColorSpace.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// Color Spacedeclaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_ColorSpace_h\n#define AMF_ColorSpace_h\n#pragma once\n\n#include \"../core/Platform.h\"\n\n// YUV <--> RGB conversion matrix with range\ntypedef enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM\n{\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN   =-1,\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_601       = 0,    // studio range \n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_709       = 1,    // studio range \n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020      = 2,    // studio range \n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_JPEG      = 3,    // full range 601\n//    AMF_VIDEO_CONVERTER_COLOR_PROFILE_G22_BT709 = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709,\n//    AMF_VIDEO_CONVERTER_COLOR_PROFILE_G10_SCRGB = 4,\n//    AMF_VIDEO_CONVERTER_COLOR_PROFILE_G10_BT709 = 5,\n//    AMF_VIDEO_CONVERTER_COLOR_PROFILE_G10_BT2020 = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020,\n//    AMF_VIDEO_CONVERTER_COLOR_PROFILE_G2084_BT2020 = 6,\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601  = AMF_VIDEO_CONVERTER_COLOR_PROFILE_JPEG,    // full range\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709  = 7,                                         // full range\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020 = 8,                                        // full range\n    AMF_VIDEO_CONVERTER_COLOR_PROFILE_COUNT\n} AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM;\n\ntypedef enum AMF_COLOR_PRIMARIES_ENUM // as in VUI color_primaries AVC and HEVC\n{\n    AMF_COLOR_PRIMARIES_UNDEFINED   = 0,\n    AMF_COLOR_PRIMARIES_BT709       = 1,\n    AMF_COLOR_PRIMARIES_UNSPECIFIED = 2,\n    AMF_COLOR_PRIMARIES_RESERVED    = 3,\n    AMF_COLOR_PRIMARIES_BT470M      = 4,\n    AMF_COLOR_PRIMARIES_BT470BG     = 5,\n    AMF_COLOR_PRIMARIES_SMPTE170M   = 6,\n    AMF_COLOR_PRIMARIES_SMPTE240M   = 7,\n    AMF_COLOR_PRIMARIES_FILM        = 8,\n    AMF_COLOR_PRIMARIES_BT2020      = 9,\n    AMF_COLOR_PRIMARIES_SMPTE428    = 10,\n    AMF_COLOR_PRIMARIES_SMPTE431    = 11,\n    AMF_COLOR_PRIMARIES_SMPTE432    = 12,\n    AMF_COLOR_PRIMARIES_JEDEC_P22   = 22,\n    AMF_COLOR_PRIMARIES_CCCS        = 1000, // Common Composition Color Space or scRGB\n} AMF_COLOR_PRIMARIES_ENUM;\n\ntypedef enum AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM // as in VUI transfer_characteristic AVC and HEVC\n{\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED     = 0,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709         = 1, //BT709\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_UNSPECIFIED   = 2,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_RESERVED      = 3,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22       = 4, //BT470_M\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA28       = 5, //BT470\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE170M     = 6, //BT601\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE240M     = 7, //SMPTE 240M\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_LINEAR        = 8,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_LOG           = 9, //LOG10\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_LOG_SQRT      = 10,//LOG10 SQRT\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_IEC61966_2_4  = 11,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_BT1361_ECG    = 12,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_IEC61966_2_1  = 13,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_BT2020_10     = 14, //BT709\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_BT2020_12     = 15, //BT709\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE2084     = 16, //PQ\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE428      = 17,\n    AMF_COLOR_TRANSFER_CHARACTERISTIC_ARIB_STD_B67  = 18, //HLG\n} AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM;\n\ntypedef enum AMF_COLOR_BIT_DEPTH_ENUM\n{\n    AMF_COLOR_BIT_DEPTH_UNDEFINED   = 0,\n    AMF_COLOR_BIT_DEPTH_8           = 8,\n    AMF_COLOR_BIT_DEPTH_10          = 10,\n} AMF_COLOR_BIT_DEPTH_ENUM;\n\ntypedef struct AMFHDRMetadata\n{\n    amf_uint16  redPrimary[2];              // normalized to 50000\n    amf_uint16  greenPrimary[2];            // normalized to 50000\n    amf_uint16  bluePrimary[2];             // normalized to 50000\n    amf_uint16  whitePoint[2];              // normalized to 50000\n    amf_uint32  maxMasteringLuminance;      // normalized to 10000\n    amf_uint32  minMasteringLuminance;      // normalized to 10000\n    amf_uint16  maxContentLightLevel;       // nit value \n    amf_uint16  maxFrameAverageLightLevel;  // nit value \n} AMFHDRMetadata;\n\n\ntypedef enum AMF_COLOR_RANGE_ENUM\n{\n    AMF_COLOR_RANGE_UNDEFINED   = 0,\n    AMF_COLOR_RANGE_STUDIO      = 1,\n    AMF_COLOR_RANGE_FULL        = 2,\n} AMF_COLOR_RANGE_ENUM;\n\n\n// these properties can be set on input or outout surface\n// IDs are the same as in decoder properties\n// can be used to dynamically pass color data between components:\n// Decoder, Capture, Encoder. Presenter etc.\n#define AMF_VIDEO_COLOR_TRANSFER_CHARACTERISTIC         L\"ColorTransferChar\"    // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 Section 7.2 See ColorSpace.h for enum \n#define AMF_VIDEO_COLOR_PRIMARIES                       L\"ColorPrimaries\"       // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 Section 7.1 See ColorSpace.h for enum \n#define AMF_VIDEO_COLOR_RANGE                           L\"ColorRange\"           // amf_int64(AMF_COLOR_RANGE_ENUM) default = AMF_COLOR_RANGE_UNDEFINED\n#define AMF_VIDEO_COLOR_HDR_METADATA                    L\"HdrMetadata\"          // AMFBuffer containing AMFHDRMetadata; default NULL\n\n#endif //#ifndef AMF_ColorSpace_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/Component.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n ***************************************************************************************************\n * @file  Component.h\n * @brief AMFComponent interface declaration\n ***************************************************************************************************\n */\n#ifndef AMF_Component_h\n#define AMF_Component_h\n#pragma once\n\n#include \"../core/Data.h\"\n#include \"../core/PropertyStorageEx.h\"\n#include \"../core/Surface.h\"\n#include \"../core/Context.h\"\n#include \"ComponentCaps.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // AMFDataAllocatorCB interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFDataAllocatorCB : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x4bf46198, 0x8b7b, 0x49d0, 0xaa, 0x72, 0x48, 0xd4, 0x7, 0xce, 0x24, 0xc5 )\n\n        virtual AMF_RESULT AMF_STD_CALL AllocBuffer(AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer) = 0;\n        virtual AMF_RESULT AMF_STD_CALL AllocSurface(AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format,\n            amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, AMFSurface** ppSurface) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFDataAllocatorCB> AMFDataAllocatorCBPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFDataAllocatorCB, 0x4bf46198, 0x8b7b, 0x49d0, 0xaa, 0x72, 0x48, 0xd4, 0x7, 0xce, 0x24, 0xc5 )\n    typedef struct AMFDataAllocatorCB AMFDataAllocatorCB;\n\n    typedef struct AMFDataAllocatorCBVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFDataAllocatorCB* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFDataAllocatorCB* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFDataAllocatorCB* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n        // AMFDataAllocatorCB interface \n        AMF_RESULT (AMF_STD_CALL *AllocBuffer)(AMFDataAllocatorCB* pThis, AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer);\n        AMF_RESULT (AMF_STD_CALL *AllocSurface)(AMFDataAllocatorCB* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format,\n            amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, AMFSurface** ppSurface);\n    } AMFDataAllocatorCBVtbl;\n\n    struct AMFDataAllocatorCB\n    {\n        const AMFDataAllocatorCBVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComponentOptimizationCallback\n    {\n    public:\n        virtual AMF_RESULT AMF_STD_CALL OnComponentOptimizationProgress(amf_uint percent) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFComponentOptimizationCallback AMFComponentOptimizationCallback;\n    typedef struct AMFComponentOptimizationCallbackVtbl\n    {\n        // AMFDataAllocatorCB interface \n        AMF_RESULT (AMF_STD_CALL *OnComponentOptimizationProgress)(AMFComponentOptimizationCallback* pThis, amf_uint percent);\n    } AMFComponentOptimizationCallbackVtbl;\n\n    struct AMFComponentOptimizationCallback\n    {\n        const AMFComponentOptimizationCallbackVtbl *pVtbl;\n    };\n\n#endif //#if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMFComponent interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComponent : public AMFPropertyStorageEx\n    {\n    public:\n        AMF_DECLARE_IID(0x8b51e5e4, 0x455d, 0x4034, 0xa7, 0x46, 0xde, 0x1b, 0xed, 0xc3, 0xc4, 0x6)\n\n        virtual AMF_RESULT  AMF_STD_CALL Init(AMF_SURFACE_FORMAT format,amf_int32 width,amf_int32 height) = 0;\n        virtual AMF_RESULT  AMF_STD_CALL ReInit(amf_int32 width,amf_int32 height) = 0;\n        virtual AMF_RESULT  AMF_STD_CALL Terminate() = 0;\n        virtual AMF_RESULT  AMF_STD_CALL Drain() = 0;\n        virtual AMF_RESULT  AMF_STD_CALL Flush() = 0;\n\n        virtual AMF_RESULT  AMF_STD_CALL SubmitInput(AMFData* pData) = 0;\n        virtual AMF_RESULT  AMF_STD_CALL QueryOutput(AMFData** ppData) = 0;\n        virtual AMFContext* AMF_STD_CALL GetContext() = 0;\n        virtual AMF_RESULT  AMF_STD_CALL SetOutputDataAllocatorCB(AMFDataAllocatorCB* callback) = 0;\n\n        virtual AMF_RESULT  AMF_STD_CALL GetCaps(AMFCaps** ppCaps) = 0;\n        virtual AMF_RESULT  AMF_STD_CALL Optimize(AMFComponentOptimizationCallback* pCallback) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFComponent> AMFComponentPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComponent, 0x8b51e5e4, 0x455d, 0x4034, 0xa7, 0x46, 0xde, 0x1b, 0xed, 0xc3, 0xc4, 0x6)\n    typedef struct AMFComponent AMFComponent;\n\n    typedef struct AMFComponentVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComponent* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComponent* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComponent* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFComponent* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFComponent* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFComponent* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFComponent* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFComponent* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFComponent* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFComponent* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFComponent* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFComponent* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFComponent* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFComponent* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFComponent* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFComponent* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFComponent* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n        // AMFComponent interface\n\n        AMF_RESULT  (AMF_STD_CALL *Init)(AMFComponent* pThis, AMF_SURFACE_FORMAT format,amf_int32 width,amf_int32 height);\n        AMF_RESULT  (AMF_STD_CALL *ReInit)(AMFComponent* pThis, amf_int32 width,amf_int32 height);\n        AMF_RESULT  (AMF_STD_CALL *Terminate)(AMFComponent* pThis);\n        AMF_RESULT  (AMF_STD_CALL *Drain)(AMFComponent* pThis);\n        AMF_RESULT  (AMF_STD_CALL *Flush)(AMFComponent* pThis);\n\n        AMF_RESULT  (AMF_STD_CALL *SubmitInput)(AMFComponent* pThis, AMFData* pData);\n        AMF_RESULT  (AMF_STD_CALL *QueryOutput)(AMFComponent* pThis, AMFData** ppData);\n        AMFContext* (AMF_STD_CALL *GetContext)(AMFComponent* pThis);\n        AMF_RESULT  (AMF_STD_CALL *SetOutputDataAllocatorCB)(AMFComponent* pThis, AMFDataAllocatorCB* callback);\n\n        AMF_RESULT  (AMF_STD_CALL *GetCaps)(AMFComponent* pThis, AMFCaps** ppCaps);\n        AMF_RESULT  (AMF_STD_CALL *Optimize)(AMFComponent* pThis, AMFComponentOptimizationCallback* pCallback);\n    } AMFComponentVtbl;\n\n    struct AMFComponent\n    {\n        const AMFComponentVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMFInput interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFInput : public AMFPropertyStorageEx\n    {\n    public:\n        AMF_DECLARE_IID(0x1181eee7, 0x95f2, 0x434a, 0x9b, 0x96, 0xea, 0x55, 0xa, 0xa7, 0x84, 0x89)\n\n        virtual AMF_RESULT  AMF_STD_CALL SubmitInput(AMFData* pData) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFInput> AMFInputPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFInput, 0x1181eee7, 0x95f2, 0x434a, 0x9b, 0x96, 0xea, 0x55, 0xa, 0xa7, 0x84, 0x89)\n    typedef struct AMFInput AMFInput;\n\n    typedef struct AMFInputVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFInput* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFInput* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFInput* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFInput* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFInput* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFInput* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFInput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFInput* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFInput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFInput* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFInput* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFInput* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFInput* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFInput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFInput* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFInput* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFInput* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n        // AMFInput interface\n        AMF_RESULT          (AMF_STD_CALL *SubmitInput)(AMFInput* pThis, AMFData* pData);\n\n    } AMFInputVtbl;\n\n    struct AMFInput\n    {\n        const AMFInputVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFOutput interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFOutput : public AMFPropertyStorageEx\n    {\n    public:\n        AMF_DECLARE_IID(0x86a8a037, 0x912c, 0x4698, 0xb0, 0x46, 0x7, 0x5a, 0x1f, 0xac, 0x6b, 0x97)\n\n        virtual AMF_RESULT  AMF_STD_CALL QueryOutput(AMFData** ppData) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFOutput> AMFOutputPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFOutput, 0x86a8a037, 0x912c, 0x4698, 0xb0, 0x46, 0x7, 0x5a, 0x1f, 0xac, 0x6b, 0x97)\n    typedef struct AMFOutput AMFOutput;\n\n    typedef struct AMFOutputVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFOutput* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFOutput* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFOutput* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFOutput* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFOutput* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFOutput* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFOutput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFOutput* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFOutput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFOutput* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFOutput* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFOutput* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFOutput* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFOutput* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFOutput* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFOutput* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFOutput* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n        // AMFOutput interface\n        AMF_RESULT          (AMF_STD_CALL *QueryOutput)(AMFOutput* pThis, AMFData** ppData);\n\n    } AMFOutputVtbl;\n\n    struct AMFOutput\n    {\n        const AMFOutputVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMFComponent interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComponentEx : public AMFComponent\n    {\n    public:\n        AMF_DECLARE_IID(0xfda792af, 0x8712, 0x44df, 0x8e, 0xa0, 0xdf, 0xfa, 0xad, 0x2c, 0x80, 0x93)\n\n        virtual amf_int32   AMF_STD_CALL GetInputCount() = 0;\n        virtual amf_int32   AMF_STD_CALL GetOutputCount() = 0;\n\n        virtual AMF_RESULT  AMF_STD_CALL GetInput(amf_int32 index, AMFInput** ppInput) = 0;\n        virtual AMF_RESULT  AMF_STD_CALL GetOutput(amf_int32 index, AMFOutput** ppOutput) = 0;\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFComponentEx> AMFComponentExPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComponentEx, 0xfda792af, 0x8712, 0x44df, 0x8e, 0xa0, 0xdf, 0xfa, 0xad, 0x2c, 0x80, 0x93)\n    typedef struct AMFComponentEx AMFComponentEx;\n\n    typedef struct AMFComponentExVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComponentEx* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComponentEx* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComponentEx* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFComponentEx* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFComponentEx* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFComponentEx* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFComponentEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFComponentEx* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFComponentEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFComponentEx* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFComponentEx* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFComponentEx* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFComponentEx* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFComponentEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFComponentEx* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFComponentEx* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFComponentEx* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n        // AMFComponent interface\n\n        AMF_RESULT  (AMF_STD_CALL *Init)(AMFComponentEx* pThis, AMF_SURFACE_FORMAT format,amf_int32 width,amf_int32 height);\n        AMF_RESULT  (AMF_STD_CALL *ReInit)(AMFComponentEx* pThis, amf_int32 width,amf_int32 height);\n        AMF_RESULT  (AMF_STD_CALL *Terminate)(AMFComponentEx* pThis);\n        AMF_RESULT  (AMF_STD_CALL *Drain)(AMFComponentEx* pThis);\n        AMF_RESULT  (AMF_STD_CALL *Flush)(AMFComponentEx* pThis);\n\n        AMF_RESULT  (AMF_STD_CALL *SubmitInput)(AMFComponentEx* pThis, AMFData* pData);\n        AMF_RESULT  (AMF_STD_CALL *QueryOutput)(AMFComponentEx* pThis, AMFData** ppData);\n        AMFContext* (AMF_STD_CALL *GetContext)(AMFComponentEx* pThis);\n        AMF_RESULT  (AMF_STD_CALL *SetOutputDataAllocatorCB)(AMFComponentEx* pThis, AMFDataAllocatorCB* callback);\n\n        AMF_RESULT  (AMF_STD_CALL *GetCaps)(AMFComponentEx* pThis, AMFCaps** ppCaps);\n        AMF_RESULT  (AMF_STD_CALL *Optimize)(AMFComponentEx* pThis, AMFComponentOptimizationCallback* pCallback);\n\n        // AMFComponentEx interface\n\n        amf_int32   (AMF_STD_CALL *GetInputCount)(AMFComponentEx* pThis);\n        amf_int32   (AMF_STD_CALL *GetOutputCount)(AMFComponentEx* pThis);\n\n        AMF_RESULT  (AMF_STD_CALL *GetInput)(AMFComponentEx* pThis, amf_int32 index, AMFInput** ppInput);\n        AMF_RESULT  (AMF_STD_CALL *GetOutput)(AMFComponentEx* pThis, amf_int32 index, AMFOutput** ppOutput);\n\n\n    } AMFComponentExVtbl;\n\n    struct AMFComponentEx\n    {\n        const AMFComponentExVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n\n#if defined(__cplusplus)\n} // namespace\n#endif\n\ntypedef enum AMF_STREAM_TYPE_ENUM\n{\n    AMF_STREAM_UNKNOWN              = 0,\n    AMF_STREAM_VIDEO                = 1,\n    AMF_STREAM_AUDIO                = 2,\n    AMF_STREAM_DATA                 = 3,\n} AMF_STREAM_TYPE_ENUM;\n\ntypedef enum AMF_STREAM_CODEC_ID_ENUM     // matched codecs from VideoDecoxcderUVD.h\n{\n    AMF_STREAM_CODEC_ID_UNKNOWN     = 0,\n    AMF_STREAM_CODEC_ID_MPEG2       = 1,  // AMFVideoDecoderUVD_MPEG2      \n    AMF_STREAM_CODEC_ID_MPEG4       = 2,  // AMFVideoDecoderUVD_MPEG4      \n    AMF_STREAM_CODEC_ID_WMV3        = 3,  // AMFVideoDecoderUVD_WMV3       \n    AMF_STREAM_CODEC_ID_VC1         = 4,  // AMFVideoDecoderUVD_VC1        \n    AMF_STREAM_CODEC_ID_H264_AVC    = 5,  // AMFVideoDecoderUVD_H264_AVC   \n    AMF_STREAM_CODEC_ID_H264_MVC    = 6,  // AMFVideoDecoderUVD_H264_MVC   \n    AMF_STREAM_CODEC_ID_H264_SVC    = 7,  // AMFVideoDecoderUVD_H264_SVC   \n    AMF_STREAM_CODEC_ID_MJPEG       = 8,  // AMFVideoDecoderUVD_MJPEG      \n    AMF_STREAM_CODEC_ID_H265_HEVC   = 9,  // AMFVideoDecoderHW_H265_HEVC   \n    AMF_STREAM_CODEC_ID_H265_MAIN10 = 10, // AMFVideoDecoderHW_H265_MAIN10 \n    AMF_STREAM_CODEC_ID_VP9         = 11, // AMFVideoDecoderHW_VP9\n    AMF_STREAM_CODEC_ID_VP9_10BIT   = 12, // AMFVideoDecoderHW_VP9_10BIT   \n    AMF_STREAM_CODEC_ID_AV1         = 13, // AMFVideoDecoderHW_AV1 \n    AMF_STREAM_CODEC_ID_AV1_12BIT   = 14, // AMFVideoDecoderHW_AV1_12BIT\n} AMF_STREAM_CODEC_ID_ENUM;\n\n// common stream properties\n#define AMF_STREAM_TYPE                     L\"StreamType\"           // amf_int64( AMF_STREAM_TYPE_ENUM )\n#define AMF_STREAM_ENABLED                  L\"Enabled\"              // bool( default = false )\n#define AMF_STREAM_CODEC_ID                 L\"CodecID\"              // amf_int64(Video: AMF_STREAM_CODEC_ID_ENUM, Audio: AVCodecID) (default = 0 - uncompressed)\n#define AMF_STREAM_BIT_RATE                 L\"BitRate\"              // amf_int64 (default = codec->bit_rate)\n#define AMF_STREAM_EXTRA_DATA               L\"ExtraData\"            // interface to AMFBuffer - as is from FFMPEG\n\n// video stream properties\n#define AMF_STREAM_VIDEO_MEMORY_TYPE        L\"VideoMemoryType\"      // amf_int64(AMF_MEMORY_TYPE); default = AMF_MEMORY_DX11\n#define AMF_STREAM_VIDEO_FORMAT             L\"VideoFormat\"          // amf_int64(AMF_SURFACE_FORMAT); default = AMF_SURFACE_NV12 (used if AMF_STREAM_CODEC_ID == 0)\n#define AMF_STREAM_VIDEO_FRAME_RATE         L\"VideoFrameRate\"       // AMFRate; default = (30,1) - video frame rate\n#define AMF_STREAM_VIDEO_FRAME_SIZE         L\"VideoFrameSize\"       // AMFSize; default = (1920,1080) - video frame rate\n#define AMF_STREAM_VIDEO_SURFACE_POOL       L\"VideoSurfacePool\"     // amf_int64; default = 5, number of allocated output surfaces\n//TODO support interlaced frames\n\n// audio stream properties\n#define AMF_STREAM_AUDIO_FORMAT             L\"AudioFormat\"          // amf_int64(AMF_AUDIO_FORMAT); default = AMFAF_S16\n#define AMF_STREAM_AUDIO_SAMPLE_RATE        L\"AudioSampleRate\"      // amf_int64; default = 48000\n#define AMF_STREAM_AUDIO_CHANNELS           L\"AudioChannels\"        // amf_int64; default = 2\n#define AMF_STREAM_AUDIO_CHANNEL_LAYOUT     L\"AudioChannelLayout\"   // amf_int64 (default = codec->channel_layout)\n#define AMF_STREAM_AUDIO_BLOCK_ALIGN        L\"AudioBlockAlign\"      // amf_int64 (default = codec->block_align)\n#define AMF_STREAM_AUDIO_FRAME_SIZE         L\"AudioFrameSize\"       // amf_int64 (default = codec->frame_size)\n\n\n#endif //#ifndef AMF_Component_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/ComponentCaps.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_ComponentCaps_h\n#define AMF_ComponentCaps_h\n\n#pragma once\n\n#include \"../core/Interface.h\"\n#include \"../core/PropertyStorage.h\"\n#include \"../core/Surface.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    typedef enum AMF_ACCELERATION_TYPE\n    {\n        AMF_ACCEL_NOT_SUPPORTED = -1,\n        AMF_ACCEL_HARDWARE,\n        AMF_ACCEL_GPU,\n        AMF_ACCEL_SOFTWARE\n    } AMF_ACCELERATION_TYPE;\n    //----------------------------------------------------------------------------------------------\n    // AMFIOCaps interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFIOCaps : public AMFInterface\n    {\n    public:\n        //  Get supported resolution ranges in pixels/lines:\n        virtual void AMF_STD_CALL GetWidthRange(amf_int32* minWidth, amf_int32* maxWidth) const = 0;\n        virtual void AMF_STD_CALL GetHeightRange(amf_int32* minHeight, amf_int32* maxHeight) const = 0;\n\n        //  Get memory alignment in lines: Vertical aligmnent should be multiples of this number\n        virtual amf_int32 AMF_STD_CALL GetVertAlign() const = 0;\n        \n        //  Enumerate supported surface pixel formats\n        virtual amf_int32 AMF_STD_CALL GetNumOfFormats() const = 0;\n        virtual  AMF_RESULT AMF_STD_CALL GetFormatAt(amf_int32 index, AMF_SURFACE_FORMAT* format, amf_bool* native) const = 0;\n\n        //  Enumerate supported memory types\n        virtual amf_int32 AMF_STD_CALL GetNumOfMemoryTypes() const = 0;\n        virtual AMF_RESULT AMF_STD_CALL GetMemoryTypeAt(amf_int32 index, AMF_MEMORY_TYPE* memType, amf_bool* native) const = 0;\n\n        virtual amf_bool AMF_STD_CALL IsInterlacedSupported() const = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFIOCaps>    AMFIOCapsPtr;\n#else // #if defined(__cplusplus)\n    typedef struct AMFIOCaps AMFIOCaps;\n\n    typedef struct AMFIOCapsVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFIOCaps* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFIOCaps* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFIOCaps* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFIOCaps interface\n        //  Get supported resolution ranges in pixels/lines:\n        void (AMF_STD_CALL *GetWidthRange)(AMFIOCaps* pThis, amf_int32* minWidth, amf_int32* maxWidth);\n        void (AMF_STD_CALL *GetHeightRange)(AMFIOCaps* pThis, amf_int32* minHeight, amf_int32* maxHeight);\n\n        //  Get memory alignment in lines: Vertical aligmnent should be multiples of this number\n        amf_int32 (AMF_STD_CALL *GetVertAlign)(AMFIOCaps* pThis);\n        \n        //  Enumerate supported surface pixel formats\n        amf_int32 (AMF_STD_CALL *GetNumOfFormats)(AMFIOCaps* pThis);\n        AMF_RESULT (AMF_STD_CALL *GetFormatAt)(AMFIOCaps* pThis, amf_int32 index, AMF_SURFACE_FORMAT* format, amf_bool* native);\n\n        //  Enumerate supported memory types\n        amf_int32 (AMF_STD_CALL *GetNumOfMemoryTypes)(AMFIOCaps* pThis);\n        AMF_RESULT (AMF_STD_CALL *GetMemoryTypeAt)(AMFIOCaps* pThis, amf_int32 index, AMF_MEMORY_TYPE* memType, amf_bool* native);\n\n        amf_bool (AMF_STD_CALL *IsInterlacedSupported)(AMFIOCaps* pThis);\n    } AMFIOCapsVtbl;\n\n    struct AMFIOCaps\n    {\n        const AMFIOCapsVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n   \n    //----------------------------------------------------------------------------------------------\n    // AMFCaps interface - base interface for every h/w module supported by Capability Manager\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFCaps : public AMFPropertyStorage\n    {\n    public:\n        virtual AMF_ACCELERATION_TYPE AMF_STD_CALL GetAccelerationType() const = 0;\n        virtual AMF_RESULT AMF_STD_CALL GetInputCaps(AMFIOCaps** input) = 0;\n        virtual AMF_RESULT AMF_STD_CALL GetOutputCaps(AMFIOCaps** output) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFCaps>  AMFCapsPtr;\n#else // #if defined(__cplusplus)\n    typedef struct AMFCaps AMFCaps;\n\n    typedef struct AMFCapsVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFCaps* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFCaps* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFCaps* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFCaps* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFCaps* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFCaps* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFCaps* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFCaps* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFCaps* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFCaps* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFCaps* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFCaps* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFCaps* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFCaps interface\n\n        AMF_ACCELERATION_TYPE (AMF_STD_CALL *GetAccelerationType)(AMFCaps* pThis);\n        AMF_RESULT (AMF_STD_CALL *GetInputCaps)(AMFCaps* pThis, AMFIOCaps** input);\n        AMF_RESULT (AMF_STD_CALL *GetOutputCaps)(AMFCaps* pThis, AMFIOCaps** output);\n    } AMFCapsVtbl;\n\n    struct AMFCaps\n    {\n        const AMFCapsVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n}\n#endif\n\n#endif //#ifndef AMF_ComponentCaps_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/CursorCapture.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// Cursor capture interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_CursorCapture_h\n#define AMF_CursorCapture_h\n#pragma once\n\nnamespace amf\n{\n    class AMFCursorCapture : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x166efa1a, 0x19b8, 0x42f2, 0x86, 0x0f, 0x56, 0x69, 0xca, 0x7a, 0x85, 0x4c)\n        virtual AMF_RESULT AMF_STD_CALL AcquireCursor(amf::AMFSurface** pSurface) = 0;\n        virtual AMF_RESULT AMF_STD_CALL Reset() = 0;\n    };\n\n    typedef AMFInterfacePtr_T<AMFCursorCapture> AMFCursorCapturePtr;\n}\n\n#endif // #ifndef AMF_CursorCapture_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/DisplayCapture.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// Desktop duplication interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_DisplayCapture_h\n#define AMF_DisplayCapture_h\n#pragma once\n\n#include \"Component.h\"\n\nextern \"C\"\n{\n    // To create capture component with Desktop Duplication API use this function\n    AMF_RESULT AMF_CDECL_CALL AMFCreateComponentDisplayCapture(amf::AMFContext* pContext, void* reserved, amf::AMFComponent** ppComponent);\n}\n\n// To create AMD Direct Capture component use this component ID with AMFFactory::CreateComponent()\n#define AMFDisplayCapture L\"AMFDisplayCapture\"\n\n// Static properties\n//\ntypedef enum AMF_DISPLAYCAPTURE_MODE_ENUM\n{\n    AMF_DISPLAYCAPTURE_MODE_KEEP_FRAMERATE      = 0, // capture component maintains the frame rate and returns current visible surface\n    AMF_DISPLAYCAPTURE_MODE_WAIT_FOR_PRESENT    = 1, // capture component waits for flip (present) event\n    AMF_DISPLAYCAPTURE_MODE_GET_CURRENT_SURFACE = 2, // returns current visible surface immediately\n} AMF_DISPLAYCAPTURE_MODE_ENUM;\n\n\n#define AMF_DISPLAYCAPTURE_MONITOR_INDEX            L\"MonitorIndex\"             // amf_int64, default = 0, Index of the display monitor; is determined by using EnumAdapters() in DXGI.\n#define AMF_DISPLAYCAPTURE_MODE                     L\"CaptureMode\"              // amf_int64(AMF_DISPLAYCAPTURE_MODE_ENUM), default =  AMF_DISPLAYCAPTURE_MODE_FRAMERATE, controls wait logic\n#define AMF_DISPLAYCAPTURE_FRAMERATE                L\"FrameRate\"                // AMFRate,  default = (0, 1) Capture framerate, if 0 - capture rate will be driven by flip event from fullscreen app or DWM\n#define AMF_DISPLAYCAPTURE_CURRENT_TIME_INTERFACE   L\"CurrentTimeInterface\"     // AMFInterface(AMFCurrentTime) Optional interface object for providing  timestamps.\n#define AMF_DISPLAYCAPTURE_FORMAT                   L\"CurrentFormat\"            // amf_int64(AMF_SURFACE_FORMAT) Capture format - read-only\n#define AMF_DISPLAYCAPTURE_RESOLUTION               L\"Resolution\"               // AMFSize - screen resolution - read-only\n#define AMF_DISPLAYCAPTURE_DUPLICATEOUTPUT          L\"DuplicateOutput\"          // amf_bool, default = false, output AMF surface is a copy of captured\n#define AMF_DISPLAYCAPTURE_DESKTOP_RECT             L\"DesktopRect\"              // AMFRect - rect of the capture desktop - read-only\n#define AMF_DISPLAYCAPTURE_ENABLE_DIRTY_RECTS       L\"EnableDirtyRects\"         // amf_bool, default = false, enable dirty rectangles attached to output as AMF_DISPLAYCAPTURE_DIRTY_RECTS\n#define AMF_DISPLAYCAPTURE_DRAW_DIRTY_RECTS         L\"DrawDirtyRects\"           // amf_bool, default = false, copies capture output and draws dirty rectangles with red - for debugging only\n#define AMF_DISPLAYCAPTURE_ROTATION                 L\"Rotation\"                 // amf_int64(AMF_ROTATION_ENUM); default = AMF_ROTATION_NONE, monitor rotation state\n\n// Properties that can be set on output AMFSurface\n#define AMF_DISPLAYCAPTURE_DIRTY_RECTS              L\"DirtyRects\"               // AMFInterface*(AMFBuffer*) - array of AMFRect(s)\n#define AMF_DISPLAYCAPTURE_FRAME_INDEX              L\"FrameIndex\"               // amf_int64; default = 0, index of presented frame since capture started\n#define AMF_DISPLAYCAPTURE_FRAME_FLIP_TIMESTAMP     L\"FlipTimesamp\"             // amf_int64; default = 0, flip timestmap of presented frame\n// see Surface.h\n//#define AMF_SURFACE_ROTATION         L\"Rotation\"    // amf_int64(AMF_ROTATION_ENUM); default = AMF_ROTATION_NONE, can be set on surfaces - the same value as AMF_DISPLAYCAPTURE_ROTATION\n\n#endif // #ifndef AMF_DisplayCapture_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGAudioConverter.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// AMFFAudioConverterFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_AudioConverterFFMPEG_h\n#define AMF_AudioConverterFFMPEG_h\n\n#pragma once\n\n\n#define FFMPEG_AUDIO_CONVERTER    L\"AudioConverterFFMPEG\"\n\n\n#define AUDIO_CONVERTER_IN_AUDIO_BIT_RATE             L\"In_BitRate\"                 // amf_int64 (default = 128000)\n#define AUDIO_CONVERTER_IN_AUDIO_SAMPLE_RATE          L\"In_SampleRate\"              // amf_int64 (default = 0)\n#define AUDIO_CONVERTER_IN_AUDIO_CHANNELS             L\"In_Channels\"                // amf_int64 (default = 2)\n#define AUDIO_CONVERTER_IN_AUDIO_SAMPLE_FORMAT        L\"In_SampleFormat\"            // amf_int64 (default = AMFAF_UNKNOWN)  (AMF_AUDIO_FORMAT)\n#define AUDIO_CONVERTER_IN_AUDIO_CHANNEL_LAYOUT       L\"In_ChannelLayout\"           // amf_int64 (default = 0)\n#define AUDIO_CONVERTER_IN_AUDIO_BLOCK_ALIGN          L\"In_BlockAlign\"              // amf_int64 (default = 0)\n\n#define AUDIO_CONVERTER_OUT_AUDIO_BIT_RATE            L\"Out_BitRate\"                // amf_int64 (default = 128000)\n#define AUDIO_CONVERTER_OUT_AUDIO_SAMPLE_RATE         L\"Out_SampleRate\"             // amf_int64 (default = 0)\n#define AUDIO_CONVERTER_OUT_AUDIO_CHANNELS            L\"Out_Channels\"               // amf_int64 (default = 2)\n#define AUDIO_CONVERTER_OUT_AUDIO_SAMPLE_FORMAT       L\"Out_SampleFormat\"           // amf_int64 (default = AMFAF_UNKNOWN)  (AMF_AUDIO_FORMAT)\n#define AUDIO_CONVERTER_OUT_AUDIO_CHANNEL_LAYOUT      L\"Out_ChannelLayout\"          // amf_int64 (default = 0)\n#define AUDIO_CONVERTER_OUT_AUDIO_BLOCK_ALIGN         L\"Out_BlockAlign\"             // amf_int64 (default = 0)\n\n\n\n#endif //#ifndef AMF_AudioConverterFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGAudioDecoder.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// AudioDecoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_AudioDecoderFFMPEG_h\n#define AMF_AudioDecoderFFMPEG_h\n\n#pragma once\n\n\n#define FFMPEG_AUDIO_DECODER    L\"AudioDecoderFFMPEG\"\n\n\n#define AUDIO_DECODER_ENABLE_DEBUGGING              L\"EnableDebug\"                  // bool (default = false) - trace some debug information if set to true\n#define AUDIO_DECODER_ENABLE_DECODING               L\"EnableDecoding\"               // bool (default = true) - if false, component will not decode anything\n\n#define AUDIO_DECODER_IN_AUDIO_CODEC_ID             L\"In_CodecID\"                   // amf_int64 (default = AV_CODEC_ID_NONE) - FFMPEG codec ID\n#define AUDIO_DECODER_IN_AUDIO_BIT_RATE             L\"In_BitRate\"                   // amf_int64 (default = 128000)\n#define AUDIO_DECODER_IN_AUDIO_EXTRA_DATA           L\"In_ExtraData\"                 // interface to AMFBuffer\n#define AUDIO_DECODER_IN_AUDIO_SAMPLE_RATE          L\"In_SampleRate\"                // amf_int64 (default = 0)\n#define AUDIO_DECODER_IN_AUDIO_CHANNELS             L\"In_Channels\"                  // amf_int64 (default = 2)\n#define AUDIO_DECODER_IN_AUDIO_SAMPLE_FORMAT        L\"In_SampleFormat\"              // amf_int64 (default = AMFAF_UNKNOWN)  (AMF_AUDIO_FORMAT)\n#define AUDIO_DECODER_IN_AUDIO_CHANNEL_LAYOUT       L\"In_ChannelLayout\"             // amf_int64 (default = 0)\n#define AUDIO_DECODER_IN_AUDIO_BLOCK_ALIGN          L\"In_BlockAlign\"                // amf_int64 (default = 0)\n#define AUDIO_DECODER_IN_AUDIO_FRAME_SIZE           L\"In_FrameSize\"                 // amf_int64 (default = 0)\n#define AUDIO_DECODER_IN_AUDIO_SEEK_POSITION        L\"In_SeekPosition\"              // amf_int64 (default = 0)\n\n#define AUDIO_DECODER_OUT_AUDIO_BIT_RATE            L\"Out_BitRate\"                  // amf_int64 (default = 128000)\n#define AUDIO_DECODER_OUT_AUDIO_SAMPLE_RATE         L\"Out_SampleRate\"               // amf_int64 (default = 0)\n#define AUDIO_DECODER_OUT_AUDIO_CHANNELS            L\"Out_Channels\"                 // amf_int64 (default = 2)\n#define AUDIO_DECODER_OUT_AUDIO_SAMPLE_FORMAT       L\"Out_SampleFormat\"             // amf_int64 (default = AMFAF_UNKNOWN)  (AMF_AUDIO_FORMAT)\n#define AUDIO_DECODER_OUT_AUDIO_CHANNEL_LAYOUT      L\"Out_ChannelLayout\"            // amf_int64 (default = 0)\n#define AUDIO_DECODER_OUT_AUDIO_BLOCK_ALIGN         L\"Out_BlockAlign\"               // amf_int64 (default = 0)\n\n\n\n#endif //#ifndef AMF_AudioDecoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGAudioEncoder.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// AudioEncoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_AudioEncoderFFMPEG_h\n#define AMF_AudioEncoderFFMPEG_h\n\n#pragma once\n\n\n#define FFMPEG_AUDIO_ENCODER    L\"AudioEncoderFFMPEG\"\n\n\n#define AUDIO_ENCODER_ENABLE_DEBUGGING              L\"EnableDebug\"                  // bool (default = false) - trace some debug information if set to true\n#define AUDIO_ENCODER_ENABLE_ENCODING               L\"EnableEncoding\"               // bool (default = true) - if false, component will not encode anything\n#define AUDIO_ENCODER_AUDIO_CODEC_ID                L\"CodecID\"                      // amf_int64 (default = AV_CODEC_ID_NONE) - FFMPEG codec ID\n\n#define AUDIO_ENCODER_IN_AUDIO_SAMPLE_RATE          L\"In_SampleRate\"                // amf_int64 (default = 44100)\n#define AUDIO_ENCODER_IN_AUDIO_CHANNELS             L\"In_Channels\"                  // amf_int64 (default = 2)\n#define AUDIO_ENCODER_IN_AUDIO_SAMPLE_FORMAT        L\"In_SampleFormat\"              // amf_int64 (default = AMFAF_S16)  (AMF_AUDIO_FORMAT)\n#define AUDIO_ENCODER_IN_AUDIO_CHANNEL_LAYOUT       L\"In_ChannelLayout\"             // amf_int64 (default = 3)\n#define AUDIO_ENCODER_IN_AUDIO_BLOCK_ALIGN          L\"In_BlockAlign\"                // amf_int64 (default = 0)\n\n#define AUDIO_ENCODER_OUT_AUDIO_BIT_RATE            L\"Out_BitRate\"                  // amf_int64 (default = 128000)\n#define AUDIO_ENCODER_OUT_AUDIO_EXTRA_DATA          L\"Out_ExtraData\"                // interface to AMFBuffer\n#define AUDIO_ENCODER_OUT_AUDIO_SAMPLE_RATE         L\"Out_SampleRate\"               // amf_int64 (default = 44100)\n#define AUDIO_ENCODER_OUT_AUDIO_CHANNELS            L\"Out_Channels\"                 // amf_int64 (default = 2)\n#define AUDIO_ENCODER_OUT_AUDIO_SAMPLE_FORMAT       L\"Out_SampleFormat\"             // amf_int64 (default = AMFAF_S16)  (AMF_AUDIO_FORMAT)\n#define AUDIO_ENCODER_OUT_AUDIO_CHANNEL_LAYOUT      L\"Out_ChannelLayout\"            // amf_int64 (default = 0)\n#define AUDIO_ENCODER_OUT_AUDIO_BLOCK_ALIGN         L\"Out_BlockAlign\"               // amf_int64 (default = 0)\n#define AUDIO_ENCODER_OUT_AUDIO_FRAME_SIZE          L\"Out_FrameSize\"                // amf_int64 (default = 0)\n\n\n\n#endif //#ifndef AMF_AudioEncoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGComponents.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// FFMPEG components definitions\n//-------------------------------------------------------------------------------------------------\n \n#ifndef AMF_ComponentsFFMPEG_h\n#define AMF_ComponentsFFMPEG_h\n\n#pragma once\n\n\n#if defined(_WIN32)\n    #if defined(_M_AMD64)\n        #define FFMPEG_DLL_NAME    L\"amf-component-ffmpeg64.dll\"\n    #else\n        #define FFMPEG_DLL_NAME    L\"amf-component-ffmpeg32.dll\"\n    #endif\n#elif defined(__linux)\n    #define FFMPEG_DLL_NAME    L\"amf-component-ffmpeg.so\"\n#endif\n\n\n#endif //#ifndef AMF_ComponentsFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGEncoderAV1.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// HEVCEncoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_AV1EncoderFFMPEG_h\n#define AMF_AV1EncoderFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_ENCODER_AV1 L\"AV1EncoderFFMPEG\"\n\n\n#endif //#ifndef AMF_HEVCEncoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGEncoderH264.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; H264/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// H264EncoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_H264EncoderFFMPEG_h\n#define AMF_H264EncoderFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_ENCODER_H264 L\"H264EncoderFFMPEG\"\n\n\n#endif //#ifndef AMF_H264EncoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGEncoderHEVC.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// HEVCEncoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_HEVCEncoderFFMPEG_h\n#define AMF_HEVCEncoderFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_ENCODER_HEVC L\"HEVCEncoderFFMPEG\"\n\n\n#endif //#ifndef AMF_HEVCEncoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGFileDemuxer.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// DemuxerFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_FileDemuxerFFMPEG_h\n#define AMF_FileDemuxerFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_DEMUXER L\"DemuxerFFMPEG\"\n\n\n// component properties\n#define FFMPEG_DEMUXER_PATH                     L\"Path\"                     // string - the file to open\n#define FFMPEG_DEMUXER_URL                      L\"Url\"                      // string - the stream url to open\n#define FFMPEG_DEMUXER_START_FRAME              L\"StartFrame\"               // amf_int64 (default = 0)\n#define FFMPEG_DEMUXER_FRAME_COUNT              L\"FramesNumber\"             // amf_int64 (default = 0)\n#define FFMPEG_DEMUXER_DURATION                 L\"Duration\"                 // amf_int64 (default = 0)\n#define FFMPEG_DEMUXER_CHECK_MVC                L\"CheckMVC\"                 // bool (default = true)\n//#define FFMPEG_DEMUXER_SYNC_AV                  L\"SyncAV\"                   // bool (default = false)\n#define FFMPEG_DEMUXER_INDIVIDUAL_STREAM_MODE   L\"StreamMode\"               // bool (default = true)\n#define FFMPEG_DEMUXER_LISTEN                   L\"Listen\"                   // bool (default = false)\n\n// for common, video and audio properties see Component.h\n\n\n// video stream properties\n#define FFMPEG_DEMUXER_VIDEO_PIXEL_ASPECT_RATIO L\"PixelAspectRatio\"         // double (default = calculated)\n#define FFMPEG_DEMUXER_VIDEO_CODEC              L\"FFmpegCodec\"              // enum (from source)\n\n\n// buffer properties\n#define FFMPEG_DEMUXER_BUFFER_TYPE              L\"BufferType\"               // amf_int64 ( AMF_STREAM_TYPE_ENUM )\n#define FFMPEG_DEMUXER_BUFFER_STREAM_INDEX      L\"BufferStreamIndexType\"    // amf_int64 ( stream index )\n#endif //#ifndef AMF_FileDemuxerFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGFileMuxer.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// MuxerFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_FileMuxerFFMPEG_h\n#define AMF_FileMuxerFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_MUXER L\"MuxerFFMPEG\"\n\n\n// component properties\n#define FFMPEG_MUXER_PATH                     L\"Path\"                     // string - the file to open\n#define FFMPEG_MUXER_URL                      L\"Url\"                      // string - the stream url to open\n#define FFMPEG_MUXER_LISTEN                   L\"Listen\"                   // bool (default = false)\n#define FFMPEG_MUXER_ENABLE_VIDEO             L\"EnableVideo\"              // bool (default = true)\n#define FFMPEG_MUXER_ENABLE_AUDIO             L\"EnableAudio\"              // bool (default = false)\n#define FFMPEG_MUXER_CURRENT_TIME_INTERFACE   L\"CurrentTimeInterface\"\n#define FFMPEG_MUXER_VIDEO_ROTATION           L\"VideoRotation\"            // amf_int64 (0, 90, 180, 270, default = 0)\n#define FFMPEG_MUXER_USAGE_IS_TRIM            L\"UsageIsTrim\"              // bool (default = false)\n\n#endif //#ifndef AMF_FileMuxerFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FFMPEGVideoDecoder.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// VideoDecoderFFMPEG  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_VideoDecoderFFMPEG_h\n#define AMF_VideoDecoderFFMPEG_h\n\n#pragma once\n\n#define FFMPEG_VIDEO_DECODER    L\"VideoDecoderFFMPEG\"\n\n#define VIDEO_DECODER_ENABLE_DECODING      L\"EnableDecoding\"   // bool (default = true) - if false, component will not decode anything\n#define VIDEO_DECODER_CODEC_ID             L\"CodecID\"          // amf_int64 (AMF_STREAM_CODEC_ID_ENUM) codec ID\n#define VIDEO_DECODER_EXTRA_DATA           L\"ExtraData\"        // interface to AMFBuffer\n#define VIDEO_DECODER_RESOLUTION           L\"Resolution\"       // AMFSize \n#define VIDEO_DECODER_BITRATE              L\"BitRate\"          // amf_int64 (default = 0)\n#define VIDEO_DECODER_FRAMERATE            L\"FrameRate\"        // AMFRate\n#define VIDEO_DECODER_SEEK_POSITION        L\"SeekPosition\"     // amf_int64 (default = 0)\n\n#define VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC L\"ColorTransferChar\"    // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2\n\n#endif //#ifndef AMF_VideoDecoderFFMPEG_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/FRC.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMFFRC_h\n#define AMFFRC_h\n\n#pragma once\n\n#define AMFFRC L\"AMFFRC\"\n\n// Select rendering API for FRC\nenum AMF_FRC_ENGINE\n{\n    FRC_ENGINE_OFF = 0,\n    FRC_ENGINE_DX12 = 1,\n    FRC_ENGINE_OPENCL = 2,\n};\n\n// Select present mode for FRC\nenum AMF_FRC_MODE_TYPE\n{\n    FRC_OFF = 0,\n    FRC_ON,\n    FRC_ONLY_INTERPOLATED,\n    FRC_x2_PRESENT,\n    TOTAL_FRC_MODES\n};\n\nenum AMF_FRC_SNAPSHOT_MODE_TYPE {\n    FRC_SNAPSHOT_OFF = 0,\n    FRC_SNAPSHOT_LOAD,\n    FRC_SNAPSHOT_STORE,\n    FRC_SNAPSHOT_REGRESSION_TEST,\n    FRC_SNAPSHOT_STORE_NO_PADDING,\n    TOTAL_FRC_SNAPSHOT_MODES\n};\n\nenum AMF_FRC_PROFILE {\n    FRC_PROFILE_LOW = 0,\n    FRC_PROFILE_HIGH = 1,\n    FRC_PROFILE_SUPER = 2,\n    TOTAL_FRC_PROFILES\n};\n\nenum AMF_FRC_MV_SEARCH_MODE {\n    FRC_MV_SEARCH_NATIVE = 0,\n    FRC_MV_SEARCH_PERFORMANCE = 1,\n    TOTAL_FRC_MV_SEARCH_MODES\n};\n\n#define AMF_FRC_ENGINE_TYPE        L\"FRCEngineType\"           // AMF_MEMORY_TYPE (DX12, OPENCL, default : DX12)\" - determines how the object is initialized and what kernels to use\n#define AMF_FRC_OUTPUT_SIZE        L\"FRCSOutputSize\"          // AMFSize - output scaling width/hieight\n#define AMF_FRC_MODE               L\"FRCMode\"                 // FRC mode (0-off, 1-on (call at x2 source FPS), 2-only interpolated, 3-x2 Present)\n#define AMF_FRC_ENABLE_FALLBACK\t   L\"FRCEnableFallback\"\t\t  // FRC enable fallback mode\n#define AMF_FRC_INDICATOR          L\"FRCIndicator\"            // bool (default : false)\n#define AMF_FRC_PROFILE\t\t       L\"FRCProfile\"\t\t      // FRC profile\n#define AMF_FRC_MV_SEARCH_MODE     L\"FRCMVSEARCHMODE\"         // FRC MV search mode\n\n#endif //#ifndef AMFFRC_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/HQScaler.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMFHQScaler_h\n#define AMFHQScaler_h\n\n#pragma once\n\n#define AMFHQScaler L\"AMFHQScaler\"\n\n\n// various types of algorithms supported by the high-quality scaler\nenum  AMF_HQ_SCALER_ALGORITHM_ENUM\n{\n    AMF_HQ_SCALER_ALGORITHM_BILINEAR = 0,\n    AMF_HQ_SCALER_ALGORITHM_BICUBIC = 1,\n    AMF_HQ_SCALER_ALGORITHM_FSR = 2, // deprecated\n    AMF_HQ_SCALER_ALGORITHM_VIDEOSR1_0 = 2,\n    AMF_HQ_SCALER_ALGORITHM_POINT = 3,\n    AMF_HQ_SCALER_ALGORITHM_VIDEOSR1_1 = 4,\n\n};\n\n\n// PA object properties\n#define AMF_HQ_SCALER_ALGORITHM         L\"HQScalerAlgorithm\"        // amf_int64(AMF_HQ_SCALER_ALGORITHM_ENUM) (Bi-linear, Bi-cubic, RCAS, Auto)\"      - determines which scaling algorithm will be used\n                                                                    //                                                                                   auto will chose best option between algorithms available\n#define AMF_HQ_SCALER_ENGINE_TYPE       L\"HQScalerEngineType\"       // AMF_MEMORY_TYPE (DX11, DX12, OPENCL, VULKAN default : DX11)\"                    - determines how the object is initialized and what kernels to use\n\n#define AMF_HQ_SCALER_OUTPUT_SIZE       L\"HQSOutputSize\"            // AMFSize                                                                         - output scaling width/hieight\n\n#define AMF_HQ_SCALER_KEEP_ASPECT_RATIO  L\"KeepAspectRatio\"         // bool (default=false) Keep aspect ratio if scaling. \n#define AMF_HQ_SCALER_FILL               L\"Fill\"                    // bool (default=false) fill area out of ROI. \n#define AMF_HQ_SCALER_FILL_COLOR         L\"FillColor\"               // AMFColor \n#define AMF_HQ_SCALER_FROM_SRGB          L\"FromSRGB\"                   //  bool (default=true) Convert to SRGB. \n\n#define AMF_HQ_SCALER_SHARPNESS          L\"HQScalerSharpness\"   // Float in the range of [0.0, 2.0]\n#define AMF_HQ_SCALER_FRAME_RATE         L\"HQScalerFrameRate\"   // Frame rate (off, 15, 30, 60)\n\n#endif //#ifndef AMFHQScaler_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/MediaSource.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_MediaSource_h\n#define AMF_MediaSource_h\n\n#pragma once\n\n#include \"public/include/core/Interface.h\"\n\nnamespace amf\n{\n    enum AMF_SEEK_TYPE\n    {\n        AMF_SEEK_PREV = 0,           // nearest packet before pts\n        AMF_SEEK_NEXT = 1,           // nearest packet after pts\n        AMF_SEEK_PREV_KEYFRAME = 2,  // nearest keyframe packet before pts\n        AMF_SEEK_NEXT_KEYFRAME = 3,  // nearest keyframe packet after pts\n    };\n\n    //----------------------------------------------------------------------------------------------\n    // media source interface.  \n    //----------------------------------------------------------------------------------------------\n    class AMFMediaSource : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xb367695a, 0xdbd0, 0x4430, 0x95, 0x3b, 0xbc, 0x7d, 0xbd, 0x2a, 0xa7, 0x66)\n\n        // interface\n        virtual AMF_RESULT  AMF_STD_CALL Seek(amf_pts pos, AMF_SEEK_TYPE seekType, amf_int32 whichStream) = 0;\n        virtual amf_pts     AMF_STD_CALL GetPosition() = 0;\n        virtual amf_pts     AMF_STD_CALL GetDuration() = 0;\n\n        virtual void        AMF_STD_CALL SetMinPosition(amf_pts pts) = 0;\n        virtual amf_pts     AMF_STD_CALL GetMinPosition() = 0;\n        virtual void        AMF_STD_CALL SetMaxPosition(amf_pts pts) = 0;\n        virtual amf_pts     AMF_STD_CALL GetMaxPosition() = 0;\n\n        virtual amf_uint64  AMF_STD_CALL GetFrameFromPts(amf_pts pts) = 0;\n        virtual amf_pts     AMF_STD_CALL GetPtsFromFrame(amf_uint64 frame) = 0;\n\n        virtual bool        AMF_STD_CALL SupportFramesAccess() = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFMediaSource> AMFMediaSourcePtr;\n} //namespace amf\n\n#endif //#ifndef AMF_MediaSource_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/PreAnalysis.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMFPreAnalysis_h\n#define AMFPreAnalysis_h\n\n#pragma once\n\n#define AMFPreAnalysis L\"AMFPreAnalysis\"\n\n\n\nenum  AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_ENUM\n{\n    AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW    = 0,\n    AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM = 1,\n    AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH   = 2\n};\n\n\nenum  AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_ENUM\n{\n\tAMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW    = 0,\n\tAMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM = 1,\n\tAMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH   = 2\n};\n\n\nenum  AMF_PA_ACTIVITY_TYPE_ENUM\n{\n    AMF_PA_ACTIVITY_Y   = 0,\n    AMF_PA_ACTIVITY_YUV = 1\n};\n\n\nenum  AMF_PA_CAQ_STRENGTH_ENUM\n{\n    AMF_PA_CAQ_STRENGTH_LOW    = 0,\n    AMF_PA_CAQ_STRENGTH_MEDIUM = 1,\n    AMF_PA_CAQ_STRENGTH_HIGH   = 2\n};\n\n// Perceptual adaptive quantization mode\nenum AMF_PA_PAQ_MODE_ENUM\n{\n    AMF_PA_PAQ_MODE_NONE = 0,\n    AMF_PA_PAQ_MODE_CAQ = 1\n};\n\n// Temporal adaptive quantization mode\nenum  AMF_PA_TAQ_MODE_ENUM\n{\n    AMF_PA_TAQ_MODE_NONE = 0,\n    AMF_PA_TAQ_MODE_1 = 1,\n    AMF_PA_TAQ_MODE_2 = 2\n};\n\nenum AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_ENUM\n{\n    AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE = 0, //default\n    AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO = 1\n};\n\n\n// PA object properties\n#define AMF_PA_ENGINE_TYPE                          L\"PAEngineType\"                         // AMF_MEMORY_TYPE (Host, DX11, OpenCL, Vulkan, Auto default : UNKNOWN (Auto))\" - determines how the object is initialized and what kernels to use\n                                                                                            //                                                                        by default it is Auto (DX11, OpenCL and Vulkan are currently available)\n\n#define AMF_PA_SCENE_CHANGE_DETECTION_ENABLE        L\"PASceneChangeDetectionEnable\"         // bool       (default : True)                                          - Enable Scene Change Detection GPU algorithm\n#define AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY   L\"PASceneChangeDetectionSensitivity\"\t// AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_ENUM (default : Medium)    - Scene Change Detection Sensitivity\n#define AMF_PA_STATIC_SCENE_DETECTION_ENABLE        L\"PAStaticSceneDetectionEnable\"         // bool       (default : False)                                         - Enable Skip Detection GPU algorithm\n#define AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY   L\"PAStaticSceneDetectionSensitivity\"\t// AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_ENUM (default : High)      - Allowable absolute difference between pixels (sample counts)\n#define AMF_PA_FRAME_SAD_ENABLE                     L\"PAFrameSadEnable\"\t                    // bool       (default : True)                                          - Enable Frame SAD algorithm\n#define AMF_PA_ACTIVITY_TYPE                        L\"PAActivityType\"                       // AMF_PA_ACTIVITY_TYPE_ENUM (default : Calculate on Y)                 - Block activity calculation mode\n#define AMF_PA_LTR_ENABLE                           L\"PALongTermReferenceEnable\"            // bool       (default : False)                                         - Enable Automatic Long Term Reference frame management\n#define AMF_PA_LOOKAHEAD_BUFFER_DEPTH               L\"PALookAheadBufferDepth\"               // amf_uint64 (default : 0)           Values: [0, MAX_LOOKAHEAD_DEPTH]  - PA lookahead buffer size\n#define AMF_PA_PAQ_MODE                             L\"PAPerceptualAQMode\"                   // AMF_PA_PAQ_MODE_ENUM     (default : AMF_PA_PAQ_MODE_NONE)            - Perceptual AQ mode\n#define AMF_PA_TAQ_MODE                             L\"PATemporalAQMode\"                     // AMF_PA_TAQ_MODE_ENUM (default: AMF_PA_TAQ_MODE_NONE)                 - Temporal AQ mode\n#define AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE       L\"PAHighMotionQualityBoostMode\"         // AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_ENUM (default: None)           - High motion quality boost mode\n\n///////////////////////////////////////////\n// the following properties are available\n// only through the Encoder - trying to\n// access/set them when PA is standalone\n// will fail\n\n\n#define AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE        L\"PAInitialQPAfterSceneChange\"          // amf_uint64 (default : 0)           Values: [0, 51]                   - Base QP to be used immediately after scene change. If this value is not set, PA will choose a proper QP value\n#define AMF_PA_MAX_QP_BEFORE_FORCE_SKIP             L\"PAMaxQPBeforeForceSkip\"               // amf_uint64 (default : 35)          Values: [0, 51]                   - When a static scene is detected, a skip frame is inserted only if the previous encoded frame average QP <= this value\n\n\n#define AMF_PA_CAQ_STRENGTH                         L\"PACAQStrength\"                        // AMF_PA_CAQ_STRENGTH_ENUM (default : Medium)                          - Content Adaptive Quantization (CAQ) strength\n\n\n\n\n//////////////////////////////////////////////////\n// properties set by PA on output buffer interface in standalone mode\n#define AMF_PA_ACTIVITY_MAP                         L\"PAActivityMap\"                        // AMFInterface* -> AMFSurface*;       Values: int32                    - When PA is standalone, there will be a 2D Activity map generated for each frame\n#define AMF_PA_SCENE_CHANGE_DETECT                  L\"PASceneChangeDetect\"                  // bool                                                                 - True/False - available if AMF_PA_SCENE_CHANGE_DETECTION_ENABLE was set to True when PA is standalone\n#define AMF_PA_STATIC_SCENE_DETECT                  L\"PAStaticSceneDetect\"                  // bool                                                                 - True/False - available if AMF_PA_STATIC_SCENE_DETECTION_ENABLE was set to True when PA is standalone\n\n\n#endif //#ifndef AMFPreAnalysis_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/PreProcessing.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2020 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMFPreProcessing_h\n#define AMFPreProcessing_h\n\n#pragma once\n\n#define AMFPreProcessing L\"AMFPreProcessing\"\n\n\n// Pre-processing object properties\n#define AMF_PP_ENGINE_TYPE                   L\"PPEngineType\"                   // AMF_MEMORY_TYPE (Host, DX11, OPENCL, Auto default : OPENCL)   - determines how the object is initialized and what kernels to use\n                                                                               //                                                                 by default it is OpenCL (Host, DX11 and OpenCL are currently available)\n// add a property that will determine the output format\n// by default we output in the same format as input\n// but in some cases we might need to change the output\n// format to be different than input\n#define AMF_PP_OUTPUT_MEMORY_TYPE            L\"PPOutputFormat\"                 // AMF_MEMORY_TYPE (Host, DX11, OPENCL    default : Unknown)     - determines format of frame going out\n\n\n#define AMF_PP_ADAPTIVE_FILTER_STRENGTH      L\"PPAdaptiveFilterStrength\"       // int       (default : 4)                                       - strength:    0 - 10: the higher the value, the stronger the filtering\n#define AMF_PP_ADAPTIVE_FILTER_SENSITIVITY   L\"PPAdaptiveFilterSensitivity\"\t   // int       (default : 4)                                       - sensitivity: 0 - 10: the lower the value, the more sensitive to edge (preserve more details)\n\n// Encoder parameters used for adaptive filtering\n#define AMF_PP_TARGET_BITRATE                L\"PPTargetBitrate\"                // int64     (default: 2000000)                                  - target bit rate\n#define AMF_PP_FRAME_RATE                    L\"PPFrameRate\"                    // AMFRate   (default: 30, 1)                                    - frame rate\n#define AMF_PP_ADAPTIVE_FILTER_ENABLE        L\"PPAdaptiveFilterEnable\"         // bool      (default: false)                                    - turn on/off adaptive filtering\n\n#endif //#ifndef AMFPreProcessing_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/SupportedCodecs.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// An interface available on some components to provide information on supported input and output codecs \n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_SupportedCodecs_h\n#define AMF_SupportedCodecs_h\n\n#pragma once\n\n#include \"public/include/core/Interface.h\"\n\n//properties on the returned AMFPropertyStorage\n#define SUPPORTEDCODEC_ID L\"CodecId\" //amf_int64\n#define SUPPORTEDCODEC_SAMPLERATE L\"SampleRate\" //amf_int32\n\nnamespace amf\n{\n    class AMFSupportedCodecs : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xc1003a83, 0x7934, 0x408a, 0x95, 0x5b, 0xc4, 0xdd, 0x85, 0x9d, 0xf5, 0x61)\n\n        //call with increasing values until it returns AMF_OUT_OF_RANGE\n        virtual AMF_RESULT     AMF_STD_CALL GetInputCodecAt(amf_size index, AMFPropertyStorage** codec) const = 0;\n        virtual AMF_RESULT     AMF_STD_CALL GetOutputCodecAt(amf_size index, AMFPropertyStorage** codec) const = 0;\n    };\n    typedef AMFInterfacePtr_T<AMFSupportedCodecs> AMFSupportedCodecsPtr;\n}\n\n#endif"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VQEnhancer.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMFVQEnhancer_h\n#define AMFVQEnhancer_h\n\n#pragma once\n\n#define VE_FCR_DEFAULT_ATTENUATION 0.1\n\n#define AMFVQEnhancer L\"AMFVQEnhancer\"\n\n#define AMF_VIDEO_ENHANCER_ENGINE_TYPE       L\"AMF_VIDEI_ENHANCER_ENGINE_TYPE\"        // AMF_MEMORY_TYPE (DX11, DX12, OPENCL, VULKAN default : DX11)\"                    - determines how the object is initialized and what kernels to use\n#define AMF_VIDEO_ENHANCER_OUTPUT_SIZE       L\"AMF_VIDEO_ENHANCER_OUTPUT_SIZE\"        // AMFSize                       \n#define AMF_VE_FCR_ATTENUATION               L\"AMF_VE_FCR_ATTENUATION\"                // Float in the range of [0.02, 0.4], default : 0.1\n#define AMF_VE_FCR_RADIUS                    L\"AMF_VE_FCR_RADIUS\"                     // int  in the range of [1, 4]\n#define AMF_VE_FCR_SPLIT_VIEW                L\"AMF_VE_FCR_SPLIT_VIEW\"                 // FCR View split window\n\n#endif //#ifndef AMFVQEnhancer_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoCapture.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// ZCamLive  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_VideoCapture_h\n#define AMF_VideoCapture_h\n\n#pragma once\n\n#define VIDEOCAP_DEVICE_COUNT           L\"VideoCapDeviceCount\"    // amf_int64, (default=2), number of video capture devices\n#define VIDEOCAP_DEVICE_NAME            L\"VideoCapDeviceName\"     // WString, (default=\"\"), name of the video capture device\n#define VIDEOCAP_DEVICE_ACTIVE          L\"VideoCapDeviceActive\"   // WString, (default=\"\"), name of the selected video capture device\n\n#define VIDEOCAP_CODEC                   L\"CodecID\"               // WString (default = \"AMFVideoDecoderUVD_H264_AVC\"), UVD codec ID\n#define VIDEOCAP_FRAMESIZE               L\"FrameSize\"             // AMFSize, (default=AMFConstructSize(1920, 1080)), frame size in pixels\n\nextern \"C\"\n{\n    AMF_RESULT AMF_CDECL_CALL AMFCreateComponentVideoCapture(amf::AMFContext* pContext, amf::AMFComponentEx** ppComponent);\n}\n#endif // AMF_VideoCapture_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoConverter.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// AMFFVideoConverter interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_VideoConverter_h\n#define AMF_VideoConverter_h\n#pragma once\n\n#include \"Component.h\"\n#include \"ColorSpace.h\"\n\n#define AMFVideoConverter L\"AMFVideoConverter\"\n\nenum AMF_VIDEO_CONVERTER_SCALE_ENUM\n{\n    AMF_VIDEO_CONVERTER_SCALE_INVALID          = -1,\n    AMF_VIDEO_CONVERTER_SCALE_BILINEAR          = 0,\n    AMF_VIDEO_CONVERTER_SCALE_BICUBIC           = 1\n};\n\nenum AMF_VIDEO_CONVERTER_TONEMAPPING_ENUM\n{\n    AMF_VIDEO_CONVERTER_TONEMAPPING_COPY = 0,\n    AMF_VIDEO_CONVERTER_TONEMAPPING_AMD = 1,\n    AMF_VIDEO_CONVERTER_TONEMAPPING_LINEAR = 2,\n    AMF_VIDEO_CONVERTER_TONEMAPPING_GAMMA = 3,\n    AMF_VIDEO_CONVERTER_TONEMAPPING_REINHARD = 4,\n    AMF_VIDEO_CONVERTER_TONEMAPPING_2390 = 5,\n};\n\n\n\n#define AMF_VIDEO_CONVERTER_OUTPUT_FORMAT                   L\"OutputFormat\"             // Values : AMF_SURFACE_NV12 or AMF_SURFACE_BGRA or AMF_SURFACE_YUV420P\n#define AMF_VIDEO_CONVERTER_MEMORY_TYPE                     L\"MemoryType\"               // Values : AMF_MEMORY_DX11 or AMF_MEMORY_DX9 or AMF_MEMORY_UNKNOWN (get from input type)\n#define AMF_VIDEO_CONVERTER_COMPUTE_DEVICE                  L\"ComputeDevice\"            // Values : AMF_MEMORY_COMPUTE_FOR_DX9 enumeration\n\n#define AMF_VIDEO_CONVERTER_OUTPUT_SIZE                     L\"OutputSize\"               // AMFSize  (default=0,0) width in pixels. default means no scaling\n#define AMF_VIDEO_CONVERTER_OUTPUT_RECT                     L\"OutputRect\"               // AMFRect  (default=0, 0, 0, 0) rectangle in pixels. default means no rect\n#define AMF_VIDEO_CONVERTER_SCALE                           L\"ScaleType\"            // amf_int64(AMF_VIDEO_CONVERTER_SCALE_ENUM); default = AMF_VIDEO_CONVERTER_SCALE_BILINEAR\n#define AMF_VIDEO_CONVERTER_FORCE_OUTPUT_SURFACE_SIZE       L\"ForceOutputSurfaceSize\" // bool (default=false) Force output size from output surface \n\n#define AMF_VIDEO_CONVERTER_KEEP_ASPECT_RATIO               L\"KeepAspectRatio\"          // bool (default=false) Keep aspect ratio if scaling. \n#define AMF_VIDEO_CONVERTER_FILL                            L\"Fill\"                     // bool (default=false) fill area out of ROI. \n#define AMF_VIDEO_CONVERTER_FILL_COLOR                      L\"FillColor\"                // AMFColor \n\n//-------------------------------------------------------------------------------------------------\n// SDR color conversion\n//-------------------------------------------------------------------------------------------------\n#define AMF_VIDEO_CONVERTER_COLOR_PROFILE                   L\"ColorProfile\"         // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO\n#define AMF_VIDEO_CONVERTER_LINEAR_RGB                      L\"LinearRGB\"             // bool (default=false) Convert to/from linear RGB instead of sRGB using AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC or by default AMF_VIDEO_CONVERTER_TRANSFER_CHARACTERISTIC\n\n//-------------------------------------------------------------------------------------------------\n// HDR color conversion\n//-------------------------------------------------------------------------------------------------\n// AMF_VIDEO_CONVERTER_COLOR_PROFILE is used to define color space conversion\n\n// HDR data - can be set on converter or respectively on input and output surfaces (output surface via custom allocator) \n// if present, HDR_METADATA primary color overwrites COLOR_PRIMARIES\n\n// these properties can be set on converter component to configure input and output \n// these properties overwrite properties set on surface - see below\n#define AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC   L\"InputTransferChar\"    // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2 See ColorSpace.h for enum \n#define AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES           L\"InputColorPrimaries\"  // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013   7.1 See ColorSpace.h for enum \n#define AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE               L\"InputColorRange\"      // amf_int64(AMF_COLOR_RANGE_ENUM) default = AMF_COLOR_RANGE_UNDEFINED\n#define AMF_VIDEO_CONVERTER_INPUT_HDR_METADATA              L\"InputHdrMetadata\"     // AMFBuffer containing AMFHDRMetadata; default NULL\n#define AMF_VIDEO_CONVERTER_INPUT_TONEMAPPING               L\"InputTonemapping\"   // amf_int64(AMF_VIDEO_CONVERTER_TONEMAPPING_ENUM) default = AMF_VIDEO_CONVERTER_TONEMAPPING_LINEAR\n\n#define AMF_VIDEO_CONVERTER_OUTPUT_TRANSFER_CHARACTERISTIC  L\"OutputTransferChar\"   // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2 See ColorSpace.h for enum \n#define AMF_VIDEO_CONVERTER_OUTPUT_COLOR_PRIMARIES          L\"OutputColorPrimaries\" // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013   7.1 See ColorSpace.h for enum \n#define AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE              L\"OutputColorRange\"     // amf_int64(AMF_COLOR_RANGE_ENUM) default = AMF_COLOR_RANGE_UNDEFINED\n#define AMF_VIDEO_CONVERTER_OUTPUT_HDR_METADATA             L\"OutputHdrMetadata\"    // AMFBuffer containing AMFHDRMetadata; default NULL\n#define AMF_VIDEO_CONVERTER_OUTPUT_TONEMAPPING              L\"OutputTonemapping\"  // amf_int64(AMF_VIDEO_CONVERTER_TONEMAPPING_ENUM) default = AMF_VIDEO_CONVERTER_TONEMAPPING_AMD\n\n// these properties can be set on input or outout surface See ColorSpace.h\n// the same as decoder properties set on input surface - see below\n//#define AMF_VIDEO_COLOR_TRANSFER_CHARACTERISTIC         L\"ColorTransferChar\"      // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2 See ColorSpace.h for enum \n//#define AMF_VIDEO_COLOR_PRIMARIES                       L\"ColorPrimaries\"         // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013   7.1 See ColorSpace.h for enum \n//#define AMF_VIDEO_COLOR_RANGE                           L\"ColorRange\"           // amf_int64(AMF_COLOR_RANGE_ENUM) default = AMF_COLOR_RANGE_UNDEFINED\n//#define AMF_VIDEO_COLOR_HDR_METADATA                    L\"HdrMetadata\"            // AMFBuffer containing AMFHDRMetadata; default NULL\n\n// If decoder properties can be set on input see VideoDecoder.h\n// AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC\n// AMF_VIDEO_DECODER_COLOR_PRIMARIES\n// AMF_VIDEO_DECODER_COLOR_RANGE\n// AMF_VIDEO_DECODER_HDR_METADATA\n\n#define AMF_VIDEO_CONVERTER_USE_DECODER_HDR_METADATA    L\"UseDecoderHDRMetadata\" // bool (default=true) enables use of decoder / surface input color properties above\n\n\n#endif //#ifndef AMF_VideoConverter_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoDecoderUVD.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n//  VideoDecoderUVD interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_VideoDecoderUVD_h\n#define AMF_VideoDecoderUVD_h\n#pragma once\n\n#include \"Component.h\"\n#include \"ColorSpace.h\"\n\n#define AMFVideoDecoderUVD_MPEG2                     L\"AMFVideoDecoderUVD_MPEG2\"\n#define AMFVideoDecoderUVD_MPEG4                     L\"AMFVideoDecoderUVD_MPEG4\"\n#define AMFVideoDecoderUVD_WMV3                      L\"AMFVideoDecoderUVD_WMV3\"\n#define AMFVideoDecoderUVD_VC1                       L\"AMFVideoDecoderUVD_VC1\"\n#define AMFVideoDecoderUVD_H264_AVC                  L\"AMFVideoDecoderUVD_H264_AVC\"\n#define AMFVideoDecoderUVD_H264_MVC                  L\"AMFVideoDecoderUVD_H264_MVC\"\n#define AMFVideoDecoderUVD_H264_SVC                  L\"AMFVideoDecoderUVD_H264_SVC\"\n#define AMFVideoDecoderUVD_MJPEG                     L\"AMFVideoDecoderUVD_MJPEG\"\n#define AMFVideoDecoderHW_H265_HEVC                  L\"AMFVideoDecoderHW_H265_HEVC\"\n#define AMFVideoDecoderHW_H265_MAIN10                L\"AMFVideoDecoderHW_H265_MAIN10\"\n#define AMFVideoDecoderHW_VP9                        L\"AMFVideoDecoderHW_VP9\"\n#define AMFVideoDecoderHW_VP9_10BIT                  L\"AMFVideoDecoderHW_VP9_10BIT\"\n#define AMFVideoDecoderHW_AV1                        L\"AMFVideoDecoderHW_AV1\"\n#define AMFVideoDecoderHW_AV1_12BIT                  L\"AMFVideoDecoderHW_AV1_12BIT\"\n\nenum AMF_VIDEO_DECODER_MODE_ENUM\n{\n    AMF_VIDEO_DECODER_MODE_REGULAR = 0,     // DPB delay is based on number of reference frames + 1 (from SPS)\n    AMF_VIDEO_DECODER_MODE_COMPLIANT,       // DPB delay is based on profile - up to 16\n    AMF_VIDEO_DECODER_MODE_LOW_LATENCY,     // DPB delay is 0. Expect stream with no reordering in P-Frames or B-Frames. B-frames can be present as long as they do not introduce any frame re-ordering \n};\nenum AMF_TIMESTAMP_MODE_ENUM\n{\n    AMF_TS_PRESENTATION = 0, // default. decoder will preserve timestamps from input to output\n       AMF_TS_SORT,             // decoder will resort PTS list \n    AMF_TS_DECODE            // timestamps reflect decode order - decoder will reuse them\n};\n\n#define AMF_VIDEO_DECODER_SURFACE_COPY                 L\"SurfaceCopy\"           // amf_bool; default = false; return output surfaces as a copy\n#define AMF_VIDEO_DECODER_EXTRADATA                    L\"ExtraData\"             // AMFInterface* -> AMFBuffer* - AVCC - size length + SPS/PPS; or as Annex B. Optional if stream is Annex B\n#define AMF_VIDEO_DECODER_FRAME_RATE                   L\"FrameRate\"             // amf_double; default = 0.0, optional property to restore duration in the output if needed\n#define AMF_TIMESTAMP_MODE                             L\"TimestampMode\"         // amf_int64(AMF_TIMESTAMP_MODE_ENUM)  - default AMF_TS_PRESENTATION - how input timestamps are treated\n\n// dynamic/adaptive resolution change\n#define AMF_VIDEO_DECODER_ADAPTIVE_RESOLUTION_CHANGE   L\"AdaptiveResolutionChange\" // amf_bool; default = false; reuse allocated surfaces if new resolution is smaller\n#define AMF_VIDEO_DECODER_ALLOC_SIZE                   L\"AllocSize\"             // AMFSize; default (1920,1088); size of allocated surface if AdaptiveResolutionChange is true\n#define AMF_VIDEO_DECODER_CURRENT_SIZE                 L\"CurrentSize\"           // AMFSize; default = (0,0); current size of the video\n\n// reference frame management\n#define AMF_VIDEO_DECODER_REORDER_MODE                 L\"ReorderMode\"           // amf_int64(AMF_VIDEO_DECODER_MODE_ENUM); default = AMF_VIDEO_DECODER_MODE_REGULAR;  defines number of surfaces in DPB list.\n#define AMF_VIDEO_DECODER_SURFACE_POOL_SIZE            L\"SurfacePoolSize\"       // amf_int64; number of surfaces in the decode pool = DPB list size + number of surfaces for presentation\n#define AMF_VIDEO_DECODER_DPB_SIZE                     L\"DPBSize\"               // amf_int64; minimum number of surfaces for reordering\n\n#define AMF_VIDEO_DECODER_DEFAULT_SURFACES_FOR_TRANSIT  5                      // if AMF_VIDEO_DECODER_SURFACE_POOL_SIZE is 0 , AMF_VIDEO_DECODER_SURFACE_POOL_SIZE=AMF_VIDEO_DECODER_DEFAULT_SURFACES_FOR_TRANSIT+AMF_VIDEO_DECODER_DPB_SIZE\n\n// Decoder capabilities - exposed in AMFCaps interface\n#define AMF_VIDEO_DECODER_CAP_NUM_OF_STREAMS            L\"NumOfStreams\"               // amf_int64; maximum number of decode streams supported \n\n\n// metadata information: can be set on output surface\n\n// Properties could be set on surface based on HDR SEI or VUI header\n#define AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC L\"ColorTransferChar\"    // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2\n#define AMF_VIDEO_DECODER_COLOR_PRIMARIES               L\"ColorPrimaries\"       // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013   7.1\n#define AMF_VIDEO_DECODER_HDR_METADATA                  L\"HdrMetadata\"          // AMFBuffer containing AMFHDRMetadata; default NULL\n\n/////// AMF_VIDEO_DECODER_FULL_RANGE_COLOR deprecated, use AMF_VIDEO_DECODER_COLOR_RANGE\n#define AMF_VIDEO_DECODER_FULL_RANGE_COLOR              L\"FullRangeColor\"       // bool; default = false; false =  studio range, true = full range \n///////\n#define AMF_VIDEO_DECODER_COLOR_RANGE                   L\"ColorRange\"           // amf_int64(AMF_COLOR_RANGE_ENUM) default = AMF_COLOR_RANGE_UNDEFINED\n\n// can be set on output surface if YUV outout or on component to overwrite VUI\n#define AMF_VIDEO_DECODER_COLOR_PROFILE                 L\"ColorProfile\"         // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO\n\n// properties to be set on decoder if internal converter is used\n#define AMF_VIDEO_DECODER_OUTPUT_TRANSFER_CHARACTERISTIC        L\"OutColorTransferChar\"     // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013   7.2 See VideoDecoderUVD.h for enum \n#define AMF_VIDEO_DECODER_OUTPUT_COLOR_PRIMARIES                L\"OutputColorPrimaries\"     // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013   7.1 See ColorSpace.h for enum \n#define AMF_VIDEO_DECODER_OUTPUT_HDR_METADATA                   L\"OutHDRMetadata\"           // AMFBuffer containing AMFHDRMetadata; default NULL\n\n#define AMF_VIDEO_DECODER_LOW_LATENCY                           L\"LowLatencyDecode\"         // amf_bool; default = false; true = low latency decode, false = regular decode\n\n#if defined(__ANDROID__)\n#define AMF_VIDEO_DECODER_NATIVEWINDOW                  L\"AndroidNativeWindow\"  // amf_int64; default = 0; pointer to native window\n#endif //__ANDROID__\n\n\n#if defined(__APPLE__)\n#define AMF_VIDEO_DECODER_NATIVEWINDOW                  L\"AppleNativeWindow\"  // amf_int64; default = 0; pointer to native window\n#endif //__APPLE__\n\n#define AMF_VIDEO_DECODER_ENABLE_SMART_ACCESS_VIDEO             L\"EnableDecoderSmartAccessVideo\"     // amf_bool; default = false; true = enables smart access video feature\n#define AMF_VIDEO_DECODER_SKIP_TRANSFER_SMART_ACCESS_VIDEO      L\"SkipTransferSmartAccessVideo\"      // amf_bool; default = false; true = keeps output on GPU where it ran\n\n#define AMF_VIDEO_DECODER_CAP_SUPPORT_SMART_ACCESS_VIDEO        L\"SupportSmartAccessVideo\"           // amf_bool; returns true if system supports SmartAccess Video\n\n#define AMF_VIDEO_DECODER_SURFACE_CPU                           L\"SurfaceCpu\"                        // amf_bool. default = false, true = hint to decoder that output will be consumed on cpu\n\n#define AMF_VIDEO_DECODER_INSTANCE_INDEX                        L\"DecoderInstance\"                   // amf_int64; selected HW instance idx\n#define AMF_VIDEO_DECODER_CAP_NUM_OF_HW_INSTANCES               L\"NumOfHwDecoderInstances\"           // amf_int64 number of HW decoder instances\n\n\n#endif //#ifndef AMF_VideoDecoderUVD_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoEncoderAV1.h",
    "content": "//\n// Copyright (c) 2021-2022 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n//  VideoEncoderHW_AV1 interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_VideoEncoderAV1_h\n#define AMF_VideoEncoderAV1_h\n#pragma once\n\n#include \"Component.h\"\n#include \"ColorSpace.h\"\n#include \"PreAnalysis.h\"\n\n#define AMFVideoEncoder_AV1 L\"AMFVideoEncoderHW_AV1\"\n\nenum AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_NONE                    = 0,    // No encoding latency requirement. Encoder will balance encoding time and power consumption.\n    AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_POWER_SAVING_REAL_TIME  = 1,    // Try the best to finish encoding a frame within 1/framerate sec. This mode may cause more power consumption\n    AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_REAL_TIME               = 2,    // Try the best to finish encoding a frame within 1/(2 x framerate) sec. This mode will cause more power consumption than POWER_SAVING_REAL_TIME\n    AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_LOWEST_LATENCY          = 3     // Encoding as fast as possible. This mode causes highest power consumption.\n};\n\nenum AMF_VIDEO_ENCODER_AV1_USAGE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING                 = 0,\n    AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY           = 2,\n    AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY                 = 1,\n    AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM                      = 3,\n    AMF_VIDEO_ENCODER_AV1_USAGE_HIGH_QUALITY                = 4,\n    AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY_HIGH_QUALITY    = 5\n};\n\nenum AMF_VIDEO_ENCODER_AV1_PROFILE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_LEVEL_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_LEVEL_2_0 = 0,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_2_1 = 1,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_2_2 = 2,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_2_3 = 3,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_3_0 = 4,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_3_1 = 5,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_3_2 = 6,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_3_3 = 7,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_4_0 = 8,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_4_1 = 9,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_4_2 = 10,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_4_3 = 11,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_5_0 = 12,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_5_1 = 13,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_5_2 = 14,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_5_3 = 15,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_6_0 = 16,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_6_1 = 17,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_6_2 = 18,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_6_3 = 19,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_7_0 = 20,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_7_1 = 21,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_7_2 = 22,\n    AMF_VIDEO_ENCODER_AV1_LEVEL_7_3 = 23\n};\n\nenum AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN                   = -1,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP               = 0,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR   = 1,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR      = 2,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR                       = 3,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_QUALITY_VBR               = 4,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR          = 5,\n    AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR          = 6\n};\n\nenum AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY               = 1,\n    AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_1080P_CODED_1082   = 2,\n    AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS          = 3\n};\n\nenum AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_NONE             = 0,\n    AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_KEY              = 1,\n    AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_INTRA_ONLY       = 2,\n    AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_SWITCH           = 3,\n    AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_SHOW_EXISTING    = 4\n};\n\nenum AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_KEY             = 0,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_INTRA_ONLY      = 1,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_INTER           = 2,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_SWITCH          = 3,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_SHOW_EXISTING   = 4\n};\n\nenum AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY   = 0,    \n    AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY        = 30,    \n    AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED       = 70,\n    AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED          = 100\n};\n\nenum AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE                = 0,\n    AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_GOP_ALIGNED         = 1,\n    AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED   = 2\n};\n\nenum AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE_NONE              = 0,\n    AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE_FIXED_INTERVAL    = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_CDEF_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_CDEF_DISABLE          = 0,\n    AMF_VIDEO_ENCODER_AV1_CDEF_ENABLE_DEFAULT   = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_CDF_FRAME_END_UPDATE_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_CDF_FRAME_END_UPDATE_MODE_DISABLE         = 0,\n    AMF_VIDEO_ENCODER_AV1_CDF_FRAME_END_UPDATE_MODE_ENABLE_DEFAULT  = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_AQ_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_AQ_MODE_NONE = 0,\n    AMF_VIDEO_ENCODER_AV1_AQ_MODE_CAQ  = 1              // Content adaptive quantization mode\n};\n\nenum AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE__DISABLED = 0,\n    AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE__GOP_ALIGNED = 1,\n    AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE__CONTINUOUS = 2\n};\nenum AMF_VIDEO_ENCODER_AV1_LTR_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_LTR_MODE_RESET_UNUSED     = 0,\n    AMF_VIDEO_ENCODER_AV1_LTR_MODE_KEEP_UNUSED      = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE_FRAME = 0,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE_TILE  = 1\n};\n\nenum AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE_FRAME = 0,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE_TILE = 1,\n    AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE_TILE_LAST = 2\n};\n// *** Static properties - can be set only before Init() *** \n\n// Encoder Engine Settings\n#define AMF_VIDEO_ENCODER_AV1_ENCODER_INSTANCE_INDEX                L\"Av1EncoderInstanceIndex\"          // amf_int64; default = 0; selected HW instance idx. The number of instances is queried by using AMF_VIDEO_ENCODER_AV1_CAP_NUM_OF_HW_INSTANCES\n#define AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE                 L\"Av1EncodingLatencyMode\"           // amf_int64(AMF_VIDEO_ENCODER_AV1_ENCODING_LATENCY_MODE_ENUM); default = depends on USAGE; The encoding latency mode. \n#define AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT                         L\"Av1QueryTimeout\"                  // amf_int64; default = 0 (no wait); timeout for QueryOutput call in ms.\n\n// Usage Settings\n#define AMF_VIDEO_ENCODER_AV1_USAGE                                 L\"Av1Usage\"                         // amf_int64(AMF_VIDEO_ENCODER_AV1_USAGE_ENUM); default = N/A; Encoder usage. fully configures parameter set.\n\n// Session Configuration\n#define AMF_VIDEO_ENCODER_AV1_FRAMESIZE                             L\"Av1FrameSize\"                     // AMFSize; default = 0,0; Frame size\n#define AMF_VIDEO_ENCODER_AV1_COLOR_BIT_DEPTH                       L\"Av1ColorBitDepth\"                 // amf_int64(AMF_COLOR_BIT_DEPTH_ENUM); default = AMF_COLOR_BIT_DEPTH_8\n#define AMF_VIDEO_ENCODER_AV1_PROFILE                               L\"Av1Profile\"                       // amf_int64(AMF_VIDEO_ENCODER_AV1_PROFILE_ENUM) ; default = depends on USAGE; the codec profile of the coded bitstream\n#define AMF_VIDEO_ENCODER_AV1_LEVEL                                 L\"Av1Level\"                         // amf_int64 (AMF_VIDEO_ENCODER_AV1_LEVEL_ENUM); default = depends on USAGE; the codec level of the coded bitstream\n#define AMF_VIDEO_ENCODER_AV1_TILES_PER_FRAME                       L\"Av1NumTilesPerFrame\"              // amf_int64; default = 1; Number of tiles Per Frame. This is treated as suggestion. The actual number of tiles might be different due to compliance or encoder limitation.\n#define AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET                        L\"Av1QualityPreset\"                 // amf_int64(AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_ENUM); default = depends on USAGE; Quality Preset\n\n// Codec Configuration\n#define AMF_VIDEO_ENCODER_AV1_SCREEN_CONTENT_TOOLS                  L\"Av1ScreenContentTools\"            // bool; default = depends on USAGE; If true, allow enabling screen content tools by AMF_VIDEO_ENCODER_AV1_PALETTE_MODE and AMF_VIDEO_ENCODER_AV1_FORCE_INTEGER_MV; if false, all screen content tools are disabled.\n#define AMF_VIDEO_ENCODER_AV1_ORDER_HINT                            L\"Av1OrderHint\"                     // bool; default = depends on USAGE; If true, code order hint; if false, don't code order hint\n#define AMF_VIDEO_ENCODER_AV1_FRAME_ID                              L\"Av1FrameId\"                       // bool; default = depends on USAGE; If true, code frame id; if false, don't code frame id\n#define AMF_VIDEO_ENCODER_AV1_TILE_GROUP_OBU                        L\"Av1TileGroupObu\"                  // bool; default = depends on USAGE; If true, code FrameHeaderObu + TileGroupObu and each TileGroupObu contains one tile; if false, code FrameObu.\n#define AMF_VIDEO_ENCODER_AV1_CDEF_MODE                             L\"Av1CdefMode\"                      // amd_int64(AMF_VIDEO_ENCODER_AV1_CDEF_MODE_ENUM); default = depends on USAGE; Cdef mode\n#define AMF_VIDEO_ENCODER_AV1_ERROR_RESILIENT_MODE                  L\"Av1ErrorResilientMode\"            // bool; default = depends on USAGE; If true, enable error resilient mode; if false, disable error resilient mode\n\n// Rate Control and Quality Enhancement\n#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD                   L\"Av1RateControlMethod\"             // amf_int64(AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_ENUM); default = depends on USAGE; Rate Control Method\n#define AMF_VIDEO_ENCODER_AV1_QVBR_QUALITY_LEVEL                    L\"Av1QvbrQualityLevel\"              // amf_int64; default = 23; QVBR quality level; range = 1-51\n#define AMF_VIDEO_ENCODER_AV1_INITIAL_VBV_BUFFER_FULLNESS           L\"Av1InitialVBVBufferFullness\"      // amf_int64; default = depends on USAGE; Initial VBV Buffer Fullness 0=0% 64=100%\n\n// Alignment Mode Configuration\n#define AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE                        L\"Av1AlignmentMode\"                 // amf_int64(AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_ENUM); default = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY; Alignment Mode.\n\n#define AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE                   L\"Av1EnablePreAnalysis\"             // bool; default = depends on USAGE; If true, enables the pre-analysis module. Refer to AMF Video PreAnalysis API reference for more details. If false, disable the pre-analysis module.\n#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_PREENCODE                L\"Av1RateControlPreEncode\"          // bool; default = depends on USAGE; If true, enables pre-encode assist in rate control; if false, disables pre-encode assist in rate control.\n#define AMF_VIDEO_ENCODER_AV1_HIGH_MOTION_QUALITY_BOOST             L\"Av1HighMotionQualityBoost\"        // bool; default = depends on USAGE; If true, enable high motion quality boost mode; if false, disable high motion quality boost mode.\n#define AMF_VIDEO_ENCODER_AV1_AQ_MODE                               L\"Av1AQMode\"                        // amd_int64(AMF_VIDEO_ENCODER_AV1_AQ_MODE_ENUM); default = depends on USAGE; AQ mode\n\n// Picture Management Configuration\n#define AMF_VIDEO_ENCODER_AV1_MAX_NUM_TEMPORAL_LAYERS               L\"Av1MaxNumOfTemporalLayers\"        // amf_int64; default = depends on USAGE; Max number of temporal layers might be enabled. The maximum value can be queried from AMF_VIDEO_ENCODER_AV1_CAP_MAX_NUM_TEMPORAL_LAYERS\n#define AMF_VIDEO_ENCODER_AV1_MAX_LTR_FRAMES                        L\"Av1MaxNumLTRFrames\"               // amf_int64; default = depends on USAGE; Max number of LTR frames. The maximum value can be queried from AMF_VIDEO_ENCODER_AV1_CAP_MAX_NUM_LTR_FRAMES\n#define AMF_VIDEO_ENCODER_AV1_LTR_MODE                              L\"Av1LTRMode\"                       // amf_int64(AMF_VIDEO_ENCODER_AV1_LTR_MODE_ENUM); default = AMF_VIDEO_ENCODER_AV1_LTR_MODE_RESET_UNUSED; remove/keep unused LTRs (not specified in property AMF_VIDEO_ENCODER_AV1_FORCE_LTR_REFERENCE_BITFIELD)\n#define AMF_VIDEO_ENCODER_AV1_MAX_NUM_REFRAMES                      L\"Av1MaxNumRefFrames\"               // amf_int64; default = 1; Maximum number of reference frames\n\n// color conversion\n#define AMF_VIDEO_ENCODER_AV1_INPUT_HDR_METADATA                    L\"Av1InHDRMetadata\"                 // AMFBuffer containing AMFHDRMetadata; default NULL\n\n// Miscellaneous\n#define AMF_VIDEO_ENCODER_AV1_EXTRA_DATA                            L\"Av1ExtraData\"                     // AMFInterface* - > AMFBuffer*; buffer to retrieve coded sequence header\n#define AMF_VIDEO_ENCODER_AV1_ENABLE_SMART_ACCESS_VIDEO             L\"Av1EnableEncoderSmartAccessVideo\" // amf_bool; default = false; true = enables smart access video feature\n#define AMF_VIDEO_ENCODER_AV1_INPUT_QUEUE_SIZE                      L\"Av1InputQueueSize\"                // amf_int64; default 16; Set amf input queue size\n\n// Tile Output\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE                           L\"AV1OutputMode\"                    // amf_int64(AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE_ENUM); default = AMF_VIDEO_ENCODER_AV1_OUTPUT_MODE_FRAME - defines encoder output mode\n\n// *** Dynamic properties - can be set anytime *** \n\n// Codec Configuration\n#define AMF_VIDEO_ENCODER_AV1_PALETTE_MODE                          L\"Av1PaletteMode\"                   // bool; default = depends on USAGE; If true, enable palette mode; if false, disable palette mode. Valid only when AMF_VIDEO_ENCODER_AV1_SCREEN_CONTENT_TOOLS is true.\n#define AMF_VIDEO_ENCODER_AV1_FORCE_INTEGER_MV                      L\"Av1ForceIntegerMv\"                // bool; default = depends on USAGE; If true, enable force integer MV; if false, disable force integer MV. Valid only when AMF_VIDEO_ENCODER_AV1_SCREEN_CONTENT_TOOLS is true.\n#define AMF_VIDEO_ENCODER_AV1_CDF_UPDATE                            L\"Av1CdfUpdate\"                     // bool; default = depends on USAGE; If true, enable CDF update; if false, disable CDF update.\n#define AMF_VIDEO_ENCODER_AV1_CDF_FRAME_END_UPDATE_MODE             L\"Av1CdfFrameEndUpdateMode\"         // amd_int64(AMF_VIDEO_ENCODER_AV1_CDF_FRAME_END_UPDATE_MODE_ENUM); default = depends on USAGE; CDF frame end update mode\n\n\n// Rate Control and Quality Enhancement\n#define AMF_VIDEO_ENCODER_AV1_VBV_BUFFER_SIZE                       L\"Av1VBVBufferSize\"                 // amf_int64; default = depends on USAGE; VBV Buffer Size in bits\n#define AMF_VIDEO_ENCODER_AV1_FRAMERATE                             L\"Av1FrameRate\"                     // AMFRate; default = depends on usage; Frame Rate\n#define AMF_VIDEO_ENCODER_AV1_ENFORCE_HRD                           L\"Av1EnforceHRD\"                    // bool; default = depends on USAGE; If true, enforce HRD; if false, HRD is not enforced.\n#define AMF_VIDEO_ENCODER_AV1_FILLER_DATA                           L\"Av1FillerData\"                    // bool; default = depends on USAGE; If true, code filler data when needed; if false, don't code filler data.\n#define AMF_VIDEO_ENCODER_AV1_TARGET_BITRATE                        L\"Av1TargetBitrate\"                 // amf_int64; default = depends on USAGE; Target bit rate in bits\n#define AMF_VIDEO_ENCODER_AV1_PEAK_BITRATE                          L\"Av1PeakBitrate\"                   // amf_int64; default = depends on USAGE; Peak bit rate in bits\n\n#define AMF_VIDEO_ENCODER_AV1_MAX_COMPRESSED_FRAME_SIZE             L\"Av1MaxCompressedFrameSize\"        // amf_int64; default = 0; Max compressed frame Size in bits. 0 - no limit\n#define AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTRA                     L\"Av1MinQIndex_Intra\"               // amf_int64; default = depends on USAGE; Min QIndex for intra frames; range = 1-255\n#define AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTRA                     L\"Av1MaxQIndex_Intra\"               // amf_int64; default = depends on USAGE; Max QIndex for intra frames; range = 1-255\n#define AMF_VIDEO_ENCODER_AV1_MIN_Q_INDEX_INTER                     L\"Av1MinQIndex_Inter\"               // amf_int64; default = depends on USAGE; Min QIndex for inter frames; range = 1-255\n#define AMF_VIDEO_ENCODER_AV1_MAX_Q_INDEX_INTER                     L\"Av1MaxQIndex_Inter\"               // amf_int64; default = depends on USAGE; Max QIndex for inter frames; range = 1-255\n\n#define AMF_VIDEO_ENCODER_AV1_Q_INDEX_INTRA                         L\"Av1QIndex_Intra\"                  // amf_int64; default = depends on USAGE; intra-frame QIndex; range = 1-255\n#define AMF_VIDEO_ENCODER_AV1_Q_INDEX_INTER                         L\"Av1QIndex_Inter\"                  // amf_int64; default = depends on USAGE; inter-frame QIndex; range = 1-255\n\n#define AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_SKIP_FRAME               L\"Av1RateControlSkipFrameEnable\"    // bool; default = depends on USAGE; If true, rate control may code skip frame when needed; if false, rate control will not code skip frame.\n\n\n// Picture Management Configuration\n#define AMF_VIDEO_ENCODER_AV1_GOP_SIZE                              L\"Av1GOPSize\"                       // amf_int64; default = depends on USAGE; GOP Size (distance between automatically inserted key frames). If 0, key frame will be inserted at first frame only. Note that GOP may be interrupted by AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE.\n#define AMF_VIDEO_ENCODER_AV1_INTRA_PERIOD                          L\"Av1IntraPeriod\"                   // amf_int64; default = 0; Intra period in frames.\n#define AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE                 L\"Av1HeaderInsertionMode\"           // amf_int64(AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_ENUM); default = depends on USAGE; sequence header insertion mode\n#define AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE           L\"Av1SwitchFrameInsertionMode\"      // amf_int64(AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE_ENUM); default = depends on USAGE; switch frame insertin mode\n#define AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INTERVAL                 L\"Av1SwitchFrameInterval\"           // amf_int64; default = depends on USAGE; the interval between two inserted switch frames. Valid only when AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE is AMF_VIDEO_ENCODER_AV1_SWITCH_FRAME_INSERTION_MODE_FIXED_INTERVAL.\n#define AMF_VIDEO_ENCODER_AV1_NUM_TEMPORAL_LAYERS                   L\"Av1NumTemporalLayers\"             // amf_int64; default = depends on USAGE; Number of temporal layers. Can be changed at any time but the change is only applied when encoding next base layer frame.\n\n#define AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE\t\t\t\t\tL\"Av1IntraRefreshMode\"              // amf_int64(AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE_ENUM); default AMF_VIDEO_ENCODER_AV1_INTRA_REFRESH_MODE__DISABLED\n#define AMF_VIDEO_ENCODER_AV1_INTRAREFRESH_STRIPES\t\t\t\t\tL\"Av1IntraRefreshNumOfStripes\"      // amf_int64; default = N/A; Valid only when intra refresh is enabled.\n\n// color conversion\n#define AMF_VIDEO_ENCODER_AV1_INPUT_COLOR_PROFILE                   L\"Av1InputColorProfile\"             // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_AV1_INPUT_TRANSFER_CHARACTERISTIC         L\"Av1InputColorTransferChar\"        // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 section 7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_AV1_INPUT_COLOR_PRIMARIES                 L\"Av1InputColorPrimaries\"           // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 section 7.1 See ColorSpace.h for enum\n\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PROFILE                  L\"Av1OutputColorProfile\"            // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_TRANSFER_CHARACTERISTIC        L\"Av1OutputColorTransferChar\"       // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 ?7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PRIMARIES                L\"Av1OutputColorPrimaries\"          // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 section 7.1 See ColorSpace.h for enum\n\n\n// Frame encode parameters\n#define AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE                      L\"Av1ForceFrameType\"                // amf_int64(AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_ENUM); default = AMF_VIDEO_ENCODER_AV1_FORCE_FRAME_TYPE_NONE; generate particular frame type\n#define AMF_VIDEO_ENCODER_AV1_FORCE_INSERT_SEQUENCE_HEADER          L\"Av1ForceInsertSequenceHeader\"     // bool; default = false; If true, force insert sequence header with current frame;\n#define AMF_VIDEO_ENCODER_AV1_MARK_CURRENT_WITH_LTR_INDEX           L\"Av1MarkCurrentWithLTRIndex\"       // amf_int64; default = N/A; Mark current frame with LTR index\n#define AMF_VIDEO_ENCODER_AV1_FORCE_LTR_REFERENCE_BITFIELD          L\"Av1ForceLTRReferenceBitfield\"     // amf_int64; default = 0; force LTR bit-field\n#define AMF_VIDEO_ENCODER_AV1_ROI_DATA                              L\"Av1ROIData\"\t\t\t\t\t    // 2D AMFSurface, surface format: AMF_SURFACE_GRAY32\n#define AMF_VIDEO_ENCODER_AV1_PSNR_FEEDBACK                         L\"Av1PSNRFeedback\"             // amf_bool; default = false; Signal encoder to calculate PSNR score\n#define AMF_VIDEO_ENCODER_AV1_SSIM_FEEDBACK                         L\"Av1SSIMFeedback\"             // amf_bool; default = false; Signal encoder to calculate SSIM score\n#define AMF_VIDEO_ENCODER_AV1_STATISTICS_FEEDBACK                   L\"Av1StatisticsFeedback\"       // amf_bool; default = false; Signal encoder to collect and feedback encoder statistics\n#define AMF_VIDEO_ENCODER_AV1_BLOCK_Q_INDEX_FEEDBACK                L\"Av1BlockQIndexFeedback\"      // amf_bool; default = false; Signal encoder to collect and feedback block level QIndex values\n\n// Encode output parameters\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE                     L\"Av1OutputFrameType\"               // amf_int64(AMF_VIDEO_ENCODER_AV1_OUTPUT_FRAME_TYPE_ENUM); default = N/A\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_MARKED_LTR_INDEX               L\"Av1MarkedLTRIndex\"                // amf_int64; default = N/A; Marked LTR index\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_REFERENCED_LTR_INDEX_BITFIELD  L\"Av1ReferencedLTRIndexBitfield\"    // amf_int64; default = N/A; referenced LTR bit-field\n#define AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE                    L\"AV1OutputBufferType\"              // amf_int64(AMF_VIDEO_ENCODER_AV1_OUTPUT_BUFFER_TYPE_ENUM); encoder output buffer type\n#define AMF_VIDEO_ENCODER_AV1_RECONSTRUCTED_PICTURE                 L\"Av1ReconstructedPicture\"     // AMFInterface(AMFSurface); returns reconstructed picture as an AMFSurface attached to the output buffer as property AMF_VIDEO_ENCODER_RECONSTRUCTED_PICTURE of AMFInterface type\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PSNR_Y                      L\"Av1PSNRY\"                        // double; PSNR Y\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PSNR_U                      L\"Av1PSNRU\"                        // double; PSNR U\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PSNR_V                      L\"Av1PSNRV\"                        // double; PSNR V\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PSNR_ALL                    L\"Av1PSNRALL\"                      // double; PSNR All\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SSIM_Y                      L\"Av1SSIMY\"                        // double; SSIM Y\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SSIM_U                      L\"Av1SSIMU\"                        // double; SSIM U\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SSIM_V                      L\"Av1SSIMV\"                        // double; SSIM V\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SSIM_ALL                    L\"Av1SSIMALL\"                      // double; SSIM ALL\n// Encoder statistics feedback\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_FRAME_Q_INDEX               L\"Av1StatisticsFeedbackFrameQIndex\"            // amf_int64; Rate control base frame/initial QIndex\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_AVERAGE_Q_INDEX             L\"Av1StatisticsFeedbackAvgQIndex\"              // amf_int64; Average QIndex of all encoded SBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped SBs.\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_MAX_Q_INDEX                 L\"Av1StatisticsFeedbackMaxQIndex\"              // amf_int64; Max QIndex among all encoded SBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped SBs.\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_MIN_Q_INDEX                 L\"Av1StatisticsFeedbackMinQIndex\"              // amf_int64; Min QIndex among all encoded SBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped SBs.\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PIX_NUM_INTRA               L\"Av1StatisticsFeedbackPixNumIntra\"            // amf_int64; Number of the intra encoded pixels\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PIX_NUM_INTER               L\"Av1StatisticsFeedbackPixNumInter\"            // amf_int64; Number of the inter encoded pixels\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_PIX_NUM_SKIP                L\"Av1StatisticsFeedbackPixNumSkip\"             // amf_int64; Number of the skip mode pixels\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_BITCOUNT_RESIDUAL           L\"Av1StatisticsFeedbackBitcountResidual\"       // amf_int64; The bit count that corresponds to residual data\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_BITCOUNT_MOTION             L\"Av1StatisticsFeedbackBitcountMotion\"         // amf_int64; The bit count that corresponds to motion vectors\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_BITCOUNT_INTER              L\"Av1StatisticsFeedbackBitcountInter\"          // amf_int64; The bit count that are assigned to inter SBs\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_BITCOUNT_INTRA              L\"Av1StatisticsFeedbackBitcountIntra\"          // amf_int64; The bit count that are assigned to intra SBs\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_BITCOUNT_ALL_MINUS_HEADER   L\"Av1StatisticsFeedbackBitcountAllMinusHeader\" // amf_int64; The bit count of the bitstream excluding header\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_MV_X                        L\"Av1StatisticsFeedbackMvX\"                    // amf_int64; Accumulated absolute values of horizontal MV's\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_MV_Y                        L\"Av1StatisticsFeedbackMvY\"                    // amf_int64; Accumulated absolute values of vertical MV's\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_RD_COST_FINAL               L\"Av1StatisticsFeedbackRdCostFinal\"            // amf_int64; Frame level final RD cost for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_RD_COST_INTRA               L\"Av1StatisticsFeedbackRdCostIntra\"            // amf_int64; Frame level intra RD cost for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_RD_COST_INTER               L\"Av1StatisticsFeedbackRdCostInter\"            // amf_int64; Frame level inter RD cost for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SAD_FINAL                   L\"Av1StatisticsFeedbackSadFinal\"               // amf_int64; Frame level final SAD for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SAD_INTRA                   L\"Av1StatisticsFeedbackSadIntra\"               // amf_int64; Frame level intra SAD for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SAD_INTER                   L\"Av1StatisticsFeedbackSadInter\"               // amf_int64; Frame level inter SAD for full encoding\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_SSE                         L\"Av1StatisticsFeedbackSSE\"                    // amf_int64; Frame level SSE (only calculated for AV1)\n#define AMF_VIDEO_ENCODER_AV1_STATISTIC_VARIANCE                    L\"Av1StatisticsFeedbackVariance\"               // amf_int64; Frame level variance for full encoding\n\n    // Encoder block level feedback\n#define AMF_VIDEO_ENCODER_AV1_BLOCK_Q_INDEX_MAP                     L\"Av1BlockQIndexMap\"                               // AMFInterface(AMFSurface); AMFSurface of format AMF_SURFACE_GRAY32 containing block level QIndex values\n\n// AV1 Encoder capabilities - exposed in AMFCaps interface\n#define AMF_VIDEO_ENCODER_AV1_CAP_NUM_OF_HW_INSTANCES               L\"Av1CapNumOfHwInstances\"           // amf_int64; default = N/A; number of HW encoder instances\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_THROUGHPUT                    L\"Av1CapMaxThroughput\"              // amf_int64; default = N/A; MAX throughput for AV1 encoder in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_AV1_CAP_REQUESTED_THROUGHPUT              L\"Av1CapRequestedThroughput\"        // amf_int64; default = N/A; Currently total requested throughput for AV1 encode in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_AV1_CAP_COLOR_CONVERSION                  L\"Av1CapColorConversion\"            // amf_int64(AMF_ACCELERATION_TYPE); default = N/A; type of supported color conversion.\n#define AMF_VIDEO_ENCODER_AV1_CAP_PRE_ANALYSIS                      L\"Av1PreAnalysis\"                   // amf_bool - pre analysis module is available for AV1 UVE encoder, n/a for the other encoders\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_BITRATE                       L\"Av1MaxBitrate\"                    // amf_int64; default = N/A; Maximum bit rate in bits\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_PROFILE                       L\"Av1MaxProfile\"                    // amf_int64(AMF_VIDEO_ENCODER_AV1_PROFILE_ENUM); default = N/A; max value of code profile\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_LEVEL                         L\"Av1MaxLevel\"                      // amf_int64(AMF_VIDEO_ENCODER_AV1_LEVEL_ENUM); default = N/A; max value of codec level\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_NUM_TEMPORAL_LAYERS           L\"Av1CapMaxNumTemporalLayers\"       // amf_int64; default = N/A; The cap of maximum number of temporal layers\n#define AMF_VIDEO_ENCODER_AV1_CAP_MAX_NUM_LTR_FRAMES                L\"Av1CapMaxNumLTRFrames\"            // amf_int64; default = N/A; The cap of maximum number of LTR frames. This value is calculated based on current value of AMF_VIDEO_ENCODER_AV1_MAX_NUM_TEMPORAL_LAYERS.\n#define AMF_VIDEO_ENCODER_AV1_CAP_SUPPORT_TILE_OUTPUT               L\"AV1SupportTileOutput\"             // amf_bool; if tile output is supported\n\n#define AMF_VIDEO_ENCODER_AV1_CAP_SUPPORT_SMART_ACCESS_VIDEO        L\"Av1EncoderSupportSmartAccessVideo\"    // amf_bool; returns true if system supports SmartAccess Video\n\n#endif //#ifndef AMF_VideoEncoderAV1_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoEncoderHEVC.h",
    "content": "//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n//  VideoEncoderHW_HEVC interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_VideoEncoderHEVC_h\n#define AMF_VideoEncoderHEVC_h\n#pragma once\n\n#include \"Component.h\"\n#include \"ColorSpace.h\"\n#include \"PreAnalysis.h\"\n\n#define AMFVideoEncoder_HEVC L\"AMFVideoEncoderHW_HEVC\"\n\nenum AMF_VIDEO_ENCODER_HEVC_USAGE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING = 0, // kept for backwards compatability\n    AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING = 0, // fixed typo\n    AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY,\n    AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY,\n    AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM,\n    AMF_VIDEO_ENCODER_HEVC_USAGE_HIGH_QUALITY,\n    AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_PROFILE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN = 1,\n    AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10 = 2\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_TIER_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_TIER_MAIN    = 0,\n    AMF_VIDEO_ENCODER_HEVC_TIER_HIGH    = 1\n};\n\nenum AMF_VIDEO_ENCODER_LEVEL_ENUM\n{\n    AMF_LEVEL_1     = 30,\n    AMF_LEVEL_2     = 60,\n    AMF_LEVEL_2_1   = 63,\n    AMF_LEVEL_3     = 90,\n    AMF_LEVEL_3_1   = 93,\n    AMF_LEVEL_4     = 120,\n    AMF_LEVEL_4_1   = 123,\n    AMF_LEVEL_5     = 150,\n    AMF_LEVEL_5_1   = 153,\n    AMF_LEVEL_5_2   = 156,\n    AMF_LEVEL_6     = 180,\n    AMF_LEVEL_6_1   = 183,\n    AMF_LEVEL_6_2   = 186\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN = -1,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP = 0,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_QUALITY_VBR,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR,\n    AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_NONE = 0,\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_SKIP,\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_IDR,\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_I,\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_P\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR,\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_I,\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_P\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY    = 0,\n    AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED   = 5,\n    AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED      = 10\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE = 0,\n    AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED,\n    AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE_OFF = 0,\n    AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE_ON\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE\n{\n    AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_STUDIO = 0,\n    AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL   = 1\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_LTR_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_LTR_MODE_RESET_UNUSED    = 0,\n    AMF_VIDEO_ENCODER_HEVC_LTR_MODE_KEEP_UNUSED\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE_FRAME = 0,\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE_SLICE = 1\n};\n\nenum AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE_FRAME = 0,\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE_SLICE = 1,\n    AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE_SLICE_LAST = 2\n};\n\n// Static properties - can be set before Init()\n#define AMF_VIDEO_ENCODER_HEVC_INSTANCE_INDEX                       L\"HevcEncoderInstance\"          // amf_int64; selected instance idx\n#define AMF_VIDEO_ENCODER_HEVC_FRAMESIZE                            L\"HevcFrameSize\"                // AMFSize; default = 0,0; Frame size\n\n#define AMF_VIDEO_ENCODER_HEVC_USAGE                                L\"HevcUsage\"                    // amf_int64(AMF_VIDEO_ENCODER_HEVC_USAGE_ENUM); default = N/A; Encoder usage type. fully configures parameter set.\n#define AMF_VIDEO_ENCODER_HEVC_PROFILE                              L\"HevcProfile\"                  // amf_int64(AMF_VIDEO_ENCODER_HEVC_PROFILE_ENUM) ; default = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;\n#define AMF_VIDEO_ENCODER_HEVC_TIER                                 L\"HevcTier\"                     // amf_int64(AMF_VIDEO_ENCODER_HEVC_TIER_ENUM) ; default = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN;\n#define AMF_VIDEO_ENCODER_HEVC_PROFILE_LEVEL                        L\"HevcProfileLevel\"             // amf_int64 (AMF_VIDEO_ENCODER_LEVEL_ENUM, default depends on HW capabilities);\n#define AMF_VIDEO_ENCODER_HEVC_MAX_LTR_FRAMES                       L\"HevcMaxOfLTRFrames\"           // amf_int64; default = 0; Max number of LTR frames\n#define AMF_VIDEO_ENCODER_HEVC_LTR_MODE                             L\"HevcLTRMode\"                  // amf_int64(AMF_VIDEO_ENCODER_HEVC_LTR_MODE_ENUM); default = AMF_VIDEO_ENCODER_HEVC_LTR_MODE_RESET_UNUSED; remove/keep unused LTRs (not specified in property AMF_VIDEO_ENCODER_HEVC_FORCE_LTR_REFERENCE_BITFIELD)\n#define AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES                     L\"HevcMaxNumRefFrames\"          // amf_int64; default = 1; Maximum number of reference frames\n#define AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET                       L\"HevcQualityPreset\"            // amf_int64(AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_ENUM); default = depends on USAGE; Quality Preset\n#define AMF_VIDEO_ENCODER_HEVC_EXTRADATA                            L\"HevcExtraData\"                // AMFInterface* - > AMFBuffer*; SPS/PPS buffer - read-only\n#define AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO                         L\"HevcAspectRatio\"              // AMFRatio; default = 1, 1\n#define AMF_VIDEO_ENCODER_HEVC_LOWLATENCY_MODE                      L\"LowLatencyInternal\"           // bool; default = false, enables low latency mode\n#define AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE                  L\"HevcEnablePreAnalysis\"        // bool; default = false; enables the pre-analysis module. Currently only works in AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR mode. Refer to AMF Video PreAnalysis API reference for more details.\n#define AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE                        L\"HevcNominalRange\"\t\t\t\t// amf_int64(AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE); default = amf_int64(AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_STUDIO); property is bool but amf_int64 also works for backward compatibility.\n#define AMF_VIDEO_ENCODER_HEVC_MAX_NUM_TEMPORAL_LAYERS              L\"HevcMaxNumOfTemporalLayers\"   // amf_int64; default = 1; Max number of temporal layers.\n\n// Picture control properties\n#define AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR                     L\"HevcGOPSPerIDR\"               // amf_int64; default = 1; The frequency to insert IDR as start of a GOP. 0 means no IDR will be inserted.\n#define AMF_VIDEO_ENCODER_HEVC_GOP_SIZE                             L\"HevcGOPSize\"                  // amf_int64; default = 60; GOP Size, in frames\n#define AMF_VIDEO_ENCODER_HEVC_DE_BLOCKING_FILTER_DISABLE           L\"HevcDeBlockingFilter\"         // bool; default = depends on USAGE; De-blocking Filter\n#define AMF_VIDEO_ENCODER_HEVC_SLICES_PER_FRAME                     L\"HevcSlicesPerFrame\"           // amf_int64; default = 1; Number of slices Per Frame\n#define AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE                L\"HevcHeaderInsertionMode\"      // amf_int64(AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_ENUM); default = NONE\n#define AMF_VIDEO_ENCODER_HEVC_INTRA_REFRESH_NUM_CTBS_PER_SLOT      L\"HevcIntraRefreshCTBsNumberPerSlot\" // amf_int64; default = depends on USAGE; Intra Refresh CTBs Number Per Slot in 64x64 CTB\n\n// Rate control properties\n#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD                  L\"HevcRateControlMethod\"        // amf_int64(AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_ENUM); default = depends on USAGE; Rate Control Method\n#define AMF_VIDEO_ENCODER_HEVC_QVBR_QUALITY_LEVEL                   L\"HevcQvbrQualityLevel\"         // amf_int64; default = 23; QVBR quality level; range = 1-51\n#define AMF_VIDEO_ENCODER_HEVC_VBV_BUFFER_SIZE                      L\"HevcVBVBufferSize\"            // amf_int64; default = depends on USAGE; VBV Buffer Size in bits\n#define AMF_VIDEO_ENCODER_HEVC_INITIAL_VBV_BUFFER_FULLNESS          L\"HevcInitialVBVBufferFullness\" // amf_int64; default =  64; Initial VBV Buffer Fullness 0=0% 64=100%\n#define AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ                          L\"HevcEnableVBAQ\"               // // bool; default = depends on USAGE; Enable auto VBAQ\n#define AMF_VIDEO_ENCODER_HEVC_HIGH_MOTION_QUALITY_BOOST_ENABLE     L\"HevcHighMotionQualityBoostEnable\"// bool; default = depends on USAGE; Enable High motion quality boost mode\n\n#define AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE                     L\"HevcRateControlPreAnalysisEnable\"  // bool; default =  depends on USAGE; enables pre-encode assisted rate control\n#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_PREANALYSIS_ENABLE      L\"HevcRateControlPreAnalysisEnable\"  // bool; default =  depends on USAGE; enables pre-encode assisted rate control. Deprecated, please use AMF_VIDEO_ENCODER_PREENCODE_ENABLE instead.\n#ifdef _MSC_VER\n    #ifndef __clang__\n        #pragma deprecated(\"AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_PREANALYSIS_ENABLE\")\n    #endif\n#endif\n\n// Motion estimation\n#define AMF_VIDEO_ENCODER_HEVC_MOTION_HALF_PIXEL                    L\"HevcHalfPixel\"                // bool; default= true; Half Pixel\n#define AMF_VIDEO_ENCODER_HEVC_MOTION_QUARTERPIXEL                  L\"HevcQuarterPixel\"             // bool; default= true; Quarter Pixel\n\n// color conversion\n#define AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH                      L\"HevcColorBitDepth\"            // amf_int64(AMF_COLOR_BIT_DEPTH_ENUM); default = AMF_COLOR_BIT_DEPTH_8\n\n#define AMF_VIDEO_ENCODER_HEVC_INPUT_COLOR_PROFILE                  L\"HevcInColorProfile\"           // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_HEVC_INPUT_TRANSFER_CHARACTERISTIC        L\"HevcInColorTransferChar\"      // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 section 7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_HEVC_INPUT_COLOR_PRIMARIES                L\"HevcInColorPrimaries\"         // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 section 7.1 See ColorSpace.h for enum\n\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE                 L\"HevcOutColorProfile\"          // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC       L\"HevcOutColorTransferChar\"     // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 ?7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES               L\"HevcOutColorPrimaries\"        // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 section 7.1 See ColorSpace.h for enum\n\n// Slice output\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE                          L\"HevcOutputMode\"               // amf_int64(AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE_ENUM); default = AMF_VIDEO_ENCODER_HEVC_OUTPUT_MODE_FRAME - defines encoder output mode\n\n// Dynamic properties - can be set at any time\n\n// Rate control properties\n#define AMF_VIDEO_ENCODER_HEVC_FRAMERATE                            L\"HevcFrameRate\"                // AMFRate; default = depends on usage; Frame Rate\n\n#define AMF_VIDEO_ENCODER_HEVC_ENFORCE_HRD                          L\"HevcEnforceHRD\"               // bool; default = depends on USAGE; Enforce HRD\n#define AMF_VIDEO_ENCODER_HEVC_FILLER_DATA_ENABLE                   L\"HevcFillerDataEnable\"         // bool; default = depends on USAGE; Enforce HRD\n#define AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE                       L\"HevcTargetBitrate\"            // amf_int64; default = depends on USAGE; Target bit rate in bits\n#define AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE                         L\"HevcPeakBitrate\"              // amf_int64; default = depends on USAGE; Peak bit rate in bits\n\n#define AMF_VIDEO_ENCODER_HEVC_MAX_AU_SIZE                          L\"HevcMaxAUSize\"                // amf_int64; default = 60; Max AU Size in bits\n\n#define AMF_VIDEO_ENCODER_HEVC_MIN_QP_I                             L\"HevcMinQP_I\"                  // amf_int64; default = depends on USAGE; Min QP; range =\n#define AMF_VIDEO_ENCODER_HEVC_MAX_QP_I                             L\"HevcMaxQP_I\"                  // amf_int64; default = depends on USAGE; Max QP; range =\n#define AMF_VIDEO_ENCODER_HEVC_MIN_QP_P                             L\"HevcMinQP_P\"                  // amf_int64; default = depends on USAGE; Min QP; range =\n#define AMF_VIDEO_ENCODER_HEVC_MAX_QP_P                             L\"HevcMaxQP_P\"                  // amf_int64; default = depends on USAGE; Max QP; range =\n\n#define AMF_VIDEO_ENCODER_HEVC_QP_I                                 L\"HevcQP_I\"                     // amf_int64; default = 26; P-frame QP; range = 0-51\n#define AMF_VIDEO_ENCODER_HEVC_QP_P                                 L\"HevcQP_P\"                     // amf_int64; default = 26; P-frame QP; range = 0-51\n\n#define AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE       L\"HevcRateControlSkipFrameEnable\" // bool; default =  depends on USAGE; Rate Control Based Frame Skip\n\n// color conversion\n#define AMF_VIDEO_ENCODER_HEVC_INPUT_HDR_METADATA                   L\"HevcInHDRMetadata\"            // AMFBuffer containing AMFHDRMetadata; default NULL\n//#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_HDR_METADATA                  L\"HevcOutHDRMetadata\"            // AMFBuffer containing AMFHDRMetadata; default NULL\n\n// SVC\n#define AMF_VIDEO_ENCODER_HEVC_NUM_TEMPORAL_LAYERS                  L\"HevcNumOfTemporalLayers\"      // amf_int64; default = 1; Number of temporal layers. Can be changed at any time but the change is only applied when encoding next base layer frame.\n\n// DPB management\n#define AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE                L\"HevcPicTransferMode\"          // amf_int64(AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE_ENUM); default = AMF_VIDEO_ENCODER_HEVC_PICTURE_TRANSFER_MODE_OFF - whether to exchange reference/reconstructed pic between encoder and application\n\n// misc\n#define AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT                        L\"HevcQueryTimeout\"             // amf_int64; default = 0 (no wait); timeout for QueryOutput call in ms.\n#define AMF_VIDEO_ENCODER_HEVC_MEMORY_TYPE                          L\"HevcEncoderMemoryType\"        // amf_int64(AMF_MEMORY_TYPE) , default is AMF_MEMORY_UNKNOWN, Values : AMF_MEMORY_DX11, AMF_MEMORY_DX9, AMF_MEMORY_UNKNOWN (auto)\n#define AMF_VIDEO_ENCODER_HEVC_ENABLE_SMART_ACCESS_VIDEO            L\"HevcEnableEncoderSmartAccessVideo\"         // amf_bool; default = false; true = enables smart access video feature\n#define AMF_VIDEO_ENCODER_HEVC_INPUT_QUEUE_SIZE                     L\"HevcInputQueueSize\"           // amf_int64; default 16; Set amf input queue size\n\n// Per-submission properties - can be set on input surface interface\n#define AMF_VIDEO_ENCODER_HEVC_END_OF_SEQUENCE                      L\"HevcEndOfSequence\"            // bool; default = false; generate end of sequence\n#define AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE                   L\"HevcForcePictureType\"         // amf_int64(AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_ENUM); default = AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_NONE; generate particular picture type\n#define AMF_VIDEO_ENCODER_HEVC_INSERT_AUD                           L\"HevcInsertAUD\"                // bool; default = false; insert AUD\n#define AMF_VIDEO_ENCODER_HEVC_INSERT_HEADER                        L\"HevcInsertHeader\"             // bool; default = false; insert header(SPS, PPS, VPS)\n\n#define AMF_VIDEO_ENCODER_HEVC_MARK_CURRENT_WITH_LTR_INDEX          L\"HevcMarkCurrentWithLTRIndex\"  // amf_int64; default = N/A; Mark current frame with LTR index\n#define AMF_VIDEO_ENCODER_HEVC_FORCE_LTR_REFERENCE_BITFIELD         L\"HevcForceLTRReferenceBitfield\"// amf_int64; default = 0; force LTR bit-field\n#define AMF_VIDEO_ENCODER_HEVC_ROI_DATA                             L\"HevcROIData\"\t\t\t\t\t// 2D AMFSurface, surface format: AMF_SURFACE_GRAY32\n#define AMF_VIDEO_ENCODER_HEVC_REFERENCE_PICTURE                    L\"HevcReferencePicture\"         // AMFInterface(AMFSurface); surface used for frame injection\n#define AMF_VIDEO_ENCODER_HEVC_PSNR_FEEDBACK                        L\"HevcPSNRFeedback\"             // amf_bool; default = false; Signal encoder to calculate PSNR score\n#define AMF_VIDEO_ENCODER_HEVC_SSIM_FEEDBACK                        L\"HevcSSIMFeedback\"             // amf_bool; default = false; Signal encoder to calculate SSIM score\n#define AMF_VIDEO_ENCODER_HEVC_STATISTICS_FEEDBACK                  L\"HevcStatisticsFeedback\"       // amf_bool; default = false; Signal encoder to collect and feedback encoder statistics\n#define AMF_VIDEO_ENCODER_HEVC_BLOCK_QP_FEEDBACK                    L\"HevcBlockQpFeedback\"          // amf_bool; default = false; Signal encoder to collect and feedback block level QP values\n\n// Properties set by encoder on output buffer interface\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE                     L\"HevcOutputDataType\"           // amf_int64(AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_ENUM); default = N/A\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_MARKED_LTR_INDEX              L\"HevcMarkedLTRIndex\"           // amf_int64; default = -1; Marked LTR index\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_REFERENCED_LTR_INDEX_BITFIELD L\"HevcReferencedLTRIndexBitfield\"// amf_int64; default = 0; referenced LTR bit-field\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_TEMPORAL_LAYER                L\"HevcOutputTemporalLayer\"      // amf_int64; Temporal layer\n#define AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE                   L\"HevcOutputBufferType\"         // amf_int64(AMF_VIDEO_ENCODER_HEVC_OUTPUT_BUFFER_TYPE_ENUM); encoder output buffer type\n#define AMF_VIDEO_ENCODER_HEVC_RECONSTRUCTED_PICTURE                L\"HevcReconstructedPicture\"     // AMFInterface(AMFSurface); returns reconstructed picture as an AMFSurface attached to the output buffer as property AMF_VIDEO_ENCODER_RECONSTRUCTED_PICTURE of AMFInterface type\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PSNR_Y                     L\"PSNRY\"                        // double; PSNR Y\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PSNR_U                     L\"PSNRU\"                        // double; PSNR U\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PSNR_V                     L\"PSNRV\"                        // double; PSNR V\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PSNR_ALL                   L\"PSNRALL\"                      // double; PSNR All\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SSIM_Y                     L\"SSIMY\"                        // double; SSIM Y\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SSIM_U                     L\"SSIMU\"                        // double; SSIM U\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SSIM_V                     L\"SSIMV\"                        // double; SSIM V\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SSIM_ALL                   L\"SSIMALL\"                      // double; SSIM ALL\n\n    // Encoder statistics feedback\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_FRAME_QP                   L\"HevcStatisticsFeedbackFrameQP\"                // amf_int64; Rate control base frame/initial QP\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_AVERAGE_QP                 L\"HevcStatisticsFeedbackAvgQP\"                  // amf_int64; Average QP of all encoded CTBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped CTBs.\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_MAX_QP                     L\"HevcStatisticsFeedbackMaxQP\"                  // amf_int64; Max QP among all encoded CTBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped CTBs.\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_MIN_QP                     L\"HevcStatisticsFeedbackMinQP\"                  // amf_int64; Min QP among all encoded CTBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped CTBs.\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PIX_NUM_INTRA              L\"HevcStatisticsFeedbackPixNumIntra\"            // amf_int64; Number of the intra encoded pixels\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PIX_NUM_INTER              L\"HevcStatisticsFeedbackPixNumInter\"            // amf_int64; Number of the inter encoded pixels\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_PIX_NUM_SKIP               L\"HevcStatisticsFeedbackPixNumSkip\"             // amf_int64; Number of the skip mode pixels\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_BITCOUNT_RESIDUAL          L\"HevcStatisticsFeedbackBitcountResidual\"       // amf_int64; The bit count that corresponds to residual data\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_BITCOUNT_MOTION            L\"HevcStatisticsFeedbackBitcountMotion\"         // amf_int64; The bit count that corresponds to motion vectors\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_BITCOUNT_INTER             L\"HevcStatisticsFeedbackBitcountInter\"          // amf_int64; The bit count that are assigned to inter CTBs\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_BITCOUNT_INTRA             L\"HevcStatisticsFeedbackBitcountIntra\"          // amf_int64; The bit count that are assigned to intra CTBs\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_BITCOUNT_ALL_MINUS_HEADER  L\"HevcStatisticsFeedbackBitcountAllMinusHeader\" // amf_int64; The bit count of the bitstream excluding header\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_MV_X                       L\"HevcStatisticsFeedbackMvX\"                    // amf_int64; Accumulated absolute values of horizontal MV's\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_MV_Y                       L\"HevcStatisticsFeedbackMvY\"                    // amf_int64; Accumulated absolute values of vertical MV's\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_RD_COST_FINAL              L\"HevcStatisticsFeedbackRdCostFinal\"            // amf_int64; Frame level final RD cost for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_RD_COST_INTRA              L\"HevcStatisticsFeedbackRdCostIntra\"            // amf_int64; Frame level intra RD cost for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_RD_COST_INTER              L\"HevcStatisticsFeedbackRdCostInter\"            // amf_int64; Frame level inter RD cost for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SAD_FINAL                  L\"HevcStatisticsFeedbackSadFinal\"               // amf_int64; Frame level final SAD for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SAD_INTRA                  L\"HevcStatisticsFeedbackSadIntra\"               // amf_int64; Frame level intra SAD for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_SAD_INTER                  L\"HevcStatisticsFeedbackSadInter\"               // amf_int64; Frame level inter SAD for full encoding\n#define AMF_VIDEO_ENCODER_HEVC_STATISTIC_VARIANCE                   L\"HevcStatisticsFeedbackVariance\"               // amf_int64; Frame level variance for full encoding\n\n    // Encoder block level feedback\n#define AMF_VIDEO_ENCODER_HEVC_BLOCK_QP_MAP                         L\"HevcBlockQpMap\"                               // AMFInterface(AMFSurface); AMFSurface of format AMF_SURFACE_GRAY32 containing block level QP values\n\n// HEVC Encoder capabilities - exposed in AMFCaps interface\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_BITRATE                      L\"HevcMaxBitrate\"               // amf_int64; Maximum bit rate in bits\n#define AMF_VIDEO_ENCODER_HEVC_CAP_NUM_OF_STREAMS                   L\"HevcNumOfStreams\"             // amf_int64; maximum number of encode streams supported\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_PROFILE                      L\"HevcMaxProfile\"               // amf_int64(AMF_VIDEO_ENCODER_HEVC_PROFILE_ENUM)\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_TIER                         L\"HevcMaxTier\"                  // amf_int64(AMF_VIDEO_ENCODER_HEVC_TIER_ENUM) maximum profile tier\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_LEVEL                        L\"HevcMaxLevel\"                 // amf_int64 maximum profile level\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MIN_REFERENCE_FRAMES             L\"HevcMinReferenceFrames\"       // amf_int64 minimum number of reference frames\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_REFERENCE_FRAMES             L\"HevcMaxReferenceFrames\"       // amf_int64 maximum number of reference frames\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_TEMPORAL_LAYERS              L\"HevcMaxTemporalLayers\"        // amf_int64 maximum number of temporal layers\n#define AMF_VIDEO_ENCODER_HEVC_CAP_NUM_OF_HW_INSTANCES              L\"HevcNumOfHwInstances\"         // amf_int64 number of HW encoder instances\n#define AMF_VIDEO_ENCODER_HEVC_CAP_COLOR_CONVERSION                 L\"HevcColorConversion\"          // amf_int64(AMF_ACCELERATION_TYPE) - type of supported color conversion. default AMF_ACCEL_GPU\n#define AMF_VIDEO_ENCODER_HEVC_CAP_PRE_ANALYSIS                     L\"HevcPreAnalysis\"              // amf_bool - pre analysis module is available for HEVC UVE encoder, n/a for the other encoders\n#define AMF_VIDEO_ENCODER_HEVC_CAP_ROI                              L\"HevcROIMap\"                   // amf_bool - ROI map support is available for HEVC UVE encoder, n/a for the other encoders\n#define AMF_VIDEO_ENCODER_HEVC_CAP_MAX_THROUGHPUT                   L\"HevcMaxThroughput\"            // amf_int64 - MAX throughput for HEVC encoder in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_HEVC_CAP_REQUESTED_THROUGHPUT             L\"HevcRequestedThroughput\"      // amf_int64 - Currently total requested throughput for HEVC encode in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_HEVC_CAP_QUERY_TIMEOUT_SUPPORT            L\"HevcQueryTimeoutSupport\"      // amf_bool - Timeout supported for QueryOutout call\n#define AMF_VIDEO_ENCODER_CAPS_HEVC_QUERY_TIMEOUT_SUPPORT           L\"HevcQueryTimeoutSupport\"      // amf_bool - Timeout supported for QueryOutout call (Deprecated, please use AMF_VIDEO_ENCODER_HEVC_CAP_QUERY_TIMEOUT_SUPPORT instead)\n#define AMF_VIDEO_ENCODER_HEVC_CAP_SUPPORT_SLICE_OUTPUT             L\"HevcSupportSliceOutput\"       // amf_bool - if slice output is supported\n\n#define AMF_VIDEO_ENCODER_HEVC_CAP_SUPPORT_SMART_ACCESS_VIDEO       L\"HevcEncoderSupportSmartAccessVideo\"        // amf_bool; returns true if system supports SmartAccess Video\n\n#endif //#ifndef AMF_VideoEncoderHEVC_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoEncoderVCE.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// AMFVideoEncoderHW_AVC interface declaration\n//-------------------------------------------------------------------------------------------------\n\n#ifndef AMF_VideoEncoderVCE_h\n#define AMF_VideoEncoderVCE_h\n#pragma once\n\n#include \"Component.h\"\n#include \"ColorSpace.h\"\n#include \"PreAnalysis.h\"\n\n#define AMFVideoEncoderVCE_AVC L\"AMFVideoEncoderVCE_AVC\"\n#define AMFVideoEncoderVCE_SVC L\"AMFVideoEncoderVCE_SVC\"\n\nenum AMF_VIDEO_ENCODER_USAGE_ENUM\n{\n    AMF_VIDEO_ENCODER_USAGE_TRANSCONDING = 0, // kept for backwards compatability\n    AMF_VIDEO_ENCODER_USAGE_TRANSCODING = 0, // fixed typo\n    AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY,\n    AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY,\n    AMF_VIDEO_ENCODER_USAGE_WEBCAM,\n    AMF_VIDEO_ENCODER_USAGE_HIGH_QUALITY,\n    AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY\n};\n\nenum AMF_VIDEO_ENCODER_PROFILE_ENUM\n{\n    AMF_VIDEO_ENCODER_PROFILE_UNKNOWN = 0,\n    AMF_VIDEO_ENCODER_PROFILE_BASELINE = 66,\n    AMF_VIDEO_ENCODER_PROFILE_MAIN = 77,\n    AMF_VIDEO_ENCODER_PROFILE_HIGH = 100,\n    AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE = 256,\n    AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH = 257\n};\n\nenum AMF_VIDEO_ENCODER_H264_LEVEL_ENUM\n{\n    AMF_H264_LEVEL__1   = 10,\n    AMF_H264_LEVEL__1_1 = 11,\n    AMF_H264_LEVEL__1_2 = 12,\n    AMF_H264_LEVEL__1_3 = 13,\n    AMF_H264_LEVEL__2   = 20,\n    AMF_H264_LEVEL__2_1 = 21,\n    AMF_H264_LEVEL__2_2 = 22,\n    AMF_H264_LEVEL__3   = 30,\n    AMF_H264_LEVEL__3_1 = 31,\n    AMF_H264_LEVEL__3_2 = 32,\n    AMF_H264_LEVEL__4   = 40,\n    AMF_H264_LEVEL__4_1 = 41,\n    AMF_H264_LEVEL__4_2 = 42,\n    AMF_H264_LEVEL__5   = 50,\n    AMF_H264_LEVEL__5_1 = 51,\n    AMF_H264_LEVEL__5_2 = 52,\n    AMF_H264_LEVEL__6   = 60,\n    AMF_H264_LEVEL__6_1 = 61,\n    AMF_H264_LEVEL__6_2 = 62\n};\n\nenum AMF_VIDEO_ENCODER_SCANTYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_SCANTYPE_PROGRESSIVE = 0,\n    AMF_VIDEO_ENCODER_SCANTYPE_INTERLACED\n};\n\nenum AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_ENUM\n{\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN = -1,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP = 0,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_QUALITY_VBR,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR,\n    AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR\n};\n\nenum AMF_VIDEO_ENCODER_QUALITY_PRESET_ENUM\n{\n    AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED = 0,\n    AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED,\n    AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY\n};\n\nenum AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_ENUM\n{\n    AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_NONE = 0,\n    AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_FRAME,\n    AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_TOP_FIELD,\n    AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_BOTTOM_FIELD\n};\n\nenum AMF_VIDEO_ENCODER_PICTURE_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_NONE = 0,\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_SKIP,\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_IDR,\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_I,\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_P,\n    AMF_VIDEO_ENCODER_PICTURE_TYPE_B\n};\n\nenum AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR,\n    AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_I,\n    AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_P,\n    AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_B\n};\n\nenum AMF_VIDEO_ENCODER_PREENCODE_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_PREENCODE_DISABLED = 0,\n    AMF_VIDEO_ENCODER_PREENCODE_ENABLED  = 1,\n};\n\nenum AMF_VIDEO_ENCODER_CODING_ENUM\n{\n    AMF_VIDEO_ENCODER_UNDEFINED = 0, // BASELINE = CALV; MAIN, HIGH = CABAC\n    AMF_VIDEO_ENCODER_CABAC,\n    AMF_VIDEO_ENCODER_CALV,\n\n};\n\nenum AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE_OFF = 0,\n    AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE_ON\n};\n\nenum AMF_VIDEO_ENCODER_LTR_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_LTR_MODE_RESET_UNUSED = 0,\n    AMF_VIDEO_ENCODER_LTR_MODE_KEEP_UNUSED\n};\n\nenum AMF_VIDEO_ENCODER_OUTPUT_MODE_ENUM\n{\n    AMF_VIDEO_ENCODER_OUTPUT_MODE_FRAME = 0,\n    AMF_VIDEO_ENCODER_OUTPUT_MODE_SLICE = 1\n};\n\nenum AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE_ENUM\n{\n    AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE_FRAME      = 0,\n    AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE_SLICE      = 1,\n    AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE_SLICE_LAST = 2\n};\n\n// Static properties - can be set before Init()\n#define AMF_VIDEO_ENCODER_INSTANCE_INDEX                        L\"EncoderInstance\"          // amf_int64; selected HW instance idx\n#define AMF_VIDEO_ENCODER_FRAMESIZE                             L\"FrameSize\"                // AMFSize; default = 0,0; Frame size\n\n#define AMF_VIDEO_ENCODER_EXTRADATA                             L\"ExtraData\"                // AMFInterface* - > AMFBuffer*; SPS/PPS buffer in Annex B format - read-only\n#define AMF_VIDEO_ENCODER_USAGE                                 L\"Usage\"                    // amf_int64(AMF_VIDEO_ENCODER_USAGE_ENUM); default = N/A; Encoder usage type. fully configures parameter set.\n#define AMF_VIDEO_ENCODER_PROFILE                               L\"Profile\"                  // amf_int64(AMF_VIDEO_ENCODER_PROFILE_ENUM) ; default = AMF_VIDEO_ENCODER_PROFILE_MAIN;  H264 profile\n#define AMF_VIDEO_ENCODER_PROFILE_LEVEL                         L\"ProfileLevel\"             // amf_int64(AMF_VIDEO_ENCODER_H264_LEVEL_ENUM); default = AMF_H264_LEVEL__4_2; H264 level\n#define AMF_VIDEO_ENCODER_MAX_LTR_FRAMES                        L\"MaxOfLTRFrames\"           // amf_int64; default = 0; Max number of LTR frames\n#define AMF_VIDEO_ENCODER_LTR_MODE                              L\"LTRMode\"                  // amf_int64(AMF_VIDEO_ENCODER_LTR_MODE_ENUM); default = AMF_VIDEO_ENCODER_LTR_MODE_RESET_UNUSED; remove/keep unused LTRs (not specified in property AMF_VIDEO_ENCODER_FORCE_LTR_REFERENCE_BITFIELD)\n#define AMF_VIDEO_ENCODER_SCANTYPE                              L\"ScanType\"                 // amf_int64(AMF_VIDEO_ENCODER_SCANTYPE_ENUM); default = AMF_VIDEO_ENCODER_SCANTYPE_PROGRESSIVE; indicates input stream type\n#define AMF_VIDEO_ENCODER_MAX_NUM_REFRAMES                      L\"MaxNumRefFrames\"          // amf_int64; Maximum number of reference frames\n#define AMF_VIDEO_ENCODER_MAX_CONSECUTIVE_BPICTURES             L\"MaxConsecutiveBPictures\"  // amf_int64; Maximum number of consecutive B Pictures\n#define AMF_VIDEO_ENCODER_ADAPTIVE_MINIGOP                      L\"AdaptiveMiniGOP\"          // bool; default = false; Disable/Enable Adaptive MiniGOP\n#define AMF_VIDEO_ENCODER_ASPECT_RATIO                          L\"AspectRatio\"              // AMFRatio; default = 1, 1\n#define AMF_VIDEO_ENCODER_FULL_RANGE_COLOR                      L\"FullRangeColor\"           // bool; default = false; inidicates that YUV input is (0,255)\n#define AMF_VIDEO_ENCODER_LOWLATENCY_MODE                       L\"LowLatencyInternal\"       // bool; default = false, enables low latency mode and POC mode 2 in the encoder\n#define AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE                   L\"EnablePreAnalysis\"        // bool; default = false; enables the pre-analysis module. Currently only works in AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR mode. Refer to AMF Video PreAnalysis API reference for more details.\n#define AMF_VIDEO_ENCODER_PREENCODE_ENABLE                      L\"RateControlPreanalysisEnable\"     // amf_int64(AMF_VIDEO_ENCODER_PREENCODE_MODE_ENUM); default =  AMF_VIDEO_ENCODER_PREENCODE_DISABLED; enables pre-encode assisted rate control\n#define AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE       L\"RateControlPreanalysisEnable\"     // amf_int64(AMF_VIDEO_ENCODER_PREENCODE_MODE_ENUM); default =  AMF_VIDEO_ENCODER_PREENCODE_DISABLED; enables pre-encode assisted rate control. Deprecated, please use AMF_VIDEO_ENCODER_PREENCODE_ENABLE instead.\n#define AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD                   L\"RateControlMethod\"        // amf_int64(AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_ENUM); default = depends on USAGE; Rate Control Method\n#define AMF_VIDEO_ENCODER_QVBR_QUALITY_LEVEL                    L\"QvbrQualityLevel\"         // amf_int64; default = 23; QVBR quality level; range = 1-51\n#define AMF_VIDEO_ENCODER_MAX_NUM_TEMPORAL_LAYERS               L\"MaxNumOfTemporalLayers\"   // amf_int64; default = 1; Max number of temporal layers.\n#if !defined(__GNUC__) && !defined(__clang__)\n    #pragma deprecated(\"AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE\")\n#endif\n\n\n    // Quality preset property\n#define AMF_VIDEO_ENCODER_QUALITY_PRESET                        L\"QualityPreset\"            // amf_int64(AMF_VIDEO_ENCODER_QUALITY_PRESET_ENUM); default = depends on USAGE; Quality Preset\n\n    // color conversion\n#define AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH                       L\"ColorBitDepth\"            // amf_int64(AMF_COLOR_BIT_DEPTH_ENUM); default = AMF_COLOR_BIT_DEPTH_8\n\n#define AMF_VIDEO_ENCODER_INPUT_COLOR_PROFILE                   L\"InColorProfile\"           // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_INPUT_TRANSFER_CHARACTERISTIC         L\"InColorTransferChar\"      // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 ?7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_INPUT_COLOR_PRIMARIES                 L\"InColorPrimaries\"         // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 Section 7.1 See ColorSpace.h for enum\n#define AMF_VIDEO_ENCODER_INPUT_HDR_METADATA                    L\"InHDRMetadata\"            // AMFBuffer containing AMFHDRMetadata; default NULL\n\n#define AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE                  L\"OutColorProfile\"          // amf_int64(AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM); default = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN - mean AUTO by size\n#define AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC        L\"OutColorTransferChar\"     // amf_int64(AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM); default = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, ISO/IEC 23001-8_2013 Section 7.2 See VideoDecoderUVD.h for enum\n#define AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES                L\"OutColorPrimaries\"        // amf_int64(AMF_COLOR_PRIMARIES_ENUM); default = AMF_COLOR_PRIMARIES_UNDEFINED, ISO/IEC 23001-8_2013 Section 7.1 See ColorSpace.h for enum\n#define AMF_VIDEO_ENCODER_OUTPUT_HDR_METADATA                   L\"OutHDRMetadata\"           // AMFBuffer containing AMFHDRMetadata; default NULL\n\n// Slice output\n#define AMF_VIDEO_ENCODER_OUTPUT_MODE                           L\"OutputMode\"               // amf_int64(AMF_VIDEO_ENCODER_OUTPUT_MODE_ENUM); default = AMF_VIDEO_ENCODER_OUTPUT_MODE_FRAME - defines encoder output mode\n\n\n// Dynamic properties - can be set at any time\n   // Rate control properties\n#define AMF_VIDEO_ENCODER_FRAMERATE                             L\"FrameRate\"                // AMFRate; default = depends on usage; Frame Rate\n#define AMF_VIDEO_ENCODER_B_PIC_DELTA_QP                        L\"BPicturesDeltaQP\"         // amf_int64; default = depends on USAGE; B-picture Delta\n#define AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP                    L\"ReferenceBPicturesDeltaQP\"// amf_int64; default = depends on USAGE; Reference B-picture Delta\n\n#define AMF_VIDEO_ENCODER_ENFORCE_HRD                           L\"EnforceHRD\"               // bool; default = depends on USAGE; Enforce HRD\n#define AMF_VIDEO_ENCODER_FILLER_DATA_ENABLE                    L\"FillerDataEnable\"         // bool; default = false; Filler Data Enable\n#define AMF_VIDEO_ENCODER_ENABLE_VBAQ                           L\"EnableVBAQ\"               // bool; default = depends on USAGE; Enable VBAQ\n#define AMF_VIDEO_ENCODER_HIGH_MOTION_QUALITY_BOOST_ENABLE      L\"HighMotionQualityBoostEnable\"// bool; default = depends on USAGE; Enable High motion quality boost mode\n\n\n#define AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE                       L\"VBVBufferSize\"            // amf_int64; default = depends on USAGE; VBV Buffer Size in bits\n#define AMF_VIDEO_ENCODER_INITIAL_VBV_BUFFER_FULLNESS           L\"InitialVBVBufferFullness\" // amf_int64; default =  64; Initial VBV Buffer Fullness 0=0% 64=100%\n\n#define AMF_VIDEO_ENCODER_MAX_AU_SIZE                           L\"MaxAUSize\"                // amf_int64; default = 0; Max AU Size in bits\n\n#define AMF_VIDEO_ENCODER_MIN_QP                                L\"MinQP\"                    // amf_int64; default = depends on USAGE; Min QP; range = 0-51\n#define AMF_VIDEO_ENCODER_MAX_QP                                L\"MaxQP\"                    // amf_int64; default = depends on USAGE; Max QP; range = 0-51\n#define AMF_VIDEO_ENCODER_QP_I                                  L\"QPI\"                      // amf_int64; default = 22; I-frame QP; range = 0-51\n#define AMF_VIDEO_ENCODER_QP_P                                  L\"QPP\"                      // amf_int64; default = 22; P-frame QP; range = 0-51\n#define AMF_VIDEO_ENCODER_QP_B                                  L\"QPB\"                      // amf_int64; default = 22; B-frame QP; range = 0-51\n#define AMF_VIDEO_ENCODER_TARGET_BITRATE                        L\"TargetBitrate\"            // amf_int64; default = depends on USAGE; Target bit rate in bits\n#define AMF_VIDEO_ENCODER_PEAK_BITRATE                          L\"PeakBitrate\"              // amf_int64; default = depends on USAGE; Peak bit rate in bits\n#define AMF_VIDEO_ENCODER_RATE_CONTROL_SKIP_FRAME_ENABLE        L\"RateControlSkipFrameEnable\"   // bool; default =  depends on USAGE; Rate Control Based Frame Skip\n\n    // Picture control properties\n#define AMF_VIDEO_ENCODER_HEADER_INSERTION_SPACING              L\"HeaderInsertionSpacing\"   // amf_int64; default = depends on USAGE; Header Insertion Spacing; range 0-1000\n#define AMF_VIDEO_ENCODER_B_PIC_PATTERN                         L\"BPicturesPattern\"         // amf_int64; default = 0; B-picture Pattern (number of B-Frames)\n#define AMF_VIDEO_ENCODER_DE_BLOCKING_FILTER                    L\"DeBlockingFilter\"         // bool; default = depends on USAGE; De-blocking Filter\n#define AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE                    L\"BReferenceEnable\"         // bool; default = true; Enable Refrence to B-frames\n#define AMF_VIDEO_ENCODER_IDR_PERIOD                            L\"IDRPeriod\"                // amf_int64; default = depends on USAGE; IDR Period in frames\n#define AMF_VIDEO_ENCODER_INTRA_PERIOD                          L\"IntraPeriod\"              // amf_int64; default = 0; Intra period in frames\n#define AMF_VIDEO_ENCODER_INTRA_REFRESH_NUM_MBS_PER_SLOT        L\"IntraRefreshMBsNumberPerSlot\" // amf_int64; default = depends on USAGE; Intra Refresh MBs Number Per Slot in Macroblocks\n#define AMF_VIDEO_ENCODER_SLICES_PER_FRAME                      L\"SlicesPerFrame\"           // amf_int64; default = 1; Number of slices Per Frame\n#define AMF_VIDEO_ENCODER_CABAC_ENABLE                          L\"CABACEnable\"              // amf_int64(AMF_VIDEO_ENCODER_CODING_ENUM) default = AMF_VIDEO_ENCODER_UNDEFINED\n\n    // Motion estimation\n#define AMF_VIDEO_ENCODER_MOTION_HALF_PIXEL                     L\"HalfPixel\"                // bool; default= true; Half Pixel\n#define AMF_VIDEO_ENCODER_MOTION_QUARTERPIXEL                   L\"QuarterPixel\"             // bool; default= true; Quarter Pixel\n\n    // SVC\n#define AMF_VIDEO_ENCODER_NUM_TEMPORAL_ENHANCMENT_LAYERS        L\"NumOfTemporalEnhancmentLayers\" // amf_int64; default = 1; range = 1-MaxTemporalLayers; Number of temporal Layers (SVC)\n\n\n    // DPB management\n#define AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE                 L\"PicTransferMode\"               // amf_int64(AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE_ENUM); default = AMF_VIDEO_ENCODER_PICTURE_TRANSFER_MODE_OFF - whether to exchange reference/reconstructed pic between encoder and application\n    // misc\n#define AMF_VIDEO_ENCODER_QUERY_TIMEOUT                         L\"QueryTimeout\"             // amf_int64; default = 0 (no wait); timeout for QueryOutput call in ms.\n#define AMF_VIDEO_ENCODER_MEMORY_TYPE                           L\"EncoderMemoryType\"        // amf_int64(AMF_MEMORY_TYPE) , default is AMF_MEMORY_UNKNOWN, Values : AMF_MEMORY_DX11, AMF_MEMORY_DX9, AMF_MEMORY_VULKAN or AMF_MEMORY_UNKNOWN (auto)\n#define AMF_VIDEO_ENCODER_ENABLE_SMART_ACCESS_VIDEO             L\"EnableEncoderSmartAccessVideo\"         // amf_bool; default = false; true = enables smart access video feature\n#define  AMF_VIDEO_ENCODER_INPUT_QUEUE_SIZE                     L\"InputQueueSize\"           // amf_int64; default 16; Set amf input queue size\n\n// Per-submission properties - can be set on input surface interface\n#define AMF_VIDEO_ENCODER_END_OF_SEQUENCE                       L\"EndOfSequence\"            // bool; default = false; generate end of sequence\n#define AMF_VIDEO_ENCODER_END_OF_STREAM                         L\"EndOfStream\"              // bool; default = false; generate end of stream\n#define AMF_VIDEO_ENCODER_FORCE_PICTURE_TYPE                    L\"ForcePictureType\"         // amf_int64(AMF_VIDEO_ENCODER_PICTURE_TYPE_ENUM); default = AMF_VIDEO_ENCODER_PICTURE_TYPE_NONE; generate particular picture type\n#define AMF_VIDEO_ENCODER_INSERT_AUD                            L\"InsertAUD\"                // bool; default = false; insert AUD\n#define AMF_VIDEO_ENCODER_INSERT_SPS                            L\"InsertSPS\"                // bool; default = false; insert SPS\n#define AMF_VIDEO_ENCODER_INSERT_PPS                            L\"InsertPPS\"                // bool; default = false; insert PPS\n#define AMF_VIDEO_ENCODER_PICTURE_STRUCTURE                     L\"PictureStructure\"         // amf_int64(AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_ENUM); default = AMF_VIDEO_ENCODER_PICTURE_STRUCTURE_FRAME; indicate picture type\n#define AMF_VIDEO_ENCODER_MARK_CURRENT_WITH_LTR_INDEX           L\"MarkCurrentWithLTRIndex\"  // //amf_int64; default = N/A; Mark current frame with LTR index\n#define AMF_VIDEO_ENCODER_FORCE_LTR_REFERENCE_BITFIELD          L\"ForceLTRReferenceBitfield\"// amf_int64; default = 0; force LTR bit-field\n#define AMF_VIDEO_ENCODER_ROI_DATA                              L\"ROIData\"                  // 2D AMFSurface, surface format: AMF_SURFACE_GRAY32\n#define AMF_VIDEO_ENCODER_REFERENCE_PICTURE                     L\"ReferencePicture\"         // AMFInterface(AMFSurface); surface used for frame injection\n#define AMF_VIDEO_ENCODER_PSNR_FEEDBACK                         L\"PSNRFeedback\"             // amf_bool; default = false; Signal encoder to calculate PSNR score\n#define AMF_VIDEO_ENCODER_SSIM_FEEDBACK                         L\"SSIMFeedback\"             // amf_bool; default = false; Signal encoder to calculate SSIM score\n#define AMF_VIDEO_ENCODER_STATISTICS_FEEDBACK                   L\"StatisticsFeedback\"       // amf_bool; default = false; Signal encoder to collect and feedback statistics\n#define AMF_VIDEO_ENCODER_BLOCK_QP_FEEDBACK                     L\"BlockQpFeedback\"          // amf_bool; default = false; Signal encoder to collect and feedback block level QP values\n\n\n// properties set by encoder on output buffer interface\n#define AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE                      L\"OutputDataType\"           // amf_int64(AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_ENUM); default = N/A\n#define AMF_VIDEO_ENCODER_OUTPUT_MARKED_LTR_INDEX               L\"MarkedLTRIndex\"           //amf_int64; default = -1; Marked LTR index\n#define AMF_VIDEO_ENCODER_OUTPUT_REFERENCED_LTR_INDEX_BITFIELD  L\"ReferencedLTRIndexBitfield\" // amf_int64; default = 0; referenced LTR bit-field\n#define AMF_VIDEO_ENCODER_OUTPUT_TEMPORAL_LAYER                 L\"OutputTemporalLayer\"      // amf_int64; Temporal layer\n#define AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE                    L\"OutputBufferType\"         // amf_int64(AMF_VIDEO_ENCODER_OUTPUT_BUFFER_TYPE_ENUM); encoder output buffer type\n#define AMF_VIDEO_ENCODER_PRESENTATION_TIME_STAMP               L\"PresentationTimeStamp\"    // amf_int64; Presentation time stamp (PTS)\n#define AMF_VIDEO_ENCODER_RECONSTRUCTED_PICTURE                 L\"ReconstructedPicture\"     // AMFInterface(AMFSurface); returns reconstructed picture as an AMFSurface attached to the output buffer as property AMF_VIDEO_ENCODER_RECONSTRUCTED_PICTURE of AMFInterface type\n#define AMF_VIDEO_ENCODER_STATISTIC_PSNR_Y                      L\"PSNRY\"                    // double; PSNR Y\n#define AMF_VIDEO_ENCODER_STATISTIC_PSNR_U                      L\"PSNRU\"                    // double; PSNR U\n#define AMF_VIDEO_ENCODER_STATISTIC_PSNR_V                      L\"PSNRV\"                    // double; PSNR V\n#define AMF_VIDEO_ENCODER_STATISTIC_PSNR_ALL                    L\"PSNRALL\"                  // double; PSNR All\n#define AMF_VIDEO_ENCODER_STATISTIC_SSIM_Y                      L\"SSIMY\"                    // double; SSIM Y\n#define AMF_VIDEO_ENCODER_STATISTIC_SSIM_U                      L\"SSIMU\"                    // double; SSIM U\n#define AMF_VIDEO_ENCODER_STATISTIC_SSIM_V                      L\"SSIMV\"                    // double; SSIM V\n#define AMF_VIDEO_ENCODER_STATISTIC_SSIM_ALL                    L\"SSIMALL\"                  // double; SSIM ALL\n\n    // Encoder statistics feedback\n#define AMF_VIDEO_ENCODER_STATISTIC_FRAME_QP                    L\"StatisticsFeedbackFrameQP\"                // amf_int64; Rate control base frame/initial QP\n#define AMF_VIDEO_ENCODER_STATISTIC_AVERAGE_QP                  L\"StatisticsFeedbackAvgQP\"                  // amf_int64; Average calculated QP of all encoded MBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped MBs.\n#define AMF_VIDEO_ENCODER_STATISTIC_MAX_QP                      L\"StatisticsFeedbackMaxQP\"                  // amf_int64; Max calculated QP among all encoded MBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped MBs.\n#define AMF_VIDEO_ENCODER_STATISTIC_MIN_QP                      L\"StatisticsFeedbackMinQP\"                  // amf_int64; Min calculated QP among all encoded MBs in a picture. Value may be different from the one reported by bitstream analyzer when there are skipped MBs.\n#define AMF_VIDEO_ENCODER_STATISTIC_PIX_NUM_INTRA               L\"StatisticsFeedbackPixNumIntra\"            // amf_int64; Number of the intra encoded pixels\n#define AMF_VIDEO_ENCODER_STATISTIC_PIX_NUM_INTER               L\"StatisticsFeedbackPixNumInter\"            // amf_int64; Number of the inter encoded pixels\n#define AMF_VIDEO_ENCODER_STATISTIC_PIX_NUM_SKIP                L\"StatisticsFeedbackPixNumSkip\"             // amf_int64; Number of the skip mode pixels\n#define AMF_VIDEO_ENCODER_STATISTIC_BITCOUNT_RESIDUAL           L\"StatisticsFeedbackBitcountResidual\"       // amf_int64; The bit count that corresponds to residual data\n#define AMF_VIDEO_ENCODER_STATISTIC_BITCOUNT_MOTION             L\"StatisticsFeedbackBitcountMotion\"         // amf_int64; The bit count that corresponds to motion vectors\n#define AMF_VIDEO_ENCODER_STATISTIC_BITCOUNT_INTER              L\"StatisticsFeedbackBitcountInter\"          // amf_int64; The bit count that are assigned to inter MBs\n#define AMF_VIDEO_ENCODER_STATISTIC_BITCOUNT_INTRA              L\"StatisticsFeedbackBitcountIntra\"          // amf_int64; The bit count that are assigned to intra MBs\n#define AMF_VIDEO_ENCODER_STATISTIC_BITCOUNT_ALL_MINUS_HEADER   L\"StatisticsFeedbackBitcountAllMinusHeader\" // amf_int64; The bit count of the bitstream excluding header\n#define AMF_VIDEO_ENCODER_STATISTIC_MV_X                        L\"StatisticsFeedbackMvX\"                    // amf_int64; Accumulated absolute values of horizontal MV's\n#define AMF_VIDEO_ENCODER_STATISTIC_MV_Y                        L\"StatisticsFeedbackMvY\"                    // amf_int64; Accumulated absolute values of vertical MV's\n#define AMF_VIDEO_ENCODER_STATISTIC_RD_COST_FINAL               L\"StatisticsFeedbackRdCostFinal\"            // amf_int64; Frame level final RD cost for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_RD_COST_INTRA               L\"StatisticsFeedbackRdCostIntra\"            // amf_int64; Frame level intra RD cost for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_RD_COST_INTER               L\"StatisticsFeedbackRdCostInter\"            // amf_int64; Frame level inter RD cost for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_SATD_FINAL                  L\"StatisticsFeedbackSatdFinal\"              // amf_int64; Frame level final SATD for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_SATD_INTRA                  L\"StatisticsFeedbackSatdIntra\"              // amf_int64; Frame level intra SATD for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_SATD_INTER                  L\"StatisticsFeedbackSatdInter\"              // amf_int64; Frame level inter SATD for full encoding\n#define AMF_VIDEO_ENCODER_STATISTIC_VARIANCE                    L\"StatisticsFeedbackVariance\"               // amf_int64; Frame level variance for full encoding\n\n    // Encoder block level feedback\n#define AMF_VIDEO_ENCODER_BLOCK_QP_MAP                          L\"BlockQpMap\"                               // AMFInterface(AMFSurface); AMFSurface of format AMF_SURFACE_GRAY32 containing block level QP values\n\n#define AMF_VIDEO_ENCODER_HDCP_COUNTER                          L\"HDCPCounter\"              //  const void*\n\n// Properties for multi-instance cloud gaming\n#define AMF_VIDEO_ENCODER_MAX_INSTANCES                         L\"EncoderMaxInstances\"      // deprecated.  amf_int64; default = 1; max number of encoder instances\n#define AMF_VIDEO_ENCODER_MULTI_INSTANCE_MODE                   L\"MultiInstanceMode\"        // deprecated.  bool; default = false;\n#define AMF_VIDEO_ENCODER_CURRENT_QUEUE                         L\"MultiInstanceCurrentQueue\"// deprecated.  amf_int64; default = 0;\n\n\n// VCE Encoder capabilities - exposed in AMFCaps interface\n#define AMF_VIDEO_ENCODER_CAP_MAX_BITRATE                       L\"MaxBitrate\"               // amf_int64; Maximum bit rate in bits\n#define AMF_VIDEO_ENCODER_CAP_NUM_OF_STREAMS                    L\"NumOfStreams\"             // amf_int64; maximum number of encode streams supported\n#define AMF_VIDEO_ENCODER_CAP_MAX_PROFILE                       L\"MaxProfile\"               // AMF_VIDEO_ENCODER_PROFILE_ENUM\n#define AMF_VIDEO_ENCODER_CAP_MAX_LEVEL                         L\"MaxLevel\"                 // amf_int64 maximum profile level\n#define AMF_VIDEO_ENCODER_CAP_BFRAMES                           L\"BFrames\"                  // bool  is B-Frames supported\n#define AMF_VIDEO_ENCODER_CAP_MIN_REFERENCE_FRAMES              L\"MinReferenceFrames\"       // amf_int64 minimum number of reference frames\n#define AMF_VIDEO_ENCODER_CAP_MAX_REFERENCE_FRAMES              L\"MaxReferenceFrames\"       // amf_int64 maximum number of reference frames\n#define AMF_VIDEO_ENCODER_CAP_MAX_TEMPORAL_LAYERS               L\"MaxTemporalLayers\"        // amf_int64 maximum number of temporal layers\n#define AMF_VIDEO_ENCODER_CAP_FIXED_SLICE_MODE                  L\"FixedSliceMode\"           // bool  is fixed slice mode supported\n#define AMF_VIDEO_ENCODER_CAP_NUM_OF_HW_INSTANCES               L\"NumOfHwInstances\"         // amf_int64 number of HW encoder instances\n#define AMF_VIDEO_ENCODER_CAP_COLOR_CONVERSION                  L\"ColorConversion\"          // amf_int64(AMF_ACCELERATION_TYPE) - type of supported color conversion. default AMF_ACCEL_GPU\n#define AMF_VIDEO_ENCODER_CAP_PRE_ANALYSIS                      L\"PreAnalysis\"              // amf_bool - pre analysis module is available for H264 UVE encoder, n/a for the other encoders\n#define AMF_VIDEO_ENCODER_CAP_ROI                               L\"ROIMap\"                   // amf_bool - ROI map support is available for H264 UVE encoder, n/a for the other encoders\n#define AMF_VIDEO_ENCODER_CAP_MAX_THROUGHPUT                    L\"MaxThroughput\"            // amf_int64 - MAX throughput for H264 encoder in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_CAP_REQUESTED_THROUGHPUT              L\"RequestedThroughput\"      // amf_int64 - Currently total requested throughput for H264 encoder in MB (16 x 16 pixel)\n#define AMF_VIDEO_ENCODER_CAPS_QUERY_TIMEOUT_SUPPORT            L\"QueryTimeoutSupport\"      // amf_bool - Timeout supported for QueryOutout call (Deprecated, please use AMF_VIDEO_ENCODER_CAP_QUERY_TIMEOUT_SUPPORT )\n#define AMF_VIDEO_ENCODER_CAP_QUERY_TIMEOUT_SUPPORT             L\"QueryTimeoutSupport\"      // amf_bool - Timeout supported for QueryOutout call\n\n#define AMF_VIDEO_ENCODER_CAP_SUPPORT_SLICE_OUTPUT              L\"SupportSliceOutput\"       // amf_bool - if slice output is supported\n\n#define AMF_VIDEO_ENCODER_CAP_SUPPORT_SMART_ACCESS_VIDEO        L\"EncoderSupportSmartAccessVideo\"        // amf_bool; returns true if system supports SmartAccess Video\n\n#endif //#ifndef AMF_VideoEncoderVCE_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/VideoStitch.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n//\n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n ***************************************************************************************************\n * @file  VideoStitch.h\n * @brief AMFVideoStitch interface declaration\n ***************************************************************************************************\n */\n#ifndef AMF_VideoStitch_h\n#define AMF_VideoStitch_h\n#pragma once\n\n#include \"public/include/components/Component.h\"\n\n#define AMFVideoStitch       L\"AMFVideoStitch\"                  //Component name\n\n// static properties \n#define AMF_VIDEO_STITCH_OUTPUT_FORMAT       L\"OutputFormat\"    // Values, AMF_SURFACE_BGRA or AMF_SURFACE_RGBA\n#define AMF_VIDEO_STITCH_MEMORY_TYPE         L\"MemoryType\"      // Values, only AMF_MEMORY_DX11 is supported for now.\n#define AMF_VIDEO_STITCH_OUTPUT_SIZE         L\"OutputSize\"      // AMFSize, (width, height) in pixels. default= (0,0), will be the same size as input.\n#define AMF_VIDEO_STITCH_INPUTCOUNT          L\"InputCount\"      // amf_uint64, number of camera inputs.\n\n// individual camera direction and location\n#define AMF_VIDEO_CAMERA_ANGLE_PITCH        L\"CameraPitch\"      // double, in radians, default = 0, camera pitch orientation \n#define AMF_VIDEO_CAMERA_ANGLE_YAW          L\"CameraYaw\"        // double, in radians, default = 0, camera yaw orientation \n#define AMF_VIDEO_CAMERA_ANGLE_ROLL         L\"CameraRoll\"       // double, in radians, default = 0, camera roll orientation \n\n#define AMF_VIDEO_CAMERA_OFFSET_X           L\"CameraOffsetX\"    // double, in pixels, default = 0, X offset of camera center of the lens from the center of the rig.\n#define AMF_VIDEO_CAMERA_OFFSET_Y           L\"CameraOffsetY\"    // double, in pixels, default = 0, Y offset of camera center of the lens from the center of the rig.\n#define AMF_VIDEO_CAMERA_OFFSET_Z           L\"CameraOffsetZ\"    // double, in pixels, default = 0, Z offset of camera center of the lens from the center of the rig.\n#define AMF_VIDEO_CAMERA_HFOV               L\"CameraHFOV\"       // double, in radians, default = PI, - horizontal field of view\n#define AMF_VIDEO_CAMERA_SCALE              L\"CameraScale\"      // double, default = 1, scale coeff\n\n// lens correction parameters\n#define AMF_VIDEO_STITCH_LENS_CORR_K1       L\"LensK1\"           // double, default = 0.\n#define AMF_VIDEO_STITCH_LENS_CORR_K2       L\"LensK2\"           // double, default = 0.\n#define AMF_VIDEO_STITCH_LENS_CORR_K3       L\"LensK3\"           // double, default = 0.\n#define AMF_VIDEO_STITCH_LENS_CORR_OFFX     L\"LensOffX\"         // double, default = 0.\n#define AMF_VIDEO_STITCH_LENS_CORR_OFFY     L\"LensOffY\"         // double, default = 0.\n#define AMF_VIDEO_STITCH_CROP               L\"Crop\"             //AMFRect, in pixels default = (0,0,0,0).\n\n#define AMF_VIDEO_STITCH_LENS_MODE          L\"LensMode\"         // Values, AMF_VIDEO_STITCH_LENS_CORR_MODE_ENUM, (default = AMF_VIDEO_STITCH_LENS_CORR_MODE_RADIAL)\n\n#define AMF_VIDEO_STITCH_OUTPUT_MODE        L\"OutputMode\"       // AMF_VIDEO_STITCH_OUTPUT_MODE_ENUM (default=AMF_VIDEO_STITCH_OUTPUT_MODE_PREVIEW) \n#define AMF_VIDEO_STITCH_COMBINED_SOURCE    L\"CombinedSource\"   // bool, (default=false) video sources are combined in one stream\n\n#define AMF_VIDEO_STITCH_COMPUTE_DEVICE     L\"ComputeDevice\"    // amf_int64(AMF_MEMORY_TYPE) Values, AMF_MEMORY_DX11, AMF_MEMORY_COMPUTE_FOR_DX11, AMF_MEMORY_OPENCL\n\n//for debug\n#define AMF_VIDEO_STITCH_WIRE_RENDER        L\"Wire\"             // bool (default=false) reder wireframe\n\n//view angle \n#define AMF_VIDEO_STITCH_VIEW_ROTATE_X      L\"AngleX\"           // double, in radians, default = 0 - delta from current position / automatilcally reset to 0 inside SetProperty() call\n#define AMF_VIDEO_STITCH_VIEW_ROTATE_Y      L\"AngleY\"           // double, in radians, default = 0 - delta from current position / automatilcally reset to 0 inside SetProperty() call\n#define AMF_VIDEO_STITCH_VIEW_ROTATE_Z      L\"AngleZ\"           // double, in radians, default = 0 - delta from current position / automatilcally reset to 0 inside SetProperty() call\n\n#define AMF_VIDEO_STITCH_COLOR_BALANCE      L\"ColorBalance\"     // bool (default=true) enables color balance\n\n//lens mode\nenum AMF_VIDEO_STITCH_LENS_ENUM\n{\n    AMF_VIDEO_STITCH_LENS_RECTILINEAR        = 0,   //rect linear lens\n    AMF_VIDEO_STITCH_LENS_FISHEYE_FULLFRAME  = 1,   //fisheye full frame\n    AMF_VIDEO_STITCH_LENS_FISHEYE_CIRCULAR   = 2,   //fisheye, circular\n};\n\n//Output Mode\nenum AMF_VIDEO_STITCH_OUTPUT_MODE_ENUM\n{\n    AMF_VIDEO_STITCH_OUTPUT_MODE_PREVIEW          = 0,    //preview mode\n    AMF_VIDEO_STITCH_OUTPUT_MODE_EQUIRECTANGULAR  = 1,    //equirectangular mode\n    AMF_VIDEO_STITCH_OUTPUT_MODE_CUBEMAP          = 2,    //cubemap mode\n    AMF_VIDEO_STITCH_OUTPUT_MODE_LAST = AMF_VIDEO_STITCH_OUTPUT_MODE_CUBEMAP,\n};\n\n//audio mode\nenum AMF_VIDEO_STITCH_AUDIO_MODE_ENUM\n{\n    AMF_VIDEO_STITCH_AUDIO_MODE_NONE        = 0,    //no audio\n    AMF_VIDEO_STITCH_AUDIO_MODE_VIDEO       = 1,    //using audio from video stream\n    AMF_VIDEO_STITCH_AUDIO_MODE_FILE        = 2,    //using audio from file\n    AMF_VIDEO_STITCH_AUDIO_MODE_CAPTURE     = 3,    //using audio from capture device\n    AMF_VIDEO_STITCH_AUDIO_MODE_INVALID = -1,       //invalid\n};\n\n\n#if defined(_M_AMD64)\n    #define STITCH_DLL_NAME    L\"amf-stitch-64.dll\"\n#else\n    #define STITCH_DLL_NAME    L\"amf-stitch-32.dll\"\n#endif\n\n#endif //#ifndef AMF_VideoStitch_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/components/ZCamLiveStream.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n//-------------------------------------------------------------------------------------------------\n// ZCamLive  interface declaration\n//-------------------------------------------------------------------------------------------------\n#ifndef AMF_ZCamLiveStream_h\n#define AMF_ZCamLiveStream_h\n\n#pragma once\n#define ZCAMLIVE_STREAMCOUNT             L\"StreamCount\"          // amf_int64 (default = 4),  number of streams\n#define ZCAMLIVE_VIDEO_FRAMESIZE         L\"FrameSize\"            // AMFSize   (default = AMFConstructSize(2704, 1520)), frame size\n#define ZCAMLIVE_VIDEO_FRAMERATE         L\"FrameRate\"            // AMFRate   (default = 30.0), video frame rate\n#define ZCAMLIVE_VIDEO_BIT_RATE          L\"BitRate\"              // amf_int64 (default = 3000000), video bitrate\n#define ZCAMLIVE_STREAM_ACTIVE_CAMERA    L\"ActiveCamera\"         // amf_int64 (default = -1, all the cameras), the index of the camera to capture\n#define ZCAMLIVE_STREAM_FRAMECOUNT       L\"FrameCount\"           // amf_int64 (default = 0), number of frames captured\n#define ZCAMLIVE_CODEC_ID                L\"CodecID\"              // WString (default = \"AMFVideoDecoderUVD_H264_AVC\"), UVD codec ID\n#define ZCAMLIVE_VIDEO_MODE              L\"VideoMode\"            // Enum (default = 0, 2K7P30), ZCam mode\n#define ZCAMLIVE_AUDIO_MODE              L\"AudioMode\"            // Enum (default = 0, Silent) - Audio mode\n#define ZCAMLIVE_LOWLATENCY              L\"LowLatency\"           // amf_int64 (default = 1, LowLatency), low latency flag\n#define ZCAMLIVE_IP_0                    L\"ZCamIP_00\"               // WString, IP address of the #1 stream, default \"10.98.32.1\"\n#define ZCAMLIVE_IP_1                    L\"ZCamIP_01\"             // WString, IP address of the #2 stream, default \"10.98.32.2\"\n#define ZCAMLIVE_IP_2                    L\"ZCamIP_02\"             // WString, IP address of the #3 stream, default \"10.98.32.3\"\n#define ZCAMLIVE_IP_3                    L\"ZCamIP_03\"             // WString, IP address of the #4 stream, default \"10.98.32.4\"\n\n//Camera live capture Mode\nenum CAMLIVE_MODE_ENUM\n{\n    CAMLIVE_MODE_ZCAM_1080P24 = 0,  //1920x1080, 24FPS\n    CAMLIVE_MODE_ZCAM_1080P30,      //1920x1080, 30FPS\n    CAMLIVE_MODE_ZCAM_1080P60,      //1920x1080, 60FPS\n    CAMLIVE_MODE_ZCAM_2K7P24,       //2704x1520, 24FPS\n    CAMLIVE_MODE_ZCAM_2K7P30,       //2704x1520, 24FPS\n    CAMLIVE_MODE_ZCAM_2K7P60,       //2704x1520, 24FPS\n    CAMLIVE_MODE_ZCAM_2544P24,      //3392x2544, 24FPS\n    CAMLIVE_MODE_ZCAM_2544P30,      //3392x2544, 24FPS\n    CAMLIVE_MODE_ZCAM_2544P60,      //3392x2544, 24FPS\n    CAMLIVE_MODE_THETAS,            //Ricoh TheataS\n    CAMLIVE_MODE_THETAV,            //Ricoh TheataV\n    CAMLIVE_MODE_INVALID = -1,\n};\n \nenum CAM_AUDIO_MODE_ENUM\n{\n    CAM_AUDIO_MODE_NONE = 0,   //None\n    CAM_AUDIO_MODE_SILENT,     //Silent audio\n    CAM_AUDIO_MODE_CAMERA      //Capture from camera, not supported yet\n};\n\nextern \"C\"\n{\n    AMF_RESULT AMF_CDECL_CALL AMFCreateComponentZCamLiveStream(amf::AMFContext* pContext, amf::AMFComponentEx** ppComponent);\n}\n#endif // AMF_ZCamLiveStream_h"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/AudioBuffer.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_AudioBuffer_h\n#define AMF_AudioBuffer_h\n#pragma once\n\n#include \"Data.h\"\n#if defined(_MSC_VER)\n    #pragma warning( push )\n    #pragma warning(disable : 4263)\n    #pragma warning(disable : 4264)\n#endif\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    typedef enum AMF_AUDIO_FORMAT\n    {\n        AMFAF_UNKNOWN   =-1,\n        AMFAF_U8        = 0,               // amf_uint8\n        AMFAF_S16       = 1,               // amf_int16\n        AMFAF_S32       = 2,               // amf_int32\n        AMFAF_FLT       = 3,               // amf_float\n        AMFAF_DBL       = 4,               // amf_double\n\n        AMFAF_U8P       = 5,               // amf_uint8\n        AMFAF_S16P      = 6,               // amf_int16\n        AMFAF_S32P      = 7,               // amf_int32\n        AMFAF_FLTP      = 8,               // amf_float\n        AMFAF_DBLP      = 9,               // amf_double\n        AMFAF_FIRST     = AMFAF_U8,\n        AMFAF_LAST      = AMFAF_DBLP,\n    } AMF_AUDIO_FORMAT;\n\n    typedef enum AMF_AUDIO_CHANNEL_LAYOUT\n    {\n        AMFACL_SPEAKER_FRONT_LEFT            = 0x1,\n        AMFACL_SPEAKER_FRONT_RIGHT           = 0x2,\n        AMFACL_SPEAKER_FRONT_CENTER          = 0x4,\n        AMFACL_SPEAKER_LOW_FREQUENCY         = 0x8,\n        AMFACL_SPEAKER_BACK_LEFT             = 0x10,\n        AMFACL_SPEAKER_BACK_RIGHT            = 0x20,\n        AMFACL_SPEAKER_FRONT_LEFT_OF_CENTER  = 0x40,\n        AMFACL_SPEAKER_FRONT_RIGHT_OF_CENTER = 0x80,\n        AMFACL_SPEAKER_BACK_CENTER           = 0x100,\n        AMFACL_SPEAKER_SIDE_LEFT             = 0x200,\n        AMFACL_SPEAKER_SIDE_RIGHT            = 0x400,\n        AMFACL_SPEAKER_TOP_CENTER            = 0x800,\n        AMFACL_SPEAKER_TOP_FRONT_LEFT        = 0x1000,\n        AMFACL_SPEAKER_TOP_FRONT_CENTER      = 0x2000,\n        AMFACL_SPEAKER_TOP_FRONT_RIGHT       = 0x4000,\n        AMFACL_SPEAKER_TOP_BACK_LEFT         = 0x8000,\n        AMFACL_SPEAKER_TOP_BACK_CENTER       = 0x10000,\n        AMFACL_SPEAKER_TOP_BACK_RIGHT        = 0x20000\n    } AMF_AUDIO_CHANNEL_LAYOUT;\n\n    // get the most common layout for a given number of speakers\n    inline int GetDefaultChannelLayout(int channels)\n    {\n        switch (channels)\n        {\n        case 1:\n            return (AMFACL_SPEAKER_FRONT_CENTER);\n        case 2:\n            return (AMFACL_SPEAKER_FRONT_LEFT | AMFACL_SPEAKER_FRONT_RIGHT);\n        case 4:\n            return (AMFACL_SPEAKER_FRONT_LEFT | AMFACL_SPEAKER_FRONT_RIGHT | AMFACL_SPEAKER_BACK_LEFT | AMFACL_SPEAKER_BACK_RIGHT);\n        case 6:\n            return (AMFACL_SPEAKER_FRONT_LEFT | AMFACL_SPEAKER_FRONT_RIGHT | AMFACL_SPEAKER_FRONT_CENTER | AMFACL_SPEAKER_LOW_FREQUENCY | AMFACL_SPEAKER_BACK_LEFT | AMFACL_SPEAKER_BACK_RIGHT);\n        case 8:\n            return (AMFACL_SPEAKER_FRONT_LEFT | AMFACL_SPEAKER_FRONT_RIGHT | AMFACL_SPEAKER_FRONT_CENTER | AMFACL_SPEAKER_LOW_FREQUENCY | AMFACL_SPEAKER_BACK_LEFT | AMFACL_SPEAKER_BACK_RIGHT | AMFACL_SPEAKER_FRONT_LEFT_OF_CENTER | AMFACL_SPEAKER_FRONT_RIGHT_OF_CENTER);\n        }\n\n        return AMFACL_SPEAKER_FRONT_LEFT | AMFACL_SPEAKER_FRONT_RIGHT;\n    }\n\n    //----------------------------------------------------------------------------------------------\n    // AMFAudioBufferObserver interface - callback\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMFAudioBuffer;\n    class AMF_NO_VTABLE AMFAudioBufferObserver\n    {\n    public:\n        virtual void                AMF_STD_CALL OnBufferDataRelease(AMFAudioBuffer* pBuffer) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFAudioBuffer AMFAudioBuffer;\n    typedef struct AMFAudioBufferObserver AMFAudioBufferObserver;\n    typedef struct AMFAudioBufferObserverVtbl\n    {\n        void                (AMF_STD_CALL *OnBufferDataRelease)(AMFAudioBufferObserver* pThis, AMFAudioBuffer* pBuffer);\n    } AMFAudioBufferObserverVtbl;\n\n    struct AMFAudioBufferObserver\n    {\n        const AMFAudioBufferObserverVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AudioBuffer interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFAudioBuffer : public AMFData\n    {\n    public:\n        AMF_DECLARE_IID(0x2212ff8, 0x6107, 0x430b, 0xb6, 0x3c, 0xc7, 0xe5, 0x40, 0xe5, 0xf8, 0xeb)\n\n        virtual amf_int32           AMF_STD_CALL GetSampleCount() = 0;\n        virtual amf_int32           AMF_STD_CALL GetSampleRate() = 0;\n        virtual amf_int32           AMF_STD_CALL GetChannelCount() = 0;\n        virtual AMF_AUDIO_FORMAT    AMF_STD_CALL GetSampleFormat() = 0;\n        virtual amf_int32           AMF_STD_CALL GetSampleSize() = 0;\n        virtual amf_uint32          AMF_STD_CALL GetChannelLayout() = 0;\n        virtual void*               AMF_STD_CALL GetNative() = 0;\n        virtual amf_size            AMF_STD_CALL GetSize() = 0;\n\n        // Observer management\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Woverloaded-virtual\"\n#endif\n        virtual void                AMF_STD_CALL AddObserver(AMFAudioBufferObserver* pObserver) = 0;\n        virtual void                AMF_STD_CALL RemoveObserver(AMFAudioBufferObserver* pObserver) = 0;\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFAudioBuffer> AMFAudioBufferPtr;\n    //----------------------------------------------------------------------------------------------\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFAudioBuffer, 0x2212ff8, 0x6107, 0x430b, 0xb6, 0x3c, 0xc7, 0xe5, 0x40, 0xe5, 0xf8, 0xeb)\n\n    typedef struct AMFAudioBufferVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFAudioBuffer* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFAudioBuffer* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFAudioBuffer* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFAudioBuffer* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFAudioBuffer* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFAudioBuffer* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFAudioBuffer* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFAudioBuffer* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFAudioBuffer* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFAudioBuffer* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFAudioBuffer* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFAudioBuffer* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFAudioBuffer* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFData interface\n\n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryType)(AMFAudioBuffer* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *Duplicate)(AMFAudioBuffer* pThis, AMF_MEMORY_TYPE type, AMFData** ppData);\n        AMF_RESULT          (AMF_STD_CALL *Convert)(AMFAudioBuffer* pThis, AMF_MEMORY_TYPE type); // optimal interop if possilble. Copy through host memory if needed\n        AMF_RESULT          (AMF_STD_CALL *Interop)(AMFAudioBuffer* pThis, AMF_MEMORY_TYPE type); // only optimal interop if possilble. No copy through host memory for GPU objects\n\n        AMF_DATA_TYPE       (AMF_STD_CALL *GetDataType)(AMFAudioBuffer* pThis);\n\n        amf_bool            (AMF_STD_CALL *IsReusable)(AMFAudioBuffer* pThis);\n\n        void                (AMF_STD_CALL *SetPts)(AMFAudioBuffer* pThis, amf_pts pts);\n        amf_pts             (AMF_STD_CALL *GetPts)(AMFAudioBuffer* pThis);\n        void                (AMF_STD_CALL *SetDuration)(AMFAudioBuffer* pThis, amf_pts duration);\n        amf_pts             (AMF_STD_CALL *GetDuration)(AMFAudioBuffer* pThis);\n\n        // AMFAudioBuffer interface\n\n        amf_int32           (AMF_STD_CALL *GetSampleCount)(AMFAudioBuffer* pThis);\n        amf_int32           (AMF_STD_CALL *GetSampleRate)(AMFAudioBuffer* pThis);\n        amf_int32           (AMF_STD_CALL *GetChannelCount)(AMFAudioBuffer* pThis);\n        AMF_AUDIO_FORMAT    (AMF_STD_CALL *GetSampleFormat)(AMFAudioBuffer* pThis);\n        amf_int32           (AMF_STD_CALL *GetSampleSize)(AMFAudioBuffer* pThis);\n        amf_uint32          (AMF_STD_CALL *GetChannelLayout)(AMFAudioBuffer* pThis);\n        void*               (AMF_STD_CALL *GetNative)(AMFAudioBuffer* pThis);\n        amf_size            (AMF_STD_CALL *GetSize)(AMFAudioBuffer* pThis);\n\n        // Observer management\n        void                (AMF_STD_CALL *AddObserver_AudioBuffer)(AMFAudioBuffer* pThis, AMFAudioBufferObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver_AudioBuffer)(AMFAudioBuffer* pThis, AMFAudioBufferObserver* pObserver);\n\n    } AMFAudioBufferVtbl;\n\n    struct AMFAudioBuffer\n    {\n        const AMFAudioBufferVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n} // namespace\n#endif\n#if defined(_MSC_VER)\n    #pragma warning( pop )\n#endif\n#endif //#ifndef AMF_AudioBuffer_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Buffer.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Buffer_h\n#define AMF_Buffer_h\n#pragma once\n\n#include \"Data.h\"\n\n#if defined(_MSC_VER)\n    #pragma warning( push )\n    #pragma warning(disable : 4263)\n    #pragma warning(disable : 4264)\n#endif\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n\n    //----------------------------------------------------------------------------------------------\n    // AMF_BUFFER_USAGE translates to D3D11_BIND_FLAG or VkBufferUsageFlagBits\n    // bit mask\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_BUFFER_USAGE_BITS\n    {                                                      // D3D11                         D3D12                                       Vulkan\n        AMF_BUFFER_USAGE_DEFAULT           = 0x80000000,   // D3D11_USAGE_STAGING,                                                      VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n        AMF_BUFFER_USAGE_NONE              = 0x00000000,   // 0                  ,          D3D12_RESOURCE_FLAG_NONE,                   0\n        AMF_BUFFER_USAGE_CONSTANT          = 0x00000001,   // D3D11_BIND_CONSTANT_BUFFER,   \t\t\t\t\t\t\t\t\t\t\tVK_BUFFER_USAGE_UNIFORM_BUFFER_BIT\n        AMF_BUFFER_USAGE_SHADER_RESOURCE   = 0x00000002,   // D3D11_BIND_SHADER_RESOURCE,   D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT\n        AMF_BUFFER_USAGE_UNORDERED_ACCESS  = 0x00000004,   // D3D11_BIND_UNORDERED_ACCESS,  D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT\n        AMF_BUFFER_USAGE_TRANSFER_SRC      = 0x00000008,   //                               \t\t\t\t\t\t\t\t            VK_BUFFER_USAGE_TRANSFER_SRC_BIT\n        AMF_BUFFER_USAGE_TRANSFER_DST      = 0x00000010,   //                               \t\t\t\t\t\t\t\t            VK_BUFFER_USAGE_TRANSFER_DST_BIT\n        AMF_BUFFER_USAGE_NOSYNC            = 0x00000020,   //  \t\t\t\t\t\t\t    no fence (AMFFenceGUID) created\t            no semaphore (AMFVulkanSync::hSemaphore) created\n        AMF_BUFFER_USAGE_DECODER_SRC       = 0x00000040,   //                               \t\t\t\t\t\t\t\t            VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR\n    } AMF_BUFFER_USAGE_BITS;\n    typedef amf_flags AMF_BUFFER_USAGE;\n    //----------------------------------------------------------------------------------------------\n\n\n    //----------------------------------------------------------------------------------------------\n    // AMFBufferObserver interface - callback\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMFBuffer;\n    class AMF_NO_VTABLE AMFBufferObserver\n    {\n    public:\n        virtual void                AMF_STD_CALL OnBufferDataRelease(AMFBuffer* pBuffer) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFBuffer AMFBuffer;\n    typedef struct AMFBufferObserver AMFBufferObserver;\n\n    typedef struct AMFBufferObserverVtbl\n    {\n        void                (AMF_STD_CALL *OnBufferDataRelease)(AMFBufferObserver* pThis, AMFBuffer* pBuffer);\n    } AMFBufferObserverVtbl;\n\n    struct AMFBufferObserver\n    {\n        const AMFBufferObserverVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFBuffer interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFBuffer : public AMFData\n    {\n    public:\n        AMF_DECLARE_IID(0xb04b7248, 0xb6f0, 0x4321, 0xb6, 0x91, 0xba, 0xa4, 0x74, 0xf, 0x9f, 0xcb)\n\n        virtual AMF_RESULT          AMF_STD_CALL SetSize(amf_size newSize) = 0;\n        virtual amf_size            AMF_STD_CALL GetSize() = 0;\n        virtual void*               AMF_STD_CALL GetNative() = 0;\n\n        // Observer management\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Woverloaded-virtual\"\n#endif\n        virtual void                AMF_STD_CALL AddObserver(AMFBufferObserver* pObserver) = 0;\n        virtual void                AMF_STD_CALL RemoveObserver(AMFBufferObserver* pObserver) = 0;\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFBuffer> AMFBufferPtr;\n    //----------------------------------------------------------------------------------------------\n\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFBuffer, 0xb04b7248, 0xb6f0, 0x4321, 0xb6, 0x91, 0xba, 0xa4, 0x74, 0xf, 0x9f, 0xcb)\n\n    typedef struct AMFBufferVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFBuffer* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFBuffer* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFBuffer* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFBuffer* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFBuffer* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFBuffer* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFBuffer* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFBuffer* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFBuffer* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFBuffer* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFBuffer* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFBuffer* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFBuffer* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFData interface\n\n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryType)(AMFBuffer* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *Duplicate)(AMFBuffer* pThis, AMF_MEMORY_TYPE type, AMFData** ppData);\n        AMF_RESULT          (AMF_STD_CALL *Convert)(AMFBuffer* pThis, AMF_MEMORY_TYPE type); // optimal interop if possilble. Copy through host memory if needed\n        AMF_RESULT          (AMF_STD_CALL *Interop)(AMFBuffer* pThis, AMF_MEMORY_TYPE type); // only optimal interop if possilble. No copy through host memory for GPU objects\n\n        AMF_DATA_TYPE       (AMF_STD_CALL *GetDataType)(AMFBuffer* pThis);\n\n        amf_bool            (AMF_STD_CALL *IsReusable)(AMFBuffer* pThis);\n\n        void                (AMF_STD_CALL *SetPts)(AMFBuffer* pThis, amf_pts pts);\n        amf_pts             (AMF_STD_CALL *GetPts)(AMFBuffer* pThis);\n        void                (AMF_STD_CALL *SetDuration)(AMFBuffer* pThis, amf_pts duration);\n        amf_pts             (AMF_STD_CALL *GetDuration)(AMFBuffer* pThis);\n\n        // AMFBuffer interface\n\n        AMF_RESULT          (AMF_STD_CALL *SetSize)(AMFBuffer* pThis, amf_size newSize);\n        amf_size            (AMF_STD_CALL *GetSize)(AMFBuffer* pThis);\n        void*               (AMF_STD_CALL *GetNative)(AMFBuffer* pThis);\n\n        // Observer management\n        void                (AMF_STD_CALL *AddObserver_Buffer)(AMFBuffer* pThis, AMFBufferObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver_Buffer)(AMFBuffer* pThis, AMFBufferObserver* pObserver);\n\n    } AMFBufferVtbl;\n\n    struct AMFBuffer\n    {\n        const AMFBufferVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n} // namespace\n#endif\n#if defined(_MSC_VER)\n    #pragma warning( pop )\n#endif\n#endif //#ifndef AMF_Buffer_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Compute.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n ***************************************************************************************************\n * @file  Compute.h\n * @brief AMFCompute interface declaration\n ***************************************************************************************************\n */\n#ifndef AMF_Compute_h\n#define AMF_Compute_h\n#pragma once\n\n#include \"Buffer.h\"\n#include \"Surface.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    typedef amf_uint64 AMF_KERNEL_ID;\n\n    //----------------------------------------------------------------------------------------------\n    // enumerations for plane conversion\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_CHANNEL_ORDER\n    {\n        AMF_CHANNEL_ORDER_INVALID       = 0,\n        AMF_CHANNEL_ORDER_R             = 1,\n        AMF_CHANNEL_ORDER_RG            = 2,\n        AMF_CHANNEL_ORDER_BGRA          = 3,\n        AMF_CHANNEL_ORDER_RGBA          = 4,\n        AMF_CHANNEL_ORDER_ARGB          = 5,\n        AMF_CHANNEL_ORDER_YUY2          = 6,\n    } AMF_CHANNEL_ORDER;\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_CHANNEL_TYPE\n    {\n        AMF_CHANNEL_INVALID             = 0,\n        AMF_CHANNEL_UNSIGNED_INT8       = 1,\n        AMF_CHANNEL_UNSIGNED_INT32      = 2,\n        AMF_CHANNEL_UNORM_INT8          = 3,\n        AMF_CHANNEL_UNORM_INT16         = 4,\n        AMF_CHANNEL_SNORM_INT16         = 5,\n        AMF_CHANNEL_FLOAT               = 6,\n        AMF_CHANNEL_FLOAT16             = 7,\n        AMF_CHANNEL_UNSIGNED_INT16      = 8,\n        AMF_CHANNEL_UNORM_INT_101010    = 9,\n} AMF_CHANNEL_TYPE;\n    //----------------------------------------------------------------------------------------------\n#define AMF_STRUCTURED_BUFFER_FORMAT        L\"StructuredBufferFormat\"                                                             // amf_int64(AMF_CHANNEL_TYPE), default - AMF_CHANNEL_UNSIGNED_INT32; to be set on AMFBuffer objects\n#if defined(_WIN32)\n    AMF_WEAK GUID  AMFStructuredBufferFormatGUID = { 0x90c5d674, 0xe90, 0x4181, {0xbd, 0xef, 0x26, 0x13, 0xc1, 0xdf, 0xa3, 0xbd} }; // UINT(DXGI_FORMAT), default - DXGI_FORMAT_R32_UINT; to be set on ID3D11Buffer or ID3D11Texture2D objects when used natively\n#endif\n    //----------------------------------------------------------------------------------------------\n    // enumeration argument type\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_ARGUMENT_ACCESS_TYPE\n    {\n        AMF_ARGUMENT_ACCESS_READ        = 0,\n        AMF_ARGUMENT_ACCESS_WRITE       = 1,\n        AMF_ARGUMENT_ACCESS_READWRITE   = 2,\n        AMF_ARGUMENT_ACCESS_READWRITE_MASK  = 0xFFFF,\n        //Sampler parameters\n        AMF_ARGUMENT_SAMPLER_LINEAR        = 0x10000000, \n        AMF_ARGUMENT_SAMPLER_NORM_COORD    = 0x20000000, \n        AMF_ARGUMENT_SAMPLER_POINT         = 0x40000000,\n        AMF_ARGUMENT_SAMPLER_MASK          = 0xFFFF0000,\n    } AMF_ARGUMENT_ACCESS_TYPE;\n    //----------------------------------------------------------------------------------------------\n    // AMFComputeKernel interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComputeKernel : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x94815701, 0x6c84, 0x4ba6, 0xa9, 0xfe, 0xe9, 0xad, 0x40, 0xf8, 0x8, 0x8)\n\n        virtual void*               AMF_STD_CALL GetNative() = 0;\n        virtual const wchar_t*      AMF_STD_CALL GetIDName() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgPlaneNative(amf_size index, void* pPlane, AMF_ARGUMENT_ACCESS_TYPE eAccess) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgBufferNative(amf_size index, void* pBuffer, AMF_ARGUMENT_ACCESS_TYPE eAccess) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL SetArgPlane(amf_size index, AMFPlane* pPlane, AMF_ARGUMENT_ACCESS_TYPE eAccess) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgBuffer(amf_size index, AMFBuffer* pBuffer, AMF_ARGUMENT_ACCESS_TYPE eAccess) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL SetArgInt32(amf_size index, amf_int32 data) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgInt64(amf_size index, amf_int64 data) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgFloat(amf_size index, amf_float data) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetArgBlob(amf_size index, amf_size dataSize, const void* pData) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL GetCompileWorkgroupSize(amf_size workgroupSize[3]) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL Enqueue(amf_size dimension, amf_size globalOffset[3], amf_size globalSize[3], amf_size localSize[3]) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFComputeKernel> AMFComputeKernelPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComputeKernel, 0x94815701, 0x6c84, 0x4ba6, 0xa9, 0xfe, 0xe9, 0xad, 0x40, 0xf8, 0x8, 0x8)\n    typedef struct AMFComputeKernel AMFComputeKernel;\n\n    typedef struct AMFComputeKernelVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComputeKernel* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComputeKernel* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComputeKernel* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFComputeKernel interface \n\n    } AMFComputeKernelVtbl;\n\n    struct AMFComputeKernel\n    {\n        const AMFComputeKernelVtbl *pVtbl;\n    };\n\n#endif //#if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFComputeSyncPoint interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComputeSyncPoint : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x66f33fe6, 0xaae, 0x4e65, 0xba, 0x3, 0xea, 0x8b, 0xa3, 0x60, 0x11, 0x2)\n\n        virtual amf_bool            AMF_STD_CALL IsCompleted() = 0;\n        virtual void                AMF_STD_CALL Wait() = 0;\n    };\n    typedef AMFInterfacePtr_T<AMFComputeSyncPoint> AMFComputeSyncPointPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComputeSyncPoint, 0x66f33fe6, 0xaae, 0x4e65, 0xba, 0x3, 0xea, 0x8b, 0xa3, 0x60, 0x11, 0x2)\n    typedef struct AMFComputeSyncPoint AMFComputeSyncPoint;\n\n    typedef struct AMFComputeSyncPointVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComputeSyncPoint* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComputeSyncPoint* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComputeSyncPoint* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFComputeSyncPoint interface \n        amf_bool            (AMF_STD_CALL *IsCompleted)(AMFComputeSyncPoint* pThis);\n        void                (AMF_STD_CALL *Wait)(AMFComputeSyncPoint* pThis);\n\n    } AMFComputeSyncPointVtbl;\n\n    struct AMFComputeSyncPoint\n    {\n        const AMFComputeSyncPointVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFCompute interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFCompute : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x3846233a, 0x3f43, 0x443f, 0x8a, 0x45, 0x75, 0x22, 0x11, 0xa9, 0xfb, 0xd5)\n\n        virtual AMF_MEMORY_TYPE     AMF_STD_CALL GetMemoryType() = 0;\n\n        virtual void*               AMF_STD_CALL GetNativeContext() = 0;\n        virtual void*               AMF_STD_CALL GetNativeDeviceID() = 0;\n        virtual void*               AMF_STD_CALL GetNativeCommandQueue() = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL GetKernel(AMF_KERNEL_ID kernelID, AMFComputeKernel** kernel) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL PutSyncPoint(AMFComputeSyncPoint** ppSyncPoint) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL FinishQueue() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL FlushQueue() = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL FillPlane(AMFPlane *pPlane, const amf_size origin[3], const amf_size region[3], const void* pColor) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL FillBuffer(AMFBuffer* pBuffer, amf_size dstOffset, amf_size dstSize, const void* pSourcePattern, amf_size patternSize) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL ConvertPlaneToBuffer(AMFPlane *pSrcPlane, AMFBuffer** ppDstBuffer) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL CopyBuffer(AMFBuffer* pSrcBuffer, amf_size srcOffset, amf_size size, AMFBuffer* pDstBuffer, amf_size dstOffset) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CopyPlane(AMFPlane *pSrcPlane, const amf_size srcOrigin[3], const amf_size region[3], AMFPlane *pDstPlane, const amf_size dstOrigin[3]) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL CopyBufferToHost(AMFBuffer* pSrcBuffer, amf_size srcOffset, amf_size size, void* pDest, amf_bool blocking) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CopyBufferFromHost(const void* pSource, amf_size size, AMFBuffer* pDstBuffer, amf_size dstOffsetInBytes, amf_bool blocking) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL CopyPlaneToHost(AMFPlane *pSrcPlane, const amf_size origin[3], const amf_size region[3], void* pDest, amf_size dstPitch, amf_bool blocking) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CopyPlaneFromHost(void* pSource, const amf_size origin[3], const amf_size region[3], amf_size srcPitch, AMFPlane *pDstPlane, amf_bool blocking) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL ConvertPlaneToPlane(AMFPlane* pSrcPlane, AMFPlane** ppDstPlane, AMF_CHANNEL_ORDER order, AMF_CHANNEL_TYPE type) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFCompute> AMFComputePtr;\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFCompute, 0x3846233a, 0x3f43, 0x443f, 0x8a, 0x45, 0x75, 0x22, 0x11, 0xa9, 0xfb, 0xd5)\n    typedef struct AMFCompute AMFCompute;\n\n    typedef struct AMFComputeVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFCompute* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFCompute* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFCompute* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFCompute interface \n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryType)(AMFCompute* pThis);\n        void*               (AMF_STD_CALL *GetNativeContext)(AMFCompute* pThis);\n        void*               (AMF_STD_CALL *GetNativeDeviceID)(AMFCompute* pThis);\n        void*               (AMF_STD_CALL *GetNativeCommandQueue)(AMFCompute* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetKernel)(AMFCompute* pThis, AMF_KERNEL_ID kernelID, AMFComputeKernel** kernel);\n        AMF_RESULT          (AMF_STD_CALL *PutSyncPoint)(AMFCompute* pThis, AMFComputeSyncPoint** ppSyncPoint);\n        AMF_RESULT          (AMF_STD_CALL *FinishQueue)(AMFCompute* pThis);\n        AMF_RESULT          (AMF_STD_CALL *FlushQueue)(AMFCompute* pThis);\n        AMF_RESULT          (AMF_STD_CALL *FillPlane)(AMFCompute* pThis, AMFPlane *pPlane, const amf_size origin[3], const amf_size region[3], const void* pColor);\n        AMF_RESULT          (AMF_STD_CALL *FillBuffer)(AMFCompute* pThis, AMFBuffer* pBuffer, amf_size dstOffset, amf_size dstSize, const void* pSourcePattern, amf_size patternSize);\n        AMF_RESULT          (AMF_STD_CALL *ConvertPlaneToBuffer)(AMFCompute* pThis, AMFPlane *pSrcPlane, AMFBuffer** ppDstBuffer);\n        AMF_RESULT          (AMF_STD_CALL *CopyBuffer)(AMFCompute* pThis, AMFBuffer* pSrcBuffer, amf_size srcOffset, amf_size size, AMFBuffer* pDstBuffer, amf_size dstOffset);\n        AMF_RESULT          (AMF_STD_CALL *CopyPlane)(AMFCompute* pThis, AMFPlane *pSrcPlane, const amf_size srcOrigin[3], const amf_size region[3], AMFPlane *pDstPlane, const amf_size dstOrigin[3]);\n        AMF_RESULT          (AMF_STD_CALL *CopyBufferToHost)(AMFCompute* pThis, AMFBuffer* pSrcBuffer, amf_size srcOffset, amf_size size, void* pDest, amf_bool blocking);\n        AMF_RESULT          (AMF_STD_CALL *CopyBufferFromHost)(AMFCompute* pThis, const void* pSource, amf_size size, AMFBuffer* pDstBuffer, amf_size dstOffsetInBytes, amf_bool blocking);\n        AMF_RESULT          (AMF_STD_CALL *CopyPlaneToHost)(AMFCompute* pThis, AMFPlane *pSrcPlane, const amf_size origin[3], const amf_size region[3], void* pDest, amf_size dstPitch, amf_bool blocking);\n        AMF_RESULT          (AMF_STD_CALL *CopyPlaneFromHost)(AMFCompute* pThis, void* pSource, const amf_size origin[3], const amf_size region[3], amf_size srcPitch, AMFPlane *pDstPlane, amf_bool blocking);\n        AMF_RESULT          (AMF_STD_CALL *ConvertPlaneToPlane)(AMFCompute* pThis, AMFPlane* pSrcPlane, AMFPlane** ppDstPlane, AMF_CHANNEL_ORDER order, AMF_CHANNEL_TYPE type);\n    } AMFComputeVtbl;\n\n    struct AMFCompute\n    {\n        const AMFComputeVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFPrograms interface - singleton\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFPrograms\n    {\n    public:\n        virtual AMF_RESULT          AMF_STD_CALL RegisterKernelSourceFile(AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, const wchar_t* filepath, const char* options) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL RegisterKernelSource(AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL RegisterKernelBinary(AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL RegisterKernelSource1(AMF_MEMORY_TYPE eMemoryType, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL RegisterKernelBinary1(AMF_MEMORY_TYPE eMemoryType, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFPrograms AMFPrograms;\n    typedef struct AMFProgramsVtbl\n    {\n        AMF_RESULT          (AMF_STD_CALL *RegisterKernelSourceFile)(AMFPrograms* pThis, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, const wchar_t* filepath, const char* options);\n        AMF_RESULT          (AMF_STD_CALL *RegisterKernelSource)(AMFPrograms* pThis, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options);\n        AMF_RESULT          (AMF_STD_CALL *RegisterKernelBinary)(AMFPrograms* pThis, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options);\n        AMF_RESULT          (AMF_STD_CALL *RegisterKernelSource1)(AMFPrograms* pThis, AMF_MEMORY_TYPE eMemoryType, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options);\n        AMF_RESULT          (AMF_STD_CALL *RegisterKernelBinary1)(AMFPrograms* pThis, AMF_MEMORY_TYPE eMemoryType, AMF_KERNEL_ID* pKernelID, const wchar_t* kernelid_name, const char* kernelName, amf_size dataSize, const amf_uint8* data, const char* options);\n    } AMFProgramsVtbl;\n\n    struct AMFPrograms\n    {\n        const AMFProgramsVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n\n#if defined(__cplusplus)\n} // namespace amf\n#endif\n\n#endif // AMF_Compute_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/ComputeFactory.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_ComputeFactory_h\n#define AMF_ComputeFactory_h\n#pragma once\n\n#include \"Compute.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n// compute device audio capabilities accessed via GetProperties() from AMFComputeDevice\n#define AMF_DEVICE_NAME                         L\"DeviceName\"                       // char*, string, device name\n#define AMF_DRIVER_VERSION_NAME                 L\"DriverVersion\"                       // char*, string, driver version\n#define AMF_AUDIO_CONVOLUTION_MAX_STREAMS       L\"ConvolutionMaxStreams\"            // amf_int64, maximum number of audio streams supported in realtime\n#define AMF_AUDIO_CONVOLUTION_LENGTH            L\"ConvolutionLength\"                // amf_int64, length of convolution in samples\n#define AMF_AUDIO_CONVOLUTION_BUFFER_SIZE       L\"ConvolutionBufferSize\"            // amf_int64, buffer size in samples\n#define AMF_AUDIO_CONVOLUTION_SAMPLE_RATE       L\"ConvolutionSampleRate\"            // amf_int64, sample rate\n\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComputeDevice  : public AMFPropertyStorage\n    {\n    public:\n        AMF_DECLARE_IID(0xb79d7cf6, 0x2c5c, 0x4deb, 0xb8, 0x96, 0xa2, 0x9e, 0xbe, 0xa6, 0xe3, 0x97)\n\n        virtual void*               AMF_STD_CALL GetNativePlatform() = 0;\n        virtual void*               AMF_STD_CALL GetNativeDeviceID() = 0;\n        virtual void*               AMF_STD_CALL GetNativeContext() = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL CreateCompute(void *reserved, AMFCompute **ppCompute) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateComputeEx(void* pCommandQueue, AMFCompute **ppCompute) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFComputeDevice> AMFComputeDevicePtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComputeDevice, 0xb79d7cf6, 0x2c5c, 0x4deb, 0xb8, 0x96, 0xa2, 0x9e, 0xbe, 0xa6, 0xe3, 0x97)\n    typedef struct AMFComputeDevice AMFComputeDevice;\n\n    typedef struct AMFComputeDeviceVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComputeDevice* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComputeDevice* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComputeDevice* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFComputeDevice* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFComputeDevice* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFComputeDevice* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFComputeDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFComputeDevice* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFComputeDevice* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFComputeDevice* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFComputeDevice* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFComputeDevice* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFComputeDevice* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFComputeDevice interface\n       void*               (AMF_STD_CALL *GetNativePlatform)(AMFComputeDevice* pThis);\n       void*               (AMF_STD_CALL *GetNativeDeviceID)(AMFComputeDevice* pThis);\n       void*               (AMF_STD_CALL *GetNativeContext)(AMFComputeDevice* pThis);\n\n       AMF_RESULT          (AMF_STD_CALL *CreateCompute)(AMFComputeDevice* pThis, void *reserved, AMFCompute **ppCompute);\n       AMF_RESULT          (AMF_STD_CALL *CreateComputeEx)(AMFComputeDevice* pThis, void* pCommandQueue, AMFCompute **ppCompute);\n\n    } AMFComputeDeviceVtbl;\n\n    struct AMFComputeDevice\n    {\n        const AMFComputeDeviceVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFComputeFactory : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xe3c24bd7, 0x2d83, 0x416c, 0x8c, 0x4e, 0xfd, 0x13, 0xca, 0x86, 0xf4, 0xd0)\n\n        virtual amf_int32           AMF_STD_CALL GetDeviceCount() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetDeviceAt(amf_int32 index, AMFComputeDevice **ppDevice) = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFComputeFactory> AMFComputeFactoryPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFComputeFactory, 0xe3c24bd7, 0x2d83, 0x416c, 0x8c, 0x4e, 0xfd, 0x13, 0xca, 0x86, 0xf4, 0xd0)\n    typedef struct AMFComputeFactory AMFComputeFactory;\n\n    typedef struct AMFComputeFactoryVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFComputeFactory* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFComputeFactory* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFComputeFactory* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFComputeFactory interface\n        amf_int32           (AMF_STD_CALL *GetDeviceCount)(AMFComputeFactory* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetDeviceAt)(AMFComputeFactory* pThis, amf_int32 index, AMFComputeDevice **ppDevice);\n    } AMFComputeFactoryVtbl;\n\n    struct AMFComputeFactory\n    {\n        const AMFComputeFactoryVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n} // namespace amf\n#endif\n\n#endif // AMF_ComputeFactory_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Context.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Context_h\n#define AMF_Context_h\n#pragma once\n\n#include \"Buffer.h\"\n#include \"AudioBuffer.h\"\n#include \"Surface.h\"\n#include \"Compute.h\"\n#include \"ComputeFactory.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // AMFContext interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFContext : public AMFPropertyStorage\n    {\n    public:\n        AMF_DECLARE_IID(0xa76a13f0, 0xd80e, 0x4fcc, 0xb5, 0x8, 0x65, 0xd0, 0xb5, 0x2e, 0xd9, 0xee)\n        \n        // Cleanup\n        virtual AMF_RESULT          AMF_STD_CALL Terminate() = 0;\n\n        // DX9\n        virtual AMF_RESULT          AMF_STD_CALL InitDX9(void* pDX9Device) = 0;\n        virtual void*               AMF_STD_CALL GetDX9Device(AMF_DX_VERSION dxVersionRequired = AMF_DX9) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockDX9() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockDX9() = 0;\n        class AMFDX9Locker;\n\n        // DX11\n        virtual AMF_RESULT          AMF_STD_CALL InitDX11(void* pDX11Device, AMF_DX_VERSION dxVersionRequired = AMF_DX11_0) = 0;\n        virtual void*               AMF_STD_CALL GetDX11Device(AMF_DX_VERSION dxVersionRequired = AMF_DX11_0) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockDX11() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockDX11() = 0;\n        class AMFDX11Locker;\n\n        // OpenCL\n        virtual AMF_RESULT          AMF_STD_CALL InitOpenCL(void* pCommandQueue = NULL) = 0;\n        virtual void*               AMF_STD_CALL GetOpenCLContext() = 0;\n        virtual void*               AMF_STD_CALL GetOpenCLCommandQueue() = 0;\n        virtual void*               AMF_STD_CALL GetOpenCLDeviceID() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetOpenCLComputeFactory(AMFComputeFactory **ppFactory) = 0; // advanced compute - multiple queries\n        virtual AMF_RESULT          AMF_STD_CALL InitOpenCLEx(AMFComputeDevice *pDevice) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockOpenCL() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockOpenCL() = 0;\n        class AMFOpenCLLocker;\n\n        // OpenGL\n        virtual AMF_RESULT          AMF_STD_CALL InitOpenGL(amf_handle hOpenGLContext, amf_handle hWindow, amf_handle hDC) = 0;\n        virtual amf_handle          AMF_STD_CALL GetOpenGLContext() = 0;\n        virtual amf_handle          AMF_STD_CALL GetOpenGLDrawable() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockOpenGL() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockOpenGL() = 0;\n        class AMFOpenGLLocker;\n\n        // XV - Linux\n        virtual AMF_RESULT          AMF_STD_CALL InitXV(void* pXVDevice) = 0;\n        virtual void*               AMF_STD_CALL GetXVDevice() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockXV() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockXV() = 0;\n        class AMFXVLocker;\n\n        // Gralloc - Android\n        virtual AMF_RESULT          AMF_STD_CALL InitGralloc(void* pGrallocDevice) = 0;\n        virtual void*               AMF_STD_CALL GetGrallocDevice() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockGralloc() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockGralloc() = 0;\n        class AMFGrallocLocker;\n\n        // Allocation\n        virtual AMF_RESULT          AMF_STD_CALL AllocBuffer(AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL AllocSurface(AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMFSurface** ppSurface) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL AllocAudioBuffer(AMF_MEMORY_TYPE type, AMF_AUDIO_FORMAT format, amf_int32 samples, amf_int32 sampleRate, amf_int32 channels, \n                                                    AMFAudioBuffer** ppAudioBuffer) = 0;\n\n        // Wrap existing objects\n        virtual AMF_RESULT          AMF_STD_CALL CreateBufferFromHostNative(void* pHostBuffer, amf_size size, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromHostNative(AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, void* pData, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromDX9Native(void* pDX9Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromDX11Native(void* pDX11Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromOpenGLNative(AMF_SURFACE_FORMAT format, amf_handle hGLTextureID, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromGrallocNative(amf_handle hGrallocSurface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromOpenCLNative(AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, void** pClPlanes, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateBufferFromOpenCLNative(void* pCLBuffer, amf_size size, AMFBuffer** ppBuffer) = 0;\n\n        // Access to AMFCompute interface - AMF_MEMORY_OPENCL, AMF_MEMORY_COMPUTE_FOR_DX9, AMF_MEMORY_COMPUTE_FOR_DX11 are currently supported\n        virtual AMF_RESULT          AMF_STD_CALL GetCompute(AMF_MEMORY_TYPE eMemType, AMFCompute** ppCompute) = 0;\n    };\n\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFContext> AMFContextPtr;\n\n    //----------------------------------------------------------------------------------------------\n    // AMFContext1 interface\n    //----------------------------------------------------------------------------------------------\n\n    class AMF_NO_VTABLE AMFContext1 : public AMFContext\n    {\n    public:\n        AMF_DECLARE_IID(0xd9e9f868, 0x6220, 0x44c6, 0xa2, 0x2f, 0x7c, 0xd6, 0xda, 0xc6, 0x86, 0x46)\n\n        virtual AMF_RESULT          AMF_STD_CALL CreateBufferFromDX11Native(void* pHostBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL AllocBufferEx(AMF_MEMORY_TYPE type, amf_size size, AMF_BUFFER_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFBuffer** ppBuffer) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL AllocSurfaceEx(AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMF_SURFACE_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFSurface** ppSurface) = 0;\n\n        // Vulkan - Windows, Linux\n        virtual AMF_RESULT          AMF_STD_CALL InitVulkan(void* pVulkanDevice) = 0;\n        virtual void*               AMF_STD_CALL GetVulkanDevice() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockVulkan() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockVulkan() = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromVulkanNative(void* pVulkanImage, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateBufferFromVulkanNative(void* pVulkanBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetVulkanDeviceExtensions(amf_size *pCount, const char **ppExtensions) = 0;\n\n        \n        class AMFVulkanLocker;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFContext1> AMFContext1Ptr;\n\n    class AMF_NO_VTABLE AMFContext2 : public AMFContext1\n    {\n    public:\n        AMF_DECLARE_IID(0x726241d3, 0xbd46, 0x4e90, 0x99, 0x68, 0x93, 0xe0, 0x7e, 0xa2, 0x98, 0x4d)\n\n        // DX12\n        virtual AMF_RESULT          AMF_STD_CALL InitDX12(void* pDX11Device, AMF_DX_VERSION dxVersionRequired = AMF_DX12) = 0;\n        virtual void*               AMF_STD_CALL GetDX12Device(AMF_DX_VERSION dxVersionRequired = AMF_DX12) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL LockDX12() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL UnlockDX12() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateSurfaceFromDX12Native(void* pResourceTexture, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateBufferFromDX12Native(void* pResourceBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver) = 0;\n\n        class AMFDX12Locker;\n    };\n    typedef AMFInterfacePtr_T<AMFContext2> AMFContext2Ptr;\n#else\n    typedef struct AMFContext AMFContext;\n    AMF_DECLARE_IID(AMFContext, 0xa76a13f0, 0xd80e, 0x4fcc, 0xb5, 0x8, 0x65, 0xd0, 0xb5, 0x2e, 0xd9, 0xee)\n\n    typedef struct AMFContextVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFContext* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFContext* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFContext* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n        \n        // AMFInterface AMFPropertyStorage\n\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFContext* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFContext* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFContext* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFContext* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFContext* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFContext* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFContext* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFContext* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFContext interface\n       \n        // Cleanup\n        AMF_RESULT          (AMF_STD_CALL *Terminate)(AMFContext* pThis);\n\n        // DX9\n        AMF_RESULT          (AMF_STD_CALL *InitDX9)(AMFContext* pThis, void* pDX9Device);\n        void*               (AMF_STD_CALL *GetDX9Device)(AMFContext* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX9)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX9)(AMFContext* pThis);\n        // DX11\n        AMF_RESULT          (AMF_STD_CALL *InitDX11)(AMFContext* pThis, void* pDX11Device, AMF_DX_VERSION dxVersionRequired);\n        void*               (AMF_STD_CALL *GetDX11Device)(AMFContext* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX11)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX11)(AMFContext* pThis);\n\n        // OpenCL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCL)(AMFContext* pThis, void* pCommandQueue);\n        void*               (AMF_STD_CALL *GetOpenCLContext)(AMFContext* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLCommandQueue)(AMFContext* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLDeviceID)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetOpenCLComputeFactory)(AMFContext* pThis, AMFComputeFactory **ppFactory); // advanced compute - multiple queries\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCLEx)(AMFContext* pThis, AMFComputeDevice *pDevice);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenCL)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenCL)(AMFContext* pThis);\n\n        // OpenGL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenGL)(AMFContext* pThis, amf_handle hOpenGLContext, amf_handle hWindow, amf_handle hDC);\n        amf_handle          (AMF_STD_CALL *GetOpenGLContext)(AMFContext* pThis);\n        amf_handle          (AMF_STD_CALL *GetOpenGLDrawable)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenGL)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenGL)(AMFContext* pThis);\n        // XV - Linux\n        AMF_RESULT          (AMF_STD_CALL *InitXV)(AMFContext* pThis, void* pXVDevice);\n        void*               (AMF_STD_CALL *GetXVDevice)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockXV)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockXV)(AMFContext* pThis);\n\n        // Gralloc - Android\n        AMF_RESULT          (AMF_STD_CALL *InitGralloc)(AMFContext* pThis, void* pGrallocDevice);\n        void*               (AMF_STD_CALL *GetGrallocDevice)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockGralloc)(AMFContext* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockGralloc)(AMFContext* pThis);\n        // Allocation\n        AMF_RESULT          (AMF_STD_CALL *AllocBuffer)(AMFContext* pThis, AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer);\n        AMF_RESULT          (AMF_STD_CALL *AllocSurface)(AMFContext* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMFSurface** ppSurface);\n        AMF_RESULT          (AMF_STD_CALL *AllocAudioBuffer)(AMFContext* pThis, AMF_MEMORY_TYPE type, AMF_AUDIO_FORMAT format, amf_int32 samples, amf_int32 sampleRate, amf_int32 channels, \n                                                    AMFAudioBuffer** ppAudioBuffer);\n\n        // Wrap existing objects\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromHostNative)(AMFContext* pThis, void* pHostBuffer, amf_size size, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromHostNative)(AMFContext* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, void* pData, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX9Native)(AMFContext* pThis, void* pDX9Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX11Native)(AMFContext* pThis, void* pDX11Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenGLNative)(AMFContext* pThis, AMF_SURFACE_FORMAT format, amf_handle hGLTextureID, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromGrallocNative)(AMFContext* pThis, amf_handle hGrallocSurface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenCLNative)(AMFContext* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, void** pClPlanes, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromOpenCLNative)(AMFContext* pThis, void* pCLBuffer, amf_size size, AMFBuffer** ppBuffer);\n\n        // Access to AMFCompute interface - AMF_MEMORY_OPENCL, AMF_MEMORY_COMPUTE_FOR_DX9, AMF_MEMORY_COMPUTE_FOR_DX11 are currently supported\n        AMF_RESULT          (AMF_STD_CALL *GetCompute)(AMFContext* pThis, AMF_MEMORY_TYPE eMemType, AMFCompute** ppCompute);\n\n    } AMFContextVtbl;\n\n    struct AMFContext\n    {\n        const AMFContextVtbl *pVtbl;\n    };\n\n\n    typedef struct AMFContext1 AMFContext1;\n    AMF_DECLARE_IID(AMFContext1, 0xd9e9f868, 0x6220, 0x44c6, 0xa2, 0x2f, 0x7c, 0xd6, 0xda, 0xc6, 0x86, 0x46)\n\n    typedef struct AMFContext1Vtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFContext1* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFContext1* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFContext1* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n        \n        // AMFInterface AMFPropertyStorage\n\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFContext1* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFContext1* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFContext1* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFContext1* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFContext1* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFContext1* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFContext1* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFContext1* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFContext interface\n       \n        // Cleanup\n        AMF_RESULT          (AMF_STD_CALL *Terminate)(AMFContext1* pThis);\n\n        // DX9\n        AMF_RESULT          (AMF_STD_CALL *InitDX9)(AMFContext1* pThis, void* pDX9Device);\n        void*               (AMF_STD_CALL *GetDX9Device)(AMFContext1* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX9)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX9)(AMFContext1* pThis);\n        // DX11\n        AMF_RESULT          (AMF_STD_CALL *InitDX11)(AMFContext1* pThis, void* pDX11Device, AMF_DX_VERSION dxVersionRequired);\n        void*               (AMF_STD_CALL *GetDX11Device)(AMFContext1* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX11)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX11)(AMFContext1* pThis);\n\n        // OpenCL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCL)(AMFContext1* pThis, void* pCommandQueue);\n        void*               (AMF_STD_CALL *GetOpenCLContext)(AMFContext1* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLCommandQueue)(AMFContext1* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLDeviceID)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetOpenCLComputeFactory)(AMFContext1* pThis, AMFComputeFactory **ppFactory); // advanced compute - multiple queries\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCLEx)(AMFContext1* pThis, AMFComputeDevice *pDevice);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenCL)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenCL)(AMFContext1* pThis);\n\n        // OpenGL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenGL)(AMFContext1* pThis, amf_handle hOpenGLContext, amf_handle hWindow, amf_handle hDC);\n        amf_handle          (AMF_STD_CALL *GetOpenGLContext)(AMFContext1* pThis);\n        amf_handle          (AMF_STD_CALL *GetOpenGLDrawable)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenGL)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenGL)(AMFContext1* pThis);\n        // XV - Linux\n        AMF_RESULT          (AMF_STD_CALL *InitXV)(AMFContext1* pThis, void* pXVDevice);\n        void*               (AMF_STD_CALL *GetXVDevice)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockXV)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockXV)(AMFContext1* pThis);\n\n        // Gralloc - Android\n        AMF_RESULT          (AMF_STD_CALL *InitGralloc)(AMFContext1* pThis, void* pGrallocDevice);\n        void*               (AMF_STD_CALL *GetGrallocDevice)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockGralloc)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockGralloc)(AMFContext1* pThis);\n        // Allocation\n        AMF_RESULT          (AMF_STD_CALL *AllocBuffer)(AMFContext1* pThis, AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer);\n        AMF_RESULT          (AMF_STD_CALL *AllocSurface)(AMFContext1* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMFSurface** ppSurface);\n        AMF_RESULT          (AMF_STD_CALL *AllocAudioBuffer)(AMFContext1* pThis, AMF_MEMORY_TYPE type, AMF_AUDIO_FORMAT format, amf_int32 samples, amf_int32 sampleRate, amf_int32 channels, \n                                                    AMFAudioBuffer** ppAudioBuffer);\n\n        // Wrap existing objects\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromHostNative)(AMFContext1* pThis, void* pHostBuffer, amf_size size, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromHostNative)(AMFContext1* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, void* pData, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX9Native)(AMFContext1* pThis, void* pDX9Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX11Native)(AMFContext1* pThis, void* pDX11Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenGLNative)(AMFContext1* pThis, AMF_SURFACE_FORMAT format, amf_handle hGLTextureID, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromGrallocNative)(AMFContext1* pThis, amf_handle hGrallocSurface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenCLNative)(AMFContext1* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, void** pClPlanes, \n                                                     AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromOpenCLNative)(AMFContext1* pThis, void* pCLBuffer, amf_size size, AMFBuffer** ppBuffer);\n\n        // Access to AMFCompute interface - AMF_MEMORY_OPENCL, AMF_MEMORY_COMPUTE_FOR_DX9, AMF_MEMORY_COMPUTE_FOR_DX11 are currently supported\n        AMF_RESULT          (AMF_STD_CALL *GetCompute)(AMFContext1* pThis, AMF_MEMORY_TYPE eMemType, AMFCompute** ppCompute);\n\n        // AMFContext1 interface\n\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromDX11Native)(AMFContext1* pThis, void* pHostBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *AllocBufferEx)(AMFContext1* pThis, AMF_MEMORY_TYPE type, amf_size size, AMF_BUFFER_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFBuffer** ppBuffer);\n        AMF_RESULT          (AMF_STD_CALL *AllocSurfaceEx)(AMFContext1* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMF_SURFACE_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFSurface** ppSurface);\n\n        // Vulkan - Windows, Linux\n        AMF_RESULT          (AMF_STD_CALL *InitVulkan)(AMFContext1* pThis, void* pVulkanDevice);\n        void*               (AMF_STD_CALL *GetVulkanDevice)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockVulkan)(AMFContext1* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockVulkan)(AMFContext1* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromVulkanNative)(AMFContext1* pThis, void* pVulkanImage, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromVulkanNative)(AMFContext1* pThis, void* pVulkanBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *GetVulkanDeviceExtensions)(AMFContext1* pThis, amf_size *pCount, const char **ppExtensions);\n\n    } AMFContext1Vtbl;\n\n    struct AMFContext1\n    {\n        const AMFContext1Vtbl *pVtbl;\n    };\n\n    typedef struct AMFContext2 AMFContext2;\n    AMF_DECLARE_IID(AMFContext2, 0xd9e9f868, 0x6220, 0x44c6, 0xa2, 0x2f, 0x7c, 0xd6, 0xda, 0xc6, 0x86, 0x46)\n\n        typedef struct AMFContext2Vtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFContext2* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFContext2* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFContext2* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFInterface AMFPropertyStorage\n\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFContext2* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFContext2* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFContext2* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFContext2* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFContext2* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFContext2* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFContext2* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFContext2* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFContext interface\n\n        // Cleanup\n        AMF_RESULT          (AMF_STD_CALL *Terminate)(AMFContext2* pThis);\n\n        // DX9\n        AMF_RESULT          (AMF_STD_CALL *InitDX9)(AMFContext2* pThis, void* pDX9Device);\n        void*                         (AMF_STD_CALL *GetDX9Device)(AMFContext2* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX9)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX9)(AMFContext2* pThis);\n        // DX11\n        AMF_RESULT          (AMF_STD_CALL *InitDX11)(AMFContext2* pThis, void* pDX11Device, AMF_DX_VERSION dxVersionRequired);\n        void*                         (AMF_STD_CALL *GetDX11Device)(AMFContext2* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX11)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX11)(AMFContext2* pThis);\n\n        // OpenCL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCL)(AMFContext2* pThis, void* pCommandQueue);\n        void*               (AMF_STD_CALL *GetOpenCLContext)(AMFContext2* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLCommandQueue)(AMFContext2* pThis);\n        void*               (AMF_STD_CALL *GetOpenCLDeviceID)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetOpenCLComputeFactory)(AMFContext2* pThis, AMFComputeFactory **ppFactory); // advanced compute - multiple queries\n        AMF_RESULT          (AMF_STD_CALL *InitOpenCLEx)(AMFContext2* pThis, AMFComputeDevice *pDevice);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenCL)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenCL)(AMFContext2* pThis);\n\n        // OpenGL\n        AMF_RESULT          (AMF_STD_CALL *InitOpenGL)(AMFContext2* pThis, amf_handle hOpenGLContext, amf_handle hWindow, amf_handle hDC);\n        amf_handle          (AMF_STD_CALL *GetOpenGLContext)(AMFContext2* pThis);\n        amf_handle          (AMF_STD_CALL *GetOpenGLDrawable)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockOpenGL)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockOpenGL)(AMFContext2* pThis);\n        // XV - Linux\n        AMF_RESULT          (AMF_STD_CALL *InitXV)(AMFContext2* pThis, void* pXVDevice);\n        void*               (AMF_STD_CALL *GetXVDevice)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockXV)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockXV)(AMFContext2* pThis);\n\n        // Gralloc - Android\n        AMF_RESULT          (AMF_STD_CALL *InitGralloc)(AMFContext2* pThis, void* pGrallocDevice);\n        void*               (AMF_STD_CALL *GetGrallocDevice)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockGralloc)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockGralloc)(AMFContext2* pThis);\n        // Allocation\n        AMF_RESULT          (AMF_STD_CALL *AllocBuffer)(AMFContext2* pThis, AMF_MEMORY_TYPE type, amf_size size, AMFBuffer** ppBuffer);\n        AMF_RESULT          (AMF_STD_CALL *AllocSurface)(AMFContext2* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMFSurface** ppSurface);\n        AMF_RESULT          (AMF_STD_CALL *AllocAudioBuffer)(AMFContext2* pThis, AMF_MEMORY_TYPE type, AMF_AUDIO_FORMAT format, amf_int32 samples, amf_int32 sampleRate, amf_int32 channels, AMFAudioBuffer** ppAudioBuffer);\n\n        // Wrap existing objects\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromHostNative)(AMFContext2* pThis, void* pHostBuffer, amf_size size, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromHostNative)(AMFContext2* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, amf_int32 hPitch, amf_int32 vPitch, void* pData,AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX9Native)(AMFContext2* pThis, void* pDX9Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX11Native)(AMFContext2* pThis, void* pDX11Surface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenGLNative)(AMFContext2* pThis, AMF_SURFACE_FORMAT format, amf_handle hGLTextureID, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromGrallocNative)(AMFContext2* pThis, amf_handle hGrallocSurface, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromOpenCLNative)(AMFContext2* pThis, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, void** pClPlanes, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromOpenCLNative)(AMFContext2* pThis, void* pCLBuffer, amf_size size, AMFBuffer** ppBuffer);\n\n        // Access to AMFCompute interface - AMF_MEMORY_OPENCL, AMF_MEMORY_COMPUTE_FOR_DX9, AMF_MEMORY_COMPUTE_FOR_DX11 are currently supported\n        AMF_RESULT          (AMF_STD_CALL *GetCompute)(AMFContext2* pThis, AMF_MEMORY_TYPE eMemType, AMFCompute** ppCompute);\n\n        // AMFContext1 interface\n\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromDX11Native)(AMFContext2* pThis, void* pHostBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *AllocBufferEx)(AMFContext2* pThis, AMF_MEMORY_TYPE type, amf_size size, AMF_BUFFER_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFBuffer** ppBuffer);\n        AMF_RESULT          (AMF_STD_CALL *AllocSurfaceEx)(AMFContext2* pThis, AMF_MEMORY_TYPE type, AMF_SURFACE_FORMAT format, amf_int32 width, amf_int32 height, AMF_SURFACE_USAGE usage, AMF_MEMORY_CPU_ACCESS access, AMFSurface** ppSurface);\n\n        // Vulkan - Windows, Linux\n        AMF_RESULT          (AMF_STD_CALL *InitVulkan)(AMFContext2* pThis, void* pVulkanDevice);\n        void*               (AMF_STD_CALL *GetVulkanDevice)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *LockVulkan)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockVulkan)(AMFContext2* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromVulkanNative)(AMFContext2* pThis, void* pVulkanImage, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromVulkanNative)(AMFContext2* pThis, void* pVulkanBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *GetVulkanDeviceExtensions)(AMFContext2* pThis, amf_size *pCount, const char **ppExtensions);\n\n        // AMFContext2 interface\n        AMF_RESULT          (AMF_STD_CALL *InitDX12)(AMFContext2* pThis, void* pDX11Device, AMF_DX_VERSION dxVersionRequired);\n        void*               (AMF_STD_CALL *GetDX12Device)(AMFContext2* pThis, AMF_DX_VERSION dxVersionRequired);\n        AMF_RESULT          (AMF_STD_CALL *LockDX12)(AMFContext2* pThis);\n        AMF_RESULT          (AMF_STD_CALL *UnlockDX12)(AMFContext2* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *CreateSurfaceFromDX12Native)(AMFContext2* pThis, void* pResourceTexture, AMFSurface** ppSurface, AMFSurfaceObserver* pObserver);\n        AMF_RESULT          (AMF_STD_CALL *CreateBufferFromDX12Native)(AMFContext2* pThis, void* pResourceBuffer, AMFBuffer** ppBuffer, AMFBufferObserver* pObserver);\n\n\n    } AMFContext2Vtbl;\n\n    struct AMFContext2\n    {\n        const AMFContext2Vtbl *pVtbl;\n    };\n#endif\n\n#if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // Lockers\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFDX9Locker\n    {\n    public:\n        AMFDX9Locker() : m_Context(NULL)\n        {}\n        AMFDX9Locker(AMFContext* resources) : m_Context(NULL)\n        {\n            Lock(resources);\n        }\n        ~AMFDX9Locker()\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockDX9();\n            }\n        }\n        void Lock(AMFContext* resources)\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockDX9();\n            }\n            m_Context = resources;\n            if(m_Context != NULL)\n            {\n                m_Context->LockDX9();\n            }\n        }\n    protected:\n        AMFContext* m_Context;\n\n    private:\n        AMFDX9Locker(const AMFDX9Locker&);\n        AMFDX9Locker& operator=(const AMFDX9Locker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFDX11Locker\n    {\n    public:\n        AMFDX11Locker() : m_Context(NULL)\n        {}\n        AMFDX11Locker(AMFContext* resources) : m_Context(NULL)\n        {\n            Lock(resources);\n        }\n        ~AMFDX11Locker()\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockDX11();\n            }\n        }\n        void Lock(AMFContext* resources)\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockDX11();\n            }\n            m_Context = resources;\n            if(m_Context != NULL)\n            {\n                m_Context->LockDX11();\n            }\n        }\n    protected:\n        AMFContext* m_Context;\n\n    private:\n        AMFDX11Locker(const AMFDX11Locker&);\n        AMFDX11Locker& operator=(const AMFDX11Locker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFOpenCLLocker\n    {\n    public:\n        AMFOpenCLLocker() : m_Context(NULL)\n        {}\n        AMFOpenCLLocker(AMFContext* resources) : m_Context(NULL)\n        {\n            Lock(resources);\n        }\n        ~AMFOpenCLLocker()\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockOpenCL();\n            }\n        }\n        void Lock(AMFContext* resources)\n        {\n            if(m_Context != NULL)\n            {\n                m_Context->UnlockOpenCL();\n            }\n            m_Context = resources;\n            if(m_Context != NULL)\n            {\n                m_Context->LockOpenCL();\n            }\n        }\n    protected:\n        AMFContext* m_Context;\n    private:\n        AMFOpenCLLocker(const AMFOpenCLLocker&);\n        AMFOpenCLLocker& operator=(const AMFOpenCLLocker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFOpenGLLocker\n    {\n    public:\n        AMFOpenGLLocker(AMFContext* pContext) : m_pContext(pContext),\n            m_GLLocked(false)\n        {\n            if(m_pContext != NULL)\n            {\n                if(m_pContext->LockOpenGL() == AMF_OK)\n                {\n                    m_GLLocked = true;\n                }\n            }\n        }\n        ~AMFOpenGLLocker()\n        {\n            if(m_GLLocked)\n            {\n                m_pContext->UnlockOpenGL();\n            }\n        }\n    private:\n        AMFContext* m_pContext;\n        amf_bool m_GLLocked; ///< AMFOpenGLLocker can be called when OpenGL is not initialized yet\n                             ///< in this case don't call UnlockOpenGL\n        AMFOpenGLLocker(const AMFOpenGLLocker&);\n        AMFOpenGLLocker& operator=(const AMFOpenGLLocker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFXVLocker\n    {\n    public:\n        AMFXVLocker() : m_pContext(NULL)\n        {}\n        AMFXVLocker(AMFContext* pContext) : m_pContext(NULL)\n        {\n            Lock(pContext);\n        }\n        ~AMFXVLocker()\n        {\n            if(m_pContext != NULL)\n            {\n                m_pContext->UnlockXV();\n            }\n        }\n        void Lock(AMFContext* pContext)\n        {\n            if((pContext != NULL) && (pContext->GetXVDevice() != NULL))\n            {\n                m_pContext = pContext;\n                m_pContext->LockXV();\n            }\n        }\n    protected:\n        AMFContext* m_pContext;\n    private:\n        AMFXVLocker(const AMFXVLocker&);\n        AMFXVLocker& operator=(const AMFXVLocker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext::AMFGrallocLocker\n    {\n    public:\n        AMFGrallocLocker() : m_pContext(NULL)\n        {}\n        AMFGrallocLocker(AMFContext* pContext) : m_pContext(NULL)\n        {\n            Lock(pContext);\n        }\n        ~AMFGrallocLocker()\n        {\n            if(m_pContext != NULL)\n            {\n                m_pContext->UnlockGralloc();\n            }\n        }\n        void Lock(AMFContext* pContext)\n        {\n            if((pContext != NULL) && (pContext->GetGrallocDevice() != NULL))\n            {\n                m_pContext = pContext;\n                m_pContext->LockGralloc();\n            }\n        }\n    protected:\n        AMFContext* m_pContext;\n    private:\n        AMFGrallocLocker(const AMFGrallocLocker&);\n        AMFGrallocLocker& operator=(const AMFGrallocLocker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext1::AMFVulkanLocker\n    {\n    public:\n        AMFVulkanLocker() : m_pContext(NULL)\n        {}\n        AMFVulkanLocker(AMFContext1* pContext) : m_pContext(NULL)\n        {\n            Lock(pContext);\n        }\n        ~AMFVulkanLocker()\n        {\n            if(m_pContext != NULL)\n            {\n                m_pContext->UnlockVulkan();\n            }\n        }\n        void Lock(AMFContext1* pContext)\n        {\n            if((pContext != NULL) && (pContext->GetVulkanDevice() != NULL))\n            {\n                m_pContext = pContext;\n                m_pContext->LockVulkan();\n            }\n        }\n    protected:\n        AMFContext1* m_pContext;\n    private:\n        AMFVulkanLocker(const AMFVulkanLocker&);\n        AMFVulkanLocker& operator=(const AMFVulkanLocker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    class AMFContext2::AMFDX12Locker\n    {\n    public:\n        AMFDX12Locker() : m_Context(NULL)\n        {}\n        AMFDX12Locker(AMFContext2* resources) : m_Context(NULL)\n        {\n            Lock(resources);\n        }\n        ~AMFDX12Locker()\n        {\n            if (m_Context != NULL)\n            {\n                m_Context->UnlockDX12();\n            }\n        }\n        void Lock(AMFContext2* resources)\n        {\n            if (m_Context != NULL)\n            {\n                m_Context->UnlockDX12();\n            }\n            m_Context = resources;\n            if (m_Context != NULL)\n            {\n                m_Context->LockDX12();\n            }\n        }\n    protected:\n        AMFContext2* m_Context;\n\n    private:\n        AMFDX12Locker(const AMFDX12Locker&);\n        AMFDX12Locker& operator=(const AMFDX12Locker&);\n    };\n    //----------------------------------------------------------------------------------------------\n    //----------------------------------------------------------------------------------------------\n    //----------------------------------------------------------------------------------------------\n#endif\n#if defined(__cplusplus)\n}\n#endif\nenum AMF_CONTEXT_DEVICETYPE_ENUM\n{\n    AMF_CONTEXT_DEVICE_TYPE_GPU = 0,\n    AMF_CONTEXT_DEVICE_TYPE_CPU\n};\n#define AMF_CONTEXT_DEVICE_TYPE  L\"AMF_Context_DeviceType\" //Value type: amf_int64; Values : AMF_CONTEXT_DEVICE_TYPE_GPU for GPU (default) , AMF_CONTEXT_DEVICE_TYPE_CPU for CPU.\n#endif //#ifndef AMF_Context_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/CurrentTime.h",
    "content": "//\n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_CurrentTime_h\n#define AMF_CurrentTime_h\n\n#include \"Platform.h\"\n#include \"Interface.h\"\n\nnamespace amf\n{\n\t// Current time interface class. This interface object can be passed\n\t// as a property to components requiring synchronized timing. The\n\t// implementation is:\n\t// - first call to Get() starts time and returns 0\n\t// - subsequent calls to Get() returns values relative to 0\n\t// - Reset() puts time back at 0 at next Get() call\n\t//\n\tclass AMF_NO_VTABLE AMFCurrentTime : public AMFInterface\n\t{\n\tpublic:\n\n\t\tvirtual amf_pts AMF_STD_CALL Get() = 0;\n\n\t\tvirtual void AMF_STD_CALL Reset() = 0;\n\t};\n\n\t//----------------------------------------------------------------------------------------------\n\t// smart pointer\n\t//----------------------------------------------------------------------------------------------\n\ttypedef AMFInterfacePtr_T<AMFCurrentTime> AMFCurrentTimePtr;\n\t//----------------------------------------------------------------------------------------------}\n}\n#endif // AMF_CurrentTime_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/D3D12AMF.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef __D3D12AMF_h__\n#define __D3D12AMF_h__\n#pragma once \n#include \"Platform.h\"\n#if defined(_WIN32)||(defined(__linux) && defined(AMF_WSL))\n\n#define AMFDX12_NUMBER_OF_DESCRYPTOR_HEAPS  L\"NumberOfDescryptorHeaps\" // amf_int64, default is 4, to be set on AMFContext\n// syncronization properties set via SetPrivateData()\nAMF_WEAK GUID  AMFResourceStateGUID = { 0x452da9bf, 0x4ad7, 0x47a5, { 0xa6, 0x9b, 0x96, 0xd3, 0x23, 0x76, 0xf2, 0xf3 } };   // Current resource state value (D3D12_RESOURCE_STATES ), sizeof(UINT), set on ID3D12Resource \nAMF_WEAK GUID  AMFFenceGUID         = { 0x910a7928, 0x57bd, 0x4b04, { 0x91, 0xa3, 0xe7, 0xb8, 0x04, 0x12, 0xcd, 0xa5 } };   // IUnknown (ID3D12Fence), set on ID3D12Resource  syncronization fence for this resource\nAMF_WEAK GUID  AMFFenceValueGUID    = { 0x62a693d3, 0xbb4a, 0x46c9, { 0xa5, 0x04, 0x9a, 0x8e, 0x97, 0xbf, 0xf0, 0x56 } };   // The last value to wait on the fence from AMFFenceGUID; sizeof(UINT64), set on ID3D12Fence \n\nAMF_WEAK GUID  AMFFenceD3D11GUID    = { 0xdffdf6e0, 0x85e0, 0x4645, { 0x9d, 0x7, 0xe6, 0x4a, 0x19, 0x6b, 0xc9, 0xbf } };   // IUnknown (ID3D11Fence) OpenSharedFence for interop\nAMF_WEAK GUID  AMFFenceValueD3D11GUID    = { 0x86581b71, 0x699f, 0x484b, { 0xb8, 0x75, 0x24, 0xda, 0x49, 0x8a, 0x74, 0xcf } };   // last value to wait on in d3d11\nAMF_WEAK GUID  AMFSharedHandleFenceGUID = { 0xca60dcc8, 0x76d1, 0x4088, 0xad, 0xd, 0x97, 0x71, 0xe7, 0xb0, 0x92, 0x49 };   // ID3D12Fence shared handle for D3D11 interop\n\n#endif\n\n#endif // __D3D12AMF_h__"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Data.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Data_h\n#define AMF_Data_h\n#pragma once\n\n#include \"PropertyStorage.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_DATA_TYPE\n    {\n        AMF_DATA_BUFFER             = 0,\n        AMF_DATA_SURFACE            = 1,\n        AMF_DATA_AUDIO_BUFFER       = 2,\n        AMF_DATA_USER               = 1000,\n        // all extensions will be AMF_DATA_USER+i\n    } AMF_DATA_TYPE;\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_MEMORY_TYPE\n    {\n        AMF_MEMORY_UNKNOWN          = 0,\n        AMF_MEMORY_HOST             = 1,\n        AMF_MEMORY_DX9              = 2,\n        AMF_MEMORY_DX11             = 3,\n\t\tAMF_MEMORY_OPENCL           = 4,\n        AMF_MEMORY_OPENGL           = 5,\n        AMF_MEMORY_XV               = 6,\n        AMF_MEMORY_GRALLOC          = 7,\n        AMF_MEMORY_COMPUTE_FOR_DX9  = 8, // deprecated, the same as AMF_MEMORY_OPENCL\n        AMF_MEMORY_COMPUTE_FOR_DX11 = 9, // deprecated, the same as AMF_MEMORY_OPENCL\n        AMF_MEMORY_VULKAN           = 10,\n        AMF_MEMORY_DX12             = 11,\n    } AMF_MEMORY_TYPE;\n\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_DX_VERSION\n    {\n        AMF_DX9                     = 90,\n        AMF_DX9_EX                  = 91,\n        AMF_DX11_0                  = 110,\n        AMF_DX11_1                  = 111,\n\t\tAMF_DX12                    = 120,\n    } AMF_DX_VERSION;\n\n    //----------------------------------------------------------------------------------------------\n    // AMF_MEMORY_CPU_ACCESS translates to D3D11_CPU_ACCESS_FLAG or VkImageUsageFlags\n    // bit mask\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_MEMORY_CPU_ACCESS_BITS\n    {                                           // D3D11                    D3D12                      Vulkan \n        AMF_MEMORY_CPU_DEFAULT  = 0x80000000,   // 0                     ,  D3D12_HEAP_TYPE_DEFAULT ,  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT\n        AMF_MEMORY_CPU_NONE     = 0x00000000,   // 0                     ,  D3D12_HEAP_TYPE_DEFAULT ,\n        AMF_MEMORY_CPU_READ     = 0x00000001,   // D3D11_CPU_ACCESS_READ ,  D3D12_HEAP_TYPE_READBACK,  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n        AMF_MEMORY_CPU_WRITE    = 0x00000002,   // D3D11_CPU_ACCESS_WRITE,  D3D12_HEAP_TYPE_UPLOAD  ,  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n        AMF_MEMORY_CPU_LOCAL    = 0x00000004,   //                       ,  D3D12_HEAP_TYPE_DEFAULT ,  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT\n        AMF_MEMORY_CPU_PINNED   = 0x00000008,   //                       ,                          ,  VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR\n    } AMF_MEMORY_CPU_ACCESS_BITS;\n    typedef amf_flags AMF_MEMORY_CPU_ACCESS;\n    //----------------------------------------------------------------------------------------------\n    // AMFData interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFData : public AMFPropertyStorage\n    {\n    public:\n        AMF_DECLARE_IID(0xa1159bf6, 0x9104, 0x4107, 0x8e, 0xaa, 0xc5, 0x3d, 0x5d, 0xba, 0xc5, 0x11)\n\n        virtual AMF_MEMORY_TYPE     AMF_STD_CALL GetMemoryType() = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL Duplicate(AMF_MEMORY_TYPE type, AMFData** ppData) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL Convert(AMF_MEMORY_TYPE type) = 0; // optimal interop if possilble. Copy through host memory if needed\n        virtual AMF_RESULT          AMF_STD_CALL Interop(AMF_MEMORY_TYPE type) = 0; // only optimal interop if possilble. No copy through host memory for GPU objects\n\n        virtual AMF_DATA_TYPE       AMF_STD_CALL GetDataType() = 0;\n\n        virtual amf_bool            AMF_STD_CALL IsReusable() = 0;\n\n        virtual void                AMF_STD_CALL SetPts(amf_pts pts) = 0;\n        virtual amf_pts             AMF_STD_CALL GetPts() = 0;\n        virtual void                AMF_STD_CALL SetDuration(amf_pts duration) = 0;\n        virtual amf_pts             AMF_STD_CALL GetDuration() = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFData> AMFDataPtr;\n    //----------------------------------------------------------------------------------------------\n\n#else // #if defined(__cplusplus)\n    typedef struct AMFData AMFData;\n    AMF_DECLARE_IID(AMFData, 0xa1159bf6, 0x9104, 0x4107, 0x8e, 0xaa, 0xc5, 0x3d, 0x5d, 0xba, 0xc5, 0x11)\n\n    typedef struct AMFDataVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFData* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFData* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFData* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFData* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFData* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFData* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFData* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFData* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFData* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFData* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFData* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFData* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFData* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFData interface\n\n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryType)(AMFData* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *Duplicate)(AMFData* pThis, AMF_MEMORY_TYPE type, AMFData** ppData);\n        AMF_RESULT          (AMF_STD_CALL *Convert)(AMFData* pThis, AMF_MEMORY_TYPE type); // optimal interop if possilble. Copy through host memory if needed\n        AMF_RESULT          (AMF_STD_CALL *Interop)(AMFData* pThis, AMF_MEMORY_TYPE type); // only optimal interop if possilble. No copy through host memory for GPU objects\n\n        AMF_DATA_TYPE       (AMF_STD_CALL *GetDataType)(AMFData* pThis);\n\n        amf_bool            (AMF_STD_CALL *IsReusable)(AMFData* pThis);\n\n        void                (AMF_STD_CALL *SetPts)(AMFData* pThis, amf_pts pts);\n        amf_pts             (AMF_STD_CALL *GetPts)(AMFData* pThis);\n        void                (AMF_STD_CALL *SetDuration)(AMFData* pThis, amf_pts duration);\n        amf_pts             (AMF_STD_CALL *GetDuration)(AMFData* pThis);\n\n    } AMFDataVtbl;\n\n    struct AMFData\n    {\n        const AMFDataVtbl *pVtbl;\n    };\n\n\n#endif // #if defined(__cplusplus)\n\n#if defined(__cplusplus)\n} // namespace\n#endif\n\n#endif //#ifndef AMF_Data_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Debug.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Debug_h\n#define AMF_Debug_h\n#pragma once\n\n#include \"Platform.h\"\n#include \"Result.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // AMFDebug interface - singleton\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFDebug\n    {\n    public:\n        virtual  void               AMF_STD_CALL EnablePerformanceMonitor(amf_bool enable) = 0;\n        virtual  amf_bool           AMF_STD_CALL PerformanceMonitorEnabled() = 0;\n        virtual  void               AMF_STD_CALL AssertsEnable(amf_bool enable) = 0;\n        virtual  amf_bool           AMF_STD_CALL AssertsEnabled() = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFDebug AMFDebug;\n    typedef struct AMFDebugVtbl\n    {\n        // AMFDebug interface\n        void               (AMF_STD_CALL *EnablePerformanceMonitor)(AMFDebug* pThis, amf_bool enable);\n        amf_bool           (AMF_STD_CALL *PerformanceMonitorEnabled)(AMFDebug* pThis);\n        void               (AMF_STD_CALL *AssertsEnable)(AMFDebug* pThis, amf_bool enable);\n        amf_bool           (AMF_STD_CALL *AssertsEnabled)(AMFDebug* pThis);\n    } AMFDebugVtbl;\n\n    struct AMFDebug\n    {\n        const AMFDebugVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n}\n#endif\n\n#endif // AMF_Debug_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Dump.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Dump_h\n#define AMF_Dump_h\n#pragma once\n\n#include \"Platform.h\"\n#include \"Result.h\"\n#include \"Interface.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFDump : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x75366ad4, 0x504c, 0x430b, 0xbb, 0xe2, 0xad, 0x21, 0x82, 0x8, 0xf, 0x72);\n\n\n        virtual const wchar_t*  AMF_STD_CALL GetDumpBasePath() const = 0;             //  Get application dump base path\n        virtual AMF_RESULT      AMF_STD_CALL SetDumpBasePath(const wchar_t* path) = 0;    //  Set application dump base path\n\n        //  Enable/disable input and/or output stream dumps\n        virtual bool            AMF_STD_CALL IsInputDumpEnabled() const = 0;\n        virtual AMF_RESULT      AMF_STD_CALL EnableInputDump(bool enabled) = 0;     \n        virtual const wchar_t*  AMF_STD_CALL GetInputDumpFullName() const = 0;  //  Get full name of dump file\n\n        //  Enable/disable input and/or output stream dumps\n        virtual bool            AMF_STD_CALL IsOutputDumpEnabled() const = 0;\n        virtual AMF_RESULT      AMF_STD_CALL EnableOutputDump(bool enabled) = 0;     \n        virtual const wchar_t*  AMF_STD_CALL GetOutputDumpFullName() const = 0;  //  Get full name of dump file\n\n        //  When enabled, each new application session will create a subfolder with a time stamp in the base path tree (disabled by default)\n        virtual bool            AMF_STD_CALL IsPerSessionDumpEnabled() const = 0;\n        virtual void            AMF_STD_CALL EnablePerSessionDump(bool enabled) = 0;      \n    };\n    typedef AMFInterfacePtr_T<AMFDump> AMFDumpPtr;\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFDump, 0x75366ad4, 0x504c, 0x430b, 0xbb, 0xe2, 0xad, 0x21, 0x82, 0x8, 0xf, 0x72);\n    typedef struct AMFDump AMFDump;\n\n    typedef struct AMFDumpVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFDump* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFDump* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFDump* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n        \n        // AMFDump interface\n        const wchar_t*  (AMF_STD_CALL *GetDumpBasePath)(AMFDump* pThis) const;             //  Get application dump base path\n        AMF_RESULT      (AMF_STD_CALL *SetDumpBasePath)(AMFDump* pThis, const wchar_t* path);    //  Set application dump base path\n        \n        //  Enable/disable input and/or output stream dumps\n        bool            (AMF_STD_CALL *IsInputDumpEnabled)(AMFDump* pThis) const;\n        AMF_RESULT      (AMF_STD_CALL *EnableInputDump)(AMFDump* pThis, bool enabled);     \n        const wchar_t*  (AMF_STD_CALL *GetInputDumpFullName)(AMFDump* pThis) const;  //  Get full name of dump file\n\n        //  Enable/disable input and/or output stream dumps\n        bool            (AMF_STD_CALL *IsOutputDumpEnabled)(AMFDump* pThis) const;\n        AMF_RESULT      (AMF_STD_CALL *EnableOutputDump)(AMFDump* pThis, bool enabled);     \n        const wchar_t*  (AMF_STD_CALL *GetOutputDumpFullName)(AMFDump* pThis) const;  //  Get full name of dump file\n\n        //  When enabled, each new application session will create a subfolder with a time stamp in the base path tree (disabled by default)\n        bool            (AMF_STD_CALL *IsPerSessionDumpEnabled)(AMFDump* pThis) const;\n        void            (AMF_STD_CALL *EnablePerSessionDump)(AMFDump* pThis, bool enabled);      \n\n    } AMFDumpVtbl;\n\n    struct AMFDump\n    {\n        const AMFDumpVtbl *pVtbl;\n    };\n\n\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n} // namespace\n#endif\n\n#endif //AMF_Dump_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Factory.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Factory_h\n#define AMF_Factory_h\n#pragma once\n\n#include \"Platform.h\"\n#include \"Version.h\"\n#include \"Result.h\"\n#include \"Context.h\"\n#include \"Debug.h\"\n#include \"Trace.h\"\n#include \"Compute.h\"\n\n#include \"../components/Component.h\"\n\n#if defined(__cplusplus)\n\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // AMFFactory interface - singleton\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFFactory\n    {\n    public:\n        virtual AMF_RESULT          AMF_STD_CALL CreateContext(AMFContext** ppContext) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CreateComponent(AMFContext* pContext, const wchar_t* id, AMFComponent** ppComponent) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetCacheFolder(const wchar_t* path) = 0;\n        virtual const wchar_t*      AMF_STD_CALL GetCacheFolder() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetDebug(AMFDebug** ppDebug) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetTrace(AMFTrace** ppTrace) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPrograms(AMFPrograms** ppPrograms) = 0;\n   };\n#else\n    typedef struct AMFFactory AMFFactory;\n\n    typedef struct AMFFactoryVtbl\n    {\n        AMF_RESULT          (AMF_STD_CALL *CreateContext)(AMFFactory* pThis, AMFContext** ppContext);\n        AMF_RESULT          (AMF_STD_CALL *CreateComponent)(AMFFactory* pThis, AMFContext* pContext, const wchar_t* id, AMFComponent** ppComponent);\n        AMF_RESULT          (AMF_STD_CALL *SetCacheFolder)(AMFFactory* pThis, const wchar_t* path);\n        const wchar_t*      (AMF_STD_CALL *GetCacheFolder)(AMFFactory* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetDebug)(AMFFactory* pThis, AMFDebug** ppDebug);\n        AMF_RESULT          (AMF_STD_CALL *GetTrace)(AMFFactory* pThis, AMFTrace** ppTrace);\n        AMF_RESULT          (AMF_STD_CALL *GetPrograms)(AMFFactory* pThis, AMFPrograms** ppPrograms);\n    } AMFFactoryVtbl;\n\n    struct AMFFactory\n    {\n        const AMFFactoryVtbl *pVtbl;\n    };\n\n#endif\n#if defined(__cplusplus)\n}\n#endif\n\n//----------------------------------------------------------------------------------------------\n// DLL entry points\n//----------------------------------------------------------------------------------------------\n\n#define AMF_INIT_FUNCTION_NAME             \"AMFInit\"\n#define AMF_QUERY_VERSION_FUNCTION_NAME    \"AMFQueryVersion\"\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n    typedef AMF_RESULT             (AMF_CDECL_CALL *AMFInit_Fn)(amf_uint64 version, amf::AMFFactory **ppFactory);\n    typedef AMF_RESULT             (AMF_CDECL_CALL *AMFQueryVersion_Fn)(amf_uint64 *pVersion);\n}\n#else\n    typedef AMF_RESULT             (AMF_CDECL_CALL *AMFInit_Fn)(amf_uint64 version, AMFFactory **ppFactory);\n    typedef AMF_RESULT             (AMF_CDECL_CALL *AMFQueryVersion_Fn)(amf_uint64 *pVersion);\n#endif\n\n#if defined(_WIN32)\n    #if defined(_M_AMD64)\n        #define AMF_DLL_NAME    L\"amfrt64.dll\"\n        #define AMF_DLL_NAMEA   \"amfrt64.dll\"\n#else\n        #define AMF_DLL_NAME    L\"amfrt32.dll\"\n        #define AMF_DLL_NAMEA   \"amfrt32.dll\"\n    #endif\n#elif defined(__ANDROID__) && !defined(AMF_ANDROID_ENCODER)\n    #define AMF_DLL_NAME    L\"libamf.so\"\n    #define AMF_DLL_NAMEA    \"libamf.so\"\n#elif defined(__APPLE__)\n    #define AMF_DLL_NAME    L\"libamfrt.framework/libamfrt\"\n    #define AMF_DLL_NAMEA   \"libamfrt.framework/libamfrt\"\n#elif defined(__linux__)\n    #if defined(__x86_64__) || defined(__aarch64__)\n        #define AMF_DLL_NAME    L\"libamfrt64.so.1\"\n        #define AMF_DLL_NAMEA   \"libamfrt64.so.1\"\n    #else\n        #define AMF_DLL_NAME    L\"libamfrt32.so.1\"\n        #define AMF_DLL_NAMEA   \"libamfrt32.so.1\"\n    #endif\n#endif\n//----------------------------------------------------------------------------------------------\n#endif   // AMF_Factory_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Interface.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Interface_h\n#define AMF_Interface_h\n#pragma once\n\n#include \"Result.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n#if defined(__cplusplus)\n    #define AMF_DECLARE_IID(_data1, _data2, _data3, _data41, _data42, _data43, _data44, _data45, _data46, _data47, _data48) \\\n        static AMF_INLINE const amf::AMFGuid IID() \\\n        { \\\n            amf::AMFGuid uid = {_data1, _data2, _data3, _data41, _data42, _data43, _data44, _data45, _data46, _data47, _data48}; \\\n            return uid; \\\n        }\n#else\n#define AMF_DECLARE_IID(name, _data1, _data2, _data3, _data41, _data42, _data43, _data44, _data45, _data46, _data47, _data48) \\\n        AMF_INLINE static const AMFGuid IID_##name(void) \\\n        { \\\n            AMFGuid uid = {_data1, _data2, _data3, _data41, _data42, _data43, _data44, _data45, _data46, _data47, _data48}; \\\n            return uid; \\\n        }\n#endif\n\n    //------------------------------------------------------------------------\n    // AMFInterface interface  - base class for all AMF interfaces\n    //------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0x9d872f34, 0x90dc, 0x4b93, 0xb6, 0xb2, 0x6c, 0xa3, 0x7c, 0x85, 0x25, 0xdb)\n\n        virtual amf_long            AMF_STD_CALL Acquire() = 0;\n        virtual amf_long            AMF_STD_CALL Release() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL QueryInterface(const AMFGuid& interfaceID, void** ppInterface) = 0;\n    };\n#else\n    AMF_DECLARE_IID(AMFInterface, 0x9d872f34, 0x90dc, 0x4b93, 0xb6, 0xb2, 0x6c, 0xa3, 0x7c, 0x85, 0x25, 0xdb)\n    typedef struct AMFInterface AMFInterface;\n\n    typedef struct AMFInterfaceVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFInterface* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFInterface* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFInterface* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n    } AMFInterfaceVtbl;\n\n    struct AMFInterface\n    {\n        const AMFInterfaceVtbl *pVtbl;\n    };\n#endif\n    //------------------------------------------------------------------------\n    // template for AMF smart pointer\n    //------------------------------------------------------------------------\n#if defined(__cplusplus)\n    template<class _Interf>\n    class AMFInterfacePtr_T\n    {\n    private:\n        _Interf* m_pInterf;\n\n        void InternalAcquire()\n        {\n            if(m_pInterf != NULL)\n            {\n                m_pInterf->Acquire();\n            }\n        }\n        void InternalRelease()\n        {\n            if(m_pInterf != NULL)\n            {\n                m_pInterf->Release();\n            }\n        }\n    public:\n        AMFInterfacePtr_T() : m_pInterf(NULL)\n        {}\n\n        AMFInterfacePtr_T(const AMFInterfacePtr_T<_Interf>& p) : m_pInterf(p.m_pInterf)\n        {\n            InternalAcquire();\n        }\n\n        AMFInterfacePtr_T(_Interf* pInterface) : m_pInterf(pInterface)\n        {\n            InternalAcquire();\n        }\n\n        template<class _OtherInterf>\n        explicit AMFInterfacePtr_T(const AMFInterfacePtr_T<_OtherInterf>& cp) : m_pInterf(NULL)\n        {\n            void* pInterf = NULL;\n            if((cp == NULL) || (cp->QueryInterface(_Interf::IID(), &pInterf) != AMF_OK))\n            {\n                pInterf = NULL;\n            }\n            m_pInterf = static_cast<_Interf*>(pInterf);\n        }\n\n        template<class _OtherInterf>\n        explicit AMFInterfacePtr_T(_OtherInterf* cp) : m_pInterf(NULL)\n        {\n            void* pInterf = NULL;\n            if((cp == NULL) || (cp->QueryInterface(_Interf::IID(), &pInterf) != AMF_OK))\n            {\n                pInterf = NULL;\n            }\n            m_pInterf = static_cast<_Interf*>(pInterf);\n        }\n\n        ~AMFInterfacePtr_T()\n        {\n            InternalRelease();\n        }\n\n        AMFInterfacePtr_T& operator=(_Interf* pInterface)\n        {\n            if(m_pInterf != pInterface)\n            {\n                _Interf* pOldInterface = m_pInterf;\n                m_pInterf = pInterface;\n                InternalAcquire();\n                if(pOldInterface != NULL)\n                {\n                    pOldInterface->Release();\n                }\n            }\n            return *this;\n        }\n\n        AMFInterfacePtr_T& operator=(const AMFInterfacePtr_T<_Interf>& cp)\n        {\n            return operator=(cp.m_pInterf);\n        }\n\n        void Attach(_Interf* pInterface)\n        {\n            InternalRelease();\n            m_pInterf = pInterface;\n        }\n\n        _Interf* Detach()\n        {\n            _Interf* const pOld = m_pInterf;\n            m_pInterf = NULL;\n            return pOld;\n        }\n        void Release()\n        {\n            InternalRelease();\n            m_pInterf = NULL;\n        }\n\n        operator _Interf*() const\n        {\n            return m_pInterf;\n        }\n\n        _Interf& operator*() const\n        {\n            return *m_pInterf;\n        }\n\n        // Returns the address of the interface pointer contained in this\n        // class. This is required for initializing from C-style factory function to\n        // avoid getting an incorrect ref count at the beginning.\n\n        _Interf** operator&()\n        {\n            InternalRelease();\n            m_pInterf = 0;\n            return &m_pInterf;\n        }\n\n        _Interf* operator->() const\n        {\n            return m_pInterf;\n        }\n\n        bool operator==(const AMFInterfacePtr_T<_Interf>& p)\n        {\n            return (m_pInterf == p.m_pInterf);\n        }\n\n        bool operator==(_Interf* p)\n        {\n            return (m_pInterf == p);\n        }\n\n        bool operator!=(const AMFInterfacePtr_T<_Interf>& p)\n        {\n            return !(operator==(p));\n        }\n        bool operator!=(_Interf* p)\n        {\n            return !(operator==(p));\n        }\n\n        _Interf* GetPtr()\n        {\n            return m_pInterf;\n        }\n\n        const _Interf* GetPtr() const\n        {\n            return m_pInterf;\n        }\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFInterface> AMFInterfacePtr;\n    //----------------------------------------------------------------------------------------------\n#endif\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif //#ifndef AMF_Interface_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Plane.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Plane_h\n#define AMF_Plane_h\n#pragma once\n\n#include \"Interface.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //---------------------------------------------------------------------------------------------\n    typedef enum AMF_PLANE_TYPE\n    {\n        AMF_PLANE_UNKNOWN       = 0,\n        AMF_PLANE_PACKED        = 1,             // for all packed formats: BGRA, YUY2, etc\n        AMF_PLANE_Y             = 2,\n        AMF_PLANE_UV            = 3,\n        AMF_PLANE_U             = 4,\n        AMF_PLANE_V             = 5,\n    } AMF_PLANE_TYPE;\n    //---------------------------------------------------------------------------------------------\n    // AMFPlane interface\n    //---------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFPlane : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xbede1aa6, 0xd8fa, 0x4625, 0x94, 0x65, 0x6c, 0x82, 0xc4, 0x37, 0x71, 0x2e)\n\n        virtual AMF_PLANE_TYPE      AMF_STD_CALL GetType() = 0;\n        virtual void*               AMF_STD_CALL GetNative() = 0;\n        virtual amf_int32           AMF_STD_CALL GetPixelSizeInBytes() = 0;\n        virtual amf_int32           AMF_STD_CALL GetOffsetX() = 0;\n        virtual amf_int32           AMF_STD_CALL GetOffsetY() = 0;\n        virtual amf_int32           AMF_STD_CALL GetWidth() = 0;\n        virtual amf_int32           AMF_STD_CALL GetHeight() = 0;\n        virtual amf_int32           AMF_STD_CALL GetHPitch() = 0;\n        virtual amf_int32           AMF_STD_CALL GetVPitch() = 0;\n        virtual bool                AMF_STD_CALL IsTiled() = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFPlane> AMFPlanePtr;\n    //----------------------------------------------------------------------------------------------\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFPlane, 0xbede1aa6, 0xd8fa, 0x4625, 0x94, 0x65, 0x6c, 0x82, 0xc4, 0x37, 0x71, 0x2e)\n    typedef struct AMFPlane AMFPlane;\n    typedef struct AMFPlaneVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFPlane* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFPlane* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFPlane* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPlane interface\n        AMF_PLANE_TYPE      (AMF_STD_CALL *GetType)(AMFPlane* pThis);\n        void*               (AMF_STD_CALL *GetNative)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetPixelSizeInBytes)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetOffsetX)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetOffsetY)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetWidth)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetHeight)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetHPitch)(AMFPlane* pThis);\n        amf_int32           (AMF_STD_CALL *GetVPitch)(AMFPlane* pThis);\n        amf_bool            (AMF_STD_CALL *IsTiled)(AMFPlane* pThis);\n\n    } AMFPlaneVtbl;\n\n    struct AMFPlane\n    {\n        const AMFPlaneVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n#if defined(__cplusplus)\n} // namespace amf\n#endif\n\n#endif //#ifndef AMF_Plane_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Platform.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Platform_h\n#define AMF_Platform_h\n#pragma once\n\n//----------------------------------------------------------------------------------------------\n// export declaration\n//----------------------------------------------------------------------------------------------\n#if defined(_WIN32)\n    #if defined(AMF_CORE_STATIC)\n        #define AMF_CORE_LINK\n    #else\n        #if defined(AMF_CORE_EXPORTS)\n            #define AMF_CORE_LINK __declspec(dllexport)\n        #else\n            #define AMF_CORE_LINK __declspec(dllimport)\n        #endif\n    #endif\n#elif defined(__linux)\n        #if defined(AMF_CORE_EXPORTS)\n            #define AMF_CORE_LINK __attribute__((visibility(\"default\")))\n        #else\n            #define AMF_CORE_LINK\n        #endif\n#else\n    #define AMF_CORE_LINK\n#endif // #ifdef _WIN32\n\n#if defined(_DEBUG) && !defined(DEBUG) // prevents other headers to #define DEBUG without assigned value what causes failure in PAL build\n#define DEBUG 1\n#endif\n\n#define AMF_MACRO_STRING2(x) #x\n#define AMF_MACRO_STRING(x) AMF_MACRO_STRING2(x)\n\n#define AMF_TODO(_todo) (__FILE__ \"(\" AMF_MACRO_STRING(__LINE__) \"): TODO: \"_todo)\n\n\n #if defined(__GNUC__) || defined(__clang__)\n     #define AMF_ALIGN(n) __attribute__((aligned(n)))\n #elif defined(_MSC_VER) || defined(__INTEL_COMPILER)\n     #define AMF_ALIGN(n) __declspec(align(n))\n #else\n    #define AMF_ALIGN(n)\n//     #error Need to define AMF_ALIGN\n #endif\n\n#ifndef _WIN32\ntypedef signed int HRESULT;\n#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)\n#define FAILED(hr) (((HRESULT)(hr)) < 0)\n#endif\n\n#include <stdio.h>\n#include <stdint.h>\n#include <string.h>\n\n#if defined(_WIN32)\n\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n    #define AMF_STD_CALL            __stdcall\n    #define AMF_CDECL_CALL          __cdecl\n    #define AMF_FAST_CALL           __fastcall\n#if defined(__GNUC__) || defined(__clang__)\n    #define AMF_INLINE              inline\n    #define AMF_FORCEINLINE         inline\n#else\n    #define AMF_INLINE              __inline\n    #define AMF_FORCEINLINE         __forceinline\n#endif\n    #define AMF_NO_VTABLE           __declspec(novtable)\n\n    #define AMFPRId64   \"I64d\"\n    #define LPRId64    L\"I64d\"\n\n    #define AMFPRIud64   \"Iu64d\"\n    #define LPRIud64    L\"Iu64d\"\n\n    #define AMFPRIx64   \"I64x\"\n    #define LPRIx64    L\"I64x\"\n\n#else // !WIN32 - Linux and Mac\n\n    #define AMF_STD_CALL\n    #define AMF_CDECL_CALL\n    #define AMF_FAST_CALL\n#if defined(__GNUC__) || defined(__clang__)\n    #define AMF_INLINE              inline\n    #define AMF_FORCEINLINE         inline\n#else\n    #define AMF_INLINE              __inline__\n    #define AMF_FORCEINLINE         __inline__\n#endif\n    #define AMF_NO_VTABLE\n\n    #if !defined(AMFPRId64)\n        #define AMFPRId64    \"lld\"\n        #define LPRId64     L\"lld\"\n\n        #define AMFPRIud64    \"ulld\"\n        #define LPRIud64     L\"ulld\"\n\n        #define AMFPRIx64    \"llx\"\n        #define LPRIx64     L\"llx\"\n    #endif\n\n#endif // WIN32\n\n\n#if defined(_WIN32)\n#define AMF_WEAK __declspec( selectany )\n#elif defined (__GNUC__) || defined (__GCC__) || defined(__clang__)//GCC or CLANG\n#define AMF_WEAK __attribute__((weak))\n#endif\n\n#define amf_countof(x) (sizeof(x) / sizeof(x[0]))\n\n//-------------------------------------------------------------------------------------------------\n// basic data types\n//-------------------------------------------------------------------------------------------------\ntypedef     int64_t             amf_int64;\ntypedef     int32_t             amf_int32;\ntypedef     int16_t             amf_int16;\ntypedef     int8_t              amf_int8;\n\ntypedef     uint64_t            amf_uint64;\ntypedef     uint32_t            amf_uint32;\ntypedef     uint16_t            amf_uint16;\ntypedef     uint8_t             amf_uint8;\ntypedef     size_t              amf_size;\n\ntypedef     void*               amf_handle;\ntypedef     double              amf_double;\ntypedef     float               amf_float;\n\ntypedef     void                amf_void;\n\n#if defined(__cplusplus)\ntypedef     bool                amf_bool;\n#else\ntypedef     amf_uint8           amf_bool;\n#define     true                1\n#define     false               0\n#endif\n\ntypedef     long                amf_long;\ntypedef     int                 amf_int;\ntypedef     unsigned long       amf_ulong;\ntypedef     unsigned int        amf_uint;\n\ntypedef     amf_int64           amf_pts;     // in 100 nanosecs\n\ntypedef amf_uint32              amf_flags;\n\n#define AMF_SECOND          10000000L    // 1 second in 100 nanoseconds\n#define AMF_MILLISECOND\t\t(AMF_SECOND / 1000)\n#define AMF_MICROSECOND     (AMF_MILLISECOND / 1000)\n\n#define AMF_MIN(a, b) ((a) < (b) ? (a) : (b))\n#define AMF_MAX(a, b) ((a) > (b) ? (a) : (b))\n#define AMF_CLAMP(x, a, b) (AMF_MIN(AMF_MAX(x, a), b))\n\n#define AMF_BITS_PER_BYTE 8\n\n#if defined(_WIN32)\n    #define PATH_SEPARATOR_WSTR         L\"\\\\\"\n    #define PATH_SEPARATOR_WCHAR        L'\\\\'\n#elif defined(__linux) || defined(__APPLE__) // Linux & Apple\n    #define PATH_SEPARATOR_WSTR          L\"/\"\n    #define PATH_SEPARATOR_WCHAR         L'/'\n#endif\n\ntypedef struct AMFRect\n{\n    amf_int32 left;\n    amf_int32 top;\n    amf_int32 right;\n    amf_int32 bottom;\n#if defined(__cplusplus)\n    bool operator==(const AMFRect& other) const\n    {\n         return left == other.left && top == other.top && right == other.right && bottom == other.bottom;\n    }\n    AMF_INLINE bool operator!=(const AMFRect& other) const { return !operator==(other); }\n    amf_int32 Width() const { return right - left; }\n    amf_int32 Height() const { return bottom - top; }\n#endif\n} AMFRect;\n\nstatic AMF_INLINE struct AMFRect AMFConstructRect(amf_int32 left, amf_int32 top, amf_int32 right, amf_int32 bottom)\n{\n    struct AMFRect object = {left, top, right, bottom};\n    return object;\n}\n\ntypedef struct AMFSize\n{\n    amf_int32 width;\n    amf_int32 height;\n#if defined(__cplusplus)\n    bool operator==(const AMFSize& other) const\n    {\n         return width == other.width && height == other.height;\n    }\n    AMF_INLINE bool operator!=(const AMFSize& other) const { return !operator==(other); }\n#endif\n} AMFSize;\n\nstatic AMF_INLINE struct AMFSize AMFConstructSize(amf_int32 width, amf_int32 height)\n{\n    struct AMFSize object = {width, height};\n    return object;\n}\n\ntypedef struct AMFPoint\n{\n    amf_int32 x;\n    amf_int32 y;\n#if defined(__cplusplus)\n    bool operator==(const AMFPoint& other) const\n    {\n         return x == other.x && y == other.y;\n    }\n    AMF_INLINE bool operator!=(const AMFPoint& other) const { return !operator==(other); }\n#endif\n} AMFPoint;\n\nstatic AMF_INLINE struct AMFPoint AMFConstructPoint(amf_int32 x, amf_int32 y)\n{\n    struct AMFPoint object = { x, y };\n    return object;\n}\n\ntypedef struct AMFFloatPoint2D\n{\n    amf_float x;\n    amf_float y;\n#if defined(__cplusplus)\n    bool operator==(const AMFFloatPoint2D& other) const\n    {\n        return x == other.x && y == other.y;\n    }\n    AMF_INLINE bool operator!=(const AMFFloatPoint2D& other) const { return !operator==(other); }\n#endif\n} AMFFloatPoint2D;\n\nstatic AMF_INLINE struct AMFFloatPoint2D AMFConstructFloatPoint2D(amf_float x, amf_float y)\n{\n    struct AMFFloatPoint2D object = {x, y};\n    return object;\n}\ntypedef struct AMFFloatSize\n{\n    amf_float width;\n    amf_float height;\n#if defined(__cplusplus)\n    bool operator==(const AMFFloatSize& other) const\n    {\n        return width == other.width && height == other.height;\n    }\n    AMF_INLINE bool operator!=(const AMFFloatSize& other) const { return !operator==(other); }\n#endif\n} AMFFloatSize;\n\nstatic AMF_INLINE struct AMFFloatSize AMFConstructFloatSize(amf_float w, amf_float h)\n{\n    struct AMFFloatSize object = { w, h };\n    return object;\n}\n\n\ntypedef struct AMFFloatPoint3D\n{\n    amf_float x;\n    amf_float y;\n    amf_float z;\n#if defined(__cplusplus)\n    bool operator==(const AMFFloatPoint3D& other) const\n    {\n        return x == other.x && y == other.y && z == other.z;\n    }\n    AMF_INLINE bool operator!=(const AMFFloatPoint3D& other) const { return !operator==(other); }\n#endif\n} AMFFloatPoint3D;\n\nstatic AMF_INLINE struct AMFFloatPoint3D AMFConstructFloatPoint3D(amf_float x, amf_float y, amf_float z)\n{\n    struct AMFFloatPoint3D object = { x, y, z };\n    return object;\n}\n\ntypedef struct AMFFloatVector4D\n{\n    amf_float x;\n    amf_float y;\n    amf_float z;\n    amf_float w;\n#if defined(__cplusplus)\n    bool operator==(const AMFFloatVector4D& other) const\n    {\n        return x == other.x && y == other.y && z == other.z && w == other.w;\n    }\n    AMF_INLINE bool operator!=(const AMFFloatVector4D& other) const { return !operator==(other); }\n#endif\n} AMFFloatVector4D;\n\nstatic AMF_INLINE struct AMFFloatVector4D AMFConstructFloatVector4D(amf_float x, amf_float y, amf_float z, amf_float w)\n{\n    struct AMFFloatVector4D object = { x, y, z, w };\n    return object;\n}\n\n\ntypedef struct AMFRate\n{\n    amf_uint32 num;\n    amf_uint32 den;\n#if defined(__cplusplus)\n    bool operator==(const AMFRate& other) const\n    {\n         return num == other.num && den == other.den;\n    }\n    AMF_INLINE bool operator!=(const AMFRate& other) const { return !operator==(other); }\n#endif\n} AMFRate;\n\nstatic AMF_INLINE struct AMFRate AMFConstructRate(amf_uint32 num, amf_uint32 den)\n{\n    struct AMFRate object = {num, den};\n    return object;\n}\n\ntypedef struct AMFRatio\n{\n    amf_uint32 num;\n    amf_uint32 den;\n#if defined(__cplusplus)\n    bool operator==(const AMFRatio& other) const\n    {\n         return num == other.num && den == other.den;\n    }\n    AMF_INLINE bool operator!=(const AMFRatio& other) const { return !operator==(other); }\n#endif\n} AMFRatio;\n\nstatic AMF_INLINE struct AMFRatio AMFConstructRatio(amf_uint32 num, amf_uint32 den)\n{\n    struct AMFRatio object = {num, den};\n    return object;\n}\n\n#pragma pack(push, 1)\n#if defined(_MSC_VER)\n    #pragma warning( push )\n    #pragma warning(disable : 4200)\n    #pragma warning(disable : 4201)\n#endif\n\ntypedef struct AMFColor\n{\n    union\n    {\n        struct\n        {\n            amf_uint8 r;\n            amf_uint8 g;\n            amf_uint8 b;\n            amf_uint8 a;\n        };\n        amf_uint32 rgba;\n    };\n#if defined(__cplusplus)\n    bool operator==(const AMFColor& other) const\n    {\n         return r == other.r && g == other.g && b == other.b && a == other.a;\n    }\n    AMF_INLINE bool operator!=(const AMFColor& other) const { return !operator==(other); }\n#endif\n} AMFColor;\n#if defined(_MSC_VER)\n    #pragma warning( pop )\n#endif\n#pragma pack(pop)\n\n\nstatic AMF_INLINE struct AMFColor AMFConstructColor(amf_uint8 r, amf_uint8 g, amf_uint8 b, amf_uint8 a)\n{\n    struct AMFColor object;\n    object.r = r;\n    object.g = g;\n    object.b = b;\n    object.a = a;\n    return object;\n}\n\n#if defined(_WIN32)\n    #include <combaseapi.h>\n\n    #if defined(__cplusplus)\n    extern \"C\"\n    {\n    #endif\n        // allocator\n        static AMF_INLINE void* AMF_CDECL_CALL amf_variant_alloc(amf_size count)\n        {\n            return CoTaskMemAlloc(count);\n        }\n        static AMF_INLINE void AMF_CDECL_CALL amf_variant_free(void* ptr)\n        {\n            CoTaskMemFree(ptr);\n        }\n    #if defined(__cplusplus)\n    }\n    #endif\n\n#else // defined(_WIN32)\n    #include <stdlib.h>\n    #if defined(__cplusplus)\n    extern \"C\"\n    {\n    #endif\n        // allocator\n        static AMF_INLINE void* AMF_CDECL_CALL amf_variant_alloc(amf_size count)\n        {\n            return malloc(count);\n        }\n        static AMF_INLINE void AMF_CDECL_CALL amf_variant_free(void* ptr)\n        {\n            free(ptr);\n        }\n    #if defined(__cplusplus)\n    }\n    #endif\n#endif // defined(_WIN32)\n\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    typedef struct AMFGuid\n    {\n        amf_uint32 data1;\n        amf_uint16 data2;\n        amf_uint16 data3;\n        amf_uint8 data41;\n        amf_uint8 data42;\n        amf_uint8 data43;\n        amf_uint8 data44;\n        amf_uint8 data45;\n        amf_uint8 data46;\n        amf_uint8 data47;\n        amf_uint8 data48;\n#if defined(__cplusplus)\n        AMFGuid(amf_uint32 _data1, amf_uint16 _data2, amf_uint16 _data3,\n                amf_uint8 _data41, amf_uint8 _data42, amf_uint8 _data43, amf_uint8 _data44,\n                amf_uint8 _data45, amf_uint8 _data46, amf_uint8 _data47, amf_uint8 _data48)\n            : data1 (_data1),\n            data2 (_data2),\n            data3 (_data3),\n            data41(_data41),\n            data42(_data42),\n            data43(_data43),\n            data44(_data44),\n            data45(_data45),\n            data46(_data46),\n            data47(_data47),\n            data48(_data48)\n        {}\n\n        bool operator==(const AMFGuid& other) const\n        {\n            return\n                data1 == other.data1 &&\n                data2 == other.data2 &&\n                data3 == other.data3 &&\n                data41 == other.data41 &&\n                data42 == other.data42 &&\n                data43 == other.data43 &&\n                data44 == other.data44 &&\n                data45 == other.data45 &&\n                data46 == other.data46 &&\n                data47 == other.data47 &&\n                data48 == other.data48;\n        }\n        AMF_INLINE bool operator!=(const AMFGuid& other) const { return !operator==(other); }\n#endif\n    } AMFGuid;\n\n#if defined(__cplusplus)\n    static AMF_INLINE bool AMFCompareGUIDs(const AMFGuid& guid1, const AMFGuid& guid2)\n    {\n        return guid1 == guid2;\n    }\n#else\n    static AMF_INLINE amf_bool AMFCompareGUIDs(const struct AMFGuid guid1, const struct AMFGuid guid2)\n    {\n        return memcmp(&guid1, &guid2, sizeof(guid1)) == 0;\n    }\n#endif\n#if defined(__cplusplus)\n}\n#endif\n\n#if defined(__APPLE__)\n//#include <MacTypes.h>\n\n#define media_status_t int\n#define ANativeWindow void\n#define JNIEnv void\n#define jobject int\n#define JavaVM void\n\n#endif\n\n#endif //#ifndef AMF_Platform_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/PropertyStorage.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_PropertyStorage_h\n#define AMF_PropertyStorage_h\n#pragma once\n\n#include \"Variant.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // AMFPropertyStorageObserver interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n\n    class AMF_NO_VTABLE AMFPropertyStorageObserver\n    {\n    public:\n        virtual void                AMF_STD_CALL OnPropertyChanged(const wchar_t* name) = 0;\n    };\n#else //#if defined(__cplusplus)\n    typedef struct AMFPropertyStorageObserver AMFPropertyStorageObserver;\n    typedef struct AMFPropertyStorageObserverVtbl\n    {\n        void                (AMF_STD_CALL *OnPropertyChanged)(AMFPropertyStorageObserver *pThis, const wchar_t* name);\n    } AMFPropertyStorageObserverVtbl;\n\n    struct AMFPropertyStorageObserver\n    {\n        const AMFPropertyStorageObserverVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMFPropertyStorage interface\n    //----------------------------------------------------------------------------------------------\n    class AMF_NO_VTABLE AMFPropertyStorage : public AMFInterface\n    {\n    public:\n        AMF_DECLARE_IID(0xc7cec05b, 0xcfb9, 0x48af, 0xac, 0xe3, 0xf6, 0x8d, 0xf8, 0x39, 0x5f, 0xe3)\n\n        virtual AMF_RESULT          AMF_STD_CALL SetProperty(const wchar_t* name, AMFVariantStruct value) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetProperty(const wchar_t* name, AMFVariantStruct* pValue) const = 0;\n\n        virtual amf_bool            AMF_STD_CALL HasProperty(const wchar_t* name) const = 0;\n        virtual amf_size            AMF_STD_CALL GetPropertyCount() const = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPropertyAt(amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue) const = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL Clear() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL AddTo(AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep) const= 0;\n        virtual AMF_RESULT          AMF_STD_CALL CopyTo(AMFPropertyStorage* pDest, amf_bool deep) const = 0;\n\n        virtual void                AMF_STD_CALL AddObserver(AMFPropertyStorageObserver* pObserver) = 0;\n        virtual void                AMF_STD_CALL RemoveObserver(AMFPropertyStorageObserver* pObserver) = 0;\n\n        template<typename _T>\n        AMF_RESULT                  AMF_STD_CALL SetProperty(const wchar_t* name, const _T& value);\n        template<typename _T>\n        AMF_RESULT                  AMF_STD_CALL GetProperty(const wchar_t* name, _T* pValue) const;\n        template<typename _T>\n        AMF_RESULT                  AMF_STD_CALL GetPropertyString(const wchar_t* name, _T* pValue) const;\n        template<typename _T>\n        AMF_RESULT                  AMF_STD_CALL GetPropertyWString(const wchar_t* name, _T* pValue) const;\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFPropertyStorage> AMFPropertyStoragePtr;\n    //----------------------------------------------------------------------------------------------\n\n#else // #if defined(__cplusplus)\n    typedef struct AMFPropertyStorage AMFPropertyStorage;\n        AMF_DECLARE_IID(AMFPropertyStorage, 0xc7cec05b, 0xcfb9, 0x48af, 0xac, 0xe3, 0xf6, 0x8d, 0xf8, 0x39, 0x5f, 0xe3)\n\n    typedef struct AMFPropertyStorageVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFPropertyStorage* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFPropertyStorage* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFPropertyStorage* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFPropertyStorage* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFPropertyStorage* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFPropertyStorage* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFPropertyStorage* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFPropertyStorage* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFPropertyStorage* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFPropertyStorage* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFPropertyStorage* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFPropertyStorage* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFPropertyStorage* pThis, AMFPropertyStorageObserver* pObserver);\n\n    } AMFPropertyStorageVtbl;\n\n    struct AMFPropertyStorage\n    {\n        const AMFPropertyStorageVtbl *pVtbl;\n    };\n\n    #define AMF_ASSIGN_PROPERTY_DATA(res, varType, pThis, name, val ) \\\n    { \\\n        AMFVariantStruct var = {0}; \\\n        AMFVariantAssign##varType(&var, val); \\\n        res = pThis->pVtbl->SetProperty(pThis, name, var ); \\\n    }\n\n    #define AMF_QUERY_INTERFACE(res, from, InterfaceTypeTo, to) \\\n    { \\\n        AMFGuid guid_##InterfaceTypeTo = IID_##InterfaceTypeTo(); \\\n        res = from->pVtbl->QueryInterface(from, &guid_##InterfaceTypeTo, (void**)&to); \\\n    }\n\n    #define AMF_ASSIGN_PROPERTY_INTERFACE(res, pThis, name, val) \\\n    { \\\n        AMFInterface *amf_interface; \\\n        AMFVariantStruct var; \\\n        res = AMFVariantInit(&var); \\\n        if (res == AMF_OK) \\\n        { \\\n            AMF_QUERY_INTERFACE(res, val, AMFInterface, amf_interface)\\\n            if (res == AMF_OK) \\\n            { \\\n                res = AMFVariantAssignInterface(&var, amf_interface); \\\n                amf_interface->pVtbl->Release(amf_interface); \\\n                if (res == AMF_OK) \\\n                { \\\n                    res = pThis->pVtbl->SetProperty(pThis, name, var); \\\n                } \\\n            } \\\n            AMFVariantClear(&var); \\\n        } \\\n    }\n\n    #define AMF_GET_PROPERTY_INTERFACE(res, pThis, name, TargetType, val) \\\n    { \\\n        AMFVariantStruct var; \\\n        res = AMFVariantInit(&var); \\\n        if (res != AMF_OK) \\\n        { \\\n            res = pThis->pVtbl->GetProperty(pThis, name, &var); \\\n            if (res == AMF_OK) \\\n            { \\\n                if (var.type == AMF_VARIANT_INTERFACE && AMFVariantInterface(&var)) \\\n                { \\\n                    AMF_QUERY_INTERFACE(res, AMFVariantInterface(&var), TargetType, val); \\\n                } \\\n                else \\\n                { \\\n                    res = AMF_INVALID_DATA_TYPE; \\\n                } \\\n            } \\\n        } \\\n        AMFVariantClear(&var); \\\n    }\n\n    #define AMF_ASSIGN_PROPERTY_TYPE(res, varType, dataType , pThis, name, val )  AMF_ASSIGN_PROPERTY_DATA(res, varType, pThis, name, (dataType)val)\n\n    #define AMF_ASSIGN_PROPERTY_INT64(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_TYPE(res, Int64, amf_int64, pThis, name, val)\n    #define AMF_ASSIGN_PROPERTY_DOUBLE(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_TYPE(res, Double, amf_double, pThis, name, val)\n    #define AMF_ASSIGN_PROPERTY_BOOL(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_TYPE(res, Bool, amf_bool, pThis, name, val)\n    #define AMF_ASSIGN_PROPERTY_RECT(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Rect, pThis, name, &val)\n    #define AMF_ASSIGN_PROPERTY_SIZE(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Size, pThis, name, &val)\n    #define AMF_ASSIGN_PROPERTY_POINT(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Point, pThis, name, &val)\n    #define AMF_ASSIGN_PROPERTY_RATE(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Rate, pThis, name, &val)\n    #define AMF_ASSIGN_PROPERTY_RATIO(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Ratio, pThis, name, &val)\n    #define AMF_ASSIGN_PROPERTY_COLOR(res, pThis, name, val ) AMF_ASSIGN_PROPERTY_DATA(res, Color, pThis, name, &val)\n\n#endif // #if defined(__cplusplus)\n\n\n#if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // template methods implementations\n    //----------------------------------------------------------------------------------------------\n    template<typename _T> inline\n    AMF_RESULT AMF_STD_CALL AMFPropertyStorage::SetProperty(const wchar_t* name, const _T& value)\n    {\n        AMF_RESULT err = SetProperty(name, static_cast<const AMFVariantStruct&>(AMFVariant(value)));\n        return err;\n    }\n    //----------------------------------------------------------------------------------------------\n    template<typename _T> inline\n    AMF_RESULT AMF_STD_CALL AMFPropertyStorage::GetProperty(const wchar_t* name, _T* pValue) const\n    {\n        AMFVariant var;\n        AMF_RESULT err = GetProperty(name, static_cast<AMFVariantStruct*>(&var));\n        if(err == AMF_OK)\n        {\n            *pValue = static_cast<_T>(var);\n        }\n        return err;\n    }\n    //----------------------------------------------------------------------------------------------\n    template<typename _T> inline\n    AMF_RESULT AMF_STD_CALL AMFPropertyStorage::GetPropertyString(const wchar_t* name, _T* pValue) const\n    {\n        AMFVariant var;\n        AMF_RESULT err = GetProperty(name, static_cast<AMFVariantStruct*>(&var));\n        if(err == AMF_OK)\n        {\n            *pValue = var.ToString().c_str();\n        }\n        return err;\n    }\n    //----------------------------------------------------------------------------------------------\n    template<typename _T> inline\n    AMF_RESULT AMF_STD_CALL AMFPropertyStorage::GetPropertyWString(const wchar_t* name, _T* pValue) const\n    {\n        AMFVariant var;\n        AMF_RESULT err = GetProperty(name, static_cast<AMFVariantStruct*>(&var));\n        if(err == AMF_OK)\n        {\n            *pValue = var.ToWString().c_str();\n        }\n        return err;\n    }\n    //----------------------------------------------------------------------------------------------\n    template<> inline\n    AMF_RESULT AMF_STD_CALL AMFPropertyStorage::GetProperty(const wchar_t* name,\n            AMFInterface** ppValue) const\n    {\n        AMFVariant var;\n        AMF_RESULT err = GetProperty(name, static_cast<AMFVariantStruct*>(&var));\n        if(err == AMF_OK)\n        {\n            *ppValue = static_cast<AMFInterface*>(var);\n        }\n        if(*ppValue)\n        {\n            (*ppValue)->Acquire();\n        }\n        return err;\n    }\n#endif // #if defined(__cplusplus)\n\n#if defined(__cplusplus)\n} //namespace amf\n#endif\n\n#endif // #ifndef AMF_PropertyStorage_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/PropertyStorageEx.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_PropertyStorageEx_h\n#define AMF_PropertyStorageEx_h\n#pragma once\n\n#include \"PropertyStorage.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_PROPERTY_CONTENT_ENUM\n    {\n        AMF_PROPERTY_CONTENT_DEFAULT = 0,\n        AMF_PROPERTY_CONTENT_XML,               // m_eType is AMF_VARIANT_STRING\n\n        AMF_PROPERTY_CONTENT_FILE_OPEN_PATH,    // m_eType AMF_VARIANT_WSTRING\n        AMF_PROPERTY_CONTENT_FILE_SAVE_PATH,    // m_eType AMF_VARIANT_WSTRING\n\n        AMF_PROPERTY_CONTENT_INTEGER_ARRAY,     // m_eType AMF_VARIANT_INTERFACE\n        AMF_PROPERTY_CONTENT_FLOAT_ARRAY        // m_eType AMF_VARIANT_INTERFACE\n    } AMF_PROPERTY_CONTENT_ENUM;\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_PROPERTY_ACCESS_TYPE\n    {\n        AMF_PROPERTY_ACCESS_PRIVATE             = 0,\n        AMF_PROPERTY_ACCESS_READ                = 0x1,\n        AMF_PROPERTY_ACCESS_WRITE               = 0x2,\n        AMF_PROPERTY_ACCESS_READ_WRITE          = (AMF_PROPERTY_ACCESS_READ | AMF_PROPERTY_ACCESS_WRITE),\n        AMF_PROPERTY_ACCESS_WRITE_RUNTIME       = 0x4,\n        AMF_PROPERTY_ACCESS_FULL                = 0xFF,\n\t\tAMF_PROPERTY_ACCESS_NON_PERSISTANT\t\t= 0x4000,\n\t\tAMF_PROPERTY_ACCESS_NON_PERSISTANT_READ = (AMF_PROPERTY_ACCESS_NON_PERSISTANT | AMF_PROPERTY_ACCESS_READ),\n\t\tAMF_PROPERTY_ACCESS_NON_PERSISTANT_READ_WRITE = (AMF_PROPERTY_ACCESS_NON_PERSISTANT | AMF_PROPERTY_ACCESS_READ_WRITE),\n\t\tAMF_PROPERTY_ACCESS_NON_PERSISTANT_FULL = (AMF_PROPERTY_ACCESS_NON_PERSISTANT | AMF_PROPERTY_ACCESS_FULL),\n\t\tAMF_PROPERTY_ACCESS_INVALID\t\t\t\t= 0x8000\n    } AMF_PROPERTY_ACCESS_TYPE;\n\n    //----------------------------------------------------------------------------------------------\n    typedef struct AMFEnumDescriptionEntry\n    {\n        amf_int             value;\n        const wchar_t*      name;\n    } AMFEnumDescriptionEntry;\n    //----------------------------------------------------------------------------------------------\n    typedef amf_uint32 AMF_PROPERTY_CONTENT_TYPE;\n\n    typedef struct AMFPropertyInfo\n    {\n        const wchar_t*                  name;\n        const wchar_t*                  desc;\n        AMF_VARIANT_TYPE                type;\n        AMF_PROPERTY_CONTENT_TYPE       contentType;\n\n        AMFVariantStruct                defaultValue;\n        AMFVariantStruct                minValue;\n        AMFVariantStruct                maxValue;\n        AMF_PROPERTY_ACCESS_TYPE        accessType;\n        const AMFEnumDescriptionEntry*  pEnumDescription;\n\n#if defined(__cplusplus)\n        AMFPropertyInfo() :\n            name(NULL),\n            desc(NULL),\n            type(),\n            contentType(),\n            defaultValue(),\n            minValue(),\n            maxValue(),\n            accessType(AMF_PROPERTY_ACCESS_FULL),\n            pEnumDescription(NULL)\n        {}\n        AMFPropertyInfo(const AMFPropertyInfo& propery) : name(propery.name),\n            desc(propery.desc),\n            type(propery.type),\n            contentType(propery.contentType),\n            defaultValue(propery.defaultValue),\n            minValue(propery.minValue),\n            maxValue(propery.maxValue),\n            accessType(propery.accessType),\n            pEnumDescription(propery.pEnumDescription)\n        {}\n        virtual ~AMFPropertyInfo(){}\n\n        amf_bool AMF_STD_CALL AllowedRead() const\n        {\n            return (accessType & AMF_PROPERTY_ACCESS_READ) != 0;\n        }\n        amf_bool AMF_STD_CALL AllowedWrite() const\n        {\n            return (accessType & AMF_PROPERTY_ACCESS_WRITE) != 0;\n        }\n        amf_bool AMF_STD_CALL AllowedChangeInRuntime() const\n        {\n            return (accessType & AMF_PROPERTY_ACCESS_WRITE_RUNTIME) != 0;\n        }\n\n        AMFPropertyInfo& operator=(const AMFPropertyInfo& propery)\n        {\n            name = propery.name;\n            desc = propery.desc;\n            type = propery.type;\n            contentType = propery.contentType;\n            defaultValue = propery.defaultValue;\n            minValue = propery.minValue;\n            maxValue = propery.maxValue;\n            accessType = propery.accessType;\n            pEnumDescription = propery.pEnumDescription;\n\n            return *this;\n        }\n#endif // #if defined(__cplusplus)\n    } AMFPropertyInfo;\n    //----------------------------------------------------------------------------------------------\n    // AMFPropertyStorageEx interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFPropertyStorageEx : public AMFPropertyStorage\n    {\n    public:\n        AMF_DECLARE_IID(0x16b8958d, 0xe943, 0x4a33, 0xa3, 0x5a, 0x88, 0x5a, 0xd8, 0x28, 0xf2, 0x67)\n\n        virtual amf_size            AMF_STD_CALL GetPropertiesInfoCount() const = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPropertyInfo(amf_size index, const AMFPropertyInfo** ppInfo) const = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPropertyInfo(const wchar_t* name, const AMFPropertyInfo** ppInfo) const = 0;\n        virtual AMF_RESULT          AMF_STD_CALL ValidateProperty(const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated) const = 0;\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFPropertyStorageEx> AMFPropertyStorageExPtr;\n#else // #if defined(__cplusplus)\n    AMF_DECLARE_IID(AMFPropertyStorageEx, 0x16b8958d, 0xe943, 0x4a33, 0xa3, 0x5a, 0x88, 0x5a, 0xd8, 0x28, 0xf2, 0x67)\n    typedef struct AMFPropertyStorageEx AMFPropertyStorageEx;\n\n    typedef struct AMFPropertyStorageExVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFPropertyStorageEx* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFPropertyStorageEx* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFPropertyStorageEx* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFPropertyStorageEx* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFPropertyStorageEx* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFPropertyStorageEx* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFPropertyStorageEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFPropertyStorageEx* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFPropertyStorageEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFPropertyStorageEx* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFPropertyStorageEx* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFPropertyStorageEx* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFPropertyStorageEx* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFPropertyStorageEx interface\n\n        amf_size            (AMF_STD_CALL *GetPropertiesInfoCount)(AMFPropertyStorageEx* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfoAt)(AMFPropertyStorageEx* pThis, amf_size index, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyInfo)(AMFPropertyStorageEx* pThis, const wchar_t* name, const AMFPropertyInfo** ppInfo);\n        AMF_RESULT          (AMF_STD_CALL *ValidateProperty)(AMFPropertyStorageEx* pThis, const wchar_t* name, AMFVariantStruct value, AMFVariantStruct* pOutValidated);\n\n    } AMFPropertyStorageExVtbl;\n\n    struct AMFPropertyStorageEx\n    {\n        const AMFPropertyStorageExVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n} //namespace amf\n#endif\n\n\n#endif //#ifndef AMF_PropertyStorageEx_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Result.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Result_h\n#define AMF_Result_h\n#pragma once\n\n#include \"Platform.h\"\n\n//----------------------------------------------------------------------------------------------\n// result codes\n//----------------------------------------------------------------------------------------------\n\ntypedef enum AMF_RESULT\n{\n    AMF_OK                                   = 0,\n    AMF_FAIL                                    ,\n\n// common errors\n    AMF_UNEXPECTED                              ,\n\n    AMF_ACCESS_DENIED                           ,\n    AMF_INVALID_ARG                             ,\n    AMF_OUT_OF_RANGE                            ,\n\n    AMF_OUT_OF_MEMORY                           ,\n    AMF_INVALID_POINTER                         ,\n\n    AMF_NO_INTERFACE                            ,\n    AMF_NOT_IMPLEMENTED                         ,\n    AMF_NOT_SUPPORTED                           ,\n    AMF_NOT_FOUND                               ,\n\n    AMF_ALREADY_INITIALIZED                     ,\n    AMF_NOT_INITIALIZED                         ,\n\n    AMF_INVALID_FORMAT                          ,// invalid data format\n\n    AMF_WRONG_STATE                             ,\n    AMF_FILE_NOT_OPEN                           ,// cannot open file\n\n// device common codes\n    AMF_NO_DEVICE                               ,\n\n// device directx\n    AMF_DIRECTX_FAILED                          ,\n// device opencl \n    AMF_OPENCL_FAILED                           ,\n// device opengl \n    AMF_GLX_FAILED                              ,//failed to use GLX\n// device XV \n    AMF_XV_FAILED                               , //failed to use Xv extension\n// device alsa\n    AMF_ALSA_FAILED                             ,//failed to use ALSA\n\n// component common codes\n\n    //result codes\n    AMF_EOF                                     ,\n    AMF_REPEAT                                  ,\n    AMF_INPUT_FULL                              ,//returned by AMFComponent::SubmitInput if input queue is full\n    AMF_RESOLUTION_CHANGED                      ,//resolution changed client needs to Drain/Terminate/Init\n    AMF_RESOLUTION_UPDATED                      ,//resolution changed in adaptive mode. New ROI will be set on output on newly decoded frames\n\n    //error codes\n    AMF_INVALID_DATA_TYPE                       ,//invalid data type\n    AMF_INVALID_RESOLUTION                      ,//invalid resolution (width or height)\n    AMF_CODEC_NOT_SUPPORTED                     ,//codec not supported\n    AMF_SURFACE_FORMAT_NOT_SUPPORTED            ,//surface format not supported\n    AMF_SURFACE_MUST_BE_SHARED                  ,//surface should be shared (DX11: (MiscFlags & D3D11_RESOURCE_MISC_SHARED) == 0, DX9: No shared handle found)\n\n// component video decoder\n    AMF_DECODER_NOT_PRESENT                     ,//failed to create the decoder\n    AMF_DECODER_SURFACE_ALLOCATION_FAILED       ,//failed to create the surface for decoding\n    AMF_DECODER_NO_FREE_SURFACES                ,\n\n// component video encoder\n    AMF_ENCODER_NOT_PRESENT                     ,//failed to create the encoder\n\n// component video processor\n\n// component video conveter\n\n// component dem\n    AMF_DEM_ERROR                               ,\n    AMF_DEM_PROPERTY_READONLY                   ,\n    AMF_DEM_REMOTE_DISPLAY_CREATE_FAILED        ,\n    AMF_DEM_START_ENCODING_FAILED               ,\n    AMF_DEM_QUERY_OUTPUT_FAILED                 ,\n\n// component TAN\n    AMF_TAN_CLIPPING_WAS_REQUIRED               , // Resulting data was truncated to meet output type's value limits.\n    AMF_TAN_UNSUPPORTED_VERSION                 , // Not supported version requested, solely for TANCreateContext().\n\n    AMF_NEED_MORE_INPUT                         ,//returned by AMFComponent::SubmitInput did not produce a buffer because more input submissions are required.\n    \n    // device vulkan\n    AMF_VULKAN_FAILED                           ,\n} AMF_RESULT;\n\n#endif //#ifndef AMF_Result_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Surface.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Surface_h\n#define AMF_Surface_h\n#pragma once\n\n#include \"Data.h\"\n#include \"Plane.h\"\n\n#if defined(_MSC_VER)\n    #pragma warning( push )\n    #pragma warning(disable : 4263)\n    #pragma warning(disable : 4264)\n#endif\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_SURFACE_FORMAT\n    {\n        AMF_SURFACE_UNKNOWN     = 0,\n        AMF_SURFACE_NV12,               ///< 1  - planar 4:2:0 Y width x height + packed UV width/2 x height/2 - 8 bit per component\n        AMF_SURFACE_YV12,               ///< 2  - planar 4:2:0 Y width x height + V width/2 x height/2 + U width/2 x height/2 - 8 bit per component\n        AMF_SURFACE_BGRA,               ///< 3  - packed 4:4:4 - 8 bit per component\n        AMF_SURFACE_ARGB,               ///< 4  - packed 4:4:4 - 8 bit per component\n        AMF_SURFACE_RGBA,               ///< 5  - packed 4:4:4 - 8 bit per component\n        AMF_SURFACE_GRAY8,              ///< 6  - single component - 8 bit\n        AMF_SURFACE_YUV420P,            ///< 7  - planar 4:2:0 Y width x height + U width/2 x height/2 + V width/2 x height/2 - 8 bit per component\n        AMF_SURFACE_U8V8,               ///< 8  - packed double component - 8 bit per component\n        AMF_SURFACE_YUY2,               ///< 9  - packed 4:2:2 Byte 0=8-bit Y'0; Byte 1=8-bit Cb; Byte 2=8-bit Y'1; Byte 3=8-bit Cr\n        AMF_SURFACE_P010,               ///< 10 - planar 4:2:0 Y width x height + packed UV width/2 x height/2 - 10 bit per component (16 allocated, upper 10 bits are used)\n        AMF_SURFACE_RGBA_F16,           ///< 11 - packed 4:4:4 - 16 bit per component float\n        AMF_SURFACE_UYVY,               ///< 12 - packed 4:2:2 the similar to YUY2 but Y and UV swapped: Byte 0=8-bit Cb; Byte 1=8-bit Y'0; Byte 2=8-bit Cr Byte 3=8-bit Y'1; (used the same DX/CL/Vulkan storage as YUY2)\n        AMF_SURFACE_R10G10B10A2,        ///< 13 - packed 4:4:4 to 4 bytes, 10 bit per RGB component, 2 bits per A\n        AMF_SURFACE_Y210,               ///< 14 - packed 4:2:2 - Word 0=10-bit Y'0; Word 1=10-bit Cb; Word 2=10-bit Y'1; Word 3=10-bit Cr\n        AMF_SURFACE_AYUV,               ///< 15 - packed 4:4:4 - 8 bit per component YUVA\n        AMF_SURFACE_Y410,               ///< 16 - packed 4:4:4 - 10 bit per YUV component, 2 bits per A, AVYU\n        AMF_SURFACE_Y416,               ///< 16 - packed 4:4:4 - 16 bit per component 4 bytes, AVYU\n        AMF_SURFACE_GRAY32,             ///< 17 - single component - 32 bit\n        AMF_SURFACE_P012,               ///< 18 - planar 4:2:0 Y width x height + packed UV width/2 x height/2 - 12 bit per component (16 allocated, upper 12 bits are used)\n        AMF_SURFACE_P016,               ///< 19 - planar 4:2:0 Y width x height + packed UV width/2 x height/2 - 16 bit per component (16 allocated, all bits are used)\n\n        AMF_SURFACE_FIRST = AMF_SURFACE_NV12,\n        AMF_SURFACE_LAST = AMF_SURFACE_P016\n    } AMF_SURFACE_FORMAT;\n    //----------------------------------------------------------------------------------------------\n    // AMF_SURFACE_USAGE translates to D3D11_BIND_FLAG or VkImageUsageFlags\n    // bit mask\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_SURFACE_USAGE_BITS\n    {                                                       // D3D11                        D3D12                                       Vulkan\n        AMF_SURFACE_USAGE_DEFAULT           = 0x80000000,   // will apply default           D3D12_RESOURCE_FLAG_NONE                    VK_IMAGE_USAGE_TRANSFER_SRC_BIT| VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT\n        AMF_SURFACE_USAGE_NONE              = 0x00000000,   // 0,                           D3D12_RESOURCE_FLAG_NONE,                   0\n        AMF_SURFACE_USAGE_SHADER_RESOURCE   = 0x00000001,   // D3D11_BIND_SHADER_RESOURCE,\tD3D12_RESOURCE_FLAG_NONE\t\t\t\t\tVK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT\n        AMF_SURFACE_USAGE_RENDER_TARGET     = 0x00000002,   // D3D11_BIND_RENDER_TARGET,    D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n        AMF_SURFACE_USAGE_UNORDERED_ACCESS  = 0x00000004,   // D3D11_BIND_UNORDERED_ACCESS, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT\n        AMF_SURFACE_USAGE_TRANSFER_SRC      = 0x00000008,   //\t\t\t\t\t\t\t\tD3D12_RESOURCE_FLAG_NONE    \t            VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n        AMF_SURFACE_USAGE_TRANSFER_DST      = 0x00000010,   //\t\t\t\t\t\t\t\tD3D12_RESOURCE_FLAG_NONE\t\t            VK_IMAGE_USAGE_TRANSFER_DST_BIT\n        AMF_SURFACE_USAGE_LINEAR            = 0x00000020,   //\n        AMF_SURFACE_USAGE_NOSYNC            = 0x00000040,   //\t\t\t\t\t\t\t    no fence (AMFFenceGUID) created\t            no semaphore (AMFVulkanSync::hSemaphore) created\n        AMF_SURFACE_USAGE_DECODER_DST       = 0x00000080,   //\t\t\t\t\t\t\t    \t                                        VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR\n        AMF_SURFACE_USAGE_DECODER_DPB       = 0x00000100,   //\t\t\t\t\t\t\t    \t                                        VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR\n    } AMF_SURFACE_USAGE_BITS;\n    typedef amf_flags AMF_SURFACE_USAGE;\n    //----------------------------------------------------------------------------------------------\n\n#if defined(_WIN32)\n    AMF_WEAK GUID  AMFFormatGUID = { 0x8cd592d0, 0x8063, 0x4af8, {0xa7, 0xd0, 0x32, 0x5b, 0xc5, 0xf7, 0x48, 0xab}}; // UINT(AMF_SURFACE_FORMAT), default - AMF_SURFACE_UNKNOWN; to be set on ID3D11Texture2D objects when used natively (i.e. force UYVY on DXGI_FORMAT_YUY2 texture)\n#endif\n\n    //----------------------------------------------------------------------------------------------\n    // frame type\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_FRAME_TYPE\n    {\n        // flags\n        AMF_FRAME_STEREO_FLAG                           = 0x10000000,\n        AMF_FRAME_LEFT_FLAG                             = AMF_FRAME_STEREO_FLAG | 0x20000000,\n        AMF_FRAME_RIGHT_FLAG                            = AMF_FRAME_STEREO_FLAG | 0x40000000,\n        AMF_FRAME_BOTH_FLAG                             = AMF_FRAME_LEFT_FLAG | AMF_FRAME_RIGHT_FLAG,\n        AMF_FRAME_INTERLEAVED_FLAG                      = 0x01000000,\n        AMF_FRAME_FIELD_FLAG                            = 0x02000000,\n        AMF_FRAME_EVEN_FLAG                             = 0x04000000,\n        AMF_FRAME_ODD_FLAG                              = 0x08000000,\n\n        // values\n        AMF_FRAME_UNKNOWN                               =-1,\n        AMF_FRAME_PROGRESSIVE                           = 0,\n\n        AMF_FRAME_INTERLEAVED_EVEN_FIRST                = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_EVEN_FLAG,\n        AMF_FRAME_INTERLEAVED_ODD_FIRST                 = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_ODD_FLAG,\n        AMF_FRAME_FIELD_SINGLE_EVEN                     = AMF_FRAME_FIELD_FLAG | AMF_FRAME_EVEN_FLAG,\n        AMF_FRAME_FIELD_SINGLE_ODD                      = AMF_FRAME_FIELD_FLAG | AMF_FRAME_ODD_FLAG,\n\n        AMF_FRAME_STEREO_LEFT                           = AMF_FRAME_LEFT_FLAG,\n        AMF_FRAME_STEREO_RIGHT                          = AMF_FRAME_RIGHT_FLAG,\n        AMF_FRAME_STEREO_BOTH                           = AMF_FRAME_BOTH_FLAG,\n\n        AMF_FRAME_INTERLEAVED_EVEN_FIRST_STEREO_LEFT    = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_EVEN_FLAG | AMF_FRAME_LEFT_FLAG,\n        AMF_FRAME_INTERLEAVED_EVEN_FIRST_STEREO_RIGHT   = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_EVEN_FLAG | AMF_FRAME_RIGHT_FLAG,\n        AMF_FRAME_INTERLEAVED_EVEN_FIRST_STEREO_BOTH    = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_EVEN_FLAG | AMF_FRAME_BOTH_FLAG,\n\n        AMF_FRAME_INTERLEAVED_ODD_FIRST_STEREO_LEFT     = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_ODD_FLAG | AMF_FRAME_LEFT_FLAG,\n        AMF_FRAME_INTERLEAVED_ODD_FIRST_STEREO_RIGHT    = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_ODD_FLAG | AMF_FRAME_RIGHT_FLAG,\n        AMF_FRAME_INTERLEAVED_ODD_FIRST_STEREO_BOTH     = AMF_FRAME_INTERLEAVED_FLAG | AMF_FRAME_ODD_FLAG | AMF_FRAME_BOTH_FLAG,\n    } AMF_FRAME_TYPE;\n\n    typedef enum AMF_ROTATION_ENUM\n    {\n        AMF_ROTATION_NONE   = 0,\n        AMF_ROTATION_90     = 1,\n        AMF_ROTATION_180    = 2,\n        AMF_ROTATION_270    = 3,\n    } AMF_ROTATION_ENUM;\n\n    #define AMF_SURFACE_ROTATION         L\"Rotation\"    // amf_int64(AMF_ROTATION_ENUM); default = AMF_ROTATION_NONE, can be set on surfaces\n\n    //----------------------------------------------------------------------------------------------\n    // AMFSurfaceObserver interface - callback; is called before internal release resources.\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMFSurface;\n    class AMF_NO_VTABLE AMFSurfaceObserver\n    {\n    public:\n        virtual void AMF_STD_CALL OnSurfaceDataRelease(AMFSurface* pSurface) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFSurface AMFSurface;\n    typedef struct AMFSurfaceObserver AMFSurfaceObserver;\n\n    typedef struct AMFSurfaceObserverVtbl\n    {\n        void                (AMF_STD_CALL *OnSurfaceDataRelease)(AMFSurfaceObserver* pThis, AMFSurface* pSurface);\n    } AMFSurfaceObserverVtbl;\n\n    struct AMFSurfaceObserver\n    {\n        const AMFSurfaceObserverVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFSurface interface\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFSurface : public AMFData\n    {\n    public:\n        AMF_DECLARE_IID(0x3075dbe3, 0x8718, 0x4cfa, 0x86, 0xfb, 0x21, 0x14, 0xc0, 0xa5, 0xa4, 0x51)\n\n        virtual AMF_SURFACE_FORMAT  AMF_STD_CALL GetFormat() = 0;\n\n        // do not store planes outside. should be used together with Surface\n        virtual amf_size            AMF_STD_CALL GetPlanesCount() = 0;\n        virtual AMFPlane*           AMF_STD_CALL GetPlaneAt(amf_size index) = 0;\n        virtual AMFPlane*           AMF_STD_CALL GetPlane(AMF_PLANE_TYPE type) = 0;\n\n        virtual AMF_FRAME_TYPE      AMF_STD_CALL GetFrameType() = 0;\n        virtual void                AMF_STD_CALL SetFrameType(AMF_FRAME_TYPE type) = 0;\n\n        virtual AMF_RESULT          AMF_STD_CALL SetCrop(amf_int32 x,amf_int32 y, amf_int32 width, amf_int32 height) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL CopySurfaceRegion(AMFSurface* pDest, amf_int32 dstX, amf_int32 dstY, amf_int32 srcX, amf_int32 srcY, amf_int32 width, amf_int32 height) = 0;\n\n        // Observer management\n#ifdef __clang__\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Woverloaded-virtual\"\n#endif\n        virtual void                AMF_STD_CALL AddObserver(AMFSurfaceObserver* pObserver) = 0;\n        virtual void                AMF_STD_CALL RemoveObserver(AMFSurfaceObserver* pObserver) = 0;\n#ifdef __clang__\n    #pragma clang diagnostic pop\n#endif\n\n    };\n    //----------------------------------------------------------------------------------------------\n    // smart pointer\n    //----------------------------------------------------------------------------------------------\n    typedef AMFInterfacePtr_T<AMFSurface> AMFSurfacePtr;\n    //----------------------------------------------------------------------------------------------\n#else // #if defined(__cplusplus)\n        AMF_DECLARE_IID(AMFSurface, 0x3075dbe3, 0x8718, 0x4cfa, 0x86, 0xfb, 0x21, 0x14, 0xc0, 0xa5, 0xa4, 0x51)\n    typedef struct AMFSurfaceVtbl\n    {\n        // AMFInterface interface\n        amf_long            (AMF_STD_CALL *Acquire)(AMFSurface* pThis);\n        amf_long            (AMF_STD_CALL *Release)(AMFSurface* pThis);\n        enum AMF_RESULT     (AMF_STD_CALL *QueryInterface)(AMFSurface* pThis, const struct AMFGuid *interfaceID, void** ppInterface);\n\n        // AMFPropertyStorage interface\n        AMF_RESULT          (AMF_STD_CALL *SetProperty)(AMFSurface* pThis, const wchar_t* name, AMFVariantStruct value);\n        AMF_RESULT          (AMF_STD_CALL *GetProperty)(AMFSurface* pThis, const wchar_t* name, AMFVariantStruct* pValue);\n        amf_bool            (AMF_STD_CALL *HasProperty)(AMFSurface* pThis, const wchar_t* name);\n        amf_size            (AMF_STD_CALL *GetPropertyCount)(AMFSurface* pThis);\n        AMF_RESULT          (AMF_STD_CALL *GetPropertyAt)(AMFSurface* pThis, amf_size index, wchar_t* name, amf_size nameSize, AMFVariantStruct* pValue);\n        AMF_RESULT          (AMF_STD_CALL *Clear)(AMFSurface* pThis);\n        AMF_RESULT          (AMF_STD_CALL *AddTo)(AMFSurface* pThis, AMFPropertyStorage* pDest, amf_bool overwrite, amf_bool deep);\n        AMF_RESULT          (AMF_STD_CALL *CopyTo)(AMFSurface* pThis, AMFPropertyStorage* pDest, amf_bool deep);\n        void                (AMF_STD_CALL *AddObserver)(AMFSurface* pThis, AMFPropertyStorageObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver)(AMFSurface* pThis, AMFPropertyStorageObserver* pObserver);\n\n        // AMFData interface\n\n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryType)(AMFSurface* pThis);\n\n        AMF_RESULT          (AMF_STD_CALL *Duplicate)(AMFSurface* pThis, AMF_MEMORY_TYPE type, AMFData** ppData);\n        AMF_RESULT          (AMF_STD_CALL *Convert)(AMFSurface* pThis, AMF_MEMORY_TYPE type); // optimal interop if possilble. Copy through host memory if needed\n        AMF_RESULT          (AMF_STD_CALL *Interop)(AMFSurface* pThis, AMF_MEMORY_TYPE type); // only optimal interop if possilble. No copy through host memory for GPU objects\n\n        AMF_DATA_TYPE       (AMF_STD_CALL *GetDataType)(AMFSurface* pThis);\n\n        amf_bool            (AMF_STD_CALL *IsReusable)(AMFSurface* pThis);\n\n        void                (AMF_STD_CALL *SetPts)(AMFSurface* pThis, amf_pts pts);\n        amf_pts             (AMF_STD_CALL *GetPts)(AMFSurface* pThis);\n        void                (AMF_STD_CALL *SetDuration)(AMFSurface* pThis, amf_pts duration);\n        amf_pts             (AMF_STD_CALL *GetDuration)(AMFSurface* pThis);\n\n        // AMFSurface interface\n\n        AMF_SURFACE_FORMAT  (AMF_STD_CALL *GetFormat)(AMFSurface* pThis);\n\n        // do not store planes outside. should be used together with Surface\n        amf_size            (AMF_STD_CALL *GetPlanesCount)(AMFSurface* pThis);\n        AMFPlane*           (AMF_STD_CALL *GetPlaneAt)(AMFSurface* pThis, amf_size index);\n        AMFPlane*           (AMF_STD_CALL *GetPlane)(AMFSurface* pThis, AMF_PLANE_TYPE type);\n\n        AMF_FRAME_TYPE      (AMF_STD_CALL *GetFrameType)(AMFSurface* pThis);\n        void                (AMF_STD_CALL *SetFrameType)(AMFSurface* pThis, AMF_FRAME_TYPE type);\n\n        AMF_RESULT          (AMF_STD_CALL *SetCrop)(AMFSurface* pThis, amf_int32 x,amf_int32 y, amf_int32 width, amf_int32 height);\n        AMF_RESULT          (AMF_STD_CALL *CopySurfaceRegion)(AMFSurface* pThis, AMFSurface* pDest, amf_int32 dstX, amf_int32 dstY, amf_int32 srcX, amf_int32 srcY, amf_int32 width, amf_int32 height);\n\n\n        // Observer management\n        void                (AMF_STD_CALL *AddObserver_Surface)(AMFSurface* pThis, AMFSurfaceObserver* pObserver);\n        void                (AMF_STD_CALL *RemoveObserver_Surface)(AMFSurface* pThis, AMFSurfaceObserver* pObserver);\n\n    } AMFSurfaceVtbl;\n\n    struct AMFSurface\n    {\n        const AMFSurfaceVtbl *pVtbl;\n    };\n#endif // #if defined(__cplusplus)\n#if defined(__cplusplus)\n}\n#endif\n#if defined(_MSC_VER)\n    #pragma warning( pop )\n#endif\n#endif //#ifndef AMF_Surface_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Trace.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Trace_h\n#define AMF_Trace_h\n#pragma once\n\n#include \"Platform.h\"\n#include \"Result.h\"\n#include \"Surface.h\"\n#include \"AudioBuffer.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // trace levels\n    //----------------------------------------------------------------------------------------------\n    #define AMF_TRACE_ERROR     0\n    #define AMF_TRACE_WARNING   1\n    #define AMF_TRACE_INFO      2 // default in sdk\n    #define AMF_TRACE_DEBUG     3\n    #define AMF_TRACE_TRACE     4\n\n    #define AMF_TRACE_TEST      5\n    #define AMF_TRACE_NOLOG     100\n\n    //----------------------------------------------------------------------------------------------\n    // available trace writers\n    //----------------------------------------------------------------------------------------------\n    #define AMF_TRACE_WRITER_CONSOLE            L\"Console\"\n    #define AMF_TRACE_WRITER_DEBUG_OUTPUT       L\"DebugOutput\"\n    #define AMF_TRACE_WRITER_FILE               L\"File\"\n\n    //----------------------------------------------------------------------------------------------\n    // AMFTraceWriter interface - callback\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFTraceWriter\n    {\n    public:\n        virtual void AMF_CDECL_CALL Write(const wchar_t* scope, const wchar_t* message) = 0;\n        virtual void AMF_CDECL_CALL Flush() = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFTraceWriter AMFTraceWriter;\n\n    typedef struct AMFTraceWriterVtbl\n    {\n        // AMFTraceWriter interface\n        void (AMF_CDECL_CALL *Write)(AMFTraceWriter* pThis, const wchar_t* scope, const wchar_t* message);\n        void (AMF_CDECL_CALL *Flush)(AMFTraceWriter* pThis);\n    } AMFTraceWriterVtbl;\n\n    struct AMFTraceWriter\n    {\n        const AMFTraceWriterVtbl *pVtbl;\n    };\n\n#endif // #if defined(__cplusplus)\n\n    //----------------------------------------------------------------------------------------------\n    // AMFTrace interface - singleton\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    class AMF_NO_VTABLE AMFTrace\n    {\n    public:\n        virtual  void               AMF_STD_CALL TraceW(const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope,amf_int32 countArgs, const wchar_t* format, ...) = 0;\n        virtual  void               AMF_STD_CALL Trace(const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope, const wchar_t* message, va_list* pArglist) = 0;\n\n        virtual amf_int32           AMF_STD_CALL SetGlobalLevel(amf_int32 level) = 0;\n        virtual amf_int32           AMF_STD_CALL GetGlobalLevel() = 0;\n\n        virtual amf_bool            AMF_STD_CALL EnableWriter(const wchar_t* writerID, bool enable) = 0;\n        virtual amf_bool            AMF_STD_CALL WriterEnabled(const wchar_t* writerID) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL TraceEnableAsync(amf_bool enable) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL TraceFlush() = 0;\n        virtual AMF_RESULT          AMF_STD_CALL SetPath(const wchar_t* path) = 0;\n        virtual AMF_RESULT          AMF_STD_CALL GetPath(wchar_t* path, amf_size* pSize) = 0;\n        virtual amf_int32           AMF_STD_CALL SetWriterLevel(const wchar_t* writerID, amf_int32 level) = 0;\n        virtual amf_int32           AMF_STD_CALL GetWriterLevel(const wchar_t* writerID) = 0;\n        virtual amf_int32           AMF_STD_CALL SetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope, amf_int32 level) = 0;\n        virtual amf_int32           AMF_STD_CALL GetWriterLevelForScope(const wchar_t* writerID, const wchar_t* scope) = 0;\n\n        virtual amf_int32           AMF_STD_CALL GetIndentation() = 0;\n        virtual void                AMF_STD_CALL Indent(amf_int32 addIndent) = 0;\n\n        virtual void                AMF_STD_CALL RegisterWriter(const wchar_t* writerID, AMFTraceWriter* pWriter, amf_bool enable) = 0;\n        virtual void                AMF_STD_CALL UnregisterWriter(const wchar_t* writerID) = 0;\n\n        virtual const wchar_t*      AMF_STD_CALL GetResultText(AMF_RESULT res) = 0;\n        virtual const wchar_t*      AMF_STD_CALL SurfaceGetFormatName(const AMF_SURFACE_FORMAT eSurfaceFormat) = 0;\n        virtual AMF_SURFACE_FORMAT  AMF_STD_CALL SurfaceGetFormatByName(const wchar_t* name) = 0;\n\n        virtual const wchar_t*      AMF_STD_CALL GetMemoryTypeName(const AMF_MEMORY_TYPE memoryType) = 0;\n        virtual AMF_MEMORY_TYPE     AMF_STD_CALL GetMemoryTypeByName(const wchar_t* name) = 0;\n\n        virtual const wchar_t*      AMF_STD_CALL GetSampleFormatName(const AMF_AUDIO_FORMAT eFormat) = 0;\n        virtual AMF_AUDIO_FORMAT    AMF_STD_CALL GetSampleFormatByName(const wchar_t* name) = 0;\n    };\n#else // #if defined(__cplusplus)\n    typedef struct AMFTrace AMFTrace;\n\n    typedef struct AMFTraceVtbl\n    {\n        // AMFTrace interface\n         void               (AMF_STD_CALL *TraceW)(AMFTrace* pThis, const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope,amf_int32 countArgs, const wchar_t* format, ...);\n         void               (AMF_STD_CALL *Trace)(AMFTrace* pThis, const wchar_t* src_path, amf_int32 line, amf_int32 level, const wchar_t* scope, const wchar_t* message, va_list* pArglist);\n\n        amf_int32           (AMF_STD_CALL *SetGlobalLevel)(AMFTrace* pThis, amf_int32 level);\n        amf_int32           (AMF_STD_CALL *GetGlobalLevel)(AMFTrace* pThis);\n\n        amf_bool            (AMF_STD_CALL *EnableWriter)(AMFTrace* pThis, const wchar_t* writerID, amf_bool enable);\n        amf_bool            (AMF_STD_CALL *WriterEnabled)(AMFTrace* pThis, const wchar_t* writerID);\n        AMF_RESULT          (AMF_STD_CALL *TraceEnableAsync)(AMFTrace* pThis, amf_bool enable);\n        AMF_RESULT          (AMF_STD_CALL *TraceFlush)(AMFTrace* pThis);\n        AMF_RESULT          (AMF_STD_CALL *SetPath)(AMFTrace* pThis, const wchar_t* path);\n        AMF_RESULT          (AMF_STD_CALL *GetPath)(AMFTrace* pThis, wchar_t* path, amf_size* pSize);\n        amf_int32           (AMF_STD_CALL *SetWriterLevel)(AMFTrace* pThis, const wchar_t* writerID, amf_int32 level);\n        amf_int32           (AMF_STD_CALL *GetWriterLevel)(AMFTrace* pThis, const wchar_t* writerID);\n        amf_int32           (AMF_STD_CALL *SetWriterLevelForScope)(AMFTrace* pThis, const wchar_t* writerID, const wchar_t* scope, amf_int32 level);\n        amf_int32           (AMF_STD_CALL *GetWriterLevelForScope)(AMFTrace* pThis, const wchar_t* writerID, const wchar_t* scope);\n\n        amf_int32           (AMF_STD_CALL *GetIndentation)(AMFTrace* pThis);\n        void                (AMF_STD_CALL *Indent)(AMFTrace* pThis, amf_int32 addIndent);\n\n        void                (AMF_STD_CALL *RegisterWriter)(AMFTrace* pThis, const wchar_t* writerID, AMFTraceWriter* pWriter, amf_bool enable);\n        void                (AMF_STD_CALL *UnregisterWriter)(AMFTrace* pThis, const wchar_t* writerID);\n\n        const wchar_t*      (AMF_STD_CALL *GetResultText)(AMFTrace* pThis, AMF_RESULT res);\n        const wchar_t*      (AMF_STD_CALL *SurfaceGetFormatName)(AMFTrace* pThis, const AMF_SURFACE_FORMAT eSurfaceFormat);\n        AMF_SURFACE_FORMAT  (AMF_STD_CALL *SurfaceGetFormatByName)(AMFTrace* pThis, const wchar_t* name);\n\n        const wchar_t*      (AMF_STD_CALL *GetMemoryTypeName)(AMFTrace* pThis, const AMF_MEMORY_TYPE memoryType);\n        AMF_MEMORY_TYPE     (AMF_STD_CALL *GetMemoryTypeByName)(AMFTrace* pThis, const wchar_t* name);\n\n        const wchar_t*      (AMF_STD_CALL *GetSampleFormatName)(AMFTrace* pThis, const AMF_AUDIO_FORMAT eFormat);\n        AMF_AUDIO_FORMAT    (AMF_STD_CALL *GetSampleFormatByName)(AMFTrace* pThis, const wchar_t* name);\n    } AMFTraceVtbl;\n\n    struct AMFTrace\n    {\n        const AMFTraceVtbl *pVtbl;\n    };\n\n#endif\n#if defined(__cplusplus)\n}\n#endif\n\n\n#endif // AMF_Trace_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Variant.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef AMF_Variant_h\n#define AMF_Variant_h\n#pragma once\n#if defined(_MSC_VER)\n    #pragma warning(disable: 4996)\n#endif\n\n#include \"Interface.h\"\n#include <locale.h>\n#include <wchar.h>\n#include <string.h>\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    //----------------------------------------------------------------------------------------------\n    // variant types\n    //----------------------------------------------------------------------------------------------\n    typedef enum AMF_VARIANT_TYPE\n    {\n        AMF_VARIANT_EMPTY           = 0,\n\n        AMF_VARIANT_BOOL            = 1,\n        AMF_VARIANT_INT64           = 2,\n        AMF_VARIANT_DOUBLE          = 3,\n\n        AMF_VARIANT_RECT            = 4,\n        AMF_VARIANT_SIZE            = 5,\n        AMF_VARIANT_POINT           = 6,\n        AMF_VARIANT_RATE            = 7,\n        AMF_VARIANT_RATIO           = 8,\n        AMF_VARIANT_COLOR           = 9,\n\n        AMF_VARIANT_STRING          = 10,  // value is char*\n        AMF_VARIANT_WSTRING         = 11,  // value is wchar_t*\n        AMF_VARIANT_INTERFACE       = 12,  // value is AMFInterface*\n        AMF_VARIANT_FLOAT           = 13,\n\n        AMF_VARIANT_FLOAT_SIZE      = 14,\n        AMF_VARIANT_FLOAT_POINT2D   = 15,\n        AMF_VARIANT_FLOAT_POINT3D   = 16,\n        AMF_VARIANT_FLOAT_VECTOR4D  = 17\n    } AMF_VARIANT_TYPE;\n    //----------------------------------------------------------------------------------------------\n    // variant struct\n   //----------------------------------------------------------------------------------------------\n    typedef struct AMFVariantStruct\n    {\n        AMF_VARIANT_TYPE            type;\n        union\n        {\n            amf_bool                boolValue;\n            amf_int64               int64Value;\n            amf_double              doubleValue;\n            char*                   stringValue;\n            wchar_t*                wstringValue;\n            AMFInterface*           pInterface;\n            struct AMFRect          rectValue;\n            struct AMFSize          sizeValue;\n            struct AMFPoint         pointValue;\n            struct AMFRate          rateValue;\n            struct AMFRatio         ratioValue;\n            struct AMFColor         colorValue;\n            amf_float               floatValue;\n            struct AMFFloatSize     floatSizeValue;\n            struct AMFFloatPoint2D  floatPoint2DValue;\n            struct AMFFloatPoint3D  floatPoint3DValue;\n            struct AMFFloatVector4D floatVector4DValue;\n        };\n    } AMFVariantStruct;\n    //----------------------------------------------------------------------------------------------\n    // variant accessors\n    //----------------------------------------------------------------------------------------------\n\n    static AMF_INLINE AMF_VARIANT_TYPE     AMF_STD_CALL AMFVariantGetType(const AMFVariantStruct* _variant) { return (_variant)->type; }\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_VARIANT_TYPE&    AMF_STD_CALL AMFVariantGetType(AMFVariantStruct* _variant) { return (_variant)->type; }\n#endif\n    static AMF_INLINE amf_bool             AMF_STD_CALL AMFVariantGetBool(const AMFVariantStruct* _variant) { return (_variant)->boolValue; }\n    static AMF_INLINE amf_int64            AMF_STD_CALL AMFVariantGetInt64(const AMFVariantStruct* _variant) { return (_variant)->int64Value; }\n    static AMF_INLINE amf_double           AMF_STD_CALL AMFVariantGetDouble(const AMFVariantStruct* _variant) { return (_variant)->doubleValue; }\n    static AMF_INLINE amf_float            AMF_STD_CALL AMFVariantGetFloat(const AMFVariantStruct* _variant) { return (_variant)->floatValue; }\n    static AMF_INLINE const char*          AMF_STD_CALL AMFVariantGetString(const AMFVariantStruct* _variant) { return (_variant)->stringValue; }\n    static AMF_INLINE const wchar_t*       AMF_STD_CALL AMFVariantGetWString(const AMFVariantStruct* _variant) { return (_variant)->wstringValue; }\n#if defined(__cplusplus)\n    static AMF_INLINE const AMFInterface*  AMF_STD_CALL AMFVariantGetInterface(const AMFVariantStruct* _variant) { return (_variant)->pInterface; }\n#endif\n    static AMF_INLINE AMFInterface*        AMF_STD_CALL AMFVariantGetInterface(AMFVariantStruct* _variant) { return (_variant)->pInterface; }\n\n#if defined(__cplusplus)\n    static AMF_INLINE const AMFRect &      AMF_STD_CALL AMFVariantGetRect (const AMFVariantStruct* _variant) { return (_variant)->rectValue; }\n    static AMF_INLINE const AMFSize &      AMF_STD_CALL AMFVariantGetSize (const AMFVariantStruct* _variant) { return (_variant)->sizeValue; }\n    static AMF_INLINE const AMFPoint&      AMF_STD_CALL AMFVariantGetPoint(const AMFVariantStruct* _variant) { return (_variant)->pointValue; }\n    static AMF_INLINE const AMFFloatSize& AMF_STD_CALL AMFVariantGetFloatSize(const AMFVariantStruct* _variant) { return (_variant)->floatSizeValue; }\n    static AMF_INLINE const AMFFloatPoint2D& AMF_STD_CALL AMFVariantGetFloatPoint2D(const AMFVariantStruct* _variant) { return (_variant)->floatPoint2DValue; }\n    static AMF_INLINE const AMFFloatPoint3D& AMF_STD_CALL AMFVariantGetFloatPoint3D(const AMFVariantStruct* _variant) { return (_variant)->floatPoint3DValue; }\n    static AMF_INLINE const AMFFloatVector4D& AMF_STD_CALL AMFVariantGetFloatVector4D(const AMFVariantStruct* _variant) { return (_variant)->floatVector4DValue; }\n    static AMF_INLINE const AMFRate &      AMF_STD_CALL AMFVariantGetRate (const AMFVariantStruct* _variant) { return (_variant)->rateValue; }\n    static AMF_INLINE const AMFRatio&      AMF_STD_CALL AMFVariantGetRatio(const AMFVariantStruct* _variant) { return (_variant)->ratioValue; }\n    static AMF_INLINE const AMFColor&      AMF_STD_CALL AMFVariantGetColor(const AMFVariantStruct* _variant) { return (_variant)->colorValue; }\n#else // #if defined(__cplusplus)\n    static AMF_INLINE const AMFRect        AMF_STD_CALL AMFVariantGetRect (const AMFVariantStruct* _variant) { return (_variant)->rectValue; }\n    static AMF_INLINE const AMFSize        AMF_STD_CALL AMFVariantGetSize (const AMFVariantStruct* _variant) { return (_variant)->sizeValue; }\n    static AMF_INLINE const AMFPoint       AMF_STD_CALL AMFVariantGetPoint(const AMFVariantStruct* _variant) { return (_variant)->pointValue; }\n    static AMF_INLINE const AMFFloatSize  AMF_STD_CALL AMFVariantGetFloatSize(const AMFVariantStruct* _variant) { return (_variant)->floatSizeValue; }\n    static AMF_INLINE const AMFFloatPoint2D  AMF_STD_CALL AMFVariantGetFloatPoint2D(const AMFVariantStruct* _variant) { return (_variant)->floatPoint2DValue; }\n    static AMF_INLINE const AMFFloatPoint3D  AMF_STD_CALL AMFVariantGetFloatPoint3D(const AMFVariantStruct* _variant) { return (_variant)->floatPoint3DValue; }\n    static AMF_INLINE const AMFFloatVector4D  AMF_STD_CALL AMFVariantGetFloatVector4D(const AMFVariantStruct* _variant) { return (_variant)->floatVector4DValue; }\n    static AMF_INLINE const AMFRate        AMF_STD_CALL AMFVariantGetRate (const AMFVariantStruct* _variant) { return (_variant)->rateValue; }\n    static AMF_INLINE const AMFRatio       AMF_STD_CALL AMFVariantGetRatio(const AMFVariantStruct* _variant) { return (_variant)->ratioValue; }\n    static AMF_INLINE const AMFColor       AMF_STD_CALL AMFVariantGetColor(const AMFVariantStruct* _variant) { return (_variant)->colorValue; }\n#endif // #if defined(__cplusplus)\n\n\n    #define AMFVariantEmpty(_variant)     0\n    #define AMFVariantBool(_variant)      (_variant)->boolValue\n    #define AMFVariantInt64(_variant)     (_variant)->int64Value\n    #define AMFVariantDouble(_variant)    (_variant)->doubleValue\n    #define AMFVariantFloat(_variant)    (_variant)->floatValue\n\n    #define AMFVariantRect(_variant)      (_variant)->rectValue\n    #define AMFVariantSize(_variant)      (_variant)->sizeValue\n    #define AMFVariantPoint(_variant)     (_variant)->pointValue\n    #define AMFVariantFloatSize(_variant)     (_variant)->floatSizeValue\n    #define AMFVariantFloatPoint2D(_variant)     (_variant)->floatPoint2DValue\n    #define AMFVariantFloatPoint3D(_variant)     (_variant)->floatPoint3DValue\n    #define AMFVariantFloatVector4D(_variant)     (_variant)->floatVector4DValue\n    #define AMFVariantRate(_variant)      (_variant)->rateValue\n    #define AMFVariantRatio(_variant)     (_variant)->ratioValue\n    #define AMFVariantColor(_variant)     (_variant)->colorValue\n\n    #define AMFVariantString(_variant)    (_variant)->stringValue\n    #define AMFVariantWString(_variant)   (_variant)->wstringValue\n    #define AMFVariantInterface(_variant) (_variant)->pInterface\n    //----------------------------------------------------------------------------------------------\n    // variant hleper functions\n    //----------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantInit(AMFVariantStruct* pVariant);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantClear(AMFVariantStruct* pVariant);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantCompare(const AMFVariantStruct* pFirst, const AMFVariantStruct* pSecond, amf_bool* pEqual);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantCopy(AMFVariantStruct* pDest, const AMFVariantStruct* pSrc);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignBool(AMFVariantStruct* pDest, amf_bool value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignInt64(AMFVariantStruct* pDest, amf_int64 value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignDouble(AMFVariantStruct* pDest, amf_double value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloat(AMFVariantStruct* pDest, amf_float value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignString(AMFVariantStruct* pDest, const char* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignWString(AMFVariantStruct* pDest, const wchar_t* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignInterface(AMFVariantStruct* pDest, AMFInterface* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRect(AMFVariantStruct* pDest, const AMFRect* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignSize(AMFVariantStruct* pDest, const AMFSize* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignPoint(AMFVariantStruct* pDest, const AMFPoint* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatSize(AMFVariantStruct* pDest, const AMFFloatSize* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatPoint2D(AMFVariantStruct* pDest, const AMFFloatPoint2D* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatPoint3D(AMFVariantStruct* pDest, const AMFFloatPoint3D* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatVector4D(AMFVariantStruct* pDest, const AMFFloatVector4D* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRate(AMFVariantStruct* pDest, const AMFRate* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRatio(AMFVariantStruct* pDest, const AMFRatio* pValue);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignColor(AMFVariantStruct* pDest, const AMFColor* pValue);\n\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRect(AMFVariantStruct* pDest, const AMFRect& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignSize(AMFVariantStruct* pDest, const AMFSize& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignPoint(AMFVariantStruct* pDest, const AMFPoint& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatSize(AMFVariantStruct* pDest, const AMFFloatSize& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatPoint2D(AMFVariantStruct* pDest, const AMFFloatPoint2D& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatPoint3D(AMFVariantStruct* pDest, const AMFFloatPoint3D& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignFloatVector4D(AMFVariantStruct* pDest, const AMFFloatVector4D& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRate(AMFVariantStruct* pDest, const AMFRate& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignRatio(AMFVariantStruct* pDest, const AMFRatio& value);\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantAssignColor(AMFVariantStruct* pDest, const AMFColor& value);\n\n    static AMF_INLINE AMF_RESULT       AMF_CDECL_CALL AMFVariantChangeType(AMFVariantStruct* pDest, const AMFVariantStruct* pSrc, AMF_VARIANT_TYPE newType);\n#endif\n    static AMF_INLINE char*            AMF_CDECL_CALL AMFVariantDuplicateString(const char* pFrom);\n    static AMF_INLINE void             AMF_CDECL_CALL AMFVariantFreeString(char* pFrom);\n    static AMF_INLINE wchar_t*         AMF_CDECL_CALL AMFVariantDuplicateWString(const wchar_t* pFrom);\n    static AMF_INLINE void             AMF_CDECL_CALL AMFVariantFreeWString(wchar_t* pFrom);\n\n#if defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMF_INLINE Variant helper class\n    //----------------------------------------------------------------------------------------------\n    class AMFVariant : public AMFVariantStruct\n    {\n    public:\n        class String;\n        class WString;\n\n    public:\n        AMFVariant() {  AMFVariantInit(this); }\n        explicit AMFVariant(const AMFVariantStruct& other) { AMFVariantInit(this); AMFVariantCopy(this, const_cast<AMFVariantStruct*>(&other)); }\n\n        explicit AMFVariant(const AMFVariantStruct* pOther);\n        template<typename T>\n        explicit AMFVariant(const AMFInterfacePtr_T<T>& pValue);\n\n        AMFVariant(const AMFVariant& other) { AMFVariantInit(this); AMFVariantCopy(this, const_cast<AMFVariantStruct*>(static_cast<const AMFVariantStruct*>(&other))); }\n\n        explicit AMF_INLINE AMFVariant(amf_bool value)          { AMFVariantInit(this); AMFVariantAssignBool(this, value); }\n        explicit AMF_INLINE AMFVariant(amf_int64 value)         { AMFVariantInit(this); AMFVariantAssignInt64(this, value); }\n        explicit AMF_INLINE AMFVariant(amf_uint64 value)        { AMFVariantInit(this); AMFVariantAssignInt64(this, (amf_int64)value); }\n        explicit AMF_INLINE AMFVariant(amf_int32 value)         { AMFVariantInit(this); AMFVariantAssignInt64(this, value); }\n        explicit AMF_INLINE AMFVariant(amf_uint32 value)        { AMFVariantInit(this); AMFVariantAssignInt64(this, value); }\n        explicit AMF_INLINE AMFVariant(amf_double value)        { AMFVariantInit(this); AMFVariantAssignDouble(this, value); }\n        explicit AMF_INLINE AMFVariant(amf_float value)         { AMFVariantInit(this); AMFVariantAssignFloat(this, value); }\n        explicit AMF_INLINE AMFVariant(const AMFRect & value)   { AMFVariantInit(this); AMFVariantAssignRect(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFSize & value)   { AMFVariantInit(this); AMFVariantAssignSize(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFPoint& value)   { AMFVariantInit(this); AMFVariantAssignPoint(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFFloatSize& value) { AMFVariantInit(this); AMFVariantAssignFloatSize(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFFloatPoint2D& value) { AMFVariantInit(this); AMFVariantAssignFloatPoint2D(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFFloatPoint3D& value) { AMFVariantInit(this); AMFVariantAssignFloatPoint3D(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFFloatVector4D& value) { AMFVariantInit(this); AMFVariantAssignFloatVector4D(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFRate & value)   { AMFVariantInit(this); AMFVariantAssignRate(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFRatio& value)   { AMFVariantInit(this); AMFVariantAssignRatio(this, &value); }\n        explicit AMF_INLINE AMFVariant(const AMFColor& value)   { AMFVariantInit(this); AMFVariantAssignColor(this, &value); }\n        explicit AMF_INLINE AMFVariant(const char* pValue)       { AMFVariantInit(this); AMFVariantAssignString(this, pValue); }\n        explicit AMF_INLINE AMFVariant(const wchar_t* pValue)    { AMFVariantInit(this); AMFVariantAssignWString(this, pValue); }\n        explicit AMF_INLINE AMFVariant(AMFInterface* pValue)    { AMFVariantInit(this); AMFVariantAssignInterface(this, pValue); }\n\n        ~AMFVariant() { AMFVariantClear(this); }\n\n        AMFVariant& operator=(const AMFVariantStruct& other);\n        AMFVariant& operator=(const AMFVariantStruct* pOther);\n        AMFVariant& operator=(const AMFVariant& other);\n\n        AMFVariant& operator=(amf_bool          value)      { AMFVariantAssignBool(this, value); return *this;}\n        AMFVariant& operator=(amf_int64         value)      { AMFVariantAssignInt64(this, value); return *this;}\n        AMFVariant& operator=(amf_uint64        value)      { AMFVariantAssignInt64(this, (amf_int64)value);  return *this;}\n        AMFVariant& operator=(amf_int32         value)      { AMFVariantAssignInt64(this, value);  return *this;}\n        AMFVariant& operator=(amf_uint32        value)      { AMFVariantAssignInt64(this, value);  return *this;}\n        AMFVariant& operator=(amf_double        value)      { AMFVariantAssignDouble(this, value);  return *this;}\n        AMFVariant& operator=(amf_float        value)       { AMFVariantAssignFloat(this, value);  return *this; }\n        AMFVariant& operator=(const AMFRect &   value)      { AMFVariantAssignRect(this, &value);  return *this;}\n        AMFVariant& operator=(const AMFSize &   value)      { AMFVariantAssignSize(this, &value);  return *this;}\n        AMFVariant& operator=(const AMFPoint&   value)      { AMFVariantAssignPoint(this, &value);  return *this;}\n        AMFVariant& operator=(const AMFFloatSize&   value) { AMFVariantAssignFloatSize(this, &value);  return *this; }\n        AMFVariant& operator=(const AMFFloatPoint2D&   value) { AMFVariantAssignFloatPoint2D(this, &value);  return *this; }\n        AMFVariant& operator=(const AMFFloatPoint3D&   value) { AMFVariantAssignFloatPoint3D(this, &value);  return *this; }\n        AMFVariant& operator=(const AMFFloatVector4D&   value) { AMFVariantAssignFloatVector4D(this, &value);  return *this; }\n        AMFVariant& operator=(const AMFRate &   value)      { AMFVariantAssignRate(this, &value);  return *this;}\n        AMFVariant& operator=(const AMFRatio&   value)      { AMFVariantAssignRatio(this, &value);  return *this;}\n        AMFVariant& operator=(const AMFColor&   value)      { AMFVariantAssignColor(this, &value);  return *this;}\n        AMFVariant& operator=(const char*       pValue)      { AMFVariantAssignString(this, pValue);  return *this;}\n        AMFVariant& operator=(const wchar_t*    pValue)      { AMFVariantAssignWString(this, pValue);  return *this;}\n        AMFVariant& operator=(AMFInterface*     pValue)      { AMFVariantAssignInterface(this, pValue);  return *this;}\n\n        template<typename T> AMFVariant& operator=(const AMFInterfacePtr_T<T>& value);\n\n        operator amf_bool() const           { return ToBool();       }\n        operator amf_int64() const          { return ToInt64();      }\n        operator amf_uint64() const         { return ToUInt64();     }\n        operator amf_int32() const          { return ToInt32();      }\n        operator amf_uint32() const         { return ToUInt32();     }\n        operator amf_double() const         { return ToDouble();     }\n        operator amf_float() const          { return ToFloat();      }\n        operator AMFRect () const           { return ToRect ();      }\n        operator AMFSize () const           { return ToSize ();      }\n        operator AMFPoint() const           { return ToPoint();      }\n        operator AMFFloatSize() const       { return ToFloatSize(); }\n        operator AMFFloatPoint2D() const    { return ToFloatPoint2D(); }\n        operator AMFFloatPoint3D() const    { return ToFloatPoint3D(); }\n        operator AMFFloatVector4D() const   { return ToFloatVector4D(); }\n        operator AMFRate () const           { return ToRate ();      }\n        operator AMFRatio() const           { return ToRatio();      }\n        operator AMFColor() const           { return ToColor();      }\n        operator AMFInterface*() const      { return ToInterface();  }\n\n        AMF_INLINE amf_bool         ToBool() const      { return Empty() ? false        : GetValue<amf_bool,   AMF_VARIANT_BOOL>(AMFVariantGetBool); }\n        AMF_INLINE amf_int64        ToInt64() const     { return Empty() ? 0            : GetValue<amf_int64,  AMF_VARIANT_INT64>(AMFVariantGetInt64); }\n        AMF_INLINE amf_uint64       ToUInt64() const    { return Empty() ? 0            : GetValue<amf_uint64, AMF_VARIANT_INT64>(AMFVariantGetInt64); }\n        AMF_INLINE amf_int32        ToInt32() const     { return Empty() ? 0            : GetValue<amf_int32,  AMF_VARIANT_INT64>(AMFVariantGetInt64); }\n        AMF_INLINE amf_uint32       ToUInt32() const    { return Empty() ? 0            : GetValue<amf_uint32, AMF_VARIANT_INT64>(AMFVariantGetInt64); }\n        AMF_INLINE amf_double       ToDouble() const    { return Empty() ? 0            : GetValue<amf_double, AMF_VARIANT_DOUBLE>(AMFVariantGetDouble); }\n        AMF_INLINE amf_float        ToFloat() const     { return Empty() ? 0            : GetValue<amf_float,  AMF_VARIANT_FLOAT>(AMFVariantGetFloat); }\n        AMF_INLINE AMFRect          ToRect () const     { return Empty() ? AMFRect()    : GetValue<AMFRect,  AMF_VARIANT_RECT>(AMFVariantGetRect); }\n        AMF_INLINE AMFSize          ToSize () const     { return Empty() ? AMFSize()    : GetValue<AMFSize,  AMF_VARIANT_SIZE>(AMFVariantGetSize); }\n        AMF_INLINE AMFPoint         ToPoint() const     { return Empty() ? AMFPoint()   : GetValue<AMFPoint, AMF_VARIANT_POINT>(AMFVariantGetPoint); }\n        AMF_INLINE AMFFloatSize     ToFloatSize() const { return Empty() ? AMFFloatSize() : GetValue<AMFFloatSize, AMF_VARIANT_FLOAT_SIZE>(AMFVariantGetFloatSize); }\n        AMF_INLINE AMFFloatPoint2D  ToFloatPoint2D() const { return Empty() ? AMFFloatPoint2D() : GetValue<AMFFloatPoint2D, AMF_VARIANT_FLOAT_POINT2D>(AMFVariantGetFloatPoint2D); }\n        AMF_INLINE AMFFloatPoint3D  ToFloatPoint3D() const { return Empty() ? AMFFloatPoint3D() : GetValue<AMFFloatPoint3D, AMF_VARIANT_FLOAT_POINT3D>(AMFVariantGetFloatPoint3D); }\n        AMF_INLINE AMFFloatVector4D ToFloatVector4D() const { return Empty() ? AMFFloatVector4D() : GetValue<AMFFloatVector4D, AMF_VARIANT_FLOAT_VECTOR4D>(AMFVariantGetFloatVector4D); }\n        AMF_INLINE AMFRate          ToRate () const     { return Empty() ? AMFRate()    : GetValue<AMFRate,  AMF_VARIANT_RATE>(AMFVariantGetRate); }\n        AMF_INLINE AMFRatio         ToRatio() const     { return Empty() ? AMFRatio()   : GetValue<AMFRatio, AMF_VARIANT_RATIO>(AMFVariantGetRatio); }\n        AMF_INLINE AMFColor         ToColor() const     { return Empty() ? AMFColor()   : GetValue<AMFColor, AMF_VARIANT_COLOR>(AMFVariantGetColor); }\n        AMF_INLINE AMFInterface*    ToInterface() const { return AMFVariantGetType(this) == AMF_VARIANT_INTERFACE ? this->pInterface : nullptr; }\n        AMF_INLINE String           ToString() const;\n        AMF_INLINE WString          ToWString() const;\n\n        bool operator==(const AMFVariantStruct& other) const;\n        bool operator==(const AMFVariantStruct* pOther) const;\n\n        bool operator!=(const AMFVariantStruct& other) const;\n        bool operator!=(const AMFVariantStruct* pOther) const;\n\n        void Clear() { AMFVariantClear(this); }\n\n        void Attach(AMFVariantStruct& variant);\n        AMFVariantStruct Detach();\n\n        AMFVariantStruct& GetVariant();\n\n        void ChangeType(AMF_VARIANT_TYPE type, const AMFVariant* pSrc = nullptr);\n\n        bool Empty() const;\n    private:\n        template<class ReturnType, AMF_VARIANT_TYPE variantType, typename Getter>\n        ReturnType GetValue(Getter getter) const;\n    };\n    //----------------------------------------------------------------------------------------------\n    // helper String class\n    //----------------------------------------------------------------------------------------------\n    class AMFVariant::String\n    {\n        friend class AMFVariant;\n    private:\n        void Free()\n        {\n            if (m_Str != nullptr)\n            {\n                AMFVariantFreeString(m_Str);\n                m_Str = nullptr;\n            }\n        }\n    public:\n        String() :m_Str(nullptr){}\n        String(const char* str) : m_Str(nullptr)\n        {\n            m_Str = AMFVariantDuplicateString(str);\n        }\n        String(const String& p_other) : m_Str(nullptr)\n        {\n            operator=(p_other);\n        }\n\n#if (__cplusplus == 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X) || (_MSC_VER >= 1600)\n#pragma warning (push)\n#pragma warning (disable : 26439) //This kind of function may not throw. Declare it 'noexcept'.\n        String(String&& p_other) : m_Str(nullptr)\n        {\n            operator=(p_other);\n        }\n#endif\n        ~String()\n        {\n            Free();\n        }\n\n        char& operator[](size_t index)\n        {\n            if (index >= size())\n            {\n                resize(index);\n            }\n            return m_Str[index];\n        }\n\n        String& operator=(const String& p_other)\n        {\n            Free();\n            m_Str = AMFVariantDuplicateString(p_other.m_Str);\n            return *this;\n        }\n#if (__cplusplus == 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X) || (_MSC_VER >= 1600)\n        String& operator=(String&& p_other)\n        {\n            Free();\n            m_Str = p_other.m_Str;\n            p_other.m_Str = nullptr;    //    Transfer the ownership\n            return *this;\n        }\n#endif\n        bool operator==(const String& p_other) const\n        {\n            if (m_Str == nullptr && p_other.m_Str == nullptr)\n            {\n                return true;\n            }\n            else if ((m_Str == nullptr && p_other.m_Str != nullptr) || (m_Str != nullptr && p_other.m_Str == nullptr))\n            {\n                return false;\n            }\n            return strcmp(c_str(), p_other.c_str()) == 0;\n        }\n        const char* c_str() const { return m_Str; }\n        size_t size() const\n        {\n            if (m_Str == nullptr)\n            {\n                return 0;\n            }\n            return (size_t)strlen(m_Str);\n        }\n\n        AMF_INLINE size_t length() const { return size(); }\n        \n        void resize(size_t sizeAlloc)\n        {\n            if (sizeAlloc == 0)\n            {\n                Free();\n                return;\n            }\n            char* str = (char*)amf_variant_alloc(sizeof(char)*(sizeAlloc + 1));\n            if (m_Str != nullptr)\n            {\n                size_t copySize = sizeAlloc;\n                if (copySize > size())\n                {\n                    copySize = size();\n                }\n                memcpy(str, m_Str, copySize * sizeof(char));\n                Free();\n                str[sizeAlloc] = 0;\n            }\n            m_Str = str;\n        }\n    private:\n        char*    m_Str;\n    };\n    //----------------------------------------------------------------------------------------------\n    // helper WString class\n    //----------------------------------------------------------------------------------------------\n    class AMFVariant::WString\n    {\n        friend class AMFVariant;\n    private:\n        void Free()\n        {\n            if (m_Str != nullptr)\n            {\n                AMFVariantFreeWString(m_Str);\n                m_Str = nullptr;\n            }\n        }\n    public:\n        WString() :m_Str(nullptr){}\n        WString(const wchar_t* str) : m_Str(nullptr)\n        {\n            m_Str = AMFVariantDuplicateWString(str);\n        }\n        WString(const WString& p_other) : m_Str(nullptr)\n        {\n            operator=(p_other);\n        }\n#if (__cplusplus == 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X) || (_MSC_VER >= 1600)\n        WString(WString&& p_other) : m_Str(nullptr)\n        {\n            operator=(p_other);\n        }\n#endif\n        ~WString()\n        {\n            Free();\n        }\n\n        WString& operator=(const WString& p_other)\n        {\n            Free();\n            m_Str = AMFVariantDuplicateWString(p_other.m_Str);\n            return *this;\n        }\n#if (__cplusplus == 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X) || (_MSC_VER >= 1600)\n        WString& operator=(WString&& p_other)\n        {\n            Free();\n            m_Str = p_other.m_Str;\n            p_other.m_Str = nullptr;    //    Transfer the ownership\n            return *this;\n        }\n#pragma warning (pop)\n#endif\n        wchar_t& operator[](size_t index)\n        {\n            if (index >= size())\n            {\n                resize(index);\n            }\n            return m_Str[index];\n        }\n\n        bool operator==(const WString& p_other) const\n        {\n            if (m_Str == nullptr && p_other.m_Str == nullptr)\n            {\n                return true;\n            }\n            else if ((m_Str == nullptr && p_other.m_Str != nullptr) || (m_Str != nullptr && p_other.m_Str == nullptr))\n            {\n                return false;\n            }\n            return wcscmp(c_str(), p_other.c_str()) == 0;\n        }\n\n        const wchar_t* c_str() const { return m_Str; }\n        size_t size()  const\n        {\n            if (m_Str == nullptr)\n            {\n                return 0;\n            }\n            return (size_t)wcslen(m_Str);\n        }\n\n        AMF_INLINE size_t length() const { return size(); }\n        \n        void resize(size_t sizeAlloc)\n        {\n            if (sizeAlloc == 0)\n            {\n                Free();\n                return;\n            }\n            wchar_t* str = (wchar_t*)amf_variant_alloc(sizeof(wchar_t)*(sizeAlloc + 1));\n            if (m_Str != nullptr)\n            {\n                size_t copySize = sizeAlloc;\n                if (copySize > size())\n                {\n                    copySize = size();\n                }\n                memcpy(str, m_Str, copySize * sizeof(wchar_t));\n                Free();\n                str[sizeAlloc] = 0;\n            }\n            m_Str = str;\n        }\n    private:\n        wchar_t*    m_Str;\n    };\n    //-------------------------------------------------------------------------------------------------\n    AMFVariant::String       AMFVariant::ToString() const\n    {\n        String temp = GetValue<String, AMF_VARIANT_STRING>(AMFVariantGetString); \n        return String(temp.c_str());\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMFVariant::WString      AMFVariant::ToWString() const\n    {\n        WString temp = GetValue<WString, AMF_VARIANT_WSTRING>(AMFVariantGetWString);\n        return WString(temp.c_str());\n    }\n#endif // defined(__cplusplus)\n    //----------------------------------------------------------------------------------------------\n    // AMF_INLINE implementation of helper functions \n    //----------------------------------------------------------------------------------------------\n    #define AMF_VARIANT_RETURN_IF_INVALID_POINTER(p) \\\n       { \\\n            if (p == NULL) \\\n                    { \\\n                 return AMF_INVALID_POINTER; \\\n            } \\\n       }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantInit(AMFVariantStruct* pVariant)\n    {\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pVariant);\n        pVariant->type = AMF_VARIANT_EMPTY;\n        return AMF_OK;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantClear(AMFVariantStruct* pVariant)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pVariant);\n\n        switch (AMFVariantGetType(pVariant))\n        {\n        case AMF_VARIANT_STRING:\n            amf_variant_free(AMFVariantString(pVariant));\n            pVariant->type = AMF_VARIANT_EMPTY;\n            break;\n\n        case AMF_VARIANT_WSTRING:\n            amf_variant_free(AMFVariantWString(pVariant));\n            pVariant->type = AMF_VARIANT_EMPTY;\n            break;\n\n        case AMF_VARIANT_INTERFACE:\n            if (AMFVariantInterface(pVariant) != NULL)\n            {\n#if defined(__cplusplus)\n                AMFVariantInterface(pVariant)->Release();\n#else\n                AMFVariantInterface(pVariant)->pVtbl->Release(AMFVariantInterface(pVariant));\n#endif\n                AMFVariantInterface(pVariant) = NULL;\n            }\n            pVariant->type = AMF_VARIANT_EMPTY;\n            break;\n\n        default:\n            pVariant->type = AMF_VARIANT_EMPTY;\n            break;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantCompare(const AMFVariantStruct* pFirst, const AMFVariantStruct* pSecond, amf_bool* pEqual)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pFirst);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pSecond);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pEqual);\n\n        if (pFirst == pSecond)\n        {\n            *pEqual = true;\n        }\n        else if (AMFVariantGetType(pFirst) != AMFVariantGetType(pSecond))\n        {\n            *pEqual = false;\n        }\n        else\n        {\n            switch (AMFVariantGetType(pFirst))\n            {\n            case AMF_VARIANT_EMPTY:\n                *pEqual = true;\n                break;\n            case AMF_VARIANT_BOOL:\n                *pEqual = AMFVariantGetBool(pFirst) == AMFVariantBool(pSecond);\n                break;\n            case AMF_VARIANT_INT64:\n                *pEqual = AMFVariantGetInt64(pFirst) == AMFVariantInt64(pSecond);\n                break;\n            case AMF_VARIANT_DOUBLE:\n                *pEqual = AMFVariantGetDouble(pFirst) == AMFVariantDouble(pSecond);\n                break;\n            case AMF_VARIANT_FLOAT:\n                *pEqual = AMFVariantGetFloat(pFirst) == AMFVariantFloat(pSecond);\n                break;\n            case AMF_VARIANT_RECT:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetRect(pFirst) == AMFVariantGetRect(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->rectValue, &pSecond->rectValue, sizeof(AMFRect)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_SIZE:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetSize(pFirst) == AMFVariantGetSize(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->sizeValue, &pSecond->sizeValue, sizeof(AMFSize)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_POINT:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetPoint(pFirst) == AMFVariantGetPoint(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->pointValue, &pSecond->pointValue, sizeof(AMFPoint)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_FLOAT_SIZE:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetFloatSize(pFirst) == AMFVariantGetFloatSize(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->floatSizeValue, &pSecond->floatSizeValue, sizeof(AMFFloatPoint2D)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_FLOAT_POINT2D:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetFloatPoint2D(pFirst) == AMFVariantGetFloatPoint2D(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->floatPoint2DValue, &pSecond->floatPoint2DValue, sizeof(AMFFloatPoint2D)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_FLOAT_POINT3D:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetFloatPoint3D(pFirst) == AMFVariantGetFloatPoint3D(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->floatPoint3DValue, &pSecond->floatPoint3DValue, sizeof(AMFFloatPoint3D)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_FLOAT_VECTOR4D:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetFloatVector4D(pFirst) == AMFVariantGetFloatVector4D(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->floatVector4DValue, &pSecond->floatVector4DValue, sizeof(AMFFloatPoint3D)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_RATE:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetRate(pFirst) == AMFVariantGetRate(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->rateValue, &pSecond->rateValue, sizeof(AMFRate)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_RATIO:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetRatio(pFirst) == AMFVariantGetRatio(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->ratioValue, &pSecond->ratioValue, sizeof(AMFRatio)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_COLOR:\n#if defined(__cplusplus)\n                *pEqual = AMFVariantGetColor(pFirst) == AMFVariantGetColor(pSecond);\n#else\n                *pEqual = memcmp(&pFirst->colorValue, &pSecond->colorValue, sizeof(AMFColor)) == 0;\n#endif\n                break;\n            case AMF_VARIANT_STRING:\n                *pEqual = strcmp(AMFVariantString(pFirst), AMFVariantString(pSecond)) == 0;\n                break;\n            case AMF_VARIANT_WSTRING:\n                *pEqual = wcscmp(AMFVariantWString(pFirst), AMFVariantWString(pSecond)) == 0;\n                break;\n            case AMF_VARIANT_INTERFACE:\n                *pEqual = AMFVariantInterface(pFirst) == AMFVariantInterface(pSecond);\n                break;\n            default:\n                errRet = AMF_INVALID_ARG;\n                break;\n            }\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantCopy(AMFVariantStruct* pDest, const AMFVariantStruct* pSrc)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pSrc);\n        if (pDest != pSrc)\n        {\n            switch (AMFVariantGetType(pSrc))\n            {\n            case AMF_VARIANT_EMPTY:\n                errRet = AMFVariantInit(pDest);\n                break;\n            case AMF_VARIANT_BOOL:\n                errRet = AMFVariantAssignBool(pDest, AMFVariantBool(pSrc));\n                break;\n            case AMF_VARIANT_INT64:\n                errRet = AMFVariantAssignInt64(pDest, AMFVariantInt64(pSrc));\n                break;\n            case AMF_VARIANT_DOUBLE:\n                errRet = AMFVariantAssignDouble(pDest, AMFVariantDouble(pSrc));\n                break;\n            case AMF_VARIANT_FLOAT:\n                errRet = AMFVariantAssignFloat(pDest, AMFVariantFloat(pSrc));\n                break;\n            case AMF_VARIANT_RECT:\n                errRet = AMFVariantAssignRect(pDest, &pSrc->rectValue);\n                break;\n            case AMF_VARIANT_SIZE:\n                errRet = AMFVariantAssignSize(pDest, &pSrc->sizeValue);\n                break;\n            case AMF_VARIANT_POINT:\n                errRet = AMFVariantAssignPoint(pDest, &pSrc->pointValue);\n                break;\n            case AMF_VARIANT_FLOAT_SIZE:\n                errRet = AMFVariantAssignFloatSize(pDest, &pSrc->floatSizeValue);\n                break;\n            case AMF_VARIANT_FLOAT_POINT2D:\n                errRet = AMFVariantAssignFloatPoint2D(pDest, &pSrc->floatPoint2DValue);\n                break;\n            case AMF_VARIANT_FLOAT_POINT3D:\n                errRet = AMFVariantAssignFloatPoint3D(pDest, &pSrc->floatPoint3DValue);\n                break;\n            case AMF_VARIANT_FLOAT_VECTOR4D:\n                errRet = AMFVariantAssignFloatVector4D(pDest, &pSrc->floatVector4DValue);\n                break;\n            case AMF_VARIANT_RATE:\n                errRet = AMFVariantAssignRate(pDest, &pSrc->rateValue);\n                break;\n            case AMF_VARIANT_RATIO:\n                errRet = AMFVariantAssignRatio(pDest, &pSrc->ratioValue);\n                break;\n            case AMF_VARIANT_COLOR:\n                errRet = AMFVariantAssignColor(pDest, &pSrc->colorValue);\n                break;\n            case AMF_VARIANT_STRING:\n                errRet = AMFVariantAssignString(pDest, AMFVariantString(pSrc));\n                break;\n            case AMF_VARIANT_WSTRING:\n                errRet = AMFVariantAssignWString(pDest, AMFVariantWString(pSrc));\n                break;\n            case AMF_VARIANT_INTERFACE:\n                errRet = AMFVariantAssignInterface(pDest, AMFVariantInterface(pSrc));\n                break;\n            default:\n                errRet = AMF_INVALID_ARG;\n                break;\n            }\n        }\n        return errRet;\n    }\n    #define AMFVariantTypeEmpty         AMF_VARIANT_EMPTY\n\n    #define AMFVariantTypeBool          AMF_VARIANT_BOOL\n    #define AMFVariantTypeInt64         AMF_VARIANT_INT64\n    #define AMFVariantTypeDouble        AMF_VARIANT_DOUBLE\n    #define AMFVariantTypeFloat         AMF_VARIANT_FLOAT\n\n    #define AMFVariantTypeRect          AMF_VARIANT_RECT\n    #define AMFVariantTypeSize          AMF_VARIANT_SIZE\n    #define AMFVariantTypePoint         AMF_VARIANT_POINT\n    #define AMFVariantTypeFloatPoint2D  AMF_VARIANT_FLOAT_POINT2D\n    #define AMFVariantTypeFloatPoint3D  AMF_VARIANT_FLOAT_POINT3D\n    #define AMFVariantTypeFloatVector4D AMF_VARIANT_FLOAT_VECTOR4D\n    \n    #define AMFVariantTypeRate          AMF_VARIANT_RATE\n    #define AMFVariantTypeRatio         AMF_VARIANT_RATIO\n    #define AMFVariantTypeColor         AMF_VARIANT_COLOR\n\n    #define AMFVariantTypeString        AMF_VARIANT_STRING\n    #define AMFVariantTypeWString       AMF_VARIANT_WSTRING\n    #define AMFVariantTypeInterface     AMF_VARIANT_INTERFACE\n\n#if defined(__cplusplus)\n    \n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignString(AMFVariantStruct* pDest, const AMFVariant::String& value)\n    {\n        return AMFVariantAssignString(pDest, value.c_str());\n    }\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignWString(AMFVariantStruct* pDest, const AMFVariant::WString& value)\n    {\n        return AMFVariantAssignWString(pDest, value.c_str());\n    }\n        \n    static AMF_INLINE amf_bool AMFConvertEmptyToBool(void*, AMF_RESULT& res) { res = AMF_OK; return false; }\n    static AMF_INLINE amf_int64 AMFConvertEmptyToInt64(void*, AMF_RESULT& res) {res = AMF_OK; return 0; }\n    static AMF_INLINE amf_double AMFConvertEmptyToDouble(void*, AMF_RESULT& res) {res = AMF_OK; return 0; }\n    static AMF_INLINE amf_float AMFConvertEmptyToFloat(void*, AMF_RESULT& res) { res = AMF_OK; return 0; }\n\n    \n    static AMF_INLINE AMFVariant::String AMFConvertEmptyToString(void*, AMF_RESULT& res) {res = AMF_OK; return \"\"; }\n    static AMF_INLINE AMFVariant::WString AMFConvertEmptyToWString(void*, AMF_RESULT& res) {res = AMF_OK; return L\"\"; }\n    static AMF_INLINE amf_int64 AMFConvertBoolToInt64(bool value, AMF_RESULT& res){res = AMF_OK; return value ? 1 : 0;}\n    static AMF_INLINE amf_double AMFConvertBoolToDouble(bool value, AMF_RESULT& res){res = AMF_OK; return value ? 1.0 : 0.0;}\n    static AMF_INLINE amf_float AMFConvertBoolToFloat(bool value, AMF_RESULT& res) { res = AMF_OK; return value ? 1.0f : 0.0f; }\n    static AMF_INLINE AMFVariant::String AMFConvertBoolToString(bool value, AMF_RESULT& res){res = AMF_OK; return value ? \"true\" : \"false\";}\n    static AMF_INLINE AMFVariant::WString AMFConvertBoolToWString(bool value, AMF_RESULT& res){res = AMF_OK; return value ? L\"true\" : L\"false\";}\n    static AMF_INLINE bool AMFConvertInt64ToBool(amf_int64 value, AMF_RESULT& res){res = AMF_OK;return value != 0;}\n    static AMF_INLINE amf_double AMFConvertInt64ToDouble(amf_int64 value, AMF_RESULT& res){res = AMF_OK;return (amf_double)value;}\n    static AMF_INLINE amf_float AMFConvertInt64ToFloat(amf_int64 value, AMF_RESULT& res) { res = AMF_OK; return (amf_float)value; }\n    static AMF_INLINE AMFVariant::String AMFConvertInt64ToString(amf_int64 value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%\" AMFPRId64, (long long)value);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::WString AMFConvertInt64ToWString(amf_int64 value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        wchar_t buff[0xFF];\n        swprintf(buff, 0xFF, L\"%\" LPRId64, (long long)value);\n        return buff;\n    }\n\n    static AMF_INLINE bool AMFConvertDoubleToBool(amf_double value, AMF_RESULT& res){res = AMF_OK;return value != 0;}\n    static AMF_INLINE bool AMFConvertFloatToBool(amf_float value, AMF_RESULT& res) { res = AMF_OK; return value != 0; }\n    static AMF_INLINE amf_int64 AMFConvertDoubleToInt64(amf_double value, AMF_RESULT& res){res = AMF_OK;return amf_int64(value);}\n    static AMF_INLINE amf_int64 AMFConvertFloatToInt64(amf_float value, AMF_RESULT& res) { res = AMF_OK; return amf_int64(value); }\n    static AMF_INLINE AMFVariant::String AMFConvertDoubleToString(amf_double value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%lf\", value);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMFConvertFloatToString(amf_float value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%f\", value);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::WString AMFConvertDoubleToWString(amf_double value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        wchar_t buff[0xFF];\n        swprintf(buff, 0xFF, L\"%lf\", value);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::WString AMFConvertFloatToWString(amf_float value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        wchar_t buff[0xFF];\n        swprintf(buff, 0xFF, L\"%f\", value);\n        return buff;\n    }\n\n    static AMF_INLINE bool AMFConvertStringToBool(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFVariant::String tmp = value;\n        if (( tmp == \"true\") || ( tmp == \"True\") || ( tmp == \"TRUE\") || ( tmp == \"1\") )\n        {\n            return true;\n        }\n        else\n        {\n            if (( tmp == \"false\") || ( tmp == \"False\") || ( tmp == \"FALSE\") || ( tmp == \"0\") )\n            {\n                return false;\n            }\n        }\n        res = AMF_INVALID_ARG;\n        return false;\n    }\n\n    static AMF_INLINE amf_int64 AMFConvertStringToInt64(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        long long tmp = 0;\n        int readElements = 0;\n\n        if (value.size() > 2 && ( value.c_str()[0] == '0') && ( value.c_str()[1] == 'x') )\n        {\n            readElements = sscanf(value.c_str(), \"0x%\" AMFPRIx64, &tmp);\n        }\n        else if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%\" AMFPRId64, &tmp);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return 0;\n    }\n\n    static AMF_INLINE amf_double AMFConvertStringToDouble(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        amf_double tmp = 0;\n        int readElements = 0;\n        if (value.size() > 0)\n        { \n            readElements = sscanf(value.c_str(), \"%lf\", &tmp);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return 0;\n    }\n    static AMF_INLINE amf_float AMFConvertStringToFloat(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        amf_float tmp = 0;\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%f\", &tmp);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return 0;\n    }\n\n    static AMF_INLINE AMFVariant::WString AMFConvertStringToWString(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n//        return amf_from_utf8_to_unicode(value);\n        AMFVariant::WString result;\n        if (0 == value.size())\n        {\n            return result;\n        }\n        const char* pUtf8Buff = value.c_str();\n\n#if defined(_WIN32)\n        _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);\n        int UnicodeBuffSize = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8Buff, -1, NULL, 0);\n        if (0 == UnicodeBuffSize)\n        {\n            return result;\n        }\n        UnicodeBuffSize += 8; // get some extra space\n        result.resize(UnicodeBuffSize);\n        UnicodeBuffSize = ::MultiByteToWideChar(CP_UTF8, 0, pUtf8Buff, -1, (LPWSTR)result.c_str(), UnicodeBuffSize);\n        UnicodeBuffSize--;\n\n#elif defined(__ANDROID__)\n        // on android mbstowcs cannot be used to define length\n        char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n\n        mbstate_t mbs;\n        mbrlen(NULL, 0, &mbs);\n        int len = value.size();\n        const char* pt = pUtf8Buff;\n        int UnicodeBuffSize = 0;\n        while (len > 0)\n        {\n            size_t length = mbrlen (pt, len, &mbs); //MM TODO Android always return 1\n            if ((length == 0) || (length > len))\n            {\n                break;\n            }\n            UnicodeBuffSize++;\n            len -= length;\n            pt += length;\n        }\n        UnicodeBuffSize += 8; // get some extra space\n        result.resize(UnicodeBuffSize);\n\n        mbrlen (NULL, 0, &mbs);\n        len = value.size();\n        pt = pUtf8Buff;\n        UnicodeBuffSize = 0;\n        while (len > 0)\n        {\n            size_t length = mbrlen (pt, len, &mbs);\n            if ((length == 0) || (length > len))\n            {\n                break;\n            }\n            mbrtowc(&((wchar_t*)(result.c_str()))[UnicodeBuffSize], pt, length, &mbs);     //MM TODO Android always return 1 char\n            UnicodeBuffSize++;\n            len -= length;\n            pt += length;\n        }\n        setlocale(LC_CTYPE, old_locale);\n\n #else\n        char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n        size_t UnicodeBuffSize = mbstowcs(NULL, pUtf8Buff, 0);\n        if (0 == UnicodeBuffSize)\n        {\n            return result;\n        }\n        UnicodeBuffSize += 8; // get some extra space\n        result.resize(UnicodeBuffSize);\n        UnicodeBuffSize = mbstowcs((wchar_t*)result.c_str(), pUtf8Buff, UnicodeBuffSize + 1);\n        setlocale(LC_CTYPE, old_locale);\n#endif\n        result.resize(UnicodeBuffSize);\n        return result;\n    }\n    static AMF_INLINE AMFVariant::String AMFConvertWStringToString(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n//      return amf_from_unicode_to_utf8(value);\n        AMFVariant::String result;\n        if (0 == value.size())\n        {\n            return result;\n        }\n\n        const wchar_t* pwBuff = value.c_str();\n\n#if defined(_WIN32)\n        _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);\n        int Utf8BuffSize = ::WideCharToMultiByte(CP_UTF8, 0, pwBuff, -1, NULL, 0, NULL, NULL);\n        if (0 == Utf8BuffSize)\n        {\n            return result;\n        }\n        Utf8BuffSize += 8; // get some extra space\n        result.resize(Utf8BuffSize);\n        Utf8BuffSize = ::WideCharToMultiByte(CP_UTF8, 0, pwBuff, -1, (LPSTR)result.c_str(), Utf8BuffSize, NULL, NULL);\n        Utf8BuffSize--;\n#elif defined(__ANDROID__)\n        char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n        int Utf8BuffSize = value.length();\n        if (0 == Utf8BuffSize)\n        {\n            return result;\n        }\n        Utf8BuffSize += 8; // get some extra space\n        result.resize(Utf8BuffSize);\n\n        mbstate_t mbs;\n        mbrlen(NULL, 0, &mbs);\n\n        Utf8BuffSize = 0;\n        for (int i = 0; i < value.length(); i++)\n        {\n            //MM TODO Android - not implemented\n            //int written = wcrtomb(&result[Utf8BuffSize], pwBuff[i], &mbs);\n            ((char*)(result.c_str()))[Utf8BuffSize] = (char)(pwBuff[i]);\n            int written = 1;\n            // temp replacement\n            Utf8BuffSize += written;\n        }\n        setlocale(LC_CTYPE, old_locale);\n\n#else\n        char* old_locale = setlocale(LC_CTYPE, \"en_US.UTF8\");\n        size_t Utf8BuffSize = wcstombs(NULL, pwBuff, 0);\n        if (0 == Utf8BuffSize)\n        {\n            return result;\n        }\n        Utf8BuffSize += 8; // get some extra space\n        result.resize(Utf8BuffSize);\n        Utf8BuffSize = wcstombs((char*)result.c_str(), pwBuff, Utf8BuffSize + 1);\n\n        setlocale(LC_CTYPE, old_locale);\n#endif\n        result.resize(Utf8BuffSize);\n        return result;\n    }\n\n\n    static AMF_INLINE bool AMFConvertWStringToBool(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToBool(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE amf_int64 AMFConvertWStringToInt64(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToInt64(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE amf_double AMFConvertWStringToDouble(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToDouble(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE amf_float AMFConvertWStringToFloat(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToFloat(AMFConvertWStringToString(value, res), res);\n    }\n\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertRectToString(const AMFRect& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%d,%d,%d,%d\", value.left, value.top, value.right, value.bottom);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertSizeToString(const AMFSize& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%d,%d\", value.width, value.height);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertPointToString(const AMFPoint& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%d,%d\", value.x, value.y);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertFloatSizeToString(const AMFFloatSize& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%f,%f\", value.width, value.height);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertFloatPoint2DToString(const AMFFloatPoint2D& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%f,%f\", value.x, value.y);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertFloatPoint3DToString(const AMFFloatPoint3D& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%f,%f,%f\", value.x, value.y, value.z);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertFloatVector4DToString(const AMFFloatVector4D& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%f,%f,%f,%f\", value.x, value.y, value.z, value.w);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertRateToString(const AMFRate& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%u,%u\", value.num, value.den);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertRatioToString(const AMFRatio& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%u,%u\", value.num, value.den);\n        return buff;\n    }\n    static AMF_INLINE AMFVariant::String AMF_STD_CALL AMFConvertColorToString(const AMFColor& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        char buff[0xFF];\n        sprintf(buff, \"%u,%u,%u,%u\", value.r, value.g, value.b, value.a);\n        return buff;\n    }\n\n    static AMF_INLINE AMFRect  AMF_STD_CALL AMFConvertStringToRect(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFRect tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%d,%d,%d,%d\", &tmp.left, &tmp.top, &tmp.right, &tmp.bottom);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n\n    static AMF_INLINE AMFSize  AMF_STD_CALL AMFConvertStringToSize(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFSize tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n          if (strchr(value.c_str(), ',') != nullptr)\n          {\n            readElements = sscanf(value.c_str(), \"%d,%d\", &tmp.width, &tmp.height);\n          } \n          else if (strchr(value.c_str(), 'x') != nullptr)\n          {\n            readElements = sscanf(value.c_str(), \"%dx%d\", &tmp.width, &tmp.height);\n          }\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFPoint AMF_STD_CALL AMFConvertStringToPoint(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFPoint tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%d,%d\", &tmp.x, &tmp.y);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFFloatSize AMF_STD_CALL AMFConvertStringToFloatSize(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFFloatSize tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%f,%f\", &tmp.width, &tmp.height);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFFloatPoint2D AMF_STD_CALL AMFConvertStringToFloatPoint2D(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFFloatPoint2D tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%f,%f\", &tmp.x, &tmp.y);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFFloatPoint3D AMF_STD_CALL AMFConvertStringToFloatPoint3D(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFFloatPoint3D tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%f,%f,%f\", &tmp.x, &tmp.y, &tmp.z);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFFloatVector4D AMF_STD_CALL AMFConvertStringToFloatVector4D(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFFloatVector4D tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%f,%f,%f,%f\", &tmp.x, &tmp.y, &tmp.z, &tmp.w);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFRate  AMF_STD_CALL AMFConvertStringToRate(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFRate tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%u,%u\", &tmp.num, &tmp.den);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFRatio AMF_STD_CALL AMFConvertStringToRatio(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        AMFRatio tmp = {};\n        int readElements = 0;\n        if (value.size() > 0)\n        {\n            readElements = sscanf(value.c_str(), \"%u,%u\", &tmp.num, &tmp.den);\n        }\n        if (readElements)\n        {\n            return tmp;\n        }\n        res = AMF_INVALID_ARG;\n        return tmp;\n    }\n    static AMF_INLINE AMFColor AMF_STD_CALL AMFConvertStringToColor(const AMFVariant::String& value, AMF_RESULT& res)\n    {\n        res = AMF_OK;\n        int readElements = 0;\n        amf_uint32 r = 0;\n        amf_uint32 g = 0;\n        amf_uint32 b = 0;\n        amf_uint32 a = 0;\n        if (value.size() > 0)\n        { \n            readElements = sscanf(value.c_str(), \"%u,%u,%u,%u\", &r, &g, &b, &a);\n        }\n        if (readElements)\n        {\n            return AMFConstructColor((amf_uint8)r, (amf_uint8)g, (amf_uint8)b, (amf_uint8)a);\n        }\n        res = AMF_INVALID_ARG;\n        return AMFConstructColor(0, 0, 0, 255);\n    }\n///////////////////////\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertRectToWString(const AMFRect& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertRectToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertSizeToWString(const AMFSize& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertSizeToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertPointToWString(const AMFPoint& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertPointToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertFloatSizeToWString(const AMFFloatSize& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertFloatSizeToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertFloatPoint2DToWString(const AMFFloatPoint2D& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertFloatPoint2DToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertFloatPoint3DToWString(const AMFFloatPoint3D& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertFloatPoint3DToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertFloatVector4DToWString(const AMFFloatVector4D& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertFloatVector4DToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertRateToWString(const AMFRate& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertRateToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertRatioToWString(const AMFRatio& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertRatioToString(value, res), res);\n    }\n    static AMF_INLINE AMFVariant::WString AMF_STD_CALL AMFConvertColorToWString(const AMFColor& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToWString(AMFConvertColorToString(value, res), res);\n    }\n\n    static AMF_INLINE AMFRect  AMF_STD_CALL AMFConvertWStringToRect(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToRect(AMFConvertWStringToString(value, res), res);\n    }\n\n    static AMF_INLINE AMFSize  AMF_STD_CALL AMFConvertWStringToSize(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToSize(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFPoint AMF_STD_CALL AMFConvertWStringToPoint(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToPoint(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFFloatSize AMF_STD_CALL AMFConvertWStringToFloatSize(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToFloatSize(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFFloatPoint2D AMF_STD_CALL AMFConvertWStringToFloatPoint2D(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToFloatPoint2D(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFFloatPoint3D AMF_STD_CALL AMFConvertWStringToFloatPoint3D(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToFloatPoint3D(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFFloatVector4D AMF_STD_CALL AMFConvertWStringToFloatVector4D(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToFloatVector4D(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFRate  AMF_STD_CALL AMFConvertWStringToRate(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToRate(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFRatio AMF_STD_CALL AMFConvertWStringToRatio(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToRatio(AMFConvertWStringToString(value, res), res);\n    }\n    static AMF_INLINE AMFColor AMF_STD_CALL AMFConvertWStringToColor(const AMFVariant::WString& value, AMF_RESULT& res)\n    {\n        return AMFConvertStringToColor(AMFConvertWStringToString(value, res), res);\n    }\n\n    //-------------------------------------------------------------------------------------------------\n    #define AMFConvertTool(srcType, dstType)\\\n        if (AMFVariantGetType(pSrc) == AMFVariantType##srcType && newType == AMFVariantType##dstType)\\\n        {\\\n            AMF_RESULT res = AMF_OK;\\\n            AMFVariantAssign##dstType(pDest, AMFConvert##srcType##To##dstType(AMFVariant##srcType(pSrc), res));\\\n            return res;\\\n        }\\\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantChangeType(AMFVariantStruct* pDest, const AMFVariantStruct* pSrc, AMF_VARIANT_TYPE newType)\n    {\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n\n        if (pSrc == nullptr)\n        {\n            pSrc = pDest;\n        }\n\n        if (AMFVariantGetType(pSrc) == newType)\n        {\n            if (pDest == pSrc)\n            {\n                return AMF_OK;\n            }\n            return AMFVariantCopy(pDest, pSrc);\n        }\n\n        if (pDest != pSrc)\n        {\n            AMFVariantClear(pDest);\n        }\n\n        AMFConvertTool(Empty, Bool);\n        AMFConvertTool(Empty, Int64);\n        AMFConvertTool(Empty, Double);\n        AMFConvertTool(Empty, Float);\n        AMFConvertTool(Empty, String);\n        AMFConvertTool(Empty, WString);\n\n        AMFConvertTool(Bool, Int64);\n        AMFConvertTool(Bool, Double);\n        AMFConvertTool(Bool, Float);\n        AMFConvertTool(Bool, String);\n        AMFConvertTool(Bool, WString);\n\n        AMFConvertTool(Int64, Bool);\n        AMFConvertTool(Int64, Double);\n        AMFConvertTool(Int64, Float);\n        AMFConvertTool(Int64, String);\n        AMFConvertTool(Int64, WString);\n\n        AMFConvertTool(Double, Bool);\n        AMFConvertTool(Double, Int64);\n        AMFConvertTool(Double, String);\n        AMFConvertTool(Double, WString);\n\n        AMFConvertTool(Float, Bool);\n        AMFConvertTool(Float, Int64);\n        AMFConvertTool(Float, String);\n        AMFConvertTool(Float, WString);\n\n        AMFConvertTool(String, Bool);\n        AMFConvertTool(String, Int64);\n        AMFConvertTool(String, Double);\n        AMFConvertTool(String, Float);\n        AMFConvertTool(String, WString);\n\n        AMFConvertTool(WString, Bool);\n        AMFConvertTool(WString, Int64);\n        AMFConvertTool(WString, Double);\n        AMFConvertTool(WString, Float);\n        AMFConvertTool(WString, String);\n\n        AMFConvertTool(String, Rect);\n        AMFConvertTool(String, Size);\n        AMFConvertTool(String, Point);\n        AMFConvertTool(String, Rate);\n        AMFConvertTool(String, Ratio);\n        AMFConvertTool(String, Color);\n\n        AMFConvertTool(Rect , String);\n        AMFConvertTool(Size , String);\n        AMFConvertTool(Point, String);\n        AMFConvertTool(Rate , String);\n        AMFConvertTool(Ratio, String);\n        AMFConvertTool(Color, String);\n\n        AMFConvertTool(WString, Rect);\n        AMFConvertTool(WString, Size);\n        AMFConvertTool(WString, Point);\n        AMFConvertTool(WString, Rate);\n        AMFConvertTool(WString, Ratio);\n        AMFConvertTool(WString, Color);\n\n        AMFConvertTool(Rect , WString);\n        AMFConvertTool(Size , WString);\n        AMFConvertTool(Point, WString);\n        AMFConvertTool(Rate , WString);\n        AMFConvertTool(Ratio, WString);\n        AMFConvertTool(Color, WString);\n\n        return AMF_INVALID_ARG;\n    }\n#endif // #if defined(__cplusplus)\n\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignBool(AMFVariantStruct* pDest, amf_bool value)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_BOOL;\n            AMFVariantBool(pDest) = value;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignInt64(AMFVariantStruct* pDest, amf_int64 value)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_INT64;\n            AMFVariantInt64(pDest) = value;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignDouble(AMFVariantStruct* pDest, amf_double value)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_DOUBLE;\n            AMFVariantDouble(pDest) = value;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloat(AMFVariantStruct* pDest, amf_float value)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_FLOAT;\n            AMFVariantFloat(pDest) = value;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignString(AMFVariantStruct* pDest, const char* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            const size_t size = (strlen(pValue) + 1);\n            pDest->type = AMF_VARIANT_STRING;\n            AMFVariantString(pDest) = (char*)amf_variant_alloc(size * sizeof(char));\n            if (AMFVariantString(pDest))\n            {\n                memcpy(AMFVariantString(pDest), pValue, size * sizeof(char));\n            }\n            else\n            {\n                errRet = AMF_OUT_OF_MEMORY;\n            }\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignWString(AMFVariantStruct* pDest, const wchar_t* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            const size_t size = (wcslen(pValue) + 1);\n            pDest->type = AMF_VARIANT_WSTRING;\n            AMFVariantWString(pDest) = (wchar_t*)amf_variant_alloc(size * sizeof(wchar_t));\n            if (AMFVariantWString(pDest))\n            {\n                memcpy(AMFVariantWString(pDest), pValue, size * sizeof(wchar_t));\n            }\n            else\n            {\n                errRet = AMF_OUT_OF_MEMORY;\n            }\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignInterface(AMFVariantStruct* pDest, AMFInterface* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        //AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);//can be NULL\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_INTERFACE;\n            AMFVariantInterface(pDest) = pValue;\n            if (AMFVariantInterface(pDest))\n            {\n#if defined(__cplusplus)\n                AMFVariantInterface(pDest)->Acquire();\n#else\n                AMFVariantInterface(pDest)->pVtbl->Acquire(AMFVariantInterface(pDest));\n#endif\n            }\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRect(AMFVariantStruct* pDest, const AMFRect& value)\n    {\n        return AMFVariantAssignRect(pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRect (AMFVariantStruct* pDest, const AMFRect* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_RECT;\n            AMFVariantRect(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignSize (AMFVariantStruct* pDest, const AMFSize& value)\n    {\n        return AMFVariantAssignSize (pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignSize (AMFVariantStruct* pDest, const AMFSize* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_SIZE;\n            AMFVariantSize(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignPoint(AMFVariantStruct* pDest, const AMFPoint& value)\n    {\n        return AMFVariantAssignPoint(pDest, &value);\n    }\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatSize(AMFVariantStruct* pDest, const AMFFloatSize& value)\n    {\n        return AMFVariantAssignFloatSize(pDest, &value);\n    }\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatPoint2D(AMFVariantStruct* pDest, const AMFFloatPoint2D& value)\n    {\n        return AMFVariantAssignFloatPoint2D(pDest, &value);\n    }\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatPoint3D(AMFVariantStruct* pDest, const AMFFloatPoint3D& value)\n    {\n        return AMFVariantAssignFloatPoint3D(pDest, &value);\n    }\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatVector4D(AMFVariantStruct* pDest, const AMFFloatVector4D& value)\n    {\n        return AMFVariantAssignFloatVector4D(pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignPoint(AMFVariantStruct* pDest, const AMFPoint* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_POINT;\n            AMFVariantPoint(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatSize(AMFVariantStruct* pDest, const AMFFloatSize* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_FLOAT_SIZE;\n            AMFVariantFloatSize(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatPoint2D(AMFVariantStruct* pDest, const AMFFloatPoint2D* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_FLOAT_POINT2D;\n            AMFVariantFloatPoint2D(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatPoint3D(AMFVariantStruct* pDest, const AMFFloatPoint3D* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_FLOAT_POINT3D;\n            AMFVariantFloatPoint3D(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignFloatVector4D(AMFVariantStruct* pDest, const AMFFloatVector4D* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_FLOAT_VECTOR4D;\n            AMFVariantFloatVector4D(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRate (AMFVariantStruct* pDest, const AMFRate& value)\n    {\n        return AMFVariantAssignRate (pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRate (AMFVariantStruct* pDest, const AMFRate* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_RATE;\n            AMFVariantRate(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRatio(AMFVariantStruct* pDest, const AMFRatio& value)\n    {\n        return AMFVariantAssignRatio(pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignRatio(AMFVariantStruct* pDest, const AMFRatio* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_RATIO;\n            AMFVariantRatio(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignColor(AMFVariantStruct* pDest, const AMFColor& value)\n    {\n        return AMFVariantAssignColor(pDest, &value);\n    }\n#endif\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE AMF_RESULT AMF_CDECL_CALL AMFVariantAssignColor(AMFVariantStruct* pDest, const AMFColor* pValue)\n    {\n        AMF_RESULT errRet = AMF_OK;\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pDest);\n        AMF_VARIANT_RETURN_IF_INVALID_POINTER(pValue);\n\n        errRet = AMFVariantInit(pDest);\n        if (errRet == AMF_OK)\n        {\n            pDest->type = AMF_VARIANT_COLOR;\n            AMFVariantColor(pDest) = *pValue;\n        }\n        return errRet;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE char* AMF_CDECL_CALL AMFVariantDuplicateString(const char* pFrom)\n    {\n        char* ret = 0;\n        if (pFrom)\n        {\n            ret = (char*)amf_variant_alloc(sizeof(char)*(strlen(pFrom) + 1));\n            if (ret)\n            {\n                strcpy(ret, pFrom);\n            }\n        }\n        return ret;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE void AMF_CDECL_CALL AMFVariantFreeString(char* pFrom)\n    {\n        amf_variant_free(pFrom);\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE wchar_t* AMF_CDECL_CALL AMFVariantDuplicateWString(const wchar_t* pFrom)\n    {\n        wchar_t* ret = 0;\n        if (pFrom)\n        {\n            ret = (wchar_t*)amf_variant_alloc(sizeof(wchar_t)*(wcslen(pFrom) + 1));\n            if (ret)\n            {\n                wcscpy(ret, pFrom);\n            }\n        }\n        return ret;\n    }\n    //-------------------------------------------------------------------------------------------------\n    static AMF_INLINE void AMF_CDECL_CALL AMFVariantFreeWString(wchar_t* pFrom)\n    {\n        amf_variant_free(pFrom);\n    }\n    //----------------------------------------------------------------------------------------------\n    // AMF_INLINE implementation of AMFVariant class\n    //----------------------------------------------------------------------------------------------\n#if defined(__cplusplus)\n    AMF_INLINE AMFVariant::AMFVariant(const AMFVariantStruct* pOther)\n    {\n        AMFVariantInit(this);\n        if (pOther != nullptr)\n        {\n            AMFVariantCopy(this, const_cast<AMFVariantStruct*>(pOther));\n        }\n    }\n    //-------------------------------------------------------------------------------------------------\n    template<typename T>\n    AMFVariant::AMFVariant(const AMFInterfacePtr_T<T>& pValue)\n    {\n        AMFVariantInit(this);\n        AMFVariantAssignInterface(this, pValue);\n    }\n    //-------------------------------------------------------------------------------------------------\n    template<class ReturnType, AMF_VARIANT_TYPE variantType, typename Getter>\n    ReturnType AMFVariant::GetValue(Getter getter) const\n    {\n        ReturnType str = ReturnType();\n        if (AMFVariantGetType(this) == variantType)\n        {\n            str = static_cast<ReturnType>(getter(this));\n        }\n        else\n        {\n            AMFVariant varDest;\n            varDest.ChangeType(variantType, this);\n            if (varDest.type != AMF_VARIANT_EMPTY)\n            {\n                str = static_cast<ReturnType>(getter(&varDest));\n            }\n        }\n        return str;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE AMFVariant& AMFVariant::operator=(const AMFVariantStruct& other)\n    {\n        AMFVariantClear(this);\n        AMFVariantCopy(this, const_cast<AMFVariantStruct*>(&other));\n        return *this;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE AMFVariant& AMFVariant::operator=(const AMFVariantStruct* pOther)\n    {\n        if (pOther != nullptr)\n        {\n            AMFVariantClear(this);\n            AMFVariantCopy(this, const_cast<AMFVariantStruct*>(pOther));\n        }\n        return *this;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE AMFVariant& AMFVariant::operator=(const AMFVariant& other)\n    {\n        AMFVariantClear(this);\n        AMFVariantCopy(this,\n                const_cast<AMFVariantStruct*>(static_cast<const AMFVariantStruct*>(&other)));\n        return *this;\n    }\n    //-------------------------------------------------------------------------------------------------\n    template<typename T>\n    AMFVariant& AMFVariant::operator=(const AMFInterfacePtr_T<T>& value)\n    {\n        AMFVariantClear(this);\n        AMFVariantAssignInterface(this, value);\n        return *this;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE bool AMFVariant::operator==(const AMFVariantStruct& other) const\n    {\n        return *this == &other;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE bool AMFVariant::operator==(const AMFVariantStruct* pOther) const\n    {\n        //TODO: double check\n        amf_bool ret = false;\n        if (pOther == nullptr)\n        {\n            ret = false;\n        }\n        else\n        {\n            AMFVariantCompare(this, pOther, &ret);\n        }\n        return ret;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE bool AMFVariant::operator!=(const AMFVariantStruct& other) const\n    {\n        return !(*this == &other);\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE bool AMFVariant::operator!=(const AMFVariantStruct* pOther) const\n    {\n        return !(*this == pOther);\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE void AMFVariant::Attach(AMFVariantStruct& pVariant)\n    {\n        Clear();\n        memcpy(this, &pVariant, sizeof(pVariant));\n        AMFVariantGetType(&pVariant) = AMF_VARIANT_EMPTY;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE AMFVariantStruct AMFVariant::Detach()\n    {\n        AMFVariantStruct varResult = *this;\n        AMFVariantGetType(this) = AMF_VARIANT_EMPTY;\n        return varResult;\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE AMFVariantStruct& AMFVariant::GetVariant()\n    {\n        return *static_cast<AMFVariantStruct*>(this);\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE void AMFVariant::ChangeType(AMF_VARIANT_TYPE newType, const AMFVariant* pSrc)\n    {\n        AMFVariantChangeType(this, pSrc, newType);\n    }\n    //-------------------------------------------------------------------------------------------------\n    AMF_INLINE bool AMFVariant::Empty() const\n    {\n        return type == AMF_VARIANT_EMPTY;\n    }\n    //-------------------------------------------------------------------------------------------------\n#endif // #if defined(__cplusplus)\n\n#if defined(__cplusplus)\n} //namespace amf\n#endif\n\n#endif //#ifndef AMF_Variant_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/Version.h",
    "content": "// \n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n// \n// MIT license \n// \n// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n/**\n***************************************************************************************************\n* @file  Version.h\n* @brief Version declaration\n***************************************************************************************************\n*/\n#ifndef AMF_Version_h\n#define AMF_Version_h\n#pragma once\n\n#include \"Platform.h\"\n\n#define AMF_MAKE_FULL_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_BUILD_NUM)    ( ((amf_uint64)(VERSION_MAJOR) << 48ull) | ((amf_uint64)(VERSION_MINOR) << 32ull) | ((amf_uint64)(VERSION_RELEASE) << 16ull)  | (amf_uint64)(VERSION_BUILD_NUM))\n\n#define AMF_GET_MAJOR_VERSION(x)      ((x >> 48ull) & 0xFFFF)\n#define AMF_GET_MINOR_VERSION(x)      ((x >> 32ull) & 0xFFFF)\n#define AMF_GET_SUBMINOR_VERSION(x)   ((x >> 16ull) & 0xFFFF)\n#define AMF_GET_BUILD_VERSION(x)      ((x >>  0ull) & 0xFFFF)\n\n#define AMF_VERSION_MAJOR       1\n#define AMF_VERSION_MINOR       4\n#define AMF_VERSION_RELEASE     33\n#define AMF_VERSION_BUILD_NUM   0\n\n#define AMF_FULL_VERSION AMF_MAKE_FULL_VERSION(AMF_VERSION_MAJOR, AMF_VERSION_MINOR, AMF_VERSION_RELEASE, AMF_VERSION_BUILD_NUM)\n\n#endif //#ifndef AMF_Version_h\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/amf/public/include/core/VulkanAMF.h",
    "content": "//\n// Notice Regarding Standards.  AMD does not provide a license or sublicense to\n// any Intellectual Property Rights relating to any standards, including but not\n// limited to any audio and/or video codec technologies such as MPEG-2, MPEG-4;\n// AVC/H.264; HEVC/H.265; AAC decode/FFMPEG; AAC encode/FFMPEG; VC-1; and MP3\n// (collectively, the \"Media Technologies\"). For clarity, you will pay any\n// royalties due for such third party technologies, which may include the Media\n// Technologies that are owed as a result of AMD providing the Software to you.\n//\n// MIT license\n//\n// Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n\n#ifndef __VulkanAMF_h__\n#define __VulkanAMF_h__\n#pragma once\n#include \"Platform.h\"\n\n#include \"vulkan/vulkan.h\"\n\n#if defined(__cplusplus)\nnamespace amf\n{\n#endif\n    typedef struct AMFVulkanDevice\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanDevice)\n        void*               pNext;              // reserved for extensions\n        VkInstance          hInstance;\n        VkPhysicalDevice    hPhysicalDevice;\n        VkDevice            hDevice;\n    } AMFVulkanDevice;\n\n    typedef struct AMFVulkanSync\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanSync)\n        void*               pNext;              // reserved for extensions\n        VkSemaphore         hSemaphore;         // VkSemaphore; can be nullptr\n        amf_bool            bSubmitted;         // if true - wait for hSemaphore. re-submit hSemaphore if not synced by other ways and set to true\n        VkFence             hFence;             // To sync on CPU; can be nullptr. Submitted in vkQueueSubmit. If waited for hFence, null it, do not delete or reset.\n    } AMFVulkanSync;\n\n    typedef struct AMFVulkanBuffer\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanBuffer)\n        void*               pNext;              // reserved for extensions\n        VkBuffer            hBuffer;\n        VkDeviceMemory      hMemory;\n        amf_int64           iSize;\n        amf_int64           iAllocatedSize;     // for reuse\n        amf_uint32          eAccessFlags;       // VkAccessFlagBits\n        amf_uint32          eUsage;             // AMF_BUFFER_USAGE\n        amf_uint32          eAccess;            // AMF_MEMORY_CPU_ACCESS\n        AMFVulkanSync       Sync;\n    } AMFVulkanBuffer;\n\n    typedef struct AMFVulkanSurface\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanSurface)\n        void*               pNext;              // reserved for extensions\n        // surface properties\n        VkImage             hImage;             // vulkan native image for which the surface is created\n        VkDeviceMemory      hMemory;            // memory for hImage, can be nullptr\n        amf_int64           iSize;              // memory size\n        amf_uint32          eFormat;            // VkFormat\n        amf_int32           iWidth;             // image width\n        amf_int32           iHeight;            // image height\n        amf_uint32          eCurrentLayout;     // VkImageLayout\n        amf_uint32          eUsage;             // AMF_SURFACE_USAGE\n        amf_uint32          eAccess;            // AMF_MEMORY_CPU_ACCESS\n        AMFVulkanSync       Sync;               // To sync on GPU\n    } AMFVulkanSurface;\n\n    typedef struct AMFVulkanSurface1\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanSurface)\n        void*               pNext;              // reserved for extensions\n        // surface properties\n        amf_uint32          eTiling;            // VkImageTiling\n    } AMFVulkanSurface1;\n\n    typedef struct AMFVulkanView\n    {\n        amf_size            cbSizeof;           // sizeof(AMFVulkanView)\n        void*               pNext;              // reserved for extensions\n        // surface properties\n        AMFVulkanSurface    *pSurface;\n        VkImageView         hView;\n        amf_int32           iPlaneWidth;\n        amf_int32           iPlaneHeight;\n        amf_int32           iPlaneWidthPitch;\n        amf_int32           iPlaneHeightPitch;\n    } AMFVulkanView;\n\n#define AMF_CONTEXT_VULKAN_COMPUTE_QUEUE  L\"VulkanComputeQueue\" // amf_int64; default=0; Compute queue index in range [0, (VkQueueFamilyProperties.queueCount-1)] of the compute queue family.\n\n#if defined(__cplusplus)\n} // namespace amf\n#endif\n#endif // __VulkanAMF_h__\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/backward.cpp",
    "content": "// Pick your poison.\n//\n// On GNU/Linux, you have few choices to get the most out of your stack trace.\n//\n// By default you get:\n//\t- object filename\n//\t- function name\n//\n// In order to add:\n//\t- source filename\n//\t- line and column numbers\n//\t- source code snippet (assuming the file is accessible)\n\n// Install one of the following libraries then uncomment one of the macro (or\n// better, add the detection of the lib and the macro definition in your build\n// system)\n\n// - apt-get install libdw-dev ...\n// - g++/clang++ -ldw ...\n// #define BACKWARD_HAS_DW 1\n\n// - apt-get install binutils-dev ...\n// - g++/clang++ -lbfd ...\n// #define BACKWARD_HAS_BFD 1\n\n// - apt-get install libdwarf-dev ...\n// - g++/clang++ -ldwarf ...\n// #define BACKWARD_HAS_DWARF 1\n\n// Regardless of the library you choose to read the debug information,\n// for potentially more detailed stack traces you can use libunwind\n// - apt-get install libunwind-dev\n// - g++/clang++ -lunwind\n// #define BACKWARD_HAS_LIBUNWIND 1\n\n#include \"backward.hpp\"\n\nnamespace backward {\n\nbackward::SignalHandling sh;\n\n} // namespace backward\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/backward.hpp",
    "content": "/*\n * backward.hpp\n * Copyright 2013 Google Inc. All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89\n#define H_6B9572DA_A64B_49E6_B234_051480991C89\n\n#ifndef __cplusplus\n#error \"It's not going to compile without a C++ compiler...\"\n#endif\n\n#if defined(BACKWARD_CXX11)\n#elif defined(BACKWARD_CXX98)\n#else\n#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)\n#define BACKWARD_CXX11\n#define BACKWARD_ATLEAST_CXX11\n#define BACKWARD_ATLEAST_CXX98\n#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)\n#define BACKWARD_ATLEAST_CXX17\n#endif\n#else\n#define BACKWARD_CXX98\n#define BACKWARD_ATLEAST_CXX98\n#endif\n#endif\n\n// You can define one of the following (or leave it to the auto-detection):\n//\n// #define BACKWARD_SYSTEM_LINUX\n//\t- specialization for linux\n//\n// #define BACKWARD_SYSTEM_DARWIN\n//\t- specialization for Mac OS X 10.5 and later.\n//\n// #define BACKWARD_SYSTEM_WINDOWS\n//  - specialization for Windows (Clang 9 and MSVC2017)\n//\n// #define BACKWARD_SYSTEM_UNKNOWN\n//\t- placebo implementation, does nothing.\n//\n#if defined(BACKWARD_SYSTEM_LINUX)\n#elif defined(BACKWARD_SYSTEM_DARWIN)\n#elif defined(BACKWARD_SYSTEM_UNKNOWN)\n#elif defined(BACKWARD_SYSTEM_WINDOWS)\n#else\n#if defined(__linux) || defined(__linux__)\n#define BACKWARD_SYSTEM_LINUX\n#elif defined(__APPLE__)\n#define BACKWARD_SYSTEM_DARWIN\n#elif defined(_WIN32)\n#define BACKWARD_SYSTEM_WINDOWS\n#else\n#define BACKWARD_SYSTEM_UNKNOWN\n#endif\n#endif\n\n#define NOINLINE __attribute__((noinline))\n\n#include <algorithm>\n#include <cctype>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <fstream>\n#include <iomanip>\n#include <iostream>\n#include <limits>\n#include <new>\n#include <sstream>\n#include <streambuf>\n#include <string>\n#include <vector>\n#include <exception>\n#include <iterator>\n\n#if defined(BACKWARD_SYSTEM_LINUX)\n\n// On linux, backtrace can back-trace or \"walk\" the stack using the following\n// libraries:\n//\n// #define BACKWARD_HAS_UNWIND 1\n//  - unwind comes from libgcc, but I saw an equivalent inside clang itself.\n//  - with unwind, the stacktrace is as accurate as it can possibly be, since\n//  this is used by the C++ runtime in gcc/clang for stack unwinding on\n//  exception.\n//  - normally libgcc is already linked to your program by default.\n//\n// #define BACKWARD_HAS_LIBUNWIND 1\n//  - libunwind provides, in some cases, a more accurate stacktrace as it knows\n//  to decode signal handler frames and lets us edit the context registers when\n//  unwinding, allowing stack traces over bad function references.\n//\n// #define BACKWARD_HAS_BACKTRACE == 1\n//  - backtrace seems to be a little bit more portable than libunwind, but on\n//  linux, it uses unwind anyway, but abstract away a tiny information that is\n//  sadly really important in order to get perfectly accurate stack traces.\n//  - backtrace is part of the (e)glib library.\n//\n// The default is:\n// #define BACKWARD_HAS_UNWIND == 1\n//\n// Note that only one of the define should be set to 1 at a time.\n//\n#if BACKWARD_HAS_UNWIND == 1\n#elif BACKWARD_HAS_LIBUNWIND == 1\n#elif BACKWARD_HAS_BACKTRACE == 1\n#else\n#undef BACKWARD_HAS_UNWIND\n#define BACKWARD_HAS_UNWIND 1\n#undef BACKWARD_HAS_LIBUNWIND\n#define BACKWARD_HAS_LIBUNWIND 0\n#undef BACKWARD_HAS_BACKTRACE\n#define BACKWARD_HAS_BACKTRACE 0\n#endif\n\n// On linux, backward can extract detailed information about a stack trace\n// using one of the following libraries:\n//\n// #define BACKWARD_HAS_DW 1\n//  - libdw gives you the most juicy details out of your stack traces:\n//    - object filename\n//    - function name\n//    - source filename\n//    - line and column numbers\n//    - source code snippet (assuming the file is accessible)\n//    - variable names (if not optimized out)\n//    - variable values (not supported by backward-cpp)\n//  - You need to link with the lib \"dw\":\n//    - apt-get install libdw-dev\n//    - g++/clang++ -ldw ...\n//\n// #define BACKWARD_HAS_BFD 1\n//  - With libbfd, you get a fair amount of details:\n//    - object filename\n//    - function name\n//    - source filename\n//    - line numbers\n//    - source code snippet (assuming the file is accessible)\n//  - You need to link with the lib \"bfd\":\n//    - apt-get install binutils-dev\n//    - g++/clang++ -lbfd ...\n//\n// #define BACKWARD_HAS_DWARF 1\n//  - libdwarf gives you the most juicy details out of your stack traces:\n//    - object filename\n//    - function name\n//    - source filename\n//    - line and column numbers\n//    - source code snippet (assuming the file is accessible)\n//    - variable names (if not optimized out)\n//    - variable values (not supported by backward-cpp)\n//  - You need to link with the lib \"dwarf\":\n//    - apt-get install libdwarf-dev\n//    - g++/clang++ -ldwarf ...\n//\n// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1\n//  - backtrace provides minimal details for a stack trace:\n//    - object filename\n//    - function name\n//  - backtrace is part of the (e)glib library.\n//\n// The default is:\n// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n//\n// Note that only one of the define should be set to 1 at a time.\n//\n#if BACKWARD_HAS_DW == 1\n#elif BACKWARD_HAS_BFD == 1\n#elif BACKWARD_HAS_DWARF == 1\n#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n#else\n#undef BACKWARD_HAS_DW\n#define BACKWARD_HAS_DW 0\n#undef BACKWARD_HAS_BFD\n#define BACKWARD_HAS_BFD 0\n#undef BACKWARD_HAS_DWARF\n#define BACKWARD_HAS_DWARF 0\n#undef BACKWARD_HAS_BACKTRACE_SYMBOL\n#define BACKWARD_HAS_BACKTRACE_SYMBOL 1\n#endif\n\n#include <cxxabi.h>\n#include <fcntl.h>\n#ifdef __ANDROID__\n//\t\tOld Android API levels define _Unwind_Ptr in both link.h and\n// unwind.h \t\tRename the one in link.h as we are not going to be using\n// it\n#define _Unwind_Ptr _Unwind_Ptr_Custom\n#include <link.h>\n#undef _Unwind_Ptr\n#else\n#include <link.h>\n#endif\n#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) ||        \\\n    defined(__POWERPC__)\n// Linux kernel header required for the struct pt_regs definition\n// to access the NIP (Next Instruction Pointer) register value\n#include <asm/ptrace.h>\n#endif\n#include <signal.h>\n#include <sys/stat.h>\n#include <syscall.h>\n#include <unistd.h>\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#include <dlfcn.h>\n#undef _GNU_SOURCE\n#else\n#include <dlfcn.h>\n#endif\n\n#if BACKWARD_HAS_BFD == 1\n//              NOTE: defining PACKAGE{,_VERSION} is required before including\n//                    bfd.h on some platforms, see also:\n//                    https://sourceware.org/bugzilla/show_bug.cgi?id=14243\n#ifndef PACKAGE\n#define PACKAGE\n#endif\n#ifndef PACKAGE_VERSION\n#define PACKAGE_VERSION\n#endif\n#include <bfd.h>\n#endif\n\n#if BACKWARD_HAS_DW == 1\n#include <dwarf.h>\n#include <elfutils/libdw.h>\n#include <elfutils/libdwfl.h>\n#endif\n\n#if BACKWARD_HAS_DWARF == 1\n#include <algorithm>\n#include <dwarf.h>\n#include <libdwarf.h>\n#include <libelf.h>\n#include <map>\n#endif\n\n#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)\n// then we shall rely on backtrace\n#include <execinfo.h>\n#endif\n\n#endif // defined(BACKWARD_SYSTEM_LINUX)\n\n#if defined(BACKWARD_SYSTEM_DARWIN)\n// On Darwin, backtrace can back-trace or \"walk\" the stack using the following\n// libraries:\n//\n// #define BACKWARD_HAS_UNWIND 1\n//  - unwind comes from libgcc, but I saw an equivalent inside clang itself.\n//  - with unwind, the stacktrace is as accurate as it can possibly be, since\n//  this is used by the C++ runtime in gcc/clang for stack unwinding on\n//  exception.\n//  - normally libgcc is already linked to your program by default.\n//\n// #define BACKWARD_HAS_LIBUNWIND 1\n//  - libunwind comes from clang, which implements an API compatible version.\n//  - libunwind provides, in some cases, a more accurate stacktrace as it knows\n//  to decode signal handler frames and lets us edit the context registers when\n//  unwinding, allowing stack traces over bad function references.\n//\n// #define BACKWARD_HAS_BACKTRACE == 1\n//  - backtrace is available by default, though it does not produce as much\n//  information as another library might.\n//\n// The default is:\n// #define BACKWARD_HAS_UNWIND == 1\n//\n// Note that only one of the define should be set to 1 at a time.\n//\n#if BACKWARD_HAS_UNWIND == 1\n#elif BACKWARD_HAS_BACKTRACE == 1\n#elif BACKWARD_HAS_LIBUNWIND == 1\n#else\n#undef BACKWARD_HAS_UNWIND\n#define BACKWARD_HAS_UNWIND 1\n#undef BACKWARD_HAS_BACKTRACE\n#define BACKWARD_HAS_BACKTRACE 0\n#undef BACKWARD_HAS_LIBUNWIND\n#define BACKWARD_HAS_LIBUNWIND 0\n#endif\n\n// On Darwin, backward can extract detailed information about a stack trace\n// using one of the following libraries:\n//\n// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1\n//  - backtrace provides minimal details for a stack trace:\n//    - object filename\n//    - function name\n//\n// The default is:\n// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n//\n#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n#else\n#undef BACKWARD_HAS_BACKTRACE_SYMBOL\n#define BACKWARD_HAS_BACKTRACE_SYMBOL 1\n#endif\n\n#include <cxxabi.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <signal.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)\n#include <execinfo.h>\n#endif\n#endif // defined(BACKWARD_SYSTEM_DARWIN)\n\n#if defined(BACKWARD_SYSTEM_WINDOWS)\n\n#include <condition_variable>\n#include <mutex>\n#include <thread>\n\n#include <basetsd.h>\n\n#ifdef _WIN64\ntypedef SSIZE_T ssize_t;\n#else\ntypedef int ssize_t;\n#endif\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n#include <windows.h>\n#include <winnt.h>\n\n#include <psapi.h>\n#include <signal.h>\n\n#ifndef __clang__\n#undef NOINLINE\n#define NOINLINE __declspec(noinline)\n#endif\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"psapi.lib\")\n#pragma comment(lib, \"dbghelp.lib\")\n#endif\n\n// Comment / packing is from stackoverflow:\n// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227\n// Some versions of imagehlp.dll lack the proper packing directives themselves\n// so we need to do it.\n#pragma pack(push, before_imagehlp, 8)\n#include <imagehlp.h>\n#pragma pack(pop, before_imagehlp)\n\n// TODO maybe these should be undefined somewhere else?\n#undef BACKWARD_HAS_UNWIND\n#undef BACKWARD_HAS_BACKTRACE\n#if BACKWARD_HAS_PDB_SYMBOL == 1\n#else\n#undef BACKWARD_HAS_PDB_SYMBOL\n#define BACKWARD_HAS_PDB_SYMBOL 1\n#endif\n\n#endif\n\n#if BACKWARD_HAS_UNWIND == 1\n\n#include <unwind.h>\n// while gcc's unwind.h defines something like that:\n//  extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *);\n//  extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *);\n//\n// clang's unwind.h defines something like this:\n//  uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context);\n//\n// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we\n// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr\n// anyway.\n//\n// Luckily we can play on the fact that the guard macros have a different name:\n#ifdef __CLANG_UNWIND_H\n// In fact, this function still comes from libgcc (on my different linux boxes,\n// clang links against libgcc).\n#include <inttypes.h>\nextern \"C\" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *);\n#endif\n\n#endif // BACKWARD_HAS_UNWIND == 1\n\n#if BACKWARD_HAS_LIBUNWIND == 1\n#define UNW_LOCAL_ONLY\n#include <libunwind.h>\n#endif // BACKWARD_HAS_LIBUNWIND == 1\n\n#ifdef BACKWARD_ATLEAST_CXX11\n#include <unordered_map>\n#include <utility> // for std::swap\nnamespace backward {\nnamespace details {\ntemplate <typename K, typename V> struct hashtable {\n  typedef std::unordered_map<K, V> type;\n};\nusing std::move;\n} // namespace details\n} // namespace backward\n#else // NOT BACKWARD_ATLEAST_CXX11\n#define nullptr NULL\n#define override\n#include <map>\nnamespace backward {\nnamespace details {\ntemplate <typename K, typename V> struct hashtable {\n  typedef std::map<K, V> type;\n};\ntemplate <typename T> const T &move(const T &v) { return v; }\ntemplate <typename T> T &move(T &v) { return v; }\n} // namespace details\n} // namespace backward\n#endif // BACKWARD_ATLEAST_CXX11\n\nnamespace backward {\nnamespace details {\n#if defined(BACKWARD_SYSTEM_WINDOWS)\nconst char kBackwardPathDelimiter[] = \";\";\n#else\nconst char kBackwardPathDelimiter[] = \":\";\n#endif\n} // namespace details\n} // namespace backward\n\nnamespace backward {\n\nnamespace system_tag {\nstruct linux_tag; // seems that I cannot call that \"linux\" because the name\n// is already defined... so I am adding _tag everywhere.\nstruct darwin_tag;\nstruct windows_tag;\nstruct unknown_tag;\n\n#if defined(BACKWARD_SYSTEM_LINUX)\ntypedef linux_tag current_tag;\n#elif defined(BACKWARD_SYSTEM_DARWIN)\ntypedef darwin_tag current_tag;\n#elif defined(BACKWARD_SYSTEM_WINDOWS)\ntypedef windows_tag current_tag;\n#elif defined(BACKWARD_SYSTEM_UNKNOWN)\ntypedef unknown_tag current_tag;\n#else\n#error \"May I please get my system defines?\"\n#endif\n} // namespace system_tag\n\nnamespace trace_resolver_tag {\n#if defined(BACKWARD_SYSTEM_LINUX)\nstruct libdw;\nstruct libbfd;\nstruct libdwarf;\nstruct backtrace_symbol;\n\n#if BACKWARD_HAS_DW == 1\ntypedef libdw current;\n#elif BACKWARD_HAS_BFD == 1\ntypedef libbfd current;\n#elif BACKWARD_HAS_DWARF == 1\ntypedef libdwarf current;\n#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1\ntypedef backtrace_symbol current;\n#else\n#error \"You shall not pass, until you know what you want.\"\n#endif\n#elif defined(BACKWARD_SYSTEM_DARWIN)\nstruct backtrace_symbol;\n\n#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1\ntypedef backtrace_symbol current;\n#else\n#error \"You shall not pass, until you know what you want.\"\n#endif\n#elif defined(BACKWARD_SYSTEM_WINDOWS)\nstruct pdb_symbol;\n#if BACKWARD_HAS_PDB_SYMBOL == 1\ntypedef pdb_symbol current;\n#else\n#error \"You shall not pass, until you know what you want.\"\n#endif\n#endif\n} // namespace trace_resolver_tag\n\nnamespace details {\n\ntemplate <typename T> struct rm_ptr { typedef T type; };\n\ntemplate <typename T> struct rm_ptr<T *> { typedef T type; };\n\ntemplate <typename T> struct rm_ptr<const T *> { typedef const T type; };\n\ntemplate <typename R, typename T, R (*F)(T)> struct deleter {\n  template <typename U> void operator()(U &ptr) const { (*F)(ptr); }\n};\n\ntemplate <typename T> struct default_delete {\n  void operator()(T &ptr) const { delete ptr; }\n};\n\ntemplate <typename T, typename Deleter = deleter<void, void *, &::free> >\nclass handle {\n  struct dummy;\n  T _val;\n  bool _empty;\n\n#ifdef BACKWARD_ATLEAST_CXX11\n  handle(const handle &) = delete;\n  handle &operator=(const handle &) = delete;\n#endif\n\npublic:\n  ~handle() {\n    if (!_empty) {\n      Deleter()(_val);\n    }\n  }\n\n  explicit handle() : _val(), _empty(true) {}\n  explicit handle(T val) : _val(val), _empty(false) {\n    if (!_val)\n      _empty = true;\n  }\n\n#ifdef BACKWARD_ATLEAST_CXX11\n  handle(handle &&from) : _empty(true) { swap(from); }\n  handle &operator=(handle &&from) {\n    swap(from);\n    return *this;\n  }\n#else\n  explicit handle(const handle &from) : _empty(true) {\n    // some sort of poor man's move semantic.\n    swap(const_cast<handle &>(from));\n  }\n  handle &operator=(const handle &from) {\n    // some sort of poor man's move semantic.\n    swap(const_cast<handle &>(from));\n    return *this;\n  }\n#endif\n\n  void reset(T new_val) {\n    handle tmp(new_val);\n    swap(tmp);\n  }\n\n  void update(T new_val) {\n    _val = new_val;\n    _empty = !static_cast<bool>(new_val);\n  }\n\n  operator const dummy *() const {\n    if (_empty) {\n      return nullptr;\n    }\n    return reinterpret_cast<const dummy *>(_val);\n  }\n  T get() { return _val; }\n  T release() {\n    _empty = true;\n    return _val;\n  }\n  void swap(handle &b) {\n    using std::swap;\n    swap(b._val, _val);     // can throw, we are safe here.\n    swap(b._empty, _empty); // should not throw: if you cannot swap two\n    // bools without throwing... It's a lost cause anyway!\n  }\n\n  T &operator->() { return _val; }\n  const T &operator->() const { return _val; }\n\n  typedef typename rm_ptr<T>::type &ref_t;\n  typedef const typename rm_ptr<T>::type &const_ref_t;\n  ref_t operator*() { return *_val; }\n  const_ref_t operator*() const { return *_val; }\n  ref_t operator[](size_t idx) { return _val[idx]; }\n\n  // Watch out, we've got a badass over here\n  T *operator&() {\n    _empty = false;\n    return &_val;\n  }\n};\n\n// Default demangler implementation (do nothing).\ntemplate <typename TAG> struct demangler_impl {\n  static std::string demangle(const char *funcname) { return funcname; }\n};\n\n#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN)\n\ntemplate <> struct demangler_impl<system_tag::current_tag> {\n  demangler_impl() : _demangle_buffer_length(0) {}\n\n  std::string demangle(const char *funcname) {\n    using namespace details;\n    char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(),\n                                       &_demangle_buffer_length, nullptr);\n    if (result) {\n      _demangle_buffer.update(result);\n      return result;\n    }\n    return funcname;\n  }\n\nprivate:\n  details::handle<char *> _demangle_buffer;\n  size_t _demangle_buffer_length;\n};\n\n#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN\n\nstruct demangler : public demangler_impl<system_tag::current_tag> {};\n\n// Split a string on the platform's PATH delimiter.  Example: if delimiter\n// is \":\" then:\n//   \"\"              --> []\n//   \":\"             --> [\"\",\"\"]\n//   \"::\"            --> [\"\",\"\",\"\"]\n//   \"/a/b/c\"        --> [\"/a/b/c\"]\n//   \"/a/b/c:/d/e/f\" --> [\"/a/b/c\",\"/d/e/f\"]\n//   etc.\ninline std::vector<std::string> split_source_prefixes(const std::string &s) {\n  std::vector<std::string> out;\n  size_t last = 0;\n  size_t next = 0;\n  size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1;\n  while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) {\n    out.push_back(s.substr(last, next - last));\n    last = next + delimiter_size;\n  }\n  if (last <= s.length()) {\n    out.push_back(s.substr(last));\n  }\n  return out;\n}\n\n} // namespace details\n\n/*************** A TRACE ***************/\n\nstruct Trace {\n  void *addr;\n  size_t idx;\n\n  Trace() : addr(nullptr), idx(0) {}\n\n  explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {}\n};\n\nstruct ResolvedTrace : public Trace {\n\n  struct SourceLoc {\n    std::string function;\n    std::string filename;\n    unsigned line;\n    unsigned col;\n\n    SourceLoc() : line(0), col(0) {}\n\n    bool operator==(const SourceLoc &b) const {\n      return function == b.function && filename == b.filename &&\n             line == b.line && col == b.col;\n    }\n\n    bool operator!=(const SourceLoc &b) const { return !(*this == b); }\n  };\n\n  // In which binary object this trace is located.\n  std::string object_filename;\n\n  // The function in the object that contain the trace. This is not the same\n  // as source.function which can be an function inlined in object_function.\n  std::string object_function;\n\n  // The source location of this trace. It is possible for filename to be\n  // empty and for line/col to be invalid (value 0) if this information\n  // couldn't be deduced, for example if there is no debug information in the\n  // binary object.\n  SourceLoc source;\n\n  // An optionals list of \"inliners\". All the successive sources location\n  // from where the source location of the trace (the attribute right above)\n  // is inlined. It is especially useful when you compiled with optimization.\n  typedef std::vector<SourceLoc> source_locs_t;\n  source_locs_t inliners;\n\n  ResolvedTrace() : Trace() {}\n  ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {}\n};\n\n/*************** STACK TRACE ***************/\n\n// default implemention.\ntemplate <typename TAG> class StackTraceImpl {\npublic:\n  size_t size() const { return 0; }\n  Trace operator[](size_t) const { return Trace(); }\n  size_t load_here(size_t = 0) { return 0; }\n  size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) {\n    return 0;\n  }\n  size_t thread_id() const { return 0; }\n  void skip_n_firsts(size_t) {}\n  void *const *begin() const { return nullptr; }\n};\n\nclass StackTraceImplBase {\npublic:\n  StackTraceImplBase()\n      : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {}\n\n  size_t thread_id() const { return _thread_id; }\n\n  void skip_n_firsts(size_t n) { _skip = n; }\n\nprotected:\n  void load_thread_info() {\n#ifdef BACKWARD_SYSTEM_LINUX\n#ifndef __ANDROID__\n    _thread_id = static_cast<size_t>(syscall(SYS_gettid));\n#else\n    _thread_id = static_cast<size_t>(gettid());\n#endif\n    if (_thread_id == static_cast<size_t>(getpid())) {\n      // If the thread is the main one, let's hide that.\n      // I like to keep little secret sometimes.\n      _thread_id = 0;\n    }\n#elif defined(BACKWARD_SYSTEM_DARWIN)\n    _thread_id = reinterpret_cast<size_t>(pthread_self());\n    if (pthread_main_np() == 1) {\n      // If the thread is the main one, let's hide that.\n      _thread_id = 0;\n    }\n#endif\n  }\n\n  void set_context(void *context) { _context = context; }\n  void *context() const { return _context; }\n\n  void set_error_addr(void *error_addr) { _error_addr = error_addr; }\n  void *error_addr() const { return _error_addr; }\n\n  size_t skip_n_firsts() const { return _skip; }\n\nprivate:\n  size_t _thread_id;\n  size_t _skip;\n  void *_context;\n  void *_error_addr;\n};\n\nclass StackTraceImplHolder : public StackTraceImplBase {\npublic:\n  size_t size() const {\n    return (_stacktrace.size() >= skip_n_firsts())\n               ? _stacktrace.size() - skip_n_firsts()\n               : 0;\n  }\n  Trace operator[](size_t idx) const {\n    if (idx >= size()) {\n      return Trace();\n    }\n    return Trace(_stacktrace[idx + skip_n_firsts()], idx);\n  }\n  void *const *begin() const {\n    if (size()) {\n      return &_stacktrace[skip_n_firsts()];\n    }\n    return nullptr;\n  }\n\nprotected:\n  std::vector<void *> _stacktrace;\n};\n\n#if BACKWARD_HAS_UNWIND == 1\n\nnamespace details {\n\ntemplate <typename F> class Unwinder {\npublic:\n  size_t operator()(F &f, size_t depth) {\n    _f = &f;\n    _index = -1;\n    _depth = depth;\n    _Unwind_Backtrace(&this->backtrace_trampoline, this);\n    if (_index == -1) {\n      // _Unwind_Backtrace has failed to obtain any backtraces\n      return 0;\n    } else {\n      return static_cast<size_t>(_index);\n    }\n  }\n\nprivate:\n  F *_f;\n  ssize_t _index;\n  size_t _depth;\n\n  static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx,\n                                                  void *self) {\n    return (static_cast<Unwinder *>(self))->backtrace(ctx);\n  }\n\n  _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) {\n    if (_index >= 0 && static_cast<size_t>(_index) >= _depth)\n      return _URC_END_OF_STACK;\n\n    int ip_before_instruction = 0;\n    uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);\n\n    if (!ip_before_instruction) {\n      // calculating 0-1 for unsigned, looks like a possible bug to sanitizers,\n      // so let's do it explicitly:\n      if (ip == 0) {\n        ip = std::numeric_limits<uintptr_t>::max(); // set it to 0xffff... (as\n                                                    // from casting 0-1)\n      } else {\n        ip -= 1; // else just normally decrement it (no overflow/underflow will\n                 // happen)\n      }\n    }\n\n    if (_index >= 0) { // ignore first frame.\n      (*_f)(static_cast<size_t>(_index), reinterpret_cast<void *>(ip));\n    }\n    _index += 1;\n    return _URC_NO_REASON;\n  }\n};\n\ntemplate <typename F> size_t unwind(F f, size_t depth) {\n  Unwinder<F> unwinder;\n  return unwinder(f, depth);\n}\n\n} // namespace details\n\ntemplate <>\nclass StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {\npublic:\n  NOINLINE\n  size_t load_here(size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    load_thread_info();\n    set_context(context);\n    set_error_addr(error_addr);\n    if (depth == 0) {\n      return 0;\n    }\n    _stacktrace.resize(depth);\n    size_t trace_cnt = details::unwind(callback(*this), depth);\n    _stacktrace.resize(trace_cnt);\n    skip_n_firsts(0);\n    return size();\n  }\n  size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    load_here(depth + 8, context, error_addr);\n\n    for (size_t i = 0; i < _stacktrace.size(); ++i) {\n      if (_stacktrace[i] == addr) {\n        skip_n_firsts(i);\n        break;\n      }\n    }\n\n    _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));\n    return size();\n  }\n\nprivate:\n  struct callback {\n    StackTraceImpl &self;\n    callback(StackTraceImpl &_self) : self(_self) {}\n\n    void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; }\n  };\n};\n\n#elif BACKWARD_HAS_LIBUNWIND == 1\n\ntemplate <>\nclass StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {\npublic:\n  __attribute__((noinline)) size_t load_here(size_t depth = 32,\n                                             void *_context = nullptr,\n                                             void *_error_addr = nullptr) {\n    set_context(_context);\n    set_error_addr(_error_addr);\n    load_thread_info();\n    if (depth == 0) {\n      return 0;\n    }\n    _stacktrace.resize(depth + 1);\n\n    int result = 0;\n\n    unw_context_t ctx;\n    size_t index = 0;\n\n    // Add the tail call. If the Instruction Pointer is the crash address it\n    // means we got a bad function pointer dereference, so we \"unwind\" the\n    // bad pointer manually by using the return address pointed to by the\n    // Stack Pointer as the Instruction Pointer and letting libunwind do\n    // the rest\n\n    if (context()) {\n      ucontext_t *uctx = reinterpret_cast<ucontext_t *>(context());\n#ifdef REG_RIP         // x86_64\n      if (uctx->uc_mcontext.gregs[REG_RIP] ==\n          reinterpret_cast<greg_t>(error_addr())) {\n        uctx->uc_mcontext.gregs[REG_RIP] =\n            *reinterpret_cast<size_t *>(uctx->uc_mcontext.gregs[REG_RSP]);\n      }\n      _stacktrace[index] =\n          reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_RIP]);\n      ++index;\n      ctx = *reinterpret_cast<unw_context_t *>(uctx);\n#elif defined(REG_EIP) // x86_32\n      if (uctx->uc_mcontext.gregs[REG_EIP] ==\n          reinterpret_cast<greg_t>(error_addr())) {\n        uctx->uc_mcontext.gregs[REG_EIP] =\n            *reinterpret_cast<size_t *>(uctx->uc_mcontext.gregs[REG_ESP]);\n      }\n      _stacktrace[index] =\n          reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_EIP]);\n      ++index;\n      ctx = *reinterpret_cast<unw_context_t *>(uctx);\n#elif defined(__arm__)\n      // libunwind uses its own context type for ARM unwinding.\n      // Copy the registers from the signal handler's context so we can\n      // unwind\n      unw_getcontext(&ctx);\n      ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0;\n      ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1;\n      ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2;\n      ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3;\n      ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4;\n      ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5;\n      ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6;\n      ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7;\n      ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8;\n      ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9;\n      ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10;\n      ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp;\n      ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip;\n      ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp;\n      ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr;\n      ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc;\n\n      // If we have crashed in the PC use the LR instead, as this was\n      // a bad function dereference\n      if (reinterpret_cast<unsigned long>(error_addr()) ==\n          uctx->uc_mcontext.arm_pc) {\n        ctx.regs[UNW_ARM_R15] =\n            uctx->uc_mcontext.arm_lr - sizeof(unsigned long);\n      }\n      _stacktrace[index] = reinterpret_cast<void *>(ctx.regs[UNW_ARM_R15]);\n      ++index;\n#elif defined(__APPLE__) && defined(__x86_64__)\n      unw_getcontext(&ctx);\n      // OS X's implementation of libunwind uses its own context object\n      // so we need to convert the passed context to libunwind's format\n      // (information about the data layout taken from unw_getcontext.s\n      // in Apple's libunwind source\n      ctx.data[0] = uctx->uc_mcontext->__ss.__rax;\n      ctx.data[1] = uctx->uc_mcontext->__ss.__rbx;\n      ctx.data[2] = uctx->uc_mcontext->__ss.__rcx;\n      ctx.data[3] = uctx->uc_mcontext->__ss.__rdx;\n      ctx.data[4] = uctx->uc_mcontext->__ss.__rdi;\n      ctx.data[5] = uctx->uc_mcontext->__ss.__rsi;\n      ctx.data[6] = uctx->uc_mcontext->__ss.__rbp;\n      ctx.data[7] = uctx->uc_mcontext->__ss.__rsp;\n      ctx.data[8] = uctx->uc_mcontext->__ss.__r8;\n      ctx.data[9] = uctx->uc_mcontext->__ss.__r9;\n      ctx.data[10] = uctx->uc_mcontext->__ss.__r10;\n      ctx.data[11] = uctx->uc_mcontext->__ss.__r11;\n      ctx.data[12] = uctx->uc_mcontext->__ss.__r12;\n      ctx.data[13] = uctx->uc_mcontext->__ss.__r13;\n      ctx.data[14] = uctx->uc_mcontext->__ss.__r14;\n      ctx.data[15] = uctx->uc_mcontext->__ss.__r15;\n      ctx.data[16] = uctx->uc_mcontext->__ss.__rip;\n\n      // If the IP is the same as the crash address we have a bad function\n      // dereference The caller's address is pointed to by %rsp, so we\n      // dereference that value and set it to be the next frame's IP.\n      if (uctx->uc_mcontext->__ss.__rip ==\n          reinterpret_cast<__uint64_t>(error_addr())) {\n        ctx.data[16] =\n            *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp);\n      }\n      _stacktrace[index] = reinterpret_cast<void *>(ctx.data[16]);\n      ++index;\n#elif defined(__APPLE__)\n      unw_getcontext(&ctx)\n          // TODO: Convert the ucontext_t to libunwind's unw_context_t like\n          // we do in 64 bits\n          if (ctx.uc_mcontext->__ss.__eip ==\n              reinterpret_cast<greg_t>(error_addr())) {\n        ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp;\n      }\n      _stacktrace[index] =\n          reinterpret_cast<void *>(ctx.uc_mcontext->__ss.__eip);\n      ++index;\n#endif\n    }\n\n    unw_cursor_t cursor;\n    if (context()) {\n#if defined(UNW_INIT_SIGNAL_FRAME)\n      result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME);\n#else\n      result = unw_init_local(&cursor, &ctx);\n#endif\n    } else {\n      unw_getcontext(&ctx);\n      ;\n      result = unw_init_local(&cursor, &ctx);\n    }\n\n    if (result != 0)\n      return 1;\n\n    unw_word_t ip = 0;\n\n    while (index <= depth && unw_step(&cursor) > 0) {\n      result = unw_get_reg(&cursor, UNW_REG_IP, &ip);\n      if (result == 0) {\n        _stacktrace[index] = reinterpret_cast<void *>(--ip);\n        ++index;\n      }\n    }\n    --index;\n\n    _stacktrace.resize(index + 1);\n    skip_n_firsts(0);\n    return size();\n  }\n\n  size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    load_here(depth + 8, context, error_addr);\n\n    for (size_t i = 0; i < _stacktrace.size(); ++i) {\n      if (_stacktrace[i] == addr) {\n        skip_n_firsts(i);\n        _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]);\n        break;\n      }\n    }\n\n    _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));\n    return size();\n  }\n};\n\n#elif defined(BACKWARD_HAS_BACKTRACE)\n\ntemplate <>\nclass StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {\npublic:\n  NOINLINE\n  size_t load_here(size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    set_context(context);\n    set_error_addr(error_addr);\n    load_thread_info();\n    if (depth == 0) {\n      return 0;\n    }\n    _stacktrace.resize(depth + 1);\n    size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size());\n    _stacktrace.resize(trace_cnt);\n    skip_n_firsts(1);\n    return size();\n  }\n\n  size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    load_here(depth + 8, context, error_addr);\n\n    for (size_t i = 0; i < _stacktrace.size(); ++i) {\n      if (_stacktrace[i] == addr) {\n        skip_n_firsts(i);\n        _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1);\n        break;\n      }\n    }\n\n    _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));\n    return size();\n  }\n};\n\n#elif defined(BACKWARD_SYSTEM_WINDOWS)\n\ntemplate <>\nclass StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {\npublic:\n  // We have to load the machine type from the image info\n  // So we first initialize the resolver, and it tells us this info\n  void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; }\n  void set_context(CONTEXT *ctx) { ctx_ = ctx; }\n  void set_thread_handle(HANDLE handle) { thd_ = handle; }\n\n  NOINLINE\n  size_t load_here(size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    set_context(static_cast<CONTEXT*>(context));\n    set_error_addr(error_addr);\n    CONTEXT localCtx; // used when no context is provided\n\n    if (depth == 0) {\n      return 0;\n    }\n\n    if (!ctx_) {\n      ctx_ = &localCtx;\n      RtlCaptureContext(ctx_);\n    }\n\n    if (!thd_) {\n      thd_ = GetCurrentThread();\n    }\n\n    HANDLE process = GetCurrentProcess();\n\n    STACKFRAME64 s;\n    memset(&s, 0, sizeof(STACKFRAME64));\n\n    // TODO: 32 bit context capture\n    s.AddrStack.Mode = AddrModeFlat;\n    s.AddrFrame.Mode = AddrModeFlat;\n    s.AddrPC.Mode = AddrModeFlat;\n#ifdef _M_X64\n    s.AddrPC.Offset = ctx_->Rip;\n    s.AddrStack.Offset = ctx_->Rsp;\n    s.AddrFrame.Offset = ctx_->Rbp;\n#else\n    s.AddrPC.Offset = ctx_->Eip;\n    s.AddrStack.Offset = ctx_->Esp;\n    s.AddrFrame.Offset = ctx_->Ebp;\n#endif\n\n    if (!machine_type_) {\n#ifdef _M_X64\n      machine_type_ = IMAGE_FILE_MACHINE_AMD64;\n#else\n      machine_type_ = IMAGE_FILE_MACHINE_I386;\n#endif\n    }\n\n    for (;;) {\n      // NOTE: this only works if PDBs are already loaded!\n      SetLastError(0);\n      if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL,\n                       SymFunctionTableAccess64, SymGetModuleBase64, NULL))\n        break;\n\n      if (s.AddrReturn.Offset == 0)\n        break;\n\n      _stacktrace.push_back(reinterpret_cast<void *>(s.AddrPC.Offset));\n\n      if (size() >= depth)\n        break;\n    }\n\n    return size();\n  }\n\n  size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,\n                   void *error_addr = nullptr) {\n    load_here(depth + 8, context, error_addr);\n\n    for (size_t i = 0; i < _stacktrace.size(); ++i) {\n      if (_stacktrace[i] == addr) {\n        skip_n_firsts(i);\n        break;\n      }\n    }\n\n    _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));\n    return size();\n  }\n\nprivate:\n  DWORD machine_type_ = 0;\n  HANDLE thd_ = 0;\n  CONTEXT *ctx_ = nullptr;\n};\n\n#endif\n\nclass StackTrace : public StackTraceImpl<system_tag::current_tag> {};\n\n/*************** TRACE RESOLVER ***************/\n\nclass TraceResolverImplBase {\npublic:\n  virtual ~TraceResolverImplBase() {}\n\n  virtual void load_addresses(void *const*addresses, int address_count) {\n    (void)addresses;\n    (void)address_count;\n  }\n\n  template <class ST> void load_stacktrace(ST &st) {\n    load_addresses(st.begin(), static_cast<int>(st.size()));\n  }\n\n  virtual ResolvedTrace resolve(ResolvedTrace t) { return t; }\n\nprotected:\n  std::string demangle(const char *funcname) {\n    return _demangler.demangle(funcname);\n  }\n\nprivate:\n  details::demangler _demangler;\n};\n\ntemplate <typename TAG> class TraceResolverImpl;\n\n#ifdef BACKWARD_SYSTEM_UNKNOWN\n\ntemplate <> class TraceResolverImpl<system_tag::unknown_tag>\n    : public TraceResolverImplBase {};\n\n#endif\n\n#ifdef BACKWARD_SYSTEM_LINUX\n\nclass TraceResolverLinuxBase : public TraceResolverImplBase {\npublic:\n  TraceResolverLinuxBase()\n      : argv0_(get_argv0()), exec_path_(read_symlink(\"/proc/self/exe\")) {}\n  std::string resolve_exec_path(Dl_info &symbol_info) const {\n    // mutates symbol_info.dli_fname to be filename to open and returns filename\n    // to display\n    if (symbol_info.dli_fname == argv0_) {\n      // dladdr returns argv[0] in dli_fname for symbols contained in\n      // the main executable, which is not a valid path if the\n      // executable was found by a search of the PATH environment\n      // variable; In that case, we actually open /proc/self/exe, which\n      // is always the actual executable (even if it was deleted/replaced!)\n      // but display the path that /proc/self/exe links to.\n      // However, this right away reduces probability of successful symbol\n      // resolution, because libbfd may try to find *.debug files in the\n      // same dir, in case symbols are stripped. As a result, it may try\n      // to find a file /proc/self/<exe_name>.debug, which obviously does\n      // not exist. /proc/self/exe is a last resort. First load attempt\n      // should go for the original executable file path.\n      symbol_info.dli_fname = \"/proc/self/exe\";\n      return exec_path_;\n    } else {\n      return symbol_info.dli_fname;\n    }\n  }\n\nprivate:\n  std::string argv0_;\n  std::string exec_path_;\n\n  static std::string get_argv0() {\n    std::string argv0;\n    std::ifstream ifs(\"/proc/self/cmdline\");\n    std::getline(ifs, argv0, '\\0');\n    return argv0;\n  }\n\n  static std::string read_symlink(std::string const &symlink_path) {\n    std::string path;\n    path.resize(100);\n\n    while (true) {\n      ssize_t len =\n          ::readlink(symlink_path.c_str(), &*path.begin(), path.size());\n      if (len < 0) {\n        return \"\";\n      }\n      if (static_cast<size_t>(len) == path.size()) {\n        path.resize(path.size() * 2);\n      } else {\n        path.resize(static_cast<std::string::size_type>(len));\n        break;\n      }\n    }\n\n    return path;\n  }\n};\n\ntemplate <typename STACKTRACE_TAG> class TraceResolverLinuxImpl;\n\n#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n\ntemplate <>\nclass TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol>\n    : public TraceResolverLinuxBase {\npublic:\n  void load_addresses(void *const*addresses, int address_count) override {\n    if (address_count == 0) {\n      return;\n    }\n    _symbols.reset(backtrace_symbols(addresses, address_count));\n  }\n\n  ResolvedTrace resolve(ResolvedTrace trace) override {\n    char *filename = _symbols[trace.idx];\n    char *funcname = filename;\n    while (*funcname && *funcname != '(') {\n      funcname += 1;\n    }\n    trace.object_filename.assign(filename,\n                                 funcname); // ok even if funcname is the ending\n                                            // \\0 (then we assign entire string)\n\n    if (*funcname) { // if it's not end of string (e.g. from last frame ip==0)\n      funcname += 1;\n      char *funcname_end = funcname;\n      while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') {\n        funcname_end += 1;\n      }\n      *funcname_end = '\\0';\n      trace.object_function = this->demangle(funcname);\n      trace.source.function = trace.object_function; // we cannot do better.\n    }\n    return trace;\n  }\n\nprivate:\n  details::handle<char **> _symbols;\n};\n\n#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1\n\n#if BACKWARD_HAS_BFD == 1\n\ntemplate <>\nclass TraceResolverLinuxImpl<trace_resolver_tag::libbfd>\n    : public TraceResolverLinuxBase {\npublic:\n  TraceResolverLinuxImpl() : _bfd_loaded(false) {}\n\n  ResolvedTrace resolve(ResolvedTrace trace) override {\n    Dl_info symbol_info;\n\n    // trace.addr is a virtual address in memory pointing to some code.\n    // Let's try to find from which loaded object it comes from.\n    // The loaded object can be yourself btw.\n    if (!dladdr(trace.addr, &symbol_info)) {\n      return trace; // dat broken trace...\n    }\n\n    // Now we get in symbol_info:\n    // .dli_fname:\n    //\t\tpathname of the shared object that contains the address.\n    // .dli_fbase:\n    //\t\twhere the object is loaded in memory.\n    // .dli_sname:\n    //\t\tthe name of the nearest symbol to trace.addr, we expect a\n    //\t\tfunction name.\n    // .dli_saddr:\n    //\t\tthe exact address corresponding to .dli_sname.\n\n    if (symbol_info.dli_sname) {\n      trace.object_function = demangle(symbol_info.dli_sname);\n    }\n\n    if (!symbol_info.dli_fname) {\n      return trace;\n    }\n\n    trace.object_filename = resolve_exec_path(symbol_info);\n    bfd_fileobject *fobj;\n    // Before rushing to resolution need to ensure the executable\n    // file still can be used. For that compare inode numbers of\n    // what is stored by the executable's file path, and in the\n    // dli_fname, which not necessarily equals to the executable.\n    // It can be a shared library, or /proc/self/exe, and in the\n    // latter case has drawbacks. See the exec path resolution for\n    // details. In short - the dli object should be used only as\n    // the last resort.\n    // If inode numbers are equal, it is known dli_fname and the\n    // executable file are the same. This is guaranteed by Linux,\n    // because if the executable file is changed/deleted, it will\n    // be done in a new inode. The old file will be preserved in\n    // /proc/self/exe, and may even have inode 0. The latter can\n    // happen if the inode was actually reused, and the file was\n    // kept only in the main memory.\n    //\n    struct stat obj_stat;\n    struct stat dli_stat;\n    if (stat(trace.object_filename.c_str(), &obj_stat) == 0 &&\n        stat(symbol_info.dli_fname, &dli_stat) == 0 &&\n        obj_stat.st_ino == dli_stat.st_ino) {\n      // The executable file, and the shared object containing the\n      // address are the same file. Safe to use the original path.\n      // this is preferable. Libbfd will search for stripped debug\n      // symbols in the same directory.\n      fobj = load_object_with_bfd(trace.object_filename);\n    } else{\n      // The original object file was *deleted*! The only hope is\n      // that the debug symbols are either inside the shared\n      // object file, or are in the same directory, and this is\n      // not /proc/self/exe.\n      fobj = nullptr;\n    }\n    if (fobj == nullptr || !fobj->handle) {\n      fobj = load_object_with_bfd(symbol_info.dli_fname);\n      if (!fobj->handle) {\n        return trace;\n      }\n    }\n\n    find_sym_result *details_selected; // to be filled.\n\n    // trace.addr is the next instruction to be executed after returning\n    // from the nested stack frame. In C++ this usually relate to the next\n    // statement right after the function call that leaded to a new stack\n    // frame. This is not usually what you want to see when printing out a\n    // stacktrace...\n    find_sym_result details_call_site =\n        find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);\n    details_selected = &details_call_site;\n\n#if BACKWARD_HAS_UNWIND == 0\n    // ...this is why we also try to resolve the symbol that is right\n    // before the return address. If we are lucky enough, we will get the\n    // line of the function that was called. But if the code is optimized,\n    // we might get something absolutely not related since the compiler\n    // can reschedule the return address with inline functions and\n    // tail-call optimization (among other things that I don't even know\n    // or cannot even dream about with my tiny limited brain).\n    find_sym_result details_adjusted_call_site = find_symbol_details(\n        fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase);\n\n    // In debug mode, we should always get the right thing(TM).\n    if (details_call_site.found && details_adjusted_call_site.found) {\n      // Ok, we assume that details_adjusted_call_site is a better estimation.\n      details_selected = &details_adjusted_call_site;\n      trace.addr = (void *)(uintptr_t(trace.addr) - 1);\n    }\n\n    if (details_selected == &details_call_site && details_call_site.found) {\n      // we have to re-resolve the symbol in order to reset some\n      // internal state in BFD... so we can call backtrace_inliners\n      // thereafter...\n      details_call_site =\n          find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);\n    }\n#endif // BACKWARD_HAS_UNWIND\n\n    if (details_selected->found) {\n      if (details_selected->filename) {\n        trace.source.filename = details_selected->filename;\n      }\n      trace.source.line = details_selected->line;\n\n      if (details_selected->funcname) {\n        // this time we get the name of the function where the code is\n        // located, instead of the function were the address is\n        // located. In short, if the code was inlined, we get the\n        // function corresponding to the code. Else we already got in\n        // trace.function.\n        trace.source.function = demangle(details_selected->funcname);\n\n        if (!symbol_info.dli_sname) {\n          // for the case dladdr failed to find the symbol name of\n          // the function, we might as well try to put something\n          // here.\n          trace.object_function = trace.source.function;\n        }\n      }\n\n      // Maybe the source of the trace got inlined inside the function\n      // (trace.source.function). Let's see if we can get all the inlined\n      // calls along the way up to the initial call site.\n      trace.inliners = backtrace_inliners(fobj, *details_selected);\n\n#if 0\n\t\t\tif (trace.inliners.size() == 0) {\n\t\t\t\t// Maybe the trace was not inlined... or maybe it was and we\n\t\t\t\t// are lacking the debug information. Let's try to make the\n\t\t\t\t// world better and see if we can get the line number of the\n\t\t\t\t// function (trace.source.function) now.\n\t\t\t\t//\n\t\t\t\t// We will get the location of where the function start (to be\n\t\t\t\t// exact: the first instruction that really start the\n\t\t\t\t// function), not where the name of the function is defined.\n\t\t\t\t// This can be quite far away from the name of the function\n\t\t\t\t// btw.\n\t\t\t\t//\n\t\t\t\t// If the source of the function is the same as the source of\n\t\t\t\t// the trace, we cannot say if the trace was really inlined or\n\t\t\t\t// not.  However, if the filename of the source is different\n\t\t\t\t// between the function and the trace... we can declare it as\n\t\t\t\t// an inliner.  This is not 100% accurate, but better than\n\t\t\t\t// nothing.\n\n\t\t\t\tif (symbol_info.dli_saddr) {\n\t\t\t\t\tfind_sym_result details = find_symbol_details(fobj,\n\t\t\t\t\t\t\tsymbol_info.dli_saddr,\n\t\t\t\t\t\t\tsymbol_info.dli_fbase);\n\n\t\t\t\t\tif (details.found) {\n\t\t\t\t\t\tResolvedTrace::SourceLoc diy_inliner;\n\t\t\t\t\t\tdiy_inliner.line = details.line;\n\t\t\t\t\t\tif (details.filename) {\n\t\t\t\t\t\t\tdiy_inliner.filename = details.filename;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (details.funcname) {\n\t\t\t\t\t\t\tdiy_inliner.function = demangle(details.funcname);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdiy_inliner.function = trace.source.function;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (diy_inliner != trace.source) {\n\t\t\t\t\t\t\ttrace.inliners.push_back(diy_inliner);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n    }\n\n    return trace;\n  }\n\nprivate:\n  bool _bfd_loaded;\n\n  typedef details::handle<bfd *,\n                          details::deleter<bfd_boolean, bfd *, &bfd_close> >\n      bfd_handle_t;\n\n  typedef details::handle<asymbol **> bfd_symtab_t;\n\n  struct bfd_fileobject {\n    bfd_handle_t handle;\n    bfd_vma base_addr;\n    bfd_symtab_t symtab;\n    bfd_symtab_t dynamic_symtab;\n  };\n\n  typedef details::hashtable<std::string, bfd_fileobject>::type fobj_bfd_map_t;\n  fobj_bfd_map_t _fobj_bfd_map;\n\n  bfd_fileobject *load_object_with_bfd(const std::string &filename_object) {\n    using namespace details;\n\n    if (!_bfd_loaded) {\n      using namespace details;\n      bfd_init();\n      _bfd_loaded = true;\n    }\n\n    fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object);\n    if (it != _fobj_bfd_map.end()) {\n      return &it->second;\n    }\n\n    // this new object is empty for now.\n    bfd_fileobject *r = &_fobj_bfd_map[filename_object];\n\n    // we do the work temporary in this one;\n    bfd_handle_t bfd_handle;\n\n    int fd = open(filename_object.c_str(), O_RDONLY);\n    bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), \"default\", fd));\n    if (!bfd_handle) {\n      close(fd);\n      return r;\n    }\n\n    if (!bfd_check_format(bfd_handle.get(), bfd_object)) {\n      return r; // not an object? You lose.\n    }\n\n    if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) {\n      return r; // that's what happen when you forget to compile in debug.\n    }\n\n    ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get());\n\n    ssize_t dyn_symtab_storage_size =\n        bfd_get_dynamic_symtab_upper_bound(bfd_handle.get());\n\n    if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) {\n      return r; // weird, is the file is corrupted?\n    }\n\n    bfd_symtab_t symtab, dynamic_symtab;\n    ssize_t symcount = 0, dyn_symcount = 0;\n\n    if (symtab_storage_size > 0) {\n      symtab.reset(static_cast<bfd_symbol **>(\n          malloc(static_cast<size_t>(symtab_storage_size))));\n      symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get());\n    }\n\n    if (dyn_symtab_storage_size > 0) {\n      dynamic_symtab.reset(static_cast<bfd_symbol **>(\n          malloc(static_cast<size_t>(dyn_symtab_storage_size))));\n      dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(),\n                                                     dynamic_symtab.get());\n    }\n\n    if (symcount <= 0 && dyn_symcount <= 0) {\n      return r; // damned, that's a stripped file that you got there!\n    }\n\n    r->handle = move(bfd_handle);\n    r->symtab = move(symtab);\n    r->dynamic_symtab = move(dynamic_symtab);\n    return r;\n  }\n\n  struct find_sym_result {\n    bool found;\n    const char *filename;\n    const char *funcname;\n    unsigned int line;\n  };\n\n  struct find_sym_context {\n    TraceResolverLinuxImpl *self;\n    bfd_fileobject *fobj;\n    void *addr;\n    void *base_addr;\n    find_sym_result result;\n  };\n\n  find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr,\n                                      void *base_addr) {\n    find_sym_context context;\n    context.self = this;\n    context.fobj = fobj;\n    context.addr = addr;\n    context.base_addr = base_addr;\n    context.result.found = false;\n    bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline,\n                          static_cast<void *>(&context));\n    return context.result;\n  }\n\n  static void find_in_section_trampoline(bfd *, asection *section, void *data) {\n    find_sym_context *context = static_cast<find_sym_context *>(data);\n    context->self->find_in_section(\n        reinterpret_cast<bfd_vma>(context->addr),\n        reinterpret_cast<bfd_vma>(context->base_addr), context->fobj, section,\n        context->result);\n  }\n\n  void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj,\n                       asection *section, find_sym_result &result) {\n    if (result.found)\n      return;\n\n#ifdef bfd_get_section_flags\n    if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0)\n#else\n    if ((bfd_section_flags(section) & SEC_ALLOC) == 0)\n#endif\n      return; // a debug section is never loaded automatically.\n\n#ifdef bfd_get_section_vma\n    bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section);\n#else\n    bfd_vma sec_addr = bfd_section_vma(section);\n#endif\n#ifdef bfd_get_section_size\n    bfd_size_type size = bfd_get_section_size(section);\n#else\n    bfd_size_type size = bfd_section_size(section);\n#endif\n\n    // are we in the boundaries of the section?\n    if (addr < sec_addr || addr >= sec_addr + size) {\n      addr -= base_addr; // oops, a relocated object, lets try again...\n      if (addr < sec_addr || addr >= sec_addr + size) {\n        return;\n      }\n    }\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"\n#endif\n    if (!result.found && fobj->symtab) {\n      result.found = bfd_find_nearest_line(\n          fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr,\n          &result.filename, &result.funcname, &result.line);\n    }\n\n    if (!result.found && fobj->dynamic_symtab) {\n      result.found = bfd_find_nearest_line(\n          fobj->handle.get(), section, fobj->dynamic_symtab.get(),\n          addr - sec_addr, &result.filename, &result.funcname, &result.line);\n    }\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n  }\n\n  ResolvedTrace::source_locs_t\n  backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) {\n    // This function can be called ONLY after a SUCCESSFUL call to\n    // find_symbol_details. The state is global to the bfd_handle.\n    ResolvedTrace::source_locs_t results;\n    while (previous_result.found) {\n      find_sym_result result;\n      result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename,\n                                           &result.funcname, &result.line);\n\n      if (result\n              .found) /* and not (\n                            cstrings_eq(previous_result.filename,\n                         result.filename) and\n                         cstrings_eq(previous_result.funcname, result.funcname)\n                            and result.line == previous_result.line\n                            )) */\n      {\n        ResolvedTrace::SourceLoc src_loc;\n        src_loc.line = result.line;\n        if (result.filename) {\n          src_loc.filename = result.filename;\n        }\n        if (result.funcname) {\n          src_loc.function = demangle(result.funcname);\n        }\n        results.push_back(src_loc);\n      }\n      previous_result = result;\n    }\n    return results;\n  }\n\n  bool cstrings_eq(const char *a, const char *b) {\n    if (!a || !b) {\n      return false;\n    }\n    return strcmp(a, b) == 0;\n  }\n};\n#endif // BACKWARD_HAS_BFD == 1\n\n#if BACKWARD_HAS_DW == 1\n\ntemplate <>\nclass TraceResolverLinuxImpl<trace_resolver_tag::libdw>\n    : public TraceResolverLinuxBase {\npublic:\n  TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {}\n\n  ResolvedTrace resolve(ResolvedTrace trace) override {\n    using namespace details;\n\n    Dwarf_Addr trace_addr = reinterpret_cast<Dwarf_Addr>(trace.addr);\n\n    if (!_dwfl_handle_initialized) {\n      // initialize dwfl...\n      _dwfl_cb.reset(new Dwfl_Callbacks);\n      _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf;\n      _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo;\n      _dwfl_cb->debuginfo_path = 0;\n\n      _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get()));\n      _dwfl_handle_initialized = true;\n\n      if (!_dwfl_handle) {\n        return trace;\n      }\n\n      // ...from the current process.\n      dwfl_report_begin(_dwfl_handle.get());\n      int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid());\n      dwfl_report_end(_dwfl_handle.get(), NULL, NULL);\n      if (r < 0) {\n        return trace;\n      }\n    }\n\n    if (!_dwfl_handle) {\n      return trace;\n    }\n\n    // find the module (binary object) that contains the trace's address.\n    // This is not using any debug information, but the addresses ranges of\n    // all the currently loaded binary object.\n    Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr);\n    if (mod) {\n      // now that we found it, lets get the name of it, this will be the\n      // full path to the running binary or one of the loaded library.\n      const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0);\n      if (module_name) {\n        trace.object_filename = module_name;\n      }\n      // We also look after the name of the symbol, equal or before this\n      // address. This is found by walking the symtab. We should get the\n      // symbol corresponding to the function (mangled) containing the\n      // address. If the code corresponding to the address was inlined,\n      // this is the name of the out-most inliner function.\n      const char *sym_name = dwfl_module_addrname(mod, trace_addr);\n      if (sym_name) {\n        trace.object_function = demangle(sym_name);\n      }\n    }\n\n    // now let's get serious, and find out the source location (file and\n    // line number) of the address.\n\n    // This function will look in .debug_aranges for the address and map it\n    // to the location of the compilation unit DIE in .debug_info and\n    // return it.\n    Dwarf_Addr mod_bias = 0;\n    Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias);\n\n#if 1\n    if (!cudie) {\n      // Sadly clang does not generate the section .debug_aranges, thus\n      // dwfl_module_addrdie will fail early. Clang doesn't either set\n      // the lowpc/highpc/range info for every compilation unit.\n      //\n      // So in order to save the world:\n      // for every compilation unit, we will iterate over every single\n      // DIEs. Normally functions should have a lowpc/highpc/range, which\n      // we will use to infer the compilation unit.\n\n      // note that this is probably badly inefficient.\n      while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) {\n        Dwarf_Die die_mem;\n        Dwarf_Die *fundie =\n            find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem);\n        if (fundie) {\n          break;\n        }\n      }\n    }\n#endif\n\n//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE\n#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE\n    if (!cudie) {\n      // If it's still not enough, lets dive deeper in the shit, and try\n      // to save the world again: for every compilation unit, we will\n      // load the corresponding .debug_line section, and see if we can\n      // find our address in it.\n\n      Dwarf_Addr cfi_bias;\n      Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias);\n\n      Dwarf_Addr bias;\n      while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) {\n        if (dwarf_getsrc_die(cudie, trace_addr - bias)) {\n\n          // ...but if we get a match, it might be a false positive\n          // because our (address - bias) might as well be valid in a\n          // different compilation unit. So we throw our last card on\n          // the table and lookup for the address into the .eh_frame\n          // section.\n\n          handle<Dwarf_Frame *> frame;\n          dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame);\n          if (frame) {\n            break;\n          }\n        }\n      }\n    }\n#endif\n\n    if (!cudie) {\n      return trace; // this time we lost the game :/\n    }\n\n    // Now that we have a compilation unit DIE, this function will be able\n    // to load the corresponding section in .debug_line (if not already\n    // loaded) and hopefully find the source location mapped to our\n    // address.\n    Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias);\n\n    if (srcloc) {\n      const char *srcfile = dwarf_linesrc(srcloc, 0, 0);\n      if (srcfile) {\n        trace.source.filename = srcfile;\n      }\n      int line = 0, col = 0;\n      dwarf_lineno(srcloc, &line);\n      dwarf_linecol(srcloc, &col);\n      trace.source.line = static_cast<unsigned>(line);\n      trace.source.col = static_cast<unsigned>(col);\n    }\n\n    deep_first_search_by_pc(cudie, trace_addr - mod_bias,\n                            inliners_search_cb(trace));\n    if (trace.source.function.size() == 0) {\n      // fallback.\n      trace.source.function = trace.object_function;\n    }\n\n    return trace;\n  }\n\nprivate:\n  typedef details::handle<Dwfl *, details::deleter<void, Dwfl *, &dwfl_end> >\n      dwfl_handle_t;\n  details::handle<Dwfl_Callbacks *, details::default_delete<Dwfl_Callbacks *> >\n      _dwfl_cb;\n  dwfl_handle_t _dwfl_handle;\n  bool _dwfl_handle_initialized;\n\n  // defined here because in C++98, template function cannot take locally\n  // defined types... grrr.\n  struct inliners_search_cb {\n    void operator()(Dwarf_Die *die) {\n      switch (dwarf_tag(die)) {\n        const char *name;\n      case DW_TAG_subprogram:\n        if ((name = dwarf_diename(die))) {\n          trace.source.function = name;\n        }\n        break;\n\n      case DW_TAG_inlined_subroutine:\n        ResolvedTrace::SourceLoc sloc;\n        Dwarf_Attribute attr_mem;\n\n        if ((name = dwarf_diename(die))) {\n          sloc.function = name;\n        }\n        if ((name = die_call_file(die))) {\n          sloc.filename = name;\n        }\n\n        Dwarf_Word line = 0, col = 0;\n        dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line);\n        dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col);\n        sloc.line = static_cast<unsigned>(line);\n        sloc.col = static_cast<unsigned>(col);\n\n        trace.inliners.push_back(sloc);\n        break;\n      };\n    }\n    ResolvedTrace &trace;\n    inliners_search_cb(ResolvedTrace &t) : trace(t) {}\n  };\n\n  static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) {\n    Dwarf_Addr low, high;\n\n    // continuous range\n    if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) {\n      if (dwarf_lowpc(die, &low) != 0) {\n        return false;\n      }\n      if (dwarf_highpc(die, &high) != 0) {\n        Dwarf_Attribute attr_mem;\n        Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem);\n        Dwarf_Word value;\n        if (dwarf_formudata(attr, &value) != 0) {\n          return false;\n        }\n        high = low + value;\n      }\n      return pc >= low && pc < high;\n    }\n\n    // non-continuous range.\n    Dwarf_Addr base;\n    ptrdiff_t offset = 0;\n    while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) {\n      if (pc >= low && pc < high) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc,\n                                      Dwarf_Die *result) {\n    if (dwarf_child(parent_die, result) != 0) {\n      return 0;\n    }\n\n    Dwarf_Die *die = result;\n    do {\n      switch (dwarf_tag(die)) {\n      case DW_TAG_subprogram:\n      case DW_TAG_inlined_subroutine:\n        if (die_has_pc(die, pc)) {\n          return result;\n        }\n      };\n      bool declaration = false;\n      Dwarf_Attribute attr_mem;\n      dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem),\n                     &declaration);\n      if (!declaration) {\n        // let's be curious and look deeper in the tree,\n        // function are not necessarily at the first level, but\n        // might be nested inside a namespace, structure etc.\n        Dwarf_Die die_mem;\n        Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem);\n        if (indie) {\n          *result = die_mem;\n          return result;\n        }\n      }\n    } while (dwarf_siblingof(die, result) == 0);\n    return 0;\n  }\n\n  template <typename CB>\n  static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc,\n                                      CB cb) {\n    Dwarf_Die die_mem;\n    if (dwarf_child(parent_die, &die_mem) != 0) {\n      return false;\n    }\n\n    bool branch_has_pc = false;\n    Dwarf_Die *die = &die_mem;\n    do {\n      bool declaration = false;\n      Dwarf_Attribute attr_mem;\n      dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem),\n                     &declaration);\n      if (!declaration) {\n        // let's be curious and look deeper in the tree, function are\n        // not necessarily at the first level, but might be nested\n        // inside a namespace, structure, a function, an inlined\n        // function etc.\n        branch_has_pc = deep_first_search_by_pc(die, pc, cb);\n      }\n      if (!branch_has_pc) {\n        branch_has_pc = die_has_pc(die, pc);\n      }\n      if (branch_has_pc) {\n        cb(die);\n      }\n    } while (dwarf_siblingof(die, &die_mem) == 0);\n    return branch_has_pc;\n  }\n\n  static const char *die_call_file(Dwarf_Die *die) {\n    Dwarf_Attribute attr_mem;\n    Dwarf_Word file_idx = 0;\n\n    dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx);\n\n    if (file_idx == 0) {\n      return 0;\n    }\n\n    Dwarf_Die die_mem;\n    Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0);\n    if (!cudie) {\n      return 0;\n    }\n\n    Dwarf_Files *files = 0;\n    size_t nfiles;\n    dwarf_getsrcfiles(cudie, &files, &nfiles);\n    if (!files) {\n      return 0;\n    }\n\n    return dwarf_filesrc(files, file_idx, 0, 0);\n  }\n};\n#endif // BACKWARD_HAS_DW == 1\n\n#if BACKWARD_HAS_DWARF == 1\n\ntemplate <>\nclass TraceResolverLinuxImpl<trace_resolver_tag::libdwarf>\n    : public TraceResolverLinuxBase {\npublic:\n  TraceResolverLinuxImpl() : _dwarf_loaded(false) {}\n\n  ResolvedTrace resolve(ResolvedTrace trace) override {\n    // trace.addr is a virtual address in memory pointing to some code.\n    // Let's try to find from which loaded object it comes from.\n    // The loaded object can be yourself btw.\n\n    Dl_info symbol_info;\n    int dladdr_result = 0;\n#if defined(__GLIBC__)\n    link_map *link_map;\n    // We request the link map so we can get information about offsets\n    dladdr_result =\n        dladdr1(trace.addr, &symbol_info, reinterpret_cast<void **>(&link_map),\n                RTLD_DL_LINKMAP);\n#else\n    // Android doesn't have dladdr1. Don't use the linker map.\n    dladdr_result = dladdr(trace.addr, &symbol_info);\n#endif\n    if (!dladdr_result) {\n      return trace; // dat broken trace...\n    }\n\n    // Now we get in symbol_info:\n    // .dli_fname:\n    //      pathname of the shared object that contains the address.\n    // .dli_fbase:\n    //      where the object is loaded in memory.\n    // .dli_sname:\n    //      the name of the nearest symbol to trace.addr, we expect a\n    //      function name.\n    // .dli_saddr:\n    //      the exact address corresponding to .dli_sname.\n    //\n    // And in link_map:\n    // .l_addr:\n    //      difference between the address in the ELF file and the address\n    //      in memory\n    // l_name:\n    //      absolute pathname where the object was found\n\n    if (symbol_info.dli_sname) {\n      trace.object_function = demangle(symbol_info.dli_sname);\n    }\n\n    if (!symbol_info.dli_fname) {\n      return trace;\n    }\n\n    trace.object_filename = resolve_exec_path(symbol_info);\n    dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname);\n    if (!fobj.dwarf_handle) {\n      return trace; // sad, we couldn't load the object :(\n    }\n\n#if defined(__GLIBC__)\n    // Convert the address to a module relative one by looking at\n    // the module's loading address in the link map\n    Dwarf_Addr address = reinterpret_cast<uintptr_t>(trace.addr) -\n                         reinterpret_cast<uintptr_t>(link_map->l_addr);\n#else\n    Dwarf_Addr address = reinterpret_cast<uintptr_t>(trace.addr);\n#endif\n\n    if (trace.object_function.empty()) {\n      symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address);\n\n      if (it != fobj.symbol_cache.end()) {\n        if (it->first != address) {\n          if (it != fobj.symbol_cache.begin()) {\n            --it;\n          }\n        }\n        trace.object_function = demangle(it->second.c_str());\n      }\n    }\n\n    // Get the Compilation Unit DIE for the address\n    Dwarf_Die die = find_die(fobj, address);\n\n    if (!die) {\n      return trace; // this time we lost the game :/\n    }\n\n    // libdwarf doesn't give us direct access to its objects, it always\n    // allocates a copy for the caller. We keep that copy alive in a cache\n    // and we deallocate it later when it's no longer required.\n    die_cache_entry &die_object = get_die_cache(fobj, die);\n    if (die_object.isEmpty())\n      return trace; // We have no line section for this DIE\n\n    die_linemap_t::iterator it = die_object.line_section.lower_bound(address);\n\n    if (it != die_object.line_section.end()) {\n      if (it->first != address) {\n        if (it == die_object.line_section.begin()) {\n          // If we are on the first item of the line section\n          // but the address does not match it means that\n          // the address is below the range of the DIE. Give up.\n          return trace;\n        } else {\n          --it;\n        }\n      }\n    } else {\n      return trace; // We didn't find the address.\n    }\n\n    // Get the Dwarf_Line that the address points to and call libdwarf\n    // to get source file, line and column info.\n    Dwarf_Line line = die_object.line_buffer[it->second];\n    Dwarf_Error error = DW_DLE_NE;\n\n    char *filename;\n    if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) {\n      trace.source.filename = std::string(filename);\n      dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING);\n    }\n\n    Dwarf_Unsigned number = 0;\n    if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) {\n      trace.source.line = number;\n    } else {\n      trace.source.line = 0;\n    }\n\n    if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) {\n      trace.source.col = number;\n    } else {\n      trace.source.col = 0;\n    }\n\n    std::vector<std::string> namespace_stack;\n    deep_first_search_by_pc(fobj, die, address, namespace_stack,\n                            inliners_search_cb(trace, fobj, die));\n\n    dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE);\n\n    return trace;\n  }\n\npublic:\n  static int close_dwarf(Dwarf_Debug dwarf) {\n    return dwarf_finish(dwarf, NULL);\n  }\n\nprivate:\n  bool _dwarf_loaded;\n\n  typedef details::handle<int, details::deleter<int, int, &::close> >\n      dwarf_file_t;\n\n  typedef details::handle<Elf *, details::deleter<int, Elf *, &elf_end> >\n      dwarf_elf_t;\n\n  typedef details::handle<Dwarf_Debug,\n                          details::deleter<int, Dwarf_Debug, &close_dwarf> >\n      dwarf_handle_t;\n\n  typedef std::map<Dwarf_Addr, int> die_linemap_t;\n\n  typedef std::map<Dwarf_Off, Dwarf_Off> die_specmap_t;\n\n  struct die_cache_entry {\n    die_specmap_t spec_section;\n    die_linemap_t line_section;\n    Dwarf_Line *line_buffer;\n    Dwarf_Signed line_count;\n    Dwarf_Line_Context line_context;\n\n    inline bool isEmpty() {\n      return line_buffer == NULL || line_count == 0 || line_context == NULL ||\n             line_section.empty();\n    }\n\n    die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {}\n\n    ~die_cache_entry() {\n      if (line_context) {\n        dwarf_srclines_dealloc_b(line_context);\n      }\n    }\n  };\n\n  typedef std::map<Dwarf_Off, die_cache_entry> die_cache_t;\n\n  typedef std::map<uintptr_t, std::string> symbol_cache_t;\n\n  struct dwarf_fileobject {\n    dwarf_file_t file_handle;\n    dwarf_elf_t elf_handle;\n    dwarf_handle_t dwarf_handle;\n    symbol_cache_t symbol_cache;\n\n    // Die cache\n    die_cache_t die_cache;\n    die_cache_entry *current_cu;\n  };\n\n  typedef details::hashtable<std::string, dwarf_fileobject>::type\n      fobj_dwarf_map_t;\n  fobj_dwarf_map_t _fobj_dwarf_map;\n\n  static bool cstrings_eq(const char *a, const char *b) {\n    if (!a || !b) {\n      return false;\n    }\n    return strcmp(a, b) == 0;\n  }\n\n  dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) {\n\n    if (!_dwarf_loaded) {\n      // Set the ELF library operating version\n      // If that fails there's nothing we can do\n      _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE;\n    }\n\n    fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object);\n    if (it != _fobj_dwarf_map.end()) {\n      return it->second;\n    }\n\n    // this new object is empty for now\n    dwarf_fileobject &r = _fobj_dwarf_map[filename_object];\n\n    dwarf_file_t file_handle;\n    file_handle.reset(open(filename_object.c_str(), O_RDONLY));\n    if (file_handle.get() < 0) {\n      return r;\n    }\n\n    // Try to get an ELF handle. We need to read the ELF sections\n    // because we want to see if there is a .gnu_debuglink section\n    // that points to a split debug file\n    dwarf_elf_t elf_handle;\n    elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL));\n    if (!elf_handle) {\n      return r;\n    }\n\n    const char *e_ident = elf_getident(elf_handle.get(), 0);\n    if (!e_ident) {\n      return r;\n    }\n\n    // Get the number of sections\n    // We use the new APIs as elf_getshnum is deprecated\n    size_t shdrnum = 0;\n    if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) {\n      return r;\n    }\n\n    // Get the index to the string section\n    size_t shdrstrndx = 0;\n    if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) {\n      return r;\n    }\n\n    std::string debuglink;\n    // Iterate through the ELF sections to try to get a gnu_debuglink\n    // note and also to cache the symbol table.\n    // We go the preprocessor way to avoid having to create templated\n    // classes or using gelf (which might throw a compiler error if 64 bit\n    // is not supported\n#define ELF_GET_DATA(ARCH)                                                     \\\n  Elf_Scn *elf_section = 0;                                                    \\\n  Elf_Data *elf_data = 0;                                                      \\\n  Elf##ARCH##_Shdr *section_header = 0;                                        \\\n  Elf_Scn *symbol_section = 0;                                                 \\\n  size_t symbol_count = 0;                                                     \\\n  size_t symbol_strings = 0;                                                   \\\n  Elf##ARCH##_Sym *symbol = 0;                                                 \\\n  const char *section_name = 0;                                                \\\n                                                                               \\\n  while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \\\n    section_header = elf##ARCH##_getshdr(elf_section);                         \\\n    if (section_header == NULL) {                                              \\\n      return r;                                                                \\\n    }                                                                          \\\n                                                                               \\\n    if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx,               \\\n                                   section_header->sh_name)) == NULL) {        \\\n      return r;                                                                \\\n    }                                                                          \\\n                                                                               \\\n    if (cstrings_eq(section_name, \".gnu_debuglink\")) {                         \\\n      elf_data = elf_getdata(elf_section, NULL);                               \\\n      if (elf_data && elf_data->d_size > 0) {                                  \\\n        debuglink =                                                            \\\n            std::string(reinterpret_cast<const char *>(elf_data->d_buf));      \\\n      }                                                                        \\\n    }                                                                          \\\n                                                                               \\\n    switch (section_header->sh_type) {                                         \\\n    case SHT_SYMTAB:                                                           \\\n      symbol_section = elf_section;                                            \\\n      symbol_count = section_header->sh_size / section_header->sh_entsize;     \\\n      symbol_strings = section_header->sh_link;                                \\\n      break;                                                                   \\\n                                                                               \\\n    /* We use .dynsyms as a last resort, we prefer .symtab */                  \\\n    case SHT_DYNSYM:                                                           \\\n      if (!symbol_section) {                                                   \\\n        symbol_section = elf_section;                                          \\\n        symbol_count = section_header->sh_size / section_header->sh_entsize;   \\\n        symbol_strings = section_header->sh_link;                              \\\n      }                                                                        \\\n      break;                                                                   \\\n    }                                                                          \\\n  }                                                                            \\\n                                                                               \\\n  if (symbol_section && symbol_count && symbol_strings) {                      \\\n    elf_data = elf_getdata(symbol_section, NULL);                              \\\n    symbol = reinterpret_cast<Elf##ARCH##_Sym *>(elf_data->d_buf);             \\\n    for (size_t i = 0; i < symbol_count; ++i) {                                \\\n      int type = ELF##ARCH##_ST_TYPE(symbol->st_info);                         \\\n      if (type == STT_FUNC && symbol->st_value > 0) {                          \\\n        r.symbol_cache[symbol->st_value] = std::string(                        \\\n            elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name));    \\\n      }                                                                        \\\n      ++symbol;                                                                \\\n    }                                                                          \\\n  }\n\n    if (e_ident[EI_CLASS] == ELFCLASS32) {\n      ELF_GET_DATA(32)\n    } else if (e_ident[EI_CLASS] == ELFCLASS64) {\n      // libelf might have been built without 64 bit support\n#if __LIBELF64\n      ELF_GET_DATA(64)\n#endif\n    }\n\n    if (!debuglink.empty()) {\n      // We have a debuglink section! Open an elf instance on that\n      // file instead. If we can't open the file, then return\n      // the elf handle we had already opened.\n      dwarf_file_t debuglink_file;\n      debuglink_file.reset(open(debuglink.c_str(), O_RDONLY));\n      if (debuglink_file.get() > 0) {\n        dwarf_elf_t debuglink_elf;\n        debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL));\n\n        // If we have a valid elf handle, return the new elf handle\n        // and file handle and discard the original ones\n        if (debuglink_elf) {\n          elf_handle = move(debuglink_elf);\n          file_handle = move(debuglink_file);\n        }\n      }\n    }\n\n    // Ok, we have a valid ELF handle, let's try to get debug symbols\n    Dwarf_Debug dwarf_debug;\n    Dwarf_Error error = DW_DLE_NE;\n    dwarf_handle_t dwarf_handle;\n\n    int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL,\n                                      &dwarf_debug, &error);\n\n    // We don't do any special handling for DW_DLV_NO_ENTRY specially.\n    // If we get an error, or the file doesn't have debug information\n    // we just return.\n    if (dwarf_result != DW_DLV_OK) {\n      return r;\n    }\n\n    dwarf_handle.reset(dwarf_debug);\n\n    r.file_handle = move(file_handle);\n    r.elf_handle = move(elf_handle);\n    r.dwarf_handle = move(dwarf_handle);\n\n    return r;\n  }\n\n  die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) {\n    Dwarf_Error error = DW_DLE_NE;\n\n    // Get the die offset, we use it as the cache key\n    Dwarf_Off die_offset;\n    if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) {\n      die_offset = 0;\n    }\n\n    die_cache_t::iterator it = fobj.die_cache.find(die_offset);\n\n    if (it != fobj.die_cache.end()) {\n      fobj.current_cu = &it->second;\n      return it->second;\n    }\n\n    die_cache_entry &de = fobj.die_cache[die_offset];\n    fobj.current_cu = &de;\n\n    Dwarf_Addr line_addr;\n    Dwarf_Small table_count;\n\n    // The addresses in the line section are not fully sorted (they might\n    // be sorted by block of code belonging to the same file), which makes\n    // it necessary to do so before searching is possible.\n    //\n    // As libdwarf allocates a copy of everything, let's get the contents\n    // of the line section and keep it around. We also create a map of\n    // program counter to line table indices so we can search by address\n    // and get the line buffer index.\n    //\n    // To make things more difficult, the same address can span more than\n    // one line, so we need to keep the index pointing to the first line\n    // by using insert instead of the map's [ operator.\n\n    // Get the line context for the DIE\n    if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) ==\n        DW_DLV_OK) {\n      // Get the source lines for this line context, to be deallocated\n      // later\n      if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer,\n                                          &de.line_count,\n                                          &error) == DW_DLV_OK) {\n\n        // Add all the addresses to our map\n        for (int i = 0; i < de.line_count; i++) {\n          if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) !=\n              DW_DLV_OK) {\n            line_addr = 0;\n          }\n          de.line_section.insert(std::pair<Dwarf_Addr, int>(line_addr, i));\n        }\n      }\n    }\n\n    // For each CU, cache the function DIEs that contain the\n    // DW_AT_specification attribute. When building with -g3 the function\n    // DIEs are separated in declaration and specification, with the\n    // declaration containing only the name and parameters and the\n    // specification the low/high pc and other compiler attributes.\n    //\n    // We cache those specifications so we don't skip over the declarations,\n    // because they have no pc, and we can do namespace resolution for\n    // DWARF function names.\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    Dwarf_Die current_die = 0;\n    if (dwarf_child(die, &current_die, &error) == DW_DLV_OK) {\n      for (;;) {\n        Dwarf_Die sibling_die = 0;\n\n        Dwarf_Half tag_value;\n        dwarf_tag(current_die, &tag_value, &error);\n\n        if (tag_value == DW_TAG_subprogram ||\n            tag_value == DW_TAG_inlined_subroutine) {\n\n          Dwarf_Bool has_attr = 0;\n          if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr,\n                            &error) == DW_DLV_OK) {\n            if (has_attr) {\n              Dwarf_Attribute attr_mem;\n              if (dwarf_attr(current_die, DW_AT_specification, &attr_mem,\n                             &error) == DW_DLV_OK) {\n                Dwarf_Off spec_offset = 0;\n                if (dwarf_formref(attr_mem, &spec_offset, &error) ==\n                    DW_DLV_OK) {\n                  Dwarf_Off spec_die_offset;\n                  if (dwarf_dieoffset(current_die, &spec_die_offset, &error) ==\n                      DW_DLV_OK) {\n                    de.spec_section[spec_offset] = spec_die_offset;\n                  }\n                }\n              }\n              dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n            }\n          }\n        }\n\n        int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error);\n        if (result == DW_DLV_ERROR) {\n          break;\n        } else if (result == DW_DLV_NO_ENTRY) {\n          break;\n        }\n\n        if (current_die != die) {\n          dwarf_dealloc(dwarf, current_die, DW_DLA_DIE);\n          current_die = 0;\n        }\n\n        current_die = sibling_die;\n      }\n    }\n    return de;\n  }\n\n  static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die,\n                                      Dwarf_Half attr, bool global) {\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Attribute attr_mem;\n\n    Dwarf_Die found_die = NULL;\n    if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) {\n      Dwarf_Off offset;\n      int result = 0;\n      if (global) {\n        result = dwarf_global_formref(attr_mem, &offset, &error);\n      } else {\n        result = dwarf_formref(attr_mem, &offset, &error);\n      }\n\n      if (result == DW_DLV_OK) {\n        if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) {\n          found_die = NULL;\n        }\n      }\n      dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n    }\n    return found_die;\n  }\n\n  static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die,\n                                             Dwarf_Half attr, bool global) {\n    Dwarf_Error error = DW_DLE_NE;\n    std::string value;\n\n    Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global);\n\n    if (found_die) {\n      char *name;\n      if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) {\n        if (name) {\n          value = std::string(name);\n        }\n        dwarf_dealloc(dwarf, name, DW_DLA_STRING);\n      }\n      dwarf_dealloc(dwarf, found_die, DW_DLA_DIE);\n    }\n\n    return value;\n  }\n\n  // Returns a spec DIE linked to the passed one. The caller should\n  // deallocate the DIE\n  static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) {\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Off die_offset;\n    if (fobj.current_cu &&\n        dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) {\n      die_specmap_t::iterator it =\n          fobj.current_cu->spec_section.find(die_offset);\n\n      // If we have a DIE that completes the current one, check if\n      // that one has the pc we are looking for\n      if (it != fobj.current_cu->spec_section.end()) {\n        Dwarf_Die spec_die = 0;\n        if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) {\n          return spec_die;\n        }\n      }\n    }\n\n    // Maybe we have an abstract origin DIE with the function information?\n    return get_referenced_die(fobj.dwarf_handle.get(), die,\n                              DW_AT_abstract_origin, true);\n  }\n\n  static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) {\n    Dwarf_Addr low_pc = 0, high_pc = 0;\n    Dwarf_Half high_pc_form = 0;\n    Dwarf_Form_Class return_class;\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    bool has_lowpc = false;\n    bool has_highpc = false;\n    bool has_ranges = false;\n\n    if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) {\n      // If we have a low_pc check if there is a high pc.\n      // If we don't have a high pc this might mean we have a base\n      // address for the ranges list or just an address.\n      has_lowpc = true;\n\n      if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) ==\n          DW_DLV_OK) {\n        // We do have a high pc. In DWARF 4+ this is an offset from the\n        // low pc, but in earlier versions it's an absolute address.\n\n        has_highpc = true;\n        // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS\n        if (return_class == DW_FORM_CLASS_CONSTANT) {\n          high_pc = low_pc + high_pc;\n        }\n\n        // We have low and high pc, check if our address\n        // is in that range\n        return pc >= low_pc && pc < high_pc;\n      }\n    } else {\n      // Reset the low_pc, in case dwarf_lowpc failing set it to some\n      // undefined value.\n      low_pc = 0;\n    }\n\n    // Check if DW_AT_ranges is present and search for the PC in the\n    // returned ranges list. We always add the low_pc, as it not set it will\n    // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair\n    bool result = false;\n\n    Dwarf_Attribute attr;\n    if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) {\n\n      Dwarf_Off offset;\n      if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) {\n        Dwarf_Ranges *ranges;\n        Dwarf_Signed ranges_count = 0;\n        Dwarf_Unsigned byte_count = 0;\n\n        if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count,\n                               &byte_count, &error) == DW_DLV_OK) {\n          has_ranges = ranges_count != 0;\n          for (int i = 0; i < ranges_count; i++) {\n            if (ranges[i].dwr_addr1 != 0 &&\n                pc >= ranges[i].dwr_addr1 + low_pc &&\n                pc < ranges[i].dwr_addr2 + low_pc) {\n              result = true;\n              break;\n            }\n          }\n          dwarf_ranges_dealloc(dwarf, ranges, ranges_count);\n        }\n      }\n    }\n\n    // Last attempt. We might have a single address set as low_pc.\n    if (!result && low_pc != 0 && pc == low_pc) {\n      result = true;\n    }\n\n    // If we don't have lowpc, highpc and ranges maybe this DIE is a\n    // declaration that relies on a DW_AT_specification DIE that happens\n    // later. Use the specification cache we filled when we loaded this CU.\n    if (!result && (!has_lowpc && !has_highpc && !has_ranges)) {\n      Dwarf_Die spec_die = get_spec_die(fobj, die);\n      if (spec_die) {\n        result = die_has_pc(fobj, spec_die, pc);\n        dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE);\n      }\n    }\n\n    return result;\n  }\n\n  static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) {\n    Dwarf_Error error = DW_DLE_NE;\n\n    Dwarf_Die child = 0;\n    if (dwarf_child(die, &child, &error) == DW_DLV_OK) {\n      get_type(dwarf, child, type);\n    }\n\n    if (child) {\n      type.insert(0, \"::\");\n      dwarf_dealloc(dwarf, child, DW_DLA_DIE);\n    }\n\n    char *name;\n    if (dwarf_diename(die, &name, &error) == DW_DLV_OK) {\n      type.insert(0, std::string(name));\n      dwarf_dealloc(dwarf, name, DW_DLA_STRING);\n    } else {\n      type.insert(0, \"<unknown>\");\n    }\n  }\n\n  static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) {\n    Dwarf_Error error = DW_DLE_NE;\n\n    Dwarf_Sig8 signature;\n    Dwarf_Bool has_attr = 0;\n    if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) {\n      if (has_attr) {\n        Dwarf_Attribute attr_mem;\n        if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) {\n          if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) {\n            return std::string(\"<no type signature>\");\n          }\n        }\n        dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n      }\n    }\n\n    Dwarf_Unsigned next_cu_header;\n    Dwarf_Sig8 tu_signature;\n    std::string result;\n    bool found = false;\n\n    while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0,\n                                  &next_cu_header, 0, &error) == DW_DLV_OK) {\n\n      if (strncmp(signature.signature, tu_signature.signature, 8) == 0) {\n        Dwarf_Die type_cu_die = 0;\n        if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) {\n          Dwarf_Die child_die = 0;\n          if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) {\n            get_type(dwarf, child_die, result);\n            found = !result.empty();\n            dwarf_dealloc(dwarf, child_die, DW_DLA_DIE);\n          }\n          dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE);\n        }\n      }\n    }\n\n    if (found) {\n      while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                                    &next_cu_header, 0, &error) == DW_DLV_OK) {\n        // Reset the cu header state. Unfortunately, libdwarf's\n        // next_cu_header API keeps its own iterator per Dwarf_Debug\n        // that can't be reset. We need to keep fetching elements until\n        // the end.\n      }\n    } else {\n      // If we couldn't resolve the type just print out the signature\n      std::ostringstream string_stream;\n      string_stream << \"<0x\" << std::hex << std::setfill('0');\n      for (int i = 0; i < 8; ++i) {\n        string_stream << std::setw(2) << std::hex\n                      << (int)(unsigned char)(signature.signature[i]);\n      }\n      string_stream << \">\";\n      result = string_stream.str();\n    }\n    return result;\n  }\n\n  struct type_context_t {\n    bool is_const;\n    bool is_typedef;\n    bool has_type;\n    bool has_name;\n    std::string text;\n\n    type_context_t()\n        : is_const(false), is_typedef(false), has_type(false), has_name(false) {\n    }\n  };\n\n  // Types are resolved from right to left: we get the variable name first\n  // and then all specifiers (like const or pointer) in a chain of DW_AT_type\n  // DIEs. Call this function recursively until we get a complete type\n  // string.\n  static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die,\n                                   type_context_t &context) {\n    char *name;\n    Dwarf_Error error = DW_DLE_NE;\n\n    // typedefs contain also the base type, so we skip it and only\n    // print the typedef name\n    if (!context.is_typedef) {\n      if (dwarf_diename(die, &name, &error) == DW_DLV_OK) {\n        if (!context.text.empty()) {\n          context.text.insert(0, \" \");\n        }\n        context.text.insert(0, std::string(name));\n        dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING);\n      }\n    } else {\n      context.is_typedef = false;\n      context.has_type = true;\n      if (context.is_const) {\n        context.text.insert(0, \"const \");\n        context.is_const = false;\n      }\n    }\n\n    bool next_type_is_const = false;\n    bool is_keyword = true;\n\n    Dwarf_Half tag = 0;\n    Dwarf_Bool has_attr = 0;\n    if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) {\n      switch (tag) {\n      case DW_TAG_structure_type:\n      case DW_TAG_union_type:\n      case DW_TAG_class_type:\n      case DW_TAG_enumeration_type:\n        context.has_type = true;\n        if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) ==\n            DW_DLV_OK) {\n          // If we have a signature it means the type is defined\n          // in .debug_types, so we need to load the DIE pointed\n          // at by the signature and resolve it\n          if (has_attr) {\n            std::string type =\n                get_type_by_signature(fobj.dwarf_handle.get(), die);\n            if (context.is_const)\n              type.insert(0, \"const \");\n\n            if (!context.text.empty())\n              context.text.insert(0, \" \");\n            context.text.insert(0, type);\n          }\n\n          // Treat enums like typedefs, and skip printing its\n          // base type\n          context.is_typedef = (tag == DW_TAG_enumeration_type);\n        }\n        break;\n      case DW_TAG_const_type:\n        next_type_is_const = true;\n        break;\n      case DW_TAG_pointer_type:\n        context.text.insert(0, \"*\");\n        break;\n      case DW_TAG_reference_type:\n        context.text.insert(0, \"&\");\n        break;\n      case DW_TAG_restrict_type:\n        context.text.insert(0, \"restrict \");\n        break;\n      case DW_TAG_rvalue_reference_type:\n        context.text.insert(0, \"&&\");\n        break;\n      case DW_TAG_volatile_type:\n        context.text.insert(0, \"volatile \");\n        break;\n      case DW_TAG_typedef:\n        // Propagate the const-ness to the next type\n        // as typedefs are linked to its base type\n        next_type_is_const = context.is_const;\n        context.is_typedef = true;\n        context.has_type = true;\n        break;\n      case DW_TAG_base_type:\n        context.has_type = true;\n        break;\n      case DW_TAG_formal_parameter:\n        context.has_name = true;\n        break;\n      default:\n        is_keyword = false;\n        break;\n      }\n    }\n\n    if (!is_keyword && context.is_const) {\n      context.text.insert(0, \"const \");\n    }\n\n    context.is_const = next_type_is_const;\n\n    Dwarf_Die ref =\n        get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true);\n    if (ref) {\n      set_parameter_string(fobj, ref, context);\n      dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE);\n    }\n\n    if (!context.has_type && context.has_name) {\n      context.text.insert(0, \"void \");\n      context.has_type = true;\n    }\n  }\n\n  // Resolve the function return type and parameters\n  static void set_function_parameters(std::string &function_name,\n                                      std::vector<std::string> &ns,\n                                      dwarf_fileobject &fobj, Dwarf_Die die) {\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Die current_die = 0;\n    std::string parameters;\n    bool has_spec = true;\n    // Check if we have a spec DIE. If we do we use it as it contains\n    // more information, like parameter names.\n    Dwarf_Die spec_die = get_spec_die(fobj, die);\n    if (!spec_die) {\n      has_spec = false;\n      spec_die = die;\n    }\n\n    std::vector<std::string>::const_iterator it = ns.begin();\n    std::string ns_name;\n    for (it = ns.begin(); it < ns.end(); ++it) {\n      ns_name.append(*it).append(\"::\");\n    }\n\n    if (!ns_name.empty()) {\n      function_name.insert(0, ns_name);\n    }\n\n    // See if we have a function return type. It can be either on the\n    // current die or in its spec one (usually true for inlined functions)\n    std::string return_type =\n        get_referenced_die_name(dwarf, die, DW_AT_type, true);\n    if (return_type.empty()) {\n      return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true);\n    }\n    if (!return_type.empty()) {\n      return_type.append(\" \");\n      function_name.insert(0, return_type);\n    }\n\n    if (dwarf_child(spec_die, &current_die, &error) == DW_DLV_OK) {\n      for (;;) {\n        Dwarf_Die sibling_die = 0;\n\n        Dwarf_Half tag_value;\n        dwarf_tag(current_die, &tag_value, &error);\n\n        if (tag_value == DW_TAG_formal_parameter) {\n          // Ignore artificial (ie, compiler generated) parameters\n          bool is_artificial = false;\n          Dwarf_Attribute attr_mem;\n          if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) ==\n              DW_DLV_OK) {\n            Dwarf_Bool flag = 0;\n            if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) {\n              is_artificial = flag != 0;\n            }\n            dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n          }\n\n          if (!is_artificial) {\n            type_context_t context;\n            set_parameter_string(fobj, current_die, context);\n\n            if (parameters.empty()) {\n              parameters.append(\"(\");\n            } else {\n              parameters.append(\", \");\n            }\n            parameters.append(context.text);\n          }\n        }\n\n        int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error);\n        if (result == DW_DLV_ERROR) {\n          break;\n        } else if (result == DW_DLV_NO_ENTRY) {\n          break;\n        }\n\n        if (current_die != die) {\n          dwarf_dealloc(dwarf, current_die, DW_DLA_DIE);\n          current_die = 0;\n        }\n\n        current_die = sibling_die;\n      }\n    }\n    if (parameters.empty())\n      parameters = \"(\";\n    parameters.append(\")\");\n\n    // If we got a spec DIE we need to deallocate it\n    if (has_spec)\n      dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE);\n\n    function_name.append(parameters);\n  }\n\n  // defined here because in C++98, template function cannot take locally\n  // defined types... grrr.\n  struct inliners_search_cb {\n    void operator()(Dwarf_Die die, std::vector<std::string> &ns) {\n      Dwarf_Error error = DW_DLE_NE;\n      Dwarf_Half tag_value;\n      Dwarf_Attribute attr_mem;\n      Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n\n      dwarf_tag(die, &tag_value, &error);\n\n      switch (tag_value) {\n        char *name;\n      case DW_TAG_subprogram:\n        if (!trace.source.function.empty())\n          break;\n        if (dwarf_diename(die, &name, &error) == DW_DLV_OK) {\n          trace.source.function = std::string(name);\n          dwarf_dealloc(dwarf, name, DW_DLA_STRING);\n        } else {\n          // We don't have a function name in this DIE.\n          // Check if there is a referenced non-defining\n          // declaration.\n          trace.source.function =\n              get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true);\n          if (trace.source.function.empty()) {\n            trace.source.function =\n                get_referenced_die_name(dwarf, die, DW_AT_specification, true);\n          }\n        }\n\n        // Append the function parameters, if available\n        set_function_parameters(trace.source.function, ns, fobj, die);\n\n        // If the object function name is empty, it's possible that\n        // there is no dynamic symbol table (maybe the executable\n        // was stripped or not built with -rdynamic). See if we have\n        // a DWARF linkage name to use instead. We try both\n        // linkage_name and MIPS_linkage_name because the MIPS tag\n        // was the unofficial one until it was adopted in DWARF4.\n        // Old gcc versions generate MIPS_linkage_name\n        if (trace.object_function.empty()) {\n          details::demangler demangler;\n\n          if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) !=\n              DW_DLV_OK) {\n            if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) !=\n                DW_DLV_OK) {\n              break;\n            }\n          }\n\n          char *linkage;\n          if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) {\n            trace.object_function = demangler.demangle(linkage);\n            dwarf_dealloc(dwarf, linkage, DW_DLA_STRING);\n          }\n          dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n        }\n        break;\n\n      case DW_TAG_inlined_subroutine:\n        ResolvedTrace::SourceLoc sloc;\n\n        if (dwarf_diename(die, &name, &error) == DW_DLV_OK) {\n          sloc.function = std::string(name);\n          dwarf_dealloc(dwarf, name, DW_DLA_STRING);\n        } else {\n          // We don't have a name for this inlined DIE, it could\n          // be that there is an abstract origin instead.\n          // Get the DW_AT_abstract_origin value, which is a\n          // reference to the source DIE and try to get its name\n          sloc.function =\n              get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true);\n        }\n\n        set_function_parameters(sloc.function, ns, fobj, die);\n\n        std::string file = die_call_file(dwarf, die, cu_die);\n        if (!file.empty())\n          sloc.filename = file;\n\n        Dwarf_Unsigned number = 0;\n        if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) {\n          if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) {\n            sloc.line = number;\n          }\n          dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n        }\n\n        if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) ==\n            DW_DLV_OK) {\n          if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) {\n            sloc.col = number;\n          }\n          dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n        }\n\n        trace.inliners.push_back(sloc);\n        break;\n      };\n    }\n    ResolvedTrace &trace;\n    dwarf_fileobject &fobj;\n    Dwarf_Die cu_die;\n    inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c)\n        : trace(t), fobj(f), cu_die(c) {}\n  };\n\n  static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj,\n                                     Dwarf_Die parent_die, Dwarf_Addr pc,\n                                     Dwarf_Die result) {\n    Dwarf_Die current_die = 0;\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n\n    if (dwarf_child(parent_die, &current_die, &error) != DW_DLV_OK) {\n      return NULL;\n    }\n\n    for (;;) {\n      Dwarf_Die sibling_die = 0;\n      Dwarf_Half tag_value;\n      dwarf_tag(current_die, &tag_value, &error);\n\n      switch (tag_value) {\n      case DW_TAG_subprogram:\n      case DW_TAG_inlined_subroutine:\n        if (die_has_pc(fobj, current_die, pc)) {\n          return current_die;\n        }\n      };\n      bool declaration = false;\n      Dwarf_Attribute attr_mem;\n      if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) ==\n          DW_DLV_OK) {\n        Dwarf_Bool flag = 0;\n        if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) {\n          declaration = flag != 0;\n        }\n        dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n      }\n\n      if (!declaration) {\n        // let's be curious and look deeper in the tree, functions are\n        // not necessarily at the first level, but might be nested\n        // inside a namespace, structure, a function, an inlined\n        // function etc.\n        Dwarf_Die die_mem = 0;\n        Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem);\n        if (indie) {\n          result = die_mem;\n          return result;\n        }\n      }\n\n      int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error);\n      if (res == DW_DLV_ERROR) {\n        return NULL;\n      } else if (res == DW_DLV_NO_ENTRY) {\n        break;\n      }\n\n      if (current_die != parent_die) {\n        dwarf_dealloc(dwarf, current_die, DW_DLA_DIE);\n        current_die = 0;\n      }\n\n      current_die = sibling_die;\n    }\n    return NULL;\n  }\n\n  template <typename CB>\n  static bool deep_first_search_by_pc(dwarf_fileobject &fobj,\n                                      Dwarf_Die parent_die, Dwarf_Addr pc,\n                                      std::vector<std::string> &ns, CB cb) {\n    Dwarf_Die current_die = 0;\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    Dwarf_Error error = DW_DLE_NE;\n\n    if (dwarf_child(parent_die, &current_die, &error) != DW_DLV_OK) {\n      return false;\n    }\n\n    bool branch_has_pc = false;\n    bool has_namespace = false;\n    for (;;) {\n      Dwarf_Die sibling_die = 0;\n\n      Dwarf_Half tag;\n      if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) {\n        if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) {\n          char *ns_name = NULL;\n          if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) {\n            if (ns_name) {\n              ns.push_back(std::string(ns_name));\n            } else {\n              ns.push_back(\"<unknown>\");\n            }\n            dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING);\n          } else {\n            ns.push_back(\"<unknown>\");\n          }\n          has_namespace = true;\n        }\n      }\n\n      bool declaration = false;\n      Dwarf_Attribute attr_mem;\n      if (tag != DW_TAG_class_type &&\n          dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) ==\n              DW_DLV_OK) {\n        Dwarf_Bool flag = 0;\n        if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) {\n          declaration = flag != 0;\n        }\n        dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n      }\n\n      if (!declaration) {\n        // let's be curious and look deeper in the tree, function are\n        // not necessarily at the first level, but might be nested\n        // inside a namespace, structure, a function, an inlined\n        // function etc.\n        branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb);\n      }\n\n      if (!branch_has_pc) {\n        branch_has_pc = die_has_pc(fobj, current_die, pc);\n      }\n\n      if (branch_has_pc) {\n        cb(current_die, ns);\n      }\n\n      int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error);\n      if (result == DW_DLV_ERROR) {\n        return false;\n      } else if (result == DW_DLV_NO_ENTRY) {\n        break;\n      }\n\n      if (current_die != parent_die) {\n        dwarf_dealloc(dwarf, current_die, DW_DLA_DIE);\n        current_die = 0;\n      }\n\n      if (has_namespace) {\n        has_namespace = false;\n        ns.pop_back();\n      }\n      current_die = sibling_die;\n    }\n\n    if (has_namespace) {\n      ns.pop_back();\n    }\n    return branch_has_pc;\n  }\n\n  static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die,\n                                   Dwarf_Die cu_die) {\n    Dwarf_Attribute attr_mem;\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Unsigned file_index;\n\n    std::string file;\n\n    if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) {\n      if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) {\n        file_index = 0;\n      }\n      dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);\n\n      if (file_index == 0) {\n        return file;\n      }\n\n      char **srcfiles = 0;\n      Dwarf_Signed file_count = 0;\n      if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) {\n        if (file_count > 0 && file_index <= static_cast<Dwarf_Unsigned>(file_count)) {\n          file = std::string(srcfiles[file_index - 1]);\n\t}\n\n        // Deallocate all strings!\n        for (int i = 0; i < file_count; ++i) {\n          dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING);\n        }\n        dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST);\n      }\n    }\n    return file;\n  }\n\n  Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) {\n    // Let's get to work! First see if we have a debug_aranges section so\n    // we can speed up the search\n\n    Dwarf_Debug dwarf = fobj.dwarf_handle.get();\n    Dwarf_Error error = DW_DLE_NE;\n    Dwarf_Arange *aranges;\n    Dwarf_Signed arange_count;\n\n    Dwarf_Die returnDie;\n    bool found = false;\n    if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) !=\n        DW_DLV_OK) {\n      aranges = NULL;\n    }\n\n    if (aranges) {\n      // We have aranges. Get the one where our address is.\n      Dwarf_Arange arange;\n      if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) ==\n          DW_DLV_OK) {\n\n        // We found our address. Get the compilation-unit DIE offset\n        // represented by the given address range.\n        Dwarf_Off cu_die_offset;\n        if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) ==\n            DW_DLV_OK) {\n          // Get the DIE at the offset returned by the aranges search.\n          // We set is_info to 1 to specify that the offset is from\n          // the .debug_info section (and not .debug_types)\n          int dwarf_result =\n              dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error);\n\n          found = dwarf_result == DW_DLV_OK;\n        }\n        dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE);\n      }\n    }\n\n    if (found)\n      return returnDie; // The caller is responsible for freeing the die\n\n    // The search for aranges failed. Try to find our address by scanning\n    // all compilation units.\n    Dwarf_Unsigned next_cu_header;\n    Dwarf_Half tag = 0;\n    returnDie = 0;\n\n    while (!found &&\n           dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n                                  &next_cu_header, 0, &error) == DW_DLV_OK) {\n\n      if (returnDie)\n        dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE);\n\n      if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) {\n        if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) &&\n            tag == DW_TAG_compile_unit) {\n          if (die_has_pc(fobj, returnDie, addr)) {\n            found = true;\n          }\n        }\n      }\n    }\n\n    if (found) {\n      while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n                                    &next_cu_header, 0, &error) == DW_DLV_OK) {\n        // Reset the cu header state. Libdwarf's next_cu_header API\n        // keeps its own iterator per Dwarf_Debug that can't be reset.\n        // We need to keep fetching elements until the end.\n      }\n    }\n\n    if (found)\n      return returnDie;\n\n    // We couldn't find any compilation units with ranges or a high/low pc.\n    // Try again by looking at all DIEs in all compilation units.\n    Dwarf_Die cudie;\n    while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n                                  &next_cu_header, 0, &error) == DW_DLV_OK) {\n      if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) {\n        Dwarf_Die die_mem = 0;\n        Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem);\n\n        if (resultDie) {\n          found = true;\n          break;\n        }\n      }\n    }\n\n    if (found) {\n      while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n                                    &next_cu_header, 0, &error) == DW_DLV_OK) {\n        // Reset the cu header state. Libdwarf's next_cu_header API\n        // keeps its own iterator per Dwarf_Debug that can't be reset.\n        // We need to keep fetching elements until the end.\n      }\n    }\n\n    if (found)\n      return cudie;\n\n    // We failed.\n    return NULL;\n  }\n};\n#endif // BACKWARD_HAS_DWARF == 1\n\ntemplate <>\nclass TraceResolverImpl<system_tag::linux_tag>\n    : public TraceResolverLinuxImpl<trace_resolver_tag::current> {};\n\n#endif // BACKWARD_SYSTEM_LINUX\n\n#ifdef BACKWARD_SYSTEM_DARWIN\n\ntemplate <typename STACKTRACE_TAG> class TraceResolverDarwinImpl;\n\ntemplate <>\nclass TraceResolverDarwinImpl<trace_resolver_tag::backtrace_symbol>\n    : public TraceResolverImplBase {\npublic:\n  void load_addresses(void *const*addresses, int address_count) override {\n    if (address_count == 0) {\n      return;\n    }\n    _symbols.reset(backtrace_symbols(addresses, address_count));\n  }\n\n  ResolvedTrace resolve(ResolvedTrace trace) override {\n    // parse:\n    // <n>  <file>  <addr>  <mangled-name> + <offset>\n    char *filename = _symbols[trace.idx];\n\n    // skip \"<n>  \"\n    while (*filename && *filename != ' ')\n      filename++;\n    while (*filename == ' ')\n      filename++;\n\n    // find start of <mangled-name> from end (<file> may contain a space)\n    char *p = filename + strlen(filename) - 1;\n    // skip to start of \" + <offset>\"\n    while (p > filename && *p != ' ')\n      p--;\n    while (p > filename && *p == ' ')\n      p--;\n    while (p > filename && *p != ' ')\n      p--;\n    while (p > filename && *p == ' ')\n      p--;\n    char *funcname_end = p + 1;\n\n    // skip to start of \"<manged-name>\"\n    while (p > filename && *p != ' ')\n      p--;\n    char *funcname = p + 1;\n\n    // skip to start of \"  <addr>  \"\n    while (p > filename && *p == ' ')\n      p--;\n    while (p > filename && *p != ' ')\n      p--;\n    while (p > filename && *p == ' ')\n      p--;\n\n    // skip \"<file>\", handling the case where it contains a\n    char *filename_end = p + 1;\n    if (p == filename) {\n      // something went wrong, give up\n      filename_end = filename + strlen(filename);\n      funcname = filename_end;\n    }\n    trace.object_filename.assign(\n        filename, filename_end); // ok even if filename_end is the ending \\0\n                                 // (then we assign entire string)\n\n    if (*funcname) { // if it's not end of string\n      *funcname_end = '\\0';\n\n      trace.object_function = this->demangle(funcname);\n      trace.object_function += \" \";\n      trace.object_function += (funcname_end + 1);\n      trace.source.function = trace.object_function; // we cannot do better.\n    }\n    return trace;\n  }\n\nprivate:\n  details::handle<char **> _symbols;\n};\n\ntemplate <>\nclass TraceResolverImpl<system_tag::darwin_tag>\n    : public TraceResolverDarwinImpl<trace_resolver_tag::current> {};\n\n#endif // BACKWARD_SYSTEM_DARWIN\n\n#ifdef BACKWARD_SYSTEM_WINDOWS\n\n// Load all symbol info\n// Based on:\n// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227\n\nstruct module_data {\n  std::string image_name;\n  std::string module_name;\n  void *base_address;\n  DWORD load_size;\n};\n\nclass get_mod_info {\n  HANDLE process;\n  static const int buffer_length = 4096;\n\npublic:\n  get_mod_info(HANDLE h) : process(h) {}\n\n  module_data operator()(HMODULE module) {\n    module_data ret;\n    char temp[buffer_length];\n    MODULEINFO mi;\n\n    GetModuleInformation(process, module, &mi, sizeof(mi));\n    ret.base_address = mi.lpBaseOfDll;\n    ret.load_size = mi.SizeOfImage;\n\n    GetModuleFileNameExA(process, module, temp, sizeof(temp));\n    ret.image_name = temp;\n    GetModuleBaseNameA(process, module, temp, sizeof(temp));\n    ret.module_name = temp;\n    std::vector<char> img(ret.image_name.begin(), ret.image_name.end());\n    std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());\n    SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address,\n                    ret.load_size);\n    return ret;\n  }\n};\n\ntemplate <> class TraceResolverImpl<system_tag::windows_tag>\n    : public TraceResolverImplBase {\npublic:\n  TraceResolverImpl() {\n\n    HANDLE process = GetCurrentProcess();\n\n    std::vector<module_data> modules;\n    DWORD cbNeeded;\n    std::vector<HMODULE> module_handles(1);\n    SymInitialize(process, NULL, false);\n    DWORD symOptions = SymGetOptions();\n    symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;\n    SymSetOptions(symOptions);\n    EnumProcessModules(process, &module_handles[0],\n                       static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)),\n\t\t       &cbNeeded);\n    module_handles.resize(cbNeeded / sizeof(HMODULE));\n    EnumProcessModules(process, &module_handles[0],\n                       static_cast<DWORD>(module_handles.size() * sizeof(HMODULE)),\n\t\t       &cbNeeded);\n    std::transform(module_handles.begin(), module_handles.end(),\n                   std::back_inserter(modules), get_mod_info(process));\n    void *base = modules[0].base_address;\n    IMAGE_NT_HEADERS *h = ImageNtHeader(base);\n    image_type = h->FileHeader.Machine;\n  }\n\n  static const int max_sym_len = 255;\n  struct symbol_t {\n    SYMBOL_INFO sym;\n    char buffer[max_sym_len];\n  } sym;\n\n  DWORD64 displacement;\n\n  ResolvedTrace resolve(ResolvedTrace t) override {\n    HANDLE process = GetCurrentProcess();\n\n    char name[256];\n\n    memset(&sym, 0, sizeof(sym));\n    sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);\n    sym.sym.MaxNameLen = max_sym_len;\n\n    if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {\n      // TODO:  error handling everywhere\n      char* lpMsgBuf;\n      DWORD dw = GetLastError();\n\n      if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |\n                             FORMAT_MESSAGE_FROM_SYSTEM |\n                             FORMAT_MESSAGE_IGNORE_INSERTS,\n                         NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\n                         (char*)&lpMsgBuf, 0, NULL)) {\n        std::fprintf(stderr, \"%s\\n\", lpMsgBuf);\n        LocalFree(lpMsgBuf);\n      }\n\n      // abort();\n    }\n    UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE);\n\n    DWORD offset = 0;\n    IMAGEHLP_LINE line;\n    if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) {\n      t.object_filename = line.FileName;\n      t.source.filename = line.FileName;\n      t.source.line = line.LineNumber;\n      t.source.col = offset;\n    }\n\n    t.source.function = name;\n    t.object_filename = \"\";\n    t.object_function = name;\n\n    return t;\n  }\n\n  DWORD machine_type() const { return image_type; }\n\nprivate:\n  DWORD image_type;\n};\n\n#endif\n\nclass TraceResolver : public TraceResolverImpl<system_tag::current_tag> {};\n\n/*************** CODE SNIPPET ***************/\n\nclass SourceFile {\npublic:\n  typedef std::vector<std::pair<unsigned, std::string> > lines_t;\n\n  SourceFile() {}\n  SourceFile(const std::string &path) {\n    // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains\n    //    a colon-separated list of path prefixes.  Try prepending each\n    //    to the given path until a valid file is found.\n    const std::vector<std::string> &prefixes = get_paths_from_env_variable();\n    for (size_t i = 0; i < prefixes.size(); ++i) {\n      // Double slashes (//) should not be a problem.\n      std::string new_path = prefixes[i] + '/' + path;\n      _file.reset(new std::ifstream(new_path.c_str()));\n      if (is_open())\n        break;\n    }\n    // 2. If no valid file found then fallback to opening the path as-is.\n    if (!_file || !is_open()) {\n      _file.reset(new std::ifstream(path.c_str()));\n    }\n  }\n  bool is_open() const { return _file->is_open(); }\n\n  lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) {\n    using namespace std;\n    // This function make uses of the dumbest algo ever:\n    //\t1) seek(0)\n    //\t2) read lines one by one and discard until line_start\n    //\t3) read line one by one until line_start + line_count\n    //\n    // If you are getting snippets many time from the same file, it is\n    // somewhat a waste of CPU, feel free to benchmark and propose a\n    // better solution ;)\n\n    _file->clear();\n    _file->seekg(0);\n    string line;\n    unsigned line_idx;\n\n    for (line_idx = 1; line_idx < line_start; ++line_idx) {\n      std::getline(*_file, line);\n      if (!*_file) {\n        return lines;\n      }\n    }\n\n    // think of it like a lambda in C++98 ;)\n    // but look, I will reuse it two times!\n    // What a good boy am I.\n    struct isspace {\n      bool operator()(char c) { return std::isspace(c); }\n    };\n\n    bool started = false;\n    for (; line_idx < line_start + line_count; ++line_idx) {\n      getline(*_file, line);\n      if (!*_file) {\n        return lines;\n      }\n      if (!started) {\n        if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end())\n          continue;\n        started = true;\n      }\n      lines.push_back(make_pair(line_idx, line));\n    }\n\n    lines.erase(\n        std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(),\n        lines.end());\n    return lines;\n  }\n\n  lines_t get_lines(unsigned line_start, unsigned line_count) {\n    lines_t lines;\n    return get_lines(line_start, line_count, lines);\n  }\n\n  // there is no find_if_not in C++98, lets do something crappy to\n  // workaround.\n  struct not_isspace {\n    bool operator()(char c) { return !std::isspace(c); }\n  };\n  // and define this one here because C++98 is not happy with local defined\n  // struct passed to template functions, fuuuu.\n  struct not_isempty {\n    bool operator()(const lines_t::value_type &p) {\n      return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) ==\n               p.second.end());\n    }\n  };\n\n  void swap(SourceFile &b) { _file.swap(b._file); }\n\n#ifdef BACKWARD_ATLEAST_CXX11\n  SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); }\n  SourceFile &operator=(SourceFile &&from) {\n    swap(from);\n    return *this;\n  }\n#else\n  explicit SourceFile(const SourceFile &from) {\n    // some sort of poor man's move semantic.\n    swap(const_cast<SourceFile &>(from));\n  }\n  SourceFile &operator=(const SourceFile &from) {\n    // some sort of poor man's move semantic.\n    swap(const_cast<SourceFile &>(from));\n    return *this;\n  }\n#endif\n\n  // Allow adding to paths gotten from BACKWARD_CXX_SOURCE_PREFIXES after loading the\n  // library; this can be useful when the library is loaded when the locations are unknown\n  // Warning: Because this edits the static paths variable, it is *not* intrinsiclly thread safe\n  static void add_paths_to_env_variable_impl(const std::string & to_add) {\n    get_mutable_paths_from_env_variable().push_back(to_add);\n  }\n\nprivate:\n  details::handle<std::ifstream *, details::default_delete<std::ifstream *> >\n      _file;\n\n  static std::vector<std::string> get_paths_from_env_variable_impl() {\n    std::vector<std::string> paths;\n    const char *prefixes_str = std::getenv(\"BACKWARD_CXX_SOURCE_PREFIXES\");\n    if (prefixes_str && prefixes_str[0]) {\n      paths = details::split_source_prefixes(prefixes_str);\n    }\n    return paths;\n  }\n\n  static std::vector<std::string> &get_mutable_paths_from_env_variable() {\n    static volatile std::vector<std::string> paths = get_paths_from_env_variable_impl();\n    return const_cast<std::vector<std::string>&>(paths);\n  }\n\n  static const std::vector<std::string> &get_paths_from_env_variable() {\n    return get_mutable_paths_from_env_variable();\n  }\n\n#ifdef BACKWARD_ATLEAST_CXX11\n  SourceFile(const SourceFile &) = delete;\n  SourceFile &operator=(const SourceFile &) = delete;\n#endif\n};\n\nclass SnippetFactory {\npublic:\n  typedef SourceFile::lines_t lines_t;\n\n  lines_t get_snippet(const std::string &filename, unsigned line_start,\n                      unsigned context_size) {\n\n    SourceFile &src_file = get_src_file(filename);\n    unsigned start = line_start - context_size / 2;\n    return src_file.get_lines(start, context_size);\n  }\n\n  lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a,\n                               const std::string &filename_b, unsigned line_b,\n                               unsigned context_size) {\n    SourceFile &src_file_a = get_src_file(filename_a);\n    SourceFile &src_file_b = get_src_file(filename_b);\n\n    lines_t lines =\n        src_file_a.get_lines(line_a - context_size / 4, context_size / 2);\n    src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines);\n    return lines;\n  }\n\n  lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a,\n                                unsigned line_b, unsigned context_size) {\n    SourceFile &src_file = get_src_file(filename);\n\n    using std::max;\n    using std::min;\n    unsigned a = min(line_a, line_b);\n    unsigned b = max(line_a, line_b);\n\n    if ((b - a) < (context_size / 3)) {\n      return src_file.get_lines((a + b - context_size + 1) / 2, context_size);\n    }\n\n    lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2);\n    src_file.get_lines(b - context_size / 4, context_size / 2, lines);\n    return lines;\n  }\n\nprivate:\n  typedef details::hashtable<std::string, SourceFile>::type src_files_t;\n  src_files_t _src_files;\n\n  SourceFile &get_src_file(const std::string &filename) {\n    src_files_t::iterator it = _src_files.find(filename);\n    if (it != _src_files.end()) {\n      return it->second;\n    }\n    SourceFile &new_src_file = _src_files[filename];\n    new_src_file = SourceFile(filename);\n    return new_src_file;\n  }\n};\n\n/*************** PRINTER ***************/\n\nnamespace ColorMode {\nenum type { automatic, never, always };\n}\n\nclass cfile_streambuf : public std::streambuf {\npublic:\n  cfile_streambuf(FILE *_sink) : sink(_sink) {}\n  int_type underflow() override { return traits_type::eof(); }\n  int_type overflow(int_type ch) override {\n    if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) {\n      return ch;\n    }\n    return traits_type::eof();\n  }\n\n  std::streamsize xsputn(const char_type *s, std::streamsize count) override {\n    return static_cast<std::streamsize>(\n        fwrite(s, sizeof *s, static_cast<size_t>(count), sink));\n  }\n\n#ifdef BACKWARD_ATLEAST_CXX11\npublic:\n  cfile_streambuf(const cfile_streambuf &) = delete;\n  cfile_streambuf &operator=(const cfile_streambuf &) = delete;\n#else\nprivate:\n  cfile_streambuf(const cfile_streambuf &);\n  cfile_streambuf &operator=(const cfile_streambuf &);\n#endif\n\nprivate:\n  FILE *sink;\n  std::vector<char> buffer;\n};\n\n#ifdef BACKWARD_SYSTEM_LINUX\n\nnamespace Color {\nenum type { yellow = 33, purple = 35, reset = 39 };\n} // namespace Color\n\nclass Colorize {\npublic:\n  Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {}\n\n  void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; }\n\n  void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); }\n\n  void set_color(Color::type ccode) {\n    if (!_enabled)\n      return;\n\n    // I assume that the terminal can handle basic colors. Seriously I\n    // don't want to deal with all the termcap shit.\n    _os << \"\\033[\" << static_cast<int>(ccode) << \"m\";\n    _reset = (ccode != Color::reset);\n  }\n\n  ~Colorize() {\n    if (_reset) {\n      set_color(Color::reset);\n    }\n  }\n\nprivate:\n  void activate(ColorMode::type mode, int fd) {\n    activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always\n                                                        : mode);\n  }\n\n  std::ostream &_os;\n  bool _reset;\n  bool _enabled;\n};\n\n#else // ndef BACKWARD_SYSTEM_LINUX\n\nnamespace Color {\nenum type { yellow = 0, purple = 0, reset = 0 };\n} // namespace Color\n\nclass Colorize {\npublic:\n  Colorize(std::ostream &) {}\n  void activate(ColorMode::type) {}\n  void activate(ColorMode::type, FILE *) {}\n  void set_color(Color::type) {}\n};\n\n#endif // BACKWARD_SYSTEM_LINUX\n\nclass Printer {\npublic:\n  bool snippet;\n  ColorMode::type color_mode;\n  bool address;\n  bool object;\n  int inliner_context_size;\n  int trace_context_size;\n  bool reverse;\n\n  Printer()\n      : snippet(true), color_mode(ColorMode::automatic), address(false),\n        object(false), inliner_context_size(5), trace_context_size(7),\n        reverse(true) {}\n\n  template <typename ST> FILE *print(ST &st, FILE *fp = stderr) {\n    cfile_streambuf obuf(fp);\n    std::ostream os(&obuf);\n    Colorize colorize(os);\n    colorize.activate(color_mode, fp);\n    print_stacktrace(st, os, colorize);\n    return fp;\n  }\n\n  template <typename ST> std::ostream &print(ST &st, std::ostream &os) {\n    Colorize colorize(os);\n    colorize.activate(color_mode);\n    print_stacktrace(st, os, colorize);\n    return os;\n  }\n\n  template <typename IT>\n  FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) {\n    cfile_streambuf obuf(fp);\n    std::ostream os(&obuf);\n    Colorize colorize(os);\n    colorize.activate(color_mode, fp);\n    print_stacktrace(begin, end, os, thread_id, colorize);\n    return fp;\n  }\n\n  template <typename IT>\n  std::ostream &print(IT begin, IT end, std::ostream &os,\n                      size_t thread_id = 0) {\n    Colorize colorize(os);\n    colorize.activate(color_mode);\n    print_stacktrace(begin, end, os, thread_id, colorize);\n    return os;\n  }\n\n  TraceResolver const &resolver() const { return _resolver; }\n\nprivate:\n  TraceResolver _resolver;\n  SnippetFactory _snippets;\n\n  template <typename ST>\n  void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) {\n    print_header(os, st.thread_id());\n    _resolver.load_stacktrace(st);\n    if ( reverse ) {\n      for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) {\n        print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize);\n      }\n    } else {\n      for (size_t trace_idx = 0; trace_idx < st.size(); ++trace_idx) {\n        print_trace(os, _resolver.resolve(st[trace_idx]), colorize);\n      }\n    }\n  }\n\n  template <typename IT>\n  void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id,\n                        Colorize &colorize) {\n    print_header(os, thread_id);\n    for (; begin != end; ++begin) {\n      print_trace(os, *begin, colorize);\n    }\n  }\n\n  void print_header(std::ostream &os, size_t thread_id) {\n    os << \"Stack trace (most recent call last)\";\n    if (thread_id) {\n      os << \" in thread \" << thread_id;\n    }\n    os << \":\\n\";\n  }\n\n  void print_trace(std::ostream &os, const ResolvedTrace &trace,\n                   Colorize &colorize) {\n    os << \"#\" << std::left << std::setw(2) << trace.idx << std::right;\n    bool already_indented = true;\n\n    if (!trace.source.filename.size() || object) {\n      os << \"   Object \\\"\" << trace.object_filename << \"\\\", at \" << trace.addr\n         << \", in \" << trace.object_function << \"\\n\";\n      already_indented = false;\n    }\n\n    for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0;\n         --inliner_idx) {\n      if (!already_indented) {\n        os << \"   \";\n      }\n      const ResolvedTrace::SourceLoc &inliner_loc =\n          trace.inliners[inliner_idx - 1];\n      print_source_loc(os, \" | \", inliner_loc);\n      if (snippet) {\n        print_snippet(os, \"    | \", inliner_loc, colorize, Color::purple,\n                      inliner_context_size);\n      }\n      already_indented = false;\n    }\n\n    if (trace.source.filename.size()) {\n      if (!already_indented) {\n        os << \"   \";\n      }\n      print_source_loc(os, \"   \", trace.source, trace.addr);\n      if (snippet) {\n        print_snippet(os, \"      \", trace.source, colorize, Color::yellow,\n                      trace_context_size);\n      }\n    }\n  }\n\n  void print_snippet(std::ostream &os, const char *indent,\n                     const ResolvedTrace::SourceLoc &source_loc,\n                     Colorize &colorize, Color::type color_code,\n                     int context_size) {\n    using namespace std;\n    typedef SnippetFactory::lines_t lines_t;\n\n    lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line,\n                                          static_cast<unsigned>(context_size));\n\n    for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) {\n      if (it->first == source_loc.line) {\n        colorize.set_color(color_code);\n        os << indent << \">\";\n      } else {\n        os << indent << \" \";\n      }\n      os << std::setw(4) << it->first << \": \" << it->second << \"\\n\";\n      if (it->first == source_loc.line) {\n        colorize.set_color(Color::reset);\n      }\n    }\n  }\n\n  void print_source_loc(std::ostream &os, const char *indent,\n                        const ResolvedTrace::SourceLoc &source_loc,\n                        void *addr = nullptr) {\n    os << indent << \"Source \\\"\" << source_loc.filename << \"\\\", line \"\n       << source_loc.line << \", in \" << source_loc.function;\n\n    if (address && addr != nullptr) {\n      os << \" [\" << addr << \"]\";\n    }\n    os << \"\\n\";\n  }\n};\n\n/*************** SIGNALS HANDLING ***************/\n\n#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN)\n\nclass SignalHandling {\npublic:\n  static std::vector<int> make_default_signals() {\n    const int posix_signals[] = {\n      // Signals for which the default action is \"Core\".\n      SIGABRT, // Abort signal from abort(3)\n      SIGBUS,  // Bus error (bad memory access)\n      SIGFPE,  // Floating point exception\n      SIGILL,  // Illegal Instruction\n      SIGIOT,  // IOT trap. A synonym for SIGABRT\n      SIGQUIT, // Quit from keyboard\n      SIGSEGV, // Invalid memory reference\n      SIGSYS,  // Bad argument to routine (SVr4)\n      SIGTRAP, // Trace/breakpoint trap\n      SIGXCPU, // CPU time limit exceeded (4.2BSD)\n      SIGXFSZ, // File size limit exceeded (4.2BSD)\n#if defined(BACKWARD_SYSTEM_DARWIN)\n      SIGEMT, // emulation instruction executed\n#endif\n    };\n    return std::vector<int>(posix_signals,\n                            posix_signals +\n                                sizeof posix_signals / sizeof posix_signals[0]);\n  }\n\n  SignalHandling(const std::vector<int> &posix_signals = make_default_signals())\n      : _loaded(false) {\n    bool success = true;\n\n    const size_t stack_size = 1024 * 1024 * 8;\n    _stack_content.reset(static_cast<char *>(malloc(stack_size)));\n    if (_stack_content) {\n      stack_t ss;\n      ss.ss_sp = _stack_content.get();\n      ss.ss_size = stack_size;\n      ss.ss_flags = 0;\n      if (sigaltstack(&ss, nullptr) < 0) {\n        success = false;\n      }\n    } else {\n      success = false;\n    }\n\n    for (size_t i = 0; i < posix_signals.size(); ++i) {\n      struct sigaction action;\n      memset(&action, 0, sizeof action);\n      action.sa_flags =\n          static_cast<int>(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND);\n      sigfillset(&action.sa_mask);\n      sigdelset(&action.sa_mask, posix_signals[i]);\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdisabled-macro-expansion\"\n#endif\n      action.sa_sigaction = &sig_handler;\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n      int r = sigaction(posix_signals[i], &action, nullptr);\n      if (r < 0)\n        success = false;\n    }\n\n    _loaded = success;\n  }\n\n  bool loaded() const { return _loaded; }\n\n  static void handleSignal(int, siginfo_t *info, void *_ctx) {\n    ucontext_t *uctx = static_cast<ucontext_t *>(_ctx);\n\n    StackTrace st;\n    void *error_addr = nullptr;\n#ifdef REG_RIP // x86_64\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_RIP]);\n#elif defined(REG_EIP) // x86_32\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_EIP]);\n#elif defined(__arm__)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.arm_pc);\n#elif defined(__aarch64__)\n    #if defined(__APPLE__)\n      error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__pc);\n    #else\n      error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.pc);\n    #endif\n#elif defined(__mips__)\n    error_addr = reinterpret_cast<void *>(\n        reinterpret_cast<struct sigcontext *>(&uctx->uc_mcontext)->sc_pc);\n#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) ||        \\\n    defined(__POWERPC__)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.regs->nip);\n#elif defined(__riscv)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.__gregs[REG_PC]);\n#elif defined(__s390x__)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.psw.addr);\n#elif defined(__APPLE__) && defined(__x86_64__)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__rip);\n#elif defined(__APPLE__)\n    error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__eip);\n#else\n#warning \":/ sorry, ain't know no nothing none not of your architecture!\"\n#endif\n    if (error_addr) {\n      st.load_from(error_addr, 32, reinterpret_cast<void *>(uctx),\n                   info->si_addr);\n    } else {\n      st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr);\n    }\n\n    Printer printer;\n    printer.address = true;\n    printer.print(st, stderr);\n\n#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \\\n    (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L)\n    psiginfo(info, nullptr);\n#else\n    (void)info;\n#endif\n  }\n\nprivate:\n  details::handle<char *> _stack_content;\n  bool _loaded;\n\n#ifdef __GNUC__\n  __attribute__((noreturn))\n#endif\n  static void\n  sig_handler(int signo, siginfo_t *info, void *_ctx) {\n    handleSignal(signo, info, _ctx);\n\n    // try to forward the signal.\n    raise(info->si_signo);\n\n    // terminate the process immediately.\n    puts(\"watf? exit\");\n    _exit(EXIT_FAILURE);\n  }\n};\n\n#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN\n\n#ifdef BACKWARD_SYSTEM_WINDOWS\n\nclass SignalHandling {\npublic:\n  SignalHandling(const std::vector<int> & = std::vector<int>())\n      : reporter_thread_([]() {\n          /* We handle crashes in a utility thread:\n            backward structures and some Windows functions called here\n            need stack space, which we do not have when we encounter a\n            stack overflow.\n            To support reporting stack traces during a stack overflow,\n            we create a utility thread at startup, which waits until a\n            crash happens or the program exits normally. */\n\n          {\n            std::unique_lock<std::mutex> lk(mtx());\n            cv().wait(lk, [] { return crashed() != crash_status::running; });\n          }\n          if (crashed() == crash_status::crashed) {\n            handle_stacktrace(skip_recs());\n          }\n          {\n            std::unique_lock<std::mutex> lk(mtx());\n            crashed() = crash_status::ending;\n          }\n          cv().notify_one();\n        }) {\n    SetUnhandledExceptionFilter(crash_handler);\n\n    signal(SIGABRT, signal_handler);\n    _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);\n\n    std::set_terminate(&terminator);\n#ifndef BACKWARD_ATLEAST_CXX17\n    std::set_unexpected(&terminator);\n#endif\n    _set_purecall_handler(&terminator);\n    _set_invalid_parameter_handler(&invalid_parameter_handler);\n  }\n  bool loaded() const { return true; }\n\n  ~SignalHandling() {\n    {\n      std::unique_lock<std::mutex> lk(mtx());\n      crashed() = crash_status::normal_exit;\n    }\n\n    cv().notify_one();\n\n    reporter_thread_.join();\n  }\n\nprivate:\n  static CONTEXT *ctx() {\n    static CONTEXT data;\n    return &data;\n  }\n\n  enum class crash_status { running, crashed, normal_exit, ending };\n\n  static crash_status &crashed() {\n    static crash_status data;\n    return data;\n  }\n\n  static std::mutex &mtx() {\n    static std::mutex data;\n    return data;\n  }\n\n  static std::condition_variable &cv() {\n    static std::condition_variable data;\n    return data;\n  }\n\n  static HANDLE &thread_handle() {\n    static HANDLE handle;\n    return handle;\n  }\n\n  std::thread reporter_thread_;\n\n  // TODO: how not to hardcode these?\n  static const constexpr int signal_skip_recs =\n#ifdef __clang__\n      // With clang, RtlCaptureContext also captures the stack frame of the\n      // current function Below that, there are 3 internal Windows functions\n      4\n#else\n      // With MSVC cl, RtlCaptureContext misses the stack frame of the current\n      // function The first entries during StackWalk are the 3 internal Windows\n      // functions\n      3\n#endif\n      ;\n\n  static int &skip_recs() {\n    static int data;\n    return data;\n  }\n\n  static inline void terminator() {\n    crash_handler(signal_skip_recs);\n    abort();\n  }\n\n  static inline void signal_handler(int) {\n    crash_handler(signal_skip_recs);\n    abort();\n  }\n\n  static inline void __cdecl invalid_parameter_handler(const wchar_t *,\n                                                       const wchar_t *,\n                                                       const wchar_t *,\n                                                       unsigned int,\n                                                       uintptr_t) {\n    crash_handler(signal_skip_recs);\n    abort();\n  }\n\n  NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) {\n    // The exception info supplies a trace from exactly where the issue was,\n    // no need to skip records\n    crash_handler(0, info->ContextRecord);\n    return EXCEPTION_CONTINUE_SEARCH;\n  }\n\n  NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) {\n\n    if (ct == nullptr) {\n      RtlCaptureContext(ctx());\n    } else {\n      memcpy(ctx(), ct, sizeof(CONTEXT));\n    }\n    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),\n                    GetCurrentProcess(), &thread_handle(), 0, FALSE,\n                    DUPLICATE_SAME_ACCESS);\n\n    skip_recs() = skip;\n\n    {\n      std::unique_lock<std::mutex> lk(mtx());\n      crashed() = crash_status::crashed;\n    }\n\n    cv().notify_one();\n\n    {\n      std::unique_lock<std::mutex> lk(mtx());\n      cv().wait(lk, [] { return crashed() != crash_status::crashed; });\n    }\n  }\n\n  static void handle_stacktrace(int skip_frames = 0) {\n    // printer creates the TraceResolver, which can supply us a machine type\n    // for stack walking. Without this, StackTrace can only guess using some\n    // macros.\n    // StackTrace also requires that the PDBs are already loaded, which is done\n    // in the constructor of TraceResolver\n    Printer printer;\n\n    StackTrace st;\n    st.set_machine_type(printer.resolver().machine_type());\n    st.set_thread_handle(thread_handle());\n    st.load_here(32 + skip_frames, ctx());\n    st.skip_n_firsts(skip_frames);\n\n    printer.address = true;\n    printer.print(st, std::cerr);\n  }\n};\n\n#endif // BACKWARD_SYSTEM_WINDOWS\n\n#ifdef BACKWARD_SYSTEM_UNKNOWN\n\nclass SignalHandling {\npublic:\n  SignalHandling(const std::vector<int> & = std::vector<int>()) {}\n  bool init() { return false; }\n  bool loaded() { return false; }\n};\n\n#endif // BACKWARD_SYSTEM_UNKNOWN\n\n} // namespace backward\n\n#endif /* H_GUARD */\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/threadtools.cpp",
    "content": "//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================\n#include \"threadtools.h\"\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCThread::CThread()\n\t: m_pThread( NULL )\n{}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCThread::~CThread()\n{\n\tJoin();\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CThread::Start()\n{\n\tif ( Init() )\n\t{\n\t\tm_pThread = new std::thread( &CThread::Run, this );\n\t}\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nvoid CThread::Join()\n{\n\tif ( m_pThread )\n\t{\n\t\tm_pThread->join();\n\t\tdelete m_pThread;\n\t\tm_pThread = NULL;\n\t}\n}\n\n#ifdef _WIN32\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCThreadEvent::CThreadEvent( bool bManualReset )\n{\n\tm_hSyncObject = CreateEvent( NULL, bManualReset, FALSE, NULL );\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nCThreadEvent::~CThreadEvent()\n{\n\tif ( m_hSyncObject )\n\t{\n\t\tCloseHandle( m_hSyncObject );\n\t}\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CThreadEvent::Wait( uint32_t nTimeoutMs )\n{\n\treturn WaitForSingleObject( m_hSyncObject, nTimeoutMs ) == WAIT_OBJECT_0;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CThreadEvent::Set()\n{\n\treturn SetEvent( m_hSyncObject ) != 0;\n}\n\n//--------------------------------------------------------------------------------------------------\n//--------------------------------------------------------------------------------------------------\nbool CThreadEvent::Reset()\n{\n\treturn ResetEvent( m_hSyncObject ) != 0;\n}\n\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/cpp/shared/threadtools.h",
    "content": "//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================\n//\n// Helper classes for working with threads.\n//\n//==================================================================================================\n#pragma once\n\n#include <thread>\n#ifdef _WIN32\n#include <windows.h>\n#endif\n\n#define THREAD_PRIORITY_MOST_URGENT 15\n\nclass CThread\n{\npublic:\n\tCThread();\n\tvirtual ~CThread();\n\tvirtual bool Init() { return true; }\n\tvirtual void Run() = 0;\n\tvoid Start();\n\tvoid Join();\nprivate:\n\tstd::thread *m_pThread;\n};\n\n#ifdef _WIN32\nclass CThreadEvent\n{\npublic:\n\tCThreadEvent( bool bManualReset = false );\n\t~CThreadEvent();\n\tbool Wait( uint32_t nTimeoutMs = INFINITE );\n\tbool Set();\n\tbool Reset();\nprivate:\n\tHANDLE m_hSyncObject;\n};\n#endif\n"
  },
  {
    "path": "alvr/server_openvr/src/graphics.rs",
    "content": "static FRAME_RENDER_VS_CSO: &[u8] = include_bytes!(\"../cpp/platform/win32/FrameRenderVS.cso\");\nstatic FRAME_RENDER_PS_CSO: &[u8] = include_bytes!(\"../cpp/platform/win32/FrameRenderPS.cso\");\nstatic QUAD_SHADER_CSO: &[u8] = include_bytes!(\"../cpp/platform/win32/QuadVertexShader.cso\");\nstatic COMPRESS_AXIS_ALIGNED_CSO: &[u8] =\n    include_bytes!(\"../cpp/platform/win32/CompressAxisAlignedPixelShader.cso\");\nstatic COLOR_CORRECTION_CSO: &[u8] =\n    include_bytes!(\"../cpp/platform/win32/ColorCorrectionPixelShader.cso\");\nstatic RGBTOYUV420_CSO: &[u8] = include_bytes!(\"../cpp/platform/win32/rgbtoyuv420.cso\");\n\nstatic QUAD_SHADER_COMP_SPV: &[u8] = include_bytes!(\"../cpp/platform/linux/shader/quad.comp.spv\");\nstatic COLOR_SHADER_COMP_SPV: &[u8] = include_bytes!(\"../cpp/platform/linux/shader/color.comp.spv\");\nstatic FFR_SHADER_COMP_SPV: &[u8] = include_bytes!(\"../cpp/platform/linux/shader/ffr.comp.spv\");\nstatic RGBTOYUV420_SHADER_COMP_SPV: &[u8] =\n    include_bytes!(\"../cpp/platform/linux/shader/rgbtoyuv420.comp.spv\");\n\npub fn initialize_shaders() {\n    unsafe {\n        crate::FRAME_RENDER_VS_CSO_PTR = FRAME_RENDER_VS_CSO.as_ptr();\n        crate::FRAME_RENDER_VS_CSO_LEN = FRAME_RENDER_VS_CSO.len() as _;\n        crate::FRAME_RENDER_PS_CSO_PTR = FRAME_RENDER_PS_CSO.as_ptr();\n        crate::FRAME_RENDER_PS_CSO_LEN = FRAME_RENDER_PS_CSO.len() as _;\n        crate::QUAD_SHADER_CSO_PTR = QUAD_SHADER_CSO.as_ptr();\n        crate::QUAD_SHADER_CSO_LEN = QUAD_SHADER_CSO.len() as _;\n        crate::COMPRESS_AXIS_ALIGNED_CSO_PTR = COMPRESS_AXIS_ALIGNED_CSO.as_ptr();\n        crate::COMPRESS_AXIS_ALIGNED_CSO_LEN = COMPRESS_AXIS_ALIGNED_CSO.len() as _;\n        crate::COLOR_CORRECTION_CSO_PTR = COLOR_CORRECTION_CSO.as_ptr();\n        crate::COLOR_CORRECTION_CSO_LEN = COLOR_CORRECTION_CSO.len() as _;\n        crate::RGBTOYUV420_CSO_PTR = RGBTOYUV420_CSO.as_ptr();\n        crate::RGBTOYUV420_CSO_LEN = RGBTOYUV420_CSO.len() as _;\n        crate::QUAD_SHADER_COMP_SPV_PTR = QUAD_SHADER_COMP_SPV.as_ptr();\n        crate::QUAD_SHADER_COMP_SPV_LEN = QUAD_SHADER_COMP_SPV.len() as _;\n        crate::COLOR_SHADER_COMP_SPV_PTR = COLOR_SHADER_COMP_SPV.as_ptr();\n        crate::COLOR_SHADER_COMP_SPV_LEN = COLOR_SHADER_COMP_SPV.len() as _;\n        crate::FFR_SHADER_COMP_SPV_PTR = FFR_SHADER_COMP_SPV.as_ptr();\n        crate::FFR_SHADER_COMP_SPV_LEN = FFR_SHADER_COMP_SPV.len() as _;\n        crate::RGBTOYUV420_SHADER_COMP_SPV_PTR = RGBTOYUV420_SHADER_COMP_SPV.as_ptr();\n        crate::RGBTOYUV420_SHADER_COMP_SPV_LEN = RGBTOYUV420_SHADER_COMP_SPV.len() as _;\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/src/lib.rs",
    "content": "mod graphics;\nmod props;\nmod tracking;\n\n#[allow(\n    non_camel_case_types,\n    non_upper_case_globals,\n    dead_code,\n    non_snake_case,\n    clippy::unseparated_literal_suffix\n)]\nmod bindings {\n    include!(concat!(env!(\"OUT_DIR\"), \"/bindings.rs\"));\n}\nuse bindings::*;\n\nuse alvr_common::{\n    BUTTON_INFO, HAND_LEFT_ID, HAND_RIGHT_ID, HAND_TRACKER_LEFT_ID, HAND_TRACKER_RIGHT_ID, HEAD_ID,\n    Pose, ViewParams, error,\n    parking_lot::{Mutex, RwLock},\n    settings_schema::Switch,\n    warn,\n};\nuse alvr_filesystem as afs;\nuse alvr_packets::{ButtonValue, Haptics};\nuse alvr_server_core::{HandType, ServerCoreContext, ServerCoreEvent};\nuse alvr_session::{CodecType, ControllersConfig};\nuse std::{\n    collections::VecDeque,\n    ffi::{CString, OsStr, c_char, c_void},\n    ptr,\n    sync::{Once, mpsc},\n    thread,\n    time::{Duration, Instant},\n};\n\nstatic SERVER_CORE_CONTEXT: RwLock<Option<ServerCoreContext>> = RwLock::new(None);\nstatic LOCAL_VIEW_PARAMS: RwLock<[ViewParams; 2]> = RwLock::new([ViewParams::DUMMY; 2]);\nstatic HEAD_POSE_QUEUE: Mutex<VecDeque<(Duration, Pose)>> = Mutex::new(VecDeque::new());\n\nfn event_loop(events_receiver: mpsc::Receiver<ServerCoreEvent>) {\n    thread::spawn(move || {\n        if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n            context.start_connection();\n        }\n\n        let mut last_resync = Instant::now();\n        loop {\n            let event = match events_receiver.recv_timeout(Duration::from_millis(5)) {\n                Ok(event) => event,\n                Err(mpsc::RecvTimeoutError::Timeout) => continue,\n                Err(mpsc::RecvTimeoutError::Disconnected) => break,\n            };\n\n            match event {\n                ServerCoreEvent::SetOpenvrProperty { device_id, prop } => {\n                    props::set_openvr_prop(None, device_id, prop)\n                }\n                ServerCoreEvent::ClientConnected => unsafe {\n                    if InitializeStreaming() {\n                        RequestDriverResync();\n                    } else {\n                        SERVER_CORE_CONTEXT.write().take();\n\n                        ShutdownSteamvr();\n                    }\n                },\n\n                ServerCoreEvent::ClientDisconnected => unsafe { DeinitializeStreaming() },\n                ServerCoreEvent::Battery(info) => unsafe {\n                    SetBattery(info.device_id, info.gauge_value, info.is_plugged)\n                },\n                ServerCoreEvent::PlayspaceSync(bounds) => unsafe {\n                    SetChaperoneArea(bounds.x, bounds.y)\n                },\n                ServerCoreEvent::LocalViewParams(params) => unsafe {\n                    *LOCAL_VIEW_PARAMS.write() = params;\n\n                    let ffi_params = [\n                        tracking::to_ffi_view_params(params[0]),\n                        tracking::to_ffi_view_params(params[1]),\n                    ];\n                    SetLocalViewParams(ffi_params.as_ptr());\n                },\n                ServerCoreEvent::Tracking { poll_timestamp } => {\n                    let headset_config = &alvr_server_core::settings().headset;\n\n                    let controllers_config = headset_config.controllers.clone().into_option();\n                    let track_body = headset_config.body_tracking.enabled();\n\n                    let tracked = controllers_config.as_ref().is_some_and(|c| c.tracked);\n                    let detached_controllers = headset_config\n                        .multimodal_tracking\n                        .as_option()\n                        .is_some_and(|c| c.detached_controllers_steamvr_sink);\n\n                    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n                        let target_timestamp =\n                            poll_timestamp + context.get_motion_to_photon_latency();\n                        let controllers_pose_time_offset = context.get_tracker_pose_time_offset();\n                        // We need to remove the additional offset that SteamVR adds\n                        let target_controller_timestamp =\n                            target_timestamp.saturating_sub(controllers_pose_time_offset);\n\n                        let ffi_head_motion = if let Some(motion) =\n                            context.get_device_motion(*HEAD_ID, poll_timestamp)\n                        {\n                            let motion = motion.predict(poll_timestamp, target_timestamp);\n\n                            let mut head_pose_queue_lock = HEAD_POSE_QUEUE.lock();\n                            head_pose_queue_lock.push_back((poll_timestamp, motion.pose));\n                            while head_pose_queue_lock.len() > 360 {\n                                head_pose_queue_lock.pop_front();\n                            }\n\n                            tracking::to_ffi_motion(*HEAD_ID, motion)\n                        } else {\n                            FfiDeviceMotion::default()\n                        };\n\n                        let (ffi_left_controller_motion, ffi_right_controller_motion) =\n                            if tracked && let Some(config) = &controllers_config {\n                                let ffi_left_controller_motion = context\n                                    .get_device_motion(*HAND_LEFT_ID, poll_timestamp)\n                                    .map(|motion| {\n                                        let motion = motion\n                                            .predict(poll_timestamp, target_controller_timestamp);\n                                        let motion = tracking::offset_controller_motion(\n                                            config,\n                                            *HAND_LEFT_ID,\n                                            motion,\n                                        );\n                                        tracking::to_ffi_motion(*HAND_LEFT_ID, motion)\n                                    });\n                                let ffi_right_controller_motion = context\n                                    .get_device_motion(*HAND_RIGHT_ID, poll_timestamp)\n                                    .map(|motion| {\n                                        let motion = motion\n                                            .predict(poll_timestamp, target_controller_timestamp);\n                                        let motion = tracking::offset_controller_motion(\n                                            config,\n                                            *HAND_RIGHT_ID,\n                                            motion,\n                                        );\n                                        tracking::to_ffi_motion(*HAND_RIGHT_ID, motion)\n                                    });\n\n                                (ffi_left_controller_motion, ffi_right_controller_motion)\n                            } else {\n                                (None, None)\n                            };\n\n                        let (\n                            ffi_left_hand_skeleton,\n                            ffi_right_hand_skeleton,\n                            use_separate_hand_trackers,\n                            predict_hand_skeleton,\n                        ) = if let Some(ControllersConfig {\n                            hand_skeleton: Switch::Enabled(hand_skeleton_config),\n                            ..\n                        }) = controllers_config\n                        {\n                            let left_hand_skeleton = context\n                                .get_hand_skeleton(HandType::Left, poll_timestamp)\n                                .map(|s| {\n                                    tracking::to_openvr_ffi_hand_skeleton(\n                                        headset_config,\n                                        *HAND_LEFT_ID,\n                                        &s,\n                                    )\n                                });\n                            let right_hand_skeleton = context\n                                .get_hand_skeleton(HandType::Right, poll_timestamp)\n                                .map(|s| {\n                                    tracking::to_openvr_ffi_hand_skeleton(\n                                        headset_config,\n                                        *HAND_RIGHT_ID,\n                                        &s,\n                                    )\n                                });\n\n                            (\n                                tracked.then_some(left_hand_skeleton).flatten(),\n                                tracked.then_some(right_hand_skeleton).flatten(),\n                                hand_skeleton_config.steamvr_input_2_0,\n                                hand_skeleton_config.predict,\n                            )\n                        } else {\n                            (None, None, false, false)\n                        };\n\n                        let ffi_left_hand_data = FfiHandData {\n                            controllerMotion: if let Some(motion) = &ffi_left_controller_motion {\n                                motion\n                            } else {\n                                ptr::null()\n                            },\n                            handSkeleton: if let Some(skeleton) = &ffi_left_hand_skeleton {\n                                skeleton\n                            } else {\n                                ptr::null()\n                            },\n                            isHandTracker: use_separate_hand_trackers\n                                && ffi_left_controller_motion.is_none()\n                                && ffi_left_hand_skeleton.is_some(),\n                            predictHandSkeleton: predict_hand_skeleton,\n                        };\n                        let ffi_right_hand_data = FfiHandData {\n                            controllerMotion: if let Some(motion) = &ffi_right_controller_motion {\n                                motion\n                            } else {\n                                ptr::null()\n                            },\n                            handSkeleton: if let Some(skeleton) = &ffi_right_hand_skeleton {\n                                skeleton\n                            } else {\n                                ptr::null()\n                            },\n                            isHandTracker: use_separate_hand_trackers\n                                && ffi_right_controller_motion.is_none()\n                                && ffi_right_hand_skeleton.is_some(),\n                            predictHandSkeleton: predict_hand_skeleton,\n                        };\n\n                        let ffi_body_tracker_motions = if track_body || detached_controllers {\n                            tracking::BODY_TRACKER_IDS\n                                .iter()\n                                .filter_map(|id| {\n                                    Some(tracking::to_ffi_motion(\n                                        *id,\n                                        context.get_device_motion(*id, poll_timestamp)?,\n                                    ))\n                                })\n                                .collect::<Vec<_>>()\n                        } else {\n                            vec![]\n                        };\n\n                        // There are two pairs of controllers/hand tracking devices registered in\n                        // OpenVR, two lefts and two rights. If enabled with use_separate_hand_trackers,\n                        // we select at runtime which device to use (selected for left and right hand\n                        // independently. Selection is done by setting deviceIsConnected.\n                        unsafe {\n                            SetTracking(\n                                poll_timestamp.as_nanos() as _,\n                                controllers_pose_time_offset.as_secs_f32(),\n                                ffi_head_motion,\n                                ffi_left_hand_data,\n                                ffi_right_hand_data,\n                                ffi_body_tracker_motions.as_ptr(),\n                                ffi_body_tracker_motions.len() as i32,\n                            )\n                        };\n                    }\n                }\n                ServerCoreEvent::Buttons(entries) => {\n                    for entry in entries {\n                        let value = match entry.value {\n                            ButtonValue::Binary(value) => FfiButtonValue {\n                                type_: FfiButtonType_BUTTON_TYPE_BINARY,\n                                __bindgen_anon_1: FfiButtonValue__bindgen_ty_1 {\n                                    binary: value.into(),\n                                },\n                            },\n\n                            ButtonValue::Scalar(value) => FfiButtonValue {\n                                type_: FfiButtonType_BUTTON_TYPE_SCALAR,\n                                __bindgen_anon_1: FfiButtonValue__bindgen_ty_1 { scalar: value },\n                            },\n                        };\n                        unsafe { SetButton(entry.path_id, value) };\n                    }\n                }\n                ServerCoreEvent::RequestIDR => unsafe { RequestIDR() },\n                ServerCoreEvent::CaptureFrame => unsafe { CaptureFrame() },\n                ServerCoreEvent::GameRenderLatencyFeedback(game_latency) => {\n                    if cfg!(target_os = \"linux\") && game_latency.as_secs_f32() > 0.25 {\n                        let now = Instant::now();\n                        if now.saturating_duration_since(last_resync).as_secs_f32() > 0.1 {\n                            last_resync = now;\n                            warn!(\"Desync detected. Attempting recovery.\");\n                            unsafe {\n                                RequestDriverResync();\n                            }\n                        }\n                    }\n                }\n                ServerCoreEvent::ShutdownPending => {\n                    SERVER_CORE_CONTEXT.write().take();\n\n                    unsafe { ShutdownSteamvr() };\n                }\n                ServerCoreEvent::RestartPending => {\n                    if let Some(context) = SERVER_CORE_CONTEXT.write().take() {\n                        context.restart();\n                    }\n\n                    unsafe { ShutdownSteamvr() };\n                }\n                ServerCoreEvent::ProximityState(headset_is_worn) => unsafe {\n                    SetProximityState(headset_is_worn)\n                },\n            }\n        }\n\n        unsafe { ShutdownOpenvrClient() };\n    });\n}\n\nextern \"C\" fn driver_ready_idle(set_default_chap: bool) {\n    thread::spawn(move || {\n        unsafe { InitOpenvrClient() };\n\n        if set_default_chap {\n            // call this when inside a new thread. Calling this on the parent thread will crash SteamVR\n            unsafe {\n                SetChaperoneArea(2.0, 2.0);\n            }\n        }\n    });\n}\n\n/// # Safety\n/// `instance_ptr` is a valid pointer to a `TrackedDevice` instance\npub unsafe extern \"C\" fn register_buttons(instance_ptr: *mut c_void, device_id: u64) {\n    let mapped_device_id = if device_id == *HAND_TRACKER_LEFT_ID {\n        *HAND_LEFT_ID\n    } else if device_id == *HAND_TRACKER_RIGHT_ID {\n        *HAND_RIGHT_ID\n    } else {\n        device_id\n    };\n\n    for button_id in alvr_server_core::registered_button_set() {\n        if let Some(info) = BUTTON_INFO.get(&button_id) {\n            if info.device_id == mapped_device_id {\n                unsafe { RegisterButton(instance_ptr, button_id) };\n            }\n        } else {\n            error!(\"Cannot register unrecognized button ID {button_id}\");\n        }\n    }\n}\n\nextern \"C\" fn send_haptics(device_id: u64, duration_s: f32, frequency: f32, amplitude: f32) {\n    if let Ok(duration) = Duration::try_from_secs_f32(duration_s)\n        && let Some(context) = &*SERVER_CORE_CONTEXT.read()\n    {\n        context.send_haptics(Haptics {\n            device_id,\n            duration,\n            frequency,\n            amplitude,\n        });\n    }\n}\n\nextern \"C\" fn set_video_config_nals(buffer_ptr: *const u8, len: i32, codec: i32) {\n    let codec = if codec == 0 {\n        CodecType::H264\n    } else if codec == 1 {\n        CodecType::Hevc\n    } else {\n        CodecType::AV1\n    };\n\n    let mut config_buffer = vec![0; len as usize];\n\n    unsafe { ptr::copy_nonoverlapping(buffer_ptr, config_buffer.as_mut_ptr(), len as usize) };\n\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.set_video_config_nals(config_buffer, codec);\n    }\n}\n\nextern \"C\" fn send_video(timestamp_ns: u64, buffer_ptr: *mut u8, len: i32, is_idr: bool) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        let timestamp = Duration::from_nanos(timestamp_ns);\n        let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, len as usize) };\n\n        let Some(head_pose) = HEAD_POSE_QUEUE\n            .lock()\n            .iter()\n            .find_map(|(ts, pose)| (*ts == timestamp).then_some(*pose))\n        else {\n            // We can't submit the frame without its pose\n            return;\n        };\n\n        let local_views_params = LOCAL_VIEW_PARAMS.read();\n\n        let global_view_params = [\n            ViewParams {\n                pose: head_pose * local_views_params[0].pose,\n                fov: local_views_params[0].fov,\n            },\n            ViewParams {\n                pose: head_pose * local_views_params[1].pose,\n                fov: local_views_params[1].fov,\n            },\n        ];\n\n        context.send_video_nal(timestamp, global_view_params, is_idr, buffer.to_vec());\n    }\n}\n\nextern \"C\" fn get_dynamic_encoder_params() -> FfiDynamicEncoderParams {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read()\n        && let Some(params) = context.get_dynamic_encoder_params()\n    {\n        FfiDynamicEncoderParams {\n            updated: 1,\n            bitrate_bps: params.bitrate_bps as u64,\n            framerate: params.framerate,\n        }\n    } else {\n        FfiDynamicEncoderParams::default()\n    }\n}\n\nextern \"C\" fn report_composed(timestamp_ns: u64, offset_ns: u64) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.report_composed(\n            Duration::from_nanos(timestamp_ns),\n            Duration::from_nanos(offset_ns),\n        );\n    }\n}\n\nextern \"C\" fn report_present(timestamp_ns: u64, offset_ns: u64) {\n    if let Some(context) = &*SERVER_CORE_CONTEXT.read() {\n        context.report_present(\n            Duration::from_nanos(timestamp_ns),\n            Duration::from_nanos(offset_ns),\n        );\n    }\n}\n\nextern \"C\" fn wait_for_vsync() {\n    // Default 120Hz-ish wait if StatisticsManager isn't up.\n    // We use 120Hz-ish so that SteamVR doesn't accidentally get\n    // any weird ideas about our display Hz with its frame pacing.\n    static PRE_HEADSET_STATS_WAIT_INTERVAL: Duration = Duration::from_millis(8);\n\n    // NB: don't sleep while locking SERVER_DATA_MANAGER or SERVER_CORE_CONTEXT\n    let sleep_duration = SERVER_CORE_CONTEXT\n        .read()\n        .as_ref()\n        .and_then(|ctx| ctx.duration_until_next_vsync());\n\n    if let Some(duration) = sleep_duration {\n        if alvr_server_core::settings()\n            .video\n            .enforce_server_frame_pacing\n        {\n            thread::sleep(duration);\n        } else {\n            thread::yield_now();\n        }\n    } else {\n        // StatsManager isn't up because the headset hasn't connected,\n        // safety fallback to prevent deadlocking.\n        thread::sleep(PRE_HEADSET_STATS_WAIT_INTERVAL);\n    }\n}\n\npub extern \"C\" fn shutdown_driver() {\n    SERVER_CORE_CONTEXT.write().take();\n}\n\n/// This is the SteamVR/OpenVR entry point\n/// # Safety\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn HmdDriverFactory(\n    interface_name: *const c_char,\n    return_code: *mut i32,\n) -> *mut c_void {\n    let Ok(driver_dir) = alvr_server_io::get_driver_dir_from_registered() else {\n        return ptr::null_mut();\n    };\n    let Some(filesystem_layout) =\n        alvr_filesystem::filesystem_layout_from_openvr_driver_root_dir(&driver_dir)\n    else {\n        return ptr::null_mut();\n    };\n\n    let dashboard_process_paths = sysinfo::System::new_all()\n        .processes_by_name(OsStr::new(&afs::dashboard_fname()))\n        .filter_map(|proc| Some(proc.exe()?.to_owned()))\n        .collect::<Vec<_>>();\n\n    // Check that there is no active dashboard instance not part of this driver installation\n    // Note: if the iterator is empty, `all()` returns true\n    if !dashboard_process_paths\n        .iter()\n        .all(|path| *path == filesystem_layout.dashboard_exe())\n    {\n        return ptr::null_mut();\n    }\n\n    static ONCE: Once = Once::new();\n    ONCE.call_once(move || {\n        alvr_server_core::initialize_environment(filesystem_layout.clone());\n\n        let log_to_disk = alvr_server_core::settings().extra.logging.log_to_disk;\n\n        alvr_server_core::init_logging(\n            log_to_disk.then(|| filesystem_layout.session_log()),\n            Some(filesystem_layout.crash_log()),\n        );\n\n        unsafe {\n            g_sessionPath = CString::new(filesystem_layout.session().to_string_lossy().to_string())\n                .unwrap()\n                .into_raw();\n            g_driverRootDir = CString::new(\n                filesystem_layout\n                    .openvr_driver_root_dir\n                    .to_string_lossy()\n                    .to_string(),\n            )\n            .unwrap()\n            .into_raw();\n        };\n\n        graphics::initialize_shaders();\n\n        unsafe {\n            LogError = Some(alvr_server_core::alvr_error);\n            LogWarn = Some(alvr_server_core::alvr_warn);\n            LogInfo = Some(alvr_server_core::alvr_info);\n            LogDebug = Some(alvr_server_core::alvr_dbg_server_impl);\n            LogEncoder = Some(alvr_server_core::alvr_dbg_encoder);\n            LogPeriodically = Some(alvr_server_core::alvr_log_periodically);\n            PathStringToHash = Some(alvr_server_core::alvr_path_to_id);\n            GetSerialNumber = Some(props::get_serial_number);\n            SetOpenvrProps = Some(props::set_device_openvr_props);\n            RegisterButtons = Some(register_buttons);\n            DriverReadyIdle = Some(driver_ready_idle);\n            HapticsSend = Some(send_haptics);\n            SetVideoConfigNals = Some(set_video_config_nals);\n            VideoSend = Some(send_video);\n            GetDynamicEncoderParams = Some(get_dynamic_encoder_params);\n            ReportComposed = Some(report_composed);\n            ReportPresent = Some(report_present);\n            WaitForVSync = Some(wait_for_vsync);\n            ShutdownRuntime = Some(shutdown_driver);\n\n            // When there is already a ALVR dashboard running, initialize the HMD device early to\n            // avoid buggy SteamVR behavior\n            // NB: we already bail out before if the dashboards don't belong to this streamer\n            let early_hmd_initialization = !dashboard_process_paths.is_empty();\n\n            CppInit(early_hmd_initialization);\n        }\n\n        let (context, events_receiver) = ServerCoreContext::new();\n\n        *SERVER_CORE_CONTEXT.write() = Some(context);\n\n        event_loop(events_receiver);\n    });\n\n    unsafe { CppOpenvrEntryPoint(interface_name, return_code) }\n}\n"
  },
  {
    "path": "alvr/server_openvr/src/props.rs",
    "content": "// Note: many properties are missing or are stubs.\n// todo: fill out more properties for headset and controllers\n// todo: add more emulation modes\n\nuse crate::{\n    FfiOpenvrProperty, FfiOpenvrPropertyType_Bool, FfiOpenvrPropertyType_Double,\n    FfiOpenvrPropertyType_Float, FfiOpenvrPropertyType_Int32, FfiOpenvrPropertyType_String,\n    FfiOpenvrPropertyType_Uint64, FfiOpenvrPropertyType_Vector3, FfiOpenvrPropertyValue,\n};\nuse alvr_common::{\n    BODY_CHEST_ID, BODY_HIPS_ID, BODY_LEFT_ELBOW_ID, BODY_LEFT_FOOT_ID, BODY_LEFT_KNEE_ID,\n    BODY_RIGHT_ELBOW_ID, BODY_RIGHT_FOOT_ID, BODY_RIGHT_KNEE_ID, DEVICE_ID_TO_PATH, HAND_LEFT_ID,\n    HAND_RIGHT_ID, HAND_TRACKER_LEFT_ID, HAND_TRACKER_RIGHT_ID, HEAD_ID, debug,\n    settings_schema::Switch, warn,\n};\nuse alvr_session::{\n    ControllersEmulationMode, HeadsetEmulationMode, OpenvrPropKey, OpenvrPropType, OpenvrProperty,\n};\nuse std::{\n    ffi::{CString, c_char, c_void},\n    ptr,\n};\n\npub fn set_openvr_prop(instance_ptr: Option<*mut c_void>, device_id: u64, prop: OpenvrProperty) {\n    let key = prop.key as u32;\n    let ty = alvr_session::openvr_prop_key_to_type(prop.key);\n    let value = prop.value;\n\n    let device_name = DEVICE_ID_TO_PATH.get(&device_id).unwrap_or(&\"Unknown\");\n\n    let (type_, maybe_value) = match ty {\n        OpenvrPropType::Bool => (\n            FfiOpenvrPropertyType_Bool,\n            value\n                .parse::<bool>()\n                .ok()\n                .map(|bool_| FfiOpenvrPropertyValue {\n                    bool_: bool_.into(),\n                }),\n        ),\n        OpenvrPropType::Float => (\n            FfiOpenvrPropertyType_Float,\n            value\n                .parse::<f32>()\n                .ok()\n                .map(|float_| FfiOpenvrPropertyValue { float_ }),\n        ),\n        OpenvrPropType::Int32 => (\n            FfiOpenvrPropertyType_Int32,\n            value\n                .parse::<i32>()\n                .ok()\n                .map(|int32| FfiOpenvrPropertyValue { int32 }),\n        ),\n        OpenvrPropType::Uint64 => (\n            FfiOpenvrPropertyType_Uint64,\n            value\n                .parse::<u64>()\n                .ok()\n                .map(|uint64| FfiOpenvrPropertyValue { uint64 }),\n        ),\n        OpenvrPropType::Vector3 => (\n            FfiOpenvrPropertyType_Vector3,\n            serde_json::from_str::<[f32; 3]>(&value)\n                .ok()\n                .map(|vector3| FfiOpenvrPropertyValue { vector3 }),\n        ),\n        OpenvrPropType::Double => (\n            FfiOpenvrPropertyType_Double,\n            value\n                .parse::<f64>()\n                .ok()\n                .map(|double_| FfiOpenvrPropertyValue { double_ }),\n        ),\n        OpenvrPropType::String => {\n            let c_string = CString::new(value.clone()).unwrap();\n            let mut string = [0; 256];\n\n            unsafe {\n                ptr::copy_nonoverlapping(\n                    c_string.as_ptr(),\n                    string.as_mut_ptr(),\n                    c_string.as_bytes_with_nul().len(),\n                );\n            }\n\n            (\n                FfiOpenvrPropertyType_String,\n                Some(FfiOpenvrPropertyValue { string }),\n            )\n        }\n    };\n\n    let Some(ffi_value) = maybe_value else {\n        warn!(\n            \"Failed to parse {device_name} value for OpenVR property: {:?}={value}\",\n            prop.key\n        );\n\n        return;\n    };\n\n    debug!(\"Setting {device_name} OpenVR prop: {:?}={value}\", prop.key);\n\n    let ffi_prop = FfiOpenvrProperty {\n        key,\n        type_,\n        value: ffi_value,\n    };\n\n    if let Some(instance_ptr) = instance_ptr {\n        unsafe { crate::SetOpenvrProperty(instance_ptr, ffi_prop) }\n    } else {\n        unsafe { crate::SetOpenvrPropByDeviceID(device_id, ffi_prop) }\n    }\n}\n\nfn serial_number(device_id: u64) -> String {\n    let settings = alvr_server_core::settings();\n\n    if device_id == *HEAD_ID {\n        match &settings.headset.emulation_mode {\n            HeadsetEmulationMode::RiftS => \"1WMGH000XX0000\".into(),\n            HeadsetEmulationMode::Quest1 => \"1PASH0X0X00000\".into(),\n            HeadsetEmulationMode::Quest2 => \"1WMHH000X00000\".into(),\n            HeadsetEmulationMode::QuestPro => \"230YC0XXXX00XX\".into(),\n            HeadsetEmulationMode::Pico4 => \"VRLINKHMDPICO4\".into(),\n            HeadsetEmulationMode::Vive => \"HTCVive-001\".into(),\n            HeadsetEmulationMode::Custom { serial_number, .. } => serial_number.clone(),\n        }\n    } else if device_id == *HAND_LEFT_ID || device_id == *HAND_RIGHT_ID {\n        if let Switch::Enabled(controllers) = &settings.headset.controllers {\n            let serial_number = match &controllers.emulation_mode {\n                ControllersEmulationMode::Quest1Touch => \"1PALCXXXX00000_Controller\", // 1PALCLC left, 1PALCRC right\n                ControllersEmulationMode::Quest2Touch => \"1WMHH000X00000_Controller\",\n                ControllersEmulationMode::Quest3Plus => \"2G0YXX0X0000XX_Controller\", // 2G0YY Left 2G0YZ Right\n                ControllersEmulationMode::QuestPro => \"230YXXXXXXXXXX_Controller\", // 230YT left, 230YV right\n                ControllersEmulationMode::RiftSTouch\n                | ControllersEmulationMode::Pico4\n                | ControllersEmulationMode::PSVR2Sense\n                | ControllersEmulationMode::ValveIndex\n                | ControllersEmulationMode::ViveWand\n                | ControllersEmulationMode::ViveTracker => \"ALVR Remote Controller\",\n                ControllersEmulationMode::Custom { serial_number, .. } => serial_number,\n            };\n\n            if device_id == *HAND_LEFT_ID {\n                format!(\"{serial_number}_Left\")\n            } else {\n                format!(\"{serial_number}_Right\")\n            }\n        } else {\n            \"Unknown\".into()\n        }\n    } else if device_id == *HAND_TRACKER_LEFT_ID {\n        \"ALVR_Left_Hand_Full_Skeletal\".into()\n    } else if device_id == *HAND_TRACKER_RIGHT_ID {\n        \"ALVR_Right_Hand_Full_Skeletal\".into()\n    } else if device_id == *BODY_CHEST_ID {\n        \"ALVR Tracker (chest)\".into()\n    } else if device_id == *BODY_HIPS_ID {\n        \"ALVR Tracker (waist)\".into()\n    } else if device_id == *BODY_LEFT_ELBOW_ID {\n        \"ALVR Tracker (left elbow)\".into()\n    } else if device_id == *BODY_RIGHT_ELBOW_ID {\n        \"ALVR Tracker (right elbow)\".into()\n    } else if device_id == *BODY_LEFT_KNEE_ID {\n        \"ALVR Tracker (left knee)\".into()\n    } else if device_id == *BODY_RIGHT_KNEE_ID {\n        \"ALVR Tracker (right knee)\".into()\n    } else if device_id == *BODY_LEFT_FOOT_ID {\n        \"ALVR Tracker (left foot)\".into()\n    } else if device_id == *BODY_RIGHT_FOOT_ID {\n        \"ALVR Tracker (right foot)\".into()\n    } else {\n        \"Unknown\".into()\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn get_serial_number(device_id: u64, out_str: *mut c_char) -> u64 {\n    let string = serial_number(device_id);\n\n    let cstring = CString::new(string).unwrap();\n\n    let len = cstring.to_bytes_with_nul().len();\n\n    if !out_str.is_null() {\n        unsafe { ptr::copy_nonoverlapping(cstring.as_ptr(), out_str, len) };\n    }\n\n    len as u64\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn set_device_openvr_props(instance_ptr: *mut c_void, device_id: u64) {\n    #[expect(clippy::enum_glob_use)]\n    use OpenvrPropKey::*;\n\n    let settings = alvr_server_core::settings();\n\n    let set_prop = |key, value: &str| {\n        set_openvr_prop(\n            Some(instance_ptr),\n            device_id,\n            OpenvrProperty {\n                key,\n                value: value.into(),\n            },\n        );\n    };\n\n    let set_icons = |base_path: &str| {\n        set_prop(\n            NamedIconPathDeviceOffString,\n            format!(\"{base_path}_off.png\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceSearchingString,\n            format!(\"{base_path}_searching.gif\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceSearchingAlertString,\n            format!(\"{base_path}_searching_alert.gif\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceReadyString,\n            format!(\"{base_path}_ready.png\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceReadyAlertString,\n            format!(\"{base_path}_ready_alert.png\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceAlertLowString,\n            format!(\"{base_path}_ready_low.png\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceStandbyString,\n            format!(\"{base_path}_standby.png\").as_str(),\n        );\n        set_prop(\n            NamedIconPathDeviceStandbyAlertString,\n            format!(\"{base_path}_standby_alert.gif\").as_str(),\n        );\n    };\n\n    let device_serial = &serial_number(device_id);\n    let headset_serial = &serial_number(*HEAD_ID);\n\n    if device_id == *HEAD_ID {\n        // Closure for all the common Quest headset properties\n        let set_oculus_common_headset_props = || {\n            set_prop(\n                RegisteredDeviceTypeString,\n                format!(\"oculus/{headset_serial}\").as_str(),\n            );\n            set_icons(\"{oculus}/icons/quest_headset\");\n        };\n\n        // Per-device props\n        match &settings.headset.emulation_mode {\n            HeadsetEmulationMode::RiftS => {\n                set_prop(TrackingSystemNameString, \"oculus\");\n                set_prop(ModelNumberString, \"Oculus Rift S\");\n                set_prop(ManufacturerNameString, \"Oculus\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(DriverVersionString, \"1.42.0\");\n                set_icons(\"{oculus}/icons/rifts_headset\");\n            }\n            HeadsetEmulationMode::Quest1 => {\n                set_prop(TrackingSystemNameString, \"oculus\");\n                set_prop(ModelNumberString, \"Oculus Quest\");\n                set_prop(ManufacturerNameString, \"Oculus\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(DriverVersionString, \"1.111.0\");\n                set_oculus_common_headset_props();\n            }\n            HeadsetEmulationMode::Quest2 => {\n                set_prop(TrackingSystemNameString, \"oculus\");\n                set_prop(ModelNumberString, \"Miramar\");\n                set_prop(ManufacturerNameString, \"Oculus\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(DriverVersionString, \"1.55.0\");\n                set_oculus_common_headset_props();\n            }\n            HeadsetEmulationMode::QuestPro => {\n                set_prop(TrackingSystemNameString, \"oculus\");\n                set_prop(ModelNumberString, \"Meta Quest Pro\");\n                set_prop(ManufacturerNameString, \"Oculus\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(DriverVersionString, \"1.55.0\");\n                set_oculus_common_headset_props();\n            }\n            HeadsetEmulationMode::Pico4 => {\n                set_prop(TrackingSystemNameString, \"vrlink\");\n                set_prop(ModelNumberString, \"PICO 4\");\n                set_prop(ManufacturerNameString, \"ByteDance\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(RegisteredDeviceTypeString, \"pico\");\n                set_prop(DriverVersionString, \"\");\n                set_icons(\"{vrlink}/icons/headset_pico4\");\n            }\n            HeadsetEmulationMode::Vive => {\n                set_prop(TrackingSystemNameString, \"Vive Tracker\");\n                set_prop(ModelNumberString, \"ALVR driver server\");\n                set_prop(ManufacturerNameString, \"HTC\");\n                set_prop(RenderModelNameString, \"generic_hmd\");\n                set_prop(RegisteredDeviceTypeString, \"vive\");\n                set_prop(DriverVersionString, \"\");\n                set_icons(\"{htc}/icons/vive_headset\");\n            }\n            HeadsetEmulationMode::Custom { .. } => (),\n        }\n\n        set_prop(UserIpdMetersFloat, \"0.063\");\n        set_prop(UserHeadToEyeDepthMetersFloat, \"0.0\");\n        set_prop(SecondsFromVsyncToPhotonsFloat, \"0.0\");\n\n        // return a constant that's not 0 (invalid) or 1 (reserved for Oculus)\n        set_prop(CurrentUniverseIdUint64, \"2\");\n\n        if cfg!(windows) {\n            // avoid \"not fullscreen\" warnings from vrmonitor\n            set_prop(IsOnDesktopBool, \"false\");\n\n            // We let SteamVR handle VSyncs. We just wait in PostPresent().\n            set_prop(DriverDirectModeSendsVsyncEventsBool, \"false\");\n        }\n        set_prop(DeviceProvidesBatteryStatusBool, \"true\");\n        set_prop(ContainsProximitySensorBool, \"true\");\n\n        for prop in &settings.headset.extra_openvr_props {\n            set_prop(prop.key, &prop.value);\n        }\n    } else if device_id == *HAND_LEFT_ID\n        || device_id == *HAND_RIGHT_ID\n        || device_id == *HAND_TRACKER_LEFT_ID\n        || device_id == *HAND_TRACKER_RIGHT_ID\n    {\n        let left_hand = device_id == *HAND_LEFT_ID || device_id == *HAND_TRACKER_LEFT_ID;\n        let right_hand = device_id == *HAND_RIGHT_ID || device_id == *HAND_TRACKER_RIGHT_ID;\n        let full_skeletal_hand =\n            device_id == *HAND_TRACKER_LEFT_ID || device_id == *HAND_TRACKER_RIGHT_ID;\n        if let Switch::Enabled(config) = &settings.headset.controllers {\n            // Closure for all the common Oculus/Meta controller properties\n            let set_oculus_common_props = || {\n                set_prop(TrackingSystemNameString, \"oculus\");\n\n                set_prop(ControllerTypeString, \"oculus_touch\");\n                set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                if left_hand {\n                    set_prop(\n                        RegisteredDeviceTypeString,\n                        format!(\"oculus/{headset_serial}_Controller_Left\").as_str(),\n                    );\n                    set_icons(\"{oculus}/icons/rifts_left_controller\");\n                } else if right_hand {\n                    set_prop(\n                        RegisteredDeviceTypeString,\n                        format!(\"oculus/{headset_serial}_Controller_Right\").as_str(),\n                    );\n                    set_icons(\"{oculus}/icons/rifts_right_controller\");\n                }\n            };\n\n            // Controller-specific properties, not shared\n            match config.emulation_mode {\n                ControllersEmulationMode::RiftSTouch => {\n                    set_prop(ManufacturerNameString, \"Oculus\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Oculus Rift S (Left Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_rifts_controller_left\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Oculus Rift S (Right Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_rifts_controller_right\");\n                    }\n                    set_prop(ControllerTypeString, \"oculus_touch\");\n                    set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                    set_oculus_common_props();\n                }\n                ControllersEmulationMode::Quest1Touch => {\n                    set_prop(ManufacturerNameString, \"Oculus\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Oculus Quest (Left Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_controller_left\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Oculus Quest (Right Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_controller_right\");\n                    }\n                    set_prop(ControllerTypeString, \"oculus_touch\");\n                    set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                    set_oculus_common_props();\n                }\n                ControllersEmulationMode::Quest2Touch => {\n                    set_prop(ManufacturerNameString, \"Oculus\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Miramar (Left Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest2_controller_left\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Miramar (Right Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest2_controller_right\");\n                    }\n                    set_prop(ControllerTypeString, \"oculus_touch\");\n                    set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                    set_oculus_common_props();\n                }\n                ControllersEmulationMode::Quest3Plus => {\n                    set_prop(ManufacturerNameString, \"Meta\");\n\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Meta Quest 3 (Left Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_plus_controller_left\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Meta Quest 3 (Right Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_plus_controller_right\");\n                    }\n                    set_prop(ControllerTypeString, \"oculus_touch\");\n                    set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                    set_oculus_common_props();\n                }\n                ControllersEmulationMode::QuestPro => {\n                    set_prop(ManufacturerNameString, \"Meta\");\n\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Meta Quest Pro (Left Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_pro_controller_left\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Meta Quest Pro (Right Controller)\");\n                        set_prop(RenderModelNameString, \"oculus_quest_pro_controller_right\");\n                    }\n                    set_oculus_common_props();\n                }\n                ControllersEmulationMode::Pico4 => {\n                    set_prop(TrackingSystemNameString, \"vrlink\");\n                    set_prop(ManufacturerNameString, \"ByteDance\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"PICO 4 (Left Controller)\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{vrlink}/rendermodels/pico_4_controller_left\",\n                        );\n                        set_icons(\"{vrlink}/icons/left_pico4\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"PICO 4 (Right Controller)\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{vrlink}/rendermodels/pico_4_controller_right\",\n                        );\n                        set_icons(\"{vrlink}/icons/right_pico4\");\n                    }\n                    set_prop(ControllerTypeString, \"pico_controller\");\n                    set_prop(\n                        InputProfilePathString,\n                        \"{vrlink}/input/pico_controller_profile.json\",\n                    );\n                }\n                ControllersEmulationMode::PSVR2Sense => {\n                    set_prop(TrackingSystemNameString, \"playstation_vr2\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"PlayStation VR2 Sense Left\");\n                        set_prop(SerialNumberString, \"playstation_vr2_sense_controller_left\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{alvr_server}/rendermodels/playstation_vr2_sense_left\",\n                        );\n                        set_icons(\"{alvr_server}/icons/left_controller_status\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"PlayStation VR2 Sense Right\");\n                        set_prop(SerialNumberString, \"playstation_vr2_sense_controller_right\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{alvr_server}/rendermodels/playstation_vr2_sense_right\",\n                        );\n                        set_icons(\"{alvr_server}/icons/right_controller_status\");\n                    }\n                    set_prop(TrackingFirmwareVersionString, \"0303\");\n                    set_prop(HardwareRevisionString, \"MP\");\n                    set_prop(ControllerTypeString, \"playstation_vr2_sense\");\n                    set_prop(\n                        InputProfilePathString,\n                        \"{alvr_server}/input/playstation_vr2_sense_controller_profile.json\",\n                    );\n                }\n                ControllersEmulationMode::ValveIndex => {\n                    set_prop(TrackingSystemNameString, \"indexcontroller\");\n                    set_prop(ManufacturerNameString, \"Valve\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Knuckles (Left Controller)\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{indexcontroller}valve_controller_knu_1_0_left\",\n                        );\n                        set_prop(\n                            RegisteredDeviceTypeString,\n                            \"valve/index_controllerLHR-E217CD00_Left\",\n                        );\n                        set_icons(\"{indexcontroller}/icons/left_controller_status\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Knuckles (Right Controller)\");\n                        set_prop(\n                            RenderModelNameString,\n                            \"{indexcontroller}valve_controller_knu_1_0_right\",\n                        );\n                        set_prop(\n                            RegisteredDeviceTypeString,\n                            \"valve/index_controllerLHR-E217CD00_Right\",\n                        );\n                        set_icons(\"{indexcontroller}/icons/right_controller_status\");\n                    }\n                    set_prop(ControllerTypeString, \"knuckles\");\n                    set_prop(\n                        InputProfilePathString,\n                        \"{indexcontroller}/input/index_controller_profile.json\",\n                    );\n                }\n                ControllersEmulationMode::ViveWand => {\n                    set_prop(TrackingSystemNameString, \"htc\");\n                    set_prop(ManufacturerNameString, \"HTC\");\n                    set_prop(RenderModelNameString, \"vr_controller_vive_1_5\");\n                    if left_hand {\n                        set_prop(\n                            ModelNumberString,\n                            \"ALVR Remote Controller (Left Controller)\",\n                        );\n                        set_prop(RegisteredDeviceTypeString, \"htc/vive_controller_Left\");\n                    } else if right_hand {\n                        set_prop(\n                            ModelNumberString,\n                            \"ALVR Remote Controller (Right Controller)\",\n                        );\n                        set_prop(RegisteredDeviceTypeString, \"htc/vive_controller_Right\");\n                    }\n                    set_prop(ControllerTypeString, \"vive_controller\");\n                    set_prop(InputProfilePathString, \"{oculus}/input/touch_profile.json\");\n                    set_icons(\"{htc}/icons/controller\");\n                }\n                ControllersEmulationMode::ViveTracker => {\n                    set_prop(TrackingSystemNameString, \"lighthouse\");\n                    set_prop(RenderModelNameString, \"{htc}vr_tracker_vive_1_0\");\n                    if left_hand {\n                        set_prop(ModelNumberString, \"Vive Tracker Pro MV (Left Controller)\");\n                        set_prop(RegisteredDeviceTypeString, \"ALVR/tracker/left_foot\");\n                        set_prop(ControllerTypeString, \"vive_tracker_left_foot\");\n                    } else if right_hand {\n                        set_prop(ModelNumberString, \"Vive Tracker Pro MV (Right Controller)\");\n                        set_prop(RegisteredDeviceTypeString, \"ALVR/tracker/right_foot\");\n                        set_prop(ControllerTypeString, \"vive_tracker_right_foot\");\n                    }\n                    set_prop(\n                        InputProfilePathString,\n                        \"{htc}/input/vive_tracker_profile.json\",\n                    );\n                    set_icons(\"{htc}/icons/tracker\");\n\n                    // All of these property values were dumped from real a vive tracker via\n                    // https://github.com/SDraw/openvr_dumper and were copied from\n                    // https://github.com/SDraw/driver_kinectV2\n                    set_prop(ResourceRootString, \"htc\");\n                    set_prop(WillDriftInYawBool, \"false\");\n                    set_prop(\n                        TrackingFirmwareVersionString,\n                        \"1541800000 RUNNER-WATCHMAN$runner-watchman@runner-watchman 2018-01-01 FPGA 512(2.56/0/0) BL 0 VRC 1541800000 Radio 1518800000\",\n                    );\n                    set_prop(\n                        HardwareRevisionString,\n                        \"product 128 rev 2.5.6 lot 2000/0/0 0\",\n                    );\n                    set_prop(ConnectedWirelessDongleString, \"D0000BE000\");\n                    set_prop(DeviceIsWirelessBool, \"true\");\n                    set_prop(DeviceIsChargingBool, \"false\");\n                    set_prop(ControllerHandSelectionPriorityInt32, \"-1\");\n                    // vr::HmdMatrix34_t l_transform = {\n                    //     {{-1.f, 0.f, 0.f, 0.f}, {0.f, 0.f, -1.f, 0.f}, {0.f, -1.f, 0.f, 0.f}}};\n                    // vr_properties->SetProperty(this->prop_container,\n                    //                            vr::Prop_StatusDisplayTransform_Matrix34,\n                    //                            &l_transform,\n                    //                            sizeof(vr::HmdMatrix34_t),\n                    //                            vr::k_unHmdMatrix34PropertyTag);\n                    set_prop(FirmwareUpdateAvailableBool, \"false\");\n                    set_prop(FirmwareManualUpdateBool, \"false\");\n                    set_prop(\n                        FirmwareManualUpdateURLString,\n                        \"https://developer.valvesoftware.com/wiki/SteamVR/HowTo_Update_Firmware\",\n                    );\n                    set_prop(HardwareRevisionUint64, \"2214720000\");\n                    set_prop(FirmwareVersionUint64, \"1541800000\");\n                    set_prop(FPGAVersionUint64, \"512\");\n                    set_prop(VRCVersionUint64, \"1514800000\");\n                    set_prop(RadioVersionUint64, \"1518800000\");\n                    set_prop(DongleVersionUint64, \"8933539758\");\n                    set_prop(DeviceCanPowerOffBool, \"true\");\n                    // vr_properties->SetStringProperty(this->prop_container,\n                    //                                  vr::Prop_Firmware_ProgrammingTargetString,\n                    //                                  GetSerialNumber().c_str());\n                    set_prop(FirmwareForceUpdateRequiredBool, device_serial);\n                    set_prop(FirmwareRemindUpdateBool, \"false\");\n                    set_prop(HasDisplayComponentBool, \"false\");\n                    set_prop(HasCameraComponentBool, \"false\");\n                    set_prop(HasDriverDirectModeComponentBool, \"false\");\n                    set_prop(HasVirtualDisplayComponentBool, \"false\");\n                }\n                ControllersEmulationMode::Custom { .. } => {}\n            }\n\n            set_prop(SerialNumberString, device_serial);\n            set_prop(AttachedDeviceIdString, device_serial);\n\n            if full_skeletal_hand {\n                set_prop(TrackingSystemNameString, \"vrlink\");\n                set_prop(ManufacturerNameString, \"VRLink\");\n\n                set_prop(RenderModelNameString, \"{vrlink}/rendermodels/shuttlecock\");\n                set_prop(ControllerTypeString, \"svl_hand_interaction_augmented\");\n                set_prop(\n                    InputProfilePathString,\n                    \"{vrlink}/input/svl_hand_interaction_augmented_input_profile.json\",\n                );\n\n                if left_hand {\n                    set_prop(ModelNumberString, \"VRLink Hand Tracker (Left Hand)\");\n                    set_prop(\n                        RegisteredDeviceTypeString,\n                        \"vrlink/VRLINKQ_HandTracker_Left\",\n                    );\n                    set_prop(SerialNumberString, \"VRLINKQ_Hand_Left\");\n                    set_prop(AttachedDeviceIdString, \"VRLINKQ_Hand_Left\");\n                    set_icons(\"{vrlink}/icons/left_handtracking\");\n                } else if right_hand {\n                    set_prop(ModelNumberString, \"VRLink Hand Tracker (Right Hand)\");\n                    set_prop(\n                        RegisteredDeviceTypeString,\n                        \"vrlink/VRLINKQ_HandTracker_Right\",\n                    );\n                    set_prop(SerialNumberString, \"VRLINKQ_Hand_Right\");\n                    set_prop(AttachedDeviceIdString, \"VRLINKQ_Hand_Right\");\n                    set_icons(\"{vrlink}/icons/right_handtracking\");\n                }\n            }\n\n            set_prop(\n                SupportedButtonsUint64,\n                0xFFFFFFFFFFFFFFFF_u64.to_string().as_str(),\n            );\n\n            // OpenXR does not support controller battery\n            set_prop(DeviceProvidesBatteryStatusBool, \"false\");\n\n            // k_eControllerAxis_Joystick = 2\n            set_prop(Axis0TypeInt32, \"2\");\n\n            if matches!(config.emulation_mode, ControllersEmulationMode::ViveTracker) {\n                // TrackedControllerRole_Invalid\n                set_prop(ControllerRoleHintInt32, \"0\");\n            } else if left_hand {\n                // TrackedControllerRole_LeftHand\n                set_prop(ControllerRoleHintInt32, \"1\");\n            } else if right_hand {\n                // TrackedControllerRole_RightHand\n                set_prop(ControllerRoleHintInt32, \"2\");\n            }\n\n            for prop in &config.extra_openvr_props {\n                set_prop(prop.key, &prop.value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "alvr/server_openvr/src/tracking.rs",
    "content": "use crate::{FfiDeviceMotion, FfiFov, FfiHandSkeleton, FfiPose, FfiQuat, FfiViewParams};\nuse alvr_common::{\n    BODY_CHEST_ID, BODY_HIPS_ID, BODY_LEFT_ELBOW_ID, BODY_LEFT_FOOT_ID, BODY_LEFT_KNEE_ID,\n    BODY_RIGHT_ELBOW_ID, BODY_RIGHT_FOOT_ID, BODY_RIGHT_KNEE_ID, DeviceMotion, Fov, HAND_LEFT_ID,\n    HAND_RIGHT_ID, Pose, ViewParams,\n    glam::{EulerRot, Quat, Vec3},\n    settings_schema::Switch,\n};\nuse alvr_session::{ControllersConfig, HeadsetConfig};\nuse std::{\n    f32::consts::{FRAC_PI_2, PI},\n    sync::LazyLock,\n};\n\nconst DEG_TO_RAD: f32 = PI / 180.0;\n\npub static BODY_TRACKER_IDS: LazyLock<[u64; 8]> = LazyLock::new(|| {\n    [\n        // Upper body\n        *BODY_CHEST_ID,\n        *BODY_HIPS_ID,\n        *BODY_LEFT_ELBOW_ID,\n        *BODY_RIGHT_ELBOW_ID,\n        // Legs\n        *BODY_LEFT_KNEE_ID,\n        *BODY_LEFT_FOOT_ID,\n        *BODY_RIGHT_KNEE_ID,\n        *BODY_RIGHT_FOOT_ID,\n    ]\n});\n\nfn to_ffi_fov(fov: Fov) -> FfiFov {\n    FfiFov {\n        left: fov.left,\n        right: fov.right,\n        up: fov.up,\n        down: fov.down,\n    }\n}\n\nfn to_ffi_quat(quat: Quat) -> FfiQuat {\n    FfiQuat {\n        x: quat.x,\n        y: quat.y,\n        z: quat.z,\n        w: quat.w,\n    }\n}\n\nfn to_ffi_pose(pose: Pose) -> FfiPose {\n    FfiPose {\n        orientation: to_ffi_quat(pose.orientation),\n        position: pose.position.to_array(),\n    }\n}\n\npub fn to_ffi_motion(device_id: u64, motion: DeviceMotion) -> FfiDeviceMotion {\n    FfiDeviceMotion {\n        deviceID: device_id,\n        pose: to_ffi_pose(motion.pose),\n        linearVelocity: motion.linear_velocity.to_array(),\n        angularVelocity: motion.angular_velocity.to_array(),\n    }\n}\n\npub fn to_ffi_view_params(params: ViewParams) -> FfiViewParams {\n    FfiViewParams {\n        pose: to_ffi_pose(params.pose),\n        fov: to_ffi_fov(params.fov),\n    }\n}\n\nfn get_hand_skeleton_offsets(config: &HeadsetConfig) -> (Pose, Pose) {\n    let left_offset;\n    let right_offset;\n    if let Switch::Enabled(controllers) = &config.controllers {\n        let t = controllers.left_hand_tracking_position_offset;\n        let r = controllers.left_hand_tracking_rotation_offset;\n\n        left_offset = Pose {\n            orientation: Quat::from_euler(\n                EulerRot::XYZ,\n                r[0] * DEG_TO_RAD,\n                r[1] * DEG_TO_RAD,\n                r[2] * DEG_TO_RAD,\n            ),\n            position: Vec3::new(t[0], t[1], t[2]),\n        };\n        right_offset = Pose {\n            orientation: Quat::from_euler(\n                EulerRot::XYZ,\n                r[0] * DEG_TO_RAD,\n                -r[1] * DEG_TO_RAD,\n                -r[2] * DEG_TO_RAD,\n            ),\n            position: Vec3::new(-t[0], t[1], t[2]),\n        };\n    } else {\n        left_offset = Pose::IDENTITY;\n        right_offset = Pose::IDENTITY;\n    }\n\n    (left_offset, right_offset)\n}\n\nfn to_ffi_skeleton(skeleton: &[Pose; 31]) -> FfiHandSkeleton {\n    FfiHandSkeleton {\n        jointRotations: skeleton\n            .iter()\n            .map(|j| to_ffi_quat(j.orientation))\n            .collect::<Vec<_>>()\n            .try_into()\n            .unwrap(),\n        jointPositions: skeleton\n            .iter()\n            .map(|j| j.position.to_array())\n            .collect::<Vec<_>>()\n            .try_into()\n            .unwrap(),\n    }\n}\n\npub fn to_openvr_ffi_hand_skeleton(\n    config: &HeadsetConfig,\n    device_id: u64,\n    hand_skeleton: &[Pose; 26],\n) -> FfiHandSkeleton {\n    let (left_hand_skeleton_offset, right_hand_skeleton_offset) = get_hand_skeleton_offsets(config);\n    let id = device_id;\n\n    let pose_offset = if id == *HAND_LEFT_ID {\n        left_hand_skeleton_offset\n    } else {\n        right_hand_skeleton_offset\n    };\n\n    // global joints\n    let gj = hand_skeleton;\n\n    // Correct the orientation for auxiliary bones.\n    pub fn aux_orientation(id: u64, pose: Pose) -> Pose {\n        let o = pose.orientation;\n        let p = pose.position;\n\n        // Convert to SteamVR basis orientations\n        let (orientation, position) = if id == *HAND_LEFT_ID {\n            (\n                Quat::from_xyzw(o.x, o.y, o.z, o.w)\n                    * Quat::from_euler(EulerRot::YXZ, -FRAC_PI_2, FRAC_PI_2, 0.0),\n                Vec3::new(p.x, p.y, p.z),\n            )\n        } else {\n            (\n                Quat::from_xyzw(o.x, o.y, o.z, o.w)\n                    * Quat::from_euler(EulerRot::YXZ, FRAC_PI_2, -FRAC_PI_2, 0.0),\n                Vec3::new(p.x, p.y, p.z),\n            )\n        };\n\n        Pose {\n            orientation,\n            position,\n        }\n    }\n\n    // Convert from global to local joint pose. The orientation frame of reference is also\n    // converted from OpenXR to SteamVR (hand-specific!)\n    pub fn local_pose(id: u64, parent: Pose, current: Pose) -> Pose {\n        let o = parent.orientation.conjugate() * current.orientation;\n        let p = parent.orientation.conjugate() * (current.position - parent.position);\n\n        // Convert to SteamVR frame of reference\n        let (orientation, position) = if id == *HAND_LEFT_ID {\n            (\n                Quat::from_xyzw(-o.z, -o.y, -o.x, o.w),\n                Vec3::new(-p.z, -p.y, -p.x),\n            )\n        } else {\n            (\n                Quat::from_xyzw(o.z, o.y, -o.x, o.w),\n                Vec3::new(p.z, p.y, -p.x),\n            )\n        };\n\n        Pose {\n            orientation,\n            position,\n        }\n    }\n\n    // Adjust hand position based on the emulated controller for joints\n    // parented to the root.\n    let root_parented_pose = |pose: Pose| -> Pose {\n        let sign = if id == *HAND_LEFT_ID { -1.0 } else { 1.0 };\n        let orientation = pose_offset.orientation.conjugate()\n            * gj[0].orientation.conjugate()\n            * pose.orientation\n            * Quat::from_euler(EulerRot::XZY, PI, sign * FRAC_PI_2, 0.0);\n\n        let position = -pose_offset.position\n            + pose_offset.orientation.conjugate()\n                * gj[0].orientation.conjugate()\n                * (pose.position - gj[0].position);\n\n        Pose {\n            orientation,\n            position,\n        }\n    };\n\n    let fixed_g_wrist = Pose {\n        orientation: gj[1].orientation\n            * Quat::from_euler(EulerRot::YXZ, -FRAC_PI_2, FRAC_PI_2, 0.0),\n        position: gj[1].position,\n    };\n\n    let skeleton = [\n        // Palm. NB: this is ignored by SteamVR\n        Pose {\n            orientation: gj[0].orientation * pose_offset.orientation,\n            position: gj[0].position\n                + gj[0].orientation * pose_offset.orientation * pose_offset.position,\n        },\n        // Wrist\n        root_parented_pose(gj[1]),\n        // Thumb\n        local_pose(id, fixed_g_wrist, gj[2]),\n        local_pose(id, gj[2], gj[3]),\n        local_pose(id, gj[3], gj[4]),\n        local_pose(id, gj[4], gj[5]),\n        // Index\n        local_pose(id, fixed_g_wrist, gj[6]),\n        local_pose(id, gj[6], gj[7]),\n        local_pose(id, gj[7], gj[8]),\n        local_pose(id, gj[8], gj[9]),\n        local_pose(id, gj[9], gj[10]),\n        // Middle\n        local_pose(id, fixed_g_wrist, gj[11]),\n        local_pose(id, gj[11], gj[12]),\n        local_pose(id, gj[12], gj[13]),\n        local_pose(id, gj[13], gj[14]),\n        local_pose(id, gj[14], gj[15]),\n        // Ring\n        local_pose(id, fixed_g_wrist, gj[16]),\n        local_pose(id, gj[16], gj[17]),\n        local_pose(id, gj[17], gj[18]),\n        local_pose(id, gj[18], gj[19]),\n        local_pose(id, gj[19], gj[20]),\n        // Little\n        local_pose(id, fixed_g_wrist, gj[21]),\n        local_pose(id, gj[21], gj[22]),\n        local_pose(id, gj[22], gj[23]),\n        local_pose(id, gj[23], gj[24]),\n        local_pose(id, gj[24], gj[25]),\n        // Aux bones\n        aux_orientation(id, root_parented_pose(gj[4])),\n        aux_orientation(id, root_parented_pose(gj[9])),\n        aux_orientation(id, root_parented_pose(gj[14])),\n        aux_orientation(id, root_parented_pose(gj[19])),\n        aux_orientation(id, root_parented_pose(gj[24])),\n    ];\n\n    to_ffi_skeleton(&skeleton)\n}\n\n// Apply controller offsets workarounds for SteamVR\npub fn offset_controller_motion(\n    config: &ControllersConfig,\n    device_id: u64,\n    motion: DeviceMotion,\n) -> DeviceMotion {\n    let t = config.left_controller_position_offset;\n    let r = config.left_controller_rotation_offset;\n\n    let pose_offset = if device_id == *HAND_LEFT_ID {\n        Pose {\n            orientation: Quat::from_euler(\n                EulerRot::XYZ,\n                r[0] * DEG_TO_RAD,\n                r[1] * DEG_TO_RAD,\n                r[2] * DEG_TO_RAD,\n            ),\n            position: Vec3::new(t[0], t[1], t[2]),\n        }\n    } else if device_id == *HAND_RIGHT_ID {\n        Pose {\n            orientation: Quat::from_euler(\n                EulerRot::XYZ,\n                r[0] * DEG_TO_RAD,\n                -r[1] * DEG_TO_RAD,\n                -r[2] * DEG_TO_RAD,\n            ),\n            position: Vec3::new(-t[0], t[1], t[2]),\n        }\n    } else {\n        panic!(\"device_id is not associated to a controller\");\n    };\n\n    DeviceMotion {\n        pose: motion.pose * pose_offset,\n        linear_velocity: motion.linear_velocity\n            + motion\n                .angular_velocity\n                .cross(motion.pose.orientation * pose_offset.position),\n        angular_velocity: motion.pose.orientation.conjugate() * motion.angular_velocity,\n    }\n}\n"
  },
  {
    "path": "alvr/session/Cargo.toml",
    "content": "[package]\nname = \"alvr_session\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_system_info.workspace = true\n\nbytemuck = { version = \"1\", features = [\"derive\"] }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nsettings-schema = { git = \"https://github.com/alvr-org/settings-schema-rs\", rev = \"676185f\" }\n\n[build-dependencies]\nalvr_filesystem.workspace = true\n\nregex = \"1\"\n"
  },
  {
    "path": "alvr/session/build.rs",
    "content": "use regex::Regex;\nuse std::{env, fmt::Write, fs, path::PathBuf};\n\nfn main() {\n    let openvr_driver_header_string =\n        fs::read_to_string(alvr_filesystem::workspace_dir().join(\"openvr/headers/openvr_driver.h\"))\n            .expect(\"Missing openvr header files, did you clone the submodule?\\n\");\n\n    let property_finder = Regex::new(\n        r\"\\tProp_([A-Za-z\\d_]+)_(Bool|Int32|Uint64|Float|String|Vector3)[\\t ]+= ([0-9]+)\",\n    )\n    .unwrap();\n\n    struct PropInfo {\n        name: String,\n        ty: String,\n        code: String,\n    }\n\n    let prop_info = property_finder\n        .captures_iter(&openvr_driver_header_string)\n        .map(|cap| {\n            let code = cap[3].into();\n            let name = format!(\"{}{}\", cap[1].replace('_', \"\"), &cap[2]);\n\n            PropInfo {\n                name,\n                ty: cap[2].into(),\n                code,\n            }\n        })\n        .collect::<Vec<_>>();\n\n    let mut mappings_fn_string: String = String::from(\n        r\"\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy, Debug)]\npub enum OpenvrPropKey {\",\n    );\n\n    for info in &prop_info {\n        write!(\n            mappings_fn_string,\n            r\"\n    {} = {},\",\n            &info.name, &info.code\n        )\n        .unwrap();\n    }\n\n    mappings_fn_string.push_str(\n        r\"\n}\n\n#[expect(clippy::match_same_arms)]\npub fn openvr_prop_key_to_type(key: OpenvrPropKey) -> OpenvrPropType {\n    match key {\",\n    );\n\n    for info in &prop_info {\n        write!(\n            mappings_fn_string,\n            r\"\n        OpenvrPropKey::{} => OpenvrPropType::{},\",\n            &info.name, info.ty\n        )\n        .unwrap();\n    }\n\n    mappings_fn_string.push_str(\n        r\"\n    }\n}\n\",\n    );\n\n    fs::write(\n        PathBuf::from(env::var(\"OUT_DIR\").unwrap()).join(\"openvr_property_keys.rs\"),\n        mappings_fn_string,\n    )\n    .unwrap();\n}\n"
  },
  {
    "path": "alvr/session/src/lib.rs",
    "content": "mod settings;\n\npub use settings::*;\npub use settings_schema;\n\nuse alvr_common::{\n    ALVR_VERSION, ConnectionState, ToAny,\n    anyhow::{Result, bail},\n    semver::Version,\n};\nuse serde::{Deserialize, Serialize};\nuse serde_json as json;\nuse settings_schema::{NumberType, SchemaNode};\nuse std::{\n    collections::{HashMap, HashSet},\n    net::IpAddr,\n};\n\n// SessionSettings is similar to Settings but it contains every branch, even unused ones. This is\n// the settings representation that the UI uses.\npub type SessionSettings = settings::SettingsDefault;\n\n// This structure is used to store the minimum configuration data that ALVR driver needs to\n// initialize OpenVR before having the chance to communicate with a client. When a client is\n// connected, a new OpenvrConfig instance is generated, then the connection is accepted only if that\n// instance is equivalent to the one stored in the session, otherwise SteamVR is restarted.\n// Other components (like the encoder, audio recorder) don't need this treatment and are initialized\n// dynamically.\n// todo: properties that can be set after the OpenVR initialization should be removed and set with\n// UpdateForStream.\n#[expect(clippy::pub_underscore_fields)]\n#[derive(Serialize, Deserialize, PartialEq, Default, Clone, Debug)]\npub struct OpenvrConfig {\n    pub eye_resolution_width: u32,\n    pub eye_resolution_height: u32,\n    pub target_eye_resolution_width: u32,\n    pub target_eye_resolution_height: u32,\n    pub tracking_ref_only: bool,\n    pub enable_vive_tracker_proxy: bool,\n    pub minimum_idr_interval_ms: u64,\n    pub adapter_index: u32,\n    pub codec: u8,\n    pub h264_profile: u32,\n    pub refresh_rate: u32,\n    pub use_10bit_encoder: bool,\n    pub encoding_gamma: f32,\n    pub enable_hdr: bool,\n    pub force_hdr_srgb_correction: bool,\n    pub clamp_hdr_extended_range: bool,\n    pub enable_amf_pre_analysis: bool,\n    pub enable_vbaq: bool,\n    pub enable_amf_hmqb: bool,\n    pub use_amf_preproc: bool,\n    pub amf_preproc_sigma: u32,\n    pub amf_preproc_tor: u32,\n    pub encoder_quality_preset: u32,\n    pub rate_control_mode: u32,\n    pub filler_data: bool,\n    pub entropy_coding: u32,\n    pub force_sw_encoding: bool,\n    pub sw_thread_count: u32,\n    pub controller_is_tracker: bool,\n    pub controllers_enabled: bool,\n    pub body_tracking_vive_enabled: bool,\n    pub body_tracking_has_legs: bool,\n    pub enable_foveated_encoding: bool,\n    pub foveation_center_size_x: f32,\n    pub foveation_center_size_y: f32,\n    pub foveation_center_shift_x: f32,\n    pub foveation_center_shift_y: f32,\n    pub foveation_edge_ratio_x: f32,\n    pub foveation_edge_ratio_y: f32,\n    pub enable_color_correction: bool,\n    pub brightness: f32,\n    pub contrast: f32,\n    pub saturation: f32,\n    pub gamma: f32,\n    pub sharpening: f32,\n    pub linux_async_compute: bool,\n    pub linux_async_reprojection: bool,\n    pub nvenc_quality_preset: u32,\n    pub nvenc_tuning_preset: u32,\n    pub nvenc_multi_pass: u32,\n    pub nvenc_adaptive_quantization_mode: u32,\n    pub nvenc_low_delay_key_frame_scale: i64,\n    pub nvenc_refresh_rate: i64,\n    pub enable_intra_refresh: bool,\n    pub intra_refresh_period: i64,\n    pub intra_refresh_count: i64,\n    pub max_num_ref_frames: i64,\n    pub gop_length: i64,\n    pub p_frame_strategy: i64,\n    pub nvenc_rate_control_mode: i64,\n    pub rc_buffer_size: i64,\n    pub rc_initial_delay: i64,\n    pub rc_max_bitrate: i64,\n    pub rc_average_bitrate: i64,\n    pub nvenc_enable_weighted_prediction: bool,\n    pub capture_frame_dir: String,\n    pub amd_bitrate_corruption_fix: bool,\n    pub use_separate_hand_trackers: bool,\n\n    // these settings are not used on the C++ side, but we need them to correctly trigger a SteamVR\n    // restart\n    pub _controller_profile: i32,\n    pub _server_impl_debug: bool,\n    pub _client_impl_debug: bool,\n    pub _server_core_debug: bool,\n    pub _client_core_debug: bool,\n    pub _connection_debug: bool,\n    pub _sockets_debug: bool,\n    pub _server_gfx_debug: bool,\n    pub _client_gfx_debug: bool,\n    pub _encoder_debug: bool,\n    pub _decoder_debug: bool,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct ClientConnectionConfig {\n    pub display_name: String,\n    pub current_ip: Option<IpAddr>,\n    pub manual_ips: HashSet<IpAddr>,\n    pub trusted: bool,\n    pub connection_state: ConnectionState,\n}\n\n#[derive(Serialize, Deserialize, Clone, Debug)]\npub struct SessionConfig {\n    pub server_version: Version,\n    pub openvr_config: OpenvrConfig,\n    // The hashmap key is the hostname\n    pub client_connections: HashMap<String, ClientConnectionConfig>,\n    pub session_settings: SessionSettings,\n}\n\nimpl Default for SessionConfig {\n    fn default() -> Self {\n        Self {\n            server_version: ALVR_VERSION.clone(),\n            openvr_config: OpenvrConfig {\n                // avoid realistic resolutions, as on first start, on Linux, it\n                // could trigger direct mode on an existing monitor\n                eye_resolution_width: 800,\n                eye_resolution_height: 900,\n                target_eye_resolution_width: 800,\n                target_eye_resolution_height: 900,\n                adapter_index: 0,\n                refresh_rate: 60,\n                controllers_enabled: false,\n                body_tracking_vive_enabled: false,\n                enable_foveated_encoding: false,\n                enable_color_correction: false,\n                linux_async_reprojection: false,\n                capture_frame_dir: \"/tmp\".into(),\n                ..<_>::default()\n            },\n            client_connections: HashMap::new(),\n            session_settings: settings::session_settings_default(),\n        }\n    }\n}\n\nimpl SessionConfig {\n    // If json_value is not a valid representation of SessionConfig (because of version upgrade),\n    // use some fuzzy logic to extrapolate as much information as possible.\n    // Since SessionConfig cannot have a schema (because SessionSettings would need to also have a\n    // schema, but it is generated out of our control), we only do basic name checking on fields and\n    // deserialization will fail if the type of values does not match. Because of this,\n    // `session_settings` must be handled separately to do a better job of retrieving data using the\n    // settings schema.\n    pub fn merge_from_json(&mut self, json_value: &json::Value) -> Result<()> {\n        const SESSION_SETTINGS_STR: &str = \"session_settings\";\n\n        if let Ok(session_desc) = json::from_value(json_value.clone()) {\n            *self = session_desc;\n            return Ok(());\n        }\n\n        // Note: unwrap is safe because current session is expected to serialize correctly\n        let old_session_json = json::to_value(self.clone()).unwrap();\n        let old_session_fields = old_session_json.as_object().unwrap();\n\n        let maybe_session_settings_json =\n            json_value\n                .get(SESSION_SETTINGS_STR)\n                .map(|new_session_settings_json| {\n                    extrapolate_session_settings_from_session_settings(\n                        &old_session_fields[SESSION_SETTINGS_STR],\n                        new_session_settings_json,\n                        &Settings::schema(settings::session_settings_default()),\n                    )\n                });\n\n        let new_fields = old_session_fields\n            .iter()\n            .map(|(name, json_field_value)| {\n                let new_json_field_value = if name == SESSION_SETTINGS_STR {\n                    json::to_value(settings::session_settings_default()).unwrap()\n                } else {\n                    json_value.get(name).unwrap_or(json_field_value).clone()\n                };\n                (name.clone(), new_json_field_value)\n            })\n            .collect();\n        // Failure to extrapolate other session_desc fields is not notified.\n        let mut session_desc_mut =\n            json::from_value::<SessionConfig>(json::Value::Object(new_fields)).unwrap_or_default();\n\n        match maybe_session_settings_json\n            .to_any()\n            .and_then(|s| serde_json::from_value::<SessionSettings>(s).map_err(|e| e.into()))\n        {\n            Ok(session_settings) => {\n                session_desc_mut.session_settings = session_settings;\n                *self = session_desc_mut;\n                Ok(())\n            }\n            Err(e) => {\n                *self = session_desc_mut;\n\n                bail!(\"Error while deserializing extrapolated session settings: {e}\")\n            }\n        }\n    }\n\n    pub fn to_settings(&self) -> Settings {\n        let session_settings_json = json::to_value(&self.session_settings).unwrap();\n        let schema = Settings::schema(settings::session_settings_default());\n\n        json::from_value::<Settings>(json_session_settings_to_settings(\n            &session_settings_json,\n            &schema,\n        ))\n        .map_err(|e| dbg!(e))\n        .unwrap()\n    }\n}\n\n// Current data extrapolation strategy: match both field name and value type exactly.\n// Integer bounds are not validated, if they do not match the schema, deserialization will fail and\n// all data is lost.\n// Future strategies: check if value respects schema constraints, fuzzy field name matching, accept\n// integer to float and float to integer, tree traversal.\nfn extrapolate_session_settings_from_session_settings(\n    old_session_settings: &json::Value,\n    new_session_settings: &json::Value,\n    schema: &SchemaNode,\n) -> json::Value {\n    match schema {\n        SchemaNode::Section {\n            entries,\n            gui_collapsible,\n        } => json::Value::Object({\n            let mut entries: json::Map<String, json::Value> = entries\n                .iter()\n                .map(|named_entry| {\n                    let value_json = extrapolate_session_settings_from_session_settings(\n                        &old_session_settings[&named_entry.name],\n                        &new_session_settings[&named_entry.name],\n                        &named_entry.content,\n                    );\n                    (named_entry.name.clone(), value_json)\n                })\n                .collect();\n\n            if *gui_collapsible {\n                let collapsed_json = if new_session_settings[\"gui_collapsed\"].is_boolean() {\n                    new_session_settings[\"gui_collapsed\"].clone()\n                } else {\n                    old_session_settings[\"gui_collapsed\"].clone()\n                };\n                entries.insert(\"gui_collapsed\".into(), collapsed_json);\n            }\n\n            entries\n        }),\n\n        SchemaNode::Choice { variants, .. } => {\n            let variant_json = json::from_value(new_session_settings[\"variant\"].clone())\n                .ok()\n                .filter(|variant_str| {\n                    variants\n                        .iter()\n                        .any(|named_entry| *variant_str == named_entry.name)\n                })\n                .map_or_else(\n                    || old_session_settings[\"variant\"].clone(),\n                    json::Value::String,\n                );\n\n            let mut fields: json::Map<_, _> = variants\n                .iter()\n                .filter_map(|named_entry| {\n                    named_entry.content.as_ref().map(|data_schema| {\n                        let value_json = extrapolate_session_settings_from_session_settings(\n                            &old_session_settings[&named_entry.name],\n                            &new_session_settings[&named_entry.name],\n                            data_schema,\n                        );\n                        (named_entry.name.clone(), value_json)\n                    })\n                })\n                .collect();\n            fields.insert(\"variant\".into(), variant_json);\n\n            json::Value::Object(fields)\n        }\n\n        SchemaNode::Optional { content, .. } => {\n            let set_value = new_session_settings[\"set\"]\n                .as_bool()\n                .unwrap_or_else(|| old_session_settings[\"set\"].as_bool().unwrap());\n\n            let content_json = extrapolate_session_settings_from_session_settings(\n                &old_session_settings[\"content\"],\n                &new_session_settings[\"content\"],\n                content,\n            );\n\n            json::json!({\n                \"set\": set_value,\n                \"content\": content_json\n            })\n        }\n\n        SchemaNode::Switch { content, .. } => {\n            let enabled = new_session_settings[\"enabled\"]\n                .as_bool()\n                .unwrap_or_else(|| old_session_settings[\"enabled\"].as_bool().unwrap());\n\n            let content_json = extrapolate_session_settings_from_session_settings(\n                &old_session_settings[\"content\"],\n                &new_session_settings[\"content\"],\n                content,\n            );\n\n            json::json!({\n                \"enabled\": enabled,\n                \"content\": content_json\n            })\n        }\n\n        SchemaNode::Boolean { .. } => {\n            if new_session_settings.is_boolean() {\n                new_session_settings.clone()\n            } else {\n                old_session_settings.clone()\n            }\n        }\n\n        SchemaNode::Number { ty, .. } => {\n            if let Some(value) = new_session_settings.as_f64() {\n                match ty {\n                    NumberType::UnsignedInteger => json::Value::from(value.abs() as u64),\n                    NumberType::SignedInteger => json::Value::from(value as i64),\n                    NumberType::Float => new_session_settings.clone(),\n                }\n            } else {\n                old_session_settings.clone()\n            }\n        }\n\n        SchemaNode::Text { .. } => {\n            if new_session_settings.is_string() {\n                new_session_settings.clone()\n            } else {\n                old_session_settings.clone()\n            }\n        }\n\n        SchemaNode::Array(array_schema) => {\n            let gui_collapsed = new_session_settings[\"gui_collapsed\"]\n                .as_bool()\n                .unwrap_or_else(|| old_session_settings[\"gui_collapsed\"].as_bool().unwrap());\n\n            let array_vec = array_schema\n                .iter()\n                .enumerate()\n                .map(|(idx, schema)| {\n                    extrapolate_session_settings_from_session_settings(\n                        &old_session_settings[\"content\"][idx],\n                        &new_session_settings[\"content\"][idx],\n                        schema,\n                    )\n                })\n                .collect::<Vec<_>>();\n\n            json::json!({\n                \"gui_collapsed\": gui_collapsed,\n                \"content\": array_vec\n            })\n        }\n\n        SchemaNode::Vector {\n            default_element, ..\n        } => {\n            let gui_collapsed = new_session_settings[\"gui_collapsed\"]\n                .as_bool()\n                .unwrap_or_else(|| old_session_settings[\"gui_collapsed\"].as_bool().unwrap());\n\n            let element_json = extrapolate_session_settings_from_session_settings(\n                &old_session_settings[\"element\"],\n                &new_session_settings[\"element\"],\n                default_element,\n            );\n\n            let content_json =\n                json::from_value::<Vec<json::Value>>(new_session_settings[\"content\"].clone())\n                    .ok()\n                    .map(|vec| {\n                        vec.iter()\n                            .enumerate()\n                            .map(|(idx, new_element)| {\n                                extrapolate_session_settings_from_session_settings(\n                                    &old_session_settings[\"content\"]\n                                        .get(idx)\n                                        .cloned()\n                                        .unwrap_or_else(|| element_json.clone()),\n                                    new_element,\n                                    default_element,\n                                )\n                            })\n                            .collect()\n                    })\n                    .map_or_else(\n                        || old_session_settings[\"content\"].clone(),\n                        json::Value::Array,\n                    );\n\n            json::json!({\n                \"gui_collapsed\": gui_collapsed,\n                \"element\": element_json,\n                \"content\": content_json\n            })\n        }\n\n        SchemaNode::Dictionary { default_value, .. } => {\n            let gui_collapsed = new_session_settings[\"gui_collapsed\"]\n                .as_bool()\n                .unwrap_or_else(|| old_session_settings[\"gui_collapsed\"].as_bool().unwrap());\n\n            let key_str = new_session_settings[\"key\"]\n                .as_str()\n                .unwrap_or_else(|| old_session_settings[\"key\"].as_str().unwrap());\n\n            let value_json = extrapolate_session_settings_from_session_settings(\n                &old_session_settings[\"value\"],\n                &new_session_settings[\"value\"],\n                default_value,\n            );\n\n            let content_json = json::from_value::<HashMap<String, json::Value>>(\n                new_session_settings[\"content\"].clone(),\n            )\n            .ok()\n            .map(|map| {\n                map.iter()\n                    .map(|(key, new_value)| {\n                        let value = extrapolate_session_settings_from_session_settings(\n                            &old_session_settings[\"content\"]\n                                .get(key)\n                                .cloned()\n                                .unwrap_or_else(|| value_json.clone()),\n                            new_value,\n                            default_value,\n                        );\n                        (key, value)\n                    })\n                    .map(|(key, value)| {\n                        json::Value::Array(vec![json::Value::String(key.clone()), value])\n                    })\n                    .collect()\n            })\n            .map_or_else(\n                || old_session_settings[\"content\"].clone(),\n                json::Value::Array,\n            );\n\n            json::json!({\n                \"gui_collapsed\": gui_collapsed,\n                \"key\": key_str,\n                \"value\": value_json,\n                \"content\": content_json\n            })\n        }\n        _ => unreachable!(),\n    }\n}\n\n// session_settings does not get validated here, it must be already valid\nfn json_session_settings_to_settings(\n    session_settings: &json::Value,\n    schema: &SchemaNode,\n) -> json::Value {\n    match schema {\n        SchemaNode::Section { entries, .. } => json::Value::Object(\n            entries\n                .iter()\n                .map(|named_entry| {\n                    (\n                        named_entry.name.clone(),\n                        json_session_settings_to_settings(\n                            &session_settings[&named_entry.name],\n                            &named_entry.content,\n                        ),\n                    )\n                })\n                .collect(),\n        ),\n\n        SchemaNode::Choice { variants, .. } => {\n            let variant = session_settings[\"variant\"].as_str().unwrap();\n            let maybe_content = variants\n                .iter()\n                .find(|named_entry| named_entry.name == variant)\n                .and_then(|named_entry| named_entry.content.as_ref())\n                .map(|data_schema| {\n                    json_session_settings_to_settings(&session_settings[variant], data_schema)\n                });\n            if let Some(content) = maybe_content {\n                json::json!({ variant: content })\n            } else {\n                json::Value::String(variant.to_owned())\n            }\n        }\n\n        SchemaNode::Optional { content, .. } => {\n            if session_settings[\"set\"].as_bool().unwrap() {\n                json_session_settings_to_settings(&session_settings[\"content\"], content)\n            } else {\n                json::Value::Null\n            }\n        }\n\n        SchemaNode::Switch { content, .. } => {\n            if session_settings[\"enabled\"].as_bool().unwrap() {\n                let content =\n                    json_session_settings_to_settings(&session_settings[\"content\"], content);\n\n                json::json!({ \"Enabled\": content })\n            } else {\n                json::Value::String(\"Disabled\".into())\n            }\n        }\n\n        SchemaNode::Boolean { .. } | SchemaNode::Number { .. } | SchemaNode::Text { .. } => {\n            session_settings.clone()\n        }\n\n        SchemaNode::Array(array_schema) => json::Value::Array(\n            array_schema\n                .iter()\n                .enumerate()\n                .map(|(idx, element_schema)| {\n                    json_session_settings_to_settings(\n                        &session_settings[\"content\"][idx],\n                        element_schema,\n                    )\n                })\n                .collect(),\n        ),\n\n        SchemaNode::Vector {\n            default_element, ..\n        } => json::to_value(\n            session_settings[\"content\"]\n                .as_array()\n                .unwrap()\n                .iter()\n                .map(|element_json| {\n                    json_session_settings_to_settings(element_json, default_element)\n                })\n                .collect::<Vec<_>>(),\n        )\n        .unwrap(),\n\n        SchemaNode::Dictionary { default_value, .. } => json::to_value(\n            json::from_value::<Vec<(String, json::Value)>>(session_settings[\"content\"].clone())\n                .unwrap()\n                .into_iter()\n                .map(|(key, value_json)| {\n                    (\n                        key,\n                        json_session_settings_to_settings(&value_json, default_value),\n                    )\n                })\n                .collect::<Vec<_>>(),\n        )\n        .unwrap(),\n        _ => unreachable!(),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_manual_session_to_settings() {\n        let default = session_settings_default();\n        let settings_schema = json::to_value(&default).unwrap();\n        let schema = Settings::schema(default);\n\n        let _settings = json::from_value::<Settings>(json_session_settings_to_settings(\n            &settings_schema,\n            &schema,\n        ))\n        .err();\n    }\n\n    #[test]\n    fn test_session_to_settings() {\n        let _settings = SessionConfig::default().to_settings();\n    }\n\n    #[test]\n    fn test_session_extrapolation_trivial() {\n        SessionConfig::default()\n            .merge_from_json(&json::to_value(SessionConfig::default()).unwrap())\n            .unwrap();\n    }\n\n    #[test]\n    fn test_session_extrapolation_diff() {\n        let input_json_string = r#\"{\n            \"session_settings\": {\n              \"fjdshfks\": false,\n              \"video\": {\n                \"preferred_fps\": 60.0\n              },\n              \"headset\": {\n                \"gui_collapsed\": false,\n                \"controllers\": {\n                  \"enabled\": false\n                }\n              }\n            }\n          }\"#;\n\n        let mut session = SessionConfig::default();\n        session\n            .merge_from_json(&json::from_str(input_json_string).unwrap())\n            .unwrap();\n\n        let settings = session.to_settings();\n\n        assert_eq!(settings.video.preferred_fps, 60.0);\n        assert!(settings.headset.controllers.as_option().is_none());\n    }\n}\n"
  },
  {
    "path": "alvr/session/src/settings.rs",
    "content": "use alvr_common::{\n    ALVR_VERSION, DebugGroupsConfig, DebugGroupsConfigDefault, LogSeverity, LogSeverityDefault,\n    LogSeverityDefaultVariant,\n};\nuse alvr_system_info::{ClientFlavor, ClientFlavorDefault, ClientFlavorDefaultVariant};\nuse bytemuck::{Pod, Zeroable};\nuse serde::{Deserialize, Serialize};\nuse settings_schema::{\n    ArrayDefault, DictionaryDefault, OptionalDefault, SettingsSchema, Switch, SwitchDefault,\n    VectorDefault,\n};\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/openvr_property_keys.rs\"));\n\npub enum OpenvrPropType {\n    Bool,\n    Float,\n    Int32,\n    Uint64,\n    Vector3,\n    Double,\n    String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Debug)]\npub struct OpenvrProperty {\n    pub key: OpenvrPropKey,\n    pub value: String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(gui = \"button_group\")]\npub enum FrameSize {\n    Scale(#[schema(gui(slider(min = 0.25, max = 2.0, step = 0.01)))] f32),\n\n    Absolute {\n        #[schema(gui(slider(min = 32, max = 8192, step = 32)))]\n        width: u32,\n        #[schema(gui(slider(min = 32, max = 8192, step = 32)))]\n        height: Option<u32>,\n    },\n}\n\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum EncoderQualityPreset {\n    Quality = 0,\n    Balanced = 1,\n    Speed = 2,\n}\n\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum EncoderQualityPresetNvidia {\n    P1 = 1,\n    P2 = 2,\n    P3 = 3,\n    P4 = 4,\n    P5 = 5,\n    P6 = 6,\n    P7 = 7,\n}\n\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum NvencTuningPreset {\n    HighQuality = 1,\n    LowLatency = 2,\n    UltraLowLatency = 3,\n    Lossless = 4,\n}\n\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum NvencMultiPass {\n    Disabled = 0,\n    #[schema(strings(display_name = \"1/4 resolution\"))]\n    QuarterResolution = 1,\n    FullResolution = 2,\n}\n\n#[repr(u32)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum NvencAdaptiveQuantizationMode {\n    Disabled = 0,\n    Spatial = 1,\n    Temporal = 2,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(gui = \"button_group\")]\npub enum RateControlMode {\n    #[schema(strings(display_name = \"CBR\"))]\n    Cbr = 0,\n    #[schema(strings(display_name = \"VBR\"))]\n    Vbr = 1,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(gui = \"button_group\")]\npub enum EntropyCoding {\n    #[schema(strings(display_name = \"CAVLC\"))]\n    Cavlc = 1,\n    #[schema(strings(display_name = \"CABAC\"))]\n    Cabac = 0,\n}\n\n/// Except for preset, the value of these fields is not applied if == -1 (flag)\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct NvencConfig {\n    #[schema(strings(\n        help = \"P1 is the fastest preset and P7 is the preset that produces better quality. P6 and P7 are too slow to be usable.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub quality_preset: EncoderQualityPresetNvidia,\n    #[schema(flag = \"steamvr-restart\")]\n    pub tuning_preset: NvencTuningPreset,\n    #[schema(strings(\n        help = \"Reduce compression artifacts at the cost of small performance penalty\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub multi_pass: NvencMultiPass,\n    #[schema(strings(\n        help = r#\"Spatial: Helps reduce color banding, but high-complexity scenes might look worse.\nTemporal: Helps improve overall encoding quality, very small trade-off in speed.\"#\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub adaptive_quantization_mode: NvencAdaptiveQuantizationMode,\n    #[schema(flag = \"steamvr-restart\")]\n    pub low_delay_key_frame_scale: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub refresh_rate: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub enable_intra_refresh: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub intra_refresh_period: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub intra_refresh_count: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub max_num_ref_frames: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub gop_length: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub p_frame_strategy: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub rate_control_mode: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub rc_buffer_size: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub rc_initial_delay: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub rc_max_bitrate: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub rc_average_bitrate: i64,\n    #[schema(flag = \"steamvr-restart\")]\n    pub enable_weighted_prediction: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct AmfConfig {\n    #[schema(\n        strings(\n            display_name = \"Enable High-Motion Quality Boost\",\n            help = r#\"Enables high motion quality boost mode.\nAllows the encoder to perform pre-analysis the motion of the video and use the information for better encoding\"#\n        ),\n        flag = \"steamvr-restart\"\n    )]\n    pub enable_hmqb: bool,\n    #[schema(flag = \"steamvr-restart\")]\n    pub use_preproc: bool,\n    #[schema(gui(slider(min = 0, max = 10)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub preproc_sigma: u32,\n    #[schema(gui(slider(min = 0, max = 10)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub preproc_tor: u32,\n    #[schema(\n        strings(\n            display_name = \"Enable Pre-analysis\",\n            help = r#\"Enables pre-analysis during encoding. This will likely result in reduced performance, but may increase quality.\nDoes not work with the \"Reduce color banding\" option, requires enabling \"Use preproc\"\"#\n        ),\n        flag = \"steamvr-restart\"\n    )]\n    pub enable_pre_analysis: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct SoftwareEncodingConfig {\n    #[schema(strings(\n        display_name = \"Force software encoding\",\n        help = \"Forces the encoder to use CPU instead of GPU\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub force_software_encoding: bool,\n\n    #[schema(strings(display_name = \"Encoder thread count\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub thread_count: u32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct HDRConfig {\n    #[schema(strings(\n        display_name = \"Enable HDR\",\n        help = \"If the client has no preference, enables compositing VR layers to an RGBA float16 framebuffer, and doing sRGB/YUV conversions in shader code.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub enable: Option<bool>,\n\n    #[schema(strings(\n        display_name = \"Force HDR sRGB Correction\",\n        help = \"Forces sRGB correction on all composited SteamVR layers. Useful if an HDR-injected game is too dark.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub force_hdr_srgb_correction: bool,\n\n    #[schema(strings(\n        display_name = \"Clamp HDR extended range\",\n        help = \"Clamps HDR extended range to 0.0~1.0, useful if you only want HDR to reduce banding.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub clamp_hdr_extended_range: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct EncoderConfig {\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(\n        display_name = \"Quality preset\",\n        help = \"Controls overall quality preset of the encoder. Works only on Windows AMD AMF, Linux VAAPI (AMD/Intel).\"\n    ))]\n    pub quality_preset: EncoderQualityPreset,\n\n    #[schema(\n        strings(\n            display_name = \"Enable VBAQ/CAQ\",\n            help = \"Enables Variance Based Adaptive Quantization on h264 and HEVC, and Content Adaptive Quantization on AV1\"\n        ),\n        flag = \"steamvr-restart\"\n    )]\n    pub enable_vbaq: bool,\n\n    #[cfg_attr(not(target_os = \"windows\"), schema(flag = \"hidden\"))]\n    #[schema(strings(help = r#\"CBR: Constant BitRate mode. This is recommended.\nVBR: Variable BitRate mode. Not commended because it may throw off the adaptive bitrate algorithm. This is only supported on Windows and only with AMD/Nvidia GPUs\"#))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub rate_control_mode: RateControlMode,\n\n    #[schema(strings(\n        display_name = \"h264: Profile\",\n        help = \"Whenever possible, attempts to use this profile. May increase compatibility with varying mobile devices. Only has an effect for h264. Doesn't affect NVENC on Windows.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub h264_profile: H264Profile,\n\n    #[schema(strings(help = r#\"CAVLC algorithm is recommended.\nCABAC produces better compression but it's significantly slower and may lead to runaway latency\"#))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub entropy_coding: EntropyCoding,\n\n    #[schema(strings(\n        help = r#\"In CBR mode, this makes sure the bitrate does not fall below the assigned value. This is mostly useful for debugging.\"#\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub filler_data: bool,\n\n    #[schema(strings(\n        display_name = \"10-bit encoding\",\n        help = \"Sets the encoder to use 10 bits per channel instead of 8, if the client has no preference. Does not work on Linux with Nvidia\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub use_10bit: Option<bool>,\n\n    #[schema(strings(\n        display_name = \"Encoding Gamma\",\n        help = \"To prioritize darker pixels at the expense of potentially additional banding in midtones, set to 2.2. To allow the encoder to decide priority on its own, set to 1.0.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub encoding_gamma: Option<f32>,\n\n    #[schema(strings(display_name = \"HDR\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub hdr: HDRConfig,\n\n    #[schema(strings(display_name = \"NVENC\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub nvenc: NvencConfig,\n\n    #[cfg_attr(not(target_os = \"windows\"), schema(flag = \"hidden\"))]\n    #[schema(strings(display_name = \"AMF\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub amf: AmfConfig,\n\n    #[schema(strings(display_name = \"Software (CPU) encoding\"))]\n    pub software: SoftwareEncodingConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Debug, PartialEq)]\npub enum MediacodecPropType {\n    Float,\n    Int32,\n    Int64,\n    String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Debug, PartialEq)]\npub struct MediacodecProperty {\n    #[schema(strings(display_name = \"Type\"))]\n    pub ty: MediacodecPropType,\n    pub value: String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub struct EncoderLatencyLimiter {\n    #[schema(strings(\n        help = \"Allowed percentage of frame interval to allocate for video encoding\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.3, max = 1.0, step = 0.01)))]\n    pub max_saturation_multiplier: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct DecoderLatencyLimiter {\n    #[schema(strings(\n        display_name = \"Maximum decoder latency\",\n        help = \"When the decoder latency goes above this threshold, the bitrate will be reduced\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 1, max = 50)), suffix = \"ms\")]\n    pub max_decoder_latency_ms: u64,\n\n    #[schema(strings(\n        display_name = \"latency overstep\",\n        help = \"Number of consecutive frames above the threshold to trigger a bitrate reduction\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 1, max = 100)), suffix = \" frames\")]\n    pub latency_overstep_frames: usize,\n\n    #[schema(strings(\n        help = \"Controls how much the bitrate is reduced when the decoder latency goes above the threshold\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.5, max = 1.0)))]\n    pub latency_overstep_multiplier: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(gui = \"button_group\")]\npub enum BitrateMode {\n    #[schema(strings(display_name = \"Constant\"))]\n    ConstantMbps(#[schema(gui(slider(min = 5, max = 1000, logarithmic)), suffix = \"Mbps\")] u64),\n\n    #[schema(collapsible)]\n    Adaptive {\n        #[schema(strings(\n            help = \"Percentage of network bandwidth to allocate for video transmission\"\n        ))]\n        #[schema(flag = \"real-time\")]\n        #[schema(gui(slider(min = 0.5, max = 5.0, step = 0.01)))]\n        saturation_multiplier: f32,\n\n        #[schema(strings(display_name = \"Maximum bitrate\"))]\n        #[schema(flag = \"real-time\")]\n        #[schema(gui(slider(min = 1, max = 1000, logarithmic)), suffix = \"Mbps\")]\n        max_throughput_mbps: Switch<u64>,\n\n        #[schema(strings(display_name = \"Minimum bitrate\"))]\n        #[schema(flag = \"real-time\")]\n        #[schema(gui(slider(min = 1, max = 100, logarithmic)), suffix = \"Mbps\")]\n        min_throughput_mbps: Switch<u64>,\n\n        #[schema(strings(display_name = \"Maximum network latency\"))]\n        #[schema(flag = \"real-time\")]\n        #[schema(gui(slider(min = 1, max = 50)), suffix = \"ms\")]\n        max_network_latency_ms: Switch<u64>,\n\n        #[schema(flag = \"real-time\")]\n        encoder_latency_limiter: Switch<EncoderLatencyLimiter>,\n\n        #[schema(strings(\n            help = \"Currently there is a bug where the decoder latency keeps rising when above a certain bitrate\"\n        ))]\n        #[schema(flag = \"real-time\")]\n        decoder_latency_limiter: Switch<DecoderLatencyLimiter>,\n    },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub struct BitrateAdaptiveFramerateConfig {\n    #[schema(strings(\n        display_name = \"FPS reset threshold multiplier\",\n        help = \"If the framerate changes more than this factor, trigger a parameters update\",\n    ))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 1.0, max = 3.0, step = 0.1)))]\n    pub framerate_reset_threshold_multiplier: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct BitrateConfig {\n    #[schema(flag = \"real-time\")]\n    pub mode: BitrateMode,\n\n    #[schema(strings(\n        help = \"Ensure that the specified bitrate value is respected regardless of the framerate\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    pub adapt_to_framerate: Switch<BitrateAdaptiveFramerateConfig>,\n\n    #[schema(strings(help = \"Controls the smoothness during calculations\"))]\n    pub history_size: usize,\n\n    #[schema(strings(\n        help = \"When this is enabled, an IDR frame is requested after the bitrate is changed.\nThis has an effect only on AMD GPUs.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub image_corruption_fix: bool,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Copy, Clone, PartialEq)]\npub enum ClientsideFoveationLevel {\n    Low = 1,\n    Medium = 2,\n    High = 3,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum ClientsideFoveationMode {\n    Static { level: ClientsideFoveationLevel },\n    Dynamic { max_level: ClientsideFoveationLevel },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub struct ClientsideFoveationConfig {\n    pub mode: ClientsideFoveationMode,\n\n    #[schema(strings(display_name = \"Foveation offset\"))]\n    #[schema(gui(slider(min = -45.0, max = 45.0, step = 0.1)), suffix = \"°\")]\n    pub vertical_offset_deg: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(collapsible)]\npub struct FoveatedEncodingConfig {\n    #[schema(strings(help = \"Force enable on smartphone clients\"))]\n    pub force_enable: bool,\n\n    #[schema(strings(display_name = \"Center region width\"))]\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub center_size_x: f32,\n\n    #[schema(strings(display_name = \"Center region height\"))]\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub center_size_y: f32,\n\n    #[schema(strings(display_name = \"Center shift X\"))]\n    #[schema(gui(slider(min = -1.0, max = 1.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub center_shift_x: f32,\n\n    #[schema(strings(display_name = \"Center shift Y\"))]\n    #[schema(gui(slider(min = -1.0, max = 1.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub center_shift_y: f32,\n\n    #[schema(strings(display_name = \"Horizontal edge ratio\"))]\n    #[schema(gui(slider(min = 1.0, max = 10.0, step = 1.0)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub edge_ratio_x: f32,\n\n    #[schema(strings(display_name = \"Vertical edge ratio\"))]\n    #[schema(gui(slider(min = 1.0, max = 10.0, step = 1.0)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub edge_ratio_y: f32,\n}\n\n#[repr(C)]\n#[derive(SettingsSchema, Clone, Copy, Serialize, Deserialize, Pod, Zeroable)]\npub struct ColorCorrectionConfig {\n    #[schema(gui(slider(min = -1.0, max = 1.0, step = 0.001)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub brightness: f32,\n\n    #[schema(gui(slider(min = -1.0, max = 1.0, step = 0.001)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub contrast: f32,\n\n    #[schema(gui(slider(min = -1.0, max = 1.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub saturation: f32,\n\n    #[schema(gui(slider(min = 0.0, max = 5.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub gamma: f32,\n\n    #[schema(gui(slider(min = -1.0, max = 5.0, step = 0.01)))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub sharpening: f32,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)]\n#[schema(gui = \"button_group\")]\npub enum CodecType {\n    #[default]\n    #[schema(strings(display_name = \"h264\"))]\n    H264 = 0,\n    #[schema(strings(display_name = \"HEVC\"))]\n    Hevc = 1,\n    #[schema(strings(display_name = \"AV1\"))]\n    AV1 = 2,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]\n#[schema(gui = \"button_group\")]\npub enum H264Profile {\n    #[schema(strings(display_name = \"High\"))]\n    High = 0,\n    #[schema(strings(display_name = \"Main\"))]\n    Main = 1,\n    #[schema(strings(display_name = \"Baseline\"))]\n    Baseline = 2,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Debug)]\npub struct RgbChromaKeyConfig {\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0, max = 255)))]\n    pub red: u8,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0, max = 255)))]\n    pub green: u8,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0, max = 255)))]\n    pub blue: u8,\n\n    #[schema(strings(help = \"The threshold is applied per-channel\"))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 1, max = 255)))]\n    pub distance_threshold: u8,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.01, max = 1.0, step = 0.01)))]\n    pub feathering: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Debug)]\npub struct HsvChromaKeyConfig {\n    #[schema(strings(display_name = \"Hue start max\"), suffix = \"°\")]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -179.0, max = 539.0, step = 1.0)))]\n    pub hue_start_max_deg: f32,\n\n    #[schema(strings(display_name = \"Hue start min\"), suffix = \"°\")]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -179.0, max = 539.0, step = 1.0)))]\n    pub hue_start_min_deg: f32,\n\n    #[schema(strings(display_name = \"Hue end min\"), suffix = \"°\")]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -179.0, max = 539.0, step = 1.0)))]\n    pub hue_end_min_deg: f32,\n\n    #[schema(strings(display_name = \"Hue end max\"), suffix = \"°\")]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -179.0, max = 539.0, step = 1.0)))]\n    pub hue_end_max_deg: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub saturation_start_max: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub saturation_start_min: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub saturation_end_min: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5,step = 0.01)))]\n    pub saturation_end_max: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub value_start_max: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub value_start_min: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub value_end_min: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -0.5, max = 1.5, step = 0.01)))]\n    pub value_end_max: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Debug)]\n#[schema(gui = \"button_group\")]\npub enum PassthroughMode {\n    Blend {\n        #[schema(strings(\n            help = \"Enabling this will adapt transparency based on the brightness of each pixel.\nThis is a similar effect to AR glasses.\"\n        ))]\n        #[schema(flag = \"real-time\")]\n        premultiplied_alpha: bool,\n\n        #[schema(flag = \"real-time\")]\n        #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n        threshold: f32,\n    },\n\n    #[schema(strings(display_name = \"RGB Chroma Key\"))]\n    RgbChromaKey(#[schema(flag = \"real-time\")] RgbChromaKeyConfig),\n\n    #[schema(strings(display_name = \"HSV Chroma Key\"))]\n    HsvChromaKey(#[schema(flag = \"real-time\")] HsvChromaKeyConfig),\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]\n#[schema(gui = \"button_group\")]\npub enum ClientsidePostProcessingSuperSamplingMode {\n    Disabled = 0,\n    Normal = 1 << 0,\n    Quality = 1 << 1,\n}\n\n#[repr(u8)]\n#[derive(SettingsSchema, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]\n#[schema(gui = \"button_group\")]\npub enum ClientsidePostProcessingSharpeningMode {\n    Disabled = 0,\n    Normal = 1 << 2,\n    Quality = 1 << 3,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Debug)]\npub struct ClientsidePostProcessingConfig {\n    #[schema(strings(\n        help = \"Reduce flicker for high contrast edges.\\nUseful when the input resolution is high compared to the headset display\"\n    ))]\n    pub super_sampling: ClientsidePostProcessingSuperSamplingMode,\n    #[schema(strings(\n        help = \"Improve clarity of high contrast edges and counteract blur.\\nUseful when the input resolution is low compared to the headset display\"\n    ))]\n    pub sharpening: ClientsidePostProcessingSharpeningMode,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Debug)]\npub struct UpscalingConfig {\n    #[schema(strings(\n        help = \"Improves visual quality by using the edge direction to upscale at a slight performance loss\"\n    ))]\n    pub edge_direction: bool,\n    #[schema(gui(slider(min = 1.0, max = 16.0, step = 1.0)))]\n    pub edge_threshold: f32,\n    #[schema(gui(slider(min = 1.0, max = 2.0, step = 0.01)))]\n    pub edge_sharpness: f32,\n    #[schema(gui(slider(min = 1.0, max = 3.0, step = 0.01)))]\n    #[schema(strings(\n        help = \"Dimensional resolution multiplier, high values will cause performance issues with weaker headset hardware or higher resolutions\"\n    ))]\n    pub upscale_factor: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct VideoConfig {\n    #[schema(flag = \"real-time\")]\n    pub passthrough: Switch<PassthroughMode>,\n\n    pub bitrate: BitrateConfig,\n\n    #[schema(strings(\n        help = \"HEVC may provide better visual fidelity at the cost of increased encoder latency\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub preferred_codec: CodecType,\n\n    #[schema(strings(\n        notice = r\"Disabling foveated encoding may result in significantly higher encode/decode times and stuttering, or even crashing.\nIf you want to reduce the amount of pixelation on the edges, increase the center region width and height\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub foveated_encoding: Switch<FoveatedEncodingConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub color_correction: Switch<ColorCorrectionConfig>,\n\n    #[schema(\n        strings(\n            display_name = \"Maximum buffering\",\n            help = \"Increasing this value will help reduce stutter but it will increase latency\"\n        ),\n        gui(slider(min = 1.0, max = 10.0, step = 0.1, logarithmic)),\n        suffix = \" frames\"\n    )]\n    pub max_buffering_frames: f32,\n\n    #[schema(gui(slider(min = 0.50, max = 0.99, step = 0.01)))]\n    pub buffering_history_weight: f32,\n\n    #[cfg_attr(not(target_os = \"windows\"), schema(flag = \"hidden\"))]\n    #[schema(strings(\n        help = r\"This works only on Windows. It shouldn't be disabled except in certain circumstances when you know the VR game will not meet the target framerate.\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    pub enforce_server_frame_pacing: bool,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub encoder_config: EncoderConfig,\n\n    #[schema(strings(\n        help = \"Attempts to use a software decoder on the device. Slow, but may work around broken codecs.\"\n    ))]\n    pub force_software_decoder: bool,\n\n    pub mediacodec_extra_options: Vec<(String, MediacodecProperty)>,\n\n    #[schema(strings(\n        help = \"Resolution used for encoding and decoding. Relative to a single eye view.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub transcoding_view_resolution: FrameSize,\n\n    #[schema(strings(\n        help = \"This is the resolution that SteamVR will use as default for the game rendering. Relative to a single eye view.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub emulated_headset_view_resolution: FrameSize,\n\n    #[schema(strings(display_name = \"Preferred FPS\"))]\n    #[schema(gui(slider(min = 60.0, max = 120.0)), suffix = \"Hz\")]\n    #[schema(flag = \"steamvr-restart\")]\n    pub preferred_fps: f32,\n\n    #[cfg_attr(not(target_os = \"windows\"), schema(flag = \"hidden\"))]\n    #[schema(strings(\n        help = \"You probably don't want to change this. Allows for changing adapter for ALVR compositor.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub adapter_index: u32,\n\n    #[schema(strings(display_name = \"Client-side foveation\"))]\n    pub clientside_foveation: Switch<ClientsideFoveationConfig>,\n\n    #[schema(strings(\n        display_name = \"Client-side post-processing\",\n        help = \"Hardware optimized algorithms, available on Quest and Pico headsets\"\n    ))]\n    #[schema(flag = \"real-time\")]\n    pub clientside_post_processing: Switch<ClientsidePostProcessingConfig>,\n\n    #[schema(strings(help = \"Snapdragon Game Super Resolution client-side upscaling\"))]\n    pub upscaling: Switch<UpscalingConfig>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(gui = \"button_group\")]\npub enum CustomAudioDeviceConfig {\n    #[schema(strings(display_name = \"By name (substring)\"))]\n    NameSubstring(String),\n    #[schema(strings(display_name = \"By index\"))]\n    Index(usize),\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct AudioBufferingConfig {\n    #[schema(strings(display_name = \"Average buffering\"))]\n    #[schema(gui(slider(min = 0, max = 200)), suffix = \"ms\")]\n    pub average_buffering_ms: u64,\n\n    #[schema(strings(display_name = \"Batch size\"))]\n    #[schema(gui(slider(min = 1, max = 20)), suffix = \"ms\")]\n    pub batch_ms: u64,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct GameAudioConfig {\n    #[cfg_attr(target_os = \"linux\", schema(flag = \"hidden\"))]\n    pub device: Option<CustomAudioDeviceConfig>,\n\n    #[cfg_attr(target_os = \"linux\", schema(flag = \"hidden\"))]\n    #[schema(strings(display_name = \"Mute desktop audio when streaming\"))]\n    pub mute_when_streaming: bool,\n\n    pub buffering: AudioBufferingConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum MicrophoneDevicesConfig {\n    Automatic,\n    #[schema(strings(display_name = \"Virtual Audio Cable\"))]\n    VAC,\n    #[schema(strings(display_name = \"VB Cable\"))]\n    VBCable,\n    #[schema(strings(display_name = \"VoiceMeeter\"))]\n    VoiceMeeter,\n    #[schema(strings(display_name = \"VoiceMeeter Aux\"))]\n    VoiceMeeterAux,\n    #[schema(strings(display_name = \"VoiceMeeter VAIO3\"))]\n    VoiceMeeterVaio3,\n    Custom {\n        #[schema(strings(help = \"This device is used by ALVR to output microphone audio\"))]\n        sink: CustomAudioDeviceConfig,\n        #[schema(strings(help = \"This device is set in SteamVR as the default microphone\"))]\n        source: CustomAudioDeviceConfig,\n    },\n}\n\n// Note: sample rate is a free parameter for microphone, because both server and client supports\n// resampling. In contrary, for game audio, the server does not support resampling.\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct MicrophoneConfig {\n    #[cfg_attr(target_os = \"linux\", schema(flag = \"hidden\"))]\n    pub devices: MicrophoneDevicesConfig,\n\n    pub buffering: AudioBufferingConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct AudioConfig {\n    #[schema(strings(display_name = \"Headset speaker\"))]\n    pub game_audio: Switch<GameAudioConfig>,\n\n    #[cfg_attr(\n        windows,\n        schema(strings(\n            display_name = \"Headset microphone\",\n            notice = r\"To be able to use the microphone on Windows, you need to install Virtual Audio Cable\"\n        ))\n    )]\n    #[cfg_attr(not(windows), schema(strings(display_name = \"Headset microphone\")))]\n    pub microphone: Switch<MicrophoneConfig>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum HeadsetEmulationMode {\n    #[schema(strings(display_name = \"Rift S\"))]\n    RiftS,\n    #[schema(strings(display_name = \"Quest 1\"))]\n    Quest1,\n    #[schema(strings(display_name = \"Quest 2\"))]\n    Quest2,\n    #[schema(strings(display_name = \"Quest Pro\"))]\n    QuestPro,\n    #[schema(strings(display_name = \"Pico 4\"))]\n    Pico4,\n    Vive,\n    Custom {\n        serial_number: String,\n    },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, PartialEq, Clone)]\npub enum PerformanceLevel {\n    #[schema(strings(display_name = \"Power Saving\"))]\n    PowerSavings,\n    #[schema(strings(display_name = \"Sustained Low\"))]\n    SustainedLow,\n    #[schema(strings(display_name = \"Sustained High\"))]\n    SustainedHigh,\n    #[schema(flag = \"hidden\")]\n    Boost,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, PartialEq, Clone)]\npub struct PerformanceLevelConfig {\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        display_name = \"CPU\",\n        help = \"When disabling this, the client needs to be restarted for the change to be applied.\"\n    ))]\n    pub cpu: Switch<PerformanceLevel>,\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        display_name = \"GPU\",\n        help = \"When disabling this, the client needs to be restarted for the change to be applied.\"\n    ))]\n    pub gpu: Switch<PerformanceLevel>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub enum FaceTrackingSourcesConfig {\n    PreferEyeTrackingOnly,\n    PreferFullFaceTracking,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum FaceTrackingSinkConfig {\n    #[schema(strings(display_name = \"VRChat Eye OSC\"))]\n    VrchatEyeOsc { port: u16 },\n    #[schema(strings(display_name = \"VRCFaceTracking\"))]\n    VrcFaceTracking,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct FaceTrackingConfig {\n    pub sources: FaceTrackingSourcesConfig,\n    pub sink: FaceTrackingSinkConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Default)]\npub struct BodyTrackingMetaConfig {\n    pub prefer_full_body: bool,\n    #[schema(strings(help = \"Prefer active upper body tracking, Quest 3 only\"))]\n    pub prefer_high_fidelity: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\n#[schema(gui = \"button_group\")]\npub enum BodyTrackingBDConfig {\n    #[schema(strings(display_name = \"Body Tracking\"))]\n    BodyTracking {\n        #[schema(strings(\n            help = \"Improves accuracy of the tracking at the cost of higher latency.\"\n        ))]\n        high_accuracy: bool,\n        #[schema(strings(\n            help = \"If trackers have not been calibrated before, the calibration process will start after you connect to the streamer.\"\n        ))]\n        prompt_calibration_on_start: bool,\n    },\n\n    #[schema(strings(display_name = \"Object Tracking\"))]\n    ObjectTracking,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq)]\npub struct BodyTrackingSourcesConfig {\n    pub meta: BodyTrackingMetaConfig,\n    pub bd: BodyTrackingBDConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum BodyTrackingSinkConfig {\n    #[schema(strings(display_name = \"Fake Vive Trackers\"))]\n    FakeViveTracker,\n    #[schema(strings(display_name = \"VRChat Body OSC\"))]\n    VrchatBodyOsc { port: u16 },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct BodyTrackingConfig {\n    pub sources: BodyTrackingSourcesConfig,\n    pub sink: BodyTrackingSinkConfig,\n    #[schema(strings(help = \"Turn this off to temporarily pause tracking.\"))]\n    #[schema(flag = \"real-time\")]\n    pub tracked: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct VMCConfig {\n    pub host: String,\n    pub port: u16,\n    #[schema(strings(help = \"Turn this off to temporarily pause sending data.\"))]\n    #[schema(flag = \"real-time\")]\n    pub publish: bool,\n    #[schema(flag = \"real-time\")]\n    pub orientation_correction: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, PartialEq, Eq)]\npub enum ControllersEmulationMode {\n    #[schema(strings(display_name = \"Rift S Touch\"))]\n    RiftSTouch,\n    #[schema(strings(display_name = \"Quest 1 Touch\"))]\n    Quest1Touch,\n    #[schema(strings(display_name = \"Quest 2 Touch\"))]\n    Quest2Touch,\n    #[schema(strings(display_name = \"Quest 3 Touch Plus\"))]\n    Quest3Plus,\n    #[schema(strings(display_name = \"Quest Pro\"))]\n    QuestPro,\n    #[schema(strings(display_name = \"Pico 4\"))]\n    Pico4,\n    #[schema(strings(display_name = \"PSVR2 Sense Controller\"))]\n    PSVR2Sense,\n    #[schema(strings(display_name = \"Valve Index\"))]\n    ValveIndex,\n    #[schema(strings(display_name = \"Vive Wand\"))]\n    ViveWand,\n    #[schema(strings(display_name = \"Vive Tracker\"))]\n    ViveTracker,\n    Custom {\n        serial_number: String,\n        button_set: Vec<String>,\n    },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub struct HysteresisThreshold {\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub value: f32,\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub deviation: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub struct BinaryToScalarStates {\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub off: f32,\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub on: f32,\n}\n\n// Remaps 0..1 to custom range\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub struct Range {\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub min: f32,\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub max: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum ButtonMappingType {\n    Passthrough,\n    HysteresisThreshold(HysteresisThreshold),\n    BinaryToScalar(BinaryToScalarStates),\n    Remap(Range),\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct ButtonBindingTarget {\n    pub destination: String,\n    pub mapping_type: ButtonMappingType,\n    pub binary_conditions: Vec<String>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct AutomaticButtonMappingConfig {\n    pub click_threshold: HysteresisThreshold,\n    pub touch_threshold: HysteresisThreshold,\n    pub force_threshold: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct HandTrackingInteractionConfig {\n    #[schema(flag = \"real-time\")]\n    pub only_touch: bool,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How close the tips of your fingers need to be to register a pinch click.\"\n    ))]\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)), suffix = \"cm\")]\n    pub pinch_touch_distance: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How close together the tips of your fingers need to be to start registering a pinch trigger pull.\"\n    ))]\n    #[schema(gui(slider(min = 0.0, max = 2.5, step = 0.025)), suffix = \"cm\")]\n    pub pinch_trigger_distance: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How close to your palm the tips of your fingers need to be to register a curl click.\"\n    ))]\n    #[schema(gui(slider(min = 0.0, max = 5.0)), suffix = \"cm\")]\n    pub curl_touch_distance: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How close to your palm the tips of your fingers need to be to start registering a trigger pull.\"\n    ))]\n    #[schema(gui(slider(min = 0.0, max = 10.0)), suffix = \"cm\")]\n    pub curl_trigger_distance: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.0, max = 100.0)), suffix = \"%\")]\n    pub joystick_deadzone: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -5.0, max = 5.0)), suffix = \"cm\")]\n    pub joystick_offset_horizontal: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = -5.0, max = 5.0)), suffix = \"cm\")]\n    pub joystick_offset_vertical: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"The radius of motion of the joystick. The joystick can be controlled if the thumb is within 2x this range.\"\n    ))]\n    #[schema(gui(slider(min = 0.0, max = 5.0)), suffix = \"cm\")]\n    pub joystick_range: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How long the gesture must be continuously held before it is activated.\"\n    ))]\n    #[schema(gui(slider(min = 0, max = 1000)), suffix = \"ms\")]\n    pub activation_delay: u32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How long the gesture must be continuously released before it is deactivated.\"\n    ))]\n    #[schema(gui(slider(min = 0, max = 1000)), suffix = \"ms\")]\n    pub deactivation_delay: u32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"How long the after the gesture has been deactivated before it can be activated again.\"\n    ))]\n    #[schema(gui(slider(min = 0, max = 1000)), suffix = \"ms\")]\n    pub repeat_delay: u32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct HapticsConfig {\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.0, max = 5.0, step = 0.1)))]\n    pub intensity_multiplier: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)))]\n    pub amplitude_curve: f32,\n\n    #[schema(strings(display_name = \"Minimum duration\"))]\n    #[schema(flag = \"real-time\")]\n    #[schema(gui(slider(min = 0.0, max = 0.1, step = 0.001)), suffix = \"s\")]\n    pub min_duration_s: f32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct HandSkeletonConfig {\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(\n        display_name = \"SteamVR input 2.0\",\n        help = r\"Enabling this will use separate tracker objects with the full skeletal tracking level when hand tracking is detected. This is required for VRChat hand tracking.\"\n    ))]\n    pub steamvr_input_2_0: bool,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = r\"Predict hand skeleton to make it less floaty. It may make hands too jittery.\"\n    ))]\n    pub predict: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\n#[schema(collapsible)]\npub struct ControllersConfig {\n    #[schema(strings(help = \"Turning this off will make the controllers appear powered off.\"))]\n    #[schema(flag = \"real-time\")]\n    pub tracked: bool,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(\n        help = \"Enabling this passes skeletal hand data (finger tracking) to SteamVR.\"\n    ))]\n    pub hand_skeleton: Switch<HandSkeletonConfig>,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(\n        help = \"Enabling this allows using hand gestures to emulate controller inputs.\"\n    ))]\n    pub hand_tracking_interaction: Switch<HandTrackingInteractionConfig>,\n\n    #[schema(strings(\n        display_name = \"Prediction\",\n        help = r\"Higher values make the controllers track smoother.\nTechnically, this is the time (counted in frames) between pose submitted to SteamVR and the corresponding virtual vsync happens.\nCurrently this cannot be reliably estimated automatically. The correct value should be 2 but 3 is default for smoother tracking at the cost of slight lag.\"\n    ))]\n    #[schema(gui(slider(min = 1.0, max = 10.0, logarithmic)), suffix = \"frames\")]\n    pub steamvr_pipeline_frames: f32,\n\n    #[schema(flag = \"real-time\")]\n    pub haptics: Switch<HapticsConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub emulation_mode: ControllersEmulationMode,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(display_name = \"Extra OpenVR properties\"))]\n    pub extra_openvr_props: Vec<OpenvrProperty>,\n\n    #[schema(flag = \"real-time\")]\n    // note: logarithmic scale seems to be glitchy for this control\n    #[schema(gui(slider(min = 0.0, max = 1.0, step = 0.01)), suffix = \"m/s\")]\n    pub linear_velocity_cutoff: f32,\n\n    #[schema(flag = \"real-time\")]\n    // note: logarithmic scale seems to be glitchy for this control\n    #[schema(gui(slider(min = 0.0, max = 100.0, step = 1.0)), suffix = \"°/s\")]\n    pub angular_velocity_cutoff: f32,\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(help = \"Right controller offset is mirrored horizontally\"))]\n    // note: logarithmic scale seems to be glitchy for this control\n    #[schema(gui(slider(min = -0.5, max = 0.5, step = 0.001)), suffix = \"m\")]\n    pub left_controller_position_offset: [f32; 3],\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(help = \"Right controller offset is mirrored horizontally\"))]\n    #[schema(gui(slider(min = -180.0, max = 180.0, step = 1.0)), suffix = \"°\")]\n    pub left_controller_rotation_offset: [f32; 3],\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(help = \"Right controller offset is mirrored horizontally\"))]\n    // note: logarithmic scale seems to be glitchy for this control\n    #[schema(gui(slider(min = -0.5, max = 0.5, step = 0.001)), suffix = \"m\")]\n    pub left_hand_tracking_position_offset: [f32; 3],\n\n    #[schema(flag = \"real-time\")]\n    #[schema(strings(help = \"Right controller offset is mirrored horizontally\"))]\n    #[schema(gui(slider(min = -180.0, max = 180.0, step = 1.0)), suffix = \"°\")]\n    pub left_hand_tracking_rotation_offset: [f32; 3],\n\n    #[schema(strings(help = \"List of OpenXR-syle paths\"))]\n    pub button_mappings: Option<Vec<(String, Vec<ButtonBindingTarget>)>>,\n\n    pub button_mapping_config: AutomaticButtonMappingConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub enum PositionRecenteringMode {\n    Disabled,\n    LocalFloor,\n    Local {\n        #[schema(gui(slider(min = 0.0, max = 3.0)), suffix = \"m\")]\n        view_height: f32,\n    },\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub enum RotationRecenteringMode {\n    Disabled,\n    Yaw,\n    Tilted,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub struct MultimodalTracking {\n    pub enabled: bool,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(\n        display_name = \"Map non-held controllers to SteamVR trackers\",\n        help = \"Non-held controllers are mapped to left and right feet.\nThis will be configurable in the future.\"\n    ))]\n    pub detached_controllers_steamvr_sink: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct HeadsetConfig {\n    #[schema(strings(\n        help = r#\"Disabled: the playspace origin is determined by the room-scale guardian setup.\nLocal floor: the origin is on the floor and resets when long pressing the oculus button.\nLocal: the origin resets when long pressing the oculus button, and is calculated as an offset from the current head position.\"#\n    ))]\n    #[schema(flag = \"real-time\")]\n    pub position_recentering_mode: PositionRecenteringMode,\n\n    #[schema(strings(\n        help = r#\"Disabled: the playspace orientation is determined by the room-scale guardian setup.\nYaw: the forward direction is reset when long pressing the oculus button.\nTilted: the world gets tilted when long pressing the oculus button. This is useful for using VR while laying down.\"#\n    ))]\n    #[schema(flag = \"real-time\")]\n    pub rotation_recentering_mode: RotationRecenteringMode,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub controllers: Switch<ControllersConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub emulation_mode: HeadsetEmulationMode,\n\n    #[schema(strings(\n        help = r#\"Power Savings might increase latency or reduce framerate consistency but decreases temperatures and improves battery life.\nSustained Low provides consistent framerates but might increase latency if necessary.\nSustained High provides consistent framerates but increases temperature.\nThis is mainly for Quest headsets, mileage may vary on other devices.\"#\n    ))]\n    pub performance_level: PerformanceLevelConfig,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(display_name = \"Extra OpenVR properties\"))]\n    pub extra_openvr_props: Vec<OpenvrProperty>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub tracking_ref_only: bool,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub enable_vive_tracker_proxy: bool,\n\n    pub face_tracking: Switch<FaceTrackingConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(\n        help = r\"Track hand skeleton while holding controllers. This will reduce hand tracking frequency to 30Hz.\nBecause of runtime limitations, this option is ignored when body tracking is active.\"\n    ))]\n    pub multimodal_tracking: Switch<MultimodalTracking>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub body_tracking: Switch<BodyTrackingConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(strings(display_name = \"VMC\"))]\n    pub vmc: Switch<VMCConfig>,\n\n    #[schema(strings(\n        help = \"Maximum prediction for head and controllers. Used to avoid too much jitter during loading.\"\n    ))]\n    #[schema(gui(slider(min = 0, max = 200, step = 5)), suffix = \"ms\")]\n    pub max_prediction_ms: u64,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\n#[schema(gui = \"button_group\")]\npub enum SocketProtocol {\n    #[schema(strings(display_name = \"UDP\"))]\n    Udp,\n    #[schema(strings(display_name = \"TCP\"))]\n    Tcp,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct DiscoveryConfig {\n    #[cfg_attr(target_os = \"linux\", schema(flag = \"hidden\"))]\n    #[schema(strings(\n        help = \"Allow untrusted clients to connect without confirmation. This is not recommended for security reasons.\"\n    ))]\n    pub auto_trust_clients: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy, Default)]\npub enum SocketBufferSize {\n    #[default]\n    Default,\n    Maximum,\n    Custom(#[schema(suffix = \"B\")] u32),\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy, Default)]\npub struct SocketBufferConfig {\n    #[schema(strings(display_name = \"Send size\"))]\n    pub send_size_bytes: SocketBufferSize,\n    #[schema(strings(display_name = \"Receive size\"))]\n    pub recv_size_bytes: SocketBufferSize,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct WiredClientAutoLaunchConfig {\n    #[schema(strings(\n        help = \"Delay in seconds to wait after booting the headset before trying to launch the client.\"\n    ))]\n    pub boot_delay: u32,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct ConnectionConfig {\n    #[schema(strings(\n        help = r#\"UDP: Faster, but less stable than TCP. Try this if your network is well optimized and free of interference.\nTCP: Slower than UDP, but more stable. Pick this if you experience video or audio stutters with UDP.\"#\n    ))]\n    pub stream_protocol: SocketProtocol,\n\n    pub client_discovery: Switch<DiscoveryConfig>,\n\n    #[schema(strings(\n        help = r#\"Which release type of client should ALVR look for when establishing a wired connection.\"#\n    ))]\n    pub wired_client_type: ClientFlavor,\n\n    #[schema(strings(\n        help = r#\"Wether ALVR should try to automatically launch the client when establishing a wired connection.\"#\n    ))]\n    pub wired_client_autolaunch: Switch<WiredClientAutoLaunchConfig>,\n\n    #[cfg_attr(\n        windows,\n        schema(strings(\n            help = \"If on_connect.bat exists alongside session.json, it will be run on headset connect. Env var ACTION will be set to `connect`.\"\n        ))\n    )]\n    #[cfg_attr(\n        not(windows),\n        schema(strings(\n            help = \"If on_connect.sh exists alongside session.json, it will be run on headset connect. Env var ACTION will be set to `connect`.\"\n        ))\n    )]\n    pub enable_on_connect_script: bool,\n\n    #[cfg_attr(\n        windows,\n        schema(strings(\n            help = \"If on_disconnect.bat exists alongside session.json, it will be run on headset disconnect. Env var ACTION will be set to `disconnect`.\"\n        ))\n    )]\n    #[cfg_attr(\n        not(windows),\n        schema(strings(\n            help = \"If on_disconnect.sh exists alongside session.json, it will be run on headset disconnect. Env var ACTION will be set to `disconnect`.\"\n        ))\n    )]\n    #[schema(flag = \"real-time\")]\n    pub enable_on_disconnect_script: bool,\n\n    #[schema(strings(\n        display_name = \"Allow untrusted HTTP\",\n        help = \"Allow cross-origin browser requests to control ALVR settings remotely.\"\n    ))]\n    pub allow_untrusted_http: bool,\n\n    #[schema(strings(\n        help = r#\"If the client, server or the network discarded one packet, discard packets until a IDR packet is found.\"#\n    ))]\n    pub avoid_video_glitching: bool,\n\n    #[schema(gui(slider(min = 1024, max = 65507, logarithmic)), suffix = \"B\")]\n    pub packet_size: i32,\n\n    pub stream_port: u16,\n    pub web_server_port: u16,\n\n    #[schema(strings(display_name = \"Local OSC port\"))]\n    pub osc_local_port: u16,\n\n    pub server_buffer_config: SocketBufferConfig,\n    pub client_buffer_config: SocketBufferConfig,\n\n    #[schema(strings(\n        help = r#\"The server discards video packets if it can't push them to the network.\nThis could happen on TCP. A IDR frame is requested in this case.\"#\n    ))]\n    pub max_queued_server_video_frames: usize,\n\n    #[schema(suffix = \" frames\")]\n    pub statistics_history_size: usize,\n\n    #[schema(strings(display_name = \"Minimum IDR interval\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    #[schema(gui(slider(min = 5, max = 1000, step = 5)), suffix = \"ms\")]\n    pub minimum_idr_interval_ms: u64,\n\n    #[schema(strings(display_name = \"DSCP (packet prio hints)\"))]\n    pub dscp: Option<DscpTos>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\n#[repr(u8)]\n#[schema(gui = \"button_group\")]\npub enum DropProbability {\n    Low = 0x01,\n    Medium = 0x10,\n    High = 0x11,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone, Copy)]\npub enum DscpTos {\n    BestEffort,\n\n    ClassSelector(#[schema(gui(slider(min = 1, max = 7)))] u8),\n\n    AssuredForwarding {\n        #[schema(gui(slider(min = 1, max = 4)))]\n        class: u8,\n        drop_probability: DropProbability,\n    },\n\n    ExpeditedForwarding,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct RawEventsConfig {\n    #[schema(flag = \"real-time\")]\n    pub hide_spammy_events: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct LoggingConfig {\n    #[schema(strings(help = \"Notification tips teach you how to use ALVR\"))]\n    pub show_notification_tip: bool,\n\n    #[schema(strings(help = \"This applies only to certain error or warning messages.\"))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub prefer_backtrace: bool,\n\n    #[schema(flag = \"real-time\")]\n    pub notification_level: LogSeverity,\n\n    pub client_log_report_level: Switch<LogSeverity>,\n\n    #[schema(flag = \"real-time\")]\n    pub show_raw_events: Switch<RawEventsConfig>,\n\n    #[schema(strings(help = \"Write logs into the session_log.txt file.\"))]\n    pub log_to_disk: bool,\n\n    #[schema(flag = \"real-time\")]\n    pub log_tracking: bool,\n\n    #[schema(flag = \"real-time\")]\n    pub log_button_presses: bool,\n\n    #[schema(flag = \"real-time\")]\n    pub log_haptics: bool,\n\n    #[cfg_attr(not(debug_assertions), schema(flag = \"hidden\"))]\n    #[schema(strings(help = \"These settings enable extra spammy logs for debugging purposes.\"))]\n    pub debug_groups: DebugGroupsConfig,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct SteamvrLauncher {\n    #[schema(strings(\n        display_name = \"Open and close SteamVR automatically\",\n        help = \"Launches SteamVR automatically when the ALVR dashboard is opened, and closes it when the dashboard is closed.\"\n    ))]\n    pub open_close_steamvr_with_dashboard: bool,\n\n    #[cfg_attr(\n        windows,\n        schema(strings(help = \"Directly start the VR server, bypassing Steam. \\\n                Will run start_server.bat if it exists alongside session.json, and try to automatically find SteamVR otherwise.\"))\n    )]\n    #[cfg_attr(\n        not(windows),\n        schema(strings(help = \"Directly start the VR server, bypassing Steam. \\\n                Will run start_server.sh if it exists alongside session.json, and try to automatically find SteamVR otherwise.\"))\n    )]\n    pub direct_launch: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct RollingVideoFilesConfig {\n    #[schema(strings(display_name = \"Duration\"))]\n    #[schema(suffix = \"s\")]\n    pub duration_s: u64,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct CaptureConfig {\n    #[schema(strings(display_name = \"Start video recording at client connection\"))]\n    pub startup_video_recording: bool,\n\n    pub rolling_video_files: Switch<RollingVideoFilesConfig>,\n\n    #[schema(flag = \"steamvr-restart\")]\n    pub capture_frame_dir: String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct Patches {\n    #[schema(strings(\n        help = \"Async Compute is currently broken in SteamVR, keep disabled. ONLY FOR TESTING.\"\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub linux_async_compute: bool,\n    #[schema(strings(\n        help = \"Async reprojection only works if you can always hit at least half of your refresh rate.\",\n    ))]\n    #[schema(flag = \"steamvr-restart\")]\n    pub linux_async_reprojection: bool,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct NewVersionPopupConfig {\n    pub hide_while_version: String,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct ExtraConfig {\n    #[schema(strings(display_name = \"SteamVR Launcher\"))]\n    pub steamvr_launcher: SteamvrLauncher,\n    pub capture: CaptureConfig,\n    pub logging: LoggingConfig,\n    #[cfg_attr(not(target_os = \"linux\"), schema(flag = \"hidden\"))]\n    pub patches: Patches,\n\n    #[schema(\n        strings(help = \"Linear and angular velocity multiplier for debug purposes.\nIt does not update in real time.\")\n    )]\n    pub velocities_multiplier: f32,\n\n    pub open_setup_wizard: bool,\n    pub new_version_popup: Switch<NewVersionPopupConfig>,\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub struct Settings {\n    pub video: VideoConfig,\n    pub audio: AudioConfig,\n    pub headset: HeadsetConfig,\n    pub connection: ConnectionConfig,\n    pub extra: ExtraConfig,\n}\n\npub fn session_settings_default() -> SettingsDefault {\n    let view_resolution = FrameSizeDefault {\n        variant: FrameSizeDefaultVariant::Absolute,\n        Scale: 1.0,\n        Absolute: FrameSizeAbsoluteDefault {\n            width: 2144,\n            height: OptionalDefault {\n                set: false,\n                content: 1072,\n            },\n        },\n    };\n    let default_custom_audio_device = CustomAudioDeviceConfigDefault {\n        NameSubstring: \"\".into(),\n        Index: 0,\n        variant: CustomAudioDeviceConfigDefaultVariant::NameSubstring,\n    };\n    let default_custom_openvr_props = VectorDefault {\n        gui_collapsed: true,\n        element: OpenvrPropertyDefault {\n            key: OpenvrPropKeyDefault {\n                variant: OpenvrPropKeyDefaultVariant::TrackingSystemNameString,\n            },\n            value: \"\".into(),\n        },\n        content: vec![],\n    };\n    let socket_buffer = SocketBufferSizeDefault {\n        Custom: 100000,\n        variant: SocketBufferSizeDefaultVariant::Maximum,\n    };\n    let socket_buffer_config = SocketBufferConfigDefault {\n        send_size_bytes: socket_buffer.clone(),\n        recv_size_bytes: socket_buffer,\n    };\n\n    SettingsDefault {\n        video: VideoConfigDefault {\n            passthrough: SwitchDefault {\n                enabled: false,\n                content: PassthroughModeDefault {\n                    variant: PassthroughModeDefaultVariant::Blend,\n                    Blend: PassthroughModeBlendDefault {\n                        premultiplied_alpha: true,\n                        threshold: 0.5,\n                    },\n                    RgbChromaKey: RgbChromaKeyConfigDefault {\n                        red: 0,\n                        green: 255,\n                        blue: 0,\n                        distance_threshold: 85,\n                        feathering: 0.05,\n                    },\n                    HsvChromaKey: HsvChromaKeyConfigDefault {\n                        hue_start_max_deg: 70.0,\n                        hue_start_min_deg: 80.0,\n                        hue_end_min_deg: 160.0,\n                        hue_end_max_deg: 170.0,\n                        saturation_start_max: 0.2,\n                        saturation_start_min: 0.3,\n                        saturation_end_min: 1.0,\n                        saturation_end_max: 1.1,\n                        value_start_max: 0.0,\n                        value_start_min: 0.1,\n                        value_end_min: 1.0,\n                        value_end_max: 1.1,\n                    },\n                },\n            },\n            clientside_post_processing: SwitchDefault {\n                enabled: false,\n                content: ClientsidePostProcessingConfigDefault {\n                    super_sampling: ClientsidePostProcessingSuperSamplingModeDefault {\n                        variant: ClientsidePostProcessingSuperSamplingModeDefaultVariant::Quality,\n                    },\n                    sharpening: ClientsidePostProcessingSharpeningModeDefault {\n                        variant: ClientsidePostProcessingSharpeningModeDefaultVariant::Quality,\n                    },\n                },\n            },\n            upscaling: SwitchDefault {\n                enabled: false,\n                content: UpscalingConfigDefault {\n                    edge_direction: true,\n                    edge_threshold: 4.0,\n                    edge_sharpness: 2.0,\n                    upscale_factor: 1.5,\n                },\n            },\n            adapter_index: 0,\n            transcoding_view_resolution: view_resolution.clone(),\n            emulated_headset_view_resolution: view_resolution,\n            preferred_fps: 72.,\n            max_buffering_frames: 2.0,\n            buffering_history_weight: 0.90,\n            enforce_server_frame_pacing: true,\n            bitrate: BitrateConfigDefault {\n                gui_collapsed: false,\n                mode: BitrateModeDefault {\n                    ConstantMbps: 30,\n                    Adaptive: BitrateModeAdaptiveDefault {\n                        gui_collapsed: true,\n                        saturation_multiplier: 0.95,\n                        max_throughput_mbps: SwitchDefault {\n                            enabled: false,\n                            content: 100,\n                        },\n                        min_throughput_mbps: SwitchDefault {\n                            enabled: false,\n                            content: 5,\n                        },\n                        max_network_latency_ms: SwitchDefault {\n                            enabled: false,\n                            content: 8,\n                        },\n                        encoder_latency_limiter: SwitchDefault {\n                            enabled: true,\n                            content: EncoderLatencyLimiterDefault {\n                                max_saturation_multiplier: 0.9,\n                            },\n                        },\n                        decoder_latency_limiter: SwitchDefault {\n                            enabled: true,\n                            content: DecoderLatencyLimiterDefault {\n                                gui_collapsed: false,\n                                max_decoder_latency_ms: 30,\n                                latency_overstep_frames: 90,\n                                latency_overstep_multiplier: 0.99,\n                            },\n                        },\n                    },\n                    variant: BitrateModeDefaultVariant::ConstantMbps,\n                },\n                adapt_to_framerate: SwitchDefault {\n                    enabled: false,\n                    content: BitrateAdaptiveFramerateConfigDefault {\n                        framerate_reset_threshold_multiplier: 2.0,\n                    },\n                },\n                history_size: 256,\n                image_corruption_fix: false,\n            },\n            preferred_codec: CodecTypeDefault {\n                variant: CodecTypeDefaultVariant::H264,\n            },\n            encoder_config: EncoderConfigDefault {\n                gui_collapsed: true,\n                rate_control_mode: RateControlModeDefault {\n                    variant: RateControlModeDefaultVariant::Cbr,\n                },\n                filler_data: false,\n                h264_profile: H264ProfileDefault {\n                    variant: H264ProfileDefaultVariant::High,\n                },\n                entropy_coding: EntropyCodingDefault {\n                    variant: EntropyCodingDefaultVariant::Cavlc,\n                },\n                use_10bit: OptionalDefault {\n                    set: false,\n                    content: false,\n                },\n                encoding_gamma: OptionalDefault {\n                    set: false,\n                    content: 1.0,\n                },\n                hdr: HDRConfigDefault {\n                    gui_collapsed: true,\n                    enable: OptionalDefault {\n                        set: false,\n                        content: false,\n                    },\n                    force_hdr_srgb_correction: false,\n                    clamp_hdr_extended_range: false,\n                },\n                nvenc: NvencConfigDefault {\n                    gui_collapsed: true,\n                    quality_preset: EncoderQualityPresetNvidiaDefault {\n                        variant: EncoderQualityPresetNvidiaDefaultVariant::P1,\n                    },\n                    tuning_preset: NvencTuningPresetDefault {\n                        variant: NvencTuningPresetDefaultVariant::LowLatency,\n                    },\n                    multi_pass: NvencMultiPassDefault {\n                        variant: NvencMultiPassDefaultVariant::QuarterResolution,\n                    },\n                    adaptive_quantization_mode: NvencAdaptiveQuantizationModeDefault {\n                        variant: NvencAdaptiveQuantizationModeDefaultVariant::Spatial,\n                    },\n                    low_delay_key_frame_scale: -1,\n                    refresh_rate: -1,\n                    enable_intra_refresh: false,\n                    intra_refresh_period: -1,\n                    intra_refresh_count: -1,\n                    max_num_ref_frames: -1,\n                    gop_length: -1,\n                    p_frame_strategy: -1,\n                    rate_control_mode: -1,\n                    rc_buffer_size: -1,\n                    rc_initial_delay: -1,\n                    rc_max_bitrate: -1,\n                    rc_average_bitrate: -1,\n                    enable_weighted_prediction: false,\n                },\n                quality_preset: EncoderQualityPresetDefault {\n                    variant: EncoderQualityPresetDefaultVariant::Speed,\n                },\n                enable_vbaq: false,\n                amf: AmfConfigDefault {\n                    gui_collapsed: true,\n                    enable_pre_analysis: false,\n                    enable_hmqb: false,\n                    use_preproc: false,\n                    preproc_sigma: 4,\n                    preproc_tor: 7,\n                },\n                software: SoftwareEncodingConfigDefault {\n                    gui_collapsed: true,\n                    force_software_encoding: false,\n                    thread_count: 0,\n                },\n            },\n            mediacodec_extra_options: {\n                fn int32_default(int32: i32) -> MediacodecPropertyDefault {\n                    MediacodecPropertyDefault {\n                        ty: MediacodecPropTypeDefault {\n                            variant: MediacodecPropTypeDefaultVariant::Int32,\n                        },\n                        value: int32.to_string(),\n                    }\n                }\n                DictionaryDefault {\n                    gui_collapsed: true,\n                    key: \"\".into(),\n                    value: int32_default(0),\n                    content: vec![\n                        (\"operating-rate\".into(), int32_default(i32::MAX)),\n                        (\"priority\".into(), int32_default(0)),\n                        // low-latency: only applicable on API level 30. Quest 1 and 2 might not be\n                        // cabable, since they are on level 29.\n                        // (\"low-latency\".into(), int32_default(1)), // Android smartphones crashes enabling this feature (https://github.com/PhoneVR-Developers/alvr-cardboard/issues/5)\n                        (\n                            \"vendor.qti-ext-dec-low-latency.enable\".into(),\n                            int32_default(1),\n                        ),\n                    ],\n                }\n            },\n            foveated_encoding: SwitchDefault {\n                enabled: true,\n                content: FoveatedEncodingConfigDefault {\n                    gui_collapsed: true,\n                    force_enable: false,\n                    center_size_x: 0.45,\n                    center_size_y: 0.4,\n                    center_shift_x: 0.4,\n                    center_shift_y: 0.1,\n                    edge_ratio_x: 4.,\n                    edge_ratio_y: 5.,\n                },\n            },\n            clientside_foveation: SwitchDefault {\n                enabled: false,\n                content: ClientsideFoveationConfigDefault {\n                    mode: ClientsideFoveationModeDefault {\n                        Static: ClientsideFoveationModeStaticDefault {\n                            level: ClientsideFoveationLevelDefault {\n                                variant: ClientsideFoveationLevelDefaultVariant::High,\n                            },\n                        },\n                        Dynamic: ClientsideFoveationModeDynamicDefault {\n                            max_level: ClientsideFoveationLevelDefault {\n                                variant: ClientsideFoveationLevelDefaultVariant::High,\n                            },\n                        },\n                        variant: ClientsideFoveationModeDefaultVariant::Dynamic,\n                    },\n                    vertical_offset_deg: 0.0,\n                },\n            },\n            force_software_decoder: false,\n            color_correction: SwitchDefault {\n                enabled: false,\n                content: ColorCorrectionConfigDefault {\n                    brightness: 0.,\n                    contrast: 0.,\n                    saturation: 0.5,\n                    gamma: 1.,\n                    sharpening: 0.5,\n                },\n            },\n        },\n        audio: AudioConfigDefault {\n            game_audio: SwitchDefault {\n                enabled: true,\n                content: GameAudioConfigDefault {\n                    gui_collapsed: true,\n                    device: OptionalDefault {\n                        set: false,\n                        content: default_custom_audio_device.clone(),\n                    },\n                    mute_when_streaming: true,\n                    buffering: AudioBufferingConfigDefault {\n                        gui_collapsed: true,\n                        average_buffering_ms: 50,\n                        batch_ms: 10,\n                    },\n                },\n            },\n            microphone: SwitchDefault {\n                enabled: cfg!(target_os = \"linux\"),\n                content: MicrophoneConfigDefault {\n                    gui_collapsed: true,\n                    devices: MicrophoneDevicesConfigDefault {\n                        Custom: MicrophoneDevicesConfigCustomDefault {\n                            source: default_custom_audio_device.clone(),\n                            sink: default_custom_audio_device,\n                        },\n                        variant: MicrophoneDevicesConfigDefaultVariant::Automatic,\n                    },\n                    buffering: AudioBufferingConfigDefault {\n                        gui_collapsed: true,\n                        average_buffering_ms: 50,\n                        batch_ms: 10,\n                    },\n                },\n            },\n        },\n        headset: HeadsetConfigDefault {\n            emulation_mode: HeadsetEmulationModeDefault {\n                Custom: HeadsetEmulationModeCustomDefault {\n                    serial_number: \"Unknown\".into(),\n                },\n                variant: HeadsetEmulationModeDefaultVariant::Quest2,\n            },\n            performance_level: PerformanceLevelConfigDefault {\n                cpu: SwitchDefault {\n                    enabled: false,\n                    content: PerformanceLevelDefault {\n                        variant: PerformanceLevelDefaultVariant::PowerSavings,\n                    },\n                },\n                gpu: SwitchDefault {\n                    enabled: false,\n                    content: PerformanceLevelDefault {\n                        variant: PerformanceLevelDefaultVariant::PowerSavings,\n                    },\n                },\n            },\n            extra_openvr_props: default_custom_openvr_props.clone(),\n            tracking_ref_only: false,\n            enable_vive_tracker_proxy: false,\n            face_tracking: SwitchDefault {\n                enabled: false,\n                content: FaceTrackingConfigDefault {\n                    gui_collapsed: true,\n                    sources: FaceTrackingSourcesConfigDefault {\n                        variant: FaceTrackingSourcesConfigDefaultVariant::PreferFullFaceTracking,\n                    },\n                    sink: FaceTrackingSinkConfigDefault {\n                        VrchatEyeOsc: FaceTrackingSinkConfigVrchatEyeOscDefault { port: 9000 },\n                        variant: FaceTrackingSinkConfigDefaultVariant::VrchatEyeOsc,\n                    },\n                },\n            },\n            multimodal_tracking: SwitchDefault {\n                enabled: false,\n                content: MultimodalTrackingDefault {\n                    enabled: true,\n                    detached_controllers_steamvr_sink: false,\n                },\n            },\n            body_tracking: SwitchDefault {\n                enabled: false,\n                content: BodyTrackingConfigDefault {\n                    gui_collapsed: true,\n                    sources: BodyTrackingSourcesConfigDefault {\n                        meta: BodyTrackingMetaConfigDefault {\n                            prefer_full_body: true,\n                            prefer_high_fidelity: true,\n                        },\n                        bd: BodyTrackingBDConfigDefault {\n                            BodyTracking: BodyTrackingBDConfigBodyTrackingDefault {\n                                high_accuracy: true,\n                                prompt_calibration_on_start: true,\n                            },\n                            variant: BodyTrackingBDConfigDefaultVariant::BodyTracking,\n                        },\n                    },\n                    sink: BodyTrackingSinkConfigDefault {\n                        VrchatBodyOsc: BodyTrackingSinkConfigVrchatBodyOscDefault { port: 9000 },\n                        variant: BodyTrackingSinkConfigDefaultVariant::FakeViveTracker,\n                    },\n                    tracked: true,\n                },\n            },\n            vmc: SwitchDefault {\n                enabled: false,\n                content: VMCConfigDefault {\n                    gui_collapsed: true,\n                    host: \"127.0.0.1\".into(),\n                    port: 39539,\n                    publish: true,\n                    orientation_correction: true,\n                },\n            },\n            controllers: SwitchDefault {\n                enabled: true,\n                content: ControllersConfigDefault {\n                    gui_collapsed: false,\n                    tracked: true,\n                    hand_skeleton: SwitchDefault {\n                        enabled: true,\n                        content: HandSkeletonConfigDefault {\n                            steamvr_input_2_0: true,\n                            predict: false,\n                        },\n                    },\n                    emulation_mode: ControllersEmulationModeDefault {\n                        Custom: ControllersEmulationModeCustomDefault {\n                            serial_number: \"ALVR Controller\".into(),\n                            button_set: VectorDefault {\n                                gui_collapsed: false,\n                                element: \"/user/hand/left/input/a/click\".into(),\n                                content: vec![],\n                            },\n                        },\n                        variant: ControllersEmulationModeDefaultVariant::Quest2Touch,\n                    },\n                    extra_openvr_props: default_custom_openvr_props,\n                    button_mappings: OptionalDefault {\n                        set: false,\n                        content: DictionaryDefault {\n                            gui_collapsed: false,\n                            key: \"/user/hand/left/input/a/click\".into(),\n                            value: VectorDefault {\n                                gui_collapsed: false,\n                                element: ButtonBindingTargetDefault {\n                                    destination: \"/user/hand/left/input/a/click\".into(),\n                                    mapping_type: ButtonMappingTypeDefault {\n                                        HysteresisThreshold: HysteresisThresholdDefault {\n                                            value: 0.5,\n                                            deviation: 0.05,\n                                        },\n                                        BinaryToScalar: BinaryToScalarStatesDefault {\n                                            off: 0.0,\n                                            on: 1.0,\n                                        },\n                                        Remap: RangeDefault { min: 0.0, max: 1.0 },\n                                        variant: ButtonMappingTypeDefaultVariant::Passthrough,\n                                    },\n                                    binary_conditions: VectorDefault {\n                                        gui_collapsed: true,\n                                        element: \"/user/hand/left/input/trigger/touch\".into(),\n                                        content: vec![],\n                                    },\n                                },\n                                content: vec![],\n                            },\n                            content: vec![],\n                        },\n                    },\n                    button_mapping_config: AutomaticButtonMappingConfigDefault {\n                        gui_collapsed: true,\n                        click_threshold: HysteresisThresholdDefault {\n                            value: 0.5,\n                            deviation: 0.05,\n                        },\n                        touch_threshold: HysteresisThresholdDefault {\n                            value: 0.1,\n                            deviation: 0.05,\n                        },\n                        force_threshold: 0.8,\n                    },\n                    hand_tracking_interaction: SwitchDefault {\n                        enabled: false,\n                        content: HandTrackingInteractionConfigDefault {\n                            only_touch: false,\n                            pinch_touch_distance: 0.0,\n                            pinch_trigger_distance: 0.25,\n                            curl_touch_distance: 2.0,\n                            curl_trigger_distance: 2.5,\n                            joystick_deadzone: 40.0,\n                            joystick_offset_horizontal: 0.0,\n                            joystick_offset_vertical: 0.0,\n                            joystick_range: 1.0,\n                            repeat_delay: 100,\n                            activation_delay: 50,\n                            deactivation_delay: 100,\n                        },\n                    },\n                    steamvr_pipeline_frames: 2.1,\n                    linear_velocity_cutoff: 0.05,\n                    angular_velocity_cutoff: 10.0,\n                    left_controller_position_offset: ArrayDefault {\n                        gui_collapsed: true,\n                        content: [0.0, 0.0, -0.11],\n                    },\n                    left_controller_rotation_offset: ArrayDefault {\n                        gui_collapsed: true,\n                        content: [0.0; 3],\n                    },\n                    left_hand_tracking_position_offset: ArrayDefault {\n                        gui_collapsed: true,\n                        content: [0.04, -0.02, -0.13],\n                    },\n                    left_hand_tracking_rotation_offset: ArrayDefault {\n                        gui_collapsed: true,\n                        content: [0.0, -45.0, -90.0],\n                    },\n                    haptics: SwitchDefault {\n                        enabled: true,\n                        content: HapticsConfigDefault {\n                            gui_collapsed: true,\n                            intensity_multiplier: 1.0,\n                            amplitude_curve: 1.0,\n                            min_duration_s: 0.01,\n                        },\n                    },\n                },\n            },\n            position_recentering_mode: PositionRecenteringModeDefault {\n                Local: PositionRecenteringModeLocalDefault { view_height: 1.5 },\n                variant: PositionRecenteringModeDefaultVariant::LocalFloor,\n            },\n            rotation_recentering_mode: RotationRecenteringModeDefault {\n                variant: RotationRecenteringModeDefaultVariant::Yaw,\n            },\n            max_prediction_ms: 100,\n        },\n        connection: ConnectionConfigDefault {\n            stream_protocol: SocketProtocolDefault {\n                variant: SocketProtocolDefaultVariant::Udp,\n            },\n            client_discovery: SwitchDefault {\n                enabled: true,\n                content: DiscoveryConfigDefault {\n                    auto_trust_clients: cfg!(debug_assertions),\n                },\n            },\n            wired_client_type: ClientFlavorDefault {\n                Custom: \"alvr.client\".to_owned(),\n                variant: if alvr_common::is_stable() {\n                    ClientFlavorDefaultVariant::Store\n                } else {\n                    ClientFlavorDefaultVariant::Github\n                },\n            },\n            wired_client_autolaunch: SwitchDefault {\n                enabled: true,\n                content: WiredClientAutoLaunchConfigDefault { boot_delay: 0 },\n            },\n            web_server_port: 8082,\n            stream_port: 9944,\n            osc_local_port: 9942,\n            dscp: OptionalDefault {\n                set: false,\n                content: DscpTosDefault {\n                    ClassSelector: 7,\n                    AssuredForwarding: DscpTosAssuredForwardingDefault {\n                        class: 4,\n                        drop_probability: DropProbabilityDefault {\n                            variant: DropProbabilityDefaultVariant::Low,\n                        },\n                    },\n                    variant: DscpTosDefaultVariant::ExpeditedForwarding,\n                },\n            },\n            server_buffer_config: socket_buffer_config.clone(),\n            client_buffer_config: socket_buffer_config,\n            max_queued_server_video_frames: 1024,\n            avoid_video_glitching: false,\n            minimum_idr_interval_ms: 100,\n            enable_on_connect_script: false,\n            enable_on_disconnect_script: false,\n            allow_untrusted_http: false,\n            packet_size: 1400,\n            statistics_history_size: 256,\n        },\n        extra: ExtraConfigDefault {\n            logging: LoggingConfigDefault {\n                client_log_report_level: SwitchDefault {\n                    enabled: true,\n                    content: LogSeverityDefault {\n                        variant: LogSeverityDefaultVariant::Error,\n                    },\n                },\n                log_to_disk: cfg!(debug_assertions),\n                log_button_presses: false,\n                log_tracking: false,\n                log_haptics: false,\n                notification_level: LogSeverityDefault {\n                    variant: if cfg!(debug_assertions) {\n                        LogSeverityDefaultVariant::Info\n                    } else {\n                        LogSeverityDefaultVariant::Warning\n                    },\n                },\n                show_raw_events: SwitchDefault {\n                    enabled: false,\n                    content: RawEventsConfigDefault {\n                        hide_spammy_events: false,\n                    },\n                },\n                prefer_backtrace: false,\n                show_notification_tip: true,\n                debug_groups: DebugGroupsConfigDefault {\n                    server_impl: false,\n                    client_impl: false,\n                    server_core: false,\n                    client_core: false,\n                    connection: false,\n                    sockets: false,\n                    server_gfx: false,\n                    client_gfx: false,\n                    encoder: false,\n                    decoder: false,\n                },\n            },\n            steamvr_launcher: SteamvrLauncherDefault {\n                open_close_steamvr_with_dashboard: false,\n                direct_launch: false,\n            },\n            capture: CaptureConfigDefault {\n                startup_video_recording: false,\n                rolling_video_files: SwitchDefault {\n                    enabled: false,\n                    content: RollingVideoFilesConfigDefault { duration_s: 5 },\n                },\n                capture_frame_dir: if !cfg!(target_os = \"linux\") {\n                    \"/tmp\".into()\n                } else {\n                    \"\".into()\n                },\n            },\n            patches: PatchesDefault {\n                linux_async_compute: false,\n                linux_async_reprojection: false,\n            },\n            velocities_multiplier: 1.0,\n            open_setup_wizard: alvr_common::is_stable() || alvr_common::is_nightly(),\n            new_version_popup: SwitchDefault {\n                enabled: alvr_common::is_stable(),\n                content: NewVersionPopupConfigDefault {\n                    hide_while_version: ALVR_VERSION.to_string(),\n                },\n            },\n        },\n    }\n}\n"
  },
  {
    "path": "alvr/sockets/Cargo.toml",
    "content": "[package]\nname = \"alvr_sockets\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[features]\ntrace-performance = [\"profiling/profile-with-tracy\"]\n\n[dependencies]\nalvr_common.workspace = true\nalvr_session.workspace = true\n\nbincode = { version = \"2\", features = [\"serde\"] }\nprofiling = { version = \"1\", optional = true }\nserde = \"1\"\nserde_json = \"1\"\nsocket2 = \"0.6\"\n"
  },
  {
    "path": "alvr/sockets/src/control_socket.rs",
    "content": "use crate::{CONTROL_PORT, LOCAL_IP};\nuse alvr_common::{ConResult, HandleTryAgain, ToCon, anyhow::Result, con_bail};\nuse alvr_session::{DscpTos, SocketBufferConfig};\nuse bincode::config;\nuse serde::{Serialize, de::DeserializeOwned};\nuse std::{\n    io::{Read, Write},\n    marker::PhantomData,\n    mem,\n    net::{IpAddr, SocketAddr, TcpListener, TcpStream},\n    time::{Duration, Instant},\n};\n\n// This corresponds to the length of the payload\nconst FRAMED_PREFIX_LENGTH: usize = mem::size_of::<u32>();\n\npub fn bind(\n    timeout: Duration,\n    port: u16,\n    dscp: Option<DscpTos>,\n    buffer_config: SocketBufferConfig,\n) -> Result<TcpListener> {\n    let socket = TcpListener::bind((LOCAL_IP, port))?.into();\n\n    crate::set_socket_buffers(&socket, buffer_config).ok();\n\n    crate::set_dscp(&socket, dscp);\n\n    socket.set_read_timeout(Some(timeout))?;\n\n    Ok(socket.into())\n}\n\npub fn accept_from_server(\n    listener: &TcpListener,\n    server_ip: Option<IpAddr>,\n    timeout: Duration,\n) -> ConResult<(TcpStream, TcpStream)> {\n    // Uses timeout set during bind()\n    let (socket, server_address) = listener.accept().handle_try_again()?;\n\n    if let Some(ip) = server_ip\n        && server_address.ip() != ip\n    {\n        con_bail!(\n            \"Connected to wrong client: Expected: {ip}, Found {}\",\n            server_address.ip()\n        );\n    }\n\n    socket.set_read_timeout(Some(timeout)).to_con()?;\n    socket.set_nodelay(true).to_con()?;\n\n    Ok((socket.try_clone().to_con()?, socket))\n}\n\npub fn connect_to_client(\n    timeout: Duration,\n    client_ips: &[IpAddr],\n    port: u16,\n    buffer_config: SocketBufferConfig,\n) -> ConResult<(TcpStream, TcpStream)> {\n    let split_timeout = timeout / client_ips.len() as u32;\n\n    let mut res = alvr_common::try_again();\n    for ip in client_ips {\n        res = TcpStream::connect_timeout(&SocketAddr::new(*ip, port), split_timeout)\n            .handle_try_again();\n\n        if res.is_ok() {\n            break;\n        }\n    }\n    let socket = res?.into();\n\n    crate::set_socket_buffers(&socket, buffer_config).ok();\n    socket.set_read_timeout(Some(timeout)).to_con()?;\n\n    let socket = TcpStream::from(socket);\n\n    socket.set_nodelay(true).to_con()?;\n\n    Ok((socket.try_clone().to_con()?, socket))\n}\n\nfn framed_send<S: Serialize>(\n    socket: &mut TcpStream,\n    buffer: &mut Vec<u8>,\n    packet: &S,\n) -> Result<()> {\n    buffer.resize(FRAMED_PREFIX_LENGTH, 0);\n\n    let encoded_size = bincode::serde::encode_into_std_write(packet, buffer, config::standard())?;\n\n    buffer[0..FRAMED_PREFIX_LENGTH].copy_from_slice(&(encoded_size as u32).to_le_bytes());\n\n    socket.write_all(buffer)?;\n\n    Ok(())\n}\n\nfn framed_recv<R: DeserializeOwned>(\n    socket: &mut TcpStream,\n    buffer: &mut Vec<u8>,\n    recv_cursor: &mut Option<usize>,\n    timeout: Duration,\n) -> ConResult<R> {\n    let deadline = Instant::now() + timeout;\n\n    let recv_cursor_ref = if let Some(cursor) = recv_cursor {\n        cursor\n    } else {\n        let mut payload_size_bytes = [0; FRAMED_PREFIX_LENGTH];\n\n        loop {\n            let count = socket.peek(&mut payload_size_bytes).handle_try_again()?;\n            if count == FRAMED_PREFIX_LENGTH {\n                break;\n            } else if Instant::now() > deadline {\n                return alvr_common::try_again();\n            }\n        }\n\n        let size = FRAMED_PREFIX_LENGTH + u32::from_le_bytes(payload_size_bytes) as usize;\n        buffer.resize(size, 0);\n\n        recv_cursor.insert(0)\n    };\n\n    loop {\n        *recv_cursor_ref += socket\n            .read(&mut buffer[*recv_cursor_ref..])\n            .handle_try_again()?;\n\n        if *recv_cursor_ref == buffer.len() {\n            break;\n        } else if Instant::now() > deadline {\n            return alvr_common::try_again();\n        }\n    }\n\n    let (packet, _) =\n        bincode::serde::decode_from_slice(&buffer[FRAMED_PREFIX_LENGTH..], config::standard())\n            .to_con()?;\n\n    *recv_cursor = None;\n\n    Ok(packet)\n}\n\npub struct ControlSocketSender<T> {\n    inner: TcpStream,\n    buffer: Vec<u8>,\n    _phantom: PhantomData<T>,\n}\n\nimpl<S: Serialize> ControlSocketSender<S> {\n    pub fn send(&mut self, packet: &S) -> Result<()> {\n        framed_send(&mut self.inner, &mut self.buffer, packet)\n    }\n}\n\npub struct ControlSocketReceiver<T> {\n    inner: TcpStream,\n    buffer: Vec<u8>,\n    recv_cursor: Option<usize>,\n    _phantom: PhantomData<T>,\n}\n\nimpl<R: DeserializeOwned> ControlSocketReceiver<R> {\n    pub fn recv(&mut self, timeout: Duration) -> ConResult<R> {\n        framed_recv(\n            &mut self.inner,\n            &mut self.buffer,\n            &mut self.recv_cursor,\n            timeout,\n        )\n    }\n}\n\npub fn get_server_listener(timeout: Duration) -> Result<TcpListener> {\n    let listener = bind(timeout, CONTROL_PORT, None, SocketBufferConfig::default())?;\n\n    Ok(listener)\n}\n\n// Proto-control-socket that can send and receive any packet. After the split, only the packets of\n// the specified types can be exchanged\npub struct ProtoControlSocket {\n    inner: TcpStream,\n}\n\npub enum PeerType<'a> {\n    AnyClient(Vec<IpAddr>),\n    Server(&'a TcpListener),\n}\n\nimpl ProtoControlSocket {\n    pub fn connect_to(timeout: Duration, peer: PeerType<'_>) -> ConResult<(Self, IpAddr)> {\n        let socket = match peer {\n            PeerType::AnyClient(ips) => {\n                connect_to_client(timeout, &ips, CONTROL_PORT, SocketBufferConfig::default())?.0\n            }\n            PeerType::Server(listener) => accept_from_server(listener, None, timeout)?.0,\n        };\n\n        let peer_ip = socket.peer_addr().to_con()?.ip();\n\n        Ok((Self { inner: socket }, peer_ip))\n    }\n\n    pub fn send<S: Serialize>(&mut self, packet: &S) -> Result<()> {\n        framed_send(&mut self.inner, &mut vec![], packet)\n    }\n\n    pub fn recv<R: DeserializeOwned>(&mut self, timeout: Duration) -> ConResult<R> {\n        framed_recv(&mut self.inner, &mut vec![], &mut None, timeout)\n    }\n\n    pub fn split<S: Serialize, R: DeserializeOwned>(\n        self,\n        timeout: Duration,\n    ) -> Result<(ControlSocketSender<S>, ControlSocketReceiver<R>)> {\n        self.inner.set_read_timeout(Some(timeout))?;\n\n        Ok((\n            ControlSocketSender {\n                inner: self.inner.try_clone()?,\n                buffer: vec![0; FRAMED_PREFIX_LENGTH],\n                _phantom: PhantomData,\n            },\n            ControlSocketReceiver {\n                inner: self.inner,\n                buffer: vec![0; FRAMED_PREFIX_LENGTH],\n                recv_cursor: None,\n                _phantom: PhantomData,\n            },\n        ))\n    }\n}\n"
  },
  {
    "path": "alvr/sockets/src/lib.rs",
    "content": "mod control_socket;\nmod stream_socket;\n\nuse alvr_common::{anyhow::Result, info};\nuse alvr_session::{DscpTos, SocketBufferConfig, SocketBufferSize};\nuse socket2::Socket;\nuse std::{\n    net::{IpAddr, Ipv4Addr},\n    time::Duration,\n};\n\npub use control_socket::*;\npub use stream_socket::*;\n\npub const LOCAL_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);\npub const CONTROL_PORT: u16 = 9943;\npub const HANDSHAKE_PACKET_SIZE_BYTES: usize = 56; // this may change in future protocols\npub const KEEPALIVE_INTERVAL: Duration = Duration::from_millis(500);\npub const KEEPALIVE_TIMEOUT: Duration = Duration::from_secs(2);\n\npub const MDNS_SERVICE_TYPE: &str = \"_alvr._tcp.local.\";\npub const MDNS_PROTOCOL_KEY: &str = \"protocol\";\npub const MDNS_DEVICE_ID_KEY: &str = \"device_id\";\n\npub const WIRED_CLIENT_HOSTNAME: &str = \"client.wired\";\n\nfn set_socket_buffers(socket: &socket2::Socket, buffer_config: SocketBufferConfig) -> Result<()> {\n    info!(\n        \"Initial socket buffer size: send: {}B, recv: {}B\",\n        socket.send_buffer_size()?,\n        socket.recv_buffer_size()?\n    );\n\n    {\n        let maybe_size = match buffer_config.send_size_bytes {\n            SocketBufferSize::Default => None,\n            SocketBufferSize::Maximum => Some(u32::MAX),\n            SocketBufferSize::Custom(size) => Some(size),\n        };\n\n        if let Some(size) = maybe_size {\n            if let Err(e) = socket.set_send_buffer_size(size as usize) {\n                info!(\"Error setting socket send buffer: {e}\");\n            } else {\n                info!(\n                    \"Set socket send buffer succeeded: {}\",\n                    socket.send_buffer_size()?\n                );\n            }\n        }\n    }\n\n    {\n        let maybe_size = match buffer_config.recv_size_bytes {\n            SocketBufferSize::Default => None,\n            SocketBufferSize::Maximum => Some(u32::MAX),\n            SocketBufferSize::Custom(size) => Some(size),\n        };\n\n        if let Some(size) = maybe_size {\n            if let Err(e) = socket.set_recv_buffer_size(size as usize) {\n                info!(\"Error setting socket recv buffer: {e}\");\n            } else {\n                info!(\n                    \"Set socket recv buffer succeeded: {}\",\n                    socket.recv_buffer_size()?\n                );\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn set_dscp(socket: &Socket, dscp: Option<DscpTos>) {\n    // https://en.wikipedia.org/wiki/Differentiated_services\n    if let Some(dscp) = dscp {\n        let tos = match dscp {\n            DscpTos::BestEffort => 0,\n            DscpTos::ClassSelector(precedence) => precedence << 3,\n            DscpTos::AssuredForwarding {\n                class,\n                drop_probability,\n            } => (class << 3) | drop_probability as u8,\n            DscpTos::ExpeditedForwarding => 0b101110,\n        };\n\n        socket.set_tos_v4((tos << 2) as u32).ok();\n    }\n}\n"
  },
  {
    "path": "alvr/sockets/src/stream_socket/mod.rs",
    "content": "// Note: for StreamSocket, the client uses a server socket, the server uses a client socket.\n// This is because of certificate management. The server needs to trust a client and its certificate\n//\n// StreamSender and StreamReceiver endpoints allow for convenient conversion of the header to/from\n// bytes while still handling the additional byte buffer with zero copies and extra allocations.\n\n// Performance analysis:\n// We want to minimize the transmission time for various sizes of packets.\n// The current code locks the write socket *per shard* and not *per packet*. This leds to the best\n// performance outcome given that the possible packets can be either very small (one shard) or very\n// large (hundreds/thousands of shards, for video). if we don't allow interleaving shards, a very\n// small packet will need to wait a long time before getting received if there was an ongoing\n// transmission of a big packet before. If we allow interleaving shards, small packets can be\n// transmitted quicker, with only minimal latency increase for the ongoing transmission of the big\n// packet.\n// Note: We can't clone the underlying socket for each StreamSender and the mutex around the socket\n// cannot be removed. This is because we need to make sure at least shards are written whole.\n\nmod tcp;\nmod udp;\n\nuse alvr_common::{\n    AnyhowToCon, ConResult, HandleTryAgain, ToCon, anyhow::Result, parking_lot::Mutex,\n};\nuse alvr_session::{DscpTos, SocketBufferConfig, SocketProtocol};\nuse bincode::config;\nuse serde::{Serialize, de::DeserializeOwned};\nuse std::{\n    cmp::Ordering,\n    collections::HashMap,\n    marker::PhantomData,\n    mem,\n    net::{IpAddr, TcpListener, UdpSocket},\n    ops::{Deref, DerefMut},\n    sync::{Arc, mpsc},\n    time::Duration,\n};\n\ntrait MultiplexedSocketWriter {\n    // Note: consts are not trait-safe, we require a method\n    fn payload_offset(&self) -> usize;\n\n    fn send(&mut self, stream_id: u16, packet_index: u32, buffer: &mut Vec<u8>) -> Result<()>;\n}\n\nstruct ReconstructedPacket {\n    index: u32,\n    buffer: Vec<u8>,\n}\n\nstruct StreamRecvQueues {\n    used_buffer_sender: mpsc::Sender<Vec<u8>>,\n    used_buffer_receiver: mpsc::Receiver<Vec<u8>>,\n    packet_queue: mpsc::Sender<ReconstructedPacket>,\n}\n\ntrait MultiplexedSocketReader {\n    fn payload_offset(&self) -> usize;\n\n    fn recv(&mut self, stream_queues: &HashMap<u16, StreamRecvQueues>) -> ConResult;\n}\n\n/// Memory buffer that contains a hidden prefix\n#[derive(Default)]\npub struct Buffer<H = ()> {\n    inner: Vec<u8>,\n    raw_payload_offset: usize, // this corresponds to prefix + header\n    _phantom: PhantomData<H>,\n}\n\nimpl<H> Deref for Buffer<H> {\n    type Target = [u8];\n\n    fn deref(&self) -> &[u8] {\n        &self.inner[self.raw_payload_offset..]\n    }\n}\n\nimpl<H> DerefMut for Buffer<H> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.inner[self.raw_payload_offset..]\n    }\n}\n\n#[derive(Clone)]\npub struct StreamSender<H> {\n    inner: Arc<Mutex<Box<dyn MultiplexedSocketWriter + Send>>>,\n    stream_id: u16,\n    payload_offset: usize,\n    next_packet_index: u32,\n    used_buffers: Vec<Vec<u8>>,\n    _phantom: PhantomData<H>,\n}\n\nimpl<H> StreamSender<H> {\n    /// Shard and send a buffer with zero copies and zero allocations.\n    /// The prefix of each shard is written over the previously sent shard to avoid reallocations.\n    pub fn send(&mut self, mut buffer: Buffer<H>) -> Result<()> {\n        self.inner\n            .lock()\n            .send(self.stream_id, self.next_packet_index, &mut buffer.inner)?;\n\n        self.used_buffers.push(buffer.inner);\n\n        self.next_packet_index = self.next_packet_index.wrapping_add(1);\n\n        Ok(())\n    }\n}\n\nimpl<H: Serialize> StreamSender<H> {\n    pub fn get_buffer(&mut self, header: &H, raw_payload_len: usize) -> Result<Buffer<H>> {\n        let mut buffer = self.used_buffers.pop().unwrap_or_default();\n\n        buffer.resize(self.payload_offset, 0);\n\n        let encoded_payload_size =\n            bincode::serde::encode_into_std_write(header, &mut buffer, config::standard())?;\n\n        let raw_payload_offset = self.payload_offset + encoded_payload_size;\n\n        buffer.resize(raw_payload_offset + raw_payload_len, 0);\n\n        Ok(Buffer {\n            inner: buffer,\n            raw_payload_offset,\n            _phantom: PhantomData,\n        })\n    }\n\n    pub fn send_header_with_payload(&mut self, header: &H, raw_payload: &[u8]) -> Result<()> {\n        let mut buffer = self.get_buffer(header, raw_payload.len())?;\n        buffer.copy_from_slice(raw_payload);\n        self.send(buffer)\n    }\n\n    pub fn send_header(&mut self, header: &H) -> Result<()> {\n        self.send_header_with_payload(header, &[])\n    }\n}\n\npub struct ReceiverData<H> {\n    buffer: Vec<u8>,\n    payload_offset: usize,\n    used_buffer_queue: mpsc::Sender<Vec<u8>>,\n    had_packet_loss: bool,\n    _phantom: PhantomData<H>,\n}\n\nimpl<H> ReceiverData<H> {\n    pub fn had_packet_loss(&self) -> bool {\n        self.had_packet_loss\n    }\n}\n\nimpl<H: DeserializeOwned> ReceiverData<H> {\n    pub fn get(&self) -> Result<(H, &[u8])> {\n        let payload = &self.buffer[self.payload_offset..];\n\n        let (header, decoded_size) =\n            bincode::serde::decode_from_slice(payload, config::standard())?;\n\n        Ok((header, &payload[decoded_size..]))\n    }\n\n    pub fn get_header(&self) -> Result<H> {\n        Ok(self.get()?.0)\n    }\n}\n\nimpl<H> Drop for ReceiverData<H> {\n    fn drop(&mut self) {\n        self.used_buffer_queue\n            .send(mem::take(&mut self.buffer))\n            .ok();\n    }\n}\n\npub struct StreamReceiver<H> {\n    payload_offset: usize,\n    packet_receiver: mpsc::Receiver<ReconstructedPacket>,\n    used_buffer_queue: mpsc::Sender<Vec<u8>>,\n    last_packet_index: Option<u32>,\n    _phantom: PhantomData<H>,\n}\n\n// Wrapping comparison for packet indices.\n// Problem: packet indices have a finite bit-width and we have to wrap around upon reaching the\n// maximum value. This function provides proper ordering capability when wrapping around. The\n// maximum index distance that two packets can have is u32::MAX / 2 (which is plenty for any\n// reasonable circumstance).\nfn wrapping_cmp(lhs: u32, rhs: u32) -> Ordering {\n    const LOWER_BOUND: u32 = u32::MAX / 4;\n    const UPPER_BOUND: u32 = u32::MAX - LOWER_BOUND;\n\n    if lhs < LOWER_BOUND && rhs > UPPER_BOUND {\n        // lhs wrapped around, so its \"unwrapped\" value would be `lhs as u64 + u32::MAX`\n        Ordering::Greater\n    } else if lhs > UPPER_BOUND && rhs < LOWER_BOUND {\n        // rhs wrapped around\n        Ordering::Less\n    } else {\n        lhs.cmp(&rhs)\n    }\n}\n\nimpl<H: DeserializeOwned + Serialize> StreamReceiver<H> {\n    pub fn recv(&mut self, timeout: Duration) -> ConResult<ReceiverData<H>> {\n        let packet = self\n            .packet_receiver\n            .recv_timeout(timeout)\n            .handle_try_again()?;\n\n        let mut had_packet_loss = false;\n\n        if let Some(last_idx) = self.last_packet_index {\n            // Use wrapping arithmetics\n            match wrapping_cmp(packet.index, last_idx.wrapping_add(1)) {\n                Ordering::Equal => (),\n                Ordering::Greater => {\n                    // Skipped some indices\n                    had_packet_loss = true\n                }\n                Ordering::Less => {\n                    // Old packet, discard\n                    self.used_buffer_queue.send(packet.buffer).to_con()?;\n                    return alvr_common::try_again();\n                }\n            }\n        }\n        self.last_packet_index = Some(packet.index);\n\n        Ok(ReceiverData {\n            buffer: packet.buffer,\n            payload_offset: self.payload_offset,\n            used_buffer_queue: self.used_buffer_queue.clone(),\n            had_packet_loss,\n            _phantom: PhantomData,\n        })\n    }\n}\n\npub enum StreamSocketBuilder {\n    Tcp(TcpListener),\n    Udp(UdpSocket),\n}\n\nimpl StreamSocketBuilder {\n    pub fn listen_for_server(\n        timeout: Duration,\n        port: u16,\n        stream_socket_config: SocketProtocol,\n        stream_tos_config: Option<DscpTos>,\n        buffer_config: SocketBufferConfig,\n    ) -> Result<Self> {\n        Ok(match stream_socket_config {\n            SocketProtocol::Udp => {\n                StreamSocketBuilder::Udp(udp::bind(port, stream_tos_config, buffer_config)?)\n            }\n            SocketProtocol::Tcp => StreamSocketBuilder::Tcp(tcp::bind(\n                timeout,\n                port,\n                stream_tos_config,\n                buffer_config,\n            )?),\n        })\n    }\n\n    pub fn accept_from_server(\n        self,\n        server_ip: IpAddr,\n        port: u16,\n        max_packet_size: usize,\n        timeout: Duration,\n    ) -> ConResult<StreamSocket> {\n        let (send_socket, receive_socket) = match self {\n            StreamSocketBuilder::Udp(socket) => {\n                udp::connect(&socket, server_ip, port, timeout).to_con()?;\n                udp::split_multiplexed(socket, max_packet_size).to_con()?\n            }\n            StreamSocketBuilder::Tcp(listener) => {\n                let socket = tcp::accept_from_server(&listener, Some(server_ip), timeout)?;\n                tcp::split_multiplexed(socket, timeout).to_con()?\n            }\n        };\n\n        Ok(StreamSocket {\n            send_socket: Arc::new(Mutex::new(send_socket)),\n            receive_socket,\n            queues: HashMap::new(),\n        })\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn connect_to_client(\n        timeout: Duration,\n        client_ip: IpAddr,\n        port: u16,\n        protocol: SocketProtocol,\n        dscp: Option<DscpTos>,\n        buffer_config: SocketBufferConfig,\n        max_packet_size: usize,\n    ) -> ConResult<StreamSocket> {\n        let (send_socket, receive_socket) = match protocol {\n            SocketProtocol::Udp => {\n                let socket = udp::bind(port, dscp, buffer_config).to_con()?;\n                udp::connect(&socket, client_ip, port, timeout).to_con()?;\n                udp::split_multiplexed(socket, max_packet_size).to_con()?\n            }\n            SocketProtocol::Tcp => {\n                let socket = tcp::connect_to_client(timeout, &[client_ip], port, buffer_config)?;\n                tcp::split_multiplexed(socket, timeout).to_con()?\n            }\n        };\n\n        Ok(StreamSocket {\n            send_socket: Arc::new(Mutex::new(send_socket)),\n            receive_socket,\n            queues: HashMap::new(),\n        })\n    }\n}\n\npub struct StreamSocket {\n    send_socket: Arc<Mutex<Box<dyn MultiplexedSocketWriter + Send>>>,\n    receive_socket: Box<dyn MultiplexedSocketReader + Send>,\n    queues: HashMap<u16, StreamRecvQueues>,\n}\n\nimpl StreamSocket {\n    pub fn request_stream<T>(&self, stream_id: u16) -> StreamSender<T> {\n        StreamSender {\n            inner: Arc::clone(&self.send_socket),\n            stream_id,\n            payload_offset: self.send_socket.lock().payload_offset(),\n            next_packet_index: 0,\n            used_buffers: vec![],\n            _phantom: PhantomData,\n        }\n    }\n\n    // max_concurrent_buffers: number of buffers allocated by this call which will be reused to\n    // receive packets for this stream ID. If packets are not read fast enough, the shards received\n    // for this particular stream will be discarded\n    pub fn subscribe_to_stream<T>(\n        &mut self,\n        stream_id: u16,\n        max_concurrent_buffers: usize,\n    ) -> StreamReceiver<T> {\n        let (packet_sender, packet_receiver) = mpsc::channel();\n        let (used_buffer_sender, used_buffer_receiver) = mpsc::channel();\n\n        for _ in 0..max_concurrent_buffers {\n            used_buffer_sender.send(vec![]).ok();\n        }\n\n        self.queues.insert(\n            stream_id,\n            StreamRecvQueues {\n                used_buffer_sender: used_buffer_sender.clone(),\n                used_buffer_receiver,\n                packet_queue: packet_sender,\n            },\n        );\n\n        StreamReceiver {\n            payload_offset: self.receive_socket.payload_offset(),\n            packet_receiver,\n            used_buffer_queue: used_buffer_sender,\n            last_packet_index: None,\n            _phantom: PhantomData,\n        }\n    }\n\n    pub fn recv(&mut self) -> ConResult {\n        self.receive_socket.recv(&self.queues)\n    }\n}\n"
  },
  {
    "path": "alvr/sockets/src/stream_socket/tcp.rs",
    "content": "use super::{\n    MultiplexedSocketReader, MultiplexedSocketWriter, ReconstructedPacket, StreamRecvQueues,\n};\nuse crate::LOCAL_IP;\nuse alvr_common::{ConResult, HandleTryAgain, ToCon, anyhow::Result, con_bail};\nuse alvr_session::{DscpTos, SocketBufferConfig};\nuse socket2::Socket;\nuse std::{\n    collections::HashMap,\n    io::Write,\n    mem::{self, MaybeUninit},\n    net::{IpAddr, SocketAddr, TcpListener, TcpStream},\n    time::Duration,\n};\n\npub const PACKET_PREFIX_SIZE: usize = mem::size_of::<u16>() // stream ID\n    + mem::size_of::<u32>() // packet index\n    + mem::size_of::<u32>(); // payload size\n\npub fn bind(\n    timeout: Duration,\n    port: u16,\n    dscp: Option<DscpTos>,\n    buffer_config: SocketBufferConfig,\n) -> Result<TcpListener> {\n    let socket = TcpListener::bind((LOCAL_IP, port))?.into();\n\n    crate::set_socket_buffers(&socket, buffer_config).ok();\n\n    crate::set_dscp(&socket, dscp);\n\n    socket.set_read_timeout(Some(timeout))?;\n\n    Ok(socket.into())\n}\n\npub fn accept_from_server(\n    listener: &TcpListener,\n    server_ip: Option<IpAddr>,\n    timeout: Duration,\n) -> ConResult<TcpStream> {\n    // Uses timeout set during bind()\n    let (socket, server_address) = listener.accept().handle_try_again()?;\n\n    if let Some(ip) = server_ip\n        && server_address.ip() != ip\n    {\n        con_bail!(\n            \"Connected to wrong client: Expected: {ip}, Found {}\",\n            server_address.ip()\n        );\n    }\n\n    socket.set_read_timeout(Some(timeout)).to_con()?;\n    socket.set_nodelay(true).to_con()?;\n\n    Ok(socket)\n}\n\npub fn connect_to_client(\n    timeout: Duration,\n    client_ips: &[IpAddr],\n    port: u16,\n    buffer_config: SocketBufferConfig,\n) -> ConResult<TcpStream> {\n    let split_timeout = timeout / client_ips.len() as u32;\n\n    let mut res = alvr_common::try_again();\n    for ip in client_ips {\n        res = TcpStream::connect_timeout(&SocketAddr::new(*ip, port), split_timeout)\n            .handle_try_again();\n\n        if res.is_ok() {\n            break;\n        }\n    }\n    let socket = res?.into();\n\n    crate::set_socket_buffers(&socket, buffer_config).ok();\n    socket.set_read_timeout(Some(timeout)).to_con()?;\n\n    let socket = TcpStream::from(socket);\n\n    socket.set_nodelay(true).to_con()?;\n\n    Ok(socket)\n}\n\npub struct MultiplexedTcpWriter {\n    inner: TcpStream,\n}\n\nimpl MultiplexedSocketWriter for MultiplexedTcpWriter {\n    fn payload_offset(&self) -> usize {\n        PACKET_PREFIX_SIZE\n    }\n\n    // `buffer` contains the payload offset by `payload_offset()`\n    fn send(&mut self, stream_id: u16, packet_index: u32, buffer: &mut Vec<u8>) -> Result<()> {\n        let payload_size = buffer.len() - PACKET_PREFIX_SIZE;\n\n        buffer[0..2].copy_from_slice(&stream_id.to_le_bytes());\n        buffer[2..6].copy_from_slice(&packet_index.to_le_bytes());\n        buffer[6..10].copy_from_slice(&(payload_size as u32).to_le_bytes());\n\n        self.inner.write_all(buffer)?;\n\n        Ok(())\n    }\n}\n\nstruct InProgressPacket {\n    stream_id: u16,\n    packet_index: u32,\n    buffer: Vec<u8>,\n    buffer_size: usize,\n    cursor: usize,\n}\n\npub struct MultiplexedTcpReader {\n    inner: Socket,\n    in_progress_packet: Option<InProgressPacket>,\n    used_buffers_poll_timeout: Duration,\n}\n\nimpl MultiplexedSocketReader for MultiplexedTcpReader {\n    fn payload_offset(&self) -> usize {\n        PACKET_PREFIX_SIZE\n    }\n\n    fn recv(&mut self, stream_queues: &HashMap<u16, StreamRecvQueues>) -> ConResult {\n        let in_progress_packet = if let Some(packet) = &mut self.in_progress_packet {\n            packet\n        } else {\n            let mut prefix_bytes = [0u8; PACKET_PREFIX_SIZE];\n\n            loop {\n                let count = self\n                    .inner\n                    .peek(unsafe {\n                        &mut *(&mut prefix_bytes as *mut [u8] as *mut [MaybeUninit<u8>])\n                    })\n                    .handle_try_again()?;\n                if count == PACKET_PREFIX_SIZE {\n                    break;\n                }\n            }\n\n            let stream_id = u16::from_le_bytes(prefix_bytes[0..2].try_into().unwrap());\n            let packet_index = u32::from_le_bytes(prefix_bytes[2..6].try_into().unwrap());\n            let payload_size = u32::from_le_bytes(prefix_bytes[6..10].try_into().unwrap()) as usize;\n\n            let mut buffer = match stream_queues.get(&stream_id) {\n                Some(queue) => queue\n                    .used_buffer_receiver\n                    .recv_timeout(self.used_buffers_poll_timeout)\n                    .handle_try_again()?,\n                None => {\n                    // This is a packet with an invalid stream id, but we must read it anyway. We\n                    // can't obtain a used buffer so we create a new one\n                    vec![]\n                }\n            };\n\n            buffer.clear();\n            buffer.reserve(PACKET_PREFIX_SIZE + payload_size);\n\n            self.in_progress_packet.insert(InProgressPacket {\n                stream_id,\n                packet_index,\n                buffer,\n                buffer_size: PACKET_PREFIX_SIZE + payload_size,\n                cursor: 0,\n            })\n        };\n\n        while in_progress_packet.cursor < in_progress_packet.buffer_size {\n            let sub_buffer = &mut in_progress_packet.buffer.spare_capacity_mut()\n                [in_progress_packet.cursor..in_progress_packet.buffer_size];\n\n            in_progress_packet.cursor += self.inner.recv(sub_buffer).handle_try_again()?;\n        }\n\n        // All writing was done to uninit capacity, here we set the final buffer length\n        unsafe {\n            in_progress_packet\n                .buffer\n                .set_len(in_progress_packet.buffer_size)\n        };\n\n        if let Some(queues) = stream_queues.get(&in_progress_packet.stream_id) {\n            // Safety: here self.in_progress_packet is always Some\n            queues\n                .packet_queue\n                .send(ReconstructedPacket {\n                    index: in_progress_packet.packet_index,\n                    buffer: self.in_progress_packet.take().unwrap().buffer,\n                })\n                .to_con()?;\n        } else {\n            // The packet had an invalid stream ID. Discard the buffer\n            self.in_progress_packet.take();\n        }\n\n        Ok(())\n    }\n}\n\npub fn split_multiplexed(\n    socket: TcpStream,\n    used_buffers_poll_timeout: Duration,\n) -> Result<(\n    Box<dyn MultiplexedSocketWriter + Send>,\n    Box<dyn MultiplexedSocketReader + Send>,\n)> {\n    let writer = MultiplexedTcpWriter {\n        inner: socket.try_clone()?,\n    };\n\n    let reader = MultiplexedTcpReader {\n        inner: socket.into(),\n        in_progress_packet: None,\n        used_buffers_poll_timeout,\n    };\n\n    Ok((Box::new(writer), Box::new(reader)))\n}\n"
  },
  {
    "path": "alvr/sockets/src/stream_socket/udp.rs",
    "content": "use super::{\n    MultiplexedSocketReader, MultiplexedSocketWriter, ReconstructedPacket, StreamRecvQueues,\n};\nuse crate::LOCAL_IP;\nuse alvr_common::{ConResult, HandleTryAgain, ToCon, anyhow::Result};\nuse alvr_session::{DscpTos, SocketBufferConfig};\nuse socket2::{MaybeUninitSlice, Socket};\nuse std::ffi::c_int;\nuse std::{\n    cmp::Ordering,\n    collections::{HashMap, HashSet},\n    mem::{self, MaybeUninit},\n    net::{IpAddr, UdpSocket},\n    ptr,\n    time::Duration,\n};\n\npub const SHARD_PREFIX_SIZE: usize = mem::size_of::<u16>() // stream ID\n    + mem::size_of::<u32>() // packet index\n    + mem::size_of::<u32>() // shards count\n    + mem::size_of::<u32>(); // shards index\n\nfn socket_peek(socket: &mut Socket, buffer: &mut [u8]) -> ConResult<usize> {\n    #[cfg(windows)]\n    const FLAGS: c_int = 0x02 | 0x8000; // MSG_PEEK | MSG_PARTIAL\n    #[cfg(not(windows))]\n    const FLAGS: c_int = 0x02 | 0x20; // MSG_PEEK | MSG_TRUNC\n\n    let buffer =\n        MaybeUninitSlice::new(unsafe { &mut *(ptr::from_mut(buffer) as *mut [MaybeUninit<u8>]) });\n    // NB: Using the non vectored call doesn't seem to work\n    Ok(socket\n        .recv_vectored_with_flags(&mut [buffer], FLAGS)\n        .handle_try_again()?\n        .0)\n}\n\n// Create tokio socket, convert to socket2, apply settings, convert back to tokio. This is done to\n// let tokio set all the internal parameters it needs from the start.\npub fn bind(\n    port: u16,\n    dscp: Option<DscpTos>,\n    buffer_config: SocketBufferConfig,\n) -> Result<UdpSocket> {\n    let socket = UdpSocket::bind((LOCAL_IP, port))?.into();\n\n    crate::set_socket_buffers(&socket, buffer_config).ok();\n    crate::set_dscp(&socket, dscp);\n\n    Ok(socket.into())\n}\n\npub fn connect(socket: &UdpSocket, peer_ip: IpAddr, port: u16, timeout: Duration) -> Result<()> {\n    socket.connect((peer_ip, port))?;\n    socket.set_read_timeout(Some(timeout))?;\n\n    Ok(())\n}\n\npub struct MultiplexedUdpWriter {\n    inner: UdpSocket,\n    max_packet_size: usize,\n}\n\nimpl MultiplexedSocketWriter for MultiplexedUdpWriter {\n    fn payload_offset(&self) -> usize {\n        SHARD_PREFIX_SIZE\n    }\n\n    fn send(&mut self, stream_id: u16, packet_index: u32, buffer: &mut Vec<u8>) -> Result<()> {\n        let max_shard_size = self.max_packet_size - SHARD_PREFIX_SIZE;\n        let payload_size = buffer.len() - SHARD_PREFIX_SIZE;\n        // rounding up:\n        let shards_count = payload_size.div_ceil(max_shard_size);\n\n        for shard_idx in 0..shards_count {\n            // this overlaps with the previous shard, this is intended behavior and allows to\n            // reduce allocations\n            let shard_start_position = shard_idx * max_shard_size;\n            let shard_size = usize::min(max_shard_size, payload_size - shard_start_position);\n\n            let shard_view = &mut buffer[shard_start_position..][..SHARD_PREFIX_SIZE + shard_size];\n\n            shard_view[0..2].copy_from_slice(&stream_id.to_le_bytes());\n            shard_view[2..6].copy_from_slice(&packet_index.to_le_bytes());\n            shard_view[6..10].copy_from_slice(&(shards_count as u32).to_le_bytes());\n            shard_view[10..14].copy_from_slice(&(shard_idx as u32).to_le_bytes());\n\n            self.inner.send(shard_view)?;\n        }\n\n        Ok(())\n    }\n}\n\n// We need to store the size seaparately because we use use the buffer as unallocated memory and\n// the capacity cannot be set precisely.\n// Note: Why do we need to keep space for the prefix in the final buffer?\n// UDP works with discrete packets, reading must be done in a single call. There is no way to\n// preemptively discard the bytes of the prefix.\nstruct InProgressPacket {\n    buffer: Vec<u8>,    // contains the prefix\n    buffer_size: usize, // size of the packet counting prefix\n    shards_count: usize,\n    received_shard_indices: HashSet<usize>,\n}\n\npub struct MultiplexedUdpReader {\n    inner: Socket,\n    max_packet_size: usize,\n    in_progress_packets: HashMap<u16, HashMap<u32, InProgressPacket>>,\n}\n\nimpl MultiplexedSocketReader for MultiplexedUdpReader {\n    fn payload_offset(&self) -> usize {\n        SHARD_PREFIX_SIZE\n    }\n\n    fn recv(&mut self, stream_queues: &HashMap<u16, StreamRecvQueues>) -> ConResult {\n        let max_shard_data_size = self.max_packet_size - SHARD_PREFIX_SIZE;\n\n        let discard_and_try_again = move |socket: &Socket| {\n            // Reading with any sized buffer (even 0) will consume the whole datagram\n            socket.recv(&mut []).ok();\n            alvr_common::try_again()\n        };\n\n        let mut prefix_bytes = [0; SHARD_PREFIX_SIZE];\n        let peek_size = socket_peek(&mut self.inner, &mut prefix_bytes)?;\n        if peek_size < SHARD_PREFIX_SIZE {\n            return discard_and_try_again(&self.inner);\n        }\n\n        // The values obtained from the prefix (stream ID, packet index, shards count, shard index)\n        // could be corrupted somehow. This method has safety checks against corrupted values and\n        // the relative packet would be discarded.\n        let stream_id = u16::from_le_bytes(prefix_bytes[0..2].try_into().unwrap());\n        let packet_index = u32::from_le_bytes(prefix_bytes[2..6].try_into().unwrap());\n        let maybe_shards_count =\n            u32::from_le_bytes(prefix_bytes[6..10].try_into().unwrap()) as usize;\n        let shard_index = u32::from_le_bytes(prefix_bytes[10..14].try_into().unwrap()) as usize;\n\n        if maybe_shards_count == 0 {\n            return discard_and_try_again(&self.inner);\n        }\n\n        let Some(queues) = stream_queues.get(&stream_id) else {\n            return discard_and_try_again(&self.inner);\n        };\n        let in_progress_packets = self.in_progress_packets.entry(stream_id).or_default();\n\n        let in_progress_packet = if let Some(packet) = in_progress_packets.get_mut(&packet_index) {\n            packet\n        } else if let Some(mut buffer) = queues.used_buffer_receiver.try_recv().ok().or_else(|| {\n            // By default, try to dequeue a used buffer. In case none were found, recycle one of the\n            // in progress packets, chances are these buffers are \"dead\" because one of their shards\n            // has been dropped by the network.\n            let idx = *in_progress_packets.iter().next()?.0;\n            Some(in_progress_packets.remove(&idx).unwrap().buffer)\n        }) {\n            // The first shard prefix will dictate the actual number of shards of the packet. The\n            // reserved capacity is an upper bound: we don't know yet the exact size, the last\n            // shard could be smaller than max_shard_data_size\n            buffer.clear();\n            buffer.reserve(SHARD_PREFIX_SIZE + max_shard_data_size * maybe_shards_count);\n\n            in_progress_packets\n                .entry(packet_index)\n                .or_insert(InProgressPacket {\n                    buffer,\n                    buffer_size: 0,\n                    shards_count: maybe_shards_count,\n                    // todo: find a way to skipping this allocation\n                    received_shard_indices: HashSet::with_capacity(maybe_shards_count),\n                })\n        } else {\n            // This branch may be hit in case the thread related to the stream hangs for some reason\n            return discard_and_try_again(&self.inner);\n        };\n\n        if shard_index >= in_progress_packet.shards_count\n            || in_progress_packet\n                .received_shard_indices\n                .contains(&shard_index)\n        {\n            return discard_and_try_again(&self.inner);\n        }\n\n        // Note: there is no prefix offset, since we want to write the prefix too.\n        let packet_start_index = shard_index * max_shard_data_size;\n\n        // Note: this is a MaybeUninit slice\n        let sub_buffer = &mut in_progress_packet.buffer.spare_capacity_mut()[packet_start_index..];\n\n        // Safety: bound checks lead from the previous code\n        let overwritten_data_backup: [_; SHARD_PREFIX_SIZE] =\n            sub_buffer[..SHARD_PREFIX_SIZE].try_into().unwrap();\n\n        // This call should never fail because the peek call succeded before.\n        // Note: in unexpected circumstances, here .to_con() is used not to emit TryAgain, which\n        // would mess with the state of the code. The connection would need to be closed instead.\n        // NB: the received_size contains the prefix\n        let received_size = self.inner.recv(sub_buffer).to_con()?;\n\n        // Restore backed up bytes\n        sub_buffer[..SHARD_PREFIX_SIZE].copy_from_slice(&overwritten_data_backup);\n\n        in_progress_packet.buffer_size = usize::max(\n            in_progress_packet.buffer_size,\n            packet_start_index + received_size,\n        );\n\n        in_progress_packet\n            .received_shard_indices\n            .insert(shard_index);\n\n        // Check if packet is complete (and not dummy) and send\n        if in_progress_packet.received_shard_indices.len() == in_progress_packet.shards_count {\n            if let Some(mut packet) = in_progress_packets.remove(&packet_index) {\n                // All writing was done to uninit capacity, here we set the final buffer length\n                unsafe { packet.buffer.set_len(packet.buffer_size) };\n\n                queues\n                    .packet_queue\n                    .send(ReconstructedPacket {\n                        index: packet_index,\n                        buffer: packet.buffer,\n                    })\n                    .ok();\n            }\n\n            // Discard older in-progress packets\n            while let Some((idx, _)) = in_progress_packets\n                .iter()\n                .find(|(idx, _)| super::wrapping_cmp(**idx, packet_index) == Ordering::Less)\n            {\n                let idx = *idx; // fix borrow rule\n                let packet = in_progress_packets.remove(&idx).unwrap();\n\n                // Recycle buffer\n                queues.used_buffer_sender.send(packet.buffer).ok();\n            }\n        }\n\n        Ok(())\n    }\n}\n\npub fn split_multiplexed(\n    socket: UdpSocket,\n    max_packet_size: usize,\n) -> Result<(\n    Box<dyn MultiplexedSocketWriter + Send>,\n    Box<dyn MultiplexedSocketReader + Send>,\n)> {\n    let writer = MultiplexedUdpWriter {\n        inner: socket.try_clone()?,\n        max_packet_size,\n    };\n\n    let reader = MultiplexedUdpReader {\n        inner: socket.into(),\n        max_packet_size,\n        in_progress_packets: HashMap::new(),\n    };\n\n    Ok((Box::new(writer), Box::new(reader)))\n}\n"
  },
  {
    "path": "alvr/system_info/Cargo.toml",
    "content": "[package]\nname = \"alvr_system_info\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\n\njni = \"0.21\"\nlocal-ip-address = \"0.6\"\nserde = { version = \"1\", features = [\"derive\"] }\nsettings-schema = { git = \"https://github.com/alvr-org/settings-schema-rs\", rev = \"676185f\" }\n\n[target.'cfg(target_os = \"android\")'.dependencies]\nndk = { version = \"0.9\", features = [\"api-level-28\", \"media\"] }\nndk-context = \"0.1\"\nndk-sys = \"0.6\"\n"
  },
  {
    "path": "alvr/system_info/src/android.rs",
    "content": "use alvr_common::warn;\nuse jni::{JNIEnv, JavaVM, objects::JObject, sys::jobject};\nuse std::net::{IpAddr, Ipv4Addr};\n\npub const MICROPHONE_PERMISSION: &str = \"android.permission.RECORD_AUDIO\";\n\npub fn vm() -> JavaVM {\n    unsafe { JavaVM::from_raw(ndk_context::android_context().vm().cast()).unwrap() }\n}\n\npub fn context() -> jobject {\n    ndk_context::android_context().context().cast()\n}\n\nfn get_api_level() -> i32 {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    env.get_static_field(\"android/os/Build$VERSION\", \"SDK_INT\", \"I\")\n        .unwrap()\n        .i()\n        .unwrap()\n}\n\npub fn try_get_permission(permission: &str) {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    let mic_perm_jstring = env.new_string(permission).unwrap();\n\n    let permission_status = env\n        .call_method(\n            unsafe { JObject::from_raw(context()) },\n            \"checkSelfPermission\",\n            \"(Ljava/lang/String;)I\",\n            &[(&mic_perm_jstring).into()],\n        )\n        .unwrap()\n        .i()\n        .unwrap();\n\n    if permission_status != 0 {\n        let string_class = env.find_class(\"java/lang/String\").unwrap();\n        let perm_array = env\n            .new_object_array(1, string_class, mic_perm_jstring)\n            .unwrap();\n\n        env.call_method(\n            unsafe { JObject::from_raw(context()) },\n            \"requestPermissions\",\n            \"([Ljava/lang/String;I)V\",\n            &[(&perm_array).into(), 0.into()],\n        )\n        .unwrap();\n\n        // todo: handle case where permission is rejected\n    }\n}\n\npub fn build_string(ty: &str) -> String {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    let jname = env\n        .get_static_field(\"android/os/Build\", ty, \"Ljava/lang/String;\")\n        .unwrap()\n        .l()\n        .unwrap();\n    let name_raw = env.get_string((&jname).into()).unwrap();\n\n    name_raw.to_string_lossy().as_ref().to_owned()\n}\n\npub fn device_name() -> String {\n    build_string(\"DEVICE\")\n}\n\npub fn model_name() -> String {\n    build_string(\"MODEL\")\n}\n\npub fn manufacturer_name() -> String {\n    build_string(\"MANUFACTURER\")\n}\n\npub fn product_name() -> String {\n    build_string(\"PRODUCT\")\n}\n\nfn get_system_service<'a>(env: &mut JNIEnv<'a>, service_name: &str) -> JObject<'a> {\n    let service_str = env.new_string(service_name).unwrap();\n\n    env.call_method(\n        unsafe { JObject::from_raw(context()) },\n        \"getSystemService\",\n        \"(Ljava/lang/String;)Ljava/lang/Object;\",\n        &[(&service_str).into()],\n    )\n    .unwrap()\n    .l()\n    .unwrap()\n}\n\n// Note: tried and failed to use libc\npub fn local_ip() -> IpAddr {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    let wifi_manager = get_system_service(&mut env, \"wifi\");\n    let wifi_info = env\n        .call_method(\n            wifi_manager,\n            \"getConnectionInfo\",\n            \"()Landroid/net/wifi/WifiInfo;\",\n            &[],\n        )\n        .unwrap()\n        .l()\n        .unwrap();\n    let ip_i32 = env\n        .call_method(wifi_info, \"getIpAddress\", \"()I\", &[])\n        .unwrap()\n        .i()\n        .unwrap();\n\n    let ip_arr = ip_i32.to_le_bytes();\n\n    IpAddr::V4(Ipv4Addr::new(ip_arr[0], ip_arr[1], ip_arr[2], ip_arr[3]))\n}\n\n// This is needed to avoid wifi scans that disrupt streaming.\n// Code inspired from https://github.com/Meumeu/WiVRn/blob/master/client/application.cpp\npub fn set_wifi_lock(enabled: bool) {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    let wifi_manager = get_system_service(&mut env, \"wifi\");\n\n    fn set_lock<'a>(env: &mut JNIEnv<'a>, lock: &JObject, enabled: bool) {\n        env.call_method(lock, \"setReferenceCounted\", \"(Z)V\", &[false.into()])\n            .unwrap();\n        env.call_method(\n            &lock,\n            if enabled { \"acquire\" } else { \"release\" },\n            \"()V\",\n            &[],\n        )\n        .unwrap();\n\n        let lock_is_aquired = env\n            .call_method(lock, \"isHeld\", \"()Z\", &[])\n            .unwrap()\n            .z()\n            .unwrap();\n\n        if lock_is_aquired != enabled {\n            warn!(\"Failed to set wifi lock: expected {enabled}, got {lock_is_aquired}\");\n        }\n    }\n\n    let wifi_lock_jstring = env.new_string(\"alvr_wifi_lock\").unwrap();\n    let wifi_lock = env\n        .call_method(\n            &wifi_manager,\n            \"createWifiLock\",\n            \"(ILjava/lang/String;)Landroid/net/wifi/WifiManager$WifiLock;\",\n            &[\n                if get_api_level() >= 29 {\n                    // Recommended for virtual reality since it disables WIFI scans\n                    4 // WIFI_MODE_FULL_LOW_LATENCY\n                } else {\n                    3 // WIFI_MODE_FULL_HIGH_PERF\n                }\n                .into(),\n                (&wifi_lock_jstring).into(),\n            ],\n        )\n        .unwrap()\n        .l()\n        .unwrap();\n    set_lock(&mut env, &wifi_lock, enabled);\n\n    let multicast_lock_jstring = env.new_string(\"alvr_multicast_lock\").unwrap();\n    let multicast_lock = env\n        .call_method(\n            wifi_manager,\n            \"createMulticastLock\",\n            \"(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;\",\n            &[(&multicast_lock_jstring).into()],\n        )\n        .unwrap()\n        .l()\n        .unwrap();\n    set_lock(&mut env, &multicast_lock, enabled);\n}\n\npub fn get_battery_status() -> (f32, bool) {\n    let vm = vm();\n    let mut env = vm.attach_current_thread().unwrap();\n\n    let intent_action_jstring = env\n        .new_string(\"android.intent.action.BATTERY_CHANGED\")\n        .unwrap();\n    let intent_filter = env\n        .new_object(\n            \"android/content/IntentFilter\",\n            \"(Ljava/lang/String;)V\",\n            &[(&intent_action_jstring).into()],\n        )\n        .unwrap();\n    let battery_intent = env\n        .call_method(\n            unsafe { JObject::from_raw(context()) },\n            \"registerReceiver\",\n            \"(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;\",\n            &[(&JObject::null()).into(), (&intent_filter).into()],\n        )\n        .unwrap()\n        .l()\n        .unwrap();\n\n    let level_jstring = env.new_string(\"level\").unwrap();\n    let level = env\n        .call_method(\n            &battery_intent,\n            \"getIntExtra\",\n            \"(Ljava/lang/String;I)I\",\n            &[(&level_jstring).into(), (-1).into()],\n        )\n        .unwrap()\n        .i()\n        .unwrap();\n    let scale_jstring = env.new_string(\"scale\").unwrap();\n    let scale = env\n        .call_method(\n            &battery_intent,\n            \"getIntExtra\",\n            \"(Ljava/lang/String;I)I\",\n            &[(&scale_jstring).into(), (-1).into()],\n        )\n        .unwrap()\n        .i()\n        .unwrap();\n\n    let plugged_jstring = env.new_string(\"plugged\").unwrap();\n    let plugged = env\n        .call_method(\n            &battery_intent,\n            \"getIntExtra\",\n            \"(Ljava/lang/String;I)I\",\n            &[(&plugged_jstring).into(), (-1).into()],\n        )\n        .unwrap()\n        .i()\n        .unwrap();\n\n    (level as f32 / scale as f32, plugged > 0)\n}\n"
  },
  {
    "path": "alvr/system_info/src/lib.rs",
    "content": "#[cfg(target_os = \"android\")]\npub mod android;\n\n#[cfg(target_os = \"android\")]\npub use android::*;\n\nuse alvr_common::settings_schema::SettingsSchema;\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Display, Formatter};\n\npub const PACKAGE_NAME_STORE: &str = \"alvr.client\";\npub const PACKAGE_NAME_GITHUB_DEV: &str = \"alvr.client.dev\";\npub const PACKAGE_NAME_GITHUB_STABLE: &str = \"alvr.client.stable\";\n\n// Platform of the device. It is used to match the VR runtime and enable features conditionally.\n#[derive(PartialEq, Eq, Clone, Copy)]\npub enum Platform {\n    Quest1,\n    Quest2,\n    Quest3,\n    Quest3S,\n    QuestPro,\n    QuestUnknown,\n    PicoNeo3,\n    Pico4,\n    Pico4Pro,\n    Pico4Enterprise,\n    Pico4Ultra,\n    PicoG3,\n    PicoUnknown,\n    Focus3,\n    FocusVision,\n    XRElite,\n    ViveUnknown,\n    Yvr,\n    PlayForDreamMR,\n    Lynx,\n    SamsungGalaxyXR,\n    AndroidUnknown,\n    VisionOSHeadset,\n    WindowsPc,\n    LinuxPc,\n    Macos,\n    Unknown,\n}\n\nimpl Platform {\n    pub const fn is_quest(&self) -> bool {\n        matches!(\n            self,\n            Self::Quest1\n                | Self::Quest2\n                | Self::Quest3\n                | Self::Quest3S\n                | Self::QuestPro\n                | Self::QuestUnknown\n        )\n    }\n\n    pub const fn is_pico(&self) -> bool {\n        matches!(\n            self,\n            Self::PicoG3\n                | Self::PicoNeo3\n                | Self::Pico4\n                | Self::Pico4Pro\n                | Self::Pico4Enterprise\n                | Self::Pico4Ultra\n                | Self::PicoUnknown\n        )\n    }\n\n    pub const fn is_vive(&self) -> bool {\n        matches!(\n            self,\n            Self::Focus3 | Self::FocusVision | Self::XRElite | Self::ViveUnknown\n        )\n    }\n\n    pub const fn is_yvr(&self) -> bool {\n        matches!(self, Self::Yvr | Self::PlayForDreamMR)\n    }\n}\n\nimpl Display for Platform {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        let name = match self {\n            Self::Quest1 => \"Quest 1\",\n            Self::Quest2 => \"Quest 2\",\n            Self::Quest3 => \"Quest 3\",\n            Self::Quest3S => \"Quest 3S\",\n            Self::QuestPro => \"Quest Pro\",\n            Self::QuestUnknown => \"Quest (unknown)\",\n            Self::PicoNeo3 => \"Pico Neo 3\",\n            Self::Pico4 => \"Pico 4\",\n            Self::Pico4Pro => \"Pico 4 Pro\",\n            Self::Pico4Enterprise => \"Pico 4 Enterprise\",\n            Self::Pico4Ultra => \"Pico 4 Ultra\",\n            Self::PicoG3 => \"Pico G3\",\n            Self::PicoUnknown => \"Pico (unknown)\",\n            Self::Focus3 => \"VIVE Focus 3\",\n            Self::FocusVision => \"VIVE Focus Vision\",\n            Self::XRElite => \"VIVE XR Elite\",\n            Self::ViveUnknown => \"HTC VIVE (unknown)\",\n            Self::Yvr => \"YVR\",\n            Self::PlayForDreamMR => \"Play For Dream MR\",\n            Self::Lynx => \"Lynx Headset\",\n            Self::SamsungGalaxyXR => \"Samsung Galaxy XR\",\n            Self::AndroidUnknown => \"Android (unknown)\",\n            Self::VisionOSHeadset => \"visionOS Headset\",\n            Self::WindowsPc => \"Windows PC\",\n            Self::LinuxPc => \"Linux PC\",\n            Self::Macos => \"macOS\",\n            Self::Unknown => \"Unknown\",\n        };\n        write!(f, \"{name}\")\n    }\n}\n\n#[cfg_attr(not(target_os = \"android\"), expect(unused_variables))]\npub fn platform(runtime_name: Option<String>, runtime_version: Option<u64>) -> Platform {\n    #[cfg(target_os = \"android\")]\n    {\n        let manufacturer = android::manufacturer_name();\n        let model = android::model_name();\n        let device = android::device_name();\n        let product = android::product_name();\n\n        // TODO: Better Android XR heuristic\n        // (Maybe check runtime json for /system/lib64/libopenxr.google.so?)\n\n        alvr_common::info!(\n            \"manufacturer: {manufacturer}, model: {model}, device: {device}, product: {product}, \\\n            runtime_name: {runtime_name:?}, runtime_version: {runtime_version:?}\",\n        );\n\n        match (\n            manufacturer.as_str(),\n            model.as_str(),\n            device.as_str(),\n            product.as_str(),\n        ) {\n            (\"Oculus\", _, \"monterey\", _) => Platform::Quest1,\n            (\"Oculus\", _, \"hollywood\", _) => Platform::Quest2,\n            (\"Oculus\", _, \"eureka\", _) => Platform::Quest3,\n            (\"Oculus\", _, \"panther\", _) => Platform::Quest3S,\n            (\"Oculus\", _, \"seacliff\", _) => Platform::QuestPro,\n            (\"Oculus\", _, _, _) => Platform::QuestUnknown,\n            (\"Pico\", \"Pico Neo 3\" | \"Pico Neo3 Link\", _, _) => Platform::PicoNeo3,\n            (\"Pico\", _, _, \"PICO 4 Pro\") => Platform::Pico4Pro,\n            (\"Pico\", _, _, \"PICO 4 Enterprise\") => Platform::Pico4Enterprise,\n            (\"Pico\", _, _, \"PICO 4\") => Platform::Pico4,\n            (\"Pico\", _, _, \"PICO 4 Ultra\") => Platform::Pico4Ultra,\n            (\"Pico\", _, _, \"PICO G3\") => Platform::PicoG3,\n            (\"Pico\", _, _, _) => Platform::PicoUnknown,\n            (\"HTC\", \"VIVE Focus 3\", _, _) => Platform::Focus3,\n            (\"HTC\", \"VIVE Focus Vision\", _, _) => Platform::FocusVision,\n            (\"HTC\", \"VIVE XR Series\", _, _) => Platform::XRElite,\n            (\"HTC\", _, _, _) => Platform::ViveUnknown,\n            (\"YVR\", _, _, _) => Platform::Yvr,\n            (\"Play For Dream\", _, _, _) => Platform::PlayForDreamMR,\n            (\"Lynx Mixed Reality\", _, _, _) => Platform::Lynx,\n            (\"samsung\", _, \"xrvst2\", _) => Platform::SamsungGalaxyXR,\n            _ => Platform::AndroidUnknown,\n        }\n    }\n    #[cfg(not(target_os = \"android\"))]\n    {\n        match std::env::consts::OS {\n            \"visionos\" => Platform::VisionOSHeadset,\n            \"windows\" => Platform::WindowsPc,\n            \"linux\" => Platform::LinuxPc,\n            \"macos\" => Platform::Macos,\n            _ => Platform::Unknown,\n        }\n    }\n}\n\n#[cfg(not(target_os = \"android\"))]\npub fn local_ip() -> std::net::IpAddr {\n    use std::net::{IpAddr, Ipv4Addr};\n\n    local_ip_address::local_ip().unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED))\n}\n\n#[derive(SettingsSchema, Serialize, Deserialize, Clone)]\npub enum ClientFlavor {\n    Store,\n    Github,\n    Custom(String),\n}\n"
  },
  {
    "path": "alvr/vrcompositor_wrapper/Cargo.toml",
    "content": "[package]\nname = \"alvr_vrcompositor_wrapper\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_common.workspace = true\nalvr_filesystem.workspace = true\n\n[build-dependencies]\nxshell = \"0.2\"\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\nexec = \"0.3.1\"\n"
  },
  {
    "path": "alvr/vrcompositor_wrapper/build.rs",
    "content": "#[cfg(target_os = \"linux\")]\nfn main() {\n    use std::{env, path::PathBuf};\n    use xshell::{Shell, cmd};\n\n    let out_dir = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    let target_dir = out_dir.join(\"../../..\");\n\n    let sh = Shell::new().unwrap();\n    let command = format!(\n        \"g++ -shared -fPIC $(pkg-config --cflags libdrm) drm-lease-shim.cpp -o {}/alvr_drm_lease_shim.so\",\n        target_dir.display()\n    );\n    cmd!(sh, \"bash -c {command}\").run().unwrap();\n}\n\n#[cfg(not(target_os = \"linux\"))]\nfn main() {}\n"
  },
  {
    "path": "alvr/vrcompositor_wrapper/drm-lease-shim.cpp",
    "content": "#include <dlfcn.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <xf86drmMode.h>\n\n#include <fstream>\n#include <filesystem>\n\n#define PICOJSON_USE_INT64\n#include \"../server_openvr/cpp/alvr_server/include/picojson.h\"\n\n#define LOAD_FN(f) \\\n    if (!real_##f) { \\\n        real_##f = reinterpret_cast<decltype(real_##f)>(dlsym(RTLD_NEXT, #f)); \\\n        if (!real_##f) { \\\n            ERR(\"Failed to load %s\", #f); \\\n            abort(); \\\n        } \\\n    } \\\n\n#define LOG(f, ...) printf(f \"\\n\" __VA_OPT__(,) __VA_ARGS__)\n#define ERR(f, ...) fprintf(stderr, f \"\\n\" __VA_OPT__(,) __VA_ARGS__)\n\ntemplate <typename X, typename Y>\nstatic constexpr bool compare_ptr(X x, Y y)\n{\n    return reinterpret_cast<void*>(x) == reinterpret_cast<void*>(y);\n}\n\nstruct wl_registry_listener {\n    void (*global)(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version);\n    void (*global_remove)(void *data, struct wl_registry *wl_registry, uint32_t name);\n};\n\nstruct wp_drm_lease_device_v1_listener {\n    void (*drm_fd)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, int32_t fd);\n    void (*connector)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, struct wp_drm_lease_connector_v1 *id);\n    void (*done)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1);\n    void (*released)(void *data, struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1);\n};\n\nstruct wp_drm_lease_connector_v1_listener {\n    void (*name)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, const char *name);\n    void (*description)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, const char *description);\n    void (*connector_id)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, uint32_t connector_id);\n    void (*done)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1);\n    void (*withdrawn)(void *data, struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1);\n};\n\nstruct wp_drm_lease_v1_listener {\n    void (*lease_fd)(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1, int32_t leased_fd);\n    void (*finished)(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1);\n};\n\nstatic struct wp_drm_lease_device_v1 {} fake_device_id;\nstatic struct wp_drm_lease_connector_v1 {} fake_connector_id;\nstatic struct wp_drm_lease_request_v1 {} fake_lease_request_id;\nstatic struct wp_drm_lease_v1 {} fake_lease_id;\n\nstatic int drm_fd = -1;\nstatic int drm_connector_id = -1;\n\nstatic void open_drm_fd()\n{\n    static drmModeResPtr (*real_drmModeGetResources)(int fd) = nullptr;\n    LOAD_FN(drmModeGetResources);\n    for(auto cardCandidate : std::filesystem::directory_iterator(\"/dev/dri\")) {\n        if(cardCandidate.path().filename().string().rfind(\"card\", 0) == 0) {\n            LOG(\"cardCandidateFound: file=%s\", cardCandidate.path().c_str());\n            drm_fd = open(cardCandidate.path().c_str(), O_RDONLY);\n            auto res = real_drmModeGetResources(drm_fd);\n            if (res && res->count_connectors) {\n                drm_connector_id = res->connectors[0];\n                break;\n            }\n        }\n    }\n    LOG(\"DRM: fd=%d, connector_id=%d\", drm_fd, drm_connector_id);\n}\n\nstatic int (*real_wl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data);\nstatic int hooked_wl_proxy_add_listener(struct wl_proxy *proxy, void (**implementation)(void), void *data)\n{\n    // wp_drm_lease_connector_v1\n    if (compare_ptr(proxy, &fake_connector_id)) {\n        LOG(\"LISTENER wp_drm_lease_connector_v1\");\n        auto listener = reinterpret_cast<struct wp_drm_lease_connector_v1_listener*>(implementation);\n        listener->name(data, &fake_connector_id, \"ALVR_name\");\n        listener->description(data, &fake_connector_id, \"ALVR_description\");\n        listener->connector_id(data, &fake_connector_id, drm_connector_id);\n        listener->done(data, &fake_connector_id);\n        LOG(\"LISTENER done\");\n        return 0;\n    }\n\n    // wp_drm_lease_v1\n    if (compare_ptr(proxy, &fake_lease_id)) {\n        LOG(\"LISTENER wp_drm_lease_v1\");\n        auto listener = reinterpret_cast<struct wp_drm_lease_v1_listener*>(implementation);\n        listener->lease_fd(data, &fake_lease_id, drm_fd);\n        LOG(\"LISTENER done\");\n        return 0;\n    }\n\n    // wp_drm_lease_device_v1\n    if (compare_ptr(proxy, &fake_device_id)) {\n        LOG(\"LISTENER wp_drm_lease_device_v1\");\n        auto listener = reinterpret_cast<struct wp_drm_lease_device_v1_listener*>(implementation);\n        open_drm_fd();\n        listener->drm_fd(data, &fake_device_id, drm_fd);\n        if (drm_connector_id != -1) {\n            listener->connector(data, &fake_device_id, &fake_connector_id);\n        }\n        listener->done(data, &fake_device_id);\n        LOG(\"LISTENER done\");\n        return 0;\n    }\n\n    const char *name = *(*reinterpret_cast<const char***>(proxy));\n\n    if (strcmp(name, \"wl_registry\") == 0) {\n        LOG(\"LISTENER wl_registry\");\n        auto listener = reinterpret_cast<struct wl_registry_listener*>(implementation);\n        listener->global(data, reinterpret_cast<struct wl_registry*>(proxy), 0, \"wp_drm_lease_device_v1\", 1);\n        LOG(\"LISTENER done\");\n        return 0;\n    }\n\n    return real_wl_proxy_add_listener(proxy, implementation, data);\n}\n\nstatic struct wl_proxy *(*real_wl_proxy_marshal_flags)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...);\nstatic struct wl_proxy *hooked_wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...)\n{\n    // wp_drm_lease_connector_v1\n    if (compare_ptr(proxy, &fake_connector_id)) {\n        if (opcode == 0) {\n            LOG(\"CALL wp_drm_lease_connector_v1_destroy\");\n        } else {\n            ERR(\"Unknown wp_drm_lease_connector_v1 opcode=%u\", opcode);\n        }\n        return nullptr;\n    }\n\n    // wp_drm_lease_request_v1\n    if (compare_ptr(proxy, &fake_lease_request_id)) {\n        if (opcode == 0) {\n            LOG(\"CALL wp_drm_lease_request_v1_request_connector\");\n        } else if (opcode == 1) {\n            LOG(\"CALL wp_drm_lease_request_v1_submit\");\n            return reinterpret_cast<struct wl_proxy*>(&fake_lease_id);\n        } else {\n            ERR(\"Unknown wp_drm_lease_request_v1 opcode=%u\", opcode);\n        }\n        return nullptr;\n    }\n\n    // wp_drm_lease_device_v1\n    if (compare_ptr(proxy, &fake_device_id)) {\n        if (opcode == 0) {\n            LOG(\"CALL wp_drm_lease_device_v1_create_lease_request\");\n            return reinterpret_cast<struct wl_proxy*>(&fake_lease_request_id);\n        } else if (opcode == 1) {\n            LOG(\"CALL wp_drm_lease_device_v1_release\");\n        } else {\n            ERR(\"Unknown wp_drm_lease_device_v1 opcode=%u\", opcode);\n        }\n        return nullptr;\n    }\n\n    const char *name = **reinterpret_cast<const char***>(proxy);\n    const char *iname = *reinterpret_cast<const char**>(const_cast<struct wl_interface*>(interface));\n\n    if (strcmp(name, \"wl_registry\") == 0 && strcmp(iname, \"wp_drm_lease_device_v1\") == 0 && opcode == 0) {\n        LOG(\"CALL wl_registry_bind - wp_drm_lease_device_v1\");\n        return reinterpret_cast<struct wl_proxy*>(&fake_device_id);\n    }\n\n    __builtin_return(__builtin_apply(reinterpret_cast<void(*)(...)>(real_wl_proxy_marshal_flags), __builtin_apply_args(), 1024));\n}\n\nextern \"C\" void *SDL_LoadFunction(void *handle, const char *name)\n{\n    static void *(*real_SDL_LoadFunction)(void *handle, const char *name) = nullptr;\n    LOAD_FN(SDL_LoadFunction);\n\n#define HOOK(f) \\\n    if (strcmp(name, #f) == 0) { \\\n        LOG(\"HOOK %s\", #f); \\\n        real_##f = reinterpret_cast<decltype(real_##f)>(real_SDL_LoadFunction(handle, #f)); \\\n        return reinterpret_cast<void*>(hooked_##f); \\\n    } \\\n\n    HOOK(wl_proxy_add_listener);\n    HOOK(wl_proxy_marshal_flags);\n\n#undef HOOK\n\n    return real_SDL_LoadFunction(handle, name);\n}\n\nextern \"C\" drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId)\n{\n    LOG(\"CALL drmModeGetConnector(%d, %u)\", fd, connectorId);\n\n    static drmModeConnectorPtr (*real_drmModeGetConnector)(int fd, uint32_t connectorId) = nullptr;\n    LOAD_FN(drmModeGetConnector);\n\n    auto con = real_drmModeGetConnector(fd, connectorId);\n    if (con) {\n        auto sessionFile = std::ifstream(getenv(\"ALVR_SESSION_JSON\"));\n        auto json = std::string(std::istreambuf_iterator<char>(sessionFile), std::istreambuf_iterator<char>());\n        picojson::value v;\n        picojson::parse(v, json);\n        auto config = v.get(\"openvr_config\");\n\n        con->count_modes = 1;\n        con->modes = (drmModeModeInfo*)calloc(1, sizeof(drmModeModeInfo));\n        con->modes->hdisplay = config.get(\"eye_resolution_width\").get<int64_t>() * 2;\n        con->modes->vdisplay = config.get(\"eye_resolution_height\").get<int64_t>();\n    }\n    return con;\n}\n\n__attribute__((constructor)) static void lib_init()\n{\n    LOG(\"ALVR: drm-lease shim loaded\");\n\n    unsetenv(\"LD_PRELOAD\");\n}\n"
  },
  {
    "path": "alvr/vrcompositor_wrapper/src/main.rs",
    "content": "#[cfg(target_os = \"linux\")]\nfn main() {\n    let argv0 = std::env::args().next().unwrap();\n    // location of the ALVR vulkan layer manifest\n    let layer_path = match std::fs::read_link(&argv0) {\n        Ok(path) => path\n            .parent()\n            .unwrap()\n            .join(\"../../share/vulkan/explicit_layer.d\"),\n        Err(err) => panic!(\"Failed to read vrcompositor symlink: {err}\"),\n    };\n    unsafe {\n        std::env::set_var(\"VK_LAYER_PATH\", layer_path);\n        // Vulkan < 1.3.234\n        std::env::set_var(\"VK_INSTANCE_LAYERS\", \"VK_LAYER_ALVR_capture\");\n        std::env::set_var(\"DISABLE_VK_LAYER_VALVE_steam_fossilize_1\", \"1\");\n        std::env::set_var(\"DISABLE_MANGOHUD\", \"1\");\n        std::env::set_var(\"DISABLE_VKBASALT\", \"1\");\n        std::env::set_var(\"DISABLE_OBS_VKCAPTURE\", \"1\");\n        // Vulkan >= 1.3.234\n        std::env::set_var(\n            \"VK_LOADER_LAYERS_ENABLE\",\n            \"VK_LAYER_ALVR_capture,VK_LAYER_MESA_device_select\",\n        );\n        std::env::set_var(\"VK_LOADER_LAYERS_DISABLE\", \"*\");\n    }\n    if std::env::var(\"WAYLAND_DISPLAY\").is_ok() {\n        let drm_lease_shim_path = match std::fs::read_link(&argv0) {\n            Ok(path) => path.parent().unwrap().join(\"alvr_drm_lease_shim.so\"),\n            Err(err) => panic!(\"Failed to read vrcompositor symlink: {err}\"),\n        };\n        unsafe {\n            std::env::set_var(\"LD_PRELOAD\", drm_lease_shim_path);\n            std::env::set_var(\n                \"ALVR_SESSION_JSON\",\n                alvr_filesystem::filesystem_layout_invalid()\n                    .session()\n                    .to_string_lossy()\n                    .to_string(),\n            );\n        }\n    }\n\n    let err = exec::execvp(argv0 + \".real\", std::env::args());\n    println!(\"Failed to run vrcompositor {err}\");\n}\n\n#[cfg(not(target_os = \"linux\"))]\nfn main() {}\n"
  },
  {
    "path": "alvr/vulkan_layer/.gitignore",
    "content": "CMakeLists.txt.user\nCMakeCache.txt\nCMakeFiles\nCMakeScripts\nTesting\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\ncompile_commands.json\nCTestTestfile.cmake\n_deps\nbuild\n"
  },
  {
    "path": "alvr/vulkan_layer/Cargo.toml",
    "content": "[package]\nname = \"alvr_vulkan_layer\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nalvr_common.workspace = true\nalvr_filesystem.workspace = true\n\n[build-dependencies]\nbindgen = \"0.72\"\ncc = { version = \"1\", features = [\"parallel\"] }\npkg-config = \"0.3\"\nwalkdir = \"2\"\n"
  },
  {
    "path": "alvr/vulkan_layer/LICENSE",
    "content": "Copyright (c) 2019 Arm Limited.\nCopyright (c) 2021-2024 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "alvr/vulkan_layer/README.md",
    "content": "# ALVR capture vulkan layer\n\n## Introduction\n\nThe ALVR capture vulkan layer is intended to overcome a limitation of SteamVR runtime on Linux: it does'nt allow software based output devices.\nThe layer is based on [vulkan wsi layer](https://gitlab.freedesktop.org/mesa/vulkan-wsi-layer), which is meant to implement window system integration as layers.\n\nThe ALVR layer adds a display to the vkGetPhysicalDeviceDisplayPropertiesKHR call, and implements all functions related to that device. It then allows images of the swapchain to be shared to an other process (the alvr server process), and communicates present calls.\n\nThere are unfortunately a few hacks that make it heavily dependent to SteamVR: requested extentions manipulation to enable the required ones, searching through the stack to find the headset position, and not fully implementing the advertised features.\n"
  },
  {
    "path": "alvr/vulkan_layer/build.rs",
    "content": "#[cfg(target_os = \"linux\")]\nfn main() {\n    use std::{env, path::PathBuf};\n\n    let out_dir = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    let cpp_dir = PathBuf::from(env::var(\"CARGO_MANIFEST_DIR\").unwrap());\n    let server_cpp_dir =\n        PathBuf::from(env::var(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"../server_openvr/cpp\");\n\n    let vulkan = pkg_config::Config::new().probe(\"vulkan\").unwrap();\n    let libunwind = pkg_config::Config::new().probe(\"libunwind\").unwrap();\n\n    let cpp_paths = walkdir::WalkDir::new(\".\")\n        .into_iter()\n        .filter_map(|maybe_entry| maybe_entry.ok())\n        .map(|entry| entry.into_path())\n        .collect::<Vec<_>>();\n\n    let source_files_paths = cpp_paths.iter().filter(|path| {\n        path.extension()\n            .filter(|ext| ext.to_string_lossy() == \"cpp\")\n            .is_some()\n    });\n\n    let mut build = cc::Build::new();\n    build\n        .cpp(true)\n        .files(source_files_paths)\n        .flag(\"-std=c++17\")\n        .flag_if_supported(\"-Wno-unused-parameter\")\n        .define(\"VK_USE_PLATFORM_XLIB_XRANDR_EXT\", None)\n        .include(cpp_dir)\n        .include(server_cpp_dir)\n        .includes(vulkan.include_paths)\n        .includes(libunwind.include_paths);\n\n    build.compile(\"VkLayer_ALVR\");\n\n    bindgen::builder()\n        .clang_arg(\"-xc++\")\n        .header(\"layer/layer.h\")\n        .derive_default(true)\n        .generate()\n        .expect(\"layer bindings\")\n        .write_to_file(out_dir.join(\"layer_bindings.rs\"))\n        .expect(\"layer_bindings.rs\");\n\n    for lib in libunwind.libs {\n        println!(\"cargo:rustc-link-lib={lib}\");\n    }\n\n    // fail build if there are undefined symbols in final library\n    println!(\"cargo:rustc-cdylib-link-arg=-Wl,--no-undefined\");\n\n    for path in cpp_paths {\n        println!(\"cargo:rerun-if-changed={}\", path.to_string_lossy());\n    }\n}\n\n#[cfg(not(target_os = \"linux\"))]\nfn main() {}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/alvr_x86_64.json",
    "content": "{\n\t\"file_format_version\" : \"1.1.2\",\n\t\"layer\" : {\n\t\t\"name\": \"VK_LAYER_ALVR_capture\",\n\t\t\"type\": \"GLOBAL\",\n\t\t\"library_path\": \"../../../lib64/libalvr_vulkan_layer.so\",\n\t\t\"api_version\": \"1.0.68\",\n\t\t\"implementation_version\": \"1\",\n\t\t\"description\": \"ALVR display intercept layer\",\n\t\t\"instance_extensions\": [\n\t\t\t{\n\t\t\t\t\"name\" : \"VK_EXT_headless_surface\",\n\t\t\t\t\"spec_version\" : \"1\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\" : \"VK_KHR_surface\",\n\t\t\t\t\"spec_version\" : \"1\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\" : \"VK_EXT_acquire_xlib_display\",\n\t\t\t\t\"spec_version\" : \"1\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\" : \"VK_KHR_display\",\n\t\t\t\t\"spec_version\" : \"1\"\n\t\t\t}\n\t\t],\n\t\t\"device_extensions\": [\n\t\t\t{\n\t\t\t\t\"name\" : \"VK_KHR_swapchain\",\n\t\t\t\t\"spec_version\" : \"1\"\n\t\t\t}\n\t\t],\n\t\t\"functions\": {\n\t\t\t\"vkNegotiateLoaderLayerInterfaceVersion\" : \"ALVR_Negotiate\"\n\t\t},\n\t\t\"disable_environment\": {\n\t\t\t\"DISABLE_ALVR_DISPLAY\": \"1\"\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/device_api.cpp",
    "content": "#include \"device_api.hpp\"\n#include \"private_data.hpp\"\n#include \"wsi/display.hpp\"\n#include \"settings.h\"\n\n#include <vector>\n\nstatic const char *alvr_display_name = \"ALVR display\";\n\nconst struct {\n} alvr_display;\nconst VkDisplayKHR alvr_display_handle = (VkDisplayKHR_T *)&alvr_display;\n\nconst struct {\n} alvr_display_mode;\nconst VkDisplayModeKHR alvr_display_mode_handle = (VkDisplayModeKHR_T *)&alvr_display_mode;\n\nextern \"C\" {\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPropertiesKHR(\n    VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties) {\n    if (!pProperties) {\n        *pPropertyCount = 1;\n        return VK_SUCCESS;\n    }\n    if (*pPropertyCount < 1) {\n        return VK_INCOMPLETE;\n    }\n    pProperties[0].display = alvr_display_handle;\n    pProperties[0].displayName = alvr_display_name;\n    pProperties[0].physicalDimensions = VkExtent2D{20, 20};\n    pProperties[0].physicalResolution = VkExtent2D{Settings::Instance().m_renderWidth, Settings::Instance().m_renderHeight};\n    pProperties[0].supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n    pProperties[0].planeReorderPossible = VK_FALSE;\n    pProperties[0].persistentContent = VK_TRUE;\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayModePropertiesKHR(\n    VkPhysicalDevice device, VkDisplayKHR display, uint32_t *pPropertyCount,\n    VkDisplayModePropertiesKHR *pProperties) {\n    if (display != alvr_display_handle) {\n        *pPropertyCount = 0;\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n    if (!pProperties) {\n        *pPropertyCount = 1;\n        return VK_SUCCESS;\n    }\n    if (*pPropertyCount < 1) {\n        return VK_INCOMPLETE;\n    }\n    pProperties[0].displayMode = alvr_display_mode_handle;\n    pProperties[0].parameters.visibleRegion = VkExtent2D{Settings::Instance().m_renderWidth, Settings::Instance().m_renderHeight};\n    pProperties[0].parameters.refreshRate = Settings::Instance().m_refreshRate * 1000;\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPlanePropertiesKHR(\n    VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties) {\n    if (!pProperties) {\n        *pPropertyCount = 1;\n        return VK_SUCCESS;\n    }\n    if (*pPropertyCount < 1) {\n        return VK_INCOMPLETE;\n    }\n    pProperties[0].currentDisplay = alvr_display_handle;\n    pProperties[0].currentStackIndex = 0;\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireXlibDisplayEXT(VkPhysicalDevice device,\n                                                                 Display *dpy,\n                                                                 VkDisplayKHR display) {\n    if (display != alvr_display_handle) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDrmDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                            int32_t drmFd,\n                                                            uint32_t connectorId,\n                                                            VkDisplayKHR *display) {\n    *display = alvr_display_handle;\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireDrmDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                                int32_t drmFd,\n                                                                VkDisplayKHR display) {\n    if (display != alvr_display_handle) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayPlaneSupportedDisplaysKHR(\n    VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t *pDisplayCount,\n    VkDisplayKHR *pDisplays) {\n    if (planeIndex != 0) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n    if (!pDisplays) {\n        *pDisplayCount = 1;\n        return VK_SUCCESS;\n    }\n    pDisplays[0] = alvr_display_handle;\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayPlaneSurfaceKHR(\n    VkInstance vkinstance, const VkDisplaySurfaceCreateInfoKHR * /*pCreateInfo*/,\n    const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {\n    auto &instance = layer::instance_private_data::get(vkinstance);\n    VkHeadlessSurfaceCreateInfoEXT createInfo = {};\n    createInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT;\n    auto res =\n        instance.disp.CreateHeadlessSurfaceEXT(vkinstance, &createInfo, pAllocator, pSurface);\n    if (*pSurface == NULL)\n        std::abort();\n    instance.add_surface(*pSurface);\n    return res;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkReleaseDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                             VkDisplayKHR display) {\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroySurfaceKHR(VkInstance vkinstance,\n                                                         VkSurfaceKHR surface,\n                                                         const VkAllocationCallbacks *pAllocator) {\n    auto &instance = layer::instance_private_data::get(vkinstance);\n    if (instance.should_layer_handle_surface(surface)) {\n        return;\n    }\n    return instance.disp.DestroySurfaceKHR(vkinstance, surface, pAllocator);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkRegisterDisplayEventEXT(\n    VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT *pDisplayEventInfo,\n    const VkAllocationCallbacks *pAllocator, VkFence *pFence) {\n    if (display != alvr_display_handle) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    auto &instance = layer::device_private_data::get(device);\n    *pFence = instance.display->get_vsync_fence();\n\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroyFence(VkDevice device, VkFence fence,\n                                                    const VkAllocationCallbacks *pAllocator) {\n    auto &instance = layer::device_private_data::get(device);\n    auto alvr_fence = instance.display->peek_vsync_fence();\n    if (fence == alvr_fence) {\n        return;\n    }\n    instance.disp.DestroyFence(device, fence, pAllocator);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkWaitForFences(VkDevice device, uint32_t fenceCount,\n                                                         const VkFence *pFences, VkBool32 waitAll,\n                                                         uint64_t timeout) {\n    auto &instance = layer::device_private_data::get(device);\n    auto alvr_fence = instance.display->peek_vsync_fence();\n    for (uint32_t i = 0; i < fenceCount; ++i) {\n        if (pFences[i] == alvr_fence) {\n            assert(fenceCount == 1); // only our fence\n            return instance.display->wait_for_vsync(timeout) ? VK_SUCCESS : VK_TIMEOUT;\n        }\n    }\n    return instance.disp.WaitForFences(device, fenceCount, pFences, waitAll, timeout);\n}\n\nVKAPI_ATTR VkResult wsi_layer_vkGetFenceStatus(VkDevice device, VkFence fence)\n{\n    auto &instance = layer::device_private_data::get(device);\n    auto alvr_fence = instance.display->peek_vsync_fence();\n    if (fence == alvr_fence) {\n        return instance.display->is_signaled() ? VK_SUCCESS : VK_NOT_READY;\n    }\n    return instance.disp.GetFenceStatus(device, fence);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayModeKHR(\n    VkPhysicalDevice                            physicalDevice,\n    VkDisplayKHR                                display,\n    const VkDisplayModeCreateInfoKHR*           pCreateInfo,\n    const VkAllocationCallbacks*                pAllocator,\n    VkDisplayModeKHR*                           pMode)\n{\n  return VK_ERROR_INITIALIZATION_FAILED;\n}\n\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/device_api.hpp",
    "content": "#pragma once\n\n#include <vulkan/vulkan.h>\n\nextern \"C\" {\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPropertiesKHR(\n    VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayModePropertiesKHR(\n    VkPhysicalDevice device, VkDisplayKHR display, uint32_t *pPropertyCount,\n    VkDisplayModePropertiesKHR *pProperties);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetPhysicalDeviceDisplayPlanePropertiesKHR(\n    VkPhysicalDevice device, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireXlibDisplayEXT(VkPhysicalDevice device,\n                                                                 Display *dpy,\n                                                                 VkDisplayKHR display);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDrmDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                            int32_t drmFd,\n                                                            uint32_t connectorId,\n                                                            VkDisplayKHR *display);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkAcquireDrmDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                                int32_t drmFd,\n                                                                VkDisplayKHR display);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkGetDisplayPlaneSupportedDisplaysKHR(\n    VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t *pDisplayCount,\n    VkDisplayKHR *pDisplays);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayPlaneSurfaceKHR(\n    VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,\n    const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkReleaseDisplayEXT(VkPhysicalDevice physicalDevice,\n                                                             VkDisplayKHR display);\n\nVKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,\n                                                         const VkAllocationCallbacks *pAllocator);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkRegisterDisplayEventEXT(\n    VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT *pDisplayEventInfo,\n    const VkAllocationCallbacks *pAllocator, VkFence *pFence);\n\nVKAPI_ATTR void VKAPI_CALL wsi_layer_vkDestroyFence(VkDevice device, VkFence fence,\n                                                    const VkAllocationCallbacks *pAllocator);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkWaitForFences(VkDevice device, uint32_t fenceCount,\n                                                         const VkFence *pFences, VkBool32 waitAll,\n                                                         uint64_t timeout);\n\nVKAPI_ATTR VkResult wsi_layer_vkGetFenceStatus(VkDevice device, VkFence fence);\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkCreateDisplayModeKHR(\n    VkPhysicalDevice                            physicalDevice,\n    VkDisplayKHR                                display,\n    const VkDisplayModeCreateInfoKHR*           pCreateInfo,\n    const VkAllocationCallbacks*                pAllocator,\n    VkDisplayModeKHR*                           pMode);\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/layer.cpp",
    "content": "/*\n * Copyright (c) 2016-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include <cassert>\n#include <cstdio>\n#include <cstring>\n#include <fstream>\n#include <iostream>\n\n#include <vulkan/vk_layer.h>\n\n#include \"settings.h\"\n#include \"device_api.hpp\"\n#include \"private_data.hpp\"\n#include \"surface_api.hpp\"\n#include \"swapchain_api.hpp\"\n#include \"util/custom_allocator.hpp\"\n#include \"util/extension_list.hpp\"\n#include \"wsi/wsi_factory.hpp\"\n#include \"layer.h\"\n\n#define VK_LAYER_API_VERSION VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION)\n\nnamespace layer {\n\nstatic const VkLayerProperties global_layer = {\n    \"VK_LAYER_ALVR_capture\",\n    VK_LAYER_API_VERSION,\n    1,\n    \"ALVR capture layer\",\n};\nstatic const VkExtensionProperties device_extension[] = {\n    {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION}};\nstatic const VkExtensionProperties instance_extension[] = {\n    {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}};\n\nVKAPI_ATTR VkResult extension_properties(const uint32_t count,\n                                         const VkExtensionProperties *layer_ext, uint32_t *pCount,\n                                         VkExtensionProperties *pProp) {\n    uint32_t size;\n\n    if (pProp == NULL || layer_ext == NULL) {\n        *pCount = count;\n        return VK_SUCCESS;\n    }\n\n    size = *pCount < count ? *pCount : count;\n    memcpy(pProp, layer_ext, size * sizeof(VkExtensionProperties));\n    *pCount = size;\n    if (size < count) {\n        return VK_INCOMPLETE;\n    }\n\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult layer_properties(const uint32_t count, const VkLayerProperties *layer_prop,\n                                     uint32_t *pCount, VkLayerProperties *pProp) {\n    uint32_t size;\n\n    if (pProp == NULL || layer_prop == NULL) {\n        *pCount = count;\n        return VK_SUCCESS;\n    }\n\n    size = *pCount < count ? *pCount : count;\n    memcpy(pProp, layer_prop, size * sizeof(VkLayerProperties));\n    *pCount = size;\n    if (size < count) {\n        return VK_INCOMPLETE;\n    }\n\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkLayerInstanceCreateInfo *get_chain_info(const VkInstanceCreateInfo *pCreateInfo,\n                                                     VkLayerFunction func) {\n    VkLayerInstanceCreateInfo *chain_info = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext;\n    while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&\n                           chain_info->function == func)) {\n        chain_info = (VkLayerInstanceCreateInfo *)chain_info->pNext;\n    }\n\n    return chain_info;\n}\n\nVKAPI_ATTR VkLayerDeviceCreateInfo *get_chain_info(const VkDeviceCreateInfo *pCreateInfo,\n                                                   VkLayerFunction func) {\n    VkLayerDeviceCreateInfo *chain_info = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext;\n    while (chain_info && !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&\n                           chain_info->function == func)) {\n        chain_info = (VkLayerDeviceCreateInfo *)chain_info->pNext;\n    }\n\n    return chain_info;\n}\n\n/* This is where the layer is initialised and the instance dispatch table is constructed. */\nVKAPI_ATTR VkResult create_instance(const VkInstanceCreateInfo *pCreateInfo,\n                                    const VkAllocationCallbacks *pAllocator,\n                                    VkInstance *pInstance) {\n    // Make sure settings are loaded before we access them\n    Settings::Instance().Load();\n\n    VkLayerInstanceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);\n    PFN_vkSetInstanceLoaderData loader_callback =\n        get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetInstanceLoaderData;\n\n    if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n\n    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =\n        layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;\n\n    PFN_vkCreateInstance fpCreateInstance =\n        (PFN_vkCreateInstance)fpGetInstanceProcAddr(nullptr, \"vkCreateInstance\");\n    if (nullptr == fpCreateInstance) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n\n    /* Advance the link info for the next element on the chain. */\n    layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;\n\n    /* The layer needs some Vulkan 1.2 functionality in order to operate correctly.\n     * We thus change the application info to require this API version, if necessary.\n     * This may have consequences for ICDs whose behaviour depends on apiVersion.\n     */\n    const uint32_t minimum_required_vulkan_version = VK_API_VERSION_1_2;\n    VkApplicationInfo modified_app_info{};\n    if (nullptr != pCreateInfo->pApplicationInfo) {\n        modified_app_info = *pCreateInfo->pApplicationInfo;\n        if (modified_app_info.apiVersion < minimum_required_vulkan_version) {\n            modified_app_info.apiVersion = minimum_required_vulkan_version;\n        }\n    } else {\n        modified_app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n        modified_app_info.apiVersion = minimum_required_vulkan_version;\n    }\n\n    // Hijack one extension name\n    // the headless extension can't be added as a new parameter, because the loader performs a copy before\n    // calling the createInstance functions. The loader must know we activated this function because it\n    // will enable bits in the wsi part, so we switch to vulkan 1.1, and replace one of the extentions\n    // that has been promoted, with a const_cast.\n    for (uint32_t i = 0 ; i < pCreateInfo->enabledExtensionCount ; ++i) {\n      if (strcmp(\"VK_KHR_external_memory_capabilities\", pCreateInfo->ppEnabledExtensionNames[i]) == 0)\n      {\n        const char** ext = const_cast<const char**>(pCreateInfo->ppEnabledExtensionNames + i);\n        *ext = VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME;\n      }\n    }\n\n    auto createInfo = *pCreateInfo;\n    createInfo.pApplicationInfo = &modified_app_info;\n\n    /* Now call create instance on the chain further down the list.\n     * Note that we do not remove the extensions that the layer supports from\n     * modified_info.ppEnabledExtensionNames. Layers have to abide the rule that vkCreateInstance\n     * must not generate an error for unrecognized extension names. Also, the loader filters the\n     * extension list to ensure that ICDs do not see extensions that they do not support.\n     */\n    VkResult result;\n    result = fpCreateInstance(&createInfo, pAllocator, pInstance);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    instance_dispatch_table table;\n    result = table.populate(*pInstance, fpGetInstanceProcAddr);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    /* Find all the platforms that the layer can handle based on\n     * pCreateInfo->ppEnabledExtensionNames. */\n    auto layer_platforms_to_enable = wsi::find_enabled_layer_platforms(pCreateInfo);\n\n    std::unique_ptr<instance_private_data> inst_data{\n        new instance_private_data{table, loader_callback, layer_platforms_to_enable}};\n    instance_private_data::set(*pInstance, std::move(inst_data));\n    return VK_SUCCESS;\n}\n\nVKAPI_ATTR VkResult create_device(VkPhysicalDevice physicalDevice,\n                                  const VkDeviceCreateInfo *pCreateInfo,\n                                  const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {\n    VkLayerDeviceCreateInfo *layerCreateInfo = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);\n    PFN_vkSetDeviceLoaderData loader_callback =\n        get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK)->u.pfnSetDeviceLoaderData;\n\n    if (nullptr == layerCreateInfo || nullptr == layerCreateInfo->u.pLayerInfo) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n\n    /* Retrieve the vkGetDeviceProcAddr and the vkCreateDevice function pointers for the next layer\n     * in the chain. */\n    PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =\n        layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr;\n    PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr =\n        layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr;\n    PFN_vkCreateDevice fpCreateDevice =\n        (PFN_vkCreateDevice)fpGetInstanceProcAddr(VK_NULL_HANDLE, \"vkCreateDevice\");\n    if (nullptr == fpCreateDevice) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n\n    /* Advance the link info for the next element on the chain. */\n    layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext;\n\n    /* Copy the extension to a util::extension_list. */\n    util::allocator allocator{pAllocator, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND};\n    util::extension_list enabled_extensions{allocator};\n    VkResult result;\n    result = enabled_extensions.add(pCreateInfo->ppEnabledExtensionNames,\n                                    pCreateInfo->enabledExtensionCount);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    /* Add the extensions required by the platforms that are being enabled in the layer. */\n    auto &inst_data = instance_private_data::get(physicalDevice);\n    const util::wsi_platform_set &enabled_platforms = inst_data.get_enabled_platforms();\n    result = wsi::add_extensions_required_by_layer(physicalDevice, enabled_platforms,\n                                                   enabled_extensions);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    util::vector<const char *> modified_enabled_extensions{allocator};\n    if (!enabled_extensions.get_extension_strings(modified_enabled_extensions)) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    /* Now call create device on the chain further down the list. */\n    VkDeviceCreateInfo modified_info = *pCreateInfo;\n    modified_info.ppEnabledExtensionNames = modified_enabled_extensions.data();\n    modified_info.enabledExtensionCount = modified_enabled_extensions.size();\n\n    // Enable timeline semaphores\n    VkPhysicalDeviceFeatures2 features = {};\n    features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;\n\n    VkPhysicalDeviceVulkan12Features features12 = {};\n    features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;\n\n    VkPhysicalDeviceFeatures2 *features_ptr = nullptr;\n    VkPhysicalDeviceVulkan12Features *features12_ptr = nullptr;\n\n    VkDeviceCreateInfo *next = &modified_info;\n    while (next->pNext) {\n        if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {\n            features_ptr = (VkPhysicalDeviceFeatures2*)next;\n        } else if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) {\n            features12_ptr = (VkPhysicalDeviceVulkan12Features*)next;\n        }\n        next = (VkDeviceCreateInfo*)next->pNext;\n    }\n    if (!features_ptr) {\n        features_ptr = &features;\n        next->pNext = features_ptr;\n        next = (VkDeviceCreateInfo*)features_ptr;\n    }\n    if (!features12_ptr) {\n        features12_ptr = &features12;\n        next->pNext = features12_ptr;\n        next = (VkDeviceCreateInfo*)features12_ptr;\n    }\n    features12_ptr->timelineSemaphore = true;\n\n    if (modified_info.pEnabledFeatures) {\n        features_ptr->features = *modified_info.pEnabledFeatures;\n        modified_info.pEnabledFeatures = nullptr;\n    }\n\n    result = fpCreateDevice(physicalDevice, &modified_info, pAllocator, pDevice);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    device_dispatch_table table;\n    result = table.populate(*pDevice, fpGetDeviceProcAddr);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    std::unique_ptr<device_private_data> device{\n        new device_private_data{inst_data, physicalDevice, *pDevice, table, loader_callback}};\n    device->display = std::make_unique<wsi::display>();\n    device_private_data::set(*pDevice, std::move(device));\n    return VK_SUCCESS;\n}\n\n/* Clean up the dispatch table for this instance. */\nVKAPI_ATTR void VKAPI_CALL\nwsi_layer_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) {\n    assert(instance);\n    layer::instance_private_data::get(instance).disp.DestroyInstance(instance, pAllocator);\n    layer::instance_private_data::destroy(instance);\n}\n\nVKAPI_ATTR void VKAPI_CALL\nwsi_layer_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {\n    layer::device_private_data::destroy(device);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL\nwsi_layer_vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo,\n                           const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) {\n    return layer::create_instance(pCreateInfo, pAllocator, pInstance);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL\nwsi_layer_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,\n                         const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) {\n    return layer::create_device(physicalDevice, pCreateInfo, pAllocator, pDevice);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkEnumerateDeviceExtensionProperties(\n    VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pCount,\n    VkExtensionProperties *pProperties) {\n    if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName))\n        return layer::extension_properties(1, layer::device_extension, pCount, pProperties);\n\n    assert(physicalDevice);\n    return layer::instance_private_data::get(physicalDevice)\n        .disp.EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties);\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_vkEnumerateInstanceExtensionProperties(\n    const char *pLayerName, uint32_t *pCount, VkExtensionProperties *pProperties) {\n    if (pLayerName && !strcmp(pLayerName, layer::global_layer.layerName))\n        return layer::extension_properties(1, layer::instance_extension, pCount, pProperties);\n\n    return VK_ERROR_LAYER_NOT_PRESENT;\n}\n\nVKAPI_ATTR VkResult VKAPI_CALL\nwsi_layer_vkEnumerateInstanceLayerProperties(uint32_t *pCount, VkLayerProperties *pProperties) {\n    return layer::layer_properties(1, &layer::global_layer, pCount, pProperties);\n}\n\n#define GET_PROC_ADDR(func)                                                                        \\\n    if (!strcmp(funcName, #func))                                                                  \\\n        return (PFN_vkVoidFunction)&wsi_layer_##func;\n\n\nPFN_vkVoidFunction VKAPI_CALL wsi_layer_vkGetDeviceProcAddr(VkDevice device,\n                                                                            const char *funcName) {\n    GET_PROC_ADDR(vkCreateSwapchainKHR);\n    GET_PROC_ADDR(vkDestroySwapchainKHR);\n    GET_PROC_ADDR(vkGetSwapchainImagesKHR);\n    GET_PROC_ADDR(vkAcquireNextImageKHR);\n    GET_PROC_ADDR(vkQueuePresentKHR);\n    GET_PROC_ADDR(vkGetSwapchainCounterEXT);\n    GET_PROC_ADDR(vkRegisterDisplayEventEXT);\n    GET_PROC_ADDR(vkDestroyFence);\n    GET_PROC_ADDR(vkWaitForFences);\n    GET_PROC_ADDR(vkGetFenceStatus);\n\n    return layer::device_private_data::get(device).disp.GetDeviceProcAddr(device, funcName);\n}\n\nVKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL\nwsi_layer_vkGetInstanceProcAddr(VkInstance instance, const char *funcName) {\n    GET_PROC_ADDR(vkGetDeviceProcAddr);\n    GET_PROC_ADDR(vkGetInstanceProcAddr);\n    GET_PROC_ADDR(vkCreateInstance);\n    GET_PROC_ADDR(vkDestroyInstance);\n    GET_PROC_ADDR(vkCreateDevice);\n    GET_PROC_ADDR(vkDestroyDevice);\n    GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceSupportKHR);\n    GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);\n    GET_PROC_ADDR(vkGetPhysicalDeviceSurfaceFormatsKHR);\n    GET_PROC_ADDR(vkGetPhysicalDeviceSurfacePresentModesKHR);\n    GET_PROC_ADDR(vkEnumerateDeviceExtensionProperties);\n    GET_PROC_ADDR(vkEnumerateInstanceExtensionProperties);\n    GET_PROC_ADDR(vkEnumerateInstanceLayerProperties);\n\n    GET_PROC_ADDR(vkGetPhysicalDeviceDisplayPropertiesKHR);\n    GET_PROC_ADDR(vkGetDisplayModePropertiesKHR);\n    GET_PROC_ADDR(vkGetPhysicalDeviceDisplayPlanePropertiesKHR);\n    GET_PROC_ADDR(vkAcquireXlibDisplayEXT);\n    GET_PROC_ADDR(vkGetDrmDisplayEXT);\n    GET_PROC_ADDR(vkAcquireDrmDisplayEXT);\n    GET_PROC_ADDR(vkGetDisplayPlaneSupportedDisplaysKHR);\n    GET_PROC_ADDR(vkCreateDisplayPlaneSurfaceKHR);\n    GET_PROC_ADDR(vkCreateDisplayModeKHR);\n    GET_PROC_ADDR(vkReleaseDisplayEXT);\n\n    return layer::instance_private_data::get(instance).disp.GetInstanceProcAddr(instance, funcName);\n}\n\n} /* namespace layer */\n\nconst char *g_sessionPath;\n\nVKAPI_ATTR VkResult VKAPI_CALL wsi_layer_Negotiate(VkNegotiateLayerInterface *nli)\n{\n    if (nli->loaderLayerInterfaceVersion < 2)\n        return VK_ERROR_INITIALIZATION_FAILED;\n\n    nli->loaderLayerInterfaceVersion = 2;\n    nli->pfnGetInstanceProcAddr = layer::wsi_layer_vkGetInstanceProcAddr;\n    nli->pfnGetDeviceProcAddr = layer::wsi_layer_vkGetDeviceProcAddr;\n\n    return VK_SUCCESS;\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/layer.h",
    "content": "#pragma once\n\n#include <vulkan/vk_layer.h>\n\nextern \"C\" const char *g_sessionPath;\n\nextern \"C\" VKAPI_ATTR VkResult VKAPI_CALL wsi_layer_Negotiate(VkNegotiateLayerInterface *nli);\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/private_data.cpp",
    "content": "/*\n * Copyright (c) 2018-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"private_data.hpp\"\n\n#include \"wsi/wsi_factory.hpp\"\n\n#include <unordered_map>\n\nnamespace layer {\n\nstatic std::mutex g_data_lock;\nstatic std::unordered_map<void *, std::unique_ptr<instance_private_data>> g_instance_data;\nstatic std::unordered_map<void *, std::unique_ptr<device_private_data>> g_device_data;\n\ntemplate <typename object_type, typename get_proc_type>\nstatic PFN_vkVoidFunction get_proc_helper(object_type obj, get_proc_type get_proc,\n                                          const char *proc_name, bool required, bool &ok) {\n    PFN_vkVoidFunction ret = get_proc(obj, proc_name);\n    if (nullptr == ret && required) {\n        ok = false;\n    }\n    return ret;\n}\n\nVkResult instance_dispatch_table::populate(VkInstance instance,\n                                           PFN_vkGetInstanceProcAddr get_proc) {\n    bool ok = true;\n#define REQUIRED(x)                                                                                \\\n    x = reinterpret_cast<PFN_vk##x>(get_proc_helper(instance, get_proc, \"vk\" #x, true, ok));\n#define OPTIONAL(x)                                                                                \\\n    x = reinterpret_cast<PFN_vk##x>(get_proc_helper(instance, get_proc, \"vk\" #x, false, ok));\n    INSTANCE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL);\n#undef REQUIRED\n#undef OPTIONAL\n    return ok ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED;\n}\n\nVkResult device_dispatch_table::populate(VkDevice device, PFN_vkGetDeviceProcAddr get_proc) {\n    bool ok = true;\n#define REQUIRED(x)                                                                                \\\n    x = reinterpret_cast<PFN_vk##x>(get_proc_helper(device, get_proc, \"vk\" #x, true, ok));\n#define OPTIONAL(x)                                                                                \\\n    x = reinterpret_cast<PFN_vk##x>(get_proc_helper(device, get_proc, \"vk\" #x, false, ok));\n    DEVICE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL);\n#undef REQUIRED\n#undef OPTIONAL\n    return ok ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED;\n}\n\ninstance_private_data::instance_private_data(const instance_dispatch_table &table,\n                                             PFN_vkSetInstanceLoaderData set_loader_data,\n                                             util::wsi_platform_set enabled_layer_platforms)\n    : disp(table), SetInstanceLoaderData(set_loader_data),\n      enabled_layer_platforms(enabled_layer_platforms) {}\n\ntemplate <typename dispatchable_type>\nstatic inline void *get_key(dispatchable_type dispatchable_object) {\n    return *reinterpret_cast<void **>(dispatchable_object);\n}\n\nvoid instance_private_data::set(VkInstance inst, std::unique_ptr<instance_private_data> inst_data) {\n    scoped_mutex lock(g_data_lock);\n    g_instance_data[get_key(inst)] = std::move(inst_data);\n}\n\ntemplate <typename dispatchable_type>\nstatic instance_private_data &get_instance_private_data(dispatchable_type dispatchable_object) {\n    scoped_mutex lock(g_data_lock);\n    return *g_instance_data[get_key(dispatchable_object)];\n}\n\ninstance_private_data &instance_private_data::get(VkInstance instance) {\n    return get_instance_private_data(instance);\n}\n\ninstance_private_data &instance_private_data::get(VkPhysicalDevice phys_dev) {\n    return get_instance_private_data(phys_dev);\n}\n\nstatic VkIcdWsiPlatform get_platform_of_surface(VkSurfaceKHR surface) {\n    VkIcdSurfaceBase *surface_base = reinterpret_cast<VkIcdSurfaceBase *>(surface);\n    return surface_base->platform;\n}\n\nbool instance_private_data::does_layer_support_surface(VkSurfaceKHR surface) {\n    return enabled_layer_platforms.contains(get_platform_of_surface(surface));\n}\n\nbool instance_private_data::do_icds_support_surface(VkPhysicalDevice, VkSurfaceKHR) {\n    /* For now assume ICDs do not support VK_KHR_surface. This means that the layer will handle all\n     * the surfaces it can handle (even if the ICDs can handle the surface) and only call down for\n     * surfaces it cannot handle. In the future we may allow system integrators to configure which\n     * ICDs have precedence handling which platforms.\n     */\n    return false;\n}\n\nbool instance_private_data::should_layer_handle_surface(VkSurfaceKHR surface) {\n    return surfaces.find(surface) != surfaces.end();\n}\n\nvoid instance_private_data::destroy(VkInstance inst) {\n    scoped_mutex lock(g_data_lock);\n    g_instance_data.erase(get_key(inst));\n}\n\nvoid instance_private_data::add_surface(VkSurfaceKHR surface) {\n    scoped_mutex lock(g_data_lock);\n    surfaces.insert(surface);\n}\n\ndevice_private_data::device_private_data(instance_private_data &inst_data,\n                                         VkPhysicalDevice phys_dev, VkDevice dev,\n                                         const device_dispatch_table &table,\n                                         PFN_vkSetDeviceLoaderData set_loader_data)\n    : disp{table}, instance_data{inst_data}, SetDeviceLoaderData{set_loader_data},\n      physical_device{phys_dev}, device{dev} {}\n\nvoid device_private_data::set(VkDevice dev, std::unique_ptr<device_private_data> dev_data) {\n    scoped_mutex lock(g_data_lock);\n    g_device_data[get_key(dev)] = std::move(dev_data);\n}\n\ntemplate <typename dispatchable_type>\nstatic device_private_data &get_device_private_data(dispatchable_type dispatchable_object) {\n    scoped_mutex lock(g_data_lock);\n    return *g_device_data[get_key(dispatchable_object)];\n}\n\ndevice_private_data &device_private_data::get(VkDevice device) {\n    return get_device_private_data(device);\n}\n\ndevice_private_data &device_private_data::get(VkQueue queue) {\n    return get_device_private_data(queue);\n}\n\nvoid device_private_data::add_layer_swapchain(VkSwapchainKHR swapchain) {\n    scoped_mutex lock(swapchains_lock);\n    swapchains.insert(swapchain);\n}\n\nbool device_private_data::layer_owns_all_swapchains(const VkSwapchainKHR *swapchain,\n                                                    uint32_t swapchain_count) const {\n    scoped_mutex lock(swapchains_lock);\n    for (uint32_t i = 0; i < swapchain_count; i++) {\n        if (swapchains.find(swapchain[i]) == swapchains.end()) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool device_private_data::should_layer_create_swapchain(VkSurfaceKHR vk_surface) {\n    return instance_data.should_layer_handle_surface(vk_surface);\n}\n\nbool device_private_data::can_icds_create_swapchain(VkSurfaceKHR vk_surface) {\n    return disp.CreateSwapchainKHR != nullptr;\n}\n\nvoid device_private_data::destroy(VkDevice dev) {\n    scoped_mutex lock(g_data_lock);\n    g_device_data.erase(get_key(dev));\n}\n} /* namespace layer */\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/private_data.hpp",
    "content": "/*\n * Copyright (c) 2018-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#pragma once\n\n#include \"util/platform_set.hpp\"\n#include \"wsi/display.hpp\"\n\n#include <vulkan/vk_icd.h>\n#include <vulkan/vk_layer.h>\n#include <vulkan/vulkan.h>\n\n#include <cassert>\n#include <memory>\n#include <mutex>\n#include <unordered_set>\n\nusing scoped_mutex = std::lock_guard<std::mutex>;\n\nnamespace layer {\n\n/* List of device entrypoints in the layer's instance dispatch table.\n * Note that the Vulkan loader implements some of these entrypoints so the fact that these are\n * non-null doesn't guarantee than we can safely call them. We still mark the entrypoints with\n * REQUIRED() and OPTIONAL(). The layer fails if vkGetInstanceProcAddr returns null for entrypoints\n * that are REQUIRED().\n */\n#define INSTANCE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL)                                              \\\n    REQUIRED(GetInstanceProcAddr)                                                                  \\\n    REQUIRED(DestroyInstance)                                                                      \\\n    REQUIRED(GetPhysicalDeviceProperties)                                                          \\\n    REQUIRED(GetPhysicalDeviceProperties2)                                                          \\\n    REQUIRED(GetPhysicalDeviceMemoryProperties)                                                    \\\n    REQUIRED(GetPhysicalDeviceImageFormatProperties)                                               \\\n    REQUIRED(EnumerateDeviceExtensionProperties)                                                   \\\n    OPTIONAL(GetPhysicalDeviceSurfaceCapabilitiesKHR)                                              \\\n    OPTIONAL(GetPhysicalDeviceSurfaceFormatsKHR)                                                   \\\n    OPTIONAL(GetPhysicalDeviceSurfacePresentModesKHR)                                              \\\n    OPTIONAL(GetPhysicalDeviceSurfaceSupportKHR)                                                   \\\n    OPTIONAL(GetPhysicalDeviceDisplayPropertiesKHR)                                                \\\n    OPTIONAL(GetDisplayModePropertiesKHR)                                                          \\\n    OPTIONAL(GetPhysicalDeviceDisplayPlanePropertiesKHR)                                           \\\n    OPTIONAL(AcquireXlibDisplayEXT)                                                                \\\n    OPTIONAL(GetDisplayPlaneSupportedDisplaysKHR)                                                  \\\n    OPTIONAL(CreateDisplayPlaneSurfaceKHR)                                                         \\\n    OPTIONAL(ReleaseDisplayEXT)                                                                    \\\n    OPTIONAL(DestroySurfaceKHR)                                                                    \\\n    OPTIONAL(CreateHeadlessSurfaceEXT)                                                             \\\n    OPTIONAL(GetPhysicalDeviceQueueFamilyProperties)                                               \\\n    OPTIONAL(CreateDisplayModeKHR)                                                                 \\\n\nstruct instance_dispatch_table {\n    VkResult populate(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);\n\n#define DISPATCH_TABLE_ENTRY(x) PFN_vk##x x{};\n    INSTANCE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY, DISPATCH_TABLE_ENTRY)\n#undef DISPATCH_TABLE_ENTRY\n};\n\n/* List of device entrypoints in the layer's device dispatch table.\n * The layer fails initializing a device instance when entrypoints marked with REQUIRED() are\n * retrieved as null. The layer will instead tolerate retrieving a null for entrypoints marked as\n * OPTIONAL(). Code in the layer needs to check these entrypoints are non-null before calling them.\n *\n * Note that we cannot rely on checking whether the physical device supports a particular extension\n * as the Vulkan loader currently aggregates all extensions advertised by all implicit layers (in\n * their JSON manifests) and adds them automatically to the output of\n * vkEnumeratePhysicalDeviceProperties.\n */\n#define DEVICE_ENTRYPOINTS_LIST(REQUIRED, OPTIONAL)                                                \\\n    REQUIRED(GetDeviceProcAddr)                                                                    \\\n    REQUIRED(GetDeviceQueue)                                                                       \\\n    REQUIRED(QueueSubmit)                                                                          \\\n    REQUIRED(QueueWaitIdle)                                                                        \\\n    REQUIRED(CreateCommandPool)                                                                    \\\n    REQUIRED(DestroyCommandPool)                                                                   \\\n    REQUIRED(AllocateCommandBuffers)                                                               \\\n    REQUIRED(FreeCommandBuffers)                                                                   \\\n    REQUIRED(ResetCommandBuffer)                                                                   \\\n    REQUIRED(BeginCommandBuffer)                                                                   \\\n    REQUIRED(EndCommandBuffer)                                                                     \\\n    REQUIRED(CreateImage)                                                                          \\\n    REQUIRED(DestroyImage)                                                                         \\\n    REQUIRED(GetImageMemoryRequirements)                                                           \\\n    REQUIRED(BindImageMemory)                                                                      \\\n    REQUIRED(AllocateMemory)                                                                       \\\n    REQUIRED(FreeMemory)                                                                           \\\n    REQUIRED(CreateFence)                                                                          \\\n    REQUIRED(DestroyFence)                                                                         \\\n    REQUIRED(ResetFences)                                                                          \\\n    REQUIRED(WaitForFences)                                                                        \\\n    OPTIONAL(CreateSwapchainKHR)                                                                   \\\n    OPTIONAL(DestroySwapchainKHR)                                                                  \\\n    OPTIONAL(GetSwapchainImagesKHR)                                                                \\\n    OPTIONAL(AcquireNextImageKHR)                                                                  \\\n    OPTIONAL(QueuePresentKHR)                                                                      \\\n    OPTIONAL(GetSwapchainCounterEXT)                                                               \\\n    OPTIONAL(RegisterDisplayEventEXT)                                                              \\\n    OPTIONAL(GetFenceStatus)                                                                       \\\n    OPTIONAL(GetMemoryFdKHR)                                                                       \\\n    OPTIONAL(CreateSemaphore)                                                                      \\\n    OPTIONAL(GetSemaphoreFdKHR)\n\nstruct device_dispatch_table {\n    VkResult populate(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);\n\n#define DISPATCH_TABLE_ENTRY(x) PFN_vk##x x{};\n    DEVICE_ENTRYPOINTS_LIST(DISPATCH_TABLE_ENTRY, DISPATCH_TABLE_ENTRY)\n#undef DISPATCH_TABLE_ENTRY\n};\n\n/**\n * @brief Layer \"mirror object\" for VkInstance.\n */\nclass instance_private_data {\n  public:\n    instance_private_data() = delete;\n    instance_private_data(const instance_private_data &) = delete;\n    instance_private_data &operator=(const instance_private_data &) = delete;\n\n    instance_private_data(const instance_dispatch_table &table,\n                          PFN_vkSetInstanceLoaderData set_loader_data,\n                          util::wsi_platform_set enabled_layer_platforms);\n    static void set(VkInstance inst, std::unique_ptr<instance_private_data> inst_data);\n\n    /**\n     * @brief Get the mirror object that the layer associates to a given Vulkan instance.\n     */\n    static instance_private_data &get(VkInstance instance);\n\n    /**\n     * @brief Get the layer instance object associated to the VkInstance owning the specified\n     * VkPhysicalDevice.\n     */\n    static instance_private_data &get(VkPhysicalDevice phys_dev);\n\n    /**\n     * @brief Get the set of enabled platforms that are also supported by the layer.\n     */\n    const util::wsi_platform_set &get_enabled_platforms() { return enabled_layer_platforms; }\n\n    /**\n     * @brief Check whether a surface command should be handled by the WSI layer.\n     *\n     * @param phys_dev Physical device involved in the Vulkan command.\n     * @param surface The surface involved in the Vulkan command.\n     *\n     * @retval @c true if the layer should handle commands for the specified surface, which may mean\n     * returning an error if the layer does not support @p surface 's platform.\n     *\n     * @retval @c false if the layer should call down to the layers and ICDs below to handle the\n     * surface commands.\n     */\n    bool should_layer_handle_surface(VkSurfaceKHR surface);\n\n    /**\n     * @brief Check whether the given surface is supported for presentation via the layer.\n     *\n     * @param surface A VK_KHR_surface surface.\n     *\n     * @return Whether the WSI layer supports this surface.\n     */\n    bool does_layer_support_surface(VkSurfaceKHR surface);\n\n    void add_surface(VkSurfaceKHR);\n\n    static void destroy(VkInstance inst);\n\n    const instance_dispatch_table disp;\n\n  private:\n    /**\n     * @brief Check whether the given surface is already supported for presentation without the\n     * layer.\n     */\n    bool do_icds_support_surface(VkPhysicalDevice phys_dev, VkSurfaceKHR surface);\n\n    const PFN_vkSetInstanceLoaderData SetInstanceLoaderData;\n    const util::wsi_platform_set enabled_layer_platforms;\n\n    std::unordered_set<VkSurfaceKHR> surfaces;\n};\n\nclass device_private_data {\n  public:\n    device_private_data() = delete;\n    device_private_data(const device_private_data &) = delete;\n    device_private_data &operator=(const device_private_data &) = delete;\n\n    device_private_data(instance_private_data &inst_data, VkPhysicalDevice phys_dev, VkDevice dev,\n                        const device_dispatch_table &table,\n                        PFN_vkSetDeviceLoaderData set_loader_data);\n    static void set(VkDevice dev, std::unique_ptr<device_private_data> dev_data);\n\n    /**\n     * @brief Get the mirror object that the layer associates to a given Vulkan device.\n     */\n    static device_private_data &get(VkDevice device);\n\n    /**\n     * @brief Get the layer device object associated to the VkDevice owning the specified VkQueue.\n     */\n    static device_private_data &get(VkQueue queue);\n\n    void add_layer_swapchain(VkSwapchainKHR swapchain);\n\n    /**\n     * @brief Return whether all the provided swapchains are owned by us (the WSI Layer).\n     */\n    bool layer_owns_all_swapchains(const VkSwapchainKHR *swapchain, uint32_t swapchain_count) const;\n\n    /**\n     * @brief Check whether the given swapchain is owned by us (the WSI Layer).\n     */\n    bool layer_owns_swapchain(VkSwapchainKHR swapchain) const {\n        return layer_owns_all_swapchains(&swapchain, 1);\n    }\n\n    /**\n     * @brief Check whether the layer can create a swapchain for the given surface.\n     */\n    bool should_layer_create_swapchain(VkSurfaceKHR vk_surface);\n\n    /**\n     * @brief Check whether the ICDs or layers below support VK_KHR_swapchain.\n     */\n    bool can_icds_create_swapchain(VkSurfaceKHR vk_surface);\n\n    static void destroy(VkDevice dev);\n\n    const device_dispatch_table disp;\n    instance_private_data &instance_data;\n    const PFN_vkSetDeviceLoaderData SetDeviceLoaderData;\n    const VkPhysicalDevice physical_device;\n    const VkDevice device;\n\n    std::unique_ptr<wsi::display> display;\n  private:\n    std::unordered_set<VkSwapchainKHR> swapchains;\n    mutable std::mutex swapchains_lock;\n};\n\n} /* namespace layer */\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/settings.cpp",
    "content": "#include \"settings.h\"\n#define PICOJSON_USE_INT64\n#include \"alvr_server/include/picojson.h\"\n#include <string>\n#include <fstream>\n#include <streambuf>\n#include <filesystem>\n#include <cstdlib>\n#include \"layer.h\"\n#include \"util/logger.h\"\n\nusing namespace std;\n\nextern uint64_t g_DriverTestMode;\n\nSettings Settings::m_Instance;\n\nSettings::Settings()\n\t: m_loaded(false)\n{\n}\n\nSettings::~Settings()\n{\n}\n\nvoid Settings::Load()\n{\n\ttry\n\t{\n\t\tauto sessionFile = std::ifstream(g_sessionPath);\n\n\t\tauto json = std::string(\n\t\t\tstd::istreambuf_iterator<char>(sessionFile),\n\t\t\tstd::istreambuf_iterator<char>());\n\n\t\tpicojson::value v;\n\t\tstd::string err = picojson::parse(v, json);\n\t\tif (!err.empty())\n\t\t{\n\t\t\tError(\"Error on parsing session config (%s): %hs\\n\", g_sessionPath, err.c_str());\n\t\t\treturn;\n\t\t}\n\n\t\tauto config = v.get(\"openvr_config\");\n\n\t\tm_renderWidth = config.get(\"eye_resolution_width\").get<int64_t>() * 2;\n\t\tm_renderHeight = config.get(\"eye_resolution_height\").get<int64_t>();\n\n\t\tm_refreshRate = (int)config.get(\"refresh_rate\").get<int64_t>();\n\t\t\n\t\tDebug(\"Config JSON: %hs\\n\", json.c_str());\n\t\tInfo(\"Render Target: %d %d\\n\", m_renderWidth, m_renderHeight);\n\t\tInfo(\"Refresh Rate: %d\\n\", m_refreshRate);\n\t\tm_loaded = true;\n\t}\n\tcatch (std::exception &e)\n\t{\n\t\tError(\"Exception on parsing session config (%s): %hs\\n\", g_sessionPath, e.what());\n\t}\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/settings.h",
    "content": "#pragma once\n\n#include <string>\n#include <cstdint>\n\nclass Settings\n{\n\tstatic Settings m_Instance;\n\tbool m_loaded;\n\n\tSettings();\n\tvirtual ~Settings();\n\npublic:\n\tvoid Load();\n\tstatic Settings &Instance() {\n\t\treturn m_Instance;\n\t}\n\n\tbool IsLoaded() {\n\t\treturn m_loaded;\n\t}\n\n\tint m_refreshRate;\n\tuint32_t m_renderWidth;\n\tuint32_t m_renderHeight;\n};\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/surface_api.cpp",
    "content": "/*\n * Copyright (c) 2016-2017, 2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"surface_api.hpp\"\n#include \"private_data.hpp\"\n#include <cassert>\n#include <wsi/wsi_factory.hpp>\n\nextern \"C\" {\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,\n    VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) {\n    auto &instance = layer::instance_private_data::get(physicalDevice);\n    if (instance.should_layer_handle_surface(surface)) {\n        wsi::surface_properties *props = wsi::get_surface_properties(surface);\n        assert(props != nullptr);\n        return props->get_surface_capabilities(physicalDevice, surface, pSurfaceCapabilities);\n    }\n\n    /* If the layer cannot handle this surface, then necessarily the surface must have been created\n     * by the ICDs (or a layer below us.) So it is safe to assume that the ICDs (or layers below us)\n     * support VK_KHR_surface and therefore it is safe to can call down. This holds for other\n     * entrypoints below.\n     */\n    return instance.disp.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface,\n                                                                 pSurfaceCapabilities);\n}\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount,\n    VkSurfaceFormatKHR *pSurfaceFormats) {\n    auto &instance = layer::instance_private_data::get(physicalDevice);\n    if (instance.should_layer_handle_surface(surface)) {\n        wsi::surface_properties *props = wsi::get_surface_properties(surface);\n        assert(props != nullptr);\n        return props->get_surface_formats(physicalDevice, surface, pSurfaceFormatCount,\n                                          pSurfaceFormats);\n    }\n\n    return instance.disp.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,\n                                                            pSurfaceFormatCount, pSurfaceFormats);\n}\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount,\n    VkPresentModeKHR *pPresentModes) {\n    auto &instance = layer::instance_private_data::get(physicalDevice);\n    if (instance.should_layer_handle_surface(surface)) {\n        wsi::surface_properties *props = wsi::get_surface_properties(surface);\n        assert(props != nullptr);\n        return props->get_surface_present_modes(physicalDevice, surface, pPresentModeCount,\n                                                pPresentModes);\n    }\n\n    return instance.disp.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface,\n                                                                 pPresentModeCount, pPresentModes);\n}\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,\n                                                                   uint32_t queueFamilyIndex,\n                                                                   VkSurfaceKHR surface,\n                                                                   VkBool32 *pSupported) {\n    auto &instance = layer::instance_private_data::get(physicalDevice);\n    if (instance.should_layer_handle_surface(surface)) {\n        *pSupported = VK_TRUE;\n        return VK_SUCCESS;\n    }\n\n    return instance.disp.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex,\n                                                            surface, pSupported);\n}\n\n} /* extern \"C\" */\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/surface_api.hpp",
    "content": "/*\n * Copyright (c) 2018-2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file surface_api.hpp\n *\n * @brief Contains the Vulkan entrypoints for the VkSurfaceKHR.\n */\n#pragma once\n\n#include <vulkan/vulkan.h>\n\nextern \"C\" {\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceCapabilitiesKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,\n    VkSurfaceCapabilitiesKHR *pSurfaceCapabilities);\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceFormatsKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceFormatsKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount,\n    VkSurfaceFormatKHR *pSurfaceFormats);\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfacePresentModesKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfacePresentModesKHR(\n    VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount,\n    VkPresentModeKHR *pPresentModes);\n\n/**\n * @brief Implements vkGetPhysicalDeviceSurfaceSupportKHR Vulkan entrypoint.\n */\nVKAPI_ATTR VkResult wsi_layer_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,\n                                                                   uint32_t queueFamilyIndex,\n                                                                   VkSurfaceKHR surface,\n                                                                   VkBool32 *pSupported);\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/swapchain_api.cpp",
    "content": "/*\n * Copyright (c) 2017, 2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain_api.cpp\n *\n * @brief Contains the Vulkan entrypoints for the swapchain.\n */\n\n#include <cassert>\n#include <cstdlib>\n#include <new>\n\n#include <wsi/wsi_factory.hpp>\n\n#include \"private_data.hpp\"\n#include \"swapchain_api.hpp\"\n\nextern \"C\" {\n\nVKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(\n    VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo,\n    const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) {\n    assert(pSwapchain != nullptr);\n    layer::device_private_data &device_data = layer::device_private_data::get(device);\n    VkSurfaceKHR surface = pSwapchainCreateInfo->surface;\n\n    if (!device_data.should_layer_create_swapchain(surface)) {\n        if (!device_data.can_icds_create_swapchain(surface)) {\n            return VK_ERROR_INITIALIZATION_FAILED;\n        }\n        return device_data.disp.CreateSwapchainKHR(device_data.device, pSwapchainCreateInfo,\n                                                   pAllocator, pSwapchain);\n    }\n\n    wsi::swapchain_base *sc = wsi::allocate_surface_swapchain(surface, device_data, pAllocator);\n    if (sc == nullptr) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    VkResult result = sc->init(device, pSwapchainCreateInfo);\n    if (result != VK_SUCCESS) {\n        /* Error occured during initialization, need to free allocated memory. */\n        wsi::destroy_surface_swapchain(sc, pAllocator);\n        return result;\n    }\n\n    *pSwapchain = reinterpret_cast<VkSwapchainKHR>(sc);\n    device_data.add_layer_swapchain(*pSwapchain);\n    return result;\n}\n\nVKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                const VkAllocationCallbacks *pAllocator) {\n    layer::device_private_data &device_data = layer::device_private_data::get(device);\n\n    if (!device_data.layer_owns_swapchain(swapc)) {\n        return device_data.disp.DestroySwapchainKHR(device_data.device, swapc, pAllocator);\n    }\n\n    assert(swapc != VK_NULL_HANDLE);\n    wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);\n    wsi::destroy_surface_swapchain(sc, pAllocator);\n}\n\nVKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                      uint32_t *pSwapchainImageCount,\n                                                      VkImage *pSwapchainImages) {\n    layer::device_private_data &device_data = layer::device_private_data::get(device);\n\n    if (!device_data.layer_owns_swapchain(swapc)) {\n        return device_data.disp.GetSwapchainImagesKHR(device_data.device, swapc,\n                                                      pSwapchainImageCount, pSwapchainImages);\n    }\n\n    assert(pSwapchainImageCount != nullptr);\n    assert(swapc != VK_NULL_HANDLE);\n    wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);\n    return sc->get_swapchain_images(pSwapchainImageCount, pSwapchainImages);\n}\n\nVKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                    uint64_t timeout, VkSemaphore semaphore,\n                                                    VkFence fence, uint32_t *pImageIndex) {\n    layer::device_private_data &device_data = layer::device_private_data::get(device);\n\n    if (!device_data.layer_owns_swapchain(swapc)) {\n        return device_data.disp.AcquireNextImageKHR(device_data.device, swapc, timeout, semaphore,\n                                                    fence, pImageIndex);\n    }\n\n    assert(swapc != VK_NULL_HANDLE);\n    assert(semaphore != VK_NULL_HANDLE || fence != VK_NULL_HANDLE);\n    assert(pImageIndex != nullptr);\n    wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);\n    return sc->acquire_next_image(timeout, semaphore, fence, pImageIndex);\n}\n\nVKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue,\n                                                const VkPresentInfoKHR *pPresentInfo) {\n    assert(queue != VK_NULL_HANDLE);\n    assert(pPresentInfo != nullptr);\n\n    layer::device_private_data &device_data = layer::device_private_data::get(queue);\n\n    if (!device_data.layer_owns_all_swapchains(pPresentInfo->pSwapchains,\n                                               pPresentInfo->swapchainCount)) {\n        return device_data.disp.QueuePresentKHR(queue, pPresentInfo);\n    }\n\n    VkResult ret = VK_SUCCESS;\n    for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) {\n        VkSwapchainKHR swapc = pPresentInfo->pSwapchains[i];\n\n        wsi::swapchain_base *sc = reinterpret_cast<wsi::swapchain_base *>(swapc);\n        assert(sc != nullptr);\n\n        VkResult res = sc->queue_present(queue, pPresentInfo, pPresentInfo->pImageIndices[i]);\n\n        if (pPresentInfo->pResults != nullptr) {\n            pPresentInfo->pResults[i] = res;\n        }\n\n        if (res != VK_SUCCESS && ret == VK_SUCCESS) {\n            ret = res;\n        }\n    }\n\n    return ret;\n}\n\nVKAPI_ATTR VkResult wsi_layer_vkGetSwapchainCounterEXT(VkDevice device, VkSwapchainKHR swapchain,\n                                                       VkSurfaceCounterFlagBitsEXT counter,\n                                                       uint64_t *pCounterValue) {\n    layer::device_private_data &device_data = layer::device_private_data::get(device);\n    if (!device_data.layer_owns_swapchain(swapchain)) {\n        return device_data.disp.GetSwapchainCounterEXT(device, swapchain, counter, pCounterValue);\n    }\n    if (VK_SURFACE_COUNTER_VBLANK_EXT == counter) {\n        *pCounterValue = device_data.display->m_vsync_count;\n    }\n    return VK_SUCCESS;\n}\n\n} /* extern \"C\" */\n"
  },
  {
    "path": "alvr/vulkan_layer/layer/swapchain_api.hpp",
    "content": "/*\n * Copyright (c) 2018-2019 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain_api.hpp\n *\n * @brief Contains the Vulkan entrypoints for the swapchain.\n */\n\n#pragma once\n\n#include <vulkan/vulkan.h>\n\nextern \"C\" {\n\nVKAPI_ATTR VkResult wsi_layer_vkCreateSwapchainKHR(\n    VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo,\n    const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain);\n\nVKAPI_ATTR void wsi_layer_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                const VkAllocationCallbacks *pAllocator);\n\nVKAPI_ATTR VkResult wsi_layer_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                      uint32_t *pSwapchainImageCount,\n                                                      VkImage *pSwapchainImages);\n\nVKAPI_ATTR VkResult wsi_layer_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapc,\n                                                    uint64_t timeout, VkSemaphore semaphore,\n                                                    VkFence fence, uint32_t *pImageIndex);\n\nVKAPI_ATTR VkResult wsi_layer_vkQueuePresentKHR(VkQueue queue,\n                                                const VkPresentInfoKHR *pPresentInfo);\n\nVKAPI_ATTR VkResult wsi_layer_vkGetSwapchainCounterEXT(VkDevice device, VkSwapchainKHR swapchain,\n                                                       VkSurfaceCounterFlagBitsEXT counter,\n                                                       uint64_t *pCounterValue);\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/src/lib.rs",
    "content": "#![cfg(target_os = \"linux\")]\n#![allow(\n    dead_code,\n    non_camel_case_types,\n    non_snake_case,\n    non_upper_case_globals,\n    unsafe_op_in_unsafe_fn,\n    unused_imports,\n    clippy::missing_safety_doc,\n    clippy::ptr_offset_with_cast,\n    clippy::too_many_arguments,\n    clippy::useless_transmute,\n    clippy::pedantic,\n    clippy::nursery\n)]\n\nuse std::ffi::CString;\n\nmod bindings {\n    include!(concat!(env!(\"OUT_DIR\"), \"/layer_bindings.rs\"));\n}\nuse bindings::*;\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ALVR_Negotiate(nli: *mut VkNegotiateLayerInterface) -> VkResult {\n    unsafe {\n        g_sessionPath = CString::new(\n            alvr_filesystem::filesystem_layout_invalid()\n                .session()\n                .to_string_lossy()\n                .to_string(),\n        )\n        .unwrap()\n        .into_raw();\n\n        bindings::wsi_layer_Negotiate(nli)\n    }\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/util/custom_allocator.cpp",
    "content": "/*\n * Copyright (c) 2020-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"custom_allocator.hpp\"\n\nextern \"C\" {\n\nstatic void *default_allocation(void *, size_t size, size_t, VkSystemAllocationScope) {\n    return malloc(size);\n}\n\nstatic void *default_reallocation(void *, void *pOriginal, size_t size, size_t,\n                                  VkSystemAllocationScope) {\n    return realloc(pOriginal, size);\n}\n\nstatic void default_free(void *, void *pMemory) { free(pMemory); }\n}\n\nnamespace util {\n\nconst allocator &allocator::get_generic() {\n    static allocator generic{nullptr, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND};\n    return generic;\n}\n\nallocator::allocator(const allocator &other, VkSystemAllocationScope new_scope)\n    : allocator{other.get_original_callbacks(), new_scope} {}\n\n/* If callbacks is already populated by vulkan then use those specified as default. */\nallocator::allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationScope scope) {\n    m_scope = scope;\n    if (callbacks != nullptr) {\n        m_callbacks = *callbacks;\n    } else {\n        m_callbacks = {};\n        m_callbacks.pfnAllocation = default_allocation;\n        m_callbacks.pfnReallocation = default_reallocation;\n        m_callbacks.pfnFree = default_free;\n    }\n}\n\nconst VkAllocationCallbacks *allocator::get_original_callbacks() const {\n    return m_callbacks.pfnAllocation == default_allocation ? nullptr : &m_callbacks;\n}\n\n} /* namespace util */\n"
  },
  {
    "path": "alvr/vulkan_layer/util/custom_allocator.hpp",
    "content": "/*\n * Copyright (c) 2020-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include <cassert>\n#include <new>\n#include <string>\n#include <vector>\n\n#include <vulkan/vulkan.h>\n\n#pragma once\n\nnamespace util {\n\n/**\n * @brief Minimalistic wrapper of VkAllocationCallbacks.\n */\nclass allocator {\n  public:\n    /**\n     * @brief Get an allocator that can be used if VkAllocationCallbacks are not provided.\n     */\n    static const allocator &get_generic();\n\n    /**\n     * @brief Construct a new wrapper for the given VK callbacks and scope.\n     * @param callbacks Pointer to allocation callbacks. If this is @c nullptr, then default\n     *   allocation callbacks are used. These can be accessed through #m_callbacks.\n     * @param scope The scope to use for this allocator.\n     */\n    allocator(const VkAllocationCallbacks *callbacks, VkSystemAllocationScope scope);\n\n    /**\n     * @brief Copy the given allocator, but change the allocation scope.\n     */\n    allocator(const allocator &other, VkSystemAllocationScope new_scope);\n\n    /**\n     * @brief Get a pointer to the allocation callbacks provided while constructing this object.\n     * @return a copy of the #VkAllocationCallback argument provided in the allocator constructor\n     *   or @c nullptr if this argument was provided as @c nullptr.\n     * @note The #m_callbacks member is always populated with callable pointers for pfnAllocation,\n     *   pfnReallocation and pfnFree.\n     */\n    const VkAllocationCallbacks *get_original_callbacks() const;\n\n    /**\n     * @brief Helper method to allocate and construct objects with a custom allocator.\n     * @param num_objects Number of objects to create.\n     * @return Pointer to the newly created objects or @c nullptr if allocation failed.\n     */\n    template <typename T, typename... arg_types>\n    T *create(size_t num_objects, arg_types &&...args) const noexcept;\n\n    /**\n     * @brief Helper method to destroy and deallocate objects constructed with allocator::create().\n     * @param num_objects Number of objects to destroy.\n     */\n    template <typename T> void destroy(size_t num_objects, T *obj) const noexcept;\n\n    VkAllocationCallbacks m_callbacks;\n    VkSystemAllocationScope m_scope;\n};\n\n/**\n * @brief Implementation of an allocator that can be used with STL containers.\n */\ntemplate <typename T> class custom_allocator {\n  public:\n    using value_type = T;\n    using pointer = T *;\n\n    custom_allocator(const allocator &alloc) : m_alloc(alloc) {}\n\n    template <typename U>\n    custom_allocator(const custom_allocator<U> &other) : m_alloc(other.get_data()) {}\n\n    const allocator &get_data() const { return m_alloc; }\n\n    pointer allocate(size_t n) const {\n        size_t size = n * sizeof(T);\n        auto &cb = m_alloc.m_callbacks;\n        void *ret = cb.pfnAllocation(cb.pUserData, size, alignof(T), m_alloc.m_scope);\n        if (ret == nullptr)\n            throw std::bad_alloc();\n        return reinterpret_cast<pointer>(ret);\n    }\n\n    pointer allocate(size_t n, void *ptr) const {\n        size_t size = n * sizeof(T);\n        auto &cb = m_alloc.m_callbacks;\n        void *ret = cb.pfnReallocation(cb.pUserData, ptr, size, alignof(T), m_alloc.m_scope);\n        if (ret == nullptr)\n            throw std::bad_alloc();\n        return reinterpret_cast<pointer>(ret);\n    }\n\n    void deallocate(void *ptr, size_t) const noexcept {\n        m_alloc.m_callbacks.pfnFree(m_alloc.m_callbacks.pUserData, ptr);\n    }\n\n  private:\n    const allocator m_alloc;\n};\n\ntemplate <typename T, typename U>\nbool operator==(const custom_allocator<T> &, const custom_allocator<U> &) {\n    return true;\n}\n\ntemplate <typename T, typename U>\nbool operator!=(const custom_allocator<T> &, const custom_allocator<U> &) {\n    return false;\n}\n\ntemplate <typename T, typename... arg_types>\nT *allocator::create(size_t num_objects, arg_types &&...args) const noexcept {\n    if (num_objects < 1) {\n        return nullptr;\n    }\n\n    custom_allocator<T> allocator(*this);\n    T *ptr;\n    try {\n        ptr = allocator.allocate(num_objects);\n    } catch (...) {\n        return nullptr;\n    }\n\n    size_t objects_constructed = 0;\n    try {\n        while (objects_constructed < num_objects) {\n            T *next_object = &ptr[objects_constructed];\n            new (next_object) T(std::forward<arg_types>(args)...);\n            objects_constructed++;\n        }\n    } catch (...) {\n        /* We catch all exceptions thrown while constructing the object, not just\n         * std::bad_alloc.\n         */\n        while (objects_constructed > 0) {\n            objects_constructed--;\n            ptr[objects_constructed].~T();\n        }\n        allocator.deallocate(ptr, num_objects);\n        return nullptr;\n    }\n    return ptr;\n}\n\ntemplate <typename T> void allocator::destroy(size_t num_objects, T *objects) const noexcept {\n    assert((objects == nullptr) == (num_objects == 0));\n    if (num_objects == 0) {\n        return;\n    }\n\n    custom_allocator<T> allocator(*this);\n    for (size_t i = 0; i < num_objects; i++) {\n        objects[i].~T();\n    }\n    allocator.deallocate(objects, num_objects);\n}\n\ntemplate <typename T> void destroy_custom(T *obj) { T::destroy(obj); }\n\n/**\n * @brief Vector using a Vulkan custom allocator to allocate its elements.\n * @note The vector must be passed a custom_allocator during construction and it takes a copy\n *   of it, meaning that the user is free to destroy the custom_allocator after constructing the\n *   vector.\n */\ntemplate <typename T> class vector : public std::vector<T, custom_allocator<T>> {\n  public:\n    using base = std::vector<T, custom_allocator<T>>;\n    using base::base;\n\n    /* Delete all methods that can cause allocation failure, i.e. can throw std::bad_alloc.\n     *\n     * Rationale: we want to force users to use our corresponding try_... method instead:\n     * this makes the API slightly more annoying to use, but hopefully safer as it encourages\n     * users to check for allocation failures, which is important for Vulkan.\n     *\n     * Note: deleting each of these methods (below) deletes all its overloads from the base class,\n     *   to be precise: the deleted method covers the methods (all overloads) in the base class.\n     * Note: clear() is already noexcept since C++11.\n     */\n    void insert() = delete;\n    void emplace() = delete;\n    void emplace_back() = delete;\n    void push_back() = delete;\n    void resize() = delete;\n    void reserve() = delete;\n\n    /* Note pop_back(), erase(), clear() do not throw std::bad_alloc exceptions. */\n\n    /* @brief Like std::vector::push_back, but non throwing.\n     * @return @c false iff the operation could not be performed due to an allocation failure.\n     */\n    template <typename... arg_types> bool try_push_back(arg_types &&...args) noexcept {\n        try {\n            base::push_back(std::forward<arg_types>(args)...);\n            return true;\n        } catch (const std::bad_alloc &e) {\n            return false;\n        }\n    }\n\n    /* @brief push back multiple elements at once\n     * @return @c false iff the operation could not be performed due to an allocation failure.\n     */\n    bool try_push_back_many(const T *begin, const T *end) noexcept {\n        for (const T *it = begin; it != end; ++it) {\n            if (!try_push_back(*it)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /* @brief Like std::vector::resize, but non throwing.\n     * @return @c false iff the operation could not be performed due to an allocation failure.\n     */\n    template <typename... arg_types> bool try_resize(arg_types &&...args) noexcept {\n        try {\n            base::resize(std::forward<arg_types>(args)...);\n            return true;\n        } catch (const std::bad_alloc &e) {\n            return false;\n        }\n    }\n};\n\n} /* namespace util */\n"
  },
  {
    "path": "alvr/vulkan_layer/util/extension_list.cpp",
    "content": "/*\n * Copyright (c) 2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"extension_list.hpp\"\n#include <cassert>\n#include <layer/private_data.hpp>\n#include <string.h>\n\nnamespace util {\n\nextension_list::extension_list(const util::allocator &allocator)\n    : m_alloc{allocator}, m_ext_props(allocator) {}\n\nVkResult extension_list::add(const struct VkEnumerateInstanceExtensionPropertiesChain *chain) {\n    uint32_t count;\n    VkResult m_error = chain->CallDown(nullptr, &count, nullptr);\n    if (m_error == VK_SUCCESS) {\n        if (!m_ext_props.try_resize(count)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n        m_error = chain->CallDown(nullptr, &count, m_ext_props.data());\n    }\n    return m_error;\n}\n\nVkResult extension_list::add(VkPhysicalDevice dev) {\n    layer::instance_private_data &inst_data = layer::instance_private_data::get(dev);\n    uint32_t count;\n    VkResult m_error =\n        inst_data.disp.EnumerateDeviceExtensionProperties(dev, nullptr, &count, nullptr);\n\n    if (m_error == VK_SUCCESS) {\n        if (!m_ext_props.try_resize(count)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n        m_error = inst_data.disp.EnumerateDeviceExtensionProperties(dev, nullptr, &count,\n                                                                    m_ext_props.data());\n    }\n    return m_error;\n}\n\nVkResult extension_list::add(\n    PFN_vkEnumerateInstanceExtensionProperties fpEnumerateInstanceExtensionProperties) {\n    uint32_t count = 0;\n    VkResult m_error = fpEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);\n\n    if (m_error == VK_SUCCESS) {\n        if (!m_ext_props.try_resize(count)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n        m_error = fpEnumerateInstanceExtensionProperties(nullptr, &count, m_ext_props.data());\n    }\n    return m_error;\n}\n\nVkResult extension_list::add(const char *const *extensions, uint32_t count) {\n    for (uint32_t i = 0; i < count; i++) {\n        VkExtensionProperties props = {};\n        strncpy(props.extensionName, extensions[i], sizeof(props.extensionName) - 1);\n        props.extensionName[sizeof(props.extensionName) - 1] = '\\0';\n        if (!m_ext_props.try_push_back(props)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nVkResult extension_list::add(const VkExtensionProperties *props, uint32_t count) {\n    if (!m_ext_props.try_push_back_many(props, props + count)) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n    return VK_SUCCESS;\n}\n\nVkResult extension_list::add(const char *ext) {\n    if (!contains(ext)) {\n        VkExtensionProperties props = {};\n        strncpy(props.extensionName, ext, sizeof(props.extensionName) - 1);\n        props.extensionName[sizeof(props.extensionName) - 1] = '\\0';\n        if (!m_ext_props.try_push_back(props)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nVkResult extension_list::add(VkExtensionProperties ext_prop) {\n    if (!contains(ext_prop.extensionName)) {\n        if (!m_ext_props.try_push_back(ext_prop)) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nVkResult extension_list::add(const char **ext_list, uint32_t count) {\n    for (uint32_t i = 0; i < count; i++) {\n        if (add(ext_list[i]) != VK_SUCCESS) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nVkResult extension_list::add(const extension_list &ext_list) {\n    util::vector<VkExtensionProperties> ext_vect = ext_list.get_extension_props();\n    for (auto &ext : ext_vect) {\n        if (add(ext) != VK_SUCCESS) {\n            return VK_ERROR_OUT_OF_HOST_MEMORY;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nbool extension_list::get_extension_strings(util::vector<const char *> &out) const {\n    size_t old_size = out.size();\n    size_t new_size = old_size + m_ext_props.size();\n    if (!out.try_resize(new_size)) {\n        return false;\n    }\n\n    for (size_t i = old_size; i < new_size; i++) {\n        out[i] = m_ext_props[i - old_size].extensionName;\n    }\n    return true;\n}\n\nbool extension_list::contains(const extension_list &req) const {\n    for (const auto &req_ext : req.m_ext_props) {\n        if (!contains(req_ext.extensionName)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool extension_list::contains(const char *extension_name) const {\n    for (const auto &p : m_ext_props) {\n        if (strcmp(p.extensionName, extension_name) == 0) {\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid extension_list::remove(const char *ext) {\n    m_ext_props.erase(std::remove_if(m_ext_props.begin(), m_ext_props.end(),\n                                     [&ext](VkExtensionProperties ext_prop) {\n                                         return (strcmp(ext_prop.extensionName, ext) == 0);\n                                     }));\n}\n} // namespace util\n"
  },
  {
    "path": "alvr/vulkan_layer/util/extension_list.hpp",
    "content": "/*\n * Copyright (c) 2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n#pragma once\n\n#include \"util/custom_allocator.hpp\"\n\n#include <algorithm>\n#include <vector>\n\n#include <vulkan/vk_layer.h>\n#include <vulkan/vulkan.h>\n\nnamespace util {\n\nclass extension_list {\n  public:\n    extension_list(const util::allocator &allocator);\n\n    extension_list(const extension_list &rhs) = delete;\n    const extension_list &operator=(const extension_list &rhs) = delete;\n\n    /**\n     * @brief Obtain a vector of #VkExtensionProperties equivalent to this extension_list object.\n     */\n    const util::vector<VkExtensionProperties> &get_extension_props() const { return m_ext_props; }\n\n    /**\n     * @brief Get the allocator used to manage the memory of this object.\n     */\n    const util::allocator get_allocator() const { return m_alloc; }\n\n    /**\n     * @brief Append pointers to extension strings to the given vector.\n     *\n     * @warning Pointers in the vector are referring to string allocated in this extension_list and\n     * will become invalid if the extension_list is modified (e.g. by adding/removing elements.)\n     *\n     * @param[out] out A vector of C strings to which all extension are appended.\n     *\n     * @return A boolean indicating whether the operation was successful. If this is @c false, then\n     * @p out is unmodified.\n     */\n    bool get_extension_strings(util::vector<const char *> &out) const;\n\n    bool contains(const extension_list &req) const;\n    bool contains(const char *ext) const;\n    void remove(const char *ext);\n    VkResult add(const char *ext);\n    VkResult add(VkExtensionProperties ext_prop);\n    VkResult add(const char **ext_list, uint32_t count);\n    VkResult add(const extension_list &ext_list);\n    VkResult add(const struct VkEnumerateInstanceExtensionPropertiesChain *chain);\n    VkResult add(PFN_vkEnumerateInstanceExtensionProperties fpEnumerateInstanceExtensionProperties);\n    VkResult add(VkPhysicalDevice dev);\n    VkResult add(const char *const *extensions, uint32_t count);\n    VkResult add(const VkExtensionProperties *props, uint32_t count);\n\n  private:\n    util::allocator m_alloc;\n    util::vector<VkExtensionProperties> m_ext_props;\n};\n} // namespace util\n"
  },
  {
    "path": "alvr/vulkan_layer/util/logger.cpp",
    "content": "#include <cstdarg>\n#include <cstdio>\n#include <cstdlib>\n\nvoid _log(const char *format, va_list args, bool err) {\n    vfprintf(err ? stderr : stdout, format, args);\n}\n\nvoid Error(const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    _log(format, args, true);\n    va_end(args);\n}\n\nvoid Warn(const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    _log(format, args, true);\n    va_end(args);\n}\n\nvoid Info(const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    _log(format, args, false);\n    va_end(args);\n}\n\nvoid Debug(const char *format, ...) {\n    if (getenv(\"ALVR_LOG_DEBUG\") == NULL) return;\n    va_list args;\n    va_start(args, format);\n    _log(format, args, true);\n    va_end(args);\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/util/logger.h",
    "content": "#pragma once\n\nvoid Error(const char *format, ...);\nvoid Warn(const char *format, ...);\nvoid Info(const char *format, ...);\nvoid Debug(const char *format, ...);\n"
  },
  {
    "path": "alvr/vulkan_layer/util/platform_set.hpp",
    "content": "/*\n * Copyright (c) 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#pragma once\n\n#include <cassert>\n#include <stdint.h>\n\n#include <vulkan/vk_icd.h>\n\nnamespace util {\n\n/**\n * @brief Set of WSI platforms.\n * @note This could be implemented via std::unordered_set, but would require handling allocation\n * callbacks and would therefore be less convenient to use. Instead, we can store all info in the\n * bits of uint64_t.\n */\nclass wsi_platform_set {\n  public:\n    void add(VkIcdWsiPlatform p) { m_platforms |= (static_cast<uint64_t>(1) << to_int(p)); }\n\n    bool contains(VkIcdWsiPlatform p) const {\n        return (m_platforms & (static_cast<uint64_t>(1) << to_int(p))) != 0;\n    }\n\n  private:\n    /**\n     * @brief Convert a VkIcdWsiPlatform to an integer between 0-63.\n     */\n    static int to_int(VkIcdWsiPlatform p) {\n        assert(static_cast<int>(p) >= 0 && static_cast<int>(p) < 64);\n        return static_cast<int>(p);\n    }\n\n    uint64_t m_platforms = 0;\n};\n\n} /* namespace util */\n"
  },
  {
    "path": "alvr/vulkan_layer/util/pose.cpp",
    "content": "#include \"pose.hpp\"\n\n#include <cmath>\n#include <string.h>\n\n#define UNW_LOCAL_ONLY\n#include <libunwind.h>\n\nnamespace {\n\ninline HmdMatrix34_t transposeMul33(const HmdMatrix34_t& a) {\n  HmdMatrix34_t result;\n  for (unsigned i = 0; i < 3; i++) {\n    for (unsigned k = 0; k < 3; k++) {\n      result.m[i][k] = a.m[k][i];\n    }\n  }\n  result.m[0][3] = a.m[0][3];\n  result.m[1][3] = a.m[1][3];\n  result.m[2][3] = a.m[2][3];\n  return result;\n}\n\n\ninline HmdMatrix34_t matMul33(const HmdMatrix34_t& a, const HmdMatrix34_t& b) {\n  HmdMatrix34_t result;\n  for (unsigned i = 0; i < 3; i++) {\n    for (unsigned j = 0; j < 3; j++) {\n      result.m[i][j] = 0.0f;\n      for (unsigned k = 0; k < 3; k++) {\n        result.m[i][j] += a.m[i][k] * b.m[k][j];\n      }\n    }\n  }\n  return result;\n}\n\n\nbool check_pose(const TrackedDevicePose_t & p)\n{\n  if (p.bPoseIsValid != 1 or p.bDeviceIsConnected != 1)\n    return false;\n\n  if (p.eTrackingResult != 200)\n    return false;\n\n  auto m = matMul33(p.mDeviceToAbsoluteTracking, transposeMul33(p.mDeviceToAbsoluteTracking));\n  for (int i = 0 ; i < 3; ++i )\n  {\n    for (int j = 0 ; j < 3 ; ++j)\n    {\n      if (std::abs(m.m[i][j] - (i == j)) > 0.1)\n        return false;\n    }\n  }\n  return true;\n}\n\n}\n\n// For a smooth experience, the correct pose for a frame must be known.\n// Of course this is not part of vulkan parameters, so we must inspect\n// the stack.\n// First we look for the correct function (CRenderThread::UpdateAsync),\n// then we scan all the local variables, and check for a suitable one.\n// Such a variable is a TrackedDevicePose_t, with both booleans to true,\n// which we compare to 1 to avoid false positives, a tracking result of\n// 200, and a rotation matrix (A*transpose(A)) close to identity.\nconst TrackedDevicePose_t & find_pose_in_call_stack()\n{\n  static TrackedDevicePose_t * res;\n  if (res != nullptr)\n    return *res;\n  static TrackedDevicePose_t notfound;\n  unw_context_t ctx;\n  unw_getcontext(&ctx);\n  unw_cursor_t cursor;\n  unw_init_local(&cursor, &ctx);\n  while (unw_step(&cursor) > 0)\n  {\n    char name[1024];\n    unw_word_t off;\n    unw_get_proc_name(&cursor, name, sizeof(name), &off);\n    if ((strcmp(\"_ZN13CRenderThread11UpdateAsyncEv\", name) == 0) || (strcmp(\"_ZN13CRenderThread6UpdateEv\", name) == 0))\n    {\n      unw_word_t sp, sp_end;\n      unw_get_reg(&cursor, UNW_REG_SP, &sp);\n      unw_step(&cursor);\n      unw_get_reg(&cursor, UNW_REG_SP, &sp_end);\n      for (uintptr_t addr = sp ; addr < sp_end; addr += 4)\n      {\n        TrackedDevicePose_t * p = (TrackedDevicePose_t *) addr;\n        if (check_pose(*p))\n        {\n          res = p;\n          return *p;\n        }\n      }\n      return notfound;\n    }\n  }\n  return notfound;\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/util/pose.hpp",
    "content": "#pragma once\n\nstruct HmdMatrix34_t\n{\n  float m[3][4];\n};\n\nstruct HmdVector3_t\n{\n  float v[3];\n};\n\nstruct TrackedDevicePose_t\n{\n  HmdMatrix34_t mDeviceToAbsoluteTracking;\n  HmdVector3_t vVelocity;       // velocity in tracker space in m/s \n  HmdVector3_t vAngularVelocity;    // angular velocity in radians/s (?)\n  int eTrackingResult;\n  char bPoseIsValid;\n\n  // This indicates that there is a device connected for this spot in the pose array.\n  // It could go from true to false if the user unplugs the device.\n  char bDeviceIsConnected;\n};\n\nconst TrackedDevicePose_t & find_pose_in_call_stack();\n"
  },
  {
    "path": "alvr/vulkan_layer/util/timed_semaphore.cpp",
    "content": "/*\n * Copyright (c) 2017, 2019 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include <cassert>\n#include <cerrno>\n\n#include \"timed_semaphore.hpp\"\n\nnamespace util {\n\nVkResult timed_semaphore::init(unsigned count) {\n    int res;\n\n    m_count = count;\n\n    pthread_condattr_t attr;\n    res = pthread_condattr_init(&attr);\n    /* the only failure that can occur is ENOMEM */\n    assert(res == 0 || res == ENOMEM);\n    if (res != 0) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);\n    /* only programming error can cause _setclock to fail */\n    assert(res == 0);\n\n    res = pthread_cond_init(&m_cond, &attr);\n    /* the only failure that can occur that is not programming error is ENOMEM */\n    assert(res == 0 || res == ENOMEM);\n    if (res != 0) {\n        pthread_condattr_destroy(&attr);\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    res = pthread_condattr_destroy(&attr);\n    /* only programming error can cause _destroy to fail */\n    assert(res == 0);\n\n    res = pthread_mutex_init(&m_mutex, NULL);\n    /* only programming errors can result in failure */\n    assert(res == 0);\n\n    initialized = true;\n\n    return VK_SUCCESS;\n}\n\ntimed_semaphore::~timed_semaphore() {\n    int res;\n    (void)res; /* unused when NDEBUG */\n\n    if (initialized) {\n        res = pthread_cond_destroy(&m_cond);\n        assert(res == 0); /* only programming error (EBUSY, EINVAL) */\n\n        res = pthread_mutex_destroy(&m_mutex);\n        assert(res == 0); /* only programming error (EBUSY, EINVAL) */\n    }\n}\n\nVkResult timed_semaphore::wait(uint64_t timeout) {\n    VkResult retval = VK_SUCCESS;\n    int res;\n\n    assert(initialized);\n\n    res = pthread_mutex_lock(&m_mutex);\n    assert(res == 0); /* only fails with programming error (EINVAL) */\n\n    if (m_count == 0) {\n        switch (timeout) {\n        case 0:\n            retval = VK_NOT_READY;\n            break;\n        case UINT64_MAX:\n            res = pthread_cond_wait(&m_cond, &m_mutex);\n            assert(res == 0); /* only fails with programming error (EINVAL) */\n\n            break;\n        default:\n            struct timespec diff = {/* narrowing casts */\n                                    static_cast<time_t>(timeout / (1000 * 1000 * 1000)),\n                                    static_cast<long>(timeout % (1000 * 1000 * 1000))};\n\n            struct timespec now;\n            res = clock_gettime(CLOCK_MONOTONIC, &now);\n            assert(res == 0); /* only fails with programming error (EINVAL, EFAULT, EPERM) */\n\n            /* add diff to now, handling overflow */\n            struct timespec end = {now.tv_sec + diff.tv_sec, now.tv_nsec + diff.tv_nsec};\n\n            if (end.tv_nsec >= 1000 * 1000 * 1000) {\n                end.tv_nsec -= 1000 * 1000 * 1000;\n                end.tv_sec++;\n            }\n\n            res = pthread_cond_timedwait(&m_cond, &m_mutex, &end);\n            /* only fails with programming error, other than timeout */\n            assert(res == 0 || res == ETIMEDOUT);\n            if (res != 0) {\n                retval = VK_TIMEOUT;\n            }\n        }\n    }\n    if (retval == VK_SUCCESS) {\n        assert(m_count > 0);\n        m_count--;\n    }\n    res = pthread_mutex_unlock(&m_mutex);\n    assert(res == 0); /* only fails with programming error (EPERM) */\n\n    return retval;\n}\n\nvoid timed_semaphore::post() {\n    int res;\n    (void)res; /* unused when NDEBUG */\n\n    assert(initialized);\n\n    res = pthread_mutex_lock(&m_mutex);\n    assert(res == 0); /* only fails with programming error (EINVAL) */\n\n    m_count++;\n\n    res = pthread_cond_signal(&m_cond);\n    assert(res == 0); /* only fails with programming error (EINVAL) */\n\n    res = pthread_mutex_unlock(&m_mutex);\n    assert(res == 0); /* only fails with programming error (EPERM) */\n}\n\n} /* namespace util */\n"
  },
  {
    "path": "alvr/vulkan_layer/util/timed_semaphore.hpp",
    "content": "/*\n * Copyright (c) 2017, 2019 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file timed_semaphore.hpp\n *\n * @brief Contains the class definition for a semaphore with a relative timedwait\n *\n * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply\n * taking the current time and adding on a relative timeout is not correct,\n * as the system time may change, resulting in an incorrect timeout period\n * (potentially by a significant amount).\n *\n * We therefore have to re-engineer semaphores using condition variables.\n *\n * This code does not use the C++ standard library to avoid exceptions.\n */\n\n#pragma once\n\nextern \"C\" {\n#include <pthread.h>\n}\n\n#include <vulkan/vulkan.h>\n\nnamespace util {\n\n/**\n * brief semaphore with a safe relative timed wait\n *\n * sem_timedwait takes an absolute time, based on CLOCK_REALTIME. Simply\n * taking the current time and adding on a relative timeout is not correct,\n * as the system time may change, resulting in an incorrect timeout period\n * (potentially by a significant amount).\n *\n * We therefore have to re-engineer semaphores using condition variables.\n *\n * This code does not use the C++ standard library to avoid exceptions.\n */\nclass timed_semaphore {\n  public:\n    /* copying not implemented */\n    timed_semaphore &operator=(const timed_semaphore &) = delete;\n    timed_semaphore(const timed_semaphore &) = delete;\n\n    ~timed_semaphore();\n    timed_semaphore() : initialized(false){};\n\n    /**\n     * @brief initializes the semaphore\n     *\n     * @param count initial value of the semaphore\n     * @retval VK_ERROR_OUT_OF_HOST_MEMORY out of memory condition from pthread calls\n     * @retval VK_SUCCESS on success\n     */\n    VkResult init(unsigned count);\n\n    /**\n     * @brief decrement semaphore, waiting (with timeout) if the value is 0\n     *\n     * @param timeout time to wait (ns). 0 doesn't block, UINT64_MAX waits indefinately.\n     * @retval VK_TIMEOUT timeout was non-zero and reached the timeout\n     * @retval VK_NOT_READY timeout was zero and count is 0\n     * @retval VK_SUCCESS on success\n     */\n    VkResult wait(uint64_t timeout);\n\n    /**\n     * @brief increment semaphore, potentially unblocking a waiting thread\n     */\n    void post();\n\n  private:\n    /**\n     * @brief true if the semaphore has been initialized\n     *\n     * Determines if the destructor should cleanup the mutex and cond.\n     */\n    bool initialized;\n    /**\n     * @brief semaphore value\n     */\n    unsigned m_count;\n\n    pthread_mutex_t m_mutex;\n    pthread_cond_t m_cond;\n};\n\n} /* namespace util */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/display.cpp",
    "content": "#include \"display.hpp\"\n\n#include\"layer/settings.h\"\n\n#include <chrono>\n\nwsi::display::display()\n{\n}\n\nVkFence wsi::display::get_vsync_fence()\n{\n  if (not std::atomic_exchange(&m_thread_running, true))\n  {\n  vsync_fence = reinterpret_cast<VkFence>(this);\n  m_vsync_thread = std::thread([this]()\n      {\n      auto refresh = Settings::Instance().m_refreshRate;\n      auto next_frame = std::chrono::steady_clock::now();\n      auto frame_time = std::chrono::duration_cast<decltype(next_frame)::duration>(std::chrono::duration<double>(1. / refresh));\n      while (not m_exiting) {\n        std::this_thread::sleep_until(next_frame);\n        m_signaled = true;\n        m_cond.notify_all();\n        m_vsync_count += 1;\n        next_frame += frame_time;\n      }\n      });\n  }\n  m_signaled = false;\n  return vsync_fence;\n}\n\nwsi::display::~display() {\n  std::unique_lock<std::mutex> lock(m_mutex);\n  m_exiting = true;\n  if (m_vsync_thread.joinable())\n    m_vsync_thread.join();\n}\n\nbool wsi::display::wait_for_vsync(uint64_t timeoutNs)\n{\n  if (!m_signaled) {\n    std::unique_lock<std::mutex> lock(m_mutex);\n    return m_cond.wait_for(lock, std::chrono::nanoseconds(timeoutNs)) == std::cv_status::no_timeout;\n  }\n  return true;\n}\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/display.hpp",
    "content": "#pragma once\n\n#include <atomic>\n#include <vulkan/vulkan.h>\n#include <thread>\n#include <condition_variable>\n\nnamespace wsi {\n\nclass display {\n  public:\n    display();\n    ~display();\n\n    VkFence get_vsync_fence();\n    VkFence peek_vsync_fence() { return vsync_fence;};\n\n    bool is_signaled() const { return m_signaled; }\n    bool wait_for_vsync(uint64_t timeoutNs);\n\n    std::atomic<uint64_t> m_vsync_count{0};\n\n  private:\n    std::atomic_bool m_thread_running{false};\n    std::atomic_bool m_exiting{false};\n    std::thread m_vsync_thread;\n    VkFence vsync_fence = VK_NULL_HANDLE;\n    std::mutex m_mutex;\n    std::condition_variable m_cond;\n    std::atomic_bool m_signaled = false;\n};\n\n} // namespace wsi\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/headless/surface_properties.cpp",
    "content": "/*\n * Copyright (c) 2017-2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include <algorithm>\n#include <array>\n#include <cassert>\n#include <cstdlib>\n#include <map>\n#include <mutex>\n\n#include <vulkan/vk_icd.h>\n#include <vulkan/vulkan.h>\n\n#include <layer/private_data.hpp>\n#include \"layer/settings.h\"\n\n#include \"surface_properties.hpp\"\n\n#define UNUSED(x) ((void)(x))\n\nnamespace wsi {\nnamespace headless {\n\nsurface_properties &surface_properties::get_instance() {\n    static surface_properties instance;\n    return instance;\n}\n\nVkResult\nsurface_properties::get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,\n                                             VkSurfaceCapabilitiesKHR *surface_capabilities) {\n    UNUSED(surface);\n    /* Image count limits */\n    surface_capabilities->minImageCount = 1;\n    /* There is no maximum theoretically speaking */\n    surface_capabilities->maxImageCount = UINT32_MAX;\n\n    /* Surface extents */\n    surface_capabilities->currentExtent = surface_capabilities->maxImageExtent =\n        surface_capabilities->minImageExtent = {Settings::Instance().m_renderWidth,\n                                                Settings::Instance().m_renderHeight};\n    /* Ask the device for max */\n    VkPhysicalDeviceProperties dev_props;\n    layer::instance_private_data::get(physical_device)\n        .disp.GetPhysicalDeviceProperties(physical_device, &dev_props);\n\n    surface_capabilities->maxImageArrayLayers = 1;\n\n    /* Surface transforms */\n    surface_capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n    surface_capabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n\n    /* Composite alpha */\n    surface_capabilities->supportedCompositeAlpha = static_cast<VkCompositeAlphaFlagBitsKHR>(\n        VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |\n        VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR);\n\n    /* Image usage flags */\n    surface_capabilities->supportedUsageFlags =\n        VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |\n        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |\n        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;\n\n    return VK_SUCCESS;\n}\n\nVkResult surface_properties::get_surface_formats(VkPhysicalDevice physical_device,\n                                                 VkSurfaceKHR surface,\n                                                 uint32_t *surface_format_count,\n                                                 VkSurfaceFormatKHR *surface_formats) {\n    UNUSED(surface);\n\n    VkResult res = VK_SUCCESS;\n    /* Construct a list of all formats supported by the driver - for color attachment */\n    VkFormat formats[] = {\n      VK_FORMAT_R8_UNORM,\n      VK_FORMAT_R16_UNORM,\n      VK_FORMAT_R8G8_UNORM,\n      VK_FORMAT_R16G16_UNORM,\n      VK_FORMAT_B8G8R8A8_UNORM,\n      VK_FORMAT_R8G8B8A8_UNORM};\n    uint32_t format_count = 0;\n\n    for (size_t id = 0; id < std::size(formats); id++) {\n        VkImageFormatProperties image_format_props;\n\n        res = layer::instance_private_data::get(physical_device)\n                  .disp.GetPhysicalDeviceImageFormatProperties(\n                      physical_device, formats[id], VK_IMAGE_TYPE_2D,\n                      VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,\n                      VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, &image_format_props);\n\n        if (res != VK_ERROR_FORMAT_NOT_SUPPORTED) {\n            formats[format_count] = formats[id];\n            format_count++;\n        }\n    }\n    assert(format_count > 0);\n    assert(surface_format_count != nullptr);\n    res = VK_SUCCESS;\n    if (nullptr == surface_formats) {\n        *surface_format_count = format_count;\n    } else {\n        if (format_count > *surface_format_count) {\n            res = VK_INCOMPLETE;\n        }\n\n        *surface_format_count = std::min(*surface_format_count, format_count);\n        for (uint32_t i = 0; i < *surface_format_count; ++i) {\n            surface_formats[i].format = formats[i];\n            surface_formats[i].colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;\n        }\n    }\n\n    return res;\n}\n\nVkResult surface_properties::get_surface_present_modes(VkPhysicalDevice physical_device,\n                                                       VkSurfaceKHR surface,\n                                                       uint32_t *present_mode_count,\n                                                       VkPresentModeKHR *present_modes) {\n    UNUSED(physical_device);\n    UNUSED(surface);\n\n    VkResult res = VK_SUCCESS;\n    static const std::array<VkPresentModeKHR, 2> modes = {VK_PRESENT_MODE_FIFO_KHR,\n                                                          VK_PRESENT_MODE_FIFO_RELAXED_KHR};\n\n    assert(present_mode_count != nullptr);\n\n    if (nullptr == present_modes) {\n        *present_mode_count = modes.size();\n    } else {\n        if (modes.size() > *present_mode_count) {\n            res = VK_INCOMPLETE;\n        }\n        *present_mode_count = std::min(*present_mode_count, static_cast<uint32_t>(modes.size()));\n        for (uint32_t i = 0; i < *present_mode_count; ++i) {\n            present_modes[i] = modes[i];\n        }\n    }\n\n    return res;\n}\n\n} /* namespace headless */\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/headless/surface_properties.hpp",
    "content": "/*\n * Copyright (c) 2017-2019 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#pragma once\n\n#include <vulkan/vk_icd.h>\n#include <vulkan/vulkan.h>\n#include <wsi/surface_properties.hpp>\n\nnamespace wsi {\nnamespace headless {\n\nclass surface_properties : public wsi::surface_properties {\n  public:\n    VkResult get_surface_capabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,\n                                      VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) override;\n\n    VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface,\n                                 uint32_t *surfaceFormatCount,\n                                 VkSurfaceFormatKHR *surfaceFormats) override;\n\n    VkResult get_surface_present_modes(VkPhysicalDevice physical_device, VkSurfaceKHR surface,\n                                       uint32_t *pPresentModeCount,\n                                       VkPresentModeKHR *pPresentModes) override;\n\n    static surface_properties &get_instance();\n};\n\n} /* namespace headless */\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/headless/swapchain.cpp",
    "content": "/*\n * Copyright (c) 2017-2020 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain.cpp\n *\n * @brief Contains the implementation for a headless swapchain.\n */\n\n#include <cassert>\n#include <cstdlib>\n#include <errno.h>\n#include <new>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include <vulkan/vulkan.h>\n#include <sys/mman.h>\n\n#include <util/timed_semaphore.hpp>\n\n#include \"util/logger.h\"\n#include \"platform/linux/protocol.h\"\n#include \"swapchain.hpp\"\n#include \"wsi/display.hpp\"\n\nnamespace wsi {\nnamespace headless {\n\nstruct image_data {\n    /* Device memory backing the image. */\n    VkDeviceMemory memory;\n};\n\nswapchain::swapchain(layer::device_private_data &dev_data, const VkAllocationCallbacks *pAllocator)\n    : wsi::swapchain_base(dev_data, pAllocator), m_display(*dev_data.display) {}\n\nswapchain::~swapchain() {\n    /* Call the base's teardown */\n    close(m_socket);\n    teardown();\n}\n\nVkResult swapchain::create_image(const VkImageCreateInfo &image_create,\n                                 wsi::swapchain_image &image) {\n    VkResult res = VK_SUCCESS;\n    m_create_info = image_create;\n    m_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n      | VK_IMAGE_USAGE_TRANSFER_DST_BIT\n      | VK_IMAGE_USAGE_SAMPLED_BIT\n      | VK_IMAGE_USAGE_STORAGE_BIT;\n    res = m_device_data.disp.CreateImage(m_device, &m_create_info, nullptr, &image.image);\n    if (res != VK_SUCCESS) {\n        return res;\n    }\n    m_create_info.pNext = nullptr;\n    m_create_info.pQueueFamilyIndices = nullptr;\n\n    VkMemoryRequirements memory_requirements;\n    m_device_data.disp.GetImageMemoryRequirements(m_device, image.image, &memory_requirements);\n\n    /* Find a memory type */\n    size_t mem_type_idx = 0;\n    VkMemoryPropertyFlags memFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n    VkPhysicalDeviceMemoryProperties prop;\n    m_device_data.instance_data.disp.GetPhysicalDeviceMemoryProperties(m_device_data.physical_device, &prop);\n    for (; mem_type_idx < prop.memoryTypeCount; ++mem_type_idx) {\n        if ((prop.memoryTypes[mem_type_idx].propertyFlags & memFlags) == memFlags && memory_requirements.memoryTypeBits & (1 << mem_type_idx)) {\n            break;\n        }\n    }\n    assert(mem_type_idx < prop.memoryTypeCount);\n\n    VkExportMemoryAllocateInfo export_info = {};\n    export_info.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO;\n    export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    VkMemoryDedicatedAllocateInfo ded_info = {};\n    ded_info.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;\n    ded_info.image = image.image;\n    ded_info.pNext = &export_info;\n\n    VkMemoryAllocateInfo mem_info = {};\n    mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    mem_info.allocationSize = memory_requirements.size;\n    mem_info.memoryTypeIndex = mem_type_idx;\n    mem_info.pNext = &ded_info;\n    m_mem_index = mem_type_idx;\n    image_data *data = nullptr;\n\n    /* Create image_data */\n    data = m_allocator.create<image_data>(1);\n    if (data == nullptr) {\n        m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n    image.data = reinterpret_cast<void *>(data);\n    image.status = wsi::swapchain_image::FREE;\n\n    res = m_device_data.disp.AllocateMemory(m_device, &mem_info, nullptr, &data->memory);\n    assert(VK_SUCCESS == res);\n    if (res != VK_SUCCESS) {\n        destroy_image(image);\n        return res;\n    }\n\n    res = m_device_data.disp.BindImageMemory(m_device, image.image, data->memory, 0);\n    assert(VK_SUCCESS == res);\n    if (res != VK_SUCCESS) {\n        destroy_image(image);\n        return res;\n    }\n\n    /* Initialize presentation fence. */\n    VkFenceCreateInfo fence_info = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0};\n    res = m_device_data.disp.CreateFence(m_device, &fence_info, nullptr, &image.present_fence);\n    if (res != VK_SUCCESS) {\n        destroy_image(image);\n        return res;\n    }\n\n    // Export into a FD to send later\n    VkMemoryGetFdInfoKHR fd_info = {};\n    fd_info.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR;\n    fd_info.pNext = NULL;\n    fd_info.memory = data->memory;\n    fd_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    int fd;\n    res = m_device_data.disp.GetMemoryFdKHR(m_device, &fd_info, &fd);\n    if (res != VK_SUCCESS) {\n        Error(\"GetMemoryFdKHR failed\\n\");\n        destroy_image(image);\n        return res;\n    }\n    m_fds.push_back(fd);\n    Debug(\"GetMemoryFdKHR returned fd=%d\\n\", fd);\n\n    VkExportSemaphoreCreateInfo exp_info = {};\n    exp_info.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;\n    exp_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    VkSemaphoreTypeCreateInfo tim_info = {};\n    tim_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;\n    tim_info.pNext = &exp_info;\n    tim_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;\n\n    VkSemaphoreCreateInfo sem_info = {};\n    sem_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n    sem_info.pNext = &tim_info;\n\n    res = m_device_data.disp.CreateSemaphore(m_device, &sem_info, nullptr, &image.semaphore);\n    if (res != VK_SUCCESS) {\n        Error(\"CreateSemaphore failed\\n\");\n        destroy_image(image);\n        return res;\n    }\n\n    VkSemaphoreGetFdInfoKHR sem_fd_info = {};\n    sem_fd_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;\n    sem_fd_info.semaphore = image.semaphore;\n    sem_fd_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    res = m_device_data.disp.GetSemaphoreFdKHR(m_device, &sem_fd_info, &fd);\n    if (res != VK_SUCCESS) {\n        Error(\"GetSemaphoreFdKHR failed\\n\");\n        destroy_image(image);\n        return res;\n    }\n    m_fds.push_back(fd);\n    Debug(\"GetSemaphoreFdKHR returned fd=%d\\n\", fd);\n\n    return res;\n}\n\nint swapchain::send_fds() {\n    // This function does the arcane magic for sending\n    // file descriptors over unix domain sockets\n    // Stolen from https://gist.github.com/kokjo/75cec0f466fc34fa2922\n    //\n    // There will always be 6 fds (for the 3 images and sempahores created in the swapchain) so we can avoid\n    // dynamic length. Initially, I tried to send the length in the normal data field (msg.msg_iov /\n    // data) but for some reason it was emptied on arrival, no matter what I did.\n    //\n    struct msghdr msg;\n    struct iovec iov[1];\n    struct cmsghdr *cmsg = NULL;\n    assert(m_fds.size() == 6);\n    int fds[6];\n    char ctrl_buf[CMSG_SPACE(sizeof(fds))];\n    char data[1];\n\n    std::copy(m_fds.begin(), m_fds.end(), fds);\n\n    memset(&msg, 0, sizeof(struct msghdr));\n    memset(ctrl_buf, 0, CMSG_SPACE(sizeof(fds)));\n\n    iov[0].iov_base = data;\n    iov[0].iov_len = sizeof(data);\n\n    msg.msg_name = NULL;\n    msg.msg_namelen = 0;\n    msg.msg_iov = iov;\n    msg.msg_iovlen = 1;\n    msg.msg_controllen = CMSG_SPACE(sizeof(fds));\n    msg.msg_control = ctrl_buf;\n\n    cmsg = CMSG_FIRSTHDR(&msg);\n    cmsg->cmsg_level = SOL_SOCKET;\n    cmsg->cmsg_type = SCM_RIGHTS;\n    cmsg->cmsg_len = CMSG_LEN(sizeof(fds));\n\n    memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));\n\n    int ret = sendmsg(m_socket, &msg, 0);\n\n    for (auto fd: m_fds)\n      close(fd);\n\n    return ret;\n}\n\nbool swapchain::try_connect() {\n    Debug(\"swapchain::try_connect\\n\");\n    m_socketPath = getenv(\"XDG_RUNTIME_DIR\");\n    m_socketPath += \"/alvr-ipc\";\n\n    int ret;\n    if (m_socket == -1) {\n        m_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);\n        if (m_socket == -1) {\n            perror(\"socket\");\n            exit(1);\n        }\n    }\n\n    struct sockaddr_un name;\n    memset(&name, 0, sizeof(name));\n    name.sun_family = AF_UNIX;\n    strncpy(name.sun_path, m_socketPath.c_str(), sizeof(name.sun_path) - 1);\n\n    ret = connect(m_socket, (const struct sockaddr *)&name, sizeof(name));\n    if (ret == -1) {\n        return false; // we will try again next frame\n    }\n\n    VkPhysicalDeviceVulkan11Properties props11 = {};\n    props11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;\n\n    VkPhysicalDeviceProperties2 props = {};\n    props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;\n    props.pNext = &props11;\n    m_device_data.instance_data.disp.GetPhysicalDeviceProperties2(m_device_data.physical_device,\n                                                                  &props);\n\n    init_packet init{.num_images = uint32_t(m_swapchain_images.size()),\n      .device_uuid = {},\n      .image_create_info = m_create_info,\n      .mem_index = m_mem_index,\n      .source_pid = getpid()};\n    memcpy(init.device_uuid.data(), props11.deviceUUID, VK_UUID_SIZE);\n    ret = write(m_socket, &init, sizeof(init));\n    if (ret == -1) {\n        perror(\"write\");\n        exit(1);\n    }\n\n    ret = send_fds();\n    if (ret == -1) {\n        perror(\"sendmsg\");\n        exit(1);\n    }\n    Debug(\"swapchain sent fds\\n\");\n\n    return true;\n}\n\nvoid swapchain::submit_image(uint32_t pending_index) {\n    const auto & pose = m_swapchain_images[pending_index].pose.mDeviceToAbsoluteTracking.m;\n    if (!m_connected) {\n        m_connected = try_connect();\n    }\n    if (m_connected) {\n        int ret;\n        present_packet packet;\n        packet.image = pending_index;\n        packet.frame = m_display.m_vsync_count;\n        packet.semaphore_value = m_swapchain_images[pending_index].semaphore_value;\n        memcpy(&packet.pose, pose, sizeof(packet.pose));\n        ret = write(m_socket, &packet, sizeof(packet));\n        if (ret == -1) {\n            //FIXME: try to reconnect?\n        }\n    }\n}\n\nvoid swapchain::present_image(uint32_t pending_index) {\n    if (in_flight_index != UINT32_MAX)\n        unpresent_image(in_flight_index);\n    in_flight_index = pending_index;\n}\n\nvoid swapchain::destroy_image(wsi::swapchain_image &image) {\n    if (image.status != wsi::swapchain_image::INVALID) {\n        if (image.present_fence != VK_NULL_HANDLE) {\n            m_device_data.disp.DestroyFence(m_device, image.present_fence, nullptr);\n            image.present_fence = VK_NULL_HANDLE;\n        }\n\n        if (image.image != VK_NULL_HANDLE) {\n            m_device_data.disp.DestroyImage(m_device, image.image, get_allocation_callbacks());\n            image.image = VK_NULL_HANDLE;\n        }\n    }\n\n    if (image.data != nullptr) {\n        auto *data = reinterpret_cast<image_data *>(image.data);\n        if (data->memory != VK_NULL_HANDLE) {\n            m_device_data.disp.FreeMemory(m_device, data->memory, nullptr);\n            data->memory = VK_NULL_HANDLE;\n        }\n        m_allocator.destroy(1, data);\n        image.data = nullptr;\n    }\n\n    image.status = wsi::swapchain_image::INVALID;\n}\n\n} /* namespace headless */\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/headless/swapchain.hpp",
    "content": "/*\n * Copyright (c) 2017-2019 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain.hpp\n *\n * @brief Contains the class definition for a headless swapchain.\n */\n\n#pragma once\n\n#include <vector>\n\n#include <vulkan/vk_icd.h>\n#include <vulkan/vulkan.h>\n#include <wsi/swapchain_base.hpp>\n\n#include \"platform/linux/protocol.h\"\n\nnamespace wsi {\nnamespace headless {\n\n/**\n * @brief Headless swapchain class.\n *\n * This class is mostly empty, because all the swapchain stuff is handled by the swapchain class,\n * which we inherit. This class only provides a way to create an image and page-flip ops.\n */\nclass swapchain : public wsi::swapchain_base {\n  public:\n    explicit swapchain(layer::device_private_data &dev_data,\n                       const VkAllocationCallbacks *pAllocator);\n\n    ~swapchain();\n\n  protected:\n    /**\n     * @brief Platform specific init\n     */\n    VkResult init_platform(VkDevice device, const VkSwapchainCreateInfoKHR *pSwapchainCreateInfo) {\n        return VK_SUCCESS;\n    };\n\n    /**\n     * @brief Creates a new swapchain image.\n     *\n     * @param image_create_info Data to be used to create the image.\n     *\n     * @param image Handle to the image.\n     *\n     * @return If image creation is successful returns VK_SUCCESS, otherwise\n     * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED\n     * depending on the error that occured.\n     */\n    VkResult create_image(const VkImageCreateInfo &image_create_info, wsi::swapchain_image &image);\n\n    void submit_image(uint32_t pendingIndex);\n\n    /**\n     * @brief Method to perform a present - just calls unpresent_image on headless\n     *\n     * @param pendingIndex Index of the pending image to be presented.\n     *\n     */\n    void present_image(uint32_t pendingIndex);\n\n    /**\n     * @brief Method to release a swapchain image\n     *\n     * @param image Handle to the image about to be released.\n     */\n    void destroy_image(wsi::swapchain_image &image);\n\n  private:\n    bool try_connect();\n    int send_fds();\n    int m_socket = -1;\n    std::string m_socketPath;\n    bool m_connected = false;\n    std::vector<int> m_fds;\n    VkImageCreateInfo m_create_info;\n    size_t m_mem_index;\n    display &m_display;\n    uint32_t in_flight_index = UINT32_MAX;\n};\n\n} /* namespace headless */\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/surface_properties.hpp",
    "content": "/*\n * Copyright (c) 2017-2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file surface_properties.hpp\n *\n * @brief Vulkan WSI surface query interfaces.\n */\n\n#pragma once\n\n#include <util/extension_list.hpp>\n#include <vulkan/vulkan.h>\n\nnamespace wsi {\n\n/**\n * @brief The base surface property query interface.\n */\nclass surface_properties {\n  public:\n    /**\n     * @brief Implementation of vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the specific VkSurface\n     * type.\n     */\n    virtual VkResult get_surface_capabilities(VkPhysicalDevice physical_device,\n                                              VkSurfaceKHR surface,\n                                              VkSurfaceCapabilitiesKHR *surface_capabilities) = 0;\n\n    /**\n     * @brief Implementation of vkGetPhysicalDeviceSurfaceFormatsKHR for the specific VkSurface\n     * type.\n     */\n    virtual VkResult get_surface_formats(VkPhysicalDevice physical_device, VkSurfaceKHR surface,\n                                         uint32_t *surface_format_count,\n                                         VkSurfaceFormatKHR *surface_formats) = 0;\n\n    /**\n     * @brief Implementation of vkGetPhysicalDeviceSurfacePresentModesKHR for the specific VkSurface\n     * type.\n     */\n    virtual VkResult get_surface_present_modes(VkPhysicalDevice physical_device,\n                                               VkSurfaceKHR surface, uint32_t *present_mode_count,\n                                               VkPresentModeKHR *present_modes) = 0;\n\n    /**\n     * @brief Return the device extensions that this surface_properties implementation needs.\n     */\n    virtual const util::extension_list &get_required_device_extensions() {\n        static const util::extension_list empty{util::allocator::get_generic()};\n        return empty;\n    }\n};\n\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/swapchain_base.cpp",
    "content": "/*\n * Copyright (c) 2017-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain_base.cpp\n *\n * @brief Contains the implementation for the swapchain.\n *\n * This file contains much of the swapchain implementation,\n * that is not specific to how images are created or presented.\n */\n\n#include <array>\n#include <cassert>\n#include <cerrno>\n#include <cstdio>\n#include <cstdlib>\n\n#include <unistd.h>\n#include <vulkan/vulkan.h>\n\n#include \"display.hpp\"\n#include \"swapchain_base.hpp\"\n\n#if VULKAN_WSI_DEBUG > 0\n#define WSI_PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__)\n#else\n#define WSI_PRINT_ERROR(...) (void)0\n#endif\n\nnamespace wsi {\n\nvoid swapchain_base::page_flip_thread() {\n    auto &sc_images = m_swapchain_images;\n    VkResult vk_res = VK_SUCCESS;\n    uint64_t timeout = UINT64_MAX;\n    constexpr uint64_t SEMAPHORE_TIMEOUT = 250000000; /* 250 ms. */\n\n    /* No mutex is needed for the accesses to m_page_flip_thread_run variable as after the variable\n     * is initialized it is only ever changed to false. The while loop will make the thread read the\n     * value repeatedly, and the combination of semaphores and thread joins will force any changes\n     * to the variable to be visible to this thread.\n     */\n    while (m_page_flip_thread_run) {\n        /* Waiting for the page_flip_semaphore which will be signalled once there is an\n         * image to display.*/\n        if ((vk_res = m_page_flip_semaphore.wait(SEMAPHORE_TIMEOUT)) == VK_TIMEOUT) {\n            /* Image is not ready yet. */\n            continue;\n        }\n        assert(vk_res == VK_SUCCESS);\n\n        /* We want to present the oldest queued for present image from our present queue,\n         * which we can find at the sc->pending_buffer_pool.head index. */\n        uint32_t pending_index = m_pending_buffer_pool.ring[m_pending_buffer_pool.head];\n        m_pending_buffer_pool.head = (m_pending_buffer_pool.head + 1) % m_pending_buffer_pool.size;\n\n        submit_image(pending_index);\n\n        /* We wait for the fence of the oldest pending image to be signalled. */\n        vk_res = m_device_data.disp.WaitForFences(\n            m_device, 1, &sc_images[pending_index].present_fence, VK_TRUE, timeout);\n        if (vk_res != VK_SUCCESS) {\n            m_is_valid = false;\n            m_free_image_semaphore.post();\n            continue;\n        }\n\n        /* If the descendant has started presenting the queue_present operation has marked the image\n         * as FREE so we simply release it and continue. */\n        if (sc_images[pending_index].status == swapchain_image::FREE) {\n            destroy_image(sc_images[pending_index]);\n            m_free_image_semaphore.post();\n            continue;\n        }\n\n        /* First present of the swapchain. If it has an ancestor, wait until all the pending buffers\n         * from the ancestor have finished page flipping before we set mode. */\n        if (m_first_present) {\n            if (m_ancestor != VK_NULL_HANDLE) {\n                auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);\n                ancestor->wait_for_pending_buffers();\n            }\n\n            sem_post(&m_start_present_semaphore);\n\n            present_image(pending_index);\n\n            m_first_present = false;\n        }\n        /* The swapchain has already started presenting. */\n        else {\n            present_image(pending_index);\n        }\n    }\n}\n\nvoid swapchain_base::unpresent_image(uint32_t presented_index) {\n    m_swapchain_images[presented_index].status = swapchain_image::FREE;\n\n    if (m_descendant != VK_NULL_HANDLE) {\n        destroy_image(m_swapchain_images[presented_index]);\n    }\n\n    m_free_image_semaphore.post();\n}\n\nswapchain_base::swapchain_base(layer::device_private_data &dev_data,\n                               const VkAllocationCallbacks *callbacks)\n    : m_device_data(dev_data), m_page_flip_thread_run(true), m_thread_sem_defined(false),\n      m_first_present(true), m_pending_buffer_pool{nullptr, 0, 0, 0},\n      m_allocator(callbacks, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT), m_swapchain_images(m_allocator),\n      m_surface(VK_NULL_HANDLE), m_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR),\n      m_descendant(VK_NULL_HANDLE), m_ancestor(VK_NULL_HANDLE), m_device(VK_NULL_HANDLE),\n      m_queue(VK_NULL_HANDLE) {}\n\nVkResult swapchain_base::init(VkDevice device,\n                              const VkSwapchainCreateInfoKHR *swapchain_create_info) {\n    assert(device != VK_NULL_HANDLE);\n    assert(swapchain_create_info != nullptr);\n    assert(swapchain_create_info->surface != VK_NULL_HANDLE);\n\n    int res;\n    VkResult result;\n\n    m_device = device;\n    m_surface = swapchain_create_info->surface;\n\n    /* Check presentMode has a compatible value with swapchain - everything else should be taken\n     * care at image creation.*/\n    static const std::array<VkPresentModeKHR, 2> present_modes = {VK_PRESENT_MODE_FIFO_KHR,\n                                                                  VK_PRESENT_MODE_FIFO_RELAXED_KHR};\n    bool present_mode_found = false;\n    for (uint32_t i = 0; i < present_modes.size() && !present_mode_found; i++) {\n        if (swapchain_create_info->presentMode == present_modes[i]) {\n            present_mode_found = true;\n        }\n    }\n\n    if (!present_mode_found) {\n        return VK_ERROR_INITIALIZATION_FAILED;\n    }\n\n    /* Init image to invalid values. */\n    if (!m_swapchain_images.try_resize(swapchain_create_info->minImageCount))\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n\n    /* Initialize ring buffer. */\n    m_pending_buffer_pool.ring = m_allocator.create<uint32_t>(m_swapchain_images.size(), 0);\n    if (m_pending_buffer_pool.ring == nullptr) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    m_pending_buffer_pool.head = 0;\n    m_pending_buffer_pool.tail = 0;\n    m_pending_buffer_pool.size = m_swapchain_images.size();\n\n    /* We have allocated images, we can call the platform init function if something needs to be\n     * done. */\n    result = init_platform(device, swapchain_create_info);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    VkExternalMemoryImageCreateInfo ext_info = {};\n    ext_info.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;\n    ext_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;\n\n    VkImageCreateInfo image_create_info = {};\n    image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n    image_create_info.pNext = &ext_info;\n    image_create_info.imageType = VK_IMAGE_TYPE_2D;\n    image_create_info.format = swapchain_create_info->imageFormat;\n    image_create_info.extent = {swapchain_create_info->imageExtent.width,\n                                swapchain_create_info->imageExtent.height, 1};\n    image_create_info.mipLevels = 1;\n    image_create_info.arrayLayers = swapchain_create_info->imageArrayLayers;\n    image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;\n    image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;\n    image_create_info.usage = swapchain_create_info->imageUsage;\n    image_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT;\n    image_create_info.sharingMode = swapchain_create_info->imageSharingMode;\n    image_create_info.queueFamilyIndexCount = swapchain_create_info->queueFamilyIndexCount;\n    image_create_info.pQueueFamilyIndices = swapchain_create_info->pQueueFamilyIndices;\n    image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n    result = m_free_image_semaphore.init(m_swapchain_images.size());\n    if (result != VK_SUCCESS) {\n        assert(result == VK_ERROR_OUT_OF_HOST_MEMORY);\n        return result;\n    }\n\n    m_device_data.disp.GetDeviceQueue(m_device, 0, 0, &m_queue);\n    result = m_device_data.SetDeviceLoaderData(m_device, m_queue);\n    if (VK_SUCCESS != result) {\n        return result;\n    }\n\n    for (auto &img : m_swapchain_images) {\n        result = create_image(image_create_info, img);\n        if (result != VK_SUCCESS) {\n            return result;\n        }\n    }\n\n    /* Setup semaphore for signaling pageflip thread */\n    result = m_page_flip_semaphore.init(0);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    res = sem_init(&m_start_present_semaphore, 0, 0);\n    /* Only programming error can cause this to fail. */\n    assert(res == 0);\n    if (res != 0) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    m_thread_sem_defined = true;\n\n    /* Launch page flipping thread */\n    m_page_flip_thread = std::thread(&swapchain_base::page_flip_thread, this);\n\n    /* Release the swapchain images of the old swapchain in order\n     * to free up memory for new swapchain. This is necessary especially\n     * on platform with limited display memory size.\n     *\n     * NB: This must be done last in initialization, when the rest of\n     * the swapchain is valid.\n     */\n    if (swapchain_create_info->oldSwapchain != VK_NULL_HANDLE) {\n        /* Set ancestor. */\n        m_ancestor = swapchain_create_info->oldSwapchain;\n\n        auto *ancestor = reinterpret_cast<swapchain_base *>(m_ancestor);\n        ancestor->deprecate(reinterpret_cast<VkSwapchainKHR>(this));\n    }\n\n    m_is_valid = true;\n\n    return VK_SUCCESS;\n}\n\nvoid swapchain_base::teardown() {\n    /* This method will block until all resources associated with this swapchain\n     * are released. Images in the ACQUIRED or FREE state can be freed\n     * immediately. For images in the PRESENTED state, we will block until the\n     * presentation engine is finished with them. */\n\n    int res;\n    bool descendent_started_presenting = false;\n\n    if (m_descendant != VK_NULL_HANDLE) {\n        auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);\n        for (auto &img : desc->m_swapchain_images) {\n            if (img.status == swapchain_image::PRESENTED ||\n                img.status == swapchain_image::PENDING) {\n                /* Here we wait for the start_present_semaphore, once this semaphore is up,\n                 * the descendant has finished waiting, we don't want to delete vkImages and\n                 * vkFences and semaphores before the waiting is done. */\n                sem_wait(&desc->m_start_present_semaphore);\n\n                descendent_started_presenting = true;\n                break;\n            }\n        }\n    }\n\n    /* If descendant started presenting, there is no pending buffer in the swapchain. */\n    if (m_is_valid && descendent_started_presenting == false) {\n        wait_for_pending_buffers();\n    }\n\n    if (m_queue != VK_NULL_HANDLE) {\n        /* Make sure the vkFences are done signaling. */\n        m_device_data.disp.QueueWaitIdle(m_queue);\n    }\n\n    /* We are safe to destroy everything. */\n    if (m_thread_sem_defined) {\n        /* Tell flip thread to end. */\n        m_page_flip_thread_run = false;\n\n        if (m_page_flip_thread.joinable()) {\n            m_page_flip_thread.join();\n        } else {\n            WSI_PRINT_ERROR(\"m_page_flip_thread is not joinable\");\n        }\n\n        res = sem_destroy(&m_start_present_semaphore);\n        if (res != 0) {\n            WSI_PRINT_ERROR(\"sem_destroy failed for start_present_semaphore with %d\\n\", errno);\n        }\n    }\n\n    if (m_descendant != VK_NULL_HANDLE) {\n        auto *sc = reinterpret_cast<swapchain_base *>(m_descendant);\n        sc->clear_ancestor();\n    }\n\n    if (m_ancestor != VK_NULL_HANDLE) {\n        auto *sc = reinterpret_cast<swapchain_base *>(m_ancestor);\n        sc->clear_descendant();\n    }\n    /* Release the images array. */\n    for (auto &img : m_swapchain_images) {\n        /* Call implementation specific release */\n        destroy_image(img);\n    }\n\n    m_allocator.destroy(m_swapchain_images.size(), m_pending_buffer_pool.ring);\n}\n\nVkResult swapchain_base::acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence,\n                                            uint32_t *image_index) {\n    VkResult retval = wait_for_free_buffer(timeout);\n    if (retval != VK_SUCCESS) {\n        return retval;\n    }\n\n    if (!m_is_valid) {\n        return VK_ERROR_OUT_OF_HOST_MEMORY;\n    }\n\n    uint32_t i = m_last_acquired_image + 1;\n    for (uint32_t j = 0; j < m_swapchain_images.size(); ++j) {\n      i = (i + 1) % m_pending_buffer_pool.size;\n        if (m_swapchain_images[i].status == swapchain_image::FREE) {\n            m_swapchain_images[i].status = swapchain_image::ACQUIRED;\n            *image_index = i;\n            m_last_acquired_image = i;\n            break;\n        }\n    }\n\n    assert(i < m_swapchain_images.size());\n\n    if (VK_NULL_HANDLE != semaphore || VK_NULL_HANDLE != fence) {\n        VkSubmitInfo submit = {};\n        submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\n        if (VK_NULL_HANDLE != semaphore) {\n            submit.signalSemaphoreCount = 1;\n            submit.pSignalSemaphores = &semaphore;\n        }\n\n        submit.commandBufferCount = 0;\n        submit.pCommandBuffers = nullptr;\n        retval = m_device_data.disp.QueueSubmit(m_queue, 1, &submit, fence);\n        assert(retval == VK_SUCCESS);\n    }\n\n    return retval;\n}\n\nVkResult swapchain_base::get_swapchain_images(uint32_t *swapchain_image_count,\n                                              VkImage *swapchain_images) {\n    if (swapchain_images == nullptr) {\n        /* Return the number of swapchain images. */\n        *swapchain_image_count = m_swapchain_images.size();\n\n        return VK_SUCCESS;\n    } else {\n        assert(m_swapchain_images.size() > 0);\n        assert(*swapchain_image_count > 0);\n\n        /* Populate array, write actual number of images returned. */\n        uint32_t current_image = 0;\n\n        do {\n            swapchain_images[current_image] = m_swapchain_images[current_image].image;\n\n            current_image++;\n\n            if (current_image == m_swapchain_images.size()) {\n                *swapchain_image_count = current_image;\n\n                return VK_SUCCESS;\n            }\n\n        } while (current_image < *swapchain_image_count);\n\n        /* If swapchain_image_count is smaller than the number of presentable images\n         * in the swapchain, VK_INCOMPLETE must be returned instead of VK_SUCCESS. */\n        *swapchain_image_count = current_image;\n\n        return VK_INCOMPLETE;\n    }\n}\n\nVkResult swapchain_base::queue_present(VkQueue queue, const VkPresentInfoKHR *present_info,\n                                       const uint32_t image_index) {\n    VkResult result;\n    bool descendent_started_presenting = false;\n\n    const auto & pose = find_pose_in_call_stack();\n\n    if (m_descendant != VK_NULL_HANDLE) {\n        auto *desc = reinterpret_cast<swapchain_base *>(m_descendant);\n        for (auto &img : desc->m_swapchain_images) {\n            if (img.status == swapchain_image::PRESENTED ||\n                img.status == swapchain_image::PENDING) {\n                descendent_started_presenting = true;\n                break;\n            }\n        }\n    }\n\n    /* When the semaphore that comes in is signalled, we know that all work is done. So, we do not\n     * want to block any future Vulkan queue work on it. So, we pass in BOTTOM_OF_PIPE bit as the\n     * wait flag.\n     */\n    VkPipelineStageFlags pipeline_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;\n\n    uint64_t signal_value = ++m_swapchain_images[image_index].semaphore_value;\n\n    VkTimelineSemaphoreSubmitInfo timeline_info = {};\n    timeline_info.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;\n    timeline_info.signalSemaphoreValueCount = 1;\n    timeline_info.pSignalSemaphoreValues = &signal_value;\n\n    VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO,\n                                &timeline_info,\n                                present_info->waitSemaphoreCount,\n                                present_info->pWaitSemaphores,\n                                &pipeline_stage_flags,\n                                0,\n                                NULL,\n                                1,\n                                &m_swapchain_images[image_index].semaphore};\n\n    assert(m_swapchain_images[image_index].status == swapchain_image::ACQUIRED);\n    result =\n        m_device_data.disp.ResetFences(m_device, 1, &m_swapchain_images[image_index].present_fence);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    result = m_device_data.disp.QueueSubmit(queue, 1, &submit_info,\n                                            m_swapchain_images[image_index].present_fence);\n    if (result != VK_SUCCESS) {\n        return result;\n    }\n\n    /* If the descendant has started presenting, we should release the image\n     * however we do not want to block inside the main thread so we mark it\n     * as free and let the page flip thread take care of it. */\n    if (descendent_started_presenting) {\n        m_swapchain_images[image_index].status = swapchain_image::FREE;\n\n        m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;\n        m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;\n\n        m_page_flip_semaphore.post();\n\n        return VK_ERROR_OUT_OF_DATE_KHR;\n    }\n\n    m_swapchain_images[image_index].status = swapchain_image::PENDING;\n    m_swapchain_images[image_index].pose = pose;\n\n    m_pending_buffer_pool.ring[m_pending_buffer_pool.tail] = image_index;\n    m_pending_buffer_pool.tail = (m_pending_buffer_pool.tail + 1) % m_pending_buffer_pool.size;\n\n    m_page_flip_semaphore.post();\n    return VK_SUCCESS;\n}\n\nvoid swapchain_base::deprecate(VkSwapchainKHR descendant) {\n    for (auto &img : m_swapchain_images) {\n        if (img.status == swapchain_image::FREE) {\n            destroy_image(img);\n        }\n    }\n\n    /* Set its descendant. */\n    m_descendant = descendant;\n}\n\nvoid swapchain_base::wait_for_pending_buffers() {\n    int num_acquired_images = 0;\n    int wait;\n\n    for (auto &img : m_swapchain_images) {\n        if (img.status == swapchain_image::ACQUIRED) {\n            ++num_acquired_images;\n        }\n    }\n\n    /* Once all the pending buffers are flipped, the swapchain should have images\n     * in ACQUIRED (application fails to queue them back for presentation), FREE\n     * and one and only one in PRESENTED. */\n    wait = m_swapchain_images.size() - num_acquired_images - 1;\n\n    while (wait > 0) {\n        /* Take down one free image semaphore. */\n        wait_for_free_buffer(UINT64_MAX);\n        --wait;\n    }\n}\n\nvoid swapchain_base::clear_ancestor() { m_ancestor = VK_NULL_HANDLE; }\n\nvoid swapchain_base::clear_descendant() { m_descendant = VK_NULL_HANDLE; }\n\nVkResult swapchain_base::wait_for_free_buffer(uint64_t timeout) {\n    VkResult retval;\n    /* first see if a buffer is already marked as free */\n    retval = m_free_image_semaphore.wait(0);\n    if (retval == VK_NOT_READY) {\n        /* if not, we still have work to do even if timeout==0 -\n         * the swapchain implementation may be able to get a buffer without\n         * waiting */\n\n        retval = get_free_buffer(&timeout);\n        if (retval == VK_SUCCESS) {\n            /* the sub-implementation has done it's thing, so re-check the\n             * semaphore */\n            retval = m_free_image_semaphore.wait(timeout);\n        }\n    }\n\n    return retval;\n}\n\n#undef WSI_PRINT_ERROR\n\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/swapchain_base.hpp",
    "content": "/*\n * Copyright (c) 2017-2020 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file swapchain_base.hpp\n *\n * @brief Contains the class definition for a base swapchain.\n */\n\n#pragma once\n\n#include <pthread.h>\n#include <semaphore.h>\n#include <thread>\n#include <vulkan/vulkan.h>\n\n#include <layer/private_data.hpp>\n#include <util/custom_allocator.hpp>\n#include <util/timed_semaphore.hpp>\n#include <util/pose.hpp>\n\nnamespace wsi {\nstruct swapchain_image {\n    enum status {\n        INVALID,\n        ACQUIRED,\n        PENDING,\n        PRESENTED,\n        FREE,\n    };\n\n    /* Implementation specific data */\n    void *data{nullptr};\n\n    VkImage image{VK_NULL_HANDLE};\n    status status{swapchain_image::INVALID};\n\n    VkFence present_fence{VK_NULL_HANDLE};\n    VkSemaphore semaphore{VK_NULL_HANDLE};\n    uint64_t semaphore_value = 0;\n\n    TrackedDevicePose_t pose;\n};\n\n/**\n * @brief Base swapchain class\n *\n * - the swapchain implementations inherit from this class.\n * - the VkSwapchain will hold a pointer to this class.\n * - much of the swapchain implementation is done by this class, as the only things needed\n *   in the implementation are how to create a presentable image and how to present an image.\n */\nclass swapchain_base {\n  public:\n    swapchain_base(layer::device_private_data &dev_data, const VkAllocationCallbacks *allocator);\n\n    virtual ~swapchain_base() { /* nop */\n    }\n\n    /**\n     * @brief Create swapchain.\n     *\n     * Perform all swapchain initialization, create presentable images etc.\n     */\n    VkResult init(VkDevice device, const VkSwapchainCreateInfoKHR *swapchain_create_info);\n\n    /**\n     * @brief Acquires a free image.\n     *\n     * Current implementation blocks until a free image is available.\n     *\n     * @param timeout Unused since we block until a free image is available.\n     *\n     * @param semaphore A semaphore signaled once an image is acquired.\n     *\n     * @param fence A fence signaled once an image is acquired.\n     *\n     * @param pImageIndex The index of the acquired image.\n     *\n     * @return VK_SUCCESS on completion.\n     */\n    VkResult acquire_next_image(uint64_t timeout, VkSemaphore semaphore, VkFence fence,\n                                uint32_t *image_index);\n\n    /**\n     * @brief Gets the number of swapchain images or a number of at most\n     * m_num_swapchain_images images.\n     *\n     * @param pSwapchainImageCount Used to return number of images in\n     * the swapchain if second parameter is nullptr or represents the\n     * number of images to be returned in second parameter.\n     *\n     * @param pSwapchainImage Array of VkImage handles.\n     *\n     * @return If number of requested images is less than the number of available\n     * images in the swapchain returns VK_INCOMPLETE otherwise VK_SUCCESS.\n     */\n    VkResult get_swapchain_images(uint32_t *swapchain_image_count, VkImage *swapchain_image);\n\n    /**\n     * @brief Submits a present request for the supplied image.\n     *\n     * @param queue The queue to which the submission will be made to.\n     *\n     * @param pPresentInfo Information about the swapchain and image to be presented.\n     *\n     * @param imageIndex The index of the image to be presented.\n     *\n     * @return If queue submission fails returns error of vkQueueSubmit, if the\n     * swapchain has a descendant who started presenting returns VK_ERROR_OUT_OF_DATE_KHR,\n     * otherwise returns VK_SUCCESS.\n     */\n    VkResult queue_present(VkQueue queue, const VkPresentInfoKHR *present_info,\n                           const uint32_t image_index);\n\n  protected:\n    layer::device_private_data &m_device_data;\n\n    /**\n     * @brief Handle to the page flip thread.\n     */\n    std::thread m_page_flip_thread;\n\n    /**\n     * @brief Whether the page flip thread has to continue running or terminate.\n     */\n    bool m_page_flip_thread_run;\n\n    /**\n     * @brief In case we encounter threading or drm errors we need a way to\n     * notify the user of the failure. When this flag is false, acquire_next_image\n     * will return an error code.\n     */\n    bool m_is_valid;\n\n    struct ring_buffer {\n        /* Ring buffer to hold the image indexes. */\n        uint32_t *ring;\n        /* Head of the ring. */\n        uint32_t head;\n        /* End of the ring. */\n        uint32_t tail;\n        /* Size of the ring. */\n        uint32_t size;\n    };\n    /**\n     * @brief A semaphore to be signalled once a page flip event occurs.\n     */\n    util::timed_semaphore m_page_flip_semaphore;\n\n    /**\n     * @brief A semaphore to be signalled once the swapchain has one frame on screen.\n     */\n    sem_t m_start_present_semaphore;\n\n    /**\n     * @brief Defines if the pthread_t and sem_t members of the class are defined.\n     *\n     * As they are opaque types theer's no known invalid value that we ca initialize to,\n     * and therefore determine if we need to cleanup.\n     */\n    bool m_thread_sem_defined;\n\n    /**\n     * @brief A flag to track if it is the first present for the chain.\n     */\n    bool m_first_present;\n\n    /**\n     * @brief In order to present the images in a FIFO order we implement\n     * a ring buffer to hold the images queued for presentation. Since the\n     * two pointers (head and tail) are used by different\n     * threads and we do not allow the application to acquire more images\n     * than we have we eliminate race conditions.\n     */\n    ring_buffer m_pending_buffer_pool;\n\n    /**\n     * @brief User provided memory allocation callbacks.\n     */\n    const util::allocator m_allocator;\n\n    /**\n     * @brief Vector of images in the swapchain.\n     */\n    util::vector<swapchain_image> m_swapchain_images;\n\n    /**\n     * @brief Handle to the surface object this swapchain will present images to.\n     */\n    VkSurfaceKHR m_surface;\n\n    /**\n     * @brief present mode to use for this swapchain\n     */\n    VkPresentModeKHR m_present_mode;\n\n    /**\n     * @brief Descendant of this swapchain.\n     * Used to check whether or not a descendant of this swapchain has started\n     * presenting images to the surface already. If it has, any calls to queuePresent\n     * for this swapchain will return VK_ERROR_OUT_OF_DATE_KHR.\n     */\n    VkSwapchainKHR m_descendant;\n\n    /**\n     * @brief Ancestor of this swapchain.\n     * Used to check whether the ancestor swapchain has completed all of its\n     * pending page flips (this is required before this swapchain presents for the\n     * first time.\n     */\n    VkSwapchainKHR m_ancestor;\n\n    /**\n     *  @brief Handle to the logical device the swapchain is created for.\n     */\n    VkDevice m_device;\n\n    /**\n     *  @brief Handle to the queue used for signalling submissions\n     */\n    VkQueue m_queue;\n\n    /**\n     * @brief Return the VkAllocationCallbacks passed in this object constructor.\n     */\n    const VkAllocationCallbacks *get_allocation_callbacks() {\n        return m_allocator.get_original_callbacks();\n    }\n\n    /**\n     * @brief Method to wait on all pending buffers to be displayed.\n     */\n    void wait_for_pending_buffers();\n\n    /**\n     * @brief Remove cached ancestor.\n     */\n    void clear_ancestor();\n\n    /**\n     * @brief Remove cached descendant.\n     */\n    void clear_descendant();\n\n    /**\n     * @brief Deprecate this swapchain.\n     *\n     * If an application replaces an old swapchain with a new one, the older swapchain\n     * needs to be deprecated. This method releases all the FREE images and sets the\n     * descendant of the swapchain. We do not need to care about images in other states\n     * at this point since they will be released by the page flip thread.\n     *\n     * @param descendant Handle to the descendant swapchain.\n     */\n    void deprecate(VkSwapchainKHR descendant);\n\n    /**\n     * @brief Platform specific initialization\n     */\n    virtual VkResult init_platform(VkDevice device,\n                                   const VkSwapchainCreateInfoKHR *swapchain_create_info) = 0;\n\n    /**\n     * @brief Base swapchain teardown.\n     *\n     * Even though the inheritance gives us a nice way to defer display specific allocation\n     * and presentation outside of the base class, it however robs the children classes - which\n     * also happen to do some of their state setting - the oppurtunity to do the last clean up\n     * call, as the base class' destructor is called at the end. This method provides a way to do\n     * it. The destructor is a virtual function and much of the swapchain teardown happens in this\n     * method which gets called from the child's destructor.\n     */\n    void teardown();\n\n    /**\n     * @brief Creates a new swapchain image.\n     *\n     * @param image_create_info Data to be used to create the image.\n     *\n     * @param image Handle to the image.\n     *\n     * @return If image creation is successful returns VK_SUCCESS, otherwise\n     * will return VK_ERROR_OUT_OF_DEVICE_MEMORY or VK_ERROR_INITIALIZATION_FAILED\n     * depending on the error that occured.\n     */\n    virtual VkResult create_image(const VkImageCreateInfo &image_create_info,\n                                  swapchain_image &image) = 0;\n\n\n    virtual void submit_image(uint32_t pending_index) = 0;\n\n    /**\n     * @brief Method to present and image\n     *\n     * @param pending_index Index of the pending image to be presented.\n     *\n     */\n    virtual void present_image(uint32_t pending_index) = 0;\n\n    /**\n     * @brief Transition a presented image to free.\n     *\n     * Called by swapchain implementation when a new image has been presented.\n     *\n     * @param presented_index Index of the image to be marked as free.\n     */\n    void unpresent_image(uint32_t presented_index);\n\n    /**\n     * @brief Method to release a swapchain image\n     *\n     * @param image Handle to the image about to be released.\n     */\n    virtual void destroy_image(swapchain_image &image){};\n\n    /**\n     * @brief Hook for any actions to free up a buffer for acquire\n     *\n     * @param[in,out] timeout time to wait, in nanoseconds. 0 doesn't block,\n     *                        UINT64_MAX waits indefinately. The timeout should\n     *                        be updated if a sleep is required - this can\n     *                        be set to 0 if the semaphore is now not expected\n     *                        block.\n     */\n    virtual VkResult get_free_buffer(uint64_t *timeout) { return VK_SUCCESS; }\n\n  private:\n    /**\n     * @brief Wait for a buffer to become free.\n     */\n    VkResult wait_for_free_buffer(uint64_t timeout);\n\n    /**\n     * @brief A semaphore to be signalled once a free image becomes available.\n     *\n     * Uses a custom semaphore implementation that uses a condition variable.\n     * it is slower, but has a safe timedwait implementation.\n     *\n     * This is kept private as waiting should be done via wait_for_free_buffer().\n     */\n    util::timed_semaphore m_free_image_semaphore;\n\n    /**\n     * @brief Per swapchain thread function that handles page flipping.\n     *\n     * This thread should be running for the lifetime of the swapchain.\n     * The thread simply calls the implementation's present_image() method.\n     * There are 3 main cases we cover here:\n     *\n     * 1. On the first present of the swapchain if the swapchain has\n     *    an ancestor we must wait for it to finish presenting.\n     * 2. The normal use case where we do page flipping, in this\n     *    case change the currently PRESENTED image with the oldest\n     *    PENDING image.\n     * 3. If the enqueued image is marked as FREE it means the\n     *    descendant of the swapchain has started presenting so we\n     *    should release the image and continue.\n     *\n     * The function always waits on the page_flip_semaphore of the\n     * swapchain. Once it passes that we must wait for the fence of the\n     * oldest pending image to be signalled, this means that the gpu has\n     * finished rendering to it and we can present it. From there on the\n     * logic splits into the above 3 cases and if an image has been\n     * presented then the old one is marked as FREE and the free_image\n     * semaphore of the swapchain will be posted.\n     **/\n    void page_flip_thread();\n\n    uint32_t m_last_acquired_image = 0;\n\n    std::vector<VkFence> m_fences;\n};\n\n} /* namespace wsi */\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/wsi_factory.cpp",
    "content": "/*\n * Copyright (c) 2019-2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file\n * @brief Implements factory methods for obtaining the specific surface and swapchain\n * implementations.\n */\n\n#include \"wsi_factory.hpp\"\n#include \"headless/surface_properties.hpp\"\n#include \"headless/swapchain.hpp\"\n\n#include <cassert>\n#include <cstdlib>\n#include <cstring>\n#include <new>\n#include <vulkan/vk_icd.h>\n#include <vulkan/vulkan_core.h>\n\nnamespace wsi {\n\nstatic struct wsi_extension {\n    VkExtensionProperties extension;\n    VkIcdWsiPlatform platform;\n} const supported_wsi_extensions[] = {\n    {{VK_KHR_DISPLAY_EXTENSION_NAME, VK_KHR_DISPLAY_SPEC_VERSION}, VK_ICD_WSI_PLATFORM_HEADLESS}};\n\nstatic surface_properties *get_surface_properties(VkIcdWsiPlatform platform) {\n    switch (platform) {\n    case VK_ICD_WSI_PLATFORM_HEADLESS:\n        return &headless::surface_properties::get_instance();\n    default:\n        return nullptr;\n    }\n}\n\nsurface_properties *get_surface_properties(VkSurfaceKHR) {\n    return get_surface_properties(VK_ICD_WSI_PLATFORM_HEADLESS);\n}\n\ntemplate <typename swapchain_type>\nstatic swapchain_base *allocate_swapchain(layer::device_private_data &dev_data,\n                                          const VkAllocationCallbacks *pAllocator) {\n    if (!pAllocator) {\n        return new swapchain_type(dev_data, pAllocator);\n    }\n    void *memory =\n        pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(swapchain_type),\n                                  alignof(swapchain_type), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);\n    return new (memory) swapchain_type(dev_data, pAllocator);\n}\n\nswapchain_base *allocate_surface_swapchain(VkSurfaceKHR,\n                                           layer::device_private_data &dev_data,\n                                           const VkAllocationCallbacks *pAllocator) {\n    return allocate_swapchain<wsi::headless::swapchain>(dev_data, pAllocator);\n}\n\nutil::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo) {\n    util::wsi_platform_set ret;\n    for (const auto &ext_provided_by_layer : supported_wsi_extensions) {\n        for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {\n            const char *ext_requested_by_user = pCreateInfo->ppEnabledExtensionNames[i];\n            if (strcmp(ext_requested_by_user, ext_provided_by_layer.extension.extensionName) == 0) {\n                ret.add(ext_provided_by_layer.platform);\n            }\n        }\n    }\n    return ret;\n}\n\nVkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev,\n                                          const util::wsi_platform_set enabled_platforms,\n                                          util::extension_list &extensions_to_enable) {\n    util::allocator allocator{extensions_to_enable.get_allocator(),\n                              VK_SYSTEM_ALLOCATION_SCOPE_COMMAND};\n    util::extension_list device_extensions{allocator};\n    VkResult res = device_extensions.add(phys_dev);\n    if (res != VK_SUCCESS) {\n        return res;\n    }\n\n    for (const auto &wsi_ext : supported_wsi_extensions) {\n        /* Skip iterating over platforms not enabled in the instance. */\n        if (!enabled_platforms.contains(wsi_ext.platform)) {\n            continue;\n        }\n\n        surface_properties *props = get_surface_properties(wsi_ext.platform);\n        const auto &extensions_required_by_layer = props->get_required_device_extensions();\n        bool supported = device_extensions.contains(extensions_required_by_layer);\n        if (!supported) {\n            /* Can we accept failure? The layer unconditionally advertises support for this platform\n             * and the loader uses this information to enable its own support of the\n             * vkCreate*SurfaceKHR entrypoints. The rest of the Vulkan stack may not support this\n             * extension so we cannot blindly fall back to it. For now treat this as an error.\n             */\n            return VK_ERROR_INITIALIZATION_FAILED;\n        }\n\n        res = extensions_to_enable.add(extensions_required_by_layer);\n        if (res != VK_SUCCESS) {\n            return res;\n        }\n    }\n    return VK_SUCCESS;\n}\n\nvoid destroy_surface_swapchain(swapchain_base *swapchain, const VkAllocationCallbacks *pAllocator) {\n    assert(swapchain);\n\n    if (!pAllocator) {\n        delete swapchain;\n    } else {\n        swapchain->~swapchain_base();\n        pAllocator->pfnFree(pAllocator->pUserData, reinterpret_cast<void *>(swapchain));\n    }\n}\n\n} // namespace wsi\n"
  },
  {
    "path": "alvr/vulkan_layer/wsi/wsi_factory.hpp",
    "content": "/*\n * Copyright (c) 2019, 2021 Arm Limited.\n *\n * SPDX-License-Identifier: MIT\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * @file\n * @brief Contains the factory methods for obtaining the specific surface and swapchain\n * implementations.\n */\n\n#pragma once\n\n#include \"surface_properties.hpp\"\n#include \"swapchain_base.hpp\"\n#include \"util/platform_set.hpp\"\n\n#include <unordered_map>\n\nnamespace wsi {\n\n/**\n * @brief Obtains the surface properties for the specific surface type.\n *\n * @param surface The surface for which to get the properties.\n *\n * @return nullptr if surface type is unsupported.\n */\nsurface_properties *get_surface_properties(VkSurfaceKHR surface);\n\n/**\n * @brief Allocates a surface specific swapchain.\n *\n * @param surface    The surface for which a swapchain is allocated.\n * @param dev_data   The device specific data.\n * @param pAllocator The allocator from which to allocate any memory.\n *\n * @return nullptr on failure.\n */\nswapchain_base *allocate_surface_swapchain(VkSurfaceKHR surface,\n                                           layer::device_private_data &dev_data,\n                                           const VkAllocationCallbacks *pAllocator);\n\n/**\n * @brief Destroys a swapchain and frees memory. Used with @ref allocate_surface_swapchain.\n *\n * @param swapchain  Pointer to the swapchain to destroy.\n * @param pAllocator The allocator to use for freeing memory.\n */\nvoid destroy_surface_swapchain(swapchain_base *swapchain, const VkAllocationCallbacks *pAllocator);\n\n/**\n * @brief Return which platforms the layer can handle for an instance constructed in the specified\n * way.\n *\n * @details This function looks at the extensions specified in @p pCreateInfo and based on this\n * returns a list of platforms that the layer can support. For example, if the @c\n * pCreateInfo.ppEnabledExtensionNames contains the string \"VK_EXT_headless_surface\" then the\n * returned platform set will contain @c VK_ICD_WSI_PLATFORM_HEADLESS.\n *\n * @param pCreateInfo Structure used when creating the instance in vkCreateInstance().\n *\n * @return A list of WS platforms supported by the layer.\n */\nutil::wsi_platform_set find_enabled_layer_platforms(const VkInstanceCreateInfo *pCreateInfo);\n\n/**\n * @brief Add extra extensions that the layer requires to support the specified list of enabled\n * platforms.\n *\n * @details Check whether @p phys_dev has support for the extensions required by the layer in order\n * to support the platforms it implements. The extensions that the layer requires to operate are\n * added to @p extensions_to_enable.\n *\n * @param[in] phys_dev The physical device to check.\n * @param[in] enabled_platforms All the platforms that the layer must enable for @p phys_dev.\n * @param[in,out] extensions_to_enable All the extensions required by the layer are added to this\n * list.\n *\n * @retval @c VK_SUCCESS if the operation was successful.\n */\nVkResult add_extensions_required_by_layer(VkPhysicalDevice phys_dev,\n                                          const util::wsi_platform_set enabled_platforms,\n                                          util::extension_list &extensions_to_enable);\n\n} // namespace wsi\n"
  },
  {
    "path": "alvr/xtask/Cargo.toml",
    "content": "[package]\nname = \"alvr_xtask\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nlicense.workspace = true\n\n[dependencies]\nalvr_filesystem.workspace = true\n\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1\"\npico-args = \"0.5\"\nxshell = \"0.2\"\nwalkdir = \"2\"\n\n[target.'cfg(target_os = \"linux\")'.dependencies]\npkg-config = \"0.3\"\n"
  },
  {
    "path": "alvr/xtask/LICENSE",
    "content": "Copyright (c) 2020-2024 alvr-org\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "alvr/xtask/README.md",
    "content": "# alvr_xtask\n\nCustom tailored build utilities. Inspired by [cargo-xtask](https://github.com/matklad/cargo-xtask).\n"
  },
  {
    "path": "alvr/xtask/build.rs",
    "content": "fn main() {}\n"
  },
  {
    "path": "alvr/xtask/firewall/alvr-firewalld.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<service>\n  <short>Stream VR games from your PC to your headset via Wi-Fi</short>\n  <description>ALVR is an open source remote VR display which allows playing SteamVR games on a standalone headset such as Gear VR or Oculus Go/Quest.</description>\n  <port protocol=\"tcp\" port=\"9943-9944\"/>\n  <port protocol=\"udp\" port=\"9943-9944\"/>\n</service>\n"
  },
  {
    "path": "alvr/xtask/firewall/alvr_fw_config.sh",
    "content": "#!/usr/bin/env bash\n# Basic script to add / remove firewall configuration for ALVR\n# Usage: ./alvr_fw_config.sh add|remove\n# Exit codes:\n# 1 - Invalid command\n# 2 - Invalid action\n# 3 - Failed to copy UFW configuration\n# 99 - Firewall not found\n# 126 - pkexec failed - Request dismissed\n\nfirewalld_cfg() {\n    # Iterate around each active zone\n    for zone in $(firewall-cmd --get-active-zones | grep -P --only-matching '^[\\w/-]+'); do\n        if [ \"${1}\" == 'add' ]; then\n            # If running or permanent alvr service is missing, add it\n            if ! firewall-cmd --zone=\"${zone}\" --list-services | grep 'alvr' >/dev/null 2>&1; then\n                firewall-cmd --zone=\"${zone}\" --add-service='alvr'\n            fi\n            if ! firewall-cmd --zone=\"${zone}\" --list-services --permanent | grep 'alvr' >/dev/null 2>&1; then\n                firewall-cmd --zone=\"${zone}\" --add-service='alvr' --permanent\n            fi\n        elif [ \"${1}\" == 'remove' ]; then\n            # If running or persistent alvr service exists, remove it\n            if firewall-cmd --zone=\"${zone}\" --list-services | grep 'alvr' >/dev/null 2>&1; then\n                firewall-cmd --zone=\"${zone}\" --remove-service='alvr'\n            fi\n            if firewall-cmd --zone=\"${zone}\" --list-services --permanent | grep 'alvr' >/dev/null 2>&1; then\n                firewall-cmd --zone=\"${zone}\" --remove-service='alvr' --permanent\n            fi\n        else\n            exit 2\n        fi\n    done\n}\n\nufw_cfg() {\n    # Try and install the application file\n    if ! ufw app info 'alvr'; then\n        # Pull application file from local build first if the script lives inside it\n        if [ -f \"$(dirname \"$(realpath \"${0}\")\")/ufw-alvr\" ]; then\n            cp \"$(dirname \"$(realpath \"${0}\")\")/ufw-alvr\" '/etc/ufw/applications.d/'\n        elif [ -f '/usr/share/alvr/ufw-alvr' ]; then\n            cp '/usr/share/alvr/ufw-alvr' '/etc/ufw/applications.d/'\n        else\n            exit 3\n        fi\n    fi\n\n    if [ \"${1}\" == 'add' ] && ! ufw status | grep 'alvr' >/dev/null 2>&1; then\n        ufw allow 'alvr'\n    elif [ \"${1}\" == 'remove' ] && ufw status | grep 'alvr' >/dev/null 2>&1; then\n        ufw delete allow 'alvr'\n    else\n        exit 2\n    fi\n}\n\niptables_cfg() {\n    first_port_match_count=$(iptables -S | grep -c '9943')\n    second_port_match_count=$(iptables -S | grep -c '9944')\n    if [ \"${1}\" == 'add' ]; then\n        if [ \"$first_port_match_count\" == \"0\" ] || [ \"$second_port_match_count\" == \"0\" ]; then\n            if [ ! -d '/etc/iptables' ]; then\n                mkdir '/etc/iptables'\n            fi\n\n            iptables -I OUTPUT -p tcp --sport 9943 -j ACCEPT\n            iptables -I INPUT -p tcp --dport 9943 -j ACCEPT\n            iptables -I OUTPUT -p udp --sport 9943 -j ACCEPT\n            iptables -I INPUT -p udp --dport 9943 -j ACCEPT\n            iptables -I OUTPUT -p tcp --sport 9944 -j ACCEPT\n            iptables -I INPUT -p tcp --dport 9944 -j ACCEPT\n            iptables -I OUTPUT -p udp --sport 9944 -j ACCEPT\n            iptables -I INPUT -p udp --dport 9944 -j ACCEPT\n            iptables-save >/etc/iptables/rules.v4\n        fi\n    elif [ \"${1}\" == 'remove' ]; then\n        if [ \"$first_port_match_count\" == \"4\" ] || [ \"$second_port_match_count\" == \"4\" ]; then\n            iptables -D OUTPUT -p tcp --sport 9943 -j ACCEPT\n            iptables -D INPUT -p tcp --dport 9943 -j ACCEPT\n            iptables -D OUTPUT -p udp --sport 9943 -j ACCEPT\n            iptables -D INPUT -p udp --dport 9943 -j ACCEPT\n            iptables -D OUTPUT -p tcp --sport 9944 -j ACCEPT\n            iptables -D INPUT -p tcp --dport 9944 -j ACCEPT\n            iptables -D OUTPUT -p udp --sport 9944 -j ACCEPT\n            iptables -D INPUT -p udp --dport 9944 -j ACCEPT\n            iptables-save >/etc/iptables/rules.v4\n        fi\n    else\n        exit 2\n    fi\n}\n\nmain() {\n    # If we're not root use pkexec for GUI prompt\n    if [ \"${USER}\" == 'root' ]; then\n        # Check if firewall-cmd exists and firewalld is running\n        if which firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then\n            firewalld_cfg \"${1,,}\"\n        # Check if ufw exists and is running\n        elif which ufw >/dev/null 2>&1 && ! ufw status | grep 'Status: inactive' >/dev/null 2>&1; then\n            ufw_cfg \"${1,,}\"\n        elif which iptables >/dev/null 2>&1; then\n            iptables_cfg \"${1,,}\"\n        else\n            exit 99\n        fi\n    else\n        pkexec \"$(realpath \"${0}\")\" \"${@}\"\n    fi\n}\n\nmain \"${@}\"\n"
  },
  {
    "path": "alvr/xtask/firewall/ufw-alvr",
    "content": "[alvr]\ntitle=ALVR\ndescription=Stream VR games from your PC to your headset via Wi-Fi\nports=9943:9944/tcp|9943:9944/udp\n"
  },
  {
    "path": "alvr/xtask/flatpak/README.md",
    "content": "Content of this file was moved to the appropiate wiki page: https://github.com/alvr-org/ALVR/wiki/Installing-ALVR-and-using-SteamVR-on-Linux-through-Flatpak\n"
  },
  {
    "path": "alvr/xtask/flatpak/build_and_install.sh",
    "content": "#!/bin/sh -e\n\n flatpak run org.flatpak.Builder --user --install --force-clean build-dir com.valvesoftware.Steam.Utility.alvr.json\n"
  },
  {
    "path": "alvr/xtask/flatpak/com.valvesoftware.Steam.Utility.alvr.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nType=Application\nName=ALVR Launcher\nGenericName=Game\nComment=ALVR is an open source remote VR display which allows playing SteamVR games on a standalone headset such as Gear VR or Oculus Go/Quest.\nExec=/usr/bin/flatpak run --command=/app/utils/alvr/bin/alvr_launcher com.valvesoftware.Steam\nIcon=application-alvr-launcher\nCategories=Game;\nStartupNotify=true\nStartupWMClass=ALVR\n"
  },
  {
    "path": "alvr/xtask/flatpak/com.valvesoftware.Steam.Utility.alvr.json",
    "content": "{\n  \"id\": \"com.valvesoftware.Steam.Utility.alvr\",\n  \"branch\": \"stable\",\n  \"sdk\": \"org.freedesktop.Sdk//24.08\",\n  \"sdk-extensions\": [\n    \"org.freedesktop.Sdk.Extension.llvm19\",\n    \"org.freedesktop.Sdk.Extension.rust-stable\"\n  ],\n  \"runtime\": \"com.valvesoftware.Steam\",\n  \"runtime-version\": \"stable\",\n  \"appstream-compose\": false,\n  \"separate-locales\": false,\n  \"build-extension\": true,\n  \"build-options\": {\n    \"append-path\": \"/usr/lib/sdk/llvm19/bin:/usr/lib/sdk/rust-stable/bin\",\n    \"build-args\": [ \"--share=network\", \"--filesystem=xdg-data\" ],\n    \"strip\": true,\n    \"env\": { \"RUST_BACKTRACE\": \"full\" }\n  },\n  \"finish-args\": [\n    \"--share=ipc\",\n    \"--share=network\",\n    \"--device=all\",\n    \"--filesystem=home\",\n    \"--talk-name=org.freedesktop.Notifications\"\n  ],\n  \"modules\": [\n    {\n      \"name\": \"alvr\",\n      \"buildsystem\": \"simple\",\n      \"build-commands\": [\n        \"mkdir -p /app/utils/alvr/bin\",\n        \"cd alvr/launcher\",\n        \"cargo xtask prepare-deps --platform linux --no-nvidia\",\n        \"cargo xtask build-launcher --release\",\n        \"mv '/run/build/alvr/build/alvr_launcher_linux/ALVR Launcher' /app/utils/alvr/bin/alvr_launcher\",\n        \" # get read-only errors from following lines - because this is an extension - so leave as comments until someone figures this out\",\n        \" # install -D dashboard.ico /app/share/icons/alvr_launcher.ico\",\n        \" # install -D com.valvesoftware.Steam.Utility.alvr.desktop /app/share/applications/com.valvesoftware.Steam.Utility.alvr.desktop\"\n      ],\n      \"sources\": [ { \"type\": \"dir\", \"path\": \"../../../\" } ]\n    }\n  ]\n}\n"
  },
  {
    "path": "alvr/xtask/flatpak/run_with_adb_keys.sh",
    "content": "#!/bin/sh -e\n\nexport ADB_VENDOR_KEYS=~/.android/adbkey.pub\nflatpak override --user --filesystem=~/.android com.valvesoftware.Steam.Utility.alvr\nflatpak run --env=ADB_VENDOR_KEYS=$ADB_VENDOR_KEYS --env=QT_QPA_PLATFORM=xcb --command=alvr_launcher com.valvesoftware.Steam"
  },
  {
    "path": "alvr/xtask/flatpak/setup_xdg_shortcut.sh",
    "content": "#!/bin/sh -e\n\n# Flatpaks usually export their own shortcut - but ALVR is an extension to steamvr, and we can't put our shortcut in steams folder\n# so for now we need to install the shortcut manually\n\n# Shortcut can be installed for user or systemwide - but system folder needs sudo\n\n# Note that newly installed shortcuts will not appear available until the desktop environment reloads it's settings\n# This can be done by restarting computer, or by logging off and on, or similar command like \"plasmashell --replace\"\n\n# systemwide shortcut\n# sudo cp com.valvesoftware.Steam.Utility.alvr.desktop /var/lib/flatpak/exports/share/applications/ \n\n# users local folder\ncp com.valvesoftware.Steam.Utility.alvr.desktop $HOME/.local/share/flatpak/exports/share/applications/\n\n# copy icon as well\nxdg-icon-resource install --size 256 alvr_icon.png application-alvr-launcher\n"
  },
  {
    "path": "alvr/xtask/licenses_template.hbs",
    "content": "<html>\n\n<head>\n    <style>\n        @media (prefers-color-scheme: dark) {\n            body {\n                background: #333;\n                color: white;\n            }\n            a {\n                color: skyblue;\n            }\n        }\n        .container {\n            font-family: sans-serif;\n            max-width: 800px;\n            margin: 0 auto;\n        }\n        .intro {\n            text-align: center;\n        }\n        .licenses-list {\n            list-style-type: none;\n            margin: 0;\n            padding: 0;\n        }\n        .license-used-by {\n            margin-top: -10px;\n        }\n        .license-text {\n            max-height: 200px;\n            overflow-y: scroll;\n            white-space: pre-wrap;\n        }\n    </style>\n</head>\n\n<body>\n    <main class=\"container\">\n        <div class=\"intro\">\n            <h1>Third Party Licenses</h1>\n            <p>This page lists the licenses of the projects used in ALVR.</p>\n        </div>\n    \n        <h2>Overview of licenses:</h2>\n        <ul class=\"licenses-overview\">\n            {{#each overview}}\n            <li><a href=\"#{{id}}\">{{name}}</a> ({{count}})</li>\n            {{/each}}\n        </ul>\n\n        <h2>All license text:</h2>\n        <ul class=\"licenses-list\">\n            {{#each licenses}}\n            <li class=\"license\">\n                <h3 id=\"{{id}}\">{{name}}</h3>\n                <h4>Used by:</h4>\n                <ul class=\"license-used-by\">\n                    {{#each used_by}}\n                    <li><a href=\"{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}\">{{crate.name}} {{crate.version}}</a></li>\n                    {{/each}}\n                </ul>\n                <pre class=\"license-text\">{{text}}</pre>\n            </li>\n            {{/each}}\n        </ul>\n    </main>\n</body>\n\n</html>\n"
  },
  {
    "path": "alvr/xtask/patches/0001-Add-AV_VAAPI_DRIVER_QUIRK_HEVC_ENCODER_ALIGN_64_16-f.patch",
    "content": "From 3d142fff33196e284b9c9df7d33c2becf609ea60 Mon Sep 17 00:00:00 2001\nFrom: nyanmisaka <nst799610810@gmail.com>\nDate: Thu, 15 Jun 2023 23:01:58 +0800\nSubject: [PATCH] Add AV_VAAPI_DRIVER_QUIRK_HEVC_ENCODER_ALIGN_64_16 for AMD\n VA-API\n\nSigned-off-by: nyanmisaka <nst799610810@gmail.com>\n---\n libavcodec/vaapi_encode_h265.c | 10 ++++++++--\n libavutil/hwcontext_vaapi.c    |  5 +++++\n libavutil/hwcontext_vaapi.h    |  6 ++++++\n 3 files changed, 19 insertions(+), 2 deletions(-)\n\ndiff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c\nindex aa7e532f9a..4ea0cf54be 100644\n--- a/libavcodec/vaapi_encode_h265.c\n+++ b/libavcodec/vaapi_encode_h265.c\n@@ -1236,8 +1236,14 @@ static av_cold int vaapi_encode_h265_get_encoder_caps(AVCodecContext *avctx)\n            \"min CB size %dx%d.\\n\", priv->ctu_size, priv->ctu_size,\n            priv->min_cb_size, priv->min_cb_size);\n \n-    ctx->surface_width  = FFALIGN(avctx->width,  priv->min_cb_size);\n-    ctx->surface_height = FFALIGN(avctx->height, priv->min_cb_size);\n+    if (priv->common.hwctx->driver_quirks &\n+        AV_VAAPI_DRIVER_QUIRK_HEVC_ENCODER_ALIGN_64_16) {\n+        ctx->surface_width  = FFALIGN(avctx->width,  64);\n+        ctx->surface_height = FFALIGN(avctx->height, 16);\n+    } else {\n+        ctx->surface_width  = FFALIGN(avctx->width,  priv->min_cb_size);\n+        ctx->surface_height = FFALIGN(avctx->height, priv->min_cb_size);\n+    }\n \n     ctx->slice_block_width = ctx->slice_block_height = priv->ctu_size;\n \ndiff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c\nindex 6c3a227ddd..337a69344e 100644\n--- a/libavutil/hwcontext_vaapi.c\n+++ b/libavutil/hwcontext_vaapi.c\n@@ -380,6 +380,11 @@ static const struct {\n         \"Splitted-Desktop Systems VDPAU backend for VA-API\",\n         AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,\n     },\n+    {\n+        \"AMD Radeon\",\n+        \"AMD Radeon\",\n+        AV_VAAPI_DRIVER_QUIRK_HEVC_ENCODER_ALIGN_64_16,\n+    }\n };\n \n static int vaapi_device_init(AVHWDeviceContext *hwdev)\ndiff --git a/libavutil/hwcontext_vaapi.h b/libavutil/hwcontext_vaapi.h\nindex 0b2e071cb3..e4ee2de9a4 100644\n--- a/libavutil/hwcontext_vaapi.h\n+++ b/libavutil/hwcontext_vaapi.h\n@@ -58,6 +58,12 @@ enum {\n      * and the results of the vaQuerySurfaceAttributes() call will be faked.\n      */\n     AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES = (1 << 3),\n+\n+    /**\n+     * The driver requires to align the surface with 64x16 for the HEVC encoder,\n+     * and it does not use the min_cb_size like other hardware implementations.\n+     */\n+    AV_VAAPI_DRIVER_QUIRK_HEVC_ENCODER_ALIGN_64_16 = (1 << 4),\n };\n \n /**\n-- \n2.34.1\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-av1-encode-backport.patch",
    "content": "From a105b11a9d1d8be33cd9ba29da41314c1abf7c82 Mon Sep 17 00:00:00 2001\nFrom: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>\nDate: Fri, 1 Jul 2022 11:44:36 +0200\nSubject: [PATCH] avcodec/cbs: Add specialization for\n ff_cbs_(read|write)_unsigned()\n\nThese functions allow not only to read and write unsigned values,\nbut also to check ranges and to emit trace output which can be\nbeautified when processing arrays (indices like \"[i]\" are replaced\nby their actual numbers).\n\nYet lots of callers actually only need something simpler:\nTheir range is only implicitly restricted by the amount\nof bits used and they are not part of arrays, hence don't\nneed this beautification.\n\nThis commit adds specializations for these callers;\nthis is very beneficial size-wise (it reduced the size\nof .text by 23312 bytes here), as a call is now cheaper.\n\nSigned-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>\n---\n libavcodec/cbs.c          | 34 ++++++++++++++++++++++++++++++----\n libavcodec/cbs_av1.c      | 28 +++++++++++++++++++---------\n libavcodec/cbs_h2645.c    | 15 +++++++++++++--\n libavcodec/cbs_internal.h | 11 ++++++++++-\n libavcodec/cbs_mpeg2.c    | 15 +++++++++++++--\n libavcodec/cbs_vp9.c      | 14 ++++++++++++--\n 6 files changed, 97 insertions(+), 20 deletions(-)\n\ndiff --git a/libavcodec/cbs.c b/libavcodec/cbs.c\nindex 13a01bef5133..3ec8285e21ea 100644\n--- a/libavcodec/cbs.c\n+++ b/libavcodec/cbs.c\n@@ -546,10 +546,13 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n            position, name, pad, bits, value);\n }\n \n-int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n-                         int width, const char *name,\n-                         const int *subscripts, uint32_t *write_to,\n-                         uint32_t range_min, uint32_t range_max)\n+static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx,\n+                                              GetBitContext *gbc,\n+                                              int width, const char *name,\n+                                              const int *subscripts,\n+                                              uint32_t *write_to,\n+                                              uint32_t range_min,\n+                                              uint32_t range_max)\n {\n     uint32_t value;\n     int position;\n@@ -589,6 +592,22 @@ int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     return 0;\n }\n \n+int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n+                         int width, const char *name,\n+                         const int *subscripts, uint32_t *write_to,\n+                         uint32_t range_min, uint32_t range_max)\n+{\n+    return cbs_read_unsigned(ctx, gbc, width, name, subscripts,\n+                             write_to, range_min, range_max);\n+}\n+\n+int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n+                                int width, const char *name, uint32_t *write_to)\n+{\n+    return cbs_read_unsigned(ctx, gbc, width, name, NULL,\n+                             write_to, 0, UINT32_MAX);\n+}\n+\n int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                           int width, const char *name,\n                           const int *subscripts, uint32_t value,\n@@ -625,6 +644,13 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     return 0;\n }\n \n+int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n+                                 int width, const char *name, uint32_t value)\n+{\n+    return ff_cbs_write_unsigned(ctx, pbc, width, name, NULL,\n+                                 value, 0, MAX_UINT_BITS(width));\n+}\n+\n int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                        int width, const char *name,\n                        const int *subscripts, int32_t *write_to,\ndiff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c\nindex 8788fee09905..452e022b3680 100644\n--- a/libavcodec/cbs_av1.c\n+++ b/libavcodec/cbs_av1.c\n@@ -412,9 +412,8 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     }\n \n     if (len < max_len) {\n-        err = ff_cbs_read_unsigned(ctx, gbc, range_bits,\n-                                   \"subexp_bits\", NULL, &value,\n-                                   0, MAX_UINT_BITS(range_bits));\n+        err = ff_cbs_read_simple_unsigned(ctx, gbc, range_bits,\n+                                          \"subexp_bits\", &value);\n         if (err < 0)\n             return err;\n \n@@ -476,10 +475,9 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc,\n         return err;\n \n     if (len < max_len) {\n-        err = ff_cbs_write_unsigned(ctx, pbc, range_bits,\n-                                    \"subexp_bits\", NULL,\n-                                    value - range_offset,\n-                                    0, MAX_UINT_BITS(range_bits));\n+        err = ff_cbs_write_simple_unsigned(ctx, pbc, range_bits,\n+                                           \"subexp_bits\",\n+                                           value - range_offset);\n         if (err < 0)\n             return err;\n \n@@ -546,8 +544,6 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n \n #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)\n \n-#define fb(width, name) \\\n-        xf(width, name, current->name, 0, MAX_UINT_BITS(width), 0, )\n #define fc(width, name, range_min, range_max) \\\n         xf(width, name, current->name, range_min, range_max, 0, )\n #define flag(name) fb(1, name)\n@@ -573,6 +569,13 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n #define READWRITE read\n #define RWContext GetBitContext\n \n+#define fb(width, name) do { \\\n+        uint32_t value; \\\n+        CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, \\\n+                                          #name, &value)); \\\n+        current->name = value; \\\n+    } while (0)\n+\n #define xf(width, name, var, range_min, range_max, subs, ...) do { \\\n         uint32_t value; \\\n         CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \\\n@@ -645,6 +648,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n #undef READ\n #undef READWRITE\n #undef RWContext\n+#undef fb\n #undef xf\n #undef xsu\n #undef uvlc\n@@ -661,6 +665,11 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n #define READWRITE write\n #define RWContext PutBitContext\n \n+#define fb(width, name) do { \\\n+        CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \\\n+                                           current->name)); \\\n+    } while (0)\n+\n #define xf(width, name, var, range_min, range_max, subs, ...) do { \\\n         CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \\\n                                     SUBSCRIPTS(subs, __VA_ARGS__), \\\n@@ -723,6 +732,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n #undef WRITE\n #undef READWRITE\n #undef RWContext\n+#undef fb\n #undef xf\n #undef xsu\n #undef uvlc\ndiff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c\nindex 21c8bc76d50b..318c997d9436 100644\n--- a/libavcodec/cbs_h2645.c\n+++ b/libavcodec/cbs_h2645.c\n@@ -264,8 +264,6 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo\n \n #define u(width, name, range_min, range_max) \\\n         xu(width, name, current->name, range_min, range_max, 0, )\n-#define ub(width, name) \\\n-        xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, )\n #define flag(name) ub(1, name)\n #define ue(name, range_min, range_max) \\\n         xue(name, current->name, range_min, range_max, 0, )\n@@ -301,6 +299,12 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo\n #define READWRITE read\n #define RWContext GetBitContext\n \n+#define ub(width, name) do { \\\n+        uint32_t value; \\\n+        CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \\\n+                                          &value)); \\\n+        current->name = value; \\\n+    } while (0)\n #define xu(width, name, var, range_min, range_max, subs, ...) do { \\\n         uint32_t value; \\\n         CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \\\n@@ -379,6 +383,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)\n #undef READ\n #undef READWRITE\n #undef RWContext\n+#undef ub\n #undef xu\n #undef xi\n #undef xue\n@@ -394,6 +399,11 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)\n #define READWRITE write\n #define RWContext PutBitContext\n \n+#define ub(width, name) do { \\\n+        uint32_t value = current->name; \\\n+        CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \\\n+                                           value)); \\\n+    } while (0)\n #define xu(width, name, var, range_min, range_max, subs, ...) do { \\\n         uint32_t value = var; \\\n         CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \\\n@@ -461,6 +471,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)\n #undef WRITE\n #undef READWRITE\n #undef RWContext\n+#undef ub\n #undef xu\n #undef xi\n #undef xue\ndiff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h\nindex b752d6468452..da84697a29f8 100644\n--- a/libavcodec/cbs_internal.h\n+++ b/libavcodec/cbs_internal.h\n@@ -163,18 +163,27 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n \n \n // Helper functions for read/write of common bitstream elements, including\n-// generation of trace output.\n+// generation of trace output. The simple functions are equivalent to\n+// their non-simple counterparts except that their range is unrestricted\n+// (i.e. only limited by the amount of bits used) and they lack\n+// the ability to use subscripts.\n \n int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                          int width, const char *name,\n                          const int *subscripts, uint32_t *write_to,\n                          uint32_t range_min, uint32_t range_max);\n \n+int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc,\n+                                int width, const char *name, uint32_t *write_to);\n+\n int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                           int width, const char *name,\n                           const int *subscripts, uint32_t value,\n                           uint32_t range_min, uint32_t range_max);\n \n+int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n+                                 int width, const char *name, uint32_t value);\n+\n int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                        int width, const char *name,\n                        const int *subscripts, int32_t *write_to,\ndiff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c\nindex 04b0c7f87ddb..37fc28a4e62b 100644\n--- a/libavcodec/cbs_mpeg2.c\n+++ b/libavcodec/cbs_mpeg2.c\n@@ -40,8 +40,6 @@\n \n #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)\n \n-#define ui(width, name) \\\n-        xui(width, name, current->name, 0, MAX_UINT_BITS(width), 0, )\n #define uir(width, name) \\\n         xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0, )\n #define uis(width, name, subs, ...) \\\n@@ -65,6 +63,12 @@\n #define READWRITE read\n #define RWContext GetBitContext\n \n+#define ui(width, name) do { \\\n+        uint32_t value; \\\n+        CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \\\n+                                          &value)); \\\n+        current->name = value; \\\n+    } while (0)\n #define xuia(width, string, var, range_min, range_max, subs, ...) do { \\\n         uint32_t value; \\\n         CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \\\n@@ -95,6 +99,7 @@\n #undef READ\n #undef READWRITE\n #undef RWContext\n+#undef ui\n #undef xuia\n #undef xsi\n #undef nextbits\n@@ -105,6 +110,11 @@\n #define READWRITE write\n #define RWContext PutBitContext\n \n+#define ui(width, name) do { \\\n+        CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \\\n+                                           current->name)); \\\n+    } while (0)\n+\n #define xuia(width, string, var, range_min, range_max, subs, ...) do { \\\n         CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \\\n                                     SUBSCRIPTS(subs, __VA_ARGS__), \\\n@@ -134,6 +144,7 @@\n #undef WRITE\n #undef READWRITE\n #undef RWContext\n+#undef ui\n #undef xuia\n #undef xsi\n #undef nextbits\ndiff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c\nindex 184fdcade6f8..b0d5bd8763fd 100644\n--- a/libavcodec/cbs_vp9.c\n+++ b/libavcodec/cbs_vp9.c\n@@ -251,8 +251,6 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n \n #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)\n \n-#define f(width, name) \\\n-        xf(width, name, current->name, 0, )\n #define s(width, name) \\\n         xs(width, name, current->name, 0, )\n #define fs(width, name, subs, ...) \\\n@@ -264,6 +262,12 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n #define READWRITE read\n #define RWContext GetBitContext\n \n+#define f(width, name) do { \\\n+        uint32_t value; \\\n+        CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \\\n+                                          &value)); \\\n+        current->name = value; \\\n+    } while (0)\n #define xf(width, name, var, subs, ...) do { \\\n         uint32_t value; \\\n         CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \\\n@@ -329,6 +333,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n #undef READ\n #undef READWRITE\n #undef RWContext\n+#undef f\n #undef xf\n #undef xs\n #undef increment\n@@ -344,6 +349,10 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n #define READWRITE write\n #define RWContext PutBitContext\n \n+#define f(width, name) do { \\\n+        CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \\\n+                                           current->name)); \\\n+    } while (0)\n #define xf(width, name, var, subs, ...) do { \\\n         CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \\\n                                     SUBSCRIPTS(subs, __VA_ARGS__), \\\n@@ -396,6 +405,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n #undef WRITE\n #undef READWRITE\n #undef RWContext\n+#undef f\n #undef xf\n #undef xs\n #undef increment\nFrom 95b5c8172968e942704cb4eaceddf8730d9e501c Mon Sep 17 00:00:00 2001\nFrom: James Almer <jamrial@gmail.com>\nDate: Thu, 21 Sep 2023 22:02:52 -0300\nSubject: [PATCH] avcodec/extract_extradata: use size_t as parameter type in\n val_in_array()\n\nIt only gets passed the return value of FF_ARRAY_ELEMS(), which is a size_t.\n\nSigned-off-by: James Almer <jamrial@gmail.com>\n---\n libavcodec/extract_extradata_bsf.c | 7 +++----\n 1 file changed, 3 insertions(+), 4 deletions(-)\n\ndiff --git a/libavcodec/extract_extradata_bsf.c b/libavcodec/extract_extradata_bsf.c\nindex efc843736b5a..baa629295fba 100644\n--- a/libavcodec/extract_extradata_bsf.c\n+++ b/libavcodec/extract_extradata_bsf.c\n@@ -49,10 +49,9 @@ typedef struct ExtractExtradataContext {\n     int remove;\n } ExtractExtradataContext;\n \n-static int val_in_array(const int *arr, int len, int val)\n+static int val_in_array(const int *arr, size_t len, int val)\n {\n-    int i;\n-    for (i = 0; i < len; i++)\n+    for (size_t i = 0; i < len; i++)\n         if (arr[i] == val)\n             return 1;\n     return 0;\n@@ -177,7 +176,7 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,\n \n     int extradata_size = 0, filtered_size = 0;\n     const int *extradata_nal_types;\n-    int nb_extradata_nal_types;\n+    size_t nb_extradata_nal_types;\n     int i, has_sps = 0, has_vps = 0, ret = 0;\n \n     if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) {\nFrom abe16daea1b72323e3544cb6ec12bec010b6ba54 Mon Sep 17 00:00:00 2001\nFrom: Mark Thompson <sw@jkqxz.net>\nDate: Mon, 11 Sep 2023 15:52:26 +0800\nSubject: [PATCH] cbs: Make tracing more general\n\nTurn tracing into callbacks for each syntax element, with default\ncallbacks to match current trace_headers behaviour for debug.  Move\nthe construction of bit strings into the trace callback, which\nsimplifies all of the read and write functions.\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/cbs.c               | 121 +++++++++----------\n libavcodec/cbs.h               |  88 +++++++++++++-\n libavcodec/cbs_av1.c           | 206 +++++++++------------------------\n libavcodec/cbs_bsf.c           |   5 +\n libavcodec/cbs_h2645.c         | 134 +++++++++------------\n libavcodec/cbs_internal.h      |  85 +++++++++++++-\n libavcodec/cbs_vp9.c           | 108 +++++------------\n libavcodec/trace_headers_bsf.c |   2 +\n 8 files changed, 373 insertions(+), 376 deletions(-)\n\ndiff --git a/libavcodec/cbs.c b/libavcodec/cbs.c\nindex 3ec8285e21ea..daf7f66427f4 100644\n--- a/libavcodec/cbs.c\n+++ b/libavcodec/cbs.c\n@@ -117,8 +117,9 @@ av_cold int ff_cbs_init(CodedBitstreamContext **ctx_ptr,\n \n     ctx->decompose_unit_types = NULL;\n \n-    ctx->trace_enable = 0;\n-    ctx->trace_level  = AV_LOG_TRACE;\n+    ctx->trace_enable  = 0;\n+    ctx->trace_level   = AV_LOG_TRACE;\n+    ctx->trace_context = ctx;\n \n     *ctx_ptr = ctx;\n     return 0;\n@@ -496,19 +497,27 @@ void ff_cbs_trace_header(CodedBitstreamContext *ctx,\n     av_log(ctx->log_ctx, ctx->trace_level, \"%s\\n\", name);\n }\n \n-void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n-                                 const char *str, const int *subscripts,\n-                                 const char *bits, int64_t value)\n+void ff_cbs_trace_read_log(void *trace_context,\n+                           GetBitContext *gbc, int length,\n+                           const char *str, const int *subscripts,\n+                           int64_t value)\n {\n+    CodedBitstreamContext *ctx = trace_context;\n     char name[256];\n+    char bits[256];\n     size_t name_len, bits_len;\n     int pad, subs, i, j, k, n;\n-\n-    if (!ctx->trace_enable)\n-        return;\n+    int position;\n \n     av_assert0(value >= INT_MIN && value <= UINT32_MAX);\n \n+    position = get_bits_count(gbc);\n+\n+    av_assert0(length < 256);\n+    for (i = 0; i < length; i++)\n+        bits[i] = get_bits1(gbc) ? '1' : '0';\n+    bits[length] = 0;\n+\n     subs = subscripts ? subscripts[0] : 0;\n     n = 0;\n     for (i = j = 0; str[i];) {\n@@ -535,7 +544,7 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n     av_assert0(n == subs);\n \n     name_len = strlen(name);\n-    bits_len = strlen(bits);\n+    bits_len = length;\n \n     if (name_len + bits_len > 60)\n         pad = bits_len + 2;\n@@ -546,6 +555,36 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n            position, name, pad, bits, value);\n }\n \n+void ff_cbs_trace_write_log(void *trace_context,\n+                            PutBitContext *pbc, int length,\n+                            const char *str, const int *subscripts,\n+                            int64_t value)\n+{\n+    CodedBitstreamContext *ctx = trace_context;\n+\n+    // Ensure that the syntax element is written to the output buffer,\n+    // make a GetBitContext pointed at the start position, then call the\n+    // read log function which can read the bits back to log them.\n+\n+    GetBitContext gbc;\n+    int position;\n+\n+    if (length > 0) {\n+        PutBitContext flush;\n+        flush = *pbc;\n+        flush_put_bits(&flush);\n+    }\n+\n+    position = put_bits_count(pbc);\n+    av_assert0(position >= length);\n+\n+    init_get_bits(&gbc, pbc->buf, position);\n+\n+    skip_bits_long(&gbc, position - length);\n+\n+    ff_cbs_trace_read_log(ctx, &gbc, length, str, subscripts, value);\n+}\n+\n static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx,\n                                               GetBitContext *gbc,\n                                               int width, const char *name,\n@@ -555,7 +594,8 @@ static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx,\n                                               uint32_t range_max)\n {\n     uint32_t value;\n-    int position;\n+\n+    CBS_TRACE_READ_START();\n \n     av_assert0(width > 0 && width <= 32);\n \n@@ -565,21 +605,9 @@ static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx,\n         return AVERROR_INVALIDDATA;\n     }\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n-\n     value = get_bits_long(gbc, width);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n-    }\n+    CBS_TRACE_READ_END();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -613,6 +641,8 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                           const int *subscripts, uint32_t value,\n                           uint32_t range_min, uint32_t range_max)\n {\n+    CBS_TRACE_WRITE_START();\n+\n     av_assert0(width > 0 && width <= 32);\n \n     if (value < range_min || value > range_max) {\n@@ -625,22 +655,13 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     if (put_bits_left(pbc) < width)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = value >> (width - i - 1) & 1 ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     if (width < 32)\n         put_bits(pbc, width, value);\n     else\n         put_bits32(pbc, value);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \n@@ -657,7 +678,8 @@ int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                        int32_t range_min, int32_t range_max)\n {\n     int32_t value;\n-    int position;\n+\n+    CBS_TRACE_READ_START();\n \n     av_assert0(width > 0 && width <= 32);\n \n@@ -667,21 +689,9 @@ int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc,\n         return AVERROR_INVALIDDATA;\n     }\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n-\n     value = get_sbits_long(gbc, width);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = value & (1U << (width - i - 1)) ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n-    }\n+    CBS_TRACE_READ_END();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -699,6 +709,8 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                         const int *subscripts, int32_t value,\n                         int32_t range_min, int32_t range_max)\n {\n+    CBS_TRACE_WRITE_START();\n+\n     av_assert0(width > 0 && width <= 32);\n \n     if (value < range_min || value > range_max) {\n@@ -711,22 +723,13 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     if (put_bits_left(pbc) < width)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = value & (1U << (width - i - 1)) ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     if (width < 32)\n         put_sbits(pbc, width, value);\n     else\n         put_bits32(pbc, value);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \ndiff --git a/libavcodec/cbs.h b/libavcodec/cbs.h\nindex b4131db5fe27..ffb2797761bb 100644\n--- a/libavcodec/cbs.h\n+++ b/libavcodec/cbs.h\n@@ -168,6 +168,51 @@ typedef struct CodedBitstreamFragment {\n     CodedBitstreamUnit *units;\n } CodedBitstreamFragment;\n \n+\n+struct CodedBitstreamContext;\n+struct GetBitContext;\n+struct PutBitContext;\n+\n+/**\n+ * Callback type for read tracing.\n+ *\n+ * @param ctx         User-set trace context.\n+ * @param gbc         A GetBitContext set at the start of the syntax\n+ *                    element.  This is a copy, the callee does not\n+ *                    need to preserve it.\n+ * @param length      Length in bits of the syntax element.\n+ * @param name        String name of the syntax elements.\n+ * @param subscripts  If the syntax element is an array, a pointer to\n+ *                    an array of subscripts into the array.\n+ * @param value       Parsed value of the syntax element.\n+ */\n+typedef void (*CBSTraceReadCallback)(void *trace_context,\n+                                     struct GetBitContext *gbc,\n+                                     int start_position,\n+                                     const char *name,\n+                                     const int *subscripts,\n+                                     int64_t value);\n+\n+/**\n+ * Callback type for write tracing.\n+ *\n+ * @param ctx         User-set trace context.\n+ * @param pbc         A PutBitContext set at the end of the syntax\n+ *                    element.  The user must not modify this, but may\n+ *                    inspect it to determine state.\n+ * @param length      Length in bits of the syntax element.\n+ * @param name        String name of the syntax elements.\n+ * @param subscripts  If the syntax element is an array, a pointer to\n+ *                    an array of subscripts into the array.\n+ * @param value       Written value of the syntax element.\n+ */\n+typedef void (*CBSTraceWriteCallback)(void *trace_context,\n+                                      struct PutBitContext *pbc,\n+                                      int start_position,\n+                                      const char *name,\n+                                      const int *subscripts,\n+                                      int64_t value);\n+\n /**\n  * Context structure for coded bitstream operations.\n  */\n@@ -211,11 +256,29 @@ typedef struct CodedBitstreamContext {\n      */\n     int trace_enable;\n     /**\n-     * Log level to use for trace output.\n+     * Log level to use for default trace output.\n      *\n      * From AV_LOG_*; defaults to AV_LOG_TRACE.\n      */\n     int trace_level;\n+    /**\n+     * User context pointer to pass to trace callbacks.\n+     */\n+    void *trace_context;\n+    /**\n+     * Callback for read tracing.\n+     *\n+     * If tracing is enabled then this is called once for each syntax\n+     * element parsed.\n+     */\n+    CBSTraceReadCallback  trace_read_callback;\n+    /**\n+     * Callback for write tracing.\n+     *\n+     * If tracing is enabled then this is called once for each syntax\n+     * element written.\n+     */\n+    CBSTraceWriteCallback trace_write_callback;\n \n     /**\n      * Write buffer. Used as intermediate buffer when writing units.\n@@ -450,4 +513,27 @@ void ff_cbs_discard_units(CodedBitstreamContext *ctx,\n                           enum AVDiscard skip,\n                           int flags);\n \n+\n+/**\n+ * Helper function for read tracing which formats the syntax element\n+ * and logs the result.\n+ *\n+ * Trace context should be set to the CodedBitstreamContext.\n+ */\n+void ff_cbs_trace_read_log(void *trace_context,\n+                           struct GetBitContext *gbc, int length,\n+                           const char *str, const int *subscripts,\n+                           int64_t value);\n+\n+/**\n+ * Helper function for write tracing which formats the syntax element\n+ * and logs the result.\n+ *\n+ * Trace context should be set to the CodedBitstreamContext.\n+ */\n+void ff_cbs_trace_write_log(void *trace_context,\n+                            struct PutBitContext *pbc, int length,\n+                            const char *str, const int *subscripts,\n+                            int64_t value);\n+\n #endif /* AVCODEC_CBS_H */\ndiff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c\nindex 7fb5bd8b42ea..6c478603f173 100644\n--- a/libavcodec/cbs_av1.c\n+++ b/libavcodec/cbs_av1.c\n@@ -31,10 +31,8 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                              uint32_t range_min, uint32_t range_max)\n {\n     uint32_t zeroes, bits_value, value;\n-    int position;\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n     zeroes = 0;\n     while (1) {\n@@ -50,6 +48,9 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     }\n \n     if (zeroes >= 32) {\n+        // Note that the spec allows an arbitrarily large number of\n+        // zero bits followed by a one bit in this case, but the\n+        // libaom implementation does not support it.\n         value = MAX_UINT_BITS(32);\n     } else {\n         if (get_bits_left(gbc) < zeroes) {\n@@ -62,36 +63,7 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc,\n         value = bits_value + (UINT32_C(1) << zeroes) - 1;\n     }\n \n-    if (ctx->trace_enable) {\n-        char bits[65];\n-        int i, j, k;\n-\n-        if (zeroes >= 32) {\n-            while (zeroes > 32) {\n-                k = FFMIN(zeroes - 32, 32);\n-                for (i = 0; i < k; i++)\n-                    bits[i] = '0';\n-                bits[i] = 0;\n-                ff_cbs_trace_syntax_element(ctx, position, name,\n-                                            NULL, bits, 0);\n-                zeroes -= k;\n-                position += k;\n-            }\n-        }\n-\n-        for (i = 0; i < zeroes; i++)\n-            bits[i] = '0';\n-        bits[i++] = '1';\n-\n-        if (zeroes < 32) {\n-            for (j = 0; j < zeroes; j++)\n-                bits[i++] = (bits_value >> (zeroes - j - 1) & 1) ? '1' : '0';\n-        }\n-\n-        bits[i] = 0;\n-        ff_cbs_trace_syntax_element(ctx, position, name,\n-                                    NULL, bits, value);\n-    }\n+    CBS_TRACE_READ_END_NO_SUBSCRIPTS();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -109,7 +81,9 @@ static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                               uint32_t range_min, uint32_t range_max)\n {\n     uint32_t v;\n-    int position, zeroes;\n+    int zeroes;\n+\n+    CBS_TRACE_WRITE_START();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -118,28 +92,17 @@ static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc,\n         return AVERROR_INVALIDDATA;\n     }\n \n-    if (ctx->trace_enable)\n-        position = put_bits_count(pbc);\n-\n     zeroes = av_log2(value + 1);\n     v = value - (1U << zeroes) + 1;\n+\n+    if (put_bits_left(pbc) < 2 * zeroes + 1)\n+        return AVERROR(ENOSPC);\n+\n     put_bits(pbc, zeroes, 0);\n     put_bits(pbc, 1, 1);\n     put_bits(pbc, zeroes, v);\n \n-    if (ctx->trace_enable) {\n-        char bits[65];\n-        int i, j;\n-        i = 0;\n-        for (j = 0; j < zeroes; j++)\n-            bits[i++] = '0';\n-        bits[i++] = '1';\n-        for (j = 0; j < zeroes; j++)\n-            bits[i++] = (v >> (zeroes - j - 1) & 1) ? '1' : '0';\n-        bits[i++] = 0;\n-        ff_cbs_trace_syntax_element(ctx, position, name, NULL,\n-                                    bits, value);\n-    }\n+    CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();\n \n     return 0;\n }\n@@ -148,20 +111,19 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                                const char *name, uint64_t *write_to)\n {\n     uint64_t value;\n-    int position, err, i;\n+    uint32_t byte;\n+    int i;\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n     value = 0;\n     for (i = 0; i < 8; i++) {\n-        int subscript[2] = { 1, i };\n-        uint32_t byte;\n-        err = ff_cbs_read_unsigned(ctx, gbc, 8, \"leb128_byte[i]\", subscript,\n-                                   &byte, 0x00, 0xff);\n-        if (err < 0)\n-            return err;\n-\n+        if (get_bits_left(gbc) < 8) {\n+            av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid leb128 at \"\n+                   \"%s: bitstream ended.\\n\", name);\n+            return AVERROR_INVALIDDATA;\n+        }\n+        byte = get_bits(gbc, 8);\n         value |= (uint64_t)(byte & 0x7f) << (i * 7);\n         if (!(byte & 0x80))\n             break;\n@@ -170,8 +132,7 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     if (value > UINT32_MAX)\n         return AVERROR_INVALIDDATA;\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position, name, NULL, \"\", value);\n+    CBS_TRACE_READ_END_NO_SUBSCRIPTS();\n \n     *write_to = value;\n     return 0;\n@@ -180,29 +141,25 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc,\n static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                                 const char *name, uint64_t value)\n {\n-    int position, err, len, i;\n+    int len, i;\n     uint8_t byte;\n \n-    len = (av_log2(value) + 7) / 7;\n+    CBS_TRACE_WRITE_START();\n \n-    if (ctx->trace_enable)\n-        position = put_bits_count(pbc);\n+    len = (av_log2(value) + 7) / 7;\n \n     for (i = 0; i < len; i++) {\n-        int subscript[2] = { 1, i };\n+        if (put_bits_left(pbc) < 8)\n+            return AVERROR(ENOSPC);\n \n         byte = value >> (7 * i) & 0x7f;\n         if (i < len - 1)\n             byte |= 0x80;\n \n-        err = ff_cbs_write_unsigned(ctx, pbc, 8, \"leb128_byte[i]\", subscript,\n-                                    byte, 0x00, 0xff);\n-        if (err < 0)\n-            return err;\n+        put_bits(pbc, 8, byte);\n     }\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position, name, NULL, \"\", value);\n+    CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();\n \n     return 0;\n }\n@@ -212,12 +169,11 @@ static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                            const int *subscripts, uint32_t *write_to)\n {\n     uint32_t m, v, extra_bit, value;\n-    int position, w;\n+    int w;\n \n-    av_assert0(n > 0);\n+    CBS_TRACE_READ_START();\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    av_assert0(n > 0);\n \n     w = av_log2(n) + 1;\n     m = (1 << w) - n;\n@@ -240,18 +196,7 @@ static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc,\n         value = (v << 1) - m + extra_bit;\n     }\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < w - 1; i++)\n-            bits[i] = (v >> i & 1) ? '1' : '0';\n-        if (v >= m)\n-            bits[i++] = extra_bit ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position,\n-                                    name, subscripts, bits, value);\n-    }\n+    CBS_TRACE_READ_END();\n \n     *write_to = value;\n     return 0;\n@@ -262,7 +207,8 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                             const int *subscripts, uint32_t value)\n {\n     uint32_t w, m, v, extra_bit;\n-    int position;\n+\n+    CBS_TRACE_WRITE_START();\n \n     if (value > n) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -271,9 +217,6 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc,\n         return AVERROR_INVALIDDATA;\n     }\n \n-    if (ctx->trace_enable)\n-        position = put_bits_count(pbc);\n-\n     w = av_log2(n) + 1;\n     m = (1 << w) - n;\n \n@@ -290,18 +233,7 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc,\n         put_bits(pbc, 1, extra_bit);\n     }\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < w - 1; i++)\n-            bits[i] = (v >> i & 1) ? '1' : '0';\n-        if (value >= m)\n-            bits[i++] = extra_bit ? '1' : '0';\n-        bits[i] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position,\n-                                    name, subscripts, bits, value);\n-    }\n+    CBS_TRACE_WRITE_END();\n \n     return 0;\n }\n@@ -311,33 +243,24 @@ static int cbs_av1_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc\n                                   const char *name, uint32_t *write_to)\n {\n     uint32_t value;\n-    int position, i;\n-    char bits[33];\n \n-    av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1);\n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n-    for (i = 0, value = range_min; value < range_max;) {\n+    av_assert0(range_min <= range_max && range_max - range_min < 32);\n+\n+    for (value = range_min; value < range_max;) {\n         if (get_bits_left(gbc) < 1) {\n             av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid increment value at \"\n                    \"%s: bitstream ended.\\n\", name);\n             return AVERROR_INVALIDDATA;\n         }\n-        if (get_bits1(gbc)) {\n-            bits[i++] = '1';\n+        if (get_bits1(gbc))\n             ++value;\n-        } else {\n-            bits[i++] = '0';\n+        else\n             break;\n-        }\n     }\n \n-    if (ctx->trace_enable) {\n-        bits[i] = 0;\n-        ff_cbs_trace_syntax_element(ctx, position,\n-                                    name, NULL, bits, value);\n-    }\n+    CBS_TRACE_READ_END_NO_SUBSCRIPTS();\n \n     *write_to = value;\n     return 0;\n@@ -349,6 +272,8 @@ static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb\n {\n     int len;\n \n+    CBS_TRACE_WRITE_START();\n+\n     av_assert0(range_min <= range_max && range_max - range_min < 32);\n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -364,23 +289,11 @@ static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb\n     if (put_bits_left(pbc) < len)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < len; i++) {\n-            if (range_min + i == value)\n-                bits[i] = '0';\n-            else\n-                bits[i] = '1';\n-        }\n-        bits[i] = 0;\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, NULL, bits, value);\n-    }\n-\n     if (len > 0)\n         put_bits(pbc, len, (1 << len) - 1 - (value != range_max));\n \n+    CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();\n+\n     return 0;\n }\n \n@@ -388,12 +301,10 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                                uint32_t range_max, const char *name,\n                                const int *subscripts, uint32_t *write_to)\n {\n-    uint32_t value;\n-    int position, err;\n-    uint32_t max_len, len, range_offset, range_bits;\n+    uint32_t value, max_len, len, range_offset, range_bits;\n+    int err;\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n     av_assert0(range_max > 0);\n     max_len = av_log2(range_max - 1) - 3;\n@@ -425,9 +336,7 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     }\n     value += range_offset;\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position,\n-                                    name, subscripts, \"\", value);\n+    CBS_TRACE_READ_END_VALUE_ONLY();\n \n     *write_to = value;\n     return err;\n@@ -437,9 +346,11 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc,\n                                 uint32_t range_max, const char *name,\n                                 const int *subscripts, uint32_t value)\n {\n-    int position, err;\n+    int err;\n     uint32_t max_len, len, range_offset, range_bits;\n \n+    CBS_TRACE_WRITE_START();\n+\n     if (value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n                \"%\"PRIu32\", but must be in [0,%\"PRIu32\"].\\n\",\n@@ -447,9 +358,6 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc,\n         return AVERROR_INVALIDDATA;\n     }\n \n-    if (ctx->trace_enable)\n-        position = put_bits_count(pbc);\n-\n     av_assert0(range_max > 0);\n     max_len = av_log2(range_max - 1) - 3;\n \n@@ -489,9 +397,7 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc,\n             return err;\n     }\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position,\n-                                    name, subscripts, \"\", value);\n+    CBS_TRACE_WRITE_END_VALUE_ONLY();\n \n     return err;\n }\ndiff --git a/libavcodec/cbs_bsf.c b/libavcodec/cbs_bsf.c\nindex 069f6e99186a..b25285483bc7 100644\n--- a/libavcodec/cbs_bsf.c\n+++ b/libavcodec/cbs_bsf.c\n@@ -123,6 +123,11 @@ int ff_cbs_bsf_generic_init(AVBSFContext *bsf, const CBSBSFType *type)\n     if (err < 0)\n         return err;\n \n+    ctx->output->trace_enable = 1;\n+    ctx->output->trace_level  = AV_LOG_TRACE;\n+    ctx->output->trace_context = ctx->output;\n+    ctx->output->trace_write_callback = ff_cbs_trace_write_log;\n+\n     if (bsf->par_in->extradata) {\n         err = ff_cbs_read_extradata(ctx->input, frag, bsf->par_in);\n         if (err < 0) {\ndiff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c\nindex 318c997d9436..0a1c8ea42660 100644\n--- a/libavcodec/cbs_h2645.c\n+++ b/libavcodec/cbs_h2645.c\n@@ -36,41 +36,38 @@ static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                               uint32_t *write_to,\n                               uint32_t range_min, uint32_t range_max)\n {\n-    uint32_t value;\n-    int position, i, j;\n-    unsigned int k;\n-    char bits[65];\n+    uint32_t leading_bits, value;\n+    int max_length, leading_zeroes;\n \n-    position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n-    for (i = 0; i < 32; i++) {\n-        if (get_bits_left(gbc) < i + 1) {\n+    max_length = FFMIN(get_bits_left(gbc), 32);\n+\n+    leading_bits = show_bits_long(gbc, max_length);\n+    if (leading_bits == 0) {\n+        if (max_length >= 32) {\n+            av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid ue-golomb code at \"\n+                   \"%s: more than 31 zeroes.\\n\", name);\n+            return AVERROR_INVALIDDATA;\n+        } else {\n             av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid ue-golomb code at \"\n                    \"%s: bitstream ended.\\n\", name);\n             return AVERROR_INVALIDDATA;\n         }\n-        k = get_bits1(gbc);\n-        bits[i] = k ? '1' : '0';\n-        if (k)\n-            break;\n     }\n-    if (i >= 32) {\n+\n+    leading_zeroes = max_length - 1 - av_log2(leading_bits);\n+    skip_bits_long(gbc, leading_zeroes);\n+\n+    if (get_bits_left(gbc) < leading_zeroes + 1) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid ue-golomb code at \"\n-               \"%s: more than 31 zeroes.\\n\", name);\n+               \"%s: bitstream ended.\\n\", name);\n         return AVERROR_INVALIDDATA;\n     }\n-    value = 1;\n-    for (j = 0; j < i; j++) {\n-        k = get_bits1(gbc);\n-        bits[i + j + 1] = k ? '1' : '0';\n-        value = value << 1 | k;\n-    }\n-    bits[i + j + 1] = 0;\n-    --value;\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n+    value = get_bits_long(gbc, leading_zeroes + 1) - 1;\n+\n+    CBS_TRACE_READ_END();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -88,45 +85,44 @@ static int cbs_read_se_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                               int32_t *write_to,\n                               int32_t range_min, int32_t range_max)\n {\n+    uint32_t leading_bits, unsigned_value;\n+    int max_length, leading_zeroes;\n     int32_t value;\n-    int position, i, j;\n-    unsigned int k;\n-    uint32_t v;\n-    char bits[65];\n \n-    position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n-    for (i = 0; i < 32; i++) {\n-        if (get_bits_left(gbc) < i + 1) {\n+    max_length = FFMIN(get_bits_left(gbc), 32);\n+\n+    leading_bits = show_bits_long(gbc, max_length);\n+    if (leading_bits == 0) {\n+        if (max_length >= 32) {\n+            av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid se-golomb code at \"\n+                   \"%s: more than 31 zeroes.\\n\", name);\n+            return AVERROR_INVALIDDATA;\n+        } else {\n             av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid se-golomb code at \"\n                    \"%s: bitstream ended.\\n\", name);\n             return AVERROR_INVALIDDATA;\n         }\n-        k = get_bits1(gbc);\n-        bits[i] = k ? '1' : '0';\n-        if (k)\n-            break;\n     }\n-    if (i >= 32) {\n+\n+    leading_zeroes = max_length - 1 - av_log2(leading_bits);\n+    skip_bits_long(gbc, leading_zeroes);\n+\n+    if (get_bits_left(gbc) < leading_zeroes + 1) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid se-golomb code at \"\n-               \"%s: more than 31 zeroes.\\n\", name);\n+               \"%s: bitstream ended.\\n\", name);\n         return AVERROR_INVALIDDATA;\n     }\n-    v = 1;\n-    for (j = 0; j < i; j++) {\n-        k = get_bits1(gbc);\n-        bits[i + j + 1] = k ? '1' : '0';\n-        v = v << 1 | k;\n-    }\n-    bits[i + j + 1] = 0;\n-    if (v & 1)\n-        value = -(int32_t)(v / 2);\n+\n+    unsigned_value = get_bits_long(gbc, leading_zeroes + 1);\n+\n+    if (unsigned_value & 1)\n+        value = -(int32_t)(unsigned_value / 2);\n     else\n-        value = v / 2;\n+        value = unsigned_value / 2;\n \n-    if (ctx->trace_enable)\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n+    CBS_TRACE_READ_END();\n \n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -146,6 +142,8 @@ static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,\n {\n     int len;\n \n+    CBS_TRACE_WRITE_START();\n+\n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n                \"%\"PRIu32\", but must be in [%\"PRIu32\",%\"PRIu32\"].\\n\",\n@@ -158,27 +156,14 @@ static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     if (put_bits_left(pbc) < 2 * len + 1)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[65];\n-        int i;\n-\n-        for (i = 0; i < len; i++)\n-            bits[i] = '0';\n-        bits[len] = '1';\n-        for (i = 0; i < len; i++)\n-            bits[len + i + 1] = (value + 1) >> (len - i - 1) & 1 ? '1' : '0';\n-        bits[len + len + 1] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     put_bits(pbc, len, 0);\n     if (len + 1 < 32)\n         put_bits(pbc, len + 1, value + 1);\n     else\n         put_bits32(pbc, value + 1);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \n@@ -190,6 +175,8 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     int len;\n     uint32_t uvalue;\n \n+    CBS_TRACE_WRITE_START();\n+\n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n                \"%\"PRId32\", but must be in [%\"PRId32\",%\"PRId32\"].\\n\",\n@@ -209,27 +196,14 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     if (put_bits_left(pbc) < 2 * len + 1)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[65];\n-        int i;\n-\n-        for (i = 0; i < len; i++)\n-            bits[i] = '0';\n-        bits[len] = '1';\n-        for (i = 0; i < len; i++)\n-            bits[len + i + 1] = (uvalue + 1) >> (len - i - 1) & 1 ? '1' : '0';\n-        bits[len + len + 1] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     put_bits(pbc, len, 0);\n     if (len + 1 < 32)\n         put_bits(pbc, len + 1, uvalue + 1);\n     else\n         put_bits32(pbc, uvalue + 1);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \ndiff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h\nindex da84697a29f8..285deb40d516 100644\n--- a/libavcodec/cbs_internal.h\n+++ b/libavcodec/cbs_internal.h\n@@ -157,10 +157,6 @@ typedef struct CodedBitstreamType {\n void ff_cbs_trace_header(CodedBitstreamContext *ctx,\n                          const char *name);\n \n-void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position,\n-                                 const char *name, const int *subscripts,\n-                                 const char *bitstring, int64_t value);\n-\n \n // Helper functions for read/write of common bitstream elements, including\n // generation of trace output. The simple functions are equivalent to\n@@ -206,6 +202,87 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc,\n // range_min in the above functions.\n #define MIN_INT_BITS(length) (-(INT64_C(1) << ((length) - 1)))\n \n+\n+// Start of a syntax element during read tracing.\n+#define CBS_TRACE_READ_START() \\\n+    GetBitContext trace_start; \\\n+    do { \\\n+        if (ctx->trace_enable) \\\n+            trace_start = *gbc; \\\n+    } while (0)\n+\n+// End of a syntax element for tracing, make callback.\n+#define CBS_TRACE_READ_END() \\\n+    do { \\\n+        if (ctx->trace_enable) { \\\n+            int start_position = get_bits_count(&trace_start); \\\n+            int end_position   = get_bits_count(gbc); \\\n+            av_assert0(start_position <= end_position); \\\n+            ctx->trace_read_callback(ctx->trace_context, &trace_start, \\\n+                                     end_position - start_position, \\\n+                                     name, subscripts, value); \\\n+        } \\\n+    } while (0)\n+\n+// End of a syntax element with no subscript entries.\n+#define CBS_TRACE_READ_END_NO_SUBSCRIPTS() \\\n+    do { \\\n+        const int *subscripts = NULL; \\\n+        CBS_TRACE_READ_END(); \\\n+    } while (0)\n+\n+// End of a syntax element which is made up of subelements which\n+// are aleady traced, so we are only showing the value.\n+#define CBS_TRACE_READ_END_VALUE_ONLY() \\\n+    do { \\\n+        if (ctx->trace_enable) { \\\n+            ctx->trace_read_callback(ctx->trace_context, &trace_start, 0, \\\n+                                     name, subscripts, value); \\\n+        } \\\n+    } while (0)\n+\n+// Start of a syntax element during write tracing.\n+#define CBS_TRACE_WRITE_START() \\\n+    int start_position; \\\n+    do { \\\n+        if (ctx->trace_enable) \\\n+            start_position = put_bits_count(pbc);; \\\n+    } while (0)\n+\n+// End of a syntax element for tracing, make callback.\n+#define CBS_TRACE_WRITE_END() \\\n+    do { \\\n+        if (ctx->trace_enable) { \\\n+            int end_position   = put_bits_count(pbc); \\\n+            av_assert0(start_position <= end_position); \\\n+            ctx->trace_write_callback(ctx->trace_context, pbc, \\\n+                                      end_position - start_position, \\\n+                                      name, subscripts, value); \\\n+        } \\\n+    } while (0)\n+\n+// End of a syntax element with no subscript entries.\n+#define CBS_TRACE_WRITE_END_NO_SUBSCRIPTS() \\\n+    do { \\\n+        const int *subscripts = NULL; \\\n+        CBS_TRACE_WRITE_END(); \\\n+    } while (0)\n+\n+// End of a syntax element which is made up of subelements which are\n+// aleady traced, so we are only showing the value.  This forges a\n+// PutBitContext to point to the position of the start of the syntax\n+// element, but the other state doesn't matter because length is zero.\n+#define CBS_TRACE_WRITE_END_VALUE_ONLY() \\\n+    do { \\\n+        if (ctx->trace_enable) { \\\n+            PutBitContext tmp; \\\n+            init_put_bits(&tmp, pbc->buf, start_position); \\\n+            skip_put_bits(&tmp, start_position); \\\n+            ctx->trace_write_callback(ctx->trace_context, &tmp, 0, \\\n+                                      name, subscripts, value); \\\n+        } \\\n+    } while (0)\n+\n #define TYPE_LIST(...) { __VA_ARGS__ }\n #define CBS_UNIT_TYPE_POD(type_, structure) { \\\n         .nb_unit_types  = 1, \\\ndiff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c\nindex b0d5bd8763fd..816d06da04d4 100644\n--- a/libavcodec/cbs_vp9.c\n+++ b/libavcodec/cbs_vp9.c\n@@ -28,11 +28,10 @@ static int cbs_vp9_read_s(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                           const int *subscripts, int32_t *write_to)\n {\n     uint32_t magnitude;\n-    int position, sign;\n+    int sign;\n     int32_t value;\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n     if (get_bits_left(gbc) < width + 1) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid signed value at \"\n@@ -44,17 +43,7 @@ static int cbs_vp9_read_s(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     sign      = get_bits1(gbc);\n     value     = sign ? -(int32_t)magnitude : magnitude;\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0';\n-        bits[i] = sign ? '1' : '0';\n-        bits[i + 1] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n-    }\n+    CBS_TRACE_READ_END();\n \n     *write_to = value;\n     return 0;\n@@ -67,27 +56,19 @@ static int cbs_vp9_write_s(CodedBitstreamContext *ctx, PutBitContext *pbc,\n     uint32_t magnitude;\n     int sign;\n \n+    CBS_TRACE_WRITE_START();\n+\n     if (put_bits_left(pbc) < width + 1)\n         return AVERROR(ENOSPC);\n \n     sign      = value < 0;\n     magnitude = sign ? -value : value;\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (i = 0; i < width; i++)\n-            bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0';\n-        bits[i] = sign ? '1' : '0';\n-        bits[i + 1] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     put_bits(pbc, width, magnitude);\n     put_bits(pbc, 1, sign);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \n@@ -96,32 +77,24 @@ static int cbs_vp9_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc\n                                   const char *name, uint32_t *write_to)\n {\n     uint32_t value;\n-    int position, i;\n-    char bits[8];\n \n-    av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1);\n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    CBS_TRACE_READ_START();\n \n-    for (i = 0, value = range_min; value < range_max;) {\n+    av_assert0(range_min <= range_max && range_max - range_min < 32);\n+\n+    for (value = range_min; value < range_max;) {\n         if (get_bits_left(gbc) < 1) {\n             av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid increment value at \"\n                    \"%s: bitstream ended.\\n\", name);\n             return AVERROR_INVALIDDATA;\n         }\n-        if (get_bits1(gbc)) {\n-            bits[i++] = '1';\n+        if (get_bits1(gbc))\n             ++value;\n-        } else {\n-            bits[i++] = '0';\n+        else\n             break;\n-        }\n     }\n \n-    if (ctx->trace_enable) {\n-        bits[i] = 0;\n-        ff_cbs_trace_syntax_element(ctx, position, name, NULL, bits, value);\n-    }\n+    CBS_TRACE_READ_END_NO_SUBSCRIPTS();\n \n     *write_to = value;\n     return 0;\n@@ -133,6 +106,8 @@ static int cbs_vp9_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb\n {\n     int len;\n \n+    CBS_TRACE_WRITE_START();\n+\n     av_assert0(range_min <= range_max && range_max - range_min < 8);\n     if (value < range_min || value > range_max) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"%s out of range: \"\n@@ -148,23 +123,11 @@ static int cbs_vp9_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb\n     if (put_bits_left(pbc) < len)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[8];\n-        int i;\n-        for (i = 0; i < len; i++) {\n-            if (range_min + i == value)\n-                bits[i] = '0';\n-            else\n-                bits[i] = '1';\n-        }\n-        bits[i] = 0;\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, NULL, bits, value);\n-    }\n-\n     if (len > 0)\n         put_bits(pbc, len, (1 << len) - 1 - (value != range_max));\n \n+    CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();\n+\n     return 0;\n }\n \n@@ -173,12 +136,11 @@ static int cbs_vp9_read_le(CodedBitstreamContext *ctx, GetBitContext *gbc,\n                            const int *subscripts, uint32_t *write_to)\n {\n     uint32_t value;\n-    int position, b;\n+    int b;\n \n-    av_assert0(width % 8 == 0);\n+    CBS_TRACE_READ_START();\n \n-    if (ctx->trace_enable)\n-        position = get_bits_count(gbc);\n+    av_assert0(width % 8 == 0);\n \n     if (get_bits_left(gbc) < width) {\n         av_log(ctx->log_ctx, AV_LOG_ERROR, \"Invalid le value at \"\n@@ -190,17 +152,7 @@ static int cbs_vp9_read_le(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     for (b = 0; b < width; b += 8)\n         value |= get_bits(gbc, 8) << b;\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (b = 0; b < width; b += 8)\n-            for (i = 0; i < 8; i++)\n-                bits[b + i] = value >> (b + i) & 1 ? '1' : '0';\n-        bits[b] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, position, name, subscripts,\n-                                    bits, value);\n-    }\n+    CBS_TRACE_READ_END();\n \n     *write_to = value;\n     return 0;\n@@ -212,26 +164,18 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc,\n {\n     int b;\n \n+    CBS_TRACE_WRITE_START();\n+\n     av_assert0(width % 8 == 0);\n \n     if (put_bits_left(pbc) < width)\n         return AVERROR(ENOSPC);\n \n-    if (ctx->trace_enable) {\n-        char bits[33];\n-        int i;\n-        for (b = 0; b < width; b += 8)\n-            for (i = 0; i < 8; i++)\n-                bits[b + i] = value >> (b + i) & 1 ? '1' : '0';\n-        bits[b] = 0;\n-\n-        ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc),\n-                                    name, subscripts, bits, value);\n-    }\n-\n     for (b = 0; b < width; b += 8)\n         put_bits(pbc, 8, value >> b & 0xff);\n \n+    CBS_TRACE_WRITE_END();\n+\n     return 0;\n }\n \ndiff --git a/libavcodec/trace_headers_bsf.c b/libavcodec/trace_headers_bsf.c\nindex 028b0a1e599a..8781f5f10012 100644\n--- a/libavcodec/trace_headers_bsf.c\n+++ b/libavcodec/trace_headers_bsf.c\n@@ -44,6 +44,8 @@ static int trace_headers_init(AVBSFContext *bsf)\n \n     ctx->cbc->trace_enable = 1;\n     ctx->cbc->trace_level  = AV_LOG_INFO;\n+    ctx->cbc->trace_context = ctx->cbc;\n+    ctx->cbc->trace_read_callback = ff_cbs_trace_read_log;\n \n     if (bsf->par_in->extradata) {\n         CodedBitstreamFragment *frag = &ctx->fragment;\nFrom 695477a1c7d3dd5ad249442b0770ad0acd99dd87 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:27 +0800\nSubject: [PATCH] avcodec/cbs_av1: Allow specifying obu size byte length\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/cbs_av1.c | 30 +++++++++++++++++++++---------\n libavcodec/cbs_av1.h |  1 +\n 2 files changed, 22 insertions(+), 9 deletions(-)\n\ndiff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c\nindex 6c478603f173..4e687ace79d5 100644\n--- a/libavcodec/cbs_av1.c\n+++ b/libavcodec/cbs_av1.c\n@@ -138,15 +138,19 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc,\n     return 0;\n }\n \n+/** Minimum byte length will be used to indicate the len128 of value if byte_len is 0. */\n static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc,\n-                                const char *name, uint64_t value)\n+                                const char *name, uint64_t value, uint8_t byte_len)\n {\n     int len, i;\n     uint8_t byte;\n \n     CBS_TRACE_WRITE_START();\n \n-    len = (av_log2(value) + 7) / 7;\n+    if (byte_len)\n+        av_assert0(byte_len >= (av_log2(value) + 7) / 7);\n+\n+    len = byte_len ? byte_len : (av_log2(value) + 7) / 7;\n \n     for (i = 0; i < len; i++) {\n         if (put_bits_left(pbc) < 8)\n@@ -618,7 +622,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc)\n     } while (0)\n \n #define leb128(name) do { \\\n-        CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name)); \\\n+        CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name, 0)); \\\n     } while (0)\n \n #define infer(name, value) do { \\\n@@ -1002,9 +1006,14 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx,\n \n     if (obu->header.obu_has_size_field) {\n         pbc_tmp = *pbc;\n-        // Add space for the size field to fill later.\n-        put_bits32(pbc, 0);\n-        put_bits32(pbc, 0);\n+        if (obu->obu_size_byte_len) {\n+            for (int i = 0; i < obu->obu_size_byte_len; i++)\n+                put_bits(pbc, 8, 0);\n+        } else {\n+            // Add space for the size field to fill later.\n+            put_bits32(pbc, 0);\n+            put_bits32(pbc, 0);\n+        }\n     }\n \n     td = NULL;\n@@ -1124,7 +1133,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx,\n     end_pos   /= 8;\n \n     *pbc = pbc_tmp;\n-    err = cbs_av1_write_leb128(ctx, pbc, \"obu_size\", obu->obu_size);\n+    err = cbs_av1_write_leb128(ctx, pbc, \"obu_size\", obu->obu_size, obu->obu_size_byte_len);\n     if (err < 0)\n         goto error;\n \n@@ -1141,8 +1150,11 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx,\n     }\n \n     if (obu->obu_size > 0) {\n-        memmove(pbc->buf + data_pos,\n-                pbc->buf + start_pos, header_size);\n+        if (!obu->obu_size_byte_len) {\n+            obu->obu_size_byte_len = start_pos - data_pos;\n+            memmove(pbc->buf + data_pos,\n+                    pbc->buf + start_pos, header_size);\n+        }\n         skip_put_bytes(pbc, header_size);\n \n         if (td) {\ndiff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h\nindex 64dfdce9c4eb..a9e2d2284fff 100644\n--- a/libavcodec/cbs_av1.h\n+++ b/libavcodec/cbs_av1.h\n@@ -401,6 +401,7 @@ typedef struct AV1RawOBU {\n     AV1RawOBUHeader header;\n \n     size_t obu_size;\n+    uint8_t obu_size_byte_len;\n \n     union {\n         AV1RawSequenceHeader sequence_header;\n\nFrom 6c3a5d625f917631d1c8bab31ed65ebe26d14f47 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:25 +0800\nSubject: [PATCH] avcodec/cbs_av1: Add tx mode enum values\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/av1.h                     | 7 +++++++\n libavcodec/cbs_av1_syntax_template.c | 4 ++--\n 2 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/libavcodec/av1.h b/libavcodec/av1.h\nindex 384f7cddc7eb..8704bc41c122 100644\n--- a/libavcodec/av1.h\n+++ b/libavcodec/av1.h\n@@ -175,6 +175,13 @@ enum {\n     AV1_RESTORE_SWITCHABLE = 3,\n };\n \n+// TX mode (section 6.8.21)\n+enum {\n+    AV1_ONLY_4X4        = 0,\n+    AV1_TX_MODE_LARGEST = 1,\n+    AV1_TX_MODE_SELECT  = 2,\n+};\n+\n // Sequence Headers are actually unbounded because one can use\n // an arbitrary number of leading zeroes when encoding via uvlc.\n // The following estimate is based around using the lowest number\ndiff --git a/libavcodec/cbs_av1_syntax_template.c b/libavcodec/cbs_av1_syntax_template.c\nindex 6f09c4e4104e..3be1f2d30f96 100644\n--- a/libavcodec/cbs_av1_syntax_template.c\n+++ b/libavcodec/cbs_av1_syntax_template.c\n@@ -1028,9 +1028,9 @@ static int FUNC(read_tx_mode)(CodedBitstreamContext *ctx, RWContext *rw,\n     int err;\n \n     if (priv->coded_lossless)\n-        infer(tx_mode, 0);\n+        infer(tx_mode, AV1_ONLY_4X4);\n     else\n-        increment(tx_mode, 1, 2);\n+        increment(tx_mode, AV1_TX_MODE_LARGEST, AV1_TX_MODE_SELECT);\n \n     return 0;\n }\nFrom 4a4400709ccfc026c19028f4b2d798eed9322f64 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:28 +0800\nSubject: [PATCH] lavc/vaapi_encode: Init pic at the beginning of API\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/vaapi_encode.c | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex 0316fe5c1855..5ae63c9f2557 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -1205,7 +1205,7 @@ static int vaapi_encode_send_frame(AVCodecContext *avctx, AVFrame *frame)\n int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n {\n     VAAPIEncodeContext *ctx = avctx->priv_data;\n-    VAAPIEncodePicture *pic;\n+    VAAPIEncodePicture *pic = NULL;\n     AVFrame *frame = ctx->frame;\n     int err;\n \n@@ -1228,8 +1228,6 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n     }\n \n     if (ctx->has_sync_buffer_func) {\n-        pic = NULL;\n-\n         if (av_fifo_can_write(ctx->encode_fifo)) {\n             err = vaapi_encode_pick_next(avctx, &pic);\n             if (!err) {\n@@ -1255,7 +1253,6 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n         av_fifo_read(ctx->encode_fifo, &pic, 1);\n         ctx->encode_order = pic->encode_order + 1;\n     } else {\n-        pic = NULL;\n         err = vaapi_encode_pick_next(avctx, &pic);\n         if (err < 0)\n             return err;\nFrom 11b81838ae64095fcc130f4747a6adc8676a4998 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:29 +0800\nSubject: [PATCH] lavc/vaapi_encode: Extract set output pkt property function\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/vaapi_encode.c | 65 +++++++++++++++++++++++----------------\n 1 file changed, 38 insertions(+), 27 deletions(-)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex 5ae63c9f2557..46762342eb40 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -650,6 +650,41 @@ static int vaapi_encode_issue(AVCodecContext *avctx,\n     return err;\n }\n \n+static int vaapi_encode_set_output_property(AVCodecContext *avctx,\n+                                            VAAPIEncodePicture *pic,\n+                                            AVPacket *pkt)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+\n+    if (pic->type == PICTURE_TYPE_IDR)\n+        pkt->flags |= AV_PKT_FLAG_KEY;\n+\n+    pkt->pts = pic->pts;\n+    pkt->duration = pic->duration;\n+\n+    // for no-delay encoders this is handled in generic codec\n+    if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY &&\n+        avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {\n+        pkt->opaque     = pic->opaque;\n+        pkt->opaque_ref = pic->opaque_ref;\n+        pic->opaque_ref = NULL;\n+    }\n+\n+    if (ctx->output_delay == 0) {\n+        pkt->dts = pkt->pts;\n+    } else if (pic->encode_order < ctx->decode_delay) {\n+        if (ctx->ts_ring[pic->encode_order] < INT64_MIN + ctx->dts_pts_diff)\n+            pkt->dts = INT64_MIN;\n+        else\n+            pkt->dts = ctx->ts_ring[pic->encode_order] - ctx->dts_pts_diff;\n+    } else {\n+        pkt->dts = ctx->ts_ring[(pic->encode_order - ctx->decode_delay) %\n+                                (3 * ctx->output_delay + ctx->async_depth)];\n+    }\n+\n+    return 0;\n+}\n+\n static int vaapi_encode_output(AVCodecContext *avctx,\n                                VAAPIEncodePicture *pic, AVPacket *pkt)\n {\n@@ -691,12 +726,6 @@ static int vaapi_encode_output(AVCodecContext *avctx,\n         ptr += buf->size;\n     }\n \n-    if (pic->type == PICTURE_TYPE_IDR)\n-        pkt->flags |= AV_PKT_FLAG_KEY;\n-\n-    pkt->pts = pic->pts;\n-    pkt->duration = pic->duration;\n-\n     vas = vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer);\n     if (vas != VA_STATUS_SUCCESS) {\n         av_log(avctx, AV_LOG_ERROR, \"Failed to unmap output buffers: \"\n@@ -705,14 +734,6 @@ static int vaapi_encode_output(AVCodecContext *avctx,\n         goto fail;\n     }\n \n-    // for no-delay encoders this is handled in generic codec\n-    if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY &&\n-        avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {\n-        pkt->opaque     = pic->opaque;\n-        pkt->opaque_ref = pic->opaque_ref;\n-        pic->opaque_ref = NULL;\n-    }\n-\n     av_buffer_unref(&pic->output_buffer_ref);\n     pic->output_buffer = VA_INVALID_ID;\n \n@@ -1273,19 +1294,9 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n         return err;\n     }\n \n-    if (ctx->output_delay == 0) {\n-        pkt->dts = pkt->pts;\n-    } else if (pic->encode_order < ctx->decode_delay) {\n-        if (ctx->ts_ring[pic->encode_order] < INT64_MIN + ctx->dts_pts_diff)\n-            pkt->dts = INT64_MIN;\n-        else\n-            pkt->dts = ctx->ts_ring[pic->encode_order] - ctx->dts_pts_diff;\n-    } else {\n-        pkt->dts = ctx->ts_ring[(pic->encode_order - ctx->decode_delay) %\n-                                (3 * ctx->output_delay + ctx->async_depth)];\n-    }\n-    av_log(avctx, AV_LOG_DEBUG, \"Output packet: pts %\"PRId64\" dts %\"PRId64\".\\n\",\n-           pkt->pts, pkt->dts);\n+    vaapi_encode_set_output_property(avctx, pic, pkt);\n+    av_log(avctx, AV_LOG_DEBUG, \"Output packet: pts %\"PRId64\", dts %\"PRId64\", \"\n+           \"size %d bytes.\\n\", pkt->pts, pkt->dts, pkt->size);\n \n     ctx->output_order = pic->encode_order;\n     vaapi_encode_clear_old(avctx);\nFrom 254c5a8134a177244fc0995c3b2998079a755848 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:30 +0800\nSubject: [PATCH] lavc/vaapi_encode: Separate reference frame into\n previous/future list\n\nTo support more reference frames from different directions.\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nReviewed-by: Neal Gompa <ngompa13@gmail.com>\n---\n libavcodec/vaapi_encode.c       | 112 +++++++++++++++++++++++++-------\n libavcodec/vaapi_encode.h       |  15 +++--\n libavcodec/vaapi_encode_h264.c  |  94 +++++++++++++--------------\n libavcodec/vaapi_encode_h265.c  |  76 +++++++++++++---------\n libavcodec/vaapi_encode_mpeg2.c |   6 +-\n libavcodec/vaapi_encode_vp8.c   |   6 +-\n libavcodec/vaapi_encode_vp9.c   |  26 ++++----\n 7 files changed, 208 insertions(+), 127 deletions(-)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex 46762342eb40..79036673e7a9 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -276,21 +276,34 @@ static int vaapi_encode_issue(AVCodecContext *avctx,\n     av_log(avctx, AV_LOG_DEBUG, \"Issuing encode for pic %\"PRId64\"/%\"PRId64\" \"\n            \"as type %s.\\n\", pic->display_order, pic->encode_order,\n            picture_type_name[pic->type]);\n-    if (pic->nb_refs == 0) {\n+    if (pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0) {\n         av_log(avctx, AV_LOG_DEBUG, \"No reference pictures.\\n\");\n     } else {\n-        av_log(avctx, AV_LOG_DEBUG, \"Refers to:\");\n-        for (i = 0; i < pic->nb_refs; i++) {\n+        av_log(avctx, AV_LOG_DEBUG, \"L0 refers to\");\n+        for (i = 0; i < pic->nb_refs[0]; i++) {\n             av_log(avctx, AV_LOG_DEBUG, \" %\"PRId64\"/%\"PRId64,\n-                   pic->refs[i]->display_order, pic->refs[i]->encode_order);\n+                   pic->refs[0][i]->display_order, pic->refs[0][i]->encode_order);\n         }\n         av_log(avctx, AV_LOG_DEBUG, \".\\n\");\n+\n+        if (pic->nb_refs[1]) {\n+            av_log(avctx, AV_LOG_DEBUG, \"L1 refers to\");\n+            for (i = 0; i < pic->nb_refs[1]; i++) {\n+                av_log(avctx, AV_LOG_DEBUG, \" %\"PRId64\"/%\"PRId64,\n+                       pic->refs[1][i]->display_order, pic->refs[1][i]->encode_order);\n+            }\n+            av_log(avctx, AV_LOG_DEBUG, \".\\n\");\n+        }\n     }\n \n     av_assert0(!pic->encode_issued);\n-    for (i = 0; i < pic->nb_refs; i++) {\n-        av_assert0(pic->refs[i]);\n-        av_assert0(pic->refs[i]->encode_issued);\n+    for (i = 0; i < pic->nb_refs[0]; i++) {\n+        av_assert0(pic->refs[0][i]);\n+        av_assert0(pic->refs[0][i]->encode_issued);\n+    }\n+    for (i = 0; i < pic->nb_refs[1]; i++) {\n+        av_assert0(pic->refs[1][i]);\n+        av_assert0(pic->refs[1][i]->encode_issued);\n     }\n \n     av_log(avctx, AV_LOG_DEBUG, \"Input surface is %#x.\\n\", pic->input_surface);\n@@ -832,8 +845,12 @@ static void vaapi_encode_add_ref(AVCodecContext *avctx,\n \n     if (is_ref) {\n         av_assert0(pic != target);\n-        av_assert0(pic->nb_refs < MAX_PICTURE_REFERENCES);\n-        pic->refs[pic->nb_refs++] = target;\n+        av_assert0(pic->nb_refs[0] < MAX_PICTURE_REFERENCES &&\n+                   pic->nb_refs[1] < MAX_PICTURE_REFERENCES);\n+        if (target->display_order < pic->display_order)\n+            pic->refs[0][pic->nb_refs[0]++] = target;\n+        else\n+            pic->refs[1][pic->nb_refs[1]++] = target;\n         ++refs;\n     }\n \n@@ -862,10 +879,16 @@ static void vaapi_encode_remove_refs(AVCodecContext *avctx,\n     if (pic->ref_removed[level])\n         return;\n \n-    for (i = 0; i < pic->nb_refs; i++) {\n-        av_assert0(pic->refs[i]);\n-        --pic->refs[i]->ref_count[level];\n-        av_assert0(pic->refs[i]->ref_count[level] >= 0);\n+    for (i = 0; i < pic->nb_refs[0]; i++) {\n+        av_assert0(pic->refs[0][i]);\n+        --pic->refs[0][i]->ref_count[level];\n+        av_assert0(pic->refs[0][i]->ref_count[level] >= 0);\n+    }\n+\n+    for (i = 0; i < pic->nb_refs[1]; i++) {\n+        av_assert0(pic->refs[1][i]);\n+        --pic->refs[1][i]->ref_count[level];\n+        av_assert0(pic->refs[1][i]->ref_count[level] >= 0);\n     }\n \n     for (i = 0; i < pic->nb_dpb_pics; i++) {\n@@ -910,7 +933,7 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx,\n             vaapi_encode_add_ref(avctx, pic, end,   1, 1, 0);\n             vaapi_encode_add_ref(avctx, pic, prev,  0, 0, 1);\n \n-            for (ref = end->refs[1]; ref; ref = ref->refs[1])\n+            for (ref = end->refs[1][0]; ref; ref = ref->refs[1][0])\n                 vaapi_encode_add_ref(avctx, pic, ref, 0, 1, 0);\n         }\n         *last = prev;\n@@ -933,7 +956,7 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx,\n         vaapi_encode_add_ref(avctx, pic, end,   1, 1, 0);\n         vaapi_encode_add_ref(avctx, pic, prev,  0, 0, 1);\n \n-        for (ref = end->refs[1]; ref; ref = ref->refs[1])\n+        for (ref = end->refs[1][0]; ref; ref = ref->refs[1][0])\n             vaapi_encode_add_ref(avctx, pic, ref, 0, 1, 0);\n \n         if (i > 1)\n@@ -947,11 +970,44 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx,\n     }\n }\n \n+static void vaapi_encode_add_next_prev(AVCodecContext *avctx,\n+                                       VAAPIEncodePicture *pic)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    int i;\n+\n+    if (!pic)\n+        return;\n+\n+    if (pic->type == PICTURE_TYPE_IDR) {\n+        for (i = 0; i < ctx->nb_next_prev; i++) {\n+            --ctx->next_prev[i]->ref_count[0];\n+            ctx->next_prev[i] = NULL;\n+        }\n+        ctx->next_prev[0] = pic;\n+        ++pic->ref_count[0];\n+        ctx->nb_next_prev = 1;\n+\n+        return;\n+    }\n+\n+    if (ctx->nb_next_prev < MAX_PICTURE_REFERENCES) {\n+        ctx->next_prev[ctx->nb_next_prev++] = pic;\n+        ++pic->ref_count[0];\n+    } else {\n+        --ctx->next_prev[0]->ref_count[0];\n+        for (i = 0; i < MAX_PICTURE_REFERENCES - 1; i++)\n+            ctx->next_prev[i] = ctx->next_prev[i + 1];\n+        ctx->next_prev[i] = pic;\n+        ++pic->ref_count[0];\n+    }\n+}\n+\n static int vaapi_encode_pick_next(AVCodecContext *avctx,\n                                   VAAPIEncodePicture **pic_out)\n {\n     VAAPIEncodeContext *ctx = avctx->priv_data;\n-    VAAPIEncodePicture *pic = NULL, *next, *start;\n+    VAAPIEncodePicture *pic = NULL, *prev = NULL, *next, *start;\n     int i, b_counter, closed_gop_end;\n \n     // If there are any B-frames already queued, the next one to encode\n@@ -962,11 +1018,18 @@ static int vaapi_encode_pick_next(AVCodecContext *avctx,\n             continue;\n         if (pic->type != PICTURE_TYPE_B)\n             continue;\n-        for (i = 0; i < pic->nb_refs; i++) {\n-            if (!pic->refs[i]->encode_issued)\n+        for (i = 0; i < pic->nb_refs[0]; i++) {\n+            if (!pic->refs[0][i]->encode_issued)\n                 break;\n         }\n-        if (i == pic->nb_refs)\n+        if (i != pic->nb_refs[0])\n+            continue;\n+\n+        for (i = 0; i < pic->nb_refs[1]; i++) {\n+            if (!pic->refs[1][i]->encode_issued)\n+                break;\n+        }\n+        if (i == pic->nb_refs[1])\n             break;\n     }\n \n@@ -1068,18 +1131,17 @@ static int vaapi_encode_pick_next(AVCodecContext *avctx,\n         vaapi_encode_add_ref(avctx, pic, start,\n                              pic->type == PICTURE_TYPE_P,\n                              b_counter > 0, 0);\n-        vaapi_encode_add_ref(avctx, pic, ctx->next_prev, 0, 0, 1);\n+        vaapi_encode_add_ref(avctx, pic, ctx->next_prev[ctx->nb_next_prev - 1], 0, 0, 1);\n     }\n-    if (ctx->next_prev)\n-        --ctx->next_prev->ref_count[0];\n \n     if (b_counter > 0) {\n         vaapi_encode_set_b_pictures(avctx, start, pic, pic, 1,\n-                                    &ctx->next_prev);\n+                                    &prev);\n     } else {\n-        ctx->next_prev = pic;\n+        prev = pic;\n     }\n-    ++ctx->next_prev->ref_count[0];\n+    vaapi_encode_add_next_prev(avctx, prev);\n+\n     return 0;\n }\n \ndiff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h\nindex bd25cd5c953a..977bc2d94634 100644\n--- a/libavcodec/vaapi_encode.h\n+++ b/libavcodec/vaapi_encode.h\n@@ -49,6 +49,7 @@ enum {\n     // A.4.1: table A.6 allows at most 20 tile columns for any level.\n     MAX_TILE_COLS          = 20,\n     MAX_ASYNC_DEPTH        = 64,\n+    MAX_REFERENCE_LIST_NUM = 2,\n };\n \n extern const AVCodecHWConfigInternal *const ff_vaapi_encode_hw_configs[];\n@@ -116,10 +117,11 @@ typedef struct VAAPIEncodePicture {\n     // but not if it isn't.\n     int                     nb_dpb_pics;\n     struct VAAPIEncodePicture *dpb[MAX_DPB_SIZE];\n-    // The reference pictures used in decoding this picture.  If they are\n-    // used by later pictures they will also appear in the DPB.\n-    int                     nb_refs;\n-    struct VAAPIEncodePicture *refs[MAX_PICTURE_REFERENCES];\n+    // The reference pictures used in decoding this picture. If they are\n+    // used by later pictures they will also appear in the DPB. ref[0][] for\n+    // previous reference frames. ref[1][] for future reference frames.\n+    int                     nb_refs[MAX_REFERENCE_LIST_NUM];\n+    struct VAAPIEncodePicture *refs[MAX_REFERENCE_LIST_NUM][MAX_PICTURE_REFERENCES];\n     // The previous reference picture in encode order.  Must be in at least\n     // one of the reference list and DPB list.\n     struct VAAPIEncodePicture *prev;\n@@ -290,8 +292,9 @@ typedef struct VAAPIEncodeContext {\n     // Current encoding window, in display (input) order.\n     VAAPIEncodePicture *pic_start, *pic_end;\n     // The next picture to use as the previous reference picture in\n-    // encoding order.\n-    VAAPIEncodePicture *next_prev;\n+    // encoding order. Order from small to large in encoding order.\n+    VAAPIEncodePicture *next_prev[MAX_PICTURE_REFERENCES];\n+    int                 nb_next_prev;\n \n     // Next input order index (display order).\n     int64_t         input_order;\ndiff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c\nindex 09e130011390..57b5ea2babf7 100644\n--- a/libavcodec/vaapi_encode_h264.c\n+++ b/libavcodec/vaapi_encode_h264.c\n@@ -628,7 +628,7 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,\n     VAAPIEncodePicture              *prev = pic->prev;\n     VAAPIEncodeH264Picture         *hprev = prev ? prev->priv_data : NULL;\n     VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params;\n-    int i;\n+    int i, j = 0;\n \n     if (pic->type == PICTURE_TYPE_IDR) {\n         av_assert0(pic->display_order == pic->encode_order);\n@@ -729,24 +729,26 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,\n         .TopFieldOrderCnt    = hpic->pic_order_cnt,\n         .BottomFieldOrderCnt = hpic->pic_order_cnt,\n     };\n-\n-    for (i = 0; i < pic->nb_refs; i++) {\n-        VAAPIEncodePicture      *ref = pic->refs[i];\n-        VAAPIEncodeH264Picture *href;\n-\n-        av_assert0(ref && ref->encode_order < pic->encode_order);\n-        href = ref->priv_data;\n-\n-        vpic->ReferenceFrames[i] = (VAPictureH264) {\n-            .picture_id          = ref->recon_surface,\n-            .frame_idx           = href->frame_num,\n-            .flags               = VA_PICTURE_H264_SHORT_TERM_REFERENCE,\n-            .TopFieldOrderCnt    = href->pic_order_cnt,\n-            .BottomFieldOrderCnt = href->pic_order_cnt,\n-        };\n+    for (int k = 0; k < MAX_REFERENCE_LIST_NUM; k++) {\n+        for (i = 0; i < pic->nb_refs[k]; i++) {\n+            VAAPIEncodePicture      *ref = pic->refs[k][i];\n+            VAAPIEncodeH264Picture *href;\n+\n+            av_assert0(ref && ref->encode_order < pic->encode_order);\n+            href = ref->priv_data;\n+\n+            vpic->ReferenceFrames[j++] = (VAPictureH264) {\n+                .picture_id          = ref->recon_surface,\n+                .frame_idx           = href->frame_num,\n+                .flags               = VA_PICTURE_H264_SHORT_TERM_REFERENCE,\n+                .TopFieldOrderCnt    = href->pic_order_cnt,\n+                .BottomFieldOrderCnt = href->pic_order_cnt,\n+            };\n+        }\n     }\n-    for (; i < FF_ARRAY_ELEMS(vpic->ReferenceFrames); i++) {\n-        vpic->ReferenceFrames[i] = (VAPictureH264) {\n+\n+    for (; j < FF_ARRAY_ELEMS(vpic->ReferenceFrames); j++) {\n+        vpic->ReferenceFrames[j] = (VAPictureH264) {\n             .picture_id = VA_INVALID_ID,\n             .flags      = VA_PICTURE_H264_INVALID,\n         };\n@@ -948,17 +950,17 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,\n \n         if (pic->type == PICTURE_TYPE_P) {\n             int need_rplm = 0;\n-            for (i = 0; i < pic->nb_refs; i++) {\n-                av_assert0(pic->refs[i]);\n-                if (pic->refs[i] != def_l0[i])\n+            for (i = 0; i < pic->nb_refs[0]; i++) {\n+                av_assert0(pic->refs[0][i]);\n+                if (pic->refs[0][i] != def_l0[i])\n                     need_rplm = 1;\n             }\n \n             sh->ref_pic_list_modification_flag_l0 = need_rplm;\n             if (need_rplm) {\n                 int pic_num = hpic->frame_num;\n-                for (i = 0; i < pic->nb_refs; i++) {\n-                    href = pic->refs[i]->priv_data;\n+                for (i = 0; i < pic->nb_refs[0]; i++) {\n+                    href = pic->refs[0][i]->priv_data;\n                     av_assert0(href->frame_num != pic_num);\n                     if (href->frame_num < pic_num) {\n                         sh->rplm_l0[i].modification_of_pic_nums_idc = 0;\n@@ -977,28 +979,29 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,\n         } else {\n             int need_rplm_l0 = 0, need_rplm_l1 = 0;\n             int n0 = 0, n1 = 0;\n-            for (i = 0; i < pic->nb_refs; i++) {\n-                av_assert0(pic->refs[i]);\n-                href = pic->refs[i]->priv_data;\n-                av_assert0(href->pic_order_cnt != hpic->pic_order_cnt);\n-                if (href->pic_order_cnt < hpic->pic_order_cnt) {\n-                    if (pic->refs[i] != def_l0[n0])\n-                        need_rplm_l0 = 1;\n-                    ++n0;\n-                } else {\n-                    if (pic->refs[i] != def_l1[n1])\n-                        need_rplm_l1 = 1;\n-                    ++n1;\n-                }\n+            for (i = 0; i < pic->nb_refs[0]; i++) {\n+                av_assert0(pic->refs[0][i]);\n+                href = pic->refs[0][i]->priv_data;\n+                av_assert0(href->pic_order_cnt < hpic->pic_order_cnt);\n+                if (pic->refs[0][i] != def_l0[n0])\n+                    need_rplm_l0 = 1;\n+                ++n0;\n+            }\n+\n+            for (i = 0; i < pic->nb_refs[1]; i++) {\n+                av_assert0(pic->refs[1][i]);\n+                href = pic->refs[1][i]->priv_data;\n+                av_assert0(href->pic_order_cnt > hpic->pic_order_cnt);\n+                if (pic->refs[1][i] != def_l1[n1])\n+                    need_rplm_l1 = 1;\n+                ++n1;\n             }\n \n             sh->ref_pic_list_modification_flag_l0 = need_rplm_l0;\n             if (need_rplm_l0) {\n                 int pic_num = hpic->frame_num;\n-                for (i = j = 0; i < pic->nb_refs; i++) {\n-                    href = pic->refs[i]->priv_data;\n-                    if (href->pic_order_cnt > hpic->pic_order_cnt)\n-                        continue;\n+                for (i = j = 0; i < pic->nb_refs[0]; i++) {\n+                    href = pic->refs[0][i]->priv_data;\n                     av_assert0(href->frame_num != pic_num);\n                     if (href->frame_num < pic_num) {\n                         sh->rplm_l0[j].modification_of_pic_nums_idc = 0;\n@@ -1019,10 +1022,8 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,\n             sh->ref_pic_list_modification_flag_l1 = need_rplm_l1;\n             if (need_rplm_l1) {\n                 int pic_num = hpic->frame_num;\n-                for (i = j = 0; i < pic->nb_refs; i++) {\n-                    href = pic->refs[i]->priv_data;\n-                    if (href->pic_order_cnt < hpic->pic_order_cnt)\n-                        continue;\n+                for (i = j = 0; i < pic->nb_refs[1]; i++) {\n+                    href = pic->refs[1][i]->priv_data;\n                     av_assert0(href->frame_num != pic_num);\n                     if (href->frame_num < pic_num) {\n                         sh->rplm_l1[j].modification_of_pic_nums_idc = 0;\n@@ -1062,14 +1063,13 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,\n         vslice->RefPicList1[i].flags      = VA_PICTURE_H264_INVALID;\n     }\n \n-    av_assert0(pic->nb_refs <= 2);\n-    if (pic->nb_refs >= 1) {\n+    if (pic->nb_refs[0]) {\n         // Backward reference for P- or B-frame.\n         av_assert0(pic->type == PICTURE_TYPE_P ||\n                    pic->type == PICTURE_TYPE_B);\n         vslice->RefPicList0[0] = vpic->ReferenceFrames[0];\n     }\n-    if (pic->nb_refs >= 2) {\n+    if (pic->nb_refs[1]) {\n         // Forward reference for B-frame.\n         av_assert0(pic->type == PICTURE_TYPE_B);\n         vslice->RefPicList1[0] = vpic->ReferenceFrames[1];\ndiff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c\nindex efa59aecf58c..239ef2359a08 100644\n--- a/libavcodec/vaapi_encode_h265.c\n+++ b/libavcodec/vaapi_encode_h265.c\n@@ -764,7 +764,7 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,\n     VAAPIEncodePicture              *prev = pic->prev;\n     VAAPIEncodeH265Picture         *hprev = prev ? prev->priv_data : NULL;\n     VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params;\n-    int i;\n+    int i, j = 0;\n \n     if (pic->type == PICTURE_TYPE_IDR) {\n         av_assert0(pic->display_order == pic->encode_order);\n@@ -789,8 +789,8 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,\n             hpic->pic_type       = 1;\n         } else {\n             VAAPIEncodePicture *irap_ref;\n-            av_assert0(pic->refs[0] && pic->refs[1]);\n-            for (irap_ref = pic; irap_ref; irap_ref = irap_ref->refs[1]) {\n+            av_assert0(pic->refs[0][0] && pic->refs[1][0]);\n+            for (irap_ref = pic; irap_ref; irap_ref = irap_ref->refs[1][0]) {\n                 if (irap_ref->type == PICTURE_TYPE_I)\n                     break;\n             }\n@@ -915,24 +915,27 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,\n         .flags         = 0,\n     };\n \n-    for (i = 0; i < pic->nb_refs; i++) {\n-        VAAPIEncodePicture      *ref = pic->refs[i];\n-        VAAPIEncodeH265Picture *href;\n-\n-        av_assert0(ref && ref->encode_order < pic->encode_order);\n-        href = ref->priv_data;\n-\n-        vpic->reference_frames[i] = (VAPictureHEVC) {\n-            .picture_id    = ref->recon_surface,\n-            .pic_order_cnt = href->pic_order_cnt,\n-            .flags = (ref->display_order < pic->display_order ?\n-                      VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) |\n-                     (ref->display_order > pic->display_order ?\n-                      VA_PICTURE_HEVC_RPS_ST_CURR_AFTER  : 0),\n-        };\n+    for (int k = 0; k < MAX_REFERENCE_LIST_NUM; k++) {\n+        for (i = 0; i < pic->nb_refs[k]; i++) {\n+            VAAPIEncodePicture      *ref = pic->refs[k][i];\n+            VAAPIEncodeH265Picture *href;\n+\n+            av_assert0(ref && ref->encode_order < pic->encode_order);\n+            href = ref->priv_data;\n+\n+            vpic->reference_frames[j++] = (VAPictureHEVC) {\n+                .picture_id    = ref->recon_surface,\n+                .pic_order_cnt = href->pic_order_cnt,\n+                .flags = (ref->display_order < pic->display_order ?\n+                          VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) |\n+                          (ref->display_order > pic->display_order ?\n+                          VA_PICTURE_HEVC_RPS_ST_CURR_AFTER  : 0),\n+            };\n+        }\n     }\n-    for (; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) {\n-        vpic->reference_frames[i] = (VAPictureHEVC) {\n+\n+    for (; j < FF_ARRAY_ELEMS(vpic->reference_frames); j++) {\n+        vpic->reference_frames[j] = (VAPictureHEVC) {\n             .picture_id = VA_INVALID_ID,\n             .flags      = VA_PICTURE_HEVC_INVALID,\n         };\n@@ -1016,21 +1019,33 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,\n         memset(rps, 0, sizeof(*rps));\n \n         rps_pics = 0;\n-        for (i = 0; i < pic->nb_refs; i++) {\n-            strp = pic->refs[i]->priv_data;\n-            rps_poc[rps_pics]  = strp->pic_order_cnt;\n-            rps_used[rps_pics] = 1;\n-            ++rps_pics;\n+        for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) {\n+            for (j = 0; j < pic->nb_refs[i]; j++) {\n+                strp = pic->refs[i][j]->priv_data;\n+                rps_poc[rps_pics]  = strp->pic_order_cnt;\n+                rps_used[rps_pics] = 1;\n+                ++rps_pics;\n+            }\n         }\n+\n         for (i = 0; i < pic->nb_dpb_pics; i++) {\n             if (pic->dpb[i] == pic)\n                 continue;\n-            for (j = 0; j < pic->nb_refs; j++) {\n-                if (pic->dpb[i] == pic->refs[j])\n+\n+            for (j = 0; j < pic->nb_refs[0]; j++) {\n+                if (pic->dpb[i] == pic->refs[0][j])\n+                    break;\n+            }\n+            if (j < pic->nb_refs[0])\n+                continue;\n+\n+            for (j = 0; j < pic->nb_refs[1]; j++) {\n+                if (pic->dpb[i] == pic->refs[1][j])\n                     break;\n             }\n-            if (j < pic->nb_refs)\n+            if (j < pic->nb_refs[1])\n                 continue;\n+\n             strp = pic->dpb[i]->priv_data;\n             rps_poc[rps_pics]  = strp->pic_order_cnt;\n             rps_used[rps_pics] = 0;\n@@ -1155,8 +1170,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,\n         vslice->ref_pic_list1[i].flags      = VA_PICTURE_HEVC_INVALID;\n     }\n \n-    av_assert0(pic->nb_refs <= 2);\n-    if (pic->nb_refs >= 1) {\n+    if (pic->nb_refs[0]) {\n         // Backward reference for P- or B-frame.\n         av_assert0(pic->type == PICTURE_TYPE_P ||\n                    pic->type == PICTURE_TYPE_B);\n@@ -1165,7 +1179,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,\n             // Reference for GPB B-frame, L0 == L1\n             vslice->ref_pic_list1[0] = vpic->reference_frames[0];\n     }\n-    if (pic->nb_refs >= 2) {\n+    if (pic->nb_refs[1]) {\n         // Forward reference for B-frame.\n         av_assert0(pic->type == PICTURE_TYPE_B);\n         vslice->ref_pic_list1[0] = vpic->reference_frames[1];\ndiff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c\nindex 2edb0c35864f..d1904bf4f5a3 100644\n--- a/libavcodec/vaapi_encode_mpeg2.c\n+++ b/libavcodec/vaapi_encode_mpeg2.c\n@@ -458,12 +458,12 @@ static int vaapi_encode_mpeg2_init_picture_params(AVCodecContext *avctx,\n         break;\n     case PICTURE_TYPE_P:\n         vpic->picture_type = VAEncPictureTypePredictive;\n-        vpic->forward_reference_picture = pic->refs[0]->recon_surface;\n+        vpic->forward_reference_picture = pic->refs[0][0]->recon_surface;\n         break;\n     case PICTURE_TYPE_B:\n         vpic->picture_type = VAEncPictureTypeBidirectional;\n-        vpic->forward_reference_picture  = pic->refs[0]->recon_surface;\n-        vpic->backward_reference_picture = pic->refs[1]->recon_surface;\n+        vpic->forward_reference_picture  = pic->refs[0][0]->recon_surface;\n+        vpic->backward_reference_picture = pic->refs[1][0]->recon_surface;\n         break;\n     default:\n         av_assert0(0 && \"invalid picture type\");\ndiff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c\nindex ea8abb2418ab..8a557b967e60 100644\n--- a/libavcodec/vaapi_encode_vp8.c\n+++ b/libavcodec/vaapi_encode_vp8.c\n@@ -86,7 +86,7 @@ static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx,\n     switch (pic->type) {\n     case PICTURE_TYPE_IDR:\n     case PICTURE_TYPE_I:\n-        av_assert0(pic->nb_refs == 0);\n+        av_assert0(pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0);\n         vpic->ref_flags.bits.force_kf = 1;\n         vpic->ref_last_frame =\n         vpic->ref_gf_frame   =\n@@ -94,14 +94,14 @@ static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx,\n             VA_INVALID_SURFACE;\n         break;\n     case PICTURE_TYPE_P:\n-        av_assert0(pic->nb_refs == 1);\n+        av_assert0(!pic->nb_refs[1]);\n         vpic->ref_flags.bits.no_ref_last = 0;\n         vpic->ref_flags.bits.no_ref_gf   = 1;\n         vpic->ref_flags.bits.no_ref_arf  = 1;\n         vpic->ref_last_frame =\n         vpic->ref_gf_frame   =\n         vpic->ref_arf_frame  =\n-            pic->refs[0]->recon_surface;\n+            pic->refs[0][0]->recon_surface;\n         break;\n     default:\n         av_assert0(0 && \"invalid picture type\");\ndiff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c\nindex 87429881f1cf..c2a8dec71b9d 100644\n--- a/libavcodec/vaapi_encode_vp9.c\n+++ b/libavcodec/vaapi_encode_vp9.c\n@@ -96,15 +96,15 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,\n \n     switch (pic->type) {\n     case PICTURE_TYPE_IDR:\n-        av_assert0(pic->nb_refs == 0);\n+        av_assert0(pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0);\n         vpic->ref_flags.bits.force_kf = 1;\n         vpic->refresh_frame_flags = 0xff;\n         hpic->slot = 0;\n         break;\n     case PICTURE_TYPE_P:\n-        av_assert0(pic->nb_refs == 1);\n+        av_assert0(!pic->nb_refs[1]);\n         {\n-            VAAPIEncodeVP9Picture *href = pic->refs[0]->priv_data;\n+            VAAPIEncodeVP9Picture *href = pic->refs[0][0]->priv_data;\n             av_assert0(href->slot == 0 || href->slot == 1);\n \n             if (ctx->max_b_depth > 0) {\n@@ -120,10 +120,10 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,\n         }\n         break;\n     case PICTURE_TYPE_B:\n-        av_assert0(pic->nb_refs == 2);\n+        av_assert0(pic->nb_refs[0] && pic->nb_refs[1]);\n         {\n-            VAAPIEncodeVP9Picture *href0 = pic->refs[0]->priv_data,\n-                                  *href1 = pic->refs[1]->priv_data;\n+            VAAPIEncodeVP9Picture *href0 = pic->refs[0][0]->priv_data,\n+                                  *href1 = pic->refs[1][0]->priv_data;\n             av_assert0(href0->slot < pic->b_depth + 1 &&\n                        href1->slot < pic->b_depth + 1);\n \n@@ -157,12 +157,14 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,\n     for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++)\n         vpic->reference_frames[i] = VA_INVALID_SURFACE;\n \n-    for (i = 0; i < pic->nb_refs; i++) {\n-        VAAPIEncodePicture *ref_pic = pic->refs[i];\n-        int slot;\n-        slot = ((VAAPIEncodeVP9Picture*)ref_pic->priv_data)->slot;\n-        av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE);\n-        vpic->reference_frames[slot] = ref_pic->recon_surface;\n+    for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) {\n+        for (int j = 0; j < pic->nb_refs[i]; j++) {\n+            VAAPIEncodePicture *ref_pic = pic->refs[i][j];\n+            int slot;\n+            slot = ((VAAPIEncodeVP9Picture*)ref_pic->priv_data)->slot;\n+            av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE);\n+            vpic->reference_frames[slot] = ref_pic->recon_surface;\n+        }\n     }\n \n     vpic->pic_flags.bits.frame_type = (pic->type != PICTURE_TYPE_IDR);\nFrom 3be81e3b449febfb04596f3f187aae27f18d02f7 Mon Sep 17 00:00:00 2001\nFrom: Fei Wang <fei.w.wang@intel.com>\nDate: Mon, 11 Sep 2023 15:52:31 +0800\nSubject: [PATCH] lavc/vaapi_encode: Add VAAPI AV1 encoder\n\nSigned-off-by: Fei Wang <fei.w.wang@intel.com>\nAcked-by: Neal Gompa <ngompa13@gmail.com>\n---\n configure                     |   3 +\n doc/encoders.texi             |  14 +\n libavcodec/Makefile           |   2 +\n libavcodec/allcodecs.c        |   1 +\n libavcodec/av1_levels.c       |  92 ++++\n libavcodec/av1_levels.h       |  58 +++\n libavcodec/vaapi_encode.c     | 198 +++++--\n libavcodec/vaapi_encode.h     |  24 +\n libavcodec/vaapi_encode_av1.c | 949 ++++++++++++++++++++++++++++++++++\n 11 files changed, 1311 insertions(+), 33 deletions(-)\n create mode 100644 libavcodec/av1_levels.c\n create mode 100644 libavcodec/av1_levels.h\n create mode 100644 libavcodec/vaapi_encode_av1.c\n\ndiff --git a/configure b/configure\nindex e40dcce09e4c..1ee840961721 100755\n--- a/configure\n+++ b/configure\n@@ -3323,6 +3323,8 @@ av1_qsv_decoder_select=\"qsvdec\"\n av1_qsv_encoder_select=\"qsvenc\"\n av1_qsv_encoder_deps=\"libvpl\"\n av1_amf_encoder_deps=\"amf\"\n+av1_vaapi_encoder_deps=\"VAEncPictureParameterBufferAV1\"\n+av1_vaapi_encoder_select=\"cbs_av1 vaapi_encode\"\n \n # parsers\n aac_parser_select=\"adts_header mpeg4audio\"\n@@ -7108,6 +7110,7 @@ if enabled vaapi; then\n     check_type \"va/va.h va/va_enc_jpeg.h\" \"VAEncPictureParameterBufferJPEG\"\n     check_type \"va/va.h va/va_enc_vp8.h\"  \"VAEncPictureParameterBufferVP8\"\n     check_type \"va/va.h va/va_enc_vp9.h\"  \"VAEncPictureParameterBufferVP9\"\n+    check_type \"va/va.h va/va_enc_av1.h\"  \"VAEncPictureParameterBufferAV1\"\n fi\n \n if enabled_all opencl libdrm ; then\ndiff --git a/doc/encoders.texi b/doc/encoders.texi\nindex 6f8f5e127e87..d7d9584a0cae 100644\n--- a/doc/encoders.texi\n+++ b/doc/encoders.texi\n@@ -3995,6 +3995,20 @@ Average variable bitrate.\n Each encoder also has its own specific options:\n @table @option\n \n+@item av1_vaapi\n+@option{profile} sets the value of @emph{seq_profile}.\n+@option{tier} sets the value of @emph{seq_tier}.\n+@option{level} sets the value of @emph{seq_level_idx}.\n+\n+@table @option\n+@item tiles\n+Set the number of tiles to encode the input video with, as columns x rows.\n+(default is auto, which means use minimal tile column/row number).\n+@item tile_groups\n+Set tile groups number. All the tiles will be distributed as evenly as possible to\n+each tile group. (default is 1).\n+@end table\n+\n @item h264_vaapi\n @option{profile} sets the value of @emph{profile_idc} and the @emph{constraint_set*_flag}s.\n @option{level} sets the value of @emph{level_idc}.\ndiff --git a/libavcodec/Makefile b/libavcodec/Makefile\nindex bf3b0a93f975..cae2e773a158 100644\n--- a/libavcodec/Makefile\n+++ b/libavcodec/Makefile\n@@ -258,6 +258,7 @@ OBJS-$(CONFIG_AV1_MEDIACODEC_DECODER)  += mediacodecdec.o\n OBJS-$(CONFIG_AV1_MEDIACODEC_ENCODER)  += mediacodecenc.o\n OBJS-$(CONFIG_AV1_NVENC_ENCODER)       += nvenc_av1.o nvenc.o\n OBJS-$(CONFIG_AV1_QSV_ENCODER)         += qsvenc_av1.o\n+OBJS-$(CONFIG_AV1_VAAPI_ENCODER)       += vaapi_encode_av1.o av1_levels.o\n OBJS-$(CONFIG_AVRN_DECODER)            += avrndec.o\n OBJS-$(CONFIG_AVRP_DECODER)            += r210dec.o\n OBJS-$(CONFIG_AVRP_ENCODER)            += r210enc.o\n@@ -1322,6 +1323,7 @@ TESTPROGS = avcodec                                                     \\\n             jpeg2000dwt                                                 \\\n             mathops                                                    \\\n \n+TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER)     += av1_levels\n TESTPROGS-$(CONFIG_CABAC)                 += cabac\n TESTPROGS-$(CONFIG_DCT)                   += avfft\n TESTPROGS-$(CONFIG_FFT)                   += fft fft-fixed32\ndiff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c\nindex 6e95ca563691..5136a566f1b5 100644\n--- a/libavcodec/allcodecs.c\n+++ b/libavcodec/allcodecs.c\n@@ -845,6 +845,7 @@ extern const FFCodec ff_av1_nvenc_encoder;\n extern const FFCodec ff_av1_qsv_decoder;\n extern const FFCodec ff_av1_qsv_encoder;\n extern const FFCodec ff_av1_amf_encoder;\n+extern const FFCodec ff_av1_vaapi_encoder;\n extern const FFCodec ff_libopenh264_encoder;\n extern const FFCodec ff_libopenh264_decoder;\n extern const FFCodec ff_h264_amf_encoder;\ndiff --git a/libavcodec/av1_levels.c b/libavcodec/av1_levels.c\nnew file mode 100644\nindex 000000000000..19b6ee173625\n--- /dev/null\n+++ b/libavcodec/av1_levels.c\n@@ -0,0 +1,92 @@\n+/*\n+ * Copyright (c) 2023 Intel Corporation\n+ *\n+ * This file is part of FFmpeg.\n+ *\n+ * FFmpeg is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation; either\n+ * version 2.1 of the License, or (at your option) any later version.\n+ *\n+ * FFmpeg is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with FFmpeg; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n+ */\n+\n+#include <stddef.h>\n+#include \"libavutil/macros.h\"\n+#include \"av1_levels.h\"\n+\n+/** ignore entries which named in spec but no details. Like level 2.2 and 7.0. */\n+static const AV1LevelDescriptor av1_levels[] = {\n+    // Name                      MaxVSize                           MainMbps              MaxTiles\n+    // |  level_idx                 | MaxDisplayRate                    | HighMbps         | MaxTileCols\n+    // |      |   MaxPicSize        |       |     MaxDecodeRate         |    |   MainCR    |   |\n+    // |      |     |    MaxHSize   |       |           | MaxHeaderRate |    |     | HighCR|   |\n+    // |      |     |        |      |       |           |       |       |    |     |  |    |   |\n+    { \"2.0\",  0,   147456,  2048, 1152,   4423680,     5529600, 150,   1.5,     0, 2, 0,   8,  4 },\n+    { \"2.1\",  1,   278784,  2816, 1584,   8363520,    10454400, 150,   3.0,     0, 2, 0,   8,  4 },\n+    { \"3.0\",  4,   665856,  4352, 2448,  19975680,    24969600, 150,   6.0,     0, 2, 0,  16,  6 },\n+    { \"3.1\",  5,  1065024,  5504, 3096,  31950720,    39938400, 150,  10.0,     0, 2, 0,  16,  6 },\n+    { \"4.0\",  8,  2359296,  6144, 3456,  70778880,    77856768, 300,  12.0,  30.0, 4, 4,  32,  8 },\n+    { \"4.1\",  9,  2359296,  6144, 3456,  141557760,  155713536, 300,  20.0,  50.0, 4, 4,  32,  8 },\n+    { \"5.0\", 12,  8912896,  8192, 4352,  267386880,  273715200, 300,  30.0, 100.0, 6, 4,  64,  8 },\n+    { \"5.1\", 13,  8912896,  8192, 4352,  534773760,  547430400, 300,  40.0, 160.0, 8, 4,  64,  8 },\n+    { \"5.2\", 14,  8912896,  8192, 4352, 1069547520, 1094860800, 300,  60.0, 240.0, 8, 4,  64,  8 },\n+    { \"5.3\", 15,  8912896,  8192, 4352, 1069547520, 1176502272, 300,  60.0, 240.0, 8, 4,  64,  8 },\n+    { \"6.0\", 16, 35651584, 16384, 8704, 1069547520, 1176502272, 300,  60.0, 240.0, 8, 4, 128, 16 },\n+    { \"6.1\", 17, 35651584, 16384, 8704, 2139095040, 2189721600, 300, 100.0, 480.0, 8, 4, 128, 16 },\n+    { \"6.2\", 18, 35651584, 16384, 8704, 4278190080, 4379443200, 300, 160.0, 800.0, 8, 4, 128, 16 },\n+    { \"6.3\", 19, 35651584, 16384, 8704, 4278190080, 4706009088, 300, 160.0, 800.0, 8, 4, 128, 16 },\n+};\n+\n+const AV1LevelDescriptor *ff_av1_guess_level(int64_t bitrate,\n+                                             int tier,\n+                                             int width,\n+                                             int height,\n+                                             int tiles,\n+                                             int tile_cols,\n+                                             float fps)\n+{\n+    int pic_size;\n+    uint64_t display_rate;\n+    float max_br;\n+\n+    pic_size = width * height;\n+    display_rate = (uint64_t)pic_size * fps;\n+\n+    for (int i = 0; i < FF_ARRAY_ELEMS(av1_levels); i++) {\n+        const AV1LevelDescriptor *level = &av1_levels[i];\n+        // Limitation: decode rate, header rate, compress rate, etc. are not considered.\n+        if (pic_size > level->max_pic_size)\n+            continue;\n+        if (width > level->max_h_size)\n+            continue;\n+        if (height > level->max_v_size)\n+            continue;\n+        if (display_rate > level->max_display_rate)\n+            continue;\n+\n+        if (tier)\n+            max_br = level->high_mbps;\n+        else\n+            max_br = level->main_mbps;\n+        if (!max_br)\n+            continue;\n+        if (bitrate > (int64_t)(1000000.0 * max_br))\n+            continue;\n+\n+        if (tiles > level->max_tiles)\n+            continue;\n+        if (tile_cols > level->max_tile_cols)\n+            continue;\n+        return level;\n+    }\n+\n+    return NULL;\n+}\ndiff --git a/libavcodec/av1_levels.h b/libavcodec/av1_levels.h\nnew file mode 100644\nindex 000000000000..164cb876ba10\n--- /dev/null\n+++ b/libavcodec/av1_levels.h\n@@ -0,0 +1,58 @@\n+/*\n+ * Copyright (c) 2023 Intel Corporation\n+ *\n+ * This file is part of FFmpeg.\n+ *\n+ * FFmpeg is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation; either\n+ * version 2.1 of the License, or (at your option) any later version.\n+ *\n+ * FFmpeg is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with FFmpeg; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n+ */\n+\n+#ifndef AVCODEC_AV1_LEVELS_H\n+#define AVCODEC_AV1_LEVELS_H\n+\n+#include <stdint.h>\n+\n+typedef struct AV1LevelDescriptor {\n+    char     name[4];\n+    uint8_t  level_idx;\n+\n+    uint32_t max_pic_size;\n+    uint32_t max_h_size;\n+    uint32_t max_v_size;\n+    uint64_t max_display_rate;\n+    uint64_t max_decode_rate;\n+\n+    uint32_t max_header_rate;\n+    float    main_mbps;\n+    float    high_mbps;\n+    uint32_t main_cr;\n+    uint32_t high_cr;\n+    uint32_t max_tiles;\n+    uint32_t max_tile_cols;\n+} AV1LevelDescriptor;\n+\n+/**\n+ * Guess the level of a stream from some parameters.\n+ *\n+ * Unknown parameters may be zero, in which case they will be ignored.\n+ */\n+const AV1LevelDescriptor *ff_av1_guess_level(int64_t bitrate,\n+                                             int tier,\n+                                             int width,\n+                                             int height,\n+                                             int tile_rows,\n+                                             int tile_cols,\n+                                             float fps);\n+\n+#endif /* AVCODEC_AV1_LEVELS_H */\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex 79036673e7a9..e3820956d160 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -683,6 +683,11 @@ static int vaapi_encode_set_output_property(AVCodecContext *avctx,\n         pic->opaque_ref = NULL;\n     }\n \n+    if (ctx->codec->flags & FLAG_TIMESTAMP_NO_DELAY) {\n+        pkt->dts = pkt->pts;\n+        return 0;\n+    }\n+\n     if (ctx->output_delay == 0) {\n         pkt->dts = pkt->pts;\n     } else if (pic->encode_order < ctx->decode_delay) {\n@@ -698,65 +703,160 @@ static int vaapi_encode_set_output_property(AVCodecContext *avctx,\n     return 0;\n }\n \n-static int vaapi_encode_output(AVCodecContext *avctx,\n-                               VAAPIEncodePicture *pic, AVPacket *pkt)\n+static int vaapi_encode_get_coded_buffer_size(AVCodecContext *avctx, VABufferID buf_id)\n {\n     VAAPIEncodeContext *ctx = avctx->priv_data;\n     VACodedBufferSegment *buf_list, *buf;\n+    int size = 0;\n     VAStatus vas;\n-    int total_size = 0;\n-    uint8_t *ptr;\n     int err;\n \n-    err = vaapi_encode_wait(avctx, pic);\n-    if (err < 0)\n-        return err;\n-\n-    buf_list = NULL;\n-    vas = vaMapBuffer(ctx->hwctx->display, pic->output_buffer,\n+    vas = vaMapBuffer(ctx->hwctx->display, buf_id,\n                       (void**)&buf_list);\n     if (vas != VA_STATUS_SUCCESS) {\n         av_log(avctx, AV_LOG_ERROR, \"Failed to map output buffers: \"\n                \"%d (%s).\\n\", vas, vaErrorStr(vas));\n         err = AVERROR(EIO);\n-        goto fail;\n+        return err;\n     }\n \n     for (buf = buf_list; buf; buf = buf->next)\n-        total_size += buf->size;\n+        size += buf->size;\n \n-    err = ff_get_encode_buffer(avctx, pkt, total_size, 0);\n-    ptr = pkt->data;\n+    vas = vaUnmapBuffer(ctx->hwctx->display, buf_id);\n+    if (vas != VA_STATUS_SUCCESS) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to unmap output buffers: \"\n+               \"%d (%s).\\n\", vas, vaErrorStr(vas));\n+        err = AVERROR(EIO);\n+        return err;\n+    }\n \n-    if (err < 0)\n-        goto fail_mapped;\n+    return size;\n+}\n+\n+static int vaapi_encode_get_coded_buffer_data(AVCodecContext *avctx,\n+                                              VABufferID buf_id, uint8_t **dst)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    VACodedBufferSegment *buf_list, *buf;\n+    VAStatus vas;\n+    int err;\n+\n+    vas = vaMapBuffer(ctx->hwctx->display, buf_id,\n+                      (void**)&buf_list);\n+    if (vas != VA_STATUS_SUCCESS) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to map output buffers: \"\n+               \"%d (%s).\\n\", vas, vaErrorStr(vas));\n+        err = AVERROR(EIO);\n+        return err;\n+    }\n \n     for (buf = buf_list; buf; buf = buf->next) {\n         av_log(avctx, AV_LOG_DEBUG, \"Output buffer: %u bytes \"\n                \"(status %08x).\\n\", buf->size, buf->status);\n \n-        memcpy(ptr, buf->buf, buf->size);\n-        ptr += buf->size;\n+        memcpy(*dst, buf->buf, buf->size);\n+        *dst += buf->size;\n     }\n \n-    vas = vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer);\n+    vas = vaUnmapBuffer(ctx->hwctx->display, buf_id);\n     if (vas != VA_STATUS_SUCCESS) {\n         av_log(avctx, AV_LOG_ERROR, \"Failed to unmap output buffers: \"\n                \"%d (%s).\\n\", vas, vaErrorStr(vas));\n         err = AVERROR(EIO);\n-        goto fail;\n+        return err;\n+    }\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_get_coded_data(AVCodecContext *avctx,\n+                                       VAAPIEncodePicture *pic, AVPacket *pkt)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    VABufferID output_buffer_prev;\n+    int total_size = 0;\n+    uint8_t *ptr;\n+    int ret;\n+\n+    if (ctx->coded_buffer_ref) {\n+        output_buffer_prev = (VABufferID)(uintptr_t)ctx->coded_buffer_ref->data;\n+        ret = vaapi_encode_get_coded_buffer_size(avctx, output_buffer_prev);\n+        if (ret < 0)\n+            goto end;\n+        total_size += ret;\n     }\n \n+    ret = vaapi_encode_get_coded_buffer_size(avctx, pic->output_buffer);\n+    if (ret < 0)\n+        goto end;\n+    total_size += ret;\n+\n+    ret = ff_get_encode_buffer(avctx, pkt, total_size, 0);\n+    if (ret < 0)\n+        goto end;\n+    ptr = pkt->data;\n+\n+    if (ctx->coded_buffer_ref) {\n+        ret = vaapi_encode_get_coded_buffer_data(avctx, output_buffer_prev, &ptr);\n+        if (ret < 0)\n+            goto end;\n+    }\n+\n+    ret = vaapi_encode_get_coded_buffer_data(avctx, pic->output_buffer, &ptr);\n+    if (ret < 0)\n+        goto end;\n+\n+end:\n+    if (ctx->coded_buffer_ref) {\n+        av_buffer_unref(&ctx->coded_buffer_ref);\n+    }\n     av_buffer_unref(&pic->output_buffer_ref);\n     pic->output_buffer = VA_INVALID_ID;\n \n+    return ret;\n+}\n+\n+static int vaapi_encode_output(AVCodecContext *avctx,\n+                               VAAPIEncodePicture *pic, AVPacket *pkt)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    AVPacket *pkt_ptr = pkt;\n+    int err;\n+\n+    err = vaapi_encode_wait(avctx, pic);\n+    if (err < 0)\n+        return err;\n+\n+    if (pic->non_independent_frame) {\n+        av_assert0(!ctx->coded_buffer_ref);\n+        ctx->coded_buffer_ref = av_buffer_ref(pic->output_buffer_ref);\n+\n+        if (pic->tail_size) {\n+            if (ctx->tail_pkt->size) {\n+                err = AVERROR(AVERROR_BUG);\n+                goto end;\n+            }\n+\n+            err = ff_get_encode_buffer(avctx, ctx->tail_pkt, pic->tail_size, 0);\n+            if (err < 0)\n+                goto end;\n+\n+            memcpy(ctx->tail_pkt->data, pic->tail_data, pic->tail_size);\n+            pkt_ptr = ctx->tail_pkt;\n+        }\n+    } else {\n+        err = vaapi_encode_get_coded_data(avctx, pic, pkt);\n+        if (err < 0)\n+            goto end;\n+    }\n+\n     av_log(avctx, AV_LOG_DEBUG, \"Output read for pic %\"PRId64\"/%\"PRId64\".\\n\",\n            pic->display_order, pic->encode_order);\n-    return 0;\n \n-fail_mapped:\n-    vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer);\n-fail:\n+    vaapi_encode_set_output_property(avctx, pic, pkt_ptr);\n+\n+end:\n     av_buffer_unref(&pic->output_buffer_ref);\n     pic->output_buffer = VA_INVALID_ID;\n     return err;\n@@ -1128,9 +1228,19 @@ static int vaapi_encode_pick_next(AVCodecContext *avctx,\n \n     vaapi_encode_add_ref(avctx, pic, pic, 0, 1, 0);\n     if (pic->type != PICTURE_TYPE_IDR) {\n-        vaapi_encode_add_ref(avctx, pic, start,\n-                             pic->type == PICTURE_TYPE_P,\n-                             b_counter > 0, 0);\n+        // TODO: apply both previous and forward multi reference for all vaapi encoders.\n+        // And L0/L1 reference frame number can be set dynamically through query\n+        // VAConfigAttribEncMaxRefFrames attribute.\n+        if (avctx->codec_id == AV_CODEC_ID_AV1) {\n+            for (i = 0; i < ctx->nb_next_prev; i++)\n+                vaapi_encode_add_ref(avctx, pic, ctx->next_prev[i],\n+                                     pic->type == PICTURE_TYPE_P,\n+                                     b_counter > 0, 0);\n+        } else\n+            vaapi_encode_add_ref(avctx, pic, start,\n+                                 pic->type == PICTURE_TYPE_P,\n+                                 b_counter > 0, 0);\n+\n         vaapi_encode_add_ref(avctx, pic, ctx->next_prev[ctx->nb_next_prev - 1], 0, 0, 1);\n     }\n \n@@ -1292,6 +1402,19 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n     AVFrame *frame = ctx->frame;\n     int err;\n \n+start:\n+    /** if no B frame before repeat P frame, sent repeat P frame out. */\n+    if (ctx->tail_pkt->size) {\n+        for (VAAPIEncodePicture *tmp = ctx->pic_start; tmp; tmp = tmp->next) {\n+            if (tmp->type == PICTURE_TYPE_B && tmp->pts < ctx->tail_pkt->pts)\n+                break;\n+            else if (!tmp->next) {\n+                av_packet_move_ref(pkt, ctx->tail_pkt);\n+                goto end;\n+            }\n+        }\n+    }\n+\n     err = ff_encode_get_frame(avctx, frame);\n     if (err < 0 && err != AVERROR_EOF)\n         return err;\n@@ -1356,17 +1479,21 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt)\n         return err;\n     }\n \n-    vaapi_encode_set_output_property(avctx, pic, pkt);\n-    av_log(avctx, AV_LOG_DEBUG, \"Output packet: pts %\"PRId64\", dts %\"PRId64\", \"\n-           \"size %d bytes.\\n\", pkt->pts, pkt->dts, pkt->size);\n-\n     ctx->output_order = pic->encode_order;\n     vaapi_encode_clear_old(avctx);\n \n+    /** loop to get an available pkt in encoder flushing. */\n+    if (ctx->end_of_stream && !pkt->size)\n+        goto start;\n+\n+end:\n+    if (pkt->size)\n+        av_log(avctx, AV_LOG_DEBUG, \"Output packet: pts %\"PRId64\", dts %\"PRId64\", \"\n+               \"size %d bytes.\\n\", pkt->pts, pkt->dts, pkt->size);\n+\n     return 0;\n }\n \n-\n static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx, int type,\n                                                   void *buffer, size_t size)\n {\n@@ -2667,6 +2794,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)\n     ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;\n     ctx->hwctx = ctx->device->hwctx;\n \n+    ctx->tail_pkt = av_packet_alloc();\n+    if (!ctx->tail_pkt) {\n+        err = AVERROR(ENOMEM);\n+        goto fail;\n+    }\n+\n     err = vaapi_encode_profile_entrypoint(avctx);\n     if (err < 0)\n         goto fail;\n@@ -2859,6 +2992,7 @@ av_cold int ff_vaapi_encode_close(AVCodecContext *avctx)\n     }\n \n     av_frame_free(&ctx->frame);\n+    av_packet_free(&ctx->tail_pkt);\n \n     av_freep(&ctx->codec_sequence_params);\n     av_freep(&ctx->codec_picture_params);\ndiff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h\nindex 977bc2d94634..d0d6cc2adf46 100644\n--- a/libavcodec/vaapi_encode.h\n+++ b/libavcodec/vaapi_encode.h\n@@ -133,6 +133,17 @@ typedef struct VAAPIEncodePicture {\n \n     int          nb_slices;\n     VAAPIEncodeSlice *slices;\n+\n+    /**\n+     * indicate if current frame is an independent frame that the coded data\n+     * can be pushed to downstream directly. Coded of non-independent frame\n+     * data will be concatenated into next independent frame.\n+     */\n+    int non_independent_frame;\n+    /** Tail data of current pic, used only for repeat header of AV1. */\n+    char tail_data[MAX_PARAM_BUFFER_SIZE];\n+    /** Byte length of tail_data. */\n+    size_t tail_size;\n } VAAPIEncodePicture;\n \n typedef struct VAAPIEncodeProfile {\n@@ -367,6 +378,16 @@ typedef struct VAAPIEncodeContext {\n     AVFifo          *encode_fifo;\n     // Max number of frame buffered in encoder.\n     int             async_depth;\n+\n+    /** Head data for current output pkt, used only for AV1. */\n+    //void  *header_data;\n+    //size_t header_data_size;\n+\n+    /** Buffered coded data of a pic if it is an non-independent frame. */\n+    AVBufferRef     *coded_buffer_ref;\n+\n+    /** Tail data of a pic, now only used for av1 repeat frame header. */\n+    AVPacket        *tail_pkt;\n } VAAPIEncodeContext;\n \n enum {\n@@ -383,6 +404,9 @@ enum {\n     // Codec supports non-IDR key pictures (that is, key pictures do\n     // not necessarily empty the DPB).\n     FLAG_NON_IDR_KEY_PICTURES  = 1 << 5,\n+    // Codec output packet without timestamp delay, which means the\n+    // output packet has same PTS and DTS.\n+    FLAG_TIMESTAMP_NO_DELAY    = 1 << 6,\n };\n \n typedef struct VAAPIEncodeType {\ndiff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c\nnew file mode 100644\nindex 000000000000..3ff1c47b532d\n--- /dev/null\n+++ b/libavcodec/vaapi_encode_av1.c\n@@ -0,0 +1,949 @@\n+/*\n+ * Copyright (c) 2023 Intel Corporation\n+ *\n+ * This file is part of FFmpeg.\n+ *\n+ * FFmpeg is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU Lesser General Public\n+ * License as published by the Free Software Foundation; either\n+ * version 2.1 of the License, or (at your option) any later version.\n+ *\n+ * FFmpeg is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ * Lesser General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Lesser General Public\n+ * License along with FFmpeg; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n+ */\n+\n+#include <va/va.h>\n+#include <va/va_enc_av1.h>\n+\n+#include \"libavutil/pixdesc.h\"\n+#include \"libavutil/opt.h\"\n+\n+#include \"cbs_av1.h\"\n+#include \"put_bits.h\"\n+#include \"codec_internal.h\"\n+#include \"av1_levels.h\"\n+#include \"vaapi_encode.h\"\n+\n+#define AV1_MAX_QUANT 255\n+\n+typedef struct VAAPIEncodeAV1Picture {\n+    int64_t last_idr_frame;\n+    int slot;\n+} VAAPIEncodeAV1Picture;\n+\n+typedef struct VAAPIEncodeAV1Context {\n+    VAAPIEncodeContext common;\n+    AV1RawOBU sh; /**< sequence header.*/\n+    AV1RawOBU fh; /**< frame header.*/\n+    CodedBitstreamContext *cbc;\n+    CodedBitstreamFragment current_obu;\n+    VAConfigAttribValEncAV1 attr;\n+    VAConfigAttribValEncAV1Ext1 attr_ext1;\n+    VAConfigAttribValEncAV1Ext2 attr_ext2;\n+\n+    char sh_data[MAX_PARAM_BUFFER_SIZE]; /**< coded sequence header data. */\n+    size_t sh_data_len; /**< bit length of sh_data. */\n+    char fh_data[MAX_PARAM_BUFFER_SIZE]; /**< coded frame header data. */\n+    size_t fh_data_len; /**< bit length of fh_data. */\n+\n+    uint8_t uniform_tile;\n+    uint8_t use_128x128_superblock;\n+    int sb_cols;\n+    int sb_rows;\n+    int tile_cols_log2;\n+    int tile_rows_log2;\n+    int max_tile_width_sb;\n+    int max_tile_height_sb;\n+    uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS];\n+    uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS];\n+\n+    int min_log2_tile_cols;\n+    int max_log2_tile_cols;\n+    int min_log2_tile_rows;\n+    int max_log2_tile_rows;\n+\n+    int q_idx_idr;\n+    int q_idx_p;\n+    int q_idx_b;\n+\n+    /** bit positions in current frame header */\n+    int qindex_offset;\n+    int loopfilter_offset;\n+    int cdef_start_offset;\n+    int cdef_param_size;\n+\n+    /** user options */\n+    int profile;\n+    int level;\n+    int tier;\n+    int tile_cols, tile_rows;\n+    int tile_groups;\n+} VAAPIEncodeAV1Context;\n+\n+static void vaapi_encode_av1_trace_write_log(void *ctx,\n+                                             PutBitContext *pbc, int length,\n+                                             const char *str, const int *subscripts,\n+                                             int64_t value)\n+{\n+    VAAPIEncodeAV1Context *priv = ctx;\n+    int position;\n+\n+    position = put_bits_count(pbc);\n+    av_assert0(position >= length);\n+\n+    if (!strcmp(str, \"base_q_idx\"))\n+        priv->qindex_offset = position - length;\n+    else if (!strcmp(str, \"loop_filter_level[0]\"))\n+        priv->loopfilter_offset = position - length;\n+    else if (!strcmp(str, \"cdef_damping_minus_3\"))\n+        priv->cdef_start_offset = position - length;\n+    else if (!strcmp(str, \"cdef_uv_sec_strength[i]\"))\n+        priv->cdef_param_size = position - priv->cdef_start_offset;\n+}\n+\n+static av_cold int vaapi_encode_av1_get_encoder_caps(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+\n+    // Surfaces must be aligned to superblock boundaries.\n+    ctx->surface_width  = FFALIGN(avctx->width,  priv->use_128x128_superblock ? 128 : 64);\n+    ctx->surface_height = FFALIGN(avctx->height, priv->use_128x128_superblock ? 128 : 64);\n+\n+    return 0;\n+}\n+\n+static av_cold int vaapi_encode_av1_configure(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeContext     *ctx = avctx->priv_data;\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+    int ret;\n+\n+    ret = ff_cbs_init(&priv->cbc, AV_CODEC_ID_AV1, avctx);\n+    if (ret < 0)\n+        return ret;\n+    priv->cbc->trace_enable  = 1;\n+    priv->cbc->trace_level   = AV_LOG_DEBUG;\n+    priv->cbc->trace_context = ctx;\n+    priv->cbc->trace_write_callback = vaapi_encode_av1_trace_write_log;\n+\n+    if (ctx->rc_mode->quality) {\n+        priv->q_idx_p = av_clip(ctx->rc_quality, 0, AV1_MAX_QUANT);\n+        if (fabs(avctx->i_quant_factor) > 0.0)\n+            priv->q_idx_idr =\n+                av_clip((fabs(avctx->i_quant_factor) * priv->q_idx_p  +\n+                         avctx->i_quant_offset) + 0.5,\n+                        0, AV1_MAX_QUANT);\n+        else\n+            priv->q_idx_idr = priv->q_idx_p;\n+\n+        if (fabs(avctx->b_quant_factor) > 0.0)\n+            priv->q_idx_b =\n+                av_clip((fabs(avctx->b_quant_factor) * priv->q_idx_p  +\n+                         avctx->b_quant_offset) + 0.5,\n+                        0, AV1_MAX_QUANT);\n+        else\n+            priv->q_idx_b = priv->q_idx_p;\n+    } else {\n+        /** Arbitrary value */\n+        priv->q_idx_idr = priv->q_idx_p = priv->q_idx_b = 128;\n+    }\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_av1_add_obu(AVCodecContext *avctx,\n+                                    CodedBitstreamFragment *au,\n+                                    uint8_t type,\n+                                    void *obu_unit)\n+{\n+    int ret;\n+\n+    ret = ff_cbs_insert_unit_content(au, -1,\n+                                     type, obu_unit, NULL);\n+    if (ret < 0) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to add OBU unit: \"\n+               \"type = %d.\\n\", type);\n+        return ret;\n+    }\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_av1_write_obu(AVCodecContext *avctx,\n+                                      char *data, size_t *data_len,\n+                                      CodedBitstreamFragment *bs)\n+{\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+    int ret;\n+\n+    ret = ff_cbs_write_fragment_data(priv->cbc, bs);\n+    if (ret < 0) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to write packed header.\\n\");\n+        return ret;\n+    }\n+\n+    if ((size_t)8 * MAX_PARAM_BUFFER_SIZE < 8 * bs->data_size - bs->data_bit_padding) {\n+        av_log(avctx, AV_LOG_ERROR, \"Access unit too large: \"\n+               \"%zu < %zu.\\n\", (size_t)8 * MAX_PARAM_BUFFER_SIZE,\n+               8 * bs->data_size - bs->data_bit_padding);\n+        return AVERROR(ENOSPC);\n+    }\n+\n+    memcpy(data, bs->data, bs->data_size);\n+    *data_len = 8 * bs->data_size - bs->data_bit_padding;\n+\n+    return 0;\n+}\n+\n+static int tile_log2(int blkSize, int target) {\n+    int k;\n+    for (k = 0; (blkSize << k) < target; k++);\n+    return k;\n+}\n+\n+static int vaapi_encode_av1_set_tile(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+    int mi_cols, mi_rows, sb_shift, sb_size;\n+    int max_tile_area_sb, max_tile_area_sb_varied;\n+    int tile_width_sb, tile_height_sb, widest_tile_sb;\n+    int tile_cols, tile_rows;\n+    int min_log2_tiles;\n+    int i;\n+\n+    if (priv->tile_cols > AV1_MAX_TILE_COLS ||\n+        priv->tile_rows > AV1_MAX_TILE_ROWS) {\n+        av_log(avctx, AV_LOG_ERROR, \"Invalid tile number %dx%d, should less than %dx%d.\\n\",\n+               priv->tile_cols, priv->tile_rows, AV1_MAX_TILE_COLS, AV1_MAX_TILE_ROWS);\n+        return AVERROR(EINVAL);\n+    }\n+\n+    mi_cols = 2 * ((avctx->width + 7) >> 3);\n+    mi_rows = 2 * ((avctx->height + 7) >> 3);\n+    priv->sb_cols = priv->use_128x128_superblock ?\n+                    ((mi_cols + 31) >> 5) : ((mi_cols + 15) >> 4);\n+    priv->sb_rows = priv->use_128x128_superblock ?\n+                    ((mi_rows + 31) >> 5) : ((mi_rows + 15) >> 4);\n+    sb_shift = priv->use_128x128_superblock ? 5 : 4;\n+    sb_size  = sb_shift + 2;\n+    priv->max_tile_width_sb = AV1_MAX_TILE_WIDTH >> sb_size;\n+    max_tile_area_sb = AV1_MAX_TILE_AREA  >> (2 * sb_size);\n+\n+    priv->min_log2_tile_cols = tile_log2(priv->max_tile_width_sb, priv->sb_cols);\n+    priv->max_log2_tile_cols = tile_log2(1, FFMIN(priv->sb_cols, AV1_MAX_TILE_COLS));\n+    priv->max_log2_tile_rows = tile_log2(1, FFMIN(priv->sb_rows, AV1_MAX_TILE_ROWS));\n+    min_log2_tiles = FFMAX(priv->min_log2_tile_cols,\n+                           tile_log2(max_tile_area_sb, priv->sb_rows * priv->sb_cols));\n+\n+    tile_cols = av_clip(priv->tile_cols, (priv->sb_cols + priv->max_tile_width_sb - 1) / priv->max_tile_width_sb, priv->sb_cols);\n+\n+    if (!priv->tile_cols)\n+        priv->tile_cols = tile_cols;\n+    else if (priv->tile_cols != tile_cols){\n+        av_log(avctx, AV_LOG_ERROR, \"Invalid tile cols %d, should be in range of %d~%d\\n\",\n+               priv->tile_cols,\n+               (priv->sb_cols + priv->max_tile_width_sb - 1) / priv->max_tile_width_sb,\n+               priv->sb_cols);\n+        return AVERROR(EINVAL);\n+    }\n+\n+    priv->tile_cols_log2 = tile_log2(1, priv->tile_cols);\n+    tile_width_sb = (priv->sb_cols + (1 << priv->tile_cols_log2) - 1) >>\n+                    priv->tile_cols_log2;\n+\n+    if (priv->tile_rows > priv->sb_rows) {\n+        av_log(avctx, AV_LOG_ERROR, \"Invalid tile rows %d, should be less than %d.\\n\",\n+               priv->tile_rows, priv->sb_rows);\n+        return AVERROR(EINVAL);\n+    }\n+\n+    /** Try user setting tile rows number first. */\n+    tile_rows = priv->tile_rows ? priv->tile_rows : 1;\n+    for (; tile_rows <= priv->sb_rows && tile_rows <= AV1_MAX_TILE_ROWS; tile_rows++) {\n+        /** try uniformed tile. */\n+        priv->tile_rows_log2 = tile_log2(1, tile_rows);\n+        if ((priv->sb_cols + tile_width_sb - 1) / tile_width_sb == priv->tile_cols) {\n+            for (i = 0; i < priv->tile_cols - 1; i++)\n+                priv->width_in_sbs_minus_1[i] = tile_width_sb - 1;\n+            priv->width_in_sbs_minus_1[i] = priv->sb_cols - (priv->tile_cols - 1) * tile_width_sb - 1;\n+\n+            tile_height_sb = (priv->sb_rows + (1 << priv->tile_rows_log2) - 1) >>\n+                             priv->tile_rows_log2;\n+\n+            if ((priv->sb_rows + tile_height_sb - 1) / tile_height_sb == tile_rows &&\n+                tile_height_sb <= max_tile_area_sb / tile_width_sb) {\n+                for (i = 0; i < tile_rows - 1; i++)\n+                    priv->height_in_sbs_minus_1[i] = tile_height_sb - 1;\n+                priv->height_in_sbs_minus_1[i] = priv->sb_rows - (tile_rows - 1) * tile_height_sb - 1;\n+\n+                priv->uniform_tile = 1;\n+                priv->min_log2_tile_rows = FFMAX(min_log2_tiles - priv->tile_cols_log2, 0);\n+\n+                break;\n+            }\n+        }\n+\n+        /** try non-uniformed tile. */\n+        widest_tile_sb = 0;\n+        for (i = 0; i < priv->tile_cols; i++) {\n+            priv->width_in_sbs_minus_1[i] = (i + 1) * priv->sb_cols / priv->tile_cols - i * priv->sb_cols / priv->tile_cols - 1;\n+            widest_tile_sb = FFMAX(widest_tile_sb, priv->width_in_sbs_minus_1[i] + 1);\n+        }\n+\n+        if (min_log2_tiles)\n+            max_tile_area_sb_varied = (priv->sb_rows * priv->sb_cols) >> (min_log2_tiles + 1);\n+        else\n+            max_tile_area_sb_varied = priv->sb_rows * priv->sb_cols;\n+        priv->max_tile_height_sb = FFMAX(1, max_tile_area_sb_varied / widest_tile_sb);\n+\n+        if (tile_rows == av_clip(tile_rows, (priv->sb_rows + priv->max_tile_height_sb - 1) / priv->max_tile_height_sb, priv->sb_rows)) {\n+            for (i = 0; i < tile_rows; i++)\n+                priv->height_in_sbs_minus_1[i] = (i + 1) * priv->sb_rows / tile_rows - i * priv->sb_rows / tile_rows - 1;\n+\n+            break;\n+        }\n+\n+        /** Return invalid parameter if explicit tile rows is set. */\n+        if (priv->tile_rows) {\n+            av_log(avctx, AV_LOG_ERROR, \"Invalid tile rows %d.\\n\", priv->tile_rows);\n+            return AVERROR(EINVAL);\n+        }\n+    }\n+\n+    priv->tile_rows = tile_rows;\n+    av_log(avctx, AV_LOG_DEBUG, \"Setting tile cols/rows to %d/%d.\\n\",\n+           priv->tile_cols, priv->tile_rows);\n+\n+    /** check if tile cols/rows is supported by driver. */\n+    if (priv->attr_ext2.bits.max_tile_num_minus1) {\n+        if ((priv->tile_cols * priv->tile_rows - 1) > priv->attr_ext2.bits.max_tile_num_minus1) {\n+            av_log(avctx, AV_LOG_ERROR, \"Unsupported tile num %d * %d = %d by driver, \"\n+                   \"should be at most %d.\\n\", priv->tile_cols, priv->tile_rows,\n+                   priv->tile_cols * priv->tile_rows,\n+                   priv->attr_ext2.bits.max_tile_num_minus1 + 1);\n+            return AVERROR(EINVAL);\n+        }\n+    }\n+\n+    /** check if tile group numbers is valid. */\n+    if (priv->tile_groups > priv->tile_cols * priv->tile_rows) {\n+        av_log(avctx, AV_LOG_WARNING, \"Invalid tile groups number %d, \"\n+        \"correct to %d.\\n\", priv->tile_groups, priv->tile_cols * priv->tile_rows);\n+        priv->tile_groups = priv->tile_cols * priv->tile_rows;\n+    }\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_av1_write_sequence_header(AVCodecContext *avctx,\n+                                                  char *data, size_t *data_len)\n+{\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+\n+    memcpy(data, &priv->sh_data, MAX_PARAM_BUFFER_SIZE * sizeof(char));\n+    *data_len = priv->sh_data_len;\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_av1_init_sequence_params(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeContext               *ctx = avctx->priv_data;\n+    VAAPIEncodeAV1Context           *priv = avctx->priv_data;\n+    AV1RawOBU                     *sh_obu = &priv->sh;\n+    AV1RawSequenceHeader              *sh = &sh_obu->obu.sequence_header;\n+    VAEncSequenceParameterBufferAV1 *vseq = ctx->codec_sequence_params;\n+    CodedBitstreamFragment           *obu = &priv->current_obu;\n+    const AVPixFmtDescriptor *desc;\n+    int ret;\n+\n+    memset(sh_obu, 0, sizeof(*sh_obu));\n+    sh_obu->header.obu_type = AV1_OBU_SEQUENCE_HEADER;\n+\n+    desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);\n+    av_assert0(desc);\n+\n+    sh->seq_profile  = avctx->profile;\n+    if (!sh->seq_force_screen_content_tools)\n+        sh->seq_force_integer_mv = AV1_SELECT_INTEGER_MV;\n+    sh->frame_width_bits_minus_1  = av_log2(avctx->width);\n+    sh->frame_height_bits_minus_1 = av_log2(avctx->height);\n+    sh->max_frame_width_minus_1   = avctx->width - 1;\n+    sh->max_frame_height_minus_1  = avctx->height - 1;\n+    sh->seq_tier[0]               = priv->tier;\n+    /** enable order hint and reserve maximum 8 bits for it by default. */\n+    sh->enable_order_hint         = 1;\n+    sh->order_hint_bits_minus_1   = 7;\n+\n+    sh->color_config = (AV1RawColorConfig) {\n+        .high_bitdepth                  = desc->comp[0].depth == 8 ? 0 : 1,\n+        .color_primaries                = avctx->color_primaries,\n+        .transfer_characteristics       = avctx->color_trc,\n+        .matrix_coefficients            = avctx->colorspace,\n+        .color_description_present_flag = (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||\n+                                           avctx->color_trc       != AVCOL_TRC_UNSPECIFIED ||\n+                                           avctx->colorspace      != AVCOL_SPC_UNSPECIFIED),\n+        .color_range                    = avctx->color_range == AVCOL_RANGE_JPEG,\n+        .subsampling_x                  = desc->log2_chroma_w,\n+        .subsampling_y                  = desc->log2_chroma_h,\n+    };\n+\n+    switch (avctx->chroma_sample_location) {\n+        case AVCHROMA_LOC_LEFT:\n+            sh->color_config.chroma_sample_position = AV1_CSP_VERTICAL;\n+            break;\n+        case AVCHROMA_LOC_TOPLEFT:\n+            sh->color_config.chroma_sample_position = AV1_CSP_COLOCATED;\n+            break;\n+        default:\n+            sh->color_config.chroma_sample_position = AV1_CSP_UNKNOWN;\n+            break;\n+    }\n+\n+    if (avctx->level != FF_PROFILE_UNKNOWN) {\n+        sh->seq_level_idx[0] = avctx->level;\n+    } else {\n+        const AV1LevelDescriptor *level;\n+        float framerate;\n+\n+        if (avctx->framerate.num > 0 && avctx->framerate.den > 0)\n+            framerate = avctx->framerate.num / avctx->framerate.den;\n+        else\n+            framerate = 0;\n+\n+        level = ff_av1_guess_level(avctx->bit_rate, priv->tier,\n+                                   ctx->surface_width, ctx->surface_height,\n+                                   priv->tile_rows * priv->tile_cols,\n+                                   priv->tile_cols, framerate);\n+        if (level) {\n+            av_log(avctx, AV_LOG_VERBOSE, \"Using level %s.\\n\", level->name);\n+            sh->seq_level_idx[0] = level->level_idx;\n+        } else {\n+            av_log(avctx, AV_LOG_VERBOSE, \"Stream will not conform to \"\n+                   \"any normal level, using maximum parameters level by default.\\n\");\n+            sh->seq_level_idx[0] = 31;\n+            sh->seq_tier[0] = 1;\n+        }\n+    }\n+    vseq->seq_profile             = sh->seq_profile;\n+    vseq->seq_level_idx           = sh->seq_level_idx[0];\n+    vseq->seq_tier                = sh->seq_tier[0];\n+    vseq->order_hint_bits_minus_1 = sh->order_hint_bits_minus_1;\n+    vseq->intra_period            = ctx->gop_size;\n+    vseq->ip_period               = ctx->b_per_p + 1;\n+\n+    vseq->seq_fields.bits.enable_order_hint = sh->enable_order_hint;\n+\n+    if (!(ctx->va_rc_mode & VA_RC_CQP)) {\n+        vseq->bits_per_second = ctx->va_bit_rate;\n+        vseq->seq_fields.bits.enable_cdef = sh->enable_cdef = 1;\n+    }\n+\n+    ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_SEQUENCE_HEADER, &priv->sh);\n+    if (ret < 0)\n+        goto end;\n+\n+    ret = vaapi_encode_av1_write_obu(avctx, priv->sh_data, &priv->sh_data_len, obu);\n+    if (ret < 0)\n+        goto end;\n+\n+end:\n+    ff_cbs_fragment_reset(obu);\n+    return ret;\n+}\n+\n+static int vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,\n+                                                VAAPIEncodePicture *pic)\n+{\n+    VAAPIEncodeContext              *ctx = avctx->priv_data;\n+    VAAPIEncodeAV1Context          *priv = avctx->priv_data;\n+    VAAPIEncodeAV1Picture          *hpic = pic->priv_data;\n+    AV1RawOBU                    *fh_obu = &priv->fh;\n+    AV1RawFrameHeader                *fh = &fh_obu->obu.frame.header;\n+    VAEncPictureParameterBufferAV1 *vpic = pic->codec_picture_params;\n+    CodedBitstreamFragment          *obu = &priv->current_obu;\n+    VAAPIEncodePicture    *ref;\n+    VAAPIEncodeAV1Picture *href;\n+    int slot, i;\n+    int ret;\n+    static const int8_t default_loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME] =\n+        { 1, 0, 0, 0, -1, 0, -1, -1 };\n+\n+    memset(fh_obu, 0, sizeof(*fh_obu));\n+    pic->nb_slices = priv->tile_groups;\n+    pic->non_independent_frame = pic->encode_order < pic->display_order;\n+    fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER;\n+    fh_obu->header.obu_has_size_field = 1;\n+\n+    switch (pic->type) {\n+    case PICTURE_TYPE_IDR:\n+        av_assert0(pic->nb_refs[0] == 0 || pic->nb_refs[1]);\n+        fh->frame_type = AV1_FRAME_KEY;\n+        fh->refresh_frame_flags = 0xFF;\n+        fh->base_q_idx = priv->q_idx_idr;\n+        hpic->slot = 0;\n+        hpic->last_idr_frame = pic->display_order;\n+        break;\n+    case PICTURE_TYPE_P:\n+        av_assert0(pic->nb_refs[0]);\n+        fh->frame_type = AV1_FRAME_INTER;\n+        fh->base_q_idx = priv->q_idx_p;\n+        ref = pic->refs[0][pic->nb_refs[0] - 1];\n+        href = ref->priv_data;\n+        hpic->slot = !href->slot;\n+        hpic->last_idr_frame = href->last_idr_frame;\n+        fh->refresh_frame_flags = 1 << hpic->slot;\n+\n+        /** set the nearest frame in L0 as all reference frame. */\n+        for (i = 0; i < AV1_REFS_PER_FRAME; i++) {\n+            fh->ref_frame_idx[i] = href->slot;\n+        }\n+        fh->primary_ref_frame = href->slot;\n+        fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame;\n+        vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST;\n+\n+        /** set the 2nd nearest frame in L0 as Golden frame. */\n+        if (pic->nb_refs[0] > 1) {\n+            ref = pic->refs[0][pic->nb_refs[0] - 2];\n+            href = ref->priv_data;\n+            fh->ref_frame_idx[3] = href->slot;\n+            fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame;\n+            vpic->ref_frame_ctrl_l0.fields.search_idx1 = AV1_REF_FRAME_GOLDEN;\n+        }\n+        break;\n+    case PICTURE_TYPE_B:\n+        av_assert0(pic->nb_refs[0] && pic->nb_refs[1]);\n+        fh->frame_type = AV1_FRAME_INTER;\n+        fh->base_q_idx = priv->q_idx_b;\n+        fh->refresh_frame_flags = 0x0;\n+        fh->reference_select = 1;\n+\n+        /** B frame will not be referenced, disable its recon frame. */\n+        vpic->picture_flags.bits.disable_frame_recon = 1;\n+\n+        /** Use LAST_FRAME and BWDREF_FRAME for reference. */\n+        vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST;\n+        vpic->ref_frame_ctrl_l1.fields.search_idx0 = AV1_REF_FRAME_BWDREF;\n+\n+        ref                            = pic->refs[0][pic->nb_refs[0] - 1];\n+        href                           = ref->priv_data;\n+        hpic->last_idr_frame           = href->last_idr_frame;\n+        fh->primary_ref_frame          = href->slot;\n+        fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame;\n+        for (i = 0; i < AV1_REF_FRAME_GOLDEN; i++) {\n+            fh->ref_frame_idx[i] = href->slot;\n+        }\n+\n+        ref                            = pic->refs[1][pic->nb_refs[1] - 1];\n+        href                           = ref->priv_data;\n+        fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame;\n+        for (i = AV1_REF_FRAME_GOLDEN; i < AV1_REFS_PER_FRAME; i++) {\n+            fh->ref_frame_idx[i] = href->slot;\n+        }\n+        break;\n+    default:\n+        av_assert0(0 && \"invalid picture type\");\n+    }\n+\n+    fh->show_frame                = pic->display_order <= pic->encode_order;\n+    fh->showable_frame            = fh->frame_type != AV1_FRAME_KEY;\n+    fh->frame_width_minus_1       = avctx->width - 1;\n+    fh->frame_height_minus_1      = avctx->height - 1;\n+    fh->render_width_minus_1      = fh->frame_width_minus_1;\n+    fh->render_height_minus_1     = fh->frame_height_minus_1;\n+    fh->order_hint                = pic->display_order - hpic->last_idr_frame;\n+    fh->tile_cols                 = priv->tile_cols;\n+    fh->tile_rows                 = priv->tile_rows;\n+    fh->tile_cols_log2            = priv->tile_cols_log2;\n+    fh->tile_rows_log2            = priv->tile_rows_log2;\n+    fh->uniform_tile_spacing_flag = priv->uniform_tile;\n+    fh->tile_size_bytes_minus1    = priv->attr_ext2.bits.tile_size_bytes_minus1;\n+\n+    /** ignore ONLY_4x4 mode for codedlossless is not fully implemented. */\n+    if (priv->attr_ext2.bits.tx_mode_support & 0x04)\n+        fh->tx_mode = AV1_TX_MODE_SELECT;\n+    else if (priv->attr_ext2.bits.tx_mode_support & 0x02)\n+        fh->tx_mode = AV1_TX_MODE_LARGEST;\n+    else {\n+        av_log(avctx, AV_LOG_ERROR, \"No available tx mode found.\\n\");\n+        return AVERROR(EINVAL);\n+    }\n+\n+    for (i = 0; i < fh->tile_cols; i++)\n+        fh->width_in_sbs_minus_1[i] = vpic->width_in_sbs_minus_1[i] = priv->width_in_sbs_minus_1[i];\n+\n+    for (i = 0; i < fh->tile_rows; i++)\n+        fh->height_in_sbs_minus_1[i] = vpic->height_in_sbs_minus_1[i] = priv->height_in_sbs_minus_1[i];\n+\n+    memcpy(fh->loop_filter_ref_deltas, default_loop_filter_ref_deltas,\n+           AV1_TOTAL_REFS_PER_FRAME * sizeof(int8_t));\n+\n+    if (fh->frame_type == AV1_FRAME_KEY && fh->show_frame) {\n+        fh->error_resilient_mode = 1;\n+    }\n+\n+    if (fh->frame_type == AV1_FRAME_KEY || fh->error_resilient_mode)\n+        fh->primary_ref_frame = AV1_PRIMARY_REF_NONE;\n+\n+    vpic->base_qindex          = fh->base_q_idx;\n+    vpic->frame_width_minus_1  = fh->frame_width_minus_1;\n+    vpic->frame_height_minus_1 = fh->frame_height_minus_1;\n+    vpic->primary_ref_frame    = fh->primary_ref_frame;\n+    vpic->reconstructed_frame  = pic->recon_surface;\n+    vpic->coded_buf            = pic->output_buffer;\n+    vpic->tile_cols            = fh->tile_cols;\n+    vpic->tile_rows            = fh->tile_rows;\n+    vpic->order_hint           = fh->order_hint;\n+#if VA_CHECK_VERSION(1, 15, 0)\n+    vpic->refresh_frame_flags  = fh->refresh_frame_flags;\n+#endif\n+\n+    vpic->picture_flags.bits.enable_frame_obu     = 0;\n+    vpic->picture_flags.bits.frame_type           = fh->frame_type;\n+    vpic->picture_flags.bits.reduced_tx_set       = fh->reduced_tx_set;\n+    vpic->picture_flags.bits.error_resilient_mode = fh->error_resilient_mode;\n+\n+    /** let driver decide to use single or compound reference prediction mode. */\n+    vpic->mode_control_flags.bits.reference_mode = fh->reference_select ? 2 : 0;\n+    vpic->mode_control_flags.bits.tx_mode = fh->tx_mode;\n+\n+    vpic->tile_group_obu_hdr_info.bits.obu_has_size_field = 1;\n+\n+    /** set reference. */\n+    for (i = 0; i < AV1_REFS_PER_FRAME; i++)\n+        vpic->ref_frame_idx[i] = fh->ref_frame_idx[i];\n+\n+    for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++)\n+        vpic->reference_frames[i] = VA_INVALID_SURFACE;\n+\n+    for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) {\n+        for (int j = 0; j < pic->nb_refs[i]; j++) {\n+            VAAPIEncodePicture *ref_pic = pic->refs[i][j];\n+\n+            slot = ((VAAPIEncodeAV1Picture*)ref_pic->priv_data)->slot;\n+            av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE);\n+\n+            vpic->reference_frames[slot] = ref_pic->recon_surface;\n+        }\n+    }\n+\n+    fh_obu->obu_size_byte_len = priv->attr_ext2.bits.obu_size_bytes_minus1 + 1;\n+    ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_FRAME_HEADER, &priv->fh);\n+    if (ret < 0)\n+        goto end;\n+\n+    ret = vaapi_encode_av1_write_obu(avctx, priv->fh_data, &priv->fh_data_len, obu);\n+    if (ret < 0)\n+        goto end;\n+\n+    if (!(ctx->va_rc_mode & VA_RC_CQP)) {\n+        vpic->min_base_qindex = av_clip(avctx->qmin, 1, AV1_MAX_QUANT);\n+        vpic->max_base_qindex = av_clip(avctx->qmax, 1, AV1_MAX_QUANT);\n+\n+        vpic->bit_offset_qindex              = priv->qindex_offset;\n+        vpic->bit_offset_loopfilter_params   = priv->loopfilter_offset;\n+        vpic->bit_offset_cdef_params         = priv->cdef_start_offset;\n+        vpic->size_in_bits_cdef_params       = priv->cdef_param_size;\n+        vpic->size_in_bits_frame_hdr_obu     = priv->fh_data_len;\n+        vpic->byte_offset_frame_hdr_obu_size = (((pic->type == PICTURE_TYPE_IDR) ?\n+                                               priv->sh_data_len / 8 : 0) +\n+                                               (fh_obu->header.obu_extension_flag ?\n+                                               2 : 1));\n+    }\n+\n+end:\n+    ff_cbs_fragment_reset(obu);\n+    return ret;\n+}\n+\n+static int vaapi_encode_av1_init_slice_params(AVCodecContext *avctx,\n+                                              VAAPIEncodePicture *pic,\n+                                              VAAPIEncodeSlice *slice)\n+{\n+    VAAPIEncodeAV1Context      *priv = avctx->priv_data;\n+    VAEncTileGroupBufferAV1  *vslice = slice->codec_slice_params;\n+    CodedBitstreamAV1Context  *cbctx = priv->cbc->priv_data;\n+    int div;\n+\n+    /** Set tile group info. */\n+    div = priv->tile_cols * priv->tile_rows / priv->tile_groups;\n+    vslice->tg_start = slice->index * div;\n+    if (slice->index == (priv->tile_groups - 1)) {\n+        vslice->tg_end = priv->tile_cols * priv->tile_rows - 1;\n+        cbctx->seen_frame_header = 0;\n+    } else {\n+        vslice->tg_end = (slice->index + 1) * div - 1;\n+    }\n+\n+    return 0;\n+}\n+\n+static int vaapi_encode_av1_write_picture_header(AVCodecContext *avctx,\n+                                                 VAAPIEncodePicture *pic,\n+                                                 char *data, size_t *data_len)\n+{\n+    VAAPIEncodeAV1Context     *priv = avctx->priv_data;\n+    CodedBitstreamFragment     *obu = &priv->current_obu;\n+    CodedBitstreamAV1Context *cbctx = priv->cbc->priv_data;\n+    AV1RawOBU               *fh_obu = &priv->fh;\n+    AV1RawFrameHeader       *rep_fh = &fh_obu->obu.frame_header;\n+    VAAPIEncodeAV1Picture *href;\n+    int ret = 0;\n+\n+    pic->tail_size = 0;\n+    /** Pack repeat frame header. */\n+    if (pic->display_order > pic->encode_order) {\n+        memset(fh_obu, 0, sizeof(*fh_obu));\n+        href = pic->refs[0][pic->nb_refs[0] - 1]->priv_data;\n+        fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER;\n+        fh_obu->header.obu_has_size_field = 1;\n+\n+        rep_fh->show_existing_frame   = 1;\n+        rep_fh->frame_to_show_map_idx = href->slot == 0;\n+        rep_fh->frame_type            = AV1_FRAME_INTER;\n+        rep_fh->frame_width_minus_1   = avctx->width - 1;\n+        rep_fh->frame_height_minus_1  = avctx->height - 1;\n+        rep_fh->render_width_minus_1  = rep_fh->frame_width_minus_1;\n+        rep_fh->render_height_minus_1 = rep_fh->frame_height_minus_1;\n+\n+        cbctx->seen_frame_header = 0;\n+\n+        ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_FRAME_HEADER, &priv->fh);\n+        if (ret < 0)\n+            goto end;\n+\n+        ret = vaapi_encode_av1_write_obu(avctx, pic->tail_data, &pic->tail_size, obu);\n+        if (ret < 0)\n+            goto end;\n+\n+        pic->tail_size /= 8;\n+    }\n+\n+    memcpy(data, &priv->fh_data, MAX_PARAM_BUFFER_SIZE * sizeof(char));\n+    *data_len = priv->fh_data_len;\n+\n+end:\n+    ff_cbs_fragment_reset(obu);\n+    return ret;\n+}\n+\n+static const VAAPIEncodeProfile vaapi_encode_av1_profiles[] = {\n+    { FF_PROFILE_AV1_MAIN,  8, 3, 1, 1, VAProfileAV1Profile0 },\n+    { FF_PROFILE_AV1_MAIN, 10, 3, 1, 1, VAProfileAV1Profile0 },\n+    { FF_PROFILE_UNKNOWN }\n+};\n+\n+static const VAAPIEncodeType vaapi_encode_type_av1 = {\n+    .profiles        = vaapi_encode_av1_profiles,\n+    .flags           = FLAG_B_PICTURES | FLAG_TIMESTAMP_NO_DELAY,\n+    .default_quality = 25,\n+\n+    .get_encoder_caps = &vaapi_encode_av1_get_encoder_caps,\n+    .configure        = &vaapi_encode_av1_configure,\n+\n+    .sequence_header_type  = VAEncPackedHeaderSequence,\n+    .sequence_params_size  = sizeof(VAEncSequenceParameterBufferAV1),\n+    .init_sequence_params  = &vaapi_encode_av1_init_sequence_params,\n+    .write_sequence_header = &vaapi_encode_av1_write_sequence_header,\n+\n+    .picture_priv_data_size = sizeof(VAAPIEncodeAV1Picture),\n+    .picture_header_type    = VAEncPackedHeaderPicture,\n+    .picture_params_size    = sizeof(VAEncPictureParameterBufferAV1),\n+    .init_picture_params    = &vaapi_encode_av1_init_picture_params,\n+    .write_picture_header   = &vaapi_encode_av1_write_picture_header,\n+\n+    .slice_params_size = sizeof(VAEncTileGroupBufferAV1),\n+    .init_slice_params = &vaapi_encode_av1_init_slice_params,\n+};\n+\n+static av_cold int vaapi_encode_av1_init(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeContext      *ctx = avctx->priv_data;\n+    VAAPIEncodeAV1Context  *priv = avctx->priv_data;\n+    VAConfigAttrib attr;\n+    VAStatus vas;\n+    int ret;\n+\n+    ctx->codec = &vaapi_encode_type_av1;\n+\n+    ctx->desired_packed_headers =\n+        VA_ENC_PACKED_HEADER_SEQUENCE |\n+        VA_ENC_PACKED_HEADER_PICTURE;\n+\n+    if (avctx->profile == FF_PROFILE_UNKNOWN)\n+        avctx->profile = priv->profile;\n+    if (avctx->level == FF_PROFILE_UNKNOWN)\n+        avctx->level = priv->level;\n+\n+    if (avctx->level != FF_PROFILE_UNKNOWN && avctx->level & ~0x1f) {\n+        av_log(avctx, AV_LOG_ERROR, \"Invalid level %d\\n\", avctx->level);\n+        return AVERROR(EINVAL);\n+    }\n+\n+    ret = ff_vaapi_encode_init(avctx);\n+    if (ret < 0)\n+        return ret;\n+\n+    attr.type = VAConfigAttribEncAV1;\n+    vas = vaGetConfigAttributes(ctx->hwctx->display,\n+                                ctx->va_profile,\n+                                ctx->va_entrypoint,\n+                                &attr, 1);\n+    if (vas != VA_STATUS_SUCCESS) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to query \"\n+               \"config attribute: %d (%s).\\n\", vas, vaErrorStr(vas));\n+        return AVERROR_EXTERNAL;\n+    } else if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {\n+        priv->attr.value = 0;\n+        av_log(avctx, AV_LOG_WARNING, \"Attribute type:%d is not \"\n+               \"supported.\\n\", attr.type);\n+    } else {\n+        priv->attr.value = attr.value;\n+    }\n+\n+    attr.type = VAConfigAttribEncAV1Ext1;\n+    vas = vaGetConfigAttributes(ctx->hwctx->display,\n+                                ctx->va_profile,\n+                                ctx->va_entrypoint,\n+                                &attr, 1);\n+    if (vas != VA_STATUS_SUCCESS) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to query \"\n+               \"config attribute: %d (%s).\\n\", vas, vaErrorStr(vas));\n+        return AVERROR_EXTERNAL;\n+    } else if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {\n+        priv->attr_ext1.value = 0;\n+        av_log(avctx, AV_LOG_WARNING, \"Attribute type:%d is not \"\n+               \"supported.\\n\", attr.type);\n+    } else {\n+        priv->attr_ext1.value = attr.value;\n+    }\n+\n+    /** This attr provides essential indicators, return error if not support. */\n+    attr.type = VAConfigAttribEncAV1Ext2;\n+    vas = vaGetConfigAttributes(ctx->hwctx->display,\n+                                ctx->va_profile,\n+                                ctx->va_entrypoint,\n+                                &attr, 1);\n+    if (vas != VA_STATUS_SUCCESS || attr.value == VA_ATTRIB_NOT_SUPPORTED) {\n+        av_log(avctx, AV_LOG_ERROR, \"Failed to query \"\n+               \"config attribute: %d (%s).\\n\", vas, vaErrorStr(vas));\n+        return AVERROR_EXTERNAL;\n+    } else {\n+        priv->attr_ext2.value = attr.value;\n+    }\n+\n+    ret = vaapi_encode_av1_set_tile(avctx);\n+    if (ret < 0)\n+        return ret;\n+\n+    return 0;\n+}\n+\n+static av_cold int vaapi_encode_av1_close(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeAV1Context *priv = avctx->priv_data;\n+\n+    ff_cbs_fragment_free(&priv->current_obu);\n+    ff_cbs_close(&priv->cbc);\n+\n+    return ff_vaapi_encode_close(avctx);\n+}\n+\n+#define OFFSET(x) offsetof(VAAPIEncodeAV1Context, x)\n+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)\n+\n+static const AVOption vaapi_encode_av1_options[] = {\n+    VAAPI_ENCODE_COMMON_OPTIONS,\n+    VAAPI_ENCODE_RC_OPTIONS,\n+    { \"profile\", \"Set profile (seq_profile)\",\n+      OFFSET(profile), AV_OPT_TYPE_INT,\n+      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS, \"profile\" },\n+\n+#define PROFILE(name, value)  name, NULL, 0, AV_OPT_TYPE_CONST, \\\n+    { .i64 = value }, 0, 0, FLAGS, \"profile\"\n+    { PROFILE(\"main\",               FF_PROFILE_AV1_MAIN) },\n+    { PROFILE(\"high\",               FF_PROFILE_AV1_HIGH) },\n+    { PROFILE(\"professional\",       FF_PROFILE_AV1_PROFESSIONAL) },\n+#undef PROFILE\n+\n+    { \"tier\", \"Set tier (seq_tier)\",\n+      OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, \"tier\" },\n+    { \"main\", NULL, 0, AV_OPT_TYPE_CONST,\n+      { .i64 = 0 }, 0, 0, FLAGS, \"tier\" },\n+    { \"high\", NULL, 0, AV_OPT_TYPE_CONST,\n+      { .i64 = 1 }, 0, 0, FLAGS, \"tier\" },\n+    { \"level\", \"Set level (seq_level_idx)\",\n+      OFFSET(level), AV_OPT_TYPE_INT,\n+      { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0x1f, FLAGS, \"level\" },\n+\n+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \\\n+      { .i64 = value }, 0, 0, FLAGS, \"level\"\n+    { LEVEL(\"2.0\",  0) },\n+    { LEVEL(\"2.1\",  1) },\n+    { LEVEL(\"3.0\",  4) },\n+    { LEVEL(\"3.1\",  5) },\n+    { LEVEL(\"4.0\",  8) },\n+    { LEVEL(\"4.1\",  9) },\n+    { LEVEL(\"5.0\", 12) },\n+    { LEVEL(\"5.1\", 13) },\n+    { LEVEL(\"5.2\", 14) },\n+    { LEVEL(\"5.3\", 15) },\n+    { LEVEL(\"6.0\", 16) },\n+    { LEVEL(\"6.1\", 17) },\n+    { LEVEL(\"6.2\", 18) },\n+    { LEVEL(\"6.3\", 19) },\n+#undef LEVEL\n+\n+    { \"tiles\", \"Tile columns x rows (Use minimal tile column/row number automatically by default)\",\n+      OFFSET(tile_cols), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, FLAGS },\n+    { \"tile_groups\", \"Number of tile groups for encoding\",\n+      OFFSET(tile_groups), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, AV1_MAX_TILE_ROWS * AV1_MAX_TILE_COLS, FLAGS },\n+\n+    { NULL },\n+};\n+\n+static const FFCodecDefault vaapi_encode_av1_defaults[] = {\n+    { \"b\",              \"0\"   },\n+    { \"bf\",             \"2\"   },\n+    { \"g\",              \"120\" },\n+    { \"qmin\",           \"1\"   },\n+    { \"qmax\",           \"255\" },\n+    { NULL },\n+};\n+\n+static const AVClass vaapi_encode_av1_class = {\n+    .class_name = \"av1_vaapi\",\n+    .item_name  = av_default_item_name,\n+    .option     = vaapi_encode_av1_options,\n+    .version    = LIBAVUTIL_VERSION_INT,\n+};\n+\n+const FFCodec ff_av1_vaapi_encoder = {\n+    .p.name         = \"av1_vaapi\",\n+    CODEC_LONG_NAME(\"AV1 (VAAPI)\"),\n+    .p.type         = AVMEDIA_TYPE_VIDEO,\n+    .p.id           = AV_CODEC_ID_AV1,\n+    .priv_data_size = sizeof(VAAPIEncodeAV1Context),\n+    .init           = &vaapi_encode_av1_init,\n+    FF_CODEC_RECEIVE_PACKET_CB(&ff_vaapi_encode_receive_packet),\n+    .close          = &vaapi_encode_av1_close,\n+    .p.priv_class   = &vaapi_encode_av1_class,\n+    .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE |\n+                      AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,\n+    .caps_internal  = FF_CODEC_CAP_NOT_INIT_THREADSAFE |\n+                      FF_CODEC_CAP_INIT_CLEANUP,\n+    .defaults       = vaapi_encode_av1_defaults,\n+    .p.pix_fmts = (const enum AVPixelFormat[]) {\n+        AV_PIX_FMT_VAAPI,\n+        AV_PIX_FMT_NONE,\n+    },\n+    .hw_configs     = ff_vaapi_encode_hw_configs,\n+    .p.wrapper_name = \"vaapi\",\n+};\n"
  },
  {
    "path": "alvr/xtask/patches/0001-clip-constants-used-with-shift-instr.patch",
    "content": "From effadce6c756247ea8bae32dc13bb3e6f464f0eb Mon Sep 17 00:00:00 2001\nFrom: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= <remi@remlab.net>\nDate: Sun, 16 Jul 2023 18:18:02 +0300\nSubject: [PATCH] avcodec/x86/mathops: clip constants used with shift\n instructions within inline assembly\n\nFixes assembling with binutil as >= 2.41\n\nSigned-off-by: James Almer <jamrial@gmail.com>\n---\n libavcodec/x86/mathops.h | 26 +++++++++++++++++++++++---\n 1 file changed, 23 insertions(+), 3 deletions(-)\n\ndiff --git a/libavcodec/x86/mathops.h b/libavcodec/x86/mathops.h\nindex 6298f5ed19..ca7e2dffc1 100644\n--- a/libavcodec/x86/mathops.h\n+++ b/libavcodec/x86/mathops.h\n@@ -35,12 +35,20 @@\n static av_always_inline av_const int MULL(int a, int b, unsigned shift)\n {\n     int rt, dummy;\n+    if (__builtin_constant_p(shift))\n     __asm__ (\n         \"imull %3               \\n\\t\"\n         \"shrdl %4, %%edx, %%eax \\n\\t\"\n         :\"=a\"(rt), \"=d\"(dummy)\n-        :\"a\"(a), \"rm\"(b), \"ci\"((uint8_t)shift)\n+        :\"a\"(a), \"rm\"(b), \"i\"(shift & 0x1F)\n     );\n+    else\n+        __asm__ (\n+            \"imull %3               \\n\\t\"\n+            \"shrdl %4, %%edx, %%eax \\n\\t\"\n+            :\"=a\"(rt), \"=d\"(dummy)\n+            :\"a\"(a), \"rm\"(b), \"c\"((uint8_t)shift)\n+        );\n     return rt;\n }\n \n@@ -113,19 +121,31 @@ __asm__ volatile(\\\n // avoid +32 for shift optimization (gcc should do that ...)\n #define NEG_SSR32 NEG_SSR32\n static inline  int32_t NEG_SSR32( int32_t a, int8_t s){\n+    if (__builtin_constant_p(s))\n     __asm__ (\"sarl %1, %0\\n\\t\"\n          : \"+r\" (a)\n-         : \"ic\" ((uint8_t)(-s))\n+         : \"i\" (-s & 0x1F)\n     );\n+    else\n+        __asm__ (\"sarl %1, %0\\n\\t\"\n+               : \"+r\" (a)\n+               : \"c\" ((uint8_t)(-s))\n+        );\n     return a;\n }\n \n #define NEG_USR32 NEG_USR32\n static inline uint32_t NEG_USR32(uint32_t a, int8_t s){\n+    if (__builtin_constant_p(s))\n     __asm__ (\"shrl %1, %0\\n\\t\"\n          : \"+r\" (a)\n-         : \"ic\" ((uint8_t)(-s))\n+         : \"i\" (-s & 0x1F)\n     );\n+    else\n+        __asm__ (\"shrl %1, %0\\n\\t\"\n+               : \"+r\" (a)\n+               : \"c\" ((uint8_t)(-s))\n+        );\n     return a;\n }\n \n-- \n2.30.2 \n"
  },
  {
    "path": "alvr/xtask/patches/0001-guid-conftest.patch",
    "content": "From 03823ac0c6a38bd6ba972539e3203a592579792f Mon Sep 17 00:00:00 2001\nFrom: Timo Rothenpieler <timo@rothenpieler.org>\nDate: Thu, 1 Jun 2023 23:24:43 +0200\nSubject: [PATCH] configure: use non-deprecated nvenc GUID for conftest\n\n---\n configure | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/configure b/configure\nindex 495493aa0e8a..ae56540f4eb0 100755\n--- a/configure\n+++ b/configure\n@@ -7079,7 +7079,7 @@ enabled nvenc &&\n     test_cc -I$source_path <<EOF || disable nvenc\n #include <ffnvcodec/nvEncodeAPI.h>\n NV_ENCODE_API_FUNCTION_LIST flist;\n-void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_PRESET_HQ_GUID } }; }\n+void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_CODEC_H264_GUID } }; }\n int main(void) { return 0; }\n EOF\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-lavu-hwcontext_vulkan-Fix-importing-RGBx-frames-to-C.patch",
    "content": "From f867c4c56ee75d633db2300c0822bfa0020a056e Mon Sep 17 00:00:00 2001\nFrom: David Rosca <nowrep@gmail.com>\nDate: Tue, 28 Nov 2023 14:04:20 +0100\nSubject: [PATCH] lavu/hwcontext_vulkan: Fix importing RGBx frames to CUDA\n\nRGBx formats needs NumChannels = 4, but the old code would set it to 1.\n---\n libavutil/hwcontext_vulkan.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c\nindex 204b57c011..e3bd6ace9b 100644\n--- a/libavutil/hwcontext_vulkan.c\n+++ b/libavutil/hwcontext_vulkan.c\n@@ -2859,7 +2859,7 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,\n                 .arrayDesc = {\n                     .Depth = 0,\n                     .Format = cufmt,\n-                    .NumChannels = 1 + ((planes == 2) && i),\n+                    .NumChannels = desc->comp[i].step,\n                     .Flags = 0,\n                 },\n                 .numLevels = 1,\n-- \n2.43.0\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-update-rc-modes.patch",
    "content": "From 1ebb0e43f9a15a12cd94db44e4bc5424f8a5b0c9 Mon Sep 17 00:00:00 2001\nFrom: Timo Rothenpieler <timo@rothenpieler.org>\nDate: Thu, 1 Jun 2023 23:46:46 +0200\nSubject: [PATCH] avcodec/nvenc: stop using deprecated rc modes with SDK 12.1\n\n---\n libavcodec/nvenc.c      | 11 +++++++++++\n libavcodec/nvenc.h      |  5 +++++\n libavcodec/nvenc_h264.c | 12 ++++++++++++\n libavcodec/nvenc_hevc.c | 12 ++++++++++++\n 4 files changed, 40 insertions(+)\n\ndiff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c\nindex 15dd819a58f7..3c68ed39300f 100644\n--- a/libavcodec/nvenc.c\n+++ b/libavcodec/nvenc.c\n@@ -43,9 +43,14 @@\n #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, dl_fn->cuda_dl, x)\n\n #define NVENC_CAP 0x30\n+\n+#ifndef NVENC_NO_DEPRECATED_RC\n #define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR ||             \\\n                     rc == NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ || \\\n                     rc == NV_ENC_PARAMS_RC_CBR_HQ)\n+#else\n+#define IS_CBR(rc) (rc == NV_ENC_PARAMS_RC_CBR)\n+#endif\n\n const enum AVPixelFormat ff_nvenc_pix_fmts[] = {\n     AV_PIX_FMT_YUV420P,\n@@ -904,6 +909,7 @@ static void nvenc_override_rate_control(AVCodecContext *avctx)\n     case NV_ENC_PARAMS_RC_CONSTQP:\n         set_constqp(avctx);\n         return;\n+#ifndef NVENC_NO_DEPRECATED_RC\n     case NV_ENC_PARAMS_RC_VBR_MINQP:\n         if (avctx->qmin < 0) {\n             av_log(avctx, AV_LOG_WARNING,\n@@ -914,12 +920,15 @@ static void nvenc_override_rate_control(AVCodecContext *avctx)\n         }\n         /* fall through */\n     case NV_ENC_PARAMS_RC_VBR_HQ:\n+#endif\n     case NV_ENC_PARAMS_RC_VBR:\n         set_vbr(avctx);\n         break;\n     case NV_ENC_PARAMS_RC_CBR:\n+#ifndef NVENC_NO_DEPRECATED_RC\n     case NV_ENC_PARAMS_RC_CBR_HQ:\n     case NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ:\n+#endif\n         break;\n     }\n\n@@ -1193,12 +1202,14 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx)\n\n     h264->outputPictureTimingSEI = 1;\n\n+#ifndef NVENC_NO_DEPRECATED_RC\n     if (cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ ||\n         cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR_HQ ||\n         cc->rcParams.rateControlMode == NV_ENC_PARAMS_RC_VBR_HQ) {\n         h264->adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;\n         h264->fmoMode = NV_ENC_H264_FMO_DISABLE;\n     }\n+#endif\n\n     if (ctx->flags & NVENC_LOSSLESS) {\n         h264->qpPrimeYZeroTransformBypassFlag = 1;\ndiff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h\nindex 6cedd5bc2756..3a4b456a41dd 100644\n--- a/libavcodec/nvenc.h\n+++ b/libavcodec/nvenc.h\n@@ -78,6 +78,11 @@ typedef void ID3D11Device;\n #define NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH\n #endif\n\n+// SDK 12.1 compile time feature checks\n+#if NVENCAPI_CHECK_VERSION(12, 1)\n+#define NVENC_NO_DEPRECATED_RC\n+#endif\n+\n typedef struct NvencSurface\n {\n     NV_ENC_INPUT_PTR input_surface;\ndiff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c\nindex 5dc2961c3bcf..698615855bf5 100644\n--- a/libavcodec/nvenc_h264.c\n+++ b/libavcodec/nvenc_h264.c\n@@ -100,6 +100,7 @@ static const AVOption options[] = {\n     { \"constqp\",      \"Constant QP mode\",                   0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP },                   0, 0, VE, \"rc\" },\n     { \"vbr\",          \"Variable bitrate mode\",              0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR },                       0, 0, VE, \"rc\" },\n     { \"cbr\",          \"Constant bitrate mode\",              0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR },                       0, 0, VE, \"rc\" },\n+#ifndef NVENC_NO_DEPRECATED_RC\n     { \"vbr_minqp\",    \"Variable bitrate mode with MinQP (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) },            0, 0, VE, \"rc\" },\n     { \"ll_2pass_quality\", \"Multi-pass optimized for image quality (deprecated)\",\n                                                             0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) },       0, 0, VE, \"rc\" },\n@@ -109,6 +110,17 @@ static const AVOption options[] = {\n     { \"cbr_ld_hq\",    \"Constant bitrate low delay high quality mode\", 0,          AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) },      0, 0, VE, \"rc\" },\n     { \"cbr_hq\",       \"Constant bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) },               0, 0, VE, \"rc\" },\n     { \"vbr_hq\",       \"Variable bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) },               0, 0, VE, \"rc\" },\n+#else\n+    { \"vbr_minqp\",    \"Variable bitrate mode with MinQP (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"ll_2pass_quality\", \"Multi-pass optimized for image quality (deprecated)\",\n+                                                            0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"ll_2pass_size\", \"Multi-pass optimized for constant frame size (deprecated)\",\n+                                                            0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"vbr_2pass\",    \"Multi-pass variable bitrate mode (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"cbr_ld_hq\",    \"Constant bitrate low delay high quality mode\", 0,          AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"cbr_hq\",       \"Constant bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"vbr_hq\",       \"Variable bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+#endif\n     { \"rc-lookahead\", \"Number of frames to look ahead for rate-control\",\n                                                             OFFSET(rc_lookahead), AV_OPT_TYPE_INT,   { .i64 = 0 }, 0, INT_MAX, VE },\n     { \"surfaces\",     \"Number of concurrent surfaces\",      OFFSET(nb_surfaces),  AV_OPT_TYPE_INT,   { .i64 = 0 }, 0, MAX_REGISTERED_FRAMES, VE },\ndiff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c\nindex 1362a927c8e4..d99077f17055 100644\n--- a/libavcodec/nvenc_hevc.c\n+++ b/libavcodec/nvenc_hevc.c\n@@ -89,6 +89,7 @@ static const AVOption options[] = {\n     { \"constqp\",      \"Constant QP mode\",                   0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP },                   0, 0, VE, \"rc\" },\n     { \"vbr\",          \"Variable bitrate mode\",              0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR },                       0, 0, VE, \"rc\" },\n     { \"cbr\",          \"Constant bitrate mode\",              0,                    AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR },                       0, 0, VE, \"rc\" },\n+#ifndef NVENC_NO_DEPRECATED_RC\n     { \"vbr_minqp\",    \"Variable bitrate mode with MinQP (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) },            0, 0, VE, \"rc\" },\n     { \"ll_2pass_quality\", \"Multi-pass optimized for image quality (deprecated)\",\n                                                             0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) },       0, 0, VE, \"rc\" },\n@@ -98,6 +99,17 @@ static const AVOption options[] = {\n     { \"cbr_ld_hq\",    \"Constant bitrate low delay high quality mode\", 0,          AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) },      0, 0, VE, \"rc\" },\n     { \"cbr_hq\",       \"Constant bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) },               0, 0, VE, \"rc\" },\n     { \"vbr_hq\",       \"Variable bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) },               0, 0, VE, \"rc\" },\n+#else\n+    { \"vbr_minqp\",    \"Variable bitrate mode with MinQP (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"ll_2pass_quality\", \"Multi-pass optimized for image quality (deprecated)\",\n+                                                            0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"ll_2pass_size\", \"Multi-pass optimized for constant frame size (deprecated)\",\n+                                                            0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"vbr_2pass\",    \"Multi-pass variable bitrate mode (deprecated)\", 0,         AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+    { \"cbr_ld_hq\",    \"Constant bitrate low delay high quality mode\", 0,          AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"cbr_hq\",       \"Constant bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) },                  0, 0, VE, \"rc\" },\n+    { \"vbr_hq\",       \"Variable bitrate high quality mode\", 0,                    AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) },                  0, 0, VE, \"rc\" },\n+#endif\n     { \"rc-lookahead\", \"Number of frames to look ahead for rate-control\",\n                                                             OFFSET(rc_lookahead), AV_OPT_TYPE_INT,   { .i64 = 0 }, 0, INT_MAX, VE },\n     { \"surfaces\",     \"Number of concurrent surfaces\",      OFFSET(nb_surfaces),  AV_OPT_TYPE_INT,   { .i64 = 0 }, 0, MAX_REGISTERED_FRAMES, VE },\n"
  },
  {
    "path": "alvr/xtask/patches/0001-vaapi_encode-Add-filler_data-option.patch",
    "content": "From 920a052dbf4ffd92b25df68acbb2b36f5b51128d Mon Sep 17 00:00:00 2001\nFrom: David Rosca <nowrep@gmail.com>\nDate: Sat, 5 Aug 2023 11:09:35 +0200\nSubject: [PATCH] vaapi_encode: Add filler_data option\n\n---\n libavcodec/vaapi_encode.c |  1 +\n libavcodec/vaapi_encode.h | 10 +++++++++-\n 2 files changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex bfca315a7a..895e12a6a8 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -1861,6 +1861,7 @@ rc_mode_found:\n             .quality_factor     = rc_quality,\n #endif\n         };\n+        ctx->rc_params.rc_flags.bits.disable_bit_stuffing = !ctx->filler_data;\n         vaapi_encode_add_global_param(avctx,\n                                       VAEncMiscParameterTypeRateControl,\n                                       &ctx->rc_params,\ndiff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h\nindex a1e639f56b..682b943475 100644\n--- a/libavcodec/vaapi_encode.h\n+++ b/libavcodec/vaapi_encode.h\n@@ -198,6 +198,9 @@ typedef struct VAAPIEncodeContext {\n     // Max Frame Size\n     int             max_frame_size;\n \n+    // Filler Data\n+    int             filler_data;\n+\n     // Explicitly set RC mode (otherwise attempt to pick from\n     // available modes).\n     int             explicit_rc_mode;\n@@ -490,7 +493,12 @@ int ff_vaapi_encode_close(AVCodecContext *avctx);\n     { \"max_frame_size\", \\\n       \"Maximum frame size (in bytes)\",\\\n       OFFSET(common.max_frame_size), AV_OPT_TYPE_INT, \\\n-      { .i64 = 0 }, 0, INT_MAX, FLAGS }\n+      { .i64 = 0 }, 0, INT_MAX, FLAGS }, \\\n+    { \"filler_data\", \\\n+      \"Enable filler data\", \\\n+      OFFSET(common.filler_data), AV_OPT_TYPE_BOOL, \\\n+      { .i64 = 1 }, 0, 1, FLAGS }\n+\n \n #define VAAPI_ENCODE_RC_MODE(name, desc) \\\n     { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \\\n-- \n2.41.0\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-vaapi_encode-Allow-to-dynamically-change-bitrate-and.patch",
    "content": "From 34c0d23c4e16775811f229cb190e72ba7d1e45ae Mon Sep 17 00:00:00 2001\nFrom: David Rosca <nowrep@gmail.com>\nDate: Fri, 17 Feb 2023 12:40:10 +0100\nSubject: [PATCH] vaapi_encode: Allow to dynamically change bitrate and\n framerate\n\n---\n libavcodec/vaapi_encode.c | 147 ++++++++++++++++++++++++++++++++++++++\n libavcodec/vaapi_encode.h |   1 +\n 2 files changed, 148 insertions(+)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex 9a58661b51..4add6458fd 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -262,6 +262,130 @@ static int vaapi_encode_make_tile_slice(AVCodecContext *avctx,\n     return 0;\n }\n \n+static av_cold int vaapi_encode_update_rate_control(AVCodecContext *avctx)\n+{\n+    VAAPIEncodeContext *ctx = avctx->priv_data;\n+    int64_t rc_bits_per_second;\n+    int     rc_target_percentage;\n+    int     rc_window_size;\n+    int64_t hrd_buffer_size;\n+    int64_t hrd_initial_buffer_fullness;\n+    int fr_num, fr_den;\n+    unsigned int frame_rate;\n+\n+    if (!ctx->rc_mode || !ctx->rc_mode->bitrate) {\n+        return 0;\n+    }\n+\n+    if (ctx->rc_mode->mode == RC_MODE_AVBR) {\n+        // For maximum confusion AVBR is hacked into the existing API\n+        // by overloading some of the fields with completely different\n+        // meanings.\n+\n+        // Target percentage does not apply in AVBR mode.\n+        rc_bits_per_second = avctx->bit_rate;\n+\n+        // Accuracy tolerance range for meeting the specified target\n+        // bitrate.  It's very unclear how this is actually intended\n+        // to work - since we do want to get the specified bitrate,\n+        // set the accuracy to 100% for now.\n+        rc_target_percentage = 100;\n+\n+        // Convergence period in frames.  The GOP size reflects the\n+        // user's intended block size for cutting, so reusing that\n+        // as the convergence period seems a reasonable default.\n+        rc_window_size = avctx->gop_size > 0 ? avctx->gop_size : 60;\n+\n+    } else if (ctx->rc_mode->maxrate) {\n+        if (avctx->rc_max_rate > 0) {\n+            if (avctx->rc_max_rate < avctx->bit_rate) {\n+                av_log(avctx, AV_LOG_ERROR, \"Invalid bitrate settings: \"\n+                       \"bitrate (%\"PRId64\") must not be greater than \"\n+                       \"maxrate (%\"PRId64\").\\n\", avctx->bit_rate,\n+                       avctx->rc_max_rate);\n+                return 0;\n+            }\n+            rc_bits_per_second   = avctx->rc_max_rate;\n+            rc_target_percentage = (avctx->bit_rate * 100) /\n+                                   avctx->rc_max_rate;\n+        } else {\n+            // We only have a target bitrate, but this mode requires\n+            // that a maximum rate be supplied as well.  Since the\n+            // user does not want this to be a constraint, arbitrarily\n+            // pick a maximum rate of double the target rate.\n+            rc_bits_per_second   = 2 * avctx->bit_rate;\n+            rc_target_percentage = 50;\n+        }\n+    } else {\n+        if (avctx->rc_max_rate > avctx->bit_rate) {\n+            av_log(avctx, AV_LOG_WARNING, \"Max bitrate is ignored \"\n+                   \"in %s RC mode.\\n\", ctx->rc_mode->name);\n+        }\n+        rc_bits_per_second   = avctx->bit_rate;\n+        rc_target_percentage = 100;\n+    }\n+\n+    if (ctx->rc_mode->hrd) {\n+        if (avctx->rc_buffer_size)\n+            hrd_buffer_size = avctx->rc_buffer_size;\n+        else if (avctx->rc_max_rate > 0)\n+            hrd_buffer_size = avctx->rc_max_rate;\n+        else\n+            hrd_buffer_size = avctx->bit_rate;\n+        if (avctx->rc_initial_buffer_occupancy) {\n+            if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {\n+                av_log(avctx, AV_LOG_ERROR, \"Invalid RC buffer settings: \"\n+                       \"must have initial buffer size (%d) <= \"\n+                       \"buffer size (%\"PRId64\").\\n\",\n+                       avctx->rc_initial_buffer_occupancy, hrd_buffer_size);\n+                return 0;\n+            }\n+            hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;\n+        } else {\n+            hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;\n+        }\n+\n+        rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;\n+    } else {\n+        if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) {\n+            av_log(avctx, AV_LOG_WARNING, \"Buffering settings are ignored \"\n+                   \"in %s RC mode.\\n\", ctx->rc_mode->name);\n+        }\n+\n+        hrd_buffer_size             = 0;\n+        hrd_initial_buffer_fullness = 0;\n+\n+        if (ctx->rc_mode->mode != RC_MODE_AVBR) {\n+            // Already set (with completely different meaning) for AVBR.\n+            rc_window_size = 1000;\n+        }\n+    }\n+\n+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0)\n+        av_reduce(&fr_num, &fr_den,\n+                  avctx->framerate.num, avctx->framerate.den, 65535);\n+    else\n+        av_reduce(&fr_num, &fr_den,\n+                  avctx->time_base.den, avctx->time_base.num, 65535);\n+\n+    frame_rate = (unsigned int)fr_den << 16 | fr_num;\n+\n+    if (ctx->va_bit_rate == rc_bits_per_second && ctx->va_frame_rate == frame_rate) {\n+        return 0;\n+    }\n+\n+    ctx->va_bit_rate                        = rc_bits_per_second;\n+    ctx->rc_params.bits_per_second          = rc_bits_per_second;\n+    ctx->rc_params.target_percentage        = rc_target_percentage;\n+    ctx->rc_params.window_size              = rc_window_size;\n+    ctx->hrd_params.initial_buffer_fullness = hrd_initial_buffer_fullness;\n+    ctx->hrd_params.buffer_size             = hrd_buffer_size;\n+    ctx->va_frame_rate                      = frame_rate;\n+    ctx->fr_params.framerate                = frame_rate;\n+\n+    return 1;\n+}\n+\n static int vaapi_encode_issue(AVCodecContext *avctx,\n                               VAAPIEncodePicture *pic)\n {\n@@ -340,6 +464,7 @@ static int vaapi_encode_issue(AVCodecContext *avctx,\n     }\n \n     if (pic->type == PICTURE_TYPE_IDR) {\n+        vaapi_encode_update_rate_control(avctx);\n         for (i = 0; i < ctx->nb_global_params; i++) {\n             err = vaapi_encode_make_misc_param_buffer(avctx, pic,\n                                                       ctx->global_params_type[i],\n@@ -348,6 +473,27 @@ static int vaapi_encode_issue(AVCodecContext *avctx,\n             if (err < 0)\n                 goto fail;\n         }\n+    } else if (vaapi_encode_update_rate_control(avctx)) {\n+            err = vaapi_encode_make_misc_param_buffer(avctx, pic,\n+                                                      VAEncMiscParameterTypeRateControl,\n+                                                      &ctx->rc_params,\n+                                                      sizeof(ctx->rc_params));\n+            if (err < 0)\n+                goto fail;\n+\n+            err = vaapi_encode_make_misc_param_buffer(avctx, pic,\n+                                                      VAEncMiscParameterTypeHRD,\n+                                                      &ctx->hrd_params,\n+                                                      sizeof(ctx->hrd_params));\n+            if (err < 0)\n+                goto fail;\n+\n+            err = vaapi_encode_make_misc_param_buffer(avctx, pic,\n+                                                      VAEncMiscParameterTypeFrameRate,\n+                                                      &ctx->fr_params,\n+                                                      sizeof(ctx->fr_params));\n+            if (err < 0)\n+                goto fail;\n     }\n \n     if (ctx->codec->init_picture_params) {\n@@ -1875,6 +2021,7 @@ rc_mode_found:\n     ctx->fr_params = (VAEncMiscParameterFrameRate) {\n         .framerate = (unsigned int)fr_den << 16 | fr_num,\n     };\n+    ctx->va_frame_rate = ctx->fr_params.framerate;\n #if VA_CHECK_VERSION(0, 40, 0)\n     vaapi_encode_add_global_param(avctx,\n                                   VAEncMiscParameterTypeFrameRate,\ndiff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h\nindex 359f954fff..e7b6482c39 100644\n--- a/libavcodec/vaapi_encode.h\n+++ b/libavcodec/vaapi_encode.h\n@@ -235,6 +235,7 @@ typedef struct VAAPIEncodeContext {\n     unsigned int    va_rc_mode;\n     // Bitrate for codec-specific encoder parameters.\n     unsigned int    va_bit_rate;\n+    unsigned int    va_frame_rate;\n     // Packed headers which will actually be sent.\n     unsigned int    va_packed_headers;\n \n-- \n2.39.2\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-vaapi_encode-Enable-global-header.patch",
    "content": "From 03e6b5617c94bf6073a17a0a7054e70345b512ae Mon Sep 17 00:00:00 2001\nFrom: David Rosca <nowrep@gmail.com>\nDate: Sat, 1 Jul 2023 10:35:16 +0200\nSubject: [PATCH] vaapi_encode: Force enable global header\n\n---\n libavcodec/vaapi_encode.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c\nindex bfca315a7a..ecce9bd721 100644\n--- a/libavcodec/vaapi_encode.c\n+++ b/libavcodec/vaapi_encode.c\n@@ -2719,8 +2719,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)\n         }\n     }\n \n-    if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&\n-        ctx->codec->write_sequence_header &&\n+    if (ctx->codec->write_sequence_header &&\n         avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {\n         char data[MAX_PARAM_BUFFER_SIZE];\n         size_t bit_len = 8 * sizeof(data);\n-- \n2.41.0\n\n"
  },
  {
    "path": "alvr/xtask/patches/0001-vaapi_encode_h265-Set-vui_parameters_present_flag.patch",
    "content": "From d96227806cab0de91c4db6b808b842c5b1477ebd Mon Sep 17 00:00:00 2001\nFrom: David Rosca <nowrep@gmail.com>\nDate: Fri, 21 Jul 2023 21:55:56 +0200\nSubject: [PATCH] vaapi_encode_h265: Set vui_parameters_present_flag\n\n---\n libavcodec/vaapi_encode_h265.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c\nindex aa7e532f9a..91212dfb58 100644\n--- a/libavcodec/vaapi_encode_h265.c\n+++ b/libavcodec/vaapi_encode_h265.c\n@@ -690,7 +690,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)\n             sps->log2_min_pcm_luma_coding_block_size_minus3 +\n             sps->log2_diff_max_min_pcm_luma_coding_block_size,\n \n-        .vui_parameters_present_flag = 0,\n+        .vui_parameters_present_flag = 1,\n     };\n \n     *vpic = (VAEncPictureParameterBufferHEVC) {\n-- \n2.41.0\n\n"
  },
  {
    "path": "alvr/xtask/resources/alvr.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nType=Application\nName=ALVR\nGenericName=Game\nComment=ALVR is an open source remote VR display which allows playing SteamVR games on a standalone headset such as Gear VR or Oculus Go/Quest.\nExec=alvr_dashboard\nIcon=alvr\nCategories=Game;\nStartupNotify=true\nStartupWMClass=alvr.dashboard\n"
  },
  {
    "path": "alvr/xtask/resources/driver.vrdrivermanifest",
    "content": "{\n\t\"alwaysActivate\": true,\n\t\"name\" : \"alvr_server\",\n\t\"redirectsDisplay\": true,\n\n\t\"hmd_presence\" :\n\t[\n\t\t\"*.*\"\n\t]\n}\n"
  },
  {
    "path": "alvr/xtask/src/build.rs",
    "content": "use alvr_filesystem::{self as afs, Layout};\nuse std::{\n    env,\n    fmt::{self, Display, Formatter},\n    fs,\n    path::PathBuf,\n    vec,\n};\nuse xshell::{Shell, cmd};\n\n#[derive(Clone, Copy)]\npub enum Profile {\n    Debug,\n    Release,\n    Distribution,\n}\n\nimpl Display for Profile {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        let string = match self {\n            Profile::Distribution => \"distribution\",\n            Profile::Release => \"release\",\n            Profile::Debug => \"debug\",\n        };\n        write!(f, \"{string}\")\n    }\n}\n\npub fn build_server_lib(profile: Profile, root: Option<String>, reproducible: bool) {\n    let sh = Shell::new().unwrap();\n\n    let mut flags = vec![];\n    match profile {\n        Profile::Distribution => {\n            flags.push(\"--profile\");\n            flags.push(\"distribution\");\n        }\n        Profile::Release => flags.push(\"--release\"),\n        Profile::Debug => (),\n    }\n    if reproducible {\n        flags.push(\"--locked\");\n    }\n    let flags_ref = &flags;\n\n    let artifacts_dir = afs::target_dir().join(profile.to_string());\n\n    let build_dir = afs::build_dir().join(\"alvr_server_core\");\n    sh.create_dir(&build_dir).unwrap();\n\n    if let Some(root) = root {\n        sh.set_var(\"ALVR_ROOT_DIR\", root);\n    }\n\n    let _push_guard = sh.push_dir(afs::crate_dir(\"server_core\"));\n    cmd!(sh, \"cargo build {flags_ref...}\").run().unwrap();\n\n    sh.copy_file(\n        artifacts_dir.join(afs::dynlib_fname(\"alvr_server_core\")),\n        &build_dir,\n    )\n    .unwrap();\n\n    if cfg!(windows) {\n        sh.copy_file(artifacts_dir.join(\"alvr_server_core.pdb\"), &build_dir)\n            .unwrap();\n    }\n\n    let out = build_dir.join(\"alvr_server_core.h\");\n    cmd!(sh, \"cbindgen --output {out}\").run().unwrap();\n}\n\npub fn build_streamer(\n    profile: Profile,\n    gpl: bool,\n    root: Option<String>,\n    reproducible: bool,\n    profiling: bool,\n    keep_config: bool,\n) {\n    let sh = Shell::new().unwrap();\n\n    let build_layout = Layout::new(&afs::streamer_build_dir());\n\n    let mut common_flags = vec![];\n    match profile {\n        Profile::Distribution => {\n            common_flags.push(\"--profile\");\n            common_flags.push(\"distribution\");\n        }\n        Profile::Release => common_flags.push(\"--release\"),\n        Profile::Debug => (),\n    }\n    if reproducible {\n        common_flags.push(\"--locked\");\n    }\n\n    let artifacts_dir = if cfg!(all(windows, target_arch = \"aarch64\")) {\n        // Fix for cross compilation\n        const TARGET: &str = \"x86_64-pc-windows-msvc\";\n\n        common_flags.push(\"--target\");\n        common_flags.push(TARGET);\n\n        afs::target_dir().join(TARGET).join(profile.to_string())\n    } else {\n        afs::target_dir().join(profile.to_string())\n    };\n\n    let common_flags_ref = &common_flags;\n\n    let maybe_config = if keep_config {\n        fs::read_to_string(build_layout.session()).ok()\n    } else {\n        None\n    };\n\n    sh.remove_path(afs::streamer_build_dir()).ok();\n    sh.create_dir(build_layout.openvr_driver_lib_dir()).unwrap();\n    sh.create_dir(&build_layout.executables_dir).unwrap();\n\n    if let Some(config) = maybe_config {\n        fs::write(build_layout.session(), config).ok();\n    }\n\n    if let Some(root) = root {\n        sh.set_var(\"ALVR_ROOT_DIR\", root);\n    }\n\n    // build server\n    {\n        let gpl_flag = if gpl {\n            vec![\"--features\", \"gpl\"]\n        } else {\n            vec![]\n        };\n\n        let profiling_flag = if profiling {\n            vec![\"--features\", \"alvr_server_core/trace-performance\"]\n        } else {\n            vec![]\n        };\n\n        let _push_guard = sh.push_dir(afs::crate_dir(\"server_openvr\"));\n        cmd!(\n            sh,\n            \"cargo build {common_flags_ref...} {gpl_flag...} {profiling_flag...}\"\n        )\n        .run()\n        .unwrap();\n\n        sh.copy_file(\n            artifacts_dir.join(afs::dynlib_fname(\"alvr_server_openvr\")),\n            build_layout.openvr_driver_lib(),\n        )\n        .unwrap();\n\n        if cfg!(windows) {\n            sh.copy_file(\n                artifacts_dir.join(\"alvr_server_openvr.pdb\"),\n                build_layout\n                    .openvr_driver_lib_dir()\n                    .join(\"alvr_server_openvr.pdb\"),\n            )\n            .unwrap();\n        }\n    }\n\n    // Build dashboard\n    {\n        let _push_guard = sh.push_dir(afs::crate_dir(\"dashboard\"));\n        cmd!(sh, \"cargo build {common_flags_ref...}\").run().unwrap();\n\n        sh.copy_file(\n            artifacts_dir.join(afs::exec_fname(\"alvr_dashboard\")),\n            build_layout.dashboard_exe(),\n        )\n        .unwrap();\n    }\n\n    // copy dependencies\n    if cfg!(windows) {\n        sh.copy_file(\n            afs::workspace_dir().join(\"openvr/bin/win64/openvr_api.dll\"),\n            build_layout.openvr_driver_lib_dir(),\n        )\n        .unwrap();\n\n        // copy ffmpeg binaries\n        if gpl {\n            let bin_dir = &build_layout.openvr_driver_lib_dir();\n            sh.create_dir(bin_dir).unwrap();\n            for lib_path in sh\n                .read_dir(afs::deps_dir().join(\"windows/ffmpeg/bin\"))\n                .unwrap()\n                .into_iter()\n                .filter(|path| path.file_name().unwrap().to_string_lossy().contains(\".dll\"))\n            {\n                sh.copy_file(lib_path.clone(), bin_dir).unwrap();\n            }\n        }\n\n        // copy libvpl.dll\n        sh.copy_file(\n            afs::deps_dir().join(\"windows/libvpl/alvr_build/bin/libvpl.dll\"),\n            build_layout.openvr_driver_lib_dir(),\n        )\n        .unwrap();\n    } else if cfg!(target_os = \"linux\") {\n        // build compositor wrapper\n        let _push_guard = sh.push_dir(afs::crate_dir(\"vrcompositor_wrapper\"));\n        cmd!(sh, \"cargo build {common_flags_ref...}\").run().unwrap();\n        sh.create_dir(&build_layout.vrcompositor_wrapper_dir)\n            .unwrap();\n        sh.copy_file(\n            artifacts_dir.join(\"alvr_vrcompositor_wrapper\"),\n            build_layout.vrcompositor_wrapper(),\n        )\n        .unwrap();\n        sh.copy_file(\n            artifacts_dir.join(\"alvr_drm_lease_shim.so\"),\n            build_layout.drm_lease_shim(),\n        )\n        .unwrap();\n\n        // build vulkan layer\n        let _push_guard = sh.push_dir(afs::crate_dir(\"vulkan_layer\"));\n        cmd!(sh, \"cargo build {common_flags_ref...}\").run().unwrap();\n        sh.create_dir(&build_layout.libraries_dir).unwrap();\n        sh.copy_file(\n            artifacts_dir.join(afs::dynlib_fname(\"alvr_vulkan_layer\")),\n            build_layout.vulkan_layer(),\n        )\n        .unwrap();\n\n        // copy vulkan layer manifest\n        sh.create_dir(&build_layout.vulkan_layer_manifest_dir)\n            .unwrap();\n        sh.copy_file(\n            afs::crate_dir(\"vulkan_layer\").join(\"layer/alvr_x86_64.json\"),\n            build_layout.vulkan_layer_manifest(),\n        )\n        .unwrap();\n\n        sh.copy_file(\n            afs::workspace_dir().join(\"openvr/bin/linux64/libopenvr_api.so\"),\n            build_layout.openvr_driver_lib_dir(),\n        )\n        .unwrap();\n\n        let firewall_script = afs::crate_dir(\"xtask\").join(\"firewall/alvr_fw_config.sh\");\n        let firewalld = afs::crate_dir(\"xtask\").join(\"firewall/alvr-firewalld.xml\");\n        let ufw = afs::crate_dir(\"xtask\").join(\"firewall/ufw-alvr\");\n\n        // copy linux specific firewalls\n        sh.copy_file(firewall_script, build_layout.firewall_script())\n            .unwrap();\n        sh.copy_file(firewalld, build_layout.firewalld_config())\n            .unwrap();\n        sh.copy_file(ufw, build_layout.ufw_config()).unwrap();\n    }\n\n    // copy static resources\n    {\n        // copy driver manifest\n        sh.copy_file(\n            afs::crate_dir(\"xtask\").join(\"resources/driver.vrdrivermanifest\"),\n            build_layout.openvr_driver_manifest(),\n        )\n        .unwrap();\n    }\n}\n\npub fn build_launcher(profile: Profile, reproducible: bool) {\n    let sh = Shell::new().unwrap();\n\n    let mut common_flags = vec![];\n    match profile {\n        Profile::Distribution => {\n            common_flags.push(\"--profile\");\n            common_flags.push(\"distribution\");\n        }\n        Profile::Release => common_flags.push(\"--release\"),\n        Profile::Debug => (),\n    }\n    if reproducible {\n        common_flags.push(\"--locked\");\n    }\n    let common_flags_ref = &common_flags;\n\n    sh.create_dir(afs::launcher_build_dir()).unwrap();\n\n    cmd!(sh, \"cargo build -p alvr_launcher {common_flags_ref...}\")\n        .run()\n        .unwrap();\n\n    sh.copy_file(\n        afs::target_dir()\n            .join(profile.to_string())\n            .join(afs::exec_fname(\"alvr_launcher\")),\n        afs::launcher_build_exe_path(),\n    )\n    .unwrap();\n}\n\nfn build_android_lib_impl(dir_name: &str, profile: Profile, link_stdcpp: bool, all_targets: bool) {\n    let sh = Shell::new().unwrap();\n\n    let mut ndk_flags = vec![\"--no-strip\", \"-p\", \"28\", \"-t\", \"arm64-v8a\"];\n\n    if all_targets {\n        ndk_flags.extend([\"-t\", \"armeabi-v7a\", \"-t\", \"x86_64\", \"-t\", \"x86\"]);\n    }\n\n    let mut rust_flags = vec![];\n    match profile {\n        Profile::Distribution => {\n            rust_flags.push(\"--profile\");\n            rust_flags.push(\"distribution\")\n        }\n        Profile::Release => rust_flags.push(\"--release\"),\n        Profile::Debug => (),\n    }\n    if !link_stdcpp {\n        rust_flags.push(\"--no-default-features\");\n    }\n    let rust_flags_ref = &rust_flags;\n\n    let build_dir = afs::build_dir().join(format!(\"alvr_{dir_name}\"));\n    sh.create_dir(&build_dir).unwrap();\n\n    let _push_guard = sh.push_dir(afs::crate_dir(dir_name));\n    cmd!(\n        sh,\n        \"cargo ndk {ndk_flags...} -o {build_dir} build {rust_flags_ref...}\"\n    )\n    .run()\n    .unwrap();\n\n    let out = build_dir.join(format!(\"alvr_{dir_name}.h\"));\n    cmd!(sh, \"cbindgen --output {out}\").run().unwrap();\n}\n\npub fn build_android_client_core_lib(profile: Profile, link_stdcpp: bool, all_targets: bool) {\n    build_android_lib_impl(\"client_core\", profile, link_stdcpp, all_targets)\n}\n\npub fn build_android_client_openxr_lib(profile: Profile, link_stdcpp: bool) {\n    build_android_lib_impl(\"client_openxr\", profile, link_stdcpp, false)\n}\n\npub fn build_android_client(profile: Profile) {\n    let sh = Shell::new().unwrap();\n\n    let mut flags = vec![];\n    match profile {\n        Profile::Distribution => {\n            flags.push(\"--profile\");\n            flags.push(\"distribution\")\n        }\n        Profile::Release => flags.push(\"--release\"),\n        Profile::Debug => (),\n    }\n    let flags_ref = &flags;\n\n    const ARTIFACT_NAME: &str = \"alvr_client_android\";\n\n    let target_dir = afs::target_dir();\n    let build_dir = afs::build_dir().join(ARTIFACT_NAME);\n    sh.create_dir(&build_dir).unwrap();\n\n    // Create debug keystore (signing will be overwritten by CI)\n    if env::var(format!(\n        \"CARGO_APK_{}_KEYSTORE\",\n        profile.to_string().to_uppercase()\n    ))\n    .is_err()\n        && matches!(profile, Profile::Release | Profile::Distribution)\n    {\n        let keystore_path = build_dir.join(\"debug.keystore\");\n        if !keystore_path.exists() {\n            let keytool = PathBuf::from(env::var(\"JAVA_HOME\").expect(\"Env var JAVA_HOME not set\"))\n                .join(\"bin\")\n                .join(afs::exec_fname(\"keytool\"));\n            let pass = \"alvrclient\";\n\n            let other = vec![\n                \"-genkey\",\n                \"-v\",\n                \"-alias\",\n                \"androiddebugkey\",\n                \"-dname\",\n                \"CN=Android Debug,O=Android,C=US\",\n                \"-keyalg\",\n                \"RSA\",\n                \"-keysize\",\n                \"2048\",\n                \"-validity\",\n                \"10000\",\n            ];\n\n            cmd!(\n                sh,\n                \"{keytool} -keystore {keystore_path} -storepass {pass} -keypass {pass} {other...}\"\n            )\n            .run()\n            .unwrap();\n        }\n    }\n\n    let _push_guard = sh.push_dir(afs::crate_dir(\"client_openxr\"));\n    cmd!(\n        sh,\n        \"cargo apk build --target-dir={target_dir} {flags_ref...}\"\n    )\n    .run()\n    .unwrap();\n\n    sh.copy_file(\n        afs::target_dir()\n            .join(profile.to_string())\n            .join(\"apk/alvr_client_openxr.apk\"),\n        build_dir.join(format!(\"{ARTIFACT_NAME}.apk\")),\n    )\n    .unwrap();\n}\n"
  },
  {
    "path": "alvr/xtask/src/ci.rs",
    "content": "use serde::Deserialize;\nuse serde_json::{self as json, Deserializer, Value};\nuse xshell::{Shell, cmd};\n\n#[derive(Debug, Deserialize)]\n#[serde(rename_all = \"snake_case\")]\nenum Level {\n    Error,\n    Warning,\n    Note,\n    Help,\n    FailureNote,\n}\n\n#[derive(Debug, Deserialize)]\nstruct Span {\n    file_name: String,\n    line_start: u64,\n    line_end: u64,\n    column_start: u64,\n    column_end: u64,\n    is_primary: bool,\n}\n\n// https://doc.rust-lang.org/rustc/json.html\n#[derive(Debug, Deserialize)]\nstruct CompilerMessage {\n    #[serde(rename = \"$message_type\")]\n    message_type: String,\n    level: Level,\n    spans: Vec<Span>,\n    rendered: String,\n}\n\npub fn clippy_ci() {\n    let sh = Shell::new().unwrap();\n    let out = cmd!(sh, \"cargo clippy --message-format=json --color=always\")\n        .ignore_status()\n        .output()\n        .unwrap();\n\n    std::print!(\"{}\", String::from_utf8_lossy(&out.stderr));\n\n    let stream = Deserializer::from_slice(&out.stdout).into_iter::<Value>();\n\n    // https://doc.rust-lang.org/cargo/reference/external-tools.html#json-messages\n    let diagnostic_messages = stream\n        .filter_map(|msg| {\n            let msg = msg.unwrap();\n\n            if msg.get(\"reason\")? == \"compiler-message\" {\n                let msg: CompilerMessage = json::from_value(msg.get(\"message\")?.clone()).ok()?;\n                (msg.message_type == \"diagnostic\").then_some(msg)\n            } else {\n                None\n            }\n        })\n        .collect::<Vec<_>>();\n\n    for msg in &diagnostic_messages {\n        let level = match msg.level {\n            Level::Error => Some(\"error\"),\n            Level::Warning => Some(\"warning\"),\n            Level::Note | Level::Help => Some(\"notice\"),\n            _ => None,\n        };\n\n        if let Some(level) = level {\n            let span = msg\n                .spans\n                .iter()\n                .find(|&sp| sp.is_primary)\n                .or(msg.spans.first())\n                .unwrap();\n\n            // may break when xtask gets cross-compiled, but that should not happen esp in ci\n            let file_name = if cfg!(windows) {\n                &span.file_name.replace('\\\\', \"/\")\n            } else {\n                &span.file_name\n            };\n\n            // https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions\n            println!(\n                \"::{level} file={},line={},endLine={},col={},endColumn={}::{}\",\n                file_name,\n                span.line_start,\n                span.line_end,\n                span.column_start,\n                span.column_end,\n                msg.rendered\n                    .replace('%', \"%25\")\n                    .replace('\\r', \"%0D\")\n                    .replace('\\n', \"%0A\"),\n            );\n        }\n    }\n\n    if !out.status.success() {\n        panic!(\"ci clippy didn't exit with 0 code, propagating failure\");\n    }\n\n    if !diagnostic_messages.is_empty() {\n        panic!(\"ci clippy produced warnings\");\n    }\n}\n"
  },
  {
    "path": "alvr/xtask/src/command.rs",
    "content": "use std::path::Path;\nuse xshell::{Shell, cmd};\n\npub fn zip(sh: &Shell, source: &Path) -> Result<(), xshell::Error> {\n    let _push_guard = sh.push_dir(source);\n    cmd!(sh, \"zip -r9X {source} .\").run()\n}\n\npub fn unzip(sh: &Shell, source: &Path, destination: &Path) -> Result<(), xshell::Error> {\n    cmd!(sh, \"unzip {source} -d {destination}\").run()\n}\n\npub fn untar(sh: &Shell, source: &Path, destination: &Path) -> Result<(), xshell::Error> {\n    cmd!(sh, \"tar -xvf {source} -C {destination}\").run()\n}\n\npub fn targz(sh: &Shell, source: &Path) -> Result<(), xshell::Error> {\n    let parent_dir = source.parent().unwrap();\n    let file_name = source.file_name().unwrap();\n\n    cmd!(sh, \"tar -czvf {source}.tar.gz -C {parent_dir} {file_name}\").run()\n}\n\npub fn download(sh: &Shell, url: &str, destination: &Path) -> Result<(), xshell::Error> {\n    cmd!(sh, \"curl -L -o {destination} --url {url}\").run()\n}\n\npub fn download_and_extract_zip(url: &str, destination: &Path) -> Result<(), xshell::Error> {\n    let sh = Shell::new().unwrap();\n    let temp_dir_guard = sh.create_temp_dir()?;\n\n    let zip_file = temp_dir_guard.path().join(\"temp_download.zip\");\n    download(&sh, url, &zip_file)?;\n\n    unzip(&sh, &zip_file, destination)\n}\n\npub fn download_and_extract_tar(url: &str, destination: &Path) -> Result<(), xshell::Error> {\n    let sh = Shell::new().unwrap();\n    let temp_dir_guard = sh.create_temp_dir()?;\n\n    let tar_file = temp_dir_guard.path().join(\"temp_download.tar\");\n    download(&sh, url, &tar_file)?;\n\n    untar(&sh, &tar_file, destination)\n}\n\npub fn date_utc_yyyymmdd(sh: &Shell) -> Result<String, xshell::Error> {\n    if cfg!(windows) {\n        cmd!(\n            sh,\n            \"powershell (Get-Date).ToUniversalTime().ToString(\\\"yyyy.MM.dd\\\")\"\n        )\n        .read()\n    } else {\n        cmd!(sh, \"date -u +%Y.%m.%d\").read()\n    }\n}\n"
  },
  {
    "path": "alvr/xtask/src/dependencies.rs",
    "content": "use crate::{BuildPlatform, command};\nuse alvr_filesystem as afs;\nuse std::{fs, path::Path};\nuse xshell::{Shell, cmd};\n\npub enum OpenXRLoadersSelection {\n    OnlyGeneric,\n    OnlyPico,\n    All,\n}\n\npub fn choco_install(sh: &Shell, packages: &[&str]) -> Result<(), xshell::Error> {\n    cmd!(\n        sh,\n        \"powershell Start-Process choco -ArgumentList \\\"install {packages...} -y\\\" -Verb runAs -Wait\"\n    )\n    .run()\n}\n\npub fn prepare_x264_windows(deps_path: &Path) {\n    let sh = Shell::new().unwrap();\n\n    const VERSION: &str = \"0.164\";\n    const REVISION: usize = 3086;\n\n    let destination = deps_path.join(\"x264\");\n\n    command::download_and_extract_zip(\n        &format!(\n            \"{}/{VERSION}.r{REVISION}/libx264_{VERSION}.r{REVISION}_msvc16.zip\",\n            \"https://github.com/ShiftMediaProject/x264/releases/download\",\n        ),\n        &destination,\n    )\n    .unwrap();\n\n    fs::write(\n        afs::deps_dir().join(\"x264.pc\"),\n        format!(\n            r#\"\nprefix={}\nexec_prefix=${{prefix}}/bin/x64\nlibdir=${{prefix}}/lib/x64\nincludedir=${{prefix}}/include\n\nName: x264\nDescription: x264 library\nVersion: {VERSION}\nLibs: -L${{libdir}} -lx264\nCflags: -I${{includedir}}\n\"#,\n            destination.to_string_lossy().replace('\\\\', \"/\")\n        ),\n    )\n    .unwrap();\n\n    cmd!(sh, \"setx PKG_CONFIG_PATH {deps_path}\").run().unwrap();\n}\n\npub fn prepare_ffmpeg_windows(deps_path: &Path) {\n    command::download_and_extract_zip(\n        &format!(\n            \"https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/{}\",\n            \"ffmpeg-n7.1-latest-win64-gpl-shared-7.1.zip\"\n        ),\n        deps_path,\n    )\n    .unwrap();\n\n    fs::rename(\n        deps_path.join(\"ffmpeg-n7.1-latest-win64-gpl-shared-7.1\"),\n        deps_path.join(\"ffmpeg\"),\n    )\n    .unwrap();\n}\n\nfn prepare_libvpl_windows(deps_path: &Path) {\n    let sh = Shell::new().unwrap();\n\n    const VERSION: &str = \"2.15.0\";\n\n    command::download_and_extract_zip(\n        &format!(\"https://github.com/intel/libvpl/archive/refs/tags/v{VERSION}.zip\"),\n        deps_path,\n    )\n    .unwrap();\n\n    let final_path = deps_path.join(\"libvpl\");\n\n    fs::rename(deps_path.join(format!(\"libvpl-{VERSION}\")), &final_path).unwrap();\n\n    let install_prefix = final_path.join(\"alvr_build\");\n    let _push_guard = sh.push_dir(final_path);\n\n    cmd!(\n        sh,\n        \"cmake -B build -DUSE_MSVC_STATIC_RUNTIME=ON -DCMAKE_INSTALL_PREFIX={install_prefix}\"\n    )\n    .run()\n    .unwrap();\n    cmd!(sh, \"cmake --build build --config Release\")\n        .run()\n        .unwrap();\n    cmd!(sh, \"cmake --install build --config Release\")\n        .run()\n        .unwrap();\n}\n\npub fn prepare_windows_deps(skip_admin_priv: bool) {\n    let sh = Shell::new().unwrap();\n\n    let deps_path = afs::deps_dir().join(\"windows\");\n    sh.remove_path(&deps_path).ok();\n    sh.create_dir(&deps_path).unwrap();\n\n    if !skip_admin_priv {\n        choco_install(\n            &sh,\n            &[\n                \"zip\",\n                \"unzip\",\n                \"llvm\",\n                \"vulkan-sdk\",\n                \"pkgconfiglite\",\n                \"cmake\",\n            ],\n        )\n        .unwrap();\n    }\n\n    prepare_x264_windows(&deps_path);\n    prepare_ffmpeg_windows(&deps_path);\n    prepare_libvpl_windows(&deps_path);\n}\n\npub fn prepare_linux_deps(enable_nvenc: bool) {\n    let sh = Shell::new().unwrap();\n\n    let deps_path = afs::deps_dir().join(\"linux\");\n    sh.remove_path(&deps_path).ok();\n    sh.create_dir(&deps_path).unwrap();\n\n    build_x264_linux(&deps_path);\n    build_ffmpeg_linux(enable_nvenc, &deps_path);\n}\n\npub fn build_x264_linux(deps_path: &Path) {\n    let sh = Shell::new().unwrap();\n\n    // x264 0.164\n    command::download_and_extract_tar(\n        \"https://code.videolan.org/videolan/x264/-/archive/c196240409e4d7c01b47448d93b1f9683aaa7cf7/x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7.tar.bz2\",\n        deps_path,\n    )\n    .unwrap();\n\n    let final_path = deps_path.join(\"x264\");\n\n    fs::rename(\n        deps_path.join(\"x264-c196240409e4d7c01b47448d93b1f9683aaa7cf7\"),\n        &final_path,\n    )\n    .unwrap();\n\n    let flags = [\"--enable-static\", \"--disable-cli\", \"--enable-pic\"];\n\n    let install_prefix = format!(\"--prefix={}\", final_path.join(\"alvr_build\").display());\n\n    let _push_guard = sh.push_dir(final_path);\n\n    cmd!(sh, \"./configure {install_prefix} {flags...}\")\n        .run()\n        .unwrap();\n\n    let nproc = cmd!(sh, \"nproc\").read().unwrap();\n    cmd!(sh, \"make -j{nproc}\").run().unwrap();\n    cmd!(sh, \"make install\").run().unwrap();\n}\n\npub fn build_ffmpeg_linux(enable_nvenc: bool, deps_path: &Path) {\n    let sh = Shell::new().unwrap();\n\n    command::download_and_extract_zip(\n        \"https://codeload.github.com/FFmpeg/FFmpeg/zip/n6.0\",\n        deps_path,\n    )\n    .unwrap();\n\n    let final_path = deps_path.join(\"ffmpeg\");\n\n    fs::rename(deps_path.join(\"FFmpeg-n6.0\"), &final_path).unwrap();\n\n    let flags = [\n        \"--enable-gpl\",\n        \"--enable-version3\",\n        \"--enable-static\",\n        \"--disable-programs\",\n        \"--disable-doc\",\n        \"--disable-avdevice\",\n        \"--disable-avformat\",\n        \"--disable-swresample\",\n        \"--disable-swscale\",\n        \"--disable-postproc\",\n        \"--disable-network\",\n        \"--disable-everything\",\n        \"--enable-encoder=h264_vaapi\",\n        \"--enable-encoder=hevc_vaapi\",\n        \"--enable-encoder=av1_vaapi\",\n        \"--enable-hwaccel=h264_vaapi\",\n        \"--enable-hwaccel=hevc_vaapi\",\n        \"--enable-hwaccel=av1_vaapi\",\n        \"--enable-filter=scale_vaapi\",\n        \"--enable-vulkan\",\n        \"--enable-libdrm\",\n        \"--enable-pic\",\n        \"--enable-rpath\",\n        \"--fatal-warnings\",\n    ];\n    let install_prefix = format!(\"--prefix={}\", final_path.join(\"alvr_build\").display());\n    // The reason for 4x$ in LDSOFLAGS var refer to https://stackoverflow.com/a/71429999\n    // all varients of --extra-ldsoflags='-Wl,-rpath,$ORIGIN' do not work! don't waste your time trying!\n    //\n    let config_vars = r#\"-Wl,-rpath,'$$$$ORIGIN'\"#;\n\n    let _push_guard = sh.push_dir(final_path);\n    let _env_vars = sh.push_env(\"LDSOFLAGS\", config_vars);\n\n    // Patches ffmpeg for workarounds and patches that have yet to be unstreamed\n    let ffmpeg_command = \"for p in ../../../alvr/xtask/patches/*; do patch -p1 < $p; done\";\n    cmd!(sh, \"bash -c {ffmpeg_command}\").run().unwrap();\n\n    if enable_nvenc {\n        #[cfg(target_os = \"linux\")]\n        {\n            let codec_header_version = \"12.1.14.0\";\n            let temp_download_dir = deps_path.join(\"dl_temp\");\n            command::download_and_extract_zip(\n                &format!(\"https://github.com/FFmpeg/nv-codec-headers/archive/refs/tags/n{codec_header_version}.zip\"),\n                &temp_download_dir\n            )\n            .unwrap();\n\n            let header_dir = deps_path.join(\"nv-codec-headers\");\n            let header_build_dir = header_dir.join(\"build\");\n            fs::rename(\n                temp_download_dir.join(format!(\"nv-codec-headers-n{codec_header_version}\")),\n                &header_dir,\n            )\n            .unwrap();\n            fs::remove_dir_all(temp_download_dir).unwrap();\n            {\n                let make_header_cmd =\n                    format!(\"make install PREFIX='{}'\", header_build_dir.display());\n                let _header_push_guard = sh.push_dir(&header_dir);\n                cmd!(sh, \"bash -c {make_header_cmd}\").run().unwrap();\n            }\n\n            let nvenc_flags = &[\n                \"--enable-encoder=h264_nvenc\",\n                \"--enable-encoder=hevc_nvenc\",\n                \"--enable-encoder=av1_nvenc\",\n            ];\n\n            let env_vars = format!(\n                \"PKG_CONFIG_PATH='{}'\",\n                header_build_dir.join(\"lib/pkgconfig\").display()\n            );\n            let flags_combined = flags.join(\" \");\n            let nvenc_flags_combined = nvenc_flags.join(\" \");\n\n            let command = format!(\n                \"{env_vars} ./configure {install_prefix} {flags_combined} {nvenc_flags_combined}\"\n            );\n\n            cmd!(sh, \"bash -c {command}\").run().unwrap();\n        }\n    } else {\n        cmd!(sh, \"./configure {install_prefix} {flags...}\")\n            .run()\n            .unwrap();\n    }\n\n    let nproc = cmd!(sh, \"nproc\").read().unwrap();\n    cmd!(sh, \"make -j{nproc}\").run().unwrap();\n    cmd!(sh, \"make install\").run().unwrap();\n}\n\npub fn prepare_macos_deps() {}\n\npub fn prepare_server_deps(\n    platform: Option<BuildPlatform>,\n    skip_admin_priv: bool,\n    enable_nvenc: bool,\n) {\n    match platform {\n        Some(BuildPlatform::Windows) => prepare_windows_deps(skip_admin_priv),\n        Some(BuildPlatform::Linux) => prepare_linux_deps(enable_nvenc),\n        Some(BuildPlatform::Macos) => prepare_macos_deps(),\n        Some(BuildPlatform::Android) => panic!(\"Android is not supported\"),\n        None => {\n            if cfg!(windows) {\n                prepare_windows_deps(skip_admin_priv);\n            } else if cfg!(target_os = \"linux\") {\n                prepare_linux_deps(enable_nvenc);\n            } else if cfg!(target_os = \"macos\") {\n                prepare_macos_deps();\n            } else {\n                panic!(\"Unsupported platform\");\n            }\n        }\n    }\n}\n\nfn get_android_openxr_loaders(selection: OpenXRLoadersSelection) {\n    fn get_openxr_loader(name: &str, url: &str, source_dir: &str) {\n        let sh = Shell::new().unwrap();\n        let temp_dir = afs::build_dir().join(\"temp_download\");\n        sh.remove_path(&temp_dir).ok();\n        sh.create_dir(&temp_dir).unwrap();\n        let destination_dir = afs::deps_dir().join(\"android_openxr/arm64-v8a\");\n        fs::create_dir_all(&destination_dir).unwrap();\n\n        command::download_and_extract_zip(url, &temp_dir).unwrap();\n        fs::copy(\n            temp_dir.join(source_dir).join(\"libopenxr_loader.so\"),\n            destination_dir.join(format!(\"libopenxr_loader{name}.so\")),\n        )\n        .unwrap();\n        fs::remove_dir_all(&temp_dir).ok();\n    }\n\n    const OPENXR_VERSION: &str = \"1.1.36\";\n    get_openxr_loader(\n        \"\",\n        &format!(\n            \"https://github.com/KhronosGroup/OpenXR-SDK-Source/releases/download/\\\n            release-{OPENXR_VERSION}/openxr_loader_for_android-{OPENXR_VERSION}.aar\",\n        ),\n        \"prefab/modules/openxr_loader/libs/android.arm64-v8a\",\n    );\n\n    if matches!(selection, OpenXRLoadersSelection::OnlyGeneric) {\n        return;\n    }\n\n    get_openxr_loader(\n        \"_pico_old\",\n        \"https://sdk.picovr.com/developer-platform/sdk/PICO_OpenXR_SDK_220.zip\",\n        \"libs/android.arm64-v8a\",\n    );\n\n    if matches!(selection, OpenXRLoadersSelection::OnlyPico) {\n        return;\n    }\n\n    get_openxr_loader(\n        \"_quest1\",\n        \"https://securecdn.oculus.com/binaries/download/?id=7577210995650755\", // Version 64\n        \"OpenXR/Libs/Android/arm64-v8a/Release\",\n    );\n\n    get_openxr_loader(\n        \"_yvr\",\n        \"https://developer.yvrdream.com/yvrdoc/sdk/openxr/yvr_openxr_mobile_sdk_2.0.0.zip\",\n        \"yvr_openxr_mobile_sdk_2.0.0/OpenXR/Libs/Android/arm64-v8a\",\n    );\n\n    get_openxr_loader(\n        \"_lynx\",\n        \"https://portal.lynx-r.com/downloads/download/16\", // version 1.0.0\n        \"jni/arm64-v8a\",\n    );\n}\n\npub fn build_android_deps(\n    skip_admin_priv: bool,\n    all_targets: bool,\n    openxr_loaders_selection: OpenXRLoadersSelection,\n) {\n    let sh = Shell::new().unwrap();\n\n    if cfg!(windows) && !skip_admin_priv {\n        choco_install(&sh, &[\"unzip\", \"llvm\"]).unwrap();\n    }\n\n    cmd!(sh, \"rustup target add aarch64-linux-android\")\n        .run()\n        .unwrap();\n    if all_targets {\n        cmd!(sh, \"rustup target add armv7-linux-androideabi\")\n            .run()\n            .unwrap();\n        cmd!(sh, \"rustup target add x86_64-linux-android\")\n            .run()\n            .unwrap();\n        cmd!(sh, \"rustup target add i686-linux-android\")\n            .run()\n            .unwrap();\n    }\n    cmd!(sh, \"cargo install cbindgen\").run().unwrap();\n    cmd!(sh, \"cargo install cargo-ndk --version 3.5.4\")\n        .run()\n        .unwrap();\n    cmd!(\n        sh,\n        \"cargo install --git https://github.com/zarik5/cargo-apk cargo-apk\"\n    )\n    .run()\n    .unwrap();\n\n    get_android_openxr_loaders(openxr_loaders_selection);\n}\n"
  },
  {
    "path": "alvr/xtask/src/format.rs",
    "content": "use alvr_filesystem as afs;\nuse std::fs;\nuse std::mem;\nuse std::path::PathBuf;\nuse walkdir::WalkDir;\nuse xshell::{Shell, cmd};\n\nfn files_to_format_paths() -> Vec<PathBuf> {\n    let cpp_dir = afs::crate_dir(\"server_openvr\").join(\"cpp\");\n\n    WalkDir::new(cpp_dir)\n        .into_iter()\n        .filter_entry(|entry| {\n            let included = entry.path().is_dir()\n                || entry\n                    .path()\n                    .extension()\n                    .is_some_and(|ext| matches!(ext.to_str().unwrap(), \"c\" | \"cpp\" | \"h\" | \"hpp\"));\n\n            let excluded = matches!(\n                entry.file_name().to_str().unwrap(),\n                \"shared\"\n                    | \"include\"\n                    | \"NvCodecUtils.h\"\n                    | \"NvEncoder.cpp\"\n                    | \"NvEncoder.h\"\n                    | \"NvEncoderD3D11.cpp\"\n                    | \"NvEncoderD3D11.h\"\n                    | \"nvEncodeAPI.h\"\n            );\n\n            included && !excluded\n        })\n        .filter_map(|entry| {\n            let entry = entry.ok()?;\n            entry.file_type().is_file().then(|| entry.path().to_owned())\n        })\n        .collect()\n}\n\npub fn format() {\n    let sh = Shell::new().unwrap();\n    let dir = sh.push_dir(afs::workspace_dir());\n\n    cmd!(sh, \"cargo fmt --all\").run().unwrap();\n\n    for path in files_to_format_paths() {\n        cmd!(sh, \"clang-format -i {path}\").run().unwrap();\n    }\n\n    mem::drop(dir);\n}\n\npub fn check_format() {\n    let sh = Shell::new().unwrap();\n    let dir = sh.push_dir(afs::workspace_dir());\n\n    cmd!(sh, \"cargo fmt --all -- --check\")\n        .run()\n        .expect(\"cargo fmt check failed\");\n\n    for path in files_to_format_paths() {\n        let content = fs::read_to_string(&path).unwrap();\n        let mut output = cmd!(sh, \"clang-format {path}\").read().unwrap();\n\n        if !content.ends_with('\\n') {\n            panic!(\"file {} missing final newline\", path.display());\n        }\n        output.push('\\n');\n\n        if content != output {\n            panic!(\"clang-format check failed for {}\", path.display());\n        }\n    }\n\n    mem::drop(dir);\n}\n"
  },
  {
    "path": "alvr/xtask/src/main.rs",
    "content": "mod build;\nmod ci;\nmod command;\nmod dependencies;\nmod format;\nmod packaging;\nmod version;\n\nuse crate::build::Profile;\nuse afs::Layout;\nuse alvr_filesystem as afs;\nuse dependencies::OpenXRLoadersSelection;\nuse packaging::ReleaseFlavor;\nuse pico_args::Arguments;\nuse std::{fs, process, time::Instant};\nuse xshell::{Shell, cmd};\n\nconst HELP_STR: &str = r#\"\ncargo xtask\nDevelopement actions for ALVR.\n\nUSAGE:\n    cargo xtask <SUBCOMMAND> [FLAG] [ARGS]\n\nSUBCOMMANDS:\n    prepare-deps        Download and compile streamer and client external dependencies\n    build-streamer      Build streamer, then copy binaries to build folder\n    build-launcher      Build launcher, then copy binaries to build folder\n    build-server-lib    Build a C-ABI ALVR server library and header\n    build-client        Build client, then copy binaries to build folder\n    build-client-lib    Build a C-ABI ALVR client library and header\n    build-client-xr-lib Build a C-ABI ALVR OpenXR entry point client library and header\n    run-streamer        Build streamer and then open the dashboard\n    run-launcher        Build launcher and then open it\n    format              Autoformat all code\n    check-format        Check if code is correctly formatted\n    package-streamer    Build streamer with distribution profile, make archive\n    package-launcher    Build launcher with distribution profile, make archive\n    package-client      Build client with distribution profile\n    package-client-lib  Build client library then zip it\n    clean               Removes all build artifacts and dependencies\n    bump                Bump streamer and client package versions\n    clippy              Show warnings for selected clippy lints\n    kill-oculus         Kill all Oculus processes\n\nFLAGS:\n    --help              Print this text\n    --keep-config       Preserve the configuration file between rebuilds (session.json)\n    --no-nvidia         Disables nVidia support on Linux. For prepare-deps subcommand\n    --release           Optimized build with less debug checks. For build subcommands\n    --profiling         Enable Profiling\n    --gpl               Bundle GPL libraries (FFmpeg). Only for Windows\n    --nightly           Append nightly tag to versions. For bump subcommand\n    --no-rebuild        Do not rebuild the streamer with run-streamer\n    --ci                Do some CI related tweaks. Depends on the other flags and subcommand\n    --no-stdcpp         Disable linking to libc++_shared with build-client-lib\n    --all-targets       For prepare-deps and build-client-lib subcommand, will build for all android supported ABI targets\n    --meta-store        For package-client subcommand, build for Meta Store\n    --pico-store        For package-client subcommand, build for Pico Store\n\nARGS:\n    --platform <NAME>   Can be one of: windows, linux, macos, android. Can be omitted\n    --version <VERSION> Specify version to set with the bump-versions subcommand\n    --root <PATH>       Installation root. By default no root is set and paths are calculated using\n                        relative paths, which requires conforming to FHS on Linux\n\"#;\n\nenum BuildPlatform {\n    Windows,\n    Linux,\n    Macos,\n    Android,\n}\n\npub fn print_help_and_exit(message: &str) -> ! {\n    eprintln!(\"\\n{message}\");\n    eprintln!(\"{HELP_STR}\");\n    process::exit(1);\n}\n\npub fn run_streamer() {\n    let sh = Shell::new().unwrap();\n\n    let dashboard_exe = Layout::new(&afs::streamer_build_dir()).dashboard_exe();\n    cmd!(sh, \"{dashboard_exe}\").run().unwrap();\n}\n\npub fn run_launcher() {\n    let sh = Shell::new().unwrap();\n\n    let launcher_exe = afs::launcher_build_exe_path();\n    cmd!(sh, \"{launcher_exe}\").run().unwrap();\n}\n\npub fn clean() {\n    fs::remove_dir_all(afs::build_dir()).ok();\n    fs::remove_dir_all(afs::deps_dir()).ok();\n    if afs::target_dir() == afs::workspace_dir().join(\"target\") {\n        // Detete target folder only if in the local wokspace!\n        fs::remove_dir_all(afs::target_dir()).ok();\n    }\n}\n\nfn clippy() {\n    // lints updated for Rust 1.59\n    let restriction_lints = [\n        \"allow_attributes_without_reason\",\n        \"clone_on_ref_ptr\",\n        \"create_dir\",\n        \"decimal_literal_representation\",\n        \"expect_used\",\n        \"float_cmp_const\",\n        \"fn_to_numeric_cast_any\",\n        \"get_unwrap\",\n        \"if_then_some_else_none\",\n        \"let_underscore_must_use\",\n        \"lossy_float_literal\",\n        \"mem_forget\",\n        \"multiple_inherent_impl\",\n        \"rest_pat_in_fully_bound_structs\",\n        // \"self_named_module_files\",\n        \"str_to_string\",\n        // \"string_slice\",\n        \"string_to_string\",\n        \"try_err\",\n        \"unnecessary_self_imports\",\n        \"unneeded_field_pattern\",\n        \"unseparated_literal_suffix\",\n        \"verbose_file_reads\",\n        \"wildcard_enum_match_arm\",\n    ];\n    let pedantic_lints = [\n        \"borrow_as_ptr\",\n        \"enum_glob_use\",\n        \"explicit_deref_methods\",\n        \"explicit_into_iter_loop\",\n        \"explicit_iter_loop\",\n        \"filter_map_next\",\n        \"flat_map_option\",\n        \"float_cmp\",\n        // todo: add more lints\n    ];\n\n    let flags = restriction_lints\n        .into_iter()\n        .chain(pedantic_lints)\n        .flat_map(|name| [\"-W\".to_owned(), format!(\"clippy::{name}\")]);\n\n    let sh = Shell::new().unwrap();\n    cmd!(sh, \"cargo clippy -- {flags...}\").run().unwrap();\n}\n\n// Avoid Oculus link popups when debugging the client\npub fn kill_oculus_processes() {\n    let sh = Shell::new().unwrap();\n    cmd!(\n        sh,\n        \"powershell Start-Process taskkill -ArgumentList \\\"/F /IM OVR* /T\\\" -Verb runas\"\n    )\n    .run()\n    .unwrap();\n}\n\nfn main() {\n    let begin_time = Instant::now();\n\n    let mut args = Arguments::from_env();\n\n    if args.contains([\"-h\", \"--help\"]) {\n        println!(\"{HELP_STR}\");\n    } else if let Ok(Some(subcommand)) = args.subcommand() {\n        let no_nvidia = args.contains(\"--no-nvidia\");\n        let is_release = args.contains(\"--release\");\n        let profile = if is_release {\n            Profile::Release\n        } else {\n            Profile::Debug\n        };\n        let profiling = args.contains(\"--profiling\");\n        let gpl = args.contains(\"--gpl\");\n        let is_nightly = args.contains(\"--nightly\");\n        let no_rebuild = args.contains(\"--no-rebuild\");\n        let for_ci = args.contains(\"--ci\");\n        let keep_config = args.contains(\"--keep-config\");\n        let link_stdcpp = !args.contains(\"--no-stdcpp\");\n        let all_targets = args.contains(\"--all-targets\");\n\n        let platform: Option<String> = args.opt_value_from_str(\"--platform\").unwrap();\n        let platform = platform.as_deref().map(|platform| match platform {\n            \"windows\" => BuildPlatform::Windows,\n            \"linux\" => BuildPlatform::Linux,\n            \"macos\" => BuildPlatform::Macos,\n            \"android\" => BuildPlatform::Android,\n            _ => print_help_and_exit(\"Unrecognized platform\"),\n        });\n\n        let version: Option<String> = args.opt_value_from_str(\"--version\").unwrap();\n        let root: Option<String> = args.opt_value_from_str(\"--root\").unwrap();\n\n        let package_flavor = if args.contains(\"--meta-store\") {\n            ReleaseFlavor::MetaStore\n        } else if args.contains(\"--pico-store\") {\n            ReleaseFlavor::PicoStore\n        } else {\n            ReleaseFlavor::GitHub\n        };\n\n        if args.finish().is_empty() {\n            match subcommand.as_str() {\n                \"prepare-deps\" => {\n                    if let Some(platform) = platform {\n                        if matches!(platform, BuildPlatform::Android) {\n                            dependencies::build_android_deps(\n                                for_ci,\n                                all_targets,\n                                OpenXRLoadersSelection::All,\n                            );\n                        } else {\n                            dependencies::prepare_server_deps(Some(platform), for_ci, !no_nvidia);\n                        }\n                    } else {\n                        dependencies::prepare_server_deps(platform, for_ci, !no_nvidia);\n\n                        dependencies::build_android_deps(\n                            for_ci,\n                            all_targets,\n                            OpenXRLoadersSelection::All,\n                        );\n                    }\n                }\n                \"build-streamer\" => {\n                    build::build_streamer(profile, gpl, None, false, profiling, keep_config)\n                }\n                \"build-launcher\" => build::build_launcher(profile, false),\n                \"build-server-lib\" => build::build_server_lib(profile, None, false),\n                \"build-client\" => build::build_android_client(profile),\n                \"build-client-lib\" => {\n                    build::build_android_client_core_lib(profile, link_stdcpp, all_targets)\n                }\n                \"build-client-xr-lib\" => {\n                    build::build_android_client_openxr_lib(profile, link_stdcpp)\n                }\n                \"run-streamer\" => {\n                    if !no_rebuild {\n                        build::build_streamer(profile, gpl, None, false, profiling, keep_config);\n                    }\n                    run_streamer();\n                }\n                \"run-launcher\" => {\n                    if !no_rebuild {\n                        build::build_launcher(profile, false);\n                    }\n                    run_launcher();\n                }\n                \"package-streamer\" => {\n                    packaging::package_streamer(platform, for_ci, !no_nvidia, gpl, root)\n                }\n                \"package-launcher\" => packaging::package_launcher(),\n                \"package-client\" => packaging::package_client_openxr(package_flavor, for_ci),\n                \"package-client-lib\" => packaging::package_client_lib(link_stdcpp, all_targets),\n                \"format\" => format::format(),\n                \"check-format\" => format::check_format(),\n                \"clean\" => clean(),\n                \"bump\" => version::bump_version(version, is_nightly),\n                \"clippy\" => {\n                    if for_ci {\n                        ci::clippy_ci()\n                    } else {\n                        clippy()\n                    }\n                }\n                \"check-msrv\" => version::check_msrv(),\n                \"check-licenses\" => {\n                    packaging::generate_licenses();\n                }\n                \"kill-oculus\" => kill_oculus_processes(),\n                _ => print_help_and_exit(\"Unrecognized subcommand.\"),\n            }\n        } else {\n            print_help_and_exit(\"Wrong arguments.\");\n        }\n    } else {\n        print_help_and_exit(\"Missing subcommand.\");\n    }\n\n    let elapsed_time = begin_time.elapsed();\n    println!(\n        \"\\nDone [{}m {}s]\\n\",\n        elapsed_time.as_secs() / 60,\n        elapsed_time.as_secs() % 60\n    );\n}\n"
  },
  {
    "path": "alvr/xtask/src/packaging.rs",
    "content": "use crate::{\n    BuildPlatform,\n    build::{self, Profile},\n    command,\n    dependencies::{self, OpenXRLoadersSelection},\n};\nuse alvr_filesystem as afs;\nuse std::{fs, path::Path};\nuse xshell::{Shell, cmd};\n\npub enum ReleaseFlavor {\n    GitHub,\n    MetaStore,\n    PicoStore,\n}\n\npub fn generate_licenses() -> String {\n    let sh = Shell::new().unwrap();\n\n    cmd!(sh, \"cargo install cargo-about --version 0.8.4 --locked\")\n        .run()\n        .unwrap();\n\n    let licenses_template = afs::crate_dir(\"xtask\").join(\"licenses_template.hbs\");\n\n    cmd!(sh, \"cargo about generate {licenses_template}\")\n        .read()\n        .unwrap()\n}\n\npub fn include_licenses(root_path: &Path, gpl: bool) {\n    let sh = Shell::new().unwrap();\n\n    // Add licenses\n    let licenses_dir = root_path.join(\"licenses\");\n    sh.create_dir(&licenses_dir).unwrap();\n    sh.copy_file(\n        afs::workspace_dir().join(\"LICENSE\"),\n        licenses_dir.join(\"ALVR.txt\"),\n    )\n    .unwrap();\n    sh.copy_file(\n        afs::crate_dir(\"server_openvr\").join(\"LICENSE-Valve\"),\n        licenses_dir.join(\"Valve.txt\"),\n    )\n    .unwrap();\n    if gpl {\n        sh.copy_file(\n            afs::deps_dir().join(\"windows/ffmpeg/LICENSE.txt\"),\n            licenses_dir.join(\"FFmpeg.txt\"),\n        )\n        .ok();\n    }\n    if cfg!(windows) {\n        sh.copy_file(\n            afs::deps_dir().join(\"windows/libvpl/alvr_build/share/vpl/licensing/license.txt\"),\n            licenses_dir.join(\"libvpl.txt\"),\n        )\n        .unwrap();\n    }\n\n    let licenses_content = generate_licenses();\n    sh.write_file(licenses_dir.join(\"dependencies.html\"), licenses_content)\n        .unwrap();\n}\n\npub fn package_streamer(\n    platform: Option<BuildPlatform>,\n    skip_admin_priv: bool,\n    enable_nvenc: bool,\n    gpl: bool,\n    root: Option<String>,\n) {\n    let sh = Shell::new().unwrap();\n\n    dependencies::prepare_server_deps(platform, skip_admin_priv, enable_nvenc);\n\n    build::build_streamer(Profile::Distribution, gpl, root, true, false, false);\n\n    include_licenses(&afs::streamer_build_dir(), gpl);\n\n    if cfg!(windows) {\n        command::zip(&sh, &afs::streamer_build_dir()).unwrap();\n    } else {\n        command::targz(&sh, &afs::streamer_build_dir()).unwrap();\n    }\n}\n\npub fn package_launcher() {\n    let sh = Shell::new().unwrap();\n\n    sh.remove_path(afs::launcher_build_dir()).ok();\n\n    build::build_launcher(Profile::Distribution, true);\n\n    include_licenses(&afs::launcher_build_dir(), false);\n\n    if cfg!(windows) {\n        command::zip(&sh, &afs::launcher_build_dir()).unwrap();\n\n        // todo: installer\n    } else {\n        command::targz(&sh, &afs::launcher_build_dir()).unwrap();\n    }\n}\n\npub fn replace_client_openxr_manifest(from_pattern: &str, to: &str) {\n    let manifest_path = afs::crate_dir(\"client_openxr\").join(\"Cargo.toml\");\n    let manifest_string = fs::read_to_string(&manifest_path)\n        .unwrap()\n        .replace(from_pattern, to);\n\n    fs::write(manifest_path, manifest_string).unwrap();\n}\n\npub fn package_client_openxr(flavor: ReleaseFlavor, skip_admin_priv: bool) {\n    fs::remove_dir_all(afs::deps_dir().join(\"android_openxr\")).ok();\n\n    let openxr_selection = match flavor {\n        ReleaseFlavor::GitHub => OpenXRLoadersSelection::All,\n        ReleaseFlavor::MetaStore => OpenXRLoadersSelection::OnlyGeneric,\n        ReleaseFlavor::PicoStore => OpenXRLoadersSelection::OnlyPico,\n    };\n\n    dependencies::build_android_deps(skip_admin_priv, false, openxr_selection);\n\n    if !matches!(flavor, ReleaseFlavor::GitHub) {\n        replace_client_openxr_manifest(\n            r#\"package = \"alvr.client.stable\"\"#,\n            r#\"package = \"alvr.client\"\"#,\n        );\n    }\n\n    if matches!(flavor, ReleaseFlavor::MetaStore) {\n        replace_client_openxr_manifest(r#\"value = \"all\"\"#, r#\"value = \"quest2|questpro|quest3\"\"#);\n    }\n\n    build::build_android_client(Profile::Distribution);\n}\n\npub fn package_client_lib(link_stdcpp: bool, all_targets: bool) {\n    let sh = Shell::new().unwrap();\n\n    build::build_android_client_core_lib(Profile::Distribution, link_stdcpp, all_targets);\n\n    command::zip(&sh, &afs::build_dir().join(\"alvr_client_core\")).unwrap();\n}\n"
  },
  {
    "path": "alvr/xtask/src/version.rs",
    "content": "use crate::command;\nuse alvr_filesystem as afs;\nuse std::fs;\nuse xshell::{Shell, cmd};\n\npub fn split_string(source: &str, start_pattern: &str, end: char) -> (String, String, String) {\n    let start_idx = source.find(start_pattern).unwrap() + start_pattern.len();\n    let end_idx = start_idx + source[start_idx..].find(end).unwrap();\n\n    (\n        source[..start_idx].to_owned(),\n        source[start_idx..end_idx].to_owned(),\n        source[end_idx..].to_owned(),\n    )\n}\n\npub fn version() -> String {\n    let manifest_path = afs::workspace_dir().join(\"Cargo.toml\");\n    println!(\"cargo:rerun-if-changed={}\", manifest_path.to_string_lossy());\n\n    let manifest = fs::read_to_string(manifest_path).unwrap();\n    let (_, version, _) = split_string(&manifest, \"version = \\\"\", '\\\"');\n\n    version\n}\n\nfn bump_cargo_version(new_version: &str) {\n    let manifest_path = afs::workspace_dir().join(\"Cargo.toml\");\n\n    let manifest = fs::read_to_string(&manifest_path).unwrap();\n\n    let (file_start, _, file_end) = split_string(&manifest, \"version = \\\"\", '\\\"');\n    let manifest = format!(\"{file_start}{new_version}{file_end}\");\n\n    fs::write(manifest_path, manifest).unwrap();\n}\n\npub fn bump_version(maybe_version: Option<String>, is_nightly: bool) {\n    let sh = Shell::new().unwrap();\n\n    let mut version = maybe_version.unwrap_or_else(version);\n\n    if is_nightly {\n        version = format!(\n            \"{version}+nightly.{}\",\n            command::date_utc_yyyymmdd(&sh).unwrap()\n        );\n    }\n\n    bump_cargo_version(&version);\n\n    println!(\"Git tag:\\nv{version}\");\n}\n\npub fn check_msrv() {\n    let sh = Shell::new().unwrap();\n\n    cmd!(\n        sh,\n        \"cargo install cargo-msrv --git https://github.com/foresterre/cargo-msrv --rev 14097beaa5fa770aabd66170572cb04f1dac87c2\"\n    )\n    .run()\n    .unwrap();\n\n    let paths = [\n        \"alvr/server_openvr\",\n        \"alvr/dashboard\",\n        \"alvr/launcher\",\n        \"alvr/client_openxr\",\n    ];\n    for path in paths {\n        cmd!(sh, \"cargo msrv verify --path {path}\").run().unwrap()\n    }\n}\n"
  },
  {
    "path": "wiki/ALVR-Checklist.md",
    "content": "## Hardware Requirements\n\n* [ ] Intel Core i5-4590/AMD FX 8350 equivalent or better\n* [ ] At least 4GB of Ram\n* [ ] NVIDIA GeForce GTX 970, AMD Radeon R9 290, Intel ARC equivalent or better\n* [ ] A 5Ghz router/access point or my PC can create its own 5Ghz hotspot\n\n## Network settings\n\n* [ ] My PC has a wired connection to the router/access point\n* [ ] The access point is placed in sight of my designated playspace without any obstructions\n* [ ] I'm using the 5ghz antenna of the router/access point\n* [ ] No one else is using the router/access point\n* [ ] I'm the only user of the 5Ghz channel of the router/access point. No one else is using the same channel in the vicinity\n* [ ] The 5Ghz and 2.4Ghz parts of the access point have different SSIDs to prevent switching to 2.4ghz\n\n## Software settings\n\n* [ ] I have the latest Windows 10 updates\n* [ ] I have a recent version of SteamVR\n\n## Troubleshooting\n\n* [ ] The firewall settings where successfully applied with the setup of ALVR\n* [ ] I did not change the network settings since the installation of ALVR (Private/Public/Work)\n* [ ] I did not move the installation folder of ALVR since the setup\n* [ ] The path to the folder of ALVR does not contain any non latin characters or accents (ツ Л Ö ...)\n"
  },
  {
    "path": "wiki/ALVR-wired-setup-(ALVR-over-USB).md",
    "content": "## ALVR native wired mode support\nAs of v20.12 ALVR supports wired connections directly through the dashboard.\nJust enable the \"Wired Connection\" toggle on the Devices screen, plug in your headset\nand accept the \"Allow USB debugging?\" popup displayed by the headset.\n\nNote that your headset will need to have Developer Mode and USB Debugging enabled to use this feature.\n\nFor Quest headsets see [here](https://developers.meta.com/horizon/documentation/native/android/mobile-device-setup/) for instructions.\nThe last step about installing ADB should be skipped, as ALVR downloads a copy of ADB on it's own and uses that.\n\nIf you have successfully followed all those steps and it still isn't connecting,\nensure that the setting \"Connection -> Wired Client Type\" matches where you installed the client from (for the launcher also use the \"Github\" option).\n\n## The DEPRECATED (and clunky) way:\nThe following sections list the old and deprecated way to get a wired connection and is only kept as reference.\n\nThis has exactly the same requirements as the native wired mode, but requires additional software and is more complex to setup, so native mode should be preferred.\n\n## ALVR Streamer (PC) Configuration\n\n* **Switch the connection streaming protocol to TCP** in Settings > Connection.\n* If your headset is detected, click \"Trust.\" Click \"Edit\", \"Add new\" and change the IP address to `127.0.0.1`.\n* If your headset is not detected, click \"Add device manually\" and use the IP address `127.0.0.1`. Use the hostname displayed on your headset screen.\n\n## Letting your PC communicate with your HMD\n\nThe Quest, Pico HMDs are Android devices, therefore, we can use [Android Device Bridge](https://developer.android.com/studio/command-line/adb) commands to tell the HMDs to look for data over USB, as well as Wi-Fi, using port forwarding.\n\nYou can accomplish this with some pre-made applications/scripts (just below), or run the commands manually with [SideQuest](https://sidequestvr.com/setup-howto)\n\nIf you haven't already, connect a USB cable from your PC to your headset. USB 2.0 will work fine but 3.0 and higher is best.\n\n**Make sure to enable dev account and authorize the computer in your headset if you're on quest or enable USB Debug on Pico in settings.**\n\n### Option 1 - Dedicated ADB Applications\n\nThe following programs serve to wrap and simplify the process of doing manual ADB commands, the first two will also automatically reconnect the headset if the USB connection is interrupted.\n\n* [**ADBForwarder (Recommended)**](https://github.com/alvr-org/ADBForwarder)\n  \n  * Easy to use\n  * Downloads ADB for you\n  * Cross-platform (Windows & Linux)\n\n* [**Python Script**](https://gist.github.com/Bad-At-Usernames/684784f42cbb69e22688a21173ec263d)\n  \n  * Lightweight and simple\n  * Requires [Python 3](https://www.python.org/downloads/) and [PyWin32](https://pypi.org/project/pywin32/)\n  * Requires [ADB Platform Tools](https://developer.android.com/studio/releases/platform-tools) to be in the same directory as `main.py`\n    * Just extract `platform-tools` to your desktop and place `main.py` in that folder, should work when you run the script\n\n* [**Batch Script**](https://gist.github.com/AtlasTheProto/1f03c3aeac70c4af5b4f2fcd9b9273c0)\n  \n  * Requires [ADB Platform Tools](https://developer.android.com/studio/releases/platform-tools), edit the path in line 2 to point to the directory where you extracted `platform-tools`\n  * Needs to be run every time you (re)connect your headset\n\n### Option 2 - [SideQuest](https://sidequestvr.com/setup-howto)\n\n* Ensure SideQuest is running, and the headset has authorized the USB connection to the PC\n* Open the 'Run ADB Commands' menu in SideQuest (top-right, box with an arrow inside it)\n* Click 'Custom Command' and run these adb commands:\n  * `adb forward tcp:9943 tcp:9943`\n  * `adb forward tcp:9944 tcp:9944`\n  * These commands will need to be run every time you (re)connect your headset.\n* Keep SideQuest opened until you want to close the connection.\n\n***\n\nOnce you are finished, the headset should now establish a connection over USB.\n"
  },
  {
    "path": "wiki/Building-From-Source.md",
    "content": "ALVR can be built on Windows and Linux. The following instructions are for both OSes.\n\n# Common Prerequisites\n\nPreferred IDE (optional): Visual Studio Code with rust-analyzer extension\n\nYou need to install [rustup](https://www.rust-lang.org/tools/install).\n\nOn Windows you also need [Chocolatey](https://chocolatey.org/install).\n\nTo clone the repository use `git clone --recurse-submodules https://github.com/alvr-org/ALVR.git`.\nIf you previously cloned the repo without submodules, simply run `git submodule update --init --checkout --recursive` in it.\n\n# Streamer Building\n\nFirst you need to gather some additional resources in preparation for the build.  \n\nIf you are on Linux, install these additional packages:\n\n* **Arch**\n  \n  Note: At time of writing Arch gcc is too new to be compatible with nvcc. This means there is no neat way to compile an nvidia compatible build. Recommended workarounds are to build in some kind of containerised environment. This has been done successfully with both nixos and flatpak - but are not documented yet.\n\n  ```bash\n  sudo pacman -S clang curl nasm pkgconf yasm vulkan-headers libva-mesa-driver unzip ffmpeg libpipewire\n  ```\n  \n  * The [`alvr-git`](https://aur.archlinux.org/packages/alvr-git) [AUR package](https://wiki.archlinux.org/title/Arch_User_Repository) may also be used to do this automatically.\n\n* **Gentoo**\n  \n  * `media-video/ffmpeg >= 4.4 [encode libdrm vulkan vaapi]`\n  * `sys-libs/libunwind`\n  * `dev-lang/rust >= 1.72`\n  * `media-video/pipewire [jacksdk]`\n\n* **Debian 12 / Ubuntu 20.04 / Pop!\\_OS 20.04**\n  \n  ```bash\n  sudo apt install pulseaudio-utils build-essential pkg-config libclang-dev libssl-dev libasound2-dev libjack-dev libgtk-3-dev libvulkan-dev libunwind-dev gcc yasm nasm curl libx264-dev libx265-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libdrm-dev libva-dev libvulkan-dev vulkan-headers libpipewire-0.3-dev libspa-0.3-dev git\n  ```\n\n  * Note: Libpipewire/libspa must be at least 0.3.49 version - make sure to use upstream pipewire <https://github.com/pipewire-debian/pipewire-debian>\n\n* **Fedora**\n  \n  ```bash\n  sudo dnf groupinstall 'Development Tools' | For c++ and build tools\n  sudo dnf install nasm yasm libdrm-devel vulkan-headers pipewire-jack-audio-connection-kit-devel atk-devel gdk-pixbuf2-devel cairo-devel rust-gdk0.15-devel x264-devel vulkan-devel libunwind-devel clang openssl-devel alsa-lib-devel libva-devel pipewire-devel git\n  ```\n  \n  If you are using Nvidia, see [Fedora cuda installation](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#fedora-cuda-installation)\n\nMove to the root directory of the project, then run this command (paying attention to the bullet points below):\n\n```bash\ncargo xtask prepare-deps --platform [your platform] [--gpl] [--no-nvidia]\n```\n\n* Replace `[your platform]` with your computer OS, either `windows` or `linux`\n* **Windows only:** Use the `--gpl` flag if you want to download, build and bundle FFmpeg inside the ALVR streamer. Keep in mind that this is only needed for software encoding. As the name suggests, if you use this flag you can only redistribute the final package as GPLv2.0 licensed; because of the x264 encoder.\n* **Linux only:** Use the `--no-nvidia` flag if you have a AMD gpu.\n\nNext up is the proper build of the streamer. Run the following:\n\n```bash\ncargo xtask build-streamer --release [--gpl]\n```\n\n**Windows only:** Again, the `--gpl` flag is needed only if you want to bundle FFmpeg.\n\nYou can find the resulting package in `build/alvr_streamer_[your platform]`\n\nIf you want to edit and rebuild the code, you can skip the `prepare-deps` command and run only the `build-streamer` command.\n\n## Fedora CUDA installation\n\nIf you are here for CUDA installation on Fedora you're at the right place! Else continue down to [Android App Building](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#android-app-building)\n\n### 1. Install Nvidia drivers and Fedora CUDA driver\n\n```bash\nsudo dnf update -y\n```\n\n(Reboot if you have a new kernel)\n\n```bash\nsudo dnf install akmod-nvidia\nsudo dnf install xorg-x11-drv-nvidia-cuda\n```\n\nWait until ```modinfo -F version nvidia``` doesn't report ```\"ERROR: Module nvidia not found\"``` anymore\n\n### 2. Install Nvidia's CUDA\n\nIn the previous step, we installed Fedora's CUDA that doesn't work with ALVR, installing Nvidia's CUDA works and creates directories instead\n\n```bash\nsudo dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/fedora37/x86_64/cuda-fedora37.repo\n```\n\nChange the Fedora version if you are on a different version. You should check if your version is supported by inspecting the repo\n\n```bash\nsudo dnf clean all\nsudo dnf module disable nvidia-driver\nsudo dnf -y install cuda\nexport PATH=/usr/local/cuda-12.3/bin${PATH:+:${PATH}}\n```\n\nIf your cuda version is different, change it to the version that is installed. You can check installed versions by doing ```ls /usr/local/ | grep \"cuda\"``` in your terminal\n\n#### Note about Nvidia's CUDA\n\n* Disabling the nvidia-driver doesn't disable Nvidia drivers but prevents nvidia dkms from installing over the akmod driver\n\n### 3. Install gcc11 install with homebrew\n\nBecuase cuda cannot be ran without a gcc version lower than or equal to gcc12, you will need to install a gcc version on homebrew. The fedora gcc11 package got removed so this is the only way sadly\nTo install homebrew, run this command:\n\n```bash\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n```\n\nThen install gcc11\n\n```bash\nbrew install gcc@11\n```\n\n#### Notes on installing gcc11 with homebrew\n\n* If brew is not found in your path, run the following separately to add brew to your path:\n  \n  ```bash\n  test -d ~/.linuxbrew && eval \"$(~/.linuxbrew/bin/brew shellenv)\" \n  test -d /home/linuxbrew/.linuxbrew && eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n  echo \"eval \\\"\\$($(brew --prefix)/bin/brew shellenv)\\\"\" >> ~/.bashrc\n  ```\n\n### 4. Modify dependencies.rs to use correct cuda path and gcc version\n\nBecause CURA installs as a symlink by default, we need to change the dependencies.rs to use the directory\nFrom the ALVR directory edit the ./alvr/xtask/src/dependencies.rs, and change two lines:\n\n* Line 159, change ```cuda``` -> ```cuda-12.3``` (or whatever version you have)\n* Line 179, replace that line with ```--nvccflags=\\\"-ccbin /home/linuxbrew/.linuxbrew/bin/g++-11 -gencode arch=compute_52,code=sm_52 -O2\\\"``` (Change homebrew path if needed, default is used)\n\nYou should be good to go! Refer to [Streamer Building](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#streamer-building) for the commands to build ALVR\n\n# Android App Building\n\n## 1. Installing necessary packages\n\nFor the app you need install:\n\n* [Android Studio](https://developer.android.com/studio) or the [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager)\n* Android SDK Platform-Tools 29 (Android 10)\n* Latest Android NDK (currently v25.1.8937393)\n\nOn Linux, the specific package names for the android tools can differ from distro to distro, see up on the wiki for more information:\n\n* Gentoo:\n  * <https://wiki.gentoo.org/wiki/Android>\n* Arch:\n  * <https://wiki.archlinux.org/title/Android>\n* Debian:\n  * <https://wiki.debian.org/AndroidStudio>\n* Ubuntu:\n  * <https://help.ubuntu.com/community/AndroidSDK>\n* Pop!\\_OS:\n  * N/A\n\nThe three mentioned developer applications can be installed from upstream; although the packages and setup responsible for the required tools can differ between distros, being:\n\n* **Arch**\n  * Packages can vary, read up on the Arch Wiki's [Android](https://wiki.archlinux.org/title/Android) page.\n* **Gentoo**\n  * `dev-util/android-studio`\n  * `dev-util/android-sdk-update-manager`\n  * `dev-util/android-ndk >= 25.1`\n\nFor Debian, it requires to have the `non-free` repository to be enabled:\n\n* **Debian 12 / Ubuntu 22.10 / Pop!\\_OS 22.10**\n  \n  ```bash\n  sudo apt install android-sdk-platform-tools-common sdkmanager google-android-ndk-r26b-installer\n  ```\n  \n## 2. Setting environment variables\n\nFor Windows, set the environment variables:\n\n* `JAVA_HOME`:\n  * Example: `C:\\Program Files\\Android\\Android Studio\\jre`\n* `ANDROID_HOME`:\n  * Example: `%LOCALAPPDATA%\\Android\\Sdk`\n* `ANDROID_NDK_HOME`:\n  * Example: `%LOCALAPPDATA%\\Android\\Sdk\\ndk\\25.1.8937393`\n\nFor Linux, the correct directories for the environment variables can greatly differ depending on the type of install. See the wiki page of your distro for more information:\n\n* Gentoo:\n  * <https://wiki.gentoo.org/wiki/Android>\n* Ubuntu:\n  * <https://help.ubuntu.com/community/AndroidSDK#Post-Installation_Configuration>\n\nDistro wikis that weren't listed above does not mention of environment variables, although generally they would be as:\n\n* `JAVA_HOME`:\n  * `/usr/lib/jvm/default-java/bin`\n* `ANDROID_HOME`:\n  * Arch: `~/Android/Sdk`\n  * Gentoo: `~/Android`\n  * Debian / Ubuntu / Pop!\\_OS: `~/AndroidSDK`\n* `ANDROID_NDK_HOME`:\n  * Arch: `/opt/android-sdk/ndk`\n  * Linux: `/usr/lib/android-sdk/ndk`\n\n## 3. Building\n\nFirst you need to gather some additional resources in preparation for the build.  \nMove to the root directory of the project, then run this command:\n\n```bash\ncargo xtask prepare-deps --platform android\n```\n\nBefore building the app, Android has to have us to agree to the licenses otherwise building the app will halt and fail. To accept the agreements, follow the instructions for your corresponding OS:\n\n* Windows:\n  \n  ```shell\n  cd \"%ANDROID_SDK_ROOT%\\tools\\bin\"\n  sdkmanager.bat --licenses\n  ```\n\n* Linux:\n  \n  ```bash\n  cd ~/AndroidSDK\n  sdkmanager --licenses\n  ```\n\nNext up is the proper build of the app. Run the following:\n\n```bash\ncargo xtask build-client --release\n```\n\nThe built APK will be in `build/alvr_client_quest`. You can then use adb or SideQuest to install it to your headset.\n\nTo build and run:\n\n```bash\ncd alvr/client_openxr\ncargo apk run\n```\n\nYou need the headset to be connected via USB and with the screen on to successfully launch the debugger and logcat.\n\n# Troubleshooting (Linux)\n\nOn some distributions, Steam Native runs ALVR a little better. To get Steam Native on Ubuntu run it with:\n\n```bash\nenv STEAM_RUNTIME=0 steam\n```\n\nOn Arch Linux, you can also get all the required libraries by downloading the `steam-native-runtime` package from the multilib repository\n\n```bash\nsudo pacman -S steam-native-runtime\n```\n\nDependencies might be missing then, so run:\n\n```bash\ncd ~/.steam/root/ubuntu12_32\nfile * | grep ELF | cut -d: -f1 | LD_LIBRARY_PATH=. xargs ldd | grep 'not found' | sort | uniq\n```\n\nSome dependencies have to be fixed manually for example instead of forcing a downgrade to libffi version 6 (which could downgrade a bunch of the system) you can do a symlink instead (requires testing):\n\n```bash\ncd /lib/i386-linux-gnu\nln -s libffi.so.7 libffi.so.6\n```\n\nand\n\n```bash\ncd /lib/x86_64-linux-gnu\nln -s libffi.so.7 libffi.so.6\n```\n\nA few dependencies are distro controlled, you can attempt to import the package at your own risk perhaps needing the use of alien or some forced import commands, but its not recommended (turns your system into a dependency hybrid mess), nor supported!\n"
  },
  {
    "path": "wiki/Controller-latency.md",
    "content": "Controller tracking will always be difficult. There are so many factors that influence the latency and the motion prediction. Its not something like \"100ms\" constantly, but depends on your movements and even the movement of the headset.\n\nThere are many parameters that influence the movement that can be changed:\n\n- Tracking is currently async to the rendering and running at 3*72=216Hz\n- Movement prediction is set to 0 to get the latest tracking info -> no prediction on the quest\n- Tracking info is sent to SteamVR\n- Tracking info is fed into SteamVR with an offset of 10ms to enable SteamVR pose prediction\n- The tracking point on the Quest is different that the point on the Rift S. Angular acceleration and linear acceleration of the controller needed to be transformed to the new reference.\n\nThere is a trade off between fast but wobbly and overshooting controllers and controllers that have a certain latency. For me, the current settings are perfectly playable for games like Skyrim, Fallout or Arizona Sunshine. Games like Beat Saber might be an issue. \n\nYou can change the 10ms offset for SteamVR in the \"Other\" tab of ALVR (Controller Pose Offset). \nThe parameter defines how old the data that is fed into SteamVR is and controls the SteamVR pose prediction. Set it to 0 to disable all predictions\n\nThe default is 0.01=10ms. Its the amount of time I needed to be able swing my sword in Skyrim without feeling weird. Its very possible that this value depends on the game/user, that's why it's exposed in the control panel, and you can change it on the fly\n"
  },
  {
    "path": "wiki/FFmpeg-Hardware-Encoding-Testing.md",
    "content": "FFmpeg hardware video offloading test commands to validate hardware encoding offloading is working.\n\nLearn more at: <https://trac.ffmpeg.org/wiki/HWAccelIntro>\n\n## Codecs\n\n* **Advanced Video Coding (AVC/h264)** - <https://en.wikipedia.org/wiki/Advanced_Video_Coding>\n\n Advanced Video Coding (AVC), also referred to as H.264 or MPEG-4 Part 10, is a video compression standard based on block-oriented, motion-compensated coding. It is by far the most commonly used format for the recording, compression, and distribution of video content. It supports a maximum resolution of 8K UHD. Hardware encoding support is widely available.\n\n* **High Efficiency Video Coding (HEVC/h265)** - <https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding>\n\n High Efficiency Video Coding (HEVC), also known as H.265 and MPEG-H Part 2, is a video compression standard designed as part of the MPEG-H project as a successor to the widely used Advanced Video Coding (AVC, H.264, or MPEG-4 Part 10). In comparison to AVC, HEVC offers from 25% to 50% better data compression at the same level of video quality, or substantially improved video quality at the same bit rate. It supports resolutions up to 8192×4320, including 8K UHD, and unlike the primarily 8-bit AVC, HEVC's higher fidelity Main 10 profile has been incorporated into nearly all supporting hardware. Hardware encoding support is widely available.\n\n* **AOMedia Video 1 (AV1)** - <https://en.wikipedia.org/wiki/AV1>\n\n AOMedia Video 1 (AV1) is an open, royalty-free video coding format initially designed for video transmissions over the Internet. It was developed as a successor to VP9 by the Alliance for Open Media (AOMedia). The AV1 bitstream specification includes a reference video codec. Hardware encoding support is limited to latest generation hardware.\n\n### Graphics Encoding APIs\n\n* **Video Acceleration API** - <https://en.wikipedia.org/wiki/Video_Acceleration_API>\n\n  Video Acceleration API (VA-API) is an open source application programming interface that allows applications such as VLC media player or GStreamer to use hardware video acceleration capabilities, usually provided by the graphics processing unit (GPU). It is implemented by the free and open-source library libva, combined with a hardware-specific driver, usually provided together with the GPU driver.\n\n  Check your current VA-API status with `vainfo`.\n\n* **Vulkan Video** - <https://en.wikipedia.org/wiki/Vulkan>\n\n Vulkan is a low-level low-overhead, cross-platform API and open standard for 3D graphics and computing. It was intended to address the shortcomings of OpenGL, and allow developers more control over the GPU. It is designed to support a wide variety of GPUs, CPUs and operating systems, it is also designed to work with modern multi-core CPUs. Support is upcoming. See: <https://www.khronos.org/blog/khronos-releases-vulkan-video-av1-decode-extension-vulkan-sdk-now-supports-h.264-h.265-encode>\n\n* **NVENC** - <https://en.wikipedia.org/wiki/Nvidia_NVENC>\n\n Nvidia NVENC is a feature in Nvidia graphics cards that performs video encoding, offloading this compute-intensive task from the CPU to a dedicated part of the GPU.\n\n* **AMD Advanced Media Framework**- <https://gpuopen.com/advanced-media-framework/>\n\n AMD AMF is a SDK for optimal access to AMD GPUs for multimedia processing.\n\n### Test Source Input Generation\n\nPlease note that using the test source for input generation induces CPU load. When monitoring for proper GPU offloading, there will still be expected CPU load from FFmpeg.\n\n```sh\nffmpeg -hide_banner -f lavfi -i testsrc2=duration=30:size=1280x720:rate=90\n```\n\n* **lavfi** - <https://ffmpeg.org/ffmpeg-devices.html#toc-lavfi>\n\n Libavfilter input virtual device. This input device reads data from the open output pads of a libavfilter filtergraph. For each filtergraph open output, the input device will create a corresponding stream which is mapped to the generated output. The filtergraph is specified through the option graph.\n\n* **testsrc2** - <https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-colorchart_002c-colorspectrum_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc>\n\n The `testsrc2` source generates a test video pattern, showing a color pattern, a scrolling gradient and a timestamp. This is mainly intended for testing purposes. The `testsrc2` source is similar to `testsrc`, but supports more pixel formats instead of just `rgb24`. This allows using it as an input for other tests without requiring a format conversion.\n\n  1) duration - how long of a clip in seconds\n  2) size - dimensions of the video\n  3) rate - frame rate per second\n\n### Render Playback\n\nUse your favorite video player to verify the video was rendered correctly.\n\n* MPV - <https://en.wikipedia.org/wiki/Mpv_(media_player)>\n\n mpv is free and open-source media player software based on MPlayer, mplayer2 and FFmpeg. It runs on several operating systems, including Unix-like operating systems (Linux, BSD-based, macOS) and Microsoft Windows, along with having an Android port called mpv-android. It is cross-platform, running on ARM, PowerPC, x86/IA-32, x86-64, and MIPS architecture.\n\n* VLC - <https://en.wikipedia.org/wiki/VLC_media_player>\n\n VLC media player (previously the VideoLAN Client and commonly known as simply VLC) is a free and open-source, portable, cross-platform media player software and streaming media server developed by the VideoLAN project. VLC is available for desktop operating systems and mobile platforms, such as Android, iOS and iPadOS. VLC is also available on digital distribution platforms such as Apple's App Store, Google Play, and Microsoft Store.\n\n### Nvidia GPU\n\nTest the Nvidia hardware encoding pipeline. Only NVENC is supported as the current Nvidia VA-API driver (<https://github.com/elFarto/nvidia-vaapi-driver>) only supports NVDEC. Check your hardware support at <https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new> for NVENC support.\n\nMonitoring utilities:\n\n* **nvtop** - <https://github.com/Syllo/nvtop>\n\n NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way.\n\n* **nvidia-smi pmon** - <https://developer.nvidia.com/nvidia-system-management-interface>\n\n The NVIDIA System Management Interface (nvidia-smi) is a command line utility, based on top of the NVIDIA Management Library (NVML), intended to aid in the management and monitoring of NVIDIA GPU devices. The `pmon` command lists the statistics for all the compute and graphics processes running on each device.\n\nNvenc AVC (h264) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-c:v h264_nvenc -qp 18 \\\nnvidia-h264_nvec-90fps-300s.mp4\n```\n\nNvenc HEVC (h265) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-c:v hevc_nvenc -qp 18 \\\nnvidia-hevc_nvec-90fps-300s.mp4\n```\n\nNvenc AV1 hardware encoding (Ada Lovelace or newer hardware):\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-c:v av1_nvenc -qp 18 \\\nnvidia-av1_nvec-90fps-300s.mp4\n```\n\n### Intel GPU\n\nTest the Intel hardware encoding pipeline. Only VA-API is supported with the intel-media-driver (<https://github.com/intel/media-driver>) on GEN based graphics hardware. Check your hardware support at <https://www.intel.com/content/www/us/en/docs/onevpl/developer-reference-media-intel-hardware/1-1/overview.html> for encoding codec support.\n\nMonitoring utilities:\n\n* **nvtop** - <https://github.com/Syllo/nvtop>\n\n NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way.\n\nVA-API AVC (h264) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v h264_vaapi -qp 18 \\\nintel-h264_vaapi-90fps-300s.mp4\n```\n\nVA-API HEVC (h265) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v hevc_vaapi -qp 18 \\\nintel-hevc_vaapi-90fps-300s.mp4\n```\n\nVA-API AV1 hardware encoding (Arc A-Series only):\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v av1_vaapi -qp 18 \\\nintel-av1_vaapi-90fps-300s.mp4\n```\n\n### AMD GPU\n\nTest the AMD hardware encoding pipeline. Only VA-API is supported with the mesa-va-drivers (<https://mesa3d.org/>) on AMD based graphics hardware. Check your hardware support at <https://en.wikipedia.org/wiki/Unified_Video_Decoder> for encoding codec support. Video Core Next (VCN) hardware is required for hardware encoding.\n\nMonitoring utilities:\n\n* **nvtop** - <https://github.com/Syllo/nvtop>\n\n NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way.\n\nVA-API AVC (h264) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v h264_vaapi -qp 18 \\\namd-h264_vaapi-90fps-300s.mp4\n```\n\nVA-API HEVC (h265) hardware encoding:\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v hevc_vaapi -qp 18 \\\namd-hevc_vaapi-90fps-300s.mp4\n```\n\nVA-API AV1 hardware encoding (VCN 4.0+, Navi 3x only):\n\n```sh\nffmpeg -hide_banner \\\n-f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \\\n-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \\\n-c:v av1_vaapi -qp 18 \\\namd-av1_vaapi-90fps-300s.mp4\n```\n"
  },
  {
    "path": "wiki/Fixed-Foveated-Rendering-(FFR).md",
    "content": "## What is it, why do I need it\n\nIn short: The human eye can only see sharp in a very small area (the fovea). That's why we move our eyes constantly to get the feeling that our whole view is a sharp image.\nThe idea of foveated rendering is, to only render the small portion of the screen where we look at at the highest resolution, and the other parts at a lower resolution. This would massively increase the performance without any noticeable visual impact.\nBut it has one drawback: You need to track the movement of the eyes. While there are already headsets out there that have eyetracking, the quest does not have it. \n\nThat's why oculus is using Fixed Foveated rendering. There is some research that shows, that if you assume that the user looks at the center of the screen, some parts of the image are more important than others ([Oculus](https://developer.oculus.com/documentation/mobilesdk/latest/concepts/mobile-ffr/)). Many games on the Quest use this to improve rendering performance.\n\n## FFR in ALVR\n\nThat's not how ALVR is using it :P \n\nWe don't have any influence on how games get rendered, we only get the final rendered image that should be displayed.\n\nWhat ALVR normally does is: \n\n- takes that image\n- encodes it as a video with the resolution you set at the video tab \n- transmits it to the Quest\n- displays the video\n\nWith FFR:\n\n- takes the image\n- projects the image, keeping the center area at the resolution you set at the video tab, reducing the resolution at the outer regions\n- encodes it as a video with the new, lower overall resolution\n- transmits the video to the Quest\n- reprojects the video to the original size\n- displays the image\n\nThere are two implementations of FFR by [zarik5](https://github.com/zarik5)\n\n- warp: Uses a radial warping of the video where the center stays the same resolution and the outer regions get \"squished\" to reduce resolution\n- slices: Slices the image into parts (center, left/right, top/bottom) and encodes the outer slices with lower resolution. This method produces a much sharper image.\n\n**Advantages**: Lower resolution results in faster encoding and decoding of the video which will decrease overall latency.  Same bitrate at lower resolution results in higher image quality. On slow networks, bitrate can be reduced resulting in the same quality as without ffr.\n\n**Drawbacks**: Using the warped method results in a slightly overall blurry image. You can compensate this by setting the initial video resolution to 125%.\nSlicing will result in a noticeable border where the high res center ends.\nIncreasing the foveation strength will result in visual artifacts with both methods\n\n## Configuration\n\nThat depends on your perception. You should try different settings going from strength = 2 up to 5 for both methods.\nThe higher you go, the more visual artifacts you will see at the edges of the screen.\nFor the slice method, you can also set a center offset. This moves the high res center up or down to accommodate games that have more interaction in the upper or lower part of the screen. \n\n[wikipedia](https://en.wikipedia.org/wiki/Foveated_rendering)\n"
  },
  {
    "path": "wiki/Hand-tracking-controller-bindings.md",
    "content": "Current bindings for ALVR 20\n===\n\nTo control state of gestures, you can toggle `Headset -> Controllers -> Gestures -> Only touch`\nEnabled state means that gestures won't be triggered, disabled would mean all gestures are activated.\n\nGestures\n---\n\n| Action         | Handtracking pinch                  |\n| -------------- | ----------------------------------- |\n| Trigger        | Pinch thumb and index               |\n| Joystick click | Curl thumb to palm                  |\n| Grip           | Curl middle, ring and little        |\n| Y/B            | Pinch thumb and middle              |\n| X/A            | Pinch thumb and ring                |\n| Menu button    | Pinch thumb and little on left hand |\n\nJoystick\n---\n\nActivation is done through curling all 4 fingers and touching top of hand with thumb\n"
  },
  {
    "path": "wiki/Headset-and-ALVR-streamer-on-separate-networks.md",
    "content": "## ALVR v14 and Above\n\nHere are explained two methods to connect PC and headset remotely, port-forwarding and ZeroTier. The primary purpose of this is connecting the headset to a Cloud PC (like ShadowPC).\n\n## Important notes on security\n\n* ALVR protocol does not have any encryption or authentication (apart from ALVR device IP address shown in ALVR streamer and the requirement to add devices on ALVR streamer).\n* It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR devices should be allowed) and ALVR streamer should not be left running unattended.\n* **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR device could take over the PC**).\n* As the license states ALVR IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND (see the file `LICENSE` in this GitHub repository for legal text/definition). You are on your own (especially if you run ALVR over the Internet without VPN).\n\n## Port-forwarding\n\nPort-forwarding allows to connect devices that are behind different NATs, i.e. local networks. You need to have administrator access to your router. This method has the best streaming performance.\n\n**IMPORTANT**: ALVR does not use end-to-end encryption of the stream data. By using this method you need to be aware that the connection is vulnerable to \"Man In The Middle\" attacks.\n\n1. Take note of the public IP of your headset. You can use the online tool [WhatIsMyIP](https://www.whatismyip.com/).\n2. Inside your router web interface or app, add a port-forwarding rule for your headset. You need to specify the ports 9943 and 9944 for both TCP and UDP.\n3. Connect to the remote PC and open ALVR. In the Devices tab press `Add device manually`. Fill in the fields with a name for your headset (you can use the name you want), the hostname (you can read it in the welcome screen in your headset when you open the ALVR app), the remote IP of the headset (that is the IP you got on step 1.) and then press `Save`.\n\nYou can now use ALVR to connect to your remote PC.\n\n**Note**: The public IP can change often. Every time you want to use ALVR you need to check that your current public IP is the same as the last time. If the IP changed, you can update it using the \"Edit connection\" interface, accessed with the `Edit` button next to your headset name on the streamer.\n\n## ZeroTier\n\n[ZeroTier](https://www.zerotier.com/) is a tunneling software that makes remote devices connect to each other as if they are in the same local network.\n\nComparing this to the port-forwarding method:\n\nPros:\n\n* Does not require access to the router interface.\n* You don't need to update the public IP often on the streamer.\n* The connection in encrypted.\n\nCons:\n\n* The streaming performance is worse. You may experience more glitches and loss of quality in the image and audio.\n\n### Requirements\n\n* [ZeroTier](https://www.zerotier.com/) for your PC\n* ZeroTier APK for your Quest (you can find it online)\n* SideQuest or some other method to install the ZeroTier APK onto your headset\n\n### Installation\n\nUse the \"Install APK\" function of SideQuest to install the ZeroTier APK to your Quest, and also download and install ZeroTier on your PC. After you've installed ZeroTier, follow Zerotier's official [Getting Started](https://zerotier.atlassian.net/wiki/spaces/SD/pages/8454145/Getting+Started+with+ZeroTier) guide to setup a network for ALVR. Join the network on both the Quest and the PC. On the Quest, make sure that the network is enabled by switching on the slider on the network in the list in the ZeroTier app (you may be prompted to allow ZeroTier to create a VPN connection).\n\nAfter both your PC and your Quest are connected to the same ZeroTier network, we'll need to manually add your quest to the ALVR dashboard. To do so, we'll need to find your Quest's ZeroTier IP. There are two ways to do this.\n\n* Go the the ZeroTier network page, find your quest under \"Members\", and copy the managed IP from there\n* Or, in the ZeroTier app on your quest, click on the network you created. The IP is under the \"Managed IPs\" section at the bottom.\n\nThe IP should look something like this `192.168.143.195`. If there's a `/` at the end with a couple numbers following it, remove them along with the slash.\n\nNext, we'll need to add the Quest to the ALVR dashboard. On your headset, launch ALVR. The on the ALVR dashboard on your PC, click the \"Add device manually\" button, provide a name and hostname (You can get this from the \"trust\" screen of ALVR on your Quest), then put in the IP address that we got from ZeroTier.\n\nAt this point, you should be ready to go. Have fun in VR!\n\n### Troubleshooting\n\n* If you can't get your Quest to connect to ALVR, and are stuck on the \"Trust\" screen, try to ping your Quest's managed IP address (the one we got earlier). If it says \"no route to host\" or something similar, your Quest can't see your PC. Try running through the steps above to make sure you didn't miss anything.\n\n## Tailscale\n\nAn alternative to ZeroTier with practically the same setup procedure. This could have better latency, depending on your distance to the datacenter.\n<https://tailscale.com/>\n\n## n2n\n\n[n2n](https://github.com/ntop/n2n) is another P2P VPN solution, just like ZeroTier. You need to run _supernode_ on a server with an IP and port publicly accessible on internet (or at least your PC and Quest can access to it), and run _edge_ node on your PC and Quest.\n\nIts pros and cons are similar to ZeroTier, but it's self-hosted and open-source if you care about privacy, though instead you need some knowledge about networking and server deploying.\n\n### Requirements\n\n* Compile [n2n](https://github.com/ntop/n2n) from source\n  * Or you can grab pre-built binaries from [here](https://github.com/lucktu/n2n) directly, compiled by lucktu.\n  * Some Linux distribution may have n2n, but be sure you're using the same version. Since the source code is v3, the following steps will also use v3 in the example below.\n* [TAP-Windows driver](https://community.openvpn.net/openvpn/wiki/GettingTapWindows) or [OpenVPN](https://openvpn.net/community/) (includes TAP-Windows) if you're using Windows PC\n* [hin2n](https://github.com/switch-iot/hin2n) APK\n* A server with public IP and allow public ports\n* SideQuest or some other method to install the hin2n APK onto your headset\n\n### Installation\n\nWe're going to use n2n v3, and set the port of _supernode_ to `1234` as the example. You can change `1234` to any port, but below `1024` requires root.\n\n* Open port `1234` on your server's firewall (usually `iptables`, if you don't know what to do, ask Google).\n* Upload _supernode_ binary to your server, run `./supernode -p 1234`.\n* Install TAP-Windows driver or OpenVPN on your PC if you're using Windows.\n* Upload _edge_ binary to your PC, run `./edge -c [network-name] -k [secret-password] -a 192.168.100.1 -l [your-server-ip]:1234` to connect to the _supernode_, assign the IP `192.168.100.1` to the PC, and use the password you provided for data encryption.\n* Once you see `[OK] edge <<< ================ >>> supernode`, your PC is done, or you need to follow the error logs to see what's wrong.\n* Install _hin2n_ on your Quest and open it, click the plus button at the top-right corner to add a new configuration and assign `192.168.100.2` to your Quest:\n  * N2N version: v3\n  * Supernode: `[your-server-ip]:1234`\n  * Community: `[network-name]`\n  * Encrypt key: `[secret-password]`\n  * IP address: `192.168.100.2`\n  * Subnet mask: `255.255.255.0`\n* Click \"Current Setting\" under the connect button, select the configuration we created just now, then click the connect button. If you're asked to allow hin2n to create a VPN connection, allow it.\n* Once you see `[OK] edge <<< ================ >>> supernode`, your Quest is done.\n* Open ALVR on your headset, record the hostname it shows.\n* Open ALVR dashboard on your PC, click \"Add device manually\" button, put the hostname you just recorded, and set IP address to `192.168.100.2` which is assigned to Quest just now.\n* Once it's done, you're all set.\n\n### Troubleshooting\n\n* Make sure you can access to the supernode, your supernode should be run on a server with public IP, and you can ping it on your PC.\n* If your Quest cannot connect to ALVR dashboard, ping the IP you assigned to Quest in hin2n. If it fails, try redoing the setup steps.\n* If the edge binary or hin2n says the IP has already been assigned and not released by supernode, you can set IP address to another one in the same subnet like `192.168.100.123` to reassign a new IP to the device.\n* If you're playing over WAN, you may see more glitches, higher stream latency, or lagger response with TCP. Use adaptive bitrate and UDP may improve your experience.\n"
  },
  {
    "path": "wiki/Home.md",
    "content": "ALVR is a vr streaming software that allows you to stream SteamVR games to your standalone VR headset.\n\nUse the sidebar to navigate the wiki.\n"
  },
  {
    "path": "wiki/How-ALVR-works.md",
    "content": "This document details some technologies used by ALVR.\n\nIf you have any doubt about what is (or isn't) written in here you can contact @zarik5, preferably on Discord.\n\nThis document was last updated on June 27th 2023 and refers to the master branch.\n\n## Table of contents\n\n* Architecture\n  * The packaged application\n  * Programming languages\n  * Source code organization\n* Logging and error management\n  * The event system\n* Session and settings\n  * Procedural generation of code and UI\n* The dashboard\n  * The user interface\n  * Driver communication\n  * Driver lifecycle\n* The streaming pipeline: Overview\n* Client-driver communication\n  * Discovery\n  * Streaming\n* SteamVR driver\n* Client and driver compositors\n  * Foveated rendering\n  * Color correction\n* Video transcoding\n* Audio\n* Tracking and display timing\n* Other streams\n* Upcoming\n  * Phase sync\n  * Sliced encoding\n\n## Architecture\n\n### The packaged application\n\nALVR is made of two applications: the streamer and client. The streamer can be installed on Windows and Linux, while the client is installed on Android VR headsets. The client communicates with the driver through TCP or UDP sockets.\n\nThe client is a single unified APK, named `alvr_client_android.apk`. It is powered by OpenXR and it is compatible with Quest headsets, recent Pico headsets and HTC Focus 3 and XR Elite.  \n\nThe streamer is made of two main parts: the dashboard and the driver (also known as server). The driver is dynamically loaded by SteamVR. This is the file structure on Windows:\n\n* `bin/win64/`\n  * `driver_alvr_server.dll`: The main binary, responsible for client discovery and streaming. Loaded by SteamVR.\n  * `driver_alvr_server.pdb`: Debugging symbols\n  * `openvr_api.dll`: OpenVR SDK used for updating the chaperone.\n  * `vcruntime140_1.dll`: Windows SDK used by C++ code in the driver.\n* `ALVR Dashboad.exe`: Dashboard binary used to change settings, manage clients, monitor statistics and do installation actions. It can launch SteamVR.\n* `driver.vrdrivermanifest`: Auxiliary config file used by the driver.\n\nAt runtime, some other files are created:\n\n* `session.json`: This contains unified configuration data used by ALVR, such as settings and client records.\n* `session_log.txt`: Main log file. Each line is a json structure and represents an event generated by the driver. This gets cleared each time a client connects.\n* `crash_log.txt`: Auxiliary log file. Same as  `session_log.txt`, except only error logs are saved, and does not get cleared.\n\n### Programming languages\n\nALVR is written in multiple languages: Rust, C, C++, HLSL, GLSL. The main language used in the codebase is Rust, which is used for the dashboard, networking, video decoding and audio code. C and C++ are used for graphics, video encoding and SteamVR integration. HLSL is used for graphics shaders on the Windows driver, GLSL is used on the Linux driver and the client. Moving forward, more code will be rewritten from C/C++ to Rust and HLSL code will be moved to GLSL or WGSL.\n\nRust is a system programming language focused on memory safety and ease of use. It is as performant as C++ but Rust code is less likely to be affected by runtime bugs. The prime feature Rust feature used by ALVR is enums, that correspond to tagged unions in C++. Rust's enum is a data type that stores different kinds of data, but only one type can be accessed at a time. For example the type `Result` can contain either an `Ok` value or an `Err` value but not both. Together with pattern matching, this is the foundation of error management in Rust applications.\n\n### Source code organization\n\nALVR code is hosted in a monorepo. This is an overview of the git tree:\n\n* `.github/`: Contains scripts used by the GitHub CI.\n* `alvr/`: Each subfolder is a Rust crate (\"crate\" means a code library or executable).\n  * `audio/`: Utility crate hosting audio related code shared by client and driver.\n  * `client_core/`: Platform agnostic code for the client. It is used as a Rust library for `alvr_client_openxr` and can also compiled to a C ABI shared library with a .h header for integration with other projects.\n  * `client_mock/`: Client mock implemented as a thin wrapper around `alvr_client_core`.\n  * `client_openxr/`: Client implementation using OpenXR, compiled to a APK binary.\n  * `common/`: Some common code shared by other crates. It contains code for versioning, logging, struct primitives, and OpenXR paths.\n  * `dashboard/`: The dashboard application.\n  * `events/`: Utility crate hosting code related to events.\n  * `filesystem/`: Utility crate hosting code for filesystem abstraction between Windows and Linux.\n  * `packets/`: Utility crate containing packet definitions for communication between client, driver and dashboard.\n  * `server/`: The driver shared library loaded by SteamVR.\n  * `server_io/`: Common functionality shared by dashboard and driver, for interaction with the host system. This allows dashboard and driver to work independently from each other.\n  * `session/`: Utility crate related to session file and data management.\n  * `sockets/`: Utility crate shared by client and driver with socket and protocol implementation.\n  * `vrcompositor_wrapper/`: Small script used on Linux to correctly load the ALVR Vulkan layer by SteamVR.\n  * `vulkan_layer/`: Vulkan WSI layer used on Linux to work around limitations of the OpenVR API on Linux. This is mostly patchwork and hopefully will be removed in the future.\n  * `xtask/`: Utility CLI hosting a variety of scripts for environment setting, building, and packaging ALVR. Should be called with `cargo xtask`.\n* `resources/`: resources for the README.\n* `wiki/`: Contains the source for the Github ALVR wiki. Changes are mirrored to the actual wiki once committed.\n* `about.toml`: Controls what dependency licenses are allowed in the codebase, and helps with generating the licenses file in the packaged ALVR streamer.\n* `Cargo.lock`: Contains versioning information about Rust dependencies used by ALVR.\n* `Cargo.toml`: Defines the list of Rust crates contained in the repository, and hosts some other workspace-level Rust configuration.\n\n## Logging and error management\n\nLogging is split into interface and implementation. The interface is defined in `alvr/common/src/logging.rs`, the implementations are defined in each binary crate as `logging_backend.rs`.\n\nALVR logging system is based on the crate [log](https://crates.io/crates/log). `log` is already very powerful on its own, since its macros can collect messages, file and line number of the invocation.\n\nALVR defines some structures, macros and functions to ease error management. The base type used for error management is `StrResult<T>` that is an alias for `Result<T, String>`. Read more about Rust's Result type [here](https://doc.rust-lang.org/std/result/).\n\nThere are many ways of logging in ALVR, each one for different use-cases. To make use of them you should add `use alvr_common::prelude::*` at the top of the Rust source file.\n\n* `error!()`, `warn!()`, `info!()`, `debug!()` (reexported macros from the `log` crate). Log is processed depending on the logging backend.\n* `show_e()` and `show_w()` are used to log a string message, additionally showing a popup.\n* `show_err()`, `show_warn()` work similarly to `show_e()` and `show_w()`, but they accept a `Result<>` and log only if the result is `Err()`.\n* `fmt_e!()` adds tracing information to a message and produces a `Err()`, that can be returned.\n* `err!()` and `enone!()` used respectively with .`.map_err()` and `.ok_or_else()`, to map a `Result` or `Option` to a `StrResult`, adding tracing information.\n* Some other similarly named functions and macros with similar functionality\n\n### The event system\n\nEvents are messages used internally in the driver and sent to dashboard instances. Events are generated with `send_event()` and is implemented on top of the logging system.\n\nThis is the layout of `Event`, in JSON form\n\n```json\n{\n  \"timestamp\": \"<timestamp>\",\n  \"event_type\": {\n    \"id\": \"<EventType>\",\n    \"content\": { <depends on id> }\n  }\n}\n```\n\nLog is a special kind of event:\n\n```json\n{\n  \"timestamp\": \"<timestamp>\",\n  \"event_type\": {\n    \"id\": \"Log\",\n    \"content\": {\n      \"severity\": \"Error or Warn or Info or Debug\",\n      \"content\": \"<the message>\"\n    }\n  }\n}\n```\n\nThe driver logs events in JSON form to `session.json`, one per line.\n\nCurrently its use is limited, but eventually this will replace the current logging system, and logging will be built on top of the event system. The goal is to create a unified star-shaped network where each client and dashboard instance sends events to the server and the server broadcasts events to all other clients and dashboard instances. This should also unify the way the server communicates with clients and dashboards, making the dashboard just another client.\n\n## Session and settings\n\nALVR uses a unified configuration file, that is `session.json`. It is generated the first time ALVR is launched. This file contains the following top-level fields:\n\n* `\"server_version\"`: the current version of the streamer. It helps during a version upgrade.\n* `\"drivers_backup\"`: temporary storage for SteamVR driver paths. Used by the dashboard.\n* `\"openvr_config\"`: contains a list of settings that have been checked for a diff. It is used by C++ code inside the driver.\n* `\"client_connections\"`: contains entries corresponding to known clients.\n* `\"session_settings\"`: all ALVR settings, laid in a tree structure.\n\n### Procedural generation of code and UI\n\nALVR lays out settings in a tree-like structure, in a way that the code itself can efficiently make use of. Settings can contain variants (in `session.json` are specified in PascalCase), which represent mutually exclusive options.\n\nALVR uses the macro `SettingsSchema` in the `settings-schema` crate to generate auxiliary code, ie a schema and the \"default representation\" of the settings. This is a crate created specifically for ALVR but can be used for other projects too.\n\nThe schema is made of nested `SchemaNode`s that contain metadata. Some of the metadata is specified directly inside inline attributes in structures and enums.\n\nThe \"default representation\" (the type names are generated by concatenating the structure/enum name with `Default`), are structures that can hold settings in a way no not lose information about unselected variants; enums are converted to structs and variants that hold a value are converted to fields. The main goal of this is to meet the user expectation of not losing nested configuration when changing some options. The default representation is exactly what is saved inside `session.json` in `\"session_settings\"`.\n\nInfo about the various types of schema nodes can be found [here](https://github.com/zarik5/settings-schema-rs).\n\nThe dashboard makes use of schema metadata and the default representation to generate the settings UI. The end result is that the settings UI layout closely matches the structures used internally in the code, and this helps understanding the inner workings of the code.\n\nWhen upgrading ALVR, the session might have a slightly different layout, usually some settings might have been added/removed/moved/renamed. ALVR is able to handle this by doing an extrapolation process: it starts from the default session, and replace values taken from the old session file with the help of the settings schema.\n\n## The dashboard\n\nThe dashboard is the main way of interacting with ALVR. Functionality is organized in tabs.\n\n### The User Interface\n\nThese are the main components:\n\nTODO: Add screenshots\n\n* Sidebar: is used to select the tab for the main content page.\n* Devices tab: used to trust clients or add them manually specifying the IP\n* Statistics tab: shows graphs for latency and FPS and a summary page\n* Settings tab: settings page split between `Presets` and `All Settings`. `All Settings` are procedurally generated from a schema. `Presets` are controls that modify other settings.\n* Installation tab: utilities for installation: setting firewall rules, registering the driver, launching the setup wizard.\n* Logs tab: shows logs and events in a table.\n* Debug tab: debugging actions.\n* About tab: information about ALVR.\n* Lower sidebar button: can be either \"Launch SteamVR\" or \"Restart SteamVR\", depending on the driver connection status\n* Notification bar: shows log in a non-obstructive way.\n\n### Driver communication\n\nThe dashboard communicates with the driver in order to update its information and save configuration. This is done through a HTTP API, with base URL `http://localhost:8082`. These are the endpoints:\n\n* `/api/dashboard-request`: This is the main URL used by the dashboard to send messages and data to the server. The body contains the specific type and body of the request.\n* `/api/events`: This endpoint is upgraded to a websocket and is used for listening to events from the driver\n* `/api/ping`: returns code 200 when the driver is alive.\n\nThe dashboard retains some functionality when the driver is not launched. It can manage settings, clients and perform installation actions, but clients cannot be discovered. Once The driver is launched all these actions are performed by the server, requested with the HTTP API. This mechanism ensures that there are no data races.\n\n### Driver lifecycle\n\nThe dashboard is able to launch and restart SteamVR, in order to manage the driver's lifecycle.\n\nThe driver launch procedure is as follows:\n\n* The driver is registered according to the \"Driver launch action\" setting, if needed. By default, current SteamVR drivers are unregistered and backed up inside `session.json`.\n* On Linux, the vrcompositor wrapper is installed if needed\n* SteamVR is launched.\n\nOnce the drivers shuts down, if there are backed up drivers, these are restored.\n\nThe driver restart procedure is as follows:\n\n* The dashboard notifies the driver that it should be restarted.\n* The driver sends a request for restart to the dashboard.\n* The driver asks SteamVR to shutdown, never unregistering drivers.\n* The dashboard waits for SteamVR to shutdown, otherwise killing it after a timeout.\n* The dashboard relaunches SteamVR.\n\nThis might seem unnecessarily complicated. The reason for the first message round trip is to plug-in to the existing restarting system used by settings invalidation, which is invoked from the driver itself. The reason which the driver cannot be autonomous in restarting is because any auxiliary process spawned by the driver will block SteamVR shutdown or leave it in a zombie state.\n\n## The streaming pipeline: Overview\n\nThe goal of ALVR is to bridge input and output of a PCVR application to a remote headset. In order to do this ALVR implements pipelines to handle input, video and audio. The tracking-video pipeline (as known as the motion-to-photon pipeline) is the most complex one and it can be summarized in the following steps:\n\n* Poll tracking data on the client\n* Send tracking to the driver\n* Execute the PCVR game logic and render layers\n* Compose layers into a frame\n* Encode the video frame\n* Send the encoded video frame to the client through the network\n* Decode the video frame on the client\n* Perform more compositor transformations\n* Submit the frame to the VR runtime\n* The runtime renders the frame during a vsync.\n\n## Client-driver communication\n\nALVR uses a custom protocol for client-driver communication. ALVR supports UDP and TCP transports. USB connection is supported although not as a first class feature; you can read more about it [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)).\n\n### Discovery\n\nUsually the first step to establish a connection is discovery. When the server discovers a client it shows it in the \"New devices\" section in the Devices tab. The user can then trust the client and the connection is established.\n\nALVR uses a UDP socket at 9943 for discovery. The client broadcasts a packet and waits for the driver to respond. It's the client that broadcasts and it's the driver that then asks for a connection: this is because of the balance in responsibility of the two peers. The client becomes the portal though a PC, that can contain sensitive data. For this reason the server has to trust the client before initiating the connection.\n\nThis is the layout of the discovery packet\n\n|      Prefix       | Protocol ID | Hostname |\n| :---------------: | :---------: | :------: |\n| \"ALVR\" + 0x0 x 12 |   8 bytes   | 32 bytes |\n\n* The prefix is used to filter packets and ensure a packet is really sent by an ALVR client\n* The protocol ID is a unique version identifier calculated from the semver version of the client. If the client version is *semver-compatible* with the streamer, the protocol ID will match.\n* Hostname: the hostname is a unique identifier for a client. When a client is launched for the first time, an hostname is chosen and it persists for then successive launches. It is reset when the app is upgraded or downgraded.\n\nThe format of the packet can change between major versions, but the prefix must remain unchanged, and the protocol ID must be 8 bytes.\n\n### Streaming\n\nALVR uses two sockets for streaming: the control socket and stream socket. Currently these are implemented with async code; there's a plan to move this back to sync code.\n\nThe control socket uses the TCP transport; it is used to exchange small messages between client and server, ALVR requires TCP to ensure reliability.\n\nThe stream socket can use UDP or TCP; it is used to send large packets and/or packets that do not require reliability, ALVR is robust to packet losses and packet reordering.\n\nThe specific packet format used over the network is not clearly defined since ALVR uses multiple abstraction layers to manipulate the data (bincode, tokio Length Delimited Coding). Furthermore, packets are broken up into shards to ensure they can support the MTU when using UDP.\n\nSince the amount of data streamed is large, the socket buffer size is increased both on the driver side and on the client.\n\n## SteamVR driver\n\nThe driver is the component responsible for most of the streamer functionality. It is implemented as a shared library loaded by SteamVR. It implements the [OpenVR API](https://github.com/ValveSoftware/openvr) in order to interface with SteamVR.\n\nUsing the OpenVR API, ALVR pushes tracking and button data to SteamVR using `vr::VRServerDriverHost()->TrackedDevicePoseUpdated()`. SteamVR then returns a rendered game frame with associated pose used for rendering. On Windows, frames are retrieved implementing the `IVRDriverDirectModeComponent` interface: SteamVR calls `IVRDriverDirectModeComponent::Present()`. On Linux this API doesn't work, and so ALVR uses a WSI Vulkan layer to intercept display driver calls done by vrcompositor. The pose associated to the frame is obtained from the vrcompositor execution stack with the help of libunwind.\n\n## Client and driver compositors\n\nALVR is essentially a bridge between PC and headset that transmits tracking, audio and video. But it also implements some additional features to improve image quality and streaming performance. To this goal, ALVR implements Fixed Foveated Rendering (FFR) and color correction.\n\nThe client compositor is implemented in OpenGL, while on the server it's either implemented with DirectX 11 on Windows or Vulkan on Linux. There are plans to move all compositor code to the graphics abstraction layer [wgpu](https://github.com/gfx-rs/wgpu), mainly for unifying the codebase.\n\nIt's important to note that ALVR's compositors are separate from the headset runtime compositor and SteamVR compositors. The headset runtime compositor is part of the headset operative system and controls compositing between different apps and overlays, and prepares the image for display (with lens distortion correction, chroma aberration correction, mura and ghosting correction). On the driver side, on Windows ALVR takes responsibility for compositing layers returned by SteamVR. The only responsibility of SteamVR is converting the frame into a valid DXGI texture if the game uses OpenGL or Vulkan graphics. On Linux ALVR grabs Vulkan frames that are already composited by vrcompostor. This introduced additional challenges since vrcompositor implements async reprojection which disrupts our head tracking mechanism.\n\n### Foveated encoding\n\nFoveated rendering is a technique where frame images are individually compressed in a way that the human eye barely detects the compression. Particularly, the center of the image is kept at original resolution, and the rest is compressed. ALVR refers to foveated rendering as \"Foveated encoding\" to clarify its scope. In native standalone or PCVR apps, foveated rendering reduces the load on the GPU by rendering parts of the image ar lower resolution. In ALVR's case frames are still rendered at full resolution, but are then \"encoded\" (compressing the outskirts of the image) before actually encoding and transmitting them. The image is then reexpanded on the client side after decoding and before display.\n\nCurrently ALVR supports only fixed foveation, but support for tracked eye foveation is planned.\n\nIn its history, ALVR implemented different algorithms for foveated encoding. The first one is \"Warp\", where the image is compressed in an elliptical pattern using the tangent function to define the compression ratio radially. A problem with algorithm is that it causes the image to become blurry. [Here](https://www.shadertoy.com/view/3l2GRR) is a demo of this algorithm in action. The second algorithm used was \"Slices\" where the image is sliced up into 9 sections (center, edges, corners), compressed to different degrees and the re-packed together into a single rectangular frame. The main issue with this algorithm was its complexity. You can find a demo [here](https://www.shadertoy.com/view/WddGz8). The current algorithm in use is reimplementation of Oculus AADT (Axis-Aligned Distorted Transfer), which simply compresses the lateral edges of the image horizontally and the vertical edged vertically. This algorithm has less compression power but it's much simpler and less taxing on the Quest's GPU.\n\n### Color correction\n\nColor correction is implemented on the server and adds simple brightness, contrast, saturation, gamma and sharpening controls. It's implemented on the server for performance reasons and to avoid amplifying image artifacts caused by transcoding.\n\n## Video transcoding\n\nTo be able to send frames from driver to client through the network, they have to be compressed since current WiFi technology doesn't allow to send the amount of data of raw frames. Doing a quick conservative calculation, let's say we have 2 x 2048x2048 eye images, 3 color channels, 8 bits per channel, sent 72 times per second, that would amount to almost 15 Gbps.\n\nALVR uses h264 and HEVC video codecs for compression. These codecs are chosen since they have hardware decoding support on Android and generally hardware encoding support on the PC side. On Windows, the driver uses NvEnc for Nvidia GPUs and AMF for AMD GPUS; on Linux ALVR supports VAAPI, NvEnc and AMF through FFmpeg. In case the GPU doesn't support hardware encoding, on both Windows and Linux ALVR supports software encoding with x264 (through FFmpeg), although the performance is often insufficient for a smooth experience. The client supports only MediaCodec, which is the API to access hardware video codecs on Android.\n\nh264 and HEVC codecs compression works on the assumption that consecutive frames are similar to each other. Each frame is reconstructed from past frames + some small additional data. For this reason, packet losses may cause glitches that persist many frames after the missing frame. When ALVR detects packet losses, it requests a new IDR frame from the encoder. A IDR frame is a packet that contains all the information to build a whole frame by itself; the encoder will ensure that successive frames will not rely on older frames than the last requested IDR.\n\n## Audio\n\nGame audio is captured on the PC and sent to the client, and microphone audio is captured on the client and sent to the PC. Windows and Linux implementation once again differ. On Windows, game audio is captured from a loopback device; microphone is is sent to virtual audio cable software to expose audio data from a (virtual) input device. On Linux the microphone does not work out-of-the-box, but there is a bash script available for creating and plugging into pipewire audio devices.\n\nUnlike for video, audio is sent as a raw PCM waveform and new packets do not rely on old packets. But packet losses may still cause popping, which happens when there is a sudden jump in the waveform. To mitigate this, when ALVR detects a packet loss (or a buffer overflow or underflow) it will render a fadeout or cross-fade.\n\n## Tracking and display timing\n\nHandling head and controller tracking is tricky for VR applications, and even more for VR streaming applications.\n\nIn a normal native VR application, tracking is polled at the beginning of the rendering cycle, it is used to render the eye views from a certain perspective and render the controller or hand models. When the game finished rendering the frame it submits it to the VR runtime which will display it on screen. From the time tracking is polled and the frame is displayed on screen, 1 or more frame durations may have passed (for example at 72fps the frame duration is 13ms). Our eyes are very sensitive to latency, especially for orientation, so VR runtimes implement image reprojection (Oculus calls it Asynchronous Time Warp). Reprojection works by rendering the frame rotated in 3D to compensate for the difference in orientation between the tracking pose polled at the beginning of the rendering cycle and the actual pose of the headset at the time of vsync when the image should be pushed to the display. To be able to correctly rotate the image, the runtime will also need to know the timestamp used for polling tracking, which can be the time of poll, or better, the predicted time of the vsync. If a time in the future is used for tracking poll, the polled tracking will be extrapolated.\n\nFor VR streaming applications, the pipeline is similar, except that tracking is polled for a more distant point in the future, to compensate for the whole transcoding pipeline, and it's not trivial to decide on how much to predict in the future. ALVR calculates the prediction offset by reading how much time passes between the tracking poll time and the time a frame rendered with the same tracking is submitted. These interval samples are averaged and then used for future tracking polls. (To calculate the correct total latency you also need to add the VR runtime compositor latency, which in the dashboard latency graph is shown as \"Client VSync\").\n\nOn the streamer side, ALVR needs to workaround a OpenVR API limitation. SteamVR returns frames with its pose, but then ALVR is responsible of matching the pose with one of the poses submitted previously and re-match its timestamp.\n\n## Other streams\n\nThere are some other kinds of data which can be streamed without requiring any special timing. These are button presses and haptics, respectively sent from client to driver and from driver to client.\n\n## Upcoming\n\n### Phase sync\n\nPhase sync is not a single algorithm but many that share similar objectives, reducing latency or jitter in the rendering/streaming pipeline. The term \"phase sync\" comes from Oculus, that describes its algorithm for reducing latency in its OpenXR runtime by starting the rendering cycle as late las possible to reduce waiting time before the vsync.\n\nIn general, a phase sync algorithm is composed of two parts: a queue that holds data resources or pointers, and a statistical model to predict event times. The statistical model is fed with duration or other kinds of timing samples and as output it returns a refined time prediction for a recurring event. The statistical model could be simple and just aim for a average-controlled event, or more complex that aims for submitting for a deadline; the second case needs to take into account the variance of the timing samples. Unlike Oculus implementation, these statistical models can be highly configurable to tune the target mean or target variance.\n\nThere are a few phase sync algorithms planned to be implemented: frame submission timing (to reduce frame queueing on the client, controlled by shifting the phase of the driver rendering cycle), SteamVR tracking submission timing (to make sure SteamVR is using exactly the tracking sample we want) and tracking poll timing (to reduce queuing on the server side).\n\n## Sliced encoding\n\nSliced encoding is another algorithm showcased by Oculus and it's about reducing latency by parallelizing work. In a simple streaming pipeline, frames are processed sequentially: rendering, then encoding, then transmission, then decoding. There is already some degree of parallelism, as rendering, encoding, transmission, and decoding can happen at the same time. Sliced encoding can help in reducing encoding and decoding time, as the frames are split into \"slices\". This allows for more efficient utilization of hardware encoders/decoders, or even use hardware and software codecs in parallel. It's crucial to note that network latency cannot be optimized. Given the constraint of network, sliced encoding can reduce waiting times between encoder/transmission and transmission/decoder as each encoded slice can be transmitted immediately and doesn't have to wait for the rest of the frame to be encoded (and a similar reasoning applies for the decoding side).\n"
  },
  {
    "path": "wiki/Information-and-Recommendations.md",
    "content": "## PC\n\n- A high-end PC is a requirement; ALVR is not a cheap alternative to a PCVR HMD\n- ALVR resolution configuration and SteamVR multi-sampling may be used to influence quality in favor of performance or vice-versa.\n- Frequent dropped frames can cause a poor experience on ALVR; this can be verified using a tool such as [OVR Advanced Settings](https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings).\n- Higher bit-rates will cause higher latency.\n- Ensure all relevant software is up to date - especially graphics and network drivers.\n- A good starting point is 100% resolution (`Very low` resolution preset) and 30mbit constant bitrate. In this config it should be very smooth with almost no lag or packet loss; packet loss seen at this point is likely a result of network issues.\n\n## Network\n\n- A wired connection from the PC to the network is **strongly recommended**.\n- A modern mid to high-end router and / or access point supporting at least 802.11ac (ideally 802.11ax) is recommended.\n\n## Wireless\n\n### General WiFi configuration best practices\n\n- Any device that can be wired should be - each wireless device slows down the overall wireless network\n- Devices should have the least amount of obstructions and be as close to the access point or router as possible\n- Any other wireless networks (ex: a printer's default wireless network) should be disabled; each network slows others down\n- Any devices that do not need high speeds but support them (ex: a thermostat) should use 2.4Ghz; often middle and higher end access points and routers support methods to \"force\" clients to use 2.4Ghz, and some can even perform this automatically based on signal strength and connection speed\n- Only WiFi revisions which are necessary should be enabled; older standards such as 802.11b, 802.11g, and to a lesser extent, 802.11n, will slow down all clients\n- Devices that require high speeds (such as standalone headset) should use:\n  - 5GHz only\n  - The newest WiFi specifications (802.11ax, followed by 802.11ac)\n  - In most environments, the largest channel width possible (160MHz for 802.11ax, 80MHz in practice for 802.11ac) (**note: some vendors do not set this to the maximum by default**)\n  - The lowest utilization, followed by the lowest channel number (sub-frequency) possible\n- **Manually selecting channels should only be done in places with extreme noise, or on older, lower quality, or ISP provided access points or routers** - modern mid to high-end routers and access points should optimize their channels fairly well, and as a result of other routers and clients \"channel hopping\", static settings are often less optimal\n- If a specific WiFi channel range is absolutely necessary, use a WiFi scanning tool on a phone or PC to determine the least used channels - mid to high-end access points and routers may provide an interface for this as well, however, this sometimes causes a disconnect when scanning\n- **Manually selecting wifi signal strength should only be done in places with extreme noise** - modern routers and access points do this well, and it is a complex task\n- If a specific transmit power is necessary, keep in mind that stronger is not always better - as transmit power increases, distortion may increase (leading to *lower* speeds), battery life of clients may increase (due to the higher power requested by the access point or router), and issues with sticky clients (devices which stay connected to wifi even with bad signal) may appear\n- If you have a significant number of devices, some routers and access points support features such as airtime fairness, which help to limit the amount of airtime slower clients take, improving the performance of higher speed clients\n\n### Things to keep in mind when configuring a wireless network and devices\n\n- All devices on the same frequency impact each other (**including other WiFi networks on the same channel**) because only one device can transmit or receive data at a time, meaning that:\n  - If one device utilizes WiFi heavily it will impact the latency and throughput of all other clients\n  - If a slow device is connected, it can still take a significant amount of \"airtime\" (time for that dedicated client to transmit / receive data to the access point or router), even though it does so at a slower rate than other clients\n  - Each connected device requires additional time, regardless of whether it is actively in use (and often devices send small amounts of data when idle for things such as NTP and DHCP)\n- WiFi is [half duplex](https://en.wikipedia.org/wiki/Duplex_(telecommunications)#Half_duplex) by nature of it being radio frequency, meaning data can only ever be transmitted **or** received on the same frequency, not both at the same time; twisted pair (copper ethernet cable) is full duplex\n- Wireless frequency bands (ex: 2.4Ghz, 5Ghz) have separate channels that can be statically assigned if needed, but **these are not mutually exclusive, meaning the channels overlap significantly and interfere with each other**\n- Different regions of the world support different channels (sub-frequencies); devices sold in these regions are generally locked to those channels (ex: in the US, 2.4Ghz channels 12 - 13 are low power only, and channel 14 is military and EMS use only)\n- Different wireless devices support different frequencies, standards, speeds, and features; using these to your advantage is key to getting best performance\n\n## Routing / Switching / Firewalling / General Info\n\n- Ideally the headset and streamer should exist on the same logical (layer 2) network and subnet - this allows for no routing overhead, and the correct function of device discovery via [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS)\n- Twisted pair (normal copper ethernet cables) should never be run alongside power cables - this can cause signal noise and result in frame loss and lowered auto-negotiation speeds\n- High quality CAT5E or higher (ideally CAT6A or CAT7) gigabit+ cabling should be used for modern networks\n- In some cases firewall, anti-virus, malware, or EDR (enhanced detection and response) software may interfere with network traffic - Windows Defender and Sophos Endpoint Protection are reported to work without issue\n- Pause frames should be disabled where possible, as these introduce additional latency and buffering\n\n***\n\nSomeone did a few blog-posts on some of the points:\n<https://imaginevr.home.blog/author/imaginevrresearch/>\n\nSome points came from [FingrMastr](https://github.com/FingrMastr)\n\n## Linux\n\n### Encoder requirements\n\nALVR uses FFmpeg for all encoders, so you will need to make sure the encoder of your choice works with FFmpeg.\nAlways consult Log tab in dashboard, it will tell you the reason why an encoder failed to initialize.\n\n### VAAPI (AMD/Intel GPUs)\n\nRequires *libva* and appropriate driver for your GPU. Check codec support with `vainfo`:\n\n```sh\n$ vainfo                                                                                                                                                                       130 ↵ !10090\nTrying display: wayland\nvainfo: VA-API version: 1.16 (libva 2.16.0)\nvainfo: Driver version: Mesa Gallium driver 23.0.0-devel for Radeon RX 7900 XTX (gfx1100, LLVM 16.0.0, DRM 3.49, 6.1.1-zen1-1-zen)\nvainfo: Supported profile and entrypoints\n      VAProfileH264ConstrainedBaseline:    VAEntrypointVLD\n      VAProfileH264ConstrainedBaseline:    VAEntrypointEncSlice\n      VAProfileH264Main               :    VAEntrypointVLD\n      VAProfileH264Main               :    VAEntrypointEncSlice\n      VAProfileH264High               :    VAEntrypointVLD\n      VAProfileH264High               :    VAEntrypointEncSlice\n      VAProfileHEVCMain               :    VAEntrypointVLD\n      VAProfileHEVCMain               :    VAEntrypointEncSlice\n      VAProfileHEVCMain10             :    VAEntrypointVLD\n      VAProfileHEVCMain10             :    VAEntrypointEncSlice\n      VAProfileJPEGBaseline           :    VAEntrypointVLD\n      VAProfileVP9Profile0            :    VAEntrypointVLD\n      VAProfileVP9Profile2            :    VAEntrypointVLD\n      VAProfileAV1Profile0            :    VAEntrypointVLD\n      VAProfileNone                   :    VAEntrypointVideoProc\n```\n\n*VAProfileH264High, VAProfileHEVCMain, VAProfileHEVCMain10* encoders (VAEntrypointEncSlice) required. If you don't see those\nin your output, your driver install is incorrect or your distribution decided to build *mesa* without non-free codecs.\n\n#### Test ffmpeg commands (VAAPI)\n\n```sh\n# H264\nffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v h264_vaapi vaapi-h264.mp4\n\n# HEVC\nffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v hevc_vaapi vaapi-hevc.mp4\n```\n\n### NVENC (Nvidia)\n\nRequires *libcuda*.\n\n#### Test ffmpeg commands (Nvidia)\n\n```sh\n# H264\nffmpeg -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v h264_nvenc nvenc-h264.mp4\n\n# HEVC\nffmpeg -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v hevc_nvenc nvenc-hevc.mp4\n```\n\n### Software (any GPUs)\n\nSoftware encoder is mainly used as a fallback and as such should work on all GPUs without any requirements.\nOnly H264 encoding is currently supported.\n"
  },
  {
    "path": "wiki/Installation-guide.md",
    "content": "## Launcher (BETA)\n\nLauncher will allow you to manage old, current and new installations of ALVR streamer and allow to automatically install and upgrade to specific ALVR app version on headset\n\n### Installation\n\n* Download `alvr_launcher_windows.zip` (on Windows) or `alvr_launcher_linux.tar.gz` (on Linux) from the release [download page](https://github.com/alvr-org/ALVR/releases/latest) and extract into a path that contains only ASCII characters (english only) and has edit permissions without administrator or root rights.\n* Run `ALVR Launcher.exe` (on Windows) or `alvr_launcher_linux/ALVR Launcher` (on Linux)\n* Press `Add version` button\n* For default installation keep channel and version as is and press `Install`\n* Wait until it finishes downloading, installing (depends on your connection)\n* To install ALVR app on headset, use button `Install APK`\n* In the list, to open streamer app (PC) press `Launch`. You will be greeted with a setup wizard. Follow the setup to set the firewall rules and other settings.\n\n### Usage\n\n* Before launching SteamVR through ALVR, please install it. First time launch will result in steamvr being blank and alvr will not work - close it and start again. It will have registered driver and should work.\n* Launch ALVR app on your headset. While the headset screen is on, click `Trust` next to the device entry (in the ALVR streamer app on PC, in the `Devices` tab) to start streaming.\n* You can change settings on the PC in the `Settings` tab. Most of the settings require to restart SteamVR to be applied. Use the apposite button on the bottom right corner.\n\nFor any problem visit the [Troubleshooting page](https://github.com/alvr-org/ALVR/wiki/Troubleshooting).\n\n## Microphone Setup on Windows\n\nTo use your microphone in ALVR on Windows you need to install **Virtual Audio Cable** (or equivalent software). However if Virtual Audio Cable is already installed but not working with ALVR **or if you encounter any issues**, it's worth following these steps to reinstall and configure it properly.\n\n### **1. Install or Reinstall Virtual Audio Cable**\n1. **Download** the latest Lite version of [Virtual Audio Cable](https://software.muzychenko.net/freeware/vac470lite.zip).\n2. **Extract** the ZIP archive.\n3. Open the extracted folder and run **\"setup64.exe\"** as administrator.\n\n### **2. Configure Windows Sound Settings**\n1. **Open** Windows Sound Settings (`Win + I` → \"Sound\").\n2. **Under Output Devices**:\n   - **Do not set any \"Virtual Audio Cable\" as the default output**, or you’ll hear yourself. Select your headphone or whatever you're using.\n\n### **3. Configure ALVR**\n1. **Open ALVR** and go to **Settings**.\n2. Set **Headset Speaker** → **System Default**.\n3. Set **Headset Microphone** → **Automatic** or **Virtual Audio Cable**.\n\n## Advanced installation\n\n### Installing app using Sidequest\n\n* Install SideQuest on your PC and enable developer mode on the headset. You can follow [this guide](https://sidequestvr.com/setup-howto).\n* Connect your headset to Sidequest. If you have Quest, Pico, and other compatible device download the ALVR app [here](https://sidequestvr.com/app/9)\n\n### Manually installing ALVR streamer\n\nThere is also a portable version for the PC that requires more manual steps to make it work.\n\n#### Windows\n\n* Download `alvr_streamer_windows.zip` from the latest release [download page](https://github.com/alvr-org/ALVR/releases/latest).\n* Unzip into a path that contains only ASCII characters and has edit permissions without administrator rights.\n* Run\n\n#### Linux\n\n* Download `alvr_streamer_linux.tar.gz` from the release [download page](https://github.com/alvr-org/ALVR/releases/latest), extract it.\n* Run `bin/alvr_dashboard`\n\n#### Nightly\n\nIf you want to get new features early or you want to help with testing you can install a nightly version.\n\nDownload the latest nightly streamer [here](https://github.com/alvr-org/ALVR-nightly/releases/latest).\n\nSince nightly releases can be unstable, always use matching versions for PC and headset. They are updated once a day.\n\n### Arch Linux (AUR)\n\n* Install `rustup` and a rust toolchain, if you don't have it: <https://wiki.archlinux.org/title/Rust#Arch_Linux_package>.\n* Install [alvr](https://aur.archlinux.org/packages/alvr)<sup>AUR</sup> (stable, amdgpu), or [alvr-nvidia](https://aur.archlinux.org/packages/alvr-nvidia)<sup>AUR</sup> (stable, nvidia), or [alvr-git](https://aur.archlinux.org/packages/alvr-git)<sup>AUR</sup>(nightly, unstable)\n* Install SteamVR, **launch it once** then close it.\n* Run `alvr_dashboard` or ALVR from your DE's application launcher.\n\n### Flatpak\n\nFor Flatpak users, refer to the instructions [here](https://github.com/alvr-org/ALVR/wiki/Installing-ALVR-and-using-SteamVR-on-Linux-through-Flatpak)\n\n## Advanced usage\n\n### Use ALVR together with third-party drivers\n\nBy default ALVR disables other SteamVR drivers before starting. Among these drivers there is [Driver4VR](https://www.driver4vr.com/) for full body tracking. ALVR disables these drivers to maximize compatibility with every PC setup. You can disable this behavior by manually registering the ALVR driver. Go to the `installation` tab and click on `Register ALVR driver`. The next time you launch ALVR you will be able to use the other drivers concurrently.\n\n### Launch ALVR together with SteamVR\n\nYou can skip the ALVR Dashboard and open ALVR automatically together with SteamVR.\n\n**Note:** You can only do that while SteamVR is not already running. Otherwise driver might be unregistered on shutdown.\n\nOpen ALVR, go to the `Installation` tab and click on `Register ALVR driver`.\n\n### Connect headset to PC via a USB Cable\n\nCheck out the guide [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)).\n"
  },
  {
    "path": "wiki/Installing-ALVR-and-using-SteamVR-on-Linux-through-Flatpak.md",
    "content": "## Disclaimer\n\n1. Flatpak suppport is experimental - but it does seem to work. Some manual steps are needed!\n\n2. Native Linux SteamVR utility applications such as OpenVRAS are not supported nor tested, use at your own risk\n\n3. Firewall configuration does not work\n\n4. Any scripts that affect the host will run within the sandbox\n\n5. Sometimes, a new instance of Steam will launch when launching the dashboard. To fix this, close both ALVR and Steam then launch Steam. As soon as Steam opens to the storefront, launch the ALVR dashboard.\n\n6. User must setup xdg shortcut themselves - see below. Without an xdg entry the launcher has to be run from terminal.\n\n```sh\nflatpak run --command=alvr_launcher com.valvesoftware.Steam\n```\n\n8. This does seem to work with both steam flatpak and native steam - it calls via xdg-open. But it is not recommended to have both versions of steam installed as this creates ambiguity.\n\n## Dependencies\n\nFirst, flatpak must be installed from your distro's repositories. Refer to [this page](https://flatpak.org/setup/) to find the instructions for your distro.\n\n## Setup\n\nFlatpak steam needs extra step compared to native steam. After installing SteamVR, run the following command:\n\n```sh\nsudo setcap CAP_SYS_NICE+eip ~/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/SteamVR/bin/linux64/vrcompositor-launcher\n```\n\nThis command is normally run by SteamVR, but due to the lack of sudo access within the Flatpak sandbox, it must be run outside of the Flatpak sandbox. After running the command, run SteamVR once then close it.\n\n### steamvr custom launch options\nAt the time of writing steamvr needs special options to work on linux - this applies to both the flatpak version and native. The flatpak uses a slightly different path is the only difference. Paths below assume steam has been installed in the \"normal\" location - if your steam is in a different place then adjust paths as appropriate.\n\nFor flatpak steam\n```\n~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%\n```\n\nFor native steam\n```\n~/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%\n```\n\n### failed to create pipewire errors\nThis is most likely caused by missing pipewire socket which is required for audio to work. \n\nIt's also possible to have this error even after applying fixes bellow. Which might suggest issues on host system (mainly - missing pipewire, a requirement for audio to work)\n\nTo fix the most likely cause, please follow guidance bellow\n\n#### Using terminal\nOpen terminal and paste commands from bellow depending on type of your Flatpak Steam installation:\n\nOn user installation of steam flatpak: `flatpak override --user --filesystem=\"xdg-run/pipewire-0\" com.valvesoftware.Steam`\n\nOn system installation of steam flatpak: `flatpak override --filesystem=\"xdg-run/pipewire-0\" com.valvesoftware.Steam`\n\nPress enter to apply it and restart SteamVR (and close Flatpak Steam) from ALVR to apply the fix.\n\n#### Using Flatseal\nGUI app for managing Flatpak application permissions: [Flathub](https://flathub.org/apps/com.github.tchx84.Flatseal)\n1. Find Steam in app's list on the left, left click on it\n2. Inside Steam app on the right -> scroll down to Filesystem section\n3. In Filesystem section -> find Other files sub-section\n4. In Other files subsection -> add new entry with content: `xdg-run/pipewire-0`\nYou should also see some other permissions there `xdg-music:ro`, `xdg-pictures:ro` and maybe more for other integration (like discord).\n\n## Install\n\nDownload `com.valvesoftware.Steam.Utility.alvr.flatpak` file from one of the latest [nightly](https://github.com/alvr-org/ALVR-nightly/releases) that contains flatpak bundle and install like so:\n\n```sh\nflatpak install --user com.valvesoftware.Steam.Utility.alvr.flatpak\n```\n\n## Notes\n\n### Running the launcher\n\nIt's recommended that user sets up an xdg shortcut - but the launcher can also be run from terminal via the following command:\n```sh\nflatpak run --command=alvr_launcher com.valvesoftware.Steam\n```\n\nAn icon and desktop file named `com.valvesoftware.Steam.Utility.alvr.desktop` is supplied within the `alvr/xtask/flatpak` directory. Move this to where other desktop files are located on your system in order to run the dashboard without the terminal.\n\n```sh\n# systemwide shortcut\n# sudo cp com.valvesoftware.Steam.Utility.alvr.desktop /var/lib/flatpak/exports/share/applications/ \n\n# users local folder\ncp com.valvesoftware.Steam.Utility.alvr.desktop $HOME/.local/share/flatpak/exports/share/applications/\n\n# install icon as well\nxdg-icon-resource install --size 256 alvr_icon.png application-alvr-launcher\n```\n\nThe shortcut may not appear until desktop session is refreshed (e.g. log off then back on)\n\n### EXPERIMENTAL - APK install via flatpak launcher \nFirst need to setup adb on host, and enable usb debugging on device. Verify that devices shows up when you run \"adb devices\" and is authorised.\nScript assumes that user has AndroidStudio installed with keys in default location ($HOME/.android/adbkey.pub) - change if necessary\nConvenience script is provided in git: run_with_adb_keys.sh\nIt's likely one the keys are exposed to the flatpak in the default location it will work without needing more changes.\n```\nexport ADB_VENDOR_KEYS=~/.android/adbkey.pub\nflatpak override --user --filesystem=~/.android com.valvesoftware.Steam.Utility.alvr\nflatpak run --env=ADB_VENDOR_KEYS=$ADB_VENDOR_KEYS --command=alvr_launcher com.valvesoftware.Steam\n```\n\n### Wayland variable causes steamvr error:\nMake sure the QT_QPA_PLATFORM var allows x11 option - or steamvr freaks out. Launch from terminal to see errors.\nThis can be a problem if you have modified this variable globally to force usage of wayland for some program like GameScope. \nYou can fix this by setting the variable passed to steamvr\nExample custom launch options for steamvr - including both QT_QPA_PLATFORM and vrmonitor fixes:\n\n```\nQT_QPA_PLATFORM=xcb ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%\n```\n\n### Hybrid graphics \nIf using desktop it's recommended to disable igpu - makes things simpler. \nIf using laptop then must pass extra options to ensure dgpu is used. These options are in addition to the others already mentioned.\n\n#### Amd/Intel integrated gpu + Amd/Intel discrete gpu\nPut DRI_PRIME=1 %command% into SteamVR's commandline options and in those of all VR games you intend to play with ALVR.\n```\nDRI_PRIME=1 QT_QPA_PLATFORM=xcb ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%\n```\n\n#### Amd/Intel integrated gpu + Nvidia discrete gpu\nPut __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia %command% into SteamVR's commandline options and in those of all VR games you intend to play with ALVR. Again - in addition to other options.\n```\n__NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia QT_QPA_PLATFORM=xcb ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%\n```\n\n### Other Applications\n\nThe support for other applications that are not launched via Steam is non-existent due to the Flatpak sandbox.\n\nVarious SteamVR utilities such as [WlxOverlay](https://github.com/galister/WlxOverlay) and [OpenVR-AdvancedSettings](https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings) cannot run within the Flatpak sandbox due to their usage of AppImage. However, unpacking the supplied AppImage or building the utilities from source and running their binaries from within the sandbox similiarly to `alvr_dashboard` could work, but there is no guarantee that they will work properly.\n\n(at time of writing it does work properly)\nDownload wlx-overlay-s appimage. \nMake it executable (chmod +x Wlx-Overlay-xxx.Appimage). \nExtract it (./Wlx-Overlay-xxx.Appimage --app-image-extract)\nUse flatseal or terminal to expose a folder to the steam flatpak (e.g. ~/test, should be in same section as the pipewire fix from above)\nCopy the extracted files into the exposed folder.\nTest it from terminal: flatpak run --command=bash com.valvesoftware.Steam (cd ~/test/squasroot-fs && ./Apprun)\nTo make a desktop shortcut, use a command like flatpak run --command=~/test/squashroot-fs/Apprun com.valvesoftware.Steam\n\n\n\nSome applications such as [Godot](https://godotengine.org) support OpenXR. However, unless they are launched within the Steam Flatpak sandbox, they will not work with the Steam Flatpak. See [here](https://github.com/flathub/com.valvesoftware.Steam/issues/1010) for more details.\n"
  },
  {
    "path": "wiki/Linux-Troubleshooting.md",
    "content": "## (! Mandatory, apply fix if not applied yet !) Black screen even when SteamVR shows movement, Dashboard not detecting launched ALVR/SteamVR\n\nThe Steam runtimes SteamVR runs in break the ALVR driver loaded by SteamVR. This causes the screen to stay black on the headset, an error to be reported that the PipeWire device is missing, or can even result in SteamVR crashing.\n\n### Fix\n\nAdd `~/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%` to the launch options of SteamVR (SteamVR -> Manage/Right Click -> Properties -> General -> Launch Options).\n\nThis path might differ based on your Steam installation; in that case, SteamVR will not start at all. If this is the case, you can figure out the actual path by going to Steam Settings -> Storage.\nThen pick the storage location with the star emoji (⭐) and take the path directly above the usage statistics. Prepend this path to `steamapps/common/SteamVR/bin/vrmonitor.sh`. Finally, put the resulting path into the SteamVR launch options instead of the original one.\n\n### Hyprland/Sway/wlroots Qt fix\n\nIf you're on Hyprland, Sway, or another wlroots-based Wayland compositor, you might have to prepend `QT_QPA_PLATFORM=xcb` to your launch options.\n\nRelated issue:\n[[BUG] No SteamVR UI on wlroots-based wayland compositors (sway, hyprland, ...) with workaround](https://github.com/ValveSoftware/SteamVR-for-Linux/issues/637).\n\n## The ALVR driver doesn't get detected by SteamVR (even after vrmonitor fix)\n\nThis can be related to the use of an AUR package on Arch.\n\n### Fix\n\nIf you're using an Nvidia-based system, ensure you are using a package which supports this (e.g. `alvr-nvidia`). Also try using a launcher (e.g. `alvr-launcher-bin`) or portable .tar.gz release from the Releases page.\n\n## Artifacting, no SteamVR Overlay or graphical glitches in streaming view\n\nThis could be related to the AMD AMDVLK or AMDGPU-PRO drivers being present on your system. AMDVLK overrides other Vulkan drivers and can cause SteamVR to break. Also to note is that AMD has discontinued the AMDVLK driver, so limited support should be expected if using it.\n\n### Fix\n\nFirst check if AMDVLK or AMDGPU-PRO are installed by seeing if `ls /usr/share/vulkan/icd.d/ | grep -e amd_icd -e amd_pro` shows anything. If so, uninstall AMDVLK and/or the AMDGPU-PRO drivers from your system to use the RADV driver instead. (This method may not catch all installations due to distro variations.)\n\nOn Arch, first install `vulkan-radeon`, then uninstall other drivers.\n\n## \"Failed to create VAAPI encoder\" error\n\nGameplay stream appears blocky or crashes, then an error window appears on your desktop saying:\n> Failed to create VAAPI encoder: Cannot open video encoder codec: Function not implemented. Please make sure you have installed VAAPI runtime.\n\n### Fix\n\nFor Fedora:\n * Switch from `mesa-va-drivers` to `mesa-va-drivers-freeworld`. [Guide on how to do so](https://fostips.com/hardware-acceleration-video-fedora/) or [the RPM docs](https://rpmfusion.org/Howto/Multimedia).\n\nFor Arch (don't use VAAPI for Nvidia):\n * Follow the steps on [this](https://wiki.archlinux.org/title/Hardware_video_acceleration#Installation) page, then reboot your machine.\n\nFor other distros (e.g. Manjaro):\n * Install the nonfree version of the Mesa/VAAPI drivers that include the proprietary codecs needed for H264/HEVC encoding.\n\n## Nvidia driver version requirements\n\nALVR requires the Nvidia driver version 535 or newer and CUDA version 12.1 or newer. If your configuration doesn't meet these requirements, SteamVR or the encoder might not work.\n\n### Fix\n\nInstall the minimum or newer versions of the Nvidia and CUDA drivers.\n\nIf errors saying CUDA was not detected persist, try using the latest ALVR nightly release.\n\n## Using ALVR with only integrated graphics\n\nBeware that using **only** integrated graphics for running ALVR is highly inadvisable, as in most cases it will lead to very poor performance (even on more powerful devices like the Steam Deck, it's still very slow). Don't expect things to work perfectly in this case either, as some older integrated graphics may simply not have the best Vulkan support and might fail to work at all. \n\n## Hybrid graphics advice\n\n### General advice\n\nIf you're using a PC and can disable your integrated GPU from the BIOS/UEFI, it's highly advised to do so to avoid multiple problems with handling hybrid graphics. If you're using a laptop and it doesn't allow disabling integrated graphics (in most cases), you'll have to resort to the methods below.\n\n### AMD/Intel integrated GPU + AMD/Intel discrete GPU\n\nPrepend `DRI_PRIME=1` to the launch options of SteamVR and of all VR games you intend to play with ALVR.\n\n### AMD/Intel integrated GPU + Nvidia discrete GPU\n\nPrepend `__NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia` to the launch options of SteamVR and of all VR games you intend to play with ALVR.\n\nIf this results in errors such as `error in encoder thread: Failed to initialize vulkan frame context: Invalid argument`, then try adding `VK_DRIVER_FILES=/usr/share/vulkan/icd.d/nvidia_icd.json` to the above launch options.\n\n- Go to `/usr/share/vulkan/icd.d` and ensure `nvidia_icd.json` exists. It may also be under the name `nvidia_icd.x86_64.json`, in which case you should adjust `VK_DRIVER_FILES` accordingly.\n- On older distributions, `VK_DRIVER_FILES` may not be available, in which case you should use the deprecated but equivalent `VK_ICD_FILENAMES`.\n\n### SteamVR Dashboard not rendering in VR on Nvidia discrete GPU\nYou may need to run the entire Steam client itself via PRIME render offload. First, ensure the Steam client is completely closed. If Steam is already open, you can do so by clicking the Steam dropdown in the top left and choosing \"Exit\". Then from a terminal run: `__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia steam-runtime`.\n\n## Wayland\n\nWhen using older Gnome versions (older than 47) under Wayland, issues may be caused by DRM leasing not being available.\n\n### Fix\n\nPrepend `WAYLAND_DISPLAY=''` to the SteamVR launch options to force XWayland on SteamVR.\n\n## The view shakes when using SlimeVR\n\nThis might be fixed in future updates of ALVR.\n\n### Fix\n\nStart the SlimeVR Server only after you have connected and gotten an image to ALVR at least once.\n\n## Error 109\n\nSteamVR displays 109 or other errors.\n\n### Fix\n\nStart Steam first before starting SteamVR through ALVR. If SteamVR is already started, restart it.\n\n## No audio or microphone\n\nAudio and/or microphone are enabled in presets, but you still can't hear audio or no one can hear you.\n\n### Fix\n\nMake sure you select `ALVR Audio` and/or `ALVR Microphone` in your device list as default **after** connecting the headset. As soon as the headset is disconnected, the devices will be removed. If you set them as default, they will be automatically selected whenever they show up, and you won't need to do it manually ever again. If you don't appear to have the audio devices, or have PipeWire errors in your logs, ensure you have `pipewire` version 0.3.49 or newer installed by using the command `pipewire --version`. For older Debian (version 11 or older) or Ubuntu-based (version 22.04 or older) distributions, you can check the [pipewire-upstream](https://github.com/pipewire-debian/pipewire-debian) page for instructions on installing newer PipeWire versions.\n\n## Low AMDGPU performance and shutters\n\nThis might be caused by [[PERF] Subpar GPU performance due to wrong power profile mode · Issue #469 · ValveSoftware/SteamVR-for-Linux · GitHub](https://github.com/ValveSoftware/SteamVR-for-Linux/issues/469).\n\n### Fix\n\nUsing CoreCtrl is highly advised (install it using your distribution's package management system). In its settings, set your GPU to the VR profile, as well as CPU to the performance profile (if it's an old Ryzen CPU).\n\n## OVR Advanced Settings\n\nOVR Advanced Settings is incompatible with ALVR, and will produce a ladder-like latency graph with very bad shifting vision. Disable the OVR Advanced Settings driver, and don't use it with ALVR.\n\n## Bindings not working/high CPU usage due to bindings UI\n\nSteamVR can't properly update bindings, open menus, and/or eats too much CPU.\n\nThis issue is caused by SteamVR's webserver spamming requests that stall the Chromium UI and cause it to use a lot of CPU.\n\n### Fix\n\nApply the following patch: `https://github.com/alvr-org/ALVR-Distrobox-Linux-Guide/blob/main/patch_bindings_spam.sh`.\n\n\nOne-liner assuming the default Steam path for Arch, Fedora: `curl -s https://raw.githubusercontent.com/alvr-org/ALVR-Distrobox-Linux-Guide/main/patch_bindings_spam.sh | sh -s ~/.steam/steam/steamapps/common/SteamVR`.\n"
  },
  {
    "path": "wiki/My-game-is-not-working-properly!-Help!.md",
    "content": "While most games do work without any problems, some do only work partially or not at all. This includes\n\n- headset not found\n- warped image\n- controller not tracking\n- buttons not working\n- ...\n\nMost of the time its the overly specific initialization of the game towards a specific headset that breaks the game.\nFor example, Vivecraft broke because ALVR reported the headset manufacturer as \"Oculus driver 1.38.0\" and not as \"Oculus\".\nIn general, this is a rather bad practice as all relevant data can be accessed trough SteamVR and the game should not make assumptions based on the manufacturer of the hmd. There are many different fields that a game could require to run.\n\nNonetheless, we want to play and support those games.\nProblem is, that we don't own all games. This is a Open Source without any funding. We can not buy any games just to fix a bug. In the case of Vivecraft, one user (thanks @Avencore) was generous to gift us a copy and the bug could be fixed.\nThere are no guaranties! Neither on the time it will take nor if the bug will ever be fixed! Please contact us before buying anything.\n"
  },
  {
    "path": "wiki/Other-resources.md",
    "content": "* Hand tracking OSC for VRChat with ALVR support: https://github.com/A3yuu/FingerTruckerOSC\n* ALVR in the browser using WebXR and WebCodecs: https://github.com/rinsuki-lab/ALVR/tree/research/alvr-web\n"
  },
  {
    "path": "wiki/Real-time-video-upscaling-experiments.md",
    "content": "## Why?\n\nThe Quest can display a resolution close to 4k. Rendering a game, encoding and decoding these kinds of resolutions is very taxing on both the PC and the Quest. So usually a lower resolution image displayed on the Quest.\n\nIdeally the output of such an upscaled image should match the screens pixels 1:1. But because of the Asynchronous Timewarp step this is not possible in the Quest. OVR only accepts undistorted frames.\n\nCurrently ALVR does no upscaling prior to the image being mapped to an OpenGL texture. This texture gets interpolated to match the screen pixels by OVR. For this process video resolutions above 100% it use bilinear interpolation and for resolutions below 100% it uses nearest neighbor.\n\nThere's a lot of good info on this topic in this issue: <https://github.com/alvr-org/ALVR/issues/39>\n\n## Lanczos resampling\n\nThis traditional upscaling method seems like a good step up from basic bilinear interpolation and is relatively light on GPU resources.\n\nA GPL 2 implementation of a Lanczos shader can be found here: <https://github.com/obsproject/obs-studio/blob/6943d9a973aa3dc935b39f99d06f4540ea79da61/libobs/data/lanczos_scale.effect>\n\n## Neural net image super resolution\n\nI did some basic investigations on the feasibility of using AI upscalers to get even better results than traditional signal processing methods.\n\n### Hardware acceleration on the XR2\n\nThere seem to be 3 paths towards getting fast NNs running on the Quest's SoC.\nThere is the [Qualcomm Neural Processing SDK](https://developer.qualcomm.com/software/qualcomm-neural-processing-sdk/tools), which automatically detects what the capabilities of the system are and picks the right hardware to run the NN on (GPU, DSP, AI accelerator).\n\nThe [TensorFlow Lite NNAPI delegate](https://www.tensorflow.org/lite/performance/nnapi) relies on hardware and driver support for the Android Neural Networks API.\n\nThen there is also the [TensorFlow Lite Hexagon delegate](https://www.tensorflow.org/lite/performance/hexagon_delegate) which specifically targets the Snapdragon DSP.\n\nI only tested an example image super-resolution app from the [tensorflow respository](https://github.com/tensorflow/examples/tree/master/lite/examples/super_resolution) in CPU and generic GPU (OpenCL) accelerated modes. Even upscaling tiny 50x50 images took around 500ms with this. Even though better hardware acceleration could improve this I do not expect 100x improvements. The only hope for NN super-resolution to be viable would be to find a significantly faster neural net, which leads us into the next topic.\n\n### Existing neural nets\n\nA well established real-time upscaler is [Anime4K](https://github.com/bloc97/Anime4K/). It states that it can achieve 1080p to 2160p upscaling in 3ms on a Vega64 GPU. A [rough estimate](https://uploadvr.com/oculus-quest-2-benchmarks/) puts the Quest 2 at a 10x performance disadvantage compared to such high end desktop GPUs. It doesn't seem entirely impossible to get this to work with some optimizations and lowering of the upscaling quality, but there is more bad news. Anime4K has a rather bad Peak signal-to-noise ratio (PSNR). It can get away with this because the stylized look anime is quite forgiving in being heavily filtered.\n\nFor an upscaler that has a better PSNR there are many options but very few that can run real-time. The smallest neural net that I could find is [SubPixel-BackProjection](https://github.com/supratikbanerjee/SubPixel-BackProjection_SuperResolution). Tt gets nice results but in my testing took 3 seconds to upscale from 720p to 1080p with CUDA acceleration. Way out of the ballpark for XR2 the chip.\n\nSo in conclusion, it does not seem like there is enough performance to squeeze out of the XR2 to do do real-time NN upscaling at such high resolutions. We will more likely get better results out of classical techniques.\n"
  },
  {
    "path": "wiki/Roadmap.md",
    "content": "This post will continue to evolve during ALVR development.\n\n## Long-term goal\n\nCreate a universal bridge between XR devices.\n\n## What is coming next\n\n* Compositor rewrite\n  * **Purpose**: add Linux support for FFR and color correction, preparation for sliced encoding\n  * **Status**: FFE and color correction done on all platforms\n* Encoder rewrite\n  * **Purpose**: support any OS and hardware with a single API, using [Vulkan video extensions](https://www.khronos.org/blog/an-introduction-to-vulkan-video)\n  * **Status**: blocked by adoption by AMD and Intel, landing of the feature on stable Nvidia drivers\n* Monado Driver\n  * **Purpose**: support other runtimes with the streamer\n  * **Status**: blocked on refactors\n\nDue to the low development capacity, no ETA can be provided. New releases will not have a regular cadence and they do not have scheduled features.\n"
  },
  {
    "path": "wiki/Settings-tutorial.md",
    "content": "Applicable to ALVR v20.\n\nThis tutorial will help you find optimal settings for your hardware and network\nas well as give you some pointers to troubleshoot common configuration issues.\n\n## Prerequisites\n\n* You have installed the ALVR streamer on your PC, and the ALVR app on your HMD.\n* You can launch up to the SteamVR void (or up to the SteamVR home) and are able to launch games.\n\n## Step 1: choose resolution, refresh rate, codec\n\nTo get a sharp image, you need combination of high resolution, enough sharpening, good bitrate with chosen codec.\nFor example, on good wireless router you can use medium resolution preset (default) with 1.0 sharpening (higher than default) with H.264 set at constant 400-500 mbps, or hevc at costant 100-150 mbps. In wired case, you can go all the way to 800-1000 mbps constant bitrate on H.264.\n\nNext, choose a refresh rate. Obviously higher is better, but on weaker/older hardware it's often preferable to use a lower setting that gives consistent results. For the Quest 2, 120 Hz has to be enabled in its settings.\n\nA few notes on codec choices:\n\n* AV1 works only on latest gen gpus (Nvidia RTX 4xxx and AMD Radeon RX 7xxx) and on Quest 3 only.\n* HEVC/H.265 is usually best for bitrate constrained scenarious.\n* AVC/H.264 (with CAVLC) may save a few milliseconds of decode latency, but needs a much higher bitrate to reach similar image quality.\n* Software encoding (x264) can give good results on a beefy high core-count CPU and a very high bitrate. Will require playing with a USB3 cable. The only choice if you don't have a hardware encoder (eg, RX6500).\n\n## Step 2: tweak encoder settings\n\nEnable foveated encoding. Go to the SteamVR void and look closely at the framerate graph under the latency graph in the statistics tab.\n\n* If the streamer FPS matches the refresh rate you chose in step 1, you can reduce the foveation settings (by increasing the center width/height, or reducing the strength).\n* If the streamer FPS is lower than the refresh rate you chose in step 1, increase the foveation settings (by decreasing the center width/height, or increasing the strength).\n\nRepeat until you are at the maximum of what your encoder can do.\n\n## Step 3: tweak bitrate\n\nSlowly increase bitrate until one of two things happen:\n\n* The image freezes for half a second or more periodically (on TCP) or you see a glitched image (on UDP): you have gone beyond what your wireless AP is capable of. Lower the bitrate, or consider using a cable.\n* The controllers stop moving, the image flips upside down, and/or becomes just a solid blinking light: the HMD's decoder is unable to keep up. Lower the bitrate.\n\n## Step 4: tweak frame buffering\n\nIf you notice micro-stuttering on the headset, especially in busy scenes with fast motion, slowly increase maxBufferingFrames until the playback is smooth.\n\nKeep in mind that increasing maxBufferingFrames will linearly increase latency;\nif the value that gives a smooth playback results in too high of a latency for\nyour use case, try a different codec, a lower bitrate and/or stronger foveation\nsettings.\n\nBy that point, your latency graph and your playback should be smooth and consistent. Enjoy!\n\n![optimal latency graph](images/latency-graphs/optimal.png)\n\n## Still not satisfied with image quality?\n\n* Tweak the color correction sliders, eg slightly increasing sharpening.\n* If using AMF, enable the pre-processor.\n* Use the quality encoder preset.\n* Try a lower refresh rate and start again from step 2.\n* Try a different codec and start again from step 2.\n* Try increasing foveation settings (allowing the encoder to use more bits for the center of the image).\n\nSee also the\n[Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Troubleshooting#common-performance-related-problems)\npage for more help.\n"
  },
  {
    "path": "wiki/Troubleshooting.md",
    "content": "## If you're looking for Linux troubleshooting, please check [here](https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting) first, and only then this page.\n\nFor ALVR 20.0.0 and later\n===\n\nFirst off, please make sure to carefully read the [Installation guide](https://github.com/alvr-org/ALVR/wiki/Installation-guide) and [Usage](https://github.com/alvr-org/ALVR/wiki/Usage) pages.\n\nThe first thing to try is to delete the file `session.json` located in the ALVR installation folder on the PC. This resets everything to default. If it doesn't work, try reinstalling ALVR.\n\nKeep in mind that sometimes a restart of ALVR/SteamVR/PC/Headset will be enough to solve some problems.\n\nHaving trouble getting ALVR to work?\n---\n\n[I'm having trouble starting ALVR.](#trouble-starting-alvr)\n\n[ALVR starts fine, but says X error.](#alvr-starts-fine-but)\n\n[ALVR starts fine and doesn't show any error, but it doesn't see (or connect to) my headset.](#alvr-cant-see-my-headset)\n\nIf you need more help, come to our [Discord](https://discord.gg/KbKk3UM) and ask in the #help channel. When asking for help, please describe the issue, if you're getting an error message, copy it, and tell us what you already tried to fix it.\n\nTrouble starting ALVR\n===\n\nALVR needs a working graphics driver to be installed in order to work.\n\n**On linux**, you also need to make sure you have either `vaapi` on AMD or `cuda` on NVIDIA for hardware encoders to work.\n\nALVR starts launching, but gets stuck on \"ALVR is not responding...\"\n===\n\nWith ALVR versions >= 20.0, some antivirus software can prevent ALVR from launching SteamVR. Try disabling any antivirus other than Windows Defender (McAfee, Norton, etc.), reboot, then try again. If the issue persists, make sure you don't have an instance of ALVR or SteamVR running in the background (check in Task Manager). If you continue having issues, hop in the [ALVR Discord server](https://discord.gg/KbKk3UM), and we'll do our best to help you get it sorted out.\n\nALVR starts fine, but\n===\n\nThis section has some advice for when ALVR shows an error (or sometimes warning) pop-up. This could be either a yellow pop-up in the setup window (`ALVR Dashboard.exe`) or a separate pop-up when you connect with a headset.\n\n[WARN] clientFoundInvalid\n---\n\nIf you get a warning pop-up inside the `ALVR Dashboard.exe` window saying `clientFoundInvalid`, make sure the version of ALVR you installed on your headset is compatible with the version you're trying to run on your PC.\n\nThe latest release can be found [here](https://github.com/alvr-org/ALVR/releases/latest) and contains both the `alvr_client.apk` file for your headset and the `alvr_streamer_windows.zip` archive with the application for your PC.\n\nThe version of ALVR available on the SideQuest store is compatible with the latest release on GitHub (the previous link). Keep in mind that the version on SideQuest might take us a while to update after a new version is released on GitHub.\n\nFailed to initialize CEncoder\n---\n\nALVR currently needs a recent AMD, Nvidia or Intel GPU to run, since it utilizes hardware video encoding (see [requirements](https://github.com/alvr-org/ALVR#requirements)). If you get an error saying something like\n\n```\nFailed to initialize CEncoder. All VideoEncoder are not available. VCE: AMF Error 1. g_AMFFactory.Init(), NVENC: NvEnc NvEncoderD3D11 failed. Code=1 NvEncoder::LoadNvEncApi : NVENC library file is not found. Please ensure NV driver is installed at c:\\src\\alvr\\alvr_server\\nvencoder.cpp:70\n```\n\nand you have up-to-date GPU drivers, then your graphics card isn't supported. If you're using a laptop with a powerful enough discrete GPU, you _might_ be able to get ALVR to work by forcing SteamVR to use it in either Windows settings, or the Nvidia control panel.\n\nIf you have a compatible GPU, you're most likely seeing a different error after either `VCE:`, `VPL:` or `NVENC:` than above. In that case, try using a different video codec in ALVR settings. You can also try lowering your video resolution setting.\n\nFailed to start audio capture\n---\n\n![Failed to start audio capture](images/ALVR-audio-crash.png)\n\nThis error can show up when connecting your headset, when SteamVR gets started. Make sure the audio device you have selected in ALVR settings isn't disabled, it should be the device you usually use for games (speakers/headphones). ALVR does not create its own audio device.\n\nYou can see if you have an \"enable audio enhancements\" option on your sound device in Windows settings and if so, make sure it's disabled.\n\nALVR can't see my headset\n===\n\nHere is some advice for issues that can come up even though you don't see any error popup from ALVR.\n\nALVR on the headset stuck on `Searching for streamer...`\n---\n\nThis issue can have multiple causes. It is likely that the issue is with the PC ALVR application. See below for more specific issues.\n\nALVR device list is empty\n---\n\n![Empty ALVR device list](images/ALVRexe-no-devices.png)\n\nCheck that the PC app and the headset app run on the latest version of ALVR. If your version is v2.3.1 or v2.4.0-alpha5 then you downloaded ALVR from the wrong link. The correct link is <https://github.com/alvr-org/ALVR>.\n\nMake sure ALVR is running both on the PC and on the headset. To be visible in the device list, ALVR on the headset sends broadcast packets which the PC application listens for. These can be blocked by your firewall or possibly your router, if both headset and PC are connected wirelessly, having AP isolation enabled on the router will cause this.\n\nTo fix this, you can try the following:\n\n* Ping the headset to check it's reachable from the PC - you can do this by opening CMD and typing `ping <headset IP>` without \"<>\" (you can find the headset's IP in the top left corner of SideQuest) - if ping fails, check that both PC and headset are connected to the same network\n* You can also try disabling your firewall for testing, but you shouldn't leave it disabled to use ALVR\n* Open ports 9943 and 9944 on your firewall\n* Disable the PMF (Protected Management Frames) setting on your Router\n\nIf pinging works but you still don't see the device on the streamer app, then headset and PC might be on separate subnets. To solve this you can add the device manually.\nIn the Devices tab press `Add device manually`. Fill in the fields with a name for your headset (you can use the name you want), the hostname (you can read it in the welcome screen in your headset when you open the ALVR app), the IP of the headset and then press `Save`.\n\nSteamVR says \"headset not detected\"\n---\n\n![SteamVR headset not detected](images/SteamVR-headset-not-detected.png)\n\nThis message means that the ALVR SteamVR driver isn't loading properly when SteamVR starts.\n\nOn linux double-check if you have software and hardware encoders installed, without them driver won't load.\n\nCheck that SteamVR isn't blocking ALVR (see SteamVR settings, enable advanced settings and check `Startup / Shutdown -> Manage Add-ons`).\n\n![SteamVR add-ons](images/SteamVR-add-ons.png)\n\nIf you're still getting this message (or otherwise not getting a headset icon in the SteamVR window), a SteamVR log (vrserver.txt) will have some information on why the driver isn't loading. You can find it where you installed Steam, in `Steam\\logs\\vrserver.txt`.\n\n### Some lines to look for and tips for them\n\n`Unable to load driver alvr_server because of error VRInitError_Init_FileNotFound(103). Skipping.` - This usually means a library that ALVR needs is missing. Make sure you followed installation instructions carefully, installed the latest Visual C++ Redistributable x64 package and no files are missing where you extracted ALVR (especially in the bin\\win64 directory).\n\n`Skipping duplicate external driver alvr_server` - This line means another ALVR driver is registered. Go to the installation tab in ALVR and remove all drivers.\n\n`Skipping external driver X:\\path\\to\\your\\alvr_streamer_windows because it is not a directory` - This can happen if you put ALVR in a OneDrive (or a similar service) directory or the path to ALVR contains characters not in UTF-8. Try putting ALVR elsewhere, preferably so that the path to ALVR contains only ASCII characters.\n\nIf you have trouble looking through the logs, none of the tips work, or don't apply to you, feel free to ask on our [Discord](https://discord.gg/KbKk3UM) in the #help channel (you may be asked to post the log there).\n\nALVR sees the headset, SteamVR shows headset icon\n---\n\n![SteamVR waiting...](images/SteamVR-waiting.png)\n\nThis is a situation where you have ALVR open on both headset and PC, you can see the headset in the device list and trust it. ALVR then starts SteamVR automatically when you try connecting and SteamVR shows an icon for the headset (and controllers).\n\nFirst make sure that SteamVR (more specifically, vrserver.exe) is allowed incoming connections (UDP, port 9944) in your firewall. You can also try disabling your firewall for testing, but you keep it disabled to use ALVR.\n\nYou can try restarting ALVR on both the headset and the PC. On the headset, when connecting, you should see the view lagging behind when you turn your head (it drops below 1 fps), this means the headset is getting a response from the streamer when connecting and is waiting for the video stream to start. If you get no lag in the headset, response from the PC isn't reaching the headset.\n\n## Common performance-related problems\n\n### Overloaded encoder\n\n![latency graph of overloaded encoder](images/latency-graphs/overloaded-encoder.png)\n\nSymptoms: stuttery playback on the headset, streamer FPS is stable but below the target refresh rate.\n\nSolution: increase foveation settings or decrease refresh rate.\n\n### Overloaded decoder\n\n![latency graph of overloaded decoder](images/latency-graphs/overloaded-decoder.png)\n\nSymptoms: laggy/frozen controllers, erroneous head tracking, image flipped upside-down, blinking solid colour.\n\nSolution: reduce bitrate.\n\n### Overloaded network\n\n![latency graph of overloaded network](images/latency-graphs/overloaded-network.png)\n\nSymptoms: stream freezes, image is glitchy.\n\nSolution: check that HMD is using 5G frequency and that no other device is connected to the 5G band on your AP, reduce bitrate or use a cable.\n\n### Overloaded streamer\n\n![latency graph of overloaded streamer](images/latency-graphs/overloaded-streamer.png)\n\nSymptoms: stuttery playback on the headset, streamer FPS dips or fluctuates below the target refresh rate.\n\nSolution:\n\n* Decrease the graphics settings in the game\n* If possible, use the game's native upscaling solution (FSR/NIS/XeSS/DLSS…)\n* Decrease the target refresh rate in ALVR\n* Decrease render resolution in SteamVR overlay or ALVR video settings. (This will severely degrade image quality.)\n\n### Micro-stuttering\n\n![latency graph of headset stuttering](images/latency-graphs/not-enough-buffering.png)\n\nSymptoms: image is not always smooth especially in high motion or fast scenes.\n\nSolution: increase maxBufferingFrames.\n\n\n### Possible temporary fix for Meta framerate scaling for throttling feature\n\n#### Problem  \nThe current version of ALVR does not support Meta's framerate scaling for throttling feature. This can cause issues where the framerate between the headset and the streamer application does not align, potentially leading to stuttering or throttling. A future update to ALVR is expected to address this issue, but a workaround is available in the meantime.\n\n#### Temporary Fix  \n1. **Reboot Your Headset**  \n   - Start by rebooting your VR headset. This may resolve the issue without further adjustments.\n\n2. **Manually Set the Framerate**  \n   - Use the **SideQuest Desktop application** to manually adjust the framerate of the ALVR Android client on your headset to match the framerate set in the ALVR streamer application.  \n     - Example: If the ALVR streamer is configured to 90Hz, set the headset's refresh rate to 90Hz in SideQuest.\n     - for more information see issue [#2537] (https://github.com/alvr-org/ALVR/issues/2537).\n\nThis adjustment bypasses the framerate scaling for throttling feature, ensuring smoother performance.\n"
  },
  {
    "path": "wiki/_Sidebar.md",
    "content": "#### Start here\n\n* [Installation guide](https://github.com/alvr-org/ALVR/wiki/Installation-guide)\n\n* [Hand tracking controller bindings](https://github.com/alvr-org/ALVR/wiki/Hand-tracking-controller-bindings)\n\n* [Other resources](https://github.com/alvr-org/ALVR/wiki/Other-resources)\n\n***\n\n#### Configuration\n\n* [Settings tutorial](https://github.com/alvr-org/ALVR/wiki/Settings-tutorial)\n\n* [Information and Recommendations](https://github.com/alvr-org/ALVR/wiki/Information-and-Recommendations)\n\n* [ALVR headset and streamer on separate networks](https://github.com/alvr-org/ALVR/wiki/Headset-and-ALVR-streamer-on-separate-networks)\n\n* [Fixed Foveated Rendering (FFR)](https://github.com/alvr-org/ALVR/wiki/Fixed-Foveated-Rendering-(FFR))\n\n* [ALVR wired setup (ALVR over USB)](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB))\n\n***\n\n#### Troubleshooting\n\n* [Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Troubleshooting)\n\n* [Linux Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Linux-Troubleshooting)\n\n* [ALVR Checklist before posting a new Issue](https://github.com/alvr-org/ALVR/wiki/ALVR-Checklist)\n\n* [Controller latency](https://github.com/alvr-org/ALVR/wiki/Controller-latency)\n\n* [My game is not working properly! Help](https://github.com/alvr-org/ALVR/wiki/My-game-is-not-working-properly!-Help!)\n\n* [Hardware Video Encoding Testing](https://github.com/alvr-org/ALVR/wiki/FFmpeg-Hardware-Encoding-Testing)\n\n***\n\n#### Development\n\n* [Roadmap](https://github.com/alvr-org/ALVR/wiki/Roadmap)\n\n* [Building From Source](https://github.com/alvr-org/ALVR/wiki/Building-From-Source)\n\n* [How ALVR works](https://github.com/alvr-org/ALVR/wiki/How-ALVR-works)\n\n* [Real time video upscaling experiments](https://github.com/alvr-org/ALVR/wiki/Real-time-video-upscaling-experiments)\n"
  }
]