clang optimizations and cleanup
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user