End Render Script: Save sequence to ProRes

User avatar
skittixch
Posts: 16
Joined: Mon Sep 28, 2015 3:33 pm

End Render Script: Save sequence to ProRes

#1

Post by skittixch » Thu Aug 15, 2019 12:38 pm

I've got a question that's been bugging me for some time. I've recently upgraded my setup to include a second machine, and I'd like to start doing distributed rendering, but in doing so, I'm aware that I have to render to a sequence, then compile that into a quicktime to adhere to our in-house pipeline. My question is, is there a way to automatically save a quicktime after all frames are completed, so I can utilize multiple machines to chew on frames more efficiently?

User avatar
SirEdric
Fusionator
Posts: 1873
Joined: Tue Aug 05, 2014 10:04 am
Answers: 2
Real name: Eric Westphal
Been thanked: 112 times
Contact:

Re: End Render Script: Save sequence to ProRes

#2

Post by SirEdric » Thu Aug 15, 2019 12:46 pm

There are multiple ways to do so, and many of them have been discussed in this forum...:-)
One would be to use an end-render-script and some template comp to compile the sequence into a streaming format.

User avatar
bowserlm
Fusionista
Posts: 334
Joined: Sat Apr 28, 2018 3:44 pm
Been thanked: 6 times

Re: End Render Script: Save sequence to ProRes

#3

Post by bowserlm » Thu Aug 15, 2019 2:22 pm

I'm also very interested in this for the same reasons. If you end up figuring out a smooth setup please keep us posted here!

User avatar
SirEdric
Fusionator
Posts: 1873
Joined: Tue Aug 05, 2014 10:04 am
Answers: 2
Real name: Eric Westphal
Been thanked: 112 times
Contact:

Re: End Render Script: Save sequence to ProRes

#4

Post by SirEdric » Thu Aug 15, 2019 9:28 pm

There's even @AndrewHazelden's ffmpeg Saver which after rendering to a sequence automagically triggers ffmpeg to encode the stream.
Or you could use a Render-Manager Script. Or, or, or.......

User avatar
Movalex
Fusioneer
Posts: 113
Joined: Fri Nov 03, 2017 5:36 am
Answers: 2
Been thanked: 27 times
Contact:

Re: End Render Script: Save sequence to ProRes

#5

Post by Movalex » Fri Aug 16, 2019 6:32 am

