Skip to content

Instantly share code, notes, and snippets.

@cdgriffith
Created October 25, 2019 18:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cdgriffith/e2608bfee68bb3d7e491cb387e8d75b1 to your computer and use it in GitHub Desktop.
Save cdgriffith/e2608bfee68bb3d7e491cb387e8d75b1 to your computer and use it in GitHub Desktop.
HDR color primaries and luminance passthrough options
#!/usr/bin/env python
from subprocess import run, PIPE
import re
import sys
import json
su = re.compile(r'",\s*}')
def run_ffprobe(video_file):
command = (f'ffprobe "{video_file}" -show_streams -select_streams v:0 -read_intervals "%+#10" '
f'-show_frames -show_entries side_data -v quiet -pretty -print_format json -of json')
result = run(command, stdout=PIPE)
if result.returncode != 0:
raise Exception(f"Error while running ffprobe {result}")
return json.loads(su.sub('"}', result.stdout.decode("utf-8")))
def parse_json(data):
master_display_raw = None
max_cll = 0
max_fall = 0
for frame in data["packets_and_frames"]:
if frame["type"] != "frame" or "side_data_list" not in frame:
continue
for item in frame["side_data_list"]:
if not master_display_raw and item["side_data_type"] == "Mastering display metadata":
master_display_raw = item
if item["side_data_type"] == "Content light level metadata":
max_cll = item["max_content"]
max_fall = item["max_average"]
if not master_display_raw:
if data["streams"][0]["codec_tag_string"] == "dvhe":
raise Exception("This is a dolby vision file, No master display data provided")
raise Exception("No master display data found")
del master_display_raw['side_data_type']
for key in master_display_raw.keys():
master_display_raw[key] = master_display_raw[key].split("/")[0]
return master_display_raw, max_cll, max_fall
if __name__ == '__main__':
video = sys.argv[1]
ffprobe_output = run_ffprobe(video)
main_stream = ffprobe_output["streams"][0]
mdr, cll, fall = parse_json(ffprobe_output)
master_display = ("master-display="
f"G({mdr['green_x']},{mdr['green_y']})"
f"B({mdr['blue_x']},{mdr['blue_y']})"
f"R({mdr['red_x']},{mdr['red_y']})"
f"WP({mdr['white_point_x']},{mdr['white_point_y']})"
f"L({mdr['max_luminance']},{mdr['min_luminance']})")
max_lum = f"max-cll={cll},{fall}"
params = (f'-pix_fmt {main_stream["pix_fmt"]} -x265-params '
f'"aq-mode=1:'
f'colorprim={main_stream["color_primaries"]}:'
f'colormatrix={main_stream["color_space"]}:'
f'transfer={main_stream["color_transfer"]}:'
f'chromaloc=2:'
f'{master_display}:'
f'{max_lum}:'
f'hdr-opt=1:hdr=1:info=1:repeat-headers=1"')
print(params)
@cdgriffith
Copy link
Author

$ x265_passthrough.py video.mkv

-pix_fmt yuv420p10le -x265-params "aq-mode=1:colorprim=bt2020:colormatrix=bt2020nc:transfer=smpte2084:chromaloc=2:master-display=G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1):max-cll=0,0:hdr-opt=1:hdr=1:info=1:repeat-headers=1"

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