From 825b7af6e30deaf85646ce93507d8cb5a0b426ae Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sat, 3 Jun 2017 20:39:32 -0400
Subject: start of background replacement components
---
components/video.py | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 components/video.py
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
new file mode 100644
index 0000000..1365f34
--- /dev/null
+++ b/components/video.py
@@ -0,0 +1,42 @@
+from PIL import Image, ImageDraw
+from PyQt4 import uic, QtGui, QtCore
+import os
+from . import __base__
+
+class Component(__base__.Component):
+ '''Video'''
+ def widget(self, parent):
+ self.parent = parent
+ page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'video.ui'))
+ self.page = page
+ return page
+
+ def update(self):
+ # read widget values
+ self.parent.drawPreview()
+
+ def previewRender(self, previewWorker):
+ width = int(previewWorker.core.settings.value('outputWidth'))
+ height = int(previewWorker.core.settings.value('outputHeight'))
+ return self.drawFrame(width, height)
+
+ def frameRender(self, moduleNo, frameNo):
+ width = int(self.worker.core.settings.value('outputWidth'))
+ height = int(self.worker.core.settings.value('outputHeight'))
+ return self.drawFrame(width, height)
+
+ def drawFrame(self, width, height):
+ return Image.new("RGBA", (width, height), (0,0,0,255))
+
+ def loadPreset(self, presetDict):
+ # update widgets using a preset dict
+ pass
+
+ def savePreset(self):
+ return {}
+
+ def cancel(self):
+ self.canceled = True
+
+ def reset(self):
+ self.canceled = False
--
cgit v1.2.3
From cfb8e17b6362719ca736997a23a939bec4975e70 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sat, 3 Jun 2017 22:58:40 -0400
Subject: basic image component
---
components/color.py | 15 +++++++++++----
components/image.py | 36 ++++++++++++++++++++++++++++++------
components/image.ui | 11 ++++-------
components/video.py | 38 ++++++++++++++++++++++++++++++++------
components/video.ui | 13 +++++--------
5 files changed, 82 insertions(+), 31 deletions(-)
(limited to 'components/video.py')
diff --git a/components/color.py b/components/color.py
index d86470c..ae818e2 100644
--- a/components/color.py
+++ b/components/color.py
@@ -57,12 +57,19 @@ class Component(__base__.Component):
r,g,b = self.color1
return Image.new("RGBA", (width, height), (r, g, b, 255))
- def loadPreset(self, presetDict):
- # update widgets using a preset dict
- pass
+ def loadPreset(self, pr):
+ self.page.lineEdit_color1.setText('%s,%s,%s' % pr['color1'])
+ self.page.lineEdit_color2.setText('%s,%s,%s' % pr['color2'])
+ btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['color1']).name()
+ self.page.pushButton_color1.setStyleSheet(btnStyle)
+ btnStyle = "QPushButton { background-color : %s; outline: none; }" % QColor(*pr['color2']).name()
+ self.page.pushButton_color2.setStyleSheet(btnStyle)
def savePreset(self):
- return {}
+ return {
+ 'color1' : self.color1,
+ 'color2' : self.color2,
+ }
def pickColor(self, num):
RGBstring, btnStyle = super().pickColor()
diff --git a/components/image.py b/components/image.py
index 3176d7c..021bb9e 100644
--- a/components/image.py
+++ b/components/image.py
@@ -7,12 +7,20 @@ class Component(__base__.Component):
'''Image'''
def widget(self, parent):
self.parent = parent
+ self.settings = parent.settings
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'image.ui'))
+ self.imagePath = ''
+ self.x = 0
+ self.y = 0
+
+ page.lineEdit_image.textChanged.connect(self.update)
+ page.pushButton_image.clicked.connect(self.pickImage)
+
self.page = page
return page
def update(self):
- # read widget values
+ self.imagePath = self.page.lineEdit_image.text()
self.parent.drawPreview()
def previewRender(self, previewWorker):
@@ -26,17 +34,33 @@ class Component(__base__.Component):
return self.drawFrame(width, height)
def drawFrame(self, width, height):
- return Image.new("RGBA", (width, height), (0,0,0,255))
+ frame = Image.new("RGBA", (width, height), (0,0,0,0))
+ if self.imagePath and os.path.exists(self.imagePath):
+ image = Image.open(self.imagePath)
+ if image.size != (width, height):
+ image = image.resize((width, height), Image.ANTIALIAS)
+ frame.paste(image)
+ return frame
- def loadPreset(self, presetDict):
- # update widgets using a preset dict
- pass
+ def loadPreset(self, pr):
+ self.page.lineEdit_image.setText(pr['image'])
def savePreset(self):
- return {}
+ return {
+ 'image' : self.imagePath,
+ }
def cancel(self):
self.canceled = True
def reset(self):
self.canceled = False
+
+ def pickImage(self):
+ imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
+ filename = QtGui.QFileDialog.getOpenFileName(self.page,
+ "Choose Image", imgDir, "Image Files (*.jpg *.png)")
+ if filename:
+ self.settings.setValue("backgroundDir", os.path.dirname(filename))
+ self.page.lineEdit_image.setText(filename)
+ self.update()
diff --git a/components/image.ui b/components/image.ui
index 6a24370..3cd5b1b 100644
--- a/components/image.ui
+++ b/components/image.ui
@@ -41,20 +41,17 @@
-
-
+
1
0
-
- 12
-
-
-
+
0
@@ -114,7 +111,7 @@
-
-
+
0
@@ -146,7 +143,7 @@
-
-
+
0
diff --git a/components/video.py b/components/video.py
index 1365f34..561e40b 100644
--- a/components/video.py
+++ b/components/video.py
@@ -7,12 +7,20 @@ class Component(__base__.Component):
'''Video'''
def widget(self, parent):
self.parent = parent
+ self.settings = parent.settings
page = uic.loadUi(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'video.ui'))
+ self.videoPath = ''
+ self.x = 0
+ self.y = 0
+
+ page.lineEdit_video.textChanged.connect(self.update)
+ page.pushButton_video.clicked.connect(self.pickVideo)
+
self.page = page
return page
def update(self):
- # read widget values
+ self.videoPath = self.page.lineEdit_video.text()
self.parent.drawPreview()
def previewRender(self, previewWorker):
@@ -26,17 +34,35 @@ class Component(__base__.Component):
return self.drawFrame(width, height)
def drawFrame(self, width, height):
- return Image.new("RGBA", (width, height), (0,0,0,255))
+ frame = Image.new("RGBA", (width, height), (0,0,0,0))
+ '''
+ if self.imagePath and os.path.exists(self.imagePath):
+ image = Image.open(self.imagePath)
+ if image.size != (width, height):
+ image = image.resize((width, height), Image.ANTIALIAS)
+ frame.paste(image)
+ '''
+ return frame
- def loadPreset(self, presetDict):
- # update widgets using a preset dict
- pass
+ def loadPreset(self, pr):
+ self.page.lineEdit_video.setText(pr['video'])
def savePreset(self):
- return {}
+ return {
+ 'video' : self.videoPath,
+ }
def cancel(self):
self.canceled = True
def reset(self):
self.canceled = False
+
+ def pickVideo(self):
+ imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
+ filename = QtGui.QFileDialog.getOpenFileName(self.page,
+ "Choose Video", imgDir, "Video Files (*.mp4)")
+ if filename:
+ self.settings.setValue("backgroundDir", os.path.dirname(filename))
+ self.page.lineEdit_video.setText(filename)
+ self.update()
diff --git a/components/video.ui b/components/video.ui
index 73697f3..6a01368 100644
--- a/components/video.ui
+++ b/components/video.ui
@@ -41,20 +41,17 @@
-
-
+
1
0
-
- 12
-
-
-
+
0
@@ -114,7 +111,7 @@
-
-
+
0
@@ -146,7 +143,7 @@
-
-
+
0
@@ -183,7 +180,7 @@
-
-
-
+
Loop
--
cgit v1.2.3
From 443c65455a1cae8ccaea0f0af7cdda3919c709f8 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 4 Jun 2017 00:19:10 -0400
Subject: half-finished video component
will finish tomorrow
---
components/video.py | 67 +++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 49 insertions(+), 18 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index 561e40b..3d6ba7a 100644
--- a/components/video.py
+++ b/components/video.py
@@ -1,6 +1,6 @@
from PIL import Image, ImageDraw
from PyQt4 import uic, QtGui, QtCore
-import os
+import os, subprocess
from . import __base__
class Component(__base__.Component):
@@ -24,25 +24,27 @@ class Component(__base__.Component):
self.parent.drawPreview()
def previewRender(self, previewWorker):
- width = int(previewWorker.core.settings.value('outputWidth'))
- height = int(previewWorker.core.settings.value('outputHeight'))
- return self.drawFrame(width, height)
+ self.width = int(previewWorker.core.settings.value('outputWidth'))
+ self.height = int(previewWorker.core.settings.value('outputHeight'))
+ frames = self.getVideoFrames(True)
+ if frames:
+ im = Image.open(frames[0])
+ im = self.resize(im)
+ return im
+ else:
+ return Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
+
+ def preFrameRender(self, **kwargs):
+ super().__init__(**kwargs)
+ self.width = int(self.worker.core.settings.value('outputWidth'))
+ self.height = int(self.worker.core.settings.value('outputHeight'))
+ self.frames = self.getVideoFrames()
def frameRender(self, moduleNo, frameNo):
- width = int(self.worker.core.settings.value('outputWidth'))
- height = int(self.worker.core.settings.value('outputHeight'))
- return self.drawFrame(width, height)
-
- def drawFrame(self, width, height):
- frame = Image.new("RGBA", (width, height), (0,0,0,0))
- '''
- if self.imagePath and os.path.exists(self.imagePath):
- image = Image.open(self.imagePath)
- if image.size != (width, height):
- image = image.resize((width, height), Image.ANTIALIAS)
- frame.paste(image)
- '''
- return frame
+ i = frameNo if frameNo < len(self.frames)-1 else len(self.frames)-1
+ im = Image.open(self.frames[i])
+ im = self.resize(im)
+ return im
def loadPreset(self, pr):
self.page.lineEdit_video.setText(pr['video'])
@@ -66,3 +68,32 @@ class Component(__base__.Component):
self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_video.setText(filename)
self.update()
+
+ def getVideoFrames(self, firstOnly=False):
+ # recreate the temporary directory so it is empty
+ # FIXME: don't dump too many frames at once
+ if not self.videoPath:
+ return
+ self.parent.core.deleteTempDir()
+ os.mkdir(self.parent.core.tempDir)
+ if firstOnly:
+ filename = 'preview%s.jpg' % os.path.basename(self.videoPath).split('.', 1)[0]
+ options = '-ss 10 -vframes 1'
+ else:
+ filename = '$frame%05d.jpg'
+ options = ''
+ subprocess.call( \
+ '%s -i "%s" -y %s "%s"' % ( \
+ self.parent.core.FFMPEG_BIN,
+ self.videoPath,
+ options,
+ os.path.join(self.parent.core.tempDir, filename)
+ ),
+ shell=True
+ )
+ return sorted([os.path.join(self.parent.core.tempDir, f) for f in os.listdir(self.parent.core.tempDir)])
+
+ def resize(self, im):
+ if im.size != (self.width, self.height):
+ im = im.resize((self.width, self.height), Image.ANTIALIAS)
+ return im
--
cgit v1.2.3
From 39e66ffa2d07b87b57ed90b369ab26aedf0a69e8 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 4 Jun 2017 13:00:36 -0400
Subject: video component almost working, rm hardcoded backgrounds
---
background.jpg | Bin 14782 -> 0 bytes
background.png | Bin 0 -> 3711 bytes
components/__base__.py | 7 +++++
components/color.py | 16 +++++------
components/image.py | 14 +++++-----
components/original.py | 20 ++++----------
components/text.py | 8 +-----
components/video.py | 68 +++++++++++++++++++++++++++++-----------------
core.py | 61 +++++------------------------------------
main.py | 50 +++++++++++-----------------------
mainwindow.ui | 72 ++-----------------------------------------------
preview_thread.py | 20 +++-----------
video_thread.py | 46 +++++++++----------------------
13 files changed, 110 insertions(+), 272 deletions(-)
delete mode 100644 background.jpg
create mode 100644 background.png
(limited to 'components/video.py')
diff --git a/background.jpg b/background.jpg
deleted file mode 100644
index f746432..0000000
Binary files a/background.jpg and /dev/null differ
diff --git a/background.png b/background.png
new file mode 100644
index 0000000..7a33158
Binary files /dev/null and b/background.png differ
diff --git a/components/__base__.py b/components/__base__.py
index 45c148d..f564aad 100644
--- a/components/__base__.py
+++ b/components/__base__.py
@@ -7,6 +7,13 @@ class Component:
def version(self):
# change this number to identify new versions of a component
return 1
+
+ def cancel(self):
+ # make sure your component responds to these variables in frameRender()
+ self.canceled = True
+
+ def reset(self):
+ self.canceled = False
def preFrameRender(self, **kwargs):
for var, value in kwargs.items():
diff --git a/components/color.py b/components/color.py
index ae818e2..c2a49e2 100644
--- a/components/color.py
+++ b/components/color.py
@@ -42,13 +42,17 @@ class Component(__base__.Component):
self.x = self.page.spinBox_x.value()
self.y = self.page.spinBox_y.value()
self.parent.drawPreview()
-
+
def previewRender(self, previewWorker):
width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
-
- def frameRender(self, moduleNo, frameNo):
+
+ def preFrameRender(self, **kwargs):
+ super().preFrameRender(**kwargs)
+ return ['static']
+
+ def frameRender(self, moduleNo, arrayNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
@@ -81,9 +85,3 @@ class Component(__base__.Component):
else:
self.page.lineEdit_color2.setText(RGBstring)
self.page.pushButton_color2.setStyleSheet(btnStyle)
-
- def cancel(self):
- self.canceled = True
-
- def reset(self):
- self.canceled = False
diff --git a/components/image.py b/components/image.py
index 021bb9e..ffbb117 100644
--- a/components/image.py
+++ b/components/image.py
@@ -27,8 +27,12 @@ class Component(__base__.Component):
width = int(previewWorker.core.settings.value('outputWidth'))
height = int(previewWorker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
-
- def frameRender(self, moduleNo, frameNo):
+
+ def preFrameRender(self, **kwargs):
+ super().preFrameRender(**kwargs)
+ return ['static']
+
+ def frameRender(self, moduleNo, arrayNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.drawFrame(width, height)
@@ -49,12 +53,6 @@ class Component(__base__.Component):
return {
'image' : self.imagePath,
}
-
- def cancel(self):
- self.canceled = True
-
- def reset(self):
- self.canceled = False
def pickImage(self):
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
diff --git a/components/original.py b/components/original.py
index 6903a5f..b0a7579 100644
--- a/components/original.py
+++ b/components/original.py
@@ -58,6 +58,8 @@ class Component(__base__.Component):
self.smoothConstantUp = 0.8
self.lastSpectrum = None
self.spectrumArray = {}
+ self.width = int(self.worker.core.settings.value('outputWidth'))
+ self.height = int(self.worker.core.settings.value('outputHeight'))
for i in range(0, len(self.completeAudioArray), self.sampleSize):
if self.canceled:
@@ -72,12 +74,9 @@ class Component(__base__.Component):
pStr = "Analyzing audio: "+ str(progress) +'%'
self.progressBarSetText.emit(pStr)
self.progressBarUpdate.emit(int(progress))
-
-
- def frameRender(self, moduleNo, frameNo):
- width = int(self.worker.core.settings.value('outputWidth'))
- height = int(self.worker.core.settings.value('outputHeight'))
- return self.drawBars(width, height, self.spectrumArray[frameNo], self.visColor, self.layout)
+
+ def frameRender(self, moduleNo, arrayNo, frameNo):
+ return self.drawBars(self.width, self.height, self.spectrumArray[arrayNo], self.visColor, self.layout)
def pickColor(self):
RGBstring, btnStyle = super().pickColor()
@@ -154,12 +153,3 @@ class Component(__base__.Component):
return im
- def cancel(self):
- self.canceled = True
-
- def reset(self):
- self.canceled = False
-
-
-
-
diff --git a/components/text.py b/components/text.py
index d2e009d..2650935 100644
--- a/components/text.py
+++ b/components/text.py
@@ -104,7 +104,7 @@ class Component(__base__.Component):
super().preFrameRender(**kwargs)
return ['static']
- def frameRender(self, moduleNo, frameNo):
+ def frameRender(self, moduleNo, arrayNo, frameNo):
width = int(self.worker.core.settings.value('outputWidth'))
height = int(self.worker.core.settings.value('outputHeight'))
return self.addText(width, height)
@@ -137,9 +137,3 @@ class Component(__base__.Component):
return
self.page.lineEdit_textColor.setText(RGBstring)
self.page.pushButton_textColor.setStyleSheet(btnStyle)
-
- def cancel(self):
- self.canceled = True
-
- def reset(self):
- self.canceled = False
diff --git a/components/video.py b/components/video.py
index 3d6ba7a..1a9f344 100644
--- a/components/video.py
+++ b/components/video.py
@@ -5,6 +5,10 @@ from . import __base__
class Component(__base__.Component):
'''Video'''
+ def __init__(self):
+ super().__init__()
+ self.working = False
+
def widget(self, parent):
self.parent = parent
self.settings = parent.settings
@@ -27,24 +31,35 @@ class Component(__base__.Component):
self.width = int(previewWorker.core.settings.value('outputWidth'))
self.height = int(previewWorker.core.settings.value('outputHeight'))
frames = self.getVideoFrames(True)
- if frames:
- im = Image.open(frames[0])
- im = self.resize(im)
- return im
- else:
- return Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
+ if not hasattr(self, 'staticFrame') or not self.working and frames:
+ frame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
+ if frames:
+ im = Image.open(frames[0])
+ im = self.resize(im)
+ frame.paste(im)
+ if not self.working:
+ self.staticFrame = frame
+ return self.staticFrame
def preFrameRender(self, **kwargs):
- super().__init__(**kwargs)
+ super().preFrameRender(**kwargs)
self.width = int(self.worker.core.settings.value('outputWidth'))
self.height = int(self.worker.core.settings.value('outputHeight'))
self.frames = self.getVideoFrames()
+ self.working = True
- def frameRender(self, moduleNo, frameNo):
- i = frameNo if frameNo < len(self.frames)-1 else len(self.frames)-1
- im = Image.open(self.frames[i])
- im = self.resize(im)
- return im
+ def frameRender(self, moduleNo, arrayNo, frameNo):
+ print(frameNo)
+ try:
+ if frameNo < len(self.frames)-1:
+ self.staticFrame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
+ im = Image.open(self.frames[frameNo])
+ im = self.resize(im)
+ self.staticFrame.paste(im)
+ except FileNotFoundError:
+ print("Video component encountered an error")
+ self.frames = []
+ return self.staticFrame
def loadPreset(self, pr):
self.page.lineEdit_video.setText(pr['video'])
@@ -53,12 +68,6 @@ class Component(__base__.Component):
return {
'video' : self.videoPath,
}
-
- def cancel(self):
- self.canceled = True
-
- def reset(self):
- self.canceled = False
def pickVideo(self):
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
@@ -69,19 +78,27 @@ class Component(__base__.Component):
self.page.lineEdit_video.setText(filename)
self.update()
- def getVideoFrames(self, firstOnly=False):
+ def getVideoFrames(self, preview=False):
# recreate the temporary directory so it is empty
- # FIXME: don't dump too many frames at once
+ # FIXME: don't dump all the frames at once, don't dump more than sound length
+ # FIXME: make cancellable, report status to user, etc etc etc
if not self.videoPath:
return
+ name = os.path.basename(self.videoPath).split('.', 1)[0]
+ if preview:
+ filename = 'preview%s.jpg' % name
+ if os.path.exists(os.path.join(self.parent.core.tempDir, filename)):
+ return False
+ else:
+ filename = name+'-frame%05d.jpg'
+
+ # recreate tempDir and dump needed frame(s)
self.parent.core.deleteTempDir()
os.mkdir(self.parent.core.tempDir)
- if firstOnly:
- filename = 'preview%s.jpg' % os.path.basename(self.videoPath).split('.', 1)[0]
- options = '-ss 10 -vframes 1'
+ if preview:
+ options = '-ss 10 -vframes 1'
else:
- filename = '$frame%05d.jpg'
- options = ''
+ options = '' #'-vframes 99999'
subprocess.call( \
'%s -i "%s" -y %s "%s"' % ( \
self.parent.core.FFMPEG_BIN,
@@ -91,6 +108,7 @@ class Component(__base__.Component):
),
shell=True
)
+ print('### Got Preview Frame From %s ###' % name if preview else '### Finished Dumping Frames From %s ###' % name)
return sorted([os.path.join(self.parent.core.tempDir, f) for f in os.listdir(self.parent.core.tempDir)])
def resize(self, im):
diff --git a/core.py b/core.py
index 16ecb35..ecbf12c 100644
--- a/core.py
+++ b/core.py
@@ -13,11 +13,8 @@ from collections import OrderedDict
class Core():
def __init__(self):
- self.lastBackgroundImage = ""
- self._image = None
-
self.FFMPEG_BIN = self.findFfmpeg()
- self.tempDir = None
+ self.tempDir = os.path.join(tempfile.gettempdir(), 'audio-visualizer-python-data')
atexit.register(self.deleteTempDir)
def findFfmpeg(self):
@@ -31,31 +28,6 @@ class Core():
except:
return "avconv"
- def parseBaseImage(self, backgroundImage, preview=False):
- ''' determines if the base image is a single frame or list of frames '''
- if backgroundImage == "":
- return ['']
- else:
- _, bgExt = os.path.splitext(backgroundImage)
- if not bgExt == '.mp4':
- return [backgroundImage]
- else:
- return self.getVideoFrames(backgroundImage, preview)
-
- def drawBaseImage(self, backgroundFile):
- if backgroundFile == '':
- im = Image.new("RGB", (int(self.settings.value('outputWidth')), int(self.settings.value('outputHeight'))), "black")
- else:
- im = Image.open(backgroundFile)
-
- if self._image == None or not self.lastBackgroundImage == backgroundFile:
- self.lastBackgroundImage = backgroundFile
- # resize if necessary
- if not im.size == (int(self.settings.value('outputWidth')), int(self.settings.value('outputHeight'))):
- im = im.resize((int(self.settings.value('outputWidth')), int(self.settings.value('outputHeight'))), Image.ANTIALIAS)
-
- return im
-
def readAudioFile(self, filename, parent):
command = [ self.FFMPEG_BIN,
'-i', filename]
@@ -121,30 +93,11 @@ class Core():
return completeAudioArray
def deleteTempDir(self):
- if self.tempDir and os.path.exists(self.tempDir):
- rmtree(self.tempDir)
-
- def getVideoFrames(self, videoPath, firstOnly=False):
- self.tempDir = os.path.join(tempfile.gettempdir(), 'audio-visualizer-python-data')
- # recreate the temporary directory so it is empty
- self.deleteTempDir()
- os.mkdir(self.tempDir)
- if firstOnly:
- filename = 'preview%s.jpg' % os.path.basename(videoPath).split('.', 1)[0]
- options = '-ss 10 -vframes 1'
- else:
- filename = '$frame%05d.jpg'
- options = ''
- sp.call( \
- '%s -i "%s" -y %s "%s"' % ( \
- self.FFMPEG_BIN,
- videoPath,
- options,
- os.path.join(self.tempDir, filename)
- ),
- shell=True
- )
- return sorted([os.path.join(self.tempDir, f) for f in os.listdir(self.tempDir)])
+ try:
+ if os.path.exists(self.tempDir):
+ rmtree(self.tempDir)
+ except FileNotFoundError:
+ pass
def cancel(self):
self.canceled = True
@@ -153,6 +106,6 @@ class Core():
self.canceled = False
@staticmethod
- def sortedStringDict(dictionary):
+ def stringOrderedDict(dictionary):
sorted_ = OrderedDict(sorted(dictionary.items(), key=lambda t: t[0]))
return repr(sorted_)
diff --git a/main.py b/main.py
index 2aa7fa9..da72b1e 100644
--- a/main.py
+++ b/main.py
@@ -133,9 +133,9 @@ class PreviewWindow(QtGui.QLabel):
class Main(QtCore.QObject):
- newTask = QtCore.pyqtSignal(str, list)
+ newTask = QtCore.pyqtSignal(list)
processTask = QtCore.pyqtSignal()
- videoTask = QtCore.pyqtSignal(str, str, str, list)
+ videoTask = QtCore.pyqtSignal(str, str, list)
def __init__(self, window):
QtCore.QObject.__init__(self)
@@ -172,14 +172,13 @@ class Main(QtCore.QObject):
# begin decorating the window and connecting events
window.toolButton_selectAudioFile.clicked.connect(self.openInputFileDialog)
- window.toolButton_selectBackground.clicked.connect(self.openBackgroundFileDialog)
window.toolButton_selectOutputFile.clicked.connect(self.openOutputFileDialog)
window.progressBar_createVideo.setValue(0)
window.pushButton_createVideo.clicked.connect(self.createAudioVisualisation)
window.pushButton_Cancel.clicked.connect(self.stopVideo)
window.setWindowTitle("Audio Visualizer")
- self.previewWindow = PreviewWindow(self, os.path.join(os.path.dirname(os.path.realpath(__file__)), "background.jpg"))
+ self.previewWindow = PreviewWindow(self, os.path.join(os.path.dirname(os.path.realpath(__file__)), "background.png"))
window.verticalLayout_previewWrapper.addWidget(self.previewWindow)
self.modules = self.findComponents()
@@ -260,17 +259,6 @@ class Main(QtCore.QObject):
self.settings.setValue("outputDir", os.path.dirname(fileName))
self.window.lineEdit_outputFile.setText(fileName)
- def openBackgroundFileDialog(self):
- backgroundDir = self.settings.value("backgroundDir", expanduser("~"))
-
- fileName = QtGui.QFileDialog.getOpenFileName(self.window,
- "Open Background Image", backgroundDir, "Image Files (*.jpg *.png);; Video Files (*.mp4)");
-
- if not fileName == "":
- self.settings.setValue("backgroundDir", os.path.dirname(fileName))
- self.window.lineEdit_background.setText(fileName)
- self.drawPreview()
-
def stopVideo(self):
print('stop')
self.videoWorker.cancel()
@@ -291,8 +279,7 @@ class Main(QtCore.QObject):
self.videoWorker.imageCreated.connect(self.showPreviewImage)
self.videoWorker.encoding.connect(self.changeEncodingStatus)
self.videoThread.start()
- self.videoTask.emit(self.window.lineEdit_background.text(),
- self.window.lineEdit_audioFile.text(),
+ self.videoTask.emit(self.window.lineEdit_audioFile.text(),
self.window.lineEdit_outputFile.text(),
self.selectedComponents)
else:
@@ -323,10 +310,6 @@ class Main(QtCore.QObject):
self.window.pushButton_savePreset.setEnabled(False)
self.window.pushButton_openProject.setEnabled(False)
self.window.listWidget_componentList.setEnabled(False)
-
- self.window.label_background.setEnabled(False)
- self.window.lineEdit_background.setEnabled(False)
- self.window.toolButton_selectBackground.setEnabled(False)
else:
self.window.pushButton_createVideo.setEnabled(True)
self.window.pushButton_Cancel.setEnabled(False)
@@ -349,12 +332,6 @@ class Main(QtCore.QObject):
self.window.pushButton_openProject.setEnabled(True)
self.window.listWidget_componentList.setEnabled(True)
- self.window.label_background.setEnabled(True)
- self.window.lineEdit_background.setEnabled(True)
- self.window.toolButton_selectBackground.setEnabled(True)
-
-
-
def progressBarSetText(self, value):
self.window.progressBar_createVideo.setFormat(value)
@@ -370,7 +347,7 @@ class Main(QtCore.QObject):
self.drawPreview()
def drawPreview(self):
- self.newTask.emit(self.window.lineEdit_background.text(), self.selectedComponents)
+ self.newTask.emit(self.selectedComponents)
# self.processTask.emit()
self.autosave()
@@ -381,7 +358,7 @@ class Main(QtCore.QObject):
def findComponents():
srcPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'components')
if os.path.exists(srcPath):
- for f in os.listdir(srcPath):
+ for f in sorted(os.listdir(srcPath)):
name, ext = os.path.splitext(f)
if name.startswith("__"):
continue
@@ -507,7 +484,7 @@ class Main(QtCore.QObject):
if self.window.comboBox_openPreset.itemText(i) == filename:
self.window.comboBox_openPreset.removeItem(i)
with open(filepath, 'w') as f:
- f.write(core.Core.sortedStringDict(saveValueStore))
+ f.write(core.Core.stringOrderedDict(saveValueStore))
self.window.comboBox_openPreset.addItem(filename)
self.window.comboBox_openPreset.setCurrentIndex(self.window.comboBox_openPreset.count()-1)
@@ -550,12 +527,13 @@ class Main(QtCore.QObject):
if not filepath.endswith(".avp"):
filepath += '.avp'
with open(filepath, 'w') as f:
+ print('creating %s' % filepath)
f.write('[Components]\n')
for comp in self.selectedComponents:
saveValueStore = comp.savePreset()
f.write('%s\n' % str(comp))
f.write('%s\n' % str(comp.version()))
- f.write('%s\n' % core.Core.sortedStringDict(saveValueStore))
+ f.write('%s\n' % core.Core.stringOrderedDict(saveValueStore))
if filepath != self.autosavePath:
self.settings.setValue("projectDir", os.path.dirname(filepath))
self.settings.setValue("currentProject", filepath)
@@ -604,14 +582,18 @@ class Main(QtCore.QObject):
saveValueStore = dict(eval(line))
self.selectedComponents[-1].loadPreset(saveValueStore)
i = 0
- except:
+ except (IndexError, ValueError, KeyError, NameError, SyntaxError, AttributeError, TypeError) as e:
self.clear()
- self.showMessage("Project file '%s' is corrupted." % filepath)
+ typ, value, _ = sys.exc_info()
+ msg = '%s: %s' % (typ.__name__, value)
+ self.showMessage("Project file '%s' is corrupted." % filepath, False,
+ QtGui.QMessageBox.Warning, msg)
- def showMessage(self, string, showCancel=False, icon=QtGui.QMessageBox.Information):
+ def showMessage(self, string, showCancel=False, icon=QtGui.QMessageBox.Information, detail=None):
msg = QtGui.QMessageBox()
msg.setIcon(icon)
msg.setText(string)
+ msg.setDetailedText(detail)
if showCancel:
msg.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
else:
diff --git a/mainwindow.ui b/mainwindow.ui
index f9e8f5e..b703997 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -6,8 +6,8 @@
0
0
- 1008
- 575
+ 1028
+ 592
@@ -421,73 +421,6 @@
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 85
- 0
-
-
-
- Background
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 28
-
-
-
-
- 16777215
- 28
-
-
-
-
- -
-
-
-
- 0
- 28
-
-
-
-
- 16777215
- 28
-
-
-
- ...
-
-
-
-
-
-
-
@@ -621,7 +554,6 @@
-
diff --git a/preview_thread.py b/preview_thread.py
index 63d1ac5..5116707 100644
--- a/preview_thread.py
+++ b/preview_thread.py
@@ -22,10 +22,9 @@ class Worker(QtCore.QObject):
@pyqtSlot(str, list)
- def createPreviewImage(self, backgroundImage, components):
+ def createPreviewImage(self, components):
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
dic = {
- "backgroundImage": backgroundImage,
"components": components,
}
self.queue.put(dic)
@@ -40,25 +39,14 @@ class Worker(QtCore.QObject):
except Empty:
continue
- bgImage = self.core.parseBaseImage(\
- nextPreviewInformation["backgroundImage"],
- preview=True
- )
- if bgImage == []:
- bgImage = ''
- else:
- bgImage = bgImage[0]
-
- im = self.core.drawBaseImage(bgImage)
width = int(self.core.settings.value('outputWidth'))
height = int(self.core.settings.value('outputHeight'))
- frame = Image.new("RGBA", (width, height),(0,0,0,255))
- frame.paste(im)
+ frame = Image.new("RGBA", (width, height),(0,0,0,0))
components = nextPreviewInformation["components"]
for component in reversed(components):
- newFrame = Image.alpha_composite(frame,component.previewRender(self))
- frame = Image.alpha_composite(frame,newFrame)
+ #newFrame = Image.alpha_composite(frame,)
+ frame = Image.alpha_composite(frame,component.previewRender(self))
self._image = ImageQt(frame)
self.imageCreated.emit(QtGui.QImage(self._image))
diff --git a/video_thread.py b/video_thread.py
index c97cc24..e74fffa 100644
--- a/video_thread.py
+++ b/video_thread.py
@@ -38,16 +38,17 @@ class Worker(QtCore.QObject):
while not self.stopped:
i = self.compositeQueue.get()
- if self.imBackground is not None:
- frame = self.imBackground
- else:
- frame = self.getBackgroundAtIndex(i[1])
+ frame = Image.new(
+ "RGBA",
+ (self.width, self.height),
+ (0, 0, 0, 0)
+ )
for compNo, comp in reversed(list(enumerate(self.components))):
if compNo in self.staticComponents and self.staticComponents[compNo] != None:
frame = Image.alpha_composite(frame, self.staticComponents[compNo])
else:
- frame = Image.alpha_composite(frame, comp.frameRender(compNo, i[0]))
+ frame = Image.alpha_composite(frame, comp.frameRender(compNo, i[0], i[1]))
# frame.paste(compFrame, mask=compFrame)
@@ -59,10 +60,8 @@ class Worker(QtCore.QObject):
for i in range(0, len(self.completeAudioArray), self.sampleSize):
self.compositeQueue.put([i, self.bgI])
- if not self.imBackground:
- # increment background video frame for next iteration
- if self.bgI < len(self.backgroundFrames)-1:
- self.bgI += 1
+ # increment tracked video frame for next iteration
+ self.bgI += 1
def previewDispatch(self):
while not self.stopped:
@@ -74,39 +73,18 @@ class Worker(QtCore.QObject):
self.previewQueue.task_done()
- def getBackgroundAtIndex(self, i):
- background = Image.new(
- "RGBA",
- (self.width, self.height),
- (0, 0, 0, 255)
- )
- layer = self.core.drawBaseImage(self.backgroundFrames[i])
- background.paste(layer)
- return background
-
- @pyqtSlot(str, str, str, list)
- def createVideo(self, backgroundImage, inputFile, outputFile, components):
+ @pyqtSlot(str, str, list)
+ def createVideo(self, inputFile, outputFile, components):
self.encoding.emit(True)
self.components = components
self.outputFile = outputFile
+ self.bgI = 0 # tracked video frame
self.reset()
self.width = int(self.core.settings.value('outputWidth'))
self.height = int(self.core.settings.value('outputHeight'))
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
progressBarValue = 0
self.progressBarUpdate.emit(progressBarValue)
- self.progressBarSetText.emit('Loading background imageā¦')
-
- self.backgroundImage = backgroundImage
-
- self.backgroundFrames = self.core.parseBaseImage(backgroundImage)
- if len(self.backgroundFrames) < 2:
- # the base image is not a video so we can draw it now
- self.imBackground = self.getBackgroundAtIndex(0)
- else:
- # base images will be drawn while drawing the audio bars
- self.imBackground = None
- self.bgI = 0
self.progressBarSetText.emit('Loading audio file...')
self.completeAudioArray = self.core.readAudioFile(inputFile, self)
@@ -165,7 +143,7 @@ class Worker(QtCore.QObject):
)
if properties and 'static' in properties:
- self.staticComponents[compNo] = copy(comp.frameRender(compNo, 0))
+ self.staticComponents[compNo] = copy(comp.frameRender(compNo, 0, 0))
self.progressBarUpdate.emit(100)
self.compositeQueue = Queue()
--
cgit v1.2.3
From 277e86f2795093deaa12f294c29ac2f951ae65cd Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 4 Jun 2017 20:27:43 -0400
Subject: not dumping frames anymore, but not working yet either
will finish later
---
components/video.py | 69 +++++++++++++++++++++++++++++++++--------------------
core.py | 5 ++--
main.py | 2 +-
video_thread.py | 1 -
4 files changed, 47 insertions(+), 30 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index 1a9f344..97b824c 100644
--- a/components/video.py
+++ b/components/video.py
@@ -30,11 +30,11 @@ class Component(__base__.Component):
def previewRender(self, previewWorker):
self.width = int(previewWorker.core.settings.value('outputWidth'))
self.height = int(previewWorker.core.settings.value('outputHeight'))
- frames = self.getVideoFrames(True)
- if not hasattr(self, 'staticFrame') or not self.working and frames:
+ frame1 = self.getPreviewFrame()
+ if not hasattr(self, 'staticFrame') or not self.working and frame1:
frame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
- if frames:
- im = Image.open(frames[0])
+ if frame1:
+ im = Image.open(frame1)
im = self.resize(im)
frame.paste(im)
if not self.working:
@@ -77,39 +77,56 @@ class Component(__base__.Component):
self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_video.setText(filename)
self.update()
-
- def getVideoFrames(self, preview=False):
- # recreate the temporary directory so it is empty
- # FIXME: don't dump all the frames at once, don't dump more than sound length
- # FIXME: make cancellable, report status to user, etc etc etc
+
+ def getPreviewFrame(self):
if not self.videoPath:
return
name = os.path.basename(self.videoPath).split('.', 1)[0]
- if preview:
- filename = 'preview%s.jpg' % name
- if os.path.exists(os.path.join(self.parent.core.tempDir, filename)):
- return False
- else:
- filename = name+'-frame%05d.jpg'
-
- # recreate tempDir and dump needed frame(s)
- self.parent.core.deleteTempDir()
- os.mkdir(self.parent.core.tempDir)
- if preview:
- options = '-ss 10 -vframes 1'
- else:
- options = '' #'-vframes 99999'
+ filename = 'preview%s.jpg' % name
+ if os.path.exists(os.path.join(self.parent.core.tempDir, filename)):
+ # no, we don't need a new preview frame
+ return False
+
+ # get a preview frame
subprocess.call( \
'%s -i "%s" -y %s "%s"' % ( \
self.parent.core.FFMPEG_BIN,
self.videoPath,
- options,
+ '-ss 10 -vframes 1',
os.path.join(self.parent.core.tempDir, filename)
),
shell=True
)
- print('### Got Preview Frame From %s ###' % name if preview else '### Finished Dumping Frames From %s ###' % name)
- return sorted([os.path.join(self.parent.core.tempDir, f) for f in os.listdir(self.parent.core.tempDir)])
+ print('### Got Preview Frame From %s ###' % name)
+ return os.path.join(self.parent.core.tempDir, filename)
+
+ def getVideoFrames(self):
+ # FIXME: make cancellable, report status to user, etc etc etc
+ if not self.videoPath:
+ return
+
+ command = [
+ self.parent.core.FFMPEG_BIN,
+ '-i', self.videoPath,
+ '-f', 'image2pipe',
+ '-vcodec', 'rawvideo', '-',
+ '-pix_fmt', 'rgba',
+ ]
+
+ # pipe in video frames from ffmpeg
+ in_pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=8**10)
+ # maybe bufsize=4*self.width*self.height+100 ?
+ chunk = 4*self.width*self.height
+
+ frames = []
+ while True:
+ byteFrame = in_pipe.stdout.read(chunk)
+ if len(byteFrame) == 0:
+ break
+ img = Image.frombytes('RGBA', (self.width, self.height), byteFrame, 'raw', 'RGBa')
+ frames.append(img)
+
+ return frames
def resize(self, im):
if im.size != (self.width, self.height):
diff --git a/core.py b/core.py
index ecbf12c..99403f1 100644
--- a/core.py
+++ b/core.py
@@ -15,6 +15,8 @@ class Core():
def __init__(self):
self.FFMPEG_BIN = self.findFfmpeg()
self.tempDir = os.path.join(tempfile.gettempdir(), 'audio-visualizer-python-data')
+ if not os.path.exists(self.tempDir):
+ os.makedirs(self.tempDir)
atexit.register(self.deleteTempDir)
def findFfmpeg(self):
@@ -94,8 +96,7 @@ class Core():
def deleteTempDir(self):
try:
- if os.path.exists(self.tempDir):
- rmtree(self.tempDir)
+ rmtree(self.tempDir)
except FileNotFoundError:
pass
diff --git a/main.py b/main.py
index da72b1e..637ece8 100644
--- a/main.py
+++ b/main.py
@@ -253,7 +253,7 @@ class Main(QtCore.QObject):
outputDir = self.settings.value("outputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getSaveFileName(self.window,
- "Set Output Video File", outputDir, "Video Files (*.mkv)");
+ "Set Output Video File", outputDir, "Video Files (*.mkv *.mp4)");
if not fileName == "":
self.settings.setValue("outputDir", os.path.dirname(fileName))
diff --git a/video_thread.py b/video_thread.py
index e74fffa..0542bc2 100644
--- a/video_thread.py
+++ b/video_thread.py
@@ -228,7 +228,6 @@ class Worker(QtCore.QObject):
self.error = False
self.canceled = False
self.parent.drawPreview()
- self.core.deleteTempDir()
self.stopped = True
self.encoding.emit(False)
self.videoCreated.emit()
--
cgit v1.2.3
From e58a1d0b2d499aca72af8472ec33c02580d2dffd Mon Sep 17 00:00:00 2001
From: tassaron
Date: Sun, 4 Jun 2017 22:57:19 -0400
Subject: frames are taken straight from the in_pipe
---
components/video.py | 49 ++++++++++++++++++++++++-------------------------
1 file changed, 24 insertions(+), 25 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index 97b824c..b009baa 100644
--- a/components/video.py
+++ b/components/video.py
@@ -1,6 +1,7 @@
from PIL import Image, ImageDraw
from PyQt4 import uic, QtGui, QtCore
import os, subprocess
+import numpy
from . import __base__
class Component(__base__.Component):
@@ -35,6 +36,7 @@ class Component(__base__.Component):
frame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
if frame1:
im = Image.open(frame1)
+ self.realSize = im.size
im = self.resize(im)
frame.paste(im)
if not self.working:
@@ -45,20 +47,26 @@ class Component(__base__.Component):
super().preFrameRender(**kwargs)
self.width = int(self.worker.core.settings.value('outputWidth'))
self.height = int(self.worker.core.settings.value('outputHeight'))
- self.frames = self.getVideoFrames()
self.working = True
+ self.frames = self.getVideoFrames()
def frameRender(self, moduleNo, arrayNo, frameNo):
- print(frameNo)
- try:
- if frameNo < len(self.frames)-1:
- self.staticFrame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
- im = Image.open(self.frames[frameNo])
- im = self.resize(im)
- self.staticFrame.paste(im)
- except FileNotFoundError:
- print("Video component encountered an error")
- self.frames = []
+ # don't make a new frame
+ if not self.working:
+ return self.staticFrame
+ byteFrame = self.frames.stdout.read(self.chunkSize)
+ if len(byteFrame) == 0:
+ self.working = False
+ self.frames.kill()
+ return self.staticFrame
+
+ # make a new frame
+ width, height = self.realSize
+ image = numpy.fromstring(byteFrame, dtype='uint8')
+ image = image.reshape((width, height, 4))
+ image = Image.frombytes('RGBA', (width, height), image, 'raw', 'RGBa')
+ image = self.resize(image)
+ self.staticFrame = image
return self.staticFrame
def loadPreset(self, pr):
@@ -101,7 +109,6 @@ class Component(__base__.Component):
return os.path.join(self.parent.core.tempDir, filename)
def getVideoFrames(self):
- # FIXME: make cancellable, report status to user, etc etc etc
if not self.videoPath:
return
@@ -109,24 +116,16 @@ class Component(__base__.Component):
self.parent.core.FFMPEG_BIN,
'-i', self.videoPath,
'-f', 'image2pipe',
- '-vcodec', 'rawvideo', '-',
'-pix_fmt', 'rgba',
+ '-vcodec', 'rawvideo', '-',
]
# pipe in video frames from ffmpeg
- in_pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=8**10)
- # maybe bufsize=4*self.width*self.height+100 ?
- chunk = 4*self.width*self.height
-
- frames = []
- while True:
- byteFrame = in_pipe.stdout.read(chunk)
- if len(byteFrame) == 0:
- break
- img = Image.frombytes('RGBA', (self.width, self.height), byteFrame, 'raw', 'RGBa')
- frames.append(img)
+ in_pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)
+ width, height = self.realSize
+ self.chunkSize = 4*width*height
- return frames
+ return in_pipe
def resize(self, im):
if im.size != (self.width, self.height):
--
cgit v1.2.3
From be18deece5843ac8d2c7af64704e3fb360a05a25 Mon Sep 17 00:00:00 2001
From: DH4
Date: Mon, 5 Jun 2017 04:54:58 -0500
Subject: Performance Tuning. FIXME: Video component frames are rendered out
of order. Video component creates a severe performance bottleneck.
---
components/video.py | 21 ++++++++++-----------
main.py | 4 ++--
video_thread.py | 20 ++++++++++----------
3 files changed, 22 insertions(+), 23 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index b009baa..422b952 100644
--- a/components/video.py
+++ b/components/video.py
@@ -36,8 +36,6 @@ class Component(__base__.Component):
frame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
if frame1:
im = Image.open(frame1)
- self.realSize = im.size
- im = self.resize(im)
frame.paste(im)
if not self.working:
self.staticFrame = frame
@@ -61,11 +59,9 @@ class Component(__base__.Component):
return self.staticFrame
# make a new frame
- width, height = self.realSize
- image = numpy.fromstring(byteFrame, dtype='uint8')
- image = image.reshape((width, height, 4))
- image = Image.frombytes('RGBA', (width, height), image, 'raw', 'RGBa')
- image = self.resize(image)
+ width = self.width
+ height = self.height
+ image = Image.frombytes('RGBA', (width, height), byteFrame)
self.staticFrame = image
return self.staticFrame
@@ -80,7 +76,7 @@ class Component(__base__.Component):
def pickVideo(self):
imgDir = self.settings.value("backgroundDir", os.path.expanduser("~"))
filename = QtGui.QFileDialog.getOpenFileName(self.page,
- "Choose Video", imgDir, "Video Files (*.mp4)")
+ "Choose Video", imgDir, "Video Files (*.mp4 *.mov)")
if filename:
self.settings.setValue("backgroundDir", os.path.dirname(filename))
self.page.lineEdit_video.setText(filename)
@@ -97,10 +93,11 @@ class Component(__base__.Component):
# get a preview frame
subprocess.call( \
- '%s -i "%s" -y %s "%s"' % ( \
+ '%s -i "%s" -y %s %s "%s"' % ( \
self.parent.core.FFMPEG_BIN,
self.videoPath,
'-ss 10 -vframes 1',
+ '-filter:v scale='+str(self.width)+':'+str(self.height),
os.path.join(self.parent.core.tempDir, filename)
),
shell=True
@@ -114,16 +111,18 @@ class Component(__base__.Component):
command = [
self.parent.core.FFMPEG_BIN,
+ '-thread_queue_size', '512',
'-i', self.videoPath,
'-f', 'image2pipe',
'-pix_fmt', 'rgba',
+ '-filter:v', 'scale='+str(self.width)+':'+str(self.height),
'-vcodec', 'rawvideo', '-',
]
# pipe in video frames from ffmpeg
in_pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)
- width, height = self.realSize
- self.chunkSize = 4*width*height
+ #width, height = self.realSize
+ self.chunkSize = 4*self.width*self.height
return in_pipe
diff --git a/main.py b/main.py
index 637ece8..36fc989 100644
--- a/main.py
+++ b/main.py
@@ -243,7 +243,7 @@ class Main(QtCore.QObject):
inputDir = self.settings.value("inputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getOpenFileName(self.window,
- "Open Music File", inputDir, "Music Files (*.mp3 *.wav *.ogg *.flac)");
+ "Open Music File", inputDir, "Music Files (*.mp3 *.wav *.ogg *.fla *.aac)");
if not fileName == "":
self.settings.setValue("inputDir", os.path.dirname(fileName))
@@ -253,7 +253,7 @@ class Main(QtCore.QObject):
outputDir = self.settings.value("outputDir", expanduser("~"))
fileName = QtGui.QFileDialog.getSaveFileName(self.window,
- "Set Output Video File", outputDir, "Video Files (*.mkv *.mp4)");
+ "Set Output Video File", outputDir, "Video Files (*.mp4 *.mov *.mkv *.avi *.webm *.flv)");
if not fileName == "":
self.settings.setValue("outputDir", os.path.dirname(fileName))
diff --git a/video_thread.py b/video_thread.py
index 0542bc2..ac4162c 100644
--- a/video_thread.py
+++ b/video_thread.py
@@ -37,20 +37,19 @@ class Worker(QtCore.QObject):
def renderNode(self):
while not self.stopped:
i = self.compositeQueue.get()
-
- frame = Image.new(
- "RGBA",
- (self.width, self.height),
- (0, 0, 0, 0)
- )
+ frame = None
for compNo, comp in reversed(list(enumerate(self.components))):
if compNo in self.staticComponents and self.staticComponents[compNo] != None:
- frame = Image.alpha_composite(frame, self.staticComponents[compNo])
+ if frame is None:
+ frame = self.staticComponents[compNo]
+ else:
+ frame = Image.alpha_composite(frame, self.staticComponents[compNo])
else:
- frame = Image.alpha_composite(frame, comp.frameRender(compNo, i[0], i[1]))
-
- # frame.paste(compFrame, mask=compFrame)
+ if frame is None:
+ frame = comp.frameRender(compNo, i[0], i[1])
+ else:
+ frame = Image.alpha_composite(frame, comp.frameRender(compNo, i[0], i[1]))
self.renderQueue.put([i[0], frame])
self.compositeQueue.task_done()
@@ -98,6 +97,7 @@ class Worker(QtCore.QObject):
ffmpegCommand = [
self.core.FFMPEG_BIN,
+ '-thread_queue_size', '512',
'-y', # (optional) means overwrite the output file if it already exists.
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
--
cgit v1.2.3
From 47509ae2b19e5a05211ae06114ba4675aa60a793 Mon Sep 17 00:00:00 2001
From: tassaron
Date: Tue, 6 Jun 2017 01:40:26 -0400
Subject: added framebuffer to keep frames in order
NOT WORKING: end of video detection
---
components/video.py | 144 +++++++++++++++++++++++++---------------------------
main.py | 13 +++--
2 files changed, 76 insertions(+), 81 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index 422b952..0ae5348 100644
--- a/components/video.py
+++ b/components/video.py
@@ -1,15 +1,56 @@
from PIL import Image, ImageDraw
from PyQt4 import uic, QtGui, QtCore
-import os, subprocess
-import numpy
+import os, subprocess, threading
+from queue import PriorityQueue
from . import __base__
+class Video:
+ '''Video Component Frame-Fetcher'''
+ def __init__(self, ffmpeg, videoPath, width, height, frameRate, chunkSize, parent):
+ self.parent = parent
+ self.chunkSize = chunkSize
+ self.size = (width, height)
+ self.frameNo = -1
+ self.command = [
+ ffmpeg,
+ '-thread_queue_size', '512',
+ '-r', frameRate,
+ '-i', videoPath,
+ '-f', 'image2pipe',
+ '-pix_fmt', 'rgba',
+ '-filter:v', 'scale='+str(width)+':'+str(height),
+ '-vcodec', 'rawvideo', '-',
+ ]
+
+ self.frameBuffer = PriorityQueue()
+ self.frameBuffer.maxsize = int(frameRate)
+ self.finishedFrames = {}
+
+ self.thread = threading.Thread(target=self.fillBuffer, name=self.__doc__)
+ self.thread.daemon = True
+ self.thread.start()
+
+ def frame(self, num):
+ while True:
+ if num in self.finishedFrames:
+ image = self.finishedFrames.pop(num)
+ return Image.frombytes('RGBA', self.size, image)
+ i, image = self.frameBuffer.get()
+ self.finishedFrames[i] = image
+ self.frameBuffer.task_done()
+
+ def fillBuffer(self):
+ self.pipe = subprocess.Popen(self.command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)
+ while True:
+ if self.parent.canceled:
+ break
+ self.frameNo += 1
+ image = self.pipe.stdout.read(self.chunkSize)
+ print('creating frame #%s' % str(self.frameNo))
+ self.frameBuffer.put((self.frameNo, image))
+
class Component(__base__.Component):
'''Video'''
- def __init__(self):
- super().__init__()
- self.working = False
-
def widget(self, parent):
self.parent = parent
self.settings = parent.settings
@@ -29,41 +70,22 @@ class Component(__base__.Component):
self.parent.drawPreview()
def previewRender(self, previewWorker):
- self.width = int(previewWorker.core.settings.value('outputWidth'))
- self.height = int(previewWorker.core.settings.value('outputHeight'))
- frame1 = self.getPreviewFrame()
- if not hasattr(self, 'staticFrame') or not self.working and frame1:
- frame = Image.new("RGBA", (self.width, self.height), (0, 0, 0, 0))
- if frame1:
- im = Image.open(frame1)
- frame.paste(im)
- if not self.working:
- self.staticFrame = frame
- return self.staticFrame
+ width = int(previewWorker.core.settings.value('outputWidth'))
+ height = int(previewWorker.core.settings.value('outputHeight'))
+ self.chunkSize = 4*width*height
+ return self.getPreviewFrame(width, height)
def preFrameRender(self, **kwargs):
super().preFrameRender(**kwargs)
- self.width = int(self.worker.core.settings.value('outputWidth'))
- self.height = int(self.worker.core.settings.value('outputHeight'))
- self.working = True
- self.frames = self.getVideoFrames()
+ width = int(self.worker.core.settings.value('outputWidth'))
+ height = int(self.worker.core.settings.value('outputHeight'))
+ self.chunkSize = 4*width*height
+ self.video = Video(self.parent.core.FFMPEG_BIN, self.videoPath,
+ width, height, self.settings.value("outputFrameRate"),
+ self.chunkSize, self.parent)
def frameRender(self, moduleNo, arrayNo, frameNo):
- # don't make a new frame
- if not self.working:
- return self.staticFrame
- byteFrame = self.frames.stdout.read(self.chunkSize)
- if len(byteFrame) == 0:
- self.working = False
- self.frames.kill()
- return self.staticFrame
-
- # make a new frame
- width = self.width
- height = self.height
- image = Image.frombytes('RGBA', (width, height), byteFrame)
- self.staticFrame = image
- return self.staticFrame
+ return self.video.frame(frameNo)
def loadPreset(self, pr):
self.page.lineEdit_video.setText(pr['video'])
@@ -82,51 +104,21 @@ class Component(__base__.Component):
self.page.lineEdit_video.setText(filename)
self.update()
- def getPreviewFrame(self):
- if not self.videoPath:
- return
- name = os.path.basename(self.videoPath).split('.', 1)[0]
- filename = 'preview%s.jpg' % name
- if os.path.exists(os.path.join(self.parent.core.tempDir, filename)):
- # no, we don't need a new preview frame
- return False
-
- # get a preview frame
- subprocess.call( \
- '%s -i "%s" -y %s %s "%s"' % ( \
- self.parent.core.FFMPEG_BIN,
- self.videoPath,
- '-ss 10 -vframes 1',
- '-filter:v scale='+str(self.width)+':'+str(self.height),
- os.path.join(self.parent.core.tempDir, filename)
- ),
- shell=True
- )
- print('### Got Preview Frame From %s ###' % name)
- return os.path.join(self.parent.core.tempDir, filename)
-
- def getVideoFrames(self):
- if not self.videoPath:
- return
-
+ def getPreviewFrame(self, width, height):
command = [
self.parent.core.FFMPEG_BIN,
'-thread_queue_size', '512',
'-i', self.videoPath,
'-f', 'image2pipe',
'-pix_fmt', 'rgba',
- '-filter:v', 'scale='+str(self.width)+':'+str(self.height),
+ '-filter:v', 'scale='+str(width)+':'+str(height),
'-vcodec', 'rawvideo', '-',
+ '-ss', '90',
+ '-vframes', '1',
]
-
- # pipe in video frames from ffmpeg
- in_pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)
- #width, height = self.realSize
- self.chunkSize = 4*self.width*self.height
-
- return in_pipe
-
- def resize(self, im):
- if im.size != (self.width, self.height):
- im = im.resize((self.width, self.height), Image.ANTIALIAS)
- return im
+ pipe = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=10**8)
+ byteFrame = pipe.stdout.read(self.chunkSize)
+ image = Image.frombytes('RGBA', (width, height), byteFrame)
+ pipe.stdout.close()
+ pipe.kill()
+ return image
diff --git a/main.py b/main.py
index 36fc989..c75a7f7 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,4 @@
-import sys, io, os, shutil, atexit, string, signal, filecmp
+import sys, io, os, shutil, atexit, string, signal, filecmp, time
from os.path import expanduser
from queue import Queue
from importlib import import_module
@@ -145,6 +145,7 @@ class Main(QtCore.QObject):
self.core = core.Core()
self.pages = []
self.selectedComponents = []
+ self.lastAutosave = time.time()
# create data directory, load/create settings
self.dataDir = QDesktopServices.storageLocation(QDesktopServices.DataLocation)
@@ -235,9 +236,11 @@ class Main(QtCore.QObject):
self.autosave()
def autosave(self):
- if os.path.exists(self.autosavePath):
- os.remove(self.autosavePath)
- self.createProjectFile(self.autosavePath)
+ if time.time() - self.lastAutosave >= 1.0:
+ if os.path.exists(self.autosavePath):
+ os.remove(self.autosavePath)
+ self.createProjectFile(self.autosavePath)
+ self.lastAutosave = time.time()
def openInputFileDialog(self):
inputDir = self.settings.value("inputDir", expanduser("~"))
@@ -423,7 +426,7 @@ class Main(QtCore.QObject):
def moveComponentDown(self):
row = self.window.listWidget_componentList.currentRow()
- if row < len(self.pages) + 1:
+ if row != -1 and row < len(self.pages)+1:
module = self.selectedComponents[row]
self.selectedComponents.pop(row)
self.selectedComponents.insert(row + 1,module)
--
cgit v1.2.3
From 7946e98f222784e25ea9c6dc00713f431e238609 Mon Sep 17 00:00:00 2001
From: DH4
Date: Tue, 6 Jun 2017 01:57:48 -0500
Subject: When out of frames, send last frame to buffer. Added ability to loop
video.
---
components/video.py | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
(limited to 'components/video.py')
diff --git a/components/video.py b/components/video.py
index 0ae5348..3162279 100644
--- a/components/video.py
+++ b/components/video.py
@@ -6,15 +6,21 @@ from . import __base__
class Video:
'''Video Component Frame-Fetcher'''
- def __init__(self, ffmpeg, videoPath, width, height, frameRate, chunkSize, parent):
+ def __init__(self, ffmpeg, videoPath, width, height, frameRate, chunkSize, parent, loopVideo):
self.parent = parent
self.chunkSize = chunkSize
self.size = (width, height)
self.frameNo = -1
+ self.currentFrame = 'None'
+ if loopVideo:
+ self.loopValue = '-1'
+ else:
+ self.loopValue = '0'
self.command = [
ffmpeg,
'-thread_queue_size', '512',
'-r', frameRate,
+ '-stream_loop', self.loopValue,
'-i', videoPath,
'-f', 'image2pipe',
'-pix_fmt', 'rgba',
@@ -45,9 +51,17 @@ class Video:
if self.parent.canceled:
break
self.frameNo += 1
- image = self.pipe.stdout.read(self.chunkSize)
- print('creating frame #%s' % str(self.frameNo))
- self.frameBuffer.put((self.frameNo, image))
+
+ # If we run out of frames, use the last good frame and loop.
+ if len(self.currentFrame) == 0:
+ self.frameBuffer.put((self.frameNo-1, self.lastFrame))
+ continue
+
+ self.currentFrame = self.pipe.stdout.read(self.chunkSize)
+ #print('creating frame #%s' % str(self.frameNo))
+ if len(self.currentFrame) != 0:
+ self.frameBuffer.put((self.frameNo, self.currentFrame))
+ self.lastFrame = self.currentFrame
class Component(__base__.Component):
'''Video'''
@@ -58,15 +72,18 @@ class Component(__base__.Component):
self.videoPath = ''
self.x = 0
self.y = 0
+ self.loopVideo = False
page.lineEdit_video.textChanged.connect(self.update)
page.pushButton_video.clicked.connect(self.pickVideo)
+ page.checkBox_loop.stateChanged.connect(self.update)
self.page = page
return page
def update(self):
self.videoPath = self.page.lineEdit_video.text()
+ self.loopVideo = self.page.checkBox_loop.isChecked()
self.parent.drawPreview()
def previewRender(self, previewWorker):
@@ -82,7 +99,7 @@ class Component(__base__.Component):
self.chunkSize = 4*width*height
self.video = Video(self.parent.core.FFMPEG_BIN, self.videoPath,
width, height, self.settings.value("outputFrameRate"),
- self.chunkSize, self.parent)
+ self.chunkSize, self.parent, self.loopVideo)
def frameRender(self, moduleNo, arrayNo, frameNo):
return self.video.frame(frameNo)
--
cgit v1.2.3