diff --git a/videoviewerwidget.cpp b/videoviewerwidget.cpp index 1e64122..27d24a5 100644 --- a/videoviewerwidget.cpp +++ b/videoviewerwidget.cpp @@ -9,7 +9,7 @@ VideoViewerWidget::VideoViewerWidget(QWidget *parent) : 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 qRegisterMetaType("QImage"); @@ -38,12 +38,37 @@ void VideoViewerWidget::setupUI() mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(5); - // Video display - no GroupBox for maximum space - m_videoDisplay = new QLabel(this); - m_videoDisplay->setMinimumSize(640, 480); - m_videoDisplay->setStyleSheet("background-color: black; border: 1px solid #666;"); + // Video display in scroll area for zoom support + m_scrollArea = new QScrollArea(this); + m_scrollArea->setWidgetResizable(false); + 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->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 QGroupBox* controlGroup = new QGroupBox("Viewer Controls", this); @@ -91,8 +116,9 @@ void VideoViewerWidget::setupUI() controlLayout->addWidget(m_statusLabel); controlGroup->setLayout(controlLayout); - // Add to main layout: video takes most space, controls at bottom - mainLayout->addWidget(m_videoDisplay, 1); + // 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); setLayout(mainLayout); @@ -220,9 +246,10 @@ void VideoViewerWidget::stopPipeline() m_busWatchId = 0; } - // Clear video display + // Clear video display and current frame m_videoDisplay->clear(); m_videoDisplay->setText(""); + m_currentFrame = QImage(); m_statusLabel->setText("Status: Stopped"); 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); } +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) { + 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(user_data); if (!viewer) { qDebug() << "[VideoViewer] Callback: viewer is null"; @@ -405,15 +452,44 @@ void VideoViewerWidget::displayFrame(const QImage& frame) } static bool firstFrame = true; + static int frameCount = 0; + frameCount++; + + bool debugThisFrame = firstFrame || (frameCount % 30 == 0); + 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; + } 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 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::SmoothTransformation); + + 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(); } diff --git a/videoviewerwidget.h b/videoviewerwidget.h index 9574ac2..289b394 100644 --- a/videoviewerwidget.h +++ b/videoviewerwidget.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -25,6 +27,7 @@ private slots: void onStartViewer(); void onStopViewer(); void onSourceTypeChanged(int index); + void onZoomChanged(int value); void displayFrame(const QImage& frame); private: @@ -39,6 +42,7 @@ private: static GstFlowReturn newSampleCallback(GstAppSink* appsink, gpointer user_data); // UI elements + QScrollArea* m_scrollArea; QLabel* m_videoDisplay; QPushButton* m_startBtn; QPushButton* m_stopBtn; @@ -46,11 +50,17 @@ private: QLineEdit* m_hostEdit; QLineEdit* m_portEdit; QLabel* m_statusLabel; + QSlider* m_zoomSlider; + QLabel* m_zoomLabel; // GStreamer elements GstElement* m_pipeline; GstElement* m_appSink; guint m_busWatchId; + + // Video state + QImage m_currentFrame; + double m_zoomFactor; }; #endif // VIDEOVIEWERWIDGET_H