1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-29 20:05:36 +02:00

feature: improve advanced call information overlay

GitLab: #925

Change-Id: Id75b14bf431ac421b135beb75918dbf37a81d53c
This commit is contained in:
Nicolas Vengeon 2022-11-21 17:46:41 -05:00 committed by Sébastien Blin
parent a28d5c5c55
commit c977c732f2
24 changed files with 853 additions and 252 deletions

0
build.py Normal file → Executable file
View file

2
daemon

@ -1 +1 @@
Subproject commit 9d76cf5cc767e33ab06054bfa40ee45f671002bd
Subproject commit eb9c52cee45660f68334adb32a23d6f743d6bcf4

View file

@ -37,19 +37,27 @@
AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent)
, rendererInformationListModel_(std::make_unique<RendererInformationListModel>())
{
set_renderersInfoList(QVariant::fromValue(rendererInformationListModel_.get()));
connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::audioDeviceEvent,
this,
&AvAdapter::onAudioDeviceEvent);
// QueuedConnection mandatory to avoid deadlock
connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::rendererStarted,
this,
&AvAdapter::onRendererStarted);
&AvAdapter::onRendererStarted,
Qt::QueuedConnection);
connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::onRendererInfosUpdated,
&lrc::api::AVModel::rendererStopped,
this,
&AvAdapter::setRenderersInfoList);
&AvAdapter::onRendererStopped);
connect(&lrcInstance_->avModel(),
&lrc::api::AVModel::onRendererFpsChange,
this,
&AvAdapter::updateRenderersFPSInfo);
}
// The top left corner of primary screen is (0, 0).
@ -323,6 +331,12 @@ AvAdapter::onRendererStarted(const QString& id, const QSize& size)
if (callId.isEmpty()) {
return;
}
// update renderer Information list
auto& avModel = lrcInstance_->avModel();
auto rendererInfo = avModel.getRenderersInfo(id)[0];
rendererInformationListModel_->addElement(qMakePair(id, rendererInfo));
auto callModel = lrcInstance_->getCurrentCallModel();
auto renderDevice = callModel->getCurrentRenderedDevice(callId);
if (!id.contains("://"))
@ -331,6 +345,12 @@ AvAdapter::onRendererStarted(const QString& id, const QSize& size)
set_currentRenderingDeviceType(renderDevice.type);
}
void
AvAdapter::onRendererStopped(const QString& id)
{
rendererInformationListModel_->removeElement(id);
}
bool
AvAdapter::isSharing() const
{
@ -448,7 +468,23 @@ AvAdapter::setHardwareAcceleration(bool accelerate)
}
void
AvAdapter::setRenderersInfoList(QVariantList renderersInfo)
AvAdapter::resetRendererInfo()
{
set_renderersInfoList(renderersInfo);
rendererInformationListModel_->reset();
}
void
AvAdapter::setRendererInfo()
{
auto& avModel = lrcInstance_->avModel();
for (auto rendererInfo : avModel.getRenderersInfo()) {
rendererInformationListModel_->addElement(
qMakePair(rendererInfo["RENDERER_ID"], rendererInfo));
}
}
void
AvAdapter::updateRenderersFPSInfo(QPair<QString, QString> fpsInfo)
{
rendererInformationListModel_->updateFps(fpsInfo.first, fpsInfo.second);
}

View file

