Skip to content

andrasmaroy/Tdarr_Plugins_DoVi

 
 

Repository files navigation

Tdarr_Plugins_DoVi

Tdarr plugins expanded with the ability to transcode Dolby Vision videos and remux them into MP4s compatible with LG TVs.

Why

LG WebOS doesn't support playing back Dolby Vision content from mkv containers, only from mp4. This could be simply solved by remuxing with ffmpeg using the -strict unofficial flag as of version 6.0. But I also wanted to downscale the videos to 1080p to save space as I don't care about 4k, but most Dolby Vision content is only available in 4k.

How it works

One cannot simply transcode the video stream to achieve this like with SDR content (or with HDR10 for that matter).

For Dolby Vision profile 7 is not supported at the moment, for profile 4, 5 and 8 the process is the following:

graph TD
A[Input File] --> B[Extract raw HEVC stream]
B --> C[Extract Dolby Vision RPU]
B --> D[Transcode video stream]
C & D --> E[Inject Dolby Vision RPU into transcoded video]
E --> F[Package raw stream into MP4]
A & F --> G[Remux with audio from original file]

Basically the Dolby Vision metadata has to be extracted first, then added back onto the transcoded video stream. This process can be done with profile 7 as well, it just involved some extra steps, but most content is not in profile 7.

This process is based on the excellent tool and writeup by @gacopl: dvmkv2mp4

Extracting the stream

The Extract Streams plugin is responsible for this step. It will extract the HEVC stream and save it in the working directory with the same name as the original file but with .hevc extension. It will also extract all ASS and SRT subtitles and save them as .srt files in the sub_streams folder in the working directory for later use. The subtitle files will be annotated with their metadata (language, forced, sdh, default) that will be picked up by Jellyfin. The plugin also supports filtering subtitles based on language.

Important: to reorder streams before this plugin and have the video stream as the last stream. This is because all the subtitle streams have their own outputs defined in the middle of the ffmpeg command. Tdarr handles streams in order when building the ffmpeg command parameters and the video stream if not the last will not be matched to the proper output.

Example command
tdarr-ffmpeg -y \
    -i /path/to/input.mkv \
    -map 0:3 -c:s:0 copy /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/sub_streams/2.hun.default.forced.srt \
    -map 0:4 -c:s:0 copy /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/sub_streams/3.hun.srt \
    -map 0:5 -c:s:0 copy /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/sub_streams/4.eng.srt \
    -map 0:6 -c:s:0 copy /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/sub_streams/5.eng.sdh.srt \
    -map 0:0 -c:v copy -bsf:v hevc_mp4toannexb /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332450149/input.hevc

Extracting Dolby Vision RPU

The Extract DoVi RPU plugin is responsible for extracting Dolby Vision RPU data. To achieve this dovi_tool is needed. The plugin will extract the RPU data from the HEVC stream and save it to the working directory as a .rpu.bin file for later use.

Example command
/usr/local/bin/dovi_tool \
    -c \    # Crop, remove letterbox
    -m 2 \  # Mode 2, converts the RPU to be profile 8.1 compatible.
    extract-rpu /shows/Transcode/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332450149/input.hevc
    -o /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/dovi_tool/input.rpu.bin

Transcoding video stream

I'm using Intel Kaby Lake CPUs (7th gen) to transcode video. There's one caveat I discovered here: while regular videos have no issues playing back if transcoded using quality based rate control methods supported by Intel Quicksync (CQP, ICQ) (ffmpeg documentation) when used on Dolby Vision content it will result in a playback error. Thus for this purpose I have to fall back on good old VBR ratecontrol. I found that Netflix is using ~5Mbps for streaming 1080p Dolby Vision content, so I've set the same.

Example command
tdarr-ffmpeg -y \
    -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -hwaccel qsv
    -i /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332450149/input.hevc \
    -map 0:0 -c:0 hevc_qsv \
    -qp 22 \    # This is ignored by the hevc_qsv encoder, but tdarr puts it in either way
    -preset slow \  # Slow preset provides a good enough quality while not taking 3 and a half decades
    -vf scale_qsv=1920:-1 \ # Set resolution and calculate height to handle 16:9 and letterbox videos as well
    -pix_fmt + \    # Keep the same pixel format as the input
    -look_ahead_depth 100 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1 \   # QSV specific parameters, some of these are probably ignored when using VBR
    -b:v 5M \   # Target an average of 5M bitrate
    /shows/Transcode/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332520936/input.hevc

Injecting RPU into the transcoded stream

The Inject DoVi RPU plugin is responsible for injecting the RPU data back into the video stream. The process is the reverse of extracting Dolby Vision RPU using dovi_tool again. Inject the previously saved RPU data from the .rpu.bin file, injects it into the transcoded stream from the previous step and save the resulting video stream as an .rpu.hevc file in the workspace.

