From 46f4294a903ef2b9bad83c000b3336b0b8f1ec9d Mon Sep 17 00:00:00 2001 From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com> Date: Wed, 4 Sep 2019 17:15:51 -0400 Subject: [PATCH] video: refactor/separate preview from the distant renderer Change-Id: I9af9e0a4bb07a83b68647015dc8a4e2a21cd2170 --- callwidget.cpp | 46 ++++- callwidget.h | 8 +- jami-qt.pro | 10 +- lrcinstance.cpp | 127 ++++++++++++ lrcinstance.h | 81 ++++++++ mainwindow.cpp | 88 +------- mainwindow.h | 5 +- newwizardwidget.cpp | 2 + photoboothwidget.cpp | 66 +++--- photoboothwidget.h | 22 +- photoboothwidget.ui | 21 +- previewrender.cpp | 151 ++++++++++++++ previewrender.h | 96 +++++++++ previewrender.ui | 19 ++ ring-client-windows.vcxproj | 7 + ring-client-windows.vcxproj.filters | 19 +- settingswidget.cpp | 156 +++----------- settingswidget.h | 21 +- settingswidget.ui | 22 +- stylesheet.css | 4 + utils.cpp | 42 ++++ utils.h | 19 +- videoview.cpp | 119 +++++------ videoview.h | 14 +- videowidget.cpp | 302 +++------------------------- videowidget.h | 59 +----- 26 files changed, 782 insertions(+), 744 deletions(-) create mode 100644 lrcinstance.cpp create mode 100644 previewrender.cpp create mode 100644 previewrender.h create mode 100644 previewrender.ui diff --git a/callwidget.cpp b/callwidget.cpp index 5d855f6..27cce37 100644 --- a/callwidget.cpp +++ b/callwidget.cpp @@ -50,6 +50,7 @@ #include <QScrollBar> #include <QWebEngineScript> #include <QMimeData> +#include <QtConcurrent/QtConcurrent> #include <algorithm> #include <memory> @@ -222,6 +223,8 @@ CallWidget::CallWidget(QWidget* parent) : setCallPanelVisibility(false); ui->containerWidget->setVisible(false); + + previewRenderer_ = PreviewRenderWidget::attachPreview(); } CallWidget::~CallWidget() @@ -256,6 +259,16 @@ CallWidget::navigated(bool to) } else { backToWelcomePage(); } + // reset preview renderer + if (LRCInstance::getActiveCalls().size() && !LRCInstance::getCurrentCallModel()->getCall(conversation->callId).isAudioOnly) { + previewRenderer_->setParent(ui->videoWidget); + previewRenderer_->changeToRoundedBoarder(); + previewRenderer_->setCurrentConainerGeo(ui->videoWidget->width(), ui->videoWidget->height()); + previewRenderer_->setPhotoMode(false); + previewRenderer_->setNeedToCentre(false); + previewRenderer_->triggerResetPreviewAfterImageReloaded(); + previewRenderer_->show(); + } } else { QObject::disconnect(smartlistSelectionConnection_); smartListModel_.reset(nullptr); @@ -676,6 +689,16 @@ CallWidget::slotShowCallView(const std::string& accountId, } ui->callStackWidget->setCurrentWidget(ui->videoPage); hideMiniSpinner(); + + // reset preview renderer when call is not audio only + if (!LRCInstance::getCurrentCallModel()->getCall(convInfo.callId).isAudioOnly) { + previewRenderer_->setParent(ui->videoWidget); + previewRenderer_->changeToRoundedBoarder(); + previewRenderer_->setCurrentConainerGeo(ui->videoWidget->width(), ui->videoWidget->height()); + previewRenderer_->setPhotoMode(false); + previewRenderer_->setNeedToCentre(false); + previewRenderer_->triggerResetPreviewAfterImageReloaded(); + } ui->videoWidget->pushRenderer(convInfo.callId, LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.type == lrc::api::profile::Type::SIP); ui->videoWidget->setFocus(); } @@ -1412,13 +1435,26 @@ CallWidget::Copy() } void -CallWidget::disconnectRendering() +CallWidget::reconnectRenderingVideoDeviceChanged() { - ui->videoWidget->disconnectRendering(); + // for distant renderer to reconnect rendering + ui->videoWidget->reconnectRenderingVideoDeviceChanged(); } void -CallWidget::connectRendering(bool started) -{ - ui->videoWidget->connectRendering(started); +CallWidget::restartPreviewWhenSwitchDevice() +{ + previewRenderer_->setCurrentConainerGeo(ui->videoWidget->width(), ui->videoWidget->height()); + // since there is the possiblity of image not reloaded properly + // after rendering reconnect, so trigger reset after image reloaded + previewRenderer_->triggerResetPreviewAfterImageReloaded(); + if (LRCInstance::getActiveCalls().size() && !LRCInstance::getIfCurrentSelectedCallIsAudioOnly()) { + // if no active calls, or device is changed -> reactive preview + previewRenderer_->connectRendering(); + QtConcurrent::run( + [this] { + LRCInstance::avModel().stopPreview(); + LRCInstance::avModel().startPreview(); + }); + } } diff --git a/callwidget.h b/callwidget.h index 0160b62..2186a5d 100644 --- a/callwidget.h +++ b/callwidget.h @@ -32,6 +32,7 @@ #include "navwidget.h" #include "smartlistmodel.h" +#include "previewrender.h" // new LRC #include "api/account.h" @@ -57,11 +58,9 @@ public: explicit CallWidget(QWidget* parent = 0); ~CallWidget(); + void restartPreviewWhenSwitchDevice(); int getLeftPanelWidth(); - void disconnectRendering(); - - // if started is true, only update, stop signals are connected - void connectRendering(bool started = false); + void reconnectRenderingVideoDeviceChanged(); // NavWidget virtual void navigated(bool to); @@ -143,6 +142,7 @@ private: QMenu* menu_; QClipboard* clipboard_; + PreviewRenderWidget* previewRenderer_; Ui::CallWidget* ui; QMovie* miniSpinner_; diff --git a/jami-qt.pro b/jami-qt.pro index 63eb28e..0c54bfc 100644 --- a/jami-qt.pro +++ b/jami-qt.pro @@ -74,7 +74,8 @@ HEADERS += ./aboutdialog.h \ ./sipinputpanel.h \ ./callaudioonlyavataroverlay.h \ ./overlaybutton.h \ - ./accountmigrationdialog.h + ./accountmigrationdialog.h \ + ./previewrender.h SOURCES += ./aboutdialog.cpp \ ./banneditemwidget.cpp \ ./conversationsfilterwidget.cpp \ @@ -130,7 +131,9 @@ SOURCES += ./aboutdialog.cpp \ ./sipinputpanel.cpp \ ./callaudioonlyavataroverlay.cpp \ ./overlaybutton.cpp \ - ./accountmigrationdialog.cpp + ./accountmigrationdialog.cpp \ + ./previewrender.cpp \ + ./lrcinstance.cpp FORMS += ./aboutdialog.ui \ ./advancedsipsettingwidget.ui \ ./callwidget.ui \ @@ -156,5 +159,6 @@ FORMS += ./aboutdialog.ui \ ./videoview.ui \ ./sipkeypad.ui \ ./callaudioonlyavataroverlay.ui \ - ./accountmigration.ui + ./accountmigration.ui \ + ./previewrender.ui RESOURCES += ressources.qrc diff --git a/lrcinstance.cpp b/lrcinstance.cpp new file mode 100644 index 0000000..431b588 --- /dev/null +++ b/lrcinstance.cpp @@ -0,0 +1,127 @@ +/************************************************************************** +| Copyright (C) 2019 by Savoir-faire Linux | +| Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> | +| | +| This program is free software; you can redistribute it and/or modify | +| it under the terms of the GNU General Public License as published by | +| the Free Software Foundation; either version 3 of the License, or | +| (at your option) any later version. | +| | +| This program is distributed in the hope that it will be useful, | +| but WITHOUT ANY WARRANTY; without even the implied warranty of | +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | +| GNU General Public License for more details. | +| | +| You should have received a copy of the GNU General Public License | +| along with this program. If not, see <https://www.gnu.org/licenses/>. | +**************************************************************************/ + +#include "lrcinstance.h" + +FrameWrapper::FrameWrapper(bool isPreview) : + isPreview_(isPreview) +{ +} + +FrameWrapper::~FrameWrapper() +{ + +} + +void +FrameWrapper::connectPreviewRendering() +{ + QObject::disconnect(frameWrapperConnections_.started); + if (isPreview_) { + frameWrapperConnections_.started = connect( + &LRCInstance::avModel(), + &lrc::api::AVModel::rendererStarted, + this, + &FrameWrapper::slotPreviewStarted); + } +} + +void +FrameWrapper::slotPreviewStarted(const std::string& id) +{ + if (id != lrc::api::video::PREVIEW_RENDERER_ID) + return; + + QObject::disconnect(frameWrapperConnections_.started); + + QObject::disconnect(frameWrapperConnections_.updated); + frameWrapperConnections_.updated = connect( + &LRCInstance::avModel(), + &lrc::api::AVModel::frameUpdated, + this, + &FrameWrapper::slotPreviewUpdated); + + QObject::disconnect(frameWrapperConnections_.stopped); + frameWrapperConnections_.stopped = connect( + &LRCInstance::avModel(), + &lrc::api::AVModel::rendererStopped, + this, + &FrameWrapper::slotPreviewStoped); +} + +void +FrameWrapper::slotPreviewUpdated(const std::string& id) +{ + if (id != lrc::api::video::PREVIEW_RENDERER_ID) + return; + auto avModel = &LRCInstance::avModel(); + auto renderer = &avModel->getRenderer(id); + if (!renderer->isRendering()) { + return; + } + previewRenderer_ = const_cast<lrc::api::video::Renderer*>(renderer); + renderFrame(id); +} + +void +FrameWrapper::renderFrame(const std::string& id) +{ + auto avModel = &LRCInstance::avModel(); + using namespace lrc::api::video; + auto renderer = &avModel->getRenderer(id); + if (renderer && renderer->isRendering()) { + { + QMutexLocker lock(&mutex_); + auto tmp = renderer->currentFrame(); + if (tmp.storage.size()) { + previewFrame_ = tmp; + } + } + emit previewRenderReady(); + } +} + +void +FrameWrapper::slotPreviewStoped(const std::string& id) +{ + if (id != lrc::api::video::PREVIEW_RENDERER_ID) + return; + QObject::disconnect(frameWrapperConnections_.updated); + QObject::disconnect(frameWrapperConnections_.stopped); + previewRenderer_ = nullptr; + emit previewRenderStopped(); +} + +RenderDistributer::RenderDistributer() +{ + previewFrameWrapper_ = std::make_unique<FrameWrapper>(true); + + connect(previewFrameWrapper_.get(), &FrameWrapper::previewRenderReady, + [this]() { + emit previewRenderReady(); + }); + connect(previewFrameWrapper_.get(), &FrameWrapper::previewRenderStopped, + [this]() { + emit previewRenderStopped(); + }); +} + +RenderDistributer::~RenderDistributer() +{ + previewFrameWrapper_.reset(); +} diff --git a/lrcinstance.h b/lrcinstance.h index 54cf86e..0e5c068 100644 --- a/lrcinstance.h +++ b/lrcinstance.h @@ -2,6 +2,7 @@ | Copyright (C) 2019 by Savoir-faire Linux | | Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> | | Author: Isa Nanic <isa.nanic@savoirfairelinux.com> | +| Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> | | | | This program is free software; you can redistribute it and/or modify | | it under the terms of the GNU General Public License as published by | @@ -23,6 +24,7 @@ #endif #include <QObject> +#include <QMutex> #include <QSettings> #include <QRegularExpression> #include <QPixmap> @@ -30,6 +32,7 @@ #include "settingskey.h" #include "accountlistmodel.h" +#include "utils.h" #include "api/lrc.h" #include "api/account.h" @@ -46,6 +49,67 @@ #include "api/conversationmodel.h" #include "api/peerdiscoverymodel.h" +#include <memory> + +class FrameWrapper : public QObject +{ + + Q_OBJECT + +public: + FrameWrapper(bool isPreview); + ~FrameWrapper(); + + void connectPreviewRendering(); + lrc::api::video::Renderer* getPreviewRenderer() { return previewRenderer_; } + lrc::api::video::Frame getPreviewFrame() { return previewFrame_; } + +signals: + void previewRenderReady(); + void previewRenderStopped(); + +private slots: + void slotPreviewStarted(const std::string& id = {}); + void slotPreviewUpdated(const std::string& id = {}); + void slotPreviewStoped(const std::string& id = {}); + +private: + bool isPreview_; + lrc::api::video::Renderer* previewRenderer_; + lrc::api::video::Frame previewFrame_; + + QMutex mutex_; + + struct frameWrapperConnections { + QMetaObject::Connection started, stopped, updated; + } frameWrapperConnections_; + + void renderFrame(const std::string& id); +}; + +class RenderDistributer : public QObject +{ + + Q_OBJECT + +public: + RenderDistributer(); + ~RenderDistributer(); + lrc::api::video::Renderer* getPreviewRenderer() { return previewFrameWrapper_->getPreviewRenderer(); } + lrc::api::video::Frame getPreviewFrame() { return previewFrameWrapper_->getPreviewFrame(); } + void connectPreviewRendering() { previewFrameWrapper_->connectPreviewRendering(); } + +signals: + void previewRenderReady(); + void previewRenderStopped(); + +private: + // one preview to rule them all + std::unique_ptr<FrameWrapper> previewFrameWrapper_; + // distant for each call/conf/conversation + //std::map<std::string, std::unique_ptr<FrameWrapper>> distantFrames_; +}; + using namespace lrc::api; using migrateCallback = std::function<void()>; @@ -67,6 +131,9 @@ public: static Lrc& getAPI() { return *(instance().lrc_); }; + static RenderDistributer* getRenderDistributer() { + return instance().renderer_.get(); + } static void connectivityChanged() { instance().lrc_->connectivityChanged(); }; @@ -132,11 +199,22 @@ public: instance().selectedConvUid_ = convUid; }; + static bool getIfCurrentSelectedCallIsAudioOnly() { + auto isAudioOnly = false; + auto convInfo = Utils::getSelectedConversation(); + if (!convInfo.uid.empty()) { + isAudioOnly = LRCInstance::getCurrentCallModel()->getCall(convInfo.callId).isAudioOnly; + } + return isAudioOnly; + }; + static void reset(bool newInstance = false) { if (newInstance) { instance().lrc_.reset(new Lrc()); + instance().renderer_.reset(new RenderDistributer()); } else { instance().lrc_.reset(); + instance().renderer_.reset(); } }; @@ -189,8 +267,11 @@ private: LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {}) { lrc_ = std::make_unique<Lrc>(willMigrateCb, didMigrateCb); + renderer_ = std::make_unique<RenderDistributer>(); }; std::string selectedAccountId_; std::string selectedConvUid_; + + std::unique_ptr<RenderDistributer> renderer_; }; diff --git a/mainwindow.cpp b/mainwindow.cpp index e09cf26..7da621e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -21,7 +21,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include <QtConcurrent/QtConcurrent> #include <QDesktopWidget> #include <QDir> #include <QScreen> @@ -43,9 +42,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) - , - - ui(new Ui::MainWindow) + , ui(new Ui::MainWindow) { ui->setupUi(this); @@ -187,17 +184,10 @@ MainWindow::MainWindow(QWidget* parent) }); timer->start(1000); #endif - connect(ui->settingswidget, &SettingsWidget::switchCallWidgetToSettingsWidgetPreview, - this, &MainWindow::slotSwitchVideoWidget); - connect(ui->settingswidget, &SettingsWidget::switchSettingsWidgetPreviewToCallWidget, - this, &MainWindow::slotSwitchVideoWidget); - connect(ui->settingswidget, &SettingsWidget::switchCallWidgetToSettingsWidgetPhotoBooth, - this, &MainWindow::slotSwitchVideoWidget); - connect(ui->settingswidget, &SettingsWidget::switchSettingsWidgetPhotoBoothToCallWidget, - this, &MainWindow::slotSwitchVideoWidget); - connect(ui->settingswidget, &SettingsWidget::videoInputDeviceConnectionLost, - this, &MainWindow::slotSwitchVideoWidget); + // preview renderer is initialized firstly here + previewRenderer_ = PreviewRenderWidget::attachPreview(this); + connect(ui->settingswidget, &SettingsWidget::videoDeviceChanged, this, &MainWindow::slotVideoDeviceChanged); connect(&LRCInstance::accountModel(), &lrc::api::NewAccountModel::accountRemoved, [this](const std::string& accountId) { Q_UNUSED(accountId); @@ -429,69 +419,11 @@ void MainWindow::slotAccountListChanged() } } -void MainWindow::slotSwitchVideoWidget(Utils::VideoWidgetSwapType type) +void MainWindow::slotVideoDeviceChanged(const std::string& device, bool avSettingOrAccountSettingVisible) { - auto convInfo = Utils::getCurrentConvInfo(); - bool isAudioOnly = LRCInstance::getCurrentCallModel()->getCall(convInfo.callId).isAudioOnly; - switch (type) - { - case Utils::VideoWidgetSwapType::CallWidgetToSettingsWidgetPreview: { - // switch local rendering from call to setting preview - ui->callwidget->disconnectRendering(); - ui->settingswidget->connectStartedRenderingToPreview(); - if (isAudioOnly) { - QtConcurrent::run( - [this] { - LRCInstance::avModel().stopPreview(); - LRCInstance::avModel().startPreview(); - }); - break; - } - break; - } - case Utils::VideoWidgetSwapType::CallWidgetToSettingsWidgetPhotoBooth: { - // switch local rendering from call to setting photo booth - ui->callwidget->disconnectRendering(); - ui->settingswidget->connectStartedRenderingToPhotoBooth(); - if (isAudioOnly) { - QtConcurrent::run( - [this] { - LRCInstance::avModel().stopPreview(); - LRCInstance::avModel().startPreview(); - }); - break; - } - break; - } - case Utils::VideoWidgetSwapType::SettingsWidgetPreviewToCallWidget: { - // switch local rendering from setting preview to call - ui->settingswidget->disconnectPreviewRendering(); - if (isAudioOnly) { - QtConcurrent::run([this] { LRCInstance::avModel().stopPreview(); }); - break; - } - ui->callwidget->connectRendering(true); - break; - } - case Utils::VideoWidgetSwapType::SettingsWidgetPhotoBoothToCallWidget: { - // switch local rendering from setting photo booth to call - ui->settingswidget->disconnectPhotoBoothRendering(); - if (isAudioOnly) { - QtConcurrent::run([this] { LRCInstance::avModel().stopPreview(); }); - break; - } - ui->callwidget->connectRendering(true); - break; - } - case Utils::VideoWidgetSwapType::VideoInputDeviceConnectionLost: { - if (isAudioOnly) { - break; - } - ui->callwidget->connectRendering(false); - break; - } - default: { - break; - } - } + Q_UNUSED(device) + ui->callwidget->reconnectRenderingVideoDeviceChanged(); + // if the device is not changed in avSettings, then restart preview manually + if(!avSettingOrAccountSettingVisible) + ui->callwidget->restartPreviewWhenSwitchDevice(); } diff --git a/mainwindow.h b/mainwindow.h index 74d1df7..d4412c4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -22,6 +22,7 @@ #include "settingswidget.h" #include "utils.h" #include "connectivitymonitor.h" +#include "previewrender.h" #include "globalsystemtray.h" #include <QMainWindow> @@ -55,8 +56,7 @@ public: void showWindow(); public slots: - // A slot where covers all cases of video rendering switch between widgets - void slotSwitchVideoWidget(Utils::VideoWidgetSwapType Type); + void slotVideoDeviceChanged(const std::string&, bool); protected: bool nativeEvent(const QByteArray& eventType, void* message, long* result); @@ -96,6 +96,7 @@ private: std::unique_ptr<ConnectivityMonitor> connectivityMonitor_; QMetaObject::Connection screenChangedConnection_; + PreviewRenderWidget* previewRenderer_; QTimer *updateTimer_; }; diff --git a/newwizardwidget.cpp b/newwizardwidget.cpp index d9b7646..fd19c27 100644 --- a/newwizardwidget.cpp +++ b/newwizardwidget.cpp @@ -238,6 +238,7 @@ void NewWizardWidget::changePage(QWidget* toPage) &LRCInstance::accountModel(), &lrc::api::NewAccountModel::registeredNameFound, this, &NewWizardWidget::slotRegisteredNameFound); validateWizardProgression(); + ui->setAvatarWidget->setUpPreviewRenderer(); ui->setAvatarWidget->startBooth(); } else if (toPage == ui->createSIPAccountPage) { ui->SIPusernameEdit->clear(); @@ -248,6 +249,7 @@ void NewWizardWidget::changePage(QWidget* toPage) ui->SIPusernameEdit->setEnabled(true); setNavBarVisibility(true); ui->nextButton->setEnabled(true); + ui->setSIPAvatarWidget->setUpPreviewRenderer(); ui->setSIPAvatarWidget->startBooth(); } else if (toPage == ui->importFromDevicePage) { ui->pinFromDevice->clear(); diff --git a/photoboothwidget.cpp b/photoboothwidget.cpp index 5f2eff6..162b38f 100644 --- a/photoboothwidget.cpp +++ b/photoboothwidget.cpp @@ -20,7 +20,6 @@ #include "photoboothwidget.h" #include "ui_photoboothwidget.h" -#include "settingswidget.h" #include <QFileDialog> #include <QStandardPaths> @@ -37,8 +36,8 @@ PhotoboothWidget::PhotoboothWidget(QWidget *parent) : hasAvatar_(false) { ui->setupUi(this); - ui->videoFeed->setIsFullPreview(true); - ui->videoFeed->setPhotoMode(true); + + previewRenderer_ = PreviewRenderWidget::attachPreview(); flashOverlay_ = new QLabel(this); flashOverlay_->setStyleSheet("background-color:#fff"); @@ -53,6 +52,8 @@ PhotoboothWidget::PhotoboothWidget(QWidget *parent) : flashAnimation_->setEndValue(0); flashAnimation_->setEasingCurve(QEasingCurve::OutCubic); + Utils::drawBlackCircularImageOntoLabel(ui->previewContainer); + ui->previewContainer->hide(); ui->takePhotoButton->setIcon(QIcon(":/images/icons/baseline-camera_alt-24px.svg")); } @@ -66,28 +67,18 @@ void PhotoboothWidget::startBooth(bool isDeviceChanged) { hasAvatar_ = false; - ui->videoFeed->setResetPreview(true); - if (!LRCInstance::getActiveCalls().size() || isDeviceChanged) { + if (!LRCInstance::getActiveCalls().size() || isDeviceChanged || LRCInstance::getIfCurrentSelectedCallIsAudioOnly()) { // if no active calls - ui->videoFeed->connectPreviewOnlyRendering(); + previewRenderer_->connectRendering(); QtConcurrent::run( [this] { LRCInstance::avModel().stopPreview(); LRCInstance::avModel().startPreview(); }); - } else if (settingsPreviewed_) { - // if setting preview is viewed - emit leaveSettingsWidgetPreviewToSettingsWidgetPhotoBooth( - Utils::VideoWidgetSwapType::SettingsWidgetPreviewToSettingsWidgetPhotoBooth); - hasConnection_ = true; - } else { - // call video rendering direct to photo booth - emit enterSettingsWidgetPhotoBoothFromCallWidget( - Utils::VideoWidgetSwapType::CallWidgetToSettingsWidgetPhotoBooth); - hasConnection_ = true; } takePhotoState_ = true; - ui->videoFeed->show(); + ui->previewContainer->show(); + setUpPreviewRenderer(); ui->avatarLabel->hide(); ui->takePhotoButton->setIcon(QIcon(":/images/icons/baseline-camera_alt-24px.svg")); } @@ -95,14 +86,9 @@ PhotoboothWidget::startBooth(bool isDeviceChanged) void PhotoboothWidget::stopBooth() { - if (!LRCInstance::getActiveCalls().size() && takePhotoState_) { + if ((!LRCInstance::getActiveCalls().size() || LRCInstance::getIfCurrentSelectedCallIsAudioOnly()) && takePhotoState_) { // if no active calls QtConcurrent::run([this] { LRCInstance::avModel().stopPreview(); }); - } else if(hasConnection_){ - // if video connection is still on photo booth (now stopBooth will onlt be called once leaving the setting widget) - emit enterCallWidgetFromSettingsWidgetPhotoBooth( - Utils::VideoWidgetSwapType::SettingsWidgetPhotoBoothToCallWidget); - hasConnection_ = false; } resetToAvatarLabel(); } @@ -118,8 +104,6 @@ PhotoboothWidget::on_importButton_clicked() picturesDir, tr("Image Files") + " (*.jpg *.jpeg *.png)"); if (fileName_.isEmpty()) { - ui->videoFeed->connectRendering(); - LRCInstance::avModel().startPreview(); return; } auto image = Utils::cropImage(QImage(fileName_)); @@ -143,8 +127,8 @@ PhotoboothWidget::on_takePhotoButton_clicked() startBooth(); return; } else { - auto videoRect = ui->videoFeed->rect(); - QPoint avatarLabelPos = ui->videoFeed->mapTo(this, videoRect.topLeft()); + auto videoRect = previewRenderer_->rect(); + QPoint avatarLabelPos = previewRenderer_->mapTo(this, videoRect.topLeft()); flashOverlay_->setGeometry( avatarLabelPos.x(), avatarLabelPos.y(), @@ -156,7 +140,7 @@ PhotoboothWidget::on_takePhotoButton_clicked() QtConcurrent::run( [this] { - auto photo = Utils::cropImage(ui->videoFeed->takePhoto()); + auto photo = Utils::cropImage(previewRenderer_->takePhoto()); auto avatar = photo.scaled(224, 224, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); avatarPixmap_ = QPixmap::fromImage(avatar); ui->avatarLabel->setPixmap(QPixmap::fromImage(Utils::getCirclePhoto(avatar, ui->avatarLabel->width()))); @@ -205,23 +189,10 @@ PhotoboothWidget::hasAvatar() return hasAvatar_; } -void -PhotoboothWidget::connectRendering() -{ - // connect only local preview - ui->videoFeed->rendererStartedWithoutDistantRender(); -} - -void -PhotoboothWidget::disconnectRendering() -{ - ui->videoFeed->disconnectRendering(); -} - void PhotoboothWidget::resetToAvatarLabel() { - ui->videoFeed->hide(); + ui->previewContainer->hide(); ui->avatarLabel->show(); takePhotoState_ = false; if (!hasAvatar_) { @@ -230,3 +201,14 @@ PhotoboothWidget::resetToAvatarLabel() ui->takePhotoButton->setIcon(QIcon(":/images/icons/baseline-refresh-24px.svg")); } } + +void +PhotoboothWidget::setUpPreviewRenderer() +{ + previewRenderer_->setParent(ui->previewContainer); + previewRenderer_->setGeometry(ui->previewContainer->rect()); + previewRenderer_->resetBoarder(); + previewRenderer_->setPhotoMode(true); + previewRenderer_->setNeedToCentre(false); + previewRenderer_->show(); +} diff --git a/photoboothwidget.h b/photoboothwidget.h index 3aa6b66..0d0894b 100644 --- a/photoboothwidget.h +++ b/photoboothwidget.h @@ -20,6 +20,7 @@ #pragma once #include "utils.h" +#include "previewrender.h" #include <QWidget> #include <QLabel> @@ -42,21 +43,9 @@ public: void setAvatarPixmap(const QPixmap& avatarPixmap, bool default = false, bool stopPhotoboothPreview = false); const QPixmap& getAvatarPixmap(); bool hasAvatar(); - void connectRendering(); - void disconnectRendering(); - - // hasConnection_ decides whether the video rendering connection is still in photobooth - // (connection transmitted from other video widget) - bool isPhotoBoothOpened() { return takePhotoState_; } - bool isPhotoBoothConnected() { return hasConnection_; } - void resetTakePhotoState(bool state) { takePhotoState_ = state; } - // settingsPreviewed_ changed once the setting's preview is previewed - void setIsSettingsPreviewed(bool state) { settingsPreviewed_ = state; } - -signals: - void enterSettingsWidgetPhotoBoothFromCallWidget(Utils::VideoWidgetSwapType type); - void enterCallWidgetFromSettingsWidgetPhotoBooth(Utils::VideoWidgetSwapType type); - void leaveSettingsWidgetPreviewToSettingsWidgetPhotoBooth(Utils::VideoWidgetSwapType type); + bool isPhotoBoothOpened() { return takePhotoState_; } + void resetTakePhotoState(bool state) { takePhotoState_ = state; } + void setUpPreviewRenderer(); private slots: void on_importButton_clicked(); @@ -68,14 +57,13 @@ private: QString fileName_; Ui::PhotoboothWidget *ui; + PreviewRenderWidget* previewRenderer_; QLabel* flashOverlay_; QPropertyAnimation *flashAnimation_; QPixmap avatarPixmap_; bool hasAvatar_; bool takePhotoState_ { false }; - bool hasConnection_ { false }; - bool settingsPreviewed_ { false }; signals: void photoTaken(); diff --git a/photoboothwidget.ui b/photoboothwidget.ui index 21a95ba..d61b423 100644 --- a/photoboothwidget.ui +++ b/photoboothwidget.ui @@ -103,7 +103,7 @@ </widget> </item> <item> - <widget class="VideoWidget" name="videoFeed" native="true"> + <widget class="QLabel" name="previewContainer"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> @@ -116,11 +116,14 @@ <height>224</height> </size> </property> - <property name="toolTip"> - <string>Photobooth display</string> + <property name="maximumSize"> + <size> + <width>224</width> + <height>224</height> + </size> </property> - <property name="autoFillBackground"> - <bool>true</bool> + <property name="text"> + <string/> </property> </widget> </item> @@ -241,14 +244,6 @@ </item> </layout> </widget> - <customwidgets> - <customwidget> - <class>VideoWidget</class> - <extends>QWidget</extends> - <header>videowidget.h</header> - <container>1</container> - </customwidget> - </customwidgets> <resources> <include location="ressources.qrc"/> </resources> diff --git a/previewrender.cpp b/previewrender.cpp new file mode 100644 index 0000000..1f5f47d --- /dev/null +++ b/previewrender.cpp @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (C) 2015-2019 by Savoir-faire Linux * + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#include "previewrender.h" +#include "ui_previewrender.h" + +#include "lrcinstance.h" +#include "utils.h" + +PreviewRenderWidget::PreviewRenderWidget(QWidget* parent) + : QWidget(parent) + , ui(new Ui::PreviewRenderWidget) +{ + ui->setupUi(this); + + setWindowFlags(Qt::CustomizeWindowHint); + setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TranslucentBackground, true); + + QPalette pal(palette()); + pal.setColor(QPalette::Background, Qt::transparent); + setAutoFillBackground(true); + setPalette(pal); + + connect(LRCInstance::getRenderDistributer(), &RenderDistributer::previewRenderReady, + [this]() { + QMutexLocker lock(&mutex_); + previewRenderer_ = LRCInstance::getRenderDistributer()->getPreviewRenderer(); + previewFrame_ = LRCInstance::getRenderDistributer()->getPreviewFrame(); + }); + connect(LRCInstance::getRenderDistributer(), &RenderDistributer::previewRenderStopped, + [this]() { + QMutexLocker lock(&mutex_); + previewRenderer_ = LRCInstance::getRenderDistributer()->getPreviewRenderer(); + previewFrame_ = LRCInstance::getRenderDistributer()->getPreviewFrame(); + }); +} + +PreviewRenderWidget::~PreviewRenderWidget() +{ + delete ui; +} + +void +PreviewRenderWidget::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + if (previewRenderer_ || (photoMode_ && hasFrame_)) { + QMutexLocker lock(&mutex_); + if (previewRenderer_ + && previewFrame_.storage.size() != 0 + && previewFrame_.storage.size() == (unsigned int)(previewRenderer_->size().height() * previewRenderer_->size().width() * 4)) { + framePreview_ = std::move(previewFrame_.storage); + previewImage_.reset( + new QImage((uchar*)framePreview_.data(), + previewRenderer_->size().width(), + previewRenderer_->size().height(), + QImage::Format_ARGB32_Premultiplied)); + hasFrame_ = true; + if (triggerResetPreviewAfterImageReloaded_) { + resetPreview_ = true; + triggerResetPreviewAfterImageReloaded_ = false; + } + } else { + hasFrame_ = false; + } + if (previewImage_) { + QImage scaledPreview; + int previewHeight = resetPreview_ ? currentConainerHeight_ / 5 : (needToCentre_ ? currentConainerHeight_ : height()); + int previewWidth = resetPreview_ ? currentConainerWidth_ / 5 : (needToCentre_ ? currentConainerWidth_ : width()); + if (photoMode_) { + scaledPreview = Utils::getCirclePhoto(*previewImage_, previewHeight); + } else { + scaledPreview = previewImage_->scaled(previewWidth, + previewHeight, + Qt::KeepAspectRatio); + } + if (isroundedBoarder_) { + scaledPreview = Utils::getRoundedEdgePhoto(scaledPreview, scaledPreview.width(), scaledPreview.height(), 10); + } + previewGeometry_.setRect(0, 0, scaledPreview.width(), scaledPreview.height()); + + QRect resetGeo(previewGeometry_); + if (resetPreview_) { + resetGeo.moveTo(currentConainerWidth_ - resetGeo.width() - previewMargin_, + currentConainerHeight_ - resetGeo.height() - previewMargin_); + resetPreview_ = false; + } else if (needToCentre_) { + auto centreX = (currentConainerWidth_ - resetGeo.width()) / 2; + auto centreY = (currentConainerHeight_ - resetGeo.height()) / 2; + resetGeo.moveTo(centreX, centreY); + } else { + resetGeo.moveTo(this->x(), this->y()); + } + this->setGeometry(resetGeo); + painter.drawImage(previewGeometry_, scaledPreview); + } + } + painter.end(); +} + +void +PreviewRenderWidget::connectRendering() +{ + LRCInstance::getRenderDistributer()->connectPreviewRendering(); +} + +QImage +PreviewRenderWidget::takePhoto() +{ + if (previewImage_) { + return previewImage_.get()->copy(); + } + return QImage(); +} + +void +PreviewRenderWidget::setPhotoMode(bool isPhotoMode) +{ + photoMode_ = isPhotoMode; +} + +void +PreviewRenderWidget::changeToRoundedBoarder() { + isroundedBoarder_ = true; +} + +void +PreviewRenderWidget::resetBoarder() +{ + isroundedBoarder_ = false; +} diff --git a/previewrender.h b/previewrender.h new file mode 100644 index 0000000..619c559 --- /dev/null +++ b/previewrender.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2015-2019 by Savoir-faire Linux * + * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + **************************************************************************/ + +#pragma once + +#include <QWidget> +#include <QPainter> +#include <QPaintEvent> +#include <QPainterPath> +#include <QMutex> + +#include <memory> +#include <array> + +#include "lrcinstance.h" + +namespace Ui { +class PreviewRenderWidget; +} + +class PreviewRenderWidget : public QWidget { + Q_OBJECT; + +public: + static PreviewRenderWidget* attachPreview(QWidget* parent = 0) { + static PreviewRenderWidget* instance_ = new PreviewRenderWidget(parent); + return instance_; + } + ~PreviewRenderWidget(); + + // rounded boarder for preview in calls + void setRoundedBoarder(bool rounded) { isroundedBoarder_ = rounded; } + // reset preview when in resize event + void resetPreview() { resetPreview_ = true; } + // set current container geometry for preview renderer + void setCurrentConainerGeo(int width, int height) { currentConainerHeight_ = height; currentConainerWidth_ = width; } + // centre for preview in avsettings + void setNeedToCentre(bool need) { needToCentre_ = need; } + // only trigger preview reset when image is reloaded + void triggerResetPreviewAfterImageReloaded() { triggerResetPreviewAfterImageReloaded_ = true; } + QImage takePhoto(); + void setPhotoMode(bool isPhotoMode); + void connectRendering(); + void changeToRoundedBoarder(); + void resetBoarder(); + QRect getPreviewRect() { return previewGeometry_; } + // force the preview to repaint + void forceRepaint() { paintEvent(&QPaintEvent(QRect(0, 0, 0, 0))); } + + constexpr static int previewMargin_ = 15; + constexpr static int boarderRadius_ = 39; + +private: + Ui::PreviewRenderWidget* ui; + + explicit PreviewRenderWidget(QWidget* parent = 0); + + struct previewRendererConnections { + QMetaObject::Connection started, stopped, updated; + } previewRendererConnections_; + + QMutex mutex_; + + bool photoMode_ { false }; + bool hasFrame_ { false }; + bool isroundedBoarder_ { false }; + bool resetPreview_ { false }; + bool needToCentre_ { false }; + bool triggerResetPreviewAfterImageReloaded_ { false }; + QRect previewGeometry_; + int currentConainerHeight_ { 0 }; + int currentConainerWidth_ { 0 }; + + lrc::api::video::Renderer* previewRenderer_; + lrc::api::video::Frame previewFrame_; + std::unique_ptr<QImage> previewImage_; + std::vector<uint8_t> framePreview_; + +protected: + void paintEvent(QPaintEvent* e); +}; diff --git a/previewrender.ui b/previewrender.ui new file mode 100644 index 0000000..9fc7dcc --- /dev/null +++ b/previewrender.ui @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreviewRenderWidget</class> + <widget class="QWidget" name="PreviewRenderWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ring-client-windows.vcxproj b/ring-client-windows.vcxproj index 87f8c13..ab31ded 100644 --- a/ring-client-windows.vcxproj +++ b/ring-client-windows.vcxproj @@ -244,7 +244,9 @@ del /s /q $(OutDir)\Jami.exp</Command> <ClCompile Include="currentaccountcombobox.cpp" /> <ClCompile Include="aboutdialog.cpp" /> <ClCompile Include="levelmeter.cpp" /> + <ClCompile Include="lrcinstance.cpp" /> <ClCompile Include="overlaybutton.cpp" /> + <ClCompile Include="previewrender.cpp" /> <ClCompile Include="sipinputpanel.cpp" /> <ClCompile Include="splashscreen.cpp" /> <ClCompile Include="updatedownloaddialog.cpp" /> @@ -483,6 +485,10 @@ del /s /q $(OutDir)\Jami.exp</Command> <Define Condition="'$(Configuration)|$(Platform)'=='Release|x64'">_WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;NIGHTLY_VERSION=20180706;ENABLE_AUTOUPDATE;QT_NO_DEBUG;NDEBUG;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB</Define> <IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> </QtMoc> + <QtMoc Include="previewrender.h"> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + <IncludePath Condition="'$(Configuration)|$(Platform)'=='ReleaseCompile|x64'">.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;$(ProjectDir)..\ring-daemon\contrib\msvc\include;$(ProjectDir)..\daemon\contrib\msvc\include;$(ProjectDir)..\ring-lrc\src;$(ProjectDir)..\lrc\src;$(ProjectDir)winsparkle\include;$(ProjectDir)qrencode-win32\qrencode-win32;$(QTDIR)\include;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtXml;$(QTDIR)\include\QtNetwork;$(QTDIR)\include\QtWebEngineWidgets;$(QTDIR)\include\QtWebChannel;$(QTDIR)\include\QtCore;$(QTDIR)\mkspecs\win32-msvc;.\release</IncludePath> + </QtMoc> <ClInclude Include="ringthemeutils.h" /> <QtMoc Include="selectareadialog.h"> </QtMoc> @@ -659,6 +665,7 @@ del /s /q $(OutDir)\Jami.exp</Command> <QtUic Include="callwidget.ui"> <SubType>Designer</SubType> </QtUic> + <QtUic Include="previewrender.ui" /> <QtUic Include="sipkeypad.ui" /> <QtUic Include="updatedownloaddialog.ui" /> <QtUic Include="contactpicker.ui" /> diff --git a/ring-client-windows.vcxproj.filters b/ring-client-windows.vcxproj.filters index 9f1315f..8554093 100644 --- a/ring-client-windows.vcxproj.filters +++ b/ring-client-windows.vcxproj.filters @@ -234,6 +234,12 @@ <ClCompile Include="connectivitymonitor.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="previewrender.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="lrcinstance.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <QtMoc Include="aboutdialog.h"> @@ -404,12 +410,8 @@ <QtMoc Include="overlaybutton.h"> <Filter>Header Files</Filter> </QtMoc> - <QtMoc Include="accountmigrationdialog.h"> - <Filter>Header Files</Filter> - </QtMoc> - <QtMoc Include="connectivitymonitor.h"> - <Filter>Header Files</Filter> - </QtMoc> +==== BASE ==== +==== BASE ==== </ItemGroup> <ItemGroup> <CustomBuild Include="debug\moc_predefs.h.cbt"> @@ -704,6 +706,9 @@ <QtUic Include="accountmigration.ui"> <Filter>Form Files</Filter> </QtUic> + <QtUic Include="previewrender.ui"> + <Filter>Form Files</Filter> + </QtUic> </ItemGroup> <ItemGroup> <None Include="images\FontAwesome.otf"> @@ -864,4 +869,4 @@ <Filter>Resource Files</Filter> </Image> </ItemGroup> -</Project> \ No newline at end of file +</Project> diff --git a/settingswidget.cpp b/settingswidget.cpp index 19e591d..8420888 100644 --- a/settingswidget.cpp +++ b/settingswidget.cpp @@ -130,6 +130,10 @@ SettingsWidget::SettingsWidget(QWidget* parent) connect(ui->mediaSettingsButton, &QPushButton::clicked, [this]() { setSelected(Button::mediaSettingsButton); }); + /*connect(ui->currentSIPAccountAvatar, &QPushButton::clicked, [this]() { + avatarClicked(); + });*/ + connect(ui->advancedAccountSettingsSIPButton, &QPushButton::clicked, this, &SettingsWidget::toggleAdvancedSIPSettings); // connect "delete SIP account" button @@ -250,39 +254,11 @@ SettingsWidget::SettingsWidget(QWidget* parent) connect(ui->hardwareAccelCheckBox, &QAbstractButton::clicked, this, &SettingsWidget::slotSetHardwareAccel); - connect(ui->currentAccountAvatar, &PhotoboothWidget::enterSettingsWidgetPhotoBoothFromCallWidget, - this, &SettingsWidget::photoBoothEnterReceived); - - connect(ui->currentSIPAccountAvatar, &PhotoboothWidget::enterSettingsWidgetPhotoBoothFromCallWidget, - this, &SettingsWidget::photoBoothEnterReceived); - - connect(ui->currentAccountAvatar, &PhotoboothWidget::enterCallWidgetFromSettingsWidgetPhotoBooth, - this, &SettingsWidget::photoBoothLeaveReceived); - - connect(ui->currentSIPAccountAvatar, &PhotoboothWidget::enterCallWidgetFromSettingsWidgetPhotoBooth, - this, &SettingsWidget::photoBoothLeaveReceived); - - connect(&LRCInstance::avModel(),&lrc::api::AVModel::deviceEvent, [this] { videoDeviceEventHandlerAndMediaSettingSetUp(); }); - - connect(this, &SettingsWidget::settingWidgetPhotoBoothTosettingWidgetPreviewSignal, - [this]() { - this->disconnectPhotoBoothRendering(); - this->connectStartedRenderingToPreview(); - }); - - connect(ui->currentAccountAvatar, &PhotoboothWidget::leaveSettingsWidgetPreviewToSettingsWidgetPhotoBooth, - [this]() { - this->disconnectPreviewRendering(); - this->connectStartedRenderingToPhotoBooth(); - }); - - connect(ui->currentSIPAccountAvatar, &PhotoboothWidget::leaveSettingsWidgetPreviewToSettingsWidgetPhotoBooth, - [this]() { - this->disconnectPreviewRendering(); - this->connectStartedRenderingToPhotoBooth(); - }); + connect(&LRCInstance::avModel(), &lrc::api::AVModel::deviceEvent, [this] { videoDeviceEventHandlerAndMediaSettingSetUp(); }); ui->containerWidget->setVisible(false); + + previewRenderer_ = PreviewRenderWidget::attachPreview(); } void SettingsWidget::slotAccountListChanged() @@ -327,21 +303,12 @@ void SettingsWidget::leaveSettingsSlot() bool isSIP = LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::SIP; auto photoBooth = isSIP ? ui->currentSIPAccountAvatar : ui->currentAccountAvatar; - if (LRCInstance::getActiveCalls().size()) { + QtConcurrent::run([this, &photoBooth] { photoBooth->stopBooth(); - bool isPhotoBoothConnected = photoBooth->isPhotoBoothConnected(); - // reset setting preview is not viewed - photoBooth->setIsSettingsPreviewed(false); - // check if is previewed and photo booth does not have video rendering connection - if (previewed_ && !isPhotoBoothConnected) { - emit switchSettingsWidgetPreviewToCallWidget(Utils::VideoWidgetSwapType::SettingsWidgetPreviewToCallWidget); - previewed_ = false; - } - } else { - QtConcurrent::run([this, &photoBooth] { - photoBooth->stopBooth(); - }); - } + if (isPreviewed_) + stopPreviewing(); + }); + emit NavigationRequested(ScreenEnum::CallScreen); } @@ -349,7 +316,6 @@ SettingsWidget::~SettingsWidget() { delete ui; } - void SettingsWidget::setSelected(Button sel) { switch (sel) { @@ -373,20 +339,12 @@ void SettingsWidget::setSelected(Button sel) toggleAdvancedSIPSettings(); } pastAccount_ = lrc::api::profile::Type::SIP; - // notify photo booth that setting preview is previewed - if (pastButton_ != Button::accountSettingsButton && previewed_) { - ui->currentSIPAccountAvatar->setIsSettingsPreviewed(previewed_); - } } else { ui->stackedWidget->setCurrentWidget(ui->currentAccountSettingsScrollWidget); if (advancedSettingsDropped_) { toggleAdvancedSettings(); } pastAccount_ = lrc::api::profile::Type::RING; - // notify photo booth that setting preview is previewed - if (pastButton_ != Button::accountSettingsButton && previewed_) { - ui->currentAccountAvatar->setIsSettingsPreviewed(previewed_); - } } break; @@ -427,7 +385,6 @@ void SettingsWidget::setSelected(Button sel) pastButton_ = sel; } - // called to update current settings information when navigating to settingsWidget void SettingsWidget::updateAccountInfoDisplayed() { @@ -1058,6 +1015,14 @@ void SettingsWidget::populateAVSettings() connect(ui->outputComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsWidget::outputDevIndexChangedSlot); + previewRenderer_->setParent(ui->previewWidgetContainer); + previewRenderer_->setGeometry(ui->previewWidgetContainer->rect()); + previewRenderer_->setCurrentConainerGeo(ui->previewWidgetContainer->width(), ui->previewWidgetContainer->height()); + previewRenderer_->resetBoarder(); + previewRenderer_->setPhotoMode(false); + previewRenderer_->setNeedToCentre(true); + previewRenderer_->show(); + // video videoDeviceEventHandlerAndMediaSettingSetUp(); @@ -1089,6 +1054,7 @@ void SettingsWidget::slotDeviceBoxCurrentIndexChanged(int index) LRCInstance::avModel().setCurrentVideoCaptureDevice(device); LRCInstance::avModel().setDefaultDevice(device); setFormatListForDevice(device); + emit videoDeviceChanged(device, true); startPreviewing(true); } @@ -1099,38 +1065,21 @@ void SettingsWidget::slotFormatBoxCurrentIndexChanged(int index) auto decive = LRCInstance::avModel().getCurrentVideoCaptureDevice(); auto currentSettings = LRCInstance::avModel().getDeviceSettings(decive); lrc::api::video::Settings settings{ {}, decive, rate, resolution }; - ui->videoWidget->connectRendering(); + previewRenderer_->connectRendering(); LRCInstance::avModel().setDeviceSettings(settings); } void SettingsWidget::startPreviewing(bool isDeviceChanged) { - ui->videoWidget->disconnectRendering(); - ui->videoWidget->connectPreviewOnlyRendering(); - ui->videoWidget->setIsFullPreview(true); - - bool isPhotoBoothConnected; - if (LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::SIP) { - isPhotoBoothConnected = ui->currentSIPAccountAvatar->isPhotoBoothConnected(); - } else { - isPhotoBoothConnected = ui->currentAccountAvatar->isPhotoBoothConnected(); - } - - if (!LRCInstance::getActiveCalls().size() || isDeviceChanged) { + if (!LRCInstance::getActiveCalls().size() || isDeviceChanged || LRCInstance::getIfCurrentSelectedCallIsAudioOnly()) { // if no active calls, or device is changed -> reactive preview + previewRenderer_->connectRendering(); QtConcurrent::run( [this] { LRCInstance::avModel().stopPreview(); LRCInstance::avModel().startPreview(); }); - } - else if (pastButton_ != Button::mediaSettingsButton && isPhotoBoothConnected) { - // if photo booth is opened before - emit settingWidgetPhotoBoothTosettingWidgetPreviewSignal(Utils::VideoWidgetSwapType::SettingsWidgetPhotoBoothToSettingsWidgetPreview); - previewed_ = true; - } else { - emit switchCallWidgetToSettingsWidgetPreview(Utils::VideoWidgetSwapType::CallWidgetToSettingsWidgetPreview); - previewed_ = true; + isPreviewed_ = true; } ui->previewUnavailableLabel->hide(); ui->videoLayoutWidget->show(); @@ -1138,9 +1087,10 @@ void SettingsWidget::startPreviewing(bool isDeviceChanged) void SettingsWidget::stopPreviewing() { - if (!LRCInstance::getActiveCalls().size()) { + if (!LRCInstance::getActiveCalls().size() || LRCInstance::getIfCurrentSelectedCallIsAudioOnly()) { QtConcurrent::run( [this] { LRCInstance::avModel().stopPreview(); }); } + isPreviewed_ = false; } void SettingsWidget::toggleVideoSettings(bool enabled) @@ -1221,44 +1171,6 @@ void SettingsWidget::stopAudioMeter(bool blocking) blocking ? f() : QtConcurrent::run(f); } -void SettingsWidget::connectStartedRenderingToPreview() -{ - ui->videoWidget->rendererStartedWithoutDistantRender(); -} - -void SettingsWidget::connectStartedRenderingToPhotoBooth() -{ - if (LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::SIP) { - ui->currentSIPAccountAvatar->connectRendering(); - } else { - ui->currentAccountAvatar->connectRendering(); - } -} - -void SettingsWidget::disconnectPreviewRendering() -{ - ui->videoWidget->disconnectRendering(); -} - -void SettingsWidget::disconnectPhotoBoothRendering() -{ - if (LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::SIP) { - ui->currentSIPAccountAvatar->disconnectRendering(); - } else { - ui->currentAccountAvatar->disconnectRendering(); - } -} - -void SettingsWidget::photoBoothEnterReceived(Utils::VideoWidgetSwapType Type) -{ - emit switchCallWidgetToSettingsWidgetPhotoBooth(Type); -} - -void SettingsWidget::photoBoothLeaveReceived(Utils::VideoWidgetSwapType Type) -{ - emit switchSettingsWidgetPhotoBoothToCallWidget(Type); -} - void SettingsWidget::resetPhotoBoothStateWhenSettingChanged(Button type) { bool stopPhotoboothPreview = false; @@ -1377,18 +1289,12 @@ void SettingsWidget::videoDeviceEventHandlerAndMediaSettingSetUp() if (shouldRestart) { // if we are in photo booth open state and in account setting widget, // reset the photo booth video + emit videoDeviceChanged(LRCInstance::avModel().getDefaultDeviceName(), true); photoBooth->startBooth(true); } else if (shouldReinitializePreview && (ui->avSettings->isVisible() || LRCInstance::getActiveCalls().size())) { // if shouldReinitializePreview && has active calls -> videoInputDeviceLoseConnection to refersh video input - if (LRCInstance::getActiveCalls().size() && !ui->avSettings->isVisible()) { - emit videoInputDeviceConnectionLost(Utils::VideoWidgetSwapType::VideoInputDeviceConnectionLost); - QtConcurrent::run( - [this] { - LRCInstance::avModel().stopPreview(); - LRCInstance::avModel().startPreview(); - }); - return; - } - startPreviewing(true); + emit videoDeviceChanged(LRCInstance::avModel().getDefaultDeviceName(), ui->avSettings->isVisible()); + if(ui->avSettings->isVisible()) + startPreviewing(true); } } diff --git a/settingswidget.h b/settingswidget.h index 541385d..5188fc1 100644 --- a/settingswidget.h +++ b/settingswidget.h @@ -30,6 +30,7 @@ #include "bannedlistmodel.h" #include "linkdevicedialog.h" #include "photoboothwidget.h" +#include "previewrender.h" #include "api/datatransfermodel.h" #include "typedefs.h" @@ -46,28 +47,14 @@ public: explicit SettingsWidget(QWidget* parent = nullptr); ~SettingsWidget(); - void connectStartedRenderingToPreview(); - void connectStartedRenderingToPhotoBooth(); - void disconnectPreviewRendering(); - void disconnectPhotoBoothRendering(); - // NavWidget virtual void navigated(bool to); virtual void updateCustomUI(); public slots: virtual void slotAccountListChanged(); - void photoBoothEnterReceived(Utils::VideoWidgetSwapType Type); - void photoBoothLeaveReceived(Utils::VideoWidgetSwapType Type); - signals: - void switchCallWidgetToSettingsWidgetPreview(Utils::VideoWidgetSwapType type); - void switchCallWidgetToSettingsWidgetPhotoBooth(Utils::VideoWidgetSwapType type); - void switchSettingsWidgetPreviewToCallWidget(Utils::VideoWidgetSwapType type); - void switchSettingsWidgetPhotoBoothToCallWidget(Utils::VideoWidgetSwapType type); - void settingWidgetPhotoBoothTosettingWidgetPreviewSignal(Utils::VideoWidgetSwapType type); - void settingWidgetPreviewTosettingWidgetPhotoBoothSignal(Utils::VideoWidgetSwapType type); - void videoInputDeviceConnectionLost(Utils::VideoWidgetSwapType type); + void videoDeviceChanged(const std::string&, bool avSettingOrAccountSettingVisible = false); private: Ui::SettingsWidget* ui; @@ -125,9 +112,10 @@ private: int avatarSIPSize_; bool regNameBtn_ = false; const int itemHeight_ = 55; - bool previewed_ {false}; int previousDeviceSize_ { static_cast<int>(LRCInstance::avModel().getDevices().size()) }; bool deviceWasEmpty_ { false }; + PreviewRenderWidget* previewRenderer_; + bool isPreviewed_ { false }; QMovie* lookupSpinnerMovie_; QPixmap statusSuccessPixmap_; @@ -168,7 +156,6 @@ private slots: void videoDeviceEventHandlerAndMediaSettingSetUp(); public: - bool getIsPreviewed() { return previewed_; } Button getPreviousButton() { return pastButton_; } }; diff --git a/settingswidget.ui b/settingswidget.ui index 9c86afd..5db9b39 100644 --- a/settingswidget.ui +++ b/settingswidget.ui @@ -1416,8 +1416,8 @@ <rect> <x>0</x> <y>0</y> - <width>749</width> - <height>1387</height> + <width>706</width> + <height>1398</height> </rect> </property> <property name="styleSheet"> @@ -2740,7 +2740,7 @@ <x>0</x> <y>0</y> <width>683</width> - <height>864</height> + <height>874</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayoutsip_12" stretch="0"> @@ -4077,7 +4077,7 @@ <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> - <enum>QSizePolicy::MinimumExpanding</enum> + <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> @@ -4088,7 +4088,7 @@ </spacer> </item> <item> - <widget class="VideoWidget" name="videoWidget" native="true"> + <widget class="QWidget" name="previewWidgetContainer" native="true"> <property name="enabled"> <bool>true</bool> </property> @@ -4100,13 +4100,13 @@ </property> <property name="minimumSize"> <size> - <width>510</width> + <width>400</width> <height>224</height> </size> </property> <property name="maximumSize"> <size> - <width>16777215</width> + <width>400</width> <height>16777215</height> </size> </property> @@ -4124,7 +4124,7 @@ <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> - <enum>QSizePolicy::MinimumExpanding</enum> + <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> @@ -4232,12 +4232,6 @@ <header>photoboothwidget.h</header> <container>1</container> </customwidget> - <customwidget> - <class>VideoWidget</class> - <extends>QWidget</extends> - <header>videowidget.h</header> - <container>1</container> - </customwidget> <customwidget> <class>AdvancedSettingsWidget</class> <extends>QWidget</extends> diff --git a/stylesheet.css b/stylesheet.css index 7e74fbf..113c775 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -54,6 +54,10 @@ QWidget#smartListOuterWidget { background-color: rgb(255, 255, 255); } +QWidget#previewContainer { + background-color: black; +} + QPushButton#btnConversations, QPushButton#btnInvites { border-radius: 0px; border-top-left-radius: 15px; diff --git a/utils.cpp b/utils.cpp index 7cbb452..00ea955 100644 --- a/utils.cpp +++ b/utils.cpp @@ -30,6 +30,7 @@ #endif //Qt +#include <QBitmap> #include <QObject> #include <QErrorMessage> #include <QPainter> @@ -261,6 +262,47 @@ Utils::getCirclePhoto(const QImage original, int sizePhoto) return target; } +QImage +Utils::getRoundedEdgePhoto(const QImage original, int widthPhoto, int heightPhoto, int roundness) +{ + // First, create a transparent image with the same size + QImage target(widthPhoto, heightPhoto, QImage::Format_ARGB32_Premultiplied); + target.fill(Qt::transparent); + + //Second, create a painter onto that image + QPainter painter(&target); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter.setBrush(QBrush(original)); + // set brush to the image that we want to draw + // Note that, the Qbrush is in default texture mode + + //Third, scale the original image into the size of the image we created + auto scaledPhoto = original + .scaled(widthPhoto, heightPhoto, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation) + .convertToFormat(QImage::Format_ARGB32_Premultiplied); + painter.drawRoundedRect(0, 0, widthPhoto, heightPhoto, roundness, roundness); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + // Source-In mode and Draw the image onto the transperant image + painter.drawImage(0, 0, scaledPhoto, 0, 0); + return target; +} + +void +Utils::drawBlackCircularImageOntoLabel(QLabel* containerWidget) +{ + // Widget is black, fill image with white + // draw a black cycle onto it + QImage target(containerWidget->width(), containerWidget->height(), QImage::Format_ARGB32_Premultiplied); + target.fill(Qt::white); + + QPainter painter(&target); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter.setBrush(QBrush(Qt::black)); + painter.drawEllipse(containerWidget->x(), containerWidget->y(), containerWidget->width(), containerWidget->height()); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + containerWidget->setPixmap(QPixmap::fromImage(target)); +} + void Utils::setStackWidget(QStackedWidget* stack, QWidget* widget) { diff --git a/utils.h b/utils.h index c02cdac..b5e641e 100644 --- a/utils.h +++ b/utils.h @@ -25,6 +25,7 @@ #include <QString> #include <QImage> +#include <QLabel> #include <QStackedWidget> #include <QTextDocument> #include <QItemDelegate> @@ -35,10 +36,8 @@ #ifdef Q_OS_WIN #include <windows.h> #include <ciso646> -#undef OUT -#undef IN #undef ERROR -#else //LINUX +#else #define LPCWSTR char* #endif @@ -67,6 +66,7 @@ void setStackWidget(QStackedWidget *stack, QWidget *widget); void showSystemNotification(QWidget* widget, const QString& message, long delay = 5000); void showSystemNotification(QWidget* widget, const QString& sender, const QString& message, long delay = 5000); QSize getRealSize(QScreen* screen); +void drawBlackCircularImageOntoLabel(QLabel* containerWidget); // updates void cleanUpdateFiles(); @@ -92,6 +92,7 @@ bool isContactValid(const std::string& contactUid, const lrc::api::ConversationM // image QImage getCirclePhoto(const QImage original, int sizePhoto); +QImage getRoundedEdgePhoto(const QImage original, int widthPhoto, int heightPhoto, int roundness); QImage conversationPhoto(const std::string& convUid, const lrc::api::account::Info& accountInfo); QColor getAvatarColor(const QString& canonicalUri); QImage fallbackAvatar(const QSize size, const QString& canonicalUriStr, const QString& letterStr = QString()); @@ -116,18 +117,6 @@ void swapQListWidgetItems(QListWidget* list, bool down = true); // Byte to human readable size QString humanFileSize(qint64 fileSize); -// Video widget change enum -enum class VideoWidgetSwapType -{ - CallWidgetToSettingsWidgetPreview, - CallWidgetToSettingsWidgetPhotoBooth, - SettingsWidgetPreviewToCallWidget, - SettingsWidgetPhotoBoothToCallWidget, - SettingsWidgetPhotoBoothToSettingsWidgetPreview, - SettingsWidgetPreviewToSettingsWidgetPhotoBooth, - VideoInputDeviceConnectionLost -}; - // device plug or unplug enum enum class DevicePlugStatus { diff --git a/videoview.cpp b/videoview.cpp index cb45868..714d26e 100644 --- a/videoview.cpp +++ b/videoview.cpp @@ -69,7 +69,13 @@ VideoView::VideoView(QWidget* parent) emit this->setChatVisibility(visible); connect(this, SIGNAL(toggleFullScreenClicked()), ui->videoWidget, SLOT(slotToggleFullScreenClicked())); }); + audioOnlyAvatar_ = new CallAudioOnlyAvatarOverlay(this); + previewRenderer_ = PreviewRenderWidget::attachPreview(); + + moveAnim_ = new QPropertyAnimation(previewRenderer_, "geometry"); + moveAnim_->setDuration(100); + moveAnim_->setEasingCurve(QEasingCurve::InOutQuad); } VideoView::~VideoView() @@ -82,42 +88,12 @@ VideoView::~VideoView() void VideoView::resizeEvent(QResizeEvent* event) { - int marginWidth = ui->videoWidget->getPreviewMargin(); - QRect& previewRect = ui->videoWidget->getPreviewRect(); - int deltaW = event->size().width() - event->oldSize().width(); - int deltaH = event->size().height() - event->oldSize().height(); - - QPoint previewCenter = ui->videoWidget->getPreviewRect().center(); - int cx = (event->oldSize().width()) / 2; - int cy = (event->oldSize().height()) / 2; - QPoint center = QPoint(cx, cy); - - // first we check if we want to displace the preview - if (previewRect.x() + deltaW > 0 && previewRect.y() + deltaH > 0) { - // then we check which way - if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() < 0) - ui->videoWidget->getPreviewRect().translate(deltaW, deltaH); - else if (center.x() - previewCenter.x() > 0 && center.y() - previewCenter.y() < 0) - ui->videoWidget->getPreviewRect().translate(0, deltaH); - else if (center.x() - previewCenter.x() < 0 && center.y() - previewCenter.y() > 0) - ui->videoWidget->getPreviewRect().translate(deltaW, 0); - } - - if (previewRect.left() <= 0) - previewRect.moveLeft(marginWidth); - previewRect.moveRight(width() - marginWidth); - - if (previewRect.right() >= width()) - previewRect.moveRight(width() - marginWidth); - - if (previewRect.top() <= 0) - previewRect.moveTop(marginWidth); - previewRect.moveBottom(height() - marginWidth); - - if (previewRect.bottom() >= height()) - previewRect.moveBottom(height() - marginWidth); - - ui->videoWidget->resetPreview(); + moveAnim_->stop(); + previewRenderer_->setCurrentConainerGeo(event->size().width(), event->size().height()); + previewRenderer_->resetPreview(); + // force preview to repaint since the geo may be changed to hide + // the preview renderer + previewRenderer_->forceRepaint(); audioOnlyAvatar_->resize(this->size()); @@ -274,10 +250,17 @@ VideoView::showContextMenu(const QPoint& pos) } connect(deviceAction, &QAction::triggered, [this, deviceName, thisCallId]() { + previewRenderer_->hide(); + previewRenderer_->setCurrentConainerGeo(this->width(), this->height()); + // since there is the possiblity of image not reloaded properly + // after rendering reconnect + previewRenderer_->triggerResetPreviewAfterImageReloaded(); + previewRenderer_->connectRendering(); + ui->videoWidget->connectDistantRendering(); + auto decive = deviceName.toStdString(); LRCInstance::avModel().switchInputTo(decive); LRCInstance::avModel().setCurrentVideoCaptureDevice(decive); - ui->videoWidget->connectRendering(); }); } @@ -367,19 +350,20 @@ VideoView::pushRenderer(const std::string& callId, bool isSIP) callStatusChangedConnection_ = QObject::connect(callModel, &lrc::api::NewCallModel::callStatusChanged, this, &VideoView::slotCallStatusChanged); - ui->videoWidget->connectRendering(); - ui->videoWidget->setPreviewDisplay(call.type != lrc::api::call::Type::CONFERENCE); + previewRenderer_->connectRendering(); + ui->videoWidget->connectDistantRendering(); } void VideoView::mousePressEvent(QMouseEvent* event) { QPoint clickPosition = event->pos(); - if (ui->videoWidget->getPreviewRect().contains(clickPosition)) { - QLine distance = QLine(clickPosition, ui->videoWidget->getPreviewRect().bottomRight()); - originMouseDisplacement_ = event->pos() - ui->videoWidget->getPreviewRect().topLeft(); - QApplication::setOverrideCursor(Qt::SizeAllCursor); - draggingPreview_ = true; + if (previewRenderer_->geometry().contains(clickPosition)) { + QLine distance = QLine(clickPosition, previewRenderer_->geometry().bottomRight()); + originMouseDisplacement_ = event->pos() - previewRenderer_->geometry().topLeft(); + QApplication::setOverrideCursor(Qt::SizeAllCursor); + draggingPreview_ = true; + moveAnim_->stop(); } } @@ -389,27 +373,35 @@ VideoView::mouseReleaseEvent(QMouseEvent* event) Q_UNUSED(event) if (draggingPreview_) { //Check preview's current central position - QRect& previewRect = ui->videoWidget->getPreviewRect(); - auto previewCentral = previewRect.center(); + auto previewCentral = previewRenderer_->geometry().center(); auto videoViewRect = ui->videoWidget->rect(); auto videoWidgetCentral = videoViewRect.center(); + auto previewInitialWidth = previewRenderer_->width(); + auto previewInitialHeight = previewRenderer_->height(); + moveAnim_->setStartValue(previewRenderer_->geometry()); if (previewCentral.x() >= videoWidgetCentral.x()) { if (previewCentral.y() >= videoWidgetCentral.y()) { //Move preview to bottom right - ui->videoWidget->movePreview(VideoWidget::TargetPointPreview::bottomRight); + + moveAnim_->setEndValue(QRect(this->width() - previewMargin_ - previewInitialWidth, this->height() - previewMargin_ - previewInitialHeight, + previewInitialWidth, previewInitialHeight)); } else { //Move preview to top right - ui->videoWidget->movePreview(VideoWidget::TargetPointPreview::topRight); + moveAnim_->setEndValue(QRect(this->width() - previewMargin_ - previewInitialWidth, previewMargin_, + previewInitialWidth, previewInitialHeight)); } } else { if (previewCentral.y() >= videoWidgetCentral.y()) { //Move preview to bottom left - ui->videoWidget->movePreview(VideoWidget::TargetPointPreview::bottomLeft); + moveAnim_->setEndValue(QRect(previewMargin_, this->height() - previewMargin_ - previewInitialHeight, + previewInitialWidth, previewInitialHeight)); } else { //Move preview to top left - ui->videoWidget->movePreview(VideoWidget::TargetPointPreview::topLeft); + moveAnim_->setEndValue(QRect(previewMargin_, previewMargin_, + previewInitialWidth, previewInitialHeight)); } } + moveAnim_->start(); } draggingPreview_ = false; @@ -426,7 +418,7 @@ VideoView::mouseMoveEvent(QMouseEvent* event) fadeTimer_.start(startfadeOverlayTime_); } - QRect& previewRect = ui->videoWidget->getPreviewRect(); + QRect previewRect = previewRenderer_->geometry(); if (draggingPreview_) { if (previewRect.left() > 0 && previewRect.top() > 0 @@ -448,7 +440,8 @@ VideoView::mouseMoveEvent(QMouseEvent* event) } } - QLine distance = QLine(previewRect.topLeft(), event->pos()); + previewRenderer_->setGeometry(previewRect); + previewRenderer_->forceRepaint(); } void @@ -492,22 +485,6 @@ VideoView::slotHoldStatusChanged(bool pauseLabelStatus) audioOnlyAvatar_->respondToPauseLabel(pauseLabelStatus); } -void -VideoView::disconnectRendering() -{ - ui->videoWidget->disconnectRendering(); -} - -void -VideoView::connectRendering(bool started) -{ - if (started) { - ui->videoWidget->slotRendererStarted(); - } else { - ui->videoWidget->connectRendering(); - } -} - void VideoView::keyPressEvent(QKeyEvent* event) { @@ -530,3 +507,9 @@ VideoView::keyReleaseEvent(QKeyEvent* event) } QWidget::keyReleaseEvent(event); } + +void +VideoView::reconnectRenderingVideoDeviceChanged() +{ + ui->videoWidget->connectDistantRendering(); +} diff --git a/videoview.h b/videoview.h index 1d8c779..de5fc7c 100644 --- a/videoview.h +++ b/videoview.h @@ -20,11 +20,12 @@ #include "callaudioonlyavataroverlay.h" #include "videooverlay.h" +#include "previewrender.h" #include "api/conversationmodel.h" -#include <QMouseEvent> #include <QKeyEvent> +#include <QMouseEvent> #include <QPropertyAnimation> #include <QTimer> #include <QWidget> @@ -44,8 +45,7 @@ public: void simulateShowChatview(bool checked); void setCurrentCalleeName(const QString& CalleeDisplayName); void resetVideoOverlay(bool isAudioMuted, bool isVideoMuted, bool isRecording, bool isHolding, bool isAudioOnly, const std::string& accountId, const lrc::api::conversation::Info& convInfo); - void disconnectRendering(); - void connectRendering(bool started = false); + void reconnectRenderingVideoDeviceChanged(); protected: void resizeEvent(QResizeEvent* event); @@ -59,8 +59,8 @@ protected: void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); - void keyPressEvent(QKeyEvent *event); - void keyReleaseEvent(QKeyEvent *event); + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); private slots: void slotCallStatusChanged(const std::string& callId); @@ -71,9 +71,11 @@ private slots: private: Ui::VideoView* ui; + PreviewRenderWidget* previewRenderer_; VideoOverlay* overlay_; CallAudioOnlyAvatarOverlay* audioOnlyAvatar_; QPropertyAnimation* fadeAnim_; + QPropertyAnimation* moveAnim_; QTimer fadeTimer_; QWidget* oldParent_; QSize oldSize_; @@ -102,6 +104,7 @@ private: // https://bugreports.qt.io/browse/QTBUG-65981 // https://bugreports.qt.io/browse/QTBUG-66803 constexpr static qreal maxOverlayOpacity_ = 0.9999999999980000442; + constexpr static int previewMargin_ = 15; private: void toggleFullScreen(); @@ -112,5 +115,4 @@ signals: void videoSettingsClicked(); void toggleFullScreenClicked(); void closing(const std::string& callid); - }; diff --git a/videowidget.cpp b/videowidget.cpp index 7c36f41..a8e9d36 100644 --- a/videowidget.cpp +++ b/videowidget.cpp @@ -22,46 +22,40 @@ #include "utils.h" -VideoWidget::VideoWidget(QWidget* parent) : - QWidget(parent) - , isPreviewDisplayed_(true) - , fullPreview_(false) +VideoWidget::VideoWidget(QWidget* parent) + : QWidget(parent) { QPalette pal(palette()); pal.setColor(QPalette::Background, Qt::black); this->setAutoFillBackground(true); this->setPalette(pal); - - previewPlace_ = bottomRight; } VideoWidget::~VideoWidget() {} void -VideoWidget::slotRendererStarted(const std::string& id) +VideoWidget::slotDistantRendererStarted(const std::string& id) { Q_UNUSED(id); - QObject::disconnect(rendererConnections_.started); + QObject::disconnect(rendererDistantConnections_.started); this->show(); - resetPreview_ = true; - - QObject::disconnect(rendererConnections_.updated); - rendererConnections_.updated = connect( + QObject::disconnect(rendererDistantConnections_.updated); + rendererDistantConnections_.updated = connect( &LRCInstance::avModel(), &lrc::api::AVModel::frameUpdated, this, - &VideoWidget::slotUpdateFullView); + &VideoWidget::slotUpdateDistantView); - QObject::disconnect(rendererConnections_.stopped); - rendererConnections_.stopped = connect( + QObject::disconnect(rendererDistantConnections_.stopped); + rendererDistantConnections_.stopped = connect( &LRCInstance::avModel(), &lrc::api::AVModel::rendererStopped, this, - &VideoWidget::slotStopFullView); + &VideoWidget::slotStopDistantView); } void @@ -75,12 +69,7 @@ VideoWidget::renderFrame(const std::string& id) QMutexLocker lock(&mutex_); auto tmp = renderer->currentFrame(); if (tmp.storage.size()) { - using namespace lrc::api::video; - if (id == PREVIEW_RENDERER_ID) { - previewFrame_ = tmp; - } else { - distantFrame_ = tmp; - } + distantFrame_ = tmp; } } update(); @@ -123,288 +112,51 @@ VideoWidget::paintEvent(QPaintEvent* e) ); } } - if ((previewRenderer_ && isPreviewDisplayed_) || (photoMode_ && hasFrame_)) { - QMutexLocker lock(&mutex_); - if (previewFrame_.storage.size() != 0 - && previewFrame_.storage.size() == - (unsigned int)(previewRenderer_->size().height() * previewRenderer_->size().width() * 4)) { - framePreview_ = std::move(previewFrame_.storage); - previewImage_.reset( - new QImage((uchar*)framePreview_.data(), - previewRenderer_->size().width(), - previewRenderer_->size().height(), - QImage::Format_ARGB32_Premultiplied) - ); - hasFrame_ = true; - } - if (previewImage_) { - if (resetPreview_) { - auto previewHeight = fullPreview_ ? height() : height() / 6; - auto previewWidth = fullPreview_ ? width() : width() / 6; - QImage scaledPreview; - if (photoMode_) - scaledPreview = Utils::getCirclePhoto(*previewImage_, previewHeight); - else - scaledPreview = previewImage_->scaled(previewWidth, previewHeight, Qt::KeepAspectRatio); - auto xDiff = (previewWidth - scaledPreview.width()) / 2; - auto yDiff = (previewHeight - scaledPreview.height()) / 2; - auto xPos = fullPreview_ ? xDiff : width() - scaledPreview.width() - previewMargin_; - auto yPos = fullPreview_ ? yDiff : height() - scaledPreview.height() - previewMargin_; - previewGeometry_.setRect(xPos, yPos, scaledPreview.width(), scaledPreview.height()); - updatePreviewPos(); - if (!fullPreview_) { - QBrush brush(scaledPreview); - brush.setTransform(QTransform::fromTranslate(previewGeometry_.x(), previewGeometry_.y())); - QPainterPath previewPath; - previewPath.addRoundRect(previewGeometry_, 25); - painter.fillPath(previewPath, brush); - } - resetPreview_ = false; - } - - QImage scaledPreview; - if (photoMode_) { - scaledPreview = Utils::getCirclePhoto(*previewImage_, previewGeometry_.height()); - } else { - scaledPreview = previewImage_->scaled(previewGeometry_.width(), - previewGeometry_.height(), - Qt::KeepAspectRatio); - } - previewGeometry_.setWidth(scaledPreview.width()); - previewGeometry_.setHeight(scaledPreview.height()); - if (!fullPreview_){ - QBrush brush(scaledPreview); - brush.setTransform(QTransform::fromTranslate(previewGeometry_.x(), previewGeometry_.y())); - QPainterPath previewPath; - previewPath.addRoundRect(previewGeometry_, 25); - painter.fillPath(previewPath, brush); - } else { - painter.drawImage(previewGeometry_, scaledPreview); - } - } - } else if (photoMode_) { - paintBackgroundColor(&painter, Qt::black); - } painter.end(); } void -VideoWidget::paintBackgroundColor(QPainter* painter, QColor color) -{ - QImage black(1, 1, QImage::Format_ARGB32); - black.fill(color); - QImage scaledPreview = Utils::getCirclePhoto(black, height()); - previewGeometry_.setWidth(scaledPreview.width()); - previewGeometry_.setHeight(scaledPreview.height()); - painter->drawImage(previewGeometry_, scaledPreview); -} - -void -VideoWidget::updatePreviewPos() -{ - if (fullPreview_) - return; - switch (previewPlace_) { - case topRight: - previewGeometry_.moveTopRight(QPoint(width() - previewMargin_, previewMargin_)); - break; - case topLeft: - previewGeometry_.moveTopLeft(QPoint(previewMargin_, previewMargin_)); - break; - case bottomRight: - previewGeometry_.moveBottomRight(QPoint(width() - previewMargin_, height() - previewMargin_)); - break; - case bottomLeft: - previewGeometry_.moveBottomLeft(QPoint(previewMargin_, height() - previewMargin_)); - break; - case top: - previewGeometry_.moveTop(previewMargin_); - break; - case right: - previewGeometry_.moveRight(previewMargin_); - break; - case bottom: - previewGeometry_.moveBottom(previewMargin_); - break; - case left: - previewGeometry_.moveLeft(previewMargin_); - break; - - default: - break; - } -} - -void -VideoWidget::movePreview(TargetPointPreview typeOfMove) +VideoWidget::connectDistantRendering() { - resetPreview(); - switch (typeOfMove) - { - case topRight: - previewPlace_ = topRight; - break; - case topLeft: - previewPlace_ = topLeft; - break; - case bottomRight: - previewPlace_ = bottomRight; - break; - case bottomLeft: - previewPlace_ = bottomLeft; - break; - case top: - previewPlace_ = top; - break; - case right: - previewPlace_ = right; - break; - case bottom: - previewPlace_ = bottom; - break; - case left: - previewPlace_ = left; - break; - - default: - break; - - } -} - -void -VideoWidget::connectRendering() -{ - rendererConnections_.started = connect( + QObject::disconnect(rendererDistantConnections_.started); + rendererDistantConnections_.started = connect( &LRCInstance::avModel(), SIGNAL(rendererStarted(const std::string&)), this, - SLOT(slotRendererStarted(const std::string&)) + SLOT(slotDistantRendererStarted(const std::string&)) ); } -void -VideoWidget::connectPreviewOnlyRendering() -{ - rendererConnections_.started = connect( - &LRCInstance::avModel(), - &lrc::api::AVModel::rendererStarted, - [this]() { - this->rendererStartedWithoutDistantRender(); - }); -} - -void -VideoWidget::setPreviewDisplay(bool display) -{ - isPreviewDisplayed_ = display; -} - -void -VideoWidget::setIsFullPreview(bool full) -{ - fullPreview_ = full; -} - -QImage -VideoWidget::takePhoto() -{ - if (previewImage_) { - return previewImage_.get()->copy(); - } - return QImage(); -} - -void -VideoWidget::setPhotoMode(bool isPhotoMode) -{ - - photoMode_ = isPhotoMode; - auto color = isPhotoMode ? Qt::transparent : Qt::black; - - QPalette pal(palette()); - pal.setColor(QPalette::Background, color); - setAutoFillBackground(true); - setPalette(pal); -} - void VideoWidget::disconnectRendering() { - QObject::disconnect(rendererConnections_.started); - QObject::disconnect(rendererConnections_.stopped); - QObject::disconnect(rendererConnections_.updated); -} - -void -VideoWidget::rendererStartedWithoutDistantRender() -{ - // connect only local preview rendering - QObject::disconnect(rendererConnections_.started); - - this->show(); - - resetPreview_ = true; - - QObject::disconnect(rendererConnections_.updated); - rendererConnections_.updated = connect( - &LRCInstance::avModel(), - &lrc::api::AVModel::frameUpdated, - this, - &VideoWidget::slotUpdatePreview); - - QObject::disconnect(rendererConnections_.stopped); - rendererConnections_.stopped = connect( - &LRCInstance::avModel(), - &lrc::api::AVModel::rendererStopped, - this, - &VideoWidget::slotStopFullView); + QObject::disconnect(rendererDistantConnections_.started); + QObject::disconnect(rendererDistantConnections_.stopped); + QObject::disconnect(rendererDistantConnections_.updated); } void -VideoWidget::slotUpdatePreview(const std::string& id) +VideoWidget::slotUpdateDistantView(const std::string& id) { - auto avModel = &LRCInstance::avModel(); - auto renderer = &avModel->getRenderer(id); - if (!renderer->isRendering()) { + if (id == lrc::api::video::PREVIEW_RENDERER_ID) { return; } - using namespace lrc::api::video; - if (id == PREVIEW_RENDERER_ID) { - previewRenderer_ = const_cast<Renderer*>(renderer); - renderFrame(id); - } -} - -void -VideoWidget::slotUpdateFullView(const std::string& id) -{ auto avModel = &LRCInstance::avModel(); auto renderer = &avModel->getRenderer(id); if (!renderer->isRendering()) { return; } - using namespace lrc::api::video; - if (id == PREVIEW_RENDERER_ID) { - previewRenderer_ = const_cast<Renderer*>(renderer); - } else { - distantRenderer_ = const_cast<Renderer*>(renderer); - } + distantRenderer_ = const_cast<lrc::api::video::Renderer*>(renderer); renderFrame(id); } void -VideoWidget::slotStopFullView(const std::string& id) +VideoWidget::slotStopDistantView(const std::string& id) { - QObject::disconnect(rendererConnections_.updated); + Q_UNUSED(id); + QObject::disconnect(rendererDistantConnections_.updated); + QObject::disconnect(rendererDistantConnections_.stopped); using namespace lrc::api::video; - if (id == PREVIEW_RENDERER_ID) { - previewRenderer_ = nullptr; - } else { - distantRenderer_ = nullptr; - } - if (!previewRenderer_ && !distantRenderer_) { - QObject::disconnect(rendererConnections_.stopped); - repaint(); - } + distantRenderer_ = nullptr; + repaint(); } diff --git a/videowidget.h b/videowidget.h index dffed8e..45e34f0 100644 --- a/videowidget.h +++ b/videowidget.h @@ -37,54 +37,23 @@ class VideoWidget : public QWidget public: explicit VideoWidget(QWidget* parent = 0); ~VideoWidget(); - void connectRendering(); - void connectPreviewOnlyRendering(); - void setPreviewDisplay(bool display); - void setIsFullPreview(bool full); - inline void setResetPreview(bool reset) { resetPreview_ = reset; hasFrame_=false; } - void setPhotoMode(bool isPhotoMode); - QImage takePhoto(); - int getPreviewMargin(){ return previewMargin_; } - void resetPreview() { resetPreview_ = true; } + void connectDistantRendering(); void disconnectRendering(); - void rendererStartedWithoutDistantRender(); protected: void paintEvent(QPaintEvent* e); public slots: void slotToggleFullScreenClicked(); - void slotRendererStarted(const std::string& id = {}); - void slotUpdatePreview(const std::string& id = {}); - void slotUpdateFullView(const std::string& id = {}); - void slotStopFullView(const std::string& id = {}); + void slotDistantRendererStarted(const std::string& id = {}); + void slotUpdateDistantView(const std::string& id = {}); + void slotStopDistantView(const std::string& id = {}); void renderFrame(const std::string& id); - inline QRect& getPreviewRect(){ return previewGeometry_; } - -public: - enum TargetPointPreview { - topRight, - topLeft, - bottomRight, - bottomLeft, - left, - right, - top, - bottom - }; - void movePreview(TargetPointPreview typeOfMove); private: - struct rendererConnections { + struct rendererDistantConnections { QMetaObject::Connection started, stopped, updated; - } rendererConnections_; - - void paintBackgroundColor(QPainter* painter, QColor color); - - video::Renderer* previewRenderer_; - video::Frame previewFrame_; - std::unique_ptr<QImage> previewImage_; - std::vector<uint8_t> framePreview_; + } rendererDistantConnections_; video::Renderer* distantRenderer_; video::Frame distantFrame_; @@ -92,20 +61,4 @@ private: std::vector<uint8_t> frameDistant_; QMutex mutex_; - - bool isPreviewDisplayed_; - bool fullPreview_; - QRect previewGeometry_; - bool resetPreview_ = false; - bool photoMode_ = false; - bool hasFrame_ = false; - TargetPointPreview previewPlace_; - - constexpr static int previewMargin_ = 15; - - - -private: - void updatePreviewPos(); - }; -- GitLab