--- a/trunk/tubetutor/ffmpeg.py +++ b/trunk/tubetutor/ffmpeg.py @@ -19,224 +19,233 @@ proc_ffmpeg=None if platform.system() == "Linux": - grabber="-video_size 1920x1080 -f x11grab -framerate 20 -i :0.0" + grabber="-video_size 1920x1080 -f x11grab -framerate 20 -i :0.0 -f pulse -i 1" else: - grabber="-f gdigrab -framerate 20 -i desktop" + grabber="-f gdigrab -framerate 20 -i desktop" # # 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" + subdir=os.path.join(os.path.dirname(sys.argv[0]), "ffmpeg -loglevel verbose") + if os.path.exists(subdir): + return os.path.join(subdir, "ffmpeg.exe -loglevel verbose") + return "ffmpeg -loglevel verbose" 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" + 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" 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" + 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" def is_recording(): - return proc_ffmpeg != None + 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=True) - (out, error) = p.communicate() - print("output:\n%s" % out) - print("error:\n%s" % error) + 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=True) + (out, error) = p.communicate() + if p.returncode != 0: + raise Exception('error: command failed: %s' % cmd) + 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) - if platform.system() == "Linux": - proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) - else: - proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) + global proc_ffmpeg + + # Kill remaining ffmpeg instances + stop() + # execute ffmpeg cmd + cmd = get_cmd_ffmpeg() + cmd += " " + params + cmd += " " + filename + print ("====================================================") + print ("cmd: %s" % cmd) + if platform.system() == "Linux": + proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) + else: + proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) def record(filename, resolution=""): - if resolution != "": - start(filename, " %s -y -vf scale=%s -v:q 1" % (grabber, resolution)) - else: - start(filename, " %s -y -v:q 1" % (grabber)) + if resolution != "": + start(filename, " %s -y -vf scale=%s -v:q 1" % (grabber, resolution)) + else: + start(filename, " %s -y -v:q 1" % (grabber)) def stop(): - global proc_ffmpeg - # Kill remaining ffmpeg instances - if proc_ffmpeg: - try: - ctypes.windll.kernel32.TerminateProcess(int(proc_ffmpeg._handle), -1) - except: - os.killpg(os.getpgid(proc_ffmpeg.pid), signal.SIGTERM) - proc_ffmpeg.kill() - proc_ffmpeg=None - + global proc_ffmpeg + # Kill remaining ffmpeg instances + if proc_ffmpeg: + try: + ctypes.windll.kernel32.TerminateProcess(int(proc_ffmpeg._handle), -1) + except: + os.killpg(os.getpgid(proc_ffmpeg.pid), signal.SIGTERM) + 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=True) - (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) + 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=True) + (output, error) = p.communicate() + if p.returncode != 0: + raise Exception('error: command failed: %s' % cmd) + 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 + 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) - tmpimage = os.path.join(self.tmppath, os.path.basename(filename) + ".png") - tmpfilename = os.path.join(self.tmppath, os.path.basename(filename) + ".mkv") - params = " -scale %s!" % self.vidresolution.replace(":", "x") - convert_image(filename, tmpimage, params) - - params = " -y -loop 1 -t %d -i %s" % (duration, tmpimage) - 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, "add_" + 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): - print("process %s -> %s" % (self.lastfilename, filename)) - params = " -y -i %s %s " % (self.lastfilename, param) - self.lastfilename = filename - params += self.qfilter - start(filename, params) - wait() - - - + 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("*** add_image(%s, %d, %s)" % (filename, duration, fadein)) + tmpimage = os.path.join(self.tmppath, os.path.basename(filename) + ".png") + tmpfilename = os.path.join(self.tmppath, os.path.basename(filename) + ".mkv") + params = " -scale %s!" % self.vidresolution.replace(":", "x") + convert_image(filename, tmpimage, params) + + params = " -y -loop 1 -t %d -i %s" % (duration, tmpimage) + # add silent audio + params += " -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -shortest" + 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): + print("*** add_video(%s)" % (filename)) + # fade video temporarily + tmpfilename = os.path.join(self.tmppath, "add_" + 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): + print("*** speedup(%s, %f)" % (filename, pts)) + 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): + print("*** mux(%s, %s)" % (filename, audiofile)) + duration = video_duration(self.lastfilename) + params = " -i %s -i %s -y" % (self.lastfilename, audiofile) + params += " -t %d" % (int(duration)+1) + params += " -filter_complex \"[0:a][1:a]amerge,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): + print("process %s -> %s" % (self.lastfilename, filename)) + params = " -y -i %s %s " % (self.lastfilename, param) + self.lastfilename = filename + params += self.qfilter + start(filename, params) + wait() + + +