Diff of /trunk/tubetutor/ffmpeg.py [000000] .. [r1]  Maximize  Restore

Switch to unified view

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