zero_copy_video_player/mainwindow.cpp
2026-08-05 08:56:00 -03:00

331 lines
13 KiB
C++

#include "mainwindow.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QFileDialog>
#include <QScrollArea>
#include <QFrame>
#include <QGroupBox>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_updatingSeek(false)
{
setWindowTitle("Retro CRT Video Player (C++/Qt6 & Native CUDA Zero-Copy)");
resize(1200, 720);
m_glWidget = new GLWidget(this);
m_videoDecoder = new VideoDecoder(this);
m_glWidget->setVideoDecoder(m_videoDecoder);
setupUI();
// Conectar las señales de posición y finalización del hilo descodificador
connect(m_videoDecoder, &VideoDecoder::positionChanged, this, &MainWindow::onPositionChanged);
connect(m_videoDecoder, &VideoDecoder::playbackFinished, this, &MainWindow::onPlaybackFinished);
}
MainWindow::~MainWindow() {
m_videoDecoder->stopThread();
}
void MainWindow::setupUI() {
// Aplicar hoja de estilos (QSS) para un look oscuro premium y retro-futurista
this->setStyleSheet(
"QMainWindow { background-color: #08080f; }"
"QWidget { color: #f0f0f5; font-family: 'Outfit', 'Segoe UI', sans-serif; font-size: 13px; }"
"QFrame#sidebar { background-color: #11111d; border-left: 1px solid #22223b; border-radius: 0px; }"
"QPushButton { background-color: #ff007f; color: white; border: none; padding: 8px 15px; border-radius: 6px; font-weight: bold; min-width: 60px; }"
"QPushButton:hover { background-color: #ff3399; }"
"QPushButton:pressed { background-color: #cc0066; }"
"QPushButton:disabled { background-color: #444; color: #888; }"
"QSlider::groove:horizontal { border: 1px solid #2d2d44; height: 6px; background: #1a1a2e; border-radius: 3px; }"
"QSlider::sub-page:horizontal { background: #ff007f; border-radius: 3px; }"
"QSlider::handle:horizontal { background: #ffffff; border: 1px solid #ff007f; width: 14px; height: 14px; margin: -4px 0; border-radius: 7px; }"
"QSlider::handle:horizontal:hover { background: #ff007f; }"
"QLabel { color: #a0a0c0; }"
"QCheckBox { spacing: 8px; }"
"QCheckBox::indicator { width: 18px; height: 18px; border: 1px solid #ff007f; border-radius: 4px; background: #1a1a2e; }"
"QCheckBox::indicator:checked { background-color: #ff007f; image: url(check.png); /* O usar un color plano */ }"
"QGroupBox { font-weight: bold; border: 1px solid #22223b; border-radius: 8px; margin-top: 15px; padding-top: 15px; color: #ff007f; }"
);
QWidget *centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
mainLayout->setContentsMargins(10, 10, 10, 10);
mainLayout->setSpacing(10);
// --- COLUMNA IZQUIERDA: REPRODUCTOR ---
QVBoxLayout *playerLayout = new QVBoxLayout();
playerLayout->addWidget(m_glWidget, 1);
// Controles Multimedia
QHBoxLayout *controlsLayout = new QHBoxLayout();
QPushButton *openBtn = new QPushButton("Abrir Video", this);
openBtn->setStyleSheet("background-color: #00bfff;");
connect(openBtn, &QPushButton::clicked, this, &MainWindow::openVideoFile);
controlsLayout->addWidget(openBtn);
m_playBtn = new QPushButton("▶", this);
connect(m_playBtn, &QPushButton::clicked, this, &MainWindow::togglePlayPause);
controlsLayout->addWidget(m_playBtn);
m_muteBtn = new QPushButton("🔊", this);
m_muteBtn->setStyleSheet("background-color: #4a4e69;");
connect(m_muteBtn, &QPushButton::clicked, this, &MainWindow::toggleMute);
controlsLayout->addWidget(m_muteBtn);
m_seekBar = new QSlider(Qt::Horizontal, this);
m_seekBar->setRange(0, 1000);
m_seekBar->setEnabled(false);
connect(m_seekBar, &QSlider::sliderMoved, this, &MainWindow::setVideoPosition);
controlsLayout->addWidget(m_seekBar, 1);
m_timeLabel = new QLabel("00:00 / 00:00", this);
m_timeLabel->setMinimumWidth(90);
m_timeLabel->setAlignment(Qt::AlignCenter);
controlsLayout->addWidget(m_timeLabel);
playerLayout->addLayout(controlsLayout);
mainLayout->addLayout(playerLayout, 3); // 3/4 del ancho de ventana
// --- COLUMNA DERECHA: SIDEBAR DE AJUSTES ---
QFrame *sidebar = new QFrame(this);
sidebar->setObjectName("sidebar");
sidebar->setFixedWidth(300);
QVBoxLayout *sidebarLayout = new QVBoxLayout(sidebar);
sidebarLayout->setContentsMargins(15, 15, 15, 15);
QLabel *sidebarTitle = new QLabel("AJUSTES CRT RETRO", this);
sidebarTitle->setStyleSheet("font-size: 16px; font-weight: bold; color: #ff007f; margin-bottom: 10px;");
sidebarTitle->setAlignment(Qt::AlignCenter);
sidebarLayout->addWidget(sidebarTitle);
QScrollArea *scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
QWidget *scrollWidget = new QWidget(this);
QVBoxLayout *scrollLayout = new QVBoxLayout(scrollWidget);
scrollLayout->setContentsMargins(0, 0, 0, 0);
// Activador General
m_crtCheckbox = new QCheckBox("Habilitar Efecto CRT", this);
m_crtCheckbox->setChecked(true);
m_crtCheckbox->setStyleSheet("font-weight: bold; font-size: 14px; color: #ffffff;");
connect(m_crtCheckbox, &QCheckBox::toggled, this, &MainWindow::onCrtToggled);
scrollLayout->addWidget(m_crtCheckbox);
// Grupo de Controles Geométricos
QGroupBox *geoGroup = new QGroupBox("Geometría y Lente", this);
QVBoxLayout *geoLayout = new QVBoxLayout(geoGroup);
QLabel *curvLabel = new QLabel("Curvatura de Pantalla:", this);
geoLayout->addWidget(curvLabel);
QHBoxLayout *curvLayout = new QHBoxLayout();
m_curvatureSlider = new QSlider(Qt::Horizontal, this);
m_curvatureSlider->setRange(0, 50); // 0.0 a 5.0
m_curvatureSlider->setValue(20);
connect(m_curvatureSlider, &QSlider::valueChanged, this, &MainWindow::onCurvatureChanged);
curvLayout->addWidget(m_curvatureSlider);
m_curvatureLabel = new QLabel("2.0", this);
m_curvatureLabel->setFixedWidth(30);
curvLayout->addWidget(m_curvatureLabel);
geoLayout->addLayout(curvLayout);
scrollLayout->addWidget(geoGroup);
// Grupo de Estilo Scanline
QGroupBox *scanlineGroup = new QGroupBox("Efecto Scanlines", this);
QVBoxLayout *scanlineLayout = new QVBoxLayout(scanlineGroup);
QLabel *densLabel = new QLabel("Densidad de Líneas:", this);
scanlineLayout->addWidget(densLabel);
QHBoxLayout *densLayout = new QHBoxLayout();
m_scanlineDensitySlider = new QSlider(Qt::Horizontal, this);
m_scanlineDensitySlider->setRange(100, 1000);
m_scanlineDensitySlider->setValue(400);
connect(m_scanlineDensitySlider, &QSlider::valueChanged, this, &MainWindow::onScanlineDensityChanged);
densLayout->addWidget(m_scanlineDensitySlider);
m_scanlineDensityLabel = new QLabel("400", this);
m_scanlineDensityLabel->setFixedWidth(30);
densLayout->addWidget(m_scanlineDensityLabel);
scanlineLayout->addLayout(densLayout);
QLabel *opacLabel = new QLabel("Opacidad de Líneas:", this);
scanlineLayout->addWidget(opacLabel);
QHBoxLayout *opacLayout = new QHBoxLayout();
m_scanlineOpacitySlider = new QSlider(Qt::Horizontal, this);
m_scanlineOpacitySlider->setRange(0, 100); // 0% a 100%
m_scanlineOpacitySlider->setValue(35);
connect(m_scanlineOpacitySlider, &QSlider::valueChanged, this, &MainWindow::onScanlineOpacityChanged);
opacLayout->addWidget(m_scanlineOpacitySlider);
m_scanlineOpacityLabel = new QLabel("0.35", this);
m_scanlineOpacityLabel->setFixedWidth(30);
opacLayout->addWidget(m_scanlineOpacityLabel);
scanlineLayout->addLayout(opacLayout);
scrollLayout->addWidget(scanlineGroup);
// Grupo de Imperfecciones
QGroupBox *imperfGroup = new QGroupBox("Óptica y Color", this);
QVBoxLayout *imperfLayout = new QVBoxLayout(imperfGroup);
QLabel *aberrLabel = new QLabel("Aberración Cromática (RGB split):", this);
imperfLayout->addWidget(aberrLabel);
QHBoxLayout *aberrLayout = new QHBoxLayout();
m_aberrationSlider = new QSlider(Qt::Horizontal, this);
m_aberrationSlider->setRange(0, 100); // 0.0000 a 0.0100
m_aberrationSlider->setValue(15);
connect(m_aberrationSlider, &QSlider::valueChanged, this, &MainWindow::onAberrationChanged);
aberrLayout->addWidget(m_aberrationSlider);
m_aberrationLabel = new QLabel("0.0015", this);
m_aberrationLabel->setFixedWidth(45);
aberrLayout->addWidget(m_aberrationLabel);
imperfLayout->addLayout(aberrLayout);
QLabel *vigLabel = new QLabel("Viñeteado (Esquinas oscuras):", this);
imperfLayout->addWidget(vigLabel);
QHBoxLayout *vigLayout = new QHBoxLayout();
m_vignetteSlider = new QSlider(Qt::Horizontal, this);
m_vignetteSlider->setRange(5, 50); // 0.05 a 0.50
m_vignetteSlider->setValue(20);
connect(m_vignetteSlider, &QSlider::valueChanged, this, &MainWindow::onVignetteChanged);
vigLayout->addWidget(m_vignetteSlider);
m_vignetteLabel = new QLabel("0.20", this);
m_vignetteLabel->setFixedWidth(30);
vigLayout->addWidget(m_vignetteLabel);
imperfLayout->addLayout(vigLayout);
QLabel *brightLabel = new QLabel("Brillo Adicional:", this);
imperfLayout->addWidget(brightLabel);
QHBoxLayout *brightLayout = new QHBoxLayout();
m_brightnessSlider = new QSlider(Qt::Horizontal, this);
m_brightnessSlider->setRange(80, 150); // 0.80 a 1.50
m_brightnessSlider->setValue(110);
connect(m_brightnessSlider, &QSlider::valueChanged, this, &MainWindow::onBrightnessChanged);
brightLayout->addWidget(m_brightnessSlider);
m_brightnessLabel = new QLabel("1.10", this);
m_brightnessLabel->setFixedWidth(30);
brightLayout->addWidget(m_brightnessLabel);
imperfLayout->addLayout(brightLayout);
scrollLayout->addWidget(imperfGroup);
scrollLayout->addStretch(1);
scrollArea->setWidget(scrollWidget);
sidebarLayout->addWidget(scrollArea);
mainLayout->addWidget(sidebar);
}
void MainWindow::openVideoFile() {
QString fileName = QFileDialog::getOpenFileName(this, "Abrir archivo de video", "", "Videos (*.mp4 *.avi *.mkv *.webm *.mov)");
if (!fileName.isEmpty()) {
openVideoFileDirect(fileName);
}
}
void MainWindow::openVideoFileDirect(const QString &fileName) {
m_glWidget->clearVideo();
m_videoDecoder->stopThread();
if (m_videoDecoder->openFile(fileName)) {
m_seekBar->setEnabled(m_videoDecoder->getDuration() > 0.0);
m_playBtn->setText("⏸");
m_videoDecoder->start(); // Arranca el hilo de decodificación
m_videoDecoder->play(); // Inicia la reproducción
}
}
void MainWindow::togglePlayPause() {
if (m_videoDecoder->isPaused()) {
m_videoDecoder->play();
m_playBtn->setText("⏸");
} else {
m_videoDecoder->pause();
m_playBtn->setText("▶");
}
}
void MainWindow::toggleMute() {
static bool muted = false;
muted = !muted;
m_muteBtn->setText(muted ? "🔇" : "🔊");
}
void MainWindow::setVideoPosition(int position) {
if (m_videoDecoder->getDuration() > 0.0) {
m_updatingSeek = true;
double targetSecs = (static_cast<double>(position) / 1000.0) * m_videoDecoder->getDuration();
m_videoDecoder->seek(targetSecs);
m_updatingSeek = false;
}
}
void MainWindow::onPositionChanged(qint64 positionMs) {
double durationSecs = m_videoDecoder->getDuration();
if (durationSecs > 0.0 && !m_updatingSeek) {
m_updatingSeek = true;
double progress = static_cast<double>(positionMs) / (durationSecs * 1000.0);
m_seekBar->setValue(static_cast<int>(progress * 1000.0));
m_updatingSeek = false;
}
// Formatear tiempos
auto format = [](qint64 ms) -> QString {
qint64 totalSecs = ms / 1000;
int mins = totalSecs / 60;
int secs = totalSecs % 60;
return QString("%1:%2").arg(mins, 2, 10, QChar('0')).arg(secs, 2, 10, QChar('0'));
};
m_timeLabel->setText(format(positionMs) + " / " + format(static_cast<qint64>(durationSecs * 1000.0)));
}
void MainWindow::onPlaybackFinished() {
m_playBtn->setText("▶");
m_seekBar->setValue(0);
}
// --- SLOTS DE PARÁMETROS DEL SHADER ---
void MainWindow::onCrtToggled(bool checked) {
m_glWidget->setCrtEnabled(checked);
}
void MainWindow::onCurvatureChanged(int value) {
float curvatureVal = value / 10.0f;
m_curvatureLabel->setText(QString::number(curvatureVal, 'f', 1));
m_glWidget->setCurvature(curvatureVal);
}
void MainWindow::onScanlineDensityChanged(int value) {
m_scanlineDensityLabel->setText(QString::number(value));
m_glWidget->setScanlineDensity(static_cast<float>(value));
}
void MainWindow::onScanlineOpacityChanged(int value) {
float opacityVal = value / 100.0f;
m_scanlineOpacityLabel->setText(QString::number(opacityVal, 'f', 2));
m_glWidget->setScanlineOpacity(opacityVal);
}
void MainWindow::onAberrationChanged(int value) {
float aberrationVal = value / 10000.0f;
m_aberrationLabel->setText(QString::number(aberrationVal, 'f', 4));
m_glWidget->setAberration(aberrationVal);
}
void MainWindow::onVignetteChanged(int value) {
float vignetteVal = value / 100.0f;
m_vignetteLabel->setText(QString::number(vignetteVal, 'f', 2));
m_glWidget->setVignette(vignetteVal);
}
void MainWindow::onBrightnessChanged(int value) {
float brightnessVal = value / 100.0f;
m_brightnessLabel->setText(QString::number(brightnessVal, 'f', 2));
m_glWidget->setBrightness(brightnessVal);
}