#pragma once #include #include #include #include #include #include #include #include #include #include #include "v4l2worker.h" // VideoWidget shows the live V4L2 preview and can additionally record it // to an MP4 file. Recording works by piping raw BGRA frames into an // external `ffmpeg` process (via QProcess/stdin), which encodes them to // H.264/MP4 - this avoids reimplementing video encoding and just needs // ffmpeg to be installed and on PATH. class VideoWidget : public QWidget { Q_OBJECT public: explicit VideoWidget(QWidget *parent = nullptr); ~VideoWidget(); public: void shutdown(); // safe to call multiple times, called from MainWindow::closeEvent public slots: void onNewFrame(const QImage &frame); void clearFrame(); // blanks the preview (used on ANSI clear-screen) private slots: void onStartStop(); void onFreeze(bool frozen); void onScreenshot(); void onRecordToggled(bool checked); void refreshDeviceList(); protected: void paintEvent(QPaintEvent *event) override; void resizeEvent(QResizeEvent *event) override; private: void setupUi(); void startCapture(); void stopCapture(); void updateScaled(); void startRecording(); void stopRecording(); void writeRecordingFrame(const QImage &frame); QThread *m_thread = nullptr; V4l2Worker *m_worker = nullptr; QImage m_frame; QImage m_scaled; bool m_frozen = false; bool m_running = false; bool m_shutdownDone = false; QComboBox *m_deviceCombo = nullptr; QPushButton *m_startBtn = nullptr; QPushButton *m_freezeBtn = nullptr; QPushButton *m_screenshotBtn = nullptr; QPushButton *m_recordBtn = nullptr; QLabel *m_infoLabel = nullptr; // Recording state. m_recordProcess is the running ffmpeg instance (or // nullptr when not recording); m_recordSize is the frame size the // recording was started with, so a mid-stream resolution change (which // ffmpeg's raw pipe can't handle) can be detected and the recording // stopped cleanly instead of corrupting the output file. QProcess *m_recordProcess = nullptr; bool m_recording = false; QSize m_recordSize; };