Audio Queue
服務是比較高級的服務。它可讓你的應用程序使用硬件進行錄音和回放音頻(如麥克風和揚聲器),而不須要知道硬件接口。同時音頻隊列服務支持一些高級功能。它提供精細的定時控制以及支持預約播放和同步。你可使用它來同步多個音頻隊列的播放,並使音頻與視頻同步。ios
音頻隊列服務是一個純C接口,您能夠在Cocoa應用程序以及Mac OS X命令行工具中使用。 爲了幫助保持專一於音頻隊列服務,本文檔中的代碼示例有時經過使用Core Audio SDK中的C ++類進行簡化。 可是,使用音頻隊列服務不須要SDK和C ++語言。web
在這個章節中,你會了解到音頻隊列的功能,體系結構和內部工做原理。將會向你介紹音頻隊列,音頻隊列緩衝區以及音頻隊列用於音頻錄製和回放的回調函數。你還能夠了解到關於音頻隊列狀態和參數的信息。編程
一個 Audio queue(音頻隊列)是用於在 ios 和 mac 平臺上的播放和錄製音頻的軟件對象。它由 AudioQueueRef 類型表示,在 AudioQueue.h 頭文件中聲明。markdown
一個 Audio queue(音頻隊列)作以下工做:數據結構
全部的 audio queue 都大體有相同的結構,都有如下部分構成:app
該結構根據音頻隊列是用於記錄仍是播放而變化。 差別在於音頻隊列如何鏈接其輸入和輸出,以及在回調函數的做用。異步
錄製的音頻隊列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);複製代碼