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());
    }
}
相关推荐
Say-hai3 小时前
音视频入门知识(七):时间戳及其音视频播放原理
音视频
Java搬砖组长3 小时前
youtube下载的视频怎么保存到本地
音视频
科技小E3 小时前
国标GB28181设备管理软件EasyGBS:P2P远程访问故障排查指南(设备端)
网络协议·智能路由器·音视频·p2p
lw向北.5 小时前
Qt For Android之环境搭建(Qt 5.12.11 Qt下载SDK的处理方案)
android·开发语言·qt
Say-hai5 小时前
音视频入门知识(二)、图像篇
音视频
小灰灰搞电子5 小时前
Qt实现Android的图案密码(图形解锁)源码分享
开发语言·qt
kiiila16 小时前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
Fre丸子_17 小时前
ffmpeg之播放一个yuv视频
ffmpeg·音视频
9527华安18 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
catmes19 小时前
设置浏览器声音或视频的自动播放策略
chrome·音视频·edge浏览器