Files
uartscope/include/videowidget.h

101 lines
3.5 KiB
C++

#pragma once
#include <QWidget>
#include <QImage>
#include <QSize>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QThread>
#include <QProcess>
#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
// Folder that saveAutoScreenshot() writes into. Empty = not configured
// (saveAutoScreenshot() will report an error instead of silently
// dropping the request).
void setAutoScreenshotDir(const QString &dir) { m_autoScreenshotDir = dir; }
public slots:
void onNewFrame(const QImage &frame);
void clearFrame(); // blanks the preview (used on ANSI clear-screen)
// Saves whichever frame is currently in m_frame under `filename` inside
// m_autoScreenshotDir. Triggered by a [SCREENSHOT] control tag from the
// firmware - the point is to grab the frame that's already in flight
// the instant the tag arrives (no dialog, no user reaction time), e.g.
// to reliably catch a video frame showing a memory dump.
void saveAutoScreenshot(const QString &filename);
signals:
// Emitted after every saveAutoScreenshot() attempt so the caller (the
// main window) can surface a transient status message. Never blocks/
// pops a dialog itself, since this can fire in rapid succession from
// firmware-driven triggers.
void autoScreenshotResult(bool success, const QString &message);
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;
// Folder for saveAutoScreenshot() ([SCREENSHOT] tag). Set via
// setAutoScreenshotDir(), persisted/restored by MainWindow.
QString m_autoScreenshotDir;
};