iphone利用AudioQueue播放wav(PCM码)
续上一篇iphone利用AudioQueue播放音频文件(mp3,aac,caf,wav等)
绝对原创,转载请注明出处:http://www.cnblogs.com/xuanyuanchen/admin/EditPosts.aspx?postid=2450169
1、ffmpeg解码音频流并且保存成wav文件。
这一步比较简单,只要熟悉ffmpeg解码音频的流程,将解码出的pcm码,保存到本地文件中,并实时统计解码的pcm的字节长度,最后解码完成之后再添加44字节的wav文件头。
save_audio.c
View Code
1 #include <stdio.h> 2 #include "libavformat/avformat.h" 3 #include "libavcodec/avcodec.h" 4 #include "libavutil/avutil.h" 5 static void writeWavHeader(AVCodecContext *pCodecCtx,AVFormatContext *pFormatCtx,FILE *audioFile) { 6 int8_t *data; 7 int32_t long_temp; 8 int16_t short_temp; 9 int16_t BlockAlign; 10 int bits=16; 11 int32_t fileSize; 12 int32_t audioDataSize; 13 14 switch(pCodecCtx->sample_fmt) { 15 case AV_SAMPLE_FMT_S16: 16 bits=16; 17 break; 18 case AV_SAMPLE_FMT_S32: 19 bits=32; 20 break; 21 case AV_SAMPLE_FMT_U8: 22 bits=8; 23 break; 24 default: 25 bits=16; 26 break; 27 } 28 audioDataSize=(pFormatCtx->duration)*(bits/8)*(pCodecCtx->sample_rate)*(pCodecCtx->channels); 29 fileSize=audioDataSize+36; 30 data="RIFF"; 31 fwrite(data,sizeof(char),4,audioFile); 32 fwrite(&fileSize,sizeof(int32_t),1,audioFile); 33 34 //"WAVE" 35 data="WAVE"; 36 fwrite(data,sizeof(char),4,audioFile); 37 data="fmt "; 38 fwrite(data,sizeof(char),4,audioFile); 39 long_temp=16; 40 fwrite(&long_temp,sizeof(int32_t),1,audioFile); 41 short_temp=0x01; 42 fwrite(&short_temp,sizeof(int16_t),1,audioFile); 43 short_temp=(pCodecCtx->channels); 44 fwrite(&short_temp,sizeof(int16_t),1,audioFile); 45 long_temp=(pCodecCtx->sample_rate); 46 fwrite(&long_temp,sizeof(int32_t),1,audioFile); 47 long_temp=(bits/8)*(pCodecCtx->channels)*(pCodecCtx->sample_rate); 48 fwrite(&long_temp,sizeof(int32_t),1,audioFile); 49 BlockAlign=(bits/8)*(pCodecCtx->channels); 50 fwrite(&BlockAlign,sizeof(int16_t),1,audioFile); 51 short_temp=(bits); 52 fwrite(&short_temp,sizeof(int16_t),1,audioFile); 53 data="data"; 54 fwrite(data,sizeof(char),4,audioFile); 55 fwrite(&audioDataSize,sizeof(int32_t),1,audioFile); 56 57 fseek(audioFile,44,SEEK_SET); 58 } 59 60 int main(){ 61 char *filename="E:\\flv\\Love_You.mp4"; 62 AVFormatContext *pFormatCtx; 63 int audioStream=-1; 64 int i; 65 int iFrame=0; 66 AVCodecContext *pCodecCtx; 67 AVCodec *pCodec=NULL; 68 static AVPacket packet; 69 uint8_t *pktData=NULL; 70 int pktSize; 71 int outSize=AVCODEC_MAX_AUDIO_FRAME_SIZE; 72 // FILE *wavfile=NULL; 73 uint8_t *inbuf=(uint8_t *)av_malloc(outSize); 74 75 FILE *wavFile=NULL; 76 int32_t audioFileSize=0; 77 av_register_all(); 78 if(av_open_input_file(&pFormatCtx,filename,NULL,0,NULL)!=0) 79 { 80 printf("Could not open input file %s\n",filename); 81 return 0; 82 } 83 if(av_find_stream_info(pFormatCtx)<0) 84 { 85 printf("Could not find stream information\n"); 86 } 87 av_dump_format(pFormatCtx,0,filename,0); 88 for(i=0;i<pFormatCtx->nb_streams;i++) { 89 if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO) { 90 audioStream=i; 91 break; 92 } 93 } 94 95 pCodecCtx=pFormatCtx->streams[audioStream]->codec; 96 pCodec=avcodec_find_decoder(pCodecCtx->codec_id); 97 if(avcodec_open(pCodecCtx,pCodec)<0) { 98 printf("Error avcodec_open failed.\n"); 99 return 1; 100 } 101 102 printf("\tbit_rate=%d\n \ 103 bytes_per_secondes=%d\n \ 104 sample_rate=%d\n \ 105 channels=%d\n \ 106 codec_name=%s\n",pCodecCtx->bit_rate,(pCodecCtx->codec_id==CODEC_ID_PCM_U8)?8:16, 107 pCodecCtx->sample_rate,pCodecCtx->channels,pCodecCtx->codec->name); 108 109 wavFile=fopen("E:\\flv\\myPlayerWav.wav","wb"); 110 if (wavFile==NULL) 111 { 112 printf("open error\n"); 113 return 1; 114 } 115 116 writeWavHeader(pCodecCtx,pFormatCtx,wavFile); 117 118 av_free_packet(&packet); 119 while(av_read_frame(pFormatCtx,&packet)>=0) { 120 if(packet.stream_index==audioStream) { 121 int len=0; 122 if((iFrame++)>=4000) 123 break; 124 pktData=packet.data; 125 pktSize=packet.size; 126 while(pktSize>0) { 127 outSize=AVCODEC_MAX_AUDIO_FRAME_SIZE; 128 len=avcodec_decode_audio3(pCodecCtx,(short *)inbuf,&outSize,&packet); 129 if(len<0){ 130 printf("Error while decoding\n"); 131 break; 132 } 133 if(outSize>0) { 134 audioFileSize+=outSize; 135 fwrite(inbuf,1,outSize,wavFile); 136 fflush(wavFile); 137 } 138 pktSize-=len; 139 pktData+=len; 140 } 141 } 142 av_free_packet(&packet); 143 } 144 145 fseek(wavFile,40,SEEK_SET); 146 fwrite(&audioFileSize,1,sizeof(int32_t),wavFile); 147 audioFileSize+=36; 148 fseek(wavFile,4,SEEK_SET); 149 fwrite(&audioFileSize,1,sizeof(int32_t),wavFile); 150 fclose(wavFile); 151 av_free(inbuf); 152 if(pCodecCtx!=NULL){ 153 avcodec_close(pCodecCtx); 154 } 155 av_close_input_file(pFormatCtx); 156 return 0; 157 }
注意:我用的ffmpeg的版本是ffmpeg-0.8.6。我已经成功地将ffmpeg在Windows、Linux(Ubuntu)、Ios平台上编译通过,这段代码没有什么平台依赖性,都是用的标准C的代码。
2、iphone读PCM码,并且送AudioQueue播放
playAudio.h
View Code
1 #import <Foundation/Foundation.h> 2 #import <AudioToolbox/AudioToolbox.h> 3 #import <AudioToolbox/AudioFile.h> 4 #define NUM_BUFFERS 3 5 6 @interface playAudio : NSObject{ 7 //播放音频文件ID 8 AudioFileID audioFile; 9 //音频流描述对象 10 AudioStreamBasicDescription dataFormat; 11 //音频队列 12 AudioQueueRef queue; 13 SInt64 packetIndex; 14 UInt32 numPacketsToRead; 15 UInt32 bufferByteSize; 16 uint8_t *inbuf; 17 AudioStreamPacketDescription *packetDescs; 18 AudioQueueBufferRef buffers[NUM_BUFFERS]; 19 FILE *wavFile; 20 } 21 22 //定义队列为实例属性 23 @property AudioQueueRef queue; 24 //播放方法定义 25 -(id)initWithAudio:(NSString *) path; 26 //定义缓存数据读取方法 27 -(void) audioQueueOutputWithQueue:(AudioQueueRef)audioQueue 28 queueBuffer:(AudioQueueBufferRef)audioQueueBuffer; 29 -(UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer; 30 //定义回调(Callback)函数 31 static void BufferCallack(void *inUserData,AudioQueueRef inAQ, 32 AudioQueueBufferRef buffer); 33 34 @end
playAudio.m
View Code
1 #import "playAudio.h" 2 3 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 4096*2// (0x10000)/4 4 //static UInt32 gBufferSizeBytes=0x10000;//65536 5 static UInt32 gBufferSizeBytes=0x10000;//It must be pow(2,x) 6 7 @implementation playAudio 8 9 @synthesize queue; 10 11 //回调函数(Callback)的实现 12 static void BufferCallback(void *inUserData,AudioQueueRef inAQ, 13 AudioQueueBufferRef buffer){ 14 playAudio* player=(__bridge playAudio*)inUserData; 15 [player audioQueueOutputWithQueue:inAQ queueBuffer:buffer]; 16 } 17 18 19 //缓存数据读取方法的实现 20 -(void) audioQueueOutputWithQueue:(AudioQueueRef)audioQueue queueBuffer:(AudioQueueBufferRef)audioQueueBuffer{ 21 //读取包数据 22 UInt32 numBytes; 23 // UInt32 numPackets=numPacketsToRead; 24 UInt32 numPackets=numPacketsToRead; 25 26 //成功读取时 27 numBytes=fread(inbuf, 1, numPackets*4,wavFile); 28 AudioQueueBufferRef outBufferRef=audioQueueBuffer; 29 NSData *aData=[[NSData alloc]initWithBytes:inbuf length:numBytes]; 30 31 if(numBytes>0){ 32 memcpy(outBufferRef->mAudioData, aData.bytes, aData.length); 33 34 outBufferRef->mAudioDataByteSize=numBytes; 35 AudioQueueEnqueueBuffer(audioQueue, outBufferRef, 0, nil); 36 packetIndex += numPackets; 37 } 38 else{ 39 } 40 } 41 42 //音频播放方法的实现 43 -(id) initWithAudio:(NSString *)path{ 44 if (!(self=[super init])) return nil; 45 int i; 46 47 wavFile=fopen([path cStringUsingEncoding:NSASCIIStringEncoding], "rb"); 48 if (wavFile==NULL) { 49 printf("open wavFile error in current file %s,in line %d",__FILE__,__LINE__); 50 return nil; 51 } 52 //跳过wav文件的44字节的文件头 53 fseek(wavFile, 44, SEEK_SET); 54 55 for (int i=0; i<NUM_BUFFERS; i++) { 56 AudioQueueEnqueueBuffer(queue, buffers[i], 0, nil); 57 } 58 59 //取得音频数据格式 60 { 61 dataFormat.mSampleRate=44100;//采样频率 62 dataFormat.mFormatID=kAudioFormatLinearPCM; 63 dataFormat.mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 64 dataFormat.mBytesPerFrame=4; 65 dataFormat.mBytesPerPacket=4; 66 dataFormat.mFramesPerPacket=1;//wav 通常为1 67 dataFormat.mChannelsPerFrame=2;//通道数 68 dataFormat.mBitsPerChannel=16;//采样的位数 69 dataFormat.mReserved=0; 70 } 71 72 //创建播放用的音频队列 73 AudioQueueNewOutput(&dataFormat, BufferCallback, self, 74 nil, nil, 0, &queue); 75 //计算单位时间包含的包数 76 77 // numPacketsToRead= gBufferSizeBytes/dataFormat.mBytesPerPacket; 78 // numPacketsToRead=AVCODEC_MAX_AUDIO_FRAME_SIZE 79 numPacketsToRead=AVCODEC_MAX_AUDIO_FRAME_SIZE; 80 packetDescs=nil; 81 82 //设置Magic Cookie,参见第二十七章的相关介绍 83 84 //创建并分配缓冲控件 85 packetIndex=0; 86 for (i=0; i<NUM_BUFFERS; i++) { 87 AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]); 88 //读取包数据 89 if ([self readPacketsIntoBuffer:buffers[i]]==1) { 90 break; 91 } 92 } 93 94 Float32 gain=1.0; 95 //设置音量 96 AudioQueueSetParameter(queue, kAudioQueueParam_Volume, gain); 97 //队列处理开始,此后系统开始自动调用回调(Callback)函数 98 AudioQueueStart(queue, nil); 99 return self; 100 } 101 102 -(UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer { 103 UInt32 numBytes,numPackets; 104 //从文件中接受数据并保存到缓存(buffer)中 105 //AVCODEC_MAX_AUDIO_FRAME_SIZE*100 106 numPackets = numPacketsToRead; 107 inbuf=(uint8_t *)malloc(numPackets); 108 AudioQueueBufferRef outBufferRef=buffer; 109 numBytes=fread(inbuf, 1, numPackets*4,wavFile); 110 NSData *aData=[[NSData alloc]initWithBytes:inbuf length:numBytes]; 111 112 113 if(numBytes>0){ 114 memcpy(outBufferRef->mAudioData, aData.bytes, aData.length); 115 outBufferRef->mAudioDataByteSize=numBytes; 116 AudioQueueEnqueueBuffer(queue, outBufferRef, 0, nil); 117 packetIndex += numPackets; 118 } 119 else{ 120 return 1;//意味着我们没有读到任何的包 121 } 122 return 0;//0代表正常的退出 123 } 124 @end