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:
evgeny committed 2026-09-16 10:17:57 +03:00
1 parent 0157f7f96b
commit b11e77e4c0
78 files changed
+7053 -70

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
#pragma once
#include <QObject>
#include <atomic>
#include <cstdint>
#include <vector>
struct ma_device;
/* Аудио-контур звонка (desktop): только miniaudio I/O + захват/воспроизведение.
* Весь аудиопроцессинг (Opus, джиттер-буфер, time-stretch, тоны) — в сервисном
* слое src/call/call_audio.c; сюда приходит/отсюда уходит финальный PCM.
*
* Потоки:
* - аудио-поток (miniaudio callback): capture → call_audio_feed_pcm,
* call_audio_pull_pcm → playback;
* - GUI-поток: start()/beginEnd()/stop() по ACCEPTED/ENDED. */
class CallAudioEngine : public QObject {
Q_OBJECT
public:
static CallAudioEngine* instance();
/* Запустить полнодуплекс для звонка (после ACCEPTED). 0 = успех. */
int start(quint64 callId);
/* По CALL_ENDED: тон завершения (~450мс), затем авто-teardown. */
void beginEnd(quint64 callId);
/* Немедленный teardown (device + освобождение call_audio). */
void stop();
bool isActive() const { return m_active.load(); }
quint64 callId() const { return m_callId.load(); }
/* Mute: пир слышит тишину (отправляем нулевые кадры вместо захваченного PCM). */
void setMuted(bool muted);
bool isMuted() const { return m_muted.load(); }
/* Устройство вывода звонка (-1 = default). Смена на лету пересоздаёт ma_device. */
void setPlaybackDeviceIndex(int index);
int playbackDeviceIndex() const { return m_playbackDeviceIndex; }
/* аудио-поток: miniaudio data callback (захват + воспроизведение) */
void dataCallback(const void* in, void* out, unsigned int frames);
private:
CallAudioEngine() = default;
void teardownDevice();
int initDevice();
std::atomic<bool> m_active{false};
std::atomic<uint64_t> m_callId{0};
std::atomic<bool> m_muted{false};
int m_playbackDeviceIndex = -1; /* только GUI-поток */
ma_device* m_device = nullptr;
bool m_deviceStarted = false;
std::vector<int16_t> m_captureBuf; /* 960 samples, аудио-поток */
int m_capturePos = 0;
};