Here's a slightly modified in-tool script written by @AndrewHazelden for rendering .exr to prores 422. It does automatic gamma correction to sRGB (can be changed to Rec709 or disabled inside the script)
Paste it to End Render script of your saver.
FFmpeg installation instructions included in file.
For smooth setup save the Saver as a macro :)
  1. -- FFMPEG Encoding Intool End Render Script v0.3
  2. -- By Andrew Hazelden <andrew@andrewhazelden.com>
  3. -- optimized for Fusion9 by Alexey Bogomolov <mail@abogomolov.com>
  4. -- This Fusion Intool script is used to FFMPEG encode your saver node rendered
  5. -- image sequences into MOV ProRes movies with a gamma 1.0 to 2.2 conversion applied
  6. -- --------------------------------------------------------------
  7. -- Step 1. Install ffmpeg.
  8.  
  9. -- Windows ffmpeg Download URL: https://ffmpeg.org/download.html
  10. -- * open https://ffmpeg.zeranoe.com/builds/, choose Achitecture (32/64 bit) and choose Linking --> Shared
  11. -- * unzip file contents to C:\ffmpeg
  12.  
  13. -- MacOS Homebrew Based Install:
  14. -- brew install ffmpeg
  15.  
  16. -- CentOS Install:
  17. -- sudo yum -y install ffmpeg
  18.  
  19. -- Ubuntu Install:
  20. -- sudo add-apt-repository ppa:mc3man/trusty-media
  21. -- sudo apt-get update
  22. -- sudo apt-get dist-upgrade
  23. -- sudo apt-get -y install ffmpeg
  24.  
  25. -- Step 2. Paste the FFMPEG Encoding Intool Script into your Saver node's "End Render Script" text field.
  26.  
  27. -- Step 3. Change the script's "ffmpegProgramPath" variable to point to the absolute filepath of the installed copy of ffmpeg.
  28. -- (On Mac/Linux you can find the active ffmpeg path out using: "which ffmpeg")
  29.  
  30. -- Step 4. Render a short test sequence in Fusion. You should have a new .mp4 movie and a log .txt file saved in the same folder as your rendered image sequence. If you have a saver node based Sound Filename entered it will be added automatically as an audio track to the encoded movie file.
  31. print('[FFMPEG Encoding Intool Script]')
  32.  
  33. fusion = fu or Fusion()
  34.  
  35. -- -------------------------------------------------------
  36. -- Specify how audio is handled
  37. -- -------------------------------------------------------
  38. -- Should ffmpeg trim the Movie to the shortest clip duration of the audio or the video track?
  39. audioTrimtoShortestClip = 1
  40. -- audioTrimtoShortestClip = 0
  41.  
  42. -- Where is the audio track coming from in the Comp:
  43.  
  44. -- Don't use any audio
  45. -- audioFilename = ' '
  46. -- Don't have any audio offset
  47. -- audioOffset = ' '
  48.  
  49. -- Use the current Saver node based audio file
  50. audioFilename = self.SoundFilename[0].Value
  51. -- Use the current Saver node based audio offset (measured in frames)
  52. audioOffset = self.SoundOffset
  53.  
  54. -- or
  55.  
  56. -- Use the Fusion timeline based audio file
  57. -- audioFilename = comp:GetAttrs().COMPS_AudioFilename
  58. -- Use the Fusion timeline based audio offset (measured in frames)
  59. -- audioOffset = comp:GetAttrs().COMPN_AudioOffset
  60.  
  61. -- -------------------------------------------------------
  62. -- Specify where the ffmpeg command line tool is installed
  63. -- -------------------------------------------------------
  64.  
  65. -- Find out the current operating system platform. The platform local variable should be set to either "Windows", "Mac", or "Linux".
  66. osPlatform = ' '
  67. ffmpegProgramPath = ' '
  68. -- Check if the OS is Windows by searching for the Program Files folder
  69. if string.find(fusion:MapPath('Fusion:/'), 'Program Files', 1) then
  70.   osPlatform = 'Windows'
  71.  
  72. ffmpegProgramPath = 'C:\\ffmpeg\\bin\\ffmpeg'
  73. -- Check if the OS is Windows by searching for the Program Files folder
  74. elseif string.find(fusion:MapPath('Fusion:/'), 'PROGRA~1', 1) then
  75.   osPlatform = 'Windows'
  76.  
  77. ffmpegProgramPath = 'C:\\ffmpeg\\bin\\ffmpeg'
  78. -- Check if the OS is Mac by searching for the Applications folder
  79. elseif string.find(fusion:MapPath('Fusion:/'), 'Applications', 1) then
  80.   osPlatform = 'Mac'
  81.   ffmpegProgramPath = '/usr/local/bin/ffmpeg'
  82.  
  83. else
  84.   osPlatform = 'Linux'
  85.   ffmpegProgramPath = '/usr/bin/ffmpeg'
  86. end
  87.  
  88. print('[OS] ' .. osPlatform)
  89. print('[FFMPEG Path] ' .. ffmpegProgramPath)
  90.  
  91. -- -------------------------------------------------------
  92. -- Helper functions copied from the scriptlib.lua file
  93. -- -------------------------------------------------------
  94.  
  95. function parseFilename(filename)
  96. print('parsing filename...')
  97. local seq = {}
  98. seq.FullPath = filename
  99.  
  100. print('full path ' .. seq.FullPath)
  101.  
  102. string.gsub(seq.FullPath, "^(.+[/\\])(.+)",
  103. function(path, name)
  104.     seq.Path = path
  105.     print('folder path: ' .. seq.Path)
  106.     seq.FullName = name
  107.     print('file name: ' .. seq.FullName)
  108. end)
  109.  
  110. string.gsub(seq.FullName, "^(.+)(%..+)$",
  111. function(name, ext)
  112.     seq.Name = name
  113.     print('name: ' .. seq.Name)
  114.     seq.Extension = ext
  115.     print('extension: ' .. seq.Extension)
  116. end)
  117.  
  118. if not seq.Name then -- no extension?
  119. seq.Name = seq.FullName
  120. end
  121.  
  122. seq.SNum = string.match(seq.Name, "%d+$")
  123.  
  124. if seq.SNum then
  125.     seq.Number = tonumber( seq.SNum )
  126.     seq.Padding = string.len( seq.SNum )
  127.     seq.CleanName = string.match(seq.Name,"^(.-)%d+$" )
  128. else
  129.     seq.SNum = "0000"
  130.     seq.Number = 0
  131.     seq.Padding = 4
  132.     seq.CleanName = seq.Name
  133. end
  134.  
  135. print('Start number: ' .. seq.SNum)    
  136. print('Clean Name: ' .. seq.CleanName)
  137.  
  138. if seq.Extension == nil then
  139. seq.Extension = ""
  140. end
  141.  
  142. return seq
  143. end
  144.  
  145. -- -------------------------------------------------------
  146. -- Figure out the comp Audio clip
  147. -- -------------------------------------------------------
  148.  
  149. ffmpegAudioPrefixCommands = ' '
  150. ffmpegAudioPostfixCommands = ' '
  151. if audioFilename == nil then
  152.   print('[FFMPEG Audio Filename] No Audio Track Active')
  153. else
  154.   if string.len(audioFilename) > 3 then
  155.     -- print('[FFMPEG Audio Filename] ' .. audioFilename)
  156.     print('[FFMPEG Audio Filename] ' .. cmp:MapPath(audioFilename))
  157.     if audioOffset == nil then
  158.       print('[FFMPEG Audio Offset] No Time Offset')
  159.     else
  160.       print('[FFMPEG Audio Offset] ' .. audioOffset)
  161.     end
  162.  
  163.     -- Build the audio track commands
  164.     --ffmpegAudioPrefixCommands = '-i "' .. audioFilename .. '"'
  165.     ffmpegAudioPrefixCommands = '-i "' .. cmp:MapPath(audioFilename) .. '"'
  166.  
  167.     -- Trim the Movie to the shortest clip duration of the audio or the video track
  168.     if audioTrimtoShortestClip == 1 then
  169.       print('[FFMPEG Trim Clip to Shortest Duration] Active')
  170.       ffmpegAudioPostfixCommands = ' ' .. '-shortest' .. ' '
  171.     end
  172.   else
  173.     -- Error: The audio filename is less then three characters long
  174.     print('[FFMPEG Audio Filename] No Audio Track Active')
  175.   end
  176. end
  177. -- -------------------------------------------------------
  178. -- Figure out the Saver node filenames
  179. -- -------------------------------------------------------
  180.  
  181. -- seq = parseFilename(self.Clip.Filename)
  182. saverfile = self.Clip.Filename
  183. print('Parsing Saver file... ' .. saverfile)
  184. seq = parseFilename(fu:MapPath(self.Clip.Filename))
  185.  
  186.  
  187. -- Debug the sequence table
  188. -- dump(seq)
  189.  
  190. -- Example: filename.%04d.exr
  191. ffmpegImageSequenceFilename = seq.Path .. seq.CleanName .. '%0' .. seq.Padding .. "d" .. seq.Extension
  192. print('[FFMPEG Start Frame] ' .. comp.RenderStart)
  193. print('[FFMPEG Formatted Image Sequence] ' .. ffmpegImageSequenceFilename)
  194.  
  195. -- Example: filename.mp4
  196. ffmpegMovieFilename = seq.Path .. seq.CleanName .. '.mov'
  197. print('[FFMPEG Exported Movie] ' .. ffmpegMovieFilename)
  198.  
  199. -- Example: filename.txt
  200. ffmpegLogFilename = seq.Path .. seq.CleanName .. '.txt'
  201. print('[FFMPEG Logfile] ' .. ffmpegLogFilename)
  202.  
  203. -- A gamma 1 to 2.2 adjustment should be applied for exr output
  204. -- Note: Your copy of FFMPEG has to support the "-apply_trc" option or you will get an "Unrecognized option 'apply_trc'." error message in the log file.
  205.  
  206. ffmpegApplyGammaCorrection = ' '
  207. if seq.Extension == '.exr' then
  208.     print('[FFMPEG EXR Gammma 1.0 to 2.2 Transform Active] [Image Format]' .. seq.Extension)
  209.     -- Convert a linear exr to REC 709
  210.     -- ffmpegApplyGammaCorrection = '-apply_trc bt709'
  211.     -- or
  212.     -- Convert a linear exr file to sRGB
  213.     ffmpegApplyGammaCorrection = '-apply_trc iec61966_2_1'
  214. end
  215.  
  216. -- Set the frame rate for the encoded movie
  217. frameRate = comp:GetPrefs("Comp.FrameFormat.Rate")
  218. if frameRate == nil then
  219.     frameRate = 25
  220. end
  221.  
  222. print('[FFMPEG Frame Rate] ' .. frameRate)
  223.  
  224. -- -------------------------------------------------------
  225. -- Encode the image sequence into a movie using ffmpeg
  226. -- -------------------------------------------------------
  227.  
  228. command = ffmpegProgramPath .. ' ' .. ffmpegAudioPrefixCommands .. ' ' .. ffmpegApplyGammaCorrection .. ' -framerate ' .. frameRate .. ' -f image2 -start_number ' .. comp.RenderStart .. ' -i "' .. ffmpegImageSequenceFilename .. '" -r ' .. frameRate .. ' -y -vcodec prores_ks -profile:v 3 -acodec pcm_s16le -ar 48000 -ac 2 ' .. ffmpegAudioPostfixCommands .. ' -strict -2  "' .. ffmpegMovieFilename .. '" >> "' .. ffmpegLogFilename .. '" 2>&1'
  229. print('[Launch Command] ' .. command)
  230. os.execute(command)
  231.  
  232. print('[Done]')
  233.  
You do not have the required permissions to view the files attached to this post.

User avatar
skittixch
Posts: 16
Joined: Mon Sep 28, 2015 3:33 pm

Re: End Render Script: Save sequence to ProRes

#6

Post by skittixch » Fri Aug 16, 2019 2:08 pm

Thanks everyone! Blown away by the responses so far :) Will try these out!