def concat(person, clips): """ Concatenates the the input clips into an output video for a given person Parameters -------------------- clips -- Array of clip names excluding filetype as we assume them to be .mp4 Returns -------------------- returns nothing but creates an output video """ #create workspace for less clutter if os.path.exists("workspacets"): shutil.rmtree("workspacets") #clear workspace and remake it os.makedirs("workspacets") else: os.makedirs("workspacets") #ffmpeg setup and calls concat_param = "concat:" first = True for clip in clips: cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", clips_path + "/" + person + "/" + clip + "/1.mp4", "-c", "copy", "-bsf:v", "h264_mp4toannexb", "-f", "mpegts", "workspacets/" + clip + ".ts" ] try: subprocess_call(cmd, False, False) except: print("Oops! " + person.title() + " doesn't know the word '" + clip + "'") return clip if first: concat_param = concat_param + "workspacets/" + clip + ".ts" first = False else: concat_param = concat_param + "|" + "workspacets/" + clip + ".ts" fcmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", concat_param, "-c", "copy", "-bsf:a", "aac_adtstoasc", output_folder + "/tmp.mp4" ] subprocess_call(fcmd, False) return ""
def ffmpeg_write_image(filename, image, logfile=False): """ Writes an image (HxWx3 or HxWx4 numpy array) to a file, using ffmpeg. """ if image.dtype != 'uint8': image = image.astype("uint8") cmd = [ get_setting("FFMPEG_BINARY"), '-y', '-s', "%dx%d"%(image.shape[:2][::-1]), "-f", 'rawvideo', '-pix_fmt', "rgba" if (image.shape[2] == 4) else "rgb24", '-i','-', filename] if logfile: log_file = open(filename + ".log", 'w+') else: log_file = sp.PIPE popen_params = {"stdout": DEVNULL, "stderr": log_file, "stdin": sp.PIPE} if os.name == "nt": popen_params["creationflags"] = 0x08000000 proc = sp.Popen(cmd, **popen_params) out, err = proc.communicate(image.tostring()) if proc.returncode: err = "\n".join(["[MoviePy] Running : %s\n" % cmd, "WARNING: this command returned an error:", err.decode('utf8')]) raise IOError(err) del proc
def trim_vid(self, path_in, path_out, start, stop): """ takes a video located at path_in, segments it between start and stop and saves the new video at path_out If the curious code-reader desires to know what the variables (e.g. -ss, -i) in cmd mean, refer to https://www.ffmpeg.org/ffmpeg.html#Advanced-options WARNING ------- read https://www.ffmpeg.org/ffmpeg.html#Advanced-options before modifying cmd variable in the slightest. Seriously. Super srsly. F****d up this function thrice now. Srsly don't change argument assignment order without INTENT, order of parsed arguments dictacte how the arguments behavior with this interface """ # seriously don't modify the order cmd = [ get_setting(varname="FFMPEG_BINARY"), '-ss', '00:{}:00'.format(start), '-t', '{}'.format(300), '-i', '{}'.format(path_in), '-c:v', 'copy', '-an', '{}'.format(path_out) ] # Trim the video proc = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = proc.communicate()[0].decode() try: self.pkill() except: pass
def ffmpeg_extract_subclip(filename, start, end, targetname=None): """ Creates a new video file playing video file "filename" between the times "start" and "end". Parameters -------------------- filename -- path start -- time to start cutting from end -- time to finish cutting to targetname -- output file name (not used) Returns -------------------- nothing """ """ Note: This function is from the moviepy library but it was buggy so we fixed it here""" name, ext = os.path.splitext(filename) if not targetname: T1, T2 = [int(1000 * t) for t in [start, end]] targetname = name + "%sSUB%d_%d.%s" (name, T1, T2, ext) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-ss", "%0.2f" % start, "-t", "%0.2f" % (end - start), "-i", filename, "-c:v", "libx264", "-x264opts", "keyint=1:min-keyint=1:scenecut=-1", targetname ] subprocess_call(cmd, False)
def _ffmpeg_extract_subclip(self, filename, t1, t2, targetname=None): """ makes a new video file playing video file ``filename`` between the times ``t1`` and ``t2``. :param filename: path of video file :type filename: str, required :param t1: time from where video to clip :type t1: float, required :param t2: time to which video to clip :type t2: float, required :param targetname: path where clipped file to be stored :type targetname: str, optional :return: None """ name, ext = os.path.splitext(filename) if not targetname: T1, T2 = [int(1000 * t) for t in [t1, t2]] targetname = name + "%sSUB%d_%d.%s" (name, T1, T2, ext) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", filename, "-ss", "%0.2f" % t1, "-t", "%0.2f" % (t2 - t1), "-vcodec", "copy", "-acodec", "copy", targetname ] subprocess_call(cmd, logger=None, errorprint=True)
def __init__(self, filename, size, fps, codec="libx264", audiofile=None, preset="ultrafast", bitrate=None, withmask=False, logfile=None, threads=2, ffmpeg_params=None): if logfile is None: logfile = sp.PIPE self.filename = filename self.codec = codec self.ext = self.filename.split(".")[-1] # order is important cmd = [ get_setting("FFMPEG_BINARY"), '-y', '-loglevel', 'error' if logfile == sp.PIPE else 'info', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-s', '%dx%d' % (size[0], size[1]), '-pix_fmt', 'rgba' if withmask else 'rgb24', '-r', '%.02f' % fps, '-i', '-', '-an', ] if audiofile is not None: cmd.extend([ '-i', audiofile, '-acodec', 'copy' ]) cmd.extend([ '-vcodec', codec, '-preset', preset, ]) if ffmpeg_params is not None: cmd.extend(ffmpeg_params) if bitrate is not None: cmd.extend([ '-b', bitrate ]) if threads is not None: cmd.extend(["-threads", str(threads)]) if ((codec == 'libx264') and (size[0] % 2 == 0) and (size[1] % 2 == 0)): cmd.extend([ '-pix_fmt', 'yuv420p' ]) cmd.extend([ filename ]) popen_params = {"stdout": DEVNULL, "stderr": logfile, "stdin": sp.PIPE} # This was added so that no extra unwanted window opens on windows # when the child process is created if os.name == "nt": popen_params["creationflags"] = 0x08000000 # CREATE_NO_WINDOW self.proc = sp.Popen(cmd, **popen_params)
def initialize(self, starttime = 0): """ Opens the file, creates the pipe. """ self.close_proc() # if any if starttime !=0 : offset = min(1,starttime) i_arg = ["-ss", "%.05f"%(starttime-offset), '-i', self.filename, '-vn', "-ss", "%.05f"%offset] else: i_arg = [ '-i', self.filename, '-vn'] cmd = ([get_setting("FFMPEG_BINARY")] + i_arg + [ '-loglevel', 'error', '-f', self.f, '-acodec', self.acodec, '-ar', "%d"%self.fps, '-ac', '%d'%self.nchannels, '-']) popen_params = {"bufsize": self.buffersize, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL} if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen( cmd, **popen_params) self.pos = np.round(self.fps*starttime)
def __init__(self, filename, fps_input, nbytes=2, nchannels=2, codec='libfdk_aac', bitrate=None, input_video=None, logfile=None, ffmpeg_params=None): self.filename = filename self.codec = codec if logfile is None: logfile = sp.PIPE cmd = ([get_setting("FFMPEG_BINARY"), '-y', "-loglevel", "error" if logfile == sp.PIPE else "info", "-f", 's%dle' % (8*nbytes), "-acodec",'pcm_s%dle' % (8*nbytes), '-ar', "%d" % fps_input, '-ac', "%d" % nchannels, '-i', '-'] + (['-vn'] if input_video is None else ["-i", input_video, '-vcodec', 'copy']) + ['-acodec', codec] + ['-ar', "%d" % fps_input] + ['-strict', '-2'] # needed to support codec 'aac' + (['-ab', bitrate] if (bitrate is not None) else []) + (ffmpeg_params if ffmpeg_params else []) + [filename]) popen_params = {"stdout": DEVNULL, "stderr": logfile, "stdin": sp.PIPE} if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def ffmpeg_extract_subclip(filename, t1, t2, targetname=None): """ Makes a new video file playing video file ``filename`` between the times ``t1`` and ``t2``. """ name, ext = os.path.splitext(filename) if not targetname: T1, T2 = [int(1000 * t) for t in [t1, t2]] targetname = "%sSUB%d_%d.%s" % (name, T1, T2, ext) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-ss", "%0.2f" % t1, "-i", filename, "-t", "%0.2f" % (t2 - t1), "-map", "0", "-vcodec", "copy", "-acodec", "copy", targetname, ] subprocess_call(cmd)
def initialize(self, starttime=0.): """Opens the file, creates the pipe. """ if starttime != 0: offset = min(1, starttime) i_arg = [ '-ss', "%.06f" % (starttime - offset), '-i', self.filename, '-ss', "%.06f" % offset ] else: i_arg = ['-i', self.filename] # print get_setting("FFMPEG_BINARY") cmd = ([get_setting("FFMPEG_BINARY")] + i_arg + [ '-loglevel', 'error', '-f', 'image2pipe', "-pix_fmt", "rgb24", '-vcodec', 'rawvideo', '-' ]) popen_params = { "bufsize": self.bufsize, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL } if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def show(self): # Colors for plotting colors = ArgusColors().getMatplotlibColors() # Shuffle to make things interesting random.shuffle(colors) for k in range(len(self.files)): # If we don't find a wav with the same name as the file, rip one using moviepy's ffmpeg binary if not os.path.isfile(self.tmpName + '/' + self.out[k]): print('Ripping audio from file number ' + str(k + 1) + ' of ' + str(len(self.files))) sys.stdout.flush() cmd = [ get_setting("FFMPEG_BINARY"), '-loglevel', 'panic', '-hide_banner', '-i', self.files[k], '-ac', '1', '-codec', 'pcm_s16le', self.tmpName + '/' + self.out[k] ] subprocess.call(cmd) else: print('Found audio from file number ' + str(k + 1) + ' of ' + str(len(self.files))) sys.stdout.flush() # Put the full signals in a list signals_ = list() print('Reading waves and displaying...') sys.stdout.flush() for k in range(len(self.files)): rate, signal = scipy.io.wavfile.read(self.tmpName + '/' + self.out[k]) signals_.append(signal) # Make a new list of signals but only using ever 100th sample signals = list() for k in range(len(signals_)): t = list() a = 0 while a < len(signals_[k]): t.append(signals_[k][a]) a += 100 signals.append(np.asarray(t)) a = 0 patches = list() width = 35 height = 3 * len(signals) plt.figure(figsize=(width, height)) frame1 = plt.gca() frame1.axes.get_yaxis().set_visible(False) # Make a plot with colors chosen by circularly pulling from the colors vector for k in range(len(signals)): color = colors[k % len(colors)] patches.append( mpatches.Patch(color=color, label=self.files[k].split('/')[-1])) t = np.linspace(0, len(signals_[k]) / 48000., num=len( signals[k])) / 60. plt.plot(t, signals[k] + float(a), color=color) a += np.nanmax(signals[k]) * 2 plt.legend(handles=patches) plt.title('Audio Streams') plt.xlabel('Minutes') signals_ = None plt.show()
def ffmpeg_resize(video,output,size): """ resizes ``video`` to new size ``size`` and write the result in file ``output``. """ cmd= [get_setting("FFMPEG_BINARY"), "-i", video, "-vf", "scale=%d:%d"%(size[0], size[1]), output] subprocess_call(cmd)
def set_movie(self, fnam, copy=False): if copy: self.copy_tmp = tempfile.mkdtemp() cmd = [ get_setting("FFMPEG_BINARY"), '-loglevel', 'panic', '-hide_banner', '-i', fnam, '-acodec', 'copy', '-vcodec', 'copy', os.path.join(self.copy_tmp, 'copy.mp4')] print('Copying video and audio codecs...') sys.stdout.flush() subprocess.call(cmd) print('Copy completed...') self.movie = cv2.VideoCapture(os.path.abspath(os.path.join(self.copy_tmp, 'copy.mp4'))) print('Using movie at {0}'.format(os.path.join(self.copy_tmp, 'copy.mp4'))) else: self.movie = cv2.VideoCapture(os.path.abspath(fnam)) print('Using movie at ' + fnam) self.infilename = fnam try: self.frameCount = int(self.movie.get(cv2.CAP_PROP_FRAME_COUNT)) self.fps = float(self.movie.get(cv2.CAP_PROP_FPS)) self.w = int(self.movie.get(cv2.CAP_PROP_FRAME_WIDTH)) self.h = int(self.movie.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.frame_msec = 1000. / self.fps return self.fps, self.frameCount except: raise ArgusError('could not read specified movie')
def copy(video, output, fps, verbose): """Create a copy of a video with different settings. Currently, this can be used to change the frame rate, but hopefully this will later support other tasks like changing the resolution.""" import subprocess from moviepy.video.io.ffmpeg_reader import ffmpeg_parse_infos from moviepy.config import get_setting if verbose: logging.getLogger().setLevel(logging.INFO) if fps is None: raise click.BadParameter( 'copy currently only supports changing frame rate.') original_fps = ffmpeg_parse_infos(video)['video_fps'] fps_scale = original_fps / fps cmd = [ get_setting("FFMPEG_BINARY"), "-i", str(video), "-vf", 'setpts={}*PTS'.format(fps_scale), '-r', str(fps), str(output) ] logging.info('Running command: {}'.format(' '.join(cmd))) try: output = subprocess.check_output(cmd, stderr=subprocess.PIPE) except subprocess.CalledProcessError as e: logging.exception('[vid] Command returned an error: ') logging.fatal(e.decode('utf8')) return
def ffmpeg_merge_video_audio( video, audio, output, vcodec="copy", acodec="copy", ffmpeg_output=False, logger="bar", ): """ merges video file ``video`` and audio file ``audio`` into one movie file ``output``. """ cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", audio, "-i", video, "-vcodec", vcodec, "-acodec", acodec, output, ] subprocess_call(cmd, logger=logger)
def list(arg): """Returns a list of all valid entries for the ``font`` or ``color`` argument of ``TextClip``""" popen_params = {"stdout": sp.PIPE, "stderr": sp.DEVNULL, "stdin": sp.DEVNULL} if os.name == "nt": popen_params["creationflags"] = 0x08000000 process = sp.Popen( [get_setting("IMAGEMAGICK_BINARY"), "-list", arg], encoding="utf-8", **popen_params, ) result = process.communicate()[0] lines = result.splitlines() if arg == "font": # Slice removes first 8 characters: " Font: " return [l[8:] for l in lines if l.startswith(" Font:")] elif arg == "color": # Each line is of the format "aqua srgb(0,255,255) SVG" so split on space and take # the first item to get the color name. # The first 5 lines are header information, not colors, so ignore return [l.split(" ")[0] for l in lines[5:]] else: raise Exception("Moviepy Error: Argument must equal 'font' or 'color'")
def initialize(self, starttime=0): """ Opens the file, creates the pipe. """ self.close_proc() # if any if starttime != 0: offset = min(1, starttime) i_arg = [ "-ss", "%.05f" % (starttime - offset), '-i', self.filename, '-vn', "-ss", "%.05f" % offset ] else: i_arg = ['-i', self.filename, '-vn'] cmd = ([get_setting("FFMPEG_BINARY")] + i_arg + [ '-loglevel', 'error', '-f', self.f, '-acodec', self.acodec, '-ar', "%d" % self.fps, '-ac', '%d' % self.nchannels, '-' ]) popen_params = { "bufsize": self.buffersize, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL } if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params) self.pos = np.round(self.fps * starttime)
def __init__(self, filename, fps_input, nbytes=2, nchannels = 2, codec='libfdk_aac', bitrate=None, input_video=None, logfile=None): self.filename = filename self.codec= codec if logfile is None: logfile = sp.PIPE cmd = ([ get_setting("FFMPEG_BINARY"), '-y', "-loglevel", "error" if logfile==sp.PIPE else "info", "-f", 's%dle'%(8*nbytes), "-acodec",'pcm_s%dle'%(8*nbytes), '-ar', "%d"%fps_input, '-ac',"%d"%nchannels, '-i', '-'] + (['-vn'] if input_video is None else [ "-i", input_video, '-vcodec', 'copy']) + ['-acodec', codec] + ['-ar', "%d"%fps_input] + ['-strict', '-2'] # needed to support codec 'aac' + (['-ab',bitrate] if (bitrate is not None) else []) + [ filename ]) popen_params = {"stdout": DEVNULL, "stderr": logfile, "stdin": sp.PIPE} if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def initialize(self, starttime=0): """Opens the file, creates the pipe. """ self.close() # if any if starttime !=0 : offset = min(1, starttime) i_arg = ['-ss', "%.03f" % (starttime - offset), '-i', self.filename, '-ss', "%.03f" % offset] else: i_arg = [ '-i', self.filename] cmd = ([get_setting("FFMPEG_BINARY")]+ i_arg + ['-loglevel', 'error', '-f', 'image2pipe', "-pix_fmt", self.pix_fmt, '-vcodec', 'rawvideo', '-']) popen_params = {"bufsize": self.bufsize, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL} if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def initialize(self, starttime=0): """Opens the file, creates the pipe. """ self.close() # if any if starttime != 0: offset = min(1, starttime) i_arg = [ '-ss', "%.06f" % (starttime - offset), '-i', self.filename, '-ss', "%.06f" % offset ] else: i_arg = ['-i', self.filename] cmd = ([get_setting("FFMPEG_BINARY")] + i_arg + [ '-loglevel', 'error', '-f', 'image2pipe', '-vf', 'scale=%d:%d' % tuple(self.size), '-sws_flags', self.resize_algo, "-pix_fmt", self.pix_fmt, '-vcodec', 'rawvideo', '-' ]) popen_params = { "bufsize": self.bufsize, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL } if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def condense_clip_ffmpeg(orig_vid, silence_threshold=0.1, min_silence_length=0.15, playback_speed=1.1): try: shutil.rmtree('_temp') except FileNotFoundError: pass tempdir = create_dir("_temp") silence_segments = get_silent_segments(clip, silence_threshold, min_silence_length) print("Extracting subclips") clip_start = 0.0 for i, seg in enumerate(silence_segments): fname = f"clipd_{i}.mp4" moviepy.video.io.ffmpeg_tools.ffmpeg_extract_subclip( orig_vid, clip_start, seg[0], str(tempdir / fname)) with open(tempdir / "render.txt", "a") as myfile: myfile.write("file '" + fname + "'" + "\n") clip_start = seg[1] if i % 50 == 0: print(f"extracted subclip {i}") cmd_string = get_setting( "FFMPEG_BINARY" ) + " -f concat -safe 0 -i _temp/render.txt -c copy _temp/concated.mp4" subprocess.run(cmd_string, shell=True) cmd_string = f"{get_setting('FFMPEG_BINARY')} -i _temp/concated.mp4 -filter_complex [0:v]setpts={1.0/playback_speed}*PTS[v];[0:a]atempo={playback_speed}[a] -map [v] -map [a] output.mp4" #cmd_string = f"{get_setting('FFMPEG_BINARY')} -i _temp/concated.mp4 -filter:v setpts={1.0/playback_speed}*PTS output.mp4" subprocess.run(cmd_string, shell=True)
def ffmpeg_extract_audio(inputfile, output, bitrate=3000, fps=44100): """ extract the sound from a video file and save it in ``output`` """ cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", inputfile, "-ab", "%dk" % bitrate, "-ar", "%d" % fps, output ] subprocess_call(cmd)
def crop_vid(self): """ Uses information saved from detect to crop rectangular videos around the circular wells. input: see below for code that generates the input, which is basically just the instance of the class attached to a number from 0 to n where n is the video duration/5. zip([self] * int(self.clip_duration / 5), xrange(int(self.clip_duration / 5))) """ #how long this is going to take p = Pool(self.n_cpus) s1 = time.time() #get the video path path = self.filename name = self.name #get the video pathname #be careful of \\ vs / dirname = os.path.dirname(path) + '\\' dirname2 = os.path.dirname(path) + '\\' + str(name) + '\\' #folder name foldername = self.root #read in the dict of well names and their x and y positions FIX THE NAMES d = self.load_obj(dirname + 'well_dictionary') c = self.load_obj(dirname + 'well_circles') os.chdir(self.root) #iterate through the wells and parse the videos for index, (x, y, r) in enumerate(c): X1, Y1 = x - r - 20, y - r - 20 # Top left X2, Y2 = x + r + 20, y + r + 20 # Bottom right side = X2 - X1 label = str(d[str(x) + '_' + str(y)]) print('---') print(label) #ffmpeg_crop_out = foldername + '{}\\'.format(label) + os.path.basename(path) ffmpeg_crop_out = dirname2 + '{}\\'.format(label) + os.path.basename(path) print(ffmpeg_crop_out) ''' subprocess.call(['ffmpeg', '-i', '{}'.format(path), # input file '-filter:v', 'crop={}:{}:{}:{}'.format(side, side, X1, Y1), '-an', '{}'.format(ffmpeg_crop_out)]) ''' cmd = [get_setting(varname="FFMPEG_BINARY"), '-i', '{}'.format(path), # input file '-filter:v', 'crop={}:{}:{}:{}'.format(side, side, X1, Y1), '-an', '{}'.format(ffmpeg_crop_out)] proc = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = proc.communicate()[0].decode() # self.jab_path.append(ffmpeg_crop_out) s2 = time.time() print('cropping individual wells time' + str(s2 - s1))
def ffmpeg_merge_video_audio(video,audio,output, vcodec='copy', acodec='copy', ffmpeg_output=False, verbose = True): """ merges video file ``video`` and audio file ``audio`` into one movie file ``output``. """ cmd = [get_setting("FFMPEG_BINARY"), "-y", "-i", audio,"-i", video, "-vcodec", vcodec, "-acodec", acodec, output] subprocess_call(cmd, verbose = verbose)
def ffmpeg_merge_video_audio(video,audio,output, vcodec='copy', acodec='copy', ffmpeg_output=False, logger = 'bar'): """ merges video file ``video`` and audio file ``audio`` into one movie file ``output``. """ cmd = [get_setting("FFMPEG_BINARY"), "-y", "-i", audio,"-i", video, "-vcodec", vcodec, "-acodec", acodec, output] subprocess_call(cmd, logger = logger)
def __init__(self, filename, print_infos=False, bufsize = None, pix_fmt="rgb24", check_duration=True, target_resolution=None, resize_algo='bicubic', fps_source='tbr', ffmpeg_params=None): self.filename = filename self.proc = None infos = ffmpeg_parse_infos(filename, print_infos, check_duration, fps_source) self.fps = infos['video_fps'] self.size = infos['video_size'] self.rotation = infos['video_rotation'] self.ffmpeg_params = ffmpeg_params or [] autorotate = get_setting('FFMPEG_SUPPORTS_AUTOROTATE') \ and not '-noautorotate' in self.ffmpeg_params if autorotate and abs(self.rotation) in [90, 270]: # swap dimensions to preserve aspect ratio after autorotate self.size = self.size[::-1] if target_resolution: # revert the order, as ffmpeg used (width, height) target_resolution = target_resolution[1], target_resolution[0] if None in target_resolution: ratio = 1 for idx, target in enumerate(target_resolution): if target: ratio = target / self.size[idx] self.size = (int(self.size[0] * ratio), int(self.size[1] * ratio)) else: self.size = target_resolution self.resize_algo = resize_algo self.duration = infos['video_duration'] self.ffmpeg_duration = infos['duration'] self.nframes = infos['video_nframes'] self.infos = infos self.pix_fmt = pix_fmt if pix_fmt == 'rgba': self.depth = 4 else: self.depth = 3 if bufsize is None: w, h = self.size bufsize = self.depth * w * h + 100 self.bufsize= bufsize self.initialize() self.pos = 1 self.lastread = self.read_frame()
def ffmpeg_extract_frame(filename, t1, targetname): """ makes a new video file playing video file ``filename`` between the times ``t1`` and ``t2``. """ cmd = [get_setting("FFMPEG_BINARY"), "-i", filename, "-ss", "%0.2f" % t1, "-vframes", "1", targetname] subprocess_call(cmd)
def video_to_img_ffmpeg(video_file, target_path, fps=1): if not os.path.isfile(video_file): assert ValueError, "Video File doesn't exist. " + video_file basename = os.path.basename(video_file)[:-4] t_path = "{}/{}.frame.%06d.jpg".format(target_path, basename) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-i", video_file, "-vf", "fps=%f" % fps, t_path ] subprocess_call(cmd)
def __init__( self, filename, fps_input, nbytes=2, nchannels=2, codec="libfdk_aac", bitrate=None, input_video=None, logfile=None, ffmpeg_params=None, ): self.filename = filename self.codec = codec if logfile is None: logfile = sp.PIPE cmd = ( [ get_setting("FFMPEG_BINARY"), "-y", "-loglevel", "error" if logfile == sp.PIPE else "info", "-f", "s%dle" % (8 * nbytes), "-acodec", "pcm_s%dle" % (8 * nbytes), "-ar", "%d" % fps_input, "-ac", "%d" % nchannels, "-i", "-", ] + ( ["-vn"] if input_video is None else ["-i", input_video, "-vcodec", "copy"] ) + ["-acodec", codec] + ["-ar", "%d" % fps_input] + ["-strict", "-2"] # needed to support codec 'aac' + (["-ab", bitrate] if (bitrate is not None) else []) + (ffmpeg_params if ffmpeg_params else []) + [filename] ) popen_params = {"stdout": sp.DEVNULL, "stderr": logfile, "stdin": sp.PIPE} if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def concatenate_videos_ffmpeg(video_folder, target_file): if not os.path.isdir(video_folder): assert ValueError, "Video Folder is not a folder. " + video_folder video_paths = ["file '%s'" % i for i in sorted(os.listdir(video_folder))] with open(os.path.join(video_folder, 'list.txt'), 'w') as f: f.write("\n".join(video_paths)) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-f", "concat", "-i", os.path.join(video_folder, "list.txt"), "-c", "copy", target_file ] subprocess_call(cmd)
def good_file(path): if path.endswith('.gif'): proc = subprocess.Popen([ get_setting("FFMPEG_BINARY"), '-i', path, '-f', 'null', '/dev/null' ], stderr=subprocess.PIPE, shell=True) data = proc.communicate()[1] proc.terminate() if 'null @' in data: return False return True
def ffmpeg_extract_subclip(filename, t1, t2, targetname): new_dir = os.path.join(os.path.dirname(filename), NO_OP_FOLDER) if not os.path.isdir(new_dir): os.mkdir(new_dir) cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-ss", "%0.2f" % t1, "-i", filename, "-t", "%0.2f" % (t2 - t1), "-c", "copy", targetname ] subprocess_call(cmd)
def SliceROI(inpath, roi, outpath, start=0): """ Crop Video by Popen (ffmpeg) """ x, y, w, h = roi cmd = [ get_setting("FFMPEG_BINARY"), "-y", "-ss", str(start), "-i", '"{}"'.format(inpath), "-vf", "crop={}:{}:{}:{}".format(w, h, x, y), '"{}"'.format(outpath) ] cmdline = ' '.join(cmd) print(cmdline) os.system(cmdline) '''
def ffmpeg_movie_from_frames(filename, folder, fps, digits=6, bitrate='v'): """ Writes a movie out of the frames (picture files) in a folder. Almost deprecated. """ s = "%" + "%02d" % digits + "d.png" cmd = [get_setting("FFMPEG_BINARY"), "-y", "-f","image2", "-r", "%d"%fps, "-i", os.path.join(folder,folder) + '/' + s, "-b", "%dk"%bitrate, "-r", "%d"%fps, filename] subprocess_call(cmd)
def ffmpeg_extract_subclip(filename, t1, t2, targetname=None): """ Makes a new video file playing video file ``filename`` between the times ``t1`` and ``t2``. """ name, ext = os.path.splitext(filename) if not targetname: T1, T2 = [int(1000*t) for t in [t1, t2]] targetname = "%sSUB%d_%d.%s" % (name, T1, T2, ext) cmd = [get_setting("FFMPEG_BINARY"),"-y", "-ss", "%0.2f"%t1, "-i", filename, "-t", "%0.2f"%(t2-t1), "-vcodec", "copy", "-acodec", "copy", targetname] subprocess_call(cmd)
def ffmpeg_extract_audio(inputfile,output,bitrate=3000,fps=44100): """ extract the sound from a video file and save it in ``output`` """ cmd = [get_setting("FFMPEG_BINARY"), "-y", "-i", inputfile, "-ab", "%dk"%bitrate, "-ar", "%d"%fps, output] subprocess_call(cmd)
def __init__(self, txt=None, filename=None, size=None, color='black', bg_color='transparent', fontsize=None, font='Courier', stroke_color=None, stroke_width=1, method='label', kerning=None, align='center', interline=None, tempfilename=None, temptxt=None, transparent=True, remove_temp=True, shadow=None, antialias=4, print_cmd=False): import tempfile import subprocess as sp from moviepy.tools import subprocess_call from moviepy.config import get_setting # from moviepy.video.VideoClip import * aa_factor= 1 if not antialias else antialias if txt is not None: if temptxt is None: temptxt_fd, temptxt = tempfile.mkstemp(suffix='.txt') try: # only in Python3 will this work os.write(temptxt_fd, bytes(txt, 'UTF8')) except TypeError: # oops, fall back to Python2 os.write(temptxt_fd, txt) os.close(temptxt_fd) txt = '@' + temptxt else: # use a file instead of a text. txt = "@%" + filename if size is not None: size = ('' if size[0] is None else size[0], '' if size[1] is None else size[1]) if shadow is not None: shadow = (80 if shadow[0] is None else shadow[0], 1 if shadow[1] is None else shadow[1], 2 if shadow[2] is None else shadow[2], 2 if shadow[3] is None else shadow[3]) cmd = ( [get_setting("IMAGEMAGICK_BINARY"), "-density", str(aa_scale(72, aa_factor)), "-background", bg_color, "-fill", color, "-font", font]) if fontsize is not None: cmd += ["-pointsize", "%d" % fontsize] if kerning is not None: cmd += ["-kerning", "%0.1f" % aa_scale(kerning, aa_factor)] if stroke_color is not None: cmd += ["-stroke", stroke_color, "-strokewidth", "%.01f" % aa_scale(stroke_width, aa_factor)] if size is not None: cmd += ["-size", "%sx%s" % aa_scale(size, aa_factor)] if align is not None: cmd += ["-gravity", align] if interline is not None: cmd += ["-interline-spacing", "%d" % interline] if tempfilename is None: tempfile_fd, tempfilename = tempfile.mkstemp(suffix='.png') os.close(tempfile_fd) if shadow is not None: shadow_cmd = ( ["(", "+clone", "-shadow", "%sx%s+%s+%s" % (tuple([shadow[0]]) + aa_scale(shadow[1:], aa_factor)), ")", "-compose", "DstOver", "-flatten"]) cmd += ["%s:%s" % (method, txt)] cmd += shadow_cmd cmd += ["-resample", "72"] cmd += ["-type", "truecolormatte", "PNG32:%s" % tempfilename] if print_cmd: print( " ".join(cmd) ) try: subprocess_call(cmd, verbose=verbose) except (IOError,OSError) as err: error = ("MoviePy Error: creation of %s failed because " "of the following error:\n\n%s.\n\n."%(filename, str(err)) + ("This error can be due to the fact that " "ImageMagick is not installed on your computer, or " "(for Windows users) that you didn't specify the " "path to the ImageMagick binary in file conf.py, or." "that the path you specified is incorrect" )) raise IOError(error) ImageClip.__init__(self, tempfilename, transparent=transparent) self.txt = txt self.color = color self.stroke_color = stroke_color if remove_temp: if os.path.exists(tempfilename): os.remove(tempfilename) if os.path.exists(temptxt): os.remove(temptxt)
def __init__(self, filename, size, fps, codec="libx264", audiofile=None, preset="medium", bitrate=None, withmask=False, logfile=None, threads=None, ffmpeg_params=None): if logfile is None: logfile = sp.PIPE self.filename = filename self.codec = codec self.ext = self.filename.split(".")[-1] # order is important cmd = [ get_setting("FFMPEG_BINARY"), '-y', '-loglevel', 'error' if logfile == sp.PIPE else 'info', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-s', '%dx%d' % (size[0], size[1]), '-pix_fmt', 'rgba' if withmask else 'rgb24', '-r', '%.02f' % fps, '-i', '-', '-an', ] if audiofile is not None: cmd.extend([ '-i', audiofile, '-acodec', 'copy' ]) cmd.extend([ '-vcodec', codec, '-preset', preset, ]) if ffmpeg_params is not None: cmd.extend(ffmpeg_params) if bitrate is not None: cmd.extend([ '-b', bitrate ]) if threads is not None: cmd.extend(["-threads", str(threads)]) if ((codec == 'libx264') and (size[0] % 2 == 0) and (size[1] % 2 == 0)): cmd.extend([ '-pix_fmt', 'yuv420p' ]) cmd.extend([ filename ]) popen_params = {"stdout": DEVNULL, "stderr": logfile, "stdin": sp.PIPE} # This was added so that no extra unwanted window opens on windows # when the child process is created if os.name == "nt": popen_params["creationflags"] = 0x08000000 self.proc = sp.Popen(cmd, **popen_params)
def write_gif(clip, filename, fps=None, program= 'ImageMagick', opt="OptimizeTransparency", fuzz=1, verbose=True, withmask=True, loop=0, dispose=True, colors=None): """ Write the VideoClip to a GIF file, without temporary files. Converts a VideoClip into an animated GIF using ImageMagick or ffmpeg. Parameters ----------- filename Name of the resulting gif file. fps Number of frames per second (see note below). If it isn't provided, then the function will look for the clip's ``fps`` attribute (VideoFileClip, for instance, have one). program Software to use for the conversion, either 'ImageMagick' or 'ffmpeg'. opt (ImageMagick only) optimalization to apply, either 'optimizeplus' or 'OptimizeTransparency'. fuzz (ImageMagick only) Compresses the GIF by considering that the colors that are less than fuzz% different are in fact the same. Notes ----- The gif will be playing the clip in real time (you can only change the frame rate). If you want the gif to be played slower than the clip you will use :: >>> # slow down clip 50% and make it a gif >>> myClip.speedx(0.5).write_gif('myClip.gif') """ # # We use processes chained with pipes. # # if program == 'ffmpeg' # frames --ffmpeg--> gif # # if program == 'ImageMagick' and optimize == (None, False) # frames --ffmpeg--> bmp frames --ImageMagick--> gif # # # if program == 'ImageMagick' and optimize != (None, False) # frames -ffmpeg-> bmp frames -ImagMag-> gif -ImagMag-> better gif # delay= 100.0/fps if clip.mask is None: withmask = False cmd1 = [get_setting("FFMPEG_BINARY"), '-y', '-loglevel', 'error', '-f', 'rawvideo', '-vcodec','rawvideo', '-r', "%.02f"%fps, '-s', "%dx%d"%(clip.w, clip.h), '-pix_fmt', ('rgba' if withmask else 'rgb24'), '-i', '-'] popen_params = {"stdout": DEVNULL, "stderr": DEVNULL, "stdin": DEVNULL} if os.name == "nt": popen_params["creationflags"] = 0x08000000 if program == "ffmpeg": popen_params["stdin"] = sp.PIPE popen_params["stdout"] = DEVNULL proc1 = sp.Popen(cmd1+[ '-pix_fmt', ('rgba' if withmask else 'rgb24'), '-r', "%.02f"%fps, filename], **popen_params) else: popen_params["stdin"] = sp.PIPE popen_params["stdout"] = sp.PIPE proc1 = sp.Popen(cmd1+ ['-f', 'image2pipe', '-vcodec', 'bmp', '-'], **popen_params) if program == 'ImageMagick': cmd2 = [get_setting("IMAGEMAGICK_BINARY"), '-delay', "%.02f"%(delay), "-dispose" ,"%d"%(2 if dispose else 1), '-loop', '%d'%loop, '-', '-coalesce'] if (opt in [False, None]): popen_params["stdin"] = proc1.stdout popen_params["stdout"] = DEVNULL proc2 = sp.Popen(cmd2+[filename], **popen_params) else: popen_params["stdin"] = proc1.stdout popen_params["stdout"] = sp.PIPE proc2 = sp.Popen(cmd2+['gif:-'], **popen_params) if opt: cmd3 = [get_setting("IMAGEMAGICK_BINARY"), '-', '-layers', opt, '-fuzz', '%d'%fuzz+'%' ]+(["-colors", "%d"%colors] if colors is not None else [])+[ filename] popen_params["stdin"] = proc2.stdout popen_params["stdout"] = DEVNULL proc3 = sp.Popen(cmd3, **popen_params) # We send all the frames to the first process verbose_print(verbose, "\n[MoviePy] >>>> Building file %s\n"%filename) verbose_print(verbose, "[MoviePy] Generating GIF frames...\n") try: for t,frame in clip.iter_frames(fps=fps, progress_bar=True, with_times=True, dtype="uint8"): if withmask: mask = 255 * clip.mask.get_frame(t) frame = np.dstack([frame, mask]).astype('uint8') proc1.stdin.write(frame.tostring()) except IOError as err: error = ("[MoviePy] Error: creation of %s failed because " "of the following error:\n\n%s.\n\n."%(filename, str(err))) if program == "ImageMagick": error = error + ("This can be due to the fact that " "ImageMagick is not installed on your computer, or " "(for Windows users) that you didn't specify the " "path to the ImageMagick binary in file conf.py." ) raise IOError(error) if program == 'ImageMagick': verbose_print(verbose, "[MoviePy] Optimizing the GIF with ImageMagick...\n") proc1.stdin.close() proc1.wait() if program == 'ImageMagick': proc2.wait() if opt: proc3.wait() verbose_print(verbose, "[MoviePy] >>>> File %s is ready!"%filename)
def write_gif_with_tempfiles(clip, filename, fps=None, program= 'ImageMagick', opt="OptimizeTransparency", fuzz=1, verbose=True, loop=0, dispose=True, colors=None): """ Write the VideoClip to a GIF file. Converts a VideoClip into an animated GIF using ImageMagick or ffmpeg. Does the same as write_gif (see this one for more docstring), but writes every frame to a file instead of passing them in the RAM. Useful on computers with little RAM. """ fileName, fileExtension = os.path.splitext(filename) tt = np.arange(0,clip.duration, 1.0/fps) tempfiles = [] verbose_print(verbose, "\n[MoviePy] Building file %s\n"%filename +40*"-"+"\n") verbose_print(verbose, "[MoviePy] Generating GIF frames...\n") total = int(clip.duration*fps)+1 for i, t in tqdm(enumerate(tt), total=total): name = "%s_GIFTEMP%04d.png"%(fileName, i+1) tempfiles.append(name) clip.save_frame(name, t, withmask=True) delay = int(100.0/fps) if program == "ImageMagick": verbose_print(verbose, "[MoviePy] Optimizing GIF with ImageMagick... ") cmd = [get_setting("IMAGEMAGICK_BINARY"), '-delay' , '%d'%delay, "-dispose" ,"%d"%(2 if dispose else 1), "-loop" , "%d"%loop, "%s_GIFTEMP*.png"%fileName, "-coalesce", "-layers", "%s"%opt, "-fuzz", "%02d"%fuzz + "%", ]+(["-colors", "%d"%colors] if colors is not None else [])+[ filename] elif program == "ffmpeg": cmd = [get_setting("FFMPEG_BINARY"), '-y', '-f', 'image2', '-r',str(fps), '-i', fileName+'_GIFTEMP%04d.png', '-r',str(fps), filename] try: subprocess_call( cmd, verbose = verbose ) verbose_print(verbose, "[MoviePy] GIF %s is ready."%filename) except (IOError,OSError) as err: error = ("MoviePy Error: creation of %s failed because " "of the following error:\n\n%s.\n\n."%(filename, str(err))) if program == "ImageMagick": error = error + ("This error can be due to the fact that " "ImageMagick is not installed on your computer, or " "(for Windows users) that you didn't specify the " "path to the ImageMagick binary in file conf.py." ) raise IOError(error) for f in tempfiles: os.remove(f)
def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True): """Get file infos using ffmpeg. Returns a dictionnary with the fields: "video_found", "video_fps", "duration", "video_nframes", "video_duration", "audio_found", "audio_fps" "video_duration" is slightly smaller than "duration" to avoid fetching the uncomplete frames at the end, which raises an error. """ # open the file in a pipe, provoke an error, read output is_GIF = filename.endswith('.gif') cmd = [get_setting("FFMPEG_BINARY"), "-i", filename] if is_GIF: cmd += ["-f", "null", "/dev/null"] popen_params = {"bufsize": 10**5, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": DEVNULL} if os.name == "nt": popen_params["creationflags"] = 0x08000000 proc = sp.Popen(cmd, **popen_params) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode('utf8') del proc if print_infos: # print the whole info text returned by FFMPEG print( infos ) lines = infos.splitlines() if "No such file or directory" in lines[-1]: raise IOError(("MoviePy error: the file %s could not be found !\n" "Please check that you entered the correct " "path.")%filename) result = dict() # get duration (in seconds) result['duration'] = None if check_duration: try: keyword = ('frame=' if is_GIF else 'Duration: ') line = [l for l in lines if keyword in l][0] match = re.findall("([0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9])", line)[0] result['duration'] = cvsecs(match) except: raise IOError(("MoviePy error: failed to read the duration of file %s.\n" "Here are the file infos returned by ffmpeg:\n\n%s")%( filename, infos)) # get the output line that speaks about video lines_video = [l for l in lines if ' Video: ' in l] result['video_found'] = ( lines_video != [] ) if result['video_found']: try: line = lines_video[0] # get the size, of the form 460x320 (w x h) match = re.search(" [0-9]*x[0-9]*(,| )", line) s = list(map(int, line[match.start():match.end()-1].split('x'))) result['video_size'] = s except: raise (("MoviePy error: failed to read video dimensions in file %s.\n" "Here are the file infos returned by ffmpeg:\n\n%s")%( filename, infos)) # get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes # tbc, and sometimes tbc/2... # Current policy: Trust tbr first, then fps. If result is near from x*1000/1001 # where x is 23,24,25,50, replace by x*1000/1001 (very common case for the fps). try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) tbr = float(line[match.start():match.end()].split(' ')[1]) result['video_fps'] = tbr except: match = re.search("( [0-9]*.| )[0-9]* fps", line) result['video_fps'] = float(line[match.start():match.end()].split(' ')[1]) # It is known that a fps of 24 is often written as 24000/1001 # but then ffmpeg nicely rounds it to 23.98, which we hate. coef = 1000.0/1001.0 fps = result['video_fps'] for x in [23,24,25,30,50]: if (fps!=x) and abs(fps - x*coef) < .01: result['video_fps'] = x*coef if check_duration: result['video_nframes'] = int(result['duration']*result['video_fps'])+1 result['video_duration'] = result['duration'] else: result['video_nframes'] = 1 result['video_duration'] = None # We could have also recomputed the duration from the number # of frames, as follows: # >>> result['video_duration'] = result['video_nframes'] / result['video_fps'] lines_audio = [l for l in lines if ' Audio: ' in l] result['audio_found'] = lines_audio != [] if result['audio_found']: line = lines_audio[0] try: match = re.search(" [0-9]* Hz", line) result['audio_fps'] = int(line[match.start()+1:match.end()]) except: result['audio_fps'] = 'unknown' return result