ffmpeg进行音频解码,QAudioOutput播放解码后的音频
项目在此 https://github.com/qyvlik/AudioTestByFFmpeg
首先我们按照Qt给的官方例子初始化一个QAudioFormat,设置好他的各项参数,ffmpeg解码后的音频可以考虑使用如下的参数进行播放
format.setSampleRate(44100);format.setChannelCount(2);format.setCodec("audio/pcm");format.setSampleType(QAudioFormat::SignedInt);format.setSampleSize(16);format.setByteOrder(QAudioFormat::LittleEndian);
然后接下来使用 QAudioDeviceInfo 查看本机的音频设备(声卡)。如果音频设备有问题的话,就返回-1
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());if (!info.isFormatSupported(format)) {qDebug() << "Raw audio format not supported by backend, cannot play audio.";return -1;}
如果声卡设备没有问题,就使用前面设置好参数的QAudioFormat 对象来构造一个 QAudioOutput对象 ,并取出QAudioOutput对象 内部的QIODevice 对象。向这个QIODevice对象写入数据,QAudioOutput对象 便会播放出声音。
QAudioOutput *audio;audio = new QAudioOutput(format,&a);QIODevice *out = audio->start();
接下来进入主要由 ffmpeg 代码构成的音频解码与播放函数,此函数参照 此链接
int decodeAndPlay(const char *filename,QIODevice *out)
{//![url] http://blog.chinaunix.net/uid-23043718-id-2563087.htmlav_register_all();//注册所有可解码类型AVFormatContext *pInFmtCtx=NULL;//文件格式AVCodecContext *pInCodecCtx=NULL;//编码格式if (av_open_input_file(&pInFmtCtx,filename,NULL, 0, NULL)!=0)//获取文件格式printf("av_open_input_file error\n");if(av_find_stream_info(pInFmtCtx) < 0)//获取文件内音视频流的信息printf("av_find_stream_info error\n");//! Find the first audio streamunsigned int j;int audioStream = -1;//找到音频对应的streamfor(j=0; j<pInFmtCtx->nb_streams; j++){if(pInFmtCtx->streams[j]->codec->codec_type==CODEC_TYPE_AUDIO){audioStream=j;break;}}if(audioStream==-1) {printf("input file has no audio stream\n");return 0; // Didn't find a audio stream}printf("audio stream num: %d\n",audioStream);//! Decode AudiopInCodecCtx = pInFmtCtx->streams[audioStream]->codec;//音频的编码上下文AVCodec *pInCodec=NULL;pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id);//根据编码ID找到用于解码的结构体if(pInCodec==NULL) {printf("error no Codec found\n");return -1 ; // Codec not found}if(avcodec_open(pInCodecCtx, pInCodec)<0) {printf("error avcodec_open failed.\n");return -1; // Could not open codec}/*static*/ AVPacket packet;int rate = pInCodecCtx->bit_rate;printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate);printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate);printf(" channels = %d \r\n", pInCodecCtx->channels);printf(" code_name = %s \r\n", pInCodecCtx->codec->name);printf(" block_align = %d\n",pInCodecCtx->block_align);//uint8_t *pktdata;int pktsize;// 1 second of 48khz 32bit audio 192000int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;uint8_t * inbuf = (uint8_t *)malloc(out_size);long start = clock();// pInFmtCtx 中调用对应格式的packet获取函数const double K=0.0054;double sleep_time=0;while(av_read_frame(pInFmtCtx, &packet)>=0) {//如果是音频if(packet.stream_index==audioStream) {pktdata = packet.data;pktsize = packet.size;while(pktsize>0){out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;//解码int len = avcodec_decode_audio2(pInCodecCtx,(short*)inbuf,&out_size,pktdata,pktsize);if (len<0) {printf("Error while decoding.\n");break;}if(out_size>0) {// out_size sleep_time// 16384 91// 8096 46// 4608 25// y = k*x + b;// sleep_time = 0.0054 * out_size + b;// rate b// 320000 1.9// 128000 0.8//// 1411200 0if(rate >= 1411200){sleep_time = K * out_size - 1;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);}else if(rate >= 320000 ){sleep_time = K * out_size + 1.9;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);} else if(rate >= 128000){sleep_time = K * out_size - 0.3;//printf( "rate : %d,sleep : %lf \n",rate,sleep_time);} else {sleep_time = K * out_size + 0.5;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);}out->write((char*)inbuf,out_size);QTest::qSleep( sleep_time );}pktsize -= len;pktdata += len;}}av_free_packet(&packet);}long end = clock();printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC);free(inbuf);if (pInCodecCtx!=NULL) {avcodec_close(pInCodecCtx);}av_close_input_file(pInFmtCtx);printf("good bye !\n");return 0;
}
这里 ffmpeg 代码稍作简介
1.注册所有可解码类型
av_register_all();
2.获取文件格式
if (av_open_input_file(&pInFmtCtx,filename,NULL, 0, NULL)!=0)//获取文件格式printf("av_open_input_file error\n");
3. 获取文件内音视频流的信息
if(av_find_stream_info(pInFmtCtx) < 0)//获取文件内音视频流的信息printf("av_find_stream_info error\n");
4. 找到音频流
//! Find the first audio streamunsigned int j;int audioStream = -1;//找到音频对应的streamfor(j=0; j<pInFmtCtx->nb_streams; j++){if(pInFmtCtx->streams[j]->codec->codec_type==CODEC_TYPE_AUDIO){audioStream=j;break;}}if(audioStream==-1) {printf("input file has no audio stream\n");return 0; // Didn't find a audio stream}printf("audio stream num: %d\n",audioStream);
5.获取音频编码上下文,获取解码器,然后打印音频编码信息
pInCodecCtx = pInFmtCtx->streams[audioStream]->codec;//音频的编码上下文AVCodec *pInCodec=NULL;pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id);//根据编码ID找到用于解码的结构体if(pInCodec==NULL) {printf("error no Codec found\n");return -1 ; // Codec not found}if(avcodec_open(pInCodecCtx, pInCodec)<0) {printf("error avcodec_open failed.\n");return -1; // Could not open codec}/*static*/ AVPacket packet;int rate = pInCodecCtx->bit_rate;printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate);printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate);printf(" channels = %d \r\n", pInCodecCtx->channels);printf(" code_name = %s \r\n", pInCodecCtx->codec->name);printf(" block_align = %d\n",pInCodecCtx->block_align);
6.解码开始,由于是将解码的数据直接写入QAudioOutput的IO设备中去,如果写入速度太快,会导致音频播放速度快,故,每次写入数据后,都会根据音频编码的信息计算出休眠的时间,让 QAudioOutput 对象有足够的时间去播放声音
uint8_t *pktdata;int pktsize;// 1 second of 48khz 32bit audio 192000int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;uint8_t * inbuf = (uint8_t *)malloc(out_size);long start = clock();// pInFmtCtx 中调用对应格式的packet获取函数const double K=0.0054;double sleep_time=0;//! Decode Audiowhile(av_read_frame(pInFmtCtx, &packet)>=0) {//如果是音频if(packet.stream_index==audioStream) {pktdata = packet.data;pktsize = packet.size;while(pktsize>0){out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;//解码int len = avcodec_decode_audio2(pInCodecCtx,(short*)inbuf,&out_size,pktdata,pktsize);if (len<0) {printf("Error while decoding.\n");break;}if(out_size>0) {// out_size sleep_time// 16384 91// 8096 46// 4608 25// y = k*x + b;// sleep_time = 0.0054 * out_size + b;// rate b// 320000 1.9// 128000 0.8//// 1411200 0if(rate >= 1411200){sleep_time = K * out_size - 1;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);}else if(rate >= 320000 ){ // rate 在320000 的音乐播放最好,时间误差 5分钟 +- 1 秒sleep_time = K * out_size + 1.9;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);} else if(rate >= 128000){sleep_time = K * out_size - 0.3;//printf( "rate : %d,sleep : %lf \n",rate,sleep_time);} else {sleep_time = K * out_size + 0.5;//printf(" rate : %d,sleep : %lf \n",rate,sleep_time);}out->write((char*)inbuf,out_size); // 这里正是向音频设备写入数据QTest::qSleep( sleep_time ); // 写入数据后,等音频设备播放}pktsize -= len;pktdata += len;}}av_free_packet(&packet);}long end = clock();printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC);
7.后期,释放掉申请的ffmpeg的内存
free(inbuf);if (pInCodecCtx!=NULL) {avcodec_close(pInCodecCtx);}av_close_input_file(pInFmtCtx);printf("good bye !\n");return 0;
本程序的思路如上所述,总逃不了声音播放噪音的问题(音频采样率高的问题不大),在下一个解决方案中会尝试使用线程来解码播放。
由于ffmpeg只负责解码音频,并不负责任对音频的加工处理,所以声音听起来有点不同。
代码&可执行程序下载
ffmpeg进行音频解码,QAudioOutput播放解码后的音频相关推荐
- 【C#通过共享内存MemoryMappedFile解码和播放WavPack等PCM音频】
这里只介绍思路,具体代码可以指路 如果有更好的方法,欢迎指出,这里仅当抛砖引玉 原理很简单,找到一个可以同时可读可写的流对象而且不必担心写入时流的位置对播放的影响就完事大吉,因为两个解码的位置和读取流 ...
- 解决华硕前置音频没声音,但后置音频有声音,没有Realtak音频管理器如何处理的问题
最进自己组装台电脑,然后前置音频没声音,音频线已接好,然后后置音频有声音,百度一直说有个Realtak音频管理器,但我用的华硕主板没有呀,重装驱动也没有,最后在打开菜单,找到realtak audio ...
- 音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放
目录 OpenSL ES基本介绍 OpenSL ES播放音频流程 代码实现 遇到的问题 资料 收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放,在Android上 ...
- iOS 9音频应用播放音频之控制播放速度
iOS 9音频应用播放音频之控制播放速度 iOS 9音频控制播放速度 iOS9音频文件在播放时是以一定的速度进行的.这个速度是可以进行更改的,从而实现iOS9音频文件的快速播放和慢速播放功能.要实现i ...
- Android --- IjkPlayer 阅读native层源码之解码成功后的音频数据如何发送回Android播放(九)
整章目录:Android------- IjkPlayer 源码学习目录 本篇会有很多源代码,请注意阅读每行代码上面的注释. 本篇介绍的主要内容为上图红框圈起部分: 在前面介绍了如何将一个AvPack ...
- FFmpeg进行音频的解码和播放
音频编码 音频数字化主要有压缩与非压缩(pcm)两种方式. 非压缩编码(PCM)PCM音频编码 PCM通过抽样.量化.编码三个步骤将连续变化的模拟信号转换为数字编码. 当采样频率fs.max大于信号中 ...
- ffmpeg播放器(二)音频解码与播放
音频解码和播放的前面准备工作和视频的格式差不多,创建两个线程分别解码和播放,这里统一只放代码了. void AudioChannel::play() {//设置为播放状态packets.setWork ...
- FFMpeg.AutoGen+D2D解码并播放视频(含音频流)
最近在捣鼓FFMpeg这个东西,可惜网上的资料实在难找,对于c#里面的FFmpeg.AutoGen更是如此.所以走了不少弯路.(语言组织能力不太好,这篇文章的东西会很杂.涉及到d2d绘图的部分,我封装 ...
- 纯前端解码、播放、录音、编码 AMR 音频,无须服务器支持
纯前端解码.播放.录音.编码 AMR 音频,无须服务器支持,基于amr.js 和 RecorderJs. 特性 方便的 API 实现解码.播放.录音.编码 AMR 文件. 支持 url 和 blob ...
最新文章
- SLAM综述(3)-视觉与惯导,视觉与深度学习SLAM
- [转]SQL 约束讲解
- 计算机组装与维修单元卷,计算机组装与维修期中考试试卷及答案
- 地形纹理Splatting技术(翻译)
- fastjson 1.1.71.android 版本发布,优化部分场景性能
- sql server中的存储过程调试
- 【Linux】一步一步学Linux——init命令(138)
- SAP Spartacus CmsComponentConnector
- 【Anaconda】InvalidVersionSpecError: Invalid version spec: =2.7
- Apache错误日志提示AH02004: SSL Proxy: Peer certificate is expired
- 微软电脑适合什么人用_#微软surface pro使用心得# 大学生挑电脑参考/平板与电脑二合一到底买的是什么...
- 让猎头雨天送伞--大话猎头
- NYU Google: 知识蒸馏无处不在,但它真的有用吗?
- 【TensorFlow】池化层max_pool中两种paddding操作
- JAVA https证书相关
- java时间往后一天_如何在Java中将日期增加一天?
- 【Elasticsearch】Elasticsearch:聚合 操作
- 让人头大的Tablet PC
- caffe 安装指南
- 2021-07-26 NLP词嵌入