使用QAudioDecoder + QAudioSink实现音频播放

要实现 QAudioDecoder + QAudioSink 的音频播放,主要是将 MP3、AAC 等压缩格式的音频文件,使用 QAudioDecoder 解码成 PCM 格式,然后通过 QAudioSink 播放出来。

QAudioSink基本概念

QAudioSink 是 Qt 6 中用于播放音频的类。它提供了低级别的接口,可以直接控制音频数据的播放和输出到设备。QAudioSink 通常用于需要更灵活地处理音频流的场景;

实现思路

  1. 解码器 (QAudioDecoder)

    • 将 MP3、AAC 等压缩音频格式解码成 PCM 格式,输出为音频缓冲区 (QAudioBuffer)
  2. 音频播放 (QAudioSink)

    • QAudioSink 负责接收解码后的 PCM 数据流,并将其传输到音频输出设备(如耳机、扬声器等)。
    • 通过 audioSink->start() 启动音频输出,返回一个 QIODevice,将解码后的数据流写入这个 QIODevice

实现流程

  1. 创建 QAudioDecoder,设置解码音频的格式、文件路径。
  2. 创建 QAudioSink,配置音频格式(采样率、位深度、通道数等)。
  3. 连接 QAudioDecoder 的 bufferReady() 信号,当解码器有新数据时,将数据写入 QAudioSink。
  4. 开始解码,当解码完成后,自动停止音频播放。

完整代码示例

#include <QCoreApplication>
#include <QAudioDecoder>
#include <QAudioSink>
#include <QAudioFormat>
#include <QFile>
#include <QDebug>

class CustomAudioPlayer : public QObject
{
    Q_OBJECT

public:
    CustomAudioPlayer() {
        // **Step 1: 配置音频格式**
        QAudioFormat format;
        format.setSampleRate(44100); // 采样率 44.1kHz
        format.setChannelCount(2);   // 立体声
        format.setSampleSize(16);    // 每个样本 16 位
        format.setCodec("audio/pcm"); 
        format.setByteOrder(QAudioFormat::LittleEndian); // 小端存储
        format.setSampleType(QAudioFormat::SignedInt); // 有符号整数

        // **Step 2: 创建 QAudioSink**
        audioSink = new QAudioSink(format, this);
        audioDevice = audioSink->start(); // 启动 QIODevice,接收音频数据

        // **Step 3: 创建 QAudioDecoder**
        decoder = new QAudioDecoder(this);
        decoder->setSourceFilename("test.mp3"); // MP3 文件路径

        // 连接 QAudioDecoder 的信号
        connect(decoder, &QAudioDecoder::bufferReady, this, &CustomAudioPlayer::onBufferReady);
        connect(decoder, &QAudioDecoder::finished, this, &CustomAudioPlayer::onDecodingFinished);
        connect(decoder, &QAudioDecoder::error, this, &CustomAudioPlayer::onDecodingError);

        // **Step 4: 启动解码器**
        decoder->start();
        qDebug() << "开始解码音频...";
    }

public slots:
    // **当 QAudioDecoder 有新的音频数据时调用此函数**
    void onBufferReady() {
        QAudioBuffer buffer = decoder->read(); // 读取音频缓冲区
        if (!buffer.isValid()) {
            qWarning() << "无效的音频缓冲区";
            return;
        }

        // **将音频数据写入 QAudioSink**
        QByteArray data = QByteArray::fromRawData(reinterpret_cast<const char*>(buffer.data()), buffer.byteCount());
        audioDevice->write(data);
        qDebug() << "已播放数据:" << buffer.byteCount() << "字节";
    }

    // **当解码完成时调用**
    void onDecodingFinished() {
        qInfo() << "音频解码完成";
        audioSink->stop();
    }

    // **当解码发生错误时调用**
    void onDecodingError(QAudioDecoder::Error error) {
        qWarning() << "解码错误:" << decoder->errorString();
    }

private:
    QAudioSink *audioSink;       // 音频播放设备
    QIODevice *audioDevice;      // 音频设备 I/O
    QAudioDecoder *decoder;      // 音频解码器
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    CustomAudioPlayer player;
    return app.exec();
}

关键讲解

1. QAudioFormat

QAudioFormat 用于配置音频格式,解码后的 PCM 数据格式必须与 QAudioSink 的格式匹配。

QAudioFormat format;
format.setSampleRate(44100);        // 采样率 44.1kHz
format.setChannelCount(2);          // 立体声
format.setSampleSize(16);           // 16 位
format.setCodec("audio/pcm");       // PCM 音频
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);

注意:
QAudioSink 和 QAudioDecoder 必须使用相同的音频格式,否则音频播放可能会失败。


2. QAudioSink

  • audioSink->start():启动音频播放设备,返回一个 QIODevice,你可以将数据写入这个 I/O 设备。
  • audioDevice->write(data):将数据流写入音频设备data解码后的 PCM 数据

3. QAudioDecoder

  • decoder->setSourceFilename("test.mp3"):指定要解码的文件。
  • connect(decoder, &QAudioDecoder::bufferReady, this, &CustomAudioPlayer::onBufferReady)
    • 当解码器有新数据时,调用 onBufferReady
    • bufferReady() 信号触发的间隔由解码器的缓冲区大小决定。

4. QAudioBuffer

  • QAudioBuffer buffer = decoder->read();
  • buffer.data() 返回原始音频数据。
  • buffer.byteCount() 返回缓冲区中的字节数。

示例输出

开始解码音频...
已播放数据: 1024 字节
已播放数据: 2048 字节
已播放数据: 1024 字节
音频解码完成

常见问题和解决方法

问题 原因 解决方案
无声音播放 音频格式不匹配 确保 QAudioFormat 与 QAudioSink、QAudioDecoder 格式一致
缓冲区溢出 数据未及时写入 增大缓冲区、使用更高的 QIODevice 速率
解码器报错 文件路径不正确或不支持的文件格式 检查音频路径,或确认音频格式是否被支持(MP3、WAV 通常是被支持的)
噪音或卡顿 缓冲区数据不稳定 检查解码器的缓冲区,确保数据连续传输
QAudioDecoder 错误 不支持的文件格式 确保音频是 MP3、AAC 等受支持的格式,或使用 ffmpeg 转换为 PCM 文件

扩展功能

  1. 实时音量控制
    可以在播放时调用 audioSink->setVolume(0.5) 设置音量。

  2. 设备选择
    使用 QMediaDevices::audioOutputs() 获取系统中的扬声器、耳机等设备,将 QAudioSink 指定为特定的音频设备。

  3. 流式数据播放
    替换 QAudioDecoder 的输入源为 QBufferQNetworkAccessManager,实现网络流媒体音频播放。

  4. 动态控制播放
    实现暂停、恢复、停止,可调用:

audioSink->suspend(); // 暂停
audioSink->resume();  // 恢复
audioSink->stop();    // 停止

总结

  • QAudioDecoder + QAudioSink 是播放音频的底层实现方案
  • QAudioDecoder 负责解码音频(如 MP3、AAC),QAudioSink 负责播放解码后的 PCM 数据。
  • QMediaPlayer 不同,你可以完全控制音频数据流(如音频数据分析、滤波处理等)。
posted @   吴海琼  阅读(123)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示