- 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
68 lines
2.3 KiB
C++
68 lines
2.3 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
/* Кольцевой буфер кадров переменной длины (SPSC, mutex-protected).
|
|
*
|
|
* Producer (uasync-поток) и consumer (аудио-поток). Память предвыделена одним
|
|
* блоком — ноль аллокаций на кадр. При переполнении дропается старейший кадр
|
|
* (семантика джиттер-буфера).
|
|
*/
|
|
class AudioFrameRing {
|
|
public:
|
|
AudioFrameRing(int capacity, int slotSize)
|
|
: cap_(capacity), slotSize_(slotSize),
|
|
buf_((size_t)capacity * (size_t)slotSize, 0),
|
|
lens_((size_t)capacity, 0) {}
|
|
|
|
/* Producer: положить кадр. Возвращает false если len > slotSize.
|
|
* При переполнении дропает старейший кадр (успех). */
|
|
bool push(const uint8_t* data, int len) {
|
|
if (len <= 0 || len > slotSize_) return false;
|
|
std::lock_guard<std::mutex> lk(mtx_);
|
|
if (count_ == cap_) {
|
|
head_ = (head_ + 1) % cap_; /* drop oldest */
|
|
count_--;
|
|
}
|
|
std::memcpy(buf_.data() + (size_t)tail_ * slotSize_, data, (size_t)len);
|
|
lens_[(size_t)tail_] = (uint16_t)len;
|
|
tail_ = (tail_ + 1) % cap_;
|
|
count_++;
|
|
return true;
|
|
}
|
|
|
|
/* Consumer: взять старейший кадр. Возвращает длину (0 = пусто). */
|
|
int pop(uint8_t* out, int outCap) {
|
|
std::lock_guard<std::mutex> lk(mtx_);
|
|
if (count_ == 0) return 0;
|
|
int len = (int)lens_[(size_t)head_];
|
|
if (out && len > 0 && len <= outCap)
|
|
std::memcpy(out, buf_.data() + (size_t)head_ * slotSize_, (size_t)len);
|
|
head_ = (head_ + 1) % cap_;
|
|
count_--;
|
|
return len;
|
|
}
|
|
|
|
void clear() {
|
|
std::lock_guard<std::mutex> lk(mtx_);
|
|
head_ = tail_ = count_ = 0;
|
|
}
|
|
|
|
int size() const {
|
|
std::lock_guard<std::mutex> lk(mtx_);
|
|
return count_;
|
|
}
|
|
|
|
int capacity() const { return cap_; }
|
|
|
|
private:
|
|
int cap_, slotSize_;
|
|
int head_ = 0, tail_ = 0, count_ = 0;
|
|
std::vector<uint8_t> buf_;
|
|
std::vector<uint16_t> lens_;
|
|
mutable std::mutex mtx_;
|
|
};
|