add zoom control
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user