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

@@ -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();