a | b/trunk/tubetutor/ffmpeg.py | ||
---|---|---|---|
1 | # |
||
2 | # Wrapper for the ffmpeg utilities |
||
3 | # |
||
4 | # Default search path is relatively to the main script that is called: |
||
5 | # <script path>/ffmpeg |
||
6 | # |
||
7 | # Otherwise, the windows search path is used |
||
8 | # |
||
9 | import subprocess |
||
10 | import ctypes |
||
11 | import sys |
||
12 | import os |
||
13 | import re |
||
14 | |||
15 | #font="Arial" |
||
16 | font=os.path.join("media", "Roboto-Regular.ttf") |
||
17 | proc_ffmpeg=None |
||
18 | |||
19 | # |
||
20 | # The following helper functions will retrieve the pathnames for the ffmpeg tools |
||
21 | # |
||
22 | def get_cmd_ffmpeg(): |
||
23 | subdir=os.path.join(os.path.dirname(sys.argv[0]), "ffmpeg") |
||
24 | if os.path.exists(subdir): |
||
25 | return os.path.join(subdir, "ffmpeg.exe") |
||
26 | return "ffmpeg.exe" |
||
27 | |||
28 | def get_cmd_ffprobe(): |
||
29 | subdir=os.path.join(os.path.dirname(sys.argv[0]), "ffmpeg") |
||
30 | if os.path.exists(subdir): |
||
31 | return os.path.join(subdir, "ffprobe.exe") |
||
32 | return "ffprobe.exe" |
||
33 | |||
34 | def get_cmd_convert(): |
||
35 | subdir=os.path.join(os.path.dirname(sys.argv[0]), "imagemagick") |
||
36 | if os.path.exists(subdir): |
||
37 | return os.path.join(subdir, "convert.exe") |
||
38 | return "convert.exe" |
||
39 | |||
40 | def is_recording(): |
||
41 | return proc_ffmpeg != None |
||
42 | |||
43 | def convert_image(input, output, params): |
||
44 | global font |
||
45 | cmd = get_cmd_convert() |
||
46 | cmd += " " + input |
||
47 | cmd += " " + params |
||
48 | cmd += " " + output |
||
49 | print ("====================================================") |
||
50 | print ("cmd: %s" % cmd) |
||
51 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) |
||
52 | (out, error) = p.communicate() |
||
53 | print("output:\n%s" % out) |
||
54 | print("error:\n%s" % error) |
||
55 | |||
56 | def start(filename, params): |
||
57 | global proc_ffmpeg |
||
58 | |||
59 | # Kill remaining ffmpeg instances |
||
60 | stop() |
||
61 | # execute ffmpeg cmd |
||
62 | cmd = get_cmd_ffmpeg() |
||
63 | cmd += " " + params |
||
64 | cmd += " " + filename |
||
65 | print ("====================================================") |
||
66 | print ("cmd: %s" % cmd) |
||
67 | proc_ffmpeg = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) |
||
68 | |||
69 | def record(filename, resolution=""): |
||
70 | if resolution != "": |
||
71 | start(filename, " -f gdigrab -framerate 20 -i desktop -y -vf scale=%s -v:q 1" % (resolution)) |
||
72 | else: |
||
73 | start(filename, " -f gdigrab -framerate 20 -i desktop -y -v:q 1") |
||
74 | |||
75 | def stop(): |
||
76 | global proc_ffmpeg |
||
77 | # Kill remaining ffmpeg instances |
||
78 | if proc_ffmpeg: |
||
79 | ctypes.windll.kernel32.TerminateProcess(int(proc_ffmpeg._handle), -1) |
||
80 | proc_ffmpeg.kill() |
||
81 | proc_ffmpeg=None |
||
82 | |||
83 | def video_duration(filename): |
||
84 | cmd = get_cmd_ffprobe() |
||
85 | cmd += " -i %s" % filename |
||
86 | cmd += " -show_entries format=duration" |
||
87 | print ("====================================================") |
||
88 | print ("cmd: %s" % cmd) |
||
89 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) |
||
90 | (output, error) = p.communicate() |
||
91 | print("duration output: %s - %s" % (output, error)) |
||
92 | durations = re.findall(r'duration=[0-9.]*', str(output)) |
||
93 | for d in durations: |
||
94 | duration = d.split("=")[1] |
||
95 | print("duration string: %s" % duration) |
||
96 | print("duration: %f" % float(duration)) |
||
97 | return float(duration) |
||
98 | |||
99 | def wait(): |
||
100 | global proc_ffmpeg |
||
101 | if proc_ffmpeg: |
||
102 | (out, error) = proc_ffmpeg.communicate() |
||
103 | print("output:\n%s" % out) |
||
104 | print("error:\n%s" % error) |
||
105 | proc_ffmpeg = None |
||
106 | |||
107 | class renderer: |
||
108 | pconcat="" |
||
109 | nconcat=0 |
||
110 | vidfilter=[] |
||
111 | vidresolution="" |
||
112 | lastfilename="" |
||
113 | tmppath="" |
||
114 | framerate=20 |
||
115 | qfilter=" -v:q 1 " |
||
116 | |||
117 | def __init__(self): |
||
118 | self.tmppath = os.path.join("videos", "tmp") |
||
119 | self.framerate = 20 |
||
120 | |||
121 | def add_text(self, filename, duration=5): |
||
122 | f = open(filename, "r") |
||
123 | if f: |
||
124 | content = f.read() |
||
125 | f.close() |
||
126 | if content.replace("\n", "") == "": |
||
127 | return |
||
128 | |||
129 | params = " -scale %s!" % self.vidresolution.replace(":", "x") |
||
130 | params += " -font %s -weight 500 -pointsize 75" % font |
||
131 | params += " -draw \"gravity northwest fill white text 100,650 '%s'\"" % content |
||
132 | params += " -scale %s!" % self.vidresolution.replace(":", "x") |
||
133 | image = os.path.splitext(filename)[0] + ".png" |
||
134 | convert_image(os.path.join("media", "back.png"), image, params) |
||
135 | self.add_image(image, duration) |
||
136 | return content |
||
137 | |||
138 | def add_image(self, filename, duration=5, fadein=True): |
||
139 | print(self.tmppath) |
||
140 | tmpfilename = os.path.join(self.tmppath, os.path.basename(filename) + ".mkv") |
||
141 | params = " -y -loop 1 -t %d -i %s" % (duration, filename) |
||
142 | if fadein: |
||
143 | params += " -filter_complex \"fade=in:0:d=1[v];[v]fade=out:st=%f:d=2\"" % ((duration-2)) |
||
144 | else: |
||
145 | params += " -filter_complex \"fade=out:st=%f:d=1\"" % ((duration-3)) |
||
146 | params += self.qfilter |
||
147 | start(tmpfilename, params) |
||
148 | wait() |
||
149 | |||
150 | self.pconcat += " -i %s" % (tmpfilename) |
||
151 | self.nconcat+=1 |
||
152 | |||
153 | def add_video(self, filename): |
||
154 | # fade video temporarily |
||
155 | tmpfilename = os.path.join(self.tmppath, os.path.basename(filename)) |
||
156 | (w,h) = self.vidresolution.split(":") |
||
157 | 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) |
||
158 | params += self.qfilter |
||
159 | start(tmpfilename, params) |
||
160 | wait() |
||
161 | |||
162 | self.pconcat += " -i %s" % (tmpfilename) |
||
163 | self.nconcat+=1 |
||
164 | |||
165 | def speedup(self, filename, pts=0.7): |
||
166 | params = " -i %s -y -filter:v \"setpts=%f*PTS\"" % (self.lastfilename, pts) |
||
167 | params += self.qfilter |
||
168 | self.lastfilename = filename |
||
169 | start(filename, params) |
||
170 | wait() |
||
171 | |||
172 | def mux(self, filename, audiofile): |
||
173 | duration = video_duration(self.lastfilename) |
||
174 | params = " -i %s -i %s -y" % (self.lastfilename, audiofile) |
||
175 | params += " -t %d" % (int(duration)+1) |
||
176 | params += " -af \"afade=out:st=%f:d=3\"" % (duration-3.0) |
||
177 | params += " -map 0:v:0 -map 1:a:0" |
||
178 | params += self.qfilter |
||
179 | self.lastfilename = filename |
||
180 | start(filename, params) |
||
181 | wait() |
||
182 | |||
183 | def filter(self, filter): |
||
184 | self.vidfilter = filter |
||
185 | |||
186 | def resolution(self, resolution): |
||
187 | self.vidresolution = resolution |
||
188 | |||
189 | def concat(self, filename, audio=False): |
||
190 | if self.lastfilename != "": |
||
191 | self.nconcat += 1 |
||
192 | self.pconcat = "-i %s %s " % (self.lastfilename, self.pconcat) |
||
193 | |||
194 | if audio: |
||
195 | a=1 |
||
196 | else: |
||
197 | a=0 |
||
198 | if len(self.vidfilter) > 0: |
||
199 | params = self.pconcat + " -y -filter_complex \"concat=n=%d:v=1:a=%d,%s\"" % (a, self.nconcat, ",".join(self.vidfilter)) |
||
200 | else: |
||
201 | params = self.pconcat + " -y -filter_complex concat=n=%d:v=1:a=%d" % (self.nconcat, a) |
||
202 | self.lastfilename = filename |
||
203 | params += self.qfilter |
||
204 | start(filename, params) |
||
205 | wait() |
||
206 | |||
207 | def filename(self, filename): |
||
208 | self.lastfilename = filename |
||
209 | |||
210 | def framerate(self, framerate): |
||
211 | self.framerate = framerate |
||
212 | |||
213 | def tmppath(self, path): |
||
214 | self.tmppath = path |
||
215 | |||
216 | def process(self, filename, param): |
||
217 | params = " -y -i %s %s " % (self.lastfilename, param) |
||
218 | self.lastfilename = filename |
||
219 | params += self.qfilter |
||
220 | start(filename, params) |
||
221 | wait() |
||
222 | |||
223 | |||
224 |