Paste: 918310
Author: | 518 |
Mode: | python |
Date: | Sun, 14 Nov 2021 14:39:58 |
Plain Text |
import argparse
import os
import sys
import time
from functools import partial
from os import path
from subprocess import run
from tempfile import NamedTemporaryFile
cargs = argparse.ArgumentParser(description="Trim video(+audio) clip, based on output from mpdecimate filter")
cargs.add_argument("--skip", type=int, help="Skip trimming, if less than SKIP parts found")
cargs.add_argument("--keep", action="store_true", help="Keep original file")
cargs.add_argument("--vaapi", type=str, help="Use VA-API device for hardware accelerated transcoding")
cargs.add_argument("directory", help="Directory of files to trim")
cargs = cargs.parse_args()
def prof(s):
e = time.time()
print(time.strftime("%H:%M:%S", time.gmtime(e - s)))
return e
def profd(f):
def a(*args, **kwargs):
s = time.time()
r = f(*args, **kwargs)
prof(s)
return r
return a
def _ffmpeg(fi, co, *args, vaapi=None):
hwargs = ["-hwaccel", "vaapi", "-hwaccel_device", vaapi, "-hwaccel_output_format", "vaapi"] if vaapi else []
return run(["ffmpeg", *hwargs, "-i", fi, *args], check=True, capture_output=co)
def trim(s, e, i, b1=b"v", b2=b""):
trim = b"%f:%f" % (s, e) if e is not None else b"%f" % s
return b"[0:%b]%btrim=%b,%bsetpts=PTS-STARTPTS[%b%d];" % (b1, b2, trim, b2, b1, i)
atrim = partial(trim, b1=b"a", b2=b"a")
def get_dframes(mpdecimate):
dframes = []
for line in mpdecimate.split(b"\n"):
try:
drop_count = int(line.split(b" drop_count:")[1])
except IndexError:
continue
pts_time = line.split(b"pts_time:")[1].split(b" ")[0]
if drop_count == -1:
pts_time = float(pts_time)
if dframes:
ff1, ff2 = dframes[-1]
if pts_time - ff2 < 10:
dframes[-1][1] = pts_time
continue
dframes.append([pts_time])
elif drop_count == 1 and dframes:
pts_time = float(pts_time)
if len(dframes[-1]) == 2:
dframes[-1][1] = pts_time
else:
dframes[-1].append(pts_time)
if len(dframes[-1]) == 1:
dframes[-1].append(None)
elif drop_count < 0:
dframes[-1][1] = None
return [[f1, f2] for f1, f2 in dframes if f2 is None or f2 - f1 > 1]
def get_enc_args():
if cargs.vaapi:
return ["hevc_vaapi", "-qp", "23"]
return ["libx265", "-preset", "fast", "-crf", "30"]
for filepath in os.listdir(cargs.directory):
filepath = os.path.join(cargs.directory, filepath)
ffmpeg = profd(partial(_ffmpeg, filepath))
dframes2 = get_dframes(ffmpeg(True, "-vf", "mpdecimate=hi=576", "-loglevel", "debug", "-f", "null", "-").stderr)
if cargs.skip and len(dframes2) < cargs.skip:
print("less than 2 parts detected, avoiding re-encode")
sys.exit(2)
with NamedTemporaryFile(prefix="mpdecimate_trim.") as fg:
for i, (s, e) in enumerate(dframes2):
fg.write(trim(s, e, i))
fg.write(b"\n")
fg.write(atrim(s, e, i))
fg.write(b"\n")
fg.write(b"".join(b"[v%d][a%d]" % (i, i) for i in range(len(dframes2))))
fg.write(b"concat=n=%d:a=1[vout][aout]" % len(dframes2))
fg.flush()
fout, ext = path.splitext(filepath)
ffmpeg(
False,
"-filter_complex_script", fg.name,
"-map", "[vout]", "-map", "[aout]",
"-c:v", *get_enc_args(),
f"{fout}.trimmed{ext}",
vaapi=cargs.vaapi,
)
if not cargs.keep:
os.remove(filepath)
New Annotation