• 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

  • 相关阅读:
    ARM 浮点运算
    手机微硬盘读取速度>50MB/s eMMC技术浅析
    Chrome 浏览器跨域和安全访问问题 使用 chrome的命令行标记:disable-web-security 参数联调线上数据
    Vue学习手札
    使用MouseWithoutBordersSetup共享鼠标键盘教程
    java使用POI获取sheet、行数、列数
    基于Spring MVC实现基于form表单上传Excel文件,批量导入数据
    org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML.
    [MYSQL]时间毫秒数转换
    springmvc 传递和接收数组参数
  • 原文地址:https://www.cnblogs.com/xuanyuanchen/p/2450169.html
Copyright © 2020-2023  润新知