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.
Last active
February 29, 2024 20:33
-
-
Save jedypod/c30adff622aab454f40e0963649af780 to your computer and use it in GitHub Desktop.
Nuke ffmpeg Render
This file contains 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
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) |
This file contains 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
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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