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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM