Repository navigation
Replies: 13 comments 17 replies
|
Adding an Intel Quick Sync / Unraid data point. Both the compatibility check and stress tests completed on my i7-12700K / UHD 770 setup. Environment
Tests were started and collected through the admin Compatibility check
The driver advertises AV1 decoding ( Stress testsSame input for both runs: a Se7en UHD remux, 3840×2160, HEVC 10-bit, Dolby Vision with HDR10, 23.976 fps, approximately 85.2 Mbps source bitrate. Both outputs were 1920×1080 at 8 Mbps, with AAC stereo audio. Both runs reported hardware decoding, hardware encoding and hardware tone mapping.
Maximum streams was set to 32, so neither result hit the configured cap. Both stopped with Feedback / potentially confusing output
The production server and test instance were healthy after testing, and the test instance had no remaining FFmpeg processes. Compatibility check — full test logH.264 stress test — full test logHEVC stress test — full test log |
Environment:
QSV SettingsEnable hardware decoding: H264, HEVC, MPEG2, VP8, VP9, HEVC 10bit, HEVC RExt 8/10bit Compatibility Check
Compatibility Check - Full logStress Test (The Batman, 4K HEVC 10bit HDR10):
Stress Test - Full logVAAPI:Enable hardware decoding: H264, HEVC, MPEG2, VP8, VP9, HEVC 10bit, HEVC RExt 8/10bit Compatibility Test:
Compatibility Check - Full logStress Test (The Batman, 4K HEVC 10bit HDR10):
Stress Test - Full logStress Test (The Batman, 4K HEVC 10bit HDR10), Turning off Tone Mapping:
Stress Test - Full logMy findings:VAAPI seems to have slightly better compatibility (e.g., tone mapping) and throughput (fps) compared to QSV, but VAAPI struggles to handle multiple streams that need tone mapping. The compatibility check seems to say the setting for AV1 8bit encode is enabled (On), but I didn't have it turned on for my testing. After restarting the server and testing VAAPI again, the compatibility test said VAAPI wasn't able to handle tone mapping anymore. I'm not sure what happened there. I'd be more than happy to test again with other settings and other files. *** Edit ***New Test with build 3cf182621-66ab809e6QSV & VAAPI had similar results, so I'll only post the new QSV test. Both had trouble with av1.mkv creation and testing (even with the setting off). Compatibility Test:
Compatibility Check - Full logResultsThe Tone Mapping check seems to be working better, but the AV1 clip is still unable to be made. If it helps, here are the encoders and decoders that are found via Jellyfin logs: Available Encoders and Decoders |
Stress Test:
Output: 1920×1080 HEVC · 8 Mbps + AAC stereoDecode: GPU · Encode: GPU · Tone mapping: none · hevc_vaapi Input: RoboCop · 3840×2160 HEVC 10-bit Dolby Vision · 99.3 Mbps · 23.976 fps Output: 1920×1080 HEVC · 8 Mbps + AAC stereoDecode: GPU · Encode: GPU · Tone mapping: GPU · hevc_vaapi Output: 1920×1080 H.264 · 8 Mbps + AAC stereoDecode: GPU · Encode: GPU · Tone mapping: GPU · h264_vaapi Input: Alien · 1920×1080 H.264 8-bit SDR · 33.9 Mbps · 23.976 fps Output: 1920×1080 H.264 · 8 Mbps + AAC stereoDecode: GPU · Encode: GPU · Tone mapping: none · h264_vaapi I try to find some time in the next days to test the 9070 XT on my main machine. |
|
I made the HWA jellyfin server and also wrote a whitepaper on the test methodology: https://github.com/JPVenson/Jellyfin.HardwareVisualizer/blob/main/HWA-Client-Whitepaper.md this testing scheme also has a provision for quick testing and should be the bases of any load testing of gpus, you can for now skip the uploading part though. is the important part for you. |
|
Adding an Intel VAAPI Arc A380 / Unraid data point. Both the compatibility check and stress tests completed on my Ryzen 9 5900x / Arc 380 setup. Environment
Compatibility check
Stress testsSame input for both runs: a Samsara UHD Stream, 3840×2160, AV1 10-bit, Dolby Vision with HDR10, 23.976 fps, approximately 28 Mbps source bitrate. Both outputs were 1920×1080 at 8 Mbps, with AAC stereo audio. Both runs reported hardware decoding, hardware encoding and hardware tone mapping.
Maximum streams was set to 24, so neither result hit the configured cap. Both stopped with Conclusion
I replaced my main Jellyfin server for testing this and was stable thoughout. Compatibility check — full test logForgot to take these down during the runs so the numbers may be slightly different. H.264 stress test — full test logHEVC stress test — full test log |
|
I'm adding a new data point for NVENC. Environment:
NVENC SettingsEnabled hardware decoding: H264, HEVC, MPEG2, VC1, VP8, VP9, AV1, HEVC 10bit, VP9 10bit, HEVC RExt 8/10bit Compatibility Check
Compatibility Check - Full logStress Test (The Batman, 4K HEVC 10bit HDR10 67.2Mbps > 1080p H.264 8Mbps):
Stress Test - Full logStress Test (The Batman, 4K HEVC 10bit HDR10 67.2Mbps > 1080p H.264 8Mbps), Tone Mapping Off:
Stress Test - Full logResults:NVENC has good encoder, decoder, and tonemapping compatibility, as well as throughput (fps). The Stress Tests seemed to fail after a few streams even though the fps remained high. |
|
Test results from four machines, with the same build everywhere (server
Runs disturbed by something outside the test (shared host disks, a production transcode, a display mode change) are listed under each table with the reason instead of being dropped silently. What I found@albarago, thanks for this, it was easy to test from end to end. Across my four machines (RTX 3060 on Linux, RTX 3080 on Windows native and WSL 2, UHD 730, M4 Pro; full results below) the check rows were right everywhere: VP8 decode and AV1 encode failed exactly where the hardware lacks them, and the tone mapping row followed the VPP and VideoToolbox settings. Results were also repeatable (12/12/12 on the 3060, 8/9/9 and 15/15 on the M4 Pro). A few things I could reproduce and pin down, from most to least useful: 1. The driver limit is detected nowhere, because the message is cut before it is checkedOn both NVIDIA cards (driver 610.57.04 on Linux, 630.20 on Windows) the 13th stream dies two seconds after it starts and the run ends as a failed stream, not as ffmpeg then prints about ten more error lines, and 2. The compatibility check fps include ffmpeg's start-up, which is huge on the first pass
On Windows the first pass wrote 7.6 MB into the NVIDIA compute cache and the second wrote nothing, so it looks like CUDA kernel compilation. A "24 fps" tone mapping row on a 3080 reads like "can't do real time". Reading the rate from ffmpeg's own progress output, or running one warm-up clip before timing, would avoid it. 3. Full VRAM looks like a performance limitOn the 10 GB RTX 3080 each 4K Dolby Vision stream tone mapped to 1080p took about 690 MB, so 12 streams put the card at 9.8 to 9.9 GB. In one run every stream froze at 0 fps at the 12th stream (VRAM 9.84 GB, GPU at 100 %, encoder near 0 %), and the result read "stream 8 fell to 3.6 fps". Logging VRAM from 4. A container without its GPU gets per-codec "turn off" adviceDocker without 5. Smaller things
Results per machineNVIDIA RTX 3060, Linux (Docker in a Proxmox LXC)Environment
Compatibility checkThree passes: the first one ever, the first one after a restart of the container, and the next one.
The results are right (AV1 encode fails, Ampere has no AV1 encoder), but the fps of the first pass, and again after every restart of the container, are far too low: 13 to 19 fps for H.264 decode and 22 to 23 fps for tone mapping, then 256 and 282 fps on the next pass. See finding 2 above. Stress tests
The limit here is the driver, not the GPU. Every clean 1080p run stopped at 12 streams because the 13th stream died two seconds after it started, while the 12 others were running at about 2x real time each (around 600 fps combined, VRAM at 9 GB of 12). I reproduced it by hand: with 12 The host shares its disks and CPU with other services, so I logged I/O wait. When the host's disks stalled, every stream dropped to 0 fps at the same time; those runs are left out, and rerun where I could. Stress test log, 1080p H.264 (run 1)Manual check: 12 h264_nvenc sessions open, then a 13thIntel UHD 730, Linux (same host)Environment
Compatibility check
The rows themselves look right: VP8 decode and AV1 encode fail, which this GPU can't do, and the tone mapping row follows the saved setting ( Stress tests
One Docker detail that cost me some time: the render node inside the container has to keep the kernel's name. Passing the iGPU's Stress test log, QSV 1080p H.264 (run 1)NVIDIA RTX 3080, Windows 11 (native build and Docker WSL 2)Environment
Compatibility check
Same first pass effect as on Linux: H.264 decode 32 then 236 fps, tone mapping 24 then 252 fps, native, minutes apart. The first pass wrote 7.6 MB into the driver's compute cache ( Stress tests, native
Stress tests, Docker WSL 2
Two different limits show up on this card:
The 4K HEVC run stopped at 10 streams on both this card and the RTX 3060, which fits both GA102 and GA106 having the same NVENC. Two WSL 2 runs are left out because every stream stopped when the monitor went to sleep or woke up during the run (the display mode changed at the same second). That happened twice, so it may be worth knowing for anyone running Jellyfin on a desktop with a monitor attached. Native stress test log, VRAM full at the 12th stream (run 1)Apple M4 Pro, macOS (native build)Environment
Compatibility check
No first pass effect here, unlike NVIDIA and Intel, apart from tone mapping (117 then 151 fps). The tone mapping row follows the setting: Stress tests
The Mac gave the cleanest runs: the three 1080p H.264 runs gave 8, 9 and 9, and the streams shared the load evenly (each stream's median between 23 and 25 fps at the limit). HEVC output held 15 streams against 8 to 9 for H.264, which matches the compatibility check where Stress test log, 1080p HEVC (run 1)Happy to rerun anything on these machines with a newer build. I used AI to help run these tests through the API, read the logs and format this comment. |
|
@albarago How about adding a 2160p AV1 and 1080p AV1 preset to the stress test? During my test of the 9070 XT, I saw that at some point multiple streams dropped slightly below the threshold. The individual streams recovered in less than 5s, so the stress test kept running. But it took multiple minutes until the streams had a 12s window without any drops to create a new stream. Here it took 174 seconds, so nearly 3 minutes from starting stream 15 to starting stream 16: In this case it took over 7 minutes (467 seconds) from starting stream 14 until stream 15 could be started: CPU: AMD Ryzen 9 5950X Compatibility check
Stress Test:RoboCop (1987) · 3840×2160 HEVC 10-bit Dolby Vision · 99.3 Mbps · 23.976 fpsOutput: 3840×2160 HEVC · 20 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_vaapi Output: 1920×1080 HEVC · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_vaapi Output: 1920×1080 H.264 · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · h264_vaapi Alien (1979) · 1920×1080 H.264 8-bit SDR · 33.9 Mbps · 23.976 fpsOutput: 1920×1080 HEVC · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: none · hevc_vaapi Output: 1920×1080 H.264 · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: none · h264_vaapi CPU: AMD Ryzen 9 5950X Compatibility check
Stress TestRoboCop (1987) · 3840×2160 HEVC 10-bit Dolby Vision · 99.3 Mbps · 23.976 fpsOutput: 3840×2160 HEVC · 20 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_amf Output: 1920×1080 HEVC · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_amf Output: 1920×1080 H.264 · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · h264_amf Dune: Part One (2021) · 3840×2160 HEVC 10-bit Dolby Vision · 69.8 Mbps · 23.976 fpsOutput: 3840×2160 HEVC · 20 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_amf Output: 1920×1080 HEVC · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · hevc_amf Output: 1920×1080 H.264 · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: GPU · h264_amf Alien (1979) · 1920×1080 H.264 8-bit SDR · 33.9 Mbps · 23.976 fpsOutput: 1920×1080 HEVC · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: none · hevc_amf Output: 1920×1080 H.264 · 8 Mbps + AAC stereo Decode: GPU · Encode: GPU · Tone mapping: none · h264_amf |
|
Adding another data point here, this time NVIDIA RTX 2000 Ada on Unraid. Really like this feature, thanks for putting the work in! Setup
I ran the test image as a seperate container next to my normal Jellyfin, on port 8097 with a fresh config and the movie folder mounted read-only. My regular Jellyfin wasn't doing anything during the runs. Compatibility checkHardware acceleration set to NVENC, everything passed:
Compatibility check logStress testsI did three runs, all with the same output: 1080p H.264 8 Mbps + AAC stereo, with decode, encode and tone mapping all on the GPU. Run 1: Akira (3840×2160 HEVC 10-bit HDR10, 98.1 Mbps remux) Akira logRun 2: Pulp Fiction (3840×1632 HEVC 10-bit Dolby Vision / HDR10, 28.5 Mbps) Pulp Fiction (HEVC) logRun 3: the same Pulp Fiction, re-encoded to AV1 The other 15 were still at 1.45–2.47× real time. Pulp Fiction (AV1) logSo on this card the real limit for runs 2 and 3 is VRAM, not speed. I checked by hand with the same ffmpeg pipeline: one HEVC stream needs roughly 550–690 MiB, an AV1 stream about 720–850 MiB. That's about 30% more per AV1 decoder, which fits nicely with 22 vs 15 streams in 16 GB.
Things I noticed
Happy to run it again on a newer build if that helps! Disclaimer: Text is written by myself, most of the data analyzation have been done with the help of Claude. |
Environment
Part 1: Compatibility Check ResultsRan multiple passes (cold & warm):
Part 2: Stress Test ResultsScenario A: Heavy 4K Dolby Vision / HDR10 Transcode
Scenario B: 1080p HEVC Transcode
Feedback & Observations
|
|
Retested on the latest image. Same card, same settings, same files as above. Short version: the memory detection works great! Both runs that ran out of VRAM now end with the orange GPU memory full badge and ffmpeg's actual
In the HEVC run the actual cause is now visible too. Before, the error text was cut off right in the middle ( Two small things I noticed:
Thanks for the quick fixes! Akira retest logPulp Fiction (HEVC) retest logPulp Fiction (AV1) retest log |
Environment
Compatibility check
Stress testInput: jellyfin-250-mbps sample, 3840×2160 HEVC 10-bit SDR, 250.1 Mbps, 29.97 fps → 1920×1080 H.264 8 Mbps ( Result: 2 streams kept up. The run ended when stream 2 fell to 27.0 fps (0.90×) for 5 s with 3 streams running; the configured maximum (16) was not reached. A single stream alone ran at 82.0 fps (2.74× real time). Average output 8.9 Mbps per stream. Single run, not repeated.
Feedback
Compatibility check logStress test logvainfo |









Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Updates / Edits
Since there's been a lot of support in testing and suggestions I was going to strikethrough all the changes. Nonetheless, for clarity and ease of reading I wont strikethrough - instead, I've made a changelog to carefully track the changes:
https://github.com/albarago/jellyfin-transcode-test/blob/main/CHANGELOG.md
I'm making targeted edits to the body of this proposal as well, for accuracy and in light of new information (thanks everyone!). The biggest one: after JPVenson's feedback, the stress test threshold changed from 90% to 100% of real time, and it now follows the HWA tool's methodology where it makes sense (see "Aligning with HWA's methodology" below).
Problem Statement
As a server operator, I've always desired a built-in method to test hardware acceleration (HWA). Today, Jellyfin operators have no built-in way to know whether their HWA settings work, or how many streams their server can serve concurrently. The only automatic check,
EncoderValidator, runs at startup and confirms that ffmpeg contains the hardware encoders and filters. It never runs a transcode. The "Enable hardware decoding for" boxes are ticked by hand, and nothing tells the operator whether those choices match their hardware.As a result, operators find problems in one of two ways:
intel_gpu_top,nvtopor Task Manager while it plays.Both happen after the fact, and some failures stay hidden. ffmpeg can silently fall back to the CPU, so playback "works" but uses far more resources than the operator expects. A wrongly ticked codec only fails when someone plays that format. Even a correct setup gives no idea of headroom, unless you try to do math based on the fps. An operator can't reliably tell whether the server handles 2 streams or 12 until real users load it.
There is clear demand for this. "Automatically test hardware transcoding" has 427 votes and is marked planned (#450). Discussion #17126 describes almost this exact button. My standalone tool, JF_hw_stress, drew enough interest that I started on a core version. Since this discussion opened, it has picked up 31 upvotes, volunteers have tested it on NVIDIA, Intel and AMD hardware I don't own, and the Reddit post got 200+ upvotes in less than 24 hours.
The goal: an operator presses one button to confirm the saved settings actually work, and another to learn how many streams the server can really serve. Both run inside the server, using the same code real playback uses, before any user hits a problem. No additional tools/clients/software or plugins. All done in minutes.
Proposed change
I ran several versions of this idea and tested time and time again, iterating to try to get it right not just from the technical standpoint but from a UI design and ease of use / intuitive feel for all of us who operate servers.. So here is what I came up with:
I propose that we integrate two new sections inside the transcoding panel of the dashboard. They should be at the bottom after the save button because they directly test what was saved so the flow made sense to me.
Fast facts about the compatibility check:
ffmpeg's software encoders, so it works with any library even if you don't have real media. The generator is testsrc2.
"Turn off VP8 under 'Enable hardware decoding for'". If a format is off and fails, it reads as "your hardware may not support this" instead of an error.
Fast facts about the stress test:
for 5 s ends the test; the result is the last count that kept up. Includes a configurable maximum (default 16, up to 64).
next stream, input and output description, per-stream fps, "x real time" speed and output bitrate.
single_worker_speed), in the result and the test log.Aligning with HWA's methodology
After feedback from JPVenson (creator of the HWA tool - thanks!), I didn't want to create a new standard, so I've followed the HWA client whitepaper wherever its rules carry over:
single_worker_speed).performance,failed_timeoutorfailed_nvenc_limit, using HWA's names.The whitepaper's rules are written for HWA clients, whose results get submitted to hwa.jellyfin.org and compared across machines. This test doesn't submit anything. Its job is to answer "what can this server do, as configured", so I only changed the parts that would stop it from doing that:
EncodingHelper/HlsCommandBuilder, same as real playbackWhat this costs: these numbers can't be compared across machines the way HWA's can. HWA's fixed files and arguments are the right design for a hardware database, so the two really do different jobs.
Possible idea for the future: We can create a "HWA mode" that runs HWA's standard test files through Jellyfin's pipeline and exports results in the survey JSON format. That would give numbers comparable with HWA and real-pipeline commands. I'd keep it out of the first PRs. @JPVenson, would that be useful to you? hwa.jellyfin.org returned 502 when I checked.
How it works
Disclaimer: I would probably split this into multiple PRs but here is the gist
Important notes:
Transcoding/Testendpoints (POST start, GET status, DELETE stop or clear). One test at a time, the web page polls once a second, so no new WebSocket message types or SDK subscription changes. The status reports aSurveyFailureReasonusing HWA's names.Results so far
In my limited testing (I only have two mac machines) I was pleasantly surprised by how this came out (and also by how powerful a fanless macbook air is!!)
Note: my numbers, and testers' rows not marked "at 100%", were measured with the old 90% threshold. At 100% they come in lower: on the same M5, 12 streams became 8-10.
Important
I've only tested VideoToolbox, since that's all I have. Others have kindly tested (thanks!)
Update: NVIDIA, Intel and AMD have now been tested by others:
*The RTX 5080 run ended when stream 9 couldn't start, not because streams fell behind. A re-run on the fixed build is pending.
The Arc A380 is the strongest result so far: 20+ streams of 4K AV1 Dolby Vision with tone mapping. In one of those runs all streams dropped to 0 fps at once; it turned out the GPU ran out of VRAM. Both NVIDIA cards stopped at the driver's 12-session limit, not their real capacity. Even the low-power N5095 handled multiple streams of 4K HDR to 1080p. Their reports also led to a round of fixes (see the changelog). Thanks all!
How this relates to other work
Below are some related pieces I found during research. Key point is that this checks what the saved settings actually do and measures headroom, which complements capability detection. I'm fully aligned to working with other ideas or changing my approach to work better for everyone, but, I think this is a great start.
EncodingHelperpieces with null output, not the full HLS playback command.A few more notes:
Questions for maintainers
Testing on other hardware
More NVIDIA results would help, especially to confirm the "Driver limit" detection now works on a real card. More AMD results are welcome too, since only one AMD GPU has been tested so far.
Test image and instructions: https://github.com/albarago/jellyfin-transcode-test
(
ghcr.io/albarago/jellyfin-transcode-test:latest, amd64 and arm64, built on jellyfin/jellyfin:unstable)What changed between builds: tester changelog
AI assistance
I dabble in developing projects on my spare time, but this would not have been possible for me to implement on my own, (unfortunately). I leveraged Kiro with varying models to help me figure out how real playback / transcoding pipelines work and how the UI works and how to integrate my ideas into it as seamlessly as possible.
Thank you for reading - I love this project and I'm happy to contribute to it.
All reactions