iOS声频播发之AudioQueue(一):播发当地歌曲

摘要: AudioQueue介绍 AudioStreamer表明 AudioQueue详细说明 AudioQueue工作中基本原理 AudioQueue关键插口 AudioQueueNewOutput AudioQueueAllocateBuffer AudioQueueEnqueueBuffer AudioQueueStart Pause Stop Flush Reset Dispose AudioQueu...

AudioQueue介绍 AudioStreamer表明 AudioQueue详细说明 AudioQueue工作中基本原理 AudioQueue关键插口 AudioQueueNewOutput AudioQueueAllocateBuffer AudioQueueEnqueueBuffer AudioQueueStart Pause Stop Flush Reset Dispose AudioQueueFreeBuffer AudioQueueGetProperty AudioQueueSetProperty 声频播发LocalAudioPlayer 播发器的原始化 播发声频 LocalAudioPlayer有关特性 载入并刚开始分析声频 分析声频信息内容 kAudioFileStreamProperty_DataFormat kAudioFileStreamProperty_FileFormat kAudioFileStreamProperty_AudioDataByteCount kAudioFileStreamProperty_BitRate kAudioFileStreamProperty_DataOffset kAudioFileStreamProperty_AudioDataPacketCount kAudioFileStreamProperty_ReadyToProducePackets 分析声频帧 播发声频数据信息 清除有关資源 完毕

iOS完成播发当地歌曲,有许多种方式,比如AVAudioPlayer,这种都能非常好的担任,有些人就怪异了,为何要退而求次之,应用更繁杂的AudioQueue来播发当地歌曲呢?请再次向下看

AudioQueue介绍

AudioQueue,在iPhone的开发设计者文本文档上是那么说的

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

AudioQueue官方网文本文档

AudioQueue服务出示一种立即的,低花销的方法以用以在iOS及Mac OS X上音频和播发歌曲。

应用AudioQueue播发歌曲的优势便是花销不大而且适用流式的播发(边下面播),可是缺陷便是开发设计难度系数大,因此有互联网声频库AudioStreamer,在网上有许多讲AudioQueue的,可是有案例编码表明的,确实是少之又少,恰好企业新项目有声频要求,尽管新项目中应用的并不是自己写的声频播发作用,但过后還是想自身来科学研究一下,这一我认为较为奇异也较为趣味的AudioQueue。

AudioStreamer表明

iOS上一个较为知名的流媒体服务器声频播发库是AudioStreamer,该库即便用了AudioQueue,但是该声频库其实不适用当地歌曲播发,我觉得很怪异,为何创作者不兼容。并且在应用全过程中,发了现该库還是有点儿难题,尽管我对声频层面的专业知识其实不如何掌握,也其实不能与高手匹敌并论,但因为我期待根据自身的学习培训,最后进行一个相近AudioStreamer的互联网歌曲库,现阶段或许仅仅一个构想,无论最终自身有木有那工作能力,至少曾经的我也试着过,但是工作中近期较为忙,再加自身专业知识的缺乏,不知道什么时候才可以完成。此次就先来补好AudioStreamer沒有适用的,应用AudioQueue播发当地歌曲。

AudioQueue详细说明 AudioQueue工作中基本原理

我在Apple的官方网文本文档上截下为下该图:
AudioQueue

该图非常好的表明了AudioQueue的工作中基本原理,以下表明:
1. 客户启用相对的方式,将声频数据信息从电脑硬盘中读入到AudioQueue的缓存区中,并将缓存区送入声频序列。
2. 客户App根据AudioQueue出示的插口,告知外放机器设备,缓存区中早已了解据,能够拿来播发。
3. 当一个缓存区中的声频数据信息播发结束以后,AudioQueue告知客户,当今有一个空的缓存区能够用于让你添充数据信息。
4. 反复之上流程,直到数据信息播发结束。

到这儿,毫无疑问有很多同学们发觉了,AudioQueue实际上便是生产制造者消費者实体模型的典型性运用。

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

该方式用以建立一个用以輸出声频的AudioQueue

主要参数及回到表明以下:
1. inFormat:该主要参数指出了将要播发的声频的数据信息文件格式
2. inCallbackProc:该回调函数用以当AudioQueue已应用完一个缓存区的时候通告客户,客户能够再次添充声频数据信息
3. inUserData:由客户传到的数据信息指针,用以传送给回调函数涵数
4. inCallbackRunLoop:指出回调函数恶性事件产生在哪儿个RunLoop当中,假如传送NULL,表明在AudioQueue所属的进程上实行该回调函数恶性事件,一般状况下,传送NULL就可以。
5. inCallbackRunLoopMode:指出回调函数恶性事件产生的RunLoop的方式,传送NULL非常于kCFRunLoopCommonModes,一般状况下传送NULL就可以
6. outAQ:该AudioQueue的引入案例,

