Skip to content

Commit

Permalink
various updates
Browse files Browse the repository at this point in the history
  • Loading branch information
dfaker committed Mar 16, 2024
1 parent 58f2c29 commit 0206652
Show file tree
Hide file tree
Showing 16 changed files with 1,347 additions and 155 deletions.
10 changes: 10 additions & 0 deletions src/cutselectionController.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,16 @@ def findRangeforLoop(self,secondsCenter,minSeconds,maxSeconds,rect):
cropCoords = (x1,y1,x2-x1,y2-y1)
self.ffmpegService.findRangeforLoop( self.currentlyPlayingFileName,secondsCenter,minSeconds,maxSeconds,cropCoords,self.foundLoopCallback )

def similarSoundCallback(self,filename,mse,tslist):
for s,e in tslist:
self.videoManager.registerNewSubclip(filename,s,e)
self.ui.setUiDirtyFlag()

def findSimilarSounds(self,startTime,endTime,limit,distance):
if self.currentlyPlayingFileName is not None:
self.ffmpegService.findSimilarSounds( self.currentlyPlayingFileName,startTime,endTime,limit,distance,self.similarSoundCallback )


def sceneChangeCallback(self,filename,timestamp,timestampEnd=None,kind='Mark'):
if kind == 'Mark':
self.videoManager.addNewInterestMark(filename,timestamp,kind='sceneChange')
Expand Down
16 changes: 15 additions & 1 deletion src/cutselectionUi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
import time
import subprocess as sp

from .modalWindows import PerfectLoopScanModal, YoutubeDLModal, TimestampModal, VoiceActivityDetectorModal, Tooltip, CutSpecificationPlanner, EditSubclipModal
from .modalWindows import (PerfectLoopScanModal,
YoutubeDLModal,
TimestampModal,
VoiceActivityDetectorModal,
Tooltip,
CutSpecificationPlanner,
EditSubclipModal,
FindMatchingSoundsModal)

from .timeLineSelectionFrameUI import TimeLineSelectionFrameUI

Expand Down Expand Up @@ -1109,6 +1116,13 @@ def clipWatchWorker(windowRef):
tk.messagebox.showinfo(title="Watching clipboard", message="Monitoring clipboard for urls, any urls copied will be downloaded with youtube-dl.\nClick 'OK' to stop watching.")
windowRef.cliprunWatch=False

def findSimilarSounds(self,startTime,endTime,limit,distance):
self.controller.findSimilarSounds(self,startTime,endTime,limit,distance)

def canvasPopupSimilarSounds(self, rid):
smm = FindMatchingSoundsModal(master=self,controller=self.controller, rid=rid)
smm.mainloop()

def loadVideoFiles(self):
initialdir=self.controller.getGlobalOptions().get('defaultVideoFolder','.')
filetypes=(('All files', '*.*'),)
Expand Down
120 changes: 116 additions & 4 deletions src/ffmpegService.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
from .ffmpegInfoParser import getVideoInfo
from .masonry import Brick,Stack


import subprocess as sp
import numpy as np
from collections import defaultdict, deque
Expand Down Expand Up @@ -90,6 +89,56 @@ def lucas_kanade_np(im1, im2, win=2):

return mag.max()-mag.mean()

mfccs = None
lastmfccfilename = ''
lastmfccsSr = 0
lastsampleToFind = None

def find_similar_regions(filename, start_sec, end_sec, num_matches=30, min_distance_sec=1):
global mfccs, lastmfccfilename, lastsampleToFind, ccsr

import librosa

if mfccs is None or filename != lastmfccfilename:
audio, ccsr = librosa.load(filename, sr=None)
mfccs = librosa.feature.mfcc(y=audio, sr=ccsr)
lastmfccfilename = filename


start_frame = librosa.time_to_frames(start_sec, sr=ccsr)
end_frame = librosa.time_to_frames(end_sec, sr=ccsr)

if lastsampleToFind is not None and start == 0 and end == 0:
target_mfccs = lastsampleToFind
else:
target_mfccs = mfccs[:, start_frame:end_frame]

lastsampleToFind = target_mfccs

window_length = end_frame - start_frame
min_distance_frames = librosa.time_to_frames(min_distance_sec, sr=ccsr)
similarities = []
for i in range(mfccs.shape[1] - window_length + 1):
if abs(i - start_frame) < min_distance_frames or abs(i + window_length - end_frame) < min_distance_frames:
continue
window_mfccs = mfccs[:, i:i+window_length]
distance = np.linalg.norm(target_mfccs - window_mfccs)
similarities.append((distance, i, i+window_length))

similarities.sort(key=lambda x: x[0])

final_matches = []
for distance, start, end in similarities:
if all(abs(start - prev_start) >= min_distance_frames and abs(end - prev_end) >= min_distance_frames
for prev_start, prev_end in final_matches):
final_matches.append((start, end))
if len(final_matches) == num_matches:
break
similar_regions = [(librosa.frames_to_time(start, sr=ccsr),
librosa.frames_to_time(end, sr=ccsr)) for start, end in final_matches]

return similar_regions