Example command
/usr/local/bin/dovi_tool \
    -i /shows/Transcode/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332520936/input.hevc \ # Transcoded video from previous step
    --rpu-in /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/dovi_tool/input.rpu.bin \
    extract-rpu /shows/Transcode/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710332450149/input.hevc \
    -o /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333079164/input.rpu.hevc

Package HEVC stream in MP4

For better compatibility I use MP4Box to package the stream into an mp4 container. Anecdotally MP4Box handles this better than ffmpeg, at least at the time of writing. This is handled by the Package DoVi mp4 plugin. This resulting mp4 will only have the video stream in it, it will be used in the next step along with the audio streams from the input file to remux into the final form.

Example command
/usr/local/bin/MP4Box \
    -add /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333079164/input.rpu.hevc:dvp=8.1:xps_inband:hdr=none \
    -tmp /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333091979/tmp \
    -brand mp42isom \
    -ab dby1 \
    -no-iod \
    -enable 1 \
    /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333091934/input.rpu.hevc.mp4

Remuxing into the final MP4

The Remux DoVi MP4 plugin is responsible for creating the right ffmpeg arguments to mux the correct streams together. The plugin will take the video stream from the mp4 file created in the previous step and mux it together with the audio streams from the original file. There are a couple of extra steps involved here: TrueHD audio streams are dropped as ffmpeg support for them in mp4 containers is experimental and I couldn't get it to work properly. Also metadata is copied from the original file.

Stream titles are handled differently in mp4 containers than in mkv, to deal with this the original audio stream titles are mapped to the handler_name metadata tag in the output file as Jellyfin will use that to read stream titles as a fallback.

Chapter data is copied as part of the metadata, but chapter titles are dropped as it will result in a data stream in the output file which I found to cause playback issues.

Example command
tdarr-ffmpeg -y \
    -i /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333091934/input.rpu.hevc.mp4 \
    -i /path/to/input.mkv \ # Original file
    -map 0:0 -c:0 copy \    # Copy video from the previous mp4
    -map 1:a -c:a copy \    # Copy audio from original file
    -map_metadata 1 \       # Copy metadata from original file
    -map_metadata:c -1 \    # Drop chapter titles
    -dn \                   # Drop data streams
    -movflags +faststart \  # Add fast start flag, not strictly necessary
    -strict unofficial \    # Dolby Vision support is behind this flag
    /temp/tdarr-workDir-node-J2D7FNt5O-worker-open-ox-ts-1710332442638/1710333113706/input.mp4

Other plugins

Parse file with Radarr or Sonarr

The Parse file with Radarr or Sonarr plugin is used to get additional info about the input file from Sonarr / Radarr. The library is handled by these two, so it's safe to assume they will have additional info about the files.

The plugin will save the info in a file named arr.json in the working directory.

{
  "fileId": 1234,
  "originalPath": "/path/to/input.mkv",
  "releaseGroup": "AWESOMERLS",
  "sceneName": "My.Input.Movie.SceneName.2042.1080p.BluRay.x264-AWESOMERLS"
}

Configuration

Use library variable to provide configuration data for this plugin.

arr_api_key: y42k7kjg5f3htd6afktixqd8e5he2n84
arr_host: http:https://radarr.media.svc.cluster.local:7878
arr: radarr

These can be referenced in the plugin input like this: {{{args.userVariables.library.arr_host}}}

Wait for rename from Radarr or Sonarr

The Wait for rename from Radarr or Sonarr plugin is responsible for checking in with Sonarr or Radarr to check if the transcoded file was imported properly. The purpose of this is to set the output path of the plugin flow in Tdarr to the path *arr imported the file to as they are handling naming for library files, Tdarr has no way of knowing the resulting filename. It will wait until the path *arr is reporting for the file is changed from the original path was, as read from arr.json.

It will try a certain number of times and wait for a set time between tries, both can be configured. It also support path mapping as the path known to *arr might be different than what Tdarr is using, this can be configured using the path_mapping_from (path used by *arr) and path_mapping_to (path used by Tdarr) parameters.

Same configuration recommendation goes as for Parse file with Radarr or Sonarr.

Move Folder Content to Blachole

The Move Folder Content to Blachole plugin is responsible for moving the given file and previously extracted subtitle streams from sub_streams into the specified blackhole folder. It will also grab all files matching the specified extension (.srt for example) in the original folder and copy those to the blackhole folder as well. This is important in the case of handling mp4 files for example which would loose the associated external subtitles unless copied.

