在使用 c++ 和 qt 的实时音频服务器中,在哪里将 Wav header 添加到数据中?

Where to add the Wav header to data in a live audio server using c++ and qt?

我知道这是一个很长的问题,但我真的希望你有耐心阅读它并帮助我,因为我在这里似乎陷入了死胡同。

我是 c++ client-server 和 Qt 多媒体开发方面的新手。我正在尝试创建一个服务器,允许使用 Qt 将音频流式传输到客户端。

使用 Qt 的 QAudioInput, QAudioDeviceInfo, QAudioFormat classes 我学会了如何录制与 QAudioRecorder 示例中给出的音频截然不同的音频。我还学习了如何将 wav header 添加到原始音频数据并使音频可播放。我用来向音频添加 wav header 的函数如下:

void Server::rawToWavQT(QByteArray* arr, long samplingRate, long bytes)
{
    long chunksize=0x10;
    struct
    {
        unsigned short    wFormatTag;
        unsigned short    wChannels;
        unsigned long     dwSamplesPerSec;
        unsigned long     dwAvgBytesPerSec;
        unsigned short    wBlockAlign;
        unsigned short    wBitsPerSample;
    } fmt;

    long samplecount = bytes/2;
    long riffsize    = samplecount*2+0x24;
    long datasize    = samplecount*2;

    arr->append("RIFF");//    fwrite( "RIFF",     1, 4, wav );
    arr->append(QByteArray((const char*)&riffsize, 4));//    fwrite( &riffsize,  4, 1, wav );
    arr->append("WAVEfmt ");//    fwrite( "WAVEfmt ", 1, 8, wav );
    arr->append(QByteArray((const char*)&chunksize, 4));//    fwrite( &chunksize, 4, 1, wav );

    int bitsPerSample = 16;
    int bytesPerSample = bitsPerSample/8;
    int channel = 2;

    fmt.wFormatTag = 1;      // PCM
    fmt.wChannels  = channel;      // MONO
    fmt.dwSamplesPerSec  = samplingRate;
    fmt.dwAvgBytesPerSec = samplingRate*channel*bytesPerSample; // 16 bit
    fmt.wBlockAlign      = channel*bytesPerSample;
    fmt.wBitsPerSample   = bitsPerSample;

    arr->append(QByteArray((const char*)&fmt, sizeof(fmt)));//    fwrite( &fmt,      sizeof(fmt), 1, wav );
    arr->append("data");//    fwrite( "data",    1,           4, wav );
    arr->append(QByteArray((const char*)&datasize, 4));//    fwrite( &datasize, 4,           1, wav );
}

我还得到了关于如何使用 Qt 进行简单 server-client 通信的 example。通过该项目,我了解了如何使用 Qt 服务器和套接字模块开发 client-server 通信。

服务器的主要windowclass是这样的:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setFixedSize(size());
}

void MainWindow::on_pushButton_clicked()
{
    QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
    QAudioDeviceInfo devinfo = devices.at(0);
    input = new AudioInput(devinfo, this);
    quint16 port = ui->lineEdit->text().toInt();
    server = new Server(port, this);
    connect(input, SIGNAL(dataReady(QByteArray)), server, SLOT(writeData(QByteArray)));

    ui->comboBox->setEnabled(false);
    ui->lineEdit->setEnabled(false);
    ui->pushButton->setEnabled(false);
}

音频数据提供者class如下:

AudioInput::AudioInput(QAudioDeviceInfo devinfo, QObject *parent) : QObject(parent)
{
    QAudioFormat format;
    format.setChannelCount(1);
    format.setSampleRate(8000);
    format.setSampleSize(8);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    audio = new QAudioInput(devinfo, format, this);
    audio->setBufferSize(8192);

    device = audio->start();
    connect(device, SIGNAL(readyRead()), SLOT(readyRead()));
}

void AudioInput::readyRead()
{
    QByteArray data;

    //Check the number of samples in input buffer
    qint64 len = audio->bytesReady();

    //Read sound samples from input device to buffer
    if (len > 0)
    {
        data.resize(len);
        device->read(data.data(), len);
    }

    emit dataReady(data);
}

而服务器class如下:

Server::Server(quint16 port, QObject *parent) : QObject(parent)
{
    socket = 0;
    server = new QTcpServer(this);
    connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
    server->listen(QHostAddress::Any, port);
}

void Server::newConnection()
{
    socket = server->nextPendingConnection();
    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    connect(socket, SIGNAL(destroyed()), SLOT(zeropointer()));
}

void Server::zeropointer()
{
    socket = 0;
}

void Server::writeData(QByteArray data)
{
    if (socket)
        socket->write(data);
}

我现在面临的问题是在写入数据以在服务器中提供服务时找到添加 wav header 的适当阶段。我已尝试使用 rawToWavQT 函数在 writeData 函数中添加 header,如上所示,但在客户端它不太有效,我在那里听不到任何正确的声音。我什至尝试将客户端接收到的音频数据保存为 wav 文件,但保存后,我无法生成任何可播放的音频文件,而且与通常的 .wav 文件相比,文件本身非常小。

感谢您的耐心等待。

我是您在问题中使用的项目的所有者,我对其进行了一些更改。修改包括错误修复、多个客户端连接到服务器、协商音频设置和客户端录音。

See it here.

服务器是一个非常基本的实现,是我从互联网上的一些阅读中学到的,一些我从这些阅读中理解的东西以及我在 Qt 提供的多媒体示例中看到的其他东西。不只有一个来源!

希望对您有所帮助!