diff --git a/linphone-desktop/assets/languages/en.ts b/linphone-desktop/assets/languages/en.ts
index 8bce1f90d..677e1d6f0 100644
--- a/linphone-desktop/assets/languages/en.ts
+++ b/linphone-desktop/assets/languages/en.ts
@@ -175,6 +175,88 @@
Realm
+
+ CallModel
+
+ callStatsCodec
+ Codec
+
+
+ callStatsUploadBandwidth
+ Upload bandwidth
+
+
+ callStatsDownloadBandwidth
+ Download bandwidth
+
+
+ callStatsIceState
+ ICE state
+
+
+ callStatsIpFamily
+ IP family
+
+
+ callStatsSenderLossRate
+ Sender loss rate
+
+
+ callStatsReceiverLossRate
+ Receiver loss rate
+
+
+ callStatsJitterBuffer
+ Jitter buffer
+
+
+ callStatsSentVideoDefinition
+ Sent video definition
+
+
+ callStatsReceivedVideoDefinition
+ Received video definition
+
+
+ iceStateNotActivated
+ Not activated
+
+
+ iceStateFailed
+ Failed
+
+
+ iceStateInProgress
+ In progress
+
+
+ iceStateReflexiveConnection
+ Reflexive connection
+
+
+ iceStateHostConnection
+ Host connection
+
+
+ iceStateRelayConnection
+ Relay connection
+
+
+ iceStateInvalid
+ Invalid
+
+
+
+ CallStatistics
+
+ audioStatsLabel
+ Audio
+
+
+ videoStatsLabel
+ Video
+
+
Calls
diff --git a/linphone-desktop/assets/languages/fr.ts b/linphone-desktop/assets/languages/fr.ts
index 19d9a7917..39caace5a 100644
--- a/linphone-desktop/assets/languages/fr.ts
+++ b/linphone-desktop/assets/languages/fr.ts
@@ -175,6 +175,88 @@
Realm
+
+ CallModel
+
+ callStatsCodec
+ Codec
+
+
+ callStatsUploadBandwidth
+ Bande passante d'envoi
+
+
+ callStatsDownloadBandwidth
+ Bande passante de réception
+
+
+ callStatsIceState
+ État ICE
+
+
+ callStatsIpFamily
+ Famille IP
+
+
+ callStatsSenderLossRate
+ Taux de perte en envoi
+
+
+ callStatsReceiverLossRate
+ Taux de perte en réception
+
+
+ callStatsJitterBuffer
+ Tampon de gigue
+
+
+ callStatsSentVideoDefinition
+ Définition vidéo envoyée
+
+
+ callStatsReceivedVideoDefinition
+ Définition vidéo reçue
+
+
+ iceStateNotActivated
+
+
+
+ iceStateFailed
+
+
+
+ iceStateInProgress
+
+
+
+ iceStateReflexiveConnection
+
+
+
+ iceStateHostConnection
+
+
+
+ iceStateRelayConnection
+
+
+
+ iceStateInvalid
+
+
+
+
+ CallStatistics
+
+ audioStatsLabel
+ Audio
+
+
+ videoStatsLabel
+ Vidéo
+
+
Calls
diff --git a/linphone-desktop/resources.qrc b/linphone-desktop/resources.qrc
index f155f8c4f..20600004a 100644
--- a/linphone-desktop/resources.qrc
+++ b/linphone-desktop/resources.qrc
@@ -280,6 +280,7 @@
ui/modules/Linphone/Calls/CallControls.qml
ui/modules/Linphone/Calls/Calls.js
ui/modules/Linphone/Calls/Calls.qml
+ ui/modules/Linphone/Calls/CallStatistics.qml
ui/modules/Linphone/Chat/Chat.js
ui/modules/Linphone/Chat/Chat.qml
ui/modules/Linphone/Chat/Event.qml
@@ -307,6 +308,7 @@
ui/modules/Linphone/Styles/Blocks/CardBlockStyle.qml
ui/modules/Linphone/Styles/Blocks/RequestBlockStyle.qml
ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml
+ ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml
ui/modules/Linphone/Styles/Calls/CallsStyle.qml
ui/modules/Linphone/Styles/Chat/ChatStyle.qml
ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml
diff --git a/linphone-desktop/src/components/call/CallModel.cpp b/linphone-desktop/src/components/call/CallModel.cpp
index c083bee1c..6ec5255ce 100644
--- a/linphone-desktop/src/components/call/CallModel.cpp
+++ b/linphone-desktop/src/components/call/CallModel.cpp
@@ -39,6 +39,7 @@ using namespace std;
CallModel::CallModel (shared_ptr linphoneCall) {
Q_ASSERT(linphoneCall != nullptr);
mLinphoneCall = linphoneCall;
+ mLinphoneCall->setData("call-model", *this);
// Deal with auto-answer.
{
@@ -118,6 +119,21 @@ void CallModel::setRecordFile (shared_ptr &callParams) {
);
}
+void CallModel::updateStats (const linphone::CallStats &stats) {
+ switch (stats.getType()) {
+ case linphone::StreamTypeAudio:
+ updateStats(stats, mAudioStats);
+ break;
+ case linphone::StreamTypeVideo:
+ updateStats(stats, mVideoStats);
+ break;
+ default:
+ break;
+ }
+
+ emit statsUpdated();
+}
+
// -----------------------------------------------------------------------------
void CallModel::accept () {
@@ -388,3 +404,97 @@ void CallModel::sendDtmf (const QString &dtmf) {
qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(dtmf);
mLinphoneCall->sendDtmf(dtmf.constData()[0].toLatin1());
}
+
+// -----------------------------------------------------------------------------
+
+QVariantList CallModel::getAudioStats () const {
+ return mAudioStats;
+}
+
+QVariantList CallModel::getVideoStats () const {
+ return mVideoStats;
+}
+
+static QVariantMap createStat (const QString &key, const QString &value) {
+ QVariantMap m;
+ m["key"] = key;
+ m["value"] = value;
+ return m;
+}
+
+void CallModel::updateStats (const linphone::CallStats &callStats, QVariantList &stats) {
+ QString family;
+ shared_ptr params = mLinphoneCall->getCurrentParams();
+ shared_ptr payloadType;
+
+ switch (callStats.getType()) {
+ case linphone::StreamTypeAudio:
+ payloadType = params->getUsedAudioPayloadType();
+ break;
+ case linphone::StreamTypeVideo:
+ payloadType = params->getUsedVideoPayloadType();
+ break;
+ default:
+ return;
+ }
+
+ switch (callStats.getIpFamilyOfRemote()) {
+ case linphone::AddressFamilyInet:
+ family = "IPv4";
+ break;
+ case linphone::AddressFamilyInet6:
+ family = "IPv6";
+ break;
+ default:
+ family = "Unknown";
+ break;
+ }
+
+ stats.clear();
+ stats << createStat(tr("callStatsCodec"), QString("%1 / %2kHz").arg(Utils::linphoneStringToQString(payloadType->getMimeType())).arg(payloadType->getClockRate() / 1000));
+ stats << createStat(tr("callStatsUploadBandwidth"), QString("%1 kbits/s").arg(int(callStats.getUploadBandwidth())));
+ stats << createStat(tr("callStatsDownloadBandwidth"), QString("%1 kbits/s").arg(int(callStats.getDownloadBandwidth())));
+ stats << createStat(tr("callStatsIceState"), iceStateToString(callStats.getIceState()));
+ stats << createStat(tr("callStatsIpFamily"), family);
+ stats << createStat(tr("callStatsSenderLossRate"), QString("%1 %").arg(callStats.getSenderLossRate()));
+ stats << createStat(tr("callStatsReceiverLossRate"), QString("%1 %").arg(callStats.getReceiverLossRate()));
+ switch (callStats.getType()) {
+ case linphone::StreamTypeAudio:
+ stats << createStat(tr("callStatsJitterBuffer"), QString("%1 ms").arg(callStats.getJitterBufferSizeMs()));
+ break;
+ case linphone::StreamTypeVideo:
+ {
+ QString sentVideoDefinitionName = Utils::linphoneStringToQString(params->getSentVideoDefinition()->getName());
+ QString sentVideoDefinition = QString("%1x%2").arg(params->getSentVideoDefinition()->getWidth()).arg(params->getSentVideoDefinition()->getHeight());
+ stats << createStat(tr("callStatsSentVideoDefinition"),
+ (sentVideoDefinition == sentVideoDefinitionName) ? sentVideoDefinition : QString("%1 (%2)").arg(sentVideoDefinition).arg(sentVideoDefinitionName));
+ QString receivedVideoDefinitionName = Utils::linphoneStringToQString(params->getReceivedVideoDefinition()->getName());
+ QString receivedVideoDefinition = QString("%1x%2").arg(params->getReceivedVideoDefinition()->getWidth()).arg(params->getReceivedVideoDefinition()->getHeight());
+ stats << createStat(tr("callStatsReceivedVideoDefinition"),
+ (receivedVideoDefinition == receivedVideoDefinitionName) ? receivedVideoDefinition : QString("%1 (%2)").arg(receivedVideoDefinition).arg(receivedVideoDefinitionName));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+QString CallModel::iceStateToString (linphone::IceState state) const {
+ switch (state) {
+ case linphone::IceStateNotActivated:
+ return tr("iceStateNotActivated");
+ case linphone::IceStateFailed:
+ return tr("iceStateFailed");
+ case linphone::IceStateInProgress:
+ return tr("iceStateInProgress");
+ case linphone::IceStateReflexiveConnection:
+ return tr("iceStateReflexiveConnection");
+ case linphone::IceStateHostConnection:
+ return tr("iceStateHostConnection");
+ case linphone::IceStateRelayConnection:
+ return tr("iceStateRelayConnection");
+ default:
+ return tr("iceStateInvalid");
+ }
+}
+
diff --git a/linphone-desktop/src/components/call/CallModel.hpp b/linphone-desktop/src/components/call/CallModel.hpp
index 344442db7..64e3ada37 100644
--- a/linphone-desktop/src/components/call/CallModel.hpp
+++ b/linphone-desktop/src/components/call/CallModel.hpp
@@ -49,6 +49,9 @@ class CallModel : public QObject {
Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged);
+ Q_PROPERTY(QVariantList audioStats READ getAudioStats NOTIFY statsUpdated);
+ Q_PROPERTY(QVariantList videoStats READ getVideoStats NOTIFY statsUpdated);
+
public:
enum CallStatus {
CallStatusConnected,
@@ -69,6 +72,7 @@ public:
}
static void setRecordFile (std::shared_ptr &callParams);
+ void updateStats (const linphone::CallStats &stats);
Q_INVOKABLE void accept ();
Q_INVOKABLE void acceptWithVideo ();
@@ -90,6 +94,7 @@ signals:
void microMutedChanged (bool status);
void videoRequested ();
void recordingChanged (bool status);
+ void statsUpdated ();
private:
void stopAutoAnswerTimer () const;
@@ -119,9 +124,16 @@ private:
bool getRecording () const;
+ QVariantList getAudioStats () const;
+ QVariantList getVideoStats () const;
+ void updateStats (const linphone::CallStats &call_stats, QVariantList &stats);
+ QString iceStateToString (linphone::IceState state) const;
+
bool mPausedByRemote = false;
bool mPausedByUser = false;
bool mRecording = false;
+ QVariantList mAudioStats;
+ QVariantList mVideoStats;
std::shared_ptr mLinphoneCall;
};
diff --git a/linphone-desktop/src/components/core/CoreHandlers.cpp b/linphone-desktop/src/components/core/CoreHandlers.cpp
index af64975c4..db446d465 100644
--- a/linphone-desktop/src/components/core/CoreHandlers.cpp
+++ b/linphone-desktop/src/components/core/CoreHandlers.cpp
@@ -52,6 +52,14 @@ void CoreHandlers::onCallStateChanged (
App::getInstance()->getNotifier()->notifyReceivedCall(call);
}
+void CoreHandlers::onCallStatsUpdated (
+ const std::shared_ptr &,
+ const std::shared_ptr &call,
+ const linphone::CallStats &stats
+) {
+ call->getData("call-model").updateStats(stats);
+}
+
void CoreHandlers::onMessageReceived (
const shared_ptr &,
const shared_ptr &,
diff --git a/linphone-desktop/src/components/core/CoreHandlers.hpp b/linphone-desktop/src/components/core/CoreHandlers.hpp
index 11af33789..8c945d200 100644
--- a/linphone-desktop/src/components/core/CoreHandlers.hpp
+++ b/linphone-desktop/src/components/core/CoreHandlers.hpp
@@ -54,6 +54,12 @@ private:
const std::string &message
) override;
+ void onCallStatsUpdated (
+ const std::shared_ptr &core,
+ const std::shared_ptr &call,
+ const linphone::CallStats &stats
+ ) override;
+
void onMessageReceived (
const std::shared_ptr &core,
const std::shared_ptr &room,
diff --git a/linphone-desktop/ui/modules/Common/qmldir b/linphone-desktop/ui/modules/Common/qmldir
index 66411e108..ed9cf3e31 100644
--- a/linphone-desktop/ui/modules/Common/qmldir
+++ b/linphone-desktop/ui/modules/Common/qmldir
@@ -68,6 +68,7 @@ Collapse 1.0 Misc/Collapse.qml
ForceScrollBar 1.0 Misc/ForceScrollBar.qml
Paned 1.0 Misc/Paned.qml
+AbstractDropDownMenu 1.0 Popup/AbstractDropDownMenu.qml
DesktopPopup 1.0 Popup/DesktopPopup.qml
DropDownDynamicMenu 1.0 Popup/DropDownDynamicMenu.qml
DropDownMenu 1.0 Popup/DropDownMenu.qml
diff --git a/linphone-desktop/ui/modules/Linphone/Calls/CallStatistics.qml b/linphone-desktop/ui/modules/Linphone/Calls/CallStatistics.qml
new file mode 100644
index 000000000..a1ac03b24
--- /dev/null
+++ b/linphone-desktop/ui/modules/Linphone/Calls/CallStatistics.qml
@@ -0,0 +1,114 @@
+import QtQuick 2.7
+import QtQuick.Layouts 1.3
+
+import Common 1.0
+import Linphone 1.0
+import Linphone.Styles 1.0
+
+// =============================================================================
+
+AbstractDropDownMenu {
+ id: callStatistics
+
+ property var call
+
+ // ---------------------------------------------------------------------------
+
+ function _computeHeight () {
+ return callStatistics.height
+ }
+
+ // ---------------------------------------------------------------------------
+
+ Component {
+ id: line
+
+ RowLayout {
+ spacing: CallStatisticsStyle.spacing
+
+ // ---------------------------------------------------------------------------
+
+ Text {
+ Layout.preferredWidth: CallStatisticsStyle.key.width
+
+ color: CallStatisticsStyle.key.color
+ elide: Text.ElideRight
+ font {
+ pointSize: CallStatisticsStyle.key.fontSize
+ bold: true
+ }
+
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+
+ text: modelData.key
+ }
+
+ // ---------------------------------------------------------------------------
+
+ Text {
+ Layout.fillWidth: true
+ color: CallStatisticsStyle.value.color
+ elide: Text.ElideRight
+ font.pointSize: CallStatisticsStyle.value.fontSize
+
+ text: modelData.value
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+
+ Component {
+ id: media
+
+ Column {
+ width: parent.width
+
+ Text {
+ width: parent.width
+ color: CallStatisticsStyle.title.color
+ font {
+ bold: true
+ pointSize: CallStatisticsStyle.title.fontSize
+ }
+ horizontalAlignment: Text.AlignHCenter
+ text: $label
+ }
+
+ Repeater {
+ model: $data
+ delegate: line
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+
+ Rectangle {
+ anchors.fill: parent
+ color: CallStatisticsStyle.color
+
+ Row {
+ anchors {
+ fill: parent
+ leftMargin: CallStatisticsStyle.leftMargin
+ rightMargin: CallStatisticsStyle.rightMargin
+ }
+
+ Loader {
+ property string $label: qsTr("audioStatsLabel")
+ property var $data: callStatistics.call.audioStats
+ sourceComponent: media
+ width: parent.width / 2
+ }
+
+ Loader {
+ property string $label: qsTr("videoStatsLabel")
+ property var $data: callStatistics.call.videoStats
+ sourceComponent: media
+ width: parent.width / 2
+ }
+ }
+ }
+}
diff --git a/linphone-desktop/ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml b/linphone-desktop/ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml
new file mode 100644
index 000000000..32fe29b01
--- /dev/null
+++ b/linphone-desktop/ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml
@@ -0,0 +1,30 @@
+pragma Singleton
+import QtQuick 2.7
+
+import Common 1.0
+
+// =============================================================================
+
+QtObject {
+ property color color: Colors.e
+ property int height: 60
+ property int leftMargin: 12
+ property int rightMargin: 12
+ property int width: 240
+
+ property QtObject title: QtObject {
+ property color color: Colors.l
+ property int fontSize: 16
+ }
+
+ property QtObject key: QtObject {
+ property int width: 200
+ property color color: Colors.l
+ property int fontSize: 10
+ }
+
+ property QtObject value: QtObject {
+ property color color: Colors.l
+ property int fontSize: 10
+ }
+}
diff --git a/linphone-desktop/ui/modules/Linphone/Styles/qmldir b/linphone-desktop/ui/modules/Linphone/Styles/qmldir
index f4db82bdf..0094337f5 100644
--- a/linphone-desktop/ui/modules/Linphone/Styles/qmldir
+++ b/linphone-desktop/ui/modules/Linphone/Styles/qmldir
@@ -13,6 +13,7 @@ singleton ChatStyle 1.0 Chat/ChatStyle.qml
singleton CallsStyle 1.0 Calls/CallsStyle.qml
singleton CallControlsStyle 1.0 Calls/CallControlsStyle.qml
+singleton CallStatisticsStyle 1.0 Calls/CallStatisticsStyle.qml
singleton CodecsViewerStyle 1.0 Codecs/CodecsViewerStyle.qml
diff --git a/linphone-desktop/ui/modules/Linphone/qmldir b/linphone-desktop/ui/modules/Linphone/qmldir
index 69bb88f52..0aa297033 100644
--- a/linphone-desktop/ui/modules/Linphone/qmldir
+++ b/linphone-desktop/ui/modules/Linphone/qmldir
@@ -12,6 +12,7 @@ CardBlock 1.0 Blocks/CardBlock.qml
RequestBlock 1.0 Blocks/RequestBlock.qml
Calls 1.0 Calls/Calls.qml
+CallStatistics 1.0 Calls/CallStatistics.qml
Chat 1.0 Chat/Chat.qml
diff --git a/linphone-desktop/ui/views/App/Calls/Incall.qml b/linphone-desktop/ui/views/App/Calls/Incall.qml
index 0646529ca..d0b6189f1 100644
--- a/linphone-desktop/ui/views/App/Calls/Incall.qml
+++ b/linphone-desktop/ui/views/App/Calls/Incall.qml
@@ -1,4 +1,5 @@
import QtQuick 2.7
+import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import Common 1.0
@@ -61,13 +62,16 @@ Rectangle {
Layout.rightMargin: CallStyle.header.rightMargin
Layout.preferredHeight: CallStyle.header.contactDescription.height
- Icon {
+ ActionButton {
id: callQuality
anchors.left: parent.left
icon: 'call_quality_0'
iconSize: CallStyle.header.iconSize
visible: call.status !== CallModel.CallStatusEnded
+ useStates: false
+
+ onClicked: callStatistics.showMenu()
// See: http://www.linphone.org/docs/liblinphone/group__call__misc.html#ga62c7d3d08531b0cc634b797e273a0a73
Timer {
@@ -78,6 +82,16 @@ Rectangle {
onTriggered: Logic.updateCallQualityIcon()
}
+
+ CallStatistics {
+ id: callStatistics
+ relativeTo: callQuality
+ relativeY: info.height + elapsedTime.height * 2
+ call: incall.call
+ width: container.width
+ height: container.height
+ launcher: callQuality
+ }
}
ContactDescription {