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

可以参考:http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioQueueReference/Reference/reference.html#//apple_ref/doc/c_ref/AudioQueueOutputCallback

posted @ 2012-04-17 12:58  xiulug  阅读(7359)  评论(7编辑  收藏  举报