call: P2P аудиозвонки — сигналинг, релей медиа, аудио-контур, headless/desktop/android
- src/call/: сигналинг (call.c, call_proto.h, ETCP_RT_ID_CALL 0x36) + релей медиа + headless аудио-сокет (call_headless.c, call_audio_bind) + Opus/jitter/тоны - chat: события CHAT_EVT_CALL_* (34–40), CHAT_EVT_DM_MESSAGES (41), DM-трамплины - chatgui: call window, miniaudio I/O, audio_frame_ring, test_audio_jitter - chatgui-android: CallScreen/IncomingCallDialog, аудио-роутинг, JNI, рингтон - tests: test_call, test_call_headless
This commit is contained in:
1 parent
0157f7f96b
commit
b11e77e4c0
78 files changed
+7053
-70
No files matched your search
@@ -35,6 +35,7 @@ static std::vector<std::unique_ptr<LottieIcon>> s_icons;
|
||||
extern "C" {
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include "chat/chat_core.h"
|
||||
#include "dm/dm_core.h"
|
||||
#include "video/video.h"
|
||||
#include "../../lib/u_async.h"
|
||||
#include "../../lib/mem.h"
|
||||
@@ -357,6 +358,21 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDmChannel(m_currentChannelId)) {
|
||||
QByteArray textData = text.toUtf8();
|
||||
QString conv = dmConvId(m_currentChannelId);
|
||||
struct dm_send_req* req = (struct dm_send_req*)u_malloc(sizeof(struct dm_send_req) + textData.size());
|
||||
if (!req) return;
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->inst = gui_bridge_get_inst();
|
||||
strncpy(req->conv_id, conv.toUtf8().constData(), sizeof(req->conv_id) - 1);
|
||||
req->data = (uint8_t*)(req + 1);
|
||||
req->data_len = (uint32_t)textData.size();
|
||||
memcpy(req->data, textData.constData(), textData.size());
|
||||
gui_bridge_post_uasync_fn(dm_send_trampoline, req);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray textData = text.toUtf8();
|
||||
auto ts = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
@@ -545,6 +561,19 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
m_currentChannelId = channelId;
|
||||
m_inputBar->setRecordingContext(m_db ? m_db->dbPath() + "/media" : QString(), channelId);
|
||||
m_readDebounce->stop();
|
||||
|
||||
/* DM: только текст; сообщения расшифровываются в dm_core (uasync-поток) */
|
||||
if (isDmChannel(channelId)) {
|
||||
m_inputBar->setMediaEnabled(false);
|
||||
requestDmMessages(channelId);
|
||||
if (prevFocus)
|
||||
prevFocus->setFocus();
|
||||
else
|
||||
m_inputBar->setFocus();
|
||||
return;
|
||||
}
|
||||
m_inputBar->setMediaEnabled(true);
|
||||
|
||||
if (!m_db || !m_db->isOpen()) return;
|
||||
|
||||
m_localNicks = m_db->getLocalNicknames(channelId);
|
||||
@@ -606,7 +635,14 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
}
|
||||
|
||||
void MessageList::refresh() {
|
||||
if (m_currentChannelId.isEmpty() || !m_db || !m_db->isOpen()) return;
|
||||
if (m_currentChannelId.isEmpty()) return;
|
||||
|
||||
if (isDmChannel(m_currentChannelId)) {
|
||||
requestDmMessages(m_currentChannelId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_db || !m_db->isOpen()) return;
|
||||
|
||||
qint64 maxId = 0;
|
||||
for (int r = 0; r < m_model->rowCount(); r++)
|
||||
@@ -909,3 +945,73 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
||||
gui_bridge_post_uasync_fn(chat_core_attachment_download_trampoline, req);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── DM (p2p чат) ── */
|
||||
|
||||
bool MessageList::isDmChannel(const QString& channelId) {
|
||||
return channelId.startsWith(QStringLiteral("dm:"));
|
||||
}
|
||||
|
||||
QString MessageList::dmConvId(const QString& channelId) {
|
||||
return channelId.mid(3);
|
||||
}
|
||||
|
||||
void MessageList::requestDmMessages(const QString& channelId) {
|
||||
QString conv = dmConvId(channelId);
|
||||
struct dm_messages_req* req = (struct dm_messages_req*)u_malloc(sizeof(*req));
|
||||
if (!req) return;
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->inst = gui_bridge_get_inst();
|
||||
strncpy(req->conv_id, conv.toUtf8().constData(), sizeof(req->conv_id) - 1);
|
||||
gui_bridge_post_uasync_fn(dm_messages_trampoline, req);
|
||||
}
|
||||
|
||||
void MessageList::onDmMessages(const uint8_t* data, int len) {
|
||||
if (len < 3) return;
|
||||
uint8_t cl = data[0];
|
||||
if (len < 1 + (int)cl + 2) return;
|
||||
QString convId = QString::fromUtf8((const char*)data + 1, cl);
|
||||
if (m_currentChannelId != QStringLiteral("dm:") + convId) return; /* stale response */
|
||||
|
||||
uint16_t count; memcpy(&count, data + 1 + cl, 2);
|
||||
const uint8_t* p = data + 1 + cl + 2;
|
||||
const uint8_t* end = data + len;
|
||||
|
||||
m_model->clear();
|
||||
m_avatarCache.clear();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (p + 1 + 8 + 8 + 8 + 1 > end) break;
|
||||
int dir = p[0]; p += 1;
|
||||
uint64_t seq; memcpy(&seq, p, 8); p += 8;
|
||||
uint64_t ts; memcpy(&ts, p, 8); p += 8;
|
||||
quint64 author; memcpy(&author, p, 8); p += 8;
|
||||
uint8_t ctl = *p; p += 1;
|
||||
if (p + ctl > end) break;
|
||||
p += ctl; /* ct игнорируем: DM v1 — текст */
|
||||
if (p + 2 > end) break;
|
||||
uint16_t dlen; memcpy(&dlen, p, 2); p += 2;
|
||||
if (p + dlen > end) break;
|
||||
QString text = QString::fromUtf8((const char*)p, dlen);
|
||||
p += dlen;
|
||||
|
||||
AccountRow acc = m_db ? m_db->getAccount(author) : AccountRow();
|
||||
QString name = authorName(author);
|
||||
QString letter = acc.avatarLetter.isEmpty() ? name.mid(0, 1) : acc.avatarLetter;
|
||||
QColor color(acc.avatarColor);
|
||||
if (!m_avatarCache.contains(author))
|
||||
m_avatarCache[author] = color;
|
||||
auto avatar = makeAvatar(color, letter);
|
||||
auto time = QDateTime::fromMSecsSinceEpoch((qint64)ts).toString("HH:mm");
|
||||
|
||||
auto *item = new QStandardItem();
|
||||
setMsg(item, name, text, time, avatar, false,
|
||||
false, QString(), QString(), QVariantList{});
|
||||
item->setData((qint64)seq, MsgIdRole);
|
||||
item->setData(QVariant::fromValue(author), MsgAuthorNodeIdRole);
|
||||
item->setData(dir == 1 ? 1 : 0, MsgIsOutgoingRole);
|
||||
m_model->appendRow(item);
|
||||
}
|
||||
|
||||
m_view->scrollToBottom();
|
||||
}
|
||||
Reference in new issue
Block a user