Refresh project documentation

This commit is contained in:
Alexa committed 2026-10-20 23:30:26 +02:00
1 parent daffc70dd0
commit ffa48632fc
6 files changed
+268 -108

No files matched your search

+8 -9
View File
@@ -1,6 +1,6 @@
# Contributing to Jellyframe
You can contribute bug fixes, playback improvements, VR controls, tests, or documentation, or report problems you find while using Jellyframe.
Contributions can include bug fixes, playback improvements, VR controls, tests, and documentation. Reports of problems you find while using Jellyframe are also welcome.
Read the [README](README.md) for the project's scope and current limitations, and [Build from source](DEVELOPMENT.md) for setup, build commands, tests, and the code layout.
@@ -10,31 +10,32 @@ Search the repository's issues before opening a new one. Include:
- The Jellyframe build or commit you used.
- Your operating system, headset, and OpenXR runtime, where applicable.
- Your GPU and the OpenGL or Vulkan renderer used for PC playback.
- Your Jellyfin server version.
- Steps to reproduce the problem, what you expected, and what happened.
- Relevant logs or screenshots.
For playback problems, include the container format, video and audio codecs, resolution, and whether the video is 2D or side-by-side 3D, along with whether Jellyfin played the file directly or converted it during playback, if you know.
For playback problems, include the container format, video and audio codecs, resolution, frame rate, HDR format, and stereo or panoramic layout. If you know whether Jellyfin played the file directly or converted it, include that too.
Remove passwords, access tokens, and private server details from logs before posting them. A short sample you have permission to share can help reproduce a playback problem.
## Propose a feature
Before implementing a large change, open an issue explaining what you want to do in the app, what gets in the way, and how the change would help, including any platform or headset requirements.
Before implementing a large change, open an issue. Explain what you want to do in the app, what gets in the way, and how the change would help. Include any platform or headset requirements.
Small fixes and documentation corrections can go straight into a pull request.
## Make a change
Fork the repository and create a branch for your change, keeping each pull request focused on one problem so reviewers can understand and test it.
Fork the repository and create a branch for your change. Keep each pull request focused on one problem so reviewers can understand and test it.
Follow the style of the surrounding code, use clear names, and keep functions small, adding comments only when the code cannot explain the reason for a decision. Write code, comments, and documentation in English.
Follow the style of the surrounding code. Use clear names and keep functions small. Add comments when the code cannot explain the reason for a decision. Write code, comments, and documentation in English.
Update the documentation when you change setup steps, controls, or app behaviour. Keep generated builds, downloaded dependencies, credentials, and local editor settings out of your commits.
## Test your change
Run the checks that cover the code you changed, using the [development guide](DEVELOPMENT.md) to find the core, HTTP, decoder, streaming, and headset tests. Add a regression test when it can reproduce the bug or check the new behaviour.
Run the checks that cover the code you changed. The [development guide](DEVELOPMENT.md) lists the core, HTTP, decoder, streaming, and headset tests. Add a regression test when it can reproduce the bug or check the new behaviour.
Desktop preview with `--xr-mode off` can check layout and library navigation, but cannot verify VR comfort, tracking, controller behaviour, stereo output, or video playback. Decoder and streaming tests check playback without a headset and cannot verify how it looks or behaves in VR.
@@ -52,6 +53,4 @@ Explain the problem and what your change does, link any related issue, and list
## Licence
Submit code contributions under the project's GPL-3.0-only licence, and include the licence, attribution, and source of any new assets or dependencies. See [Licence and attribution](LICENSING.md) and the full [licence text](LICENSE).
Thank you for contributing to Jellyframe.
Submit code contributions under the project's GPL-3.0-only licence. Include the licence, attribution, and source of any new assets or dependencies. See [Licence and attribution](LICENSING.md) and the full [licence text](LICENSE).
+71 -23
View File
@@ -1,6 +1,8 @@
# Build from source
Jellyframe uses Godot and OpenXR, with an Android video plugin and a shared libmpv video plugin for Windows and Linux.
Jellyframe uses Godot and OpenXR. It has an Android video plugin and a shared PC plugin for Windows and Linux. The PC plugin uses libmpv for OpenGL playback and audio. Vulkan video uses FFmpeg and Godot's RenderingDevice.
Check the [README's tested results](README.md#what-has-been-tested) and [current limitations](README.md#current-limitations) before making platform or performance claims. Windows SteamVR playback with Vulkan, HDR, 4K, and 8K has been confirmed on an RTX 4070. Other platforms and renderers need separate testing.
## Android build requirements
@@ -9,13 +11,13 @@ Jellyframe uses Godot and OpenXR, with an Android video plugin and a shared libm
- Android SDK platforms 35 and 36, with build tools 36.1.0.
- Gradle 8.10.2.
The Android plugin compiles against Godot's 4.5.1 Android API and uses the Compatibility renderer because video playback requires an external OpenGL texture. Jellyframe uses a fixed Android codec profile and does not check which codecs the headset can decode, while Windows and Linux use libmpv for PC VR playback.
The Android plugin compiles against Godot's 4.5.1 Android API and uses the Compatibility renderer because video playback requires an external OpenGL texture. Jellyframe uses a fixed Android codec profile and does not check which codecs the headset can decode. The PC Vulkan backend's hardware decoding and HDR conversion do not apply to Android.
## Build the Quest APK
To build a Quest 3 debug APK, open **Actions > Build Android APK > Run workflow**, which builds the native video plugin, exports the APK, and checks its signature. The workflow needs no repository secrets and keeps the APK artifact for 14 days.
Open **Actions > Build Android APK > Run workflow** to build a Quest 3 debug APK. The workflow builds the native video plugin, exports the APK, and checks its signature. It needs no repository secrets and keeps the APK artifact for 14 days.
Each workflow run creates a new debug signing key, so an installed APK signed with a different key must be uninstalled before you can install the new build. Uninstalling removes saved settings and sign-in, and these APKs are test builds rather than store releases.
Each workflow run creates a new debug signing key. If the installed APK has a different key, uninstall it before installing the new build. Uninstalling removes saved settings and sign-in. These APKs are test builds, not store releases.
To build locally:
@@ -42,7 +44,7 @@ On Windows, use PowerShell:
-AndroidSdk 'C:/tools/android-sdk' -JavaHome 'C:/tools/jdk-17'
```
The script creates `dist/jellyframe-android.apk` for installation on your headset and downloads Godot OpenXR Vendors 5.1.0 for Quest passthrough support. It checks the archive's SHA-256 checksum and includes the vendor licences in the APK, keeping the downloaded addon in the ignored `addons/godotopenxrvendors` directory.
The script creates `dist/jellyframe-android.apk` for installation on your headset. It downloads Godot OpenXR Vendors 5.1.0 for Quest passthrough support, checks the archive's SHA-256 checksum, and includes the vendor licences in the APK. The downloaded addon stays in the ignored `addons/godotopenxrvendors` directory.
## Build and test on Windows
@@ -55,9 +57,17 @@ godot --headless --xr-mode off --path . --editor --import --quit
godot --headless --xr-mode off --path . --export-release 'Windows' dist/windows/jellyframe.exe
```
The plugin build script downloads pinned CMake, LLVM-MinGW, godot-cpp, libmpv, and FFmpeg archives into `build/desktop`, verifies their checksums, and generates `addons/jellyframe_mpv` without requiring a system-wide compiler installation. Close running Jellyframe instances before rebuilding; Git ignores the generated plugin, and Android exports exclude it.
The plugin build script downloads pinned CMake, LLVM-MinGW, godot-cpp, libmpv, and FFmpeg archives into `build/desktop` and verifies their checksums. It generates `addons/jellyframe_mpv` without a system-wide compiler installation. Close Jellyframe before rebuilding. Git ignores the generated plugin, and Android exports exclude it.
The Windows workflow builds the plugin, exports the app, verifies the output files, and uploads the build. Tests run separately using the commands below. Build commands disable OpenXR, while the exported app keeps it enabled. The workflow needs no repository secrets, and GitHub keeps its build artifacts for 14 days.
The Windows workflow builds the plugin, exports the app, verifies the output files, and uploads the build. Run the tests separately with the commands below. Build commands disable OpenXR; the exported app keeps it enabled. The workflow needs no repository secrets. GitHub keeps its build artifacts for 14 days.
The Windows preset exports one executable and defaults to OpenGL. With SteamVR and the headset running, launch Vulkan explicitly:
```powershell
.\dist\windows\jellyframe.exe --rendering-method mobile --rendering-driver vulkan
```
The Linux presets select their renderer automatically. The workflow does not create separate Windows renderer packages or launchers.
After building the PC plugin, run its decoder tests without a headset:
@@ -71,6 +81,8 @@ Run GPU playback checks with a graphics context:
```powershell
godot --xr-mode off --rendering-method gl_compatibility --path . --script tests/test_pc_gpu_video.gd
godot --xr-mode off --rendering-method mobile --rendering-driver vulkan --path . --script tests/test_pc_gpu_video.gd
godot --xr-mode off --rendering-method mobile --rendering-driver vulkan --path . --script tests/test_pc_video_transitions.gd
godot --xr-mode off --rendering-method gl_compatibility --path . --script tests/test_pc_video_transitions.gd
python tests/test_pc_vulkan.py --godot 'C:/path/to/godot_console.exe' --ffmpeg 'C:/path/to/ffmpeg.exe'
python tests/test_pc_hdr.py --godot 'C:/path/to/godot_console.exe' --ffmpeg 'C:/path/to/ffmpeg.exe'
python tests/test_pc_streaming.py --godot 'C:/path/to/godot_console.exe' --vulkan
@@ -78,13 +90,19 @@ python tests/test_pc_streaming.py --godot 'C:/path/to/godot_console.exe' --vulka
The Vulkan test generates ten-bit video fixtures and checks BT.601 limited-range and BT.709 full-range colors, paused seeks, completion, and playback with audio. Pass `--video=<path>` and `--size=7680x4320` to `test_pc_gpu_video.gd` to check an 8K source and reopening at 1080p. These checks do not measure sustained frame rate or verify headset playback.
OpenGL keeps its libmpv renderer and shared GPU textures. Vulkan uses a separate FFmpeg decoder and a compute shader on Godot's main Vulkan device. Native NV12, P010, and planar 4:2:0 frames keep their original plane layout; other formats are converted to ten-bit planar data. Windows tries CUDA/NVDEC before D3D11VA, while Linux tries VA-API or CUDA. CUDA downloads go directly into reusable pinned upload buffers.
The transition test renders the PC video material before playback, after stopping, and when reopening a video. It checks that a missing texture produces black and that a decoded video becomes visible. Vulkan and OpenGL checks have passed locally. The black start/stop transition still needs headset confirmation.
A reader thread buffers compressed video packets independently of decoding, up to four seconds, 128 MiB, or 512 packets. After video falls behind, audio waits for video to catch up and for one second of compressed video to accumulate, unless the reader reaches the end or the buffer limit. Seeking clears queued packets and flushes the decoder. Decoding and downloads run on another worker; uploads are queued through Godot's render thread, with one upload scheduled at a time. Uploads still share the renderer's frame budget. Decoded planes pass through system memory, so this is not a zero-copy backend.
OpenGL uses the existing libmpv renderer and shared GPU textures. Vulkan uses a separate FFmpeg decoder and a compute shader on Godot's main Vulkan device.
Native NV12, P010, and planar 4:2:0 frames keep their original plane layout. The decoder converts other formats to ten-bit planar data. Windows tries CUDA/NVDEC before D3D11VA. Linux tries VA-API or CUDA. CUDA downloads go directly into reusable pinned upload buffers.
A reader thread buffers compressed video packets independently of decoding. It reads ahead up to four seconds, 128 MiB, or 512 packets. If video falls behind, audio waits for it to catch up and for one second of compressed video to accumulate. Playback can also resume when the reader reaches the end or the buffer limit. Seeking clears queued packets and flushes the decoder.
Decoding and downloads run on another worker. Godot's render thread handles uploads, with one upload scheduled at a time. Uploads share the renderer's frame budget. Decoded planes pass through system memory, so this is not a zero-copy backend.
Desktop RenderingDevice settings allow up to 512 MiB of staging buffers and use larger upload regions to reduce stalls with 8K ten-bit frames. The engine allocates this memory as needed. OpenGL and Android Compatibility rendering do not use these buffers.
PQ and HLG video use Hable tone mapping to SDR, using mastering or content-light metadata when available and a 1,000-nit default otherwise. The HDR test compares gray patches with FFmpeg's reference conversion and checks their brightness through the actual 3D video material. HDR output and display calibration are not supported.
PQ and HLG video use Hable tone mapping to SDR. The conversion uses mastering or content-light metadata when available, or a 1,000-nit default. The HDR test compares gray patches with FFmpeg's reference conversion and checks their brightness through the 3D video material. HDR output and display calibration are not supported.
Measure presentation rate and frame times with a source long enough for the requested sample, including one second of warm-up:
@@ -92,17 +110,42 @@ Measure presentation rate and frame times with a source long enough for the requ
godot --xr-mode off --rendering-method mobile --rendering-driver vulkan --path . --script tests/test_pc_video_performance.gd -- --video='C:/videos/8k.mp4' --seconds=20 --minimum-fps=57
```
The result reports presented frames per second, dropped frames, decoder and transfer timings, and main-loop frame times. The default source is the locally generated `build/gpu-worker/8k-av1-test.mp4`; supply your own file on a fresh checkout. This windowed benchmark does not establish SteamVR performance. During ordinary playback, the startup log identifies the renderer, codec, hardware device, HDR transfer, and backend version. Set `JELLYFRAME_VIDEO_STATS=1` to log counters every five seconds. Isolated corrupt video packets are skipped; repeated errors still trigger the software fallback.
The result reports presented frames per second, dropped frames, decoder and transfer timings, and main-loop frame times. The default source is the locally generated `build/gpu-worker/8k-av1-test.mp4`. Supply your own file on a fresh checkout. This windowed benchmark does not measure SteamVR performance.
For videos with audio, libmpv opens the source separately with video disabled and supplies the audio clock. Remote playback can therefore make two sets of media requests. Silent videos use the FFmpeg frames and a local playback clock. Initialization failures, decoder failures, and stalled GPU playback fall back to the existing software renderer. `JELLYFRAME_PC_RENDERER=software` forces that fallback for troubleshooting.
During ordinary playback, the startup log identifies the renderer, codec, hardware device, HDR transfer, and backend version. Set `JELLYFRAME_VIDEO_STATS=1` to log counters every five seconds. The decoder skips isolated corrupt video packets. Repeated errors trigger the software fallback.
To test buffering, supply a video with audio and at least 20 seconds of content. The test uses FFmpeg and `ffprobe` from the same directory and serves the file over local HTTP:
```powershell
python tests/test_pc_video_recovery.py --godot 'C:/path/to/godot_console.exe' --ffmpeg 'C:/path/to/ffmpeg.exe' --video 'C:/videos/8k-with-audio.mp4'
python tests/test_pc_video_recovery.py --godot 'C:/path/to/godot_console.exe' --ffmpeg 'C:/path/to/ffmpeg.exe' --video 'C:/videos/8k-with-audio.mp4' --expect-buffer-free
```
The first command interrupts delivery and checks recovery, audio synchronization, and a manual pause during buffering. The second delivers at twice the average bitrate, adds a 1.5-second interruption, and checks that read-ahead absorbs it without a buffer stop. Omitting `--video` generates a 4K HEVC fixture and requires FFmpeg's NVIDIA NVENC encoder.
These checks passed with an 8K AV1 source on the Windows RTX 4070 setup. A separate 15-second local benchmark measured about 60 presented frames per second without dropped frames. Headset confirmation of HDR, 4K, and 8K playback is recorded separately in the [README](README.md#what-has-been-tested).
For a Vulkan playback log in PowerShell, run the exported app with SteamVR active:
```powershell
New-Item -ItemType Directory -Force logs | Out-Null
$env:JELLYFRAME_VIDEO_STATS = '1'
.\dist\windows\jellyframe.exe --rendering-method mobile --rendering-driver vulkan --log-file (Join-Path $PWD 'logs/vulkan-playback.log')
```
The log includes the codec, hardware decoder, audio and video positions, read-ahead duration and size, dropped frames, and video buffering count. Remove access tokens and private server details before sharing a log.
For videos with audio, libmpv opens the source separately with video disabled and supplies the audio clock. Remote playback can therefore make two sets of media requests. Silent videos use the FFmpeg frames and a local playback clock.
If initialization, decoding, or GPU playback stalls or fails, playback falls back to the existing software renderer. `JELLYFRAME_PC_RENDERER=software` forces that fallback for troubleshooting.
With SteamVR and your headset running, test the stereo screen and controllers:
```powershell
godot --path . --script tests/test_steamvr_video.gd
godot --rendering-method mobile --rendering-driver vulkan --path . --script tests/test_steamvr_video.gd
```
The headset test loops a labelled stereo clip for two minutes so you can close one eye at a time to check the LEFT and RIGHT labels, then press A or B to open the playback controls. The decoder tests cover resume, pause, seeking, completion, reopening, and errors.
The headset test loops a labelled stereo clip for two minutes. Close one eye at a time to check the LEFT and RIGHT labels. Press A or B to open the playback controls. Use `--rendering-method gl_compatibility` instead of the Vulkan arguments to check OpenGL. The decoder tests cover resume, pause, seeking, completion, reopening, and errors.
Import `project.godot` into Godot, or run:
@@ -110,7 +153,7 @@ Import `project.godot` into Godot, or run:
godot --path . --xr-mode off
```
Connect to your Jellyfin server to browse your library in desktop mode, which does not save sessions or play videos.
Connect to your Jellyfin server to browse your library in desktop mode. It remembers sign-in through the local session store but does not play videos in the app. Decoder test scripts are separate from this desktop interface mode.
## Build Linux x64 or ARM64
@@ -122,7 +165,7 @@ bash tools/build-pc-video.sh
godot --headless --xr-mode off --path . --editor --import --quit
```
The script detects x64 or ARM64, downloads and verifies the godot-cpp bindings, and builds the matching plugin in `addons/jellyframe_mpv` using the system libmpv and FFmpeg development packages. FFmpeg 6 or newer is required. Close Jellyframe before rebuilding its plugin.
The script detects x64 or ARM64, downloads and verifies the godot-cpp bindings, and builds the matching plugin in `addons/jellyframe_mpv`. It uses the system libmpv and FFmpeg development packages. FFmpeg 6 or newer is required. Close Jellyframe before rebuilding its plugin.
For x64:
@@ -140,9 +183,11 @@ godot --headless --xr-mode off --path . --export-release 'Linux ARM64' dist/linu
godot --headless --xr-mode off --path . --export-release 'Linux ARM64 Vulkan' dist/linux-arm64/jellyframe-vulkan.arm64
```
The OpenXR presets select OpenGL Compatibility. The Vulkan presets select the Mobile renderer and Vulkan through custom feature tags. Both executables embed their own resources and share the native decoder library in the output folder.
The OpenGL presets use Compatibility. The Vulkan presets use custom feature tags to select the Mobile renderer and Vulkan. Both executables embed their own resources and share the native decoder library in the output folder.
**Build Linux** uses native `ubuntu-24.04` and `ubuntu-24.04-arm` runners. Each job builds its plugin, exports the OpenGL and Vulkan apps, checks output files and decoder dependencies, and packages a system-libraries archive plus separate OpenGL and Vulkan AppImages. Tests run separately using the commands below. The system-libraries artifact is a tar archive that preserves Linux executable permissions; GitHub keeps them for 14 days, and the workflow needs no repository secrets.
The Build Linux workflow uses native `ubuntu-24.04` and `ubuntu-24.04-arm` runners. Each job builds its plugin, exports the OpenGL and Vulkan apps, and checks output files and decoder dependencies. It then packages a system-libraries archive and separate OpenGL and Vulkan AppImages. Run the tests separately with the commands in this guide.
The system-libraries artifact is a tar archive that preserves Linux executable permissions. GitHub keeps the build artifacts for 14 days. The workflow needs no repository secrets.
To package AppImages after exporting and copying the licenses into `dist/linux-<arch>`, install `patchelf` and run:
@@ -152,9 +197,11 @@ bash tools/package-linux-appimages.sh x86_64
bash tools/package-linux-appimages.sh arm64
```
The packager reuses the decoder library bundler, includes its license notices and package manifest, and leaves graphics drivers and OpenXR to the host. It downloads appimagetool 1.9.1 for the selected architecture, verifies its SHA-256 checksum, and runs it without requiring FUSE. appimagetool downloads its AppImage runtime during packaging, so packaging requires network access. Outputs are `dist/jellyframe-linux-<arch>-opengl.AppImage` and `dist/jellyframe-linux-<arch>-vulkan.AppImage`.
The packager reuses the decoder library bundler and includes its license notices and package manifest. The host supplies graphics drivers and OpenXR. The packager downloads appimagetool 1.9.1 for the selected architecture, verifies its SHA-256 checksum, and runs it without FUSE.
The decoder and headset tests in [Build and test on Windows](#build-and-test-on-windows) also work on Linux, where you should use `python3` for the HTTP streaming test and pass the path to your Linux Godot executable.
appimagetool downloads its AppImage runtime during packaging, so packaging needs network access. Outputs are `dist/jellyframe-linux-<arch>-opengl.AppImage` and `dist/jellyframe-linux-<arch>-vulkan.AppImage`.
The decoder and headset tests in [Build and test on Windows](#build-and-test-on-windows) also work on Linux. Use `python3` for the HTTP streaming test and pass the path to your Linux Godot executable.
## Run the core and HTTP tests
@@ -166,17 +213,18 @@ godot --headless --xr-mode off --path . --script tests/test_http.gd
godot --headless --xr-mode off --path . --script tests/test_autoplay.gd
godot --headless --xr-mode off --path . --script tests/test_player_return.gd
godot --headless --xr-mode off --path . --script tests/test_player_tooltips.gd
godot --headless --xr-mode off --path . --script tests/test_player_loading.gd
```
## Check 180° and 360° rendering
To render synthetic panoramas, check top-bottom eye selection, and check that the view behind a 180° video stays dark on desktop:
Render synthetic panoramas to check top-bottom eye selection and confirm that the view behind a 180° video stays dark on desktop:
```powershell
godot --xr-mode off --path . --script tests/capture_360.gd
```
The check tests the PC shader with a generated texture and saves panorama and settings previews in `dist`, without enabling desktop video playback. It cannot validate headset tracking, Android textures, or stereo output in a headset.
The check uses a generated texture to test the PC shader and saves panorama and settings previews in `dist`. It does not enable desktop video playback in the app. It cannot test headset tracking, Android textures, or stereo output in a headset.
## Code layout
@@ -184,5 +232,5 @@ The check tests the PC shader with a generated texture and saves panorama and se
- `scripts/vr` handles environments, controller pointing, and recentering.
- `scripts/player` selects Android playback or PC VR playback.
- `native/android` contains the Android decoder.
- `native/pc` contains the libmpv PC decoder.
- `native/pc` contains the libmpv OpenGL renderer, FFmpeg Vulkan decoder, GPU plane conversion, and playback synchronization.
- `scripts/core` contains screen settings and stereo geometry.
+24 -4
View File
@@ -12,7 +12,7 @@ The initial Jellyfin compatibility target is version 10.11.6. Use HTTPS when con
Download the Android APK from **Assets** on the [latest release page](https://github.com/Cuteminded/jellyframe/releases/latest). To build the APK yourself, follow the [build instructions](DEVELOPMENT.md).
Connect your headset to your computer, approve USB debugging in the headset, and install the APK with Android's `adb` tool. Run these commands from the folder containing the downloaded APK:
Connect your headset to your computer and approve USB debugging in the headset. Install the APK with Android's `adb` tool. Run these commands from the folder containing the downloaded APK:
```powershell
adb devices
@@ -27,9 +27,25 @@ Open Jellyframe from Unknown Sources in your headset, enter your Jellyfin server
Download the Windows ZIP from **Assets** on the [latest release page](https://github.com/Cuteminded/jellyframe/releases/latest). Extract the entire ZIP and keep the executable, PCK, and any DLLs together in the same folder.
Install SteamVR, connect your headset, and set SteamVR as the active OpenXR runtime in SteamVR's **Settings > OpenXR**. Start SteamVR and run `jellyframe.exe`; Godot uses the active OpenXR runtime, as described in the [Godot Windows PCVR guide](https://www.khronos.org/assets/uploads/developers/presentations/Godot_guide_to_OpenXR_for_Windows_PCVR.pdf).
Install SteamVR and connect your headset. In SteamVR's **Settings > OpenXR**, set SteamVR as the active OpenXR runtime. Start SteamVR, then run `jellyframe.exe`. Godot uses the active OpenXR runtime, as described in the [Godot Windows PCVR guide](https://www.khronos.org/assets/uploads/developers/presentations/Godot_guide_to_OpenXR_for_Windows_PCVR.pdf).
Sign in to Jellyfin and select a title to play it in VR, using the same pause, seek, resume, and side-by-side 3D controls as the Quest build. Keep `jellyframe_mpv.dll` and `libmpv-2.dll` beside the executable.
The current Windows export defaults to OpenGL. To use the Vulkan backend tested with HDR, 4K, and 8K video, run this from the extracted folder in PowerShell:
```powershell
.\jellyframe.exe --rendering-method mobile --rendering-driver vulkan
```
For OpenGL:
```powershell
.\jellyframe.exe --rendering-method gl_compatibility
```
Sign in to Jellyfin and select a title to play it in VR. Pause, seek, resume, and side-by-side 3D controls work as they do in the Quest build.
Keep all DLLs from the matching build beside the executable. Current source builds need `jellyframe_mpv.dll`, `libmpv-2.dll`, `avcodec-62.dll`, `avformat-62.dll`, `avutil-60.dll`, `swscale-9.dll`, and `swresample-6.dll`.
The confirmed Vulkan HDR, 4K, and 8K results apply to a Windows SteamVR setup with an NVIDIA RTX 4070. Check the [current limitations](README.md#current-limitations) for other configurations. A published release may predate these changes; use [the source build instructions](DEVELOPMENT.md#build-and-test-on-windows) when testing the current implementation.
## Try it on Linux x64 or ARM64
@@ -46,7 +62,11 @@ If OpenGL shows a black screen, download and run `jellyframe-linux-x86_64-vulkan
If mounting an AppImage fails because FUSE is unavailable, run it with `--appimage-extract-and-run`.
The builds target Ubuntu 24.04. AppImages still need compatible system libraries, graphics drivers, and an OpenXR runtime; they do not guarantee compatibility with older Linux distributions. Start your OpenXR runtime with the headset connected before opening Jellyframe, and set SteamVR as the active runtime on x64. The runtime must support the selected graphics API. SteamVR on ARM64 and standalone Steam Frame support still need headset testing.
The builds target Ubuntu 24.04. AppImages need compatible system libraries, graphics drivers, and an OpenXR runtime. They may not work on older Linux distributions.
Connect your headset and start your OpenXR runtime before opening Jellyframe. On x64, set SteamVR as the active runtime. The runtime must support the selected graphics API. SteamVR on ARM64 and standalone Steam Frame support still need headset testing.
SteamOS Vulkan playback was confirmed with an earlier PC backend. The current native Vulkan decoder, HDR conversion, and high-resolution playback still need Linux headset testing. The Windows results do not confirm those cases on Linux. See the [current limitations](README.md#current-limitations).
The `system-libs` archive remains available if you prefer to install libmpv yourself. It contains both renderers. On Ubuntu x64:
+25 -22
View File
@@ -1,39 +1,42 @@
# License and Attribution
# License and attribution
Copyright © 2026 Cuteminded.
Jellyframe's source code is licensed under the [GNU General Public License v3.0 only](LICENSE) (`GPL-3.0-only`).
## Third-Party Components
## Third-party components
Jellyframe uses third-party software and assets that are subject to their respective licenses:
Jellyframe uses third-party software and assets under their own licenses:
- **Godot and Android:** Components retain their original licenses and copyright notices.
- **Material Symbols:** Icons used throughout the player and library interfaces are sourced from [Google's Material Symbols Rounded](https://github.com/google/material-design-icons).
- **Cinema Environment:** The virtual cinema environment is based on [Comicaroid's Cinema/Movie Theater Interior](https://sketchfab.com/3d-models/cinemamovie-theater-interior-dcaf6cdebcad4f03879c24186da257ee), licensed under [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/).
- Godot and Android components retain their original licenses and copyright notices.
- Icons in the player and library come from [Google's Material Symbols Rounded](https://github.com/google/material-design-icons).
- The virtual cinema is based on [Comicaroid's Cinema/Movie Theater Interior](https://sketchfab.com/3d-models/cinemamovie-theater-interior-dcaf6cdebcad4f03879c24186da257ee), licensed under [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/).
## Media Artwork
## Media artwork
Movie posters, backdrops, and other media artwork are retrieved from the user's Jellyfin server. These assets are not included in Jellyframe's source code or distributed with the application.
Jellyframe retrieves movie posters, backdrops, and other media artwork from the user's Jellyfin server. It does not include these assets in its source code or distribute them with the app.
# PC Playback Dependencies
# PC playback dependencies
The Windows version of Jellyframe includes third-party libraries required for video playback and integration with Godot.
The Windows build includes third-party libraries for video playback and integration with Godot.
The build bundles **libmpv** from [shinchiro's Windows builds](https://github.com/shinchiro/mpv-winbuild-cmake/releases/tag/20261010), using release `20261010`, based on mpv revision `b2c255c13e`. It also links against **godot-cpp**, using the `godot-4.5-stable` version.
The build bundles libmpv from [shinchiro's Windows builds](https://github.com/shinchiro/mpv-winbuild-cmake/releases/tag/20261010), release `20261010`, based on mpv revision `b2c255c13e`. It also links against godot-cpp version `godot-4.5-stable`.
The Vulkan video backend also links against FFmpeg. Windows builds bundle the shared libraries from [BtbN's release of October 19, 2026](https://github.com/BtbN/FFmpeg-Builds/releases/tag/autobuild-2026-10-19-13-06), archive `ffmpeg-n8.1.3-18-g46d899d7a0-win64-gpl-shared-8.1.zip`.
This archive uses FFmpeg revision `46d899d7a0` with GPL components enabled. Its GPLv3 license is included in the exported app's `licenses/ffmpeg.txt`.
The [build scripts](https://github.com/BtbN/FFmpeg-Builds) describe its configuration and dependency sources, and the [FFmpeg source](https://github.com/FFmpeg/FFmpeg/tree/46d899d7a0) is available separately.
Linux builds use distribution FFmpeg libraries, with their notices included when packaging the bundled libraries.
The Vulkan video backend also links against FFmpeg. Windows builds bundle the shared libraries from [BtbN's release of October 19, 2026](https://github.com/BtbN/FFmpeg-Builds/releases/tag/autobuild-2026-10-19-13-06), archive `ffmpeg-n8.1.3-18-g46d899d7a0-win64-gpl-shared-8.1.zip`.
## Sources and Attribution
This archive uses FFmpeg revision `46d899d7a0` with GPL components enabled. The exported app includes its GPLv3 license in `licenses/ffmpeg.txt`.
The following links provide access to the original source code, build configurations, and licensing information:
The [build scripts](https://github.com/BtbN/FFmpeg-Builds) describe the configuration and dependency sources. The [FFmpeg source](https://github.com/FFmpeg/FFmpeg/tree/46d899d7a0) is available separately.
- **libmpv:** [Windows binary release (20261010)](https://github.com/shinchiro/mpv-winbuild-cmake/releases/tag/20261010)
- **mpv:** [Source code and copyright information (b2c255c13e)](https://github.com/mpv-player/mpv/tree/b2c255c13e)
- **Windows Decoder Dependencies:** [Build scripts and dependency sources](https://github.com/shinchiro/mpv-winbuild-cmake)
- **godot-cpp:** [Source code (Godot 4.5 stable)](https://github.com/godotengine/godot-cpp/tree/godot-4.5-stable)
Linux builds use distribution FFmpeg libraries. Packaging includes the notices for the bundled libraries.
These components remain subject to their respective licenses and copyright notices.
## Sources and attribution
Sources, build configurations, and license information:
- [libmpv Windows binary release, 20261010](https://github.com/shinchiro/mpv-winbuild-cmake/releases/tag/20261010)
- [mpv source code and copyright information, b2c255c13e](https://github.com/mpv-player/mpv/tree/b2c255c13e)
- [Windows decoder build scripts and dependency sources](https://github.com/shinchiro/mpv-winbuild-cmake)
- [godot-cpp source code, Godot 4.5 stable](https://github.com/godotengine/godot-cpp/tree/godot-4.5-stable)
Each component keeps its own license and copyright notices.
+23 -21
View File
@@ -1,23 +1,38 @@
<img src="assets/logo/jellyframe-wordmark.png" alt="Jellyframe" style="width:400px;"/>
Watch your Jellyfin movies and shows on a virtual screen in VR.
Jellyframe is a free, open source app that connects to your Jellyfin server so you can browse your library, choose a title, and position the screen where you want it. Your server supplies the movies and shows.
Jellyframe is a free, open source app for browsing your Jellyfin library and watching videos in VR. Choose a title and position the screen where you want it. Your Jellyfin server supplies the movies and shows.
## From JellyQuest to Jellyframe
This project started as JellyQuest, a Jellyfin VR app for the Quest 3. I renamed it Jellyframe because I plan to switch to Steam Frame, which is now the main target for future development. I do not have a Steam Frame yet, so I cannot test it on the headset, but I am still developing the Quest version under the Jellyframe name.
This project started as JellyQuest, a Jellyfin VR app for the Quest 3. I renamed it Jellyframe because I plan to switch to Steam Frame.
## What you can do
Steam Frame is now the main target for future development.
Browse and search your Jellyfin libraries, watch 2D, side-by-side 3D, 180° and 360° video, and use your controllers to select titles and control playback. Choose a dark background, a cinema, or passthrough on supported devices, and let episodes play automatically in series order.
> I do not have a Steam Frame yet, so I cannot test it on the headset. Development of the Quest version continues under the Jellyframe name.
See [Using Jellyframe](USAGE.md) for controls, screen settings, environments, and sign-in behaviour.
## What has been tested
## Get started
- Browsing and searching Jellyfin libraries.
- Selecting titles and controlling playback with controllers.
- Dark backgrounds, cinema environments, and passthrough on supported devices.
- 2D, side-by-side 3D, 180° and 360° video.
- Automatic episode playback in series order.
- HDR, 1080p, 4K, and 8K video playback.
You need a Jellyfin server and an account on that server; the initial compatibility target is Jellyfin 10.11.6.
## Current limitations
Follow the [installation guide](INSTALLATION.md) to download a build, install the app, and sign in on Quest 3, Windows with SteamVR, or Linux x64 and ARM64 with OpenXR.
- Steam Frame installation and playback remain untested.
- 180° projection and top-bottom 3D still need headset testing. Desktop projection tests cannot confirm stereo output or viewing comfort in VR.
- Quest HLS seeking still needs in-headset confirmation after the timeline fix.
- Linux VR playback was previously tested on a Steam Machine running SteamOS with Vulkan and SteamVR. The new native Vulkan decoder, HDR conversion, and 4K/8K playback still need Linux headset testing.
- ARM64 and other Linux systems also need headset testing.
- Quest and OpenGL HDR behaviour still need separate testing.
- Native HDR output and display calibration are not implemented.
- Some files need conversion by your Jellyfin server during playback. This can fail if the server cannot handle the conversion.
- Audio and subtitle selection, favourites, and collections are not available yet.
- Manually marking titles as watched and automatically hiding playback controls are planned but not available yet.
## Documentation
@@ -27,19 +42,6 @@ Follow the [installation guide](INSTALLATION.md) to download a build, install th
- [Contributing](CONTRIBUTING.md)
- [Licence and attribution](LICENSING.md)
## Current limitations
- 180° projection and top-bottom 3D still need headset validation.
- Steam Frame installation and playback remain untested.
- Linux VR playback has been tested on a Steam Machine running SteamOS, using Vulkan and SteamVR. ARM64 and other Linux systems still need headset testing.
- CI playback checks run without a headset and do not verify tracking, controller input, or stereo output.
- Desktop mode only shows the interface. PC video playback requires an active OpenXR session with a connected headset.
- PC OpenGL builds render video on a worker thread into buffered GPU textures. Vulkan builds use FFmpeg to decode video and a Vulkan compute shader to convert YUV planes into the screen texture. Both paths have a software fallback and can copy decoded frames through system memory. High-resolution playback still needs headset performance testing, especially with 8K and full-SBS files.
- Specific 4K HEVC and HDR videos still need testing; HDR playback relies on your Jellyfin server to convert the video to standard dynamic range with correct colours.
- Some files need your Jellyfin server to convert the video during playback, which can fail if the server cannot handle the conversion.
- Audio and subtitle selection, favourites, and collections are not available yet.
- Marking titles as watched manually and hiding playback controls automatically are planned but not available yet.
## Licence
Jellyframe uses GPL-3.0-only; see [Licence and attribution](LICENSING.md) for licence terms and third-party credits.
+117 -29
View File
@@ -1,50 +1,138 @@
# Using Jellyframe
## Browsing and playback
1. Enter the address you normally use to open Jellyfin, such as `https://jellyfin.example.com`. If someone else runs the server, ask them for the address.
2. Enter your Jellyfin username and password.
3. Select **Sign in**.
- Sign in with your Jellyfin username and password or Quick Connect.
- Browse and search your libraries, view title details, and continue watching.
- Watch 2D movies and side-by-side 3D videos.
- Watch equirectangular 180° and 360° videos in 2D, side-by-side 3D, or top-bottom 3D.
- Resize the screen and adjust its distance, height, and curvature.
- Recenter the screen in your viewing direction.
- Watch against a dark background, in a cinema, or with your room visible through passthrough.
- Use your controllers to select titles, type, pause, and skip through a video, or point at a player control to see its label beneath the buttons.
- Use the Library button to return to the selected movie or series, keeping the current season open for a series. Selecting the active title returns to playback.
- When an episode ends, a 5-second countdown appears before the next available episode starts in series order, including across seasons. Cancel the countdown to return to your library.
To use **Quick Connect**, select it and note the code Jellyframe displays. Open Jellyfin on your phone or computer, sign in, and approve that code using Quick Connect.
Jellyframe supports one server and one user at a time, saves your screen settings, and remembers your sign-in without storing your password. On PC, it stores the sign-in token in `session.cfg` in Godot's local user-data folder.
Jellyframe remembers your sign-in without saving your password. Select **Sign out** before switching accounts or servers. You can use one server and account at a time.
Default PC session locations:
## Reset your saved sign-in on PC
| Platform | File |
If you cannot sign out through the app, remove the saved sign-in file:
1. Close Jellyframe completely.
2. Find the file for your computer in the table below.
3. Delete `session.cfg`.
4. Open Jellyframe again and sign in.
| Computer | Saved sign-in file |
| --- | --- |
| Windows | `%APPDATA%\Jellyframe\session.cfg` |
| Linux | `~/.local/share/Jellyframe/session.cfg` |
Jellyframe reads the 3D format from Jellyfin's metadata or the filename, but you can select half or full side-by-side mode and swap the left and right views in Settings if the picture looks wrong. Top-bottom uses the upper half for the left eye and the lower half for the right eye; format and eye-swap changes are not saved per title.
To reset the saved app settings as well, delete `settings.cfg` from the same folder while Jellyframe is closed. You will need to enter your server address and choose your settings again. This does not delete movies, shows, or your Jellyfin account.
## 180° and 360° videos
## Choose something to watch
Start a video, open Settings > Screen, and choose **180°** or **360°** under Video projection, then select **2D**, **Half-SBS**, **Full-SBS**, or **Top-bottom** under Video format to match the file. 180° fills the view in front of you and leaves the view behind you dark, while 360° surrounds you.
1. Select **Home** to see recently added movies and shows, or choose one of your libraries from the menu.
2. Browse the titles, or enter a name in the search box and select **Search**.
3. Select a movie to open its details, then choose **Play movie**. For a series, choose a season and select **Play episode** beside the episode you want.
4. If you have watched part of a title before, choose **Resume** or **Resume episode** to continue from where you left off.
Look around with your headset to see the scene, use the headset's recenter command to set the forward direction, and press A or B to show the playback controls in front of you.
In VR, point your controller at a button and squeeze the trigger to select it. Point at a text box to open the keyboard. Use the thumbstick to scroll through a list.
Jellyframe selects 360° automatically for filenames with a separate `360`, `360deg`, or `VR360` marker, such as `Beach.360.mp4`, and selects 180° for a separate `180`, `180deg`, or `VR180` marker. It does not infer the projection from video dimensions; files with a separate `TB`, `OU`, `top-bottom`, or `over-under` marker select top-bottom 3D.
## Control playback
180° and 360° modes support equirectangular video, but not cubemap or fisheye layouts. The video replaces the chosen environment during playback, and screen size and position controls do not affect it. Select **Screen** to return to the virtual screen; projection overrides apply to the current playback and are not saved per title, so a new title uses automatic detection again.
Press **A** or **B** to show the playback controls. Point at a button to see its name beneath the controls.
## Settings and environments
| Control | What it does |
| --- | --- |
| Play / Pause | Pause the video or continue watching. |
| Stop | Stop the video and return to your library. |
| Back 10 seconds | Go back a little to watch something again. |
| Forward 30 seconds | Skip ahead. |
| Time bar | Choose a different point in the video. |
| Settings | Change the screen, background, or playback quality. |
| Library | Return to the current movie or series details while the video keeps playing. |
| Hide controls | Remove the controls from your view. Press A or B to show them again. |
Use the separate settings pages to change library sorting, screen controls, and environments. For library sorting, choose a field and ascending or descending order; Home always shows the newest additions first.
After using **Library**, select the title that is already playing to return to the video. For a series, Jellyframe keeps the current season open.
Choose an environment:
When an episode finishes, a five-second countdown appears before the next available episode starts. Choose **Cancel** during the countdown to return to your library.
- Void, the default, gives you a dark background.
- Cinema uses the detailed cinema interior.
- Theatre uses the original simple cinema.
- Passthrough shows your room around the screen and is disabled on unsupported devices.
## Make the screen comfortable
Cinema and Theatre have a fixed front screen with black bars to preserve the video's proportions, while Void and Passthrough let you adjust the screen's size, distance, height, and curvature.
Open **Settings**, then choose **Screen**. If your chosen background allows screen adjustments, you can change:
Reset to defaults restores the screen settings and keeps your chosen environment.
- **Width** to make the screen larger or smaller.
- **Distance** to move it closer or farther away.
- **Height** to move it up or down.
- **Curvature** to make it flat or curved around you.
If the screen is facing the wrong way, look in the direction you want to watch and use your headset's recenter command.
**Cinema** and **Theatre** have fixed screens. To move or resize the screen yourself, choose **Void** or **Passthrough** under **Settings > Environment**.
Select **Reset to defaults** on the Screen page to restore the usual screen size and position. This keeps your chosen background and video format. Settings are saved as you change them.
## Choose your surroundings
Open **Settings > Environment** and choose:
| Background | What you see |
| --- | --- |
| Void | A dark background with an adjustable screen. This is the default. |
| Cinema | A detailed cinema with a fixed screen at the front. |
| Theatre | A simpler cinema with a fixed screen. |
| Passthrough | Your real room around the video screen, if your headset supports it. |
If your device does not support Passthrough, you cannot select it. In Cinema and Theatre, black bars keep the picture from looking stretched.
## Watch a 3D video
Jellyframe usually chooses the right picture layout automatically. If you see two pictures side by side or one above the other, open **Settings > Screen** and look for **Video format**.
| Option | When to use it |
| --- | --- |
| 2D | Ordinary movies and shows without 3D. |
| Half-SBS | A 3D video that stores the left-eye and right-eye pictures side by side. |
| Full-SBS | Another side-by-side 3D format, with a wider picture for each eye. |
| Top-bottom | A 3D video that stores one eye's picture above the other. |
SBS means "side by side". If you do not know which version your video uses, try Half-SBS first. If the picture looks too wide or too narrow, try Full-SBS.
If the depth looks wrong, try **Swap left and right eyes**. These choices are not remembered separately for each title, so another video may need a different setting.
## Watch a 180° or 360° video
These videos let you look around inside the scene instead of watching a normal screen.
1. Start the video.
2. Open **Settings > Screen**.
3. Under **Video projection**, choose **180°** for a scene in front of you, or **360°** for a scene that surrounds you.
4. If the video is also in 3D, choose the matching **Video format** as described above. Otherwise, leave it on **2D**.
Move your head to look around. For a 180° video, the space behind you stays dark. Use your headset's recenter command if the scene faces the wrong direction. Press A or B to bring up the controls.
The video replaces your chosen background while you watch. Screen width, distance, height, and curvature do not affect these videos. Select **Screen** under Video projection to return to a normal video screen.
Jellyframe can recognize some 180° and 360° videos automatically, but you can change the setting if it chooses incorrectly. A new title uses automatic detection again. Some panoramic video formats are not supported, so changing this setting cannot fix every distorted video.
## Change playback quality
Open **Settings > Playback**. The choice applies to the next video you start.
- **Automatic** plays the original video when possible and asks Jellyfin to convert it when needed. Start with this option if you are unsure.
- **Original full resolution** keeps the original video quality. Your headset or PC must be able to play the file.
- **Transcoded** asks your Jellyfin server to make a version that is easier to play, up to 1080p. This can help with files that will not play smoothly at their original quality.
Picture quality depends on the video, your device, your connection, and what your Jellyfin server can handle. To use the Windows setup tested with HDR, 4K, and 8K videos, follow the [Windows launch instructions](INSTALLATION.md#try-it-on-windows-with-steamvr).
## Change the order of your library
Open **Settings > Library sorting** and choose how you want titles arranged. **Ascending** means A to Z, lowest to highest, or oldest first. **Descending** reverses that order.
Home always shows the newest additions first. The sort order does not affect Random.
## If something does not look right
- If the controls disappear, press A or B to show them again.
- If you cannot resize the screen, choose Void or Passthrough under Settings > Environment, and select Screen under Video projection.
- If you see two copies of the picture, check Video format under Settings > Screen.
- If the scene surrounds you when you wanted a normal screen, select Screen under Video projection.
- If the video pauses with a loading message, it is waiting for more video data. Give it a moment. If this keeps happening, try Automatic or Transcoded under Settings > Playback, then start the video again.
- Audio-language and subtitle selection are not available yet.
For setup problems, see [Install and start Jellyframe](INSTALLATION.md). The [README](README.md#current-limitations) lists the remaining limitations.