Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/sip/IMHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#include "SIPCall.h"
#include "SIPAccount.h"
#include "UserInfo.h"
#include "IMHandler.h"
#include "ViewHelper.h"

Expand All @@ -19,13 +18,14 @@ IMHandler::IMHandler(SIPCall *parent) : QObject(parent), m_call(parent)
ReadOnlyConfdSettings settings;

if (settings.contains("jitsi/url")) {

settings.beginGroup("jitsi");
m_jitsiBaseURL = settings.value("url", "").toString();
m_jitsiPreconfig = settings.value("preconfig", false).toBool();

settings.endGroup();
}

m_dtmfDebugConfigured = settings.value("generic/dtmfDebug", false).toBool();
}

bool IMHandler::process(const QString &contentType, const QString &message)
Expand Down Expand Up @@ -88,6 +88,33 @@ bool IMHandler::process(const QString &contentType, const QString &message)
return true;
}

// Call delays
else if (path == "callDelay" && dtmfDebugEnabled()) {
bool validTimestamp = false;
qint64 timestamp = 0;
QString digit;
QUrlQuery q(callUrl);
auto qitems = q.queryItems();

for (auto &qi : std::as_const(qitems)) {
if (qi.first == "timestamp") {
timestamp = qi.second.toLongLong(&validTimestamp);
}
if (qi.first == "digit") {
digit = qi.second;
}
}

if (!validTimestamp) {
qCWarning(lcIMHandler) << "invalid call delay timestamp received";
return false;
}

m_call->setCallDelayTx(timestamp, digit);

return true;
}

return false;
}

Expand Down Expand Up @@ -153,6 +180,9 @@ bool IMHandler::sendCapabilities()
m_ownCapabilities.push_back("jitsi");
}

q.addQueryItem("callDelay", "1");
m_ownCapabilities.push_back("callDelay");

QUrl jitsiUrl("gonnect:");
jitsiUrl.setPath("capabilities");
jitsiUrl.setQuery(q);
Expand Down
6 changes: 6 additions & 0 deletions src/sip/IMHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ class IMHandler : public QObject
{
return !m_jitsiBaseURL.isEmpty() && m_capabilities.contains("jitsi");
}
bool dtmfDebugEnabled() const
{
return m_dtmfDebugConfigured && m_capabilities.contains("callDelay");
}

bool process(const QString &contentType, const QString &message);

bool capabilitiesSent() const { return m_capabilitiesSent; }
Expand Down Expand Up @@ -53,4 +58,5 @@ class IMHandler : public QObject

bool m_capabilitiesSent = false;
bool m_jitsiPreconfig = false;
bool m_dtmfDebugConfigured = false;
};
92 changes: 83 additions & 9 deletions src/sip/SIPCall.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QUrl>
#include <qurlquery.h>

#include "SIPCall.h"
#include "SIPCallManager.h"
Expand Down Expand Up @@ -72,6 +74,21 @@ SIPCall::SIPCall(SIPAccount *account, int callId, const QString &contactId, bool
globalCallState.holdAllCalls(this);
}
}

m_callDelayCycleTimer.setInterval(10s);
connect(&m_callDelayCycleTimer, &QTimer::timeout, this, [this]() {
QString digit = QString("%1").arg((m_callDelayCounter % 9) + 1);
m_callDelayCounter++;

requestCallDelay(digit);
});

connect(this, &SIPCall::capabilitiesChanged, [this]() {
if (m_imHandler->dtmfDebugEnabled() && isEstablished()
&& !m_callDelayCycleTimer.isActive()) {
m_callDelayCycleTimer.start();
}
});
}

SIPCall::~SIPCall()
Expand Down Expand Up @@ -265,6 +282,10 @@ void SIPCall::onCallState(pj::OnCallStateParam &prm)
m_isEstablished = false;
m_earlyCallState = false;

if (m_imHandler->dtmfDebugEnabled() && m_callDelayCycleTimer.isActive()) {
m_callDelayCycleTimer.stop();
}

break;

default:
Expand Down Expand Up @@ -348,16 +369,10 @@ void SIPCall::onInstantMessageStatus(pj::OnInstantMessageStatusParam &prm)
qCWarning(lcSIPCall) << "failed to send message:" << prm.code << prm.reason;
}

pj::AudioMedia *SIPCall::audioMedia() const
void SIPCall::onDtmfDigit(pj::OnDtmfDigitParam &prm)
{
const auto callInfo = getInfo();

for (unsigned i = 0; i < callInfo.media.size(); ++i) {
if (callInfo.media[i].type == PJMEDIA_TYPE_AUDIO) {
return static_cast<pj::AudioMedia *>(getMedia(i));
}
}
return nullptr;
// INFO: Currently only used for DTMF call latency debugging
setCallDelayRx(QDateTime::currentMSecsSinceEpoch(), QString::fromStdString(prm.digit));
}

