Skip to content
This repository has been archived by the owner on Jul 3, 2023. It is now read-only.

Commit

Permalink
Merge pull request fract4d#25 from cjmayo/video
Browse files Browse the repository at this point in the history
Fix Video Creation
  • Loading branch information
edyoung authored and da2ce7 committed Feb 17, 2020
2 parents c255a59 + 53585d6 commit 7978a7a
Show file tree
Hide file tree
Showing 11 changed files with 604 additions and 640 deletions.
9 changes: 4 additions & 5 deletions doc/gnofract4d-manual/C/gnofract4d-manual.xml
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,16 @@ keyframe will stay in video (<guilabel>stopped for</guilabel>) and
interpolation type between several possibilities. When you hit
<guibutton>Render</guibutton> button, Director will render all frames
and put them in directory you selected and then it will try to create
video using <ulink
url="http://www.transcoding.org/"><emphasis>transcode</emphasis></ulink>
tool. </para>
a video using <ulink url="https://www.ffmpeg.org/">
<emphasis>FFmpeg</emphasis></ulink>.</para>

<para>
Tips:
<itemizedlist>

<listitem><para>
In order to end up with video file, not just a bunch of images, you need to have
<emphasis>transcode</emphasis> tool compiled with support for ImageMagick.
In order to end up with a video file, not just a bunch of images, you need to have
<emphasis>ffmpeg</emphasis> compiled with support for zlib and libvpx.
</para></listitem>

<listitem><para>
Expand Down
170 changes: 82 additions & 88 deletions fract4d/animation.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
#!/usr/bin/python

import os, sys, copy, math
import os
import math

from xml.sax import make_parser
from xml.sax.handler import ContentHandler

from . import fracttypes
from . import fractal
from . import fractconfig

#interpolation type constants
INT_LINEAR= 0
INT_LOG= 1
INT_INVLOG= 2
INT_COS= 3
# interpolation type constants
INT_LINEAR = 0
INT_LOG = 1
INT_INVLOG = 2
INT_COS = 3

def getAttrOrDefault(attrs, name, default):
x = attrs.get(name)
if x == None:
if x is None:
x = default
return x

def getAttrOrElse(attrs, name):
x = attrs.get(name)
if x == None:
if x is None:
raise ValueError(
"Invalid file: Cannot find required attribute '%s'" % name)
return x

class KeyFrame:
def __init__(self,filename,duration,stop,int_type,flags=(0,0,0,0,0,0)):
def __init__(self, filename, duration, stop, int_type, flags=(0,0,0,0,0,0)):
self.filename = filename
self.duration = duration
self.stop = stop
self.int_type = int_type
self.flags = flags

def save(self,fh):
def save(self, fh):
fh.write(
'\t\t<keyframe filename="%s" duration="%d" stop="%d" inttype="%d" ' % (self.filename, self.duration, self.stop, self.int_type))
fh.write(
'xy="%d" xz="%d" xw="%d" yz="%d" yw="%d" zw="%d"' % self.flags)
fh.write('/>\n')

@staticmethod
def load_from_xml(attrs):
kf = KeyFrame(
getAttrOrElse(attrs,"filename"),
Expand All @@ -56,22 +56,20 @@ def load_from_xml(attrs):
int(getAttrOrDefault(attrs,"yw",0)),
int(getAttrOrDefault(attrs,"zw",0))))
return kf

load_from_xml=staticmethod(load_from_xml)

class T:
def __init__(self, compiler):
self.compiler = compiler
self.reset()

def reset(self):
self.avi_file=""
self.width=640
self.height=480
self.framerate=25
self.redblue=True
#keyframes is a list of KeyFrame objects
self.keyframes=[]
self.avi_file = ""
self.width = 640
self.height = 480
self.framerate = 25
self.redblue = False
# keyframes is a list of KeyFrame objects
self.keyframes = []

def get_fct_enabled(self):
return fractconfig.instance.getboolean("director","fct_enabled")
Expand All @@ -98,63 +96,63 @@ def get_avi_file(self):
return self.avi_file

