Last active
May 9, 2022 15:21
-
-
Save stanionascu/aa25d96f23da0a0fed2cdb406697818b to your computer and use it in GitHub Desktop.
MKV to DolbyVision MP4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import subprocess | |
import json | |
from pathlib import Path | |
CODEC_TO_EXT = { | |
'MPEG-H/HEVC/h.265': 'h265', | |
'VC-1': 'vc1', | |
'AC-3': 'ac3', | |
'TrueHD Atmos': 'atmos', | |
'HDMV PGS': 'pgs', | |
'DTS-HD Master Audio': 'dts' | |
} | |
def track_name(track): | |
t_id = track['id'] | |
t_lang = track['properties']['language'] | |
t_codec = track['codec'] | |
return '{0}_{1}.{2}'.format(t_id, t_lang, CODEC_TO_EXT[t_codec]) | |
def make_extract_tracks_cmd(in_file, tracks): | |
cmd = ['mkvextract', in_file, 'chapters', 'chapters.xml', 'tracks'] | |
for track in tracks: | |
t_id = track['id'] | |
cmd += ['{}:{}'.format(t_id, track_name(track))] | |
return cmd | |
def make_mux_cmd(out_file, tracks): | |
cmd = ['mp4muxer', '--dv-profile', '7', '--output-file', out_file] | |
# video tracks are first, sort them by size? | |
for track in filter(is_video, tracks): | |
cmd += ['--input-file', track_name(track)] | |
# then all ac3 tracks | |
for track in filter(is_audio, tracks): | |
t_lang = track['properties']['language'] | |
cmd += ['--input-file', track_name(track), | |
'--media-lang', t_lang] | |
return cmd | |
def is_video(track): | |
return track['type'] == 'video' | |
def is_audio(track): | |
return track['type'] == 'audio' | |
def is_lang(track, languages): | |
return ('properties' in track and | |
track['properties']['language'] in ['und'] + languages) | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('input', nargs=1, type=str) | |
parser.add_argument('--keep-audio-lang', nargs='+', default=None) | |
cmd = parser.parse_args() | |
for file in cmd.input: | |
info = subprocess.run(['mkvmerge', '-J', file], capture_output=True) | |
if info.returncode == 0: | |
info = json.loads(info.stdout.decode('utf-8')) | |
tracks = info['tracks'] | |
# must have 2 video tracks | |
if len(list(filter(is_video, tracks))) < 2: | |
raise Exception('2 video tracks are needed for Dolby Vision mux') | |
# filter audio audio tracks | |
tracks = list(filter(lambda t: is_lang(t, cmd.keep_audio_lang) or not is_audio(t), | |
tracks)) | |
extract_cmd = make_extract_tracks_cmd(file, tracks) | |
print(extract_cmd) | |
out_file = Path(file).stem + '.mp4' | |
remux_cmd = make_mux_cmd(out_file, tracks) | |
print(remux_cmd) | |
subprocess.run(extract_cmd) | |
subprocess.run(remux_cmd) | |
print('cleaning up...') | |
for track in tracks: | |
t_file = Path(track_name(track)) | |
if t_file.exists(): | |
Path(track_name(track)).unlink() | |
else: | |
print('{} is already gone'.format(t_file.as_posix())) | |
print('done: {}'.format(out_file)) | |
if __name__ == "__main__": | |
main() |
Hi,
The topic looks interesting but I can't seem to get it to run. Do you have to edit that python file based on the title of the .mkv somehow?
You need to have the mkvtoolnix and dlb_mp4base from dolby labs in the path.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
The topic looks interesting but I can't seem to get it to run. Do you have to edit that python file based on the title of the .mkv somehow?