encoderMap = {
'webm:VP8':webmvp8Encoder
,'webm:VP9':webmvp9Encoder
Expand All @@ -112,6 +161,27 @@ def lucas_kanade_np(im1, im2, win=2):
except Exception as e:
print('Custom Encode Spec Exception',fn,e)

isEnvencSupportedFlag = None
def isEnvencSupported():

global isEnvencSupportedFlag

if isEnvencSupportedFlag is not None:
return isEnvencSupportedFlag

proc = sp.Popen(['ffmpeg', '-loglevel', 'error', '-f', 'lavfi',
'-i', 'color=black:s=1080x1080', '-vframes', '1',
'-an', '-c:v', 'hevc_nvenc', '-f', 'null', '-'],stdout=sp.PIPE, stderr=sp.PIPE)
outs,errs = proc.communicate()
resp = (outs+errs).strip()

if len(resp) == 0:
isEnvencSupportedFlag = True
logging.debug("hevc_nvenc support confirmed!")
else:
isEnvencSupportedFlag = False
logging.debug("hevc_nvenc not supported!")
return isEnvencSupportedFlag


class FFmpegService():
Expand All @@ -135,6 +205,28 @@ def convertFactorToAtempoSequence(self,target_change):
atempos.append(target_change)
return ','.join(['atempo={}'.format(i) for i in atempos])

def getFilteredScreenshot(self,filename,timestamp,filters,n='a'):

popen_params = {
"bufsize": 10 ** 5,
"stdout": sp.PIPE
}

vi = getVideoInfo(filename,filters)

print("filters")
print(filters)

divisor = 1

cmd1=['ffmpeg','-y',"-loglevel", "quiet",'-filter_complex',filters,'-ss',str(timestamp),'-i',cleanFilenameForFfmpeg(filename), "-pix_fmt", "rgb24", '-vframes', '1', "-f","image2pipe","-an","-pix_fmt","bgr24","-vcodec","rawvideo","-vsync", "vfr","-"]

f1 = np.frombuffer(sp.Popen(cmd1,**popen_params).stdout.read(), dtype="uint8")
f1.shape = (vi.height,vi.width,3)

return f1,divisor


def __init__(self,globalStatusCallback=print,imageWorkerCount=2,encodeWorkerCount=1,statsWorkerCount=1,globalOptions={}):

self.globalOptions=globalOptions
Expand Down Expand Up @@ -749,6 +841,7 @@ def encodeGrid(tempPathname,outputPathName,runNumber,requestId,mode,seqClips,opt
totalExpectedEncodedSeconds,
statusCallback, requestId=requestId, globalOptions=self.globalOptions)


def encodeConcat(tempPathname,outputPathName,runNumber,requestId,mode,seqClips,options,filenamePrefix,statusCallback):

expectedTimes = []
Expand All @@ -759,7 +852,7 @@ def encodeConcat(tempPathname,outputPathName,runNumber,requestId,mode,seqClips,o
preciseDurations = {}
infoOut={}

usNVHWenc = self.globalOptions.get('alwaysForcenvEncIntermediateFiles',False) or ('_Nvenc' in options.get('outputFormat','mp4:x264') and self.globalOptions.get('nvEncIntermediateFiles',True))
usNVHWenc = isEnvencSupported() and self.globalOptions.get('alwaysForcenvEncIntermediateFiles',False) or ('_Nvenc' in options.get('outputFormat','mp4:x264') and self.globalOptions.get('nvEncIntermediateFiles',True))

fadeStartToEnd = options.get('fadeStartToEnd',True)

Expand Down Expand Up @@ -1375,8 +1468,19 @@ def statsWorker():
filename,requestType,options,callback = self.statsRequestQueue.get()
print(requestType,options)

if requestType =='SimilarSounds':

filename=options.get('filename')
start=options.get('start')
end=options.get('end')
limit=options.get('limit')
distance=options.get('distance')
similar_regions = find_similar_regions(filename, start, end, num_matches=limit, min_distance_sec=distance)
print(similar_regions)

callback(filename,1,similar_regions)

if requestType =='MaxInterFrameMove':
elif requestType =='MaxInterFrameMove':


logging.debug('inter frame move start')
Expand Down Expand Up @@ -2296,7 +2400,15 @@ def findRangeforLoop(self,filename,secondsCenter,minSeconds,maxSeconds,cropRect,
minSeconds=minSeconds,
maxSeconds=maxSeconds,
cropRect=cropRect),callback) )


def findSimilarSounds(self,filename,start,end,limit,distance,callback):
self.statsRequestQueue.put( (filename,'SimilarSounds',dict(filename=filename,
start=start,
end=end,
limit=limit,
distance=distance),callback))


def fullLoopSearch(self,filename,duration,callback,midThreshold=30,minLength=1,maxLength=5,timeSkip=1,threshold=30,addCuts=True,useRange=False,rangeStart=None,rangeEnd=None,ifdmode=False,selectionMode='bestFirst'):
self.statsRequestQueue.put( (filename,'FullLoopSearch',dict(filename=filename,
duration=duration,
Expand Down
8 changes: 8 additions & 0 deletions src/filterSelectionController.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def getClipDuration(self):
d = e-s
return d

def getScreenshot(self,ts,filters):
shot = self.ffmpegService.getFilteredScreenshot(self.currentlyPlayingFileName,ts,filters)
return shot

def getTemplateListing(self):
return self.templates.items()

Expand Down Expand Up @@ -251,6 +255,10 @@ def setVideoRect(self,x,y,w,h,desc=''):
def clearVideoRect(self):
self.player.command('script-message','screenspacetools_clear')

def updateSketch(self,sketch):
sketchstr = json.dumps(sketch).replace('[','{').replace(']','}')
self.player.command('script-message','screenspacetools_sketch',sketchstr)

def getVideoDimensions(self):
osd_w = self.player.width
osd_h = self.player.height
Expand Down
Loading

0 comments on commit 0206652

Please sign in to comment.