kopia lustrzana https://gitlab.com/eliggett/wfview
Change audio output to use single/slot
rodzic
a108f1ca99
commit
50c8b4e545
|
@ -31,6 +31,12 @@ audioHandler::~audioHandler()
|
|||
audioOutput = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (audioTimer != Q_NULLPTR) {
|
||||
audioTimer->stop();
|
||||
delete audioTimer;
|
||||
audioTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
|
||||
if (resampler != Q_NULLPTR) {
|
||||
speex_resampler_destroy(resampler);
|
||||
|
@ -128,6 +134,11 @@ bool audioHandler::init(audioSetup setupIn)
|
|||
|
||||
if (setup.isinput) {
|
||||
audioInput = new QAudioInput(setup.port, format, this);
|
||||
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Starting audio timer";
|
||||
audioTimer = new QTimer();
|
||||
audioTimer->setTimerType(Qt::PreciseTimer);
|
||||
connect(audioTimer, &QTimer::timeout, this, &audioHandler::getNextAudioChunk);
|
||||
|
||||
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
|
||||
}
|
||||
else {
|
||||
|
@ -186,6 +197,7 @@ void audioHandler::start()
|
|||
if (setup.isinput) {
|
||||
audioDevice = audioInput->start();
|
||||
connect(audioInput, &QAudioOutput::destroyed, audioDevice, &QIODevice::deleteLater, Qt::UniqueConnection);
|
||||
audioTimer->start(setup.blockSize);
|
||||
}
|
||||
else {
|
||||
// Buffer size must be set before audio is started.
|
||||
|
@ -394,19 +406,17 @@ void audioHandler::incomingAudio(audioPacket inPacket)
|
|||
}
|
||||
|
||||
|
||||
void audioHandler::getNextAudioChunk(QByteArray& ret)
|
||||
void audioHandler::getNextAudioChunk()
|
||||
{
|
||||
audioPacket livePacket;
|
||||
livePacket.time= QTime::currentTime();
|
||||
livePacket.sent = 0;
|
||||
// Don't start sending until we have at least 1/2 of setup.latency of audio buffered
|
||||
// For some reason the audioDevice->bytesAvailable() function always returns 0?
|
||||
if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR && audioInput->bytesReady() > format.bytesForDuration(setup.latency)) {
|
||||
if (setup.codec == 0x40 || setup.codec == 0x80) {
|
||||
livePacket.data = audioDevice->read(format.bytesForDuration(20000)); // 20000uS is 20ms in NATIVE format.
|
||||
}
|
||||
else {
|
||||
livePacket.data = audioDevice->readAll(); // 20000uS is 20ms in NATIVE format.
|
||||
}
|
||||
memcpy(&livePacket.guid, setup.guid, GUIDLEN);
|
||||
|
||||
if (audioInput != Q_NULLPTR && audioDevice != Q_NULLPTR) {
|
||||
|
||||
livePacket.data = audioDevice->readAll();
|
||||
|
||||
if (livePacket.data.length() > 0)
|
||||
{
|
||||
Eigen::VectorXf samplesF;
|
||||
|
@ -555,12 +565,13 @@ void audioHandler::getNextAudioChunk(QByteArray& ret)
|
|||
livePacket.data.clear();
|
||||
livePacket.data = outPacket; // Copy output packet back to input buffer.
|
||||
}
|
||||
ret = livePacket.data;
|
||||
emit haveAudioData(livePacket);
|
||||
//ret = livePacket.data;
|
||||
}
|
||||
}
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun);
|
||||
}
|
||||
|
||||
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun);
|
||||
|
||||
return;
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ struct audioSetup {
|
|||
QAudioDeviceInfo port;
|
||||
quint8 resampleQuality;
|
||||
unsigned char localAFgain;
|
||||
quint16 blockSize=20; // Each 'block' of audio is 20ms long by default.
|
||||
quint8 guid[GUIDLEN];
|
||||
};
|
||||
|
||||
// For QtMultimedia, use a native QIODevice
|
||||
|
@ -85,7 +87,6 @@ public:
|
|||
void start();
|
||||
void stop();
|
||||
|
||||
void getNextAudioChunk(QByteArray &data);
|
||||
quint16 getAmplitude();
|
||||
|
||||
public slots:
|
||||
|
@ -96,14 +97,14 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void stateChanged(QAudio::State state);
|
||||
void clearUnderrun();
|
||||
void clearUnderrun();
|
||||
void getNextAudioChunk();
|
||||
|
||||
signals:
|
||||
void audioMessage(QString message);
|
||||
void sendLatency(quint16 newSize);
|
||||
void haveAudioData(const QByteArray& data);
|
||||
void haveAudioData(const audioPacket& data);
|
||||
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under);
|
||||
|
||||
private:
|
||||
|
||||
bool isUnderrun = false;
|
||||
|
@ -114,9 +115,9 @@ private:
|
|||
QAudioOutput* audioOutput=Q_NULLPTR;
|
||||
QAudioInput* audioInput=Q_NULLPTR;
|
||||
QIODevice* audioDevice=Q_NULLPTR;
|
||||
QTimer* audioTimer = Q_NULLPTR;
|
||||
QAudioFormat format;
|
||||
QAudioDeviceInfo deviceInfo;
|
||||
|
||||
SpeexResamplerState* resampler = Q_NULLPTR;
|
||||
|
||||
//r8b::CFixedBuffer<double>* resampBufs;
|
||||
|
|
108
udphandler.cpp
108
udphandler.cpp
|
@ -826,17 +826,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
|||
|
||||
txSetup.format.setChannelCount(1); // TX Audio is always single channel.
|
||||
|
||||
txaudio = new audioHandler();
|
||||
txAudioThread = new QThread(this);
|
||||
|
||||
txaudio->moveToThread(txAudioThread);
|
||||
|
||||
txAudioThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
|
||||
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool)));
|
||||
|
||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||
|
||||
sendControl(false, 0x03, 0x00); // First connect packet
|
||||
|
||||
|
@ -845,6 +835,18 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
|||
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
|
||||
|
||||
if (enableTx) {
|
||||
txaudio = new audioHandler();
|
||||
txAudioThread = new QThread(this);
|
||||
|
||||
txaudio->moveToThread(txAudioThread);
|
||||
|
||||
txAudioThread->start(QThread::TimeCriticalPriority);
|
||||
|
||||
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
|
||||
connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
|
||||
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool)));
|
||||
|
||||
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
|
||||
emit setupTxAudio(txSetup);
|
||||
}
|
||||
|
||||
|
@ -854,10 +856,6 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint
|
|||
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
|
||||
watchdogTimer->start(WATCHDOG_PERIOD);
|
||||
|
||||
txAudioTimer = new QTimer();
|
||||
txAudioTimer->setTimerType(Qt::PreciseTimer);
|
||||
connect(txAudioTimer, &QTimer::timeout, this, &udpAudio::sendTxAudio);
|
||||
|
||||
areYouThereTimer = new QTimer();
|
||||
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
|
||||
areYouThereTimer->start(AREYOUTHERE_PERIOD);
|
||||
|
@ -889,13 +887,6 @@ udpAudio::~udpAudio()
|
|||
watchdogTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (txAudioTimer != Q_NULLPTR)
|
||||
{
|
||||
qDebug(logUdp()) << "Stopping txaudio timer";
|
||||
txAudioTimer->stop();
|
||||
delete txAudioTimer;
|
||||
}
|
||||
|
||||
if (rxAudioThread != Q_NULLPTR) {
|
||||
qDebug(logUdp()) << "Stopping rxaudio thread";
|
||||
rxAudioThread->quit();
|
||||
|
@ -935,44 +926,41 @@ void udpAudio::sendTxAudio()
|
|||
if (txaudio == Q_NULLPTR) {
|
||||
return;
|
||||
}
|
||||
QByteArray audio;
|
||||
if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
txaudio->getNextAudioChunk(audio);
|
||||
// Now we have the next audio chunk, we can release the mutex.
|
||||
audioMutex.unlock();
|
||||
|
||||
if (audio.length() > 0) {
|
||||
int counter = 1;
|
||||
int len = 0;
|
||||
}
|
||||
|
||||
while (len < audio.length()) {
|
||||
QByteArray partial = audio.mid(len, 1364);
|
||||
audio_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p) + partial.length();
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
if (partial.length() == 0xa0) {
|
||||
p.ident = 0x9781;
|
||||
}
|
||||
else {
|
||||
p.ident = 0x0080; // TX audio is always this?
|
||||
}
|
||||
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
|
||||
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
tx.append(partial);
|
||||
len = len + partial.length();
|
||||
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
|
||||
sendTrackedPacket(tx);
|
||||
sendAudioSeq++;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
void udpAudio::receiveAudioData(audioPacket audio) {
|
||||
// I really can't see how this could be possible but a quick sanity check!
|
||||
if (txaudio == Q_NULLPTR) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio";
|
||||
if (audio.data.length() > 0) {
|
||||
int counter = 1;
|
||||
int len = 0;
|
||||
|
||||
while (len < audio.data.length()) {
|
||||
QByteArray partial = audio.data.mid(len, 1364);
|
||||
audio_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p) + partial.length();
|
||||
p.sentid = myId;
|
||||
p.rcvdid = remoteId;
|
||||
if (partial.length() == 0xa0) {
|
||||
p.ident = 0x9781;
|
||||
}
|
||||
else {
|
||||
p.ident = 0x0080; // TX audio is always this?
|
||||
}
|
||||
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
|
||||
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
tx.append(partial);
|
||||
len = len + partial.length();
|
||||
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
|
||||
sendTrackedPacket(tx);
|
||||
sendAudioSeq++;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1006,12 +994,7 @@ void udpAudio::dataReceived()
|
|||
{
|
||||
case (16): // Response to control packet handled in udpBase
|
||||
{
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
if (in->type == 0x04 && enableTx)
|
||||
{
|
||||
txAudioTimer->start(AUDIO_PERIOD);
|
||||
}
|
||||
|
||||
//control_packet_t in = (control_packet_t)r.constData();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1024,7 +1007,6 @@ void udpAudio::dataReceived()
|
|||
|
||||
*/
|
||||
control_packet_t in = (control_packet_t)r.constData();
|
||||
|
||||
|
||||
if (in->type != 0x01 && in->len >= 0x20) {
|
||||
if (in->seq == 0)
|
||||
|
|
|
@ -196,6 +196,7 @@ public slots:
|
|||
void setVolume(unsigned char value);
|
||||
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
|
||||
void receiveAudioData(audioPacket audio);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -211,7 +212,7 @@ private:
|
|||
audioHandler* txaudio = Q_NULLPTR;
|
||||
QThread* txAudioThread = Q_NULLPTR;
|
||||
|
||||
QTimer* txAudioTimer=Q_NULLPTR;
|
||||
QTimer* txAudioTimer = Q_NULLPTR;
|
||||
bool enableTx = true;
|
||||
|
||||
QMutex audioMutex;
|
||||
|
|
118
udpserver.cpp
118
udpserver.cpp
|
@ -353,6 +353,7 @@ void udpServer::controlReceived()
|
|||
radio->txAudioSetup.format.setSampleRate(current->txSampleRate);
|
||||
radio->txAudioSetup.isinput = false;
|
||||
radio->txAudioSetup.latency = current->txBufferLen;
|
||||
|
||||
outAudio.isinput = false;
|
||||
|
||||
radio->txaudio = new audioHandler();
|
||||
|
@ -393,6 +394,7 @@ void udpServer::controlReceived()
|
|||
radio->rxAudioSetup.format.setSampleRate(current->rxSampleRate);
|
||||
radio->rxAudioSetup.latency = current->txBufferLen;
|
||||
radio->rxAudioSetup.isinput = true;
|
||||
memcpy(radio->rxAudioSetup.guid, radio->guid, GUIDLEN);
|
||||
|
||||
radio->rxaudio = new audioHandler();
|
||||
|
||||
|
@ -404,6 +406,7 @@ void udpServer::controlReceived()
|
|||
|
||||
connect(this, SIGNAL(setupRxAudio(audioSetup)), radio->rxaudio, SLOT(init(audioSetup)));
|
||||
connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater()));
|
||||
connect(radio->rxaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
|
||||
QMetaObject::invokeMethod(radio->rxaudio, [=]() {
|
||||
|
@ -414,10 +417,6 @@ void udpServer::controlReceived()
|
|||
setupRxAudio(radio->rxAudioSetup);
|
||||
#endif
|
||||
|
||||
radio->rxAudioTimer = new QTimer();
|
||||
radio->rxAudioTimer->setTimerType(Qt::PreciseTimer);
|
||||
connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this));
|
||||
radio->rxAudioTimer->start(AUDIO_PERIOD);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1596,30 +1595,12 @@ void udpServer::dataForServer(QByteArray d)
|
|||
return;
|
||||
}
|
||||
|
||||
void udpServer::sendRxAudio()
|
||||
void udpServer::sendRxAudio(const audioPacket& audio)
|
||||
{
|
||||
QByteArray audio;
|
||||
audioHandler* sender = qobject_cast<audioHandler*>(QObject::sender());
|
||||
for (RIGCONFIG* rig : config.rigs) {
|
||||
|
||||
if (rig->rxaudio != Q_NULLPTR) {
|
||||
if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
audio.clear();
|
||||
rig->rxaudio->getNextAudioChunk(audio);
|
||||
int len = 0;
|
||||
while (len < audio.length()) {
|
||||
audioPacket partial;
|
||||
memcpy(partial.guid, rig->guid, GUIDLEN);
|
||||
partial.data = audio.mid(len, 1364);
|
||||
receiveAudioData(partial);
|
||||
len = len + partial.data.length();
|
||||
}
|
||||
// Now we have the next audio chunk, we can release the mutex.
|
||||
audioMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio";
|
||||
}
|
||||
if (sender != Q_NULLPTR && rig->rxaudio == sender) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1637,52 +1618,58 @@ void udpServer::receiveAudioData(const audioPacket& d)
|
|||
else {
|
||||
memcpy(guid, d.guid, GUIDLEN);
|
||||
}
|
||||
//qInfo(logUdpServer()) << "Server got:" << d.data.length();
|
||||
//qInfo(logUdpServer()) << "Server got:" << d.data.length();
|
||||
foreach(CLIENT * client, audioClients)
|
||||
{
|
||||
if (client != Q_NULLPTR && client->connected && !memcmp(client->guid,guid, GUIDLEN)) {
|
||||
audio_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p) + d.data.length();
|
||||
p.sentid = client->myId;
|
||||
p.rcvdid = client->remoteId;
|
||||
p.ident = 0x0080; // audio is always this?
|
||||
p.datalen = (quint16)qToBigEndian((quint16)d.data.length());
|
||||
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||
p.seq = client->txSeq;
|
||||
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
t.append(d.data);
|
||||
int len = 0;
|
||||
while (len < d.data.length()) {
|
||||
QByteArray partial;
|
||||
partial = d.data.mid(len, 1364);
|
||||
len = len + partial.length();
|
||||
if (client != Q_NULLPTR && client->connected && !memcmp(client->guid, guid, GUIDLEN)) {
|
||||
audio_packet p;
|
||||
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||
p.len = sizeof(p) + partial.length();
|
||||
p.sentid = client->myId;
|
||||
p.rcvdid = client->remoteId;
|
||||
p.ident = 0x0080; // audio is always this?
|
||||
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
|
||||
p.sendseq = (quint16)qToBigEndian((quint16)client->sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||
p.seq = client->txSeq;
|
||||
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||
t.append(d.data);
|
||||
|
||||
SEQBUFENTRY s;
|
||||
s.seqNum = p.seq;
|
||||
s.timeSent = QTime::currentTime();
|
||||
s.retransmitCount = 0;
|
||||
s.data = t;
|
||||
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
if (client->txSeqBuf.size() > BUFSIZE)
|
||||
SEQBUFENTRY s;
|
||||
s.seqNum = p.seq;
|
||||
s.timeSent = QTime::currentTime();
|
||||
s.retransmitCount = 0;
|
||||
s.data = t;
|
||||
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
|
||||
if (client->txSeqBuf.size() > BUFSIZE)
|
||||
{
|
||||
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
|
||||
}
|
||||
client->txSeqBuf.insert(p.seq, s);
|
||||
client->txSeq++;
|
||||
client->sendAudioSeq++;
|
||||
client->txMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock txMutex()";
|
||||
}
|
||||
client->txSeqBuf.insert(p.seq, s);
|
||||
client->txSeq++;
|
||||
client->sendAudioSeq++;
|
||||
client->txMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock txMutex()";
|
||||
}
|
||||
|
||||
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
client->socket->writeDatagram(t, client->ipAddress, client->port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
|
||||
}
|
||||
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
|
||||
{
|
||||
client->socket->writeDatagram(t, client->ipAddress, client->port);
|
||||
udpMutex.unlock();
|
||||
}
|
||||
else {
|
||||
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1871,11 +1858,6 @@ void udpServer::deleteConnection(QList<CLIENT*>* l, CLIENT* c)
|
|||
for (RIGCONFIG* radio : config.rigs) {
|
||||
if (!memcmp(radio->guid, guid, GUIDLEN))
|
||||
{
|
||||
if (radio->rxAudioTimer != Q_NULLPTR) {
|
||||
radio->rxAudioTimer->stop();
|
||||
delete radio->rxAudioTimer;
|
||||
radio->rxAudioTimer = Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (radio->rxAudioThread != Q_NULLPTR) {
|
||||
radio->rxAudioThread->quit();
|
||||
|
|
|
@ -96,6 +96,7 @@ public slots:
|
|||
void dataForServer(QByteArray);
|
||||
void receiveAudioData(const audioPacket &data);
|
||||
void receiveRigCaps(rigCapabilities caps);
|
||||
void sendRxAudio(const audioPacket &audio);
|
||||
|
||||
signals:
|
||||
void haveDataFromServer(QByteArray);
|
||||
|
@ -184,7 +185,6 @@ private:
|
|||
void sendStatus(CLIENT* c);
|
||||
void sendRetransmitRequest(CLIENT* c);
|
||||
void watchdog();
|
||||
void sendRxAudio();
|
||||
void deleteConnection(QList<CLIENT*> *l, CLIENT* c);
|
||||
|
||||
SERVERCONFIG config;
|
||||
|
|
Ładowanie…
Reference in New Issue