You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Qt 6.4.x(Windows)实现麦克风音频转码并通过QUdpSocket发送的技术咨询

Qt 6.4.x Windows麦克风音频捕获转码UDP发送实现方案

核心组件选择

  • 用QAudioSource捕获麦克风音频,不需要QAudioDecoder(它仅用于解码音频文件,和实时音频捕获无关)
  • 自定义循环缓冲区解耦音频捕获与网络发送的线程逻辑
  • 用QUdpSocket发送固定大小的转码后音频数据包

实现步骤及示例代码

1. 配置麦克风捕获参数

初始化音频输入格式,匹配麦克风的16位小端输出:

// 配置麦克风输入格式
QAudioFormat inputFormat;
inputFormat.setSampleRate(48000); // 可按需调整为44100等
inputFormat.setChannelCount(1);   // 单声道,也支持双声道
inputFormat.setSampleFormat(QAudioFormat::Int16); // 16位采样
inputFormat.setByteOrder(QAudioFormat::LittleEndian); // 小端字节序

// 获取默认麦克风设备
QAudioDevice inputDevice = QMediaDevices::defaultAudioInput();
if (!inputDevice.isFormatSupported(inputFormat)) {
    qWarning() << "指定格式不支持,自动切换为设备首选格式";
    inputFormat = inputDevice.preferredFormat();
}

// 创建音频捕获源
QAudioSource* audioSource = new QAudioSource(inputDevice, inputFormat, this);

2. 线程安全的循环缓冲区

用于暂存捕获到的音频数据,避免捕获与发送线程冲突:

class AudioBuffer : public QObject
{
    Q_OBJECT
public:
    explicit AudioBuffer(QObject *parent = nullptr) : QObject(parent) {}

    void write(const QByteArray& data) {
        QMutexLocker locker(&m_mutex);
        m_buffer.append(data);
        emit dataAvailable();
    }

    QByteArray read(qint64 maxSize) {
        QMutexLocker locker(&m_mutex);
        qint64 readSize = qMin(maxSize, m_buffer.size());
        QByteArray data = m_buffer.left(readSize);
        m_buffer.remove(0, readSize);
        return data;
    }

    qint64 size() {
        QMutexLocker locker(&m_mutex);
        return m_buffer.size();
    }

signals:
    void dataAvailable();

private:
    QByteArray m_buffer;
    QMutex m_mutex;
};

3. 启动音频捕获并写入缓冲区

将麦克风捕获的数据持续写入循环缓冲:

AudioBuffer* audioBuffer = new AudioBuffer(this);

// 启动捕获,获取输入IO设备
QIODevice* inputIO = audioSource->start();
connect(inputIO, &QIODevice::readyRead, this, [=](){
    QByteArray rawData = inputIO->readAll();
    audioBuffer->write(rawData);
});

4. 16位小端转24位大端转码函数

实现采样格式的转换逻辑:

QByteArray convert16LETo24BE(const QByteArray& inputData) {
    QByteArray outputData;
    outputData.reserve(inputData.size() * 3 / 2); // 预分配内存

    for (int i = 0; i < inputData.size(); i += 2) {
        // 读取16位小端采样值
        qint16 sample = *reinterpret_cast<const qint16*>(inputData.constData() + i);
        // 扩展为24位(左移8位补高位0)
        qint32 extendedSample = static_cast<qint32>(sample) << 8;
        // 转换为大端字节序,写入3个字节
        char bytes[3];
        bytes[0] = (extendedSample >> 16) & 0xFF;
        bytes[1] = (extendedSample >> 8) & 0xFF;
        bytes[2] = extendedSample & 0xFF;
        outputData.append(bytes, 3);
    }
    return outputData;
}

5. 打包固定大小数据包并UDP发送

在独立线程中监听缓冲数据,转码后按固定大小打包发送:

class RtpWorker : public QThread
{
    Q_OBJECT
public:
    explicit RtpWorker(AudioBuffer* buffer, QObject *parent = nullptr) 
        : QThread(parent), m_audioBuffer(buffer) {}

    void setTarget(const QString& ip, quint16 port) {
        m_targetIp = ip;
        m_targetPort = port;
    }

protected:
    void run() override {
        QUdpSocket udpSocket;

        // 监听缓冲数据,转码后发送
        connect(m_audioBuffer, &AudioBuffer::dataAvailable, this, [=](){
            const int targetPacketSize = 1024; // UDP包大小建议不超过MTU(如1500)
            // 计算转码前需要的原始数据量:24位包大小/3 * 2(16位每个样本占2字节)
            qint64 requiredRawSize = (targetPacketSize / 3) * 2;

            while (m_audioBuffer->size() >= requiredRawSize) {
                QByteArray rawData = m_audioBuffer->read(requiredRawSize);
                QByteArray transcodedData = convert16LETo24BE(rawData);
                // 不足固定大小补0(可选,根据接收端需求调整)
                if (transcodedData.size() < targetPacketSize) {
                    transcodedData.append(targetPacketSize - transcodedData.size(), 0);
                }
                // 发送UDP数据包
                udpSocket.writeDatagram(transcodedData, QHostAddress(m_targetIp), m_targetPort);
            }
        }, Qt::QueuedConnection);

        exec(); // 启动线程事件循环
    }

private:
    AudioBuffer* m_audioBuffer;
    QString m_targetIp;
    quint16 m_targetPort;
};

// 启动发送线程
RtpWorker* rtpWorker = new RtpWorker(audioBuffer, this);
rtpWorker->setTarget("127.0.0.1", 8888); // 设置目标IP和端口
rtpWorker->start();

关键注意事项

  • 线程安全:循环缓冲区必须加锁,防止读写冲突
  • 格式兼容:若麦克风不支持指定的16位小端格式,需用设备的preferredFormat自动适配
  • 数据包大小:UDP包建议控制在1500字节以内(MTU限制),避免分片
  • 资源清理:程序退出前需停止QAudioSource,销毁线程和缓冲区对象

内容的提问来源于stack exchange,提问作者johnco3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:25:58