added ability to use an mp4 as the background
might not be the best way to do this (dumping all the video frames to a temp location), but it works for clips of a few minutes or less
This commit is contained in:
parent
01de150f74
commit
ff818836dd
62
core.py
62
core.py
|
@ -6,6 +6,9 @@ import subprocess as sp
|
|||
import numpy
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from PIL.ImageQt import ImageQt
|
||||
import tempfile
|
||||
from shutil import rmtree
|
||||
import atexit
|
||||
|
||||
class Core():
|
||||
|
||||
|
@ -14,6 +17,8 @@ class Core():
|
|||
self._image = None
|
||||
|
||||
self.FFMPEG_BIN = self.findFfmpeg()
|
||||
self.tempDir = None
|
||||
atexit.register(self.deleteTempDir)
|
||||
|
||||
def findFfmpeg(self):
|
||||
if sys.platform == "win32":
|
||||
|
@ -26,23 +31,34 @@ class Core():
|
|||
except:
|
||||
return "avconv"
|
||||
|
||||
def drawBaseImage(self, backgroundImage, titleText, titleFont, fontSize, alignment, xOffset, yOffset):
|
||||
|
||||
if self._image == None or not self.lastBackgroundImage == backgroundImage:
|
||||
self.lastBackgroundImage = backgroundImage
|
||||
|
||||
def parseBaseImage(self, backgroundImage, preview=False):
|
||||
''' determines if the base image is a single frame or list of frames '''
|
||||
if backgroundImage == "":
|
||||
im = Image.new("RGB", (1280, 720), "black")
|
||||
return []
|
||||
else:
|
||||
im = Image.open(backgroundImage)
|
||||
_, bgExt = os.path.splitext(backgroundImage)
|
||||
if not bgExt == '.mp4':
|
||||
return [backgroundImage]
|
||||
else:
|
||||
return self.getVideoFrames(backgroundImage, preview)
|
||||
|
||||
def drawBaseImage(self, backgroundFile, titleText, titleFont, fontSize, alignment, xOffset, yOffset):
|
||||
if backgroundFile == '':
|
||||
im = Image.new("RGB", (1280, 720), "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 == (1280, 720):
|
||||
im = im.resize((1280, 720), Image.ANTIALIAS)
|
||||
|
||||
self._image = ImageQt(im)
|
||||
|
||||
|
||||
self._image1 = QtGui.QImage(self._image)
|
||||
|
||||
painter = QPainter(self._image1)
|
||||
font = titleFont
|
||||
font.setPointSizeF(fontSize)
|
||||
|
@ -83,7 +99,6 @@ class Core():
|
|||
imBottom = imTop.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
im = Image.new("RGB", (1280, 720), "black")
|
||||
|
||||
im.paste(image, (0, 0))
|
||||
im.paste(imTop, (0, 0), mask=imTop)
|
||||
im.paste(imBottom, (0, 360), mask=imBottom)
|
||||
|
@ -99,7 +114,7 @@ class Core():
|
|||
'-ac', '1', # mono (set to '2' for stereo)
|
||||
'-']
|
||||
in_pipe = sp.Popen(command, stdout=sp.PIPE, stderr=sp.DEVNULL, bufsize=10**8)
|
||||
|
||||
|
||||
completeAudioArray = numpy.empty(0, dtype="int16")
|
||||
|
||||
while True:
|
||||
|
@ -150,3 +165,30 @@ class Core():
|
|||
x = frequencies[0:int(paddedSampleSize/2) - 1]
|
||||
|
||||
return lastSpectrum
|
||||
|
||||
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.jpg'
|
||||
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)])
|
||||
|
|
20
main.py
20
main.py
|
@ -35,13 +35,13 @@ class Main(QtCore.QObject):
|
|||
|
||||
self.previewWorker.moveToThread(self.previewThread)
|
||||
self.previewWorker.imageCreated.connect(self.showPreviewImage)
|
||||
|
||||
|
||||
self.previewThread.start()
|
||||
|
||||
self.timer = QtCore.QTimer(self)
|
||||
self.timer.timeout.connect(self.processTask.emit)
|
||||
self.timer.start(500)
|
||||
|
||||
|
||||
window.pushButton_selectInput.clicked.connect(self.openInputFileDialog)
|
||||
window.pushButton_selectOutput.clicked.connect(self.openOutputFileDialog)
|
||||
window.pushButton_createVideo.clicked.connect(self.createAudioVisualisation)
|
||||
|
@ -69,7 +69,7 @@ class Main(QtCore.QObject):
|
|||
window.textYSpinBox.setValue(375)
|
||||
|
||||
titleFont = self.settings.value("titleFont")
|
||||
if not titleFont == None:
|
||||
if not titleFont == None:
|
||||
window.fontComboBox.setCurrentFont(QFont(titleFont))
|
||||
|
||||
alignment = self.settings.value("alignment")
|
||||
|
@ -113,7 +113,7 @@ class Main(QtCore.QObject):
|
|||
fileName = QtGui.QFileDialog.getOpenFileName(self.window,
|
||||
"Open Music File", inputDir, "Music Files (*.mp3 *.wav *.ogg *.flac)");
|
||||
|
||||
if not fileName == "":
|
||||
if not fileName == "":
|
||||
self.settings.setValue("inputDir", os.path.dirname(fileName))
|
||||
self.window.label_input.setText(fileName)
|
||||
|
||||
|
@ -123,7 +123,7 @@ class Main(QtCore.QObject):
|
|||
fileName = QtGui.QFileDialog.getSaveFileName(self.window,
|
||||
"Set Output Video File", outputDir, "Video Files (*.mkv)");
|
||||
|
||||
if not fileName == "":
|
||||
if not fileName == "":
|
||||
self.settings.setValue("outputDir", os.path.dirname(fileName))
|
||||
self.window.label_output.setText(fileName)
|
||||
|
||||
|
@ -131,9 +131,9 @@ class Main(QtCore.QObject):
|
|||
backgroundDir = self.settings.value("backgroundDir", expanduser("~"))
|
||||
|
||||
fileName = QtGui.QFileDialog.getOpenFileName(self.window,
|
||||
"Open Background Image", backgroundDir, "Image Files (*.jpg *.png)");
|
||||
"Open Background Image", backgroundDir, "Image Files (*.jpg *.png);; Videos (*.mp4)");
|
||||
|
||||
if not fileName == "":
|
||||
if not fileName == "":
|
||||
self.settings.setValue("backgroundDir", os.path.dirname(fileName))
|
||||
self.window.label_background.setText(fileName)
|
||||
self.drawPreview()
|
||||
|
@ -147,7 +147,7 @@ class Main(QtCore.QObject):
|
|||
self.videoWorker.moveToThread(self.videoThread)
|
||||
self.videoWorker.videoCreated.connect(self.videoCreated)
|
||||
self.videoWorker.progressBarUpdate.connect(self.progressBarUpdated)
|
||||
|
||||
|
||||
self.videoThread.start()
|
||||
self.videoTask.emit(self.window.label_background.text(),
|
||||
self.window.lineEdit_title.text(),
|
||||
|
@ -158,7 +158,7 @@ class Main(QtCore.QObject):
|
|||
self.window.textYSpinBox.value(),
|
||||
self.window.label_input.text(),
|
||||
self.window.label_output.text())
|
||||
|
||||
|
||||
|
||||
def progressBarUpdated(self, value):
|
||||
self.window.progressBar_create.setValue(value)
|
||||
|
@ -186,7 +186,7 @@ class Main(QtCore.QObject):
|
|||
if __name__ == "__main__":
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
window = uic.loadUi("main.ui")
|
||||
|
||||
|
||||
main = Main(window)
|
||||
|
||||
atexit.register(main.cleanUp)
|
||||
|
|
|
@ -43,8 +43,17 @@ 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(
|
||||
nextPreviewInformation["backgroundImage"],
|
||||
bgImage,
|
||||
nextPreviewInformation["titleText"],
|
||||
nextPreviewInformation["titleFont"],
|
||||
nextPreviewInformation["fontSize"],
|
||||
|
|
|
@ -21,18 +21,26 @@ class Worker(QtCore.QObject):
|
|||
@pyqtSlot(str, str, QtGui.QFont, int, int, int, int, str, str)
|
||||
def createVideo(self, backgroundImage, titleText, titleFont, fontSize, alignment, xOffset, yOffset, inputFile, outputFile):
|
||||
# print('worker thread id: {}'.format(QtCore.QThread.currentThreadId()))
|
||||
|
||||
imBackground = self.core.drawBaseImage(
|
||||
backgroundImage,
|
||||
titleText,
|
||||
titleFont,
|
||||
fontSize,
|
||||
alignment,
|
||||
xOffset,
|
||||
yOffset)
|
||||
def getBackgroundAtIndex(i):
|
||||
return self.core.drawBaseImage(
|
||||
backgroundFrames[i],
|
||||
titleText,
|
||||
titleFont,
|
||||
fontSize,
|
||||
alignment,
|
||||
xOffset,
|
||||
yOffset)
|
||||
|
||||
backgroundFrames = self.core.parseBaseImage(backgroundImage)
|
||||
if len(backgroundFrames) < 2:
|
||||
# the base image is not a video so we can draw it now
|
||||
imBackground = getBackgroundAtIndex(0)
|
||||
else:
|
||||
# base images will be drawn while drawing the audio bars
|
||||
imBackground = None
|
||||
|
||||
self.progressBarUpdate.emit(0)
|
||||
|
||||
|
||||
completeAudioArray = self.core.readAudioFile(inputFile)
|
||||
|
||||
# test if user has libfdk_aac
|
||||
|
@ -64,7 +72,7 @@ class Worker(QtCore.QObject):
|
|||
ffmpegCommand.append('-2')
|
||||
|
||||
ffmpegCommand.append(outputFile)
|
||||
|
||||
|
||||
out_pipe = sp.Popen(ffmpegCommand,
|
||||
stdin=sp.PIPE,stdout=sys.stdout, stderr=sys.stdout)
|
||||
|
||||
|
@ -75,7 +83,7 @@ class Worker(QtCore.QObject):
|
|||
sampleSize = 1470
|
||||
|
||||
numpy.seterr(divide='ignore')
|
||||
|
||||
bgI = 0
|
||||
for i in range(0, len(completeAudioArray), sampleSize):
|
||||
# create video for output
|
||||
lastSpectrum = self.core.transformData(
|
||||
|
@ -85,7 +93,12 @@ class Worker(QtCore.QObject):
|
|||
smoothConstantDown,
|
||||
smoothConstantUp,
|
||||
lastSpectrum)
|
||||
im = self.core.drawBars(lastSpectrum, imBackground)
|
||||
if imBackground != None:
|
||||
im = self.core.drawBars(lastSpectrum, imBackground)
|
||||
else:
|
||||
im = self.core.drawBars(lastSpectrum, getBackgroundAtIndex(bgI))
|
||||
if bgI < len(backgroundFrames)-1:
|
||||
bgI += 1
|
||||
|
||||
# write to out_pipe
|
||||
try:
|
||||
|
|
Reference in New Issue