class Video(object): """ Video manipulation Open video, create effects, save video. """ def __init__(self, origin, duration=None, get_audio=True, path="/tmp"): self.assets = {} self.path = origin self.temp = path if duration is None: self.width, self.heigth, self.duration = self._get_video_info() else: self.duration = duration self.freeze_in_time = 0 self.freeze_out_time = self.duration if get_audio is True: self.audio = Audio(self._get_audio_from_video(), path=self.temp) def call(self, fx_name, time): if fx_name == "fade_in": self.fade_in(time) if fx_name == "fade_out": self.fade_out(time) if fx_name == "freeze_in": self.freeze_in(time) if fx_name == "freeze_out": self.freeze_out(time) return self def overlay(self, overlayable, x=0, y=0, start=0, end=0, duration=0): """ DEPRECATED Merges the mask generate by overlable to the video stream when applicable. Args: overlayable -- instance of any class that implements Overlayable Keyword args: x, y -- relative starting position [0 ~ ) start -- time in seconds when starts to be applicable [0.0 ~ ) end -- time in seconds when stops to be applicable [0.0 ~ ) duration -- time in seconds when stops to be applicable after start [0.0 ~ ) it always prevails over 'end' If both 'end' and 'duration' are 0.0 there is no upper limit. """ out_path = os.path.join(self._get_tmp_folder(), self._get_tmp_file()) save = Saver(out_path) this_stream = MPlayer(self.path) applyable_min = int(start * 25) inf = False if end == 0 and duration == 0: inf = True elif duration == 0 and end != 0: applyable_max = int(ceil(end * 25)) else: applyable_max = applyable_min + int(ceil(duration * 25)) for count, frame in enumerate(this_stream.next_frame()): if inf: applyable_max = count + 1 if applyable_min <= count <= applyable_max: overlayable.run(frame, x=x, y=y) save.dump_frame(frame) save.close_file() output_video = Video(out_path, duration=self.duration, get_audio=False, path=self.temp) output_video.audio = self.audio output_video._join_audio() return output_video def fade_in(self, time): """ Fades from black 'time' seconds Args: time -- seconds as float [0.0 ~ ) """ self.audio = self.audio.fade_in(time) end = int(ceil(time * 25)) fade = Fade(duration=(time - 0.01), initial=100) for index in range(0, end + 1): self.assets[index] = fade return self def fade_out(self, time): """ Fades to black the last 'time' seconds Args: time -- seconds as float [0.0 ~ ) """ self.audio = self.audio.fade_out(time) end = int(ceil(self.duration * 25)) start = end - int(ceil(time * 25)) fade = Fade(duration=(time - 0.1), initial=0) for index in range(start, end + 1): self.assets[index] = fade return self def freeze_in(self, time): """ Freeze first frame for "time" seconds Args: time -- seconds as float [0.0 ~ ) """ self.duration += time self.freeze_out_time = int(ceil(self.duration * 25)) self.audio = self.audio.set_start(start=time) self.freeze_in_time = int(ceil(time * 25)) return self def freeze_out(self, time): """ Freeze last frame for "time" seconds Args: time -- seconds as float [0.0 ~ ) """ self.duration += time self.freeze_out_time = int(ceil(25 * self.duration)) return self def _join_audio(self): paths = self.path.split('.') final_path = '.'.join(paths[:-1]) + "mix." + paths[-1] if self.duration < self.audio.duration: self.audio = self.audio.trim(0, self.duration) command = "ffmpeg -i %s -i %s -sameq %s >/dev/null" % (self.audio.path, self.path, final_path) p = subprocess.Popen(command, shell=True, bufsize=-1, stderr=subprocess.PIPE) p.wait() self.path = final_path def _delete_audio_from_video(self): out_path = os.path.join(self._get_tmp_folder(), self._get_tmp_file()) command = "ffmpeg -i %s -an %s >/dev/null" % (self.path, out_path) p = subprocess.Popen(command, shell=True, bufsize=-1, stderr=subprocess.PIPE) p.wait() return out_path def _get_audio_from_video(self): audio_re = re.compile("Output file #0 does not contain any stream") paths = self.path.split('.') out_path = '.'.join(paths[:-1]) + ".wav" command = "ffmpeg -y -i %s" % (self.path) command += " -vn -ar 44100 -ac 2 -ab 192 -f wav " command += out_path + " >/dev/null" p = subprocess.Popen(command, shell=True, bufsize=-1, stderr=subprocess.PIPE) output = p.stderr.read() if audio_re.search(output) is not None: command = "sox -n -r 44100 -c 2 %s trim 0.0 %s" % (out_path, self.duration) p = subprocess.Popen(command, shell=True, bufsize=-1, stderr=subprocess.PIPE) p.wait() if p.returncode != 0: raise Exception("Sox failed!") return out_path def _get_video_info(self): p = subprocess.Popen("ffmpeg -i %s >/dev/null" % self.path, shell=True, bufsize=64, stderr=subprocess.PIPE) output = p.stderr.read() m = duration_re.search(output) duration_str = m.groups()[0] h, m, s = duration_str.split(':') duration = float(h) * 60 * 60 + float(m) * 60 + float(s) m = ratio_re.search(output) w, h = [int(x) for x in m.groups()] return w, h, duration def _get_tmp_folder(self): """ Generate temporal path for video """ date = datetime.now() now = date.strftime('%Y%m%d%H%M%S%f') path = os.path.join(self.temp, now) try: os.makedirs(path) except: pass return path def _get_tmp_file(self): r = random.randint(0, 10000) return str(r) + ".mpg"