Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-2955

Memory leak when using multimedia(QCamera QVideoSink) modules

    XMLWordPrintable

Details

    • Bug
    • Resolution: Out of scope
    • Not Evaluated
    • None
    • 6.8.1
    • PySide
    • None
    • Windows 10
    • Windows

    Description

      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_64

      I used Windows's Task Manager to observe the memory

       

      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()
      
      
      #include <QApplication>
      #include <QCamera>
      #include <QCameraDevice>
      #include <QMediaCaptureSession>
      #include <QMediaDevices>
      #include <QPainter>
      #include <QVideoSink>
      #include <QWidget>
      
      class Widget : public QWidget {
      public:
          explicit Widget(QWidget* parent = nullptr)
              : QWidget(parent)
          {
              resize(800, 600);
              setWindowTitle("Memory Leak Test");
      
              m_videoSink = new QVideoSink(this);
              connect(m_videoSink, &QVideoSink::videoFrameChanged, this, &Widget::onFrameChanged);
      
              m_camera = new QCamera(QMediaDevices::defaultVideoInput());
              m_session = new QMediaCaptureSession(this);
              m_session->setCamera(m_camera);
              m_session->setVideoOutput(m_videoSink);
              m_camera->start();
          }
      
      protected:
          void paintEvent(QPaintEvent* event) override {
              QPainter painter{ this };
      
              if (m_currentFrame.isValid()) {
                  painter.drawImage(rect(), m_currentFrame.toImage().scaled(rect().size(), Qt::KeepAspectRatio));
              }
          }
      
      private slots:
          void onFrameChanged(const QVideoFrame& frame) {
              m_currentFrame = frame;
              update();
          }
      
      private:
          QVideoSink* m_videoSink;
          QCamera* m_camera;
          QMediaCaptureSession* m_session;
          QVideoFrame m_currentFrame;
      };
      
      int main(int argc, char *argv[]) {
          QApplication app(argc, argv);
          Widget w;
          w.show();
          return QApplication::exec();
      }
      
      

      Attachments

        1. start.png
          152 kB
          rainzee wang
        2. pyside2955.py
          3 kB
          Friedemann Kleint
        3. pyside2955_objgraph.py
          3 kB
          Friedemann Kleint
        4. pyside2955_cpp.zip
          2 kB
          Friedemann Kleint
        5. memory_leak_test.py
          3 kB
          rainzee wang
        6. memory_leak_test.cpp
          1 kB
          rainzee wang
        7. after.png
          148 kB
          rainzee wang
        8. 3.png
          147 kB
          rainzee wang
        9. 2.png
          145 kB
          rainzee wang
        10. 1.png
          154 kB
          rainzee wang

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              crmaurei Cristian Maureira-Fredes
              rainzee rainzee wang
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes