Audio Queue
服务是比较高级的服务。它可让你的应用程序使用硬件进行录音和回放音频(如麦克风和扬声器),而不须要知道硬件接口。同时音频队列服务支持一些高级功能。它提供精细的定时控制以及支持预约播放和同步。你可使用它来同步多个音频队列的播放,并使音频与视频同步。ios
音频队列服务是一个纯C接口,您能够在Cocoa应用程序以及Mac OS X命令行工具中使用。 为了帮助保持专一于音频队列服务,本文档中的代码示例有时经过使用Core Audio SDK中的C ++类进行简化。 可是,使用音频队列服务不须要SDK和C ++语言。web
在这个章节中,你会了解到音频队列的功能,体系结构和内部工做原理。将会向你介绍音频队列,音频队列缓冲区以及音频队列用于音频录制和回放的回调函数。你还能够了解到关于音频队列状态和参数的信息。编程
一个 Audio queue(音频队列)是用于在 ios 和 mac 平台上的播放和录制音频的软件对象。它由 AudioQueueRef 类型表示,在 AudioQueue.h 头文件中声明。数据结构
一个 Audio queue(音频队列)作以下工做:app
全部的 audio queue 都大体有相同的结构,都有如下部分构成:异步
该结构根据音频队列是用于记录仍是播放而变化。 差别在于音频队列如何链接其输入和输出,以及在回调函数的做用。async
录制的音频队列函数
一个录制的音频队列,使用 AudioQueueNewInput
函数建立,具备以下结构:工具
记录音频队列的输入侧一般链接到外部音频硬件,例如麦克风.在iOS中,音频来自麦克风或耳机链接的设备。 在Mac OS X的默认状况下,音频来自系统的默认音频输入设备,由用户在系统首选项中设置。优化
录制音频队列的输出端是你的回调函数。当记录到磁盘时,回调将从其音频队列接收的新音频数据的缓冲器写入音频文件。然而,能够以其它方式使用记录音频队列。您还可使用一个,例如,在实时音频分析仪。 在这种状况下,您的回调将直接提供音频数据到您的应用程序,而不是写入磁盘。
每个音频队列——不管是录制的或者是播放的,都有一个或多个音频队列缓冲器。这些缓冲器以称为缓冲器队列的特定顺序排列。如图所示,音频队列缓冲器根据它们被填充的顺序被编号,这是它们被切换到回调的相同的顺序。
播放的音频队列
一个播放的音频队列,使用AudioQueueNewOutput
函数建立,具备以下结构:
在播放音频队列中,回调在输入侧。回调负责从磁盘(或一些其余源)获取音频数据并将其移交到音频队列。当没有更多的数据要播放时,回放回调也会告诉他们的音频队列中止。
一个 audio queue buffer(音频队列缓冲区)是一个 AudioQueueBuffer
类型的数据结构,声明在 AudioQueue.h
中:
typedef struct AudioQueueBuffer {
const UInt32 mAudioDataBytesCapacity;
void *const mAudioData;
UInt32 mAudioDataByteSize;
void *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;
复制代码
上面结构中的 mAudioData
字段,它指向缓冲区自己:a block of memory that serves as a container for transient blocks of audio data being played or recorded. 其它字段帮助 audio queue 管理缓冲区。
音频队列可使用你的应用程序指定的任意数量的缓冲区。不过通常都是指定为3个。这样就容许一个去写数据到磁盘,另外一个正在填充新的数据,若是须要补偿诸如磁盘I / O延迟之类的事情,则第三缓冲器可用。
Audio queues(音频队列)为其缓冲区提供内存管理。
这提升了添加到应用程序中的录制和播放功能的鲁棒性。 它还有助于优化资源使用。
录音过程
播放过程
控制播放过程
音频队列缓冲区老是按它们入队的顺序播放。 可是,音频队列服务使用AudioQueueEnqueueBufferWithParameters
函数为您提供了对播放过程的一些控制。 此功能容许您:
一般,使用音频队列服务的大部分编程工做包括编写音频队列回调函数。
在记录或播放期间,音频队列回调由拥有它的音频队列重复调用。 呼叫之间的时间取决于音频队列缓冲器的容量,而且一般在半秒到几秒的范围内。
音频队列回调的一个职责,不论是用于记录仍是播放,都是将音频队列缓冲区返回到缓冲区队列。 回调使用AudioQueueEnqueueBuffer
函数将缓冲区添加到缓冲区队列的末尾。 对于播放,若是须要更多的控制,您能够改用AudioQueueEnqueueBufferWithParameters
函数,如控制播放过程当中所述。
录制音频队列的回调函数
本节介绍了在将音频录制到磁盘文件的常见状况下编写的回调。 下面是AudioQueue.h
头文件中声明的录音音频队列回调的原型:
AudioQueueInputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs
);
复制代码
记录音频队列在调用回调时提供回调须要将下一组音频数据写入音频文件的全部内容:
回放音频队列的回调函数
AudioQueueOutputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
);
复制代码
回放音频队列在调用回调时提供回调须要从音频文件读取下一组音频数据的内容:
音频队列服务能够根据不一样的音频格式来转换须要使用的编解码器(音频数据编码和解码)。您的录音或播放应用程序可使用任何已安装编解码器的音频格式,你不须要去编写自定义代码来处理各类音频格式。简单来讲也就是你的回调不须要知道具体的音频数据格式。
下图所示的是它如何工做的。每个音频队列对应一种音频数据格式,音频数据格式使用AudioStreamBasicDescription
描述。当你为 mFormatID
字段设置值得时候,音频队列就会使用与之相对应的编解码器,而后你再为其定义采样率和通道数。
如上图所示,第一步,你的应用程序告诉音频队列要使用的音频数据格式,并开始录制。第二步,音频队列将根据您指定的格式使用编解码器获取新的音频数据并对其进行转换。 音频队列而后调用回调,处理一个包含适当造成的音频数据的缓冲区。第三部,你的回调将格式化的音频数据写入磁盘。 一样,你的回调不须要知道数据格式。
下图是表示音频回放是如何回放的。
音频队列可使用任何已安装的编解码器,不管是Mac OS X原生的仍是由第三方提供的。
音频队列在建立和处理之间有一个生命周期。 您的应用程序管理今生命周期,并使用AudioQueue.h头文件中声明的六个函数来控制音频队列的状态:
你可使用 AudioQueueStop
函数在同步或异步模式下:
在这里将会罗列出我使用Audio Queue
的时候所遇到的问题,及须要注意的事项的等问题。
利用Audio Queue
播放音频是,若是音频队列中长时间没有数据时,播放的回调会自动中止。因此即便往音频队列中拷贝空的音频数据也能够,这样就避免了播放的回调自动终止。
下面是示例代码:
void AudioPlayer::handleTVUWebRTCAudioOutputBuffer(void * aqData,AudioQueueRef inAQ , AudioQueueBufferRef inBuffer)
{
TVUWebRTCManager *webRTCManager = [TVUWebRTCManager shareInstance];
if (!webRTCManager) {
return;
}
if (webRTCManager.isEndupCall) {
NSLog(@"isEndupCall is YES...");
}else{
NSLog(@"isEndupCall is NO...");
}
RTCPeerConnection *rtcPeerConn = [webRTCManager getNewestPeerConnection];
if (rtcPeerConn == NULL || rtcPeerConn == nil) {
// [mLock unlock];
return;
}
int8_t * source = [rtcPeerConn getPlayoutAudioBuffer]; // 拷贝空的音频数据到缓冲队列中
if (source == NULL || source == nil) {
inBuffer->mAudioDataByteSize = kTVUWebRTCAudioDataByteSize;
memset(inBuffer->mAudioData,0,kTVUWebRTCAudioDataByteSize);
AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
// [mLock unlock];
return;
}
inBuffer->mAudioDataByteSize = kTVUWebRTCAudioDataByteSize;
if ([rtcPeerConn isWebRTCPlayoutActived] || [rtcPeerConn isWebRTCAudioFrameAvailable])
{
memset(inBuffer->mAudioData,0,kTVUWebRTCAudioDataByteSize);
AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
// [mLock unlock];
return;
}
// demon add , caculate volume db
caculateVolumeDB(inBuffer, 0, k_Mono);
memcpy(inBuffer->mAudioData, source, kTVUWebRTCAudioDataByteSize);
AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
// [mLock unlock];
return ;
}
复制代码
功能:中止音频的播放或录制
描述:若是音频队列未被其它音频服务使用,则此功能将重置音频队列并中止与队列相关联的音频硬件。同步中止(synchronous stops)会马上执行播放或录制,不管音频队列中是否有音频内容;异步中止(asynchronous stops)直到音频队列中的音频数据被播放完后,才会中止播放或者录制。
参数:
inAQ
: 要中止的音频队列;
inImmediate
:值为yes时,同步中止即马上执行;值为NO时,异步中止,要等到音频队列中的内容播放或录制完成后才会中止。
注意:
方法原型:
extern OSStatus
AudioQueueStop( AudioQueueRef inAQ,
Boolean inImmediate) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
复制代码
功能: 暂停音频播放或录制
描述:暂停队列不影响缓冲区或充值音频队列。要使用音频队列恢复播放或录制,请调用 AudioQueueStart
方法原型:
extern OSStatus
AudioQueuePause( AudioQueueRef inAQ) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
复制代码
功能:重置音频队列的解码器状态
描述:在全部的音频队列被播放完成以后,这个函数清除全部解码器状态的信息。您必须按照编码音频的缓冲区序列调用此函数; 不然,某些音频可能不会在下一组排队的缓冲区中播放。 惟一没有必要调用AudioQueueFlush的时间是使用inImmediate = false的AudioQueueStop。 (此操做内部调用AudioQueueFlush。)
此外,您可能但愿在调用AudioQueueStop以前调用此函数,具体取决于您是否要当即中止,不管播放什么,或者是否要确保全部缓冲数据和处理中间的全部数据都被记录或播放中止。
函数原型:
extern OSStatus
AudioQueueFlush( AudioQueueRef inAQ) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
复制代码
功能:重置音频队列
描述:该功能当即重置音频队列,刷新任何音频队列的缓冲区,从先前调度的使用中删除全部缓冲区,并重置任何解码器和数字信号处理(DSP)状态信息。
注意: 在重置过程当中,一般会调用全部挂起的缓冲区回调,可是若是调用线程响应缓冲区回调,则能够在AudioQueueReset返回后发生额外的缓冲区回调。
函数原型:
extern OSStatus
AudioQueueReset( AudioQueueRef inAQ) __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);复制代码