add zoom control

This commit is contained in:
Maik Jurischka
2025-12-19 07:01:27 +01:00
parent 7313592993
commit 5fed3070de
2 changed files with 98 additions and 12 deletions

View File

@@ -9,7 +9,7 @@
VideoViewerWidget::VideoViewerWidget(QWidget *parent) VideoViewerWidget::VideoViewerWidget(QWidget *parent)
: QWidget(parent), m_pipeline(nullptr), m_appSink(nullptr), : QWidget(parent), m_pipeline(nullptr), m_appSink(nullptr),
m_busWatchId(0) m_busWatchId(0), m_zoomFactor(1.0)
{ {
// Register QImage as meta type for signal/slot across threads // Register QImage as meta type for signal/slot across threads
qRegisterMetaType<QImage>("QImage"); qRegisterMetaType<QImage>("QImage");
@@ -38,12 +38,37 @@ void VideoViewerWidget::setupUI()
mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(5); mainLayout->setSpacing(5);
// Video display - no GroupBox for maximum space // Video display in scroll area for zoom support
m_videoDisplay = new QLabel(this); m_scrollArea = new QScrollArea(this);
m_videoDisplay->setMinimumSize(640, 480); m_scrollArea->setWidgetResizable(false);
m_videoDisplay->setStyleSheet("background-color: black; border: 1px solid #666;"); m_scrollArea->setAlignment(Qt::AlignCenter);
m_scrollArea->setStyleSheet("QScrollArea { background-color: black; border: 1px solid #666; }");
m_videoDisplay = new QLabel();
m_videoDisplay->setMinimumSize(320, 240);
m_videoDisplay->setStyleSheet("background-color: black;");
m_videoDisplay->setAlignment(Qt::AlignCenter); m_videoDisplay->setAlignment(Qt::AlignCenter);
m_videoDisplay->setScaledContents(false); // Disable for better aspect ratio control m_videoDisplay->setScaledContents(false);
m_scrollArea->setWidget(m_videoDisplay);
// Zoom control
QHBoxLayout* zoomLayout = new QHBoxLayout();
zoomLayout->addWidget(new QLabel("Zoom:", this));
m_zoomSlider = new QSlider(Qt::Horizontal, this);
m_zoomSlider->setMinimum(50); // 50%
m_zoomSlider->setMaximum(200); // 200%
m_zoomSlider->setValue(100); // 100% default
m_zoomSlider->setTickPosition(QSlider::TicksBelow);
m_zoomSlider->setTickInterval(25);
connect(m_zoomSlider, &QSlider::valueChanged, this, &VideoViewerWidget::onZoomChanged);
m_zoomLabel = new QLabel("100%", this);
m_zoomLabel->setMinimumWidth(50);
zoomLayout->addWidget(m_zoomSlider);
zoomLayout->addWidget(m_zoomLabel);
// Controls // Controls
QGroupBox* controlGroup = new QGroupBox("Viewer Controls", this); QGroupBox* controlGroup = new QGroupBox("Viewer Controls", this);
@@ -91,8 +116,9 @@ 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, controls at bottom // Add to main layout: video takes most space, zoom control, then viewer controls at bottom
mainLayout->addWidget(m_videoDisplay, 1); mainLayout->addWidget(m_scrollArea, 1);
mainLayout->addLayout(zoomLayout);
mainLayout->addWidget(controlGroup); mainLayout->addWidget(controlGroup);
setLayout(mainLayout); setLayout(mainLayout);
@@ -220,9 +246,10 @@ void VideoViewerWidget::stopPipeline()
m_busWatchId = 0; m_busWatchId = 0;
} }
// Clear video display // Clear video display and current frame
m_videoDisplay->clear(); m_videoDisplay->clear();
m_videoDisplay->setText(""); m_videoDisplay->setText("");
m_currentFrame = QImage();
m_statusLabel->setText("Status: Stopped"); m_statusLabel->setText("Status: Stopped");
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }"); m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
@@ -322,8 +349,28 @@ void VideoViewerWidget::onSourceTypeChanged(int index)
m_portEdit->setEnabled(needsNetwork); m_portEdit->setEnabled(needsNetwork);
} }
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);
}
}
GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer user_data) GstFlowReturn VideoViewerWidget::newSampleCallback(GstAppSink* appsink, gpointer user_data)
{ {
static int callbackCount = 0;
callbackCount++;
if (callbackCount == 1) {
qDebug() << "[VideoViewer] Callback: First sample callback!";
} else if (callbackCount % 30 == 0) {
qDebug() << "[VideoViewer] Callback: Samples received:" << callbackCount;
}
VideoViewerWidget* viewer = static_cast<VideoViewerWidget*>(user_data); VideoViewerWidget* viewer = static_cast<VideoViewerWidget*>(user_data);
if (!viewer) { if (!viewer) {
qDebug() << "[VideoViewer] Callback: viewer is null"; qDebug() << "[VideoViewer] Callback: viewer is null";
@@ -405,15 +452,44 @@ void VideoViewerWidget::displayFrame(const QImage& frame)
} }
static bool firstFrame = true; static bool firstFrame = true;
static int frameCount = 0;
frameCount++;
bool debugThisFrame = firstFrame || (frameCount % 30 == 0);
if (firstFrame) { if (firstFrame) {
qDebug() << "[VideoViewer] First frame received! Size:" << frame.width() << "x" << frame.height(); qDebug() << "[VideoViewer] First frame received! Size:" << frame.width() << "x" << frame.height()
<< "Format:" << frame.format();
firstFrame = false; firstFrame = false;
} else if (frameCount % 30 == 0) {
qDebug() << "[VideoViewer] Frames received:" << frameCount;
} }
// Convert QImage to QPixmap and scale to fit label while keeping aspect ratio // Store current frame for zoom changes
m_currentFrame = frame;
// Convert QImage to QPixmap
QPixmap pixmap = QPixmap::fromImage(frame); QPixmap pixmap = QPixmap::fromImage(frame);
QPixmap scaledPixmap = pixmap.scaled(m_videoDisplay->size(), 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::KeepAspectRatio,
Qt::SmoothTransformation); Qt::SmoothTransformation);
m_videoDisplay->setPixmap(scaledPixmap);
if (debugThisFrame) {
qDebug() << "[VideoViewer] Target size:" << targetSize << "Zoom:" << m_zoomFactor
<< "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

@@ -7,6 +7,8 @@
#include <QLineEdit> #include <QLineEdit>
#include <QLabel> #include <QLabel>
#include <QImage> #include <QImage>
#include <QSlider>
#include <QScrollArea>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/app/gstappsink.h> #include <gst/app/gstappsink.h>
@@ -25,6 +27,7 @@ private slots:
void onStartViewer(); void onStartViewer();
void onStopViewer(); void onStopViewer();
void onSourceTypeChanged(int index); void onSourceTypeChanged(int index);
void onZoomChanged(int value);
void displayFrame(const QImage& frame); void displayFrame(const QImage& frame);
private: private:
@@ -39,6 +42,7 @@ private:
static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data); static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data);
// UI elements // UI elements
QScrollArea* m_scrollArea;
QLabel* m_videoDisplay; QLabel* m_videoDisplay;
QPushButton* m_startBtn; QPushButton* m_startBtn;
QPushButton* m_stopBtn; QPushButton* m_stopBtn;
@@ -46,11 +50,17 @@ private:
QLineEdit* m_hostEdit; QLineEdit* m_hostEdit;
QLineEdit* m_portEdit; QLineEdit* m_portEdit;
QLabel* m_statusLabel; QLabel* m_statusLabel;
QSlider* m_zoomSlider;
QLabel* m_zoomLabel;
// GStreamer elements // GStreamer elements
GstElement* m_pipeline; GstElement* m_pipeline;
GstElement* m_appSink; GstElement* m_appSink;
guint m_busWatchId; guint m_busWatchId;
// Video state
QImage m_currentFrame;
double m_zoomFactor;
}; };
#endif // VIDEOVIEWERWIDGET_H #endif // VIDEOVIEWERWIDGET_H