Skip to content

Instantly share code, notes, and snippets.

@tuetenk0pp
Last active February 22, 2024 06:16
Show Gist options
  • Save tuetenk0pp/516dce7aa4951e029ee64403191ac6f2 to your computer and use it in GitHub Desktop.
Save tuetenk0pp/516dce7aa4951e029ee64403191ac6f2 to your computer and use it in GitHub Desktop.
Media Workflow for Jellyfin

Media Workflow for Jellyfin

I thought that it would be nice to share this not only because it would save others a lot of time figuring out this mess. So if you find something that could be improved or is just wrong, I will happily update this post.

Navigation

Introduction

I spent a lot of time reading all sorts of different blogs, reddit posts, guides etc. and came up with this Media Workflow that I will use from now on to rip and encode my media for use with the wonderfull Jellyfin Media Server. I had three main criteria when doing my research:

  1. Since I don't have infinite space on my server the file size has to be reasonable.
  2. I want to keep as much quality and detail as possible while not breaking criteria number one.
  3. The media has to be compatible for direct play with the most, if possible all devices through one or more clients.

A note on Dolby Atmos and Dolby Vision. While it is technically possible to put those into a .mp4 container, I was not able to get it to work. Also, Dolby Atmos is often uncompressed and I could not find a way to compress it whithout losing the Dolby Atmos black magic. Today, this is not a concern for me but I would like to make my media future proof so that when the day comes, I have media that supports dynamic HDR metadata and object based audio.

Before we start: I recommend you use Windows for this as it probably is the easier to install and use the software on we are going to use. Not hating Linux here, I'm into it myself.

Step One: MakeMKV

Not much to say here other than please, buy a key! Once you have the software installed, insert a disc and select the files you want to rip. When selecting audio tracks look for terms like True-HD or DTS as this will give you the best possible audio most of the time (reddit post). Use the default profile and rip to a .mkv conainer.

If you have trouble opening an UHD Blu-ray with MakeMKV you might want to look at this forum post and find someone who would sell you a so called UHD Friendly Drive on eBay. Alternatively, if you find your drives model number on the Ultimate UHD Drives Flashing Guide and you feel lucky, you can flash it with a custom firmware that would enable MakeMKV to read any disc. I have had success in flashing an LG-BU40N drive inside a TechPulse120 disc enclosure.

Step Two: Subtitles and Container Optimisation

MKVToolNix

kakarroto007 on reddit suggested I include MKVToolNix in this. It essentially gives you the opportunity to edit what's the default audio or subtitle track plus some other nice features to make your media behave exactly like you would expect it to when you press play. Definitely do this before step three as this software doesn't support mp4 containers.

Subtitle Edit

Skip this part if you don't care about subtitles or you use Open Subtitles.

Use Subtitle Edit to extract Subtitles from your .mkv file and save it as MEDIAFILENAME.LANGUAGECODE.srt. Make sure that you select Tesseract as OCR method and download the nessesary libraries. You might want it to promt you for words it doesn't know for the first files. I found it really reliable but it sometimes needs help when processing italic text.

Step Three: Encoding

You will have to install Handbrake, FastFlix and hdr10plus_tool in order to follow. ffmpeg comes installed with FastFlix I believe.

I use 10-bit encoding on everything. Chris Griffith explains this very well in one of his articles (link to his source). While it doesn't save time, it does save disk space. I don't use hardware encoders for the same reason (for one exception). Downside is, software encoding is very slow. We can save a bit of time by avoiding x265 if possible: DVDs and Blu-rays are AVC (H.264) video, so there is no real benefit from using the x265 encoder. UHD Blu-ray and therefore HDR media uses HEVC (H.265), so defenetly use the x265 encoder here. I prefer crf over constant bitrate, since the bitrate needed to keep the quality on a constant level varies a lot. I chose a crf setting of 22 with the slow preset for all encodings. You might want to choose a lower setting like 20 or 18 if you see a difference.

I also compress audio to aac to save on disk space. It's not like I can hear the difference between lossless and a 512 bit/s stream. Sure I lose Dolby Atmos or DTS:X but 7.1 is still pretty good.

I did a few testruns with short cuts from MakeMKV rips and was able to shrink the filesize down to about 20 %.

If you have a DVD or Blu-ray source, jump to the Handbrake profile. If not, read further.

UHD Blu-ray

The next step depends on the source. We need to know two things: 1. What resolution was the source mastered at? 2. Does it contain HDR10+ or Dolby Vision metadata?

Master Format

find out what resolution your media was mastered at. Look at the Technical Specifications section on the corresponding IMDb page. At Cinematographic Process you will find a note that says 2k or 4k master format.

But why should I buy an UHD Blu-ray if it's upscaled anyway?

While an UHD Blu-ray with a 2k master is only upscaled to 4k, it still is of better quality than the standard Blu-ray. Even after reencoding in 2k resolution. The UHD Blu-ray specifikation requires:

  • at least HDR10, most newer movies have HDR10+ and Dolby Vision metatdata included
  • 10-bit color space means about 30 % bigger color gamut
  • support for 60 frames per second

HDR10+ Metadata

Personally, I find it easier to do this through WSL. If you are already used to it, I assume you know how to install packages manually and add them to the PATH variable.

To find out if your media has HDR10+ metadata, you need to install hdr10plus_tool. Make sure that you add it to the PATH variable (how to do I do this?). If you already installed FastFlix, it should also have installed ffmpeg for you. If not, install manually and also don't forget to add it to the PATH variable (guide).

From now on, this section follows this guide by Chris Griffith.

Now that you have everything you need to find out if your media has HDR10+ metadata, open a Command Promt Window (I like to use Windows Terminal) and run this command:

ffmpeg -loglevel panic -i FILENAMEHERE.mkv -c:v copy -vbsf hevc_mp4toannexb -f hevc - | hdr10plus_tool --verify extract -

Another way to find out if your media has HDR10+ metadata is to search it on blu-ray.com. Although you can't know for sure unless you ask the command line. Now choose your preset unless you are interested in Dolby Vision.

Dolby Vision Metadata

I know, ffmpeg 5.0+ can do Dolby Vision but only through the -v:c copy option which will leave the video stream as is and does not copy the enhancement layer (which is the whole point since it alone contains all the dynamic metadata. Read the Dolby Vision Specifications). After all, I am going through all this effort, because I want to reduce filesize. But there is another way. First, find out if your media contains Dolby Vision layers. Mediainfo is a great tool for that.

mediainfo FILENAMEHERE.mkv

This will produce something like this (I have shortened it to the relevant information):

General
Unique ID                                : 235157854891415310469122651174257647686 (0xB0E9C16478B8E96A4E98F0CC0FDD6046)
Complete name                            : Dune_t00.mkv
Format                                   : Matroska
Format version                           : Version 2
File size                                : 73.5 GiB
Duration                                 : 2 h 35 min
Overall bit rate mode                    : Variable
Overall bit rate                         : 67.7 Mb/s
Movie name                               : Dune
Encoded date                             : UTC 2022-02-03 12:54:58
Writing application                      : MakeMKV v1.16.5 win(x64-release)
Writing library                          : libmakemkv v1.16.5 (1.3.10/1.5.2) win(x64-release)

Video
ID                                       : 1
ID in the original source medium         : 4113 (0x1011)
Format                                   : HEVC
Format/Info                              : High Efficiency Video Coding
Format profile                           : Main 10@L5.1@High
HDR format                               : Dolby Vision, Version 1.0, dvhe.07.06, BL+EL+RPU, Blu-ray compatible / SMPTE ST 2086, HDR10 compatible
Codec ID                                 : V_MPEGH/ISO/HEVC
Duration                                 : 2 h 35 min
Bit rate                                 : 59.8 Mb/s
Width                                    : 3 840 pixels
Height                                   : 2 160 pixels
Display aspect ratio                     : 16:9
Frame rate mode                          : Constant
Frame rate                               : 23.976 (24000/1001) FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0 (Type 2)
Bit depth                                : 10 bits
Bits/(Pixel*Frame)                       : 0.300
Stream size                              : 64.9 GiB (88%)
Language                                 : English
Default                                  : No
Forced                                   : No
Color range                              : Limited
Color primaries                          : BT.2020
Transfer characteristics                 : PQ
Matrix coefficients                      : BT.2020 non-constant
Mastering display color primaries        : Display P3
Mastering display luminance              : min: 0.0050 cd/m2, max: 4000 cd/m2
Maximum Content Light Level              : 787 cd/m2
Maximum Frame-Average Light Level        : 239 cd/m2
Original source medium                   : Blu-ray

