Russian Qt Forum
Март 28, 2024, 14:58 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: [Решено] QUdpSocket::readyRead()  (Прочитано 15743 раз)
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;
}
« Последнее редактирование: Январь 17, 2012, 17:24 от ProGOLD » Записан
popper
Гость
« Ответ #1 : Январь 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;
}
Записан
ProGOLD
Гость
« Ответ #2 : Январь 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;
}

Увы, проблема не решилась. Теперь с обеих сторон слышен треск и ничего больше. Может, я с памятью работаю неправильно?
Записан
andrew.k
Гость
« Ответ #3 : Январь 16, 2012, 18:38 »

мне-кажется ты скорее неправильно работаешь с qsound*
ты его по приходу каждого куска растартуешь. потому и треск.
сам не пользовался, поэтому подробнее не скажу.
Записан
ProGOLD
Гость
« Ответ #4 : Январь 16, 2012, 19:00 »

Я его перезапускаю только если он уже остановился.
Код:
void VoIPClient::ResumePlaying(void)
{
    if (audio_output->state() != QAudio::ActiveState)
    {
        audio_output->stop();
        audio_output->start(reciever);
    }
}
Записан
popper
Гость
« Ответ #5 : Январь 16, 2012, 19:48 »

Пока еще предлагаю поиграть с QAudioFormat::LittleEndian и QAudioFormat::BigEndian.
И вот этот код мне не нравится:
Код:
connect(reciever, SIGNAL(readyRead()), this, SLOT(ResumePlaying()));
Позже посмотрю еще внимательнее
Записан
ProGOLD
Гость
« Ответ #6 : Январь 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;
}
Записан
ProGOLD
Гость
« Ответ #7 : Январь 16, 2012, 20:46 »

QUdpSocket принимает корректный массив char*, это было проверено ранее. Следовательно, порча происходит дальше. Но дебаггер в Qt Creator странно показывает данные при отладке, и я не могу понять, где именно данные портятся.
Записан
popper
Гость
« Ответ #8 : Январь 17, 2012, 10:42 »

Мне кажется, что класс AudioTransmitter вообще не нужен, т.к. QUDPSocket наследуется от QIODevice и QAudioInput может с ним напрямую работать.
Может быть лагание звука связано с пакетной передачей данных?
Попробуй сохранить принятые данные в файл и проиграть их отдельно.
Записан
ProGOLD
Гость
« Ответ #9 : Январь 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;
}
Записан
popper
Гость
« Ответ #10 : Январь 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 сам должен возобновлять проигрывание звука.
Записан
ProGOLD
Гость
« Ответ #11 : Январь 17, 2012, 17:21 »

Спасибо за помощь. Нашел подсказку тут: 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;
}

Может быть, кому-то мой проект (пока не доделанный) окажется полезным. Посему добавляю к посту вложение.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #12 : Январь 18, 2012, 12:36 »

еще советовал бы сделать внутренний буфер на несколько секунд, в который клеять принятые данные, возможно лаг уменьшится...
Записан
popper
Гость
« Ответ #13 : Январь 18, 2012, 12:55 »

Еще можно попробовать уменьшить частоту семплирования до минимального поддерживаемого значения
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.071 секунд. Запросов: 22.