Qt使用QAudioInput、QAudioOutput实现局域网的音频通话

news2025/8/9 9:12:17

本文旨在介绍一下用Qt来实现局域网音频通话功能

技术实现

QAudioInput、QAudioOutput(Qt采集和播放音频类)

QUdpSocket(Qt的UDP通信类)

  话不多说,直接上代码链接,想下载的朋友可以直接去gitee下载。

  整体的思路就是,读取声卡的数据,通过UDP发送出去,同时也会读取UDP发送过来的流的数据,写入到音频播放设备里进行播放。

以下是一些比较简单的对这两个技术点的解释,以及部分代码实现细节。

QAudioFormat(音频采样格式)

这个类,保存了音频流的参数信息。主要的参数有:

采样频率代表,在一秒钟里面,采样的音频的数量。采样频率越大,就代表这个声音的振幅越准确,换言之就是声音的质量也就越高
采样位数代表,对采样的声音的振幅等级数量。采样位数越大,声音振幅的划分越细,得到的声音的就越真实,噪声就越少。

QAudioDeviceInfo

这个类是用来保存音频播放设备的一些信息的,在这里,我们主要用来获取设备所支持的语音格式。

QAudioInput、QAudioOutput

这两个类,是Qt中的用于采集和播放音频的类。简单的用法如下:

// 设置音频采样的参数
m_format.setSampleRate(8000);
m_format.setChannelCount(1);
m_format.setSampleSize(8);
m_format.setCodec("audio/pcm");
m_format.setByteOrder(QAudioFormat::LittleEndian);
m_format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(m_format)) {
    qWarning() << "Default format not supported, trying to use the nearest.";
    m_format = info.nearestFormat(m_format);
}

// 用采样的参数来实例化一个QAudioInput对象
m_audioInput = new QAudioInput(m_format);

// 用采样的参数来实例化一个QAudioOutput对象
m_audioOutput = new QAudioOutput(m_format, this);
m_outputDevice = m_audioOutput->start();

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

这两个类有一个函数start( ),这个函数会开启音频的读取或者写入,并返回一个对应的QIODevice,用来从设备里读取和写入音频数据。

当通话接通的时候,打开QAudioInput,将音频流数据,通过UDP发送到对方端口。

void MainWindow::slot_callResponse(int response)
{
    ui->stackedWidget->setCurrentIndex(0);
    m_dialogTimer.stop();
    if (response == 0) {
        slot_connected();
        m_inputDevice = m_audioInput->start();
        connect(m_inputDevice, &QIODevice::readyRead, this, &MainWindow::slot_sendAudioData, Qt::UniqueConnection);
    } else if (response == 1) {
        // TODO 添加拒绝通话时,将等待框关掉
    }

}

void MainWindow::slot_sendAudioData()
{
    m_socket.writeDatagram(m_inputDevice->read(1024), QHostAddress(m_targetIP), m_targetPort);
}

QUdpSocket

这个类是Qt的udp通信的类,详细的类的介绍,可以看Qt的帮助文档。在这个项目,主要用到了几个函数:

bind

  这个函数用来绑定到某个ip和端口上,代表发到这个ip和这个端口上的数据,能被当前socket认为是发给自己的。当然,如果你仅仅只要发送udp数据的话,是不需要进行bind的。

readyRead

  这是一个信号,当数据准备好可以读取的时候,就会发射这个信号。这个时候,就可以调用reciveDatagram来读取数据。

使用代码如下:

void RecvData::slot_start()
{
    qDebug() << QThread::currentThread();
    QString dir = QApplication::applicationDirPath();
    QSettings settings(dir+"/config.ini", QSettings::IniFormat);
    int port = settings.value("Network/hostPort").toInt();
    QString ip = settings.value("Network/hostIP").toString();

    m_socket = new QUdpSocket;
    int ret = m_socket->bind(QHostAddress(ip), port);
    qDebug() << ip << port;
    if (!ret) {
        QString error =  QString("%1:%2 绑定失败, 原因: %3")
                            .arg(ip)
                            .arg(port)
                            .arg(m_socket->errorString());
        Q_EMIT signal_bindFailed(error);
    }

    connect(m_socket, &QUdpSocket::readyRead, this, &RecvData::slot_writeDataToOutput);
}

在收到UDP的数据时,会对数据进行解析,然后通过信号和槽的方式来执行对应的步骤:

