Название: [Решено] QUdpSocket::readyRead()
Отправлено: ProGOLD от Январь 16, 2012, 01:29
Здравствуйте, люди добрые. Уже месяц мучаюсь с одной проблемой. Пишу курсовую работу на Qt: голосовая связь по протоколу UDP. Разумеется, клиенты взаимодействуют peer-to-peer, а для захвата и воспроизведения звука используются QAudioInput и QAudioOutput. Сначала стал изобретать одноколесный велосипед с QBuffer. Потом понял, что он тут вообще не нужен, и создал два класса для отправки и получения звука по UDP: AudioTransmitter и AudioReciever, соответственно. Эти классы являются потомками QIODevice и передаются в качестве параметров методу start(QIODevice*) классов QAudioInput и QAudioOutput, соответственно. Отправка звука работает на ура (это подтверждает сниффер), а вот прием работает только на одной стороне. Причем всегда звук проигрывается на том клиенте, который "стартовал" позже. В логе видно, что после старта QAudioOutput 4 раза вызывается метод qint64 AudioReciever::readData(char*, qint64), который возвращает -1 (потому что второй клиент еще ничего не присылал). И больше этот метод не вызывается. Я сделал единственный логичный вывод: QUdpSocket не генерирует сигнал readyRead(). В документации по QUdpSocket есть одна любопытная фраза: An incoming datagram should be read when you receive the readyRead() signal, otherwise this signal will not be emitted for the next datagram.Это единственная зацепка в документации, но не похоже, что она имеет место в моем случае, т.к. readyRead всегда обрабатывается. Любопытно следующее: если в метод AudioReciever::readData добавить цикл while (socket.hasPendingDatagram()), звук проигрывается с обеих сторон, но жутко лагает (видимо, часть пакетов просто пропадает в этом случае). В общем, не знаю, что делать и расчитываю на помощь специалистов. Два основных исходных файла я запостил ниже, а весь проект в прикрепленном архиве. Заранее спасибо. #include <QObject> #include <QIODevice> #include <QUdpSocket> #include <QHostAddress> #include <QAudioInput> #include <QAudioOutput> #include <QDebug>
// Function returns constant audio format of all audio streams. QAudioFormat GetStreamAudioFormat(void);
// Class produces a device to send audio over the network. class AudioTransmitter : public QIODevice { Q_OBJECT public: QUdpSocket socket; QHostAddress localHOST; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT;
AudioTransmitter(QHostAddress localHost, quint16 localPort, QHostAddress remoteHost, quint16 remotePort); qint64 writeData(const char *data, qint64 len); qint64 readData(char *data, qint64 maxlen); };
// Class produces a device to recieve audio from the network. class AudioReciever : public QIODevice { Q_OBJECT public: QUdpSocket socket; QHostAddress localHOST; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT;
AudioReciever(QHostAddress localHost, quint16 localPort, QHostAddress remoteHost, quint16 remotePort); bool Bind(void); qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len); bool isSequential(void) const; bool seek(qint64 pos); void Clear(void); };
QAudioFormat GetStreamAudioFormat(void) { QAudioFormat format; format.setSampleRate(8000); format.setChannels(1); format.setSampleSize(8); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(format)) { qDebug()<< "default format not supported try to use nearest"; format = info.nearestFormat(format); }
return format; }
////////////////////////////////// // AudioTransmitter Class //////////////////////////////////
AudioTransmitter::AudioTransmitter(QHostAddress localHost, quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localHOST = localHost; localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
qint64 AudioTransmitter::writeData(const char *data, qint64 len) { qint64 writtenBytes = socket.writeDatagram(data, len, remoteHOST, remotePORT); //qDebug() << "Sent " << writtenBytes << " bytes."; return writtenBytes; }
qint64 AudioTransmitter::readData(char *data, qint64 maxlen) { Q_UNUSED(data); Q_UNUSED(maxlen); qDebug() << "IOError: device is write-only."; return 0; }
////////////////////////////////// // AudioReciever Class //////////////////////////////////
AudioReciever::AudioReciever(QHostAddress localHost, quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localHOST = localHost; localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
bool AudioReciever::Bind(void) { connect(&socket, SIGNAL(readyRead()), this, SIGNAL(readyRead())); return socket.bind(localHOST, localPORT, QUdpSocket::DontShareAddress); //qDebug() << "Successfully binded to" << localHOST.toString() << ":" << localPORT << "."; }
qint64 AudioReciever::readData(char *data, qint64 maxlen) { qint64 readBytes = 0; // for (unsigned int i=0; i<2; i++) // if (socket.hasPendingDatagrams()) readBytes = socket.readDatagram(data, maxlen); qDebug() << "Recieved " << readBytes << " bytes."; return readBytes; }
qint64 AudioReciever::writeData(const char *data, qint64 len) { Q_UNUSED(data); Q_UNUSED(len); qDebug() << "IOError: device is read-only."; return 0; }
bool AudioReciever::isSequential(void) const { return true; }
bool AudioReciever::seek(qint64 pos) { Q_UNUSED(pos); return false; }
void AudioReciever::Clear(void) { // char * data; // qint64 size = 320; // while (socket.hasPendingDatagrams()) // socket.readDatagram(data,size);
}
#include <QThread> #include "audio.h"
class VoIPClient : public QThread { Q_OBJECT public: explicit VoIPClient(QObject *parent = 0); ~VoIPClient(void);
void SetLocalHost(QHostAddress HOST); void SetLocalPort(quint16 PORT); void SetRemoteHost(QHostAddress HOST); void SetRemotePort(quint16 PORT); void GetUserInfo(QString LOGIN);
public slots: void Call(void); void FinishCall(void); void ResumePlaying(void); void ResumePlaying(QAudio::State);
private slots: void StartPlaying(void);
protected: void run(void);
private: QHostAddress localHOST; QHostAddress remoteHOST; quint16 localPORT; quint16 remotePORT;
QAudioInput * audio_input; QAudioOutput * audio_output; AudioTransmitter * transmitter; AudioReciever * reciever;
void StartConversation(void); void StopConversation(void); volatile bool stopped;
signals: };
VoIPClient::VoIPClient(QObject *parent) : QThread(parent) { stopped = false; }
VoIPClient::~VoIPClient(void) { }
void VoIPClient::run(void) { QAudioFormat format = GetStreamAudioFormat(); audio_input = new QAudioInput(format); audio_output = new QAudioOutput(format); transmitter = new AudioTransmitter(localHOST, localPORT, remoteHOST, remotePORT); reciever = new AudioReciever(localHOST, localPORT, remoteHOST, remotePORT);
//connect(audio_output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(ResumePlaying(QAudio::State))); connect(reciever, SIGNAL(readyRead()), this, SLOT(ResumePlaying())); reciever->Bind();
transmitter->open(QIODevice::WriteOnly | QIODevice::Truncate); reciever->open(QIODevice::ReadOnly);
audio_output->start(reciever); // reciever->Clear(); audio_input->start(transmitter);
qDebug() << "Started conversation with" << remoteHOST.toString() << ".";
exec();
audio_input->stop(); audio_output->stop(); transmitter->close(); reciever->close();
delete audio_input; delete audio_output; delete transmitter; delete reciever;
qDebug() << "Finished conversation."; }
void VoIPClient::Call(void) { start(); }
void VoIPClient::FinishCall(void) { exit(0); }
void VoIPClient::StartConversation(void) {
}
void VoIPClient::StopConversation(void) {
}
void VoIPClient::StartPlaying(void) { audio_output->start(reciever); }
void VoIPClient::ResumePlaying(void) { if (audio_output->state() != QAudio::ActiveState) { audio_output->stop(); audio_output->start(reciever); } }
void VoIPClient::ResumePlaying(QAudio::State state) { if (state != QAudio::ActiveState) { audio_output->stop(); audio_output->start(reciever); } }
void VoIPClient::SetLocalHost(QHostAddress HOST) { localHOST = HOST; }
void VoIPClient::SetLocalPort(quint16 PORT) { localPORT = PORT; }
void VoIPClient::SetRemoteHost(QHostAddress HOST) { remoteHOST = HOST; }
void VoIPClient::SetRemotePort(quint16 PORT) { remotePORT = PORT; }
Название: Re: QUdpSocket readyRead
Отправлено: popper от Январь 16, 2012, 15:25
Ориентируясь на пример из документации по QUDPSocket, предлагаю попробовать так: AudioReciever::Bind() { connect(&socket, SIGNAL(readyRead()), this, SLOT(myPrivateReadSocket())); ... }
AudioReciever::myPrivateReadSocket() { // QByteArray m_datagram is private buffer
while (udpSocket->hasPendingDatagrams()) { QByteArray tmp; tmp.resize(udpSocket->pendingDatagramSize()); socket.readDatagram(tmp.data(), tmp.size()); m_datagram.append(tmp); } emit readyRead(); }
qint64 AudioReciever::readData(char *data, qint64 maxlen) { qint64 readBytes = qMin(m_datagram.size(), maxlen); // copy readBytes bytes from m_datagram to data and remove this bytes from m_datagram
return readBytes; }
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 16, 2012, 18:22
Внес следующие изменения: bool AudioReciever::Bind(void) { connect(&socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); return socket.bind(localHOST, localPORT, QUdpSocket::DontShareAddress); }
void AudioReciever::readPendingDatagrams(void) { while (socket.hasPendingDatagrams()) { QByteArray tmp; tmp.resize(socket.pendingDatagramSize()); socket.readDatagram(tmp.data(), tmp.size()); m_datagram.append(tmp); } emit readyRead(); }
qint64 AudioReciever::readData(char *data, qint64 maxlen) { qint64 readBytes = qMin(qint64(m_datagram.size()), maxlen);
if (readBytes > 0) { QByteArray tmp = m_datagram.left(readBytes); m_datagram.remove(0,readBytes);
data = new char[readBytes]; char * tmp_ptr = tmp.data();
for (unsigned int i = 0; i < readBytes; i++) data[i] = tmp_ptr[i];
qDebug() << "Recieved " << readBytes << " bytes."; } return readBytes; } Увы, проблема не решилась. Теперь с обеих сторон слышен треск и ничего больше. Может, я с памятью работаю неправильно?
Название: Re: QUdpSocket readyRead
Отправлено: andrew.k от Январь 16, 2012, 18:38
мне-кажется ты скорее неправильно работаешь с qsound* ты его по приходу каждого куска растартуешь. потому и треск. сам не пользовался, поэтому подробнее не скажу.
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 16, 2012, 19:00
Я его перезапускаю только если он уже остановился. void VoIPClient::ResumePlaying(void) { if (audio_output->state() != QAudio::ActiveState) { audio_output->stop(); audio_output->start(reciever); } }
Название: Re: QUdpSocket readyRead
Отправлено: popper от Январь 16, 2012, 19:48
Пока еще предлагаю поиграть с QAudioFormat::LittleEndian и QAudioFormat::BigEndian. И вот этот код мне не нравится: connect(reciever, SIGNAL(readyRead()), this, SLOT(ResumePlaying()));
Позже посмотрю еще внимательнее
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 16, 2012, 20:38
Пока еще предлагаю поиграть с QAudioFormat::LittleEndian и QAudioFormat::BigEndian. Безрезультатно. И вот этот код мне не нравится: connect(reciever, SIGNAL(readyRead()), this, SLOT(ResumePlaying())); Без него на клиенте, который был запущен первым, звук воспроизводиться не будет. Я почистил проект от лишнего. #include <QObject> #include <QtGlobal> #include <QIODevice> #include <QUdpSocket> #include <QHostAddress> #include <QAudioInput> #include <QAudioOutput> #include <QDebug>
// Function returns constant audio format of all audio streams. QAudioFormat GetStreamAudioFormat(void);
// Class produces a device to send audio over the network. class AudioTransmitter : public QIODevice { Q_OBJECT public: AudioTransmitter(quint16 localPort, QHostAddress remoteHost, quint16 remotePort); qint64 writeData(const char *data, qint64 len); qint64 readData(char *data, qint64 maxlen); private: QUdpSocket socket; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT; };
// Class produces a device to recieve audio from the network. class AudioReciever : public QIODevice { Q_OBJECT public: AudioReciever(quint16 localPort, QHostAddress remoteHost, quint16 remotePort); bool Bind(void); bool isSequential(void) const; bool seek(qint64 pos); qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len);
private: QUdpSocket socket; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT; QByteArray buffer;
private slots: void readPendingDatagrams(void); };
QAudioFormat GetStreamAudioFormat(void) { QAudioFormat format; format.setSampleRate(8000); format.setChannels(1); format.setSampleSize(8); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(format)) { qDebug()<< "default format not supported try to use nearest"; format = info.nearestFormat(format); }
return format; }
////////////////////////////////// // AudioTransmitter Class //////////////////////////////////
AudioTransmitter::AudioTransmitter(quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
qint64 AudioTransmitter::writeData(const char *data, qint64 len) { qint64 writtenBytes = socket.writeDatagram(data, len, remoteHOST, remotePORT); return writtenBytes; }
qint64 AudioTransmitter::readData(char *data, qint64 maxlen) { Q_UNUSED(data); Q_UNUSED(maxlen); qDebug() << "IOError: device is write-only."; return 0; }
////////////////////////////////// // AudioReciever Class //////////////////////////////////
AudioReciever::AudioReciever(quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
bool AudioReciever::Bind(void) { connect(&socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); return socket.bind(QHostAddress::Any, localPORT, QUdpSocket::DontShareAddress); }
bool AudioReciever::isSequential(void) const { return true; }
bool AudioReciever::seek(qint64 pos) { Q_UNUSED(pos); return false; }
void AudioReciever::readPendingDatagrams(void) { while (socket.hasPendingDatagrams()) { QByteArray tmp; tmp.resize(socket.pendingDatagramSize()); socket.readDatagram(tmp.data(), tmp.size()); buffer.append(tmp); } emit readyRead(); }
qint64 AudioReciever::readData(char *data, qint64 maxlen) { qint64 readBytes = qMin(qint64(buffer.size()), maxlen);
if (readBytes > 0) { QByteArray tmp = buffer.left(readBytes); buffer.remove(0,readBytes);
data = new char[readBytes]; char * tmp_ptr = tmp.data();
for (unsigned int i = 0; i < readBytes; i++) data[i] = tmp_ptr[i];
qDebug() << "Recieved " << readBytes << " bytes."; } return readBytes; }
qint64 AudioReciever::writeData(const char *data, qint64 len) { Q_UNUSED(data); Q_UNUSED(len); qDebug() << "IOError: device is read-only."; return 0; }
#include <QThread>
class VoIPClient : public QThread { Q_OBJECT public: explicit VoIPClient(QObject *parent = 0); ~VoIPClient(void);
void SetLocalPort(quint16 PORT); void SetRemoteHost(QHostAddress HOST); void SetRemotePort(quint16 PORT);
public slots: void Call(void); void FinishCall(void); void ResumePlaying(void); void ResumePlaying(QAudio::State);
protected: void run(void);
private: QHostAddress remoteHOST; quint16 localPORT; quint16 remotePORT; QAudioInput * audio_input; QAudioOutput * audio_output; AudioTransmitter * transmitter; AudioReciever * reciever; };
VoIPClient::VoIPClient(QObject *parent) : QThread(parent) { }
VoIPClient::~VoIPClient(void) { }
void VoIPClient::run(void) { QAudioFormat format = GetStreamAudioFormat(); audio_input = new QAudioInput(format); audio_output = new QAudioOutput(format); transmitter = new AudioTransmitter(localPORT, remoteHOST, remotePORT); reciever = new AudioReciever(localPORT, remoteHOST, remotePORT);
//connect(audio_output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(ResumePlaying(QAudio::State))); connect(reciever, SIGNAL(readyRead()), this, SLOT(ResumePlaying()));
reciever->Bind();
transmitter->open(QIODevice::WriteOnly | QIODevice::Truncate); reciever->open(QIODevice::ReadOnly);
audio_output->start(reciever); audio_input->start(transmitter);
qDebug() << "Started conversation with" << remoteHOST.toString() << ".";
exec();
audio_input->stop(); audio_output->stop(); transmitter->close(); reciever->close();
delete audio_input; delete audio_output; delete transmitter; delete reciever;
qDebug() << "Finished conversation."; }
void VoIPClient::Call(void) { start(); }
void VoIPClient::FinishCall(void) { exit(0); }
void VoIPClient::ResumePlaying(void) { if (audio_output->state() != QAudio::ActiveState) { audio_output->start(reciever); } }
void VoIPClient::ResumePlaying(QAudio::State state) { if (state != QAudio::ActiveState) { audio_output->start(reciever); } }
void VoIPClient::SetLocalPort(quint16 PORT) { localPORT = PORT; }
void VoIPClient::SetRemoteHost(QHostAddress HOST) { remoteHOST = HOST; }
void VoIPClient::SetRemotePort(quint16 PORT) { remotePORT = PORT; }
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 16, 2012, 20:46
QUdpSocket принимает корректный массив char*, это было проверено ранее. Следовательно, порча происходит дальше. Но дебаггер в Qt Creator странно показывает данные при отладке, и я не могу понять, где именно данные портятся.
Название: Re: QUdpSocket readyRead
Отправлено: popper от Январь 17, 2012, 10:42
Мне кажется, что класс AudioTransmitter вообще не нужен, т.к. QUDPSocket наследуется от QIODevice и QAudioInput может с ним напрямую работать. Может быть лагание звука связано с пакетной передачей данных? Попробуй сохранить принятые данные в файл и проиграть их отдельно.
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 17, 2012, 13:43
При записи в QFile все нормально. Следовательно, этот блок (и весь слот readPendingDatagrams()) работает правильно: QMutexLocker locker(&mutex); QByteArray tmp = buffer.left(readBytes); buffer.remove(0,readBytes); locker.unlock();
Как видите, даже синхронизация буфера между потоками не помогает. AudioReciever::AudioReciever(quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; dest = new QFile("C:\\test.raw"); dest->open(QIODevice::WriteOnly | QIODevice::Truncate); }
AudioReciever::~AudioReciever(void) { dest->close(); delete dest; }
bool AudioReciever::Bind(void) { connect(&socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); return socket.bind(QHostAddress::Any, localPORT, QUdpSocket::DontShareAddress); }
bool AudioReciever::isSequential(void) const { return true; }
bool AudioReciever::seek(qint64 pos) { Q_UNUSED(pos); return false; }
void AudioReciever::readPendingDatagrams(void) { while (socket.hasPendingDatagrams()) { QByteArray tmp; tmp.resize(socket.pendingDatagramSize()); socket.readDatagram(tmp.data(), tmp.size());
QMutexLocker locker(&mutex); buffer.append(tmp); locker.unlock();
qDebug() << "Recieved " << tmp.size() << " bytes."; emit readyRead(); } }
qint64 AudioReciever::readData(char *data, qint64 maxlen) { qint64 readBytes = qMin(qint64(buffer.size()), maxlen);
if (readBytes > 0) { QMutexLocker locker(&mutex); QByteArray tmp = buffer.left(readBytes); buffer.remove(0,readBytes); locker.unlock();
data = new char[readBytes]; qstrncpy(data, tmp.data(), readBytes); dest->write(tmp.data(), tmp.size());
qDebug() << "Returned from AudioReciever::readData():" << readBytes; } return readBytes; }
qint64 AudioReciever::writeData(const char *data, qint64 len) { Q_UNUSED(data); Q_UNUSED(len); qDebug() << "IOError: device is read-only."; return 0; }
Название: Re: QUdpSocket readyRead
Отправлено: popper от Январь 17, 2012, 14:47
Еще 3 рекомендации: 1. В AudioReceiver переопределить метод QIODevice::bytesAvailable () 2. Завести в потоке слот на сигнал void QAudioOutput::stateChanged ( QAudio::State state ) [signal] и вывести в дебаг информацию 3. Пройтись по исходникам QAudioOutput и посмотреть, как он работает с объектом QIODevice Проверь, может ли в твоем коде void VoIPClient::ResumePlaying(void) { if (audio_output->state() != QAudio::ActiveState) { audio_output->start(reciever); } }
быть такое, что пока данные по UDP обрабатывались в AudioReceiver, QAudioOutput уже закончил проигрывать имеющиеся данные. Тогда QAudio::ActiveState == QAudio::IdleState
В этом случае, по идее, при получении сигнала readyRead() QAudioOutput сам должен возобновлять проигрывание звука.
Название: Re: QUdpSocket readyRead
Отправлено: ProGOLD от Январь 17, 2012, 17:21
Спасибо за помощь. Нашел подсказку тут: http://stackoverflow.com/a/3438566 (http://stackoverflow.com/a/3438566). Нужно было вызывать именно QIODevice * QAudioOutput.start(void), теперь все работает, хотя и немного лагает. В погоне за симметрией я модифицировал и без того работавший AudioTransmiter. Конечный код выглядит так: #include <QObject> #include <QIODevice> #include <QUdpSocket> #include <QHostAddress> #include <QAudioInput> #include <QAudioOutput>
// Function returns constant audio format of all audio streams. QAudioFormat GetStreamAudioFormat(void);
// Class produces a device to capture and send audio over the network. class AudioTransmitter : public QObject { Q_OBJECT public: AudioTransmitter(quint16 localPort, QHostAddress remoteHost, quint16 remotePort); void Start(void); void Stop(void);
private: QUdpSocket socket; QAudioInput * audio_input; QIODevice * audio_device; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT; const qint64 maxsize;
private slots: void sendDatagrams(void); };
// Class produces a device to recieve audio from the network and play it. class AudioReciever : public QObject { Q_OBJECT public: AudioReciever(quint16 localPort, QHostAddress remoteHost, quint16 remotePort); bool Start(void); void Stop(void);
private: QUdpSocket socket; QAudioOutput * audio_output; QIODevice * audio_device; quint16 localPORT; QHostAddress remoteHOST; quint16 remotePORT;
private slots: void readPendingDatagrams(void); };
QAudioFormat GetStreamAudioFormat(void) { QAudioFormat format; format.setSampleRate(44100); format.setChannels(1); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(format)) format = info.nearestFormat(format);
return format; }
////////////////////////////////// // AudioTransmitter Class //////////////////////////////////
AudioTransmitter::AudioTransmitter(quint16 localPort, QHostAddress remoteHost, quint16 remotePort) : maxsize(8192) { localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
void AudioTransmitter::Start(void) { audio_input = new QAudioInput(GetStreamAudioFormat()); audio_device = audio_input->start(); connect(audio_device, SIGNAL(readyRead()), this, SLOT(sendDatagrams())); }
void AudioTransmitter::Stop(void) { audio_input->stop(); delete audio_input; socket.close(); disconnect(audio_device, SIGNAL(readyRead()), this, SLOT(sendDatagrams())); }
void AudioTransmitter::sendDatagrams(void) { QByteArray tmp = audio_device->read(maxsize); socket.writeDatagram(tmp.data(), tmp.size(), remoteHOST, remotePORT); }
////////////////////////////////// // AudioReciever Class //////////////////////////////////
AudioReciever::AudioReciever(quint16 localPort, QHostAddress remoteHost, quint16 remotePort) { localPORT = localPort; remoteHOST = remoteHost; remotePORT = remotePort; }
bool AudioReciever::Start(void) { audio_output = new QAudioOutput(GetStreamAudioFormat()); audio_device = audio_output->start(); connect(&socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); return socket.bind(QHostAddress::Any, localPORT, QUdpSocket::DontShareAddress); }
void AudioReciever::Stop(void) { audio_output->stop(); delete audio_output; socket.close(); disconnect(&socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); }
void AudioReciever::readPendingDatagrams(void) { while (socket.hasPendingDatagrams()) { qint64 size = socket.pendingDatagramSize(); char * data = new char[size]; socket.readDatagram(data, size); audio_device->write(data, size); } }
#include <QThread>
class VoIPClient : public QThread { Q_OBJECT public: explicit VoIPClient(QObject *parent = 0);
void SetLocalPort(quint16 PORT); void SetRemoteHost(QHostAddress HOST); void SetRemotePort(quint16 PORT);
public slots: void Call(void); void FinishCall(void);
protected: void run(void);
private: AudioTransmitter * transmitter; AudioReciever * reciever; QHostAddress remoteHOST; quint16 localPORT; quint16 remotePORT; };
VoIPClient::VoIPClient(QObject *parent) : QThread(parent) { }
void VoIPClient::run(void) { transmitter = new AudioTransmitter(localPORT, remoteHOST, remotePORT); reciever = new AudioReciever(localPORT, remoteHOST, remotePORT); reciever->Start(); transmitter->Start();
exec();
transmitter->Stop(); reciever->Stop(); delete transmitter; delete reciever; }
void VoIPClient::Call(void) { start(); }
void VoIPClient::FinishCall(void) { exit(0); }
void VoIPClient::SetLocalPort(quint16 PORT) { localPORT = PORT; }
void VoIPClient::SetRemoteHost(QHostAddress HOST) { remoteHOST = HOST; }
void VoIPClient::SetRemotePort(quint16 PORT) { remotePORT = PORT; }
Может быть, кому-то мой проект (пока не доделанный) окажется полезным. Посему добавляю к посту вложение.
Название: Re: [Решено] QUdpSocket::readyRead()
Отправлено: Fregloin от Январь 18, 2012, 12:36
еще советовал бы сделать внутренний буфер на несколько секунд, в который клеять принятые данные, возможно лаг уменьшится...
Название: Re: [Решено] QUdpSocket::readyRead()
Отправлено: popper от Январь 18, 2012, 12:55
Еще можно попробовать уменьшить частоту семплирования до минимального поддерживаемого значения
|