Blocking connection for posting lambdas on model from GUI.

Fix camera crash on deletion.

Call crash + 1-1 AS
This commit is contained in:
Julien Wadel 2024-03-28 14:23:50 +01:00
parent 80b0880e7e
commit 576bd0892c
10 changed files with 87 additions and 61 deletions

View file

@ -86,6 +86,15 @@ public:
} }
} }
template <typename Func, typename... Args>
static auto postModelBlock(Func &&callable, Args &&...args) {
if (QThread::currentThread() != CoreModel::getInstance()->thread()) {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, args..., Qt::BlockingQueuedConnection);
} else {
QMetaObject::invokeMethod(CoreModel::getInstance().get(), callable, Qt::DirectConnection);
}
}
void clean(); void clean();
void init(); void init();
void initCppInterfaces(); void initCppInterfaces();

View file

@ -33,8 +33,8 @@
DEFINE_ABSTRACT_OBJECT(CameraGui) DEFINE_ABSTRACT_OBJECT(CameraGui)
QMutex CameraGui::mPreviewCounterMutex; QMutex CameraGui::gPreviewCounterMutex;
int CameraGui::mPreviewCounter = 0; int CameraGui::gPreviewCounter = 0;
// ============================================================================= // =============================================================================
CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) { CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
@ -51,13 +51,15 @@ CameraGui::CameraGui(QQuickItem *parent) : QQuickFramebufferObject(parent) {
CameraGui::~CameraGui() { CameraGui::~CameraGui() {
mustBeInMainThread("~" + getClassName()); mustBeInMainThread("~" + getClassName());
mRefreshTimer.stop(); mRefreshTimer.stop();
App::postModelSync([this]() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); }); mIsDeleting = true;
deactivatePreview();
setWindowIdLocation(None);
} }
QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const { QQuickFramebufferObject::Renderer *CameraGui::createRenderer() const {
auto renderer = createRenderer(false); auto renderer = createRenderer(false);
if (!renderer) { if (!renderer) {
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Dummy, " << getSourceLocation(); qInfo() << log().arg("(%1) Setting Camera to Dummy, %2").arg(mQmlName).arg(getSourceLocation());
QTimer::singleShot(1, this, &CameraGui::isNotReady); QTimer::singleShot(1, this, &CameraGui::isNotReady);
renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL. renderer = new CameraDummy(); // Used to fill a renderer to avoid pushing a NULL.
QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer); QTimer::singleShot(1000, this, &CameraGui::requestNewRenderer);
@ -69,14 +71,13 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId)
QQuickFramebufferObject::Renderer *renderer = NULL; QQuickFramebufferObject::Renderer *renderer = NULL;
// A renderer is mandatory, we cannot wait async. // A renderer is mandatory, we cannot wait async.
switch (getSourceLocation()) { switch (getSourceLocation()) {
case CorePreview: case CorePreview: {
App::postModelSync([this, &renderer, resetWindowId]() { auto f = [qmlName = mQmlName, &renderer, resetWindowId]() {
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Preview"; qInfo() << "[Camera] (" << qmlName << ") Setting Camera to Preview";
auto coreModel = CoreModel::getInstance(); auto coreModel = CoreModel::getInstance();
if (coreModel) { if (coreModel) {
auto core = coreModel->getCore(); auto core = coreModel->getCore();
if (!core) return; if (!core) return;
core->enableVideoPreview(true);
if (resetWindowId) { if (resetWindowId) {
renderer = (QQuickFramebufferObject::Renderer *)core->getNativePreviewWindowId(); renderer = (QQuickFramebufferObject::Renderer *)core->getNativePreviewWindowId();
if (renderer) core->setNativePreviewWindowId(NULL); if (renderer) core->setNativePreviewWindowId(NULL);
@ -85,13 +86,16 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId)
if (renderer) core->setNativePreviewWindowId(renderer); if (renderer) core->setNativePreviewWindowId(renderer);
} }
} }
}); };
break; if (mIsDeleting) {
case Call: App::postModelBlock(f);
App::postModelSync([this, &renderer, resetWindowId]() { } else App::postModelSync(f);
auto call = mCallGui->getCore()->getModel()->getMonitor(); } break;
case Call: {
auto f = [qmlName = mQmlName, callGui = mCallGui, &renderer, resetWindowId]() {
auto call = callGui->getCore()->getModel()->getMonitor();
if (call) { if (call) {
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to CallModel"; qInfo() << "[Camera] (" << qmlName << ") Setting Camera to CallModel";
if (resetWindowId) { if (resetWindowId) {
renderer = (QQuickFramebufferObject::Renderer *)call->getNativeVideoWindowId(); renderer = (QQuickFramebufferObject::Renderer *)call->getNativeVideoWindowId();
if (renderer) call->setNativeVideoWindowId(NULL); if (renderer) call->setNativeVideoWindowId(NULL);
@ -100,21 +104,27 @@ QQuickFramebufferObject::Renderer *CameraGui::createRenderer(bool resetWindowId)
if (renderer) call->setNativeVideoWindowId(renderer); if (renderer) call->setNativeVideoWindowId(renderer);
} }
} }
}); };
break; if (mIsDeleting) {
case Device: App::postModelBlock(f);
App::postModelSync([this, &renderer, resetWindowId]() { } else App::postModelSync(f);
auto device = mParticipantDeviceGui->getCore()->getModel()->getMonitor(); } break;
case Device: {
auto f = [qmlName = mQmlName, participantDeviceGui = mParticipantDeviceGui, &renderer, resetWindowId]() {
auto device = participantDeviceGui->getCore()->getModel()->getMonitor();
if (device) { if (device) {
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to ParticipantDeviceModel"; qInfo() << "[Camera] (" << qmlName << ") Setting Camera to ParticipantDeviceModel";
if (resetWindowId) { if (resetWindowId) {
} else { } else {
renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId(); renderer = (QQuickFramebufferObject::Renderer *)device->createNativeVideoWindowId();
if (renderer) device->setNativeVideoWindowId(renderer); if (renderer) device->setNativeVideoWindowId(renderer);
} }
} }
}); };
break; if (mIsDeleting) {
App::postModelBlock(f);
} else App::postModelSync(f);
} break;
default: { default: {
} }
} }
@ -169,9 +179,7 @@ bool CameraGui::getIsPreview() const {
void CameraGui::setIsPreview(bool status) { void CameraGui::setIsPreview(bool status) {
if (mIsPreview != status) { if (mIsPreview != status) {
mIsPreview = status; mIsPreview = status;
if (mIsPreview) activatePreview(); updateWindowIdLocation();
else deactivatePreview();
// updateWindowIdLocation();
update(); update();
emit isPreviewChanged(status); emit isPreviewChanged(status);
@ -198,7 +206,7 @@ ParticipantDeviceGui *CameraGui::getParticipantDeviceGui() const {
void CameraGui::setParticipantDeviceGui(ParticipantDeviceGui *deviceGui) { void CameraGui::setParticipantDeviceGui(ParticipantDeviceGui *deviceGui) {
if (mParticipantDeviceGui != deviceGui) { if (mParticipantDeviceGui != deviceGui) {
mParticipantDeviceGui = deviceGui; mParticipantDeviceGui = deviceGui;
qDebug() << "Set Device " << mParticipantDeviceGui; qDebug() << log().arg("Set Device %1").arg((quint64)mParticipantDeviceGui);
// setIsPreview(mParticipantDeviceGui->getCore()->isLocal()); // setIsPreview(mParticipantDeviceGui->getCore()->isLocal());
emit participantDeviceGuiChanged(mParticipantDeviceGui); emit participantDeviceGuiChanged(mParticipantDeviceGui);
updateWindowIdLocation(); updateWindowIdLocation();
@ -210,33 +218,37 @@ CameraGui::WindowIdLocation CameraGui::getSourceLocation() const {
} }
void CameraGui::activatePreview() { void CameraGui::activatePreview() {
mPreviewCounterMutex.lock(); gPreviewCounterMutex.lock();
setWindowIdLocation(WindowIdLocation::CorePreview); if (++gPreviewCounter == 1) {
if (++mPreviewCounter == 1) { auto f = []() { CoreModel::getInstance()->getCore()->enableVideoPreview(true); };
App::postModelSync([this]() { if (mIsDeleting) App::postModelBlock(f);
auto coreModel = CoreModel::getInstance(); else App::postModelSync(f);
coreModel->getCore()->enableVideoPreview(true);
});
} }
mPreviewCounterMutex.unlock(); gPreviewCounterMutex.unlock();
} }
void CameraGui::deactivatePreview() { void CameraGui::deactivatePreview() {
mPreviewCounterMutex.lock(); gPreviewCounterMutex.lock();
setWindowIdLocation(WindowIdLocation::None); if (getSourceLocation() == CorePreview) {
if (--mPreviewCounter == 0) { if (--gPreviewCounter == 0) {
App::postModelSync([this]() { auto f = []() { CoreModel::getInstance()->getCore()->enableVideoPreview(false); };
auto coreModel = CoreModel::getInstance(); if (mIsDeleting) App::postModelBlock(f);
coreModel->getCore()->enableVideoPreview(false); else App::postModelSync(f);
}); }
mPreviewCounterMutex.unlock();
} }
gPreviewCounterMutex.unlock();
} }
void CameraGui::setWindowIdLocation(const WindowIdLocation &location) { void CameraGui::setWindowIdLocation(const WindowIdLocation &location) {
if (mWindowIdLocation != location) { if (mWindowIdLocation != location) {
qDebug() << "Update Window Id location from " << mWindowIdLocation << " to " << 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. resetWindowId(); // Location change: Reset old window ID.
mWindowIdLocation = location; mWindowIdLocation = location;
if (mWindowIdLocation == CorePreview) activatePreview();
update(); update();
// if (mWindowIdLocation == WindowIdLocation::CorePreview) { // if (mWindowIdLocation == WindowIdLocation::CorePreview) {
// mLastVideoDefinition = // mLastVideoDefinition =
@ -247,7 +259,8 @@ void CameraGui::setWindowIdLocation(const WindowIdLocation &location) {
} }
void CameraGui::updateWindowIdLocation() { void CameraGui::updateWindowIdLocation() {
bool useDefaultWindow = true; bool useDefaultWindow = true;
if (mCallGui) setWindowIdLocation(WindowIdLocation::Call); if (mIsPreview) setWindowIdLocation(WindowIdLocation::CorePreview);
else if (mCallGui) setWindowIdLocation(WindowIdLocation::Call);
else if (mParticipantDeviceGui && !mParticipantDeviceGui->getCore()->isLocal()) else if (mParticipantDeviceGui && !mParticipantDeviceGui->getCore()->isLocal())
setWindowIdLocation(WindowIdLocation::Device); setWindowIdLocation(WindowIdLocation::Device);
else setWindowIdLocation(WindowIdLocation::CorePreview); else setWindowIdLocation(WindowIdLocation::CorePreview);

View file

@ -42,7 +42,7 @@ class CameraGui : public QQuickFramebufferObject, public AbstractObject {
Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged) Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged)
// Q_PROPERTY(SoundPlayer * linphonePlayer READ getLinphonePlayer WRITE setLinphonePlayer NOTIFY // Q_PROPERTY(SoundPlayer * linphonePlayer READ getLinphonePlayer WRITE setLinphonePlayer NOTIFY
// linphonePlayerChanged) // linphonePlayerChanged)
Q_PROPERTY(QString qmlName READ getQmlName WRITE setQmlName NOTIFY qmlNameChanged) Q_PROPERTY(QString qmlName READ getQmlName WRITE setQmlName NOTIFY qmlNameChanged REQUIRED)
typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation; typedef enum { None = -1, CorePreview = 0, Call, Device, Player, Core } WindowIdLocation;
@ -55,8 +55,8 @@ public:
Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer() Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer()
void checkVideoDefinition(); void checkVideoDefinition();
static QMutex mPreviewCounterMutex; static QMutex gPreviewCounterMutex;
static int mPreviewCounter; static int gPreviewCounter;
bool getIsReady() const; bool getIsReady() const;
void setIsReady(bool isReady); void setIsReady(bool isReady);
@ -105,6 +105,7 @@ private:
WindowIdLocation mWindowIdLocation = None; WindowIdLocation mWindowIdLocation = None;
mutable bool mIsWindowIdSet = false; mutable bool mIsWindowIdSet = false;
bool mIsDeleting = false;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -75,7 +75,7 @@ QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::getMe() const {
void ParticipantDeviceList::setDevices(QList<QSharedPointer<ParticipantDeviceCore>> devices) { void ParticipantDeviceList::setDevices(QList<QSharedPointer<ParticipantDeviceCore>> devices) {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
add(devices); add(devices);
qDebug() << "[ParticipantDeviceList] : add " << devices.size() << " devices"; qDebug() << log().arg("Add %1 devices").arg(devices.size());
} }
QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::findDeviceByUniqueAddress(const QString &address) { QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::findDeviceByUniqueAddress(const QString &address) {
@ -93,7 +93,7 @@ QSharedPointer<ParticipantDeviceCore> ParticipantDeviceList::findDeviceByUniqueA
void ParticipantDeviceList::setConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel) { void ParticipantDeviceList::setConferenceModel(const std::shared_ptr<ConferenceModel> &conferenceModel) {
mustBeInMainThread(log().arg(Q_FUNC_INFO)); mustBeInMainThread(log().arg(Q_FUNC_INFO));
mConferenceModel = conferenceModel; mConferenceModel = conferenceModel;
qDebug() << "[ParticipantDeviceList] : set Conference " << mConferenceModel.get(); qDebug() << log().arg("Set Conference %1").arg((quint64)mConferenceModel.get());
if (mConferenceModelConnection->mCore.lock()) { // Unsure to get myself if (mConferenceModelConnection->mCore.lock()) { // Unsure to get myself
auto oldConnect = mConferenceModelConnection->mCore; // Setself rebuild safepointer auto oldConnect = mConferenceModelConnection->mCore; // Setself rebuild safepointer
setSelf(mConferenceModelConnection->mCore.mQData); // reset connections setSelf(mConferenceModelConnection->mCore.mQData); // reset connections

View file

@ -72,8 +72,8 @@ void CallModel::accept(bool withVideo) {
break; break;
} }
} }
mMonitor->acceptWithParams(params); mMonitor->acceptWithParams(params);
emit cameraEnabledChanged(withVideo);
} }
void CallModel::decline() { void CallModel::decline() {

View file

@ -358,7 +358,7 @@ Window {
Layout.fillHeight: true Layout.fillHeight: true
Control.StackView { Control.StackView {
id: middleItemStackView id: middleItemStackView
initialItem: waitingRoom initialItem: mainWindow.call ? inCallItem : waitingRoom
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
Layout.margins: 20 * DefaultStyle.dp Layout.margins: 20 * DefaultStyle.dp
@ -597,7 +597,7 @@ Window {
} }
onAddParticipantRequested: participantsStack.currentIndex = 1 onAddParticipantRequested: participantsStack.currentIndex = 1
} }
AddParticipantLayout { AddParticipantsLayout {
conferenceInfoGui: mainWindow.conferenceInfo conferenceInfoGui: mainWindow.conferenceInfo
} }
} }

View file

@ -13,6 +13,7 @@ import UtilsCpp
Item { Item {
id: mainItem id: mainItem
property var callObj
signal addAccountRequest() signal addAccountRequest()
@ -220,7 +221,7 @@ Item {
background: Item{} background: Item{}
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
property var callObj
contentItem: Image { contentItem: Image {
anchors.fill: parent anchors.fill: parent
width: 24 * DefaultStyle.dp width: 24 * DefaultStyle.dp
@ -228,7 +229,7 @@ Item {
source: AppIcons.phone source: AppIcons.phone
} }
onClicked: { onClicked: {
callObj = UtilsCpp.createCall(sipAddr.text) mainItem.callObj = UtilsCpp.createCall(sipAddr.text)
} }
} }
Button { Button {
@ -241,7 +242,7 @@ Item {
height: 24 * DefaultStyle.dp height: 24 * DefaultStyle.dp
source: AppIcons.videoCamera source: AppIcons.videoCamera
} }
onClicked: callObj = UtilsCpp.createCall(sipAddr.text, true) onClicked: mainItem.callObj = UtilsCpp.createCall(sipAddr.text, true)
} }
} }
Button { Button {

View file

@ -7,7 +7,7 @@ import UtilsCpp 1.0
RowLayout { RowLayout {
id: mainItem id: mainItem
property bool cameraEnabled: true property alias cameraEnabled: preview.cameraEnabled
property bool microEnabled: true property bool microEnabled: true
property bool settingsButtonChecked: settingsButton.checked property bool settingsButtonChecked: settingsButton.checked
property ConferenceInfoGui conferenceInfo property ConferenceInfoGui conferenceInfo
@ -24,6 +24,7 @@ RowLayout {
id: preview id: preview
Layout.preferredHeight: 330 * DefaultStyle.dp Layout.preferredHeight: 330 * DefaultStyle.dp
Layout.preferredWidth: 558 * DefaultStyle.dp Layout.preferredWidth: 558 * DefaultStyle.dp
qmlName: "WP"
AccountProxy{ AccountProxy{
id: accounts id: accounts
} }

View file

@ -81,7 +81,7 @@ Item {
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;} onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
} }
active: mainItem.visible && mainItem.cameraEnabled active: mainItem.visible && mainItem.cameraEnabled
onActiveChanged: console.log("camera active", active) onActiveChanged: console.log("("+mainItem.qmlName+") Camera active " + active)
sourceComponent: cameraComponent sourceComponent: cameraComponent
} }
Component{ Component{
@ -94,10 +94,10 @@ Item {
id: cameraItem id: cameraItem
anchors.fill: parent anchors.fill: parent
visible: false visible: false
qmlName: mainItem.qmlName
call: mainItem.call call: mainItem.call
participantDevice: mainItem.participantDevice participantDevice: mainItem.participantDevice
isPreview: mainItem.previewEnabled isPreview: mainItem.previewEnabled
qmlName: mainItem.qmlName
onRequestNewRenderer: { onRequestNewRenderer: {
console.log("Request new renderer") console.log("Request new renderer")
resetTimer.restart() resetTimer.restart()

View file

@ -35,8 +35,7 @@ Item{
call: mainItem.call call: mainItem.call
participantDevice: mainItem.conference && mainItem.conference.core.activeSpeaker participantDevice: mainItem.conference && mainItem.conference.core.activeSpeaker
property var address: participantDevice && participantDevice.core.address property var address: participantDevice && participantDevice.core.address
onAddressChanged: console.log(address) cameraEnabled: call && call.core.remoteVideoEnabled
cameraEnabled: true
qmlName: 'AS' qmlName: 'AS'
Timer { Timer {
@ -83,6 +82,8 @@ Item{
visible: allDevices.count > 2 visible: allDevices.count > 2
spacing: 15 * DefaultStyle.dp spacing: 15 * DefaultStyle.dp
model: allDevices model: allDevices
snapMode: ListView.SnapOneItem
clip: true
delegate: delegate:
Sticker { Sticker {
visible: mainItem.callState != LinphoneEnums.CallState.End && mainItem.callState != LinphoneEnums.CallState.Released visible: mainItem.callState != LinphoneEnums.CallState.End && mainItem.callState != LinphoneEnums.CallState.Released
@ -109,7 +110,7 @@ Item{
anchors.rightMargin: 10 * DefaultStyle.dp anchors.rightMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp anchors.bottomMargin: 10 * DefaultStyle.dp
//participantDevice: allDevices.me //participantDevice: allDevices.me
cameraEnabled: allDevices.count <= 2 cameraEnabled: visible && mainItem.call && mainItem.call.core.cameraEnabled
previewEnabled: true previewEnabled: true
qmlName: 'P' qmlName: 'P'