了解更多IOS底层原理知识,关注腾讯课堂八点钟学院IOS高级开发
IOS学习交流群 431449751;
即时音频的实现分为两个部分 1网络层 2音频流 把网络数据准确的放到音频队列(AudioQueueRef)中播放。
在实现的过程中有多种方法,网络层这里采用CFNetwork,音频队列采用重复使用已分配好的缓冲队列AudioQueueBufferRef内存
AudioQueueAllocateBuffer(audioQueue,packetBufferSize, &audioQueueBuffer[i])分配内存;
再使用mencpy,代码如下
AudioQueueBufferRef fillBuf =audioQueueBuffer[fillBufferIndex];
memcpy((char*)fillBuf->mAudioData +bytesFilled, (constchar*)inInputData + packetOffset, packetSize);
这种方法来重复使用内存。
但这种使用方法节省了内存的分配,但是要确保音频数据播放的正确顺序,因为音频数据来自于网络,所以我们要协调好网络数据和音频队列中的数据,
由于我们使用的是重复使用内存,音频队列存放音频数据是有限的(可以不断的分配新的AudioQueueBufferRef来存储音频数据,再入音频对列AudioQueueRef中,就不需要考虑网络数据和音频队列数据的同步性了),
需要网络数据和音频队列中的数据要同步,数据的同步行,我们这里使用的锁机制,用条件锁,当网络数据下载过多时,而我们的音频队列中的数据都没有播放时,必须等待音频队列中存在播放过的队列数据才能把新的数据存放进去队列中,不然会造成音频数据播放的丢失和不连续性。
网络层代码
这里获取网络header,简历音频文件流,这个主要用于过渡数据,把网络数据解析成可播放的音频数据包,解析成的音频数据在AudioFileStreamOpen的回调函数中处理,一个是音频文件属性的解析,一个音频数据的解析,从前文中我们知道即文件格式读取和数据格式读取,我们在音频数据解析中创建我们的音频队列,并把音频数据存放到音频队列中去
createQueue接口
当音频队列读完一个队列中的数据就会回调 MyAudioQueueOutputCallback, 我们在这个回调处理播放完的队列数据
数据入音频队列的接口enqueueBuffer
里面用到的pthread_cond_wait 是在等待音频队列中有多得空间来存放新的音频数据, 为什么还要使用pthread_mutex_lock,主要是保证逻辑的正确性,因为在互斥锁中间处理的可能不止一条命令,可以查看互斥锁和条件锁的使用。
音频队列中队列数据处理完的回调处理
文件属性的回调处理