Notice the line HDR format. This tells us that this container has indeed Dolby Vision with a base layer (BC), enhancement layer (EL) and RPU. And also that it is Profile 7. I found this article pretty helpful. You can always refer to the Dolby Vision Specifications. About dvhe.07.06 it says: This string represents a dual-layer Blu-ray HDR10 compatible Dolby Vision stream encoded as 10-bit HEVC video with a pixel rate that does not exceed 299,065,600 pixels/sec (for example, 3840 × 2160 at 24 fps).

Choose Your Preset

Step Four: Library Organisation

Organise your media according to the Jellyfin Documentation and don't forget to include your subtitle files.

Step Five: Configure Jellyfin 'The Right Way'

  • to reduce server load: configure Hardware Acceleration
  • use the right clients: use clients that are compatible with your media so that your server doesn't have to transcode in the first place
  • disallow transcoding for certain users. Find this setting in Administration -> Users -> USERNAME -> Media Playback. I don't know how that plays out for tone mapping...

Jellyfin only supports HDR10 tone mapping. What about HDR10+?

A video stream can only be either SDR or HDR. If it's HDR, it's HDR10. HDR10+ video is simply HDR10 video with HDR10+ metadata. So no difference in the video itself. This means that if the client cannot display HDR10, it won't most certainly do nothing with any HDR related metadata. So, as long as either Jellyfin or your client takes care of tone mapping, it will be fine.

A note on tone mapping: I have tried different ways to do this with MPV (Jellyfin Media Player is essentially the same thing whith the web client strapped to it) but they all look like poop. Pink spots everywhere. So now I keep one SDR and one HDR version of a movie (UHD Blu-rays often include a regular Blu-ray). Thanks to TheLegendaryCat on Reddit for giving me a headsup on this. I compared a few encodes with the source mkv container and found that the Interlace recognition or Deinterlace in Handbrake must have caused this. These filters are set by default and I saw no reason to disable them. Well, now I know better. Now, without any tweaking, tone mapping looks good!

My client can't direct stream HEVC media. What about criteria number three, remember?

I was very specific about the word 'devices'. For example: I have a Samsung Tablet and the Android client is not compatible with HEVC media. I was able to solve this by changing a setting in the client settings. Whenever I play media through Jellyfin on my tablet now, it uses the tablet's media player, not the media player inside the Jellyfin Android client.

Final Step: Enjoy Your Media

Cheers!

Code Appendix

Handbrake Preset for DVD or Blu-ray

