TL;DR: Preemptively convert videos files to guarantee Direct Play to avoid expensive & error-prone on-the-fly transcoding operations during playtime.
Direct Play: THE Best Home Video Streaming Server Optimization
I run any service I can on my home network. Why? It gives me the fastest, most reliable access to what I need and gives me control. I also get the opportunity to build and tune fairly complex systems, usually having to rely on and consider what the big service providers are doing to solve the very same problems. Replacing Netflix, Hulu, Disney+, etc with an old gaming PC and some open source software requires some shrewd engineering to deliver a similar user experience and service quality.
One of the most important pieces of my home lab is my media server. It’s one of the only things in my home lab I interact with more as an actual user than as someone trying to build or fix something, and more importantly it’s also used by over a dozen of my friends and family. That being said, it can be a rather needy service in terms of the amount of engineering and tuning TLC needed for it to run optimally.
Problem
My video streaming server on average has 6 concurrent users and gets as many as 20 during peak hours. They’re split across 4 continents, stream on all different kinds of devices (browser, mobile, TV, tablets) and have varying levels of tech know-how, but all of them want high-quality video and audio to start playing when they press the play button and to encounter no buffering beyond maybe a few seconds at the start.
That’s fair, eh? After all, when I use Youtube or Netflix I just hit the play button and everything works — I can’t even remember the last time a video began buffering with those services! These are the kinds of expectations we’re looking to satisfy here as economically as possible.
Unfortunately, downloading a bunch of HQ video files and attempting to stream them using Jellyfin, Plex or any DLNA server sofware results in various problems:
- Constant video buffering while playing even smaller, lower quality media.
- Frequent transcoding errors while playing videos.
- Inconsistent performance depending on how many concurrent users the media server has.
- Users need to dig into various menus practically every time they play a video to get a working set of video, audio, subtitle, etc options.
The Media Server
I run a typical ‘junkbox’ home media server without much in terms of hardware or networking resources and a pretty standard software stack.
-
Hardware
The server itself is just a has-been gaming PC that’s past its prime:
CPU: quad core Intel Core i7-4790K (-MT MCP-) speed/min/max: 4397/800/4400 MHz Kernel: 6.10.6 x86_64 Up: 282d 16h 53m Mem: 4.3/31.29 GiB (13.7%) Storage: 21.18 TiB (31.5% used) Procs: 221 Graphics: Device-1: NVIDIA GM206 [GeForce GTX 960] driver: nvidia v: 560.35.03 Device-2: NVIDIA GM206 [GeForce GTX 960] driver: nvidia v: 560.35.03 Display: unspecified server: X.org v: 1.21.1.13 driver: X: loaded: N/A API: EGL v: 1.5 drivers: kms_swrast,nvidia,swrast platforms: gbm,surfaceless,device API: OpenGL v: 4.6.0 compat-v: 4.5 vendor: mesa v: 24.1.6 note: console (EGL sourced) renderer: llvmpipe (LLVM 18.1.8 256 bits), NVIDIA GeForce GTX 960/PCIe/SSE2
-
Networking
My home internet connection isn’t anything to write home about either:
Retrieving speedtest.net configuration... Testing from Comcast Cable (XX.XXX.XX.XX)... Retrieving speedtest.net server list... Selecting best server based on ping... Hosted by AT&T (Cicero, IL) [9.61 km]: 39.933 ms Testing download speed................................................................................ Download: 604.67 Mbit/s Testing upload speed...................................................................................................... Upload: 522.55 Mbit/s
Whoa! How am I supposed to take on Netflix and Youtube with that? Well, thankfully the ‘free’ software and FOSS out there does a lot of the work for us.
-
Software
I run a pretty conventional, rather boring home server software stack used by many other home media enthusiasts (it runs on NixOS, but that’s not important right now):
Software Primary Function Additional Features Plex Organizes and streams your media library (movies, TV, music) User-friendly interface, remote access, transcoding, mobile apps Sonarr Automatically downloads and manages TV shows Renaming and organizing files, metadata fetching, integration with media servers (like Plex) Radarr Automatically downloads and manages movies Similar features to Sonarr, but focused on movies Bazarr Downloads and manages subtitles for your media library Subtitle format conversion, automatic subtitle matching, integration with Sonarr and Radarr Lidarr Automatically downloads and manages music Similar features to Sonarr/Radarr, focused on music, integration with media servers
Performance
So if the software is so great what is there left for us to do and why write this blog post? Well, given our previously-stated problems, we can’t exactly leave things as-is. Our users cannot simply play any media on our home server the way they can on Netflix or Youtube without encountering difficulties.
Transcoding
Can’t we transcode media streams on the fly to all these users? We have two GPUs after all, and Plex1 / Jellyfin2 enable transcoding by default!
Unfortunately, media stream transcoding limitations prevent us from relying on it here:
- Even more modern GPUs than ours only support a handful of concurrent transcoding processes at a time.3 Anything else would need to be done by the already-stressed CPU, potentially making the entire server less responsive.
- Transcoding can be an error-prone and unreliable process4 which leads to many users encountering transcoding or conversion errors when attempting to play videos.
- Transmitting transcoded media streams can actually increase the network bandwith usage compared to sending the raw stream depending on the output stream attributes.
Resource Contention
Given our limited resources there’s bound to be resource contention once we attempt to service our total number of users simultaneously. To test this, I attempted to play an average, legally obtained video file on my plex server in 20 browser tabs and monitored the CPU, GPU and network load during that time:
(Imagine 20 of these)
As we can see from the above charts, our key resources are completely exhausted. During this time we’ve occupied >100% of the available concurrent sessions for NVENC GPU-accelerated transcoding and those workloads have began spilling over onto the CPU. All of my video streams are stuck buffering, transcoder progress is slow and the Plex UI as well as the other services on my home media server have become unresponsive. Maybe we can’t compete with Netflix and Hulu after all?
Solution
Our solution is the single best optimization I’ve come across for home media streaming servers: ahead-of-time convert all our media for maximum Direct Play compatibility.
Default Path | Happy Path |
---|---|
Direct Play
Direct Play is a scenario where our media files can be streamed to our users without any modification by the streaming service. This means we can remove all the expensive overhead and any chance of failures of on-the-fly transcoding except for when it is absolutely needed.
This scenario has the following requirements for the streamed video file:5
- Is stored in a compatible file container
- Is encoded in a compatible bitrate
- Is encoded with compatible codecs
- Is a compatible resolution
-
Direct Play Compatibility
So how do we ensure that all users meet with this scenario every video? We can convert all our media well in advance of when they’re played to make sure each of the four requirements are satisfied. We can do this no matter what device or app they’re playing their media on (so long as we know their Direct Play requirements). Here’s a visualization of the conversion steps needed to accomplish this for all Google Chromecast devices:6
direct-play-nice
In order to make it easier to ahead-of-time convert all media to Direct Play Compatible video formats, I created a CLI utility, written in Rust, called direct-play-nice which uses FFmpeg to convert any input video file to one with video, audio and subtitle streams which satisfy the Direct Play requirements of all the streaming devices you specify (or you can also specify ‘all’ for the --streaming-devices
argument):
Usage: direct_play_nice.exe [OPTIONS] <INPUT_FILE> <OUTPUT_FILE>
Arguments:
<INPUT_FILE> Video file to convert
<OUTPUT_FILE> Our output direct-play-compatible video file
Options:
-s, --streaming-devices <STREAMING_DEVICES> List of StreamingDevice
-c, --config-file <CONFIG_FILE> Path to the configuration file
-h, --help Print help
-V, --version Print version
By converting our media files to Direct-Play-compatible versions we ensure that the Direct Play scenario is encountered by every user each time they play any media on our streaming server.
-
Sonarr/Radarr integration
This program also comes out-of-the-box with Sonarr / Radarr integration. See the docs for adding it as a Custom Script Connection.
-
Why not just use the conversion feature of Plex / Jellyfin?
All my users never want to think about the actual video files or streams they’re watching. All they care about is hitting the play button and getting HQ video to play uninterrupted. Most would give up without even selecting a different version of the video, since that’s a lot less intuitive than selecting the quality of the video.
Performance
When running the same test we did previously, except now with the video converted to be Direct-Play-compatible, we get a significantly improved performance profile although the quality has not changed:
(Imagine 20 of these)
During this test my 20 streaming video sessions were able to play just fine with no load on the GPUs (allowing for ad hoc transcoding jobs when needed), all my service web GUIs remain responsive and even my pitiful network upload speed limit was not reached. Maybe we can compete with those premium video streaming services after all!
Conclusion
By making use of ahead-of-time video conversion to ensure we guarantee Direct Play scenarios for all our users, we’re able to provide premium video streaming service quality to 20+ concurrent users using only an old gaming PC, gratis / free software and a slightly above-average metropolitan home broadband internet connection.
-
https://support.plex.tv/articles/200250377-transcoding-media/ ↩︎
-
https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new#Encoder ↩︎
-
https://forums.developer.nvidia.com/t/ffmpeg-transcoding-processes-are-stuck-for-long-time-making-gpu-unusable/69116/2 ↩︎
-
https://support.plex.tv/articles/200250387-streaming-media-direct-play-and-direct-stream/ ↩︎