Skip to content

Instantly share code, notes, and snippets.

@Chaparro
Last active March 11, 2020 16:23
Show Gist options
  • Save Chaparro/7f8b0b9bfaf38dae469e635f51a69a9e to your computer and use it in GitHub Desktop.
Save Chaparro/7f8b0b9bfaf38dae469e635f51a69a9e to your computer and use it in GitHub Desktop.
#all rights, credits and congratulations go to Bazoo from the project slippi discord
import re
import time
import ffmpeg
import subprocess
in_name = '1x.mp4'
out_name = 'out.mp4'
def get_black_frames():
# cmd = f'ffmpeg -i {in_name} -vf blackdetect=d=0:pic_th=.98 -f null -'
# cmd = f'ffmpeg -i {in_name} -vf blackdetect=d=0.016667:pix_th=.0001 -f null -'
# cmd = f'ffmpeg -i {in_name} -vf blackdetect=d=0.000001:pix_th=.0001 -f null -'
cmd = f'ffmpeg -hwaccel nvdec -i {in_name} -vf blackdetect=d=0.000001:pix_th=.0001 -f null -'
regex = r'\[blackdetect @ .+] black_start:(.+) black_end:(.+) '
return get_info(cmd, regex, 'err')
# def get_duration():
# cmd = f'ffmpeg -i {in_name} -f null -'
# regex = r'Duration: (\d{2}):(\d{2}):(\d{2})\.(\d{2}), start'
# return get_info(cmd, regex)[0]
def get_duration():
cmd = f'ffprobe -i {in_name} -show_entries format=duration'
regex = r'duration=(.*)'
return get_info(cmd, regex)[0]
def get_info(cmd, regex, type='out'):
pr = None
txt = None
if (type == 'out'):
pr = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
txt = pr.communicate()[0]
elif (type == 'err'):
pr = subprocess.Popen(cmd.split(), stderr=subprocess.PIPE)
txt = pr.communicate()[1]
else:
return
return re.findall(regex, txt.decode('utf-8').replace('\r', ''))
def trim_black_frames_ffmpeg():
black_frames = get_black_frames()
if (len(black_frames) == 0):
return
in_file = ffmpeg.input(in_name)
for frames in black_frames:
in_file = ffmpeg.trim(in_file, start_pts=frames[0], end_pts=frames[1])
in_file = ffmpeg.output(in_file, out_name)
subprocess.run(['ffmpeg', *ffmpeg.get_args(in_file, overwrite_output=True)])
# in_file = ffmpeg.run(in_file, overwrite_output=True)
def trim_black_frames():
black_frames = get_black_frames()
if (len(black_frames) == 0):
return
cmd1 = f'ffmpeg -hwaccel nvdec -i {in_name} -y -vf select=\'not('
cmd2 = ')\',setpts=N/FRAME_RATE/TB -af aselect=\'not('
cmd3 = ')\',asetpts=N/SR/TB -c:v libx264 -crf 15 -preset veryslow '
# cmd3 = ')\',asetpts=N/SR/TB -c:v h264_nvenc -tier high -preset slow -profile:v high -rc cbr_hq -cbr 1 -2pass 1 '
trims = ''
count = 0
for frames in black_frames:
if count > 0:
trims += '+'
trims += f'between(t,{frames[0]},{frames[1]})'
count += 1
cmd = cmd1 + trims + cmd2 + trims + cmd3 + out_name
print(cmd)
subprocess.run(cmd.split())
def cut(ifile, ofile, start='00:00.000000', end=None):
# -ss before -i is apparently faster, but less precise.
# try using -t instead of -to? requieres black_duration regex group capture
if (end is None):
# return f'ffmpeg -ss {start} -i {ifile} -y -c copy {ofile}'
return f'ffmpeg -ss {start} -i {ifile} -y -c copy -avoid_negative_ts 1 {ofile}'
else:
# return f'ffmpeg -ss {start} -to {end} -i {ifile} -y -c copy {ofile}'
return f'ffmpeg -ss {start} -i {ifile} -to {end} -y -c copy -avoid_negative_ts 1 {ofile}'
def duration_to_seconds(duration):
pass
def trim_parts():
black_frames = get_black_frames()
duration = get_duration()
length = len(black_frames)
ends_with_black_frame = False
if (length > 0):
ends_with_black_frame = 0.017 > float(duration) - float(black_frames[length - 1][1])
print(f'\n\n\n\n{(float(duration) - float(black_frames[length - 1][1]))}\n\n\n')
# print(black_frames, duration)
cmd = ''
if (length == 0):
return
elif (length == 1):
frames = black_frames[0]
if (frames[0] == '0'):
cmd = cut(in_name, out_name, frames[1])
subprocess.run(cmd.split())
elif (ends_with_black_frame): # TODO: adjust precision
# print('ends with black frame')
cmd = cut(in_name, out_name, frames[1], duration)
subprocess.run(cmd.split())
else:
cmd = cut(in_name, f'parts/part_0_{out_name}', end=frames[0])
subprocess.run(cmd.split())
cmd = cut(in_name, f'parts/part_1_{out_name}', frames[1], duration)
subprocess.run(cmd.split())
write_parts(f'file \'part_0_{out_name}\'\nfile \'part_1_{out_name}\'')
subprocess.run(f'ffmpeg -f concat -i parts/parts.txt -y -c copy {out_name}'.split())
# TODO: Cut black frames for each black_frames
else:
print(black_frames)
i = 0
text = ''
for frames in black_frames:
cmd = ''
name = f'part_{i}_{out_name}'
pname = f'parts/{name}'
if (frames[0] == '0' and length > 1):
print('\n\n\n\n\n\n first frame is bf\n\n\n\n')
cmd = cut(in_name, pname, hack(frames[1]), black_frames[i + 1][0])
print(frames[1], black_frames[i + 1][0])
elif (i == length - 1 and ends_with_black_frame):
print('\n\n\n\n\nends with bf\n\n\n\n')
cmd = cut(in_name, pname, hack(black_frames[i - 1][1]))
else:
print(
f'\n\n\n\n\n inbetween {black_frames[i - 1]}, {frames}\n\n\n\n')
cmd = cut(in_name, pname, hack(black_frames[i - 1][1]), frames[0])
subprocess.run(cmd.split())
text += f'file \'{name}\'\n'
i += 1
write_parts(text)
cmd = f'ffmpeg -f concat -i parts/parts.txt -y -c copy {out_name}'
(
ffmpeg
.input(out_name)
.concat()
)
subprocess.run(cmd.split())
def write_parts(text):
with open('parts/parts.txt', 'w') as file:
file.write(text)
def hack(seconds):
return seconds
fseconds = float(seconds)
# return str(round(fseconds + 0.01697999999999933, 6))
return str(round(fseconds + 0.016667, 6))
# return str(fseconds - 0.017)
# trim_parts()
trim_black_frames()
# trim_black_frames_ffmpeg()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment