--- a +++ b/trunk/tubetutor/ffmpeg.py @@ -0,0 +1,224 @@ +# +# Wrapper for the ffmpeg utilities +# +# Default search path is relatively to the main script that is called: +# <script path>/ffmpeg +# +# Otherwise, the windows search path is used +# +import subprocess +import ctypes +import sys +import os +import re + +#font="Arial" +font=os.path.join("media", "Roboto-Regular.ttf") +proc_ffmpeg=None + +# +# The following helper functions will retrieve the pathnames for the ffmpeg tools +# +def get_cmd_ffmpeg(): + subdir=os.path.join(os.path.dirname(sys.argv[0]), "ffmpeg") + if os.path.exists(subdir): + return os.path.join(subdir, "ffmpeg.exe") + return "ffmpeg.exe" + +def get_cmd_ffprobe(): + subdir=os.path.join(os.path.dirname(sys.argv[0]), "ffmpeg") + if os.path.exists(subdir): + return os.path.join(subdir, "ffprobe.exe") + return "ffprobe.exe" + +def get_cmd_convert(): + subdir=os.path.join(os.path.dirname(sys.argv[0]), "imagemagick") + if os.path.exists(subdir): + return os.path.join(subdir, "convert.exe") + return "convert.exe" + +def is_recording(): + return proc_ffmpeg != None + +def convert_image(input, output, params): + global font + cmd = get_cmd_convert() + cmd += " " + input + cmd += " " + params + cmd += " " + output + print ("====================================================") + print ("cmd: %s" % cmd) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) + (out, error) = p.communicate() + print("output:\n%s" % out) + print("error:\n%s" % error) + +def start(filename, params): + global proc_ffmpeg + + # Kill remaining ffmpeg instances + stop() + # execute ffmpeg cmd + cmd = get_cmd_ffmpeg() + cmd += " " + params + cmd += " " + filename + print ("====================================================") + print ("cmd: %s" % cmd) + proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) + +def record(filename, resolution=""): + if resolution != "": + start(filename, " -f gdigrab -framerate 20 -i desktop -y -vf scale=%s -v:q 1" % (resolution)) + else: + start(filename, " -f gdigrab -framerate 20 -i desktop -y -v:q 1") + +def stop(): + global proc_ffmpeg + # Kill remaining ffmpeg instances + if proc_ffmpeg: + ctypes.windll.kernel32.TerminateProcess(int(proc_ffmpeg._handle), -1) + proc_ffmpeg.kill() + proc_ffmpeg=None + +def video_duration(filename): + cmd = get_cmd_ffprobe() + cmd += " -i %s" % filename + cmd += " -show_entries format=duration" + print ("====================================================") + print ("cmd: %s" % cmd) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) + (output, error) = p.communicate() + print("duration output: %s - %s" % (output, error)) + durations = re.findall(r'duration=[0-9.]*', str(output)) + for d in durations: + duration = d.split("=")[1] + print("duration string: %s" % duration) + print("duration: %f" % float(duration)) + return float(duration) + +def wait(): + global proc_ffmpeg + if proc_ffmpeg: + (out, error) = proc_ffmpeg.communicate() + print("output:\n%s" % out) + print("error:\n%s" % error) + proc_ffmpeg = None + +class renderer: + pconcat="" + nconcat=0 + vidfilter=[] + vidresolution="" + lastfilename="" + tmppath="" + framerate=20 + qfilter=" -v:q 1 " + + def __init__(self): + self.tmppath = os.path.join("videos", "tmp") + self.framerate = 20 + + def add_text(self, filename, duration=5): + f = open(filename, "r") + if f: + content = f.read() + f.close() + if content.replace("\n", "") == "": + return + + params = " -scale %s!" % self.vidresolution.replace(":", "x") + params += " -font %s -weight 500 -pointsize 75" % font + params += " -draw \"gravity northwest fill white text 100,650 '%s'\"" % content + params += " -scale %s!" % self.vidresolution.replace(":", "x") + image = os.path.splitext(filename)[0] + ".png" + convert_image(os.path.join("media", "back.png"), image, params) + self.add_image(image, duration) + return content + + def add_image(self, filename, duration=5, fadein=True): + print(self.tmppath) + tmpfilename = os.path.join(self.tmppath, os.path.basename(filename) + ".mkv") + params = " -y -loop 1 -t %d -i %s" % (duration, filename) + if fadein: + params += " -filter_complex \"fade=in:0:d=1[v];[v]fade=out:st=%f:d=2\"" % ((duration-2)) + else: + params += " -filter_complex \"fade=out:st=%f:d=1\"" % ((duration-3)) + params += self.qfilter + start(tmpfilename, params) + wait() + + self.pconcat += " -i %s" % (tmpfilename) + self.nconcat+=1 + + def add_video(self, filename): + # fade video temporarily + tmpfilename = os.path.join(self.tmppath, os.path.basename(filename)) + (w,h) = self.vidresolution.split(":") + params = " -y -i %s -filter_complex \"scale=-1:%s[v];[v]crop=%s:0:0[v];[v]fade=in:0:10\"" % (filename, h, self.vidresolution) + params += self.qfilter + start(tmpfilename, params) + wait() + + self.pconcat += " -i %s" % (tmpfilename) + self.nconcat+=1 + + def speedup(self, filename, pts=0.7): + params = " -i %s -y -filter:v \"setpts=%f*PTS\"" % (self.lastfilename, pts) + params += self.qfilter + self.lastfilename = filename + start(filename, params) + wait() + + def mux(self, filename, audiofile): + duration = video_duration(self.lastfilename) + params = " -i %s -i %s -y" % (self.lastfilename, audiofile) + params += " -t %d" % (int(duration)+1) + params += " -af \"afade=out:st=%f:d=3\"" % (duration-3.0) + params += " -map 0:v:0 -map 1:a:0" + params += self.qfilter + self.lastfilename = filename + start(filename, params) + wait() + + def filter(self, filter): + self.vidfilter = filter + + def resolution(self, resolution): + self.vidresolution = resolution + + def concat(self, filename, audio=False): + if self.lastfilename != "": + self.nconcat += 1 + self.pconcat = "-i %s %s " % (self.lastfilename, self.pconcat) + + if audio: + a=1 + else: + a=0 + if len(self.vidfilter) > 0: + params = self.pconcat + " -y -filter_complex \"concat=n=%d:v=1:a=%d,%s\"" % (a, self.nconcat, ",".join(self.vidfilter)) + else: + params = self.pconcat + " -y -filter_complex concat=n=%d:v=1:a=%d" % (self.nconcat, a) + self.lastfilename = filename + params += self.qfilter + start(filename, params) + wait() + + def filename(self, filename): + self.lastfilename = filename + + def framerate(self, framerate): + self.framerate = framerate + + def tmppath(self, path): + self.tmppath = path + + def process(self, filename, param): + params = " -y -i %s %s " % (self.lastfilename, param) + self.lastfilename = filename + params += self.qfilter + start(filename, params) + wait() + + +