@ -26,6 +26,8 @@
#include <QString>
#include <qtutils.h>
#include "rendererinformationlistmodel.h"
class AvAdapter final : public QmlAdapterBase
{
Q_OBJECT
@ -36,7 +38,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)
QML_RO_PROPERTY(QVariant, renderersInfoList)
public:
explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr);
@ -103,14 +105,18 @@ protected:
Q_INVOKABLE void increaseCodecPriority(unsigned int id, bool isVideo);
Q_INVOKABLE void decreaseCodecPriority(unsigned int id, bool isVideo);
Q_INVOKABLE void resetRendererInfo();
Q_INVOKABLE void setRendererInfo();
// TODO: to be removed
Q_INVOKABLE bool getHardwareAcceleration();
Q_INVOKABLE void setHardwareAcceleration(bool accelerate);
private Q_SLOTS:
void setRenderersInfoList(QVariantList renderersInfo);
void updateRenderersFPSInfo(QPair<QString, QString> fpsInfo);
void onAudioDeviceEvent();
void onRendererStarted(const QString& id, const QSize& size);
void onRendererStopped(const QString& id);
private:
// Get screens arrangement rect relative to primary screen.
@ -118,4 +124,6 @@ private:
// Get the screen number
int getScreenNumber(int screenId = 0) const;
std::unique_ptr<RendererInformationListModel> rendererInformationListModel_;
};

View file

@ -40,7 +40,10 @@
CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent)
: QmlAdapterBase(instance, parent)
, systemTray_(systemTray)
, callInformationListModel_(std::make_unique<CallInformationListModel>())
{
set_callInformationList(QVariant::fromValue(callInformationListModel_.get()));
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation);
@ -127,7 +130,6 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
auto& callModel = accInfo.callModel;
const auto call = callModel->getCall(callId);
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
const auto& convInfo = lrcInstance_->getConversationFromCallId(callId, accountId);
if (convInfo.uid.isEmpty() || call.isOutgoing)
@ -150,7 +152,7 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId
auto from = accInfo.conversationModel->title(convInfo.uid);
auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid);
systemTray_->showNotification(notifId,
tr("%1 missed call").arg(to),
tr("Missed call"),
tr("Missed call with %1").arg(from),
NotificationType::CHAT,
Utils::QImageToByteArray(convAvatar));
@ -170,6 +172,7 @@ CallAdapter::onParticipantAdded(const QString& callId, int index)
try {
if (lrcInstance_->get_selectedConvUid().isEmpty())
return;
const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid(
lrcInstance_->get_selectedConvUid());
if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) {
@ -191,6 +194,7 @@ CallAdapter::onParticipantRemoved(const QString& callId, int index)
try {
if (lrcInstance_->get_selectedConvUid().isEmpty())
return;
const auto& currentConvInfo = accInfo.conversationModel.get()->getConversationForUid(
lrcInstance_->get_selectedConvUid());
if (callId != currentConvInfo->get().callId && callId != currentConvInfo->get().confId) {
@ -223,6 +227,27 @@ CallAdapter::onParticipantUpdated(const QString& callId, int index)
}
}
void
CallAdapter::onCallStarted(const QString& callId)
{
if (lrcInstance_->get_selectedConvUid().isEmpty())
return;
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId_);
auto& callModel = accInfo.callModel;
// update call Information list by adding the new information related to the callId
callInformationListModel_->addElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)));
}
void
CallAdapter::onCallEnded(const QString& callId)
{
if (lrcInstance_->get_selectedConvUid().isEmpty())
return;
// update call Information list by removing information related to the callId
callInformationListModel_->removeElement(callId);
}
void
CallAdapter::onCallStatusChanged(const QString& callId, int code)
{
@ -570,7 +595,6 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid)
{
auto& accInfo = lrcInstance_->getAccountInfo(accountId);
auto title = accInfo.conversationModel->title(convUid);
auto to = lrcInstance_->accountModel().bestNameForAccount(accountId);
auto preferences = accInfo.conversationModel->getConversationPreferences(convUid);
// Ignore notifications for this conversation
@ -581,7 +605,7 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid)
auto convAvatar = Utils::conversationAvatar(lrcInstance_, convUid, QSize(50, 50), accountId);
auto notifId = QString("%1;%2").arg(accountId).arg(convUid);
systemTray_->showNotification(notifId,
tr("%1 incoming call").arg(to),
tr("Incoming call"),
tr("%1 is calling you").arg(title),
NotificationType::CALL,
Utils::QImageToByteArray(convAvatar));
@ -620,6 +644,18 @@ CallAdapter::connectCallModel(const QString& accountId)
&CallAdapter::onParticipantUpdated,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callStarted,
this,
&CallAdapter::onCallStarted,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callEnded,
this,
&CallAdapter::onCallEnded,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callStatusChanged,
this,
@ -1071,13 +1107,39 @@ CallAdapter::getCallDurationTime(const QString& accountId, const QString& convUi
return QString();
}
void
CallAdapter::resetCallInfo()
{
callInformationListModel_->reset();
}
void
CallAdapter::setCallInfo()
{
try {
auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel;
for (auto callId : callModel->getCallIds()) {
callInformationListModel_->addElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)));
}
} catch (const std::exception& e) {
qWarning() << e.what();
}
}
void
CallAdapter::updateAdvancedInformation()
{
try {
auto& callModel = lrcInstance_->accountModel().getAccountInfo(accountId_).callModel;
if (callModel)
set_callInformation(QVariantList::fromList(callModel->getAdvancedInformation()));
for (auto callId : callModel->getCallIds()) {
if (!callInformationListModel_->addElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)))) {
callInformationListModel_->editElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)));
}
}
} catch (const std::exception& e) {
qWarning() << e.what();
}

