My GUI is able to play videos automatically when selected in the QListWidget
. However, instead of normal speed, the videos play very fast. I use 720p Mp4 videos as examples and are placed in a certain folder. I tried using cv2.CAP_PROP_FPS
and cv2.CAP_PROP_BUFFERSIZE
, but they are both not working. I am using pyqtSignal
in the QThread
and the convert_cv_qt
function which I saw in other guides. How do I play the videos in normal speed / frame rate?
import os
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtMultimedia import QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
class VideoThread(QThread):
change_pixmap_signal = pyqtSignal(np.ndarray)
def __init__(self, parent=None):
super(VideoThread, self).__init__(parent)
self.get_file_dir = self.parent().file_dir #Get the file_dir from UI_MainWindow
def run(self):
cap = cv2.VideoCapture(self.get_file_dir)
while True:
ret, cv_img = cap.read()
if ret:
self.change_pixmap_signal.emit(cv_img)
cap.release()
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Ui_MainWindow, self).__init__(parent)
self.setupUI()
def setupUI(self):
MainWindow.resize(1720, 863)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
self.tabWidget.setTabShape(QtWidgets.QTabWidget.Triangular)
self.tabWidget.setElideMode(QtCore.Qt.ElideNone)
self.tabWidget.setTabsClosable(False)
self.tabWidget.setMovable(True)
self.tab = QtWidgets.QWidget()
self.videoimage = QtWidgets.QLabel(self.tab)
self.videoimage.setGeometry(QtCore.QRect(0, 10, 1280, 720))
self.listWidget = QtWidgets.QListWidget(self.tab)
self.listWidget.setGeometry(QtCore.QRect(1290, 60, 391, 241))
self.listWidget.setObjectName("listWidget")
self.listWidget.itemClicked.connect(self.itemclick)
self.tabWidget.addTab(self.tab, "")
self.tabWidget.setTabText(0, "Testing")
self.verticalLayout_2.addWidget(self.tabWidget)
MainWindow.setCentralWidget(self.centralwidget)
self.listvideos()
self.tabWidget.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
@pyqtSlot(np.ndarray)
def listvideos(self):
file_extension = ['avi','mp4','mov']
arr = os.listdir('D:\\Files\\VSCODE\\APPS\\HDTS\\videos')
for i in arr:
for fe in file_extension:
if i[-3:] == fe:
item = QtWidgets.QListWidgetItem(str(i))
self.listWidget.addItem(item)
def itemclick(self):
try:
self.thread.terminate()
except:
print("Nothing to terminate!")
item = self.listWidget.currentItem()
self.file_dir = 'D:\\Files\\VSCODE\\APPS\\HDTS\\videos\\' + item.text()
print(self.file_dir)
self.thread = VideoThread(self)
self.thread.change_pixmap_signal.connect(self.update_image)
self.thread.start()
def update_image(self, cv_img):
qt_img = self.convert_cv_qt(cv_img)
self.videoimage.setPixmap(qt_img)
def convert_cv_qt(self, cv_img):
rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_image.shape
bytes_per_line = ch * w
convert_to_Qt_format = QtGui.QImage(rgb_image.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
p = convert_to_Qt_format.scaled(1280, 720, Qt.KeepAspectRatio)
return QtGui.QPixmap.fromImage(p)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
MainWindow.show()
sys.exit(app.exec_())
When you use VideoCapture with a file if you call "cap.read()" you will obtain the next frame on the video, regardless of its actual framerate. Hence, you should use a "msleep" every time you capture a frame:
def run(self):
cap = cv2.VideoCapture(self.get_file_dir)
while True:
ret, cv_img = cap.read()
if ret:
self.change_pixmap_signal.emit(cv_img)
self.msleep(1000//30) # You can change 30 with 60 if you need 60 fps.
cap.release()