#pragma once #include #include #include #include #include class V4l2Worker : public QObject { Q_OBJECT public: explicit V4l2Worker(QObject *parent = nullptr); ~V4l2Worker(); public slots: void startCapture(const QString &device, int width = 1920, int height = 1080); void stopCapture(); signals: void newFrame(const QImage &frame); void captureStarted(const QString &device, int width, int height, const QString &format); void captureStopped(); void errorOccurred(const QString &message); private: void stop(); // joins m_captureThread, closes device – safe from any thread void captureLoop(); // runs inside m_captureThread bool openDevice(const QString &device, int width, int height); void closeDevice(); bool initMmap(); void freeMmap(); QImage convertToImage(const void *data, size_t size, int width, int height); QImage yuyv2Rgb(const uchar *src, int width, int height); QImage nv122Rgb(const uchar *src, int width, int height); int m_fd = -1; int m_width = 1920; int m_height = 1080; uint32_t m_pixFmt = 0; static constexpr int MMAP_BUFFERS = 4; struct MmapBuffer { void *start = nullptr; size_t length = 0; }; MmapBuffer m_buffers[MMAP_BUFFERS]; int m_nBuffers = 0; std::atomic m_running{false}; std::thread m_captureThread; // dedicated capture thread – not the Qt event thread };