QT-播放原始PCM音频流

QT += multimedia

audioplay.h

cpp 复制代码
/*************************************************************************
接口描述:原始音频播放类
拟制:
接口版本:V1.0
时间:20220922
说明:
*************************************************************************/

#ifndef AUDIOPLAY_H
#define AUDIOPLAY_H

#include <QAudioFormat>
#include <QAudioOutput>
#include <QMutex>
#include <QThread>

class AudioThread;
class AudioPlay : public QObject
{
    Q_OBJECT
public:
    explicit AudioPlay(QObject *parent = nullptr);
    ~AudioPlay();

public:
    void inputVoice(char *pcVoice, int nLen);   //PCM数据输入
    void setSampleRate(int nSampleRate);        //设置采样率
    void stop();                                //停止播放

signals:
    void sendDataSignal(QByteArray qbaData);
    void inputVoiceSignal(QByteArray qbaData);
    void setSampleRateSignal(int nSampleRate);
    void stopAudio();

private slots:
    void audioStateChanged(QAudio::State state);

private:
    QAudioFormat m_audioFormat;

    int m_nSampleRate;
    bool m_bAudioOpen;

    QAudioOutput *m_pAudioOutput;
    AudioThread *m_pAudioThread;
    QThread *m_pThread;

private:
    void audioPlay();
    void setFormat();
    void releaseAudio();
};

class AudioDevice : public QIODevice
{
    Q_OBJECT
public:
    explicit AudioDevice(QObject *parent = nullptr);

public:
    void inputData(char *pcData, int nLen);
    void start();
    void stop();

    // QIODevice interface
protected:
    qint64 readData(char *data, qint64 maxlen);
    qint64 writeData(const char *data, qint64 len);
    qint64 bytesAvailable() const;

private:
    QByteArray m_qbaAudioBuffer;
};

class AudioThread : public QObject
{
    Q_OBJECT
public:
    explicit AudioThread(QAudioOutput *pAudioOutput, QObject *parent = nullptr);
    ~AudioThread();

public slots:
    void inputData(QByteArray qbaData);

private:
    QAudioOutput *m_pAudioOutput;
    AudioDevice *m_pAudioDevice;
};

#endif // AUDIOPLAY_H

audioplay.cpp

cpp 复制代码
#include "audioplay.h"
#include <QDebug>

static QMutex m_mutex;

AudioPlay::AudioPlay(QObject *parent)
    : QObject(parent)
{
    m_nSampleRate = 8000;
    m_bAudioOpen = false;
    m_pAudioOutput = nullptr;
    m_pAudioThread = nullptr;
    setFormat();
    m_pThread = new QThread;
    m_pThread->start();
    connect(this,
            &AudioPlay::inputVoiceSignal,
            this,
            [&](QByteArray qbaData) {
        if (!m_bAudioOpen) {
            audioPlay();
        }
        emit sendDataSignal(qbaData);
    },
    Qt::QueuedConnection);

    connect(this,
            &AudioPlay::setSampleRateSignal,
            this,
            [&](int nSampleRate) {
        if (m_nSampleRate != nSampleRate) {
            m_nSampleRate = nSampleRate;
            setFormat();
        }
    },
    Qt::QueuedConnection);

    connect(this, &AudioPlay::stopAudio, this, [&] {
        m_bAudioOpen = false;
        releaseAudio();
    });
}

AudioPlay::~AudioPlay()
{
    releaseAudio();
    m_pThread->exit();
    if (m_pThread != nullptr) {
        m_pThread->deleteLater();
        m_pThread = nullptr;
    }
}

void AudioPlay::inputVoice(char *pcVoice, int nLen)
{
    emit inputVoiceSignal(QByteArray(pcVoice, nLen));
}

void AudioPlay::setSampleRate(int nSampleRate)
{
    emit setSampleRateSignal(nSampleRate);
}

void AudioPlay::stop()
{
    emit stopAudio();
}

void AudioPlay::audioStateChanged(QAudio::State state)
{
    switch (state) {
    case QAudio::IdleState:
        m_bAudioOpen = false;
        releaseAudio();
        setFormat();
        break;
    default:
        break;
    }
}

void AudioPlay::audioPlay()
{
    QList<QAudioDeviceInfo> outputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
    if (outputDevices.size() <= 0) {
        return;
    }
    if (m_pAudioOutput == nullptr) {
        m_pAudioOutput = new QAudioOutput(m_audioFormat);
        connect(m_pAudioOutput, &QAudioOutput::stateChanged, this, &AudioPlay::audioStateChanged);
        if (m_pAudioThread) {
            m_pAudioThread->deleteLater();
            m_pAudioThread = nullptr;
        }

        m_pAudioThread = new AudioThread(m_pAudioOutput);
        m_pAudioThread->moveToThread(m_pThread);

        connect(this,
                &AudioPlay::sendDataSignal,
                m_pAudioThread,
                &AudioThread::inputData,
                Qt::QueuedConnection);
    }
    m_bAudioOpen = true;
}

