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:

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.

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:

  1. 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.
  2. Transcoding can be an error-prone and unreliable process4 which leads to many users encountering transcoding or conversion errors when attempting to play videos.
  3. 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

  1. Is stored in a compatible file container
  2. Is encoded in a compatible bitrate
  3. Is encoded with compatible codecs
  4. Is a compatible resolution

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.

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.