From 62ab09e3f36dcaf6c1a4680dc6c4d048fb2e165c Mon Sep 17 00:00:00 2001 From: tassaron Date: Sat, 15 Jul 2017 01:00:03 -0400 Subject: Video comp verifies audio streams, videoThread moved into Core off-by-1 bug fixed in exporting, & use fewer threads for fewer CPUs --- src/command.py | 22 ++++++++-------------- src/components/video.py | 25 ++++++++++++++++++++----- src/core.py | 16 ++++++++++++++++ src/mainwindow.py | 22 ++++++---------------- src/video_thread.py | 35 +++++++++++++++++++++++------------ 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/src/command.py b/src/command.py index be194d8..41618f8 100644 --- a/src/command.py +++ b/src/command.py @@ -9,13 +9,12 @@ import os import sys import core -import video_thread from toolkit import LoadDefaultSettings class Command(QtCore.QObject): - videoTask = QtCore.pyqtSignal(str, str, list) + createVideo = QtCore.pyqtSignal() def __init__(self): QtCore.QObject.__init__(self) @@ -112,21 +111,16 @@ class Command(QtCore.QObject): quit(1) def createAudioVisualisation(self, input, output): - self.videoThread = QtCore.QThread(self) - self.videoWorker = video_thread.Worker(self) - self.videoWorker.moveToThread(self.videoThread) - self.videoWorker.videoCreated.connect(self.videoCreated) - - self.videoThread.start() - self.videoTask.emit( - input, - output, - list(reversed(self.core.selectedComponents)) + self.core.selectedComponents = list( + reversed(self.core.selectedComponents)) + self.core.componentListChanged() + self.worker = self.core.newVideoWorker( + self, input, output ) + self.worker.videoCreated.connect(self.videoCreated) + self.createVideo.emit() def videoCreated(self): - self.videoThread.quit() - self.videoThread.wait() quit(0) def showMessage(self, **kwargs): diff --git a/src/components/video.py b/src/components/video.py index 8aa1420..b3b6a59 100644 --- a/src/components/video.py +++ b/src/components/video.py @@ -8,7 +8,7 @@ from queue import PriorityQueue from component import Component, BadComponentInit from frame import BlankFrame -from toolkit import openPipe +from toolkit import openPipe, checkOutput class Video: @@ -155,14 +155,29 @@ class Component(Component): def properties(self): props = [] - if self.useAudio: - props.append('audio') if not self.videoPath or self.badVideo \ or not os.path.exists(self.videoPath): - props.append('error') + return ['error'] + + if self.useAudio: + props.append('audio') + # test if an audio stream really exists + audioTestCommand = [ + self.core.FFMPEG_BIN, + '-i', self.videoPath, + '-vn', '-f', 'null', '-' + ] + try: + checkOutput(audioTestCommand, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.badAudio = True + return ['error'] + return props def error(self): + if hasattr(self, 'badAudio'): + return "Could not identify an audio stream in this video." if not self.videoPath: return "There is no video selected." if not os.path.exists(self.videoPath): @@ -180,7 +195,7 @@ class Component(Component): self.blankFrame_ = BlankFrame(width, height) self.updateChunksize(width, height) self.video = Video( - ffmpeg=self.parent.core.FFMPEG_BIN, videoPath=self.videoPath, + ffmpeg=self.core.FFMPEG_BIN, videoPath=self.videoPath, width=width, height=height, chunkSize=self.chunkSize, frameRate=int(self.settings.value("outputFrameRate")), parent=self.parent, loopVideo=self.loopVideo, diff --git a/src/core.py b/src/core.py index 2500fa6..55bf261 100644 --- a/src/core.py +++ b/src/core.py @@ -12,6 +12,7 @@ from PyQt5.QtCore import QStandardPaths import toolkit from frame import Frame +import video_thread class Core: @@ -633,6 +634,21 @@ class Core: return completeAudioArray + def newVideoWorker(self, loader, audioFile, outputPath): + self.videoThread = QtCore.QThread(loader) + videoWorker = video_thread.Worker( + loader, audioFile, outputPath, self.selectedComponents + ) + videoWorker.moveToThread(self.videoThread) + videoWorker.videoCreated.connect(self.videoCreated) + + self.videoThread.start() + return videoWorker + + def videoCreated(self): + self.videoThread.quit() + self.videoThread.wait() + def cancel(self): self.canceled = True diff --git a/src/mainwindow.py b/src/mainwindow.py index 771b6b8..76ed179 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -16,7 +16,6 @@ import time import core import preview_thread -import video_thread from presetmanager import PresetManager from toolkit import LoadDefaultSettings, disableWhenEncoding, checkOutput @@ -49,9 +48,9 @@ class PreviewWindow(QtWidgets.QLabel): class MainWindow(QtWidgets.QMainWindow): - newTask = QtCore.pyqtSignal(list) + createVideo = QtCore.pyqtSignal() + newTask = QtCore.pyqtSignal(list) # for the preview window processTask = QtCore.pyqtSignal() - videoTask = QtCore.pyqtSignal(str, str, list) def __init__(self, window, project): QtWidgets.QMainWindow.__init__(self) @@ -497,20 +496,15 @@ class MainWindow(QtWidgets.QMainWindow): self.canceled = False self.progressBarUpdated(-1) - self.videoThread = QtCore.QThread(self) - self.videoWorker = video_thread.Worker(self) - self.videoWorker.moveToThread(self.videoThread) - self.videoWorker.videoCreated.connect(self.videoCreated) + self.videoWorker = self.core.newVideoWorker( + self, audioFile, outputPath + ) self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated) self.videoWorker.progressBarSetText.connect( self.progressBarSetText) self.videoWorker.imageCreated.connect(self.showPreviewImage) self.videoWorker.encoding.connect(self.changeEncodingStatus) - self.videoThread.start() - self.videoTask.emit( - audioFile, - outputPath, - self.core.selectedComponents) + self.createVideo.emit() def changeEncodingStatus(self, status): self.encoding = status @@ -569,10 +563,6 @@ class MainWindow(QtWidgets.QMainWindow): else: self.window.progressBar_createVideo.setFormat(value) - def videoCreated(self): - self.videoThread.quit() - self.videoThread.wait() - def updateResolution(self): resIndex = int(self.window.comboBox_resolution.currentIndex()) res = self.resolutions[resIndex].split('x') diff --git a/src/video_thread.py b/src/video_thread.py index b0562db..5295a3b 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -19,7 +19,7 @@ import time import signal import core -from toolkit import openPipe, checkOutput +from toolkit import openPipe from frame import Checkerboard @@ -31,13 +31,19 @@ class Worker(QtCore.QObject): progressBarSetText = pyqtSignal(str) encoding = pyqtSignal(bool) - def __init__(self, parent=None): + + def __init__(self, parent, inputFile, outputFile, components): QtCore.QObject.__init__(self) self.core = parent.core self.settings = parent.core.settings self.modules = parent.core.modules + parent.createVideo.connect(self.createVideo) + self.parent = parent - parent.videoTask.connect(self.createVideo) + self.components = components + self.outputFile = outputFile + self.inputFile = inputFile + self.sampleSize = 1470 # 44100 / 30 = 1470 self.canceled = False self.error = False @@ -55,7 +61,7 @@ class Worker(QtCore.QObject): bgI = int(audioI / self.sampleSize) frame = None for compNo, comp in reversed(list(enumerate(self.components))): - layerNo = len(self.components) - compNo + layerNo = len(self.components) - compNo - 1 if layerNo in self.staticComponents: if self.staticComponents[layerNo] is None: # this layer was merged into a following layer @@ -106,12 +112,10 @@ class Worker(QtCore.QObject): self.previewQueue.task_done() - @pyqtSlot(str, str, list) - def createVideo(self, inputFile, outputFile, components): + @pyqtSlot() + def createVideo(self): numpy.seterr(divide='ignore') self.encoding.emit(True) - self.components = components - self.outputFile = outputFile self.extraAudio = [] self.width = int(self.settings.value('outputWidth')) self.height = int(self.settings.value('outputHeight')) @@ -131,7 +135,7 @@ class Worker(QtCore.QObject): # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ self.progressBarSetText.emit("Loading audio file...") - self.completeAudioArray = self.core.readAudioFile(inputFile, self) + self.completeAudioArray = self.core.readAudioFile(self.inputFile, self) self.progressBarUpdate.emit(0) self.progressBarSetText.emit("Starting components...") @@ -189,7 +193,9 @@ class Worker(QtCore.QObject): ) self.staticComponents[compNo] = None - ffmpegCommand = self.core.createFfmpegCommand(inputFile, outputFile) + ffmpegCommand = self.core.createFfmpegCommand( + self.inputFile, self.outputFile + ) print('###### FFMPEG COMMAND ######\n%s' % " ".join(ffmpegCommand)) print('############################') self.out_pipe = openPipe( @@ -200,9 +206,14 @@ class Worker(QtCore.QObject): # START CREATING THE VIDEO # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ - # Make three renderNodes in new threads to create the frames + # Make 2 or 3 renderNodes in new threads to create the frames self.renderThreads = [] - for i in range(3): + try: + numCpus = len(os.sched_getaffinity(0)) + except: + numCpus = os.cpu_count() + + for i in range(2 if numCpus <= 2 else 3): self.renderThreads.append( Thread(target=self.renderNode, name="Render Thread")) self.renderThreads[i].daemon = True -- cgit v1.2.3