It will also change the release group on the file to TDARR, this is useful for making sure *arr always imports these files by setting it with a higher custom score.

The goal of this plugin is to put all the appropriate content in a folder watched by a blackhole downloader of Sonarr or Radarr.

For this to work the following is needed on *arr side:

  1. A blackhole downloader watching the folder set as the output folder of this plugin
  2. A custom format matching the TDARR release group
  3. The Tdarr custom format configured with a positive score value in the quality profile used

When all of this is configured, Tdarr will move the final file and appropriate additional files into the blackhole folder, *arr will import it like it was freshly downloaded and place it in the library with the proper naming scheme.

Check HDR type

The Check HDR type plugin is an extended version of the original Check HDR Video plugin, supporting determining the HDR standard used by the file, not just if it is HDR or not.

Supports: Dolby Vision, HDR10+, HDR10 and SDR.

Check DoVi Profile

The Check DoVi Profile plugin is responsible for determining the Dolby Vision profile of the given file. As mentioned in the How it works section this setup doesn't support profile 7, so it is important to determine the profile used by the file. Check DoVi profile

Remuxing Dolby Vision content without transcoding

The Remux DoVi MP4 plugin can also handle remuxing an input mkv file into a playable mp4 file without transcoding the video. Use the Set Container plugin before to set the container to mp4 then this plugin to remux. Same as previously TrueHD audio streams are dropped and metadata is copied and mapped for audio streams.

I recommend to extract subtitle streams beforehand to keep them using the blackhole plugin.

Example command
tdarr-ffmpeg -y \
    -i /path/to/input.mkv \
    -map 0:0 -c:0 copy \
    -map 0:1 -c:1 copy \
    -map 0:2 -c:2 copy \
    -map_metadata 0 \
    -map_metadata:c -1 \
    -bsf:v hevc_mp4toannexb \
    -dn \
    -movflags +faststart \
    -strict unofficial \
    /temp/tdarr-workDir-node-J2D7FNt5O-worker-mean-moose-ts-1710205452909/1710205516754/input.mp4

Using this in Tdarr

Getting plugins from this repo

In order the use the plugins from this repository simply change the Community plugins repository option to https://github.com/andrasmaroy/Tdarr_Plugins_DoVi/archive/master.zip in the Tdarr options tabs then restart the Tdarr server then the nodes as well. On startup the server will pull the plugins from the given repository and the nodes when starting will pull the plugins from the server. Check the server logs before restarting the nodes to make sure the server finished updating the plugins.

Using dovi_tool and MP4Box

MP4Box and dovi_tool are not part of the official Tdarr image, to solve this I've extended the official image with these tools. It is available at packages.

Tdarr flow

Everything discussed above comes together in a Tdarr flow. My setup is available exported as flow.json.

The flow on the high level is setup like so:

flowchart LR
   a[Input File] --> IsHDR{IsHDR}
   IsHDR --> |Dolby Vision| DVR
   IsHDR --> |HDR10| codec
   IsHDR --> |HDR| codec
   subgraph Dolby Vision
   DVR{Resolution}
   container{Container}
   DVR --> |At or below 1080p| container
   DVR --> |Above 1080p| Transcode --> Remux
   container --> |mkv| Remux
   end
   subgraph Regular
   codec{Is HEVC}
   codec --> |Yes| res{Resolution}
   codec --> |No| tc[Transcode]
   res --> |Above 1080p| tc
   end
   res --> |At or below 1080p| skip[Nothing to do]
   container --> |mp4| skip
   Remux & tc --> blackhole[Move to blackhole]

References

Original readme

Tdarr_Plugins

Visit the docs for more info: https://docs.tdarr.io/docs/plugins/basics

Development

Make sure NodeJS v16 is installed

Install dependencies:

npm install

Run ESLint:

npm run lint:fix

Check plugins using some extra custom rules:

npm run checkPlugins

Run tests:

npm run test

Steps to write a Tdarr Flow plugin:

  1. Clone this repo
  2. Set env variable pluginsDir to the location of the plugins repo and run Tdarr Server and Node. E.g. export pluginsDir=C:/Tdarr_Plugins
  3. Browse the typescript plugins here https://github.com/HaveAGitGat/Tdarr_Plugins/tree/master/FlowPluginsTs/CommunityFlowPlugins and make edits locally or create a new one locally:
  4. Make sure typescript is intalled with npm i -g typescript then run tsc to compile the changes.
  5. Refresh the browser and Tdarr will pick up the changes

About

Tdarr Plugins for transcoding Dolby Vision content

Topics

Resources

License

Stars

Watchers

Forks

Languages

  • JavaScript 80.5%
  • TypeScript 19.4%
  • Dockerfile 0.1%