clang optimizations and cleanup
This commit is contained in:
@@ -14,17 +14,15 @@ CameraControlWidget::CameraControlWidget(SocketClient* socketClient, QWidget *pa
|
|||||||
|
|
||||||
void CameraControlWidget::setupUI()
|
void CameraControlWidget::setupUI()
|
||||||
{
|
{
|
||||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
auto* mainLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
// Create scroll area for all controls
|
auto* scrollArea = new QScrollArea(this);
|
||||||
QScrollArea* scrollArea = new QScrollArea(this);
|
|
||||||
scrollArea->setWidgetResizable(true);
|
scrollArea->setWidgetResizable(true);
|
||||||
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
QWidget* scrollWidget = new QWidget();
|
auto* scrollWidget = new QWidget();
|
||||||
QVBoxLayout* scrollLayout = new QVBoxLayout(scrollWidget);
|
auto* scrollLayout = new QVBoxLayout(scrollWidget);
|
||||||
|
|
||||||
// Add all control groups
|
|
||||||
scrollLayout->addWidget(createFormatGroup());
|
scrollLayout->addWidget(createFormatGroup());
|
||||||
scrollLayout->addWidget(createExposureGroup());
|
scrollLayout->addWidget(createExposureGroup());
|
||||||
scrollLayout->addWidget(createWhiteBalanceGroup());
|
scrollLayout->addWidget(createWhiteBalanceGroup());
|
||||||
@@ -36,7 +34,6 @@ void CameraControlWidget::setupUI()
|
|||||||
|
|
||||||
mainLayout->addWidget(scrollArea);
|
mainLayout->addWidget(scrollArea);
|
||||||
|
|
||||||
// Status label at bottom
|
|
||||||
m_statusLabel = new QLabel("Status: Ready", this);
|
m_statusLabel = new QLabel("Status: Ready", this);
|
||||||
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
||||||
mainLayout->addWidget(m_statusLabel);
|
mainLayout->addWidget(m_statusLabel);
|
||||||
@@ -46,8 +43,8 @@ void CameraControlWidget::setupUI()
|
|||||||
|
|
||||||
QGroupBox* CameraControlWidget::createFormatGroup()
|
QGroupBox* CameraControlWidget::createFormatGroup()
|
||||||
{
|
{
|
||||||
QGroupBox* groupBox = new QGroupBox("Video Format", this);
|
auto* groupBox = new QGroupBox("Video Format", this);
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
auto* layout = new QVBoxLayout();
|
||||||
|
|
||||||
m_formatCombo = new QComboBox(this);
|
m_formatCombo = new QComboBox(this);
|
||||||
m_formatCombo->addItem("1280x720@30fps UYVY (Supported)", "1280,720,30,UYVY");
|
m_formatCombo->addItem("1280x720@30fps UYVY (Supported)", "1280,720,30,UYVY");
|
||||||
@@ -69,10 +66,10 @@ QGroupBox* CameraControlWidget::createFormatGroup()
|
|||||||
|
|
||||||
QGroupBox* CameraControlWidget::createExposureGroup()
|
QGroupBox* CameraControlWidget::createExposureGroup()
|
||||||
{
|
{
|
||||||
QGroupBox* groupBox = new QGroupBox("Exposure", this);
|
auto* groupBox = new QGroupBox("Exposure", this);
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
auto* layout = new QVBoxLayout();
|
||||||
|
|
||||||
QButtonGroup* exposureGroup = new QButtonGroup(this);
|
auto* exposureGroup = new QButtonGroup(this);
|
||||||
m_exposureAuto = new QRadioButton("Auto", this);
|
m_exposureAuto = new QRadioButton("Auto", this);
|
||||||
m_exposureManual = new QRadioButton("Manual", this);
|
m_exposureManual = new QRadioButton("Manual", this);
|
||||||
m_exposureAuto->setChecked(true);
|
m_exposureAuto->setChecked(true);
|
||||||
@@ -82,7 +79,7 @@ QGroupBox* CameraControlWidget::createExposureGroup()
|
|||||||
|
|
||||||
connect(m_exposureAuto, &QRadioButton::toggled, this, &CameraControlWidget::onExposureModeChanged);
|
connect(m_exposureAuto, &QRadioButton::toggled, this, &CameraControlWidget::onExposureModeChanged);
|
||||||
|
|
||||||
QHBoxLayout* modeLayout = new QHBoxLayout();
|
auto* modeLayout = new QHBoxLayout();
|
||||||
modeLayout->addWidget(m_exposureAuto);
|
modeLayout->addWidget(m_exposureAuto);
|
||||||
modeLayout->addWidget(m_exposureManual);
|
modeLayout->addWidget(m_exposureManual);
|
||||||
|
|
||||||
@@ -94,7 +91,7 @@ QGroupBox* CameraControlWidget::createExposureGroup()
|
|||||||
m_setExposureBtn = new QPushButton("Set Exposure", this);
|
m_setExposureBtn = new QPushButton("Set Exposure", this);
|
||||||
connect(m_setExposureBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetExposure);
|
connect(m_setExposureBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetExposure);
|
||||||
|
|
||||||
QFormLayout* formLayout = new QFormLayout();
|
auto* formLayout = new QFormLayout();
|
||||||
formLayout->addRow("Mode:", modeLayout);
|
formLayout->addRow("Mode:", modeLayout);
|
||||||
formLayout->addRow("Value:", m_exposureValue);
|
formLayout->addRow("Value:", m_exposureValue);
|
||||||
|
|
||||||
@@ -107,10 +104,10 @@ QGroupBox* CameraControlWidget::createExposureGroup()
|
|||||||
|
|
||||||
QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
||||||
{
|
{
|
||||||
QGroupBox* groupBox = new QGroupBox("White Balance", this);
|
auto* groupBox = new QGroupBox("White Balance", this);
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
auto* layout = new QVBoxLayout();
|
||||||
|
|
||||||
QButtonGroup* wbGroup = new QButtonGroup(this);
|
auto* wbGroup = new QButtonGroup(this);
|
||||||
m_whiteBalanceAuto = new QRadioButton("Auto", this);
|
m_whiteBalanceAuto = new QRadioButton("Auto", this);
|
||||||
m_whiteBalanceManual = new QRadioButton("Manual", this);
|
m_whiteBalanceManual = new QRadioButton("Manual", this);
|
||||||
m_whiteBalanceAuto->setChecked(true);
|
m_whiteBalanceAuto->setChecked(true);
|
||||||
@@ -120,7 +117,7 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
|||||||
|
|
||||||
connect(m_whiteBalanceAuto, &QRadioButton::toggled, this, &CameraControlWidget::onWhiteBalanceModeChanged);
|
connect(m_whiteBalanceAuto, &QRadioButton::toggled, this, &CameraControlWidget::onWhiteBalanceModeChanged);
|
||||||
|
|
||||||
QHBoxLayout* modeLayout = new QHBoxLayout();
|
auto* modeLayout = new QHBoxLayout();
|
||||||
modeLayout->addWidget(m_whiteBalanceAuto);
|
modeLayout->addWidget(m_whiteBalanceAuto);
|
||||||
modeLayout->addWidget(m_whiteBalanceManual);
|
modeLayout->addWidget(m_whiteBalanceManual);
|
||||||
|
|
||||||
@@ -133,7 +130,7 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
|||||||
m_setWhiteBalanceBtn = new QPushButton("Set White Balance", this);
|
m_setWhiteBalanceBtn = new QPushButton("Set White Balance", this);
|
||||||
connect(m_setWhiteBalanceBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetWhiteBalance);
|
connect(m_setWhiteBalanceBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetWhiteBalance);
|
||||||
|
|
||||||
QFormLayout* formLayout = new QFormLayout();
|
auto* formLayout = new QFormLayout();
|
||||||
formLayout->addRow("Mode:", modeLayout);
|
formLayout->addRow("Mode:", modeLayout);
|
||||||
formLayout->addRow("Temperature:", m_whiteBalanceTemp);
|
formLayout->addRow("Temperature:", m_whiteBalanceTemp);
|
||||||
|
|
||||||
@@ -146,8 +143,8 @@ QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
|||||||
|
|
||||||
QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
|
QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
|
||||||
{
|
{
|
||||||
QGroupBox* groupBox = new QGroupBox("Image Adjustments", this);
|
auto* groupBox = new QGroupBox("Image Adjustments", this);
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
auto* layout = new QVBoxLayout();
|
||||||
|
|
||||||
layout->addWidget(createSliderControl("Brightness (0-255):", 0, 255, 128,
|
layout->addWidget(createSliderControl("Brightness (0-255):", 0, 255, 128,
|
||||||
&m_brightnessSlider, &m_brightnessSpinBox));
|
&m_brightnessSlider, &m_brightnessSpinBox));
|
||||||
@@ -180,13 +177,13 @@ QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
|
|||||||
QWidget* CameraControlWidget::createSliderControl(const QString& label, int min, int max, int defaultValue,
|
QWidget* CameraControlWidget::createSliderControl(const QString& label, int min, int max, int defaultValue,
|
||||||
QSlider** slider, QSpinBox** spinBox)
|
QSlider** slider, QSpinBox** spinBox)
|
||||||
{
|
{
|
||||||
QWidget* widget = new QWidget(this);
|
auto* widget = new QWidget(this);
|
||||||
QVBoxLayout* layout = new QVBoxLayout(widget);
|
auto* layout = new QVBoxLayout(widget);
|
||||||
layout->setContentsMargins(0, 5, 0, 5);
|
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 = new QSlider(Qt::Horizontal, this);
|
||||||
(*slider)->setRange(min, max);
|
(*slider)->setRange(min, max);
|
||||||
@@ -223,9 +220,8 @@ void CameraControlWidget::onGetFormats()
|
|||||||
int fps = fmt["framerate"].toInt();
|
int fps = fmt["framerate"].toInt();
|
||||||
QString format = fmt["format"].toString();
|
QString format = fmt["format"].toString();
|
||||||
|
|
||||||
QString displayText = QString("%1x%2@%3fps %4")
|
QString displayText = QString("%1x%2@%3fps %4").arg(width, height, fps).arg(format);
|
||||||
.arg(width).arg(height).arg(fps).arg(format);
|
QString data = QString("%1,%2,%3,%4").arg(width, height, fps).arg(format);
|
||||||
QString data = QString("%1,%2,%3,%4").arg(width).arg(height).arg(fps).arg(format);
|
|
||||||
|
|
||||||
m_formatCombo->addItem(displayText, data);
|
m_formatCombo->addItem(displayText, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,11 @@ GStreamerPipelineWidget::GStreamerPipelineWidget(SocketClient* socketClient, QWi
|
|||||||
|
|
||||||
void GStreamerPipelineWidget::setupUI()
|
void GStreamerPipelineWidget::setupUI()
|
||||||
{
|
{
|
||||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
auto* mainLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
QGroupBox* groupBox = new QGroupBox("GStreamer Pipeline", this);
|
auto* groupBox = new QGroupBox("GStreamer Pipeline", this);
|
||||||
QVBoxLayout* groupLayout = new QVBoxLayout(groupBox);
|
auto* groupLayout = new QVBoxLayout(groupBox);
|
||||||
|
|
||||||
// Info label with instructions
|
|
||||||
m_infoLabel = new QLabel(
|
m_infoLabel = new QLabel(
|
||||||
"<b>Quick Start:</b> Click 'Quick Start' to automatically configure and start streaming.<br>"
|
"<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);
|
"<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);
|
m_infoLabel->setWordWrap(true);
|
||||||
groupLayout->addWidget(m_infoLabel);
|
groupLayout->addWidget(m_infoLabel);
|
||||||
|
|
||||||
// Quick Start button (prominent)
|
|
||||||
m_quickStartBtn = new QPushButton("⚡ Quick Start (Auto Configure & Stream)", this);
|
m_quickStartBtn = new QPushButton("⚡ Quick Start (Auto Configure & Stream)", this);
|
||||||
m_quickStartBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px; }");
|
m_quickStartBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px; }");
|
||||||
connect(m_quickStartBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onQuickStart);
|
connect(m_quickStartBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onQuickStart);
|
||||||
groupLayout->addWidget(m_quickStartBtn);
|
groupLayout->addWidget(m_quickStartBtn);
|
||||||
|
|
||||||
// Separator
|
auto* line = new QFrame(this);
|
||||||
QFrame* line = new QFrame(this);
|
|
||||||
line->setFrameShape(QFrame::HLine);
|
line->setFrameShape(QFrame::HLine);
|
||||||
line->setFrameShadow(QFrame::Sunken);
|
line->setFrameShadow(QFrame::Sunken);
|
||||||
groupLayout->addWidget(line);
|
groupLayout->addWidget(line);
|
||||||
|
|
||||||
// Format selection
|
auto* formatLabel = new QLabel("Video Format:", this);
|
||||||
QLabel* formatLabel = new QLabel("Video Format:", this);
|
|
||||||
m_formatCombo = new QComboBox(this);
|
m_formatCombo = new QComboBox(this);
|
||||||
m_formatCombo->addItem("1280x720@30fps UYVY (Default/Supported)", "1280,720,30,UYVY");
|
m_formatCombo->addItem("1280x720@30fps UYVY (Default/Supported)", "1280,720,30,UYVY");
|
||||||
groupLayout->addWidget(formatLabel);
|
groupLayout->addWidget(formatLabel);
|
||||||
groupLayout->addWidget(m_formatCombo);
|
groupLayout->addWidget(m_formatCombo);
|
||||||
|
|
||||||
// Pipeline presets
|
auto* presetsLabel = new QLabel("Pipeline Presets:", this);
|
||||||
QLabel* presetsLabel = new QLabel("Pipeline Presets:", this);
|
|
||||||
m_pipelinePresets = new QComboBox(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("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");
|
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(presetsLabel);
|
||||||
groupLayout->addWidget(m_pipelinePresets);
|
groupLayout->addWidget(m_pipelinePresets);
|
||||||
|
|
||||||
// Pipeline editor
|
auto* pipelineLabel = new QLabel("Pipeline:", this);
|
||||||
QLabel* pipelineLabel = new QLabel("Pipeline:", this);
|
|
||||||
m_pipelineEdit = new QTextEdit(this);
|
m_pipelineEdit = new QTextEdit(this);
|
||||||
m_pipelineEdit->setMaximumHeight(80);
|
m_pipelineEdit->setMaximumHeight(80);
|
||||||
m_pipelineEdit->setPlaceholderText("Enter GStreamer pipeline here...\nExample: videoconvert ! autovideosink");
|
m_pipelineEdit->setPlaceholderText("Enter GStreamer pipeline here...\nExample: videoconvert ! autovideosink");
|
||||||
@@ -75,13 +69,11 @@ void GStreamerPipelineWidget::setupUI()
|
|||||||
groupLayout->addWidget(pipelineLabel);
|
groupLayout->addWidget(pipelineLabel);
|
||||||
groupLayout->addWidget(m_pipelineEdit);
|
groupLayout->addWidget(m_pipelineEdit);
|
||||||
|
|
||||||
// Set pipeline button
|
|
||||||
m_setPipelineBtn = new QPushButton("Set Pipeline", this);
|
m_setPipelineBtn = new QPushButton("Set Pipeline", this);
|
||||||
connect(m_setPipelineBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onSetPipeline);
|
connect(m_setPipelineBtn, &QPushButton::clicked, this, &GStreamerPipelineWidget::onSetPipeline);
|
||||||
groupLayout->addWidget(m_setPipelineBtn);
|
groupLayout->addWidget(m_setPipelineBtn);
|
||||||
|
|
||||||
// Stream control buttons
|
auto* buttonLayout = new QHBoxLayout();
|
||||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
|
||||||
m_startStreamBtn = new QPushButton("Start Stream", this);
|
m_startStreamBtn = new QPushButton("Start Stream", this);
|
||||||
m_stopStreamBtn = new QPushButton("Stop Stream", this);
|
m_stopStreamBtn = new QPushButton("Stop Stream", this);
|
||||||
m_getStatusBtn = new QPushButton("Get Status", this);
|
m_getStatusBtn = new QPushButton("Get Status", this);
|
||||||
@@ -95,7 +87,6 @@ void GStreamerPipelineWidget::setupUI()
|
|||||||
buttonLayout->addWidget(m_getStatusBtn);
|
buttonLayout->addWidget(m_getStatusBtn);
|
||||||
groupLayout->addLayout(buttonLayout);
|
groupLayout->addLayout(buttonLayout);
|
||||||
|
|
||||||
// Status label
|
|
||||||
m_statusLabel = new QLabel("Status: Unknown", this);
|
m_statusLabel = new QLabel("Status: Unknown", this);
|
||||||
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
||||||
groupLayout->addWidget(m_statusLabel);
|
groupLayout->addWidget(m_statusLabel);
|
||||||
@@ -148,7 +139,6 @@ void GStreamerPipelineWidget::onStartStream()
|
|||||||
|
|
||||||
m_socketClient->sendCommand("set_format", formatParams,
|
m_socketClient->sendCommand("set_format", formatParams,
|
||||||
[this](const QJsonObject& response) {
|
[this](const QJsonObject& response) {
|
||||||
// Now start stream
|
|
||||||
m_socketClient->sendCommand("start_stream", QJsonObject(),
|
m_socketClient->sendCommand("start_stream", QJsonObject(),
|
||||||
[this](const QJsonObject& response) {
|
[this](const QJsonObject& response) {
|
||||||
updateStatus("Streaming started", true);
|
updateStatus("Streaming started", true);
|
||||||
@@ -213,7 +203,6 @@ void GStreamerPipelineWidget::updateStatus(const QString& status, bool streaming
|
|||||||
|
|
||||||
void GStreamerPipelineWidget::onQuickStart()
|
void GStreamerPipelineWidget::onQuickStart()
|
||||||
{
|
{
|
||||||
// Disable button during process
|
|
||||||
m_quickStartBtn->setEnabled(false);
|
m_quickStartBtn->setEnabled(false);
|
||||||
m_quickStartBtn->setText("Configuring...");
|
m_quickStartBtn->setText("Configuring...");
|
||||||
|
|
||||||
@@ -306,10 +295,8 @@ void GStreamerPipelineWidget::onFormatsReceived(const QJsonObject& response)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear existing formats
|
|
||||||
m_formatCombo->clear();
|
m_formatCombo->clear();
|
||||||
|
|
||||||
// Add all available formats
|
|
||||||
for (const QJsonValue& val : formats) {
|
for (const QJsonValue& val : formats) {
|
||||||
QJsonObject fmt = val.toObject();
|
QJsonObject fmt = val.toObject();
|
||||||
int width = fmt["width"].toInt();
|
int width = fmt["width"].toInt();
|
||||||
@@ -317,9 +304,8 @@ void GStreamerPipelineWidget::onFormatsReceived(const QJsonObject& response)
|
|||||||
int fps = fmt["framerate"].toInt();
|
int fps = fmt["framerate"].toInt();
|
||||||
QString format = fmt["format"].toString();
|
QString format = fmt["format"].toString();
|
||||||
|
|
||||||
QString displayText = QString("%1x%2@%3fps %4")
|
QString displayText = QString("%1x%2@%3fps %4").arg(width, height, fps).arg(format);
|
||||||
.arg(width).arg(height).arg(fps).arg(format);
|
QString data = QString("%1,%2,%3,%4").arg(width, height, fps).arg(format);
|
||||||
QString data = QString("%1,%2,%3,%4").arg(width).arg(height).arg(fps).arg(format);
|
|
||||||
|
|
||||||
m_formatCombo->addItem(displayText, data);
|
m_formatCombo->addItem(displayText, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,26 +24,21 @@ MainWindow::~MainWindow()
|
|||||||
|
|
||||||
void MainWindow::setupUI()
|
void MainWindow::setupUI()
|
||||||
{
|
{
|
||||||
// Create socket client
|
|
||||||
m_socketClient = new SocketClient("/tmp/vizion_control.sock", this);
|
m_socketClient = new SocketClient("/tmp/vizion_control.sock", this);
|
||||||
|
|
||||||
// Create widgets
|
|
||||||
m_videoWidget = new VideoViewerWidget(this);
|
m_videoWidget = new VideoViewerWidget(this);
|
||||||
m_pipelineWidget = new GStreamerPipelineWidget(m_socketClient, this);
|
m_pipelineWidget = new GStreamerPipelineWidget(m_socketClient, this);
|
||||||
m_cameraWidget = new CameraControlWidget(m_socketClient, this);
|
m_cameraWidget = new CameraControlWidget(m_socketClient, this);
|
||||||
|
|
||||||
// Create tab widget for controls on the right
|
auto* controlTabs = new QTabWidget(this);
|
||||||
QTabWidget* controlTabs = new QTabWidget(this);
|
|
||||||
controlTabs->addTab(m_pipelineWidget, "Pipeline Control");
|
controlTabs->addTab(m_pipelineWidget, "Pipeline Control");
|
||||||
controlTabs->addTab(m_cameraWidget, "Camera Control");
|
controlTabs->addTab(m_cameraWidget, "Camera Control");
|
||||||
|
|
||||||
// Create horizontal splitter: video on left (full height), controls on right
|
auto* mainSplitter = new QSplitter(Qt::Horizontal, this);
|
||||||
QSplitter* mainSplitter = new QSplitter(Qt::Horizontal, this);
|
|
||||||
mainSplitter->addWidget(m_videoWidget);
|
mainSplitter->addWidget(m_videoWidget);
|
||||||
mainSplitter->addWidget(controlTabs);
|
mainSplitter->addWidget(controlTabs);
|
||||||
mainSplitter->setStretchFactor(0, 2); // Video gets more space (2/3)
|
mainSplitter->setStretchFactor(0, 2); // Video gets more space (2/3)
|
||||||
mainSplitter->setStretchFactor(1, 1); // Controls get less space (1/3)
|
mainSplitter->setStretchFactor(1, 1); // Controls get less space (1/3)
|
||||||
|
|
||||||
// Set as central widget
|
|
||||||
setCentralWidget(mainSplitter);
|
setCentralWidget(mainSplitter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,25 @@
|
|||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
|
||||||
VideoViewerWidget::VideoViewerWidget(QWidget *parent)
|
VideoViewerWidget::VideoViewerWidget(QWidget *parent)
|
||||||
: QWidget(parent), m_pipeline(nullptr), m_appSink(nullptr),
|
: QWidget(parent),
|
||||||
m_busWatchId(0), m_zoomFactor(1.0)
|
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();
|
initGStreamer();
|
||||||
setupUI();
|
setupUI();
|
||||||
|
|
||||||
// Connect signal for frame display
|
|
||||||
connect(this, &VideoViewerWidget::newFrameAvailable,
|
connect(this, &VideoViewerWidget::newFrameAvailable,
|
||||||
this, &VideoViewerWidget::displayFrame, Qt::QueuedConnection);
|
this, &VideoViewerWidget::displayFrame, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
@@ -34,11 +43,10 @@ void VideoViewerWidget::initGStreamer()
|
|||||||
|
|
||||||
void VideoViewerWidget::setupUI()
|
void VideoViewerWidget::setupUI()
|
||||||
{
|
{
|
||||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
auto* mainLayout = new QVBoxLayout(this);
|
||||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
mainLayout->setSpacing(5);
|
mainLayout->setSpacing(5);
|
||||||
|
|
||||||
// Video display in scroll area for zoom support
|
|
||||||
m_scrollArea = new QScrollArea(this);
|
m_scrollArea = new QScrollArea(this);
|
||||||
m_scrollArea->setWidgetResizable(false);
|
m_scrollArea->setWidgetResizable(false);
|
||||||
m_scrollArea->setAlignment(Qt::AlignCenter);
|
m_scrollArea->setAlignment(Qt::AlignCenter);
|
||||||
@@ -52,8 +60,7 @@ void VideoViewerWidget::setupUI()
|
|||||||
|
|
||||||
m_scrollArea->setWidget(m_videoDisplay);
|
m_scrollArea->setWidget(m_videoDisplay);
|
||||||
|
|
||||||
// Zoom control
|
auto* zoomLayout = new QHBoxLayout();
|
||||||
QHBoxLayout* zoomLayout = new QHBoxLayout();
|
|
||||||
zoomLayout->addWidget(new QLabel("Zoom:", this));
|
zoomLayout->addWidget(new QLabel("Zoom:", this));
|
||||||
|
|
||||||
m_zoomSlider = new QSlider(Qt::Horizontal, this);
|
m_zoomSlider = new QSlider(Qt::Horizontal, this);
|
||||||
@@ -70,12 +77,10 @@ void VideoViewerWidget::setupUI()
|
|||||||
zoomLayout->addWidget(m_zoomSlider);
|
zoomLayout->addWidget(m_zoomSlider);
|
||||||
zoomLayout->addWidget(m_zoomLabel);
|
zoomLayout->addWidget(m_zoomLabel);
|
||||||
|
|
||||||
// Controls
|
auto* controlGroup = new QGroupBox("Viewer Controls", this);
|
||||||
QGroupBox* controlGroup = new QGroupBox("Viewer Controls", this);
|
auto* controlLayout = new QVBoxLayout();
|
||||||
QVBoxLayout* controlLayout = new QVBoxLayout();
|
|
||||||
|
|
||||||
// Source type selection
|
auto* sourceLayout = new QHBoxLayout();
|
||||||
QHBoxLayout* sourceLayout = new QHBoxLayout();
|
|
||||||
sourceLayout->addWidget(new QLabel("Source Type:", this));
|
sourceLayout->addWidget(new QLabel("Source Type:", this));
|
||||||
m_sourceType = new QComboBox(this);
|
m_sourceType = new QComboBox(this);
|
||||||
m_sourceType->addItem("UDP MJPEG Stream (No plugins needed)", "udp-mjpeg");
|
m_sourceType->addItem("UDP MJPEG Stream (No plugins needed)", "udp-mjpeg");
|
||||||
@@ -87,15 +92,13 @@ void VideoViewerWidget::setupUI()
|
|||||||
this, &VideoViewerWidget::onSourceTypeChanged);
|
this, &VideoViewerWidget::onSourceTypeChanged);
|
||||||
sourceLayout->addWidget(m_sourceType);
|
sourceLayout->addWidget(m_sourceType);
|
||||||
|
|
||||||
// Host and port
|
auto* formLayout = new QFormLayout();
|
||||||
QFormLayout* formLayout = new QFormLayout();
|
|
||||||
m_hostEdit = new QLineEdit("127.0.0.1", this);
|
m_hostEdit = new QLineEdit("127.0.0.1", this);
|
||||||
m_portEdit = new QLineEdit("5000", this);
|
m_portEdit = new QLineEdit("5000", this);
|
||||||
formLayout->addRow("Host:", m_hostEdit);
|
formLayout->addRow("Host:", m_hostEdit);
|
||||||
formLayout->addRow("Port:", m_portEdit);
|
formLayout->addRow("Port:", m_portEdit);
|
||||||
|
|
||||||
// Control buttons
|
auto* buttonLayout = new QHBoxLayout();
|
||||||
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
|
||||||
m_startBtn = new QPushButton("Start Viewer", this);
|
m_startBtn = new QPushButton("Start Viewer", this);
|
||||||
m_stopBtn = new QPushButton("Stop Viewer", this);
|
m_stopBtn = new QPushButton("Stop Viewer", this);
|
||||||
m_stopBtn->setEnabled(false);
|
m_stopBtn->setEnabled(false);
|
||||||
@@ -106,7 +109,6 @@ void VideoViewerWidget::setupUI()
|
|||||||
buttonLayout->addWidget(m_startBtn);
|
buttonLayout->addWidget(m_startBtn);
|
||||||
buttonLayout->addWidget(m_stopBtn);
|
buttonLayout->addWidget(m_stopBtn);
|
||||||
|
|
||||||
// Status label
|
|
||||||
m_statusLabel = new QLabel("Status: Stopped", this);
|
m_statusLabel = new QLabel("Status: Stopped", this);
|
||||||
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
||||||
|
|
||||||
@@ -116,7 +118,6 @@ void VideoViewerWidget::setupUI()
|
|||||||
controlLayout->addWidget(m_statusLabel);
|
controlLayout->addWidget(m_statusLabel);
|
||||||
controlGroup->setLayout(controlLayout);
|
controlGroup->setLayout(controlLayout);
|
||||||
|
|
||||||
// Add to main layout: video takes most space, zoom control, then viewer controls at bottom
|
|
||||||
mainLayout->addWidget(m_scrollArea, 1);
|
mainLayout->addWidget(m_scrollArea, 1);
|
||||||
mainLayout->addLayout(zoomLayout);
|
mainLayout->addLayout(zoomLayout);
|
||||||
mainLayout->addWidget(controlGroup);
|
mainLayout->addWidget(controlGroup);
|
||||||
@@ -124,8 +125,7 @@ void VideoViewerWidget::setupUI()
|
|||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VideoViewerWidget::buildPipelineString()
|
QString VideoViewerWidget::buildPipelineString() const {
|
||||||
{
|
|
||||||
QString sourceType = m_sourceType->currentData().toString();
|
QString sourceType = m_sourceType->currentData().toString();
|
||||||
QString host = m_hostEdit->text();
|
QString host = m_hostEdit->text();
|
||||||
QString port = m_portEdit->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";
|
QString sinkPipeline = "videoconvert ! video/x-raw,format=RGB ! appsink name=videosink emit-signals=true";
|
||||||
|
|
||||||
if (sourceType == "udp-mjpeg") {
|
if (sourceType == "udp-mjpeg") {
|
||||||
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=JPEG,payload=26 ! "
|
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! %2")
|
||||||
"rtpjpegdepay ! jpegdec ! %2")
|
.arg(port, sinkPipeline);
|
||||||
.arg(port).arg(sinkPipeline);
|
|
||||||
} else if (sourceType == "udp-h264") {
|
} else if (sourceType == "udp-h264") {
|
||||||
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=H264 ! "
|
pipeline = QString("udpsrc port=%1 ! application/x-rtp,encoding-name=H264 ! rtph264depay ! h264parse ! avdec_h264 ! %2")
|
||||||
"rtph264depay ! h264parse ! avdec_h264 ! %2")
|
.arg(port, sinkPipeline);
|
||||||
.arg(port).arg(sinkPipeline);
|
|
||||||
} else if (sourceType == "tcp") {
|
} else if (sourceType == "tcp") {
|
||||||
pipeline = QString("tcpclientsrc host=%1 port=%2 ! tsdemux ! h264parse ! avdec_h264 ! %3")
|
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") {
|
} else if (sourceType == "http") {
|
||||||
pipeline = QString("souphttpsrc location=http://%1:%2 ! multipartdemux ! jpegdec ! %3")
|
pipeline = QString("souphttpsrc location=http://%1:%2 ! multipartdemux ! jpegdec ! %3")
|
||||||
.arg(host).arg(port).arg(sinkPipeline);
|
.arg(host, port, sinkPipeline);
|
||||||
} else if (sourceType == "test") {
|
} else if (sourceType == "test") {
|
||||||
pipeline = QString("videotestsrc ! %1").arg(sinkPipeline);
|
pipeline = QString("videotestsrc ! %1").arg(sinkPipeline);
|
||||||
}
|
}
|
||||||
@@ -182,12 +180,10 @@ void VideoViewerWidget::startPipeline()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up bus callback
|
|
||||||
GstBus* bus = gst_element_get_bus(m_pipeline);
|
GstBus* bus = gst_element_get_bus(m_pipeline);
|
||||||
m_busWatchId = gst_bus_add_watch(bus, busCallback, this);
|
m_busWatchId = gst_bus_add_watch(bus, busCallback, this);
|
||||||
gst_object_unref(bus);
|
gst_object_unref(bus);
|
||||||
|
|
||||||
// Get appsink element and configure it
|
|
||||||
m_appSink = gst_bin_get_by_name(GST_BIN(m_pipeline), "videosink");
|
m_appSink = gst_bin_get_by_name(GST_BIN(m_pipeline), "videosink");
|
||||||
if (!m_appSink) {
|
if (!m_appSink) {
|
||||||
m_statusLabel->setText("Status: Failed to get appsink element");
|
m_statusLabel->setText("Status: Failed to get appsink element");
|
||||||
@@ -196,10 +192,9 @@ void VideoViewerWidget::startPipeline()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure appsink
|
|
||||||
g_object_set(m_appSink, "emit-signals", TRUE, "sync", FALSE, "max-buffers", 1, "drop", TRUE, nullptr);
|
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 };
|
GstAppSinkCallbacks callbacks = { 0 };
|
||||||
callbacks.new_sample = newSampleCallback;
|
callbacks.new_sample = newSampleCallback;
|
||||||
callbacks.eos = nullptr;
|
callbacks.eos = nullptr;
|
||||||
@@ -209,7 +204,6 @@ void VideoViewerWidget::startPipeline()
|
|||||||
#endif
|
#endif
|
||||||
gst_app_sink_set_callbacks(GST_APP_SINK(m_appSink), &callbacks, this, nullptr);
|
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);
|
GstStateChangeReturn ret = gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
|
||||||
|
|
||||||
qDebug() << "[VideoViewer] Pipeline state change return:" << ret;
|
qDebug() << "[VideoViewer] Pipeline state change return:" << ret;
|
||||||
@@ -246,7 +240,6 @@ void VideoViewerWidget::stopPipeline()
|
|||||||
m_busWatchId = 0;
|
m_busWatchId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear video display and current frame
|
|
||||||
m_videoDisplay->clear();
|
m_videoDisplay->clear();
|
||||||
m_videoDisplay->setText("");
|
m_videoDisplay->setText("");
|
||||||
m_currentFrame = QImage();
|
m_currentFrame = QImage();
|
||||||
@@ -354,7 +347,6 @@ void VideoViewerWidget::onZoomChanged(int value)
|
|||||||
m_zoomFactor = value / 100.0;
|
m_zoomFactor = value / 100.0;
|
||||||
m_zoomLabel->setText(QString("%1%").arg(value));
|
m_zoomLabel->setText(QString("%1%").arg(value));
|
||||||
|
|
||||||
// Re-display the current frame with new zoom factor
|
|
||||||
if (!m_currentFrame.isNull()) {
|
if (!m_currentFrame.isNull()) {
|
||||||
displayFrame(m_currentFrame);
|
displayFrame(m_currentFrame);
|
||||||
}
|
}
|
||||||
@@ -377,14 +369,12 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
|
|||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull the sample from appsink
|
|
||||||
GstSample* sample = gst_app_sink_pull_sample(appsink);
|
GstSample* sample = gst_app_sink_pull_sample(appsink);
|
||||||
if (!sample) {
|
if (!sample) {
|
||||||
qDebug() << "[VideoViewer] Callback: Failed to pull sample";
|
qDebug() << "[VideoViewer] Callback: Failed to pull sample";
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the buffer from the sample
|
|
||||||
GstBuffer* buffer = gst_sample_get_buffer(sample);
|
GstBuffer* buffer = gst_sample_get_buffer(sample);
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
qDebug() << "[VideoViewer] Callback: No buffer in sample";
|
qDebug() << "[VideoViewer] Callback: No buffer in sample";
|
||||||
@@ -392,7 +382,6 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
|
|||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the caps to extract width and height
|
|
||||||
GstCaps* caps = gst_sample_get_caps(sample);
|
GstCaps* caps = gst_sample_get_caps(sample);
|
||||||
if (!caps) {
|
if (!caps) {
|
||||||
qDebug() << "[VideoViewer] Callback: No caps in sample";
|
qDebug() << "[VideoViewer] Callback: No caps in sample";
|
||||||
@@ -409,7 +398,6 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
|
|||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the buffer to access the raw data
|
|
||||||
GstMapInfo map;
|
GstMapInfo map;
|
||||||
if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
|
if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
|
||||||
qDebug() << "[VideoViewer] Callback: Failed to map buffer";
|
qDebug() << "[VideoViewer] Callback: Failed to map buffer";
|
||||||
@@ -417,7 +405,7 @@ GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer
|
|||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate expected size for RGB888 format
|
// RGB888 format: width * height * 3 bytes
|
||||||
gsize expected_size = width * height * 3;
|
gsize expected_size = width * height * 3;
|
||||||
if (map.size < expected_size) {
|
if (map.size < expected_size) {
|
||||||
qDebug() << "[VideoViewer] Callback: Buffer too small. Expected:" << expected_size << "Got:" << map.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;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create QImage from the RGB data with proper stride
|
// QImage requires RGB data with proper stride (width * 3 bytes per row)
|
||||||
// QImage::Format_RGB888 expects RGB data
|
|
||||||
QImage frame(map.data, width, height, width * 3, QImage::Format_RGB888);
|
QImage frame(map.data, width, height, width * 3, QImage::Format_RGB888);
|
||||||
|
|
||||||
// Make a deep copy since the buffer will be unmapped
|
// Make a deep copy since the buffer will be unmapped
|
||||||
QImage frameCopy = frame.copy();
|
QImage frameCopy = frame.copy();
|
||||||
|
|
||||||
// Unmap and cleanup
|
|
||||||
gst_buffer_unmap(buffer, &map);
|
gst_buffer_unmap(buffer, &map);
|
||||||
gst_sample_unref(sample);
|
gst_sample_unref(sample);
|
||||||
|
|
||||||
@@ -465,20 +451,16 @@ void VideoViewerWidget::displayFrame(const QImage& frame)
|
|||||||
qDebug() << "[VideoViewer] Frames received:" << frameCount;
|
qDebug() << "[VideoViewer] Frames received:" << frameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store current frame for zoom changes
|
|
||||||
m_currentFrame = frame;
|
m_currentFrame = frame;
|
||||||
|
|
||||||
// Convert QImage to QPixmap
|
|
||||||
QPixmap pixmap = QPixmap::fromImage(frame);
|
QPixmap pixmap = QPixmap::fromImage(frame);
|
||||||
if (pixmap.isNull()) {
|
if (pixmap.isNull()) {
|
||||||
qDebug() << "[VideoViewer] ERROR: Pixmap conversion failed!";
|
qDebug() << "[VideoViewer] ERROR: Pixmap conversion failed!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate target size with zoom factor
|
|
||||||
QSize targetSize = frame.size() * m_zoomFactor;
|
QSize targetSize = frame.size() * m_zoomFactor;
|
||||||
|
|
||||||
// Scale pixmap with zoom factor
|
|
||||||
QPixmap scaledPixmap = pixmap.scaled(targetSize,
|
QPixmap scaledPixmap = pixmap.scaled(targetSize,
|
||||||
Qt::KeepAspectRatio,
|
Qt::KeepAspectRatio,
|
||||||
Qt::SmoothTransformation);
|
Qt::SmoothTransformation);
|
||||||
@@ -488,7 +470,6 @@ void VideoViewerWidget::displayFrame(const QImage& frame)
|
|||||||
<< "Scaled pixmap:" << scaledPixmap.size();
|
<< "Scaled pixmap:" << scaledPixmap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the label size to match the scaled pixmap
|
|
||||||
m_videoDisplay->setPixmap(scaledPixmap);
|
m_videoDisplay->setPixmap(scaledPixmap);
|
||||||
m_videoDisplay->resize(scaledPixmap.size());
|
m_videoDisplay->resize(scaledPixmap.size());
|
||||||
m_videoDisplay->update();
|
m_videoDisplay->update();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class VideoViewerWidget : public QWidget
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VideoViewerWidget(QWidget *parent = nullptr);
|
explicit VideoViewerWidget(QWidget *parent = nullptr);
|
||||||
~VideoViewerWidget();
|
~VideoViewerWidget() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newFrameAvailable(const QImage& frame);
|
void newFrameAvailable(const QImage& frame);
|
||||||
@@ -36,12 +36,12 @@ private:
|
|||||||
void cleanupGStreamer();
|
void cleanupGStreamer();
|
||||||
void startPipeline();
|
void startPipeline();
|
||||||
void stopPipeline();
|
void stopPipeline();
|
||||||
QString buildPipelineString();
|
QString buildPipelineString() const;
|
||||||
|
|
||||||
static gboolean busCallback(GstBus* bus, GstMessage* msg, gpointer data);
|
static gboolean busCallback(GstBus* bus, GstMessage* msg, gpointer data);
|
||||||
static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data);
|
static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data);
|
||||||
|
|
||||||
// UI elements
|
// UI elements (8-byte pointers)
|
||||||
QScrollArea* m_scrollArea;
|
QScrollArea* m_scrollArea;
|
||||||
QLabel* m_videoDisplay;
|
QLabel* m_videoDisplay;
|
||||||
QPushButton* m_startBtn;
|
QPushButton* m_startBtn;
|
||||||
@@ -53,14 +53,18 @@ private:
|
|||||||
QSlider* m_zoomSlider;
|
QSlider* m_zoomSlider;
|
||||||
QLabel* m_zoomLabel;
|
QLabel* m_zoomLabel;
|
||||||
|
|
||||||
// GStreamer elements
|
// GStreamer elements (8-byte pointers)
|
||||||
GstElement* m_pipeline;
|
GstElement* m_pipeline;
|
||||||
GstElement* m_appSink;
|
GstElement* m_appSink;
|
||||||
|
|
||||||
|
// Zoom factor (8-byte double)
|
||||||
|
double m_zoomFactor;
|
||||||
|
|
||||||
|
// Bus watch ID (4-byte unsigned int)
|
||||||
guint m_busWatchId;
|
guint m_busWatchId;
|
||||||
|
|
||||||
// Video state
|
// Video state (large object at end)
|
||||||
QImage m_currentFrame;
|
QImage m_currentFrame;
|
||||||
double m_zoomFactor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VIDEOVIEWERWIDGET_H
|
#endif // VIDEOVIEWERWIDGET_H
|
||||||
|
|||||||
Reference in New Issue
Block a user