summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton <anton@picapica.im>2025-08-19 17:20:35 +0400
committerAnton <anton@picapica.im>2025-08-19 17:20:35 +0400
commitdc8dc3d49d4b98e28cee3e68cf408bc8af9633b9 (patch)
treeb051634fd022a79c4368cc7dcb65f737588dc645
parentc725f856286dc7918c20a6fb9de34d3d72cb2768 (diff)
downloadpica-pica-dc8dc3d49d4b98e28cee3e68cf408bc8af9633b9.tar.gz
pica-pica-dc8dc3d49d4b98e28cee3e68cf408bc8af9633b9.zip
pica-client: add call initiation & termination in skynet and audiovideocallscontroller and callwindow
-rw-r--r--pica-client/audiovideocallcontroller.cpp67
-rw-r--r--pica-client/audiovideocallcontroller.h22
-rw-r--r--pica-client/callwindow.cpp38
-rw-r--r--pica-client/callwindow.h12
-rw-r--r--pica-client/skynet.cpp225
-rw-r--r--pica-client/skynet.h49
6 files changed, 382 insertions, 31 deletions
diff --git a/pica-client/audiovideocallcontroller.cpp b/pica-client/audiovideocallcontroller.cpp
index 97557af..9eeb89c 100644
--- a/pica-client/audiovideocallcontroller.cpp
+++ b/pica-client/audiovideocallcontroller.cpp
@@ -2,11 +2,33 @@
#include "globals.h"
#include "skynet.h"
#include <QByteArray>
+#include <QDebug>
AudioVideoCallController::AudioVideoCallController(QObject *parent)
- : QObject(parent), callwindow(0)
+ : QObject(parent), callwindow(0), is_active(false)
{
+ connect(skynet, SIGNAL(IncomingCall(QByteArray)), this, SLOT(call_from(QByteArray)));
+ connect(skynet, SIGNAL(CallFailed(QString)), this, SLOT(call_failed(QString)));
+ connect(skynet, SIGNAL(CallAccepted(QByteArray)), this, SLOT(call_accepted(QByteArray)));
+ connect(skynet, SIGNAL(CallRejected(QByteArray)), this, SLOT(call_rejected(QByteArray)));
+ connect(skynet, SIGNAL(CallHungup(QByteArray)), this, SLOT(call_hungup(QByteArray)));
+}
+void AudioVideoCallController::call_failed(QString reason)
+{
+ qDebug() << "Call failed: " << reason << "\n";
+}
+
+void AudioVideoCallController::call_accepted(QByteArray peer_id)
+{
+}
+
+void AudioVideoCallController::call_rejected(QByteArray peer_id)
+{
+}
+
+void AudioVideoCallController::call_hungup(QByteArray peer_id)
+{
}
void AudioVideoCallController::start_call(QByteArray peer_id)
@@ -17,6 +39,47 @@ void AudioVideoCallController::start_call(QByteArray peer_id)
return;
}
- callwindow = new CallWindow(peer_id);
+ m_peer_id = peer_id;
+ callwindow = new CallWindow(peer_id, false);
+ connect(callwindow, SIGNAL(start_call_pressed()), this, SLOT(initiate_call()));
+ connect(callwindow, SIGNAL(hang_call_pressed()), this, SLOT(end_call()));
callwindow->show();
}
+
+void AudioVideoCallController::call_from(QByteArray peer_id)
+{
+ if (callwindow)
+ {
+ /* HANDLE BUSY CASE!!! */
+ /* call is already in progress */
+ return;
+ }
+
+ m_peer_id = peer_id;
+ callwindow = new CallWindow(peer_id, true);
+ connect(callwindow, SIGNAL(accept_call_pressed()), this, SLOT(accept_call()));
+ connect(callwindow, SIGNAL(hang_call_pressed()), this, SLOT(end_call()));
+ callwindow->show();
+
+ /* play ring tone */
+
+}
+
+void AudioVideoCallController::initiate_call()
+{
+ skynet->StartCall(m_peer_id);
+}
+
+void AudioVideoCallController::accept_call()
+{
+ skynet->AcceptCall(m_peer_id);
+}
+
+void AudioVideoCallController::end_call()
+{
+ if (is_active)
+ skynet->HangupCall(m_peer_id);
+ else
+ skynet->RejectCall(m_peer_id);
+}
+
diff --git a/pica-client/audiovideocallcontroller.h b/pica-client/audiovideocallcontroller.h
index 9bc9c38..82e1dc8 100644
--- a/pica-client/audiovideocallcontroller.h
+++ b/pica-client/audiovideocallcontroller.h
@@ -19,17 +19,39 @@
#include <QObject>
#include "callwindow.h"
+#include "audiodevice.h"
+#include "videodevice.h"
class AudioVideoCallController : public QObject
{
Q_OBJECT
public:
explicit AudioVideoCallController(QObject *parent = nullptr);
+ // is call active (i.e. are voice/video packets exchanged now)
+ bool const isActive() {return is_active;}
+ void processCall();
public slots:
void start_call(QByteArray peer_id);
+ void call_from(QByteArray peer_id);
+ void call_failed(QString reason);
+ void call_accepted(QByteArray peer_id);
+ void call_rejected(QByteArray peer_id);
+ void call_hungup(QByteArray peer_id);
signals:
private:
CallWindow *callwindow;
+ AudioDevice *ringdevice;
+ AudioDevice *microphone;
+ AudioDevice *output;
+ VideoDevice *cam;
+ bool is_active;
+ QByteArray m_peer_id;
+private slots:
+ // button handlers
+ void initiate_call();
+ void accept_call();
+ void end_call();
+
};
#endif // AUDIOVIDEOCALLCONTROLLER_H
diff --git a/pica-client/callwindow.cpp b/pica-client/callwindow.cpp
index d63f1b7..4443807 100644
--- a/pica-client/callwindow.cpp
+++ b/pica-client/callwindow.cpp
@@ -19,17 +19,47 @@
#include <QHBoxLayout>
#include <QVBoxLayout>
-CallWindow::CallWindow(QByteArray peer_id)
- : m_peer_id(peer_id)
+CallWindow::CallWindow(QByteArray peer_id, bool incoming)
+ : m_peer_id(peer_id), is_incoming(incoming)
{
QHBoxLayout *lh = new QHBoxLayout(this);
QVBoxLayout *lv = new QVBoxLayout(this);
+ pbAccept = new QPushButton(tr("Accept"), this);
pbCall = new QPushButton(tr("Call"), this);
pbHang = new QPushButton(tr("Hang Up"), this);
- lh->addWidget(pbCall, Qt::AlignRight);
- lh->addWidget(pbHang, Qt::AlignLeft);
+ lh->addWidget(pbAccept, Qt::AlignLeft);
+ lh->addWidget(pbCall, Qt::AlignLeft);
+ lh->addWidget(pbHang, Qt::AlignRight);
lh->addLayout(lv);
setLayout(lh);
+
+ connect(pbCall, SIGNAL(clicked()), this, SLOT(call()));
+ connect(pbHang, SIGNAL(clicked()), this, SLOT(hang()));
+ connect(pbAccept, SIGNAL(clicked()), this, SLOT(accept()));
+
+ if (is_incoming)
+ {
+ pbCall->hide();
+ }
+ else
+ {
+ pbAccept->hide();
+ }
+}
+
+void CallWindow::call()
+{
+ emit start_call_pressed();
+}
+
+void CallWindow::hang()
+{
+ emit hang_call_pressed();
+}
+
+void CallWindow::accept()
+{
+ emit accept_call_pressed();
}
diff --git a/pica-client/callwindow.h b/pica-client/callwindow.h
index 2a0431e..710074c 100644
--- a/pica-client/callwindow.h
+++ b/pica-client/callwindow.h
@@ -26,11 +26,21 @@ class CallWindow : public QWidget
{
Q_OBJECT
public:
- explicit CallWindow(QByteArray peer_id);
+ explicit CallWindow(QByteArray peer_id, bool incoming);
+signals:
+ void start_call_pressed();
+ void accept_call_pressed();
+ void hang_call_pressed();
private:
QByteArray m_peer_id;
+ bool is_incoming;
+ QPushButton *pbAccept;
QPushButton *pbCall;
QPushButton *pbHang;
+private slots:
+ void call();
+ void accept();
+ void hang();
};
#endif
diff --git a/pica-client/skynet.cpp b/pica-client/skynet.cpp
index a5c30b0..0ce6882 100644
--- a/pica-client/skynet.cpp
+++ b/pica-client/skynet.cpp
@@ -34,6 +34,7 @@ SkyNet::SkyNet()
: nodes(config_dbname), QObject(0), active_filetransfers(0)
{
self_aware = false;
+ call_waiting_c2c_connect = false;
PICA_client_callbacks cbs =
{
@@ -57,7 +58,16 @@ SkyNet::SkyNet()
c2n_closed_cb,
listener_error_cb,
multilogin_cb,
- direct_c2c_established_cb
+ direct_c2c_established_cb,
+ incoming_call_cb,
+ call_picked_up_cb,
+ call_rejected_cb,
+ call_hangup_cb,
+ call_audio_params_cb,
+ call_video_params_cb,
+ call_audio_packet_cb,
+ call_video_packet_cb
+
};
PICA_client_init(&cbs);
@@ -718,6 +728,114 @@ void SkyNet::SendMessage(QByteArray to, QString msg)
}
+void SkyNet::AcceptCall(QByteArray from)
+{
+ int ret = PICA_OK;
+ struct PICA_c2c *iptr;
+
+ if ((iptr = find_active_chan(from)))
+ {
+ ret = PICA_pickup_call(iptr);
+
+ if (ret != PICA_OK)
+ emit CallFailed(QString(tr("Failed to pickup the call: %1")).arg(ret));
+
+ return;
+ }
+
+}
+
+void SkyNet::RejectCall(QByteArray from)
+{
+ int ret = PICA_OK;
+ struct PICA_c2c *iptr;
+
+ if ((iptr = find_active_chan(from)))
+ {
+ ret = PICA_reject_call(iptr);
+
+ if (ret != PICA_OK)
+ emit CallFailed(QString(tr("Failed to reject the call: %1")).arg(ret));
+
+ return;
+ }
+
+}
+
+void SkyNet::HangupCall(QByteArray with)
+{
+ int ret = PICA_OK;
+ struct PICA_c2c *iptr;
+
+ if ((iptr = find_active_chan(with)))
+ {
+ ret = PICA_hangup_call(iptr);
+
+ if (ret != PICA_OK)
+ emit CallFailed(QString(tr("Failed to hang up the call: %1")).arg(ret));
+
+ return;
+ }
+
+}
+
+void SkyNet::StartCall(QByteArray to)
+{
+ if (!isSelfAware())
+ {
+ emit CallFailed(QString(tr("Pica Pica client is not online")));
+ return;
+ }
+
+ int ret = PICA_OK;
+ struct PICA_c2c *iptr;
+
+ if ((iptr = find_active_chan(to)))
+ {
+ ret = PICA_start_call(iptr);
+
+ if (ret != PICA_OK)
+ emit CallFailed(QString(tr("Failed to initiate the call: %1")).arg(ret));
+
+ return;
+ }
+
+ call_waiting_c2c_connect = true;
+ call_waiting_to = to;
+
+ struct PICA_c2c *chan = NULL;
+
+ ret = PICA_new_c2c(active_nodelink, (const unsigned char*)to.constData(), NULL, &chan);
+
+ if (ret != PICA_OK)
+ emit CallFailed(QString(tr("Failed to create the C2C connection for the call: %1")).arg(ret));
+}
+
+bool SkyNet::is_call_waiting_for(const QByteArray &peer_id)
+{
+ if (call_waiting_c2c_connect && call_waiting_to == peer_id)
+ return true;
+
+ return false;
+}
+
+void SkyNet::continue_start_call()
+{
+ int ret = PICA_ERRCALLNOTINPROGRESS;
+ struct PICA_c2c *iptr;
+
+ if ((iptr = find_active_chan(call_waiting_to)))
+ {
+ ret = PICA_start_call(iptr);
+ }
+
+ if (ret != PICA_OK)
+ {
+ emit CallFailed(QString(tr("Failed to initiate the call: %1")).arg(ret));
+ call_waiting_c2c_connect = false;
+ }
+}
+
void SkyNet::flush_queues(QByteArray to)
{
struct PICA_c2c *chan;
@@ -911,6 +1029,31 @@ void SkyNet::emit_ConnectionStatusUpdated(QByteArray peer_id, QString status)
emit ConnectionStatusUpdated(peer_id, status);
}
+void SkyNet::emit_CallFailed(QString reason)
+{
+ emit CallFailed(reason);
+}
+
+void SkyNet::emit_IncomingCall(QByteArray from)
+{
+ emit IncomingCall(from);
+}
+
+void SkyNet::emit_CallAccepted(QByteArray by)
+{
+ emit CallAccepted(by);
+}
+
+void SkyNet::emit_CallRejected(QByteArray by)
+{
+ emit CallRejected(by);
+}
+
+void SkyNet::emit_CallHungup(QByteArray by)
+{
+ emit CallHungup(by);
+}
+
//callbacks
void SkyNet::newmsg_cb(const unsigned char *peer_id, const char *msgbuf, unsigned int nb, int type)
@@ -927,16 +1070,30 @@ void SkyNet::msgok_cb(const unsigned char *peer_id)
void SkyNet::c2c_established_cb(const unsigned char *peer_id, const char *ciphersuitename)
{
- skynet->reset_c2c_reconnect_timeout(QByteArray((const char*)peer_id, PICA_ID_SIZE));
- skynet->emit_ConnectionStatusUpdated(QByteArray((const char*)peer_id, PICA_ID_SIZE), QString("🔐: %1 c2c").arg(ciphersuitename));
- skynet->flush_queues(QByteArray((const char*)peer_id, PICA_ID_SIZE));
+ QByteArray peer((const char*)peer_id, PICA_ID_SIZE);
+ skynet->reset_c2c_reconnect_timeout(peer);
+ skynet->emit_ConnectionStatusUpdated(peer, QString("🔐: %1 c2c").arg(ciphersuitename));
+ skynet->flush_queues(peer);
+
+ if (skynet->is_call_waiting_for(peer))
+ {
+ skynet->continue_start_call();
+ }
}
void SkyNet::c2c_failed(const unsigned char *peer_id)
{
- skynet->update_c2c_reconnect_timeout(QByteArray((const char*)peer_id, PICA_ID_SIZE));
- qDebug() << "c2c failed (" << QByteArray((const char*)peer_id, PICA_ID_SIZE).toBase64() << ")\n";
- skynet->emit_ConnectionStatusUpdated(QByteArray((const char*)peer_id, PICA_ID_SIZE), QString(tr("Failed to connect")));
+ QByteArray peer((const char*)peer_id, PICA_ID_SIZE);
+
+ skynet->update_c2c_reconnect_timeout(peer);
+ qDebug() << "c2c failed (" << peer.toBase64() << ")\n";
+ skynet->emit_ConnectionStatusUpdated(peer, QString(tr("Failed to connect")));
+
+ if (skynet->is_call_waiting_for(peer))
+ {
+ skynet->emit_CallFailed(QString(tr("Failed to establish the C2C connection to %1")).arg(QString(peer.toBase64())));
+ skynet->call_waiting_c2c_connect = false;
+ }
}
int SkyNet::accept_cb(const unsigned char *caller_id)
@@ -946,20 +1103,16 @@ int SkyNet::accept_cb(const unsigned char *caller_id)
void SkyNet::notfound_cb(const unsigned char *callee_id)
{
- skynet->update_c2c_reconnect_timeout(QByteArray((const char*)callee_id, PICA_ID_SIZE));
- qDebug() << "not found (" << QByteArray((const char*)callee_id, PICA_ID_SIZE).toBase64() << ")\n";
+ QByteArray peer((const char*)callee_id, PICA_ID_SIZE);
- /*
- if (skynet->msgqueues.contains(callee_id))
+ skynet->update_c2c_reconnect_timeout(peer);
+ qDebug() << "not found (" << peer.toBase64() << ")\n";
+
+ if (skynet->is_call_waiting_for(peer))
{
- while ( ! skynet->msgqueues[callee_id].empty())
- {
- skynet->emit_UnableToDeliver(callee_id, skynet->msgqueues[callee_id].last());
- skynet->msgqueues[callee_id].removeLast();
- }
- // msgqueues.remove(callee_id) was forgotten
+ skynet->emit_CallFailed(QString(tr("Requested peer was not found online in the Pica Pica network")));
+ skynet->call_waiting_c2c_connect = false;
}
- */
}
void SkyNet::c2c_closed_cb(const unsigned char *peer_id, int reason)
@@ -1114,3 +1267,39 @@ void SkyNet::direct_c2c_established_cb(const unsigned char *peer_id, const char
{
skynet->emit_ConnectionStatusUpdated(QByteArray((const char*)peer_id, PICA_ID_SIZE), QString("🔐: %1 direct c2c").arg(ciphersuitename));
}
+
+void SkyNet::incoming_call_cb(const unsigned char *peer_id)
+{
+ skynet->emit_IncomingCall(QByteArray((const char*)peer_id, PICA_ID_SIZE));
+}
+
+void SkyNet::call_picked_up_cb(const unsigned char *peer_id)
+{
+ skynet->emit_CallAccepted(QByteArray((const char*)peer_id, PICA_ID_SIZE));
+}
+
+void SkyNet::call_rejected_cb(const unsigned char *peer_id)
+{
+ skynet->emit_CallRejected(QByteArray((const char*)peer_id, PICA_ID_SIZE));
+}
+
+void SkyNet::call_hangup_cb(const unsigned char *peer_id)
+{
+ skynet->emit_CallHungup(QByteArray((const char*)peer_id, PICA_ID_SIZE));
+}
+
+void SkyNet::call_audio_params_cb(const unsigned char *peer_id, const char *codec, uint16_t sample_rate)
+{
+}
+
+void SkyNet::call_video_params_cb(const unsigned char *peer_id, const char *codec, uint16_t width, uint16_t height)
+{
+}
+
+void SkyNet::call_audio_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data)
+{
+}
+
+void SkyNet::call_video_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data)
+{
+}
diff --git a/pica-client/skynet.h b/pica-client/skynet.h
index 57baeba..c4846ab 100644
--- a/pica-client/skynet.h
+++ b/pica-client/skynet.h
@@ -44,6 +44,11 @@ public:
void ResumeFile(QByteArray peer_id, bool resume_sending);
void CancelFile(QByteArray peer_id, bool cancel_sending);
+ void StartCall(QByteArray to);
+ void AcceptCall(QByteArray from);
+ void RejectCall(QByteArray from);
+ void HangupCall(QByteArray with);
+
Accounts::AccountRecord CurrentAccount()
{
return skynet_account;
@@ -78,11 +83,18 @@ signals:
void c2cClosed(QByteArray peer_id);
+ void CallFailed(QString reason);
+ void IncomingCall(QByteArray from);
+ void CallAccepted(QByteArray by);
+ void CallRejected(QByteArray by);
+ void CallHungup(QByteArray by);
private:
Nodes nodes;
QList<QPair<struct PICA_c2n *, Nodes::NodeRecord> > connecting_nodes;
QList<struct PICA_c2n *> connected_nodes_to_close;
bool self_aware;
+ bool call_waiting_c2c_connect;
+ QByteArray call_waiting_to;
QString status;
struct PICA_c2n *active_nodelink;
struct PICA_acc *acc;
@@ -111,6 +123,10 @@ private:
void active_filetransfers_reset();
quint32 active_filetransfers;
+ void continue_start_call();
+ // check if there is a call waiting to be initiated with peer_id
+ bool is_call_waiting_for(const QByteArray &peer_id);
+
void emit_MessageReceived(QByteArray from, QString msg);
void emit_UnableToDeliver(QByteArray to, QString msg);
void emit_Delivered(QByteArray to);
@@ -136,17 +152,22 @@ private:
void emit_c2cClosed(QByteArray peer_id);
void emit_ConnectionStatusUpdated(QByteArray peer_id, QString status);
- //получение сообщения.
+ void emit_CallFailed(QString reason);
+ void emit_IncomingCall(QByteArray from);
+ void emit_CallAccepted(QByteArray by);
+ void emit_CallRejected(QByteArray by);
+ void emit_CallHungup(QByteArray by);
+
static void newmsg_cb(const unsigned char *peer_id, const char* msgbuf, unsigned int nb, int type);
- //получение подтверждения о доставке сообщения
+
static void msgok_cb(const unsigned char *peer_id);
- //создание канала с собеседником
+
static void c2c_established_cb(const unsigned char *peer_id, const char *ciphersuitename);
- //создать канал не удалось
+
static void c2c_failed(const unsigned char *peer_id);
- //входящий запрос на создание канала от пользователя с номером caller_id
+
static int accept_cb(const unsigned char *caller_id);
- //запрошенный пользователь не найден, в оффлайне или отказался от общения
+
static void notfound_cb(const unsigned char *callee_id);
static void c2c_closed_cb(const unsigned char *peer_id, int reason);
@@ -179,6 +200,22 @@ private:
static void direct_c2c_established_cb(const unsigned char *peer_id, const char *ciphersuitename);
+ static void incoming_call_cb(const unsigned char *peer_id);
+
+ static void call_picked_up_cb(const unsigned char *peer_id);
+
+ static void call_rejected_cb(const unsigned char *peer_id);
+
+ static void call_hangup_cb(const unsigned char *peer_id);
+
+ static void call_audio_params_cb(const unsigned char *peer_id, const char *codec, uint16_t sample_rate);
+
+ static void call_video_params_cb(const unsigned char *peer_id, const char *codec, uint16_t width, uint16_t height);
+
+ static void call_audio_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data);
+
+ static void call_video_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data);
+
private slots:
void nodelink_activated(PICA_c2n *c2n);
void nodelink_closed(PICA_c2n *c2n, int error);