From 7f3d7748171c8ed7535407fb0d9e9c1a13ef9fc3 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Tue, 23 Apr 2024 12:55:52 +0200 Subject: [PATCH] Rework on Camera behavior. --- Linphone/core/camera/CameraGui.cpp | 139 ++++++++++++------ Linphone/core/camera/CameraGui.hpp | 12 +- Linphone/core/camera/PreviewManager.cpp | 4 +- .../participant/ParticipantDeviceCore.cpp | 6 +- Linphone/view/Item/Contact/Sticker.qml | 2 +- .../view/Layout/Call/ActiveSpeakerLayout.qml | 2 +- Linphone/view/Layout/Call/GridLayout.qml | 4 +- 7 files changed, 110 insertions(+), 59 deletions(-) diff --git a/Linphone/core/camera/CameraGui.cpp b/Linphone/core/camera/CameraGui.cpp index ae4fa2104..d1b826949 100644 --- a/Linphone/core/camera/CameraGui.cpp +++ b/Linphone/core/camera/CameraGui.cpp @@ -51,77 +51,117 @@ CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) { CameraGui::~CameraGui() { mustBeInMainThread("~" + getClassName()); mRefreshTimer.stop(); - mIsDeleting = true; setWindowIdLocation(None); } -QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const { - auto renderer = createRenderer(false); - if (!renderer) { - lInfo() << 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. - /*if (getSourceLocation() != CorePreview)*/ QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer); - // TODO : peut etre enelever le check sur le corepreview - } else QTimer::singleShot(1, this, &CameraGui::isReady); // Hack because of constness of createRenderer() - return renderer; +// Hack for Qt constness on create Renderer. +// We need to store the renderer in order to update the SDK filters with this renderer. +QMap gRenderers; +QMutex gRenderesLock; + +//------------------------------------------------------------- + +void CameraGui::refreshLastRenderer() { + gRenderesLock.lock(); + if (gRenderers.contains(this)) setRenderer(gRenderers[this]); + else clearRenderer(); + updateSDKRenderer(); + gRenderesLock.unlock(); } -QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId) const { +void CameraGui::setRenderer(QQuickFramebufferObject::Renderer *renderer) { + mLastRenderer = renderer; +} + +void CameraGui::clearRenderer() { + mLastRenderer = nullptr; +} + +QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const { QQuickFramebufferObject::Renderer *renderer = NULL; - lDebug() << log().arg("CreateRenderer. Reset=") << resetWindowId; + lDebug() << log().arg("CreateRenderer"); + // A renderer is mandatory, we cannot wait async. switch (getSourceLocation()) { case CorePreview: { - if (resetWindowId) PreviewManager::getInstance()->unsubscribe(this); - else renderer = PreviewManager::getInstance()->subscribe(this); + // if (resetWindowId) PreviewManager::getInstance()->unsubscribe(this); + renderer = PreviewManager::getInstance()->subscribe(this); + //(QQuickFramebufferObject::Renderer *)CoreModel::getInstance()->getCore()->createNativePreviewWindowId(); + } break; case Call: { - auto f = [qmlName = mQmlName, callGui = mCallGui, &renderer, resetWindowId]() { + App::postModelBlock([qmlName = mQmlName, callGui = mCallGui, &renderer]() { auto call = callGui->getCore()->getModel()->getMonitor(); if (call) { - lInfo() << "[Camera] (" << qmlName << ") " << (resetWindowId ? "Resetting" : "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); - else { - renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId(); - } - } + lInfo() << "[Camera] (" << qmlName << ") Camera create from CallModel"; + renderer = (QQuickFramebufferObject::Renderer *)call->createNativeVideoWindowId(); } - }; - App::postModelBlock(f); + }); } break; case Device: { - auto f = [qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, &renderer, resetWindowId]() { + App::postModelBlock([qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, &renderer]() { auto device = participantDeviceGui->getCore()->getModel()->getMonitor(); if (device) { - lInfo() << "[Camera] (" << qmlName << ") " << (resetWindowId ? "Resetting" : "Setting") - << " Camera to ParticipantDeviceModel"; - if (resetWindowId) { - renderer = (QQuickFramebufferObject::Renderer *)device->getNativeVideoWindowId(); - if (renderer) device->setNativeVideoWindowId(NULL); - } else { - renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId(); - if (renderer) device->setNativeVideoWindowId(renderer); - } + lInfo() << "[Camera] (" << qmlName << ") Camera create from ParticipantDeviceModel"; + renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId(); } - }; - App::postModelBlock(f); + }); } break; default: { } } + // Storing Qt renderer + gRenderesLock.lock(); + gRenderers[this] = renderer; + gRenderesLock.unlock(); + QTimer::singleShot( + 1, this, &CameraGui::refreshLastRenderer); // Assign new renderer to the current CameraGui (bypassing constness) + + if (!renderer) { + lInfo() << 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; } -void CameraGui::resetWindowId() const { - createRenderer(true); +void CameraGui::updateSDKRenderer() { + updateSDKRenderer(mLastRenderer); +} + +void CameraGui::updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer) { + lDebug() << log().arg("Apply Qt Renderer to SDK") << renderer; + switch (getSourceLocation()) { + case CorePreview: { + + } break; + case Call: { + App::postModelAsync([qmlName = mQmlName, callGui = mCallGui, renderer]() { + auto call = callGui->getCore()->getModel()->getMonitor(); + if (call) { + lInfo() << "[Camera] (" << qmlName << ") Camera to CallModel"; + call->setNativeVideoWindowId(renderer); + } + }); + } break; + case Device: { + App::postModelAsync([qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, renderer]() { + auto device = participantDeviceGui->getCore()->getModel()->getMonitor(); + if (device) { + lInfo() << "[Camera] (" << qmlName << ") Camera to ParticipantDevice"; + device->setNativeVideoWindowId(renderer); + } + }); + } break; + default: { + } + } +} + +void CameraGui::resetWindowId() { + updateSDKRenderer(nullptr); } void CameraGui::checkVideoDefinition() { /* if (mWindowIdLocation == WindowIdLocation::CorePreview) { @@ -201,15 +241,16 @@ void CameraGui::setWindowIdLocation(const WindowIdLocation &location) { if (mWindowIdLocation != location) { lDebug() << log().arg("Update Window Id location from %2 to %3").arg(mWindowIdLocation).arg(location); if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->unsubscribe(this); - else if (mWindowIdLocation != None) resetWindowId(); // Location change: Reset old window ID. + // else if (mWindowIdLocation != None) resetWindowId(); // Location change: Reset old window ID. + resetWindowId(); mWindowIdLocation = location; if (mWindowIdLocation == CorePreview) PreviewManager::getInstance()->subscribe(this); - update(); - QTimer::singleShot(100, this, &CameraGui::requestNewRenderer); + else updateSDKRenderer(); + // QTimer::singleShot(100, this, &CameraGui::requestNewRenderer); // if (mWindowIdLocation == WindowIdLocation::CorePreview) { // mLastVideoDefinition = - // CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit - // videoDefinitionChanged(); mLastVideoDefinitionChecker.start(); + // CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit + // videoDefinitionChanged(); mLastVideoDefinitionChecker.start(); // } else mLastVideoDefinitionChecker.stop(); } } diff --git a/Linphone/core/camera/CameraGui.hpp b/Linphone/core/camera/CameraGui.hpp index b876ebd6b..c22033c34 100644 --- a/Linphone/core/camera/CameraGui.hpp +++ b/Linphone/core/camera/CameraGui.hpp @@ -49,9 +49,8 @@ public: CameraGui(QQuickItem *parent = Q_NULLPTR); virtual ~CameraGui(); QQuickFramebufferObject::Renderer *createRenderer() const override; - QQuickFramebufferObject::Renderer *createRenderer(bool resetWindowid) const; - Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer() + Q_INVOKABLE void resetWindowId(); void checkVideoDefinition(); bool getIsReady() const; @@ -75,6 +74,12 @@ public: void callStateChanged(LinphoneEnums::CallState state); + void setRenderer(QQuickFramebufferObject::Renderer *); + void refreshLastRenderer(); // Lookup in stocked renderer and link it. + void clearRenderer(); + void updateSDKRenderer(); + void updateSDKRenderer(QQuickFramebufferObject::Renderer *renderer); + signals: void requestNewRenderer(); void isReadyChanged(bool isReady); @@ -95,9 +100,10 @@ private: CallGui *mCallGui = nullptr; ParticipantDeviceGui *mParticipantDeviceGui = nullptr; + QQuickFramebufferObject::Renderer *mLastRenderer = nullptr; + WindowIdLocation mWindowIdLocation = None; mutable bool mIsWindowIdSet = false; - bool mIsDeleting = false; DECLARE_ABSTRACT_OBJECT DECLARE_GUI_OBJECT diff --git a/Linphone/core/camera/PreviewManager.cpp b/Linphone/core/camera/PreviewManager.cpp index 2dadbc3c0..e52ac4810 100644 --- a/Linphone/core/camera/PreviewManager.cpp +++ b/Linphone/core/camera/PreviewManager.cpp @@ -46,6 +46,7 @@ PreviewManager *PreviewManager::getInstance() { } } +// Create a Renderer from SDK preview QQuickFramebufferObject::Renderer *PreviewManager::subscribe(const CameraGui *candidate) { QQuickFramebufferObject::Renderer *renderer = nullptr; mCounterMutex.lock(); @@ -101,8 +102,7 @@ void PreviewManager::unsubscribe(const CameraGui *candidate) { // If nullptr, Us mCandidates.erase(itCandidate); lDebug() << log().arg("Update") << mCandidates.first().first->getQmlName(); auto renderer = mCandidates.first().second; - if (!renderer) QTimer::singleShot(1, mCandidates.first().first, &CameraGui::requestNewRenderer); - else + if (renderer) App::postModelBlock([renderer = mCandidates.first().second]() { CoreModel::getInstance()->getCore()->setNativePreviewWindowId(renderer); }); diff --git a/Linphone/core/participant/ParticipantDeviceCore.cpp b/Linphone/core/participant/ParticipantDeviceCore.cpp index 5ec2d1fb0..e03f0724c 100644 --- a/Linphone/core/participant/ParticipantDeviceCore.cpp +++ b/Linphone/core/participant/ParticipantDeviceCore.cpp @@ -83,7 +83,11 @@ void ParticipantDeviceCore::setSelf(QSharedPointer me) { }); mParticipantDeviceModelConnection->makeConnectToModel( &ParticipantDeviceModel::stateChanged, [this](LinphoneEnums::ParticipantDeviceState state) { - mParticipantDeviceModelConnection->invokeToCore([this, state] { setState(state); }); + mParticipantDeviceModelConnection->invokeToCore( + [this, state, isVideoEnabled = mParticipantDeviceModel->isVideoEnabled()] { + setState(state); + setIsVideoEnabled(isVideoEnabled); + }); }); mParticipantDeviceModelConnection->makeConnectToModel( &ParticipantDeviceModel::streamCapabilityChanged, [this](linphone::StreamType) { diff --git a/Linphone/view/Item/Contact/Sticker.qml b/Linphone/view/Item/Contact/Sticker.qml index 69a9a0e09..ada0a87a5 100644 --- a/Linphone/view/Item/Contact/Sticker.qml +++ b/Linphone/view/Item/Contact/Sticker.qml @@ -88,7 +88,7 @@ Item { onTriggered: {cameraLoader.reset = !cameraLoader.reset} } active: mainItem.visible && mainItem.videoEnabled && !cameraLoader.reset - onActiveChanged: console.log("("+mainItem.qmlName+") Camera active " + active) + onActiveChanged: console.log("("+mainItem.qmlName+") Camera active " + active +", visible="+mainItem.visible +", videoEnabled="+mainItem.videoEnabled +", reset="+cameraLoader.reset) sourceComponent: cameraComponent } Component{ diff --git a/Linphone/view/Layout/Call/ActiveSpeakerLayout.qml b/Linphone/view/Layout/Call/ActiveSpeakerLayout.qml index dd2a68387..4e274efeb 100644 --- a/Linphone/view/Layout/Call/ActiveSpeakerLayout.qml +++ b/Linphone/view/Layout/Call/ActiveSpeakerLayout.qml @@ -94,7 +94,7 @@ Item{ clip: true delegate: Item{ // Spacing workaround visible: $modelData && mainItem.callState != LinphoneEnums.CallState.End && mainItem.callState != LinphoneEnums.CallState.Released - && $modelData.core.address != activeSpeakerSticker.address + && $modelData.core.address != activeSpeakerSticker.address || false height: visible ? (180 + 15) * DefaultStyle.dp : 0 width: 300 * DefaultStyle.dp Sticker { diff --git a/Linphone/view/Layout/Call/GridLayout.qml b/Linphone/view/Layout/Call/GridLayout.qml index 367d91ef6..44abd2964 100644 --- a/Linphone/view/Layout/Call/GridLayout.qml +++ b/Linphone/view/Layout/Call/GridLayout.qml @@ -21,11 +21,11 @@ Mosaic { id: allDevices qmlName: "G" Component.onCompleted: console.log("Loaded : " +allDevices + " = " +allDevices.count) - } + } model: grid.call.core.isConference ? participantDevices: [0,1] delegate: Item{ id: avatarCell - property ParticipantDeviceGui currentDevice: grid.call.core.isConference ? gridModel.participantDevices.getAt(index) : null + property ParticipantDeviceGui currentDevice: index >= 0 && grid.call.core.isConference ? $modelData : null onCurrentDeviceChanged: { if(index < 0) cameraView.enabled = false // this is a delegate destruction. We need to stop camera before Qt change its currentDevice (and then, let CameraView to delete wrong renderer) }