Diff of /trunk/tubetutor/ffmpeg.py [r8] .. [r9]  Maximize  Restore

Switch to unified view

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