def renderAv(ffmpeg, vidFile: str, args, chunks: list, speeds: list, temp, log): import av totalFrames = chunks[len(chunks) - 1][1] videoProgress = ProgressBar(totalFrames, 'Creating new video', args.machine_readable_progress, args.no_progress) input_ = av.open(vidFile) inputVideoStream = input_.streams.video[0] inputVideoStream.thread_type = 'AUTO' width = inputVideoStream.width height = inputVideoStream.height pix_fmt = inputVideoStream.pix_fmt fps = float(inputVideoStream.average_rate) log.debug(f' - pix_fmt: {pix_fmt}') cmd = [ffmpeg.getPath(), '-y', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-pix_fmt', pix_fmt, '-s', f'{width}*{height}', '-framerate', f'{fps}', '-i', '-', '-pix_fmt', pix_fmt] if(args.scale != 1): cmd.extend(['-vf', f'scale=iw*{args.scale}:ih*{args.scale}']) cmd = properties(cmd, args) cmd.append(f'{temp}{sep()}spedup.mp4') if(args.show_ffmpeg_debug): process2 = subprocess.Popen(cmd, stdin=subprocess.PIPE) else: process2 = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) inputEquavalent = 0.0 outputEquavalent = 0 index = 0 chunk = chunks.pop(0) for packet in input_.demux(inputVideoStream): for frame in packet.decode(): index += 1 if(len(chunks) > 0 and index >= chunk[1]): chunk = chunks.pop(0) if(speeds[chunk[2]] != 99999): inputEquavalent += (1 / speeds[chunk[2]]) while inputEquavalent > outputEquavalent: in_bytes = frame.to_ndarray().tobytes() process2.stdin.write(in_bytes) outputEquavalent += 1 videoProgress.tick(index - 1) process2.stdin.close() process2.wait() if(log.is_debug): log.debug('Writing the output file.') else: log.conwrite('Writing the output file.')
def fastAudio(theFile, outFile, chunks: list, speeds: list, log, fps: float, machineReadable, hideBar): from wavfile import read, write import os import numpy as np log.checkType(chunks, 'chunks', list) log.checkType(speeds, 'speeds', list) def speedsOtherThan1And99999(a: list) -> bool: return len([x for x in a if x != 1 and x != 99999]) > 0 if (speedsOtherThan1And99999(speeds)): from audiotsm2 import phasevocoder from audiotsm2.io.array import ArrReader, ArrWriter if (len(chunks) == 1 and chunks[0][2] == 0): log.error('Trying to create an empty file.') if (not os.path.isfile(theFile)): log.error('fastAudio.py could not find file: ' + theFile) samplerate, audioData = read(theFile) newL = getNewLength(chunks, speeds, fps) # Get the new length in samples with some extra leeway. estLeng = int(newL * samplerate * 1.5) + int(samplerate * 2) # Create an empty array for the new audio. newAudio = np.zeros((estLeng, 2), dtype=np.int16) channels = 2 yPointer = 0 audioProgress = ProgressBar(len(chunks), 'Creating new audio', machineReadable, hideBar) for chunkNum, chunk in enumerate(chunks): audioSampleStart = int(chunk[0] / fps * samplerate) audioSampleEnd = int(audioSampleStart + (samplerate / fps) * (chunk[1] - chunk[0])) theSpeed = speeds[chunk[2]] if (theSpeed != 99999): spedChunk = audioData[audioSampleStart:audioSampleEnd] if (theSpeed == 1): yPointerEnd = yPointer + spedChunk.shape[0] newAudio[yPointer:yPointerEnd] = spedChunk else: spedupAudio = np.zeros((0, 2), dtype=np.int16) with ArrReader(spedChunk, channels, samplerate, 2) as reader: with ArrWriter(spedupAudio, channels, samplerate, 2) as writer: phasevocoder(reader.channels, speed=theSpeed).run(reader, writer) spedupAudio = writer.output yPointerEnd = yPointer + spedupAudio.shape[0] newAudio[yPointer:yPointerEnd] = spedupAudio myL = chunk[1] - chunk[0] mySamples = (myL / fps) * samplerate newSamples = int(mySamples / theSpeed) yPointer = yPointer + newSamples else: # Speed is too high so skip this section. yPointerEnd = yPointer audioProgress.tick(chunkNum) log.debug('\n - Total Samples: ' + str(yPointer)) log.debug(' - Samples per Frame: ' + str(samplerate / fps)) log.debug(' - Expected video length: ' + str(yPointer / (samplerate / fps))) newAudio = newAudio[:yPointer] write(outFile, samplerate, newAudio)
def motionDetection(path: str, ffprobe, motionThreshold: float, log, width: int, dilates: int, blur: int) -> np.ndarray: import cv2 import subprocess from usefulFunctions import ProgressBar cap = cv2.VideoCapture(path) # Find total frames if (path.endswith('.mp4') or path.endswith('.mov')): # Query Container cmd = [ ffprobe.getPath(), '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=nb_frames', '-of', 'default=nokey=1:noprint_wrappers=1', path ] else: # Count the number of frames (slow) cmd = [ ffprobe.getPath(), '-v', 'error', '-count_frames', '-select_streams', 'v:0', '-show_entries', 'stream=nb_read_frames', '-of', 'default=nokey=1:noprint_wrappers=1', path ] # Read what ffprobe piped in. process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, __ = process.communicate() output = stdout.decode() totalFrames = int(output) + 1 log.debug(f' - Cutting totalFrames: {totalFrames}') prevFrame = None gray = None hasMotion = np.zeros((totalFrames), dtype=np.bool_) total = None def resize(image, width=None, height=None, inter=cv2.INTER_AREA): if (width is None and height is None): return image h, w = image.shape[:2] if (width is None): r = height / h dim = (int(w * r), height) else: r = width / w dim = (width, int(h * r)) return cv2.resize(image, dim, interpolation=inter) progress = ProgressBar(totalFrames, 'Detecting motion') while cap.isOpened(): if (gray is None): prevFrame = None else: prevFrame = gray ret, frame = cap.read() if (not ret): break cframe = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) # current frame frame = resize(frame, width=width) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Convert frame to grayscale. if (blur > 0): gray = cv2.GaussianBlur(gray, (blur, blur), 0) if (prevFrame is not None): frameDelta = cv2.absdiff(prevFrame, gray) thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1] # Dilate the thresholded image to fill in holes. if (dilates > 0): thresh = cv2.dilate(thresh, None, iterations=dilates) if (total is None): total = thresh.shape[0] * thresh.shape[1] if (np.count_nonzero(thresh) / total >= motionThreshold): hasMotion[cframe] = True progress.tick(cframe) cap.release() cv2.destroyAllWindows() log.conwrite('') return hasMotion
def renderOpencv(ffmpeg, vidFile: str, args, chunks: list, speeds: list, fps, temp, log): import cv2 cap = cv2.VideoCapture(vidFile) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(f'{temp}/spedup.mp4', fourcc, fps, (width, height)) totalFrames = chunks[len(chunks) - 1][1] cframe = 0 cap.set(cv2.CAP_PROP_POS_FRAMES, cframe) remander = 0 framesWritten = 0 videoProgress = ProgressBar(totalFrames, 'Creating new video', args.machine_readable_progress, args.no_progress) def findState(chunks, cframe) -> int: low = 0 high = len(chunks) - 1 while low <= high: mid = low + (high - low) // 2 if (cframe >= chunks[mid][0] and cframe < chunks[mid][1]): return chunks[mid][2] elif (cframe > chunks[mid][0]): low = mid + 1 else: high = mid - 1 # cframe not in chunks return 0 while cap.isOpened(): ret, frame = cap.read() if (not ret or cframe > totalFrames): break cframe = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) # current frame state = findState(chunks, cframe) mySpeed = speeds[state] if (mySpeed != 99999): doIt = (1 / mySpeed) + remander for __ in range(int(doIt)): out.write(frame) framesWritten += 1 remander = doIt % 1 videoProgress.tick(cframe) log.debug(f'\n - Frames Written: {framesWritten}') log.debug(f' - Total Frames: {totalFrames}') cap.release() out.release() cv2.destroyAllWindows() if (args.video_codec != 'uncompressed'): cmd = properties([], args) cmd.append(f'{temp}/spedup.mp4') ffmpeg.run(cmd) if (log.is_debug): log.debug('Writing the output file.') else: log.conwrite('Writing the output file.')
def renderAv(ffmpeg, ffprobe, vidFile: str, args, chunks: list, speeds: list, fps, has_vfr, temp, log): import av totalFrames = chunks[len(chunks) - 1][1] videoProgress = ProgressBar(totalFrames, 'Creating new video', args.machine_readable_progress, args.no_progress) if(has_vfr): class Wrapper: """ Wrapper which only exposes the `read` method to avoid PyAV trying to use `seek`. From: github.com/PyAV-Org/PyAV/issues/578#issuecomment-621362337 """ name = "<wrapped>" def __init__(self, fh): self._fh = fh def read(self, buf_size): return self._fh.read(buf_size) # Create a cfr stream on stdout. cmd = ['-i', vidFile, '-map', '0:v:0', '-vf', f'fps=fps={fps}', '-r', str(fps), '-vsync', '1', '-f', 'matroska', '-vcodec', 'rawvideo', 'pipe:1'] wrapper = Wrapper(ffmpeg.Popen(cmd).stdout) input_ = av.open(wrapper, 'r') else: input_ = av.open(vidFile) inputVideoStream = input_.streams.video[0] inputVideoStream.thread_type = 'AUTO' width = inputVideoStream.width height = inputVideoStream.height pix_fmt = inputVideoStream.pix_fmt log.debug(f' - pix_fmt: {pix_fmt}') cmd = [ffmpeg.getPath(), '-hide_banner', '-y', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-pix_fmt', pix_fmt, '-s', f'{width}*{height}', '-framerate', f'{fps}', '-i', '-', '-pix_fmt', pix_fmt] if(args.scale != 1): cmd.extend(['-vf', f'scale=iw*{args.scale}:ih*{args.scale}']) cmd = properties(cmd, args, vidFile, ffprobe) cmd.append(f'{temp}{sep()}spedup.mp4') if(args.show_ffmpeg_debug): process2 = subprocess.Popen(cmd, stdin=subprocess.PIPE) else: process2 = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) inputEquavalent = 0.0 outputEquavalent = 0 index = 0 chunk = chunks.pop(0) try: for packet in input_.demux(inputVideoStream): for frame in packet.decode(): index += 1 if(len(chunks) > 0 and index >= chunk[1]): chunk = chunks.pop(0) if(speeds[chunk[2]] != 99999): inputEquavalent += (1 / speeds[chunk[2]]) while inputEquavalent > outputEquavalent: in_bytes = frame.to_ndarray().tobytes() process2.stdin.write(in_bytes) outputEquavalent += 1 videoProgress.tick(index - 1) process2.stdin.close() process2.wait() except BrokenPipeError: log.print(cmd) process2 = subprocess.Popen(cmd, stdin=subprocess.PIPE) log.error('Broken Pipe Error!') if(log.is_debug): log.debug('Writing the output file.') else: log.conwrite('Writing the output file.')
def renderOpencv(ffmpeg, ffprobe, vidFile: str, args, chunks: list, speeds: list, fps, has_vfr, effects, temp, log): import cv2 if(has_vfr): cmd = ['-i', vidFile, '-map', '0:v:0', '-vf', f'fps=fps={fps}', '-r', str(fps), '-vsync', '1', '-f','matroska', '-vcodec', 'rawvideo', 'pipe:1'] fileno = ffmpeg.Popen(cmd).stdout.fileno() cap = cv2.VideoCapture('pipe:{}'.format(fileno)) else: cap = cv2.VideoCapture(vidFile) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') if(args.scale != 1): width = int(width * args.scale) height = int(height * args.scale) if(width < 2 or height < 2): log.error('Resolution too small.') log.debug(f'\n Resolution {width}x{height}') out = cv2.VideoWriter(f'{temp}/spedup.mp4', fourcc, fps, (width, height)) totalFrames = chunks[len(chunks) - 1][1] cframe = 0 cap.set(cv2.CAP_PROP_POS_FRAMES, cframe) remander = 0 framesWritten = 0 videoProgress = ProgressBar(totalFrames, 'Creating new video', args.machine_readable_progress, args.no_progress) def findState(chunks, cframe) -> int: low = 0 high = len(chunks) - 1 while low <= high: mid = low + (high - low) // 2 if(cframe >= chunks[mid][0] and cframe < chunks[mid][1]): return chunks[mid][2] elif(cframe > chunks[mid][0]): low = mid + 1 else: high = mid - 1 # cframe not in chunks return 0 import numpy as np from interpolate import interpolate def values(val, log, _type, totalFrames, width, height): if(val == 'centerX'): return int(width / 2) if(val == 'centerY'): return int(height / 2) if(val == 'start'): return 0 if(val == 'end'): return totalFrames - 1 if(val == 'width'): return width if(val == 'height'): return height if(not isinstance(val, int) and not (val.replace('.', '', 1)).replace('-', '', 1).isdigit()): log.error(f'Variable {val} not implemented.') return _type(val) effect_sheet = [] for effect in effects: if(effect[0] == 'rectangle'): rectx1_sheet = np.zeros((totalFrames + 1), dtype=int) recty1_sheet = np.zeros((totalFrames + 1), dtype=int) rectx2_sheet = np.zeros((totalFrames + 1), dtype=int) recty2_sheet = np.zeros((totalFrames + 1), dtype=int) rectco_sheet = np.zeros((totalFrames + 1, 3), dtype=int) rect_t_sheet = np.zeros((totalFrames + 1), dtype=int) r = effect[1:] for i in range(6): r[i] = values(r[i], log, int, totalFrames, width, height) rectx1_sheet[r[0]:r[1]] = r[2] recty1_sheet[r[0]:r[1]] = r[3] rectx2_sheet[r[0]:r[1]] = r[4] recty2_sheet[r[0]:r[1]] = r[5] rectco_sheet[r[0]:r[1]] = r[6] rect_t_sheet[r[0]:r[1]] = r[7] effect_sheet.append( ['rectangle', rectx1_sheet, recty1_sheet, rectx2_sheet, recty2_sheet, rectco_sheet, rect_t_sheet] ) if(effect[0] == 'zoom'): zoom_sheet = np.ones((totalFrames + 1), dtype=float) zoomx_sheet = np.full((totalFrames + 1), int(width / 2), dtype=float) zoomy_sheet = np.full((totalFrames + 1), int(height / 2), dtype=float) z = effect[1:] z[0] = values(z[0], log, int, totalFrames, width, height) z[1] = values(z[1], log, int, totalFrames, width, height) if(z[7] is not None): # hold value z[7] = values(z[7], log, int, totalFrames, width, height) if(z[7] is None or z[7] > z[1]): zoom_sheet[z[0]:z[1]] = interpolate(z[2], z[3], z[1] - z[0], log, method=z[6]) else: zoom_sheet[z[0]:z[0]+z[7]] = interpolate(z[2], z[3], z[7], log, method=z[6]) zoom_sheet[z[0]+z[7]:z[1]] = z[3] zoomx_sheet[z[0]:z[1]] = values(z[4], log, float, totalFrames, width, height) zoomy_sheet[z[0]:z[1]] = values(z[5], log, float, totalFrames, width, height) effect_sheet.append( ['zoom', zoom_sheet, zoomx_sheet, zoomy_sheet] ) while cap.isOpened(): ret, frame = cap.read() if(not ret or cframe > totalFrames): break for effect in effect_sheet: if(effect[0] == 'rectangle'): x1 = int(effect[1][cframe]) y1 = int(effect[2][cframe]) x2 = int(effect[3][cframe]) y2 = int(effect[4][cframe]) if(x1 == y1 and y1 == x2 and x2 == y2 and y2 == 0): pass else: np_color = effect[5][cframe] color = (int(np_color[0]), int(np_color[1]), int(np_color[2])) t = int(effect[6][cframe]) frame = cv2.rectangle(frame, (x1,y1), (x2,y2), color, thickness=t) if(effect[0] == 'zoom'): zoom = effect[1][cframe] zoom_x = effect[2][cframe] zoom_y = effect[3][cframe] # Resize Frame new_size = (int(width * zoom), int(height * zoom)) if(zoom == 1 and args.scale == 1): blown = frame elif(new_size[0] < 1 or new_size[1] < 1): blown = cv2.resize(frame, (1, 1), interpolation=cv2.INTER_AREA) else: inter = cv2.INTER_CUBIC if zoom > 1 else cv2.INTER_AREA blown = cv2.resize(frame, new_size, interpolation=inter) x1 = int((zoom_x * zoom)) - int((width / 2)) x2 = int((zoom_x * zoom)) + int((width / 2)) y1 = int((zoom_y * zoom)) - int((height / 2)) y2 = int((zoom_y * zoom)) + int((height / 2)) top, bottom, left, right = 0, 0, 0, 0 if(y1 < 0): top = -y1 y1 = 0 if(x1 < 0): left = -x1 x1 = 0 frame = blown[y1:y2+1, x1:x2+1] bottom = (height + 1) - (frame.shape[0]) - top right = (width + 1) - frame.shape[1] - left frame = cv2.copyMakeBorder( frame, top = top, bottom = bottom, left = left, right = right, borderType = cv2.BORDER_CONSTANT, value = args.background ) if(frame.shape != (height+1, width+1, 3)): # Throw error so that opencv dropped frames don't go unnoticed. print(f'cframe {cframe}') log.error(f'Wrong frame shape. was {frame.shape},' \ f' should be {(height+1, width+1, 3)} ') if(effects == [] and args.scale != 1): inter = cv2.INTER_CUBIC if args.scale > 1 else cv2.INTER_AREA frame = cv2.resize(frame, (width, height), interpolation=inter) cframe = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) # current frame state = findState(chunks, cframe) mySpeed = speeds[state] if(mySpeed != 99999): doIt = (1 / mySpeed) + remander for __ in range(int(doIt)): out.write(frame) framesWritten += 1 remander = doIt % 1 videoProgress.tick(cframe) log.debug(f'\n - Frames Written: {framesWritten}') log.debug(f' - Total Frames: {totalFrames}') cap.release() out.release() cv2.destroyAllWindows() cmd = properties(['-i', vidFile], args, vidFile, ffprobe) cmd.append(f'{temp}/spedup.mp4') ffmpeg.run(cmd) if(log.is_debug): log.debug('Writing the output file.') else: log.conwrite('Writing the output file.')
def fastVideo(vidFile: str, chunks: list, includeFrame: np.ndarray, speeds: list, fps, machineReadable, hideBar, temp, log): import cv2 cap = cv2.VideoCapture(vidFile) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(f'{temp}/spedup.mp4', fourcc, fps, (width, height)) log.checkType(vidFile, 'vidFile', str) log.checkType(includeFrame, 'includeFrame', np.ndarray) if (speeds[0] == 99999 and speeds[1] != 99999): totalFrames = int(np.where(includeFrame == True)[0][-1]) cframe = int(np.where(includeFrame == True)[0][0]) elif (speeds[0] != 99999 and speeds[1] == 99999): totalFrames = int(np.where(includeFrame == False)[0][-1]) cframe = int(np.where(includeFrame == False)[0][0]) else: totalFrames = chunks[len(chunks) - 1][1] cframe = 0 starting = cframe cap.set(cv2.CAP_PROP_POS_FRAMES, cframe) remander = 0 framesWritten = 0 videoProgress = ProgressBar(totalFrames - starting, 'Creating new video', machineReadable, hideBar) while cap.isOpened(): ret, frame = cap.read() if (not ret or cframe > totalFrames): break cframe = int(cap.get(cv2.CAP_PROP_POS_FRAMES)) # current frame try: state = includeFrame[cframe] except IndexError: state = False mySpeed = speeds[state] if (mySpeed != 99999): doIt = (1 / mySpeed) + remander for __ in range(int(doIt)): out.write(frame) framesWritten += 1 remander = doIt % 1 videoProgress.tick(cframe - starting) log.debug(f'\n - Frames Written: {framesWritten}') log.debug(f' - Starting: {starting}') log.debug(f' - Total Frames: {totalFrames}') if (log.is_debug): log.debug('Writing the output file.') else: log.conwrite('Writing the output file.') cap.release() out.release() cv2.destroyAllWindows()