diff options
| author | Anton <anton@picapica.im> | 2025-08-19 17:20:35 +0400 |
|---|---|---|
| committer | Anton <anton@picapica.im> | 2025-08-19 17:20:35 +0400 |
| commit | dc8dc3d49d4b98e28cee3e68cf408bc8af9633b9 (patch) | |
| tree | b051634fd022a79c4368cc7dcb65f737588dc645 | |
| parent | c725f856286dc7918c20a6fb9de34d3d72cb2768 (diff) | |
| download | pica-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.cpp | 67 | ||||
| -rw-r--r-- | pica-client/audiovideocallcontroller.h | 22 | ||||
| -rw-r--r-- | pica-client/callwindow.cpp | 38 | ||||
| -rw-r--r-- | pica-client/callwindow.h | 12 | ||||
| -rw-r--r-- | pica-client/skynet.cpp | 225 | ||||
| -rw-r--r-- | pica-client/skynet.h | 49 |
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); |
