/* * Copyright (c) 2010-2024 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "CameraDummy.hpp" #include "CameraGui.hpp" #include "core/App.hpp" #include "core/call/CallCore.hpp" #include "core/call/CallGui.hpp" #include "core/participant/ParticipantDeviceCore.hpp" #include "core/participant/ParticipantDeviceGui.hpp" DEFINE_ABSTRACT_OBJECT(CameraGui) QMutex CameraGui::gPreviewCounterMutex; int CameraGui::gPreviewCounter = 0; // ============================================================================= CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) { mustBeInMainThread(getClassName()); // The fbo content must be y-mirrored because the ms rendering is y-inverted. setMirrorVertically(true); mRefreshTimer.setInterval(1000 / mMaxFps); connect(&mRefreshTimer, &QTimer::timeout, this, &QQuickFramebufferObject::update, Qt::QueuedConnection); mRefreshTimer.start(); } // TODO : Deactivate only if there are no previews to display (Could be open in settings and calls) CameraGui::~CameraGui() { mustBeInMainThread("~" + getClassName()); mRefreshTimer.stop(); mIsDeleting = true; deactivatePreview(); setWindowIdLocation(None); } QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const { auto renderer = createRenderer(false); if (!renderer) { qInfo() << log().arg("(%1) Setting Camera to Dummy, %2").arg(mQmlName).arg(getSourceLocation()); QTimer::singleShot(1, this, &CameraGui::isNotReady); renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL. QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer); } else QTimer::singleShot(1, this, &CameraGui::isReady); // Hack because of constness of createRenderer() return renderer; } QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId) const { QQuickFramebufferObject::Renderer *renderer = NULL; // A renderer is mandatory, we cannot wait async. switch (getSourceLocation()) { case CorePreview: { auto f = [qmlName = mQmlName, &renderer, resetWindowId]() { qInfo() << "[Camera] (" << qmlName << ") Setting Camera to Preview"; auto coreModel = CoreModel::getInstance(); if (coreModel) { auto core = coreModel->getCore(); if (!core) return; if (resetWindowId) { renderer = (QQuickFramebufferObject::Renderer *)core->getNativePreviewWindowId(); if (renderer) core->setNativePreviewWindowId(NULL); } else { renderer = (QQuickFramebufferObject::Renderer *)core->createNativePreviewWindowId(); if (renderer) core->setNativePreviewWindowId(renderer); } } }; if (mIsDeleting) { App::postModelBlock(f); } else App::postModelSync(f); } break; case Call: { auto f = [qmlName = mQmlName, callGui = mCallGui, &renderer, resetWindowId]() { auto call = callGui->getCore()->getModel()->getMonitor(); if (call) { qInfo() << "[Camera] (" << qmlName << ") Setting Camera to CallModel"; if (resetWindowId) { renderer = (QQuickFramebufferObject::Renderer *)call->getNativeVideoWindowId(); if (renderer) call->setNativeVideoWindowId(NULL); } else { renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId(); if (renderer) call->setNativeVideoWindowId(renderer); } } }; if (mIsDeleting) { App::postModelBlock(f); } else App::postModelSync(f); } break; case Device: { auto f = [qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, &renderer, resetWindowId]() { auto device = participantDeviceGui->getCore()->getModel()->getMonitor(); if (device) { qInfo() << "[Camera] (" << qmlName << ") Setting Camera to ParticipantDeviceModel"; if (resetWindowId) { } else { renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId(); if (renderer) device->setNativeVideoWindowId(renderer); } } }; if (mIsDeleting) { App::postModelBlock(f); } else App::postModelSync(f); } break; default: { } } return renderer; } void CameraGui::resetWindowId() const { createRenderer(true); } void CameraGui::checkVideoDefinition() { /* if (mWindowIdLocation == WindowIdLocation::CorePreview) { auto videoDefinition = CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); if (videoDefinition["width"] != mLastVideoDefinition["width"] || videoDefinition["height"] != mLastVideoDefinition["height"]) { mLastVideoDefinition = videoDefinition; emit videoDefinitionChanged(); } }*/ } QString CameraGui::getQmlName() const { return mQmlName; } void CameraGui::setQmlName(const QString &name) { if (name != mQmlName) { mQmlName = name; emit qmlNameChanged(); } } bool CameraGui::getIsReady() const { return mIsReady; } void CameraGui::setIsReady(bool isReady) { if (mIsReady != isReady) { mIsReady = isReady; emit isReadyChanged(mIsReady); } } void CameraGui::isReady() { setIsReady(true); } void CameraGui::isNotReady() { setIsReady(false); } bool CameraGui::getIsPreview() const { return mIsPreview; } void CameraGui::setIsPreview(bool status) { if (mIsPreview != status) { mIsPreview = status; updateWindowIdLocation(); update(); emit isPreviewChanged(status); } } CallGui *CameraGui::getCallGui() const { return mCallGui; } void CameraGui::setCallGui(CallGui *callGui) { if (mCallGui != callGui) { mCallGui = callGui; qDebug() << "Set Call " << mCallGui; emit callGuiChanged(mCallGui); updateWindowIdLocation(); } } ParticipantDeviceGui *CameraGui::getParticipantDeviceGui() const { return mParticipantDeviceGui; } void CameraGui::setParticipantDeviceGui(ParticipantDeviceGui *deviceGui) { if (mParticipantDeviceGui != deviceGui) { mParticipantDeviceGui = deviceGui; qDebug() << log().arg("Set Device %1").arg((quint64)mParticipantDeviceGui); // setIsPreview(mParticipantDeviceGui->getCore()->isLocal()); emit participantDeviceGuiChanged(mParticipantDeviceGui); updateWindowIdLocation(); } } CameraGui::WindowIdLocation CameraGui::getSourceLocation() const { return mWindowIdLocation; } void CameraGui::activatePreview() { gPreviewCounterMutex.lock(); if (++gPreviewCounter == 1) { auto f = []() { CoreModel::getInstance()->getCore()->enableVideoPreview(true); }; if (mIsDeleting) App::postModelBlock(f); else App::postModelSync(f); } gPreviewCounterMutex.unlock(); } void CameraGui::deactivatePreview() { gPreviewCounterMutex.lock(); if (getSourceLocation() == CorePreview) { if (--gPreviewCounter == 0) { auto f = []() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); }; if (mIsDeleting) App::postModelBlock(f); else App::postModelSync(f); } } gPreviewCounterMutex.unlock(); } void CameraGui::setWindowIdLocation(const WindowIdLocation &location) { if (mWindowIdLocation != location) { qDebug() << log() .arg("( %1 ) Update Window Id location from %2 to %3") .arg(mQmlName) .arg(mWindowIdLocation) .arg(location); if (mWindowIdLocation == CorePreview) deactivatePreview(); resetWindowId(); // Location change: Reset old window ID. mWindowIdLocation = location; if (mWindowIdLocation == CorePreview) activatePreview(); update(); // if (mWindowIdLocation == WindowIdLocation::CorePreview) { // mLastVideoDefinition = // CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit // videoDefinitionChanged(); mLastVideoDefinitionChecker.start(); // } else mLastVideoDefinitionChecker.stop(); } } void CameraGui::updateWindowIdLocation() { bool useDefaultWindow = true; if (mIsPreview) setWindowIdLocation(WindowIdLocation::CorePreview); else if (mCallGui) setWindowIdLocation(WindowIdLocation::Call); else if (mParticipantDeviceGui && !mParticipantDeviceGui->getCore()->isLocal()) setWindowIdLocation(WindowIdLocation::Device); else setWindowIdLocation(WindowIdLocation::CorePreview); }