int RecvData::analysisData(const QByteArray &data)
{
    if (data.size() > 30)
        return 0;
    
    if (data == m_protocolManager.protocolContent(Protocol::CallRequest)) {
        m_connectStatus = ConnectStatus::Connected;
        Q_EMIT signal_callRequest();
    }

    if (data == m_protocolManager.protocolContent(Protocol::Accept)) {
        m_connectStatus = ConnectStatus::Connected;
        Q_EMIT signal_callResponse(0);
    }

    if (data == m_protocolManager.protocolContent(Protocol::Refuse)) {
        m_connectStatus = ConnectStatus::Disconnected;
        Q_EMIT signal_callResponse(1);
    }

    if (data == m_protocolManager.protocolContent(Protocol::HangUp)) {
        m_connectStatus = ConnectStatus::Disconnected;
        Q_EMIT signal_hangUp();
    }

    if (data == m_protocolManager.protocolContent(Protocol::Cancel)) {
        m_connectStatus = ConnectStatus::Disconnected;
        Q_EMIT signal_callCancel();
    }
    
    return 1;
}

如果是音频的数据,就直接将数据写入到QAudioOutput开启时返回的QIODevice里,

void RecvData::slot_writeDataToOutput()
{
    QNetworkDatagram datagram = m_socket->receiveDatagram();
    int ret = analysisData(datagram.data());
    if (ret == 1)
        return;
    if (m_connectStatus != ConnectStatus::Connected)
        return;
    int writeSize = m_outputDevice->write(datagram.data());
    Q_UNUSED(writeSize)
}

 本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/34118.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目管理范围(上)

范围管理概述 产品范围与项目范围的区别&#xff1a; 产品范围是指产品或者服务所应该包含的功能&#xff0c;项目范围是指为了能够交付产品&#xff0c;项目所必须做的工作。 什么是WBS&#xff1f; 工作分解结构&#xff08;简称WBS&#xff0c; Work Breakdown Structure…

科学孕育:婴幼儿食物过敏怎么办?

近年来&#xff0c;食物过敏的发病率呈明显上升趋势&#xff0c;已成为危害儿童健康的全球性问题。据统计&#xff0c;我国儿童食物过敏的发病率为3%~10%[1]。其中&#xff0c;0~4岁婴幼儿和学龄前儿童食物过敏的发病率最高。婴儿过敏反复发生.具有湿疹和食物过敏史的婴儿易患哮…

热门Java IDE——MyEclipse的数据库和持久化支持

MyEclipse支持开发者使用Java EE、Spring、JPA、Hibernate和POJOs快速开发以数据库为中心的应用程序&#xff0c;通过使用JDBC标准和嵌入式Derby数据库的数十个数据库的连接器&#xff0c;开发人员可以快速启动并运行。从持久性到逆向工程&#xff0c;再到可视化ER模型&#xf…

JS if else语句详解

在正常情况下&#xff0c;JavaScript 脚本是按顺序从上到下执行的&#xff0c;这种结构被称为顺序结构。如果使用 if、else/if 或 switch 语句&#xff0c;可以改变这种流程顺序&#xff0c;让代码根据条件选择执行的方向&#xff0c;这种结构被称为分支结构。 if语句 if 语句…

【微信小程序】实现页面跳转功能

&#x1f3c6;今日学习目标&#xff1a;第十三期——实现页面跳转功能 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人主页&#xff1a;颜颜yan_的个人主页 ⏰预计时间&#xff1a;20分钟 &#x1f389;专栏系列&#xff1a;我的第一个微信小程序 文章目录前言实现效果事件如…

Unity3D占用内存太大怎么解决呢? -下

什么时候才是UnusedAssets?看一个例子&#xff1a; Object obj Resources.Load("MyPrefab"); GameObject instance Instantiate(obj) as GameObject; ......... Destroy(instance); 创建随后销毁了一个Prefab实例&#xff0c;这时候 MyPrefab已经没有被实际的物体…

传奇列表获取失败与登录器太老怎么解决

一.登录器列表读取失败,无法获取登录器远程列表解决方案 1.查看登陆器列表地址是否能正常访问&#xff0c;登陆器读取的列表地址都是一个txt文本格式。如下图&#xff1a; 例如&#xff1a;我们的列表为: http://域名/123.txt&#xff0c;我们把列表复制到浏览器中查看是否能正…

锐捷MPLS跨域方案C1实验配置

目录 配置IPv4的BGP邻居 配置PE之间的Vpnv4邻居 此时配置PE与CE设备对接命令 手工配置为PE地址分配标签 MPLS隧道——跨域解决方案C1、C2讲解_静下心来敲木鱼的博客-CSDN博客https://blog.csdn.net/m0_49864110/article/details/127634890?ops_request_misc%257B%2522requ…

使用backdrop-filter实现elementui官网的模糊滤镜效果的和毛玻璃效果

前置 element-ui官网有一个属性很好看,可以看到,当滚动的时候,文字会被显示白色带阴影背景 你可能有点印象,因为公交车的时候也是类似于这种效果 他们是怎么做的呢?我看到源码使用到了 backdrop-filterbackground-sizebackground-image就实现了,很少的属性,达到了不错的效果,…

鲲鹏devkit训练营——《锁长期等待》项目解析

《锁长期等待》项目解析 通过本次实验学习掌握性能分析工具&#xff0c;利用性能分析工具找出程序中加锁范围不合理的地方并解决 安装web端性能分析工具 获取性能分析工具软件包 1、登录远程服务器 获取鲲鹏性能分析工具软件包上传到远端服务器 2、安装性能分析工具 使用…

【Flink】基本转换算子使用之fliter、flatMap,键控流转换算子和分布式转换算子

文章目录一 Flink DataStream API1 基本转换算子的使用&#xff08;1&#xff09;flitera 使用匿名类实现b 使用外部类函数实现b 使用flatMap实现&#xff08;2&#xff09;flatMapa 使用匿名类实现b 使用匿名函数实现2 键控流转换算子&#xff08;1&#xff09; keyBy&#xf…

mysql InnoDB存储引擎的锁机制

目录 前言 1. 锁的分类 1.1 实现方式 1.2 锁的粒度 2. 查询操作加锁方式 2.1 一致性非锁定读 2.2 一致性锁定读 3. 锁的算法 4. 锁的升级 5. 死锁 6.总结 前言 锁机制的目的是最大程度提高数据库的并发访问&#xff0c;另一方面确保可以以一致的方式读取和修改数据。…

Camunda工作流引擎简记

本文转载自玩转Camunda之实战篇-赶紧收藏起来吧_哔哩哔哩_bilibili 其中部分内容&#xff0c;经过本人修改 一、工作流相关介绍 BPM(BusinessProcessManagement)&#xff0c;业务流程管理是一种管理原则&#xff0c;通常也可以代指BPMS(BusinessProcessManagementSuite)&#…

火山引擎 DataTester 应用故事:一个A/B测试,将产品DAU提升了数十万

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 疫情让线下的需求大量转移到线上&#xff0c;催生出了远程办公、网络授课、线上健身等新的生态现象。如何更好地为用户服务&#xff0c;提升用户体验&#xff0c;成…

计算机的组成

文章目录五大部件1) 输入设备2) 存储器3) CPU&#xff08;中央处理器&#xff09;4) 输出设备五大部件 所有类型的计算机&#xff0c;其本质都是接收用户输入的原始数据&#xff0c;并将其加工、处理成对用户有用的数据&#xff0c;它们都支持执行如表 1 所示的 5 项基本操作。…

SpringCloud Sentinel 使用

哈喽~大家好&#xff0c;SpringCloud Sentinel 使用。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【微服务】 &#x1f949;推荐专栏&#xff1a; JavaEE框架 目录 一、前言 1、什么是Sentinel&…

时间复杂度和空间复杂度【一学就会】

目录 &#x1f947;1.算法效率 &#x1f50e;2.时间复杂度 &#x1f4d7;2.1 大O渐进表示法 &#x1f4d8;2.2 时间复杂度的练习&#xff08;没有说明即最坏情况&#xff09; &#x1f511;3.空间复杂度 &#x1f308;如何评价一个代码呢&#xff1f;它的效率高不高&#…

美创科技发布数据安全综合评估系统|推进安全评估高效开展

数字化深入的今天&#xff0c;数据价值和风险相伴相生&#xff0c;让数据要素发挥更大价值&#xff0c;提高风险预见预判&#xff0c;数据安全评估日益紧迫和必要。《数据安全法》提出&#xff1a;“重要数据处理者应对其数据处理活动定期开展风险评估&#xff0c;并向有关主管…

精彩回顾|关系网络赋能银行数字化转型的应用与实践

本文是根据11月10日Galaxybase图创课堂&#xff1a;乘金融科技之风&#xff1a;关系网络赋能银行数字化转型的应用与实践整理&#xff0c;错过的小伙伴们可以观看回放&#xff1a;https://uao.so/pct862806 精彩回顾 近年&#xff0c;知识图谱的重要性和实际应用逐步呈上升趋…

十大排序(总结+算法实现)

十大排序&#xff08;总结算法实现&#xff09; 十大排队的性能分析 冒泡排序 使用冒泡排序&#xff0c;时间复杂度为O(n^2),空间复杂度为O(1) 像气泡一样从低往上浮现 vector<int> bubbleSort(vector<int>nums) {int lengthnums.size();for(int i0;i<lengt…