Реализация teampeak как голосовой сервер
Я внедряю сервер голосового чата, который будет использоваться в моем приложении электронного обучения Virtual Class для Windows, которое использует API удаленного рабочего стола.
До сих пор я сжимал голос в OPUS и тестировал различные варианты:
- Для передачи голоса через виртуальный канал RDP. Это работает, но создает много лагов, несмотря на создание канала с CHANNEL_PRIORITY_HI.
- Чтобы использовать мой собственный голосовой сервер TCP (или UDP). Для этого варианта мне было интересно, что будет лучшим способом для реализации.
В настоящее время я отправляю полученную дейтаграмму udp всем остальным клиентам (позже я сделаю микширование на стороне сервера).
Проблема с моим текущим голосовым сервером UDP заключается в том, что он имеет отставание даже на одном и том же компьютере: например, один сервер и четыре подключенных клиента, два из них имеют открытые микрофоны.
Я получаю слышимое отставание с этой настройкой:
void VoiceServer(int port)
{
XSOCKET Y = make_shared<XSOCKET>(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (!Y->Bind(port))
return;
auto VoiceServer2 = [&]()
{
OPUSBUFF o;
char d[200] = { 0 };
map<int, vector<char>> udps;
for (;;)
{
// get datagram
int sle = sizeof(sockaddr_in6);
int r = recvfrom(*Y, o.d, 4000, 0, (sockaddr*)d, &sle);
if (r <= 0)
break;
// a MESSAGE is a header and opus data follows
MESSAGE* m = (MESSAGE*)o.d;
// have we received data from this client already?
// m->arb holds the RDP ID of the user
if (udps.find(m->arb) == udps.end())
{
vector<char>& uu = udps[m->arb];
uu.resize(sle);
memcpy(uu.data(), d, sle);
}
for (auto& att2 : aatts) // attendee list
{
long lxid = 0;
att2->get_Id(&lxid);
#ifndef _DEBUG
if (lxid == m->arb) // if same
continue;
#endif
const vector<char>& uud = udps[lxid];
sendto(*Y, o.d + sizeof(MESSAGE), r - sizeof(MESSAGE), 0, (sockaddr*)uud.data(), uud.size());
}
}
};
// 10 threads receiving
for (int i = 0; i < 9; i++)
{
std::thread t(VoiceServer2);
t.detach();
}
VoiceServer2();
}
Каждый клиент запускает поток VoiceServer:
void VoiceServer()
{
char b[4000] = { 0 };
vector<char> d2;
for (;;)
{
int r = recvfrom(Socket, b, 4000, 0, 0,0);
if (r <= 0)
break;
d2.resize(r);
memcpy(d2.data(), b, r);
if (audioin && wout)
audioin->push(d2); // this pushes the buffer to a waveOut writing class
SetEvent(hPlayEvent);
}
}
Это потому, что я тестирую на одной машине? Но с клиентом TeamSpeak, который я настраивал в прошлом, нет никакой задержки.
Спасибо за ваше мнение.
1 ответ
SendTo ():
Для сокетов, ориентированных на сообщения, необходимо соблюдать осторожность, чтобы не превысить максимальный размер пакета базовых подсетей, который можно получить с помощью
getsockopt
получить значение параметра сокетаSO_MAX_MSG_SIZE
, Если данные слишком длинные для атомарной передачи через базовый протокол, возвращается ошибка WSAEMSGSIZE и данные не передаются.
Типичный заголовок IPv4 составляет 20 байтов, а заголовок UDP - 8 байтов. Теоретический предел (в Windows) для максимального размера пакета UDP составляет 65507 байт (определяется по следующей формуле: 0xffff - 20 - 8 = 65507). Это действительно лучший способ отправить такой большой пакет? Если мы установим слишком большой размер пакета, нижняя часть сетевого протокола будет разбивать пакеты на уровне IP. Это занимает много пропускной способности сети, вызывает задержку.
MTU (максимальная единица передачи) фактически относится к протоколу канального уровня. Структура кадра EthernetII DMAC+SMAC+Type+Data+CRC имеет минимальный размер 64 байта на кадр Ethernet из-за электрических ограничений передачи Ethernet, а максимальный размер не может превышать 1518 байтов. Для кадров Ethernet меньше или больше этого ограничения мы можем расценивать это как ошибку. Так как самый большой кадр данных Ethernet EthernetII составляет 1518 байт, за исключением заголовка кадра 14Bytes и части 4Byte проверки CRC хвоста кадра, в области данных осталось только 1500 байтов. Это MTU.
В случае, если MTU составляет 1500 байтов, максимальный размер пакета UDP должен составлять 1500 байтов - заголовок IP (20 байтов) - заголовок UDP (8 байтов) = 1472 байта, если вы хотите, чтобы уровень IP не разделял пакеты. Однако, поскольку стандартное значение MTU в Интернете составляет 576 байтов, рекомендуется, чтобы длина данных UDP контролировалась в пределах (576-8-20) 548 байтов в sendto/recvfrom при программировании UDP в Интернете.
Вам нужно уменьшить количество байтов отправки / получения, а затем контролировать количество раз.