def load_infos(self, print_infos=False): """ reads the FFMPEG info on the file and sets self.size and self.fps """ # open the file in a pipe, provoke an error, read output proc = sp.Popen([FFMPEG_BINARY,"-i",self.filename, "-"], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode('utf8') 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("%s not found ! Wrong path ?"%self.filename) # get duration (in seconds) line = [l for l in lines if 'Duration: ' in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]",line) hms = map(float,line[match.start()+1:match.end()].split(':')) self.duration = cvsecs(*hms) self.nframes = int(self.duration*self.fps)
def show(clip, t=0, with_mask=True): """ Splashes the frame of clip corresponding to time ``t``. Parameters ------------ t Time in seconds of the frame to display. with_mask ``False`` if the clip has a mask but you want to see the clip without the mask. """ if isinstance(t, tuple): t = cvsecs(*t) if clip.ismask: clip = clip.to_RGB() if with_mask and (clip.mask != None): import moviepy.video.compositing.CompositeVideoClip as cvc clip = cvc.CompositeVideoClip([clip.set_pos((0,0))]) imdisplay(clip.get_frame(t))
def load_infos(self, print_infos=False): """ reads the FFMPEG info on the file and sets self.size and self.fps """ # open the file in a pipe, provoke an error, read output proc = sp.Popen([FFMPEG_BINARY, "-i", self.filename, "-"], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode("utf8") 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("%s not found ! Wrong path ?" % self.filename) # get the output line that speaks about video line = [l for l in lines if " Video: " in l][0] # get the size, of the form 460x320 (w x h) match = re.search(" [0-9]*x[0-9]*(,| )", line) self.size = list(map(int, line[match.start() : match.end() - 1].split("x"))) # get the frame rate match = re.search("( [0-9]*.| )[0-9]* (tbr|fps)", line) self.fps = float(line[match.start() : match.end()].split(" ")[1]) # get duration (in seconds) line = [l for l in lines if "Duration: " in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start() + 1 : match.end()].split(":")) self.duration = cvsecs(*hms) self.nframes = int(self.duration * self.fps)
def load_infos(self, print_infos=False): """ reads the FFMPEG info on the file and sets self.size and self.fps """ # open the file in a pipe, provoke an error, read output proc = sp.Popen([FFMPEG_BINARY, "-i", self.filename, "-"], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read() 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("%s not found ! Wrong path ?" % self.filename) # get duration (in seconds) line = [l for l in lines if 'Duration: ' in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start() + 1:match.end()].split(':')) self.duration = cvsecs(*hms) self.nframes = int(self.duration * self.fps)
def time_can_be_tuple(f, clip, *a, **k): """ All tuples in the arguments of f will be considered as time and converted to seconds. """ fun = lambda e: e if (not isinstance(e,tuple)) else cvsecs(*e) a = map(fun,a) k = dict( [(m, fun(n)) for m,n in k.items()]) return f(clip, *a, **k)
def time_can_be_tuple(f, clip, *a, **k): """ This decorator tells that the function f (audioclip -> audioclip) can be also used on a video clip, at which case it returns a videoclip with modified audio. """ fun = lambda e: e if (not isinstance(e,tuple)) else cvsecs(*e) a = map(fun,a) k = dict( [(m, fun(n)) for m,n in k.items()]) return f(clip, *a, **k)
def test_3(): """Test the cvsecs funtion outputs correct times as per the docstring.""" lefts = [15.4, (1,21.5), (1,1,2), '01:01:33.5', '01:01:33.045' ] rights = [15.4, 81.5, 3662, 3693.5, 3693.045] for i in range(len(lefts)): left = tools.cvsecs(lefts[i]) right = rights[i] message = "{0} resulted in {1}, but {2} was expected"\ .format(lefts[i],left, right) assert left == right, message
def time_can_be_tuple(f, clip, *a, **k): """ This decorator tells that the function f (audioclip -> audioclip) can be also used on a video clip, at which case it returns a videoclip with modified audio. """ fun = lambda e: e if (not isinstance(e, tuple)) else cvsecs(*e) a = map(fun, a) k = dict([(m, fun(n)) for m, n in k.items()]) return f(clip, *a, **k)
def get_chapter_duration(self, chapters, full_duration=None, idx=None): """ :param chapters: :param full_duration: :param idx: :return: """ start = cvsecs(chapters[idx].start) if idx is 0: start = 0 try: chapter_end_time = chapters[idx + 1].start except IndexError: chapter_end_time = full_duration chapter_end_time = cvsecs(chapter_end_time) duration = chapter_end_time - start return duration
def warper(f, *a, **kw): if hasattr(f, "func_code"): func_code = f.func_code # Python 2 else: func_code = f.__code__ # Python 3 names = func_code.co_varnames new_a = [cvsecs(arg) if (name in varnames) else arg for (arg, name) in zip(a, names)] new_kw = {k: fun(v) if k in varnames else v for (k,v) in kw.items()} return f(*new_a, **new_kw)
def warper(f, *a, **kw): if hasattr(f, "func_code"): func_code = f.func_code # Python 2 else: func_code = f.__code__ # Python 3 names = func_code.co_varnames new_a = [ cvsecs(arg) if (name in varnames) else arg for (arg, name) in zip(a, names) ] new_kw = {k: fun(v) if k in varnames else v for (k, v) in kw.items()} return f(*new_a, **new_kw)
def time(self, value): """ Sets the time of the clock. Useful for seeking. Parameters ---------- value : str or int The time to seek to. Can be any of the following formats: >>> 15.4 -> 15.4 # seconds >>> (1,21.5) -> 81.5 # (min,sec) >>> (1,1,2) -> 3662 # (hr, min, sec) >>> '01:01:33.5' -> 3693.5 #(hr,min,sec) >>> '01:01:33.045' -> 3693.045 >>> '01:01:33,5' #comma works too """ seconds = cvsecs(value) self.reset() self.previous_intervals.append(seconds)
def show(clip, t=0, with_mask=True, interactive=False): """ Splashes the frame of clip corresponding to time ``t``. Parameters ------------ t Time in seconds of the frame to display. with_mask ``False`` if the clip has a mask but you want to see the clip without the mask. """ if isinstance(t, tuple): t = cvsecs(*t) if clip.ismask: clip = clip.to_RGB() if with_mask and (clip.mask != None): import moviepy.video.compositing.CompositeVideoClip as cvc clip = cvc.CompositeVideoClip([clip.set_pos((0, 0))]) img = clip.get_frame(t) imdisplay(img) if interactive: result = [] while True: for event in pg.event.get(): if event.type == pg.KEYDOWN: if (event.key == pg.K_ESCAPE): print("Keyboard interrupt") return result elif event.type == pg.MOUSEBUTTONDOWN: x, y = pg.mouse.get_pos() rgb = img[y, x] result.append({'position': (x, y), 'color': rgb}) print("position, color : ", "%s, %s" % (str( (x, y)), str(rgb))) time.sleep(.03)
def show(clip, t=0, with_mask=True, interactive=False): """ Splashes the frame of clip corresponding to time ``t``. Parameters ------------ t Time in seconds of the frame to display. with_mask ``False`` if the clip has a mask but you want to see the clip without the mask. """ if isinstance(t, tuple): t = cvsecs(*t) if clip.ismask: clip = clip.to_RGB() if with_mask and (clip.mask != None): import moviepy.video.compositing.CompositeVideoClip as cvc clip = cvc.CompositeVideoClip([clip.set_pos((0,0))]) img = clip.get_frame(t) imdisplay(img) if interactive: result=[] while True: for event in pg.event.get(): if event.type == pg.KEYDOWN: if (event.key == pg.K_ESCAPE): print( "Keyboard interrupt" ) return result elif event.type == pg.MOUSEBUTTONDOWN: x,y = pg.mouse.get_pos() rgb = img[y,x] result.append({'position':(x,y), 'color':rgb}) print( "position, color : ", "%s, %s"%( str((x,y)),str(rgb))) time.sleep(.03)
def load_infos(self, print_infos=False): """Get file infos using ffmpeg. Grabs the FFMPEG info on the file and use them to set the attributes ``self.size`` and ``self.fps`` """ # open the file in a pipe, provoke an error, read output proc = sp.Popen([FFMPEG_BINARY, "-i", self.filename, "-"], bufsize=10**6, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode('utf8') 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("%s not found ! Wrong path ?" % self.filename) # get the output line that speaks about video line = [l for l in lines if ' Video: ' in l][0] # get the size, of the form 460x320 (w x h) match = re.search(" [0-9]*x[0-9]*(,| )", line) self.size = list( map(int, line[match.start():match.end() - 1].split('x'))) # get the frame rate match = re.search("( [0-9]*.| )[0-9]* (tbr|fps)", line) self.fps = float(line[match.start():match.end()].split(' ')[1]) # get duration (in seconds) line = [l for l in lines if 'Duration: ' in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start() + 1:match.end()].split(':')) self.duration = cvsecs(*hms) self.nframes = int(self.duration * self.fps)
def file_to_subtitles(filename): """ Converts a srt file into subtitles. The returned list is of the form ``[((ta,tb),'some text'),...]`` and can be fed to SubtitlesClip. Only works for '.srt' format for the moment. """ times_texts = [] current_times = None current_text = "" with open(filename, "r") as f: for line in f: times = re.findall("([0-9]*:[0-9]*:[0-9]*,[0-9]*)", line) if times: current_times = [cvsecs(t) for t in times] elif line.strip() == "": times_texts.append((current_times, current_text.strip("\n"))) current_times, current_text = None, "" elif current_times: current_text += line return times_texts
def to_srt(sub_element): (ta, tb), txt = sub_element fta = cvsecs(ta) ftb = cvsecs(tb) return "%s - %s\n%s" % (fta, ftb, txt)
def ffmpeg_parse_infos(filename, print_infos=False, check_duration=True, fps_source='tbr'): """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) out, err = proc.communicate() proc.terminate() infos = err.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: ') # for large GIFS the "full" duration is presented as the last element in the list. index = -1 if is_GIF else 0 line = [l for l in lines if keyword in l][index] 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 and re.search('\d+x\d+', 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 IOError( ("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 unless fps_source is # specified as 'fps' in which case try fps then tbr # 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). def get_tbr(): match = re.search("( [0-9]*.| )[0-9]* tbr", line) # Sometimes comes as e.g. 12k. We need to replace that with 12000. s_tbr = line[match.start():match.end()].split(' ')[1] if "k" in s_tbr: tbr = float(s_tbr.replace("k", "")) * 1000 else: tbr = float(s_tbr) return tbr def get_fps(): match = re.search("( [0-9]*.| )[0-9]* fps", line) fps = float(line[match.start():match.end()].split(' ')[1]) return fps if fps_source == 'tbr': try: result['video_fps'] = get_tbr() except: result['video_fps'] = get_fps() elif fps_source == 'fps': try: result['video_fps'] = get_fps() except: result['video_fps'] = get_tbr() # 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'] # get the video rotation info. try: rotation_lines = [ l for l in lines if 'rotate :' in l and re.search('\d+$', l) ] if len(rotation_lines): rotation_line = rotation_lines[0] match = re.search('\d+$', rotation_line) result['video_rotation'] = int( rotation_line[match.start():match.end()]) else: result['video_rotation'] = 0 except: raise IOError( ("MoviePy error: failed to read video rotation in file %s.\n" "Here are the file infos returned by ffmpeg:\n\n%s") % (filename, infos)) 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) hz_string = line[match.start() + 1:match.end() - 3] # Removes the 'hz' from the end result['audio_fps'] = int(hz_string) except: result['audio_fps'] = 'unknown' return result
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 cmd = [get_setting("FFMPEG_BINARY"), "-i", filename] 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 = '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
def ffmpeg_parse_infos( filename, decode_file=False, print_infos=False, check_duration=True, fps_source="fps", ): """Get file infos using ffmpeg. Returns a dictionnary with the fields: "video_found", "video_fps", "duration", "video_nframes", "video_duration", "video_bitrate","audio_found", "audio_fps", "audio_bitrate" "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, read output cmd = [FFMPEG_BINARY, "-i", filename] if decode_file: cmd.extend(["-f", "null", "-"]) popen_params = { "bufsize": 10**5, "stdout": sp.PIPE, "stderr": sp.PIPE, "stdin": sp.DEVNULL, } if os.name == "nt": popen_params["creationflags"] = 0x08000000 proc = sp.Popen(cmd, **popen_params) (output, error) = proc.communicate() infos = error.decode("utf8", errors="ignore") proc.terminate() 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: if decode_file: line = [l for l in lines if "time=" in l][-1] else: line = [l for l in lines if "Duration:" in l][-1] 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 Exception: raise IOError( f"MoviePy error: failed to read the duration of file {filename}.\nHere are the file infos returned by ffmpeg:\n\n{infos}" ) # get the output line that speaks about video lines_video = [ l for l in lines if " Video: " in l and re.search(r"\d+x\d+", 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 Exception: raise IOError( ("MoviePy error: failed to read video dimensions in file %s.\n" "Here are the file infos returned by ffmpeg:\n\n%s") % (filename, infos)) match_bit = re.search(r"(\d+) kb/s", line) result["video_bitrate"] = int( match_bit.group(1)) if match_bit else None # Get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes # tbc, and sometimes tbc/2... # Current policy: Trust fps first, then tbr unless fps_source is # specified as 'tbr' in which case try tbr 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). def get_tbr(): match = re.search("( [0-9]*.| )[0-9]* tbr", line) # Sometimes comes as e.g. 12k. We need to replace that with 12000. s_tbr = line[match.start():match.end()].split(" ")[1] if "k" in s_tbr: tbr = float(s_tbr.replace("k", "")) * 1000 else: tbr = float(s_tbr) return tbr def get_fps(): match = re.search("( [0-9]*.| )[0-9]* fps", line) fps = float(line[match.start():match.end()].split(" ")[1]) return fps if fps_source == "tbr": try: result["video_fps"] = get_tbr() except Exception: result["video_fps"] = get_fps() elif fps_source == "fps": try: result["video_fps"] = get_fps() except Exception: result["video_fps"] = get_tbr() # 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) < 0.01: result["video_fps"] = x * coef if check_duration: result["video_nframes"] = int(result["duration"] * result["video_fps"]) 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'] # get the video rotation info. try: rotation_lines = [ l for l in lines if "rotate :" in l and re.search(r"\d+$", l) ] if len(rotation_lines): rotation_line = rotation_lines[0] match = re.search(r"\d+$", rotation_line) result["video_rotation"] = int( rotation_line[match.start():match.end()]) else: result["video_rotation"] = 0 except Exception: raise IOError( ("MoviePy error: failed to read video rotation in file %s.\n" "Here are the file infos returned by ffmpeg:\n\n%s") % (filename, infos)) 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) hz_string = line[match.start() + 1:match.end() - 3] # Removes the 'hz' from the end result["audio_fps"] = int(hz_string) except Exception: result["audio_fps"] = "unknown" match_bit = re.search(r"(\d+) kb/s", line) result["audio_bitrate"] = int( match_bit.group(1)) if match_bit else None return result
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
def test_cvsecs(given, expected): """Test the cvsecs funtion outputs correct times as per the docstring.""" assert tools.cvsecs(given) == expected
def ffmpeg_parse_infos(filename, print_infos=False): """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 proc = sp.Popen([FFMPEG_BINARY, "-i", filename, "-"], bufsize=10**6, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode('utf8') 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("%s not found ! Wrong path ?"%filename) result = dict() # get duration (in seconds) line = [l for l in lines if 'Duration: ' in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start()+1:match.end()].split(':')) result['duration'] = cvsecs(*hms) # 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']: 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 # get the frame rate try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) result['video_fps'] = float(line[match.start():match.end()].split(' ')[1]) except: match = re.search("( [0-9]*.| )[0-9]* fps", line) result['video_fps'] = float(line[match.start():match.end()].split(' ')[1]) result['video_nframes'] = int(result['duration']*result['video_fps']) 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
def ffmpeg_parse_infos(filename, print_infos=False): """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 proc = sp.Popen([FFMPEG_BINARY, "-i", filename, "-"], bufsize=10**6, stdout=sp.PIPE, stderr=sp.PIPE) proc.stdout.readline() proc.terminate() infos = proc.stderr.read().decode('utf8') 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("%s not found ! Wrong path ?" % filename) result = dict() # get duration (in seconds) line = [l for l in lines if 'Duration: ' in l][0] match = re.search(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start() + 1:match.end()].split(':')) result['duration'] = cvsecs(*hms) # 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']: 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 # get the frame rate try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) result['video_fps'] = float( line[match.start():match.end()].split(' ')[1]) except: match = re.search("( [0-9]*.| )[0-9]* fps", line) result['video_fps'] = float( line[match.start():match.end()].split(' ')[1]) result['video_nframes'] = int(result['duration'] * result['video_fps']) 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
def ffmpeg_parse_infos(filename, print_infos=False): """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 = [FFMPEG_BINARY, "-i", filename] if is_GIF: cmd += ["-f", "null", "/dev/null"] proc = sp.Popen(cmd, bufsize=10**5, stdout=sp.PIPE, stderr=sp.PIPE) 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) try: keyword = ('frame=' if is_GIF else 'Duration: ') line = [l for l in lines if keyword in l][0] match = re.search("[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start()+1:match.end()].split(':')) result['duration'] = cvsecs(*hms) 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']: 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 # get the frame rate. Sometimes it's 'tbr', sometimes 'fps', sometimes # tbc, and sometimes tbc/2... Trust tbc first, then tbr, then fps. try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) tbr = float(line[match.start():match.end()].split(' ')[1]) try: match = re.search("( [0-9]*.| )[0-9]* tbc", line) tbc = float(line[match.start():match.end()].split(' ')[1]) if abs(tbr - tbc/2) < abs(tbr-tbc): result['video_fps'] = 1.0*tbc /2 else: result['video_fps'] = tbc except: 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]: if (fps!=x) and abs(fps - x*coef) < .01: result['video_fps'] = x*coef result['video_nframes'] = int(result['duration']*result['video_fps'])+1 result['video_duration'] = result['duration'] # 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
def ffmpeg_parse_infos(filename, print_infos=False): """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 = [FFMPEG_BINARY, "-i", filename] if is_GIF: cmd += ["-f", "null", "/dev/null"] proc = sp.Popen(cmd, bufsize=10**5, stdout=sp.PIPE, stderr=sp.PIPE) 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("%s not found ! Wrong path ?"%filename) result = dict() # get duration (in seconds) try: keyword = ('frame=' if is_GIF else 'Duration: ') line = [l for l in lines if keyword in l][0] match = re.search("[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start()+1:match.end()].split(':')) result['duration'] = cvsecs(*hms) except: raise IOError("Error reading duration in file %s,"%(filename)+ "Text parsed: %s"%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']: 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 # get the frame rate try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) result['video_fps'] = float(line[match.start():match.end()].split(' ')[1]) except: match = re.search("( [0-9]*.| )[0-9]* fps", line) result['video_fps'] = float(line[match.start():match.end()].split(' ')[1]) result['video_nframes'] = int(result['duration']*result['video_fps'])+1 result['video_duration'] = result['duration'] # 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
def ffmpeg_parse_infos(filename, print_infos=False): """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 = [FFMPEG_BINARY, "-i", filename] if is_GIF: cmd += ["-f", "null", "/dev/null"] proc = sp.Popen(cmd, bufsize=10**5, stdout=sp.PIPE, stderr=sp.PIPE) 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("%s not found ! Wrong path ?" % filename) result = dict() # get duration (in seconds) try: keyword = ('frame=' if is_GIF else 'Duration: ') line = [l for l in lines if keyword in l][0] match = re.search("[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9]", line) hms = map(float, line[match.start() + 1:match.end()].split(':')) result['duration'] = cvsecs(*hms) except: raise IOError("Error reading duration in file %s," % (filename) + "Text parsed: %s" % 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']: 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 # get the frame rate try: match = re.search("( [0-9]*.| )[0-9]* tbr", line) result['video_fps'] = float( line[match.start():match.end()].split(' ')[1]) except: match = re.search("( [0-9]*.| )[0-9]* fps", line) result['video_fps'] = float( line[match.start():match.end()].split(' ')[1]) result['video_nframes'] = int( result['duration'] * result['video_fps']) + 1 result['video_duration'] = result['duration'] # 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