From 7e9dce9c00230a8b627d400e49fd220c63b6e28d Mon Sep 17 00:00:00 2001
From: Nicolas Vengeon <nicolas.vengeon@savoirfairelinux.com>
Date: Tue, 25 Oct 2022 11:25:14 -0400
Subject: [PATCH] feature: Add 'advanced information' call overlay

Change-Id: Ia54d01ec56e01d0c04e360ec06da87aa37fe74fe
GitLab: #510
---
 src/app/avadapter.cpp                         |  11 +
 src/app/avadapter.h                           |   2 +
 src/app/calladapter.cpp                       |  28 +++
 src/app/calladapter.h                         |   5 +
 src/app/constant/JamiStrings.qml              |   1 +
 src/app/constant/JamiTheme.qml                |   7 +
 .../components/CallInformationWindow.qml      | 213 ++++++++++++++++++
 src/app/mainview/components/CallOverlay.qml   |   9 +
 .../components/CallViewContextMenu.qml        |  12 +
 .../mainview/components/OngoingCallPage.qml   |   1 +
 src/app/qml.qrc                               |   1 +
 src/libclient/CMakeLists.txt                  |   2 -
 src/libclient/api/avmodel.h                   |   9 +
 src/libclient/api/callmodel.h                 |   6 +
 src/libclient/avmodel.cpp                     |  25 +-
 src/libclient/callmodel.cpp                   |  31 +++
 src/libclient/directrenderer.cpp              |  19 ++
 src/libclient/private/smartInfoHub_p.h        |   1 -
 src/libclient/qtwrapper/conversions_wrap.hpp  |  10 +
 src/libclient/renderer.cpp                    |  22 ++
 src/libclient/renderer.h                      |  19 ++
 src/libclient/shmrenderer.cpp                 |   7 +-
 src/libclient/smartinfohub.cpp                | 178 ---------------
 src/libclient/smartinfohub.h                  |  65 ------
 24 files changed, 432 insertions(+), 252 deletions(-)
 create mode 100644 src/app/mainview/components/CallInformationWindow.qml
 delete mode 100644 src/libclient/smartinfohub.cpp
 delete mode 100644 src/libclient/smartinfohub.h

diff --git a/src/app/avadapter.cpp b/src/app/avadapter.cpp
index f3fe0ed57..9c68deb19 100644
--- a/src/app/avadapter.cpp
+++ b/src/app/avadapter.cpp
@@ -46,6 +46,10 @@ AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent)
             &lrc::api::AVModel::rendererStarted,
             this,
             &AvAdapter::onRendererStarted);
+    connect(&lrcInstance_->avModel(),
+            &lrc::api::AVModel::onRendererInfosUpdated,
+            this,
+            &AvAdapter::setRenderersInfoList);
 }
 
 // The top left corner of primary screen is (0, 0).
@@ -415,8 +419,15 @@ AvAdapter::getHardwareAcceleration()
 {
     return lrcInstance_->avModel().getHardwareAcceleration();
 }
+
 void
 AvAdapter::setHardwareAcceleration(bool accelerate)
 {
     lrcInstance_->avModel().setHardwareAcceleration(accelerate);
 }
+
+void
+AvAdapter::setRenderersInfoList(QVariantList renderersInfo)
+{
+    set_renderersInfoList(renderersInfo);
+}
diff --git a/src/app/avadapter.h b/src/app/avadapter.h
index f565ce59a..2f115cf8a 100644
--- a/src/app/avadapter.h
+++ b/src/app/avadapter.h
@@ -36,6 +36,7 @@ class AvAdapter final : public QmlAdapterBase
     QML_PROPERTY(bool, muteCamera)
     QML_RO_PROPERTY(QStringList, windowsNames)
     QML_RO_PROPERTY(QList<QVariant>, windowsIds)
+    QML_RO_PROPERTY(QVariantList, renderersInfoList)
 
 public:
     explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr);
@@ -107,6 +108,7 @@ protected:
     Q_INVOKABLE void setHardwareAcceleration(bool accelerate);
 
 private Q_SLOTS:
+    void setRenderersInfoList(QVariantList renderersInfo);
     void onAudioDeviceEvent();
     void onRendererStarted(const QString& id, const QSize& size);
 
diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp
index d266673f5..25c473c58 100644
--- a/src/app/calladapter.cpp
+++ b/src/app/calladapter.cpp
@@ -41,6 +41,9 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
     : QmlAdapterBase(instance, parent)
     , systemTray_(systemTray)
 {
+    timer = new QTimer(this);
+    connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation);
+
     participantsModel_.reset(new CallParticipantsModel(lrcInstance_, this));
     QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, participantsModel_.get(), "CallParticipantsModel");
 
@@ -95,6 +98,19 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
             &CallAdapter::saveConferenceSubcalls);
 }
 
+void
+CallAdapter::startTimerInformation()
+{
+    updateAdvancedInformation();
+    timer->start(1000);
+}
+
+void
+CallAdapter::stopTimerInformation()
+{
+    timer->stop();
+}
+
 void
 CallAdapter::onAccountChanged()
 {
@@ -1147,6 +1163,18 @@ CallAdapter::getCallDurationTime(const QString& accountId, const QString& convUi
     return QString();
 }
 
+void
+CallAdapter::updateAdvancedInformation()
+{
+    try {
+        auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel;
+        if (callModel)
+            set_callInformation(QVariantList::fromList(callModel->getAdvancedInformation()));
+    } catch (const std::exception& e) {
+        qWarning() << e.what();
+    }
+}
+
 void
 CallAdapter::preventScreenSaver(bool state)
 {
diff --git a/src/app/calladapter.h b/src/app/calladapter.h
index 0e18614b5..88e375cb1 100644
--- a/src/app/calladapter.h
+++ b/src/app/calladapter.h
@@ -37,8 +37,10 @@ class CallAdapter final : public QmlAdapterBase
 {
     Q_OBJECT
     QML_PROPERTY(bool, hasCall)
+    QML_RO_PROPERTY(QVariantList, callInformation)
 
 public:
+    QTimer* timer;
     enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED };
     Q_ENUM(MuteStates)
 
@@ -49,6 +51,8 @@ protected:
     void safeInit() override {};
 
 public:
+    Q_INVOKABLE void startTimerInformation();
+    Q_INVOKABLE void stopTimerInformation();
     Q_INVOKABLE void placeAudioOnlyCall();
     Q_INVOKABLE void placeCall();
     Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid);
@@ -88,6 +92,7 @@ public:
                                 const QString& accountId = {},
                                 bool forceCallOnly = false);
     Q_INVOKABLE QString getCallDurationTime(const QString& accountId, const QString& convUid);
+    Q_INVOKABLE void updateAdvancedInformation();
 
 Q_SIGNALS:
     void callStatusChanged(int index, const QString& accountId, const QString& convUid);
diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 7ab85e0cd..df70ac49c 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -291,6 +291,7 @@ Item {
     property string shareFile: qsTr("Share file")
     property string selectShareMethod: qsTr("Select sharing method")
     property string viewPlugin: qsTr("View plugin")
+    property string advancedInformation: qsTr("Advanced information")
     property string noVideoDevice: qsTr("No video device")
     property string notAvailable: qsTr("Unavailable")
     property string lowerHand: qsTr("Lower hand")
diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml
index f80e128f9..994eb68c4 100644
--- a/src/app/constant/JamiTheme.qml
+++ b/src/app/constant/JamiTheme.qml
@@ -344,6 +344,13 @@ Item {
     property real swarmDetailsPageDocumentsPaperClipSize: 24
     property real swarmDetailsPageDocumentsMediaSize: 175
 
+    //Call information
+    property real textFontPointSize: calcSize(10)
+    property real titleFontPointSize: calcSize(13)
+    property color callInfoColor: chatviewTextColor
+    property int callInformationElementsSpacing: 5
+    property int callInformationBlockSpacing: 25
+
     // Jami switch
     property real switchIndicatorRadius: 30
     property real switchPreferredHeight: 25
diff --git a/src/app/mainview/components/CallInformationWindow.qml b/src/app/mainview/components/CallInformationWindow.qml
new file mode 100644
index 000000000..533434950
--- /dev/null
+++ b/src/app/mainview/components/CallInformationWindow.qml
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ * Author: Nicolas Vengeon <nicolas.vengeon@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/>.
+ */
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+import net.jami.Models 1.1
+import net.jami.Adapters 1.1
+import net.jami.Constants 1.1
+import Qt5Compat.GraphicalEffects
+
+import "../../commoncomponents"
+
+Window {
+    id: root
+
+    width: parent.width * 2 / 3
+    height: parent.height * 2 / 3
+    property var advancedList
+    property var fps
+
+    onClosing: {
+        CallAdapter.stopTimerInformation()
+    }
+
+    Rectangle {
+        id: container
+
+        anchors.fill: parent
+        color: JamiTheme.secondaryBackgroundColor
+
+        RowLayout {
+            id:  windowContent
+
+            anchors.fill: parent
+
+            ColumnLayout {
+                spacing: JamiTheme.callInformationBlockSpacing
+
+                Text{
+                    color: JamiTheme.callInfoColor
+                    text: "Call information"
+                    font.pointSize: JamiTheme.titleFontPointSize
+                }
+
+                Item {
+                    id: itemCallInformation
+
+                    Layout.fillHeight: true
+                    Layout.fillWidth: true
+                    clip: true
+
+                    ListView {
+                        model: advancedList
+                        width: parent.width
+                        height: root.height
+                        spacing: JamiTheme.callInformationBlockSpacing
+
+                        delegate: Column {
+                            spacing: JamiTheme.callInformationElementsSpacing
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Call id: " + modelData.CALL_ID
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Video codec: " + modelData.VIDEO_CODEC
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Audio codec: " + modelData.AUDIO_CODEC
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                function stringWithoutRing(peerNumber){
+                                    return peerNumber.replace("@ring.dht","") ;
+                                }
+                                color: JamiTheme.callInfoColor
+                                text: "PEER_NUMBER: " + stringWithoutRing(modelData.PEER_NUMBER)
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Hardware acceleration: " + modelData.HARDWARE_ACCELERATION
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Video min bitrate: " + modelData.VIDEO_MIN_BITRATE
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Video max bitrate: " + modelData.VIDEO_MAX_BITRATE
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Video bitrate: " + modelData.VIDEO_BITRATE
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Sockets: " + modelData.SOCKETS
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemCallInformation.width
+                            }
+
+                        }
+                    }
+                }
+            }
+
+            ColumnLayout {
+                spacing: JamiTheme.callInformationBlockSpacing
+
+                Text {
+                    color: JamiTheme.callInfoColor
+                    text: "Renderers information"
+                    font.pointSize: JamiTheme.titleFontPointSize
+                }
+
+                Item {
+                    id: itemParticipantInformation
+
+                    Layout.fillHeight: true
+                    Layout.fillWidth: true
+                    clip: true
+
+
+                    ListView {
+                        width: parent.width
+                        height: root.height
+                        spacing: JamiTheme.callInformationBlockSpacing
+                        model: fps
+
+                        delegate: Column {
+                            spacing: JamiTheme.callInformationElementsSpacing
+
+                            Text{
+                                color: JamiTheme.callInfoColor
+                                text: "Renderer id: " + modelData.ID
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemParticipantInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Fps: " + modelData.FPS
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemParticipantInformation.width
+                            }
+
+                            Text {
+                                color: JamiTheme.callInfoColor
+                                text: "Resolution: " + modelData.RES
+                                font.pointSize: JamiTheme.textFontPointSize
+                                wrapMode: Text.WrapAnywhere
+                                width: itemParticipantInformation.width
+                            }
+
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/app/mainview/components/CallOverlay.qml b/src/app/mainview/components/CallOverlay.qml
index 86f6b50d1..87f8f2e79 100644
--- a/src/app/mainview/components/CallOverlay.qml
+++ b/src/app/mainview/components/CallOverlay.qml
@@ -83,6 +83,7 @@ Item {
         SelectScreenWindowCreation.destroySelectScreenWindow()
         ScreenRubberBandCreation.destroyScreenRubberBandWindow()
         PluginHandlerPickerCreation.closePluginHandlerPicker()
+        callInformationWindow.close()
     }
 
     // x, y position does not need to be translated
@@ -123,6 +124,14 @@ Item {
         y: root.height / 2 - sipInputPanel.height / 2
     }
 
+    CallInformationWindow {
+        id: callInformationWindow
+
+        visible: false
+        advancedList: CallAdapter.callInformation
+        fps: AvAdapter.renderersInfoList
+    }
+
     JamiFileDialog {
         id: jamiFileDialog
 
diff --git a/src/app/mainview/components/CallViewContextMenu.qml b/src/app/mainview/components/CallViewContextMenu.qml
index 8e607242a..1b8e86c42 100644
--- a/src/app/mainview/components/CallViewContextMenu.qml
+++ b/src/app/mainview/components/CallViewContextMenu.qml
@@ -176,6 +176,18 @@ ContextMenuAutoLoader {
             onClicked: {
                 root.pluginItemClicked()
             }
+        },
+        GeneralMenuItem {
+            id: advancedInformation
+
+            canTrigger: true
+            itemName: JamiStrings.advancedInformation
+            iconSource: JamiResources.settings_24dp_svg
+
+            onClicked: {
+                CallAdapter.startTimerInformation();
+                callInformationWindow.show()
+            }
         }
     ]
 
diff --git a/src/app/mainview/components/OngoingCallPage.qml b/src/app/mainview/components/OngoingCallPage.qml
index 5917cfa9e..93e0b2434 100644
--- a/src/app/mainview/components/OngoingCallPage.qml
+++ b/src/app/mainview/components/OngoingCallPage.qml
@@ -151,6 +151,7 @@ Rectangle {
 
         Rectangle {
             id: callPageMainRect
+
             SplitView.preferredHeight: mainColumnLayout.isHorizontal ? root.height : (root.height / 3) * 2
             SplitView.preferredWidth: mainColumnLayout.isHorizontal ? (root.width / 3) * 2 : root.width
             SplitView.minimumHeight: root.height / 2 + 20
diff --git a/src/app/qml.qrc b/src/app/qml.qrc
index 8f558284d..fd4cdc7d7 100644
--- a/src/app/qml.qrc
+++ b/src/app/qml.qrc
@@ -212,5 +212,6 @@
         <file>mainview/components/DocumentsScrollview.qml</file>
         <file>mainview/components/FilePreview.qml</file>
         <file>mainview/components/MediaPreview.qml</file>
+	<file>mainview/components/CallInformationWindow.qml</file>
     </qresource>
 </RCC>
diff --git a/src/libclient/CMakeLists.txt b/src/libclient/CMakeLists.txt
index 2676c9c4c..99780f5fa 100644
--- a/src/libclient/CMakeLists.txt
+++ b/src/libclient/CMakeLists.txt
@@ -278,7 +278,6 @@ set(LIBCLIENT_SOURCES
   avmodel.cpp
   pluginmodel.cpp
   namedirectory.cpp
-  smartinfohub.cpp
   renderer.cpp)
 
 set(LIBCLIENT_HEADERS
@@ -286,7 +285,6 @@ set(LIBCLIENT_HEADERS
   globalinstances.h
   pixmapmanipulatordefault.h
   dbuserrorhandlerdefault.h
-  smartinfohub.h
   vcard.h
   namedirectory.h
   messagelistmodel.h
diff --git a/src/libclient/api/avmodel.h b/src/libclient/api/avmodel.h
index d0ab442e2..1e0a782f7 100644
--- a/src/libclient/api/avmodel.h
+++ b/src/libclient/api/avmodel.h
@@ -276,8 +276,17 @@ public:
     QSize getRendererSize(const QString& id);
     video::Frame getRendererFrame(const QString& id);
     bool useDirectRenderer() const;
+    /**
+     * Update renderers information list
+     */
+    Q_SLOT void updateRenderersInfo();
 
 Q_SIGNALS:
+    /**
+     * Emitted after an update of renderers information
+     * @param renderersInfoList Information on all renderers (RES, ID, FPS)
+     */
+    void onRendererInfosUpdated(QVariantList renderersInfoList);
     /**
      * Emitted when a renderer is started
      * @param id of the renderer
diff --git a/src/libclient/api/callmodel.h b/src/libclient/api/callmodel.h
index 4b152bf3d..e635617e5 100644
--- a/src/libclient/api/callmodel.h
+++ b/src/libclient/api/callmodel.h
@@ -391,6 +391,12 @@ public:
     QString getDisplay(const QString& windowId);
 
     void emplaceConversationConference(const QString& callId);
+
+    /**
+     * Get advanced information from every current call
+     */
+    QList<QVariant> getAdvancedInformation() const;
+
 Q_SIGNALS:
 
     /**
diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp
index 2f1e934d8..67ade3482 100644
--- a/src/libclient/avmodel.cpp
+++ b/src/libclient/avmodel.cpp
@@ -168,6 +168,23 @@ AVModel::~AVModel()
     }
 }
 
+void
+AVModel::updateRenderersInfo()
+{
+    QVariantList renderersInfoList;
+
+    for (auto r = pimpl_->renderers_.begin(); r != pimpl_->renderers_.end(); r++) {
+        QVariantMap qmap;
+        auto& rend = r->second;
+        MapStringString mapInfo = rend->getInfos();
+        qmap.insert(rend->RES, mapInfo["RES"]);
+        qmap.insert(rend->ID, mapInfo["ID"]);
+        qmap.insert(rend->FPS, mapInfo["FPS"]);
+        renderersInfoList.append(qmap);
+    }
+    Q_EMIT onRendererInfosUpdated(renderersInfoList);
+}
+
 bool
 AVModel::getDecodingAccelerated() const
 {
@@ -197,7 +214,7 @@ AVModel::setEncodingAccelerated(bool accelerate)
 bool
 AVModel::getHardwareAcceleration() const
 {
-    bool result = getDecodingAccelerated() & getEncodingAccelerated();
+    bool result = getDecodingAccelerated() && getEncodingAccelerated();
     return result;
 }
 void
@@ -825,6 +842,12 @@ void
 AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& shmPath)
 {
     auto connectRenderer = [this](Renderer* renderer, const QString& id) {
+        connect(
+            renderer,
+            &Renderer::fpsChanged,
+            this,
+            [this, id](void) { Q_EMIT linked_.updateRenderersInfo(); },
+            Qt::DirectConnection);
         connect(
             renderer,
             &Renderer::started,
diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp
index 5f1e4b117..e5fe8c8a2 100644
--- a/src/libclient/callmodel.cpp
+++ b/src/libclient/callmodel.cpp
@@ -55,8 +55,12 @@
 
 using namespace libjami::Media;
 
+constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION";
+constexpr static const char CALL_ID[] = "CALL_ID";
+
 static std::uniform_int_distribution<int> dis {0, std::numeric_limits<int>::max()};
 static const std::map<short, QString>
+
     sip_call_status_code_map {{0, QObject::tr("Null")},
                               {100, QObject::tr("Trying")},
                               {180, QObject::tr("Ringing")},
@@ -128,6 +132,8 @@ public:
                    const BehaviorController& behaviorController);
     ~CallModelPimpl();
 
+    QVariantList callAdvancedInformation();
+
     /**
      * Send the profile VCard into a call
      * @param callId
@@ -397,6 +403,12 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin
     return callId;
 }
 
+QList<QVariant>
+CallModel::getAdvancedInformation() const
+{
+    return pimpl_->callAdvancedInformation();
+}
+
 void
 CallModel::muteMedia(const QString& callId, const QString& label, bool mute)
 {
@@ -950,6 +962,25 @@ CallModelPimpl::CallModelPimpl(const CallModel& linked,
 
 CallModelPimpl::~CallModelPimpl() {}
 
+QVariantList
+CallModelPimpl::callAdvancedInformation()
+{
+    QVariantList advancedInformationList;
+
+    QStringList callList = CallManager::instance().getCallList(linked.owner.id);
+    for (const auto& callId : callList) {
+        MapStringString mapStringDetailsList = CallManager::instance()
+                                                   .getCallDetails(linked.owner.id, callId);
+        QVariantMap detailsList = mapStringStringToQVariantMap(mapStringDetailsList);
+
+        detailsList.insert(CALL_ID, callId);
+        detailsList.insert(HARDWARE_ACCELERATION, lrc.getAVModel().getHardwareAcceleration());
+        advancedInformationList.append(detailsList);
+    }
+
+    return advancedInformationList;
+}
+
 void
 CallModelPimpl::initCallFromDaemon()
 {
diff --git a/src/libclient/directrenderer.cpp b/src/libclient/directrenderer.cpp
index a6a292e5f..47df29f2a 100644
--- a/src/libclient/directrenderer.cpp
+++ b/src/libclient/directrenderer.cpp
@@ -33,10 +33,19 @@ using namespace lrc::api::video;
 struct DirectRenderer::Impl : public QObject
 {
     Q_OBJECT
+private:
+    int fpsC;
+    int fps;
+
 public:
+    std::chrono::time_point<std::chrono::system_clock> lastFrameDebug;
+
     Impl(DirectRenderer* parent)
         : QObject(nullptr)
         , parent_(parent)
+        , fpsC(0)
+        , fps(0)
+        , lastFrameDebug(std::chrono::system_clock::now())
     {
         configureTarget();
         if (!VideoManager::instance().registerSinkTarget(parent_->id(), target))
@@ -81,6 +90,16 @@ public:
             QMutexLocker lk(&mutex);
             frameBufferPtr = std::move(buf);
         }
+        // compute FPS
+        ++fpsC;
+        auto currentTime = std::chrono::system_clock::now();
+        const std::chrono::duration<double> seconds = currentTime - lastFrameDebug;
+        if (seconds.count() >= FPS_RATE_SEC) {
+            fps = static_cast<int>(fpsC / seconds.count());
+            fpsC = 0;
+            lastFrameDebug = currentTime;
+            parent_->setFPS(fps);
+        }
 
         Q_EMIT parent_->frameUpdated();
     };
diff --git a/src/libclient/private/smartInfoHub_p.h b/src/libclient/private/smartInfoHub_p.h
index 83b90c5b1..8dd19065e 100644
--- a/src/libclient/private/smartInfoHub_p.h
+++ b/src/libclient/private/smartInfoHub_p.h
@@ -18,7 +18,6 @@
 
 /* widget_p.h (_p means private) */
 #include <QObject>
-#include "../smartinfohub.h"
 #include "typedefs.h"
 
 #pragma once
diff --git a/src/libclient/qtwrapper/conversions_wrap.hpp b/src/libclient/qtwrapper/conversions_wrap.hpp
index cf20d60d1..7389dc596 100644
--- a/src/libclient/qtwrapper/conversions_wrap.hpp
+++ b/src/libclient/qtwrapper/conversions_wrap.hpp
@@ -43,6 +43,16 @@
 #define LOG_LIBJAMI_SIGNAL4(name, arg, arg2, arg3, arg4)
 #endif
 
+inline QVariantMap
+mapStringStringToQVariantMap(const MapStringString& map)
+{
+    QVariantMap convertedMap;
+    for (auto i = map.begin(); i != map.end(); i++) {
+        convertedMap.insert(i.key(), i.value());
+    }
+    return convertedMap;
+}
+
 inline MapStringString
 convertMap(const std::map<std::string, std::string>& m)
 {
diff --git a/src/libclient/renderer.cpp b/src/libclient/renderer.cpp
index 22f6fea4a..e79ceb51f 100644
--- a/src/libclient/renderer.cpp
+++ b/src/libclient/renderer.cpp
@@ -34,6 +34,12 @@ Renderer::Renderer(const QString& id, const QSize& res)
 
 Renderer::~Renderer() {}
 
+int
+Renderer::fps() const
+{
+    return fps_;
+}
+
 QString
 Renderer::id() const
 {
@@ -45,6 +51,22 @@ Renderer::size() const
 {
     return size_;
 }
+void
+Renderer::setFPS(int fps)
+{
+    fps_ = fps;
+    Q_EMIT fpsChanged();
+}
+
+MapStringString
+Renderer::getInfos() const
+{
+    MapStringString map;
+    map[ID] = id();
+    map[FPS] = QString::number(fps());
+    map[RES] = QString::number(size().width()) + " * " + QString::number(size().height());
+    return map;
+}
 
 } // namespace video
 } // namespace lrc
diff --git a/src/libclient/renderer.h b/src/libclient/renderer.h
index df7419768..f850e2f77 100644
--- a/src/libclient/renderer.h
+++ b/src/libclient/renderer.h
@@ -36,9 +36,19 @@ class Renderer : public QObject
 {
     Q_OBJECT
 public:
+    constexpr static const char ID[] = "ID";
+    constexpr static const char FPS[] = "FPS";
+    constexpr static const char RES[] = "RES";
+    constexpr static const int FPS_RATE_SEC = 1;
+
     Renderer(const QString& id, const QSize& res);
     virtual ~Renderer();
 
+    /**
+     * @return renderer's fps
+     */
+    int fps() const;
+
     /**
      * @return renderer's id
      */
@@ -54,6 +64,13 @@ public:
      */
     virtual lrc::api::video::Frame currentFrame() const = 0;
 
+    /**
+     * set fps
+     */
+    void setFPS(int fps);
+
+    MapStringString getInfos() const;
+
 public Q_SLOTS:
     virtual void startRendering() = 0;
     virtual void stopRendering() = 0;
@@ -63,10 +80,12 @@ Q_SIGNALS:
     void stopped();
     void started(const QSize& size);
     void frameBufferRequested(AVFrame* avFrame);
+    void fpsChanged();
 
 private:
     QString id_;
     QSize size_;
+    int fps_;
 };
 
 } // namespace video
diff --git a/src/libclient/shmrenderer.cpp b/src/libclient/shmrenderer.cpp
index 0a2f9b892..3a0ca6424 100644
--- a/src/libclient/shmrenderer.cpp
+++ b/src/libclient/shmrenderer.cpp
@@ -90,10 +90,7 @@ public:
         , fpsC(0)
         , fps(0)
         , timer(new QTimer(this))
-#ifdef DEBUG_FPS
-        , frameCount(0)
         , lastFrameDebug(std::chrono::system_clock::now())
-#endif
     {
         timer->setInterval(33);
         connect(timer, &QTimer::timeout, [this]() { Q_EMIT parent_->frameUpdated(); });
@@ -110,7 +107,6 @@ public:
     }
 
     // Constants
-    constexpr static const int FPS_RATE_SEC = 1;
     constexpr static const int FRAME_CHECK_RATE_HZ = 120;
 
     // Lock the memory while the copy is being made
@@ -172,9 +168,10 @@ public:
         auto currentTime = std::chrono::system_clock::now();
         const std::chrono::duration<double> seconds = currentTime - lastFrameDebug;
         if (seconds.count() >= FPS_RATE_SEC) {
-            fps = (int) (fpsC / seconds.count());
+            fps = static_cast<int>(fpsC / seconds.count());
             fpsC = 0;
             lastFrameDebug = currentTime;
+            parent_->setFPS(fps);
 #ifdef DEBUG_FPS
             qDebug() << this << ": FPS " << fps;
 #endif
diff --git a/src/libclient/smartinfohub.cpp b/src/libclient/smartinfohub.cpp
deleted file mode 100644
index dd9d11ba9..000000000
--- a/src/libclient/smartinfohub.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/****************************************************************************
- *    Copyright (C) 2016-2022 Savoir-faire Linux Inc.                       *
- *   Author: Olivier Grégoire <olivier.gregoire@savoirfairelinux.com>       *
- *                                                                          *
- *   This library is free software; you can redistribute it and/or          *
- *   modify it under the terms of the GNU Lesser General Public             *
- *   License as published by the Free Software Foundation; either           *
- *   version 2.1 of the License, or (at your option) any later version.     *
- *                                                                          *
- *   This library 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      *
- *   Lesser 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 "smartinfohub.h"
-#include "private/smartInfoHub_p.h"
-
-#include <dbus/videomanager.h>
-#include <dbus/callmanager.h>
-#include <dbus/callmanager.h>
-
-SmartInfoHub::SmartInfoHub()
-{
-    d_ptr = new SmartInfoHubPrivate;
-    connect(&CallManager::instance(),
-            &CallManagerInterface::SmartInfo,
-            this,
-            &SmartInfoHub::slotSmartInfo,
-            Qt::QueuedConnection);
-}
-
-SmartInfoHub::~SmartInfoHub() {}
-
-void
-SmartInfoHub::start()
-{
-    CallManager::instance().startSmartInfo(d_ptr->m_refreshTimeInformationMS);
-}
-
-void
-SmartInfoHub::stop()
-{
-    CallManager::instance().stopSmartInfo();
-}
-
-SmartInfoHub&
-SmartInfoHub::instance()
-{
-    // Singleton
-    static SmartInfoHub instance_;
-    return instance_;
-}
-
-void
-SmartInfoHub::setRefreshTime(uint32_t timeMS)
-{
-    d_ptr->m_refreshTimeInformationMS = timeMS;
-}
-
-// Retrieve information from the map and implement all the variables
-void
-SmartInfoHub::slotSmartInfo(const MapStringString& map)
-{
-    for (int i = 0; i < map.size(); i++)
-        d_ptr->m_information[map.keys().at(i)] = map[map.keys().at(i)];
-
-    Q_EMIT changed();
-}
-// Getter
-
-bool
-SmartInfoHub::isConference() const
-{
-    return (d_ptr->m_information["type"] == "conference");
-}
-
-float
-SmartInfoHub::localFps() const
-{
-    if (!d_ptr->m_information[LOCAL_FPS].isEmpty())
-        return d_ptr->m_information[LOCAL_FPS].toFloat();
-
-    return 0.0;
-}
-
-float
-SmartInfoHub::remoteFps() const
-{
-    if (!d_ptr->m_information[REMOTE_FPS].isEmpty())
-        return d_ptr->m_information[REMOTE_FPS].toFloat();
-
-    return 0.0;
-}
-
-int
-SmartInfoHub::remoteWidth() const
-{
-    if (!d_ptr->m_information[REMOTE_WIDTH].isEmpty())
-        return d_ptr->m_information[REMOTE_WIDTH].toInt();
-    else
-        return 0;
-}
-
-int
-SmartInfoHub::remoteHeight() const
-{
-    if (!d_ptr->m_information[REMOTE_HEIGHT].isEmpty())
-        return d_ptr->m_information[REMOTE_HEIGHT].toInt();
-    else
-        return 0;
-}
-
-int
-SmartInfoHub::localWidth() const
-{
-    if (!d_ptr->m_information[LOCAL_WIDTH].isEmpty())
-        return d_ptr->m_information[LOCAL_WIDTH].toInt();
-    else
-        return 0;
-}
-
-int
-SmartInfoHub::localHeight() const
-{
-    if (!d_ptr->m_information[LOCAL_HEIGHT].isEmpty())
-        return d_ptr->m_information[LOCAL_HEIGHT].toInt();
-    else
-        return 0;
-}
-
-QString
-SmartInfoHub::callID() const
-{
-    if (!d_ptr->m_information[CALL_ID].isEmpty())
-        return d_ptr->m_information[CALL_ID];
-    else
-        return SmartInfoHubPrivate::DEFAULT_RETURN_VALUE_QSTRING;
-}
-
-QString
-SmartInfoHub::localVideoCodec() const
-{
-    if (!d_ptr->m_information[LOCAL_VIDEO_CODEC].isEmpty())
-        return d_ptr->m_information[LOCAL_VIDEO_CODEC];
-    else
-        return SmartInfoHubPrivate::DEFAULT_RETURN_VALUE_QSTRING;
-}
-
-QString
-SmartInfoHub::localAudioCodec() const
-{
-    if (!d_ptr->m_information[LOCAL_AUDIO_CODEC].isEmpty())
-        return d_ptr->m_information[LOCAL_AUDIO_CODEC];
-    else
-        return SmartInfoHubPrivate::DEFAULT_RETURN_VALUE_QSTRING;
-}
-
-QString
-SmartInfoHub::remoteVideoCodec() const
-{
-    if (!d_ptr->m_information[REMOTE_VIDEO_CODEC].isEmpty())
-        return d_ptr->m_information[REMOTE_VIDEO_CODEC];
-    else
-        return SmartInfoHubPrivate::DEFAULT_RETURN_VALUE_QSTRING;
-}
-
-QString
-SmartInfoHub::remoteAudioCodec() const
-{
-    if (!d_ptr->m_information[REMOTE_AUDIO_CODEC].isEmpty())
-        return d_ptr->m_information[REMOTE_AUDIO_CODEC];
-    else
-        return SmartInfoHubPrivate::DEFAULT_RETURN_VALUE_QSTRING;
-}
diff --git a/src/libclient/smartinfohub.h b/src/libclient/smartinfohub.h
deleted file mode 100644
index 42b84e028..000000000
--- a/src/libclient/smartinfohub.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
- *    Copyright (C) 2016-2022 Savoir-faire Linux Inc.                       *
- *   Author: Olivier Grégoire <olivier.gregoire@savoirfairelinux.com>       *
- *                                                                          *
- *   This library is free software; you can redistribute it and/or          *
- *   modify it under the terms of the GNU Lesser General Public             *
- *   License as published by the Free Software Foundation; either           *
- *   version 2.1 of the License, or (at your option) any later version.     *
- *                                                                          *
- *   This library 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      *
- *   Lesser 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 <QObject>
-#include "typedefs.h"
-
-class SmartInfoHubPrivate;
-
-class SmartInfoHub final : public QObject
-{
-    Q_OBJECT
-public:
-    // Singleton
-    static SmartInfoHub& instance();
-
-    void start();
-    void stop();
-
-    void setRefreshTime(uint32_t timeMS);
-
-    // Getter
-    float localFps() const;
-    float remoteFps() const;
-    int remoteWidth() const;
-    int remoteHeight() const;
-    int localWidth() const;
-    int localHeight() const;
-    QString callID() const;
-    QString localVideoCodec() const;
-    QString localAudioCodec() const;
-    QString remoteVideoCodec() const;
-    QString remoteAudioCodec() const;
-    bool isConference() const;
-
-Q_SIGNALS:
-    /// Emitted when informations have changed
-    void changed();
-
-private Q_SLOTS:
-    void slotSmartInfo(const MapStringString& info);
-
-private:
-    // use to initialise the connection between the Qsignal and the lambda function
-    SmartInfoHub();
-    virtual ~SmartInfoHub();
-
-    SmartInfoHubPrivate* d_ptr;
-};
-- 
GitLab