View file

@ -31,13 +31,15 @@
#include <QVariant>
#include <QSystemTrayIcon>
#include "callInformationListModel.h"
class SystemTray;
class CallAdapter final : public QmlAdapterBase
{
Q_OBJECT
QML_PROPERTY(bool, hasCall)
QML_RO_PROPERTY(QVariantList, callInformation)
QML_RO_PROPERTY(QVariant, callInformationList)
public:
QTimer* timer;
@ -92,6 +94,8 @@ public:
const QString& accountId = {},
bool forceCallOnly = false);
Q_INVOKABLE QString getCallDurationTime(const QString& accountId, const QString& convUid);
Q_INVOKABLE void resetCallInfo();
Q_INVOKABLE void setCallInfo();
Q_INVOKABLE void updateAdvancedInformation();
Q_SIGNALS:
@ -112,6 +116,8 @@ public Q_SLOTS:
void onParticipantAdded(const QString& callId, int index);
void onParticipantRemoved(const QString& callId, int index);
void onParticipantUpdated(const QString& callId, int index);
void onCallStarted(const QString& callId);
void onCallEnded(const QString& callId);
private:
void showNotification(const QString& accountId, const QString& convUid);
@ -126,4 +132,6 @@ private:
QScopedPointer<CallOverlayModel> overlayModel_;
QScopedPointer<CallParticipantsModel> participantsModel_;
VectorString currentConfSubcalls_;
std::unique_ptr<CallInformationListModel> callInformationListModel_;
};

View file

@ -299,6 +299,19 @@ Item {
property string raiseHand: qsTr("Raise hand")
property string layoutSettings: qsTr("Layout settings")
//advanced information
property string renderersInformation: qsTr("Renderers information")
property string callInformation: qsTr("Call information")
property string peerNumber: qsTr("Peer number")
property string callId: qsTr("Call id")
property string sockets: qsTr("Sockets")
property string videoCodec: qsTr("Video codec")
property string hardwareAcceleration: qsTr("Hardware acceleration")
property string videoBitrate: qsTr("Video bitrate")
property string audioCodec: qsTr("Audio codec")
property string rendererId: qsTr("Renderer id")
property string fps_short: qsTr("Fps")
// Share location/position
property string shareLocation: qsTr("Share location")
property string stopSharingLocation: qsTr("Stop sharing")

View file

@ -376,9 +376,11 @@ Item {
//Call information
property real textFontPointSize: calcSize(10)
property real titleFontPointSize: calcSize(13)
property color callInfoColor: chatviewTextColor
property color callInfoColor: whiteColor
property int callInformationElementsSpacing: 5
property int callInformationBlockSpacing: 25
property int callInformationlayoutMargins: 10
// Jami switch
property real switchIndicatorRadius: 30

View file

@ -0,0 +1,255 @@
/*
* 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"
Popup {
id: root
property real maxHeight: parent.height * 40 / 100
property real maxTextWidth: parent.width * 30 / 100
property var advancedList
property var fps
width: container.width
height: container.height
closePolicy: Popup.NoAutoClosed
onClosed: {
CallAdapter.stopTimerInformation()
}
onOpened: {
AvAdapter.resetRendererInfo()
CallAdapter.resetCallInfo()
CallAdapter.setCallInfo()
AvAdapter.setRendererInfo()
}
background: Rectangle {
color: JamiTheme.transparentColor
}
Rectangle {
id: container
color: JamiTheme.blackColor
opacity: 0.85
radius: 10
width: windowContent.width
height: windowContent.height
PushButton {
id: closeButton
anchors.top: container.top
anchors.topMargin: 5
anchors.right: container.right
anchors.rightMargin: 5
normalColor: JamiTheme.transparentColor
imageColor: JamiTheme.callInfoColor
source: JamiResources.round_close_24dp_svg
circled: false
toolTipText: JamiStrings.close
onClicked: {
root.close()
}
}
RowLayout {
id: windowContent
ColumnLayout {
spacing: JamiTheme.callInformationBlockSpacing
Layout.margins: JamiTheme.callInformationlayoutMargins
Layout.preferredWidth: callInfoListview.width
Layout.alignment: Qt.AlignTop
Text{
id: textTest
color: JamiTheme.callInfoColor
text: JamiStrings.callInformation
font.pointSize: JamiTheme.titleFontPointSize
}
ListView {
id: callInfoListview
model: advancedList
Layout.preferredWidth: root.maxTextWidth
Layout.preferredHeight: contentItem.childrenRect.height < root.maxHeight ? contentItem.childrenRect.height : root.maxHeight
spacing: JamiTheme.callInformationBlockSpacing
clip: true
delegate: Column {
spacing: JamiTheme.callInformationElementsSpacing
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.callId + ": " + CALL_ID
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
Text {
function stringWithoutRing(peerNumber){
return peerNumber.replace("@ring.dht","") ;
}
color: JamiTheme.callInfoColor
text: JamiStrings.peerNumber + ": " + stringWithoutRing(PEER_NUMBER)
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
Column {
id: socketLayout
property bool showAll: false
width: callInfoListview.width
RowLayout {
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.sockets
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: socketLayout.width
}
PushButton {
source: socketLayout.showAll ? JamiResources.expand_less_24dp_svg : JamiResources.expand_more_24dp_svg
normalColor: JamiTheme.transparentColor
Layout.preferredWidth: 20
Layout.preferredHeight: 20
imageColor: JamiTheme.callInfoColor
onClicked: {
socketLayout.showAll = !socketLayout.showAll
}
}
}
Text {
color: JamiTheme.callInfoColor
text: SOCKETS
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
visible: socketLayout.showAll
width: socketLayout.width
}
}
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.videoCodec + ": " + VIDEO_CODEC
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.audioCodec + ": " + AUDIO_CODEC + " " + AUDIO_SAMPLE_RATE + " Hz"
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.hardwareAcceleration + ": " + HARDWARE_ACCELERATION
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.videoBitrate + ": " + VIDEO_BITRATE + " bps"
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: callInfoListview.width
}
}
}
}
ColumnLayout {
spacing: JamiTheme.callInformationBlockSpacing
Layout.margins: JamiTheme.callInformationlayoutMargins
Layout.preferredWidth: renderersInfoListview.width
Layout.alignment: Qt.AlignTop
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.renderersInformation
font.pointSize: JamiTheme.titleFontPointSize
}
ListView {
id: renderersInfoListview
Layout.preferredWidth: root.maxTextWidth
Layout.preferredHeight: contentItem.childrenRect.height < root.maxHeight ? contentItem.childrenRect.height : root.maxHeight
spacing: JamiTheme.callInformationBlockSpacing
model: fps
clip: true
delegate: Column {
spacing: JamiTheme.callInformationElementsSpacing
Text{
color: JamiTheme.callInfoColor
text: JamiStrings.rendererId + ": " + RENDERER_ID
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: renderersInfoListview.width
}
Text {
id: testText
color: JamiTheme.callInfoColor
text: JamiStrings.fps_short + ": " + FPS
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: renderersInfoListview.width
}
Text {
color: JamiTheme.callInfoColor
text: JamiStrings.resolution + ": " + RES
font.pointSize: JamiTheme.textFontPointSize
wrapMode: Text.WrapAnywhere
width: renderersInfoListview.width
}
}
}
}
}
}
}

View file

@ -1,213 +0,0 @@
/*
* 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
}
}
}
}
}
}
}
}

View file

@ -46,7 +46,7 @@ Item {
SelectScreenWindowCreation.destroySelectScreenWindow()
ScreenRubberBandCreation.destroyScreenRubberBandWindow()
PluginHandlerPickerCreation.closePluginHandlerPicker()
callInformationWindow.close()
callInformationOverlay.close()
}
// x, y position does not need to be translated
@ -71,12 +71,16 @@ Item {
y: root.height / 2 - sipInputPanel.height / 2
}
CallInformationWindow {
id: callInformationWindow
CallInformationOverlay {
id: callInformationOverlay
visible: false
advancedList: CallAdapter.callInformation
advancedList: CallAdapter.callInformationList
fps: AvAdapter.renderersInfoList
Component.onDestruction: {
CallAdapter.stopTimerInformation();
}
}
JamiFileDialog {

View file

@ -192,7 +192,7 @@ ContextMenuAutoLoader {
onClicked: {
CallAdapter.startTimerInformation();
callInformationWindow.show()
callInformationOverlay.open()
}
}
]

View file

@ -45,6 +45,8 @@
#include "smartlistmodel.h"
#include "conversationlistmodelbase.h"
#include "filestosendlistmodel.h"
#include "callInformationListModel.h"
#include "rendererinformationlistmodel.h"
#include "qrimageprovider.h"
#include "avatarimageprovider.h"
@ -170,6 +172,9 @@ registerTypes(QQmlEngine* engine,
QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel);
QML_REGISTERTYPE(NS_MODELS, SmartListModel);
QML_REGISTERTYPE(NS_MODELS, MessageListModel);
QML_REGISTERTYPE(NS_MODELS, CallInformationListModel);
QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel);
// Roles & type enums for models
QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList");

View file

@ -261,6 +261,8 @@ set(LIBCLIENT_SOURCES
behaviorcontroller.cpp
datatransfermodel.cpp
messagelistmodel.cpp
callInformationListModel.cpp
rendererinformationlistmodel.cpp
# communication
dbus/configurationmanager.cpp
@ -289,6 +291,8 @@ set(LIBCLIENT_HEADERS
vcard.h
namedirectory.h
messagelistmodel.h
callInformationListModel.h
rendererinformationlistmodel.h
# interfaces
interfaces/pixmapmanipulatori.h

View file

@ -276,17 +276,22 @@ public:
QSize getRendererSize(const QString& id);
video::Frame getRendererFrame(const QString& id);
bool useDirectRenderer() const;
/**
* Get Renderers information
* @param id (optional) : for a specific renderer or for all renderers
*/
QList<MapStringString> getRenderersInfo(QString id = {});
/**
* Update renderers information list
*/
Q_SLOT void updateRenderersInfo();
Q_SLOT void updateRenderersFPSInfo(QString rendererId);
Q_SIGNALS:
/**
* Emitted after an update of renderers information
* @param renderersInfoList Information on all renderers (RES, ID, FPS)
* Emitted after an update of renderer's fps
* @param pair of renderer id and its fps value
*/
void onRendererInfosUpdated(QVariantList renderersInfoList);
void onRendererFpsChange(QPair<QString, QString> fpsInfo);
/**
* Emitted when a renderer is started
* @param id of the renderer

View file

@ -397,6 +397,10 @@ public:
*/
QList<QVariant> getAdvancedInformation() const;
MapStringString advancedInformationForCallId(QString callId) const;
QStringList getCallIds() const;
Q_SIGNALS:
/**

View file

@ -173,21 +173,36 @@ AVModel::~AVModel()
}
}
void
AVModel::updateRenderersInfo()
QList<MapStringString>
AVModel::getRenderersInfo(QString id)
{
QVariantList renderersInfoList;
QList<MapStringString> infoList;
std::lock_guard<std::mutex> lk(pimpl_->renderers_mtx_);
for (auto r = pimpl_->renderers_.begin(); r != pimpl_->renderers_.end(); r++) {
QVariantMap qmap;
MapStringString 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);
if (id.isEmpty() || mapInfo["RENDERER_ID"] == id) {
qmap.insert(rend->RES, mapInfo["RES"]);
qmap.insert(rend->RENDERER_ID, mapInfo["RENDERER_ID"]);
qmap.insert(rend->FPS, mapInfo["FPS"]);
infoList.append(qmap);
}
}
Q_EMIT onRendererInfosUpdated(renderersInfoList);
return infoList;
return {};
}
void
AVModel::updateRenderersFPSInfo(QString rendererId)
{
auto it = std::find_if(pimpl_->renderers_.begin(),
pimpl_->renderers_.end(),
[&rendererId](const auto& c) {
return rendererId == c.second->getInfos()["RENDERER_ID"];
});
if (it != pimpl_->renderers_.end())
Q_EMIT onRendererFpsChange(qMakePair(rendererId, it->second->getInfos()["FPS"]));
}
bool
@ -925,7 +940,7 @@ AVModelPimpl::addRenderer(const QString& id, const QSize& res, const QString& sh
renderer,
&Renderer::fpsChanged,
this,
[this, id](void) { Q_EMIT linked_.updateRenderersInfo(); },
[this, id](void) { Q_EMIT linked_.updateRenderersFPSInfo(id); },
Qt::DirectConnection);
connect(
renderer,

View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2023 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "callInformationListModel.h"
CallInformationListModel::CallInformationListModel(QObject* parent)
: QAbstractListModel(parent)
{}
int
CallInformationListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return callsInfolist_.size();
}
QVariant
CallInformationListModel::data(const QModelIndex& index, int role) const
{
using namespace InfoList;
if (role == Role::CALL_ID)
return callsInfolist_[index.row()].first;
switch (role) {
#define X(var) \
case Role::var: \
return callsInfolist_[index.row()].second[#var];
CALLINFO_ROLES
#undef X
}
return QVariant();
}
bool
CallInformationListModel::addElement(QPair<QString, MapStringString> callInfo)
{
// check element existence
auto callId = callInfo.first;
auto it = std::find_if(callsInfolist_.begin(), callsInfolist_.end(), [&callId](const auto& c) {
return callId == c.first;
});
// if element doesn't exist
if (it == callsInfolist_.end()) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
callsInfolist_.append(callInfo);
endInsertRows();
return true;
}
return false;
}
void
CallInformationListModel::editElement(QPair<QString, MapStringString> callInfo)
{
auto it = std::find_if(callsInfolist_.begin(),
callsInfolist_.end(),
[&callInfo](const auto& c) { return callInfo.first == c.first; });
if (it != callsInfolist_.end()) {
// update infos
auto index = std::distance(callsInfolist_.begin(), it);
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
it->second = callInfo.second;
Q_EMIT dataChanged(modelIndex, modelIndex);
}
}
QHash<int, QByteArray>
CallInformationListModel::roleNames() const
{
using namespace InfoList;
QHash<int, QByteArray> roles;
#define X(var) roles[var] = #var;
CALLINFO_ROLES
#undef X
return roles;
}
void
CallInformationListModel::reset()
{
beginResetModel();
callsInfolist_.clear();
endResetModel();
}
void
CallInformationListModel::removeElement(QString callId)
{
auto it = std::find_if(callsInfolist_.begin(), callsInfolist_.end(), [&callId](const auto& c) {
return callId == c.first;
});
if (it != callsInfolist_.end()) {
auto elementIndex = std::distance(callsInfolist_.begin(), it);
beginRemoveRows(QModelIndex(), elementIndex, elementIndex);
callsInfolist_.remove(elementIndex);
endRemoveRows();
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2023 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "api/interaction.h"
#include <QAbstractListModel>
#define CALLINFO_ROLES \
X(CALL_ID) \
X(PEER_NUMBER) \
X(SOCKETS) \
X(VIDEO_CODEC) \
X(AUDIO_CODEC) \
X(AUDIO_SAMPLE_RATE) \
X(HARDWARE_ACCELERATION) \
X(VIDEO_BITRATE)
namespace InfoList {
Q_NAMESPACE
enum Role {
DummyRole = Qt::UserRole + 1,
#define X(role) role,
CALLINFO_ROLES
#undef X
};
Q_ENUM_NS(Role)
} // namespace InfoList
class CallInformationListModel : public QAbstractListModel
{
Q_OBJECT
public:
CallInformationListModel(QObject* parent = 0);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
Q_INVOKABLE QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool addElement(QPair<QString, MapStringString> callInfo);
void editElement(QPair<QString, MapStringString> callInfo);
QHash<int, QByteArray> roleNames() const override;
void reset();
void removeElement(QString callId);
protected:
using Role = InfoList::Role;
private:
QList<QPair<QString, MapStringString>> callsInfolist_;
};

View file

@ -24,6 +24,8 @@
#include "api/avmodel.h"
#include "api/behaviorcontroller.h"
#include "api/conversationmodel.h"
#include "api/codecmodel.h"
#include "api/contact.h"
#include "api/contactmodel.h"
#include "api/pluginmodel.h"
@ -56,6 +58,7 @@
using namespace libjami::Media;
constexpr static const char HARDWARE_ACCELERATION[] = "HARDWARE_ACCELERATION";
constexpr static const char AUDIO_CODEC[] = "AUDIO_CODEC";
constexpr static const char CALL_ID[] = "CALL_ID";
static std::uniform_int_distribution<int> dis {0, std::numeric_limits<int>::max()};
@ -133,6 +136,9 @@ public:
~CallModelPimpl();
QVariantList callAdvancedInformation();
MapStringString advancedInformationForCallId(QString callId);
QStringList getCallIds();
/**
* Send the profile VCard into a call
@ -389,7 +395,7 @@ CallModel::createCall(const QString& uri, bool isAudioOnly, VectorMapStringStrin
}
#ifdef ENABLE_LIBWRAP
auto callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList);
#else // dbus
#else // dbus
// do not use auto here (QDBusPendingReply<QString>)
QString callId = CallManager::instance().placeCallWithMedia(owner.id, uri, mediaList);
#endif // ENABLE_LIBWRAP
@ -419,6 +425,18 @@ CallModel::getAdvancedInformation() const
return pimpl_->callAdvancedInformation();
}
MapStringString
CallModel::advancedInformationForCallId(QString callId) const
{
return pimpl_->advancedInformationForCallId(callId);
}
QStringList
CallModel::getCallIds() const
{
return pimpl_->getCallIds();
}
void
CallModel::emplaceConversationConference(const QString& confId)
{
@ -1026,6 +1044,23 @@ CallModelPimpl::callAdvancedInformation()
return advancedInformationList;
}
MapStringString
CallModelPimpl::advancedInformationForCallId(QString callId)
{
MapStringString infoMap = CallManager::instance().getCallDetails(linked.owner.id, callId);
if (lrc.getAVModel().getHardwareAcceleration())
infoMap[HARDWARE_ACCELERATION] = "True";
else
infoMap[HARDWARE_ACCELERATION] = "False";
return infoMap;
}
QStringList
CallModelPimpl::getCallIds()
{
return CallManager::instance().getCallList(linked.owner.id);
}
void
CallModelPimpl::initCallFromDaemon()
{

View file

@ -62,7 +62,7 @@ MapStringString
Renderer::getInfos() const
{
MapStringString map;
map[ID] = id();
map[RENDERER_ID] = id();
map[FPS] = QString::number(fps());
map[RES] = QString::number(size().width()) + " * " + QString::number(size().height());
return map;

View file

@ -36,7 +36,7 @@ class Renderer : public QObject
{
Q_OBJECT
public:
constexpr static const char ID[] = "ID";
constexpr static const char RENDERER_ID[] = "RENDERER_ID";
constexpr static const char FPS[] = "FPS";
constexpr static const char RES[] = "RES";
constexpr static const int FPS_RATE_SEC = 1;

View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2023 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "rendererinformationlistmodel.h"
RendererInformationListModel::RendererInformationListModel(QObject* parent)
: QAbstractListModel(parent)
{}
int
RendererInformationListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return renderersInfoList_.size();
}
QVariant
RendererInformationListModel::data(const QModelIndex& index, int role) const
{
using namespace RendererInfoList;
if (role == Role::RENDERER_ID)
return renderersInfoList_[index.row()].first;
switch (role) {
#define X(var) \
case Role::var: \
return renderersInfoList_[index.row()].second[#var];
RENDERERINFO_ROLES
#undef X
}
return QVariant();
}
void
RendererInformationListModel::updateFps(QString rendererId, QString fps)
{
auto it = std::find_if(renderersInfoList_.begin(),
renderersInfoList_.end(),
[&rendererId](const auto& c) { return rendererId == c.first; });
if (it != renderersInfoList_.end()) {
// update fps
auto index = std::distance(renderersInfoList_.begin(), it);
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
it->second["FPS"] = fps;
Q_EMIT dataChanged(modelIndex, modelIndex, {Role::FPS});
}
}
void
RendererInformationListModel::addElement(QPair<QString, MapStringString> rendererInfo)
{
// check element existence
auto rendererId = rendererInfo.first;
auto it = std::find_if(renderersInfoList_.begin(),
renderersInfoList_.end(),
[&rendererId](const auto& c) { return rendererId == c.first; });
// if element doesn't exist
if (it == renderersInfoList_.end()) {
beginInsertRows(QModelIndex(), rowCount(), rowCount());
renderersInfoList_.append(rendererInfo);
endInsertRows();
}
}
void
RendererInformationListModel::removeElement(QString rendererId)
{
auto it = std::find_if(renderersInfoList_.begin(),
renderersInfoList_.end(),
[&rendererId](const auto& c) { return rendererId == c.first; });
if (it != renderersInfoList_.end()) {
auto elementIndex = std::distance(renderersInfoList_.begin(), it);
beginRemoveRows(QModelIndex(), elementIndex, elementIndex);
renderersInfoList_.remove(elementIndex);
endRemoveRows();
}
}
QHash<int, QByteArray>
RendererInformationListModel::roleNames() const
{
using namespace RendererInfoList;
QHash<int, QByteArray> roles;
#define X(var) roles[var] = #var;
RENDERERINFO_ROLES
#undef X
return roles;
}
void
RendererInformationListModel::reset()
{
beginResetModel();
renderersInfoList_.clear();
endResetModel();
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "api/interaction.h"
#include <QAbstractListModel>
#define RENDERERINFO_ROLES \
X(RENDERER_ID) \
X(RES) \
X(FPS)
namespace RendererInfoList {
Q_NAMESPACE
enum Role {
DummyRole = Qt::UserRole + 1,
#define X(role) role,
RENDERERINFO_ROLES
#undef X
};
Q_ENUM_NS(Role)
} // namespace RendererInfoList
class RendererInformationListModel : public QAbstractListModel
{
Q_OBJECT
public:
RendererInformationListModel(QObject* parent = 0);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
Q_INVOKABLE QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
void updateFps(QString rendererId, QString fps);
void addElement(QPair<QString, MapStringString> rendererInfo);
void removeElement(QString rendererId);
QHash<int, QByteArray> roleNames() const override;
void reset();
protected:
using Role = RendererInfoList::Role;
private:
QList<QPair<QString, MapStringString>> renderersInfoList_;
};