void AudioPlay::setFormat()
{
    if (m_pAudioOutput != nullptr) {
        m_pAudioOutput->reset();
        m_pAudioOutput->start();
    }
    //设置采样率
    m_audioFormat.setSampleRate(m_nSampleRate);
    //设置通道数
    m_audioFormat.setChannelCount(1);
    //设置采样大小,一般为8位或16位
    m_audioFormat.setSampleSize(16);
    //设置编码方式
    m_audioFormat.setCodec("audio/pcm");
    //设置字节序
    m_audioFormat.setByteOrder(QAudioFormat::LittleEndian);
    //设置样本数据类型
    m_audioFormat.setSampleType(QAudioFormat::SignedInt);
}

void AudioPlay::releaseAudio()
{
    if (m_pAudioThread != nullptr) {
        m_pAudioThread->deleteLater();
        m_pAudioThread = nullptr;
    }
    if (m_pAudioOutput != nullptr) {
        m_pAudioOutput->stop();
        m_pAudioOutput->deleteLater();
        m_pAudioOutput = nullptr;
    }
}

AudioDevice::AudioDevice(QObject *parent)
    : QIODevice(parent)
{
    QMutexLocker locker(&m_mutex);
    m_qbaAudioBuffer.clear();
}

void AudioDevice::inputData(char *pcData, int nLen)
{
    QMutexLocker locker(&m_mutex);
    m_qbaAudioBuffer.append(pcData, nLen);
}

void AudioDevice::start()
{
    if (!this->isOpen()) {
        open(QIODevice::ReadOnly);
    }
}

void AudioDevice::stop()
{
    QMutexLocker locker(&m_mutex);
    m_qbaAudioBuffer.clear();
    this->close();
}

qint64 AudioDevice::bytesAvailable() const
{
    QMutexLocker locker(&m_mutex);
    qint64 llReturn = m_qbaAudioBuffer.size() + QIODevice::bytesAvailable();
    return llReturn;
}

qint64 AudioDevice::readData(char *data, qint64 maxlen)
{
    QMutexLocker locker(&m_mutex);
    memset(data, 0, maxlen);
    if (m_qbaAudioBuffer.size() < maxlen) {
        maxlen = m_qbaAudioBuffer.size();
    }
    if (maxlen > 0) {
        memcpy(data, m_qbaAudioBuffer.left(maxlen).data(), maxlen);
        m_qbaAudioBuffer.remove(0, maxlen);
    }

    return maxlen;
}

qint64 AudioDevice::writeData(const char *data, qint64 len)
{
    Q_UNUSED(data);
    Q_UNUSED(len);
    return 0;
}

AudioThread::AudioThread(QAudioOutput *pAudioOutput, QObject *parent)
    : QObject(parent)
{
    m_pAudioOutput = nullptr;
    m_pAudioDevice = nullptr;

    m_pAudioDevice = new AudioDevice(this);
    m_pAudioDevice->start();
    m_pAudioOutput = pAudioOutput;
    m_pAudioOutput->start(m_pAudioDevice);
}

AudioThread::~AudioThread()
{
    if (m_pAudioDevice != nullptr) {
        m_pAudioDevice->stop();
        m_pAudioDevice->deleteLater();
        m_pAudioDevice = nullptr;
    }
}

void AudioThread::inputData(QByteArray qbaData)
{
    if (m_pAudioDevice != nullptr) {
        m_pAudioDevice->inputData(qbaData.data(), qbaData.size());
    }
}
相关推荐
martian66511 分钟前
QT开发: Qt 框架中字符串核心类QString详解
开发语言·c++·qt
我会说:明天更快乐17 分钟前
[项目:微服务即时通讯系统客户端(基于C++QT)]三,左侧界面搭建
c++·qt·微服务
躺着要饭2 小时前
Qt 注册表操作
开发语言·qt
SQingL9 小时前
用OPenCV分割视频
人工智能·opencv·音视频
弘毅_Hao9 小时前
创建Application(Qt)模板项目时的 Base class选择
qt·base class
零点零一11 小时前
如何在堆和栈上分别创建一个`QObject`子类对象
qt
Skrrapper14 小时前
智谱AI:CogVideoX-2b——视频生成模型的得力工具
人工智能·音视频
拒绝游泳的小鱼儿14 小时前
云盘视频保护神器,支持云盘视频加密与在线播放,配合alist使用,超完美!
音视频·alist·网盘工具·视频加密
Bubbliiiing14 小时前
AIGC专栏15——CogVideoX-Fun详解 支持图&文生视频 拓展CogVideoX到256~1024任意分辨率生成
aigc·音视频·easyanimate·cogvideox·cogvideox-fun
x.100000100016 小时前
下载分享抖音视频并转成文本
音视频