#!/usr/bin/env python3 # convert_to_dnx.py # by Jamie Hardt # (c) 2025 Squad 51, Inc. All rights reserved. # This script attempts to take an input file and attempts to generate # the appropriate ffmpeg output arguments (or all possible appropriate # ffmpeg output arguments) given the file's frame rate, dimensions, # and chroma subsampling. # # I'm not sure if I meant this seriously or if it's all a big satire of # DNxHD's codecs. I never use this myself I just convert to prores. import sys import subprocess import json import csv import re import io import pprint # https://askubuntu.com/questions/907398/how-to-convert-a-video-with-ffmpeg-into-the-dnxhd-dnxhr-format # https://en.wikipedia.org/wiki/List_of_Avid_DNxHD_resolutions # Resolution,Frame Width, Frame Height,Chroma Subsampling,Bits,Frames Per Second,Megabits per second,Minutes per Gigabyte dnx_rates = """Avid DNxHD 440x,1920,1080,p,yuv422p10,60/1,440,0.325 Avid DNxHD 440,1920,1080,p,yuv422p,60/1,440,0.325 Avid DNxHD 290,1920,1080,p,yuv422p,60/1,291,0.492 Avid DNxHD 90,1920,1080,p,yuv422p,60/1,90,1.585 Avid DNxHD 440x,1920,1080,p,yuv422p10,60000/1001,440,0.325 Avid DNxHD 440,1920,1080,p,yuv422p,60000/1001,440,0.325 Avid DNxHD 290,1920,1080,p,yuv422p,60000/1001,291,0.493 Avid DNxHD 90,1920,1080,p,yuv422p,60000/1001,90,1.585 Avid DNxHD 365x,1920,1080,p,yuv422p10,50/1,367,0.39 Avid DNxHD 365,1920,1080,p,yuv422p,50/1,367,0.39 Avid DNxHD 240,1920,1080,p,yuv422p,50/1,242,0.59 Avid DNxHD 75,1920,1080,p,yuv422p,50/1,75,1.9 Avid DNxHD 220x,1920,1080,i,yuv422p10,30000/1001,220,0.651 Avid DNxHD 220,1920,1080,i,yuv422p,30000/1001,220,0.651 Avid DNxHD 145,1920,1080,i,yuv422p,30000/1001,145,0.985 Avid DNxHD 100,1920,1080,i,yuv422p,30000/1001,100,1.429 Avid DNxHD 440x,1920,1080,p,yuv444p10,30000/1001,440,0.325 Avid DNxHD 220x,1920,1080,p,yuv422p10,30000/1001,220,0.651 Avid DNxHD 220,1920,1080,p,yuv422p,30000/1001,220,0.651 Avid DNxHD 145,1920,1080,p,yuv422p,30000/1001,145,0.985 Avid DNxHD 100,1920,1080,p,yuv422p,30000/1001,100,1.429 Avid DNxHD 45,1920,1080,p,yuv422p,30000/1001,45,3.18 Avid DNxHD 185x,1920,1080,i,yuv422p10,25/1,184,0.78 Avid DNxHD 185,1920,1080,i,yuv422p,25/1,184,0.78 Avid DNxHD 120,1920,1080,i,yuv422p,25/1,121,1.713 Avid DNxHD 85,1920,1080,i,yuv422p,25/1,84,1.181 Avid DNxHD 365x,1920,1080,p,yuv444p10,25/1,367,0.39 Avid DNxHD 185x,1920,1080,p,yuv422p10,25/1,184,0.78 Avid DNxHD 185,1920,1080,p,yuv422p,25/1,184,0.78 Avid DNxHD 120,1920,1080,p,yuv422p,25/1,121,1.181 Avid DNxHD 85,1920,1080,p,yuv422p,25/1,84,1.713 Avid DNxHD 36,1920,1080,p,yuv422p,25/1,36,3.98 Avid DNxHD 350x,1920,1080,p,yuv444p10,24/1,352,0.406 Avid DNxHD 175x,1920,1080,p,yuv422p10,24/1,176,0.814 Avid DNxHD 175,1920,1080,p,yuv422p,24/1,176,0.814 Avid DNxHD 115,1920,1080,p,yuv422p,24/1,116,1.231 Avid DNxHD 80,1920,1080,p,yuv422p,24/1,80,1.785 Avid DNxHD 36,1920,1080,p,yuv422p,24/1,36,3.98 Avid DNxHD 350x,1920,1080,p,yuv444p10,24000/1001,352,0.407 Avid DNxHD 175x,1920,1080,p,yuv422p10,24000/1001,176,0.814 Avid DNxHD 175,1920,1080,p,yuv422p,24000/1001,176,0.814 Avid DNxHD 115,1920,1080,p,yuv422p,24000/1001,116,1.231 Avid DNxHD 80,1920,1080,p,yuv422p,24000/1001,80,1.787 Avid DNxHD 36,1920,1080,p,yuv422p,24000/1001,36,3.98 Avid DNxHD 220x,1280,720,p,yuv422p10,60000/1001,220,0.651 Avid DNxHD 220,1280,720,p,yuv422p,60000/1001,220,0.651 Avid DNxHD 145,1280,720,p,yuv422p,60000/1001,145,0.985 Avid DNxHD 100,1280,720,p,yuv422p,60000/1001,100,1.402 Avid DNxHD 175x,1280,720,p,yuv422p10,50/1,175,0.818 Avid DNxHD 175,1280,720,p,yuv422p,50/1,175,0.818 Avid DNxHD 115,1280,720,p,yuv422p,50/1,115,1.244 Avid DNxHD 85,1280,720,p,yuv422p,50/1,85,1.68 Avid DNxHD 110x,1280,720,p,yuv422p10,30000/1001,110,1.3 Avid DNxHD 110,1280,720,p,yuv422p,30000/1001,110,1.3 Avid DNxHD 75,1280,720,p,yuv422p,30000/1001,72,2.05 Avid DNxHD 50,1280,720,p,yuv422p,30000/1001,51,2.8 Avid DNxHD 90x,1280,720,p,yuv422p10,25/1,92,1.59 Avid DNxHD 90,1280,720,p,yuv422p,25/1,92,1.59 Avid DNxHD 60,1280,720,p,yuv422p,25/1,60,2.39 Avid DNxHD 45,1280,720,p,yuv422p,25/1,43,3.361 Avid DNxHD 90x,1280,720,p,yuv422p10,24000/1001,92,1.566 Avid DNxHD 90,1280,720,p,yuv422p,24000/1001,92,1.566 Avid DNxHD 60,1280,720,p,yuv422p,24000/1001,60,2.381 Avid DNxHD 45,1280,720,p,yuv422p,24000/1001,43,3.504 """ available_dnx_resolutions = [] dnx_reader = csv.DictReader(io.StringIO(dnx_rates), ["name","width","height","interlaced", "pix_fmt","frame_rate","mb","mins_per_gig"]) for res in dnx_reader: available_dnx_resolutions += [res] available_dnx_resolutions = sorted(available_dnx_resolutions, key=lambda x: float(x["mins_per_gig"]), reverse=True) # ffmpeg -loglevel error -f lavfi -i testsrc2 -c:v dnxhd -f null - result = subprocess.run(["/usr/local/bin/ffmpeg", "-hide_banner", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc2", "-c:v", "dnxhd", "-f", "null", "-"], capture_output=True) # result.check_returncode() ffmpeg_dnx_resolutions = [] for line in result.stderr.splitlines(): line = line.decode("utf-8") matches = re.match(".*Frame size: (\d+)x(\d+)([pi]).*", line) if matches is not None: width, height = matches[1], matches[2] interlace = matches[3] matches = re.match(".*format: (yuv\w+).*", line) fmt = matches[1] matches = re.match(".*bitrate: (\d+)Mbps.*", line) mbps = matches[1] ffmpeg_dnx_resolutions += [{ "width": width, "height": height, "pix_fmt": fmt, "mb": mbps, "interlaced": interlace }] def filter_pred(res): def inner_pred(ff_res): # print(res, ff_res) # input("Press return...") result = ff_res["interlaced"] == res["interlaced"] and \ ff_res["width"] == res["width"] and \ ff_res["height"] == res["height"] and \ ff_res["pix_fmt"] == res["pix_fmt"] and \ ff_res["mb"] == res["mb"] # print("Result", result) return result found = filter(inner_pred,ffmpeg_dnx_resolutions) return len(list(found)) > 0 available_dnx_resolutions = list(filter(filter_pred, available_dnx_resolutions)) # probe input file input_file = sys.argv[1] result = subprocess.run(["/usr/local/bin/ffprobe","-hide_banner","-of","json", "-show_streams","-select_streams","v", input_file], capture_output=True) result.check_returncode() probe_output = json.loads(result.stdout) pix_fmt = probe_output["streams"][0]["pix_fmt"] frame_size = (probe_output["streams"][0]["width"], probe_output["streams"][0]["height"]) frame_rate = probe_output["streams"][0]["r_frame_rate"] field_order_progressive = "p" if probe_output["streams"][0]["field_order"] == "progressive" else "i" def filter_for_frame_rate(res): return res["frame_rate"] == frame_rate and res["interlaced"] == field_order_progressive def filter_for_scale(res): return res["width"] == frame_size[0] and res["height"] == frame_size[1] and \ res["interlaced"] == field_order_progressive # print("Available DNx resolutions:") for n in filter(lambda r: filter_for_frame_rate(r) and filter_for_scale(r), available_dnx_resolutions): print(f"-vcodec dnxhd -b:v {n['mb']}M -pix_fmt {n['pix_fmt']}") # print("Available with scaling:") for n in filter(filter_for_frame_rate, available_dnx_resolutions): # https://superuser.com/questions/991371/ffmpeg-scale-and-pad print(f"""-vf "scale=w={n['width']}:h={n['height']}:force_original_aspect_ratio=1,""" f"""pad={n['width']}:{n['height']}:(ow-iw)/2:(oh-ih)/2" -vcodec dnxhd -b:v {n['mb']}M -pix_fmt {n['pix_fmt']}""")