From 4196aa34ec2807e6f46c983c760ca891fb0f0a9b Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Wed, 26 May 2021 16:39:00 +0200 Subject: [PATCH] * Moved trivial subprocess execution to separate wrapper function. * Reintroduced fast ffprobe container scanning method. --- ffpreview.py | 148 ++++++++++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 67 deletions(-) diff --git a/ffpreview.py b/ffpreview.py index eb7dbdb..47fdfd0 100755 --- a/ffpreview.py +++ b/ffpreview.py @@ -130,6 +130,11 @@ def str2float(s): return float(m.groups()[0]) return 0.0 +def sfrac2float(s): + a = s.split('/') + d = 1 if len(a) < 2 else str2float(a[1]) + return str2float(a[0]) / (d if d else 1) + def hr_size(sz, prec=1): i = 0 while sz >= 1024: @@ -1692,86 +1697,95 @@ class sMainWindow(QMainWindow): ############################################################ # Helper functions -# get video meta information -def get_meta(vidfile): +def proc_cmd(cmd): global proc - meta = { 'frames': -1, 'duration':-1, 'fps':-1.0, 'nsubs': -1 } if proc: - return meta, False - meta['nsubs'] = count_subss(vidfile) - # try ffprobe method - try: - cmd = cfg['ffprobe'] + ' -v error -select_streams v:0 -of json -count_packets' - cmd += ' -show_entries format=duration:stream=nb_read_packets' - cmd += ' "' + vidfile + '"' - eprint(2, cmd) - proc = Popen(cfg['exec'] + ' ' + cmd, shell=True, stdout=PIPE, stderr=PIPE, env=cfg['env']) - stdout, stderr = proc.communicate() - retval = proc.wait() - proc = None - if retval == 0: - info = json.loads(stdout.decode()) - meta['frames'] = int(info['streams'][0]['nb_read_packets']) - d = float(info['format']['duration']) - meta['duration'] = max(d, 0.0001) - meta['fps'] = round(meta['frames'] / meta['duration'], 2) - return meta, True - else: - eprint(0, cmd + '\n returned %d' % retval) - eprint(1, stderr.decode()) - except Exception as e: - eprint(0, cmd + '\n failed: ' + str(e)) - proc = kill_proc(proc) - # ffprobe didn't cut it, try ffmpeg instead + return '', '', None + retval = 0 try: - cmd = cfg['ffmpeg'] + ' -nostats -i "' + vidfile + '"' - cmd += ' -c:v copy -f rawvideo -y ' + os.devnull - eprint(2, cmd) + eprint(2, 'run', cmd) proc = Popen(cfg['exec'] + ' ' + cmd, shell=True, stdout=PIPE, stderr=PIPE, env=cfg['env']) stdout, stderr = proc.communicate() + stdout = stdout.decode() + stderr = stderr.decode() retval = proc.wait() proc = None - if retval == 0: - for line in io.StringIO(stderr.decode()).readlines(): - m = re.match(r'^frame=\s*(\d+).*time=\s*(\d+:\d+:\d+(\.\d+)?)', line) - if m: - meta['frames'] = int(m.group(1)) - d = hms2s(m.group(2)) - meta['duration'] = max(d, 0.0001) - meta['fps'] = round(meta['frames'] / meta['duration'], 2) - return meta, True - else: + if retval != 0: eprint(0, cmd + '\n returned %d' % retval) - eprint(1, stderr.decode()) + eprint(1, stderr) except Exception as e: eprint(0, cmd + '\n failed: ' + str(e)) proc = kill_proc(proc) - return meta, False + return stdout, stderr, retval -# count subtitle streams -def count_subss(vidfile): +# get video meta information +def get_meta(vidfile): + meta = { 'frames': -1, 'duration':-1, 'fps':-1.0, 'nsubs': -1 } global proc if proc: - return -1 - nstreams = -1 - try: - cmd = cfg['ffprobe'] + ' -v error -select_streams s -show_entries stream=index -of csv=p=0' - cmd += ' "' + vidfile + '"' - eprint(2, cmd) - proc = Popen(cfg['exec'] + ' ' + cmd, shell=True, stdout=PIPE, stderr=PIPE, env=cfg['env']) - stdout, stderr = proc.communicate() - retval = proc.wait() - proc = None - if retval == 0: - nstreams = len(stdout.decode().splitlines()) - eprint(2, 'number of subtitle streams:', nstreams) - else: - eprint(0, cmd + '\n returned %d' % retval) - eprint(1, stderr.decode()) - except Exception as e: - eprint(0, cmd + '\n failed: ' + str(e)) - proc = kill_proc(proc) - return nstreams + return meta, False + # count subtitle streams + cmd = cfg['ffprobe'] + ' -v error -select_streams s -show_entries stream=index' + cmd += ' -of csv=p=0 "' + vidfile + '"' + out, err, rc = proc_cmd(cmd) + if rc == 0: + meta['nsubs'] = len(out.splitlines()) + eprint(2, 'number of subtitle streams:', meta['nsubs']) + # get frames / duration / fps + # try ffprobe fast method + cmd = cfg['ffprobe'] + ' -v error -select_streams v:0 -show_streams' + cmd += ' -show_format -of json "' + vidfile + '"' + out, err, rc = proc_cmd(cmd) + if rc == 0: + info = json.loads(out) + strinf = info['streams'][0] + fmtinf = info['format'] + d = f = None + fps = -1 + if 'duration' in strinf: + d = float(strinf['duration']) + elif 'duration' in fmtinf: + d = float(fmtinf['duration']) + if d is not None: + d = max(d, 0.000001) + if 'nb_frames' in strinf: + f = int(strinf['nb_frames']) + fps = f / d + elif 'avg_frame_rate' in strinf: + fps = sfrac2float(strinf['avg_frame_rate']) + f = int(fps * d) + if f is not None: + meta['duration'] = d + meta['frames'] = f + meta['fps'] = fps + return meta, True + # no dice, try ffprobe slow method + cmd = cfg['ffprobe'] + ' -v error -select_streams v:0 -of json -count_packets' + cmd += ' -show_entries format=duration:stream=nb_read_packets' + cmd += ' "' + vidfile + '"' + out, err, rc = proc_cmd(cmd) + if rc == 0: + info = json.loads(out) + meta['frames'] = int(info['streams'][0]['nb_read_packets']) + d = float(info['format']['duration']) + meta['duration'] = max(d, 0.0001) + meta['fps'] = round(meta['frames'] / meta['duration'], 2) + return meta, True + # ffprobe didn't cut it, try ffmpeg instead + cmd = cfg['ffmpeg'] + ' -nostats -i "' + vidfile + '"' + cmd += ' -c:v copy -f rawvideo -y ' + os.devnull + out, err, rc = proc_cmd(cmd) + if rc == 0: + for line in io.StringIO(err).readlines(): + m = re.match(r'^frame=\s*(\d+).*time=\s*(\d+:\d+:\d+(\.\d+)?)', line) + if m: + meta['frames'] = int(m.group(1)) + d = hms2s(m.group(2)) + meta['duration'] = max(d, 0.0001) + meta['fps'] = round(meta['frames'] / meta['duration'], 2) + return meta, True + # not our lucky day, eh?! + return meta, False # extract thumbnails from video and collect timestamps def make_thumbs(vidfile, thinfo, thdir, prog_cb=None): @@ -1800,7 +1814,7 @@ def make_thumbs(vidfile, thinfo, thdir, prog_cb=None): cmd += ' -vf "select=eq(pict_type\,I)' cmd += ',showinfo,scale=' + str(cfg['thumb_width']) + ':-1' if thinfo['addss'] >= 0: - cmd += ',subtitles=\'' + vidfile + '\':si=' + str(thinfo['addss']) + cmd += ',subtitles="' + vidfile + '":si=' + str(thinfo['addss']) cmd += '" -vsync vfr "' + os.path.join(thdir, pictemplate) + '"' eprint(2, cmd) ebuf = '' -- 2.30.2