Skip to content

Instantly share code, notes, and snippets.

@jedypod
Last active February 29, 2024 20:33
Show Gist options
  • Save jedypod/c30adff622aab454f40e0963649af780 to your computer and use it in GitHub Desktop.
Save jedypod/c30adff622aab454f40e0963649af780 to your computer and use it in GitHub Desktop.
Nuke ffmpeg Render

Nuke ffmpeg Render

Writeffmpeg is a python script and a node that allows you to render from Nuke directly into ffmpeg. Instead of a 2 step process where you render from Nuke to a temporary image sequence that you then transcode using ffmpeg, this solution writes to a single uint16 cache tiff file and this data gets piped into ffmpeg.

I've included a little screen recordingto help demo how it works.

Files

ffmpeg_render.py Writeffmpeg.nk

Dependencies

from __future__ import with_statement
from __future__ import print_function
from __future__ import division
import os, sys
import subprocess, shlex
import argparse
import tempfile
import numpy as np
from libtiff import TIFF
import nuke
from sys import platform as __platform
if __platform == "linux" or __platform == "linux2":
_platform = 'linux'
elif __platform == "darwin":
_platform = 'osx'
elif __platform == "win32":
_platform = 'win'
def frames_to_tc(total_frames, frame_rate):
fps_int = int(round(frame_rate))
smpte_token = ":"
hours = int(total_frames / (3600 * fps_int))
minutes = int(total_frames / (60 * fps_int) % 60)
seconds = int(total_frames / fps_int % 60)
frames = int(total_frames % fps_int)
return "%02d:%02d:%02d%s%02d" % (hours, minutes, seconds, smpte_token, frames)
def terminal_render():
parser = argparse.ArgumentParser(description='Render from Nuke to ffmpeg.')
parser.add_argument("nuke_script",
help="Nuke script to render.")
parser.add_argument("-X", "--write",
help="Name of the WriteFFMPEG node to render.")
parser.add_argument("-F", "--framerange",
help="framerange to render. Please specify <start>-<end>.",
required=False)
parser.add_argument("-o", "--output",
help="Output qt to render to. Will use the value of the file knob on the WriteFFMPEG node if not specified.",
required=False)
args = parser.parse_args()
nuke_script = args.nuke_script
nuke.scriptOpen(nuke_script)
node = nuke.toNode(args.write)
node.begin()
write = nuke.toNode('write_tmp')
if args.framerange and "-" in args.framerange:
fr = nuke.FrameRange()
fr.setLast(int(args.framerange.split('-')[-1]))
fr.setFirst(int(args.framerange.split('-')[0]))
else:
node_framerange = node['framerange'].getValue()
if node_framerange and "-" in node_framerange:
fr = nuke.FrameRange()
fr.setLast(int(node_framerange.split('-')[-1]))
fr.setFirst(int(node_framerange.split('-')[0]))
else:
fr = node.frameRange()
tmpimg = tempfile.mkstemp('.tiff', "ffmpeg_temp_")[1]
write['file'].setValue(tmpimg)
framerate = node['framerate'].getValue()
output = node['file'].getValue()
tc = frames_to_tc(fr.first(), framerate)
ffmpeg_args = "ffmpeg -hide_banner -loglevel info -y \
-f rawvideo -pixel_format rgb48le -video_size {0}x{1} \
-framerate {2} -i pipe:0 -timecode {3} {4} {5}".format(
node.width(), node.height(), framerate, tc,
node['ffmpeg_args'].getValue(), output)
print(ffmpeg_args)
ffproc = subprocess.Popen(
shlex.split(ffmpeg_args),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
for i, f in enumerate(fr):
nuke.execute(write, f, f)
print("Rendering frame \t{0} of {1}".format(i, fr.frames()))
img = TIFF.open(tmpimg, mode='r')
img = img.read_image()
img.tofile(ffproc.stdin)
os.remove(tmpimg)
result, error = ffproc.communicate()
if __name__=="__main__":
terminal_render()
def prep():
nuke.scriptSave()
node = nuke.thisNode()
ffpy = __file__
ffpy = ffpy.replace('pyc', 'py')
node_framerange = node['framerange'].getValue()
nk_cmd = "{0} -t {1} {2} --write {3} --output {4}".format(
nuke.EXE_PATH,
ffpy,
nuke.root().knob("name").value(),
node.fullName(),
node['file'].getValue())
print("RENDER COMMAND:\n\t{0}".format(nk_cmd))
if _platform == "win":
nuke.message("Windows not supported.")
return
if _platform == "osx":
cmd = '''osascript 2>/dev/null <<EOF
tell application "Terminal"
if not (exists window 1) then reopen
activate
do script "{0}"
end tell
EOF'''.format(nk_cmd)
elif _platform == "linux":
# cmd = 'xterm -e "bash {0}"'.format(nk_cmd)
cmd = 'gnome-terminal -e "bash -c \\"{0}; exec bash\\""'.format(nk_cmd)
#cmd = 'gnome-terminal -e "bash -c \\"{0}\\""'.format(nk_cmd)
subprocess.Popen(cmd, shell=True)
set cut_paste_input [stack 0]
push $cut_paste_input
Group {
name Writeffmpeg
tile_color 0xbfbf00ff
addUserKnob {20 ffmpeg}
addUserKnob {35 presets M {"Prores 422" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 2 -qscale:v 7 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 422\\\"\" framerate 24 label \"Prores 422\"\}" "Prores 422 HQ" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 3 -qscale:v 7 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 422 HQ\\\"\" framerate 24 label \"Prores 422 HQ\"\}" "Prores 4444" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 4 -qscale:v 5 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 4444\\\"\\\"\" framerate 24 label \"Prores 4444\"\}" "DNxHD 36" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 36M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 36\"\}" "DNxHD 115" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 115M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 115\"\}" "DNxHD 175" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 175M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 175\"\}" "DNxHD 175 10bit" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p10 -b:v 175M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 175 10bit\"\}" "DNxHD 220 10bit" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p10 -b:v 220M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 220 10bit\"\}" "DNxHR HQ" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_hq -pix_fmt yuv422p -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR HQ\"\}" "DNxHR HQX" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_hqx -pix_fmt yuv422p10le -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR HQX\"\}" "DNxHR 444" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_444 -pix_fmt yuv444p10le -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR 444\"\}" "h264 standard" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high -crf 15 -preset slow -tune film -pix_fmt yuv420p -g 4 -bf 2 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 standard\"\}" "h264 intra" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high -crf 12 -preset slow -tune film -pix_fmt yuv420p -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra\"\}" "h264 intra 444" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high444 -crf 12 -preset slow -tune film -pix_fmt yuv444p -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra 444\"\}" "h264 intra 444 10bit" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high444 -crf 12 -preset slow -tune film -pix_fmt yuv444p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra 444 10bit\"\}" "h265 standard 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main10 -crf 15 -preset slow -tune psnr -pix_fmt yuv420p10le -g 2 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 standard 10bit\"\}" "h265 intra 422 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main422-10-intra -crf 12 -preset slow -tune psnr -pix_fmt yuv422p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 intra 10bit\"\}" "h265 intra 444 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main444-10-intra -crf 12 -preset slow -tune psnr -pix_fmt yuv444p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 intra 444 10bit\"\}" ""}}
addUserKnob {2 file t "output file"}
addUserKnob {7 framerate R 23 30}
framerate 24
addUserKnob {1 ffmpeg_args l "ffmpeg args"}
addUserKnob {1 ffmpeg_cmd l "ffmpeg command" t "Enter the path to the ffmpeg executable. \n\nffmpeg without a path will use the environment."}
ffmpeg_cmd ffmpeg
addUserKnob {1 framerange -STARTLINE}
addUserKnob {22 set_framerange l "Set to Input" -STARTLINE T "n = nuke.thisNode()\nfr = n.frameRange()\nn\['framerange'].setValue(\"\{0\}-\{1\}\".format(fr.first(), fr.last()))"}
addUserKnob {22 render l Render T "import ffmpeg_render\nffmpeg_render.prep()" +STARTLINE}
}
Input {
inputs 0
name Input
xpos -40
ypos -10
}
Write {
raw true
file_type tiff
datatype "16 bit"
compression none
checkHashOnRead false
version 486
name write_tmp
selected true
xpos -40
ypos 98
}
Output {
name Output
xpos -40
ypos 230
}
end_group
This file has been truncated, but you can view the full file.
@brolyleman
Copy link

hello, wondering if this still works in nuke 13?

I open the nukescript in Nuke, press render, and it returns: no module named 'numpy'.

I installed numpy using Anaconda, I import it then run it in the powershell and returns its location:

<module 'numpy' from 'C:\ProgramData\Anaconda3\lib\site-packages\numpy\init.py'>

Not sure where to go from here. Any advice appreciated

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