Dealing with Thread-related Stuttering and Lag with QPixmap in pyqt6
-
I made a more detailed post on stackoverflow, but I hope here is also an appropriate place to address my issue since I am still hitting a wall. I am facing a troublesome issue with video playback using FFpyPlayer in my pyqt6 application. When running my application with FFpyPlayer, I observe a catch-up time-lapse buffering effect, causing the video playback to accelerate significantly and then return to normal speed intermittently. Moreover, certain videos display stuttering or lag during playback.
The application uses FFpyPlayer to handle video playback from a UDP stream within a background thread with frames, rendered with PyQt's QPixmap for frame display. The issue seems to arise during the display of frames.
from PyQt6 import QtGui, QtWidgets, QtCore from ffpyplayer.player import MediaPlayer import time from threading import Thread class VideoDisplayWidget(QtWidgets.QWidget): def __init__(self): super().__init__() self.player = None self.layout = QtWidgets.QVBoxLayout(self) self.video_frame_widget = QtWidgets.QLabel() self.layout.addWidget(self.video_frame_widget) self.frame_rate = 30 self.latest_frame = QtGui.QPixmap() self.timer = QtCore.QTimer() self.timer.setTimerType(QtCore.Qt.TimerType.PreciseTimer) self.timer.timeout.connect(self.nextFrameSlot) self.timer.start(int(1000.0/self.frame_rate)) self.play() self.setLayout(self.layout) def play(self): play_thread = Thread(target=self._play, daemon=True) play_thread.start() def _play(self): player = MediaPlayer("udp://127.0.0.1:51234") val = '' while val != 'eof': frame, val = player.get_frame() if val != 'eof' and frame is not None: img, t = frame # display img w = img.get_size()[0] h = img.get_size()[1] data = img.to_bytearray()[0] qimage = QtGui.QImage(data, w, h, QtGui.QImage.Format.Format_RGB888) self.latest_frame = QtGui.QPixmap.fromImage(qimage) time.sleep(0.001) def nextFrameSlot(self): self.video_frame_widget.setPixmap(self.latest_frame) if __name__ == "__main__": current_port = 51234 app = QtWidgets.QApplication([]) player = VideoDisplayWidget() player.show() exit(app.exec())
Would this be a threads issue? Should I go with something async such as qasync
-
Hi,
Your architecture is risky. You are accessing the same data from two different threads without synchronization which means you are pretty lucky it does not crash. Might be thanks to the current Python GIL.
You should consider using the worker object approach to push the images received further. Depending on the size of the image you receive updating a label might have some performance issues.
On a side note, since Qt 6.5, the QtMultimedia module uses FFMPEG for its backend. Did you test it ?