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());
    }
}
相关推荐
2601_9668274511 小时前
电视端云盘在线播放怎么选?支持4K HDR的应用推荐
音视频
sycmancia11 小时前
Qml——Image和AnimatedImage使用
qt
开开心心就好12 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
lhbweilai15 小时前
QT(了解各个控件和布局)上
数据库·qt
AI视频工具指南15 小时前
AI视频的声音和画面怎么配合?先分清谁在传递信息
人工智能·aigc·音视频
辰辰观影18 小时前
无广告播放器推荐来啦
音视频
TrueDei18 小时前
GStreamer 使用 valve 分别控制音视频流的暂停与恢复
音视频
一条破秋裤19 小时前
基于 Hi3516CV610 的立定跳远视频测量系统调研与实施方案
arm开发·音视频
2601_9668274519 小时前
视频里的特效字幕怎么翻译?外挂字幕与AI字幕一键处理教程
音视频
八月的雨季 最後的冰吻20 小时前
音视频面试--延迟优化
面试·职场和发展·音视频