void SIPCall::onCallTsxState(pj::OnCallTsxStateParam &prm)
Expand All @@ -377,6 +392,18 @@ void SIPCall::onCallTsxState(pj::OnCallTsxStateParam &prm)
}
}

pj::AudioMedia *SIPCall::audioMedia() const
{
const auto callInfo = getInfo();

for (unsigned i = 0; i < callInfo.media.size(); ++i) {
if (callInfo.media[i].type == PJMEDIA_TYPE_AUDIO) {
return static_cast<pj::AudioMedia *>(getMedia(i));
}
}
return nullptr;
}

bool SIPCall::hold()
{
pj::CallOpParam op(true);
Expand Down Expand Up @@ -646,3 +673,50 @@ void SIPCall::addMetadata(const QString &data)

Q_EMIT metadataChanged();
}

void SIPCall::requestCallDelay(QString digit)
{
// DMTF
QString timestamp = QString("%1").arg(QDateTime::currentMSecsSinceEpoch());
auto &cm = SIPCallManager::instance();
cm.sendDtmf(m_account->id(), getId(), digit);

// IM
pj::SendInstantMessageParam prm;
prm.contentType = "application/x-www-form-urlencoded";

QUrlQuery q;
q.addQueryItem("timestamp", timestamp);
q.addQueryItem("digit", digit);

QUrl delayUrl("gonnect:");
delayUrl.setPath("callDelay");
delayUrl.setQuery(q);
prm.content = delayUrl.toString().toStdString();

try {
sendInstantMessage(prm);
} catch (pj::Error &err) {
qCWarning(lcSIPCall) << "failed to send call delay data:" << err.info();
}
}

void SIPCall::setCallDelayTx(qint64 timestamp, QString digit)
{
m_callDelayTx.first = timestamp;
m_callDelayTx.second = digit;
}

void SIPCall::setCallDelayRx(qint64 timestamp, QString digit)
{
m_callDelayRx.first = timestamp;
m_callDelayRx.second = digit;

if (m_callDelayRx.second != m_callDelayTx.second) {
m_callDelay = -1;
} else {
m_callDelay = m_callDelayRx.first - m_callDelayTx.first;
}

Q_EMIT callDelayChanged();
}
16 changes: 16 additions & 0 deletions src/sip/SIPCall.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <QObject>
#include <QPointer>
#include <QTimer>
#include <QDateTime>
#include <pjsua2.hpp>

Expand Down Expand Up @@ -28,6 +29,7 @@ class SIPCall : public ICallState, public pj::Call
virtual void onCallMediaState(pj::OnCallMediaStateParam &prm) override;
virtual void onInstantMessage(pj::OnInstantMessageParam &prm) override;
virtual void onInstantMessageStatus(pj::OnInstantMessageStatusParam &prm) override;
virtual void onDtmfDigit(pj::OnDtmfDigitParam &prm) override;
virtual void onCallTsxState(pj::OnCallTsxStateParam &prm) override;

SIPAccount *account() const { return m_account; };
Expand All @@ -50,6 +52,11 @@ class SIPCall : public ICallState, public pj::Call
bool hasMetadata() const { return m_hasMetadata; }
QList<ResponseItem *> metadata() { return m_metadata; };

void requestCallDelay(QString digit);

void setCallDelayTx(qint64 timestamp, QString digit);
void setCallDelayRx(qint64 timestamp, QString digit);

bool hold();
bool unhold();
bool isHolding() const { return m_isHolding; }
Expand All @@ -64,6 +71,8 @@ class SIPCall : public ICallState, public pj::Call
/// The time when the call was established (i.e. answered); invalid QDateTime if not established
QDateTime establishedTime() const { return m_establishedTime; }

int callDelay() const { return m_callDelay; }

bool earlyCallState() const { return m_earlyCallState; }

virtual ContactInfo remoteContactInfo() const override { return m_contactInfo; }
Expand All @@ -81,6 +90,7 @@ class SIPCall : public ICallState, public pj::Call
void capabilitiesChanged();
void contactChanged();
void metadataChanged();
void callDelayChanged();

private Q_SLOTS:
void updateIsBlocked();
Expand All @@ -99,6 +109,12 @@ private Q_SLOTS:

QList<ResponseItem *> m_metadata;

QTimer m_callDelayCycleTimer;
QPair<qint64, QString> m_callDelayTx;
QPair<qint64, QString> m_callDelayRx;
int m_callDelayCounter = 0;
int m_callDelay = -1;

pj::AudioMedia *m_aud_med = NULL;
IMHandler *m_imHandler = nullptr;

Expand Down
2 changes: 2 additions & 0 deletions src/sip/SIPCallManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,8 @@ void SIPCallManager::addCall(SIPCall *call)
[call, this]() { Q_EMIT callContactChanged(call); });
connect(call, &SIPCall::metadataChanged, this,
[call, this]() { Q_EMIT metadataChanged(call); });
connect(call, &SIPCall::callDelayChanged, this,
[call, this]() { Q_EMIT callDelayChanged(call); });

connect(call, &SIPCall::missed, this, [this, call]() {
if (call->isBlocked()) {
Expand Down
1 change: 1 addition & 0 deletions src/sip/SIPCallManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class SIPCallManager : public QObject
void isConferenceModeChanged();
void callContactChanged(SIPCall *call);
void metadataChanged(SIPCall *call);
void callDelayChanged(SIPCall *call);
void capabilitiesChanged(SIPCall *call);
void audioLevelChanged(SIPCall *call, qreal level);
void showCallWindow();
Expand Down
18 changes: 18 additions & 0 deletions src/ui/CallsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,31 @@ CallsModel::CallsModel(QObject *parent) : QAbstractListModel{ parent }
if (index >= 0) {
callInfo->isEstablished = call->isEstablished();
callInfo->established = call->establishedTime();
callInfo->callDelay = call->callDelay();
callInfo->hasCapabilityJitsi = call->hasCapability("jitsi") && callInfo->isEstablished;

auto idx = createIndex(index, 0);
Q_EMIT dataChanged(idx, idx,
{
static_cast<int>(Roles::IsEstablished),
static_cast<int>(Roles::EstablishedTime),
static_cast<int>(Roles::CallDelay),
static_cast<int>(Roles::HasCapabilityJitsi),
});
}
});

connect(&callManager, &SIPCallManager::callDelayChanged, this, [this](SIPCall *call) {
auto callInfo = m_callsHash.value(call->getId());
const auto index = m_calls.indexOf(callInfo);
if (index >= 0) {
callInfo->callDelay = call->callDelay();

auto idx = createIndex(index, 0);
Q_EMIT dataChanged(idx, idx, { static_cast<int>(Roles::CallDelay) });
}
});

connect(&callManager, &SIPCallManager::metadataChanged, this, [this](SIPCall *call) {
auto callInfo = m_callsHash.value(call->getId());
const auto index = m_calls.indexOf(callInfo);
Expand Down Expand Up @@ -136,6 +149,7 @@ QHash<int, QByteArray> CallsModel::roleNames() const
{ static_cast<int>(Roles::Company), "company" },
{ static_cast<int>(Roles::IsEstablished), "isEstablished" },
{ static_cast<int>(Roles::EstablishedTime), "establishedTime" },
{ static_cast<int>(Roles::CallDelay), "callDelay" },
{ static_cast<int>(Roles::IsHolding), "isHolding" },
{ static_cast<int>(Roles::IsBlocked), "isBlocked" },
{ static_cast<int>(Roles::StatusCode), "statusCode" },
Expand Down Expand Up @@ -179,6 +193,7 @@ void CallsModel::updateCalls()
callInfo->accountId = qobject_cast<SIPAccount *>(call->parent())->id();
callInfo->remoteUri = call->sipUrl();
callInfo->established = call->establishedTime();
callInfo->callDelay = call->callDelay();
callInfo->isEstablished = call->isEstablished();
callInfo->isIncoming = call->isIncoming();
callInfo->isBlocked = call->isBlocked();
Expand Down Expand Up @@ -255,6 +270,9 @@ QVariant CallsModel::data(const QModelIndex &index, int role) const
case static_cast<int>(Roles::EstablishedTime):
return callInfo->established;

case static_cast<int>(Roles::CallDelay):
return callInfo->callDelay;

case static_cast<int>(Roles::IsIncoming):
return callInfo->isIncoming;

Expand Down
2 changes: 2 additions & 0 deletions src/ui/CallsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class CallsModel : public QAbstractListModel
qreal incomingAudioLevel = 0.0;
bool hasMetadata = false;
QDateTime established;
int callDelay = -1;
ContactInfo contactInfo;
pjsip_status_code statusCode = PJSIP_SC_NULL;
};
Expand All @@ -46,6 +47,7 @@ class CallsModel : public QAbstractListModel
Company,
IsEstablished,
EstablishedTime,
CallDelay,
IsHolding,
IsBlocked,
StatusCode,
Expand Down
Loading
Loading