示例#1
0
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"