导读

        本项目目的是使用QAudioOutput播放声音 ,音频数据来源为ffmpeg解码后的音频数据。

Qt音频播放类说明 

QAudioFormat

QAudioFormat是Qt多媒体框架中用于定义音频格式的核心类,用于设置音频数据的参数,确保与硬件设备兼容。其主要功能和参数如下:
一,采样率(Sample Rate)
定义每秒采集或播放的样本数,单位为Hz。常用值:
44,100 Hz(CD音质)
48,000 Hz(高清音频)

二,通道数(Channel Count)
定义音频声道数量:
1:单声道(Mono)
2:立体声(Stereo)

三,样本大小(Sample Size)
定义每个样本的位数(量化精度),常见值为16位

四,编码类型(Codec)
指定音频编码格式,通常为PCM原始数据

五,字节序(Byte Order)
定义数据存储顺序:
QAudioFormat::LittleEndian(小端序,Intel架构常用)
QAudioFormat::BigEndian(大端序)

六,样本类型(Sample Type)
定义样本数据类型:
QAudioFormat::SignedInt(有符号整数)
QAudioFormat::UnSignedInt(无符号整数)

QAudioOutput

QAudioOutput 是 Qt 多媒体框架中用于音频输出的核心类,支持将 PCM 原始音频数据发送到音频设备(如扬声器)。以下是其核心特性和使用要点:
一,功能定位
音频输出控制
管理音频播放状态(播放/暂停/停止)和音频数据流传输,适用于低延迟实时音频场景。
对比高级类 QMediaPlayer,它更底层且灵活性强,但需手动处理原始 PCM 数据。‌

二,核心方法
方法 功能说明
start(QIODevice*)    绑定输入设备(如 QFile 或自定义 QIODevice)并开始播放音频数据。
stop()    停止播放并释放资源。
suspend()    暂停播放(保留资源)。
resume()    从暂停状态恢复播放。
setVolume(float)    设置音量(0.0 静音 ~ 1.0 最大)。
setBufferSize(int)    设置缓冲区大小(需在 start() 前调用生效)。

三,关键信号
stateChanged(QAudio::State)
播放状态变更时触发,状态包括:
ActiveState(正在播放)
SuspendedState(暂停)
StoppedState(停止)
IdleState(数据耗尽或未启动)

QIODevice

QIODevice是Qt框架中所有输入/输出设备的核心抽象基类,为文件、内存缓冲区和网络通信等设备提供了统一的I/O接口。其主要特性如下:

一,设备抽象与继承结构
作为所有Qt I/O设备的基类,定义了通用读写接口,无法直接实例化。
具体子类包括:
QFile(文件操作)
QBuffer(内存缓冲区)
QTcpSocket/QTcpServer(网络通信)
QProcess(进程通信)
QSerialPort(串口通信)

二,设备类型区分
随机访问设备(如文件):支持seek()定位和pos()获取当前位置 
顺序设备(如网络套接字):不支持随机定位,数据必须一次性读取
通过isSequential()判断设备类型

音频播放代码关键实现

初始化qt音频相关对象 

int FFPlayer::initQtAudio(const AVCodecParameters *codecPar)
{QAudioFormat format;format.setSampleRate(codecPar->sample_rate);format.setChannelCount(codecPar->channels);format.setSampleSize(16); // 统一转换为16位输出format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::SignedInt);m_audioOutput = new QAudioOutput(format);m_audioDevice = m_audioOutput->start();// 初始化重采样器swr_ctx = swr_alloc_set_opts(NULL,av_get_default_channel_layout(codecPar->channels),AV_SAMPLE_FMT_S16,codecPar->sample_rate,av_get_default_channel_layout(audio_codec_ctx->channels),audio_codec_ctx->sample_fmt,audio_codec_ctx->sample_rate,0, NULL);logger()->info("swr_alloc_set_opts()sample_fmt:%d ",audio_codec_ctx->sample_fmt);if(!swr_ctx || swr_init(swr_ctx)<0){logger()->info("重采样初始化失败");return -1;}//初始化音频播放线程_audioPlayThread = std::thread(playAudioFunc,this);return 0;
}

音频解码线程实现

void decodeAudioFunc(void*ctx)
{FFPlayer* ptx = (FFPlayer*)ctx;if(ptx == nullptr)return;while(!ptx->quit){AVPacket pkt;if(ptx->_audioPktList.size()>0){std::lock_guard<std::mutex> lock(ptx->audioQueueMutex);pkt = ptx->_audioPktList.front();ptx->_audioPktList.pop_front();}else{std::this_thread::sleep_for(std::chrono::milliseconds(20));continue;}
#if 1//解码int ret = 0;ret = avcodec_send_packet(ptx->audio_codec_ctx, &pkt);if (ret != 0) {logger()->info("avcodec_send_packet error: %d", ret);return;}while(avcodec_receive_frame(ptx->audio_codec_ctx, ptx->audioFrame)>=0){// 重采样uint8_t* output;int out_samples = swr_get_out_samples(ptx->swr_ctx, ptx->audioFrame->nb_samples);av_samples_alloc(&output, NULL, ptx->audio_codec_ctx->channels,out_samples, AV_SAMPLE_FMT_S16, 0);out_samples = swr_convert(ptx->swr_ctx, &output, out_samples,(const uint8_t**)ptx->audioFrame->data, ptx->audioFrame->nb_samples);if(out_samples < 0){logger()->info("swr_convert failed %d",out_samples);}// 填充音频缓冲区int aDataSize = 0;//两种计算重采样后音频数据大小的方法 
#if 0aDataSize = out_samples * ptx->audio_codec_ctx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
#elseaDataSize = av_samples_get_buffer_size(NULL,                         // 行大小(可忽略)ptx->audio_codec_ctx->channels, // 声道数out_samples,                  // 实际样本数AV_SAMPLE_FMT_S16,            // 目标格式0                             // 字节对齐);
#endif//将重采样后的音频数据加入队列{QByteArray aData((char*)output,aDataSize);{std::lock_guard<mutex> lck(ptx->_audioDataMutex);ptx->_audioDataList.push_back(aData);}}av_freep(&output);}
#endifav_packet_unref(&pkt);}logger()->info("quit decodeAudioFunc");
}

音频播放线程实现

void playAudioFunc(void*ctx)
{FFPlayer* player = (FFPlayer*)ctx;if(player == nullptr)return;QByteArray aData;while(!player->quit){if(!player->_audioDataList.empty()){std::lock_guard<std::mutex> lock(player->_audioDataMutex);aData = player->_audioDataList.front();player->_audioDataList.pop_front();}else{std::this_thread::sleep_for(std::chrono::milliseconds(2));continue;}if(player->m_audioDevice){//将pcm音频数据写入声卡player->m_audioDevice->write((const char*)aData.data(),aData.length());}}
}

        

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

日语学习-日语知识点小记-构建基础-JLPT-N3阶段(9):ようなN

日语学习-日语知识点小记-构建基础-JLPT-N3阶段&#xff08;9&#xff09;&#xff1a;ようなN 1、前言&#xff08;1&#xff09;情况说明&#xff08;2&#xff09;工程师的信仰2、知识点&#xff08;&#xff11;&#xff09;复习&#xff08;&#xff12;&#xff09;复习&…

洛谷P1512 伊甸园日历游戏

一开始&#xff0c;我发现有“必胜策略”&#xff0c;就知道是博弈论&#xff0c;然后看了两种操作&#xff08;月份1和天数1&#xff09;&#xff0c;于是想到用记忆化搜索找出所有的可能性 &#xff0c;但不知道怎么判断当前是否为先手必胜/必败态&#xff0c;使用了TJ方法后…

Kafka——消费者组到底是什么?

引言在分布式系统中&#xff0c;消息中间件的核心价值在于高效地连接生产者与消费者&#xff0c;实现数据的可靠传递。然而&#xff0c;传统消息引擎面临一个两难困境&#xff1a;如何在“消息不重复消费”与“系统可扩展性”之间找到平衡&#xff1f;点对点模型&#xff08;如…

新mac电脑软件安装指南(前端开发用)

1. 下载git 未下载git直接下载homebrew也会提示你下载git 2. 下载homebrew 介绍&#xff1a; Homebrew 是 macOS 和 Linux 系统的开源包管理器‌&#xff0c;通过命令行实现软件的快速安装、更新和管理&#xff0c;极大简化了开发者及普通用户的工作流程。 命令&#xff1a;…

【HarmonyOS】ArkUI 布局与容器组件

目录前言一、线性布局(Column/Row)1.先布局后内容2.元素在主轴上的排列方式3.元素在交叉轴上的排列方式二、层叠布局(Stack)1.开发布局2.对齐方式三、弹性布局(Flex)四、创建列表(List)五、创建轮播(Swiper)1.基本用法2.常用属性3.样式自定义六、选项卡Tabs1.基本用法2.常用属性…

MCNN-BiLSTM-Attention分类预测模型等!

MCNN-BiLSTM-Attention分类预测模型基于多尺度卷积神经网络(MCNN)双向长短期记忆网络(BiLSTM)注意力机制(Attention)的分类预测模型&#xff0c;matlab代码&#xff0c;直接运行使用&#xff01;1、模型介绍&#xff1a;针对传统方法在噪声环境下诊断精度低的问题&#xff0c;提…

【Luogu】每日一题——Day12. P3149 排序 (树状数组 + 逆序对)

链接&#xff1a;P3149 排序 - 洛谷 题目&#xff1a; 思路&#xff1a; 经典搭配了 首先我们来分析以下操作的作用&#xff0c;如果我们选了 a[k]&#xff0c;那么对逆序对有什么影响呢&#xff1f; ①.对于 x y&#xff0c;且 x > a[k]&#xff0c;y < a[k] 由于 x…

电商项目_秒杀_架构升级

1. 秒杀当前架构设计nginx节点和订单服务都可以方便的扩容&#xff08;增加机器&#xff09;redis扩容需则需要考虑架构设计当前架构面临的痛点&#xff1a;秒杀系统redis是单节点&#xff08;主从&#xff09;部署&#xff0c;读redis时并发量会成为瓶颈。所以考虑将增加redis…

CodeBuddy IDE发布:编程新时代的颠覆者?

开场&#xff1a;编程界的 “新风暴” 来袭 你能想象&#xff0c;不用敲一行代码就能开发软件吗&#xff1f;这个曾经只存在于科幻电影里的场景&#xff0c;如今已经成为现实&#xff01;就在最近&#xff0c;编程界迎来了一场 “新风暴”——CodeBuddy IDE 重磅发布&#xff…

深度分析Java类加载机制

Java 的类加载机制是其实现平台无关性、安全性和动态性的核心基石。它不仅仅是简单地将 .class 文件加载到内存中&#xff0c;而是一个精巧、可扩展、遵循特定规则的生命周期管理过程。以下是对其深度分析&#xff1a; 一、核心概念与生命周期 一个类型&#xff08;Class 或 In…

神经网络实战案例:用户情感分析模型

在当今数字化时代&#xff0c;用户评论和反馈成为企业了解产品满意度的重要渠道。本项目将通过神经网络构建一个情感分析模型&#xff0c;自动识别用户评论中的情感倾向。我们将使用真实的产品评论数据&#xff0c;从数据预处理到模型部署&#xff0c;完整展示神经网络在NLP领域…

now能减少mysql的压力吗

是否用数据库的 NOW() 能减少 MySQL 的压力&#xff1f;​答案是否定的——使用 NOW() 不仅不会降低压力&#xff0c;反而可能略微增加 MySQL 的负载。以下是详细分析&#xff1a;&#x1f50d; 性能对比&#xff1a;NOW() vs. Java 传参​指标​​Java 传参 (e.g., new Date()…

数据结构01:链表

数据结构 链表 链表和数组的区别 1. 存储方式 数组&#xff1a; 元素在内存中连续存储&#xff0c;占用一块连续的内存空间元素的地址可以通过索引计算&#xff08;基地址 索引 元素大小&#xff09;大小固定&#xff0c;在创建时需要指定容量 链表&#xff1a; 元素&#xf…

【Java学习|黑马笔记|Day21】IO流|缓冲流,转换流,序列化流,反序列化流,打印流,解压缩流,常用工具包相关用法及练习

标题【Java学习|黑马笔记|Day20】 今天看的是黑马程序员的《Java从入门到起飞》下部的95-118节&#xff0c;笔记包含IO流中的字节、字符缓冲流&#xff0c;转换流&#xff0c;序列化流反序列化流&#xff0c;打印流&#xff0c;解压缩流&#xff0c;常用工具包相关用法及练习 …

API网关原理与使用场景详解

一、API网关核心原理 1. 架构定位 #mermaid-svg-hpDCWfqoiLcVvTzq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hpDCWfqoiLcVvTzq .error-icon{fill:#552222;}#mermaid-svg-hpDCWfqoiLcVvTzq .error-text{fill:#5…

OSPF路由协议——上

OSPF路由协议 RIP的不足 以跳数评估的路由并非最优路径如果RTA选择s0/0传输&#xff0c;传输需时会大大缩短为3s 最大跳数为16跳&#xff0c;导致网络尺度小RIP协议限制网络直径不能超过16跳&#xff0c;并且16跳为不可达。 收敛速度慢 RIP 定期路由更新 更新计时器&#xff1a…

(LeetCode 面试经典 150 题) 219. 存在重复元素 II (哈希表)

题目&#xff1a;219. 存在重复元素 II 思路&#xff1a;哈希表&#xff0c;时间复杂度0(n)。 哈希表记录每个数最新的下标&#xff0c;遇到符合要求的返回true即可。 C版本&#xff1a; class Solution { public:bool containsNearbyDuplicate(vector<int>& nums,…

Cookies 详解及其与 Session 的协同工作

Cookies 详解及其与 Session 的协同工作 一、Cookies 的本质与作用 1. 什么是 Cookies&#xff1f; Cookies 是由服务器发送到用户浏览器并存储在本地的小型文本文件。核心特性&#xff1a; 存储位置&#xff1a;客户端浏览器数据形式&#xff1a;键值对字符串&#xff08;最大…

DeepSeek Janus Pro本地部署与调用

step1、Janus模型下载与项目部署 创建文件夹autodl-tmp https://github.com/deepseek-ai/Janus?tabreadme-ov-file# janusflow 查看是否安装了git&#xff0c;没有安装的话安装一下&#xff0c;或者是直接github上下载&#xff0c;上传到服务器&#xff0c;然后解压 git --v…

【Elasticsearch】BM25的discount_overlaps参数

discount_overlaps 是 Elasticsearch/Lucene 相似度模型&#xff08;Similarity&#xff09;里的一个布尔参数&#xff0c;用来决定&#xff1a;> 在计算文档长度归一化因子&#xff08;norm&#xff09;时&#xff0c;是否忽略“重叠 token”&#xff08;即位置增量 positi…