回到OSStatus,假如数值noErr,则表明沒有不正确,AudioQueue建立取得成功。

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

该方式的功效是为储放声频数据信息的缓存区开拓室内空间

主要参数及回到表明以下:
1. inAQ:AudioQueue的引入案例
2. inBufferByteSize:必须开拓的缓存区的尺寸
3. outBuffer:开拓的缓存区的引入案例

回到OSStatus,假如数值noErr,则表明缓存区开拓取得成功。

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

该方式用以将早已添充数据信息的AudioQueueBuffer入队到AudioQueue

主要参数及回到表明以下:
1. inAQ:AudioQueue的引入案例
2. inBuffer:必须入队的缓存区案例
3. inNumPacketDescs:缓存区中国共产党存有有是多少帧声频数据信息
4. inPacketDescs:缓存区中每一帧的有关信息内容,客户必须指出在其中每一帧在缓存区中数据信息的偏位值,根据字段名mStartOffset来特定

回到OSStatus,假如数值noErr,则表明缓存区早已取得成功入队。等候播发

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);

说白了,前三个方式用以声频的播发,中止及终止。后2个方式用以在最终清理及重设声频序列,清理保证序列中的数据信息彻底輸出。AudioQueuDispose用以清除AudioQueue所占資源。

主要参数及回到表明以下:
1. inAQ:AudioQueue的引入案例
2. inStartTime:指出要刚开始播发声频的時间,假如要马上刚开始,传送NULL
3. inImmediate:指出是不是要马上终止声频播发,如果是,传送true

回到OSStatus表明有关的实际操作是不是取得成功实行。

AudioQueueFreeBuffer
OSStatus AudioQueueFreeBuffer(AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);

该方式用以在播发完毕时,释放出来清除缓存区的时候应用

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

此GET/SET方式,用以设定获得AudioQueue的有关特性,请查阅AudioQueue.h头文档中的有关表明。

声频播发(LocalAudioPlayer)

应用AudioQueue播发歌曲,一般必须相互配合AudioFileStream一起,AudioFileStream承担分析声频数据信息,AudioQueue承担播发分析到的声频数据信息。

本次仅完成最基本的当地声频播发作用,致力于为之后奠定基本,不解决一切有关的情况(如中止、终止、SEEK),不正确等。播发方法相近流式的播发,仅仅声频数据信息来源于于当地文档并非互联网,需历经下列好多个流程:
1. 不断持续的文本文件中载入一部分数据信息,直至数据信息所有载入完毕
2. 将文档中读取的数据信息,交到AudioFileStream开展数据信息分析
3. 建立AudioQueue,当数据信息放进到AudioQueueBuffer中
4. 将缓存区放进到AudioQueue中,刚开始播发声频
5. 播发声频完毕,清除有关資源

播发器的原始化

播发器的init关键用以特定要播发的声频文档,以下所显示:
init方法
载入文档实际操作,应用NSFileHandle类,audioInUseLock,是一个NSLock*种类,用以在AudioQueue通告大家有时间的缓存区可使用时做标识。

大家再用户点一下play按键的情况下,原始化该播发器,并启用play方式开展播发
初始化播放器实例

播发声频将分成下列流程:
1. 载入并刚开始分析声频
2. 分析声频信息内容
3. 分析声频帧
4. 播发声频数据信息
5. 清除有关資源

大家先界定好多个宏,用以特定一些缓存区的尺寸

#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案例,需储存供事后应用

载入到数据信息以后,启用AudioFileStreamParseBytes分析数据信息,其原形以下:

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。

当文档数据信息合部载入完毕的情况下,这时即可以关掉文档。

分析声频信息内容

假如分析到声频信息内容,那麼可能启用以前特定的回调函数涵数,以下所显示:
解析音频信息1
解析音频信息2

每一个有关的特性都可以以启用AudioFileStreamGetProperty来获得到相对的值,原形以下:

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

该特性指出了声频数据信息的文件格式信息内容,回到的数据信息是一个AudioStreamBasicDescription构造。需储存用以AudioQueue的应用

kAudioFileStreamProperty_FileFormat

该特性指出了声频数据信息的编号文件格式,如MPEG等。

kAudioFileStreamProperty_AudioDataByteCount

该特性可获得到声频数据信息的长短,能用于测算声频时间,测算公式计算为:
时间 = (声频数据信息字节数尺寸 * 8) / 取样率

kAudioFileStreamProperty_BitRate

该特性可获得到声频的取样率,能用于测算声频时间

kAudioFileStreamProperty_DataOffset

该特性指出了声频数据信息在全部声频文档中的偏位量:
声频文档总尺寸 = 偏位量 + 声频数据信息字节数尺寸

kAudioFileStreamProperty_AudioDataPacketCount

该特性指出了声频文档中国共产党有是多少帧

kAudioFileStreamProperty_ReadyToProducePackets

该特性告知大家,早已分析到详细的声频帧数据信息,提前准备造成声频帧,以后会启用到此外一个回调函数涵数,大家在这里里建立声频序列AudioQueue,假如声频数据信息中有Magic Cookie Data,则先启用AudioFileStreamGetPropertyInfo,获得该数据信息是不是可写,假如可写再取下该特性值,并载入到AudioQueue。以后就是声频数据信息帧的分析。

分析声频帧

声频信息内容分析结束以后,就应当分析声频数据信息帧了,编码以下所显示:

解析音频数据帧1
在这里里,大家运用以前设定的inClientData,将该回调函数涵数,由C語言方式改成Objc的方式,分析到声频数据信息以后的解决编码以下所显示:
解析音频数据帧2

分析到声频数据信息以后,大家穴ky"qq/" target="_blank" >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指出了该帧数据信息的尺寸。

这时大家最先建立一个声频序列,用于播发声频,并且为每一个缓存区别配室内空间,以下所显示:
创建音频队列

以后大家解析xml每一帧,获得每一帧的偏位部位及数据信息尺寸,假如当今帧的数据信息尺寸,早已没法储放到当今的缓存区之中,这时,大家应当将当今的缓存区入队,改动当今应用的缓存区为下一个,并举置有关的数据信息添充信息内容及已包括的数据信息帧信息内容。

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

假如这时还没有有刚开始播发歌曲,则能够刚开始播发歌曲

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

若下一个特定的缓存区早已在应用,即所有缓存区已满且入队,则应当开展等候

while(inuse[self.audioQueueCurrentBufferIndex]);

最终,假如缓存区室内空间还能储放一帧数据信息,大家就应用memcpy将数据信息拷贝到缓存区中相对的部位上来,储存每一帧的有关信息内容,设定每一帧在缓存区中的偏位量(mStartOffset),设定当今缓存区已存方的数据信息尺寸,及已包括的帧量。

当一个缓存区应用完毕以后,AudioQueue可能启用以前由AudioQueueNewOutput设定的回调函数涵数,以下所显示:

空缓冲区回调
在该回调函数中,大家解析xml每一个缓存区,寻找空缓存区,将该缓存区的标识改动为未应用。

播发声频数据信息

在解决数据信息帧的情况下,假如缓存区早已满(指缓存区的室内空间不够以储放下一帧数据信息),则这时能够刚开始播发声频

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

大家还能够启用AudioQueuePause等有关方式来中止和停止声频播发,以下所显示:

暂停和终止播放

清除有关資源

最后,大家清除有关資源,在前边,大家运用了AudioQueueAddPropertyListener为kAudioQueueProperty_IsRunning特性设定了一个监视器,当AudioQueue起动或是是停止的情况下,会启用该涵数:
设置监听器

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

在该方式中,大家应用AudioQueueReset重设播发序列,启用AudioQueueFreeBuffer释放出来缓存区室内空间,释放出来AudioQueue全部資源,关掉AudioFileStream。

但是在具体应用全过程中,发了现数据信息通ky"qq/" target="_blank" >qq/1bXEyrG69kF1ZGlvUXVldWWyorK7u+HW97av1tXWuaOsvLSyu7vh1ve2r7X308O4w7vYtfejrMv50tTO0s/ro6zTprjDysfSqs7Sw8fX1Ly6u/HIobW9tbHHsLKlt8W1xL34tsijrLWxsqW3xc3qsc+1xMqxuvK199PDQXVkaW9RdWV1ZVN0b三dW1da5sqW3xbDJo6zV4rj2zsrM4sH018W0/dLUuvPU2bzM0PjR0L6/oaM8L3A+CjxoMyBpZD0="完毕">完毕

总算进行了所有的编码,客户要是点一下play,就可以以听见《漫长的她》这首美好的歌曲了。因为页面上仅有一个play按键,没放演试图了,戴上耳机,静静的享有自身的成效与这美好的歌曲。。



联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503

技术支持:网页申请