def set_avi_file(self,file):
if file!=None:
self.avi_file=file
if file is not None:
self.avi_file = file
else:
self.avi_file=""
self.avi_file = ""

def get_width(self):
return self.width

def set_width(self,width):
if width!=None:
self.width=int(width)
if width is not None:
self.width = int(width)
else:
self.width=640
self.width = 640

def get_height(self):
return self.height

def set_height(self,height):
if height!=None:
self.height=int(height)
if height is not None:
self.height = int(height)
else:
self.height=480
self.height = 480

def get_framerate(self):
return self.framerate

def set_framerate(self,fr):
if fr!=None:
self.framerate=int(fr)
if fr is not None:
self.framerate = int(fr)
else:
self.framerate=25
self.framerate = 25

def get_redblue(self):
return self.redblue

def set_redblue(self,rb):
if rb!=None:
if rb==1:
self.redblue=True
elif rb==0:
self.redblue=False
self.redblue=rb
if rb is not None:
if rb == 1:
self.redblue = True
elif rb == 0:
self.redblue = False
self.redblue = rb
else:
self.redblue=True
self.redblue = True

def add_keyframe(self,filename,duration,stop,int_type,index=None):
kf = KeyFrame(filename,duration,stop,int_type)
if index==None:
if index is None:
self.keyframes.append(kf)
else:
self.keyframes.insert(index, kf)

def remove_keyframe(self,index):
self.keyframes[index:index+1]=[]
del self.keyframes[index:index+1]

def change_keyframe(self,index,duration,stop,int_type):
if index<len(self.keyframes):
if index < len(self.keyframes):
kf = self.keyframes[index]
kf.duration = duration
kf.stop = stop
Expand All @@ -170,110 +168,107 @@ def get_keyframe_duration(self,index):
return self.keyframes[index].duration

def set_keyframe_duration(self,index,duration):
if index<len(self.keyframes):
if index < len(self.keyframes):
self.keyframes[index].duration = duration

def get_keyframe_stop(self,index):
return self.keyframes[index].stop

def set_keyframe_stop(self,index,stop):
if index<len(self.keyframes):
self.keyframes[index].stop= stop
if index < len(self.keyframes):
self.keyframes[index].stop = stop

def get_keyframe_int(self,index):
return self.keyframes[index].int_type

def set_keyframe_int(self,index,int_type):
if index<len(self.keyframes):
self.keyframes[index].int_type=int_type
if index < len(self.keyframes):
self.keyframes[index].int_type = int_type

def get_directions(self,index):
return self.keyframes[index].flags

def set_directions(self,index,drct):
if index<len(self.keyframes):
if index < len(self.keyframes):
self.keyframes[index].flags = drct

def keyframes_count(self):
return len(self.keyframes)

def __getstate__(self):
odict = self.__dict__.copy() # copy the dict since we change it
odict = self.__dict__.copy() # copy the dict since we change it
#del odict['fh'] # remove filehandle entry
return odict

def __setstate__(self,dict):
self.keyframes=[]
self.keyframes = []
self.__dict__.update(dict) # update attributes

def load_animation(self,file):
#save __dict__ if there was error
# save __dict__ if there was error
odict = self.__dict__.copy()
import traceback
try:
self.keyframes=[]
self.keyframes = []
parser = make_parser()
ah = AnimationHandler(self)
parser.setContentHandler(ah)
parser.parse(open(file))
except Exception as err:
#retrieve previous__dict__
self.__dict__=odict
# retrieve previous__dict__
self.__dict__ = odict
raise

def save_animation(self,file):
fh=open(file,"w")
fh.write('<?xml version="1.0"?>\n')
fh.write("<animation>\n")
fh.write('\t<keyframes>\n')
for kf in self.keyframes:
kf.save(fh)
fh.write('\t</keyframes>\n')
fh.write('\t<output filename="%s" framerate="%d" width="%d" height="%d" swap="%d"/>\n'%
(self.avi_file,self.framerate,self.width,self.height,self.redblue))
fh.write("</animation>\n")
fh.close()

#leftover from debugging purposes
with open(file, "w") as fh:
fh.write('<?xml version="1.0"?>\n')
fh.write("<animation>\n")
fh.write('\t<keyframes>\n')
for kf in self.keyframes:
kf.save(fh)
fh.write('\t</keyframes>\n')
fh.write('\t<output filename="%s" framerate="%d" width="%d" height="%d" swap="%d"/>\n' %
(self.avi_file,self.framerate,self.width,self.height,self.redblue))
fh.write("</animation>\n")

# leftover from debugging purposes
def pr(self):
print(self.__dict__)

def get_image_filename(self,n):
"The filename of the image containing the Nth frame"
return os.path.join(self.get_png_dir(),"image_%07d.png" %n)
return os.path.join(self.get_png_dir(), "image_%07d.png" % n)

def get_fractal_filename(self,n):
"The filename of the .fct file which generates the Nth frame"
return os.path.join(self.get_fct_dir(),"file_%07d.fct" % n)

def get_mu(self, int_type, x):
if int_type==INT_LINEAR:
mu=x
elif int_type==INT_LOG:
mu=math.log(x+1,2)
elif int_type==INT_INVLOG:
mu=(math.exp(x)-1)/(math.e-1)
elif int_type==INT_COS:
mu=(1-math.cos(x*math.pi))/2
if int_type == INT_LINEAR:
mu = x
elif int_type == INT_LOG:
mu = math.log(x+1,2)
elif int_type == INT_INVLOG:
mu = (math.exp(x)-1) / (math.e-1)
elif int_type == INT_COS:
mu = (1-math.cos(x*math.pi)) / 2
else:
raise ValueError("Unknown interpolation type %d" % int_type)
return mu

# create a list containing all the filenames of the frames
def create_list(self):
framelist = []
folder_png=self.get_png_dir()

current=1
current = 1
for i in range(self.keyframes_count()):
for j in range(self.get_keyframe_stop(i)): #output keyframe 'stop' times
for j in range(self.get_keyframe_stop(i)): # output keyframe 'stop' times
framelist.append(self.get_image_filename(current-1))

if i < self.keyframes_count()-1:
# final frame has no transitions following it
for j in range(self.get_keyframe_duration(i)): #output all transition files
for j in range(self.get_keyframe_duration(i)): # output all transition files
framelist.append(self.get_image_filename(current))
current=current+1
current += 1

return framelist

Expand All @@ -296,17 +291,16 @@ def get_total_frames(self):

class AnimationHandler(ContentHandler):
def __init__(self,animation):
self.animation=animation
self.animation = animation

def startElement(self, name, attrs):
if name=="output":
if name == "output":
self.animation.set_avi_file(attrs.get("filename"))
self.animation.set_framerate(attrs.get("framerate"))
self.animation.set_width(attrs.get("width"))
self.animation.set_height(attrs.get("height"))
self.animation.set_redblue(int(attrs.get("swap")))
elif name=="keyframe":
kf= KeyFrame.load_from_xml(attrs)
elif name == "keyframe":
kf = KeyFrame.load_from_xml(attrs)
self.animation.keyframes.append(kf)
return

3 changes: 1 addition & 2 deletions fract4d/fractconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ def __init__(self, file):
"helpers" : {
"editor" : self.get_default_editor(),
"mailer" : self.get_default_mailer(),
"browser" : self.get_default_browser(),
"video_encoder" : "transcode"
"browser" : self.get_default_browser()
},
"general" : {
"threads" : "1",
Expand Down
2 changes: 1 addition & 1 deletion fract4d/test_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def testDefault(self):
self.assertEqual(self.anim.get_width(),640)
self.assertEqual(self.anim.get_height(),480)
self.assertEqual(self.anim.get_framerate(),25)
self.assertEqual(self.anim.get_redblue(),True)
self.assertEqual(self.anim.get_redblue(),False)
self.assertEqual(self.anim.keyframes_count(),0)

def testChangeOptions(self):
Expand Down
Loading

0 comments on commit 7978a7a

Please sign in to comment.