{
  "PresetList": [
    {
      "AlignAVStart": true,
      "AudioCopyMask": [],
      "AudioEncoderFallback": "av_aac",
      "AudioLanguageList": [
        "eng",
        "deu"
      ],
      "AudioList": [
        {
          "AudioBitrate": 0,
          "AudioCompressionLevel": 0,
          "AudioEncoder": "copy",
          "AudioMixdown": "none",
          "AudioNormalizeMixLevel": false,
          "AudioSamplerate": "auto",
          "AudioTrackQualityEnable": false,
          "AudioTrackQuality": 5,
          "AudioTrackGainSlider": 0,
          "AudioTrackDRCSlider": 0
        }
      ],
      "AudioSecondaryEncoderMode": true,
      "AudioTrackSelectionBehavior": "all",
      "ChapterMarkers": true,
      "ChildrenArray": [],
      "Default": true,
      "FileFormat": "av_mp4",
      "Folder": false,
      "FolderOpen": false,
      "Mp4HttpOptimize": true,
      "Mp4iPodCompatible": false,
      "PictureAutoCrop": true,
      "PictureBottomCrop": 42,
      "PictureLeftCrop": 0,
      "PictureRightCrop": 0,
      "PictureTopCrop": 42,
      "PictureDARWidth": 1998,
      "PictureDeblockPreset": "off",
      "PictureDeblockTune": "medium",
      "PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
      "PictureDeinterlaceFilter": "off",
      "PictureCombDetectPreset": "off",
      "PictureCombDetectCustom": "",
      "PictureDeinterlaceCustom": "",
      "PictureDenoiseCustom": "",
      "PictureDenoiseFilter": "off",
      "PictureDenoisePreset": "light",
      "PictureDenoiseTune": "none",
      "PictureSharpenCustom": "",
      "PictureSharpenFilter": "off",
      "PictureSharpenPreset": "medium",
      "PictureSharpenTune": "none",
      "PictureDetelecine": "off",
      "PictureDetelecineCustom": "",
      "PictureColorspacePreset": "off",
      "PictureColorspaceCustom": "",
      "PictureChromaSmoothPreset": "off",
      "PictureChromaSmoothTune": "none",
      "PictureChromaSmoothCustom": "",
      "PictureItuPAR": false,
      "PictureKeepRatio": true,
      "PictureLooseCrop": false,
      "PicturePAR": "auto",
      "PicturePARWidth": 180,
      "PicturePARHeight": 173,
      "PictureWidth": 1920,
      "PictureHeight": 1080,
      "PictureUseMaximumSize": true,
      "PictureAllowUpscaling": false,
      "PictureForceHeight": 0,
      "PictureForceWidth": 0,
      "PicturePadMode": "none",
      "PicturePadTop": 0,
      "PicturePadBottom": 0,
      "PicturePadLeft": 0,
      "PicturePadRight": 0,
      "PresetName": "DVD/Blu-ray",
      "Type": 1,
      "SubtitleAddCC": false,
      "SubtitleAddForeignAudioSearch": false,
      "SubtitleAddForeignAudioSubtitle": false,
      "SubtitleBurnBehavior": "none",
      "SubtitleBurnBDSub": false,
      "SubtitleBurnDVDSub": false,
      "SubtitleLanguageList": [],
      "SubtitleTrackSelectionBehavior": "none",
      "VideoAvgBitrate": 0,
      "VideoColorMatrixCode": 0,
      "VideoEncoder": "x264_10bit",
      "VideoFramerateMode": "vfr",
      "VideoGrayScale": false,
      "VideoScaler": "swscale",
      "VideoPreset": "slow",
      "VideoTune": "",
      "VideoProfile": "auto",
      "VideoLevel": "auto",
      "VideoOptionExtra": "",
      "VideoQualityType": 2,
      "VideoQualitySlider": 22,
      "VideoQSVDecode": true,
      "VideoQSVAsyncDepth": 0,
      "VideoTwoPass": true,
      "VideoTurboTwoPass": false,
      "x264UseAdvancedOptions": false,
      "PresetDisabled": false,
      "MetadataPassthrough": true
    }
  ],
  "VersionMajor": 47,
  "VersionMicro": 0,
  "VersionMinor": 0
}

Once you're done encoding, go to Step Four.

Handbrake Preset UHD Blu-ray, 2k Master, HDR10

{
  "PresetList": [
    {
      "AlignAVStart": true,
      "AudioCopyMask": [],
      "AudioEncoderFallback": "av_aac",
      "AudioLanguageList": [
        "eng",
        "deu"
      ],
      "AudioList": [
        {
          "AudioBitrate": 0,
          "AudioCompressionLevel": 0,
          "AudioEncoder": "copy",
          "AudioMixdown": "none",
          "AudioNormalizeMixLevel": false,
          "AudioSamplerate": "auto",
          "AudioTrackQualityEnable": false,
          "AudioTrackQuality": 5,
          "AudioTrackGainSlider": 0,
          "AudioTrackDRCSlider": 0
        }
      ],
      "AudioSecondaryEncoderMode": true,
      "AudioTrackSelectionBehavior": "all",
      "ChapterMarkers": true,
      "ChildrenArray": [],
      "Default": false,
      "FileFormat": "av_mp4",
      "Folder": false,
      "FolderOpen": false,
      "Mp4HttpOptimize": true,
      "Mp4iPodCompatible": false,
      "PictureAutoCrop": true,
      "PictureBottomCrop": 42,
      "PictureLeftCrop": 0,
      "PictureRightCrop": 0,
      "PictureTopCrop": 42,
      "PictureDARWidth": 1998,
      "PictureDeblockPreset": "off",
      "PictureDeblockTune": "medium",
      "PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
      "PictureDeinterlaceFilter": "off",
      "PictureCombDetectPreset": "off",
      "PictureCombDetectCustom": "",
      "PictureDeinterlaceCustom": "",
      "PictureDenoiseCustom": "",
      "PictureDenoiseFilter": "off",
      "PictureDenoisePreset": "light",
      "PictureDenoiseTune": "none",
      "PictureSharpenCustom": "",
      "PictureSharpenFilter": "off",
      "PictureSharpenPreset": "medium",
      "PictureSharpenTune": "none",
      "PictureDetelecine": "off",
      "PictureDetelecineCustom": "",
      "PictureColorspacePreset": "off",
      "PictureColorspaceCustom": "",
      "PictureChromaSmoothPreset": "off",
      "PictureChromaSmoothTune": "none",
      "PictureChromaSmoothCustom": "",
      "PictureItuPAR": false,
      "PictureKeepRatio": true,
      "PictureLooseCrop": false,
      "PicturePAR": "auto",
      "PicturePARWidth": 180,
      "PicturePARHeight": 173,
      "PictureWidth": 1920,
      "PictureHeight": 1080,
      "PictureUseMaximumSize": true,
      "PictureAllowUpscaling": false,
      "PictureForceHeight": 0,
      "PictureForceWidth": 0,
      "PicturePadMode": "none",
      "PicturePadTop": 0,
      "PicturePadBottom": 0,
      "PicturePadLeft": 0,
      "PicturePadRight": 0,
      "PresetName": "UHD Blu-ray 2k Master HDR10",
      "Type": 1,
      "SubtitleAddCC": false,
      "SubtitleAddForeignAudioSearch": false,
      "SubtitleAddForeignAudioSubtitle": false,
      "SubtitleBurnBehavior": "none",
      "SubtitleBurnBDSub": false,
      "SubtitleBurnDVDSub": false,
      "SubtitleLanguageList": [],
      "SubtitleTrackSelectionBehavior": "none",
      "VideoAvgBitrate": 0,
      "VideoColorMatrixCode": 0,
      "VideoEncoder": "x265_10bit",
      "VideoFramerateMode": "vfr",
      "VideoGrayScale": false,
      "VideoScaler": "swscale",
      "VideoPreset": "slow",
      "VideoTune": "",
      "VideoProfile": "auto",
      "VideoLevel": "auto",
      "VideoOptionExtra": "",
      "VideoQualityType": 2,
      "VideoQualitySlider": 22,
      "VideoQSVDecode": true,
      "VideoQSVAsyncDepth": 0,
      "VideoTwoPass": false,
      "VideoTurboTwoPass": false,
      "x264UseAdvancedOptions": false,
      "PresetDisabled": false,
      "MetadataPassthrough": true
    }
  ],
  "VersionMajor": 47,
  "VersionMicro": 0,
  "VersionMinor": 0
}

Once you're done encoding, go to Step Four.

Handbrake Preset UHD Blu-ray, 4k Master, HDR10

A note on this one: I tend to use hardware encoding with 4k media despite the larger filesize. It just takes too long with software encoding on my hardware.

{
  "PresetList": [
    {
      "AlignAVStart": true,
      "AudioCopyMask": [],
      "AudioEncoderFallback": "av_aac",
      "AudioLanguageList": [
        "eng",
        "deu"
      ],
      "AudioList": [
        {
          "AudioBitrate": 0,
          "AudioCompressionLevel": 0,
          "AudioEncoder": "copy",
          "AudioMixdown": "none",
          "AudioNormalizeMixLevel": false,
          "AudioSamplerate": "auto",
          "AudioTrackQualityEnable": false,
          "AudioTrackQuality": 5,
          "AudioTrackGainSlider": 0,
          "AudioTrackDRCSlider": 0
        }
      ],
      "AudioSecondaryEncoderMode": true,
      "AudioTrackSelectionBehavior": "all",
      "ChapterMarkers": true,
      "ChildrenArray": [],
      "Default": false,
      "FileFormat": "av_mp4",
      "Folder": false,
      "FolderOpen": false,
      "Mp4HttpOptimize": true,
      "Mp4iPodCompatible": false,
      "PictureAutoCrop": true,
      "PictureBottomCrop": 42,
      "PictureLeftCrop": 0,
      "PictureRightCrop": 0,
      "PictureTopCrop": 42,
      "PictureDARWidth": 3840,
      "PictureDeblockPreset": "off",
      "PictureDeblockTune": "medium",
      "PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
      "PictureDeinterlaceFilter": "off",
      "PictureCombDetectPreset": "off",
      "PictureCombDetectCustom": "",
      "PictureDeinterlaceCustom": "",
      "PictureDenoiseCustom": "",
      "PictureDenoiseFilter": "off",
      "PictureDenoisePreset": "light",
      "PictureDenoiseTune": "none",
      "PictureSharpenCustom": "",
      "PictureSharpenFilter": "off",
      "PictureSharpenPreset": "medium",
      "PictureSharpenTune": "none",
      "PictureDetelecine": "off",
      "PictureDetelecineCustom": "",
      "PictureColorspacePreset": "off",
      "PictureColorspaceCustom": "",
      "PictureChromaSmoothPreset": "off",
      "PictureChromaSmoothTune": "none",
      "PictureChromaSmoothCustom": "",
      "PictureItuPAR": false,
      "PictureKeepRatio": true,
      "PictureLooseCrop": false,
      "PicturePAR": "auto",
      "PicturePARWidth": 1,
      "PicturePARHeight": 1,
      "PictureWidth": 3840,
      "PictureHeight": 2160,
      "PictureUseMaximumSize": true,
      "PictureAllowUpscaling": false,
      "PictureForceHeight": 0,
      "PictureForceWidth": 0,
      "PicturePadMode": "none",
      "PicturePadTop": 0,
      "PicturePadBottom": 0,
      "PicturePadLeft": 0,
      "PicturePadRight": 0,
      "PresetName": "UHD Blu-ray 4k Master HDR10",
      "Type": 1,
      "SubtitleAddCC": false,
      "SubtitleAddForeignAudioSearch": false,
      "SubtitleAddForeignAudioSubtitle": false,
      "SubtitleBurnBehavior": "none",
      "SubtitleBurnBDSub": false,
      "SubtitleBurnDVDSub": false,
      "SubtitleLanguageList": [],
      "SubtitleTrackSelectionBehavior": "none",
      "VideoAvgBitrate": 0,
      "VideoColorMatrixCode": 0,
      "VideoEncoder": "qsv_h265_10bit",
      "VideoFramerateMode": "vfr",
      "VideoGrayScale": false,
      "VideoScaler": "swscale",
      "VideoPreset": "quality",
      "VideoTune": "",
      "VideoProfile": "auto",
      "VideoLevel": "auto",
      "VideoOptionExtra": "",
      "VideoQualityType": 2,
      "VideoQualitySlider": 22,
      "VideoQSVDecode": true,
      "VideoQSVAsyncDepth": 0,
      "VideoTwoPass": false,
      "VideoTurboTwoPass": false,
      "x264UseAdvancedOptions": false,
      "PresetDisabled": false,
      "MetadataPassthrough": true
    }
  ],
  "VersionMajor": 47,
  "VersionMicro": 0,
  "VersionMinor": 0
}

Once you're done encoding, go to Step Four.

FastFlix UHD Blu-ray, any Master, HDR10+

Unfortunately, I have found no 'nice' way (%APPDATA%\FastFlix\fastflix.yaml) of exporting and importing presets in FastFlix, so you will have to make and save your own preset.

  • Set the encoder to x265.
  • Dependent on the master format, set the resolution width to 1920 or 3840 but keep the aspect ratio.
  • In the Quality tab
    • slow preset
    • 10-bit bit depth
    • CRF 22
    • check HDR10+ Optimizations
    • browse your metadata.json file
  • In the Advanced tab: set HDR -> SDR Tone Map to none
  • Select audio tracks and disable subtitles.

Once you're done encoding, go to Step Four.

UHD Blu-ray, any Master, Dolby Vision

Please refer to Chris Griffith's Guide on how to do this. I have chosen not to do this and spend my time on something else.

Once you're done encoding, go to Step Four.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment