From c13d8a3e8a23dcedfcd3d7cea800e7c29aa47e80 Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 18:27:26 -0400 Subject: log QThread IDs --- src/gui/preview_thread.py | 1 + src/video_thread.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/gui/preview_thread.py b/src/gui/preview_thread.py index 137864b..8ec5d28 100644 --- a/src/gui/preview_thread.py +++ b/src/gui/preview_thread.py @@ -38,6 +38,7 @@ class Worker(QtCore.QObject): "components": components, } self.queue.put(dic) + log.debug('Preview thread id: {}'.format(int(QtCore.QThread.currentThreadId()))) @pyqtSlot() def process(self): diff --git a/src/video_thread.py b/src/video_thread.py index 5a28beb..0472aa3 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -133,6 +133,8 @@ class Worker(QtCore.QObject): @pyqtSlot() def createVideo(self): log.debug("Video worker received signal to createVideo") + log.debug( + 'Video thread id: {}'.format(int(QtCore.QThread.currentThreadId()))) numpy.seterr(divide='ignore') self.encoding.emit(True) self.extraAudio = [] -- cgit v1.2.3 From fdbaf844230b01ff96f20863b762cc83fad34c38 Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 19:37:02 -0400 Subject: add useful comment about why this reference exists --- src/gui/preview_thread.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/preview_thread.py b/src/gui/preview_thread.py index 8ec5d28..3943a5c 100644 --- a/src/gui/preview_thread.py +++ b/src/gui/preview_thread.py @@ -80,6 +80,8 @@ class Worker(QtCore.QObject): except RuntimeError as e: log.error(str(e)) else: + # We must store a reference to this QImage + # or else Qt will garbage-collect it on the C++ side self.frame = ImageQt(frame) self.imageCreated.emit(QtGui.QImage(self.frame)) -- cgit v1.2.3 From e8097b4065d257586ffa4a16f7ed98f4120656ca Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 19:39:21 -0400 Subject: fixes #70 - store a ref to preview frame & update preview synchronously removing the Python thread might not have been necessary. I will test this next --- src/gui/mainwindow.py | 4 ++-- src/video_thread.py | 43 +++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/gui/mainwindow.py b/src/gui/mainwindow.py index f6de763..2bfc4e3 100644 --- a/src/gui/mainwindow.py +++ b/src/gui/mainwindow.py @@ -4,7 +4,7 @@ This shows a preview of the video being created and allows for saving projects and exporting the video at a later time. ''' -from PyQt5 import QtCore, QtGui, QtWidgets, uic +from PyQt5 import QtCore, QtWidgets, uic import PyQt5.QtWidgets as QtWidgets from PIL import Image from queue import Queue @@ -754,7 +754,7 @@ class MainWindow(QtWidgets.QMainWindow): self.autosave() self.updateWindowTitle() - @QtCore.pyqtSlot(QtGui.QImage) + @QtCore.pyqtSlot('QImage') def showPreviewImage(self, image): self.previewWindow.changePixmap(image) diff --git a/src/video_thread.py b/src/video_thread.py index 0472aa3..0a95201 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -114,21 +114,19 @@ class Worker(QtCore.QObject): for audioI in range(0, self.audioArrayLen, self.sampleSize): self.compositeQueue.put(audioI) - def previewDispatch(self): + def showPreview(self, frame): ''' - Grabs frames from the previewQueue, adds them to the checkerboard - and emits a final QImage to the MainWindow for the live preview + Receives a final frame that will be piped to FFmpeg, + adds it to the checkerboard and emits a final QImage + to the MainWindow for the live preview ''' background = Checkerboard(self.width, self.height) - - while not self.stopped: - audioI, frame = self.previewQueue.get() - if time.time() - self.lastPreview >= 0.06 or audioI == 0: - image = Image.alpha_composite(background.copy(), frame) - self.imageCreated.emit(QtGui.QImage(ImageQt(image))) - self.lastPreview = time.time() - - self.previewQueue.task_done() + image = Image.alpha_composite(background.copy(), frame) + # We must store a reference to this QImage + # or else Qt will garbage-collect it on the C++ side + self.latestPreview = ImageQt(image) + self.imageCreated.emit(QtGui.QImage(self.latestPreview)) + self.lastPreview = time.time() @pyqtSlot() def createVideo(self): @@ -145,7 +143,6 @@ class Worker(QtCore.QObject): self.compositeQueue.maxsize = 20 self.renderQueue = PriorityQueue() self.renderQueue.maxsize = 20 - self.previewQueue = PriorityQueue() self.reset() progressBarValue = 0 @@ -307,15 +304,13 @@ class Worker(QtCore.QObject): self.dispatchThread.daemon = True self.dispatchThread.start() - self.lastPreview = 0.0 - self.previewDispatch = Thread( - target=self.previewDispatch, name="Render Dispatch Thread" - ) - self.previewDispatch.daemon = True - self.previewDispatch.start() + # Last time preview was drawn + self.lastPreview = time.time() # Begin piping into ffmpeg! - frameBuffer = {} + frameBuffer = { + # audioI: bytes ready to be piped + } progressBarValue = 0 self.progressBarUpdate.emit(progressBarValue) self.progressBarSetText.emit("Exporting video...") @@ -328,14 +323,17 @@ class Worker(QtCore.QObject): break # else fetch the next frame & add to the buffer audioI_, frame = self.renderQueue.get() - frameBuffer[audioI_] = frame + frameBuffer[audioI_] = frame.copy() self.renderQueue.task_done() if self.canceled: break + # Update live preview + if time.time() - self.lastPreview > 0.5: + self.showPreview(frameBuffer[audioI]) + try: self.out_pipe.stdin.write(frameBuffer[audioI].tobytes()) - self.previewQueue.put([audioI, frameBuffer.pop(audioI)]) except Exception: break @@ -348,6 +346,7 @@ class Worker(QtCore.QObject): "Exporting video: %s%%" % str(int(progressBarValue)) ) + numpy.seterr(all='print') self.closePipe() -- cgit v1.2.3 From 8293328d66a27245e1c90e19b1fbc004ee8ef033 Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 19:55:29 -0400 Subject: Don't render checkerboard during preview It was nice for consistency with the editing preview, but this slows down the main thread if we're doing the preview synchronously. And it's not really inaccurate to the final product, as far as I know. --- src/video_thread.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/video_thread.py b/src/video_thread.py index 0a95201..0895547 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -120,11 +120,9 @@ class Worker(QtCore.QObject): adds it to the checkerboard and emits a final QImage to the MainWindow for the live preview ''' - background = Checkerboard(self.width, self.height) - image = Image.alpha_composite(background.copy(), frame) # We must store a reference to this QImage # or else Qt will garbage-collect it on the C++ side - self.latestPreview = ImageQt(image) + self.latestPreview = ImageQt(frame) self.imageCreated.emit(QtGui.QImage(self.latestPreview)) self.lastPreview = time.time() -- cgit v1.2.3 From 7f97e8f155c4a51517670c0dc7574f88afeffe6c Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 20:16:47 -0400 Subject: add commandline option to disable preview during export the weird use of type() is to avoid restructuring the code at this time. I will refactor this in a different pull request --- src/command.py | 9 ++++++++- src/core.py | 1 + src/video_thread.py | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/command.py b/src/command.py index bf7941a..6d76906 100644 --- a/src/command.py +++ b/src/command.py @@ -58,12 +58,16 @@ class Command(QtCore.QObject): ) self.parser.add_argument( '--test', action='store_true', - help='run tests, generate logfiles, then exit' + help='run tests and create a report full of debugging info' ) self.parser.add_argument( '--debug', action='store_true', help='create bigger logfiles while program is running' ) + self.parser.add_argument( + '--no-preview', action='store_true', + help='disable live preview during export in GUI mode' + ) # optional arguments self.parser.add_argument( @@ -140,6 +144,9 @@ class Command(QtCore.QObject): self.createAudioVisualisation(self.args.input, self.args.output) return "commandline" + elif self.args.no_preview: + core.Core.previewEnabled = False + elif 'help' not in sys.argv and self.args.projpath is None and '--debug' not in sys.argv: self.parser.print_help() quit(1) diff --git a/src/core.py b/src/core.py index 77b0894..1ad4a67 100644 --- a/src/core.py +++ b/src/core.py @@ -487,6 +487,7 @@ class Core: ], 'logDir': os.path.join(dataDir, 'log'), 'logEnabled': False, + 'previewEnabled': True, } settings['videoFormats'] = toolkit.appendUppercase([ diff --git a/src/video_thread.py b/src/video_thread.py index 0895547..27379ef 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -44,6 +44,7 @@ class Worker(QtCore.QObject): self.settings = parent.settings self.modules = parent.core.modules parent.createVideo.connect(self.createVideo) + self.previewEnabled = type(parent.core).previewEnabled #self.parent = parent self.components = components @@ -327,7 +328,7 @@ class Worker(QtCore.QObject): break # Update live preview - if time.time() - self.lastPreview > 0.5: + if self.previewEnabled and time.time() - self.lastPreview > 0.5: self.showPreview(frameBuffer[audioI]) try: -- cgit v1.2.3 From 6c3410da0b7fc39a9bc613c208b6e6cd3d3c6ed2 Mon Sep 17 00:00:00 2001 From: tassaron Date: Mon, 2 May 2022 20:29:06 -0400 Subject: remove unneeded call to .copy() --- src/video_thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_thread.py b/src/video_thread.py index 27379ef..dc6db89 100644 --- a/src/video_thread.py +++ b/src/video_thread.py @@ -322,7 +322,7 @@ class Worker(QtCore.QObject): break # else fetch the next frame & add to the buffer audioI_, frame = self.renderQueue.get() - frameBuffer[audioI_] = frame.copy() + frameBuffer[audioI_] = frame self.renderQueue.task_done() if self.canceled: break -- cgit v1.2.3