Skip to content

Instantly share code, notes, and snippets.

@dfaker
Created April 20, 2020 20:01
Show Gist options
  • Save dfaker/d3e9bb9e4bff26c4895f2dc30ee1916d to your computer and use it in GitHub Desktop.
Save dfaker/d3e9bb9e4bff26c4895f2dc30ee1916d to your computer and use it in GitHub Desktop.
import sys
import os
import mimetypes
import random
import mpv
import cv2
import numpy as np
import subprocess as sp
from win32api import GetSystemMetrics
import multiprocessing as mp
workers=1
maxWidth = 1280
imshape = (100,GetSystemMetrics(0)-10,3)
targetSize_max = 4194304
targetSize_min = 4100000
audio_mp = 8
video_mp = 1024
crf=4
default_span=30.0
logo = 'logo.png'
threads = 2
files = []
arg = sys.argv[-1]
queue = []
breakLoop = False
def processWorker(q):
while 1:
(src,s,e),(cw,ch,cx,cy) = q.get()
print( src,s,e,cw,ch,cx,cy)
s = max(0,s)
dur = abs(s-e)
br = int( ((3.8*video_mp)/dur) - ((32 / audio_mp)/dur) ) *8
cropCmd = ''
if cx!=0 and cy !=0 and cw!=0 and ch!=0:
cropCmd = 'crop={}:{}:{}:{},'.format(cw,ch,cx,cy)
brmult=1
os.path.exists('out') or os.mkdir('out')
os.path.exists('temp') or os.mkdir('temp')
outFilename=os.path.join('out',os.path.splitext(os.path.basename(src))[0]+'.'+str(int(s))+'.'+str(int(e))+".webm")
tempname = os.path.join('temp',os.path.splitext(os.path.basename(src))[0]+'.'+str(int(s))+'.'+str(int(e))+".webm")
attempt=0
while 1:
attempt+=1
print( "Attempt {}: {} br:{}[{}]".format(attempt,os.path.basename(src),br,brmult) )
print('PASS 1 Start.')
cmd = ["ffmpeg"
,"-y"
,"-i" , src
,"-i" , logo
,"-ss" , str(s)
,"-to" , str(e)
,"-c:v" ,"libvpx"
,"-threads", str(threads)
,"-quality", "best"
,"-auto-alt-ref", "1"
,"-lag-in-frames", "16"
,"-slices", "8"
,"-passlogfile", tempname+".log"
,"-crf" ,str(crf)
,"-b:v" ,str(br*brmult)+'K'
,"-ac" ,"1"
,"-an"
,"-filter_complex", "[0:v] "+cropCmd+"scale='min("+str(maxWidth)+"\\,iw):-1' [v0], [v0][1:v] overlay='5:5'"
,"-pass" ,"1"
,"-f" ,"webm"
,"nul"]
#print(' '.join(cmd))
proc = sp.Popen(cmd,stderr=sp.PIPE,stdout=sp.PIPE)
proc.communicate()
print('PASS 2 Start.')
cmd = ["ffmpeg"
,"-y"
,"-i" , src
,"-i" , logo
,"-ss" , str(s)
,"-to" , str(e)
,"-c:v" ,"libvpx"
,"-threads", str(threads)
,"-quality", "best"
,"-auto-alt-ref", "1"
,"-lag-in-frames", "16"
,"-slices", "8"
,"-passlogfile", tempname+".log"
,"-crf" ,str(crf)
,"-b:v" ,str(br*brmult)+'K'
,"-ac" ,"1"
,"-c:a" ,"libvorbis"
,"-b:a" ,"32k"
,"-filter_complex", "[0:v] "+cropCmd+"scale='min("+str(maxWidth)+"\\,iw):-1' [v0], [v0][1:v] overlay='5:5'"
,"-pass" ,"2"
,tempname]
#print(' '.join(cmd))
proc = sp.Popen(cmd,stderr=sp.PIPE,stdout=sp.PIPE)
proc.communicate()
finalSize = os.stat(tempname).st_size
if targetSize_min<finalSize<targetSize_max or (finalSize<targetSize_max and attempt>10):
print("Complete.")
os.rename(tempname,outFilename)
break
else:
if finalSize<targetSize_min:
print("File size too small",finalSize,targetSize_min-finalSize)
elif finalSize>targetSize_max:
print("File size too large",finalSize,finalSize-targetSize_max)
brmult= brmult+(1.0-(finalSize/(targetSize_max*0.999) ))
q.task_done()
if __name__ == '__main__':
mp.set_start_method('spawn')
q = mp.JoinableQueue()
pl = []
for _ in range(workers):
p = mp.Process(target=processWorker, args=(q,),daemon=True)
p.start()
pl.append(p)
if os.path.isfile(arg):
g = mimetypes.guess_type(arg)
if g is not None and g[0] is not None and 'video' in g[0]:
files = [arg]
elif os.path.isdir(arg):
for r,dl,fl in os.walk(arg):
for f in fl:
p = os.path.join(r,f)
print(p)
g = mimetypes.guess_type(p)
if g is not None and g[0] is not None and 'video' in g[0]:
files.append(p)
random.shuffle(files)
print(files)
for src in files:
if breakLoop and 'Y' in input('End Seletion?').upper():
break
else:
breakLoop=False
while 1:
if breakLoop:
break
print(src)
player = mpv.MPV(input_default_bindings=True, osc=True)
player.loop_playlist = 'inf'
player.mute = 'yes'
player.speed = 2
cw,ch,cx,cy=0,0,0,0
total_duration=None
current_time=None
selected=False
span=default_span
keydown=False
posa=posb=None
xSelectionPos=None
player.command('load-script','easycrop.lua')
player.play(src)
player.autofit=min(1280,maxWidth)
player.geometry='100%:50%'
seeker = np.zeros(imshape,np.uint8)
def updateScrollImage(mouseX):
global seeker
if mouseX is not None:
seeker[:,:,:] = 0
spanDur = (span/total_duration)*imshape[1]
seeker[:,max(mouseX-int(spanDur//2),0):min(mouseX+int(spanDur//2),imshape[1]),:]=100
seeker[:,mouseX,:]=255
if total_duration is not None and current_time is not None:
seeker[:, min(max(int(imshape[1]*(current_time/total_duration)),0),imshape[1]-1),: ] = (0,255,0)
@player.message_handler('easycrop')
def luaHandler(w,h,x,y):
global cw,ch,cx,cy
cw,ch,cx,cy = int(w),int(h),int(x),int(y)
print(w,h,x,y)
def click(event, x, y, flags, param):
global keydown,total_duration,posa,posb,span,xSelectionPos
if event in (cv2.EVENT_LBUTTONDOWN,cv2.EVENT_LBUTTONUP):
keydown = event==cv2.EVENT_LBUTTONDOWN
if event==cv2.EVENT_MOUSEWHEEL:
incVal = 0.1
if keydown:
incVal = 1.0
if flags>0:
span+=incVal
else:
span-=incVal
span = max(1,span)
if total_duration is not None:
xSelectionPos=x
localDur = (x/imshape[1])*total_duration
posa = min(localDur-(span/2),0)
posb = max(localDur+(span/2),total_duration)
player.ab_loop_a=posa
player.ab_loop_b=posb
player.command('seek', posb-1, 'absolute', 'exact')
updateScrollImage(xSelectionPos)
if keydown:
xSelectionPos=x
if total_duration is not None:
xSelectionPos=x
localDur = (x/imshape[1])*total_duration
posa = localDur-(span/2)
posb = localDur+(span/2)
player.ab_loop_a=posa
player.ab_loop_b=posb
player.command('seek', posb-1, 'absolute', 'exact')
updateScrollImage(xSelectionPos)
def setValues(state,key):
global player,selected,breakLoop
if state=='d-' and key in ('q','e'):
player.terminate()
selected=True
del player
if key == 'e':
print('EKEY')
breakLoop=True
player.register_key_binding("q", setValues)
player.register_key_binding("e", setValues)
player.register_key_binding("CLOSE_WIN", setValues)
@player.property_observer('time-pos')
def time_observer(_name, value):
global total_duration,current_time
current_time=value
if total_duration is None:
total_duration = player.duration
updateScrollImage(xSelectionPos)
cv2.namedWindow("seeker")
cv2.imshow("seeker",seeker)
cv2.setMouseCallback("seeker", click)
while not selected:
cv2.imshow("seeker",seeker)
k = cv2.waitKey(1)
if k in (ord('q'),ord('e')):
setValues('d-',chr(k))
cv2.destroyAllWindows()
if not breakLoop:
q.put( ((src,posa,posb),(cw,ch,cx,cy)) )
if breakLoop:
print('lcBREAK')
break
q.join()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment