基于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
相关产品推荐
相关产品推荐

