Memory leak when using multimedia(QCamera QVideoSink) modules
-
In the following implementation (see the attached code), I use the same Qt version (6.7.3 and 6.8.1) to implement equivalent code using PySide and Qt respectively. The initial running memory is about 20MB, and after switching to the viewfinder page, it rises to about 200MB respectively. When switching back to the empty page, the Qt implementation will quickly fall back to 20MB, while the PySide version does not fall back at all. I tried many methods, including but not limited to calling the delatelater method and using Python's built-in del method, but none of them worked.
Operating system Windows 10 21H2
Python version 3.11, 3.12
Qt version Qt 6.7.3 msvc2019_64I used Windows's Task Manager to observe the memory
I tried using many Python memory analyzers, but I couldn't find what was not being collected, causing the memory to raise up about ten times, but I think the problem seems to be related with QImage. I read the Qt source code and it seems that there are many buffers in the conversion process from QVideoFrame to QImage. Is it possible that this is the point? Sorry, I am not an expert in memory, so the analysis I provide is very limited : (. On the Python side, I've tried everything I can, including consulting from LLM, and I collected the memory size of all gc objects, but I couldn't find where the huge 200mb came from.
from PySide6.QtCore import Qt from PySide6.QtGui import QHideEvent, QPainter, QPaintEvent, QShowEvent from PySide6.QtMultimedia import QCamera, QMediaCaptureSession, QMediaDevices, QVideoFrame, QVideoSink from PySide6.QtWidgets import QApplication, QPushButton, QStackedWidget, QVBoxLayout, QWidget class NormalPage(QWidget): def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) class ViewfinderPage(QWidget): def __init__(self, parent: QWidget | None = None) -> None: super().__init__(parent) self.videoSink = QVideoSink() self.videoSink.videoFrameChanged.connect(self.onFrameChanged) self.camera = QCamera(QMediaDevices.defaultVideoInput()) self.session = QMediaCaptureSession() self.session.setCamera(self.camera) self.session.setVideoOutput(self.videoSink) self.currentFrame: QVideoFrame | None = None def onFrameChanged(self, frame: QVideoFrame) -> None: self.currentFrame = frame self.update() def showEvent(self, event: QShowEvent) -> None: super().showEvent(event) self.camera.start() def hideEvent(self, event: QHideEvent) -> None: super().hideEvent(event) self.camera.stop() def paintEvent(self, event: QPaintEvent) -> None: super().paintEvent(event) painter = QPainter(self) if self.currentFrame: painter.fillRect( self.rect(), self.currentFrame.toImage().scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatioByExpanding) ) painter.end() class Win(QWidget): def __init__(self) -> None: super().__init__() self.resize(800, 600) self.setWindowTitle("Memory Leak Test") self.mainLayout = QVBoxLayout(self) self.toggleButton = QPushButton("Toggle Page") self.toggleButton.clicked.connect(self.onToggleButtonClicked) self.container = QStackedWidget() self.normalPage = NormalPage() self.viewfinderPage = ViewfinderPage() self.container.addWidget(self.normalPage) self.container.addWidget(self.viewfinderPage) self.setLayout(QVBoxLayout()) self.mainLayout.addWidget(self.container) self.mainLayout.addWidget(self.toggleButton) def onToggleButtonClicked(self) -> None: self.container.setCurrentIndex(1 - self.container.currentIndex()) if __name__ == "__main__": app = QApplication([]) win = Win() win.show() app.exec()
-
I also submited an issue on Qt issue tracker https://bugreports.qt.io/browse/PYSIDE-2955