当前位置: 首页 > news >正文

使用Qt下QAudioOutput播放声音

导读

        本项目目的是使用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.lryc.cn/news/597816.html

相关文章:

  • Qt 常用控件 - 1
  • iview表单验证一直提示为空的几个原因?
  • DDD领域驱动设计C++实现案例:订单管理系统
  • 【读代码】Facebook Denoiser:开源端到端语音降噪系统原理与实战
  • 2025 ACT 汽车功能安全相关PPT分享
  • Linux网络:网络层-IP协议
  • 飞算JavaAI:从“工具革命”到“认知革命”——开发者如何借力AI重构技术竞争力
  • 【已解决】Jetson Orin NX apt更换国内源
  • ​​SBOM 软件供应链安全(转)
  • Class14参数管理
  • 从零搭建 OpenCV 项目(新手向)-- 第二天 OpenCV图像预处理(一)
  • lammps滚动模拟
  • AJAX案例合集
  • LeetCode热题100--383
  • MCU芯片AS32S601在卫星光纤放大器(EDFA)中的应用探索
  • Github上传文件流程图
  • mysql中ROW_NUMBER()、RANK()、DENSE_RANK()用法及区别
  • SpringBoot整合Langchain4j
  • ZKmall开源商城微服务架构实战:Java 商城系统的模块化拆分与通信之道
  • 开源的语音合成大模型-Cosyvoice使用介绍
  • 【Linux庖丁解牛】— 信号量 !
  • Petalinux的常用指令
  • python3写一个异步流式 http 接口服务调用大模型(async, stream, sanic)---6.2
  • 若依前后端分离版学习笔记(二)——系统菜单介绍
  • 前端资源缓存优化案例:深入探讨 Nginx 配置中的 Cache-Control 头部叠加问题
  • 【科研绘图系列】R语言绘制黑白填充等显著性标记条形图
  • Java按模板导出Excel
  • Redis能完全保证数据不丢失吗?
  • 《WebGL与Three.js打造会“讲故事“的虚拟博物馆》
  • 氢气传感器在氢燃料电池中的应用与技术保障