Skip to content

Commit 1ffbd13

Browse files
committed
FEAT(server, client): Add rolling connection quality information
1 parent d23314a commit 1ffbd13

File tree

8 files changed

+429
-102
lines changed

8 files changed

+429
-102
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ set(SHARED_SOURCES
8585

8686
"crypto/CryptographicHash.cpp"
8787
"crypto/CryptographicRandom.cpp"
88+
"crypto/CryptState.cpp"
8889
"crypto/CryptStateOCB2.cpp"
8990

9091
"${3RDPARTY_DIR}/arc4random/arc4random_uniform.cpp"

src/Mumble.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,12 @@ message UserStats {
553553
// True if the user has a strong certificate.
554554
optional bool strong_certificate = 18 [default = false];
555555
optional bool opus = 19 [default = false];
556+
557+
optional uint32 rolling_time = 20;
558+
// Rolling packet statistics for packets received from the client.
559+
optional Stats rolling_from_client = 21;
560+
// Rolling packet statistics for packets sent by the server.
561+
optional Stats rolling_from_server = 22;
556562
}
557563

558564
// Used by the client to request binary data from the server. By default large

src/crypto/CryptState.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright The Mumble Developers. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license
3+
// that can be found in the LICENSE file at the root of the
4+
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
5+
6+
#include "CryptState.h"
7+
8+
#include <iostream>
9+
#include <QtGlobal>
10+
11+
void CryptState::handleRollingStats() {
12+
bool noData = m_statsLocalReference.empty() && m_statsRemoteReference.empty();
13+
14+
std::chrono::time_point< std::chrono::steady_clock > now = std::chrono::steady_clock::now();
15+
16+
qWarning("handle");
17+
18+
std::cout << now.time_since_epoch().count() << " - " << m_rollingTimer.time_since_epoch().count() << " = " << (now - m_rollingTimer).count() << " > " << m_rollingScanInterval.count() << " = " << ((now - m_rollingTimer) > m_rollingScanInterval) << "\n";
19+
20+
// Update every 5 seconds
21+
if ((now - m_rollingTimer) < m_rollingScanInterval && !noData) {
22+
return;
23+
}
24+
qWarning("Every 5 seconds");
25+
26+
m_rollingTimer = std::chrono::steady_clock::now();
27+
28+
PacketStatsSnapshot snapshotLocal;
29+
snapshotLocal.stats = m_statsLocal;
30+
snapshotLocal.timestamp = now;
31+
m_statsLocalReference.push(snapshotLocal);
32+
33+
PacketStatsSnapshot snapshotRemote;
34+
snapshotRemote.stats = m_statsRemote;
35+
snapshotRemote.timestamp = now;
36+
m_statsRemoteReference.push(snapshotRemote);
37+
38+
while (!m_statsLocalReference.empty() && (now - m_statsLocalReference.front().timestamp) > m_rollingWindow) {
39+
qWarning("pop");
40+
m_statsLocalReference.pop();
41+
}
42+
43+
while (!m_statsRemoteReference.empty() && (now - m_statsRemoteReference.front().timestamp) > m_rollingWindow) {
44+
m_statsRemoteReference.pop();
45+
}
46+
47+
if (!m_statsLocalReference.empty()) {
48+
qWarning("Update");
49+
m_statsLocalRolling.good = m_statsLocal.good - m_statsLocalReference.front().stats.good;
50+
m_statsLocalRolling.late = m_statsLocal.late - m_statsLocalReference.front().stats.late;
51+
m_statsLocalRolling.lost = m_statsLocal.lost - m_statsLocalReference.front().stats.lost;
52+
m_statsLocalRolling.resync = m_statsLocal.resync - m_statsLocalReference.front().stats.resync;
53+
}
54+
55+
if (!m_statsRemoteReference.empty()) {
56+
m_statsRemoteRolling.good = m_statsRemote.good - m_statsRemoteReference.front().stats.good;
57+
m_statsRemoteRolling.late = m_statsRemote.late - m_statsRemoteReference.front().stats.late;
58+
m_statsRemoteRolling.lost = m_statsRemote.lost - m_statsRemoteReference.front().stats.lost;
59+
m_statsRemoteRolling.resync = m_statsRemote.resync - m_statsRemoteReference.front().stats.resync;
60+
}
61+
}

src/crypto/CryptState.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#define MUMBLE_CRYPTSTATE_H_
88

99
#include "Timer.h"
10+
#include <chrono>
11+
#include <queue>
1012
#include <string>
1113

1214
struct PacketStats {
@@ -16,12 +18,32 @@ struct PacketStats {
1618
unsigned int resync = 0;
1719
};
1820

21+
struct PacketStatsSnapshot {
22+
PacketStats stats;
23+
std::chrono::time_point< std::chrono::steady_clock > timestamp;
24+
};
25+
1926
class CryptState {
2027
private:
2128
Q_DISABLE_COPY(CryptState)
29+
30+
const std::chrono::seconds m_rollingScanInterval = std::chrono::seconds(5);
31+
std::chrono::time_point< std::chrono::steady_clock > m_rollingTimer = std::chrono::steady_clock::now();
32+
33+
std::queue< PacketStatsSnapshot > m_statsLocalReference;
34+
std::queue< PacketStatsSnapshot > m_statsRemoteReference;
35+
36+
protected:
37+
void handleRollingStats();
38+
2239
public:
23-
PacketStats m_statsLocal = {};
24-
PacketStats m_statsRemote = {};
40+
PacketStats m_statsLocal = {};
41+
PacketStats m_statsRemote = {};
42+
PacketStats m_statsLocalRolling = {};
43+
PacketStats m_statsRemoteRolling = {};
44+
45+
/// This is the packet statistics sliding time window size in seconds
46+
std::chrono::duration< uint32_t, std::ratio< 1 > > m_rollingWindow = std::chrono::seconds(60 * 5);
2547

2648
Timer tLastGood;
2749
Timer tLastRequest;

src/crypto/CryptStateOCB2.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ bool CryptStateOCB2::decrypt(const unsigned char *source, unsigned char *dst, un
220220
m_statsLocal.lost -= static_cast< unsigned int >(std::abs(lost));
221221
}
222222

223+
handleRollingStats();
223224
tLastGood.restart();
224225
return true;
225226
}

src/mumble/UserInformation.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,12 @@ void UserInformation::update(const MumbleProto::UserStats &msg) {
163163
qlTCPVar->setText(QString::number(msg.tcp_ping_var() > 0.0f ? sqrtf(msg.tcp_ping_var()) : 0.0f, 'f', 2));
164164
qlUDPVar->setText(QString::number(msg.udp_ping_var() > 0.0f ? sqrtf(msg.udp_ping_var()) : 0.0f, 'f', 2));
165165

166-
if (msg.has_from_client() && msg.has_from_server()) {
167-
qgbUDP->setVisible(true);
166+
bool hasTotalStats = msg.has_from_client() && msg.has_from_server();
167+
bool hasRollingStats = msg.has_rolling_time() && msg.has_rolling_from_client() && msg.has_rolling_from_server();
168+
169+
qgbUDP->setVisible(hasTotalStats || hasRollingStats);
170+
171+
if (hasTotalStats) {
168172
const MumbleProto::UserStats_Stats &from = msg.from_client();
169173
qlFromGood->setText(QString::number(from.good()));
170174
qlFromLate->setText(QString::number(from.late()));
@@ -179,17 +183,65 @@ void UserInformation::update(const MumbleProto::UserStats &msg) {
179183

180184
quint32 allFromPackets = from.good() + from.late() + from.lost();
181185
qlFromLatePercent->setText(
182-
QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 2));
186+
QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 1));
183187
qlFromLostPercent->setText(
184-
QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 2));
188+
QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 1));
185189

186190
quint32 allToPackets = to.good() + to.late() + to.lost();
187-
qlToLatePercent->setText(QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 2));
188-
qlToLostPercent->setText(QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 2));
189-
} else {
190-
qgbUDP->setVisible(false);
191+
qlToLatePercent->setText(QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 1));
192+
qlToLostPercent->setText(QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 1));
191193
}
192194

195+
if (hasRollingStats) {
196+
const MumbleProto::UserStats_Stats &from = msg.rolling_from_client();
197+
qlFromGoodRolling->setText(QString::number(from.good()));
198+
qlFromLateRolling->setText(QString::number(from.late()));
199+
qlFromLostRolling->setText(QString::number(from.lost()));
200+
qlFromResyncRolling->setText(QString::number(from.resync()));
201+
202+
const MumbleProto::UserStats_Stats &to = msg.rolling_from_server();
203+
qlToGoodRolling->setText(QString::number(to.good()));
204+
qlToLateRolling->setText(QString::number(to.late()));
205+
qlToLostRolling->setText(QString::number(to.lost()));
206+
qlToResyncRolling->setText(QString::number(to.resync()));
207+
208+
quint32 allFromPackets = from.good() + from.late() + from.lost();
209+
qlFromLatePercentRolling->setText(
210+
QString::number(allFromPackets > 0 ? from.late() * 100.0 / allFromPackets : 0., 'f', 1));
211+
qlFromLostPercentRolling->setText(
212+
QString::number(allFromPackets > 0 ? from.lost() * 100.0 / allFromPackets : 0., 'f', 1));
213+
214+
quint32 allToPackets = to.good() + to.late() + to.lost();
215+
qlToLatePercentRolling->setText(
216+
QString::number(allToPackets > 0 ? to.late() * 100.0 / allToPackets : 0., 'f', 1));
217+
qlToLostPercentRolling->setText(
218+
QString::number(allToPackets > 0 ? to.lost() * 100.0 / allToPackets : 0., 'f', 1));
219+
220+
uint32_t rollingSeconds = msg.rolling_time();
221+
QString rollingText = tr("Last %1 %2:");
222+
if (rollingSeconds < 120) {
223+
qliRolling->setText(rollingText.arg(QString::number(rollingSeconds)).arg(tr("seconds")));
224+
} else {
225+
qliRolling->setText(rollingText.arg(QString::number(rollingSeconds / 60)).arg(tr("minutes")));
226+
}
227+
}
228+
229+
qlFromGoodRolling->setVisible(hasRollingStats);
230+
qlFromLateRolling->setVisible(hasRollingStats);
231+
qlFromLostRolling->setVisible(hasRollingStats);
232+
qlFromResyncRolling->setVisible(hasRollingStats);
233+
qlToGoodRolling->setVisible(hasRollingStats);
234+
qlToLateRolling->setVisible(hasRollingStats);
235+
qlToLostRolling->setVisible(hasRollingStats);
236+
qlToResyncRolling->setVisible(hasRollingStats);
237+
qlFromLatePercentRolling->setVisible(hasRollingStats);
238+
qlFromLostPercentRolling->setVisible(hasRollingStats);
239+
qlToLatePercentRolling->setVisible(hasRollingStats);
240+
qlToLostPercentRolling->setVisible(hasRollingStats);
241+
qliRolling->setVisible(hasRollingStats);
242+
qliRollingFrom->setVisible(hasRollingStats);
243+
qliRollingTo->setVisible(hasRollingStats);
244+
193245
if (msg.has_onlinesecs()) {
194246
if (msg.has_idlesecs())
195247
qlTime->setText(

0 commit comments

Comments
 (0)