clang optimizations and cleanup

This commit is contained in:
Maik Jurischka
2025-12-19 07:27:21 +01:00
parent 5fed3070de
commit bef80372b6
5 changed files with 78 additions and 116 deletions

View File

@@ -14,17 +14,15 @@ CameraControlWidget::CameraControlWidget(SocketClient* socketClient, QWidget *pa
void CameraControlWidget::setupUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
auto* mainLayout = new QVBoxLayout(this);
// Create scroll area for all controls
QScrollArea* scrollArea = new QScrollArea(this);
auto* scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QWidget* scrollWidget = new QWidget();
QVBoxLayout* scrollLayout = new QVBoxLayout(scrollWidget);
auto* scrollWidget = new QWidget();
auto* scrollLayout = new QVBoxLayout(scrollWidget);
// Add all control groups
scrollLayout->addWidget(createFormatGroup());
scrollLayout->addWidget(createExposureGroup());
scrollLayout->addWidget(createWhiteBalanceGroup());
@@ -36,7 +34,6 @@ void CameraControlWidget::setupUI()
mainLayout->addWidget(scrollArea);
// Status label at bottom
m_statusLabel = new QLabel("Status: Ready", this);
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
mainLayout->addWidget(m_statusLabel);
@@ -46,8 +43,8 @@ void CameraControlWidget::setupUI()
QGroupBox* CameraControlWidget::createFormatGroup()
{
QGroupBox* groupBox = new QGroupBox("Video Format", this);
QVBoxLayout* layout = new QVBoxLayout();
auto* groupBox = new QGroupBox("Video Format", this);
auto* layout = new QVBoxLayout();
m_formatCombo = new QComboBox(this);
m_formatCombo->addItem("1280x720@30fps UYVY (Supported)", "1280,720,30,UYVY");
@@ -69,10 +66,10 @@ QGroupBox* CameraControlWidget::createFormatGroup()
QGroupBox* CameraControlWidget::createExposureGroup()
{
QGroupBox* groupBox = new QGroupBox("Exposure", this);
QVBoxLayout* layout = new QVBoxLayout();
auto* groupBox = new QGroupBox("Exposure", this);
auto* layout = new QVBoxLayout();
QButtonGroup* exposureGroup = new QButtonGroup(this);
auto* exposureGroup = new QButtonGroup(this);
m_exposureAuto = new QRadioButton("Auto", this);
m_exposureManual = new QRadioButton("Manual", this);
m_exposureAuto->setChecked(true);
@@ -82,7 +79,7 @@ QGroupBox* CameraControlWidget::createExposureGroup()
connect(m_exposureAuto, &QRadioButton::toggled, this, &CameraControlWidget::onExposureModeChanged);
QHBoxLayout* modeLayout = new QHBoxLayout();
auto* modeLayout = new QHBoxLayout();
modeLayout->addWidget(m_exposureAuto);
modeLayout->addWidget(m_exposureManual);
@@ -94,7 +91,7 @@ QGroupBox* CameraControlWidget::createExposureGroup()
m_setExposureBtn = new QPushButton("Set Exposure", this);
connect(m_setExposureBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetExposure);
QFormLayout* formLayout = new QFormLayout();
auto* formLayout = new QFormLayout();
formLayout->addRow("Mode:", modeLayout);
formLayout->addRow("Value:", m_exposureValue);
@@ -107,10 +104,10 @@ QGroupBox* CameraControlWidget::createExposureGroup()
QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
{
QGroupBox* groupBox = new QGroupBox("White Balance", this);
QVBoxLayout* layout = new QVBoxLayout();
auto* groupBox = new QGroupBox("White Balance", this);
auto* layout = new QVBoxLayout();
QButtonGroup* wbGroup = new QButtonGroup(this);
auto* wbGroup = new QButtonGroup(this);
m_whiteBalanceAuto = new QRadioButton("Auto", this);
m_whiteBalanceManual = new QRadioButton("Manual", this);
m_whiteBalanceAuto->setChecked(true);
@@ -120,7 +117,7 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
connect(m_whiteBalanceAuto, &QRadioButton::toggled, this, &CameraControlWidget::onWhiteBalanceModeChanged);
QHBoxLayout* modeLayout = new QHBoxLayout();
auto* modeLayout = new QHBoxLayout();
modeLayout->addWidget(m_whiteBalanceAuto);
modeLayout->addWidget(m_whiteBalanceManual);
@@ -133,7 +130,7 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
m_setWhiteBalanceBtn = new QPushButton("Set White Balance", this);
connect(m_setWhiteBalanceBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetWhiteBalance);
QFormLayout* formLayout = new QFormLayout();
auto* formLayout = new QFormLayout();
formLayout->addRow("Mode:", modeLayout);
formLayout->addRow("Temperature:", m_whiteBalanceTemp);
@@ -146,8 +143,8 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
{
QGroupBox* groupBox = new QGroupBox("Image Adjustments", this);
QVBoxLayout* layout = new QVBoxLayout();
auto* groupBox = new QGroupBox("Image Adjustments", this);
auto* layout = new QVBoxLayout();
layout->addWidget(createSliderControl("Brightness (0-255):", 0, 255, 128,
&m_brightnessSlider, &m_brightnessSpinBox));
@@ -180,13 +177,13 @@ QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
QWidget* CameraControlWidget::createSliderControl(const QString& label, int min, int max, int defaultValue,
QSlider** slider, QSpinBox** spinBox)
{
QWidget* widget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(widget);
auto* widget = new QWidget(this);
auto* layout = new QVBoxLayout(widget);
layout->setContentsMargins(0, 5, 0, 5);
QLabel* titleLabel = new QLabel(label, this);
auto* titleLabel = new QLabel(label, this);
QHBoxLayout* controlLayout = new QHBoxLayout();
auto* controlLayout = new QHBoxLayout();
*slider = new QSlider(Qt::Horizontal, this);
(*slider)->setRange(min, max);
@@ -223,9 +220,8 @@ void CameraControlWidget::onGetFormats()
int fps = fmt["framerate"].toInt();
QString format = fmt["format"].toString();
QString displayText = QString("%1x%2@%3fps %4")
.arg(width).arg(height).arg(fps).arg(format);
QString data = QString("%1,%2,%3,%4").arg(width).arg(height).arg(fps).arg(format);
QString displayText = QString("%1x%2@%3fps %4").arg(width, height, fps).arg(format);
QString data = QString("%1,%2,%3,%4").arg(width, height, fps).arg(format);
m_formatCombo->addItem(displayText, data);
}

View File

@@ -16,12 +16,11 @@ GStreamerPipelineWidget::GStreamerPipelineWidget(SocketClient* socketClient, QWi
void GStreamerPipelineWidget::setupUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
auto* mainLayout = new QVBoxLayout(this);
QGroupBox* groupBox = new QGroupBox("GStreamer Pipeline", this);
QVBoxLayout* groupLayout = new QVBoxLayout(groupBox);
auto* groupBox = new QGroupBox("GStreamer Pipeline", this);
auto* groupLayout = new QVBoxLayout(groupBox);
// Info label with instructions
m_infoLabel = new QLabel(
"<b>Quick Start:</b> Click 'Quick Start' to automatically configure and start streaming.<br>"
"<b>Manual:</b> 1. Set video format → 2. Set pipeline → 3. Start stream", this);
@@ -29,27 +28,23 @@ void GStreamerPipelineWidget::setupUI()
m_infoLabel->setWordWrap(true);
groupLayout->addWidget(m_infoLabel);
// Quick Start button (prominent)
m_quickStartBtn = new QPushButton("⚡ Quick Start (Auto Configure & Stream)", this);
m_quickStartBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px; }");
connect(m_quickStartBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onQuickStart);
groupLayout->addWidget(m_quickStartBtn);
// Separator
QFrame* line = new QFrame(this);
auto* line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
groupLayout->addWidget(line);
// Format selection
QLabel* formatLabel = new QLabel("Video Format:", this);
auto* formatLabel = new QLabel("Video Format:", this);
m_formatCombo = new QComboBox(this);
m_formatCombo->addItem("1280x720@30fps UYVY (Default/Supported)", "1280,720,30,UYVY");
groupLayout->addWidget(formatLabel);
groupLayout->addWidget(m_formatCombo);
// Pipeline presets
QLabel* presetsLabel = new QLabel("Pipeline Presets:", this);
auto* presetsLabel = new QLabel("Pipeline Presets:", this);
m_pipelinePresets = new QComboBox(this);
m_pipelinePresets->addItem("MJPEG UDP Stream (Best for raw formats)", "videoconvert ! jpegenc ! rtpjpegpay ! udpsink host=127.0.0.1 port=5000");
m_pipelinePresets->addItem("UDP H.264 Stream (Requires gst-libav)", "videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=127.0.0.1 port=5000");
@@ -66,8 +61,7 @@ void GStreamerPipelineWidget::setupUI()
groupLayout->addWidget(presetsLabel);
groupLayout->addWidget(m_pipelinePresets);
// Pipeline editor
QLabel* pipelineLabel = new QLabel("Pipeline:", this);
auto* pipelineLabel = new QLabel("Pipeline:", this);
m_pipelineEdit = new QTextEdit(this);
m_pipelineEdit->setMaximumHeight(80);
m_pipelineEdit->setPlaceholderText("Enter GStreamer pipeline here...\nExample: videoconvert ! autovideosink");
@@ -75,13 +69,11 @@ void GStreamerPipelineWidget::setupUI()
groupLayout->addWidget(pipelineLabel);
groupLayout->addWidget(m_pipelineEdit);
// Set pipeline button
m_setPipelineBtn = new QPushButton("Set Pipeline", this);
connect(m_setPipelineBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onSetPipeline);
groupLayout->addWidget(m_setPipelineBtn);
// Stream control buttons
QHBoxLayout* buttonLayout = new QHBoxLayout();
auto* buttonLayout = new QHBoxLayout();
m_startStreamBtn = new QPushButton("Start Stream", this);
m_stopStreamBtn = new QPushButton("Stop Stream", this);
m_getStatusBtn = new QPushButton("Get Status", this);
@@ -95,7 +87,6 @@ void GStreamerPipelineWidget::setupUI()
buttonLayout->addWidget(m_getStatusBtn);
groupLayout->addLayout(buttonLayout);
// Status label
m_statusLabel = new QLabel("Status: Unknown", this);
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
groupLayout->addWidget(m_statusLabel);
@@ -148,7 +139,6 @@ void GStreamerPipelineWidget::onStartStream()
m_socketClient->sendCommand("set_format", formatParams,
[this](const QJsonObject& response) {
// Now start stream
m_socketClient->sendCommand("start_stream", QJsonObject(),
[this](const QJsonObject& response) {
updateStatus("Streaming started", true);
@@ -213,7 +203,6 @@ void GStreamerPipelineWidget::updateStatus(const QString& status, bool streaming
void GStreamerPipelineWidget::onQuickStart()
{
// Disable button during process
m_quickStartBtn->setEnabled(false);
m_quickStartBtn->setText("Configuring...");
@@ -306,10 +295,8 @@ void GStreamerPipelineWidget::onFormatsReceived(const QJsonObject& response)
return;
}
// Clear existing formats
m_formatCombo->clear();
// Add all available formats
for (const QJsonValue& val : formats) {
QJsonObject fmt = val.toObject();
int width = fmt["width"].toInt();
@@ -317,9 +304,8 @@ void GStreamerPipelineWidget::onFormatsReceived(const QJsonObject& response)
int fps = fmt["framerate"].toInt();
QString format = fmt["format"].toString();
QString displayText = QString("%1x%2@%3fps %4")
.arg(width).arg(height).arg(fps).arg(format);
QString data = QString("%1,%2,%3,%4").arg(width).arg(height).arg(fps).arg(format);
QString displayText = QString("%1x%2@%3fps %4").arg(width, height, fps).arg(format);
QString data = QString("%1,%2,%3,%4").arg(width, height, fps).arg(format);
m_formatCombo->addItem(displayText, data);
}

View File

@@ -24,26 +24,21 @@ MainWindow::~MainWindow()
void MainWindow::setupUI()
{
// Create socket client
m_socketClient = new SocketClient("/tmp/vizion_control.sock", this);
// Create widgets
m_videoWidget = new VideoViewerWidget(this);
m_pipelineWidget = new GStreamerPipelineWidget(m_socketClient, this);
m_cameraWidget = new CameraControlWidget(m_socketClient, this);
// Create tab widget for controls on the right
QTabWidget* controlTabs = new QTabWidget(this);
auto* controlTabs = new QTabWidget(this);
controlTabs->addTab(m_pipelineWidget, "Pipeline Control");
controlTabs->addTab(m_cameraWidget, "Camera Control");
// Create horizontal splitter: video on left (full height), controls on right
QSplitter* mainSplitter = new QSplitter(Qt::Horizontal, this);
auto* mainSplitter = new QSplitter(Qt::Horizontal, this);
mainSplitter->addWidget(m_videoWidget);
mainSplitter->addWidget(controlTabs);
mainSplitter->setStretchFactor(0, 2); // Video gets more space (2/3)
mainSplitter->setStretchFactor(1, 1); // Controls get less space (1/3)
// Set as central widget
setCentralWidget(mainSplitter);
}

View File

@@ -8,16 +8,25 @@
#include <gst/video/video.h>
VideoViewerWidget::VideoViewerWidget(QWidget *parent)
: QWidget(parent), m_pipeline(nullptr), m_appSink(nullptr),
m_busWatchId(0), m_zoomFactor(1.0)
: QWidget(parent),
m_scrollArea(nullptr),
m_videoDisplay(nullptr),
m_startBtn(nullptr),
m_stopBtn(nullptr),
m_sourceType(nullptr),
m_hostEdit(nullptr),
m_portEdit(nullptr),
m_statusLabel(nullptr),
m_zoomSlider(nullptr),
m_zoomLabel(nullptr),
m_pipeline(nullptr),
m_appSink(nullptr),
m_zoomFactor(1.0),
m_busWatchId(0)
{
// Register QImage as meta type for signal/slot across threads
qRegisterMetaType<QImage>("QImage");
initGStreamer();
setupUI();
// Connect signal for frame display
connect(this, &VideoViewerWidget::newFrameAvailable,
this, &VideoViewerWidget::displayFrame, Qt::QueuedConnection);
}
@@ -34,11 +43,10 @@ void VideoViewerWidget::initGStreamer()
void VideoViewerWidget::setupUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
auto* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(5);
// Video display in scroll area for zoom support
m_scrollArea = new QScrollArea(this);
m_scrollArea->setWidgetResizable(false);
m_scrollArea->setAlignment(Qt::AlignCenter);
@@ -52,8 +60,7 @@ void VideoViewerWidget::setupUI()
m_scrollArea->setWidget(m_videoDisplay);
// Zoom control
QHBoxLayout* zoomLayout = new QHBoxLayout();
auto* zoomLayout = new QHBoxLayout();
zoomLayout->addWidget(new QLabel("Zoom:", this));
m_zoomSlider = new QSlider(Qt::Horizontal, this);
@@ -70,12 +77,10 @@ void VideoViewerWidget::setupUI()
zoomLayout->addWidget(m_zoomSlider);
zoomLayout->addWidget(m_zoomLabel);
// Controls
QGroupBox* controlGroup = new QGroupBox("Viewer Controls", this);
QVBoxLayout* controlLayout = new QVBoxLayout();
auto* controlGroup = new QGroupBox("Viewer Controls", this);
auto* controlLayout = new QVBoxLayout();
// Source type selection
QHBoxLayout* sourceLayout = new QHBoxLayout();
auto* sourceLayout = new QHBoxLayout();
sourceLayout->addWidget(new QLabel("Source Type:", this));
m_sourceType = new QComboBox(this);
m_sourceType->addItem("UDP MJPEG Stream (No plugins needed)", "udp-mjpeg");
@@ -87,15 +92,13 @@ void VideoViewerWidget::setupUI()
this, &VideoViewerWidget::onSourceTypeChanged);
sourceLayout->addWidget(m_sourceType);
// Host and port
QFormLayout* formLayout = new QFormLayout();
auto* formLayout = new QFormLayout();
m_hostEdit = new QLineEdit("127.0.0.1", this);
m_portEdit = new QLineEdit("5000", this);
formLayout->addRow("Host:", m_hostEdit);
formLayout->addRow("Port:", m_portEdit);
// Control buttons
QHBoxLayout* buttonLayout = new QHBoxLayout();
auto* buttonLayout = new QHBoxLayout();
m_startBtn = new QPushButton("Start Viewer", this);
m_stopBtn = new QPushButton("Stop Viewer", this);
m_stopBtn->setEnabled(false);
@@ -106,7 +109,6 @@ void VideoViewerWidget::setupUI()
buttonLayout->addWidget(m_startBtn);
buttonLayout->addWidget(m_stopBtn);
// Status label
m_statusLabel = new QLabel("Status: Stopped", this);
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
@@ -116,7 +118,6 @@ void VideoViewerWidget::setupUI()
controlLayout->addWidget(m_statusLabel);
controlGroup->setLayout(controlLayout);
// Add to main layout: video takes most space, zoom control, then viewer controls at bottom
mainLayout->addWidget(m_scrollArea, 1);
mainLayout->addLayout(zoomLayout);
mainLayout->addWidget(controlGroup);
@@ -124,8 +125,7 @@ void VideoViewerWidget::setupUI()
setLayout(mainLayout);
}
QString VideoViewerWidget::buildPipelineString()
{
QString VideoViewerWidget::buildPipelineString() const {
QString sourceType = m_sourceType->currentData().toString();
QString host = m_hostEdit->text();
QString port = m_portEdit->text();
@@ -136,19 +136,17 @@ QString VideoViewerWidget::buildPipelineString()
QString sinkPipeline = "videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true";
if (sourceType == "udp-mjpeg") {
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=JPEG,payload=26 ! "
"rtpjpegdepay ! jpegdec ! %2")
.arg(port).arg(sinkPipeline);
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! %2")
.arg(port, sinkPipeline);
} else if (sourceType == "udp-h264") {
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=H264 ! "
"rtph264depay ! h264parse ! avdec_h264 ! %2")
.arg(port).arg(sinkPipeline);
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! avdec_h264 ! %2")
.arg(port, sinkPipeline);
} else if (sourceType == "tcp") {
pipeline = QString("tcpclientsrc host=%1 port=%2 ! tsdemux ! h264parse ! avdec_h264 ! %3")
.arg(host).arg(port).arg(sinkPipeline);
.arg(host, port, sinkPipeline);
} else if (sourceType == "http") {
pipeline = QString("souphttpsrc location=http://%1:%2 ! multipartdemux ! jpegdec ! %3")
.arg(host).arg(port).arg(sinkPipeline);
.arg(host, port, sinkPipeline);
} else if (sourceType == "test") {
pipeline = QString("videotestsrc ! %1").arg(sinkPipeline);
}
@@ -182,12 +180,10 @@ void VideoViewerWidget::startPipeline()
return;
}
// Set up bus callback
GstBus* bus = gst_element_get_bus(m_pipeline);
m_busWatchId = gst_bus_add_watch(bus, busCallback, this);
gst_object_unref(bus);
// Get appsink element and configure it
m_appSink = gst_bin_get_by_name(GST_BIN(m_pipeline), "videosink");
if (!m_appSink) {
m_statusLabel->setText("Status: Failed to get appsink element");
@@ -196,10 +192,9 @@ void VideoViewerWidget::startPipeline()
return;
}
// Configure appsink
g_object_set(m_appSink, "emit-signals", TRUE, "sync", FALSE, "max-buffers", 1, "drop", TRUE, nullptr);
// Set callback for new samples - properly initialize all fields
// Properly initialize all callback fields
GstAppSinkCallbacks callbacks = { 0 };
callbacks.new_sample = newSampleCallback;
callbacks.eos = nullptr;
@@ -209,7 +204,6 @@ void VideoViewerWidget::startPipeline()
#endif
gst_app_sink_set_callbacks(GST_APP_SINK(m_appSink), &callbacks, this, nullptr);
// Start playing
GstStateChangeReturn ret = gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
qDebug() << "[VideoViewer] Pipeline state change return:" << ret;
@@ -246,7 +240,6 @@ void VideoViewerWidget::stopPipeline()
m_busWatchId = 0;
}
// Clear video display and current frame
m_videoDisplay->clear();
m_videoDisplay->setText("");
m_currentFrame = QImage();
@@ -354,7 +347,6 @@ void VideoViewerWidget::onZoomChanged(int value)
m_zoomFactor = value / 100.0;
m_zoomLabel->setText(QString("%1%").arg(value));
// Re-display the current frame with new zoom factor
if (!m_currentFrame.isNull()) {
displayFrame(m_currentFrame);
}
@@ -377,14 +369,12 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
return GST_FLOW_ERROR;
}
// Pull the sample from appsink
GstSample* sample = gst_app_sink_pull_sample(appsink);
if (!sample) {
qDebug() << "[VideoViewer] Callback: Failed to pull sample";
return GST_FLOW_ERROR;
}
// Get the buffer from the sample
GstBuffer* buffer = gst_sample_get_buffer(sample);
if (!buffer) {
qDebug() << "[VideoViewer] Callback: No buffer in sample";
@@ -392,7 +382,6 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
return GST_FLOW_ERROR;
}
// Get the caps to extract width and height
GstCaps* caps = gst_sample_get_caps(sample);
if (!caps) {
qDebug() << "[VideoViewer] Callback: No caps in sample";
@@ -409,7 +398,6 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
return GST_FLOW_ERROR;
}
// Map the buffer to access the raw data
GstMapInfo map;
if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
qDebug() << "[VideoViewer] Callback: Failed to map buffer";
@@ -417,7 +405,7 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
return GST_FLOW_ERROR;
}
// Calculate expected size for RGB888 format
// RGB888 format: width * height * 3 bytes
gsize expected_size = width * height * 3;
if (map.size < expected_size) {
qDebug() << "[VideoViewer] Callback: Buffer too small. Expected:" << expected_size << "Got:" << map.size;
@@ -426,14 +414,12 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
return GST_FLOW_ERROR;
}
// Create QImage from the RGB data with proper stride
// QImage::Format_RGB888 expects RGB data
// QImage requires RGB data with proper stride (width * 3 bytes per row)
QImage frame(map.data, width, height, width * 3, QImage::Format_RGB888);
// Make a deep copy since the buffer will be unmapped
QImage frameCopy = frame.copy();
// Unmap and cleanup
gst_buffer_unmap(buffer, &map);
gst_sample_unref(sample);
@@ -465,20 +451,16 @@ void VideoViewerWidget::displayFrame(const QImage& frame)
qDebug() << "[VideoViewer] Frames received:" << frameCount;
}
// Store current frame for zoom changes
m_currentFrame = frame;
// Convert QImage to QPixmap
QPixmap pixmap = QPixmap::fromImage(frame);
if (pixmap.isNull()) {
qDebug() << "[VideoViewer] ERROR: Pixmap conversion failed!";
return;
}
// Calculate target size with zoom factor
QSize targetSize = frame.size() * m_zoomFactor;
// Scale pixmap with zoom factor
QPixmap scaledPixmap = pixmap.scaled(targetSize,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
@@ -488,7 +470,6 @@ void VideoViewerWidget::displayFrame(const QImage& frame)
<< "Scaled pixmap:" << scaledPixmap.size();
}
// Update the label size to match the scaled pixmap
m_videoDisplay->setPixmap(scaledPixmap);
m_videoDisplay->resize(scaledPixmap.size());
m_videoDisplay->update();

View File

@@ -18,7 +18,7 @@ class VideoViewerWidget : public QWidget
public:
explicit VideoViewerWidget(QWidget *parent = nullptr);
~VideoViewerWidget();
~VideoViewerWidget() override;
signals:
void newFrameAvailable(const QImage& frame);
@@ -36,12 +36,12 @@ private:
void cleanupGStreamer();
void startPipeline();
void stopPipeline();
QString buildPipelineString();
QString buildPipelineString() const;
static gboolean busCallback(GstBus* bus, GstMessage* msg, gpointer data);
static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data);
// UI elements
// UI elements (8-byte pointers)
QScrollArea* m_scrollArea;
QLabel* m_videoDisplay;
QPushButton* m_startBtn;
@@ -53,14 +53,18 @@ private:
QSlider* m_zoomSlider;
QLabel* m_zoomLabel;
// GStreamer elements
// GStreamer elements (8-byte pointers)
GstElement* m_pipeline;
GstElement* m_appSink;
// Zoom factor (8-byte double)
double m_zoomFactor;
// Bus watch ID (4-byte unsigned int)
guint m_busWatchId;
// Video state
// Video state (large object at end)
QImage m_currentFrame;
double m_zoomFactor;
};
#endif // VIDEOVIEWERWIDGET_H