荣耀彩票代理

  • 热门专题

iOS音频播放之AudioQueue(一):播放本地音乐

作者:  发布日期:2016-12-26 20:23:28
  • AudioQueueJIANJIE AudioStreamerSHUOMING AudioQueueXIANGJIE AudioQueueGONGZUOYUANLI AudioQueueZHUYAOJIEKOU AudioQueueNewOutput AudioQueueAllocateBuffer AudioQueueEnqueueBuffer AudioQueueStart Pause Stop Flush Reset Dispose AudioQueueFreeBuffer AudioQueueGetProperty AudioQueueSetProperty YINPINBOFANGLocalAudioPlayer BOFANGQIDECHUSHIHUA BOFANGYINPIN LocalAudioPlayerXIANGGUANSHUXING DUQUBINGKAISHIJIEXIYINPIN JIEXIYINPINXINXI kAudioFileStreamProperty_DataFormat kAudioFileStreamProperty_FileFormat kAudioFileStreamProperty_AudioDataByteCount kAudioFileStreamProperty_BitRate kAudioFileStreamProperty_DataOffset kAudioFileStreamProperty_AudioDataPacketCount kAudioFileStreamProperty_ReadyToProducePackets JIEXIYINPINZHEN BOFANGYINPINSHUJU QINGLIXIANGGUANZIYUAN JIESHU

    iOSSHIXIANBOFANGBENDIYINLE,YOUHENDUOZHONGFANGFA,LIRUAVAudioPlayer,ZHEIXIEDOUNENGHENHAODESHENGREN,YOURENJIUQIGUAILE,WEISHENMEYAOTUIERQIUQICI,SHIYONGGENGFUZADEAudioQueueLAIBOFANGBENDIYINLENI?QINGJIXUWANGXIAKAN

    AudioQueue简介

    AudioQueue,ZAIPINGGUODEKAIFAZHEWENDANGSHANGSHIZHEIMESHUODE

    'Audio Queue Services provides a straightforward, low overhead way to record and play audio in iOS and Mac OS X.'
    

    荣耀彩票代理AudioQueueGUANFANGWENDANG

    AudioQueueFUWUTIGONGYIZHONGZHIJIEDE,DIKAIXIAODEFANGSHIYIYONGYUZAIiOSJIMac OS XSHANGLUYINHEBOFANGYINLE。

    SHIYONGAudioQueueBOFANGYINLEDEYOUDIANJIUSHIKAIXIAOHENXIAOBINGQIEZHICHILIUSHIBOFANG(BIANXIABIANBO),DANSHIQUEDIANJIUSHIKAIFANANDUDA,SUOYIYOUWANGLUOYINPINKUAudioStreamer,WANGSHANGYOUHENDUOJIANGAudioQueueDE,DANSHIYOUSHILIDAIMASHUOMINGDE,SHIZAISHISHAOZHIYOUSHAO,ZHENGHAOGONGSIXIANGMUYOUYINPINXUQIU,SUIRANXIANGMUZHONGSHIYONGDEBINGFEIWOZIJIXIEDEYINPINBOFANGGONGNENG,DANSHIHOUHAISHIXIANGZIJILAIYANJIUYIXIA,ZHEIGEZAIWOKANLAIBIJIAOSHENQIYEBIJIAOYOUQUDEAudioQueue。

    AudioStreamer说明

    iOSSHANGYIGEBIJIAOYOUMINGDELIUMEITIYINPINBOFANGKUSHIAudioStreamer,GAIKUJISHIYONGLEAudioQueue,BUGUOGAIYINPINKUBINGBUZHICHIBENDIYINLEBOFANG,WOGANJUEHENQIGUAI,WEISHENMEZUOZHEBUZHICHI。ERQIEZAISHIYONGGUOCHENGZHONG,WOFAXIANGAIKUHAISHIYOUDIANWENTI,SUIRANWODUIYINPINFANGMIANDEZHISHIBINGBUZENMELEJIE,YEBINGBUNENGYUDASHIPIMEIBINGLUN,DANWOYEXIWANGTONGGUOZIJIDEXUEXI,ZUIZHONGWANCHENGYIGELEISIAudioStreamerDEWANGLUOYINLEKU,MUQIANYEXUZHISHIYIGESHEXIANG,BUGUANZUIHOUZIJIYOUMEIYOUNEINENGLI,QIMAWOCENGJINGYECHANGSHIGUO,BUGUOGONGZUOZUIJINBIJIAOMANG,JIASHANGZIJIZHISHIDEQIANQUE,BUZHIHESHICAINENGSHIXIAN。BENCIJIUXIANLAIBUSHANGAudioStreamerMEIYOUZHICHIDE,SHIYONGAudioQueueBOFANGBENDIYINLE。

    AudioQueue详解

    AudioQueue工作原理

    我从Apple的官方文档上截下以下该图:
    AudioQueue

    该图很好的说明了AudioQueue的工作原理,如下说明:
    1. 用户调用相应的方法,将音频数据从硬盘中读入到AudioQueue的缓冲区中,并将缓冲区送入音频队列。
    2. 用户App通过AudioQueue提供的接口,告诉外放设备,缓冲区中已经有数据,可以拿去播放。
    3. 当一个缓冲区中的音频数据播放完毕之后,AudioQueue告诉用户,当前有一个空的缓冲区可以用来给你填充数据。
    4. 重复以上步骤,直至数据播放完毕。

    DAOZHEILI,KENDINGYOUBUSHAOTONGXUEFAXIANLE,AudioQueueQISHIJIUSHISHENGCHANZHEXIAOFEIZHEMOXINGDEDIANXINGYINGYONG。

    AudioQueue主要接口

    AudioQueueNewOutput

    OSStatus AudioQueueNewOutput(const AudioStreamBasicDescription *inFormat, AudioQueueOutputCallback inCallbackProc, void *inUserData, CFRunLoopRef inCallbackRunLoop, CFStringRef inCallbackRunLoopMode, UInt32 inFlags, AudioQueueRef  _Nullable *outAQ);

    GAIFANGFAYONGYUCHUANGJIANYIGEYONGYUSHUCHUYINPINDEAudioQueue

    参数及返回说明如下:
    1. inFormat:该参数指明了即将播放的音频的数据格式
    2. inCallbackProc:该回调用于当AudioQueue已使用完一个缓冲区时通知用户,用户可以继续填充音频数据
    3. inUserData:由用户传入的数据指针,用于传递给回调函数
    4. inCallbackRunLoop:指明回调事件发生在哪个RunLoop之中,如果传递NULL,表示在AudioQueue所在的线程上执行该回调事件,一般情况下,传递NULL即可。
    5. inCallbackRunLoopMode:指明回调事件发生的RunLoop的模式,传递NULL相当于kCFRunLoopCommonModes,通常情况下传递NULL即可
    6. outAQ:该AudioQueue的引用实例,

    FANHUIOSStatus,RUGUOZHIWEInoErr,ZEBIAOSHIMEIYOUCUOWU,AudioQueueCHUANGJIANCHENGGONG。

    AudioQueueAllocateBuffer

    OSStatus AudioQueueAllocateBuffer(AudioQueueRef inAQ, UInt32 inBufferByteSize, AudioQueueBufferRef  _Nullable *outBuffer);

    荣耀彩票代理GAIFANGFADEZUOYONGSHIWEICUNFANGYINPINSHUJUDEHUANCHONGQUKAIPIKONGJIAN

    参数及返回说明如下:
    1. inAQ:AudioQueue的引用实例
    2. inBufferByteSize:需要开辟的缓冲区的大小
    3. outBuffer:开辟的缓冲区的引用实例

    荣耀彩票代理FANHUIOSStatus,RUGUOZHIWEInoErr,ZEBIAOSHIHUANCHONGQUKAIPICHENGGONG。

    AudioQueueEnqueueBuffer

    OSStatus AudioQueueEnqueueBuffer(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, UInt32 inNumPacketDescs, const AudioStreamPacketDescription *inPacketDescs);

    GAIFANGFAYONGYUJIANGYIJINGTIANCHONGSHUJUDEAudioQueueBufferRUDUIDAOAudioQueue

    参数及返回说明如下:
    1. inAQ:AudioQueue的引用实例
    2. inBuffer:需要入队的缓冲区实例
    3. inNumPacketDescs:缓冲区中共存在有多少帧音频数据
    4. inPacketDescs荣耀彩票代理:缓冲区中每一帧的相关信息,用户需要指明其中每一帧在缓冲区中数据的偏移值,通过字段mStartOffset来指定

    FANHUIOSStatus,RUGUOZHIWEInoErr,ZEBIAOSHIHUANCHONGQUYIJINGCHENGGONGRUDUI。DENGDAIBOFANG

    AudioQueueStart Pause Stop Flush Reset Dispose

    OSStatus AudioQueueStart(AudioQueueRef inAQ, const AudioTimeStamp *inStartTime);
    OSStatus AudioQueuePause(AudioQueueRef inAQ);
    OSStatus AudioQueueStop(AudioQueueRef inAQ, Boolean inImmediate);
    OSStatus AudioQueueFlush(AudioQueueRef inAQ);
    OSStatus AudioQueueReset(AudioQueueRef inAQ);
    OSStatus AudioQueueDispose(AudioQueueRef inAQ, Boolean inImmediate);

    GUMINGSIYI,QIANSANGEFANGFAYONGYUYINPINDEBOFANG,ZANTINGJITINGZHI。HOULIANGGEFANGFAYONGYUZAIZUIHOUQINGXIJIZHONGZHIYINPINDUILIE,QINGXIQUEBAODUILIEZHONGDESHUJUWANQUANSHUCHU。AudioQueuDisposeYONGYUQINGLIAudioQueueSUOZHANZIYUAN。

    参数及返回说明如下:
    1. inAQ:AudioQueue的引用实例
    2. inStartTime:指明要开始播放音频的时间,如果要立即开始,传递NULL
    3. inImmediate:指明是否要立即停止音频播放,如是,传递true

    FANHUIOSStatusBIAOSHIXIANGGUANDECAOZUOSHIFOUCHENGGONGZHIXING。

    AudioQueueFreeBuffer

    OSStatus AudioQueueFreeBuffer(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);

    GAIFANGFAYONGYUZAIBOFANGJIESHUSHI,SHIFANGQINGLIHUANCHONGQUSHISHIYONG

    AudioQueueGetProperty AudioQueueSetProperty

    OSStatus AudioQueueGetProperty(AudioQueueRef inAQ, AudioQueuePropertyID inID, void *outData, UInt32 *ioDataSize);
    OSStatus AudioQueueSetProperty(AudioQueueRef inAQ, AudioQueuePropertyID inID, const void *inData, UInt32 inDataSize);

    CIGET/SETFANGFA,YONGYUSHEZHIHUOQUAudioQueueDEXIANGGUANSHUXING,QINGCANKANAudioQueue.hTOUWENJIANZHONGDEXIANGGUANSHUOMING。

    音频播放(LocalAudioPlayer)

    荣耀彩票代理SHIYONGAudioQueueBOFANGYINLE,YIBANXUYAOPEIHEAudioFileStreamYIQI,AudioFileStreamFUZEJIEXIYINPINSHUJU,AudioQueueFUZEBOFANGJIEXIDAODEYINPINSHUJU。

    此次仅实现最基本的本地音频播放功能,旨在为以后打下基础,不处理任何相关的状态(如暂停、停止、SEEK),错误等。播放方式类似流式播放,只是音频数据来源于本地文件而非网络,需经过以下几个步骤:
    1. 持续不断的从文件中读取部分数据,直到数据全部读取结束
    2. 将文件中读出的数据,交给AudioFileStream进行数据解析
    3. 创建AudioQueue,当数据放入到AudioQueueBuffer中
    4. 将缓冲区放到到AudioQueue中,开始播放音频
    5. 播放音频结束,清理相关资源

    播放器的初始化

    播放器的init主要用于指定要播放的音频文件,如下所示:
    init方法
    荣耀彩票代理 读取文件操作,使用NSFileHandle类,audioInUseLock,是一个NSLock*类型,用于在AudioQueue通知我们有空的缓冲区可以使用时做标记。

    我们在用户点击play按钮的时候,初始化该播放器,并调用play方法进行播放
    初始化播放器实例

    播放音频

    播放音频将分为以下步骤:
    1. 读取并开始解析音频
    2. 解析音频信息
    3. 解析音频帧
    4. 播放音频数据
    5. 清理相关资源

    WOMENXIANDINGYIJIGEHONG,YONGYUZHIDINGYIXIEHUANCHONGQUDEDAXIAO

    #define kNumberOfBuffers 3              //AudioQueueBuffer数量,一般指明为3
    #define kAQBufSize 128 * 1024           //每个AudioQueueBuffer的大小
    #define kAudioFileBufferSize 2048       //文件读取数据的缓冲区大小
    #define kMaxPacketDesc 512              //最大的AudioStreamPacketDescription个数

    LocalAudioPlayer相关属性

    LocalAudioPlayer中定义的属性如下所示:
    LocalAudioPlayer的相关属性

    读取并开始解析音频

    我们使用AudioFileStream来解析音频信息,在用户调用play方法之后,首先调用AudioFileStreamOpen,打开AudioFileStream,如下所示:
    打开AudioFileStream

    extern OSStatus 
    AudioFileStreamOpen (void *inClientData, AudioFileStream_PropertyListenerProc   inPropertyListenerProc, AudioFileStream_PacketsProc             inPacketsProc, AudioFileTypeID                          inFileTypeHint, AudioFileStreamID * outAudioFileStream);

    AudioFileStreamOpen的参数说明如下:
    1. inClientData:用户指定的数据,用于传递给回调函数,这里我们指定(__bridge LocalAudioPlayer*)self
    2. inPropertyListenerProc:当解析到一个音频信息时,将回调该方法
    3. inPacketsProc:当解析到一个音频帧时,将回调该方法
    4. inFileTypeHint:指明音频数据的格式,如果你不知道音频数据的格式,可以传0
    5. outAudioFileStream:AudioFileStreamID实例,需保存供后续使用

    DUQUDAOSHUJUZHIHOU,DIAOYONGAudioFileStreamParseBytesJIEXISHUJU,QIYUANXINGRUXIA:

    extern OSStatus
    AudioFileStreamParseBytes(AudioFileStreamID inAudioFileStream, UInt32 inDataByteSize, const void * inData, AudioFileStreamParseFlags inFlags);

    参数的说明如下:
    1. inAudioFileStream:AudioFileStreamID实例,由AudioFileStreamOpen打开
    2. inDataByteSize:此次解析的数据字节大小
    3. inData:此次解析的数据大小
    4. inFlags荣耀彩票代理:数据解析标志,其中只有一个值kAudioFileStreamParseFlag_Discontinuity = 1,表示解析的数据是否是不连续的,目前我们可以传0。

    荣耀彩票代理DANGWENJIANSHUJUHEBUDUQUJIESHUDESHIHOU,CISHIBIANKEYIGUANBIWENJIAN。

    解析音频信息

    如果解析到音频信息,那么将会调用之前指定的回调函数,如下所示:
    解析音频信息1
    解析音频信息2

    MEIGEXIANGGUANDESHUXINGDOUKEYIDIAOYONGAudioFileStreamGetPropertyLAIHUOQUDAOXIANGYINGDEZHI,YUANXINGRUXIA:

    extern OSStatus
    AudioFileStreamGetProperty(AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioPropertyDataSize, void *                              outPropertyData);

    参数说明:
    1. inAudioFileStream:AudioFileStreamID实例,由AudioFileStreamOpen打开
    2. inPropertyID:要获取的属性名称,参见AudioFileStream.h
    3. ioPropertyDataSize:指明该属性的大小
    4. outPropertyData荣耀彩票代理:用于存放该属性值的空间

    kAudioFileStreamProperty_DataFormat

    GAISHUXINGZHIMINGLEYINPINSHUJUDEGESHIXINXI,FANHUIDESHUJUSHIYIGEAudioStreamBasicDescriptionJIEGOU。XUBAOCUNYONGYUAudioQueueDESHIYONG

    kAudioFileStreamProperty_FileFormat

    GAISHUXINGZHIMINGLEYINPINSHUJUDEBIANMAGESHI,RUMPEGDENG。

    kAudioFileStreamProperty_AudioDataByteCount

    该属性可获取到音频数据的长度,可用于计算音频时长,计算公式为:
    荣耀彩票代理 时长 = (音频数据字节大小 * 8) / 采样率

    kAudioFileStreamProperty_BitRate

    荣耀彩票代理GAISHUXINGKEHUOQUDAOYINPINDECAIYANGLV,KEYONGYUJISUANYINPINSHIZHANG

    kAudioFileStreamProperty_DataOffset

    该属性指明了音频数据在整个音频文件中的偏移量:
    荣耀彩票代理 音频文件总大小 = 偏移量 + 音频数据字节大小

    kAudioFileStreamProperty_AudioDataPacketCount

    GAISHUXINGZHIMINGLEYINPINWENJIANZHONGGONGYOUDUOSHAOZHEN

    kAudioFileStreamProperty_ReadyToProducePackets

    荣耀彩票代理GAISHUXINGGAOSUWOMEN,YIJINGJIEXIDAOWANZHENGDEYINPINZHENSHUJU,ZHUNBEICHANSHENGYINPINZHEN,ZHIHOUHUIDIAOYONGDAOLINGWAIYIGEHUIDIAOHANSHU,WOMENZAIZHEILICHUANGJIANYINPINDUILIEAudioQueue,RUGUOYINPINSHUJUZHONGYOUMagic Cookie Data,ZEXIANDIAOYONGAudioFileStreamGetPropertyInfo,HUOQUGAISHUJUSHIFOUKEXIE,RUGUOKEXIEZAIQUCHUGAISHUXINGZHI,BINGXIERUDAOAudioQueue。ZHIHOUBIANSHIYINPINSHUJUZHENDEJIEXI。

    解析音频帧

    YINPINXINXIJIEXIWANBIZHIHOU,JIUYINGGAIJIEXIYINPINSHUJUZHENLE,DAIMARUXIASUOSHI:

    解析音频数据帧1
    在这里,我们利用之前设置的inClientData,将该回调函数,由C语言形式改为Objc的形式,解析到音频数据之后的处理代码如下所示:
    解析音频数据帧2

    解析到音频数据之后,我们穴ky"http://chsp8.com/qq/" target="_blank" class="keylink">qq9q8r9vt3QtMjrtb1BdWRpb1F1ZXVlQnVmZmVy1tCjrMrXz8ijrLjDu9i197qvyv21xNSt0M3I58/Cy/nKvqO6PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;"> typedef void (*AudioFileStream_PacketsProc)(void * inClientData, UInt32 inNumberBytes,UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions);

    参数说明:
    1. inClientData:由AudioFileStreamOpen设置的用户数据
    2. inNumberBytes:音频数据的字节数
    3. inNumberPackets:解析到的音频帧数
    4. inInputData:包含这些音频数据帧的数据
    5. inPacketDescriptions:AudioStreamPacketDescription数组,其中包含mStartOffset,指明了该帧相关数据的起始位置,mDataByteSize指明了该帧数据的大小。

    此时我们首先创建一个音频队列,用以播放音频,并为每个缓冲区分配空间,如下所示:
    创建音频队列

    荣耀彩票代理ZHIHOUWOMENBIANLIMEIYIZHEN,HUOQUMEIYIZHENDEPIANYIWEIZHIJISHUJUDAXIAO,RUGUODANGQIANZHENDESHUJUDAXIAO,YIJINGWUFACUNFANGDAODANGQIANDEHUANCHONGQUDANGZHONG,CISHI,WOMENYINGGAIJIANGDANGQIANDEHUANCHONGQURUDUI,XIUGAIDANGQIANSHIYONGDEHUANCHONGQUWEIXIAYIGE,BINGZHONGZHIXIANGGUANDESHUJUTIANCHONGXINXIJIYIBAOHANDESHUJUZHENXINXI。

    self.audioQueueCurrentBufferIndex = (++self.audioQueueCurrentBufferIndex) % kNumberOfBuffers;
    self.audioPacketsFilled = 0;
    self.audioDataBytesFilled = 0;

    荣耀彩票代理RUGUOCISHIHAIMEIYOUKAISHIBOFANGYINLE,ZEKEYIKAISHIBOFANGYINLE

    if(self.isPlaying == NO) {
        err = AudioQueueStart(audioQueueRef, NULL);
        self.isPlaying = YES;
    }

    RUOXIAYIGEZHIDINGDEHUANCHONGQUYIJINGZAISHIYONG,JIQUANBUHUANCHONGQUYIMANQIERUDUI,ZEYINGGAIJINXINGDENGDAI

    while(inuse[self.audioQueueCurrentBufferIndex]);

    荣耀彩票代理ZUIHOU,RUGUOHUANCHONGQUKONGJIANHAINENGCUNFANGYIZHENSHUJU,WOMENJIUSHIYONGmemcpyJIANGSHUJUFUZHIDAOHUANCHONGQUZHONGXIANGYINGDEWEIZHISHANGQU,BAOCUNMEIYIZHENDEXIANGGUANXINXI,SHEZHIMEIYIZHENZAIHUANCHONGQUZHONGDEPIANYILIANG(mStartOffset),SHEZHIDANGQIANHUANCHONGQUYICUNFANGDESHUJUDAXIAO,JIYIBAOHANDEZHENLIANG。

    DANGYIGEHUANCHONGQUSHIYONGJIESHUZHIHOU,AudioQueueJIANGHUIDIAOYONGZHIQIANYOUAudioQueueNewOutputSHEZHIDEHUIDIAOHANSHU,RUXIASUOSHI:

    空缓冲区回调
    荣耀彩票代理 在该回调中,我们遍历每个缓冲区,找到空缓冲区,将该缓冲区的标记修改为未使用。

    播放音频数据

    ZAICHULISHUJUZHENDESHIHOU,RUGUOHUANCHONGQUYIJINGMAN(ZHIHUANCHONGQUDEKONGJIANBUZUYICUNFANGXIAYIZHENSHUJU),ZECISHIKEYIKAISHIBOFANGYINPIN

    if(self.isPlaying == NO) {
        err = AudioQueueStart(audioQueueRef, NULL);
        self.isPlaying = YES;
    }

    荣耀彩票代理WOMENHAIKEYIDIAOYONGAudioQueuePauseDENGXIANGGUANFANGFALAIZANTINGHEZHONGZHIYINPINBOFANG,RUXIASUOSHI:

    暂停和终止播放

    清理相关资源

    最终,我们清理相关资源,在前面,我们利用了AudioQueueAddPropertyListener为kAudioQueueProperty_IsRunning属性设置了一个监听器,当AudioQueue启动或者是终止的时候,会调用该函数:
    设置监听器

    该回调函数,如下所示:
    AudioQueue启动或终止回调

    ZAIGAIFANGFAZHONG,WOMENSHIYONGAudioQueueResetZHONGZHIBOFANGDUILIE,DIAOYONGAudioQueueFreeBufferSHIFANGHUANCHONGQUKONGJIAN,SHIFANGAudioQueueSUOYOUZIYUAN,GUANBIAudioFileStream。

    不过在实际使用过程中,我发现数据通ky"http://chsp8.com/qq/" target="_blank" class="keylink">qq/1bXEyrG68kF1ZGlvUXVldWWyorK7u+HW97av1tXWuaOsvLSyu7vh1ve2r7X308O4w7vYtfejrMv50tTO0s/ro6zTprjDysfSqs7Sw8fX1Ly6u/HIobW9tbHHsLKlt8W1xL34tsijrLWxsqW3xc3qsc+1xMqxuvK199PDQXVkaW9RdWV1ZVN0b3DW1da5sqW3xbDJo6zV4rj2zsrM4sH018W0/dLUuvPU2bzM0PjR0L6/oaM8L3A+CjxoMyBpZD0="结束">结束

    ZHONGYUWANCHENGLEQUANBUDEDAIMA,YONGHUZHIYAODIANJIplay,JIKEYITINGDAO《YAOYUANDETA》ZHEISHOUMEIMIAODEYINLELE。YOUYUJIEMIANSHANGZHIYOUYIGEplayANNIU,BUFANGYANSHITULE,DAISHANGERJI,JINGJINGDEXIANGSHOUZIJIDECHENGGUOYUZHEIMEIMIAODEYINLE。。

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规