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 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'src/command.py') 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): -- cgit v1.2.3 From bcb8f27c2e4434d2296dcd66bf279b76ee0d0a4f Mon Sep 17 00:00:00 2001 From: tassaron Date: Sat, 15 Jul 2017 13:13:53 -0400 Subject: use -t on inputs so ffmpeg knows when to stop filters + better feedback in cmd mode --- src/command.py | 20 ++++++++++++++++++++ src/components/sound.py | 5 +++++ src/components/video.py | 38 ++++++++++++++++++++++++++------------ src/core.py | 8 ++++++-- src/main.py | 11 ++++++----- src/video_thread.py | 8 ++++---- 6 files changed, 67 insertions(+), 23 deletions(-) (limited to 'src/command.py') diff --git a/src/command.py b/src/command.py index 41618f8..84d798d 100644 --- a/src/command.py +++ b/src/command.py @@ -7,6 +7,7 @@ from PyQt5 import QtCore import argparse import os import sys +import time import core from toolkit import LoadDefaultSettings @@ -118,8 +119,27 @@ class Command(QtCore.QObject): self, input, output ) self.worker.videoCreated.connect(self.videoCreated) + self.lastProgressUpdate = time.time() + self.worker.progressBarSetText.connect(self.progressBarSetText) self.createVideo.emit() + @QtCore.pyqtSlot(str) + def progressBarSetText(self, value): + if 'Export ' in value: + # Don't duplicate completion/failure messages + return + if not value.startswith('Exporting') \ + and time.time() - self.lastProgressUpdate >= 0.05: + # Show most messages very often + print(value) + elif time.time() - self.lastProgressUpdate >= 2.0: + # Give user time to read ffmpeg's output during the export + print('##### %s' % value) + else: + return + self.lastProgressUpdate = time.time() + + @QtCore.pyqtSlot() def videoCreated(self): quit(0) diff --git a/src/components/sound.py b/src/components/sound.py index fedc32b..4a5714b 100644 --- a/src/components/sound.py +++ b/src/components/sound.py @@ -79,6 +79,11 @@ class Component(Component): if not arg.startswith('preset=') and '=' in arg: key, arg = arg.split('=', 1) if key == 'path': + if '*%s' % os.path.splitext(arg)[1] \ + not in self.core.audioFormats: + print("Not a supported audio format") + quit(1) self.page.lineEdit_sound.setText(arg) return + super().command(arg) diff --git a/src/components/video.py b/src/components/video.py index b3b6a59..0b93293 100644 --- a/src/components/video.py +++ b/src/components/video.py @@ -116,6 +116,7 @@ class Component(Component): page = self.loadUi('video.ui') self.videoPath = '' self.badVideo = False + self.badAudio = False self.x = 0 self.y = 0 self.loopVideo = False @@ -161,22 +162,14 @@ class Component(Component): 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 + self.testAudioStream() + if self.badAudio: return ['error'] return props def error(self): - if hasattr(self, 'badAudio'): + if self.badAudio: return "Could not identify an audio stream in this video." if not self.videoPath: return "There is no video selected." @@ -185,6 +178,20 @@ class Component(Component): if self.badVideo: return "The video selected is corrupt!" + def testAudioStream(self): + # 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 + else: + self.badAudio = False + def audio(self): return (self.videoPath, {'map': '-v'}) @@ -277,7 +284,7 @@ class Component(Component): if not arg.startswith('preset=') and '=' in arg: key, arg = arg.split('=', 1) if key == 'path' and os.path.exists(arg): - if os.path.splitext(arg)[1] in self.core.videoFormats: + if '*%s' % os.path.splitext(arg)[1] in self.core.videoFormats: self.page.lineEdit_video.setText(arg) self.page.spinBox_scale.setValue(100) self.page.checkBox_loop.setChecked(True) @@ -285,10 +292,17 @@ class Component(Component): else: print("Not a supported video format") quit(1) + elif arg == 'audio': + if not self.page.lineEdit_video.text(): + print("'audio' option must follow a video selection") + quit(1) + self.page.checkBox_useAudio.setChecked(True) + return super().command(arg) def commandHelp(self): print('Load a video:\n path=/filepath/to/video.mp4') + print('Using audio:\n path=/filepath/to/video.mp4 audio') def scale(scale, width, height, returntype=None): diff --git a/src/core.py b/src/core.py index 55bf261..4c12209 100644 --- a/src/core.py +++ b/src/core.py @@ -464,10 +464,11 @@ class Core: except sp.CalledProcessError: return "avconv" - def createFfmpegCommand(self, inputFile, outputFile): + def createFfmpegCommand(self, inputFile, outputFile, duration): ''' Constructs the major ffmpeg command used to export the video ''' + duration = str(duration) # Test if user has libfdk_aac encoders = toolkit.checkOutput( @@ -516,10 +517,12 @@ class Core: ), '-pix_fmt', 'rgba', '-r', self.settings.value('outputFrameRate'), + '-t', duration, '-i', '-', # the video input comes from a pipe '-an', # the video input has no sound # INPUT SOUND + '-t', duration, '-i', inputFile ] @@ -532,6 +535,7 @@ class Core: for streamNo, params in enumerate(extraAudio): extraInputFile, params = params ffmpegCommand.extend([ + '-t', duration, '-i', extraInputFile ]) if 'map' in params and params['map'] == '-v': @@ -632,7 +636,7 @@ class Core: completeAudioArrayCopy[:len(completeAudioArray)] = completeAudioArray completeAudioArray = completeAudioArrayCopy - return completeAudioArray + return (completeAudioArray, duration) def newVideoWorker(self, loader, audioFile, outputPath): self.videoThread = QtCore.QThread(loader) diff --git a/src/main.py b/src/main.py index b0ece29..2216d2a 100644 --- a/src/main.py +++ b/src/main.py @@ -8,13 +8,13 @@ import video_thread if __name__ == "__main__": - mode = 'gui' + mode = 'GUI' if len(sys.argv) > 2: - mode = 'cmd' + mode = 'commandline' elif len(sys.argv) == 2: if sys.argv[1].startswith('-'): - mode = 'cmd' + mode = 'commandline' else: # opening a project file with gui proj = sys.argv[1] @@ -22,16 +22,17 @@ if __name__ == "__main__": # normal gui launch proj = None + print('Starting Audio Visualizer in %s mode' % mode) app = QtWidgets.QApplication(sys.argv) app.setApplicationName("audio-visualizer") # app.setOrganizationName("audio-visualizer") - if mode == 'cmd': + if mode == 'commandline': from command import * main = Command() - elif mode == 'gui': + elif mode == 'GUI': from mainwindow import * import atexit import signal diff --git a/src/video_thread.py b/src/video_thread.py index 5295a3b..674765a 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -31,7 +31,6 @@ class Worker(QtCore.QObject): progressBarSetText = pyqtSignal(str) encoding = pyqtSignal(bool) - def __init__(self, parent, inputFile, outputFile, components): QtCore.QObject.__init__(self) self.core = parent.core @@ -135,7 +134,9 @@ class Worker(QtCore.QObject): # =~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~==~=~=~=~=~=~=~=~=~=~=~=~=~=~ self.progressBarSetText.emit("Loading audio file...") - self.completeAudioArray = self.core.readAudioFile(self.inputFile, self) + self.completeAudioArray, duration = self.core.readAudioFile( + self.inputFile, self + ) self.progressBarUpdate.emit(0) self.progressBarSetText.emit("Starting components...") @@ -144,7 +145,6 @@ class Worker(QtCore.QObject): for num, component in enumerate(reversed(self.components)) ])) self.staticComponents = {} - numComps = len(self.components) for compNo, comp in enumerate(reversed(self.components)): comp.preFrameRender( worker=self, @@ -194,7 +194,7 @@ class Worker(QtCore.QObject): self.staticComponents[compNo] = None ffmpegCommand = self.core.createFfmpegCommand( - self.inputFile, self.outputFile + self.inputFile, self.outputFile, duration ) print('###### FFMPEG COMMAND ######\n%s' % " ".join(ffmpegCommand)) print('############################') -- cgit v1.2.3