QT 用QAudio语音库实现音频信号的采集 以及发送到另一台电脑 播放

一年多以前曾经写过一篇用QT  audio语音库实现音频的采集和播放的博文:https://blog.csdn.net/hanzhen7541/article/details/80152381

上面那个是初级版,实现的是从inputdevice音频采集,发送到目的地址端口;目的主机收到音频收到了信号,直接写入音频的设备进行播放。这几天又用到了QT的语音库,所以做了改进,因为网络环境不稳定,所以做了优化:接受端接收到音频流之后,并不直接写在输出的outputdevice进行播放,而是定义了一个缓冲区m_PCMDataBuffer,接受端sicket接收到音频流数据,调用函数addAudioBuffer向缓冲区里面写; 同时,重载了QThread的run函数,调用run()函数,每当缓冲区超过960字节(我社定的8000采样率,16比特采样,960字节对应60ms数据)就从缓冲区读取数据,写入QOutputDevice从而听到声音。

这样当网络不稳定的时候,每一包语音流有不同的延迟,在接受端也能听到较为流畅的声音。

话不多说,现在来看具体代码:

首先是发送线程,这个和之前博文里面的没用太多区别,在主函数中包含头文件之后初始化对象,调用mystart()函数就可以实现发送,调用mystop()函数就可以停止。发送端的头文件:

#ifndef AUDIOSENDTHREAD_H
#define AUDIOSENDTHREAD_H
//这是发送线程

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

class audiosendthread : public QThread
{
    Q_OBJECT
public:
    explicit audiosendthread(QObject *parent = nullptr);
    ~audiosendthread();

    QUdpSocket *udpSocket;
    QHostAddress destaddr;

    QAudioInput *input;
    QIODevice *inputDevice;
    QAudioFormat format;

    struct video{
        int lens;
        char data[960];
    };

    void setaudioformat(int samplerate, int channelcount, int samplesize);
    void mystart();
    void mystop();

public slots:
    void onReadyRead();

};

#endif // AUDIOSENDTHREAD_H

发送的.cpp

#include "audiosendthread.h"


audiosendthread::audiosendthread(QObject *parent)
    : QThread(parent)
{
    udpSocket = new QUdpSocket(this);
    udpSocket -> bind(QHostAddress::Any, 10005);
    destaddr.setAddress("127.0.0.1");//改成你的目的地址就行
}
audiosendthread::~audiosendthread(){
    delete udpSocket;
    delete input;
    delete inputDevice;
}

void audiosendthread::setaudioformat(int samplerate, int channelcount, int samplesize){
    format.setSampleRate(samplerate);
    format.setChannelCount(channelcount);
    format.setSampleSize(samplesize);
    format.setCodec("audio/pcm");
    format.setSampleType(QAudioFormat::SignedInt);
    format.setByteOrder(QAudioFormat::LittleEndian);

    input = new QAudioInput(format, this);

}

void audiosendthread::mystart(){
    qDebug()<<"audio begins to send";
    inputDevice = input->start();
    connect(inputDevice,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
}

void audiosendthread::mystop(){
    qDebug()<<"audio ends!";
    input->stop();
}

void audiosendthread::onReadyRead(){
    video vp;
    memset(&vp.data,0,sizeof(vp));

    // read audio from input device
    vp.lens = inputDevice -> read(vp.data,960);
    int num = udpSocket -> writeDatagram((const char*)&vp, sizeof(vp),destaddr,10004);
    qDebug()<

以及接受线程的头文件:

#ifndef AUDIO_PLAY_THREAD_H
#define AUDIO_PLAY_THREAD_H
//这是接收线程
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#define MAX_AUDIO_LEN 960000 //如果接收缓冲区大于这个数值就剪掉
#define FRAME_LEN_60ms 960 //每一个语音帧长度是960字节
class AudioPlayThread : public QThread
{
    Q_OBJECT

public:
    AudioPlayThread(QObject *parent = nullptr);
    ~AudioPlayThread();

    // ----------- 添加数据相关 ----------------------------------------
    // 设置当前的PCM Buffer
    void setCurrentBuffer(QByteArray buffer);
    // 添加数据
    void addAudioBuffer(char* pData, int len);
    // 清空当前的数据
    void cleanAllAudioBuffer(void);
    // ------------- End ----------------------------------------------

    // 设置当前的采样率、采样位数、通道数目
    void setCurrentSampleInfo(int sampleRate, int sampleSize, int channelCount);

    virtual void run(void) override;//多线程重载运行函数run

    // 设置音量
    void setCurrentVolumn(qreal volumn);

    void stop();//停止

private:
    QAudioOutput *m_OutPut = nullptr;
    QIODevice *m_AudioIo = nullptr;

    QByteArray m_PCMDataBuffer;
    int m_CurrentPlayIndex = 0;

    QMutex m_Mutex;
    // 播放状态
    volatile bool m_IsPlaying = true;

    //for Audio
    QUdpSocket *udpsocket;

    struct video{
        int lens;
        char data[960];
    };

private slots:
    void readyReadSlot();

};

#endif

接受线程.cpp

#include "audioplaythread.h"

AudioPlayThread::AudioPlayThread(QObject *parent)
    :QThread(parent)
{
    m_PCMDataBuffer.clear();

    udpsocket = new QUdpSocket(this);
    udpsocket->bind(QHostAddress::Any,10004);
    connect(udpsocket,SIGNAL(readyRead()),this,SLOT(readyReadSlot()));//收到网络数据报就开始往outputDevice写入,进行播放
}

AudioPlayThread::~AudioPlayThread()
{
    delete udpsocket;
    delete m_OutPut;
    delete m_AudioIo;

}

void AudioPlayThread::setCurrentVolumn(qreal volumn){
    m_OutPut->setVolume(volumn);
}

void AudioPlayThread::setCurrentSampleInfo(int sampleRate, int sampleSize, int channelCount)
{
    QMutexLocker locker(&m_Mutex);

    // Format
    QAudioFormat nFormat;
    nFormat.setSampleRate(sampleRate);
    nFormat.setSampleSize(sampleSize);
    nFormat.setChannelCount(channelCount);
    nFormat.setCodec("audio/pcm");
    nFormat.setSampleType(QAudioFormat::SignedInt);
    nFormat.setByteOrder(QAudioFormat::LittleEndian);

    if (m_OutPut != nullptr) delete m_OutPut;

    m_OutPut = new QAudioOutput(nFormat);
    m_AudioIo = m_OutPut->start();
    //this->start();
}

void AudioPlayThread::run(void)
{
    while (!this->isInterruptionRequested())
    {
        if (!m_IsPlaying)
        {
            break;
        }

        QMutexLocker locker(&m_Mutex);

        if(m_PCMDataBuffer.size() < m_CurrentPlayIndex + FRAME_LEN_60ms){//缓冲区不够播放60ms音频
            continue;
        }
        else{
            //拷贝960字节的数据
            char *writeData = new char[FRAME_LEN_60ms];
            memcpy(writeData,&m_PCMDataBuffer.data()[m_CurrentPlayIndex], FRAME_LEN_60ms);
            // 写入音频数据
            m_AudioIo->write(writeData, FRAME_LEN_60ms);
            m_CurrentPlayIndex += FRAME_LEN_60ms;
            qDebug()< MAX_AUDIO_LEN){
                m_PCMDataBuffer = m_PCMDataBuffer.right(m_PCMDataBuffer.size()-MAX_AUDIO_LEN);
                m_CurrentPlayIndex -= MAX_AUDIO_LEN;
            }
        }
    }
    m_PCMDataBuffer.clear();
    qDebug()<<"audio receiver stop!";
}

// 添加数据
void AudioPlayThread::addAudioBuffer(char* pData, int len)
{
    QMutexLocker locker(&m_Mutex);

    m_PCMDataBuffer.append(pData, len);
    //m_IsPlaying = true;
}

void AudioPlayThread::cleanAllAudioBuffer(void)
{
    QMutexLocker locker(&m_Mutex);
    m_CurrentPlayIndex = 0;
    m_PCMDataBuffer.clear();
    m_IsPlaying = false;
}

void AudioPlayThread::readyReadSlot(){
    while(udpsocket->hasPendingDatagrams()){
            QHostAddress senderip;
            quint16 senderport;
            qDebug()<<"audio is being received..."<readDatagram((char*)&vp,sizeof(vp),&senderip,&senderport);
            //outputDevice->write(vp.data,vp.lens);
            addAudioBuffer(vp.data, vp.lens);
    }
}

void AudioPlayThread::stop(){

    udpsocket->close();
    m_OutPut->stop();
    cleanAllAudioBuffer();
}

在主函数mainwindow.h中首先定义

    AudioPlayThread aud;
    audiosendthread audsend;

然后在mainwindow.cpp中分别开启两个线程就行了:

    //接受线程启动
    aud.setCurrentSampleInfo(8000,16,1);
    aud.setCurrentVolumn(100);
    aud.start();

    //发送线程启动
    audsend.setaudioformat(8000,1,16);
    audsend.mystart();

    //... 其他代码

    //结束两个线程
    audsend.mystop();
    aud.stop();

别忘了在.pro文件中首先加入下面两个库,否则会造成编译错误:

  1. QT += network

  2. QT += multimedia

以上就是QT发送接受语音的实现,欢迎指正。

相关代码已经上传至github:   https://github.com/Wzing0421/QTAudio

你可能感兴趣的:(Qt)