ffmpeg进行音频解码,QAudioOutput播放解码后的音频
2015-03-07 18:48
1431 查看
首先我们按照Qt给的官方例子初始化一个QAudioFormat,设置好他的各项参数,ffmpeg解码后的音频可以考虑使用如下的参数进行播放
然后接下来使用 QAudioDeviceInfo 查看本机的音频设备(声卡)。如果音频设备有问题的话,就返回-1
如果声卡设备没有问题,就使用前面设置好参数的QAudioFormat 对象来构造一个 QAudioOutput对象 ,并取出QAudioOutput对象 内部的QIODevice 对象。向这个QIODevice对象写入数据,QAudioOutput对象 便会播放出声音。
接下来进入主要由 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) { //!
这里 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 stream unsigned int j; int audioStream = -1; //找到音频对应的stream for(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;//音频的编码上下文 ***Codec *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*/ ***Packet 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 192000 int out_size = ***CODEC_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 Audio while(av_read_frame(pInFmtCtx, &packet)>=0) { //如果是音频 if(packet.stream_index==audioStream) { pktdata = packet.data; pktsize = packet.size; while(pktsize>0){ out_size = ***CODEC_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 0 if(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只负责解码音频,并不负责任对音频的加工处理,所以声音听起来有点不同。
代码&可执行程序[url=http://download.csdn.net/detail/qyvlik/8481201]下载" target=_blank> http://blog.chinaunix.net/uid-23043718-id-2563087.html
av_register_all();//注册所有可解码类型
***FormatContext *pInFmtCtx=NULL;//文件格式
***CodecContext *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 stream unsigned int j; int audioStream = -1; //找到音频对应的stream for(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 Audio
pInCodecCtx = pInFmtCtx->streams[audioStream]->codec;//音频的编码上下文 ***Codec *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*/ ***Packet 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 192000
int out_size = ***CODEC_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 = ***CODEC_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 0
if(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;
}[/code]
这里 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 stream unsigned int j; int audioStream = -1; //找到音频对应的stream for(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;//音频的编码上下文 ***Codec *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*/ ***Packet 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 192000 int out_size = ***CODEC_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 Audio while(av_read_frame(pInFmtCtx, &packet)>=0) { //如果是音频 if(packet.stream_index==audioStream) { pktdata = packet.data; pktsize = packet.size; while(pktsize>0){ out_size = ***CODEC_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 0 if(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只负责解码音频,并不负责任对音频的加工处理,所以声音听起来有点不同。
代码&可执行程序[url=http://download.csdn.net/detail/qyvlik/8481201]下载
相关文章推荐
- FFMPEG 之音频解码及AudioTrack播放音频
- iOS 音视频高级编程:Audio Unit播放FFmpeg解码的音频
- iOS 音视频开发:Audio Unit播放FFmpeg解码的音频
- iOS 音视频高级编程:Audio Unit播放FFmpeg解码的音频
- ffmpeg和Opencv结合进行视频解码播放
- 利用ffmpeg和opencv进行视频的解码播放
- qt+ffmpeg 使用qAudioOutput播放声音,解决有沙沙的杂音问题。
- iOS开发 ( iPhone/iPad):利用ffmpeg 实现音频解码、声音播放
- FFmpeg+SDL2.0 音频解码播放(部分格式杂音处理)
- Android音频处理——通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能
- 用 Qt 的 QAudioOutput 类播放 WAV 音频文件(使用了libsndfile外部库)
- 利用ffmpeg和opencv进行视频的解码播放
- ffmpeg解码音频数据时,进行重采样(即改变文件原有的采样率)
- 用 Qt 的 QAudioOutput 类播放 WAV 音频文件
- EasyDarwin开源音频解码项目EasyAudioDecoder:基于ffmpeg的安卓音频(AAC、G726)解码库(第一部分,ffmpeg-android的编译)
- 再谈使用ffmpeg进行单纯音频编解码
- 分享代码,用QtMultimedia类播放ffmpeg解码的音频
- Android+FFmpeg+OpenSL ES音频解码播放
- ffmpeg编程基础:视频解码、音频播放
- 用 Qt 的 QAudioOutput 类播放 WAV 音频文件