Keep desktop video in a resizable window and retain decoder on audio clock updates
This commit is contained in:
1 parent
21c22bd911
commit
898154b2b6
3 files changed
+37
-24
No files matched your search
@@ -258,8 +258,8 @@ void CallVideoEngine::receive() {
|
|||||||
call_audio_get_clock(m->id, now, &clock);
|
call_audio_get_clock(m->id, now, &clock);
|
||||||
if (clock.valid) {
|
if (clock.valid) {
|
||||||
if (sourceEpoch && sourceEpoch != clock.source_epoch) {
|
if (sourceEpoch && sourceEpoch != clock.source_epoch) {
|
||||||
pictures.clear(); decoder.close(); call_video_port_request_key(m->id); lastShown = -1;
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video audio clock epoch %llu -> %llu; decoder retained",
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video playback audio source epoch changed");
|
(unsigned long long)sourceEpoch, (unsigned long long)clock.source_epoch);
|
||||||
}
|
}
|
||||||
sourceEpoch = clock.source_epoch;
|
sourceEpoch = clock.source_epoch;
|
||||||
QImage selected;
|
QImage selected;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
#include <QScreen>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -34,6 +35,26 @@ enum class Glyph { Phone, PhoneDown, Mic, MicOff };
|
|||||||
|
|
||||||
static QString callText(const char* text) { return QCoreApplication::translate("ChatGui", text); }
|
static QString callText(const char* text) { return QCoreApplication::translate("ChatGui", text); }
|
||||||
|
|
||||||
|
// Только изображение и миниатюра; подписи/настройки находятся в отдельной панели окна.
|
||||||
|
class CallVideoCanvas : public QWidget {
|
||||||
|
public:
|
||||||
|
explicit CallVideoCanvas(QWidget* parent) : QWidget(parent) { setMinimumSize(160, 120); }
|
||||||
|
QImage remote, local;
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent*) override {
|
||||||
|
QPainter painter(this); painter.fillRect(rect(), Qt::black); painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
|
if (remote.isNull()) return;
|
||||||
|
QSize fitted = remote.size().scaled(size(), Qt::KeepAspectRatio);
|
||||||
|
QRect videoRect(QPoint((width() - fitted.width()) / 2, (height() - fitted.height()) / 2), fitted);
|
||||||
|
painter.drawImage(videoRect, remote);
|
||||||
|
if (!local.isNull()) {
|
||||||
|
QSize preview = local.size().scaled(qMin(200, fitted.width() / 4), qMin(150, fitted.height() / 4), Qt::KeepAspectRatio);
|
||||||
|
QRect target(QPoint(videoRect.right() - preview.width() - 12, videoRect.top() + 12), preview);
|
||||||
|
painter.setPen(QPen(Qt::white, 2)); painter.drawRect(target.adjusted(-1, -1, 1, 1)); painter.drawImage(target, local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static QColor colorForName(const QString& name) {
|
static QColor colorForName(const QString& name) {
|
||||||
static const QColor palette[] = {
|
static const QColor palette[] = {
|
||||||
QColor("#5B9BD5"), QColor("#70AD47"), QColor("#ED7D31"),
|
QColor("#5B9BD5"), QColor("#70AD47"), QColor("#ED7D31"),
|
||||||
@@ -193,11 +214,11 @@ CallWindow::CallWindow(QWidget* parent)
|
|||||||
" padding: 6px 10px; font-size: 13px; background: palette(base); }");
|
" padding: 6px 10px; font-size: 13px; background: palette(base); }");
|
||||||
|
|
||||||
auto* root = new QVBoxLayout(this);
|
auto* root = new QVBoxLayout(this);
|
||||||
root->setContentsMargins(12, 12, 12, 12); root->addStretch(1);
|
root->setContentsMargins(12, 12, 12, 12);
|
||||||
|
m_videoArea = new CallVideoCanvas(this); m_videoArea->hide(); root->addWidget(m_videoArea, 1);
|
||||||
m_panel = new QWidget(this); m_panel->setObjectName("callPanel"); m_panel->setMaximumWidth(640);
|
m_panel = new QWidget(this); m_panel->setObjectName("callPanel"); m_panel->setMaximumWidth(640);
|
||||||
m_panel->setStyleSheet("QWidget#callPanel { background: palette(window); border-radius: 12px; }");
|
m_panel->setStyleSheet("QWidget#callPanel { background: palette(window); border-radius: 12px; }");
|
||||||
root->addWidget(m_panel, 0, Qt::AlignHCenter | Qt::AlignBottom);
|
root->addWidget(m_panel, 0, Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
root->addStretch(1);
|
|
||||||
QVBoxLayout* lay = new QVBoxLayout(m_panel);
|
QVBoxLayout* lay = new QVBoxLayout(m_panel);
|
||||||
lay->setContentsMargins(16, 20, 16, 16);
|
lay->setContentsMargins(16, 20, 16, 16);
|
||||||
lay->setSpacing(8);
|
lay->setSpacing(8);
|
||||||
@@ -246,11 +267,13 @@ CallWindow::CallWindow(QWidget* parent)
|
|||||||
});
|
});
|
||||||
connect(m_video, &CallVideoEngine::imageChanged, this, [this](const QImage& image) {
|
connect(m_video, &CallVideoEngine::imageChanged, this, [this](const QImage& image) {
|
||||||
bool changed = m_remoteImage.isNull() != image.isNull(); m_remoteImage = image;
|
bool changed = m_remoteImage.isNull() != image.isNull(); m_remoteImage = image;
|
||||||
|
static_cast<CallVideoCanvas*>(m_videoArea)->remote = image; m_videoArea->update();
|
||||||
if (changed) updateVideoPresentation();
|
if (changed) updateVideoPresentation();
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
connect(m_video, &CallVideoEngine::localImageChanged, this, [this](const QImage& image) {
|
connect(m_video, &CallVideoEngine::localImageChanged, this, [this](const QImage& image) {
|
||||||
bool changed = m_localImage.isNull() != image.isNull(); m_localImage = image;
|
bool changed = m_localImage.isNull() != image.isNull(); m_localImage = image;
|
||||||
|
static_cast<CallVideoCanvas*>(m_videoArea)->local = image; m_videoArea->update();
|
||||||
if (!image.isNull()) {
|
if (!image.isNull()) {
|
||||||
QSize size = image.size().scaled(200, 150, Qt::KeepAspectRatio); m_localPreview->setFixedSize(size);
|
QSize size = image.size().scaled(200, 150, Qt::KeepAspectRatio); m_localPreview->setFixedSize(size);
|
||||||
m_localPreview->setPixmap(QPixmap::fromImage(image).scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
m_localPreview->setPixmap(QPixmap::fromImage(image).scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
@@ -397,11 +420,17 @@ void CallWindow::updateVideoPresentation() {
|
|||||||
m_receivingVideo = receiving;
|
m_receivingVideo = receiving;
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video presentation remote=%d local=%d", receiving, !m_localImage.isNull());
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call video presentation remote=%d local=%d", receiving, !m_localImage.isNull());
|
||||||
if (receiving) {
|
if (receiving) {
|
||||||
m_settingsBtn->setChecked(false); m_windowGeometry = saveGeometry(); showFullScreen();
|
m_settingsBtn->setChecked(false); m_windowGeometry = saveGeometry();
|
||||||
} else leaveFullScreen();
|
QSize available = screen() ? screen()->availableGeometry().size() : QSize(1280, 900);
|
||||||
|
resize(qMin(1100, available.width() * 9 / 10), qMin(800, available.height() * 9 / 10));
|
||||||
|
} else {
|
||||||
|
leaveFullScreen();
|
||||||
|
if (!m_windowGeometry.isEmpty()) restoreGeometry(m_windowGeometry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool active = m_state == Active, settings = active && m_settingsBtn->isChecked();
|
bool active = m_state == Active, settings = active && m_settingsBtn->isChecked();
|
||||||
static_cast<QVBoxLayout*>(layout())->setStretch(2, receiving ? 0 : 1);
|
m_videoArea->setVisible(receiving);
|
||||||
|
layout()->setAlignment(m_panel, receiving ? Qt::AlignHCenter | Qt::AlignBottom : Qt::AlignCenter);
|
||||||
m_localPreview->setVisible(active && !receiving && !m_localImage.isNull());
|
m_localPreview->setVisible(active && !receiving && !m_localImage.isNull());
|
||||||
m_avatarLabel->setVisible(!receiving); m_statusLabel->setVisible(!receiving);
|
m_avatarLabel->setVisible(!receiving); m_statusLabel->setVisible(!receiving);
|
||||||
m_connectionLabel->setVisible(settings); m_bgpPathLabel->setVisible(settings && !m_bgpPathLabel->text().isEmpty());
|
m_connectionLabel->setVisible(settings); m_bgpPathLabel->setVisible(settings && !m_bgpPathLabel->text().isEmpty());
|
||||||
@@ -413,21 +442,6 @@ void CallWindow::updateVideoPresentation() {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CallWindow::paintEvent(QPaintEvent* event) {
|
|
||||||
QWidget::paintEvent(event);
|
|
||||||
if (!m_receivingVideo) return;
|
|
||||||
QPainter painter(this); painter.fillRect(rect(), Qt::black); painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
|
||||||
QSize remote = m_remoteImage.size().scaled(size(), Qt::KeepAspectRatio);
|
|
||||||
QRect videoRect(QPoint((width() - remote.width()) / 2, (height() - remote.height()) / 2), remote);
|
|
||||||
painter.drawImage(videoRect, m_remoteImage);
|
|
||||||
if (!m_localImage.isNull()) {
|
|
||||||
QSize preview = m_localImage.size().scaled(qMin(240, remote.width() / 4), qMin(180, remote.height() / 4), Qt::KeepAspectRatio);
|
|
||||||
QRect target(QPoint(videoRect.right() - preview.width() - 20, videoRect.top() + 20), preview);
|
|
||||||
painter.setPen(QPen(Qt::white, 2)); painter.drawRect(target.adjusted(-1, -1, 1, 1));
|
|
||||||
painter.drawImage(target, m_localImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CallWindow::keyPressEvent(QKeyEvent* event) {
|
void CallWindow::keyPressEvent(QKeyEvent* event) {
|
||||||
if (event->key() == Qt::Key_Escape && isFullScreen()) { toggleFullScreen(); event->accept(); return; }
|
if (event->key() == Qt::Key_Escape && isFullScreen()) { toggleFullScreen(); event->accept(); return; }
|
||||||
if (event->key() == Qt::Key_F11 && m_receivingVideo) { toggleFullScreen(); event->accept(); return; }
|
if (event->key() == Qt::Key_F11 && m_receivingVideo) { toggleFullScreen(); event->accept(); return; }
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
void paintEvent(QPaintEvent* event) override;
|
|
||||||
void keyPressEvent(QKeyEvent* event) override;
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
void mouseDoubleClickEvent(QMouseEvent* event) override;
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ private:
|
|||||||
QComboBox *m_camera, *m_resolution, *m_codec;
|
QComboBox *m_camera, *m_resolution, *m_codec;
|
||||||
QSpinBox *m_videoMin, *m_videoMax;
|
QSpinBox *m_videoMin, *m_videoMax;
|
||||||
QLabel *m_localPreview, *m_videoStatus;
|
QLabel *m_localPreview, *m_videoStatus;
|
||||||
QWidget *m_videoControls, *m_panel;
|
QWidget *m_videoControls, *m_panel, *m_videoArea;
|
||||||
QPushButton *m_settingsBtn, *m_fullScreenBtn;
|
QPushButton *m_settingsBtn, *m_fullScreenBtn;
|
||||||
QImage m_remoteImage, m_localImage;
|
QImage m_remoteImage, m_localImage;
|
||||||
QByteArray m_windowGeometry;
|
QByteArray m_windowGeometry;
|
||||||
|
|||||||
